1*e622eac4Sisaki /* $NetBSD: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki Exp $ */
2d3e86955Sjmcneill
3d3e86955Sjmcneill /*-
4d3e86955Sjmcneill * Copyright (c) 2014-2017 Jared McNeill <jmcneill@invisible.ca>
5d3e86955Sjmcneill * All rights reserved.
6d3e86955Sjmcneill *
7d3e86955Sjmcneill * Redistribution and use in source and binary forms, with or without
8d3e86955Sjmcneill * modification, are permitted provided that the following conditions
9d3e86955Sjmcneill * are met:
10d3e86955Sjmcneill * 1. Redistributions of source code must retain the above copyright
11d3e86955Sjmcneill * notice, this list of conditions and the following disclaimer.
12d3e86955Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13d3e86955Sjmcneill * notice, this list of conditions and the following disclaimer in the
14d3e86955Sjmcneill * documentation and/or other materials provided with the distribution.
15d3e86955Sjmcneill *
16d3e86955Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17d3e86955Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18d3e86955Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19d3e86955Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20d3e86955Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21d3e86955Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22d3e86955Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23d3e86955Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24d3e86955Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d3e86955Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d3e86955Sjmcneill * SUCH DAMAGE.
27d3e86955Sjmcneill */
28d3e86955Sjmcneill
29d3e86955Sjmcneill #include <sys/cdefs.h>
30*e622eac4Sisaki __KERNEL_RCSID(0, "$NetBSD: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki Exp $");
31d3e86955Sjmcneill
32d3e86955Sjmcneill #include <sys/param.h>
33d3e86955Sjmcneill #include <sys/bus.h>
34d3e86955Sjmcneill #include <sys/cpu.h>
35d3e86955Sjmcneill #include <sys/device.h>
36d3e86955Sjmcneill #include <sys/kmem.h>
37d3e86955Sjmcneill #include <sys/bitops.h>
38d3e86955Sjmcneill
39d3e86955Sjmcneill #include <sys/audioio.h>
40*e622eac4Sisaki #include <dev/audio/audio_if.h>
41d3e86955Sjmcneill
42d3e86955Sjmcneill #include <arm/sunxi/sunxi_codec.h>
43d3e86955Sjmcneill
44d3e86955Sjmcneill #define A31_DEFAULT_HPVOL 0x20
45d3e86955Sjmcneill
46d3e86955Sjmcneill #define OMIXER_DACA_CTRL 0x20
47d3e86955Sjmcneill #define DACAREN __BIT(31)
48d3e86955Sjmcneill #define DACALEN __BIT(30)
49d3e86955Sjmcneill #define RMIXEN __BIT(29)
50d3e86955Sjmcneill #define LMIXEN __BIT(28)
51d3e86955Sjmcneill #define RMIXMUTE __BITS(23,17)
52d3e86955Sjmcneill #define RMIXMUTE_MIC1 __BIT(23)
53d3e86955Sjmcneill #define RMIXMUTE_MIC2 __BIT(22)
54d3e86955Sjmcneill #define RMIXMUTE_PHONEP_PHONEN __BIT(21)
55d3e86955Sjmcneill #define RMIXMUTE_PHONEP __BIT(20)
56d3e86955Sjmcneill #define RMIXMUTE_LINEINR __BIT(19)
57d3e86955Sjmcneill #define RMIXMUTE_DACR __BIT(18)
58d3e86955Sjmcneill #define RMIXMUTE_DACL __BIT(17)
59d3e86955Sjmcneill #define LMIXMUTE __BITS(16,10)
60d3e86955Sjmcneill #define LMIXMUTE_MIC1 __BIT(16)
61d3e86955Sjmcneill #define LMIXMUTE_MIC2 __BIT(15)
62d3e86955Sjmcneill #define LMIXMUTE_PHONEP_PHONEN __BIT(14)
63d3e86955Sjmcneill #define LMIXMUTE_PHONEN __BIT(13)
64d3e86955Sjmcneill #define LMIXMUTE_LINEINL __BIT(12)
65d3e86955Sjmcneill #define LMIXMUTE_DACL __BIT(11)
66d3e86955Sjmcneill #define LMIXMUTE_DACR __BIT(10)
67d3e86955Sjmcneill #define RHPIS __BIT(9)
68d3e86955Sjmcneill #define LHPIS __BIT(8)
69d3e86955Sjmcneill #define RHPPAMUTE __BIT(7)
70d3e86955Sjmcneill #define LHPPAMUTE __BIT(6)
71d3e86955Sjmcneill #define HPVOL __BITS(5,0)
72d3e86955Sjmcneill
73d3e86955Sjmcneill #define OMIXER_PA_CTRL 0x24
74d3e86955Sjmcneill #define HPPAEN __BIT(31)
75d3e86955Sjmcneill #define HPCOM_CTL __BITS(30,29)
76d3e86955Sjmcneill #define COMPTEN __BIT(28)
77d3e86955Sjmcneill #define PA_ANTI_POP_CTRL __BITS(27,26)
78d3e86955Sjmcneill #define MIC1G __BITS(17,15)
79d3e86955Sjmcneill #define MIC2G __BITS(14,12)
80d3e86955Sjmcneill #define LINEING __BITS(11,9)
81d3e86955Sjmcneill #define PHONEG __BITS(8,6)
82d3e86955Sjmcneill #define PHONEPG __BITS(5,3)
83d3e86955Sjmcneill #define PHONENG __BITS(2,0)
84d3e86955Sjmcneill
85d3e86955Sjmcneill #define AC_MIC_CTRL 0x28
86d3e86955Sjmcneill #define HBIASEN __BIT(31)
87d3e86955Sjmcneill #define MBIASEN __BIT(30)
88d3e86955Sjmcneill #define HBIASADCEN __BIT(29)
89d3e86955Sjmcneill #define MIC1AMPEN __BIT(28)
90d3e86955Sjmcneill #define MIC1BOOST __BITS(27,25)
91d3e86955Sjmcneill #define MIC2AMPEN __BIT(24)
92d3e86955Sjmcneill #define MIC2BOOST __BITS(23,21)
93d3e86955Sjmcneill #define MIC2SLT __BIT(20)
94d3e86955Sjmcneill #define LINEOUTLEN __BIT(19)
95d3e86955Sjmcneill #define LINEOUTREN __BIT(18)
96d3e86955Sjmcneill #define LINEOUTLSRC __BIT(17)
97d3e86955Sjmcneill #define LINEOUTRSRC __BIT(16)
98d3e86955Sjmcneill #define LINEOUTVC __BITS(15,11)
99d3e86955Sjmcneill #define PHONEPREG __BITS(10,8)
100d3e86955Sjmcneill #define PHONEOUTG __BITS(7,5)
101d3e86955Sjmcneill #define PHONEOUTEN __BIT(4)
102d3e86955Sjmcneill #define PHONEOUTS0 __BIT(3)
103d3e86955Sjmcneill #define PHONEOUTS1 __BIT(2)
104d3e86955Sjmcneill #define PHONEOUTS2 __BIT(1)
105d3e86955Sjmcneill #define PHONEOUTS3 __BIT(0)
106d3e86955Sjmcneill
107d3e86955Sjmcneill #define AC_ADCA_CTRL 0x2c
108d3e86955Sjmcneill #define ADCREN __BIT(31)
109d3e86955Sjmcneill #define ADCLEN __BIT(30)
110d3e86955Sjmcneill #define ADCRG __BITS(29,27)
111d3e86955Sjmcneill #define ADCLG __BITS(26,24)
112d3e86955Sjmcneill #define RADCMIXMUTE __BITS(13,7)
113d3e86955Sjmcneill #define RADCMIXMUTE_MIC1 __BIT(13)
114d3e86955Sjmcneill #define RADCMIXMUTE_MIC2 __BIT(12)
115d3e86955Sjmcneill #define RADCMIXMUTE_PHONEP_PHONEN __BIT(11)
116d3e86955Sjmcneill #define RADCMIXMUTE_PHONEP __BIT(10)
117d3e86955Sjmcneill #define RADCMIXMUTE_LINEINR __BIT(9)
118d3e86955Sjmcneill #define RADCMIXMUTE_ROM __BIT(8)
119d3e86955Sjmcneill #define RADCMIXMUTE_LOM __BIT(7)
120d3e86955Sjmcneill #define LADCMIXMUTE __BITS(6,0)
121d3e86955Sjmcneill #define LADCMIXMUTE_MIC1 __BIT(6)
122d3e86955Sjmcneill #define LADCMIXMUTE_MIC2 __BIT(5)
123d3e86955Sjmcneill #define LADCMIXMUTE_PHONEP_PHONEN __BIT(4)
124d3e86955Sjmcneill #define LADCMIXMUTE_PHONEN __BIT(3)
125d3e86955Sjmcneill #define LADCMIXMUTE_LINEINL __BIT(2)
126d3e86955Sjmcneill #define LADCMIXMUTE_LOM __BIT(1)
127d3e86955Sjmcneill #define LADCMIXMUTE_ROM __BIT(0)
128d3e86955Sjmcneill
129d3e86955Sjmcneill enum a31_codec_mixer_ctrl {
130d3e86955Sjmcneill A31_CODEC_OUTPUT_CLASS,
131d3e86955Sjmcneill A31_CODEC_INPUT_CLASS,
132d3e86955Sjmcneill
133d3e86955Sjmcneill A31_CODEC_OUTPUT_MASTER_VOLUME,
134d3e86955Sjmcneill A31_CODEC_INPUT_DAC_VOLUME,
135d3e86955Sjmcneill
136d3e86955Sjmcneill A31_CODEC_MIXER_CTRL_LAST
137d3e86955Sjmcneill };
138d3e86955Sjmcneill
139d3e86955Sjmcneill static const struct a31_codec_mixer {
140d3e86955Sjmcneill const char * name;
141d3e86955Sjmcneill enum a31_codec_mixer_ctrl mixer_class;
142d3e86955Sjmcneill u_int reg;
143d3e86955Sjmcneill u_int mask;
144d3e86955Sjmcneill } a31_codec_mixers[A31_CODEC_MIXER_CTRL_LAST] = {
145d3e86955Sjmcneill [A31_CODEC_OUTPUT_MASTER_VOLUME] = { AudioNmaster,
146d3e86955Sjmcneill A31_CODEC_OUTPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
147d3e86955Sjmcneill [A31_CODEC_INPUT_DAC_VOLUME] = { AudioNdac,
148d3e86955Sjmcneill A31_CODEC_INPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
149d3e86955Sjmcneill };
150d3e86955Sjmcneill
151d3e86955Sjmcneill #define RD4(sc, reg) \
152d3e86955Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
153d3e86955Sjmcneill #define WR4(sc, reg, val) \
154d3e86955Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
155d3e86955Sjmcneill #define SET4(sc, reg, mask) \
156d3e86955Sjmcneill WR4((sc), (reg), RD4((sc), (reg)) | (mask))
157d3e86955Sjmcneill #define CLR4(sc, reg, mask) \
158d3e86955Sjmcneill WR4((sc), (reg), RD4((sc), (reg)) & ~(mask))
159d3e86955Sjmcneill
160d3e86955Sjmcneill static int
a31_codec_init(struct sunxi_codec_softc * sc)161d3e86955Sjmcneill a31_codec_init(struct sunxi_codec_softc *sc)
162d3e86955Sjmcneill {
163d3e86955Sjmcneill
164d3e86955Sjmcneill /* Disable HPCOM and HPCOM output protection */
165d3e86955Sjmcneill CLR4(sc, OMIXER_PA_CTRL, HPCOM_CTL | COMPTEN);
166d3e86955Sjmcneill /* Enable headphone power amp */
167d3e86955Sjmcneill SET4(sc, OMIXER_PA_CTRL, HPPAEN);
168d3e86955Sjmcneill
169d3e86955Sjmcneill /* Set headphone PA input to DAC */
170d3e86955Sjmcneill CLR4(sc, OMIXER_DACA_CTRL, RHPIS | LHPIS);
171d3e86955Sjmcneill /* Mute inputs to headphone PA */
172d3e86955Sjmcneill CLR4(sc, OMIXER_DACA_CTRL, RHPPAMUTE | LHPPAMUTE);
173d3e86955Sjmcneill /* Set initial volume */
174d3e86955Sjmcneill CLR4(sc, OMIXER_DACA_CTRL, HPVOL);
175d3e86955Sjmcneill SET4(sc, OMIXER_DACA_CTRL, __SHIFTIN(A31_DEFAULT_HPVOL, HPVOL));
176d3e86955Sjmcneill
177d3e86955Sjmcneill /* Disable lineout */
178d3e86955Sjmcneill CLR4(sc, AC_MIC_CTRL, LINEOUTLEN | LINEOUTREN | LINEOUTVC);
179d3e86955Sjmcneill /* Enable headset microphone bias, current sensor, and ADC */
180d3e86955Sjmcneill SET4(sc, AC_MIC_CTRL, HBIASEN | HBIASADCEN);
181d3e86955Sjmcneill
182d3e86955Sjmcneill return 0;
183d3e86955Sjmcneill }
184d3e86955Sjmcneill
185d3e86955Sjmcneill static void
a31_codec_mute(struct sunxi_codec_softc * sc,int mute,u_int mode)186d3e86955Sjmcneill a31_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
187d3e86955Sjmcneill {
188d3e86955Sjmcneill if (mode == AUMODE_PLAY) {
189d3e86955Sjmcneill if (sc->sc_pin_pa != NULL)
190d3e86955Sjmcneill fdtbus_gpio_write(sc->sc_pin_pa, !mute);
191d3e86955Sjmcneill
192d3e86955Sjmcneill if (mute) {
193d3e86955Sjmcneill CLR4(sc, OMIXER_DACA_CTRL, DACAREN | DACALEN);
194d3e86955Sjmcneill } else {
195d3e86955Sjmcneill CLR4(sc, OMIXER_DACA_CTRL, RMIXMUTE | LMIXMUTE);
196d3e86955Sjmcneill SET4(sc, OMIXER_DACA_CTRL,
197d3e86955Sjmcneill LHPIS | RHPIS | LHPPAMUTE | RHPPAMUTE |
198d3e86955Sjmcneill DACAREN | DACALEN | RMIXEN | LMIXEN |
199d3e86955Sjmcneill RMIXMUTE_DACR | LMIXMUTE_DACL);
200d3e86955Sjmcneill }
201d3e86955Sjmcneill } else {
202d3e86955Sjmcneill if (mute) {
203d3e86955Sjmcneill CLR4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
204d3e86955Sjmcneill } else {
205d3e86955Sjmcneill SET4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
206d3e86955Sjmcneill }
207d3e86955Sjmcneill }
208d3e86955Sjmcneill }
209d3e86955Sjmcneill
210d3e86955Sjmcneill static int
a31_codec_set_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)211d3e86955Sjmcneill a31_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
212d3e86955Sjmcneill {
213d3e86955Sjmcneill const struct a31_codec_mixer *mix;
214d3e86955Sjmcneill u_int val, shift;
215d3e86955Sjmcneill int nvol;
216d3e86955Sjmcneill
217d3e86955Sjmcneill switch (mc->dev) {
218d3e86955Sjmcneill case A31_CODEC_OUTPUT_MASTER_VOLUME:
219d3e86955Sjmcneill case A31_CODEC_INPUT_DAC_VOLUME:
220d3e86955Sjmcneill mix = &a31_codec_mixers[mc->dev];
221d3e86955Sjmcneill val = RD4(sc, mix->reg);
222d3e86955Sjmcneill shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
223d3e86955Sjmcneill nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
224d3e86955Sjmcneill val &= ~mix->mask;
225d3e86955Sjmcneill val |= __SHIFTIN(nvol, mix->mask);
226d3e86955Sjmcneill WR4(sc, mix->reg, val);
227d3e86955Sjmcneill return 0;
228d3e86955Sjmcneill }
229d3e86955Sjmcneill
230d3e86955Sjmcneill return ENXIO;
231d3e86955Sjmcneill }
232d3e86955Sjmcneill
233d3e86955Sjmcneill static int
a31_codec_get_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)234d3e86955Sjmcneill a31_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
235d3e86955Sjmcneill {
236d3e86955Sjmcneill const struct a31_codec_mixer *mix;
237d3e86955Sjmcneill u_int val, shift;
238d3e86955Sjmcneill int nvol;
239d3e86955Sjmcneill
240d3e86955Sjmcneill switch (mc->dev) {
241d3e86955Sjmcneill case A31_CODEC_OUTPUT_MASTER_VOLUME:
242d3e86955Sjmcneill case A31_CODEC_INPUT_DAC_VOLUME:
243d3e86955Sjmcneill mix = &a31_codec_mixers[mc->dev];
244d3e86955Sjmcneill val = RD4(sc, mix->reg);
245d3e86955Sjmcneill shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
246d3e86955Sjmcneill nvol = __SHIFTOUT(val, mix->mask) << shift;
247d3e86955Sjmcneill mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
248d3e86955Sjmcneill mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
249d3e86955Sjmcneill return 0;
250d3e86955Sjmcneill }
251d3e86955Sjmcneill
252d3e86955Sjmcneill return ENXIO;
253d3e86955Sjmcneill }
254d3e86955Sjmcneill
255d3e86955Sjmcneill static int
a31_codec_query_devinfo(struct sunxi_codec_softc * sc,mixer_devinfo_t * di)256d3e86955Sjmcneill a31_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
257d3e86955Sjmcneill {
258d3e86955Sjmcneill const struct a31_codec_mixer *mix;
259d3e86955Sjmcneill
260d3e86955Sjmcneill switch (di->index) {
261d3e86955Sjmcneill case A31_CODEC_OUTPUT_CLASS:
262d3e86955Sjmcneill di->mixer_class = di->index;
263d3e86955Sjmcneill strcpy(di->label.name, AudioCoutputs);
264d3e86955Sjmcneill di->type = AUDIO_MIXER_CLASS;
265d3e86955Sjmcneill di->next = di->prev = AUDIO_MIXER_LAST;
266d3e86955Sjmcneill return 0;
267d3e86955Sjmcneill
268d3e86955Sjmcneill case A31_CODEC_INPUT_CLASS:
269d3e86955Sjmcneill di->mixer_class = di->index;
270d3e86955Sjmcneill strcpy(di->label.name, AudioCinputs);
271d3e86955Sjmcneill di->type = AUDIO_MIXER_CLASS;
272d3e86955Sjmcneill di->next = di->prev = AUDIO_MIXER_LAST;
273d3e86955Sjmcneill return 0;
274d3e86955Sjmcneill
275d3e86955Sjmcneill case A31_CODEC_OUTPUT_MASTER_VOLUME:
276d3e86955Sjmcneill case A31_CODEC_INPUT_DAC_VOLUME:
277d3e86955Sjmcneill mix = &a31_codec_mixers[di->index];
278d3e86955Sjmcneill di->mixer_class = mix->mixer_class;
279d3e86955Sjmcneill strcpy(di->label.name, mix->name);
280d3e86955Sjmcneill di->un.v.delta =
281d3e86955Sjmcneill 256 / (__SHIFTOUT_MASK(mix->mask) + 1);
282d3e86955Sjmcneill di->type = AUDIO_MIXER_VALUE;
283d3e86955Sjmcneill di->next = di->prev = AUDIO_MIXER_LAST;
284d3e86955Sjmcneill di->un.v.num_channels = 2;
285d3e86955Sjmcneill strcpy(di->un.v.units.name, AudioNvolume);
286d3e86955Sjmcneill return 0;
287d3e86955Sjmcneill }
288d3e86955Sjmcneill
289d3e86955Sjmcneill return ENXIO;
290d3e86955Sjmcneill }
291d3e86955Sjmcneill
292d3e86955Sjmcneill const struct sunxi_codec_conf sun6i_a31_codecconf = {
293d3e86955Sjmcneill .name = "A31 Audio Codec",
294d3e86955Sjmcneill
295d3e86955Sjmcneill .init = a31_codec_init,
296d3e86955Sjmcneill .mute = a31_codec_mute,
297d3e86955Sjmcneill .set_port = a31_codec_set_port,
298d3e86955Sjmcneill .get_port = a31_codec_get_port,
299d3e86955Sjmcneill .query_devinfo = a31_codec_query_devinfo,
300d3e86955Sjmcneill
301d3e86955Sjmcneill .DPC = 0x00,
302d3e86955Sjmcneill .DAC_FIFOC = 0x04,
303d3e86955Sjmcneill .DAC_FIFOS = 0x08,
304d3e86955Sjmcneill .DAC_TXDATA = 0x0c,
305d3e86955Sjmcneill .ADC_FIFOC = 0x10,
306d3e86955Sjmcneill .ADC_FIFOS = 0x14,
307d3e86955Sjmcneill .ADC_RXDATA = 0x18,
308d3e86955Sjmcneill .DAC_CNT = 0x40,
309d3e86955Sjmcneill .ADC_CNT = 0x44,
310d3e86955Sjmcneill };
311