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