1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5 * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/bus.h>
32#include <sys/kernel.h>
33#include <sys/lock.h>
34#include <sys/module.h>
35#include <sys/mutex.h>
36#include <sys/rman.h>
37#include <sys/resource.h>
38#include <machine/bus.h>
39#include <sys/gpio.h>
40
41#include <dev/ofw/ofw_bus.h>
42#include <dev/ofw/ofw_bus_subr.h>
43
44#include <dev/clk/clk.h>
45#include <dev/hwreset/hwreset.h>
46
47#include <dev/gpio/gpiobusvar.h>
48
49#include "opt_snd.h"
50#include <dev/sound/pcm/sound.h>
51#include <dev/sound/fdt/audio_dai.h>
52#include "audio_dai_if.h"
53
54#define	SYSCLK_CTL		0x00c
55#define	 AIF1CLK_ENA			(1 << 11)
56#define	 AIF1CLK_SRC_MASK		(3 << 8)
57#define	 AIF1CLK_SRC_PLL		(2 << 8)
58#define	 SYSCLK_ENA			(1 << 3)
59#define	 SYSCLK_SRC			(1 << 0)
60
61#define	MOD_CLK_ENA		0x010
62#define	MOD_RST_CTL		0x014
63#define	MOD_AIF1			(1 << 15)
64#define	MOD_ADC				(1 << 3)
65#define	MOD_DAC				(1 << 2)
66
67#define	SYS_SR_CTRL		0x018
68#define	 AIF1_FS_MASK		(0xf << 12)
69#define	  AIF_FS_48KHZ		(8 << 12)
70
71#define	AIF1CLK_CTRL		0x040
72#define	 AIF1_MSTR_MOD		(1 << 15)
73#define	 AIF1_BCLK_INV		(1 << 14)
74#define	 AIF1_LRCK_INV		(1 << 13)
75#define	 AIF1_BCLK_DIV_MASK	(0xf << 9)
76#define	  AIF1_BCLK_DIV_16	(6 << 9)
77#define	 AIF1_LRCK_DIV_MASK	(7 << 6)
78#define	  AIF1_LRCK_DIV_16	(0 << 6)
79#define	  AIF1_LRCK_DIV_64	(2 << 6)
80#define	 AIF1_WORD_SIZ_MASK	(3 << 4)
81#define	  AIF1_WORD_SIZ_16	(1 << 4)
82#define	 AIF1_DATA_FMT_MASK	(3 << 2)
83#define	  AIF1_DATA_FMT_I2S	(0 << 2)
84#define	  AIF1_DATA_FMT_LJ	(1 << 2)
85#define	  AIF1_DATA_FMT_RJ	(2 << 2)
86#define	  AIF1_DATA_FMT_DSP	(3 << 2)
87
88#define	AIF1_ADCDAT_CTRL	0x044
89#define		AIF1_ADC0L_ENA		(1 << 15)
90#define		AIF1_ADC0R_ENA		(1 << 14)
91
92#define	AIF1_DACDAT_CTRL	0x048
93#define	 AIF1_DAC0L_ENA		(1 << 15)
94#define	 AIF1_DAC0R_ENA		(1 << 14)
95
96#define	AIF1_MXR_SRC		0x04c
97#define		AIF1L_MXR_SRC_MASK	(0xf << 12)
98#define		AIF1L_MXR_SRC_AIF1	(0x8 << 12)
99#define		AIF1L_MXR_SRC_ADC	(0x2 << 12)
100#define		AIF1R_MXR_SRC_MASK	(0xf << 8)
101#define		AIF1R_MXR_SRC_AIF1	(0x8 << 8)
102#define		AIF1R_MXR_SRC_ADC	(0x2 << 8)
103
104#define	ADC_DIG_CTRL		0x100
105#define	 ADC_DIG_CTRL_ENAD	(1 << 15)
106
107#define	HMIC_CTRL1		0x110
108#define	 HMIC_CTRL1_N_MASK		(0xf << 8)
109#define	 HMIC_CTRL1_N(n)		(((n) & 0xf) << 8)
110#define	 HMIC_CTRL1_JACK_IN_IRQ_EN	(1 << 4)
111#define	 HMIC_CTRL1_JACK_OUT_IRQ_EN	(1 << 3)
112#define	 HMIC_CTRL1_MIC_DET_IRQ_EN	(1 << 0)
113
114#define	HMIC_CTRL2		0x114
115#define	 HMIC_CTRL2_MDATA_THRES	__BITS(12,8)
116
117#define	HMIC_STS		0x118
118#define	 HMIC_STS_MIC_PRESENT	(1 << 6)
119#define	 HMIC_STS_JACK_DET_OIRQ	(1 << 4)
120#define	 HMIC_STS_JACK_DET_IIRQ	(1 << 3)
121#define	 HMIC_STS_MIC_DET_ST	(1 << 0)
122
123#define	DAC_DIG_CTRL		0x120
124#define	 DAC_DIG_CTRL_ENDA	(1 << 15)
125
126#define	DAC_MXR_SRC		0x130
127#define	 DACL_MXR_SRC_MASK		(0xf << 12)
128#define	  DACL_MXR_SRC_AIF1_DAC0L (0x8 << 12)
129#define	 DACR_MXR_SRC_MASK		(0xf << 8)
130#define	  DACR_MXR_SRC_AIF1_DAC0R (0x8 << 8)
131
132static struct ofw_compat_data compat_data[] = {
133	{ "allwinner,sun8i-a33-codec",	1},
134	{ NULL,				0 }
135};
136
137static struct resource_spec sun8i_codec_spec[] = {
138	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
139	{ SYS_RES_IRQ,		0,	RF_ACTIVE | RF_SHAREABLE },
140	{ -1, 0 }
141};
142
143struct sun8i_codec_softc {
144	device_t	dev;
145	struct resource	*res[2];
146	struct mtx	mtx;
147	clk_t		clk_gate;
148	clk_t		clk_mod;
149	void *		intrhand;
150};
151
152#define	CODEC_LOCK(sc)			mtx_lock(&(sc)->mtx)
153#define	CODEC_UNLOCK(sc)		mtx_unlock(&(sc)->mtx)
154#define	CODEC_READ(sc, reg)		bus_read_4((sc)->res[0], (reg))
155#define	CODEC_WRITE(sc, reg, val)	bus_write_4((sc)->res[0], (reg), (val))
156
157static int sun8i_codec_probe(device_t dev);
158static int sun8i_codec_attach(device_t dev);
159static int sun8i_codec_detach(device_t dev);
160
161static int
162sun8i_codec_probe(device_t dev)
163{
164	if (!ofw_bus_status_okay(dev))
165		return (ENXIO);
166
167	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
168		return (ENXIO);
169
170	device_set_desc(dev, "Allwinner Codec");
171	return (BUS_PROBE_DEFAULT);
172}
173
174static int
175sun8i_codec_attach(device_t dev)
176{
177	struct sun8i_codec_softc *sc;
178	int error;
179	uint32_t val;
180	struct gpiobus_pin *pa_pin;
181	phandle_t node;
182
183	sc = device_get_softc(dev);
184	sc->dev = dev;
185	node = ofw_bus_get_node(dev);
186
187	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
188
189	if (bus_alloc_resources(dev, sun8i_codec_spec, sc->res) != 0) {
190		device_printf(dev, "cannot allocate resources for device\n");
191		error = ENXIO;
192		goto fail;
193	}
194
195	error = clk_get_by_ofw_name(dev, 0, "mod", &sc->clk_mod);
196	if (error != 0) {
197		device_printf(dev, "cannot get \"mod\" clock\n");
198		goto fail;
199	}
200
201	error = clk_get_by_ofw_name(dev, 0, "bus", &sc->clk_gate);
202	if (error != 0) {
203		device_printf(dev, "cannot get \"bus\" clock\n");
204		goto fail;
205	}
206
207	error = clk_enable(sc->clk_gate);
208	if (error != 0) {
209		device_printf(dev, "cannot enable \"bus\" clock\n");
210		goto fail;
211	}
212
213	/* Enable clocks */
214	val = CODEC_READ(sc, SYSCLK_CTL);
215	val |= AIF1CLK_ENA;
216	val &= ~AIF1CLK_SRC_MASK;
217	val |= AIF1CLK_SRC_PLL;
218	val |= SYSCLK_ENA;
219	val &= ~SYSCLK_SRC;
220	CODEC_WRITE(sc, SYSCLK_CTL, val);
221	CODEC_WRITE(sc, MOD_CLK_ENA, MOD_AIF1 | MOD_ADC | MOD_DAC);
222	CODEC_WRITE(sc, MOD_RST_CTL, MOD_AIF1 | MOD_ADC | MOD_DAC);
223
224	/* Enable digital parts */
225	CODEC_WRITE(sc, DAC_DIG_CTRL, DAC_DIG_CTRL_ENDA);
226	CODEC_WRITE(sc, ADC_DIG_CTRL, ADC_DIG_CTRL_ENAD);
227
228	/* Set AIF1 to 48 kHz */
229	val = CODEC_READ(sc, SYS_SR_CTRL);
230	val &= ~AIF1_FS_MASK;
231	val |= AIF_FS_48KHZ;
232	CODEC_WRITE(sc, SYS_SR_CTRL, val);
233
234	/* Set AIF1 to 16-bit */
235	val = CODEC_READ(sc, AIF1CLK_CTRL);
236	val &= ~AIF1_WORD_SIZ_MASK;
237	val |= AIF1_WORD_SIZ_16;
238	CODEC_WRITE(sc, AIF1CLK_CTRL, val);
239
240	/* Enable AIF1 DAC timelot 0 */
241	val = CODEC_READ(sc, AIF1_DACDAT_CTRL);
242	val |= AIF1_DAC0L_ENA;
243	val |= AIF1_DAC0R_ENA;
244	CODEC_WRITE(sc, AIF1_DACDAT_CTRL, val);
245
246	/* Enable AIF1 ADC timelot 0 */
247	val = CODEC_READ(sc, AIF1_ADCDAT_CTRL);
248	val |= AIF1_ADC0L_ENA;
249	val |= AIF1_ADC0R_ENA;
250	CODEC_WRITE(sc, AIF1_ADCDAT_CTRL, val);
251
252	/* DAC mixer source select */
253	val = CODEC_READ(sc, DAC_MXR_SRC);
254	val &= ~DACL_MXR_SRC_MASK;
255	val |= DACL_MXR_SRC_AIF1_DAC0L;
256	val &= ~DACR_MXR_SRC_MASK;
257	val |= DACR_MXR_SRC_AIF1_DAC0R;
258	CODEC_WRITE(sc, DAC_MXR_SRC, val);
259
260	/* ADC mixer source select */
261	val = CODEC_READ(sc, AIF1_MXR_SRC);
262	val &= ~AIF1L_MXR_SRC_MASK;
263	val |= AIF1L_MXR_SRC_ADC;
264	val &= ~AIF1R_MXR_SRC_MASK;
265	val |= AIF1R_MXR_SRC_ADC;
266	CODEC_WRITE(sc, AIF1_MXR_SRC, val);
267
268	/* Enable PA power */
269	/* Unmute PA */
270	if (gpio_pin_get_by_ofw_property(dev, node, "allwinner,pa-gpios",
271	    &pa_pin) == 0) {
272		error = gpio_pin_set_active(pa_pin, 1);
273		if (error != 0)
274			device_printf(dev, "failed to unmute PA\n");
275	}
276
277	OF_device_register_xref(OF_xref_from_node(node), dev);
278
279	return (0);
280
281fail:
282	sun8i_codec_detach(dev);
283	return (error);
284}
285
286static int
287sun8i_codec_detach(device_t dev)
288{
289	struct sun8i_codec_softc *sc;
290
291	sc = device_get_softc(dev);
292
293	if (sc->clk_gate)
294		clk_release(sc->clk_gate);
295
296	if (sc->clk_mod)
297		clk_release(sc->clk_mod);
298
299	if (sc->intrhand != NULL)
300		bus_teardown_intr(sc->dev, sc->res[1], sc->intrhand);
301
302	bus_release_resources(dev, sun8i_codec_spec, sc->res);
303	mtx_destroy(&sc->mtx);
304
305	return (0);
306}
307
308static int
309sun8i_codec_dai_init(device_t dev, uint32_t format)
310{
311	struct sun8i_codec_softc *sc;
312	int fmt, pol, clk;
313	uint32_t val;
314
315	sc = device_get_softc(dev);
316
317	fmt = AUDIO_DAI_FORMAT_FORMAT(format);
318	pol = AUDIO_DAI_FORMAT_POLARITY(format);
319	clk = AUDIO_DAI_FORMAT_CLOCK(format);
320
321	val = CODEC_READ(sc, AIF1CLK_CTRL);
322
323	val &= ~AIF1_DATA_FMT_MASK;
324	switch (fmt) {
325	case AUDIO_DAI_FORMAT_I2S:
326		val |= AIF1_DATA_FMT_I2S;
327		break;
328	case AUDIO_DAI_FORMAT_RJ:
329		val |= AIF1_DATA_FMT_RJ;
330		break;
331	case AUDIO_DAI_FORMAT_LJ:
332		val |= AIF1_DATA_FMT_LJ;
333		break;
334	case AUDIO_DAI_FORMAT_DSPA:
335	case AUDIO_DAI_FORMAT_DSPB:
336		val |= AIF1_DATA_FMT_DSP;
337		break;
338	default:
339		return EINVAL;
340	}
341
342	val &= ~(AIF1_BCLK_INV|AIF1_LRCK_INV);
343	/* Codec LRCK polarity is inverted (datasheet is wrong) */
344	if (!AUDIO_DAI_POLARITY_INVERTED_FRAME(pol))
345		val |= AIF1_LRCK_INV;
346	if (AUDIO_DAI_POLARITY_INVERTED_BCLK(pol))
347		val |= AIF1_BCLK_INV;
348
349	switch (clk) {
350	case AUDIO_DAI_CLOCK_CBM_CFM:
351		val &= ~AIF1_MSTR_MOD;	/* codec is master */
352		break;
353	case AUDIO_DAI_CLOCK_CBS_CFS:
354		val |= AIF1_MSTR_MOD;	/* codec is slave */
355		break;
356	default:
357		return EINVAL;
358	}
359
360	val &= ~AIF1_LRCK_DIV_MASK;
361	val |= AIF1_LRCK_DIV_64;
362
363	val &= ~AIF1_BCLK_DIV_MASK;
364	val |= AIF1_BCLK_DIV_16;
365
366	CODEC_WRITE(sc, AIF1CLK_CTRL, val);
367
368	return (0);
369}
370
371static int
372sun8i_codec_dai_trigger(device_t dev, int go, int pcm_dir)
373{
374
375	return (0);
376}
377
378static int
379sun8i_codec_dai_setup_mixer(device_t dev, device_t pcmdev)
380{
381
382	/* Do nothing for now */
383	return (0);
384}
385
386
387static device_method_t sun8i_codec_methods[] = {
388	/* Device interface */
389	DEVMETHOD(device_probe,		sun8i_codec_probe),
390	DEVMETHOD(device_attach,	sun8i_codec_attach),
391	DEVMETHOD(device_detach,	sun8i_codec_detach),
392
393	DEVMETHOD(audio_dai_init,	sun8i_codec_dai_init),
394	DEVMETHOD(audio_dai_setup_mixer,	sun8i_codec_dai_setup_mixer),
395	DEVMETHOD(audio_dai_trigger,	sun8i_codec_dai_trigger),
396
397	DEVMETHOD_END
398};
399
400static driver_t sun8i_codec_driver = {
401	"sun8icodec",
402	sun8i_codec_methods,
403	sizeof(struct sun8i_codec_softc),
404};
405
406DRIVER_MODULE(sun8i_codec, simplebus, sun8i_codec_driver, 0, 0);
407SIMPLEBUS_PNP_INFO(compat_data);
408