1 /* $NetBSD: sun8i_h3_codec.c,v 1.5 2021/01/27 03:10:20 thorpej 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: sun8i_h3_codec.c,v 1.5 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 <sys/audioio.h>
40 #include <dev/audio/audio_if.h>
41
42 #include <arm/sunxi/sunxi_codec.h>
43
44 #define H3_PR_CFG 0x00
45 #define H3_AC_PR_RST __BIT(28)
46 #define H3_AC_PR_RW __BIT(24)
47 #define H3_AC_PR_ADDR __BITS(20,16)
48 #define H3_ACDA_PR_WDAT __BITS(15,8)
49 #define H3_ACDA_PR_RDAT __BITS(7,0)
50
51 #define H3_LOMIXSC 0x01
52 #define H3_LOMIXSC_LDAC __BIT(1)
53 #define H3_ROMIXSC 0x02
54 #define H3_ROMIXSC_RDAC __BIT(1)
55 #define H3_DAC_PA_SRC 0x03
56 #define H3_DACAREN __BIT(7)
57 #define H3_DACALEN __BIT(6)
58 #define H3_RMIXEN __BIT(5)
59 #define H3_LMIXEN __BIT(4)
60 #define H3_LINEIN_GCTR 0x05
61 #define H3_LINEING __BITS(6,4)
62 #define H3_MIC_GCTR 0x06
63 #define H3_MIC1_GAIN __BITS(6,4)
64 #define H3_MIC2_GAIN __BITS(2,0)
65 #define H3_PAEN_CTR 0x07
66 #define H3_LINEOUTEN __BIT(7)
67 #define H3_LINEOUT_VOLC 0x09
68 #define H3_LINEOUTVOL __BITS(7,3)
69 #define H3_MIC2G_LINEOUT_CTR 0x0a
70 #define H3_LINEOUT_LSEL __BIT(3)
71 #define H3_LINEOUT_RSEL __BIT(2)
72 #define H3_LADCMIXSC 0x0c
73 #define H3_RADCMIXSC 0x0d
74 #define H3_ADCMIXSC_MIC1 __BIT(6)
75 #define H3_ADCMIXSC_MIC2 __BIT(5)
76 #define H3_ADCMIXSC_LINEIN __BIT(2)
77 #define H3_ADCMIXSC_OMIXER __BITS(1,0)
78 #define H3_ADC_AP_EN 0x0f
79 #define H3_ADCREN __BIT(7)
80 #define H3_ADCLEN __BIT(6)
81 #define H3_ADCG __BITS(2,0)
82
83 struct h3_codec_softc {
84 device_t sc_dev;
85 bus_space_tag_t sc_bst;
86 bus_space_handle_t sc_bsh;
87 int sc_phandle;
88 };
89
90 enum h3_codec_mixer_ctrl {
91 H3_CODEC_OUTPUT_CLASS,
92 H3_CODEC_INPUT_CLASS,
93 H3_CODEC_RECORD_CLASS,
94
95 H3_CODEC_OUTPUT_MASTER_VOLUME,
96 H3_CODEC_INPUT_DAC_VOLUME,
97 H3_CODEC_INPUT_LINEIN_VOLUME,
98 H3_CODEC_INPUT_MIC1_VOLUME,
99 H3_CODEC_INPUT_MIC2_VOLUME,
100 H3_CODEC_RECORD_AGC_VOLUME,
101 H3_CODEC_RECORD_SOURCE,
102
103 H3_CODEC_MIXER_CTRL_LAST
104 };
105
106 static const struct h3_codec_mixer {
107 const char * name;
108 enum h3_codec_mixer_ctrl mixer_class;
109 u_int reg;
110 u_int mask;
111 } h3_codec_mixers[H3_CODEC_MIXER_CTRL_LAST] = {
112 [H3_CODEC_OUTPUT_MASTER_VOLUME] = { AudioNmaster,
113 H3_CODEC_OUTPUT_CLASS, H3_LINEOUT_VOLC, H3_LINEOUTVOL },
114 [H3_CODEC_INPUT_DAC_VOLUME] = { AudioNdac,
115 H3_CODEC_INPUT_CLASS, H3_LINEOUT_VOLC, H3_LINEOUTVOL },
116 [H3_CODEC_INPUT_LINEIN_VOLUME] = { AudioNline,
117 H3_CODEC_INPUT_CLASS, H3_LINEIN_GCTR, H3_LINEING },
118 [H3_CODEC_INPUT_MIC1_VOLUME] = { "mic1",
119 H3_CODEC_INPUT_CLASS, H3_MIC_GCTR, H3_MIC1_GAIN },
120 [H3_CODEC_INPUT_MIC2_VOLUME] = { "mic2",
121 H3_CODEC_INPUT_CLASS, H3_MIC_GCTR, H3_MIC2_GAIN },
122 [H3_CODEC_RECORD_AGC_VOLUME] = { AudioNagc,
123 H3_CODEC_RECORD_CLASS, H3_ADC_AP_EN, H3_ADCG },
124 };
125
126 #define RD4(sc, reg) \
127 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
128 #define WR4(sc, reg, val) \
129 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
130
131 static struct h3_codec_softc *
h3_codec_find(int phandle)132 h3_codec_find(int phandle)
133 {
134 struct h3_codec_softc *csc;
135 device_t dev;
136
137 dev = device_find_by_driver_unit("h3codec", 0);
138 if (dev == NULL)
139 return NULL;
140 csc = device_private(dev);
141 if (csc->sc_phandle != phandle)
142 return NULL;
143
144 return csc;
145 }
146
147 static u_int
h3_codec_pr_read(struct h3_codec_softc * csc,u_int addr)148 h3_codec_pr_read(struct h3_codec_softc *csc, u_int addr)
149 {
150 uint32_t val;
151
152 /* Read current value */
153 val = RD4(csc, H3_PR_CFG);
154
155 /* De-assert reset */
156 val |= H3_AC_PR_RST;
157 WR4(csc, H3_PR_CFG, val);
158
159 /* Read mode */
160 val &= ~H3_AC_PR_RW;
161 WR4(csc, H3_PR_CFG, val);
162
163 /* Set address */
164 val &= ~H3_AC_PR_ADDR;
165 val |= __SHIFTIN(addr, H3_AC_PR_ADDR);
166 WR4(csc, H3_PR_CFG, val);
167
168 /* Read data */
169 return __SHIFTOUT(RD4(csc, H3_PR_CFG), H3_ACDA_PR_RDAT);
170 }
171
172 static void
h3_codec_pr_write(struct h3_codec_softc * csc,u_int addr,u_int data)173 h3_codec_pr_write(struct h3_codec_softc *csc, u_int addr, u_int data)
174 {
175 uint32_t val;
176
177 /* Read current value */
178 val = RD4(csc, H3_PR_CFG);
179
180 /* De-assert reset */
181 val |= H3_AC_PR_RST;
182 WR4(csc, H3_PR_CFG, val);
183
184 /* Set address */
185 val &= ~H3_AC_PR_ADDR;
186 val |= __SHIFTIN(addr, H3_AC_PR_ADDR);
187 WR4(csc, H3_PR_CFG, val);
188
189 /* Write data */
190 val &= ~H3_ACDA_PR_WDAT;
191 val |= __SHIFTIN(data, H3_ACDA_PR_WDAT);
192 WR4(csc, H3_PR_CFG, val);
193
194 /* Write mode */
195 val |= H3_AC_PR_RW;
196 WR4(csc, H3_PR_CFG, val);
197
198 /* Clear write mode */
199 val &= ~H3_AC_PR_RW;
200 WR4(csc, H3_PR_CFG, val);
201 }
202
203 static void
h3_codec_pr_set_clear(struct h3_codec_softc * csc,u_int addr,u_int set,u_int clr)204 h3_codec_pr_set_clear(struct h3_codec_softc *csc, u_int addr, u_int set, u_int clr)
205 {
206 u_int old, new;
207
208 old = h3_codec_pr_read(csc, addr);
209 new = set | (old & ~clr);
210 h3_codec_pr_write(csc, addr, new);
211 }
212
213 static int
h3_codec_init(struct sunxi_codec_softc * sc)214 h3_codec_init(struct sunxi_codec_softc *sc)
215 {
216 struct h3_codec_softc *csc;
217 int phandle;
218
219 /* Lookup the codec analog controls phandle */
220 phandle = fdtbus_get_phandle(sc->sc_phandle,
221 "allwinner,codec-analog-controls");
222 if (phandle < 0) {
223 aprint_error_dev(sc->sc_dev,
224 "missing allwinner,codec-analog-controls property\n");
225 return ENXIO;
226 }
227
228 /* Find a matching h3codec instance */
229 sc->sc_codec_priv = h3_codec_find(phandle);
230 if (sc->sc_codec_priv == NULL) {
231 aprint_error_dev(sc->sc_dev, "couldn't find codec analog controls\n");
232 return ENOENT;
233 }
234 csc = sc->sc_codec_priv;
235
236 /* Right & Left LINEOUT enable */
237 h3_codec_pr_set_clear(csc, H3_PAEN_CTR, H3_LINEOUTEN, 0);
238 h3_codec_pr_set_clear(csc, H3_MIC2G_LINEOUT_CTR,
239 H3_LINEOUT_LSEL | H3_LINEOUT_RSEL, 0);
240
241 return 0;
242 }
243
244 static void
h3_codec_mute(struct sunxi_codec_softc * sc,int mute,u_int mode)245 h3_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
246 {
247 struct h3_codec_softc * const csc = sc->sc_codec_priv;
248
249 if (mode == AUMODE_PLAY) {
250 if (mute) {
251 /* Mute DAC l/r channels to output mixer */
252 h3_codec_pr_set_clear(csc, H3_LOMIXSC,
253 0, H3_LOMIXSC_LDAC);
254 h3_codec_pr_set_clear(csc, H3_ROMIXSC,
255 0, H3_ROMIXSC_RDAC);
256 /* Disable DAC analog l/r channels and output mixer */
257 h3_codec_pr_set_clear(csc, H3_DAC_PA_SRC,
258 0, H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN);
259 } else {
260 /* Enable DAC analog l/r channels and output mixer */
261 h3_codec_pr_set_clear(csc, H3_DAC_PA_SRC,
262 H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN, 0);
263 /* Unmute DAC l/r channels to output mixer */
264 h3_codec_pr_set_clear(csc, H3_LOMIXSC, H3_LOMIXSC_LDAC, 0);
265 h3_codec_pr_set_clear(csc, H3_ROMIXSC, H3_ROMIXSC_RDAC, 0);
266 }
267 } else {
268 if (mute) {
269 /* Disable ADC analog l/r channels */
270 h3_codec_pr_set_clear(csc, H3_ADC_AP_EN,
271 0, H3_ADCREN | H3_ADCLEN);
272 } else {
273 /* Enable ADC analog l/r channels */
274 h3_codec_pr_set_clear(csc, H3_ADC_AP_EN,
275 H3_ADCREN | H3_ADCLEN, 0);
276 }
277 }
278 }
279
280 static int
h3_codec_set_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)281 h3_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
282 {
283 struct h3_codec_softc * const csc = sc->sc_codec_priv;
284 const struct h3_codec_mixer *mix;
285 u_int val, shift;
286 int nvol;
287
288 switch (mc->dev) {
289 case H3_CODEC_OUTPUT_MASTER_VOLUME:
290 case H3_CODEC_INPUT_DAC_VOLUME:
291 case H3_CODEC_INPUT_LINEIN_VOLUME:
292 case H3_CODEC_INPUT_MIC1_VOLUME:
293 case H3_CODEC_INPUT_MIC2_VOLUME:
294 case H3_CODEC_RECORD_AGC_VOLUME:
295 mix = &h3_codec_mixers[mc->dev];
296 val = h3_codec_pr_read(csc, 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 h3_codec_pr_write(csc, mix->reg, val);
302 return 0;
303
304 case H3_CODEC_RECORD_SOURCE:
305 h3_codec_pr_write(csc, H3_LADCMIXSC, mc->un.mask);
306 h3_codec_pr_write(csc, H3_RADCMIXSC, mc->un.mask);
307 return 0;
308 }
309
310 return ENXIO;
311 }
312
313 static int
h3_codec_get_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)314 h3_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
315 {
316 struct h3_codec_softc * const csc = sc->sc_codec_priv;
317 const struct h3_codec_mixer *mix;
318 u_int val, shift;
319 int nvol;
320
321 switch (mc->dev) {
322 case H3_CODEC_OUTPUT_MASTER_VOLUME:
323 case H3_CODEC_INPUT_DAC_VOLUME:
324 case H3_CODEC_INPUT_LINEIN_VOLUME:
325 case H3_CODEC_INPUT_MIC1_VOLUME:
326 case H3_CODEC_INPUT_MIC2_VOLUME:
327 case H3_CODEC_RECORD_AGC_VOLUME:
328 mix = &h3_codec_mixers[mc->dev];
329 val = h3_codec_pr_read(csc, mix->reg);
330 shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
331 nvol = __SHIFTOUT(val, mix->mask) << shift;
332 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
333 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
334 return 0;
335
336 case H3_CODEC_RECORD_SOURCE:
337 mc->un.mask =
338 h3_codec_pr_read(csc, H3_LADCMIXSC) |
339 h3_codec_pr_read(csc, H3_RADCMIXSC);
340 return 0;
341 }
342
343 return ENXIO;
344 }
345
346 static int
h3_codec_query_devinfo(struct sunxi_codec_softc * sc,mixer_devinfo_t * di)347 h3_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
348 {
349 const struct h3_codec_mixer *mix;
350
351 switch (di->index) {
352 case H3_CODEC_OUTPUT_CLASS:
353 di->mixer_class = di->index;
354 strcpy(di->label.name, AudioCoutputs);
355 di->type = AUDIO_MIXER_CLASS;
356 di->next = di->prev = AUDIO_MIXER_LAST;
357 return 0;
358
359 case H3_CODEC_INPUT_CLASS:
360 di->mixer_class = di->index;
361 strcpy(di->label.name, AudioCinputs);
362 di->type = AUDIO_MIXER_CLASS;
363 di->next = di->prev = AUDIO_MIXER_LAST;
364 return 0;
365
366 case H3_CODEC_RECORD_CLASS:
367 di->mixer_class = di->index;
368 strcpy(di->label.name, AudioCrecord);
369 di->type = AUDIO_MIXER_CLASS;
370 di->next = di->prev = AUDIO_MIXER_LAST;
371 return 0;
372
373 case H3_CODEC_OUTPUT_MASTER_VOLUME:
374 case H3_CODEC_INPUT_DAC_VOLUME:
375 case H3_CODEC_INPUT_LINEIN_VOLUME:
376 case H3_CODEC_INPUT_MIC1_VOLUME:
377 case H3_CODEC_INPUT_MIC2_VOLUME:
378 case H3_CODEC_RECORD_AGC_VOLUME:
379 mix = &h3_codec_mixers[di->index];
380 di->mixer_class = mix->mixer_class;
381 strcpy(di->label.name, mix->name);
382 di->un.v.delta =
383 256 / (__SHIFTOUT_MASK(mix->mask) + 1);
384 di->type = AUDIO_MIXER_VALUE;
385 di->next = di->prev = AUDIO_MIXER_LAST;
386 di->un.v.num_channels = 2;
387 strcpy(di->un.v.units.name, AudioNvolume);
388 return 0;
389
390 case H3_CODEC_RECORD_SOURCE:
391 di->mixer_class = H3_CODEC_RECORD_CLASS;
392 strcpy(di->label.name, AudioNsource);
393 di->type = AUDIO_MIXER_SET;
394 di->next = di->prev = AUDIO_MIXER_LAST;
395 di->un.s.num_mem = 4;
396 strcpy(di->un.s.member[0].label.name, AudioNline);
397 di->un.s.member[0].mask = H3_ADCMIXSC_LINEIN;
398 strcpy(di->un.s.member[1].label.name, "mic1");
399 di->un.s.member[1].mask = H3_ADCMIXSC_MIC1;
400 strcpy(di->un.s.member[2].label.name, "mic2");
401 di->un.s.member[2].mask = H3_ADCMIXSC_MIC2;
402 strcpy(di->un.s.member[3].label.name, AudioNdac);
403 di->un.s.member[3].mask = H3_ADCMIXSC_OMIXER;
404 return 0;
405
406 }
407
408 return ENXIO;
409 }
410
411 const struct sunxi_codec_conf sun8i_h3_codecconf = {
412 .name = "H3 Audio Codec",
413
414 .init = h3_codec_init,
415 .mute = h3_codec_mute,
416 .set_port = h3_codec_set_port,
417 .get_port = h3_codec_get_port,
418 .query_devinfo = h3_codec_query_devinfo,
419
420 .DPC = 0x00,
421 .DAC_FIFOC = 0x04,
422 .DAC_FIFOS = 0x08,
423 .DAC_TXDATA = 0x20,
424 .ADC_FIFOC = 0x10,
425 .ADC_FIFOS = 0x14,
426 .ADC_RXDATA = 0x18,
427 .DAC_CNT = 0x40,
428 .ADC_CNT = 0x44,
429 };
430
431 /*
432 * Device glue, only here to claim resources on behalf of the sunxi_codec driver.
433 */
434
435 static const struct device_compatible_entry compat_data[] = {
436 { .compat = "allwinner,sun8i-h3-codec-analog" },
437 DEVICE_COMPAT_EOL
438 };
439
440 static int
h3_codec_match(device_t parent,cfdata_t cf,void * aux)441 h3_codec_match(device_t parent, cfdata_t cf, void *aux)
442 {
443 struct fdt_attach_args * const faa = aux;
444
445 return of_compatible_match(faa->faa_phandle, compat_data);
446 }
447
448 static void
h3_codec_attach(device_t parent,device_t self,void * aux)449 h3_codec_attach(device_t parent, device_t self, void *aux)
450 {
451 struct h3_codec_softc * const sc = device_private(self);
452 struct fdt_attach_args * const faa = aux;
453 const int phandle = faa->faa_phandle;
454 bus_addr_t addr;
455 bus_size_t size;
456
457 sc->sc_dev = self;
458 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
459 aprint_error(": couldn't get registers\n");
460 return;
461 }
462 sc->sc_bst = faa->faa_bst;
463 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
464 aprint_error(": couldn't map registers\n");
465 return;
466 }
467
468 sc->sc_phandle = phandle;
469
470 aprint_naive("\n");
471 aprint_normal(": H3 Audio Codec (analog part)\n");
472 }
473
474 CFATTACH_DECL_NEW(h3_codec, sizeof(struct h3_codec_softc),
475 h3_codec_match, h3_codec_attach, NULL, NULL);
476