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