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