xref: /netbsd-src/sys/arch/arm/sunxi/sun8i_v3s_codec.c (revision 6c4affb9229be854a846947409d7c06ace8271d4)
1 /* $NetBSD: sun8i_v3s_codec.c,v 1.1 2021/05/05 10:24:04 jmcneill Exp $ */
2 
3 /*-
4  * Copyright (c) 2021 Rui-Xiang Guo
5  * Copyright (c) 2014-2017 Jared McNeill <jmcneill@invisible.ca>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: sun8i_v3s_codec.c,v 1.1 2021/05/05 10:24:04 jmcneill Exp $");
32 
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/cpu.h>
36 #include <sys/device.h>
37 #include <sys/kmem.h>
38 #include <sys/bitops.h>
39 
40 #include <sys/audioio.h>
41 #include <dev/audio/audio_if.h>
42 
43 #include <arm/sunxi/sunxi_codec.h>
44 
45 #define	V3S_PR_CFG		0x00
46 #define	 V3S_PR_RST		__BIT(28)
47 #define	 V3S_PR_RW		__BIT(24)
48 #define	 V3S_PR_ADDR		__BITS(20,16)
49 #define	 V3S_ADDA_PR_WDAT	__BITS(15,8)
50 #define	 V3S_ADDA_PR_RDAT	__BITS(7,0)
51 
52 #define	V3S_PAG_HPV		0x00
53 #define	 V3S_HPVOL		__BITS(5,0)
54 
55 #define	V3S_LMIXMUTE		0x01
56 #define	 V3S_LMIXMUTE_LDAC	__BIT(1)
57 #define	V3S_RMIXMUTE		0x02
58 #define	 V3S_RMIXMUTE_RDAC	__BIT(1)
59 #define	V3S_DAC_PA_SRC		0x03
60 #define	 V3S_DACAREN		__BIT(7)
61 #define	 V3S_DACALEN		__BIT(6)
62 #define	 V3S_RMIXEN		__BIT(5)
63 #define	 V3S_LMIXEN		__BIT(4)
64 #define	 V3S_RHPPAMUTE		__BIT(3)
65 #define	 V3S_LHPPAMUTE		__BIT(2)
66 #define	V3S_MIC_GCTR		0x06
67 #define	 V3S_MIC_GAIN		__BITS(6,4)
68 #define	V3S_HP_CTRL		0x07
69 #define	 V3S_HPPAEN		__BIT(7)
70 #define	V3S_LADCMIXMUTE		0x0c
71 #define	V3S_RADCMIXMUTE		0x0d
72 #define	 V3S_ADCMIXMUTE_MIC	__BIT(6)
73 #define	 V3S_ADCMIXMUTE_MIXER	__BITS(1,0)
74 #define	V3S_ADC_CTRL		0x0f
75 #define	 V3S_ADCREN		__BIT(7)
76 #define	 V3S_ADCLEN		__BIT(6)
77 #define	 V3S_ADCG		__BITS(2,0)
78 
79 struct v3s_codec_softc {
80 	device_t		sc_dev;
81 	bus_space_tag_t		sc_bst;
82 	bus_space_handle_t	sc_bsh;
83 	int			sc_phandle;
84 };
85 
86 enum v3s_codec_mixer_ctrl {
87 	V3S_CODEC_OUTPUT_CLASS,
88 	V3S_CODEC_INPUT_CLASS,
89 	V3S_CODEC_RECORD_CLASS,
90 
91 	V3S_CODEC_OUTPUT_MASTER_VOLUME,
92 	V3S_CODEC_INPUT_MIC_VOLUME,
93 	V3S_CODEC_RECORD_AGC_VOLUME,
94 	V3S_CODEC_RECORD_SOURCE,
95 
96 	V3S_CODEC_MIXER_CTRL_LAST
97 };
98 
99 static const struct v3s_codec_mixer {
100 	const char *			name;
101 	enum v3s_codec_mixer_ctrl	mixer_class;
102 	u_int				reg;
103 	u_int				mask;
104 } v3s_codec_mixers[V3S_CODEC_MIXER_CTRL_LAST] = {
105 	[V3S_CODEC_OUTPUT_MASTER_VOLUME]	= { AudioNmaster,
106 	    V3S_CODEC_OUTPUT_CLASS, V3S_PAG_HPV, V3S_HPVOL },
107 	[V3S_CODEC_INPUT_MIC_VOLUME]	= { "mic",
108 	    V3S_CODEC_INPUT_CLASS, V3S_MIC_GCTR, V3S_MIC_GAIN },
109 	[V3S_CODEC_RECORD_AGC_VOLUME]	= { AudioNagc,
110 	    V3S_CODEC_RECORD_CLASS, V3S_ADC_CTRL, V3S_ADCG },
111 };
112 
113 #define	RD4(sc, reg)			\
114 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
115 #define	WR4(sc, reg, val)		\
116 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
117 
118 static struct v3s_codec_softc *
v3s_codec_find(int phandle)119 v3s_codec_find(int phandle)
120 {
121 	struct v3s_codec_softc *csc;
122 	device_t dev;
123 
124 	dev = device_find_by_driver_unit("v3scodec", 0);
125 	if (dev == NULL)
126 		return NULL;
127 	csc = device_private(dev);
128 	if (csc->sc_phandle != phandle)
129 		return NULL;
130 
131 	return csc;
132 }
133 
134 static u_int
v3s_codec_pr_read(struct v3s_codec_softc * csc,u_int addr)135 v3s_codec_pr_read(struct v3s_codec_softc *csc, u_int addr)
136 {
137 	uint32_t val;
138 
139 	/* Read current value */
140 	val = RD4(csc, V3S_PR_CFG);
141 
142 	/* De-assert reset */
143 	val |= V3S_PR_RST;
144 	WR4(csc, V3S_PR_CFG, val);
145 
146 	/* Read mode */
147 	val &= ~V3S_PR_RW;
148 	WR4(csc, V3S_PR_CFG, val);
149 
150 	/* Set address */
151 	val &= ~V3S_PR_ADDR;
152 	val |= __SHIFTIN(addr, V3S_PR_ADDR);
153 	WR4(csc, V3S_PR_CFG, val);
154 
155 	/* Read data */
156 	return __SHIFTOUT(RD4(csc, V3S_PR_CFG), V3S_ADDA_PR_RDAT);
157 }
158 
159 static void
v3s_codec_pr_write(struct v3s_codec_softc * csc,u_int addr,u_int data)160 v3s_codec_pr_write(struct v3s_codec_softc *csc, u_int addr, u_int data)
161 {
162 	uint32_t val;
163 
164 	/* Read current value */
165 	val = RD4(csc, V3S_PR_CFG);
166 
167 	/* De-assert reset */
168 	val |= V3S_PR_RST;
169 	WR4(csc, V3S_PR_CFG, val);
170 
171 	/* Set address */
172 	val &= ~V3S_PR_ADDR;
173 	val |= __SHIFTIN(addr, V3S_PR_ADDR);
174 	WR4(csc, V3S_PR_CFG, val);
175 
176 	/* Write data */
177 	val &= ~V3S_ADDA_PR_WDAT;
178 	val |= __SHIFTIN(data, V3S_ADDA_PR_WDAT);
179 	WR4(csc, V3S_PR_CFG, val);
180 
181 	/* Write mode */
182 	val |= V3S_PR_RW;
183 	WR4(csc, V3S_PR_CFG, val);
184 
185 	/* Clear write mode */
186 	val &= ~V3S_PR_RW;
187 	WR4(csc, V3S_PR_CFG, val);
188 }
189 
190 static void
v3s_codec_pr_set_clear(struct v3s_codec_softc * csc,u_int addr,u_int set,u_int clr)191 v3s_codec_pr_set_clear(struct v3s_codec_softc *csc, u_int addr, u_int set, u_int clr)
192 {
193 	u_int old, new;
194 
195 	old = v3s_codec_pr_read(csc, addr);
196 	new = set | (old & ~clr);
197 	v3s_codec_pr_write(csc, addr, new);
198 }
199 
200 static int
v3s_codec_init(struct sunxi_codec_softc * sc)201 v3s_codec_init(struct sunxi_codec_softc *sc)
202 {
203 	struct v3s_codec_softc *csc;
204 	int phandle;
205 
206 	/* Lookup the codec analog controls phandle */
207 	phandle = fdtbus_get_phandle(sc->sc_phandle,
208 	    "allwinner,codec-analog-controls");
209 	if (phandle < 0) {
210 		aprint_error_dev(sc->sc_dev,
211 		    "missing allwinner,codec-analog-controls property\n");
212 		return ENXIO;
213 	}
214 
215 	/* Find a matching v3scodec instance */
216 	sc->sc_codec_priv = v3s_codec_find(phandle);
217 	if (sc->sc_codec_priv == NULL) {
218 		aprint_error_dev(sc->sc_dev, "couldn't find codec analog controls\n");
219 		return ENOENT;
220 	}
221 	csc = sc->sc_codec_priv;
222 
223 	/* Right & Left Headphone enable */
224 	v3s_codec_pr_set_clear(csc, V3S_HP_CTRL, V3S_HPPAEN, 0);
225 
226 	return 0;
227 }
228 
229 static void
v3s_codec_mute(struct sunxi_codec_softc * sc,int mute,u_int mode)230 v3s_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
231 {
232 	struct v3s_codec_softc * const csc = sc->sc_codec_priv;
233 
234 	if (mode == AUMODE_PLAY) {
235 		if (mute) {
236 			/* Mute DAC l/r channels to output mixer */
237 			v3s_codec_pr_set_clear(csc, V3S_LMIXMUTE,
238 			    0, V3S_LMIXMUTE_LDAC);
239 			v3s_codec_pr_set_clear(csc, V3S_RMIXMUTE,
240 			    0, V3S_RMIXMUTE_RDAC);
241 			/* Disable DAC analog l/r channels and output mixer */
242 			v3s_codec_pr_set_clear(csc, V3S_DAC_PA_SRC,
243 			    0, V3S_DACAREN | V3S_DACALEN | V3S_RMIXEN | V3S_LMIXEN | V3S_RHPPAMUTE | V3S_LHPPAMUTE);
244 		} else {
245 			/* Enable DAC analog l/r channels and output mixer */
246 			v3s_codec_pr_set_clear(csc, V3S_DAC_PA_SRC,
247 			    V3S_DACAREN | V3S_DACALEN | V3S_RMIXEN | V3S_LMIXEN | V3S_RHPPAMUTE | V3S_LHPPAMUTE, 0);
248 			/* Unmute DAC l/r channels to output mixer */
249 			v3s_codec_pr_set_clear(csc, V3S_LMIXMUTE, V3S_LMIXMUTE_LDAC, 0);
250 			v3s_codec_pr_set_clear(csc, V3S_RMIXMUTE, V3S_RMIXMUTE_RDAC, 0);
251 		}
252 	} else {
253 		if (mute) {
254 			/* Disable ADC analog l/r channels */
255 			v3s_codec_pr_set_clear(csc, V3S_ADC_CTRL,
256 			    0, V3S_ADCREN | V3S_ADCLEN);
257 		} else {
258 			/* Enable ADC analog l/r channels */
259 			v3s_codec_pr_set_clear(csc, V3S_ADC_CTRL,
260 			    V3S_ADCREN | V3S_ADCLEN, 0);
261 		}
262 	}
263 }
264 
265 static int
v3s_codec_set_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)266 v3s_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
267 {
268 	struct v3s_codec_softc * const csc = sc->sc_codec_priv;
269 	const struct v3s_codec_mixer *mix;
270 	u_int val, shift;
271 	int nvol;
272 
273 	switch (mc->dev) {
274 	case V3S_CODEC_OUTPUT_MASTER_VOLUME:
275 	case V3S_CODEC_INPUT_MIC_VOLUME:
276 	case V3S_CODEC_RECORD_AGC_VOLUME:
277 		mix = &v3s_codec_mixers[mc->dev];
278 		val = v3s_codec_pr_read(csc, mix->reg);
279 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
280 		nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
281 		val &= ~mix->mask;
282 		val |= __SHIFTIN(nvol, mix->mask);
283 		v3s_codec_pr_write(csc, mix->reg, val);
284 		return 0;
285 
286 	case V3S_CODEC_RECORD_SOURCE:
287 		v3s_codec_pr_write(csc, V3S_LADCMIXMUTE, mc->un.mask);
288 		v3s_codec_pr_write(csc, V3S_RADCMIXMUTE, mc->un.mask);
289 		return 0;
290 	}
291 
292 	return ENXIO;
293 }
294 
295 static int
v3s_codec_get_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)296 v3s_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
297 {
298 	struct v3s_codec_softc * const csc = sc->sc_codec_priv;
299 	const struct v3s_codec_mixer *mix;
300 	u_int val, shift;
301 	int nvol;
302 
303 	switch (mc->dev) {
304 	case V3S_CODEC_OUTPUT_MASTER_VOLUME:
305 	case V3S_CODEC_INPUT_MIC_VOLUME:
306 	case V3S_CODEC_RECORD_AGC_VOLUME:
307 		mix = &v3s_codec_mixers[mc->dev];
308 		val = v3s_codec_pr_read(csc, mix->reg);
309 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
310 		nvol = __SHIFTOUT(val, mix->mask) << shift;
311 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
312 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
313 		return 0;
314 
315 	case V3S_CODEC_RECORD_SOURCE:
316 		mc->un.mask =
317 		    v3s_codec_pr_read(csc, V3S_LADCMIXMUTE) |
318 		    v3s_codec_pr_read(csc, V3S_RADCMIXMUTE);
319 		return 0;
320 	}
321 
322 	return ENXIO;
323 }
324 
325 static int
v3s_codec_query_devinfo(struct sunxi_codec_softc * sc,mixer_devinfo_t * di)326 v3s_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
327 {
328 	const struct v3s_codec_mixer *mix;
329 
330 	switch (di->index) {
331 	case V3S_CODEC_OUTPUT_CLASS:
332 		di->mixer_class = di->index;
333 		strcpy(di->label.name, AudioCoutputs);
334 		di->type = AUDIO_MIXER_CLASS;
335 		di->next = di->prev = AUDIO_MIXER_LAST;
336 		return 0;
337 
338 	case V3S_CODEC_INPUT_CLASS:
339 		di->mixer_class = di->index;
340 		strcpy(di->label.name, AudioCinputs);
341 		di->type = AUDIO_MIXER_CLASS;
342 		di->next = di->prev = AUDIO_MIXER_LAST;
343 		return 0;
344 
345 	case V3S_CODEC_RECORD_CLASS:
346 		di->mixer_class = di->index;
347 		strcpy(di->label.name, AudioCrecord);
348 		di->type = AUDIO_MIXER_CLASS;
349 		di->next = di->prev = AUDIO_MIXER_LAST;
350 		return 0;
351 
352 	case V3S_CODEC_OUTPUT_MASTER_VOLUME:
353 	case V3S_CODEC_INPUT_MIC_VOLUME:
354 	case V3S_CODEC_RECORD_AGC_VOLUME:
355 		mix = &v3s_codec_mixers[di->index];
356 		di->mixer_class = mix->mixer_class;
357 		strcpy(di->label.name, mix->name);
358 		di->un.v.delta =
359 		    256 / (__SHIFTOUT_MASK(mix->mask) + 1);
360 		di->type = AUDIO_MIXER_VALUE;
361 		di->next = di->prev = AUDIO_MIXER_LAST;
362 		di->un.v.num_channels = 2;
363 		strcpy(di->un.v.units.name, AudioNvolume);
364 		return 0;
365 
366 	case V3S_CODEC_RECORD_SOURCE:
367 		di->mixer_class = V3S_CODEC_RECORD_CLASS;
368 		strcpy(di->label.name, AudioNsource);
369 		di->type = AUDIO_MIXER_SET;
370 		di->next = di->prev = AUDIO_MIXER_LAST;
371 		di->un.s.num_mem = 2;
372 		strcpy(di->un.s.member[0].label.name, "mic");
373 		di->un.s.member[1].mask = V3S_ADCMIXMUTE_MIC;
374 		strcpy(di->un.s.member[1].label.name, AudioNdac);
375 		di->un.s.member[3].mask = V3S_ADCMIXMUTE_MIXER;
376 		return 0;
377 
378 	}
379 
380 	return ENXIO;
381 }
382 
383 const struct sunxi_codec_conf sun8i_v3s_codecconf = {
384 	.name = "V3s Audio Codec",
385 
386 	.init = v3s_codec_init,
387 	.mute = v3s_codec_mute,
388 	.set_port = v3s_codec_set_port,
389 	.get_port = v3s_codec_get_port,
390 	.query_devinfo = v3s_codec_query_devinfo,
391 
392 	.DPC		= 0x00,
393 	.DAC_FIFOC	= 0x04,
394 	.DAC_FIFOS	= 0x08,
395 	.DAC_TXDATA	= 0x20,
396 	.ADC_FIFOC	= 0x10,
397 	.ADC_FIFOS	= 0x14,
398 	.ADC_RXDATA	= 0x18,
399 	.DAC_CNT	= 0x40,
400 	.ADC_CNT	= 0x44,
401 };
402 
403 /*
404  * Device glue, only here to claim resources on behalf of the sunxi_codec driver.
405  */
406 
407 static const struct device_compatible_entry compat_data[] = {
408 	{ .compat = "allwinner,sun8i-v3s-codec-analog" },
409 	DEVICE_COMPAT_EOL
410 };
411 
412 static int
v3s_codec_match(device_t parent,cfdata_t cf,void * aux)413 v3s_codec_match(device_t parent, cfdata_t cf, void *aux)
414 {
415 	struct fdt_attach_args * const faa = aux;
416 
417 	return of_compatible_match(faa->faa_phandle, compat_data);
418 }
419 
420 static void
v3s_codec_attach(device_t parent,device_t self,void * aux)421 v3s_codec_attach(device_t parent, device_t self, void *aux)
422 {
423 	struct v3s_codec_softc * const sc = device_private(self);
424 	struct fdt_attach_args * const faa = aux;
425 	const int phandle = faa->faa_phandle;
426 	bus_addr_t addr;
427 	bus_size_t size;
428 
429 	sc->sc_dev = self;
430 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
431 		aprint_error(": couldn't get registers\n");
432 		return;
433 	}
434 	sc->sc_bst = faa->faa_bst;
435 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
436 		aprint_error(": couldn't map registers\n");
437 		return;
438 	}
439 
440 	sc->sc_phandle = phandle;
441 
442 	aprint_naive("\n");
443 	aprint_normal(": V3s Audio Codec (analog part)\n");
444 }
445 
446 CFATTACH_DECL_NEW(v3s_codec, sizeof(struct v3s_codec_softc),
447     v3s_codec_match, v3s_codec_attach, NULL, NULL);
448