xref: /netbsd/sys/arch/arm/sunxi/sun50i_a64_acodec.c (revision 8e90f9ed)
1 /* $NetBSD: sun50i_a64_acodec.c,v 1.10 2021/01/27 03:10:20 thorpej Exp $ */
2 
3 /*-
4  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
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/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: sun50i_a64_acodec.c,v 1.10 2021/01/27 03:10:20 thorpej Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/cpu.h>
35 #include <sys/device.h>
36 #include <sys/kmem.h>
37 #include <sys/bitops.h>
38 
39 #include <dev/audio/audio_dai.h>
40 
41 #include <dev/fdt/fdtvar.h>
42 
43 #define	A64_PR_CFG		0x00
44 #define	 A64_AC_PR_RST		__BIT(28)
45 #define	 A64_AC_PR_RW		__BIT(24)
46 #define	 A64_AC_PR_ADDR		__BITS(20,16)
47 #define	 A64_ACDA_PR_WDAT	__BITS(15,8)
48 #define	 A64_ACDA_PR_RDAT	__BITS(7,0)
49 
50 #define	A64_HP_CTRL		0x00
51 #define	 A64_HPPA_EN		__BIT(6)
52 #define	 A64_HPVOL		__BITS(5,0)
53 #define	A64_OL_MIX_CTRL		0x01
54 #define	 A64_LMIXMUTE_LDAC	__BIT(1)
55 #define	A64_OR_MIX_CTRL		0x02
56 #define	 A64_RMIXMUTE_RDAC	__BIT(1)
57 #define	A64_LINEOUT_CTRL0	0x05
58 #define	 A64_LINEOUT_LEFT_EN	__BIT(7)
59 #define	 A64_LINEOUT_RIGHT_EN	__BIT(6)
60 #define	 A64_LINEOUT_EN		(A64_LINEOUT_LEFT_EN|A64_LINEOUT_RIGHT_EN)
61 #define	A64_LINEOUT_CTRL1	0x06
62 #define	 A64_LINEOUT_VOL	__BITS(4,0)
63 #define	A64_MIC1_CTRL		0x07
64 #define	 A64_MIC1G		__BITS(6,4)
65 #define	 A64_MIC1AMPEN		__BIT(3)
66 #define	 A64_MIC1BOOST		__BITS(2,0)
67 #define	A64_MIC2_CTRL		0x08
68 #define	 A64_MIC2_SEL		__BIT(7)
69 #define	 A64_MIC2G		__BITS(6,4)
70 #define	 A64_MIC2AMPEN		__BIT(3)
71 #define	 A64_MIC2BOOST		__BITS(2,0)
72 #define	A64_LINEIN_CTRL		0x09
73 #define	 A64_LINEING		__BITS(6,4)
74 #define	A64_MIX_DAC_CTRL	0x0a
75 #define	 A64_DACAREN		__BIT(7)
76 #define	 A64_DACALEN		__BIT(6)
77 #define	 A64_RMIXEN		__BIT(5)
78 #define	 A64_LMIXEN		__BIT(4)
79 #define	 A64_RHPPAMUTE		__BIT(3)
80 #define	 A64_LHPPAMUTE		__BIT(2)
81 #define	 A64_RHPIS		__BIT(1)
82 #define	 A64_LHPIS		__BIT(0)
83 #define	A64_L_ADCMIX_SRC	0x0b
84 #define	A64_R_ADCMIX_SRC	0x0c
85 #define	 A64_ADCMIX_SRC_MIC1	__BIT(6)
86 #define	 A64_ADCMIX_SRC_MIC2	__BIT(5)
87 #define	 A64_ADCMIX_SRC_LINEIN	__BIT(2)
88 #define	 A64_ADCMIX_SRC_OMIXER	__BIT(1)
89 #define	A64_ADC_CTRL		0x0d
90 #define	 A64_ADCREN		__BIT(7)
91 #define	 A64_ADCLEN		__BIT(6)
92 #define	 A64_ADCG		__BITS(2,0)
93 #define	A64_JACK_MIC_CTRL	0x1d
94 #define	 A64_JACKDETEN		__BIT(7)
95 #define	 A64_INNERRESEN		__BIT(6)
96 #define	 A64_AUTOPLEN		__BIT(1)
97 
98 struct a64_acodec_softc {
99 	device_t		sc_dev;
100 	bus_space_tag_t		sc_bst;
101 	bus_space_handle_t	sc_bsh;
102 	int			sc_phandle;
103 
104 	struct audio_dai_device	sc_dai;
105 	int			sc_master_dev;
106 };
107 
108 enum a64_acodec_mixer_ctrl {
109 	A64_CODEC_OUTPUT_CLASS,
110 	A64_CODEC_INPUT_CLASS,
111 	A64_CODEC_RECORD_CLASS,
112 
113 	A64_CODEC_OUTPUT_MASTER_VOLUME,
114 	A64_CODEC_OUTPUT_MUTE,
115 	A64_CODEC_OUTPUT_SOURCE,
116 	A64_CODEC_INPUT_LINE_VOLUME,
117 	A64_CODEC_INPUT_HP_VOLUME,
118 	A64_CODEC_RECORD_LINE_VOLUME,
119 	A64_CODEC_RECORD_MIC1_VOLUME,
120 	A64_CODEC_RECORD_MIC1_PREAMP,
121 	A64_CODEC_RECORD_MIC2_VOLUME,
122 	A64_CODEC_RECORD_MIC2_PREAMP,
123 	A64_CODEC_RECORD_AGC_VOLUME,
124 	A64_CODEC_RECORD_SOURCE,
125 
126 	A64_CODEC_MIXER_CTRL_LAST
127 };
128 
129 #define	A64_OUTPUT_SOURCE_LINE	__BIT(0)
130 #define	A64_OUTPUT_SOURCE_HP	__BIT(1)
131 
132 static const struct a64_acodec_mixer {
133 	const char *			name;
134 	enum a64_acodec_mixer_ctrl	mixer_class;
135 	u_int				reg;
136 	u_int				mask;
137 } a64_acodec_mixers[A64_CODEC_MIXER_CTRL_LAST] = {
138 	[A64_CODEC_INPUT_LINE_VOLUME]	= { AudioNline,
139 	    A64_CODEC_INPUT_CLASS, A64_LINEOUT_CTRL1, A64_LINEOUT_VOL },
140 	[A64_CODEC_INPUT_HP_VOLUME]	= { AudioNheadphone,
141 	    A64_CODEC_INPUT_CLASS, A64_HP_CTRL, A64_HPVOL },
142 
143 	[A64_CODEC_RECORD_LINE_VOLUME]	= { AudioNline,
144 	    A64_CODEC_RECORD_CLASS, A64_LINEIN_CTRL, A64_LINEING },
145 	[A64_CODEC_RECORD_MIC1_VOLUME]	= { AudioNmicrophone,
146 	    A64_CODEC_RECORD_CLASS, A64_MIC1_CTRL, A64_MIC1G },
147 	[A64_CODEC_RECORD_MIC2_VOLUME]	= { AudioNmicrophone "2",
148 	    A64_CODEC_RECORD_CLASS, A64_MIC2_CTRL, A64_MIC2G },
149 	[A64_CODEC_RECORD_AGC_VOLUME]	= { AudioNagc,
150 	    A64_CODEC_RECORD_CLASS, A64_ADC_CTRL, A64_ADCG },
151 };
152 
153 #define	RD4(sc, reg)			\
154 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
155 #define	WR4(sc, reg, val)		\
156 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
157 
158 static u_int
a64_acodec_pr_read(struct a64_acodec_softc * sc,u_int addr)159 a64_acodec_pr_read(struct a64_acodec_softc *sc, u_int addr)
160 {
161 	uint32_t val;
162 
163 	/* Read current value */
164 	val = RD4(sc, A64_PR_CFG);
165 
166 	/* De-assert reset */
167 	val |= A64_AC_PR_RST;
168 	WR4(sc, A64_PR_CFG, val);
169 
170 	/* Read mode */
171 	val &= ~A64_AC_PR_RW;
172 	WR4(sc, A64_PR_CFG, val);
173 
174 	/* Set address */
175 	val &= ~A64_AC_PR_ADDR;
176 	val |= __SHIFTIN(addr, A64_AC_PR_ADDR);
177 	WR4(sc, A64_PR_CFG, val);
178 
179 	/* Read data */
180 	return __SHIFTOUT(RD4(sc, A64_PR_CFG), A64_ACDA_PR_RDAT);
181 }
182 
183 static void
a64_acodec_pr_write(struct a64_acodec_softc * sc,u_int addr,u_int data)184 a64_acodec_pr_write(struct a64_acodec_softc *sc, u_int addr, u_int data)
185 {
186 	uint32_t val;
187 
188 	/* Read current value */
189 	val = RD4(sc, A64_PR_CFG);
190 
191 	/* De-assert reset */
192 	val |= A64_AC_PR_RST;
193 	WR4(sc, A64_PR_CFG, val);
194 
195 	/* Set address */
196 	val &= ~A64_AC_PR_ADDR;
197 	val |= __SHIFTIN(addr, A64_AC_PR_ADDR);
198 	WR4(sc, A64_PR_CFG, val);
199 
200 	/* Write data */
201 	val &= ~A64_ACDA_PR_WDAT;
202 	val |= __SHIFTIN(data, A64_ACDA_PR_WDAT);
203 	WR4(sc, A64_PR_CFG, val);
204 
205 	/* Write mode */
206 	val |= A64_AC_PR_RW;
207 	WR4(sc, A64_PR_CFG, val);
208 
209 	/* Clear write mode */
210 	val &= ~A64_AC_PR_RW;
211 	WR4(sc, A64_PR_CFG, val);
212 }
213 
214 static void
a64_acodec_pr_set_clear(struct a64_acodec_softc * sc,u_int addr,u_int set,u_int clr)215 a64_acodec_pr_set_clear(struct a64_acodec_softc *sc, u_int addr, u_int set, u_int clr)
216 {
217 	u_int old, new;
218 
219 	old = a64_acodec_pr_read(sc, addr);
220 	new = set | (old & ~clr);
221 	a64_acodec_pr_write(sc, addr, new);
222 }
223 
224 static int
a64_acodec_trigger_output(void * priv,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * params)225 a64_acodec_trigger_output(void *priv, void *start, void *end, int blksize,
226     void (*intr)(void *), void *intrarg, const audio_params_t *params)
227 {
228 	struct a64_acodec_softc * const sc = priv;
229 
230 	/* Enable DAC analog l/r channels, HP PA, and output mixer */
231 	a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL,
232 	    A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN |
233 	    A64_RHPPAMUTE | A64_LHPPAMUTE, 0);
234 
235 	return 0;
236 }
237 
238 static int
a64_acodec_trigger_input(void * priv,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const audio_params_t * params)239 a64_acodec_trigger_input(void *priv, void *start, void *end, int blksize,
240     void (*intr)(void *), void *intrarg, const audio_params_t *params)
241 {
242 	struct a64_acodec_softc * const sc = priv;
243 
244 	/* Enable ADC analog l/r channels */
245 	a64_acodec_pr_set_clear(sc, A64_ADC_CTRL,
246 	    A64_ADCREN | A64_ADCLEN, 0);
247 
248 	return 0;
249 }
250 
251 static int
a64_acodec_halt_output(void * priv)252 a64_acodec_halt_output(void *priv)
253 {
254 	struct a64_acodec_softc * const sc = priv;
255 
256 	/* Disable DAC analog l/r channels, HP PA, and output mixer */
257 	a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL,
258 	    0, A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN |
259 	    A64_RHPPAMUTE | A64_LHPPAMUTE);
260 
261 	return 0;
262 }
263 
264 static int
a64_acodec_halt_input(void * priv)265 a64_acodec_halt_input(void *priv)
266 {
267 	struct a64_acodec_softc * const sc = priv;
268 
269 	/* Disable ADC analog l/r channels */
270 	a64_acodec_pr_set_clear(sc, A64_ADC_CTRL,
271 	    0, A64_ADCREN | A64_ADCLEN);
272 
273 	return 0;
274 }
275 
276 static int
a64_acodec_set_port(void * priv,mixer_ctrl_t * mc)277 a64_acodec_set_port(void *priv, mixer_ctrl_t *mc)
278 {
279 	struct a64_acodec_softc * const sc = priv;
280 	const struct a64_acodec_mixer *mix;
281 	u_int val, shift;
282 	int nvol, dev;
283 
284 	dev = mc->dev;
285 	if (dev == A64_CODEC_OUTPUT_MASTER_VOLUME)
286 		dev = sc->sc_master_dev;
287 
288 	switch (dev) {
289 	case A64_CODEC_INPUT_LINE_VOLUME:
290 	case A64_CODEC_INPUT_HP_VOLUME:
291 	case A64_CODEC_RECORD_LINE_VOLUME:
292 	case A64_CODEC_RECORD_MIC1_VOLUME:
293 	case A64_CODEC_RECORD_MIC2_VOLUME:
294 	case A64_CODEC_RECORD_AGC_VOLUME:
295 		mix = &a64_acodec_mixers[dev];
296 		val = a64_acodec_pr_read(sc, mix->reg);
297 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
298 		nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
299 		val &= ~mix->mask;
300 		val |= __SHIFTIN(nvol, mix->mask);
301 		a64_acodec_pr_write(sc, mix->reg, val);
302 		return 0;
303 
304 	case A64_CODEC_RECORD_MIC1_PREAMP:
305 		if (mc->un.ord < 0 || mc->un.ord > 1)
306 			return EINVAL;
307 		if (mc->un.ord) {
308 			a64_acodec_pr_set_clear(sc, A64_MIC1_CTRL, A64_MIC1AMPEN, 0);
309 		} else {
310 			a64_acodec_pr_set_clear(sc, A64_MIC1_CTRL, 0, A64_MIC1AMPEN);
311 		}
312 		return 0;
313 
314 	case A64_CODEC_RECORD_MIC2_PREAMP:
315 		if (mc->un.ord < 0 || mc->un.ord > 1)
316 			return EINVAL;
317 		if (mc->un.ord) {
318 			a64_acodec_pr_set_clear(sc, A64_MIC2_CTRL, A64_MIC2AMPEN, 0);
319 		} else {
320 			a64_acodec_pr_set_clear(sc, A64_MIC2_CTRL, 0, A64_MIC2AMPEN);
321 		}
322 		return 0;
323 
324 	case A64_CODEC_OUTPUT_MUTE:
325 		if (mc->un.ord < 0 || mc->un.ord > 1)
326 			return EINVAL;
327 		if (mc->un.ord) {
328 			a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL,
329 			    0, A64_LMIXMUTE_LDAC);
330 			a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL,
331 			    0, A64_RMIXMUTE_RDAC);
332 		} else {
333 			a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL,
334 			    A64_LMIXMUTE_LDAC, 0);
335 			a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL,
336 			    A64_RMIXMUTE_RDAC, 0);
337 		}
338 		return 0;
339 
340 	case A64_CODEC_OUTPUT_SOURCE:
341 		if (mc->un.mask & A64_OUTPUT_SOURCE_LINE)
342 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
343 			    A64_LINEOUT_EN, 0);
344 		else
345 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
346 			    0, A64_LINEOUT_EN);
347 
348 		if (mc->un.mask & A64_OUTPUT_SOURCE_HP)
349 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
350 			    A64_HPPA_EN, 0);
351 		else
352 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
353 			    0, A64_HPPA_EN);
354 		return 0;
355 
356 	case A64_CODEC_RECORD_SOURCE:
357 		a64_acodec_pr_write(sc, A64_L_ADCMIX_SRC, mc->un.mask);
358 		a64_acodec_pr_write(sc, A64_R_ADCMIX_SRC, mc->un.mask);
359 		return 0;
360 	}
361 
362 	return ENXIO;
363 }
364 
365 static int
a64_acodec_get_port(void * priv,mixer_ctrl_t * mc)366 a64_acodec_get_port(void *priv, mixer_ctrl_t *mc)
367 {
368 	struct a64_acodec_softc * const sc = priv;
369 	const struct a64_acodec_mixer *mix;
370 	u_int val, shift;
371 	int nvol, dev;
372 
373 	dev = mc->dev;
374 	if (dev == A64_CODEC_OUTPUT_MASTER_VOLUME)
375 		dev = sc->sc_master_dev;
376 
377 	switch (dev) {
378 	case A64_CODEC_INPUT_LINE_VOLUME:
379 	case A64_CODEC_INPUT_HP_VOLUME:
380 	case A64_CODEC_RECORD_LINE_VOLUME:
381 	case A64_CODEC_RECORD_MIC1_VOLUME:
382 	case A64_CODEC_RECORD_MIC2_VOLUME:
383 	case A64_CODEC_RECORD_AGC_VOLUME:
384 		mix = &a64_acodec_mixers[dev];
385 		val = a64_acodec_pr_read(sc, mix->reg);
386 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
387 		nvol = __SHIFTOUT(val, mix->mask) << shift;
388 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
389 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
390 		return 0;
391 
392 	case A64_CODEC_RECORD_MIC1_PREAMP:
393 		mc->un.ord = !!(a64_acodec_pr_read(sc, A64_MIC1_CTRL) & A64_MIC1AMPEN);
394 		return 0;
395 
396 	case A64_CODEC_RECORD_MIC2_PREAMP:
397 		mc->un.ord = !!(a64_acodec_pr_read(sc, A64_MIC2_CTRL) & A64_MIC2AMPEN);
398 		return 0;
399 
400 	case A64_CODEC_OUTPUT_MUTE:
401 		mc->un.ord = 1;
402 		if (a64_acodec_pr_read(sc, A64_OL_MIX_CTRL) & A64_LMIXMUTE_LDAC)
403 			mc->un.ord = 0;
404 		if (a64_acodec_pr_read(sc, A64_OR_MIX_CTRL) & A64_RMIXMUTE_RDAC)
405 			mc->un.ord = 0;
406 		return 0;
407 
408 	case A64_CODEC_OUTPUT_SOURCE:
409 		mc->un.mask = 0;
410 		if (a64_acodec_pr_read(sc, A64_LINEOUT_CTRL0) & A64_LINEOUT_EN)
411 			mc->un.mask |= A64_OUTPUT_SOURCE_LINE;
412 		if (a64_acodec_pr_read(sc, A64_HP_CTRL) & A64_HPPA_EN)
413 			mc->un.mask |= A64_OUTPUT_SOURCE_HP;
414 		return 0;
415 
416 	case A64_CODEC_RECORD_SOURCE:
417 		mc->un.mask =
418 		    a64_acodec_pr_read(sc, A64_L_ADCMIX_SRC) |
419 		    a64_acodec_pr_read(sc, A64_R_ADCMIX_SRC);
420 		return 0;
421 	}
422 
423 	return ENXIO;
424 }
425 
426 static int
a64_acodec_query_devinfo(void * priv,mixer_devinfo_t * di)427 a64_acodec_query_devinfo(void *priv, mixer_devinfo_t *di)
428 {
429 	struct a64_acodec_softc * const sc = priv;
430 	const struct a64_acodec_mixer *mix;
431 
432 	switch (di->index) {
433 	case A64_CODEC_OUTPUT_CLASS:
434 		di->mixer_class = di->index;
435 		strcpy(di->label.name, AudioCoutputs);
436 		di->type = AUDIO_MIXER_CLASS;
437 		di->next = di->prev = AUDIO_MIXER_LAST;
438 		return 0;
439 
440 	case A64_CODEC_INPUT_CLASS:
441 		di->mixer_class = di->index;
442 		strcpy(di->label.name, AudioCinputs);
443 		di->type = AUDIO_MIXER_CLASS;
444 		di->next = di->prev = AUDIO_MIXER_LAST;
445 		return 0;
446 
447 	case A64_CODEC_RECORD_CLASS:
448 		di->mixer_class = di->index;
449 		strcpy(di->label.name, AudioCrecord);
450 		di->type = AUDIO_MIXER_CLASS;
451 		di->next = di->prev = AUDIO_MIXER_LAST;
452 		return 0;
453 
454 	case A64_CODEC_OUTPUT_MASTER_VOLUME:
455 		mix = &a64_acodec_mixers[sc->sc_master_dev];
456 		di->mixer_class = A64_CODEC_OUTPUT_CLASS;
457 		strcpy(di->label.name, AudioNmaster);
458 		di->un.v.delta =
459 		    256 / (__SHIFTOUT_MASK(mix->mask) + 1);
460 		di->type = AUDIO_MIXER_VALUE;
461 		di->next = di->prev = AUDIO_MIXER_LAST;
462 		di->un.v.num_channels = 2;
463 		strcpy(di->un.v.units.name, AudioNvolume);
464 		return 0;
465 
466 	case A64_CODEC_INPUT_LINE_VOLUME:
467 	case A64_CODEC_INPUT_HP_VOLUME:
468 	case A64_CODEC_RECORD_LINE_VOLUME:
469 	case A64_CODEC_RECORD_MIC1_VOLUME:
470 	case A64_CODEC_RECORD_MIC2_VOLUME:
471 	case A64_CODEC_RECORD_AGC_VOLUME:
472 		mix = &a64_acodec_mixers[di->index];
473 		di->mixer_class = mix->mixer_class;
474 		strcpy(di->label.name, mix->name);
475 		di->un.v.delta =
476 		    256 / (__SHIFTOUT_MASK(mix->mask) + 1);
477 		di->type = AUDIO_MIXER_VALUE;
478 		di->prev = AUDIO_MIXER_LAST;
479 		if (di->index == A64_CODEC_RECORD_MIC1_VOLUME)
480 			di->next = A64_CODEC_RECORD_MIC1_PREAMP;
481 		else if (di->index == A64_CODEC_RECORD_MIC2_VOLUME)
482 			di->next = A64_CODEC_RECORD_MIC2_PREAMP;
483 		else
484 			di->next = AUDIO_MIXER_LAST;
485 		di->un.v.num_channels = 2;
486 		strcpy(di->un.v.units.name, AudioNvolume);
487 		return 0;
488 
489 	case A64_CODEC_RECORD_MIC1_PREAMP:
490 	case A64_CODEC_RECORD_MIC2_PREAMP:
491 		di->mixer_class = A64_CODEC_RECORD_CLASS;
492 		strcpy(di->label.name, AudioNpreamp);
493 		di->type = AUDIO_MIXER_ENUM;
494 		if (di->index == A64_CODEC_RECORD_MIC1_PREAMP)
495 			di->prev = A64_CODEC_RECORD_MIC1_VOLUME;
496 		else
497 			di->prev = A64_CODEC_RECORD_MIC2_VOLUME;
498 		di->next = AUDIO_MIXER_LAST;
499 		di->un.e.num_mem = 2;
500 		strcpy(di->un.e.member[0].label.name, AudioNoff);
501 		di->un.e.member[0].ord = 0;
502 		strcpy(di->un.e.member[1].label.name, AudioNon);
503 		di->un.e.member[1].ord = 1;
504 		return 0;
505 
506 	case A64_CODEC_OUTPUT_MUTE:
507 		di->mixer_class = A64_CODEC_OUTPUT_CLASS;
508 		strcpy(di->label.name, AudioNmute);
509 		di->type = AUDIO_MIXER_ENUM;
510 		di->next = di->prev = AUDIO_MIXER_LAST;
511 		di->un.e.num_mem = 2;
512 		strcpy(di->un.e.member[0].label.name, AudioNoff);
513 		di->un.e.member[0].ord = 0;
514 		strcpy(di->un.e.member[1].label.name, AudioNon);
515 		di->un.e.member[1].ord = 1;
516 		return 0;
517 
518 	case A64_CODEC_OUTPUT_SOURCE:
519 		di->mixer_class = A64_CODEC_OUTPUT_CLASS;
520 		strcpy(di->label.name, AudioNsource);
521 		di->type = AUDIO_MIXER_SET;
522 		di->next = di->prev = AUDIO_MIXER_LAST;
523 		di->un.s.num_mem = 2;
524 		strcpy(di->un.s.member[0].label.name, AudioNline);
525 		di->un.s.member[0].mask = A64_OUTPUT_SOURCE_LINE;
526 		strcpy(di->un.s.member[1].label.name, AudioNheadphone);
527 		di->un.s.member[1].mask = A64_OUTPUT_SOURCE_HP;
528 		return 0;
529 
530 	case A64_CODEC_RECORD_SOURCE:
531 		di->mixer_class = A64_CODEC_RECORD_CLASS;
532 		strcpy(di->label.name, AudioNsource);
533 		di->type = AUDIO_MIXER_SET;
534 		di->next = di->prev = AUDIO_MIXER_LAST;
535 		di->un.s.num_mem = 4;
536 		strcpy(di->un.s.member[0].label.name, AudioNline);
537 		di->un.s.member[0].mask = A64_ADCMIX_SRC_LINEIN;
538 		strcpy(di->un.s.member[1].label.name, AudioNmicrophone);
539 		di->un.s.member[1].mask = A64_ADCMIX_SRC_MIC1;
540 		strcpy(di->un.s.member[2].label.name, AudioNmicrophone "2");
541 		di->un.s.member[2].mask = A64_ADCMIX_SRC_MIC2;
542 		strcpy(di->un.s.member[3].label.name, AudioNdac);
543 		di->un.s.member[3].mask = A64_ADCMIX_SRC_OMIXER;
544 		return 0;
545 
546 	}
547 
548 	return ENXIO;
549 }
550 
551 static const struct audio_hw_if a64_acodec_hw_if = {
552 	.trigger_output = a64_acodec_trigger_output,
553 	.trigger_input = a64_acodec_trigger_input,
554 	.halt_output = a64_acodec_halt_output,
555 	.halt_input = a64_acodec_halt_input,
556 	.set_port = a64_acodec_set_port,
557 	.get_port = a64_acodec_get_port,
558 	.query_devinfo = a64_acodec_query_devinfo,
559 };
560 
561 static audio_dai_tag_t
a64_acodec_dai_get_tag(device_t dev,const void * data,size_t len)562 a64_acodec_dai_get_tag(device_t dev, const void *data, size_t len)
563 {
564 	struct a64_acodec_softc * const sc = device_private(dev);
565 
566 	if (len != 4)
567 		return NULL;
568 
569 	return &sc->sc_dai;
570 }
571 
572 static struct fdtbus_dai_controller_func a64_acodec_dai_funcs = {
573 	.get_tag = a64_acodec_dai_get_tag
574 };
575 
576 static int
a64_acodec_dai_jack_detect(audio_dai_tag_t dai,u_int jack,int present)577 a64_acodec_dai_jack_detect(audio_dai_tag_t dai, u_int jack, int present)
578 {
579 	struct a64_acodec_softc * const sc = audio_dai_private(dai);
580 
581 	switch (jack) {
582 	case AUDIO_DAI_JACK_HP:
583 		if (present) {
584 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
585 			    0, A64_LINEOUT_EN);
586 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
587 			    A64_HPPA_EN, 0);
588 		} else {
589 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
590 			    A64_LINEOUT_EN, 0);
591 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
592 			    0, A64_HPPA_EN);
593 		}
594 
595 		/* Master volume controls either HP or line out */
596 		sc->sc_master_dev = present ?
597 		    A64_CODEC_INPUT_HP_VOLUME : A64_CODEC_INPUT_LINE_VOLUME;
598 
599 		break;
600 
601 	case AUDIO_DAI_JACK_MIC:
602 		/* XXX TODO */
603 		break;
604 	}
605 
606 	return 0;
607 }
608 
609 static const struct device_compatible_entry compat_data[] = {
610 	{ .compat = "allwinner,sun50i-a64-codec-analog" },
611 	DEVICE_COMPAT_EOL
612 };
613 
614 static int
a64_acodec_match(device_t parent,cfdata_t cf,void * aux)615 a64_acodec_match(device_t parent, cfdata_t cf, void *aux)
616 {
617 	struct fdt_attach_args * const faa = aux;
618 
619 	return of_compatible_match(faa->faa_phandle, compat_data);
620 }
621 
622 static void
a64_acodec_attach(device_t parent,device_t self,void * aux)623 a64_acodec_attach(device_t parent, device_t self, void *aux)
624 {
625 	struct a64_acodec_softc * const sc = device_private(self);
626 	struct fdt_attach_args * const faa = aux;
627 	const int phandle = faa->faa_phandle;
628 	bus_addr_t addr;
629 	bus_size_t size;
630 
631 	sc->sc_dev = self;
632 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
633 		aprint_error(": couldn't get registers\n");
634 		return;
635 	}
636 	sc->sc_bst = faa->faa_bst;
637 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
638 		aprint_error(": couldn't map registers\n");
639 		return;
640 	}
641 
642 	sc->sc_phandle = phandle;
643 
644 	aprint_naive("\n");
645 	aprint_normal(": A64 Audio Codec (analog part)\n");
646 
647 	/* Right & Left Headphone PA enable */
648 	a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
649 	    A64_HPPA_EN, 0);
650 
651 	/* Jack detect enable */
652 	sc->sc_master_dev = A64_CODEC_INPUT_HP_VOLUME;
653 	a64_acodec_pr_set_clear(sc, A64_JACK_MIC_CTRL,
654 	    A64_JACKDETEN | A64_INNERRESEN | A64_AUTOPLEN, 0);
655 
656 	/* Unmute DAC to output mixer */
657 	a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL,
658 	    A64_LMIXMUTE_LDAC, 0);
659 	a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL,
660 	    A64_RMIXMUTE_RDAC, 0);
661 
662 	sc->sc_dai.dai_jack_detect = a64_acodec_dai_jack_detect;
663 	sc->sc_dai.dai_hw_if = &a64_acodec_hw_if;
664 	sc->sc_dai.dai_dev = self;
665 	sc->sc_dai.dai_priv = sc;
666 	fdtbus_register_dai_controller(self, phandle, &a64_acodec_dai_funcs);
667 }
668 
669 CFATTACH_DECL_NEW(a64_acodec, sizeof(struct a64_acodec_softc),
670     a64_acodec_match, a64_acodec_attach, NULL, NULL);
671