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
98struct 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
108enum 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
132static 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
158static u_int
159a64_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
183static void
184a64_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
214static void
215a64_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
224static int
225a64_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
238static int
239a64_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
251static int
252a64_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
264static int
265a64_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
276static int
277a64_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
365static int
366a64_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
426static int
427a64_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
551static 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
561static audio_dai_tag_t
562a64_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
572static struct fdtbus_dai_controller_func a64_acodec_dai_funcs = {
573	.get_tag = a64_acodec_dai_get_tag
574};
575
576static int
577a64_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
609static const struct device_compatible_entry compat_data[] = {
610	{ .compat = "allwinner,sun50i-a64-codec-analog" },
611	DEVICE_COMPAT_EOL
612};
613
614static int
615a64_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
622static void
623a64_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
669CFATTACH_DECL_NEW(a64_acodec, sizeof(struct a64_acodec_softc),
670    a64_acodec_match, a64_acodec_attach, NULL, NULL);
671