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