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