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