xref: /netbsd-src/sys/arch/arm/sunxi/sun6i_a31_codec.c (revision e622eac459adf11c2e710d7a4de0f05510bbbe61)
1 /* $NetBSD: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki Exp $ */
2 
3 /*-
4  * Copyright (c) 2014-2017 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: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki 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 <sys/audioio.h>
40 #include <dev/audio/audio_if.h>
41 
42 #include <arm/sunxi/sunxi_codec.h>
43 
44 #define	A31_DEFAULT_HPVOL	0x20
45 
46 #define	OMIXER_DACA_CTRL	0x20
47 #define	 DACAREN		__BIT(31)
48 #define	 DACALEN		__BIT(30)
49 #define	 RMIXEN			__BIT(29)
50 #define	 LMIXEN			__BIT(28)
51 #define	 RMIXMUTE		__BITS(23,17)
52 #define	 RMIXMUTE_MIC1		__BIT(23)
53 #define	 RMIXMUTE_MIC2		__BIT(22)
54 #define	 RMIXMUTE_PHONEP_PHONEN	__BIT(21)
55 #define	 RMIXMUTE_PHONEP	__BIT(20)
56 #define	 RMIXMUTE_LINEINR	__BIT(19)
57 #define	 RMIXMUTE_DACR		__BIT(18)
58 #define	 RMIXMUTE_DACL		__BIT(17)
59 #define	 LMIXMUTE		__BITS(16,10)
60 #define	 LMIXMUTE_MIC1		__BIT(16)
61 #define	 LMIXMUTE_MIC2		__BIT(15)
62 #define	 LMIXMUTE_PHONEP_PHONEN	__BIT(14)
63 #define	 LMIXMUTE_PHONEN	__BIT(13)
64 #define	 LMIXMUTE_LINEINL	__BIT(12)
65 #define	 LMIXMUTE_DACL		__BIT(11)
66 #define	 LMIXMUTE_DACR		__BIT(10)
67 #define	 RHPIS			__BIT(9)
68 #define	 LHPIS			__BIT(8)
69 #define	 RHPPAMUTE		__BIT(7)
70 #define	 LHPPAMUTE		__BIT(6)
71 #define	 HPVOL			__BITS(5,0)
72 
73 #define	OMIXER_PA_CTRL		0x24
74 #define	 HPPAEN			__BIT(31)
75 #define	 HPCOM_CTL		__BITS(30,29)
76 #define	 COMPTEN		__BIT(28)
77 #define	 PA_ANTI_POP_CTRL	__BITS(27,26)
78 #define	 MIC1G			__BITS(17,15)
79 #define	 MIC2G			__BITS(14,12)
80 #define	 LINEING		__BITS(11,9)
81 #define	 PHONEG			__BITS(8,6)
82 #define	 PHONEPG		__BITS(5,3)
83 #define	 PHONENG		__BITS(2,0)
84 
85 #define	AC_MIC_CTRL		0x28
86 #define	 HBIASEN		__BIT(31)
87 #define	 MBIASEN		__BIT(30)
88 #define	 HBIASADCEN		__BIT(29)
89 #define	 MIC1AMPEN		__BIT(28)
90 #define	 MIC1BOOST		__BITS(27,25)
91 #define	 MIC2AMPEN		__BIT(24)
92 #define	 MIC2BOOST		__BITS(23,21)
93 #define	 MIC2SLT		__BIT(20)
94 #define	 LINEOUTLEN		__BIT(19)
95 #define	 LINEOUTREN		__BIT(18)
96 #define	 LINEOUTLSRC		__BIT(17)
97 #define	 LINEOUTRSRC		__BIT(16)
98 #define	 LINEOUTVC		__BITS(15,11)
99 #define	 PHONEPREG		__BITS(10,8)
100 #define	 PHONEOUTG		__BITS(7,5)
101 #define	 PHONEOUTEN		__BIT(4)
102 #define	 PHONEOUTS0		__BIT(3)
103 #define	 PHONEOUTS1		__BIT(2)
104 #define	 PHONEOUTS2		__BIT(1)
105 #define	 PHONEOUTS3		__BIT(0)
106 
107 #define	AC_ADCA_CTRL		0x2c
108 #define	 ADCREN			__BIT(31)
109 #define	 ADCLEN			__BIT(30)
110 #define	 ADCRG			__BITS(29,27)
111 #define	 ADCLG			__BITS(26,24)
112 #define	 RADCMIXMUTE		__BITS(13,7)
113 #define	 RADCMIXMUTE_MIC1	__BIT(13)
114 #define	 RADCMIXMUTE_MIC2	__BIT(12)
115 #define	 RADCMIXMUTE_PHONEP_PHONEN __BIT(11)
116 #define	 RADCMIXMUTE_PHONEP	__BIT(10)
117 #define	 RADCMIXMUTE_LINEINR	__BIT(9)
118 #define	 RADCMIXMUTE_ROM	__BIT(8)
119 #define	 RADCMIXMUTE_LOM	__BIT(7)
120 #define	 LADCMIXMUTE		__BITS(6,0)
121 #define	 LADCMIXMUTE_MIC1	__BIT(6)
122 #define	 LADCMIXMUTE_MIC2	__BIT(5)
123 #define	 LADCMIXMUTE_PHONEP_PHONEN __BIT(4)
124 #define	 LADCMIXMUTE_PHONEN	__BIT(3)
125 #define	 LADCMIXMUTE_LINEINL	__BIT(2)
126 #define	 LADCMIXMUTE_LOM	__BIT(1)
127 #define	 LADCMIXMUTE_ROM	__BIT(0)
128 
129 enum a31_codec_mixer_ctrl {
130 	A31_CODEC_OUTPUT_CLASS,
131 	A31_CODEC_INPUT_CLASS,
132 
133 	A31_CODEC_OUTPUT_MASTER_VOLUME,
134 	A31_CODEC_INPUT_DAC_VOLUME,
135 
136 	A31_CODEC_MIXER_CTRL_LAST
137 };
138 
139 static const struct a31_codec_mixer {
140 	const char *			name;
141 	enum a31_codec_mixer_ctrl	mixer_class;
142 	u_int				reg;
143 	u_int				mask;
144 } a31_codec_mixers[A31_CODEC_MIXER_CTRL_LAST] = {
145 	[A31_CODEC_OUTPUT_MASTER_VOLUME]	= { AudioNmaster,
146 	    A31_CODEC_OUTPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
147 	[A31_CODEC_INPUT_DAC_VOLUME]		= { AudioNdac,
148 	    A31_CODEC_INPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
149 };
150 
151 #define	RD4(sc, reg)			\
152 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
153 #define	WR4(sc, reg, val)		\
154 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
155 #define	SET4(sc, reg, mask)		\
156 	WR4((sc), (reg), RD4((sc), (reg)) | (mask))
157 #define	CLR4(sc, reg, mask)		\
158 	WR4((sc), (reg), RD4((sc), (reg)) & ~(mask))
159 
160 static int
a31_codec_init(struct sunxi_codec_softc * sc)161 a31_codec_init(struct sunxi_codec_softc *sc)
162 {
163 
164 	/* Disable HPCOM and HPCOM output protection */
165 	CLR4(sc, OMIXER_PA_CTRL, HPCOM_CTL | COMPTEN);
166 	/* Enable headphone power amp */
167 	SET4(sc, OMIXER_PA_CTRL, HPPAEN);
168 
169 	/* Set headphone PA input to DAC */
170 	CLR4(sc, OMIXER_DACA_CTRL, RHPIS | LHPIS);
171 	/* Mute inputs to headphone PA */
172 	CLR4(sc, OMIXER_DACA_CTRL, RHPPAMUTE | LHPPAMUTE);
173 	/* Set initial volume */
174 	CLR4(sc, OMIXER_DACA_CTRL, HPVOL);
175 	SET4(sc, OMIXER_DACA_CTRL, __SHIFTIN(A31_DEFAULT_HPVOL, HPVOL));
176 
177 	/* Disable lineout */
178 	CLR4(sc, AC_MIC_CTRL, LINEOUTLEN | LINEOUTREN | LINEOUTVC);
179 	/* Enable headset microphone bias, current sensor, and ADC */
180 	SET4(sc, AC_MIC_CTRL, HBIASEN | HBIASADCEN);
181 
182 	return 0;
183 }
184 
185 static void
a31_codec_mute(struct sunxi_codec_softc * sc,int mute,u_int mode)186 a31_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
187 {
188 	if (mode == AUMODE_PLAY) {
189 		if (sc->sc_pin_pa != NULL)
190 			fdtbus_gpio_write(sc->sc_pin_pa, !mute);
191 
192 		if (mute) {
193 			CLR4(sc, OMIXER_DACA_CTRL, DACAREN | DACALEN);
194 		} else {
195 			CLR4(sc, OMIXER_DACA_CTRL, RMIXMUTE | LMIXMUTE);
196 			SET4(sc, OMIXER_DACA_CTRL,
197 			    LHPIS | RHPIS | LHPPAMUTE | RHPPAMUTE |
198 			    DACAREN | DACALEN | RMIXEN | LMIXEN |
199 			    RMIXMUTE_DACR | LMIXMUTE_DACL);
200 		}
201 	} else {
202 		if (mute) {
203 			CLR4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
204 		} else {
205 			SET4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
206 		}
207 	}
208 }
209 
210 static int
a31_codec_set_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)211 a31_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
212 {
213 	const struct a31_codec_mixer *mix;
214 	u_int val, shift;
215 	int nvol;
216 
217 	switch (mc->dev) {
218 	case A31_CODEC_OUTPUT_MASTER_VOLUME:
219 	case A31_CODEC_INPUT_DAC_VOLUME:
220 		mix = &a31_codec_mixers[mc->dev];
221 		val = RD4(sc, mix->reg);
222 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
223 		nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
224 		val &= ~mix->mask;
225 		val |= __SHIFTIN(nvol, mix->mask);
226 		WR4(sc, mix->reg, val);
227 		return 0;
228 	}
229 
230 	return ENXIO;
231 }
232 
233 static int
a31_codec_get_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)234 a31_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
235 {
236 	const struct a31_codec_mixer *mix;
237 	u_int val, shift;
238 	int nvol;
239 
240 	switch (mc->dev) {
241 	case A31_CODEC_OUTPUT_MASTER_VOLUME:
242 	case A31_CODEC_INPUT_DAC_VOLUME:
243 		mix = &a31_codec_mixers[mc->dev];
244 		val = RD4(sc, mix->reg);
245 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
246 		nvol = __SHIFTOUT(val, mix->mask) << shift;
247 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
248 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
249 		return 0;
250 	}
251 
252 	return ENXIO;
253 }
254 
255 static int
a31_codec_query_devinfo(struct sunxi_codec_softc * sc,mixer_devinfo_t * di)256 a31_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
257 {
258 	const struct a31_codec_mixer *mix;
259 
260 	switch (di->index) {
261 	case A31_CODEC_OUTPUT_CLASS:
262 		di->mixer_class = di->index;
263 		strcpy(di->label.name, AudioCoutputs);
264 		di->type = AUDIO_MIXER_CLASS;
265 		di->next = di->prev = AUDIO_MIXER_LAST;
266 		return 0;
267 
268 	case A31_CODEC_INPUT_CLASS:
269 		di->mixer_class = di->index;
270 		strcpy(di->label.name, AudioCinputs);
271 		di->type = AUDIO_MIXER_CLASS;
272 		di->next = di->prev = AUDIO_MIXER_LAST;
273 		return 0;
274 
275 	case A31_CODEC_OUTPUT_MASTER_VOLUME:
276 	case A31_CODEC_INPUT_DAC_VOLUME:
277 		mix = &a31_codec_mixers[di->index];
278 		di->mixer_class = mix->mixer_class;
279 		strcpy(di->label.name, mix->name);
280 		di->un.v.delta =
281 		    256 / (__SHIFTOUT_MASK(mix->mask) + 1);
282 		di->type = AUDIO_MIXER_VALUE;
283 		di->next = di->prev = AUDIO_MIXER_LAST;
284 		di->un.v.num_channels = 2;
285 		strcpy(di->un.v.units.name, AudioNvolume);
286 		return 0;
287 	}
288 
289 	return ENXIO;
290 }
291 
292 const struct sunxi_codec_conf sun6i_a31_codecconf = {
293 	.name = "A31 Audio Codec",
294 
295 	.init = a31_codec_init,
296 	.mute = a31_codec_mute,
297 	.set_port = a31_codec_set_port,
298 	.get_port = a31_codec_get_port,
299 	.query_devinfo = a31_codec_query_devinfo,
300 
301 	.DPC		= 0x00,
302 	.DAC_FIFOC	= 0x04,
303 	.DAC_FIFOS	= 0x08,
304 	.DAC_TXDATA	= 0x0c,
305 	.ADC_FIFOC	= 0x10,
306 	.ADC_FIFOS	= 0x14,
307 	.ADC_RXDATA	= 0x18,
308 	.DAC_CNT	= 0x40,
309 	.ADC_CNT	= 0x44,
310 };
311