1/* $NetBSD: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki 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: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki 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	A31_DEFAULT_HPVOL	0x20
45
46#define	OMIXER_DACA_CTRL	0x20
47#define	 DACAREN		__BIT(31)
48#define	 DACALEN		__BIT(30)
49#define	 RMIXEN			__BIT(29)
50#define	 LMIXEN			__BIT(28)
51#define	 RMIXMUTE		__BITS(23,17)
52#define	 RMIXMUTE_MIC1		__BIT(23)
53#define	 RMIXMUTE_MIC2		__BIT(22)
54#define	 RMIXMUTE_PHONEP_PHONEN	__BIT(21)
55#define	 RMIXMUTE_PHONEP	__BIT(20)
56#define	 RMIXMUTE_LINEINR	__BIT(19)
57#define	 RMIXMUTE_DACR		__BIT(18)
58#define	 RMIXMUTE_DACL		__BIT(17)
59#define	 LMIXMUTE		__BITS(16,10)
60#define	 LMIXMUTE_MIC1		__BIT(16)
61#define	 LMIXMUTE_MIC2		__BIT(15)
62#define	 LMIXMUTE_PHONEP_PHONEN	__BIT(14)
63#define	 LMIXMUTE_PHONEN	__BIT(13)
64#define	 LMIXMUTE_LINEINL	__BIT(12)
65#define	 LMIXMUTE_DACL		__BIT(11)
66#define	 LMIXMUTE_DACR		__BIT(10)
67#define	 RHPIS			__BIT(9)
68#define	 LHPIS			__BIT(8)
69#define	 RHPPAMUTE		__BIT(7)
70#define	 LHPPAMUTE		__BIT(6)
71#define	 HPVOL			__BITS(5,0)
72
73#define	OMIXER_PA_CTRL		0x24
74#define	 HPPAEN			__BIT(31)
75#define	 HPCOM_CTL		__BITS(30,29)
76#define	 COMPTEN		__BIT(28)
77#define	 PA_ANTI_POP_CTRL	__BITS(27,26)
78#define	 MIC1G			__BITS(17,15)
79#define	 MIC2G			__BITS(14,12)
80#define	 LINEING		__BITS(11,9)
81#define	 PHONEG			__BITS(8,6)
82#define	 PHONEPG		__BITS(5,3)
83#define	 PHONENG		__BITS(2,0)
84
85#define	AC_MIC_CTRL		0x28
86#define	 HBIASEN		__BIT(31)
87#define	 MBIASEN		__BIT(30)
88#define	 HBIASADCEN		__BIT(29)
89#define	 MIC1AMPEN		__BIT(28)
90#define	 MIC1BOOST		__BITS(27,25)
91#define	 MIC2AMPEN		__BIT(24)
92#define	 MIC2BOOST		__BITS(23,21)
93#define	 MIC2SLT		__BIT(20)
94#define	 LINEOUTLEN		__BIT(19)
95#define	 LINEOUTREN		__BIT(18)
96#define	 LINEOUTLSRC		__BIT(17)
97#define	 LINEOUTRSRC		__BIT(16)
98#define	 LINEOUTVC		__BITS(15,11)
99#define	 PHONEPREG		__BITS(10,8)
100#define	 PHONEOUTG		__BITS(7,5)
101#define	 PHONEOUTEN		__BIT(4)
102#define	 PHONEOUTS0		__BIT(3)
103#define	 PHONEOUTS1		__BIT(2)
104#define	 PHONEOUTS2		__BIT(1)
105#define	 PHONEOUTS3		__BIT(0)
106
107#define	AC_ADCA_CTRL		0x2c
108#define	 ADCREN			__BIT(31)
109#define	 ADCLEN			__BIT(30)
110#define	 ADCRG			__BITS(29,27)
111#define	 ADCLG			__BITS(26,24)
112#define	 RADCMIXMUTE		__BITS(13,7)
113#define	 RADCMIXMUTE_MIC1	__BIT(13)
114#define	 RADCMIXMUTE_MIC2	__BIT(12)
115#define	 RADCMIXMUTE_PHONEP_PHONEN __BIT(11)
116#define	 RADCMIXMUTE_PHONEP	__BIT(10)
117#define	 RADCMIXMUTE_LINEINR	__BIT(9)
118#define	 RADCMIXMUTE_ROM	__BIT(8)
119#define	 RADCMIXMUTE_LOM	__BIT(7)
120#define	 LADCMIXMUTE		__BITS(6,0)
121#define	 LADCMIXMUTE_MIC1	__BIT(6)
122#define	 LADCMIXMUTE_MIC2	__BIT(5)
123#define	 LADCMIXMUTE_PHONEP_PHONEN __BIT(4)
124#define	 LADCMIXMUTE_PHONEN	__BIT(3)
125#define	 LADCMIXMUTE_LINEINL	__BIT(2)
126#define	 LADCMIXMUTE_LOM	__BIT(1)
127#define	 LADCMIXMUTE_ROM	__BIT(0)
128
129enum a31_codec_mixer_ctrl {
130	A31_CODEC_OUTPUT_CLASS,
131	A31_CODEC_INPUT_CLASS,
132
133	A31_CODEC_OUTPUT_MASTER_VOLUME,
134	A31_CODEC_INPUT_DAC_VOLUME,
135
136	A31_CODEC_MIXER_CTRL_LAST
137};
138
139static const struct a31_codec_mixer {
140	const char *			name;
141	enum a31_codec_mixer_ctrl	mixer_class;
142	u_int				reg;
143	u_int				mask;
144} a31_codec_mixers[A31_CODEC_MIXER_CTRL_LAST] = {
145	[A31_CODEC_OUTPUT_MASTER_VOLUME]	= { AudioNmaster,
146	    A31_CODEC_OUTPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
147	[A31_CODEC_INPUT_DAC_VOLUME]		= { AudioNdac,
148	    A31_CODEC_INPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
149};
150
151#define	RD4(sc, reg)			\
152	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
153#define	WR4(sc, reg, val)		\
154	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
155#define	SET4(sc, reg, mask)		\
156	WR4((sc), (reg), RD4((sc), (reg)) | (mask))
157#define	CLR4(sc, reg, mask)		\
158	WR4((sc), (reg), RD4((sc), (reg)) & ~(mask))
159
160static int
161a31_codec_init(struct sunxi_codec_softc *sc)
162{
163
164	/* Disable HPCOM and HPCOM output protection */
165	CLR4(sc, OMIXER_PA_CTRL, HPCOM_CTL | COMPTEN);
166	/* Enable headphone power amp */
167	SET4(sc, OMIXER_PA_CTRL, HPPAEN);
168
169	/* Set headphone PA input to DAC */
170	CLR4(sc, OMIXER_DACA_CTRL, RHPIS | LHPIS);
171	/* Mute inputs to headphone PA */
172	CLR4(sc, OMIXER_DACA_CTRL, RHPPAMUTE | LHPPAMUTE);
173	/* Set initial volume */
174	CLR4(sc, OMIXER_DACA_CTRL, HPVOL);
175	SET4(sc, OMIXER_DACA_CTRL, __SHIFTIN(A31_DEFAULT_HPVOL, HPVOL));
176
177	/* Disable lineout */
178	CLR4(sc, AC_MIC_CTRL, LINEOUTLEN | LINEOUTREN | LINEOUTVC);
179	/* Enable headset microphone bias, current sensor, and ADC */
180	SET4(sc, AC_MIC_CTRL, HBIASEN | HBIASADCEN);
181
182	return 0;
183}
184
185static void
186a31_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
187{
188	if (mode == AUMODE_PLAY) {
189		if (sc->sc_pin_pa != NULL)
190			fdtbus_gpio_write(sc->sc_pin_pa, !mute);
191
192		if (mute) {
193			CLR4(sc, OMIXER_DACA_CTRL, DACAREN | DACALEN);
194		} else {
195			CLR4(sc, OMIXER_DACA_CTRL, RMIXMUTE | LMIXMUTE);
196			SET4(sc, OMIXER_DACA_CTRL,
197			    LHPIS | RHPIS | LHPPAMUTE | RHPPAMUTE |
198			    DACAREN | DACALEN | RMIXEN | LMIXEN |
199			    RMIXMUTE_DACR | LMIXMUTE_DACL);
200		}
201	} else {
202		if (mute) {
203			CLR4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
204		} else {
205			SET4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
206		}
207	}
208}
209
210static int
211a31_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
212{
213	const struct a31_codec_mixer *mix;
214	u_int val, shift;
215	int nvol;
216
217	switch (mc->dev) {
218	case A31_CODEC_OUTPUT_MASTER_VOLUME:
219	case A31_CODEC_INPUT_DAC_VOLUME:
220		mix = &a31_codec_mixers[mc->dev];
221		val = RD4(sc, mix->reg);
222		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
223		nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
224		val &= ~mix->mask;
225		val |= __SHIFTIN(nvol, mix->mask);
226		WR4(sc, mix->reg, val);
227		return 0;
228	}
229
230	return ENXIO;
231}
232
233static int
234a31_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
235{
236	const struct a31_codec_mixer *mix;
237	u_int val, shift;
238	int nvol;
239
240	switch (mc->dev) {
241	case A31_CODEC_OUTPUT_MASTER_VOLUME:
242	case A31_CODEC_INPUT_DAC_VOLUME:
243		mix = &a31_codec_mixers[mc->dev];
244		val = RD4(sc, mix->reg);
245		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
246		nvol = __SHIFTOUT(val, mix->mask) << shift;
247		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
248		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
249		return 0;
250	}
251
252	return ENXIO;
253}
254
255static int
256a31_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
257{
258	const struct a31_codec_mixer *mix;
259
260	switch (di->index) {
261	case A31_CODEC_OUTPUT_CLASS:
262		di->mixer_class = di->index;
263		strcpy(di->label.name, AudioCoutputs);
264		di->type = AUDIO_MIXER_CLASS;
265		di->next = di->prev = AUDIO_MIXER_LAST;
266		return 0;
267
268	case A31_CODEC_INPUT_CLASS:
269		di->mixer_class = di->index;
270		strcpy(di->label.name, AudioCinputs);
271		di->type = AUDIO_MIXER_CLASS;
272		di->next = di->prev = AUDIO_MIXER_LAST;
273		return 0;
274
275	case A31_CODEC_OUTPUT_MASTER_VOLUME:
276	case A31_CODEC_INPUT_DAC_VOLUME:
277		mix = &a31_codec_mixers[di->index];
278		di->mixer_class = mix->mixer_class;
279		strcpy(di->label.name, mix->name);
280		di->un.v.delta =
281		    256 / (__SHIFTOUT_MASK(mix->mask) + 1);
282		di->type = AUDIO_MIXER_VALUE;
283		di->next = di->prev = AUDIO_MIXER_LAST;
284		di->un.v.num_channels = 2;
285		strcpy(di->un.v.units.name, AudioNvolume);
286		return 0;
287	}
288
289	return ENXIO;
290}
291
292const struct sunxi_codec_conf sun6i_a31_codecconf = {
293	.name = "A31 Audio Codec",
294
295	.init = a31_codec_init,
296	.mute = a31_codec_mute,
297	.set_port = a31_codec_set_port,
298	.get_port = a31_codec_get_port,
299	.query_devinfo = a31_codec_query_devinfo,
300
301	.DPC		= 0x00,
302	.DAC_FIFOC	= 0x04,
303	.DAC_FIFOS	= 0x08,
304	.DAC_TXDATA	= 0x0c,
305	.ADC_FIFOC	= 0x10,
306	.ADC_FIFOS	= 0x14,
307	.ADC_RXDATA	= 0x18,
308	.DAC_CNT	= 0x40,
309	.ADC_CNT	= 0x44,
310};
311