xref: /freebsd/sys/arm/allwinner/aw_i2s.c (revision cb25a9455802de29a3609f34bef9e94af842b890)
1*cb25a945SOleksandr Tymoshenko /*-
2*cb25a945SOleksandr Tymoshenko  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*cb25a945SOleksandr Tymoshenko  *
4*cb25a945SOleksandr Tymoshenko  * Copyright (c) 2020 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5*cb25a945SOleksandr Tymoshenko  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
6*cb25a945SOleksandr Tymoshenko  *
7*cb25a945SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
8*cb25a945SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
9*cb25a945SOleksandr Tymoshenko  * are met:
10*cb25a945SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
11*cb25a945SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
12*cb25a945SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
13*cb25a945SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
14*cb25a945SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
15*cb25a945SOleksandr Tymoshenko  *
16*cb25a945SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17*cb25a945SOleksandr Tymoshenko  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18*cb25a945SOleksandr Tymoshenko  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19*cb25a945SOleksandr Tymoshenko  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20*cb25a945SOleksandr Tymoshenko  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*cb25a945SOleksandr Tymoshenko  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22*cb25a945SOleksandr Tymoshenko  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*cb25a945SOleksandr Tymoshenko  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*cb25a945SOleksandr Tymoshenko  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*cb25a945SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*cb25a945SOleksandr Tymoshenko  * SUCH DAMAGE.
27*cb25a945SOleksandr Tymoshenko  *
28*cb25a945SOleksandr Tymoshenko  * $FreeBSD$
29*cb25a945SOleksandr Tymoshenko  */
30*cb25a945SOleksandr Tymoshenko 
31*cb25a945SOleksandr Tymoshenko #include <sys/cdefs.h>
32*cb25a945SOleksandr Tymoshenko __FBSDID("$FreeBSD$");
33*cb25a945SOleksandr Tymoshenko 
34*cb25a945SOleksandr Tymoshenko #include <sys/param.h>
35*cb25a945SOleksandr Tymoshenko #include <sys/systm.h>
36*cb25a945SOleksandr Tymoshenko #include <sys/bus.h>
37*cb25a945SOleksandr Tymoshenko #include <sys/kernel.h>
38*cb25a945SOleksandr Tymoshenko #include <sys/lock.h>
39*cb25a945SOleksandr Tymoshenko #include <sys/module.h>
40*cb25a945SOleksandr Tymoshenko #include <sys/mutex.h>
41*cb25a945SOleksandr Tymoshenko #include <sys/rman.h>
42*cb25a945SOleksandr Tymoshenko #include <sys/resource.h>
43*cb25a945SOleksandr Tymoshenko #include <machine/bus.h>
44*cb25a945SOleksandr Tymoshenko 
45*cb25a945SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h>
46*cb25a945SOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h>
47*cb25a945SOleksandr Tymoshenko 
48*cb25a945SOleksandr Tymoshenko #include <dev/extres/clk/clk.h>
49*cb25a945SOleksandr Tymoshenko #include <dev/extres/hwreset/hwreset.h>
50*cb25a945SOleksandr Tymoshenko 
51*cb25a945SOleksandr Tymoshenko #include "syscon_if.h"
52*cb25a945SOleksandr Tymoshenko 
53*cb25a945SOleksandr Tymoshenko #include "opt_snd.h"
54*cb25a945SOleksandr Tymoshenko #include <dev/sound/pcm/sound.h>
55*cb25a945SOleksandr Tymoshenko #include <dev/sound/fdt/audio_dai.h>
56*cb25a945SOleksandr Tymoshenko #include "audio_dai_if.h"
57*cb25a945SOleksandr Tymoshenko 
58*cb25a945SOleksandr Tymoshenko #define	FIFO_LEVEL	0x40
59*cb25a945SOleksandr Tymoshenko 
60*cb25a945SOleksandr Tymoshenko #define	DA_CTL		0x00
61*cb25a945SOleksandr Tymoshenko #define		DA_CTL_BCLK_OUT (1 << 18)	/* sun8i */
62*cb25a945SOleksandr Tymoshenko #define		DA_CLK_LRCK_OUT (1 << 17)	/* sun8i */
63*cb25a945SOleksandr Tymoshenko #define		DA_CTL_SDO_EN	(1 << 8)
64*cb25a945SOleksandr Tymoshenko #define		DA_CTL_MS	(1 << 5)	/* sun4i */
65*cb25a945SOleksandr Tymoshenko #define		DA_CTL_PCM	(1 << 4)	/* sun4i */
66*cb25a945SOleksandr Tymoshenko #define		DA_CTL_MODE_SEL_MASK	(3 << 4) /* sun8i */
67*cb25a945SOleksandr Tymoshenko #define		DA_CTL_MODE_SEL_PCM	(0 << 4) /* sun8i */
68*cb25a945SOleksandr Tymoshenko #define		DA_CTL_MODE_SEL_LJ	(1 << 4) /* sun8i */
69*cb25a945SOleksandr Tymoshenko #define		DA_CTL_MODE_SEL_RJ	(2 << 4) /* sun8i */
70*cb25a945SOleksandr Tymoshenko #define		DA_CTL_TXEN	(1 << 2)
71*cb25a945SOleksandr Tymoshenko #define		DA_CTL_RXEN	(1 << 1)
72*cb25a945SOleksandr Tymoshenko #define		DA_CTL_GEN	(1 << 0)
73*cb25a945SOleksandr Tymoshenko #define	DA_FAT0		0x04
74*cb25a945SOleksandr Tymoshenko #define		DA_FAT0_LRCK_PERIOD_MASK	(0x3ff << 8) /* sun8i */
75*cb25a945SOleksandr Tymoshenko #define		DA_FAT0_LRCK_PERIOD(n)		(((n) & 0x3fff) << 8) /* sun8i */
76*cb25a945SOleksandr Tymoshenko #define		DA_FAT0_LRCP_MASK	(1 << 7)
77*cb25a945SOleksandr Tymoshenko #define		DA_LRCP_NORMAL		(0 << 7)
78*cb25a945SOleksandr Tymoshenko #define		DA_LRCP_INVERTED	(1 << 7)
79*cb25a945SOleksandr Tymoshenko #define		DA_FAT0_BCP_MASK	(1 << 6)
80*cb25a945SOleksandr Tymoshenko #define		DA_BCP_NORMAL		(0 << 6)
81*cb25a945SOleksandr Tymoshenko #define		DA_BCP_INVERTED		(1 << 6)
82*cb25a945SOleksandr Tymoshenko #define		DA_FAT0_SR	__BITS(5,4)
83*cb25a945SOleksandr Tymoshenko #define		DA_FAT0_WSS	__BITS(3,2)
84*cb25a945SOleksandr Tymoshenko #define		DA_FAT0_FMT_MASK	(3 << 0)
85*cb25a945SOleksandr Tymoshenko #define		 DA_FMT_I2S	0
86*cb25a945SOleksandr Tymoshenko #define		 DA_FMT_LJ	1
87*cb25a945SOleksandr Tymoshenko #define		 DA_FMT_RJ	2
88*cb25a945SOleksandr Tymoshenko #define	DA_FAT1		0x08
89*cb25a945SOleksandr Tymoshenko #define	DA_ISTA		0x0c
90*cb25a945SOleksandr Tymoshenko #define		DA_ISTA_TXUI_INT	(1 << 6)
91*cb25a945SOleksandr Tymoshenko #define		DA_ISTA_TXEI_INT	(1 << 4)
92*cb25a945SOleksandr Tymoshenko #define		DA_ISTA_RXAI_INT	(1 << 0)
93*cb25a945SOleksandr Tymoshenko #define	DA_RXFIFO	0x10
94*cb25a945SOleksandr Tymoshenko #define	DA_FCTL		0x14
95*cb25a945SOleksandr Tymoshenko #define		DA_FCTL_HUB_EN	(1 << 31)
96*cb25a945SOleksandr Tymoshenko #define		DA_FCTL_FTX	(1 << 25)
97*cb25a945SOleksandr Tymoshenko #define		DA_FCTL_FRX	(1 << 24)
98*cb25a945SOleksandr Tymoshenko #define		DA_FCTL_TXTL_MASK	(0x7f << 12)
99*cb25a945SOleksandr Tymoshenko #define		DA_FCTL_TXTL(v)		(((v) & 0x7f) << 12)
100*cb25a945SOleksandr Tymoshenko #define		DA_FCTL_TXIM	(1 << 2)
101*cb25a945SOleksandr Tymoshenko #define	DA_FSTA		0x18
102*cb25a945SOleksandr Tymoshenko #define		DA_FSTA_TXE_CNT(v)	(((v) >> 16) & 0xff)
103*cb25a945SOleksandr Tymoshenko #define		DA_FSTA_RXA_CNT(v)	((v) & 0x3f)
104*cb25a945SOleksandr Tymoshenko #define	DA_INT		0x1c
105*cb25a945SOleksandr Tymoshenko #define		DA_INT_TX_DRQ	(1 << 7)
106*cb25a945SOleksandr Tymoshenko #define		DA_INT_TXUI_EN	(1 << 6)
107*cb25a945SOleksandr Tymoshenko #define		DA_INT_TXEI_EN	(1 << 4)
108*cb25a945SOleksandr Tymoshenko #define		DA_INT_RX_DRQ	(1 << 3)
109*cb25a945SOleksandr Tymoshenko #define		DA_INT_RXAI_EN	(1 << 0)
110*cb25a945SOleksandr Tymoshenko #define	DA_TXFIFO	0x20
111*cb25a945SOleksandr Tymoshenko #define	DA_CLKD		0x24
112*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_MCLKO_EN_SUN8I (1 << 8)
113*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_MCLKO_EN_SUN4I (1 << 7)
114*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_BCLKDIV_SUN8I(n)	(((n) & 0xf) << 4)
115*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_BCLKDIV_SUN8I_MASK	(0xf << 4)
116*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_BCLKDIV_SUN4I(n)	(((n) & 7) << 4)
117*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_BCLKDIV_SUN4I_MASK	(7 << 4)
118*cb25a945SOleksandr Tymoshenko #define		 DA_CLKD_BCLKDIV_8	3
119*cb25a945SOleksandr Tymoshenko #define		 DA_CLKD_BCLKDIV_16	5
120*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_MCLKDIV(n)	(((n) & 0xff) << 0)
121*cb25a945SOleksandr Tymoshenko #define		DA_CLKD_MCLKDIV_MASK	(0xf << 0)
122*cb25a945SOleksandr Tymoshenko #define		 DA_CLKD_MCLKDIV_1	0
123*cb25a945SOleksandr Tymoshenko #define	DA_TXCNT	0x28
124*cb25a945SOleksandr Tymoshenko #define	DA_RXCNT	0x2c
125*cb25a945SOleksandr Tymoshenko #define	DA_CHCFG	0x30		/* sun8i */
126*cb25a945SOleksandr Tymoshenko #define		DA_CHCFG_TX_SLOT_HIZ	(1 << 9)
127*cb25a945SOleksandr Tymoshenko #define		DA_CHCFG_TXN_STATE	(1 << 8)
128*cb25a945SOleksandr Tymoshenko #define		DA_CHCFG_RX_SLOT_NUM_MASK	(7 << 4)
129*cb25a945SOleksandr Tymoshenko #define		DA_CHCFG_RX_SLOT_NUM(n)		(((n) & 7) << 4)
130*cb25a945SOleksandr Tymoshenko #define		DA_CHCFG_TX_SLOT_NUM_MASK	(7 << 0)
131*cb25a945SOleksandr Tymoshenko #define		DA_CHCFG_TX_SLOT_NUM(n)		(((n) & 7) << 0)
132*cb25a945SOleksandr Tymoshenko 
133*cb25a945SOleksandr Tymoshenko #define	DA_CHSEL_OFFSET(n)	(((n) & 3) << 12)	/* sun8i */
134*cb25a945SOleksandr Tymoshenko #define	DA_CHSEL_OFFSET_MASK	(3 << 12)	/* sun8i */
135*cb25a945SOleksandr Tymoshenko #define	DA_CHSEL_EN(n)		(((n) & 0xff) << 4)
136*cb25a945SOleksandr Tymoshenko #define	DA_CHSEL_EN_MASK	(0xff << 4)
137*cb25a945SOleksandr Tymoshenko #define	DA_CHSEL_SEL(n)		(((n) & 7) << 0)
138*cb25a945SOleksandr Tymoshenko #define	DA_CHSEL_SEL_MASK	(7 << 0)
139*cb25a945SOleksandr Tymoshenko 
140*cb25a945SOleksandr Tymoshenko #define	AUDIO_BUFFER_SIZE	48000 * 4
141*cb25a945SOleksandr Tymoshenko 
142*cb25a945SOleksandr Tymoshenko #define	AW_I2S_SAMPLE_RATE	48000
143*cb25a945SOleksandr Tymoshenko #define	AW_I2S_CLK_RATE		24576000
144*cb25a945SOleksandr Tymoshenko 
145*cb25a945SOleksandr Tymoshenko enum sunxi_i2s_type {
146*cb25a945SOleksandr Tymoshenko 	SUNXI_I2S_SUN4I,
147*cb25a945SOleksandr Tymoshenko 	SUNXI_I2S_SUN8I,
148*cb25a945SOleksandr Tymoshenko };
149*cb25a945SOleksandr Tymoshenko 
150*cb25a945SOleksandr Tymoshenko struct sunxi_i2s_config {
151*cb25a945SOleksandr Tymoshenko 	const char	*name;
152*cb25a945SOleksandr Tymoshenko 	enum sunxi_i2s_type type;
153*cb25a945SOleksandr Tymoshenko 	bus_size_t	txchsel;
154*cb25a945SOleksandr Tymoshenko 	bus_size_t	txchmap;
155*cb25a945SOleksandr Tymoshenko 	bus_size_t	rxchsel;
156*cb25a945SOleksandr Tymoshenko 	bus_size_t	rxchmap;
157*cb25a945SOleksandr Tymoshenko };
158*cb25a945SOleksandr Tymoshenko 
159*cb25a945SOleksandr Tymoshenko static const struct sunxi_i2s_config sun50i_a64_codec_config = {
160*cb25a945SOleksandr Tymoshenko 	.name = "Audio Codec (digital part)",
161*cb25a945SOleksandr Tymoshenko 	.type = SUNXI_I2S_SUN4I,
162*cb25a945SOleksandr Tymoshenko 	.txchsel = 0x30,
163*cb25a945SOleksandr Tymoshenko 	.txchmap = 0x34,
164*cb25a945SOleksandr Tymoshenko 	.rxchsel = 0x38,
165*cb25a945SOleksandr Tymoshenko 	.rxchmap = 0x3c,
166*cb25a945SOleksandr Tymoshenko };
167*cb25a945SOleksandr Tymoshenko 
168*cb25a945SOleksandr Tymoshenko static const struct sunxi_i2s_config sun8i_h3_config = {
169*cb25a945SOleksandr Tymoshenko 	.name = "I2S/PCM controller",
170*cb25a945SOleksandr Tymoshenko 	.type = SUNXI_I2S_SUN8I,
171*cb25a945SOleksandr Tymoshenko 	.txchsel = 0x34,
172*cb25a945SOleksandr Tymoshenko 	.txchmap = 0x44,
173*cb25a945SOleksandr Tymoshenko 	.rxchsel = 0x54,
174*cb25a945SOleksandr Tymoshenko 	.rxchmap = 0x58,
175*cb25a945SOleksandr Tymoshenko };
176*cb25a945SOleksandr Tymoshenko 
177*cb25a945SOleksandr Tymoshenko static const u_int sun4i_i2s_bclk_divmap[] = {
178*cb25a945SOleksandr Tymoshenko 	[0] = 2,
179*cb25a945SOleksandr Tymoshenko 	[1] = 4,
180*cb25a945SOleksandr Tymoshenko 	[2] = 6,
181*cb25a945SOleksandr Tymoshenko 	[3] = 8,
182*cb25a945SOleksandr Tymoshenko 	[4] = 12,
183*cb25a945SOleksandr Tymoshenko 	[5] = 16,
184*cb25a945SOleksandr Tymoshenko };
185*cb25a945SOleksandr Tymoshenko 
186*cb25a945SOleksandr Tymoshenko static const u_int sun4i_i2s_mclk_divmap[] = {
187*cb25a945SOleksandr Tymoshenko 	[0] = 1,
188*cb25a945SOleksandr Tymoshenko 	[1] = 2,
189*cb25a945SOleksandr Tymoshenko 	[2] = 4,
190*cb25a945SOleksandr Tymoshenko 	[3] = 6,
191*cb25a945SOleksandr Tymoshenko 	[4] = 8,
192*cb25a945SOleksandr Tymoshenko 	[5] = 12,
193*cb25a945SOleksandr Tymoshenko 	[6] = 16,
194*cb25a945SOleksandr Tymoshenko 	[7] = 24,
195*cb25a945SOleksandr Tymoshenko };
196*cb25a945SOleksandr Tymoshenko 
197*cb25a945SOleksandr Tymoshenko static const u_int sun8i_i2s_divmap[] = {
198*cb25a945SOleksandr Tymoshenko 	[1] = 1,
199*cb25a945SOleksandr Tymoshenko 	[2] = 2,
200*cb25a945SOleksandr Tymoshenko 	[3] = 4,
201*cb25a945SOleksandr Tymoshenko 	[4] = 6,
202*cb25a945SOleksandr Tymoshenko 	[5] = 8,
203*cb25a945SOleksandr Tymoshenko 	[6] = 12,
204*cb25a945SOleksandr Tymoshenko 	[7] = 16,
205*cb25a945SOleksandr Tymoshenko 	[8] = 24,
206*cb25a945SOleksandr Tymoshenko 	[9] = 32,
207*cb25a945SOleksandr Tymoshenko 	[10] = 48,
208*cb25a945SOleksandr Tymoshenko 	[11] = 64,
209*cb25a945SOleksandr Tymoshenko 	[12] = 96,
210*cb25a945SOleksandr Tymoshenko 	[13] = 128,
211*cb25a945SOleksandr Tymoshenko 	[14] = 176,
212*cb25a945SOleksandr Tymoshenko 	[15] = 192,
213*cb25a945SOleksandr Tymoshenko };
214*cb25a945SOleksandr Tymoshenko 
215*cb25a945SOleksandr Tymoshenko 
216*cb25a945SOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = {
217*cb25a945SOleksandr Tymoshenko 	{ "allwinner,sun50i-a64-codec-i2s", (uintptr_t)&sun50i_a64_codec_config },
218*cb25a945SOleksandr Tymoshenko 	{ "allwinner,sun8i-h3-i2s", (uintptr_t)&sun8i_h3_config },
219*cb25a945SOleksandr Tymoshenko 	{ NULL,					0 }
220*cb25a945SOleksandr Tymoshenko };
221*cb25a945SOleksandr Tymoshenko 
222*cb25a945SOleksandr Tymoshenko static struct resource_spec aw_i2s_spec[] = {
223*cb25a945SOleksandr Tymoshenko 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
224*cb25a945SOleksandr Tymoshenko 	{ SYS_RES_IRQ,		0,	RF_ACTIVE | RF_SHAREABLE },
225*cb25a945SOleksandr Tymoshenko 	{ -1, 0 }
226*cb25a945SOleksandr Tymoshenko };
227*cb25a945SOleksandr Tymoshenko 
228*cb25a945SOleksandr Tymoshenko struct aw_i2s_softc {
229*cb25a945SOleksandr Tymoshenko 	device_t	dev;
230*cb25a945SOleksandr Tymoshenko 	struct resource	*res[2];
231*cb25a945SOleksandr Tymoshenko 	struct mtx	mtx;
232*cb25a945SOleksandr Tymoshenko 	clk_t		clk;
233*cb25a945SOleksandr Tymoshenko 	struct sunxi_i2s_config	*cfg;
234*cb25a945SOleksandr Tymoshenko 	void *		intrhand;
235*cb25a945SOleksandr Tymoshenko 	/* pointers to playback/capture buffers */
236*cb25a945SOleksandr Tymoshenko 	uint32_t	play_ptr;
237*cb25a945SOleksandr Tymoshenko 	uint32_t	rec_ptr;
238*cb25a945SOleksandr Tymoshenko };
239*cb25a945SOleksandr Tymoshenko 
240*cb25a945SOleksandr Tymoshenko #define	I2S_LOCK(sc)		mtx_lock(&(sc)->mtx)
241*cb25a945SOleksandr Tymoshenko #define	I2S_UNLOCK(sc)		mtx_unlock(&(sc)->mtx)
242*cb25a945SOleksandr Tymoshenko #define	I2S_READ(sc, reg)	bus_read_4((sc)->res[0], (reg))
243*cb25a945SOleksandr Tymoshenko #define	I2S_WRITE(sc, reg, val)	bus_write_4((sc)->res[0], (reg), (val))
244*cb25a945SOleksandr Tymoshenko #define	I2S_TYPE(sc)		((sc)->cfg->type)
245*cb25a945SOleksandr Tymoshenko 
246*cb25a945SOleksandr Tymoshenko static int aw_i2s_probe(device_t dev);
247*cb25a945SOleksandr Tymoshenko static int aw_i2s_attach(device_t dev);
248*cb25a945SOleksandr Tymoshenko static int aw_i2s_detach(device_t dev);
249*cb25a945SOleksandr Tymoshenko 
250*cb25a945SOleksandr Tymoshenko static u_int
251*cb25a945SOleksandr Tymoshenko sunxi_i2s_div_to_regval(const u_int *divmap, u_int divmaplen, u_int div)
252*cb25a945SOleksandr Tymoshenko {
253*cb25a945SOleksandr Tymoshenko 	u_int n;
254*cb25a945SOleksandr Tymoshenko 
255*cb25a945SOleksandr Tymoshenko 	for (n = 0; n < divmaplen; n++)
256*cb25a945SOleksandr Tymoshenko 		if (divmap[n] == div)
257*cb25a945SOleksandr Tymoshenko 			return n;
258*cb25a945SOleksandr Tymoshenko 
259*cb25a945SOleksandr Tymoshenko 	return -1;
260*cb25a945SOleksandr Tymoshenko }
261*cb25a945SOleksandr Tymoshenko 
262*cb25a945SOleksandr Tymoshenko static uint32_t sc_fmt[] = {
263*cb25a945SOleksandr Tymoshenko 	SND_FORMAT(AFMT_S16_LE, 2, 0),
264*cb25a945SOleksandr Tymoshenko 	0
265*cb25a945SOleksandr Tymoshenko };
266*cb25a945SOleksandr Tymoshenko static struct pcmchan_caps aw_i2s_caps = {AW_I2S_SAMPLE_RATE, AW_I2S_SAMPLE_RATE, sc_fmt, 0};
267*cb25a945SOleksandr Tymoshenko 
268*cb25a945SOleksandr Tymoshenko 
269*cb25a945SOleksandr Tymoshenko static int
270*cb25a945SOleksandr Tymoshenko aw_i2s_init(struct aw_i2s_softc *sc)
271*cb25a945SOleksandr Tymoshenko {
272*cb25a945SOleksandr Tymoshenko 	uint32_t val;
273*cb25a945SOleksandr Tymoshenko 	int error;
274*cb25a945SOleksandr Tymoshenko 
275*cb25a945SOleksandr Tymoshenko 	error = clk_enable(sc->clk);
276*cb25a945SOleksandr Tymoshenko 	if (error != 0) {
277*cb25a945SOleksandr Tymoshenko 		device_printf(sc->dev, "cannot enable mod clock\n");
278*cb25a945SOleksandr Tymoshenko 		return (ENXIO);
279*cb25a945SOleksandr Tymoshenko 	}
280*cb25a945SOleksandr Tymoshenko 
281*cb25a945SOleksandr Tymoshenko 	/* Reset */
282*cb25a945SOleksandr Tymoshenko 	val = I2S_READ(sc, DA_CTL);
283*cb25a945SOleksandr Tymoshenko 	val &= ~(DA_CTL_TXEN|DA_CTL_RXEN|DA_CTL_GEN);
284*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_CTL, val);
285*cb25a945SOleksandr Tymoshenko 
286*cb25a945SOleksandr Tymoshenko 	val = I2S_READ(sc, DA_FCTL);
287*cb25a945SOleksandr Tymoshenko 	val &= ~(DA_FCTL_FTX|DA_FCTL_FRX);
288*cb25a945SOleksandr Tymoshenko 	val &= ~(DA_FCTL_TXTL_MASK);
289*cb25a945SOleksandr Tymoshenko 	val |= DA_FCTL_TXTL(FIFO_LEVEL);
290*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_FCTL, val);
291*cb25a945SOleksandr Tymoshenko 
292*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_TXCNT, 0);
293*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_RXCNT, 0);
294*cb25a945SOleksandr Tymoshenko 
295*cb25a945SOleksandr Tymoshenko 	/* Enable */
296*cb25a945SOleksandr Tymoshenko 	val = I2S_READ(sc, DA_CTL);
297*cb25a945SOleksandr Tymoshenko 	val |= DA_CTL_GEN;
298*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_CTL, val);
299*cb25a945SOleksandr Tymoshenko 	val |= DA_CTL_SDO_EN;
300*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_CTL, val);
301*cb25a945SOleksandr Tymoshenko 
302*cb25a945SOleksandr Tymoshenko 	/* Setup channels */
303*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, sc->cfg->txchmap, 0x76543210);
304*cb25a945SOleksandr Tymoshenko 	val = I2S_READ(sc, sc->cfg->txchsel);
305*cb25a945SOleksandr Tymoshenko 	val &= ~DA_CHSEL_EN_MASK;
306*cb25a945SOleksandr Tymoshenko 	val |= DA_CHSEL_EN(3);
307*cb25a945SOleksandr Tymoshenko 	val &= ~DA_CHSEL_SEL_MASK;
308*cb25a945SOleksandr Tymoshenko 	val |= DA_CHSEL_SEL(1);
309*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, sc->cfg->txchsel, val);
310*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, sc->cfg->rxchmap, 0x76543210);
311*cb25a945SOleksandr Tymoshenko 	val = I2S_READ(sc, sc->cfg->rxchsel);
312*cb25a945SOleksandr Tymoshenko 	val &= ~DA_CHSEL_EN_MASK;
313*cb25a945SOleksandr Tymoshenko 	val |= DA_CHSEL_EN(3);
314*cb25a945SOleksandr Tymoshenko 	val &= ~DA_CHSEL_SEL_MASK;
315*cb25a945SOleksandr Tymoshenko 	val |= DA_CHSEL_SEL(1);
316*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, sc->cfg->rxchsel, val);
317*cb25a945SOleksandr Tymoshenko 
318*cb25a945SOleksandr Tymoshenko 	if (I2S_TYPE(sc) == SUNXI_I2S_SUN8I) {
319*cb25a945SOleksandr Tymoshenko 		val = I2S_READ(sc, DA_CHCFG);
320*cb25a945SOleksandr Tymoshenko 		val &= ~DA_CHCFG_TX_SLOT_NUM_MASK;
321*cb25a945SOleksandr Tymoshenko 		val |= DA_CHCFG_TX_SLOT_NUM(1);
322*cb25a945SOleksandr Tymoshenko 		val &= ~DA_CHCFG_RX_SLOT_NUM_MASK;
323*cb25a945SOleksandr Tymoshenko 		val |= DA_CHCFG_RX_SLOT_NUM(1);
324*cb25a945SOleksandr Tymoshenko 		I2S_WRITE(sc, DA_CHCFG, val);
325*cb25a945SOleksandr Tymoshenko 	}
326*cb25a945SOleksandr Tymoshenko 
327*cb25a945SOleksandr Tymoshenko 	return (0);
328*cb25a945SOleksandr Tymoshenko }
329*cb25a945SOleksandr Tymoshenko 
330*cb25a945SOleksandr Tymoshenko static int
331*cb25a945SOleksandr Tymoshenko aw_i2s_probe(device_t dev)
332*cb25a945SOleksandr Tymoshenko {
333*cb25a945SOleksandr Tymoshenko 	if (!ofw_bus_status_okay(dev))
334*cb25a945SOleksandr Tymoshenko 		return (ENXIO);
335*cb25a945SOleksandr Tymoshenko 
336*cb25a945SOleksandr Tymoshenko 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
337*cb25a945SOleksandr Tymoshenko 		return (ENXIO);
338*cb25a945SOleksandr Tymoshenko 
339*cb25a945SOleksandr Tymoshenko 	device_set_desc(dev, "Allwinner I2S");
340*cb25a945SOleksandr Tymoshenko 	return (BUS_PROBE_DEFAULT);
341*cb25a945SOleksandr Tymoshenko }
342*cb25a945SOleksandr Tymoshenko 
343*cb25a945SOleksandr Tymoshenko static int
344*cb25a945SOleksandr Tymoshenko aw_i2s_attach(device_t dev)
345*cb25a945SOleksandr Tymoshenko {
346*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc *sc;
347*cb25a945SOleksandr Tymoshenko 	int error;
348*cb25a945SOleksandr Tymoshenko 	phandle_t node;
349*cb25a945SOleksandr Tymoshenko 	hwreset_t rst;
350*cb25a945SOleksandr Tymoshenko 	clk_t clk;
351*cb25a945SOleksandr Tymoshenko 
352*cb25a945SOleksandr Tymoshenko 	sc = device_get_softc(dev);
353*cb25a945SOleksandr Tymoshenko 	sc->dev = dev;
354*cb25a945SOleksandr Tymoshenko 
355*cb25a945SOleksandr Tymoshenko 	sc->cfg  = (void*)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
356*cb25a945SOleksandr Tymoshenko 
357*cb25a945SOleksandr Tymoshenko 	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
358*cb25a945SOleksandr Tymoshenko 
359*cb25a945SOleksandr Tymoshenko 	if (bus_alloc_resources(dev, aw_i2s_spec, sc->res) != 0) {
360*cb25a945SOleksandr Tymoshenko 		device_printf(dev, "cannot allocate resources for device\n");
361*cb25a945SOleksandr Tymoshenko 		error = ENXIO;
362*cb25a945SOleksandr Tymoshenko 		goto fail;
363*cb25a945SOleksandr Tymoshenko 	}
364*cb25a945SOleksandr Tymoshenko 
365*cb25a945SOleksandr Tymoshenko 	error = clk_get_by_ofw_name(dev, 0, "mod", &sc->clk);
366*cb25a945SOleksandr Tymoshenko 	if (error != 0) {
367*cb25a945SOleksandr Tymoshenko 		device_printf(dev, "cannot get i2s_clk clock\n");
368*cb25a945SOleksandr Tymoshenko 		goto fail;
369*cb25a945SOleksandr Tymoshenko 	}
370*cb25a945SOleksandr Tymoshenko 
371*cb25a945SOleksandr Tymoshenko 	error = clk_get_by_ofw_name(dev, 0, "apb", &clk);
372*cb25a945SOleksandr Tymoshenko 	if (error != 0) {
373*cb25a945SOleksandr Tymoshenko 		device_printf(dev, "cannot get APB clock\n");
374*cb25a945SOleksandr Tymoshenko 		goto fail;
375*cb25a945SOleksandr Tymoshenko 	}
376*cb25a945SOleksandr Tymoshenko 
377*cb25a945SOleksandr Tymoshenko 	error = clk_enable(clk);
378*cb25a945SOleksandr Tymoshenko 	if (error != 0) {
379*cb25a945SOleksandr Tymoshenko 		device_printf(dev, "cannot enable APB clock\n");
380*cb25a945SOleksandr Tymoshenko 		goto fail;
381*cb25a945SOleksandr Tymoshenko 	}
382*cb25a945SOleksandr Tymoshenko 
383*cb25a945SOleksandr Tymoshenko 	if (hwreset_get_by_ofw_idx(dev, 0, 0, &rst) == 0) {
384*cb25a945SOleksandr Tymoshenko 		error = hwreset_deassert(rst);
385*cb25a945SOleksandr Tymoshenko 		if (error != 0) {
386*cb25a945SOleksandr Tymoshenko 			device_printf(dev, "cannot de-assert reset\n");
387*cb25a945SOleksandr Tymoshenko 			goto fail;
388*cb25a945SOleksandr Tymoshenko 		}
389*cb25a945SOleksandr Tymoshenko 	}
390*cb25a945SOleksandr Tymoshenko 
391*cb25a945SOleksandr Tymoshenko 	aw_i2s_init(sc);
392*cb25a945SOleksandr Tymoshenko 
393*cb25a945SOleksandr Tymoshenko 	node = ofw_bus_get_node(dev);
394*cb25a945SOleksandr Tymoshenko 	OF_device_register_xref(OF_xref_from_node(node), dev);
395*cb25a945SOleksandr Tymoshenko 
396*cb25a945SOleksandr Tymoshenko 	return (0);
397*cb25a945SOleksandr Tymoshenko 
398*cb25a945SOleksandr Tymoshenko fail:
399*cb25a945SOleksandr Tymoshenko 	aw_i2s_detach(dev);
400*cb25a945SOleksandr Tymoshenko 	return (error);
401*cb25a945SOleksandr Tymoshenko }
402*cb25a945SOleksandr Tymoshenko 
403*cb25a945SOleksandr Tymoshenko static int
404*cb25a945SOleksandr Tymoshenko aw_i2s_detach(device_t dev)
405*cb25a945SOleksandr Tymoshenko {
406*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc *i2s;
407*cb25a945SOleksandr Tymoshenko 
408*cb25a945SOleksandr Tymoshenko 	i2s = device_get_softc(dev);
409*cb25a945SOleksandr Tymoshenko 
410*cb25a945SOleksandr Tymoshenko 	if (i2s->clk)
411*cb25a945SOleksandr Tymoshenko 		clk_release(i2s->clk);
412*cb25a945SOleksandr Tymoshenko 
413*cb25a945SOleksandr Tymoshenko 	if (i2s->intrhand != NULL)
414*cb25a945SOleksandr Tymoshenko 		bus_teardown_intr(i2s->dev, i2s->res[1], i2s->intrhand);
415*cb25a945SOleksandr Tymoshenko 
416*cb25a945SOleksandr Tymoshenko 	bus_release_resources(dev, aw_i2s_spec, i2s->res);
417*cb25a945SOleksandr Tymoshenko 	mtx_destroy(&i2s->mtx);
418*cb25a945SOleksandr Tymoshenko 
419*cb25a945SOleksandr Tymoshenko 	return (0);
420*cb25a945SOleksandr Tymoshenko }
421*cb25a945SOleksandr Tymoshenko 
422*cb25a945SOleksandr Tymoshenko static int
423*cb25a945SOleksandr Tymoshenko aw_i2s_dai_init(device_t dev, uint32_t format)
424*cb25a945SOleksandr Tymoshenko {
425*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc *sc;
426*cb25a945SOleksandr Tymoshenko 	int fmt, pol, clk;
427*cb25a945SOleksandr Tymoshenko 	uint32_t ctl, fat0, chsel;
428*cb25a945SOleksandr Tymoshenko 	u_int offset;
429*cb25a945SOleksandr Tymoshenko 
430*cb25a945SOleksandr Tymoshenko 	sc = device_get_softc(dev);
431*cb25a945SOleksandr Tymoshenko 
432*cb25a945SOleksandr Tymoshenko 	fmt = AUDIO_DAI_FORMAT_FORMAT(format);
433*cb25a945SOleksandr Tymoshenko 	pol = AUDIO_DAI_FORMAT_POLARITY(format);
434*cb25a945SOleksandr Tymoshenko 	clk = AUDIO_DAI_FORMAT_CLOCK(format);
435*cb25a945SOleksandr Tymoshenko 
436*cb25a945SOleksandr Tymoshenko 	ctl = I2S_READ(sc, DA_CTL);
437*cb25a945SOleksandr Tymoshenko 	fat0 = I2S_READ(sc, DA_FAT0);
438*cb25a945SOleksandr Tymoshenko 
439*cb25a945SOleksandr Tymoshenko 	if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
440*cb25a945SOleksandr Tymoshenko 		fat0 &= ~DA_FAT0_FMT_MASK;
441*cb25a945SOleksandr Tymoshenko 		switch (fmt) {
442*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_I2S:
443*cb25a945SOleksandr Tymoshenko 			fat0 |= DA_FMT_I2S;
444*cb25a945SOleksandr Tymoshenko 			break;
445*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_RJ:
446*cb25a945SOleksandr Tymoshenko 			fat0 |= DA_FMT_RJ;
447*cb25a945SOleksandr Tymoshenko 			break;
448*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_LJ:
449*cb25a945SOleksandr Tymoshenko 			fat0 |= DA_FMT_LJ;
450*cb25a945SOleksandr Tymoshenko 			break;
451*cb25a945SOleksandr Tymoshenko 		default:
452*cb25a945SOleksandr Tymoshenko 			return EINVAL;
453*cb25a945SOleksandr Tymoshenko 		}
454*cb25a945SOleksandr Tymoshenko 		ctl &= ~DA_CTL_PCM;
455*cb25a945SOleksandr Tymoshenko 	} else {
456*cb25a945SOleksandr Tymoshenko 		ctl &= ~DA_CTL_MODE_SEL_MASK;
457*cb25a945SOleksandr Tymoshenko 		switch (fmt) {
458*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_I2S:
459*cb25a945SOleksandr Tymoshenko 			ctl |= DA_CTL_MODE_SEL_LJ;
460*cb25a945SOleksandr Tymoshenko 			offset = 1;
461*cb25a945SOleksandr Tymoshenko 			break;
462*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_LJ:
463*cb25a945SOleksandr Tymoshenko 			ctl |= DA_CTL_MODE_SEL_LJ;
464*cb25a945SOleksandr Tymoshenko 			offset = 0;
465*cb25a945SOleksandr Tymoshenko 			break;
466*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_RJ:
467*cb25a945SOleksandr Tymoshenko 			ctl |= DA_CTL_MODE_SEL_RJ;
468*cb25a945SOleksandr Tymoshenko 			offset = 0;
469*cb25a945SOleksandr Tymoshenko 			break;
470*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_DSPA:
471*cb25a945SOleksandr Tymoshenko 			ctl |= DA_CTL_MODE_SEL_PCM;
472*cb25a945SOleksandr Tymoshenko 			offset = 1;
473*cb25a945SOleksandr Tymoshenko 			break;
474*cb25a945SOleksandr Tymoshenko 		case AUDIO_DAI_FORMAT_DSPB:
475*cb25a945SOleksandr Tymoshenko 			ctl |= DA_CTL_MODE_SEL_PCM;
476*cb25a945SOleksandr Tymoshenko 			offset = 0;
477*cb25a945SOleksandr Tymoshenko 			break;
478*cb25a945SOleksandr Tymoshenko 		default:
479*cb25a945SOleksandr Tymoshenko 			return EINVAL;
480*cb25a945SOleksandr Tymoshenko 		}
481*cb25a945SOleksandr Tymoshenko 
482*cb25a945SOleksandr Tymoshenko 		chsel = I2S_READ(sc, sc->cfg->txchsel);
483*cb25a945SOleksandr Tymoshenko 		chsel &= ~DA_CHSEL_OFFSET_MASK;
484*cb25a945SOleksandr Tymoshenko 		chsel |= DA_CHSEL_OFFSET(offset);
485*cb25a945SOleksandr Tymoshenko 		I2S_WRITE(sc, sc->cfg->txchsel, chsel);
486*cb25a945SOleksandr Tymoshenko 
487*cb25a945SOleksandr Tymoshenko 		chsel = I2S_READ(sc, sc->cfg->rxchsel);
488*cb25a945SOleksandr Tymoshenko 		chsel &= ~DA_CHSEL_OFFSET_MASK;
489*cb25a945SOleksandr Tymoshenko 		chsel |= DA_CHSEL_OFFSET(offset);
490*cb25a945SOleksandr Tymoshenko 		I2S_WRITE(sc, sc->cfg->rxchsel, chsel);
491*cb25a945SOleksandr Tymoshenko 	}
492*cb25a945SOleksandr Tymoshenko 
493*cb25a945SOleksandr Tymoshenko 	fat0 &= ~(DA_FAT0_LRCP_MASK|DA_FAT0_BCP_MASK);
494*cb25a945SOleksandr Tymoshenko 	if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
495*cb25a945SOleksandr Tymoshenko 		if (AUDIO_DAI_POLARITY_INVERTED_BCLK(pol))
496*cb25a945SOleksandr Tymoshenko 			fat0 |= DA_BCP_INVERTED;
497*cb25a945SOleksandr Tymoshenko 		if (AUDIO_DAI_POLARITY_INVERTED_FRAME(pol))
498*cb25a945SOleksandr Tymoshenko 			fat0 |= DA_LRCP_INVERTED;
499*cb25a945SOleksandr Tymoshenko 	} else {
500*cb25a945SOleksandr Tymoshenko 		if (AUDIO_DAI_POLARITY_INVERTED_BCLK(pol))
501*cb25a945SOleksandr Tymoshenko 			fat0 |= DA_BCP_INVERTED;
502*cb25a945SOleksandr Tymoshenko 		if (!AUDIO_DAI_POLARITY_INVERTED_FRAME(pol))
503*cb25a945SOleksandr Tymoshenko 			fat0 |= DA_LRCP_INVERTED;
504*cb25a945SOleksandr Tymoshenko 
505*cb25a945SOleksandr Tymoshenko 		fat0 &= ~DA_FAT0_LRCK_PERIOD_MASK;
506*cb25a945SOleksandr Tymoshenko 		fat0 |= DA_FAT0_LRCK_PERIOD(32 - 1);
507*cb25a945SOleksandr Tymoshenko 	}
508*cb25a945SOleksandr Tymoshenko 
509*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_CTL, ctl);
510*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_FAT0, fat0);
511*cb25a945SOleksandr Tymoshenko 
512*cb25a945SOleksandr Tymoshenko 	return (0);
513*cb25a945SOleksandr Tymoshenko }
514*cb25a945SOleksandr Tymoshenko 
515*cb25a945SOleksandr Tymoshenko 
516*cb25a945SOleksandr Tymoshenko static int
517*cb25a945SOleksandr Tymoshenko aw_i2s_dai_intr(device_t dev, struct snd_dbuf *play_buf, struct snd_dbuf *rec_buf)
518*cb25a945SOleksandr Tymoshenko {
519*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc *sc;
520*cb25a945SOleksandr Tymoshenko 	int ret = 0;
521*cb25a945SOleksandr Tymoshenko 	uint32_t val, status;
522*cb25a945SOleksandr Tymoshenko 
523*cb25a945SOleksandr Tymoshenko 	sc = device_get_softc(dev);
524*cb25a945SOleksandr Tymoshenko 
525*cb25a945SOleksandr Tymoshenko 	I2S_LOCK(sc);
526*cb25a945SOleksandr Tymoshenko 
527*cb25a945SOleksandr Tymoshenko 	status = I2S_READ(sc, DA_ISTA);
528*cb25a945SOleksandr Tymoshenko 	/* Clear interrupts */
529*cb25a945SOleksandr Tymoshenko 	// device_printf(sc->dev, "status: %08x\n", status);
530*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_ISTA, status);
531*cb25a945SOleksandr Tymoshenko 
532*cb25a945SOleksandr Tymoshenko 	if (status & DA_ISTA_TXEI_INT) {
533*cb25a945SOleksandr Tymoshenko 		uint8_t *samples;
534*cb25a945SOleksandr Tymoshenko 		uint32_t count, size, readyptr, written, empty;
535*cb25a945SOleksandr Tymoshenko 
536*cb25a945SOleksandr Tymoshenko 		val  = I2S_READ(sc, DA_FSTA);
537*cb25a945SOleksandr Tymoshenko 		empty = DA_FSTA_TXE_CNT(val);
538*cb25a945SOleksandr Tymoshenko 		count = sndbuf_getready(play_buf);
539*cb25a945SOleksandr Tymoshenko 		size = sndbuf_getsize(play_buf);
540*cb25a945SOleksandr Tymoshenko 		readyptr = sndbuf_getreadyptr(play_buf);
541*cb25a945SOleksandr Tymoshenko 
542*cb25a945SOleksandr Tymoshenko 		samples = (uint8_t*)sndbuf_getbuf(play_buf);
543*cb25a945SOleksandr Tymoshenko 		written = 0;
544*cb25a945SOleksandr Tymoshenko 		if (empty > count / 2)
545*cb25a945SOleksandr Tymoshenko 			empty = count / 2;
546*cb25a945SOleksandr Tymoshenko 		for (; empty > 0; empty--) {
547*cb25a945SOleksandr Tymoshenko 			val = (samples[readyptr++ % size] << 16);
548*cb25a945SOleksandr Tymoshenko 			val |= (samples[readyptr++ % size] << 24);
549*cb25a945SOleksandr Tymoshenko 			written += 2;
550*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_TXFIFO, val);
551*cb25a945SOleksandr Tymoshenko 		}
552*cb25a945SOleksandr Tymoshenko 		sc->play_ptr += written;
553*cb25a945SOleksandr Tymoshenko 		sc->play_ptr %= size;
554*cb25a945SOleksandr Tymoshenko 		ret |= AUDIO_DAI_PLAY_INTR;
555*cb25a945SOleksandr Tymoshenko 	}
556*cb25a945SOleksandr Tymoshenko 
557*cb25a945SOleksandr Tymoshenko 	if (status & DA_ISTA_RXAI_INT) {
558*cb25a945SOleksandr Tymoshenko 		uint8_t *samples;
559*cb25a945SOleksandr Tymoshenko 		uint32_t count, size, freeptr, recorded, available;
560*cb25a945SOleksandr Tymoshenko 
561*cb25a945SOleksandr Tymoshenko 		val  = I2S_READ(sc, DA_FSTA);
562*cb25a945SOleksandr Tymoshenko 		available = DA_FSTA_RXA_CNT(val);
563*cb25a945SOleksandr Tymoshenko 
564*cb25a945SOleksandr Tymoshenko 		count = sndbuf_getfree(rec_buf);
565*cb25a945SOleksandr Tymoshenko 		size = sndbuf_getsize(rec_buf);
566*cb25a945SOleksandr Tymoshenko 		freeptr = sndbuf_getfreeptr(rec_buf);
567*cb25a945SOleksandr Tymoshenko 		samples = (uint8_t*)sndbuf_getbuf(rec_buf);
568*cb25a945SOleksandr Tymoshenko 		recorded = 0;
569*cb25a945SOleksandr Tymoshenko 		if (available > count / 2)
570*cb25a945SOleksandr Tymoshenko 			available = count / 2;
571*cb25a945SOleksandr Tymoshenko 
572*cb25a945SOleksandr Tymoshenko 		for (; available > 0; available--) {
573*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_RXFIFO);
574*cb25a945SOleksandr Tymoshenko 			samples[freeptr++ % size] = (val >> 16) & 0xff;
575*cb25a945SOleksandr Tymoshenko 			samples[freeptr++ % size] = (val >> 24) & 0xff;
576*cb25a945SOleksandr Tymoshenko 			recorded += 2;
577*cb25a945SOleksandr Tymoshenko 		}
578*cb25a945SOleksandr Tymoshenko 		sc->rec_ptr += recorded;
579*cb25a945SOleksandr Tymoshenko 		sc->rec_ptr %= size;
580*cb25a945SOleksandr Tymoshenko 		ret |= AUDIO_DAI_REC_INTR;
581*cb25a945SOleksandr Tymoshenko 	}
582*cb25a945SOleksandr Tymoshenko 
583*cb25a945SOleksandr Tymoshenko 	I2S_UNLOCK(sc);
584*cb25a945SOleksandr Tymoshenko 
585*cb25a945SOleksandr Tymoshenko 	return (ret);
586*cb25a945SOleksandr Tymoshenko }
587*cb25a945SOleksandr Tymoshenko 
588*cb25a945SOleksandr Tymoshenko static struct pcmchan_caps *
589*cb25a945SOleksandr Tymoshenko aw_i2s_dai_get_caps(device_t dev)
590*cb25a945SOleksandr Tymoshenko {
591*cb25a945SOleksandr Tymoshenko 	return (&aw_i2s_caps);
592*cb25a945SOleksandr Tymoshenko }
593*cb25a945SOleksandr Tymoshenko 
594*cb25a945SOleksandr Tymoshenko static int
595*cb25a945SOleksandr Tymoshenko aw_i2s_dai_trigger(device_t dev, int go, int pcm_dir)
596*cb25a945SOleksandr Tymoshenko {
597*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc 	*sc = device_get_softc(dev);
598*cb25a945SOleksandr Tymoshenko 	uint32_t val;
599*cb25a945SOleksandr Tymoshenko 
600*cb25a945SOleksandr Tymoshenko 	if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC))
601*cb25a945SOleksandr Tymoshenko 		return (EINVAL);
602*cb25a945SOleksandr Tymoshenko 
603*cb25a945SOleksandr Tymoshenko 	switch (go) {
604*cb25a945SOleksandr Tymoshenko 	case PCMTRIG_START:
605*cb25a945SOleksandr Tymoshenko 		if (pcm_dir == PCMDIR_PLAY) {
606*cb25a945SOleksandr Tymoshenko 			/* Flush FIFO */
607*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_FCTL);
608*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FTX);
609*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FTX);
610*cb25a945SOleksandr Tymoshenko 
611*cb25a945SOleksandr Tymoshenko 			/* Reset TX sample counter */
612*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_TXCNT, 0);
613*cb25a945SOleksandr Tymoshenko 
614*cb25a945SOleksandr Tymoshenko 			/* Enable TX block */
615*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_CTL);
616*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_CTL, val | DA_CTL_TXEN);
617*cb25a945SOleksandr Tymoshenko 
618*cb25a945SOleksandr Tymoshenko 			/* Enable TX underrun interrupt */
619*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_INT);
620*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_INT, val | DA_INT_TXEI_EN);
621*cb25a945SOleksandr Tymoshenko 		}
622*cb25a945SOleksandr Tymoshenko 
623*cb25a945SOleksandr Tymoshenko 		if (pcm_dir == PCMDIR_REC) {
624*cb25a945SOleksandr Tymoshenko 			/* Flush FIFO */
625*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_FCTL);
626*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FRX);
627*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FRX);
628*cb25a945SOleksandr Tymoshenko 
629*cb25a945SOleksandr Tymoshenko 			/* Reset RX sample counter */
630*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_RXCNT, 0);
631*cb25a945SOleksandr Tymoshenko 
632*cb25a945SOleksandr Tymoshenko 			/* Enable RX block */
633*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_CTL);
634*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_CTL, val | DA_CTL_RXEN);
635*cb25a945SOleksandr Tymoshenko 
636*cb25a945SOleksandr Tymoshenko 			/* Enable RX data available interrupt */
637*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_INT);
638*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_INT, val | DA_INT_RXAI_EN);
639*cb25a945SOleksandr Tymoshenko 		}
640*cb25a945SOleksandr Tymoshenko 
641*cb25a945SOleksandr Tymoshenko 		break;
642*cb25a945SOleksandr Tymoshenko 
643*cb25a945SOleksandr Tymoshenko 	case PCMTRIG_STOP:
644*cb25a945SOleksandr Tymoshenko 	case PCMTRIG_ABORT:
645*cb25a945SOleksandr Tymoshenko 		I2S_LOCK(sc);
646*cb25a945SOleksandr Tymoshenko 
647*cb25a945SOleksandr Tymoshenko 		if (pcm_dir == PCMDIR_PLAY) {
648*cb25a945SOleksandr Tymoshenko 			/* Disable TX block */
649*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_CTL);
650*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_TXEN);
651*cb25a945SOleksandr Tymoshenko 
652*cb25a945SOleksandr Tymoshenko 			/* Enable TX underrun interrupt */
653*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_INT);
654*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_INT, val & ~DA_INT_TXEI_EN);
655*cb25a945SOleksandr Tymoshenko 
656*cb25a945SOleksandr Tymoshenko 			sc->play_ptr = 0;
657*cb25a945SOleksandr Tymoshenko 		} else {
658*cb25a945SOleksandr Tymoshenko 			/* Disable RX block */
659*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_CTL);
660*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_RXEN);
661*cb25a945SOleksandr Tymoshenko 
662*cb25a945SOleksandr Tymoshenko 			/* Disable RX data available interrupt */
663*cb25a945SOleksandr Tymoshenko 			val = I2S_READ(sc, DA_INT);
664*cb25a945SOleksandr Tymoshenko 			I2S_WRITE(sc, DA_INT, val & ~DA_INT_RXAI_EN);
665*cb25a945SOleksandr Tymoshenko 
666*cb25a945SOleksandr Tymoshenko 			sc->rec_ptr = 0;
667*cb25a945SOleksandr Tymoshenko 		}
668*cb25a945SOleksandr Tymoshenko 
669*cb25a945SOleksandr Tymoshenko 		I2S_UNLOCK(sc);
670*cb25a945SOleksandr Tymoshenko 		break;
671*cb25a945SOleksandr Tymoshenko 	}
672*cb25a945SOleksandr Tymoshenko 
673*cb25a945SOleksandr Tymoshenko 	return (0);
674*cb25a945SOleksandr Tymoshenko }
675*cb25a945SOleksandr Tymoshenko 
676*cb25a945SOleksandr Tymoshenko static uint32_t
677*cb25a945SOleksandr Tymoshenko aw_i2s_dai_get_ptr(device_t dev, int pcm_dir)
678*cb25a945SOleksandr Tymoshenko {
679*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc *sc;
680*cb25a945SOleksandr Tymoshenko 	uint32_t ptr;
681*cb25a945SOleksandr Tymoshenko 
682*cb25a945SOleksandr Tymoshenko 	sc = device_get_softc(dev);
683*cb25a945SOleksandr Tymoshenko 
684*cb25a945SOleksandr Tymoshenko 	I2S_LOCK(sc);
685*cb25a945SOleksandr Tymoshenko 	if (pcm_dir == PCMDIR_PLAY)
686*cb25a945SOleksandr Tymoshenko 		ptr = sc->play_ptr;
687*cb25a945SOleksandr Tymoshenko 	else
688*cb25a945SOleksandr Tymoshenko 		ptr = sc->rec_ptr;
689*cb25a945SOleksandr Tymoshenko 	I2S_UNLOCK(sc);
690*cb25a945SOleksandr Tymoshenko 
691*cb25a945SOleksandr Tymoshenko 	return ptr;
692*cb25a945SOleksandr Tymoshenko }
693*cb25a945SOleksandr Tymoshenko 
694*cb25a945SOleksandr Tymoshenko static int
695*cb25a945SOleksandr Tymoshenko aw_i2s_dai_setup_intr(device_t dev, driver_intr_t intr_handler, void *intr_arg)
696*cb25a945SOleksandr Tymoshenko {
697*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc 	*sc = device_get_softc(dev);
698*cb25a945SOleksandr Tymoshenko 
699*cb25a945SOleksandr Tymoshenko 	if (bus_setup_intr(dev, sc->res[1],
700*cb25a945SOleksandr Tymoshenko 	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, intr_handler, intr_arg,
701*cb25a945SOleksandr Tymoshenko 	    &sc->intrhand)) {
702*cb25a945SOleksandr Tymoshenko 		device_printf(dev, "cannot setup interrupt handler\n");
703*cb25a945SOleksandr Tymoshenko 		return (ENXIO);
704*cb25a945SOleksandr Tymoshenko 	}
705*cb25a945SOleksandr Tymoshenko 
706*cb25a945SOleksandr Tymoshenko 	return (0);
707*cb25a945SOleksandr Tymoshenko }
708*cb25a945SOleksandr Tymoshenko 
709*cb25a945SOleksandr Tymoshenko static uint32_t
710*cb25a945SOleksandr Tymoshenko aw_i2s_dai_set_chanformat(device_t dev, uint32_t format)
711*cb25a945SOleksandr Tymoshenko {
712*cb25a945SOleksandr Tymoshenko 
713*cb25a945SOleksandr Tymoshenko 	return (0);
714*cb25a945SOleksandr Tymoshenko }
715*cb25a945SOleksandr Tymoshenko 
716*cb25a945SOleksandr Tymoshenko static int
717*cb25a945SOleksandr Tymoshenko aw_i2s_dai_set_sysclk(device_t dev, unsigned int rate, int dai_dir)
718*cb25a945SOleksandr Tymoshenko {
719*cb25a945SOleksandr Tymoshenko 	struct aw_i2s_softc *sc;
720*cb25a945SOleksandr Tymoshenko 	int bclk_val, mclk_val;
721*cb25a945SOleksandr Tymoshenko 	uint32_t val;
722*cb25a945SOleksandr Tymoshenko 	int error;
723*cb25a945SOleksandr Tymoshenko 
724*cb25a945SOleksandr Tymoshenko 	sc = device_get_softc(dev);
725*cb25a945SOleksandr Tymoshenko 
726*cb25a945SOleksandr Tymoshenko 	error = clk_set_freq(sc->clk, AW_I2S_CLK_RATE, CLK_SET_ROUND_DOWN);
727*cb25a945SOleksandr Tymoshenko 	if (error != 0) {
728*cb25a945SOleksandr Tymoshenko 		device_printf(sc->dev,
729*cb25a945SOleksandr Tymoshenko 		    "couldn't set mod clock rate to %u Hz: %d\n", AW_I2S_CLK_RATE, error);
730*cb25a945SOleksandr Tymoshenko 		return error;
731*cb25a945SOleksandr Tymoshenko 	}
732*cb25a945SOleksandr Tymoshenko 	error = clk_enable(sc->clk);
733*cb25a945SOleksandr Tymoshenko 	if (error != 0) {
734*cb25a945SOleksandr Tymoshenko 		device_printf(sc->dev,
735*cb25a945SOleksandr Tymoshenko 		    "couldn't enable mod clock: %d\n", error);
736*cb25a945SOleksandr Tymoshenko 		return error;
737*cb25a945SOleksandr Tymoshenko 	}
738*cb25a945SOleksandr Tymoshenko 
739*cb25a945SOleksandr Tymoshenko 	const u_int bclk_prate = I2S_TYPE(sc) == SUNXI_I2S_SUN4I ? rate : AW_I2S_CLK_RATE;
740*cb25a945SOleksandr Tymoshenko 
741*cb25a945SOleksandr Tymoshenko 	const u_int bclk_div = bclk_prate / (2 * 32 * AW_I2S_SAMPLE_RATE);
742*cb25a945SOleksandr Tymoshenko 	const u_int mclk_div = AW_I2S_CLK_RATE / rate;
743*cb25a945SOleksandr Tymoshenko 
744*cb25a945SOleksandr Tymoshenko 	if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
745*cb25a945SOleksandr Tymoshenko 		bclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_bclk_divmap,
746*cb25a945SOleksandr Tymoshenko 		    nitems(sun4i_i2s_bclk_divmap), bclk_div);
747*cb25a945SOleksandr Tymoshenko 		mclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_mclk_divmap,
748*cb25a945SOleksandr Tymoshenko 		    nitems(sun4i_i2s_mclk_divmap), mclk_div);
749*cb25a945SOleksandr Tymoshenko 	} else {
750*cb25a945SOleksandr Tymoshenko 		bclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap,
751*cb25a945SOleksandr Tymoshenko 		    nitems(sun8i_i2s_divmap), bclk_div);
752*cb25a945SOleksandr Tymoshenko 		mclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap,
753*cb25a945SOleksandr Tymoshenko 		    nitems(sun8i_i2s_divmap), mclk_div);
754*cb25a945SOleksandr Tymoshenko 	}
755*cb25a945SOleksandr Tymoshenko 	if (bclk_val == -1 || mclk_val == -1) {
756*cb25a945SOleksandr Tymoshenko 		device_printf(sc->dev, "couldn't configure bclk/mclk dividers\n");
757*cb25a945SOleksandr Tymoshenko 		return EIO;
758*cb25a945SOleksandr Tymoshenko 	}
759*cb25a945SOleksandr Tymoshenko 
760*cb25a945SOleksandr Tymoshenko 	val = I2S_READ(sc, DA_CLKD);
761*cb25a945SOleksandr Tymoshenko 	if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) {
762*cb25a945SOleksandr Tymoshenko 		val |= DA_CLKD_MCLKO_EN_SUN4I;
763*cb25a945SOleksandr Tymoshenko 		val &= ~DA_CLKD_BCLKDIV_SUN4I_MASK;
764*cb25a945SOleksandr Tymoshenko 		val |= DA_CLKD_BCLKDIV_SUN4I(bclk_val);
765*cb25a945SOleksandr Tymoshenko 	} else {
766*cb25a945SOleksandr Tymoshenko 		val |= DA_CLKD_MCLKO_EN_SUN8I;
767*cb25a945SOleksandr Tymoshenko 		val &= ~DA_CLKD_BCLKDIV_SUN8I_MASK;
768*cb25a945SOleksandr Tymoshenko 		val |= DA_CLKD_BCLKDIV_SUN8I(bclk_val);
769*cb25a945SOleksandr Tymoshenko 	}
770*cb25a945SOleksandr Tymoshenko 	val &= ~DA_CLKD_MCLKDIV_MASK;
771*cb25a945SOleksandr Tymoshenko 	val |= DA_CLKD_MCLKDIV(mclk_val);
772*cb25a945SOleksandr Tymoshenko 	I2S_WRITE(sc, DA_CLKD, val);
773*cb25a945SOleksandr Tymoshenko 
774*cb25a945SOleksandr Tymoshenko 
775*cb25a945SOleksandr Tymoshenko 	return (0);
776*cb25a945SOleksandr Tymoshenko }
777*cb25a945SOleksandr Tymoshenko 
778*cb25a945SOleksandr Tymoshenko static uint32_t
779*cb25a945SOleksandr Tymoshenko aw_i2s_dai_set_chanspeed(device_t dev, uint32_t speed)
780*cb25a945SOleksandr Tymoshenko {
781*cb25a945SOleksandr Tymoshenko 
782*cb25a945SOleksandr Tymoshenko 	return (speed);
783*cb25a945SOleksandr Tymoshenko }
784*cb25a945SOleksandr Tymoshenko 
785*cb25a945SOleksandr Tymoshenko static device_method_t aw_i2s_methods[] = {
786*cb25a945SOleksandr Tymoshenko 	/* Device interface */
787*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(device_probe,		aw_i2s_probe),
788*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(device_attach,	aw_i2s_attach),
789*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(device_detach,	aw_i2s_detach),
790*cb25a945SOleksandr Tymoshenko 
791*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_init,	aw_i2s_dai_init),
792*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_setup_intr,	aw_i2s_dai_setup_intr),
793*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_set_sysclk,	aw_i2s_dai_set_sysclk),
794*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_set_chanspeed,	aw_i2s_dai_set_chanspeed),
795*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_set_chanformat,	aw_i2s_dai_set_chanformat),
796*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_intr,	aw_i2s_dai_intr),
797*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_get_caps,	aw_i2s_dai_get_caps),
798*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_trigger,	aw_i2s_dai_trigger),
799*cb25a945SOleksandr Tymoshenko 	DEVMETHOD(audio_dai_get_ptr,	aw_i2s_dai_get_ptr),
800*cb25a945SOleksandr Tymoshenko 
801*cb25a945SOleksandr Tymoshenko 	DEVMETHOD_END
802*cb25a945SOleksandr Tymoshenko };
803*cb25a945SOleksandr Tymoshenko 
804*cb25a945SOleksandr Tymoshenko static driver_t aw_i2s_driver = {
805*cb25a945SOleksandr Tymoshenko 	"i2s",
806*cb25a945SOleksandr Tymoshenko 	aw_i2s_methods,
807*cb25a945SOleksandr Tymoshenko 	sizeof(struct aw_i2s_softc),
808*cb25a945SOleksandr Tymoshenko };
809*cb25a945SOleksandr Tymoshenko 
810*cb25a945SOleksandr Tymoshenko static devclass_t aw_i2s_devclass;
811*cb25a945SOleksandr Tymoshenko 
812*cb25a945SOleksandr Tymoshenko DRIVER_MODULE(aw_i2s, simplebus, aw_i2s_driver, aw_i2s_devclass, 0, 0);
813*cb25a945SOleksandr Tymoshenko SIMPLEBUS_PNP_INFO(compat_data);
814