1 1.12 thorpej /* $NetBSD: sunxi_i2s.c,v 1.12 2021/01/27 03:10:20 thorpej Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca> 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 8 1.1 jmcneill * modification, are permitted provided that the following conditions 9 1.1 jmcneill * are met: 10 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 11 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 12 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 14 1.1 jmcneill * documentation and/or other materials provided with the distribution. 15 1.1 jmcneill * 16 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 1.1 jmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 1.1 jmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 1.1 jmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 1.1 jmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 1.1 jmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 1.1 jmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 1.1 jmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 1.1 jmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 jmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 jmcneill * SUCH DAMAGE. 27 1.1 jmcneill */ 28 1.1 jmcneill 29 1.1 jmcneill #include <sys/cdefs.h> 30 1.12 thorpej __KERNEL_RCSID(0, "$NetBSD: sunxi_i2s.c,v 1.12 2021/01/27 03:10:20 thorpej Exp $"); 31 1.1 jmcneill 32 1.1 jmcneill #include <sys/param.h> 33 1.1 jmcneill #include <sys/bus.h> 34 1.1 jmcneill #include <sys/cpu.h> 35 1.1 jmcneill #include <sys/device.h> 36 1.1 jmcneill #include <sys/kmem.h> 37 1.1 jmcneill #include <sys/gpio.h> 38 1.1 jmcneill 39 1.1 jmcneill #include <sys/audioio.h> 40 1.4 isaki #include <dev/audio/audio_if.h> 41 1.4 isaki #include <dev/audio/linear.h> 42 1.1 jmcneill 43 1.1 jmcneill #include <dev/fdt/fdtvar.h> 44 1.1 jmcneill 45 1.7 jmcneill #define SUNXI_I2S_CLK_RATE 24576000 46 1.7 jmcneill #define SUNXI_I2S_SAMPLE_RATE 48000 47 1.1 jmcneill 48 1.1 jmcneill #define DA_CTL 0x00 49 1.7 jmcneill #define DA_CTL_BCLK_OUT __BIT(18) /* sun8i */ 50 1.7 jmcneill #define DA_CLK_LRCK_OUT __BIT(17) /* sun8i */ 51 1.1 jmcneill #define DA_CTL_SDO_EN __BIT(8) 52 1.7 jmcneill #define DA_CTL_MS __BIT(5) /* sun4i */ 53 1.7 jmcneill #define DA_CTL_PCM __BIT(4) /* sun4i */ 54 1.7 jmcneill #define DA_CTL_MODE_SEL __BITS(5,4) /* sun8i */ 55 1.7 jmcneill #define DA_CTL_MODE_SEL_PCM 0 56 1.7 jmcneill #define DA_CTL_MODE_SEL_LJ 1 57 1.7 jmcneill #define DA_CTL_MODE_SEL_RJ 2 58 1.1 jmcneill #define DA_CTL_TXEN __BIT(2) 59 1.1 jmcneill #define DA_CTL_RXEN __BIT(1) 60 1.1 jmcneill #define DA_CTL_GEN __BIT(0) 61 1.1 jmcneill #define DA_FAT0 0x04 62 1.7 jmcneill #define DA_FAT0_LRCK_PERIOD __BITS(17,8) /* sun8i */ 63 1.1 jmcneill #define DA_FAT0_LRCP __BIT(7) 64 1.1 jmcneill #define DA_LRCP_NORMAL 0 65 1.1 jmcneill #define DA_LRCP_INVERTED 1 66 1.1 jmcneill #define DA_FAT0_BCP __BIT(6) 67 1.1 jmcneill #define DA_BCP_NORMAL 0 68 1.1 jmcneill #define DA_BCP_INVERTED 1 69 1.1 jmcneill #define DA_FAT0_SR __BITS(5,4) 70 1.1 jmcneill #define DA_FAT0_WSS __BITS(3,2) 71 1.1 jmcneill #define DA_FAT0_FMT __BITS(1,0) 72 1.1 jmcneill #define DA_FMT_I2S 0 73 1.1 jmcneill #define DA_FMT_LJ 1 74 1.1 jmcneill #define DA_FMT_RJ 2 75 1.1 jmcneill #define DA_FAT1 0x08 76 1.1 jmcneill #define DA_ISTA 0x0c 77 1.1 jmcneill #define DA_RXFIFO 0x10 78 1.1 jmcneill #define DA_FCTL 0x14 79 1.1 jmcneill #define DA_FCTL_HUB_EN __BIT(31) 80 1.1 jmcneill #define DA_FCTL_FTX __BIT(25) 81 1.1 jmcneill #define DA_FCTL_FRX __BIT(24) 82 1.3 jmcneill #define DA_FCTL_TXIM __BIT(2) 83 1.3 jmcneill #define DA_FCTL_RXIM __BITS(1,0) 84 1.1 jmcneill #define DA_FSTA 0x18 85 1.1 jmcneill #define DA_INT 0x1c 86 1.1 jmcneill #define DA_INT_TX_DRQ __BIT(7) 87 1.1 jmcneill #define DA_INT_RX_DRQ __BIT(3) 88 1.1 jmcneill #define DA_TXFIFO 0x20 89 1.1 jmcneill #define DA_CLKD 0x24 90 1.7 jmcneill #define DA_CLKD_MCLKO_EN_SUN8I __BIT(8) 91 1.7 jmcneill #define DA_CLKD_MCLKO_EN_SUN4I __BIT(7) 92 1.7 jmcneill #define DA_CLKD_BCLKDIV_SUN8I __BITS(7,4) 93 1.7 jmcneill #define DA_CLKD_BCLKDIV_SUN4I __BITS(6,4) 94 1.3 jmcneill #define DA_CLKD_BCLKDIV_8 3 95 1.1 jmcneill #define DA_CLKD_BCLKDIV_16 5 96 1.1 jmcneill #define DA_CLKD_MCLKDIV __BITS(3,0) 97 1.1 jmcneill #define DA_CLKD_MCLKDIV_1 0 98 1.1 jmcneill #define DA_TXCNT 0x28 99 1.1 jmcneill #define DA_RXCNT 0x2c 100 1.7 jmcneill #define DA_CHCFG 0x30 /* sun8i */ 101 1.7 jmcneill #define DA_CHCFG_TX_SLOT_HIZ __BIT(9) 102 1.7 jmcneill #define DA_CHCFG_TXN_STATE __BIT(8) 103 1.7 jmcneill #define DA_CHCFG_RX_SLOT_NUM __BITS(6,4) 104 1.7 jmcneill #define DA_CHCFG_TX_SLOT_NUM __BITS(2,0) 105 1.1 jmcneill 106 1.7 jmcneill #define DA_CHSEL_OFFSET __BITS(13,12) /* sun8i */ 107 1.1 jmcneill #define DA_CHSEL_EN __BITS(11,4) 108 1.1 jmcneill #define DA_CHSEL_SEL __BITS(2,0) 109 1.1 jmcneill 110 1.7 jmcneill enum sunxi_i2s_type { 111 1.7 jmcneill SUNXI_I2S_SUN4I, 112 1.7 jmcneill SUNXI_I2S_SUN8I, 113 1.7 jmcneill }; 114 1.7 jmcneill 115 1.1 jmcneill struct sunxi_i2s_config { 116 1.1 jmcneill const char *name; 117 1.7 jmcneill enum sunxi_i2s_type type; 118 1.1 jmcneill bus_size_t txchsel; 119 1.1 jmcneill bus_size_t txchmap; 120 1.1 jmcneill bus_size_t rxchsel; 121 1.1 jmcneill bus_size_t rxchmap; 122 1.1 jmcneill }; 123 1.1 jmcneill 124 1.1 jmcneill static const struct sunxi_i2s_config sun50i_a64_codec_config = { 125 1.1 jmcneill .name = "Audio Codec (digital part)", 126 1.7 jmcneill .type = SUNXI_I2S_SUN4I, 127 1.1 jmcneill .txchsel = 0x30, 128 1.1 jmcneill .txchmap = 0x34, 129 1.1 jmcneill .rxchsel = 0x38, 130 1.1 jmcneill .rxchmap = 0x3c, 131 1.1 jmcneill }; 132 1.1 jmcneill 133 1.7 jmcneill static const struct sunxi_i2s_config sun8i_h3_config = { 134 1.7 jmcneill .name = "I2S/PCM controller", 135 1.7 jmcneill .type = SUNXI_I2S_SUN8I, 136 1.7 jmcneill .txchsel = 0x34, 137 1.7 jmcneill .txchmap = 0x44, 138 1.7 jmcneill .rxchsel = 0x54, 139 1.7 jmcneill .rxchmap = 0x58, 140 1.7 jmcneill }; 141 1.7 jmcneill 142 1.9 thorpej static const struct device_compatible_entry compat_data[] = { 143 1.9 thorpej { .compat = "allwinner,sun50i-a64-codec-i2s", 144 1.9 thorpej .data = &sun50i_a64_codec_config }, 145 1.9 thorpej { .compat = "allwinner,sun8i-h3-i2s", 146 1.9 thorpej .data = &sun8i_h3_config }, 147 1.1 jmcneill 148 1.11 thorpej DEVICE_COMPAT_EOL 149 1.1 jmcneill }; 150 1.1 jmcneill 151 1.1 jmcneill struct sunxi_i2s_softc; 152 1.1 jmcneill 153 1.1 jmcneill struct sunxi_i2s_chan { 154 1.1 jmcneill struct sunxi_i2s_softc *ch_sc; 155 1.1 jmcneill u_int ch_mode; 156 1.1 jmcneill 157 1.1 jmcneill struct fdtbus_dma *ch_dma; 158 1.1 jmcneill struct fdtbus_dma_req ch_req; 159 1.1 jmcneill 160 1.1 jmcneill audio_params_t ch_params; 161 1.1 jmcneill 162 1.1 jmcneill bus_addr_t ch_start_phys; 163 1.1 jmcneill bus_addr_t ch_end_phys; 164 1.1 jmcneill bus_addr_t ch_cur_phys; 165 1.1 jmcneill int ch_blksize; 166 1.1 jmcneill 167 1.1 jmcneill void (*ch_intr)(void *); 168 1.1 jmcneill void *ch_intrarg; 169 1.1 jmcneill }; 170 1.1 jmcneill 171 1.1 jmcneill struct sunxi_i2s_dma { 172 1.1 jmcneill LIST_ENTRY(sunxi_i2s_dma) dma_list; 173 1.1 jmcneill bus_dmamap_t dma_map; 174 1.1 jmcneill void *dma_addr; 175 1.1 jmcneill size_t dma_size; 176 1.1 jmcneill bus_dma_segment_t dma_segs[1]; 177 1.1 jmcneill int dma_nsegs; 178 1.1 jmcneill }; 179 1.1 jmcneill 180 1.1 jmcneill struct sunxi_i2s_softc { 181 1.1 jmcneill device_t sc_dev; 182 1.1 jmcneill bus_space_tag_t sc_bst; 183 1.1 jmcneill bus_space_handle_t sc_bsh; 184 1.1 jmcneill bus_dma_tag_t sc_dmat; 185 1.1 jmcneill int sc_phandle; 186 1.1 jmcneill bus_addr_t sc_baseaddr; 187 1.7 jmcneill struct clk *sc_clk; 188 1.1 jmcneill 189 1.9 thorpej const struct sunxi_i2s_config *sc_cfg; 190 1.1 jmcneill 191 1.1 jmcneill LIST_HEAD(, sunxi_i2s_dma) sc_dmalist; 192 1.1 jmcneill 193 1.1 jmcneill kmutex_t sc_lock; 194 1.1 jmcneill kmutex_t sc_intr_lock; 195 1.1 jmcneill 196 1.1 jmcneill struct audio_format sc_format; 197 1.1 jmcneill 198 1.1 jmcneill struct sunxi_i2s_chan sc_pchan; 199 1.1 jmcneill struct sunxi_i2s_chan sc_rchan; 200 1.1 jmcneill 201 1.1 jmcneill struct audio_dai_device sc_dai; 202 1.1 jmcneill }; 203 1.1 jmcneill 204 1.7 jmcneill #define I2S_TYPE(sc) ((sc)->sc_cfg->type) 205 1.7 jmcneill 206 1.1 jmcneill #define I2S_READ(sc, reg) \ 207 1.1 jmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 208 1.1 jmcneill #define I2S_WRITE(sc, reg, val) \ 209 1.1 jmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 210 1.1 jmcneill 211 1.7 jmcneill static const u_int sun4i_i2s_bclk_divmap[] = { 212 1.7 jmcneill [0] = 2, 213 1.7 jmcneill [1] = 4, 214 1.7 jmcneill [2] = 6, 215 1.7 jmcneill [3] = 8, 216 1.7 jmcneill [4] = 12, 217 1.7 jmcneill [5] = 16, 218 1.7 jmcneill }; 219 1.7 jmcneill 220 1.7 jmcneill static const u_int sun4i_i2s_mclk_divmap[] = { 221 1.7 jmcneill [0] = 1, 222 1.7 jmcneill [1] = 2, 223 1.7 jmcneill [2] = 4, 224 1.7 jmcneill [3] = 6, 225 1.7 jmcneill [4] = 8, 226 1.7 jmcneill [5] = 12, 227 1.7 jmcneill [6] = 16, 228 1.7 jmcneill [7] = 24, 229 1.7 jmcneill }; 230 1.7 jmcneill 231 1.7 jmcneill static const u_int sun8i_i2s_divmap[] = { 232 1.7 jmcneill [1] = 1, 233 1.7 jmcneill [2] = 2, 234 1.7 jmcneill [3] = 4, 235 1.7 jmcneill [4] = 6, 236 1.7 jmcneill [5] = 8, 237 1.7 jmcneill [6] = 12, 238 1.7 jmcneill [7] = 16, 239 1.7 jmcneill [8] = 24, 240 1.7 jmcneill [9] = 32, 241 1.7 jmcneill [10] = 48, 242 1.7 jmcneill [11] = 64, 243 1.7 jmcneill [12] = 96, 244 1.7 jmcneill [13] = 128, 245 1.7 jmcneill [14] = 176, 246 1.7 jmcneill [15] = 192, 247 1.7 jmcneill }; 248 1.7 jmcneill 249 1.7 jmcneill static u_int 250 1.7 jmcneill sunxi_i2s_div_to_regval(const u_int *divmap, u_int divmaplen, u_int div) 251 1.7 jmcneill { 252 1.7 jmcneill u_int n; 253 1.7 jmcneill 254 1.7 jmcneill for (n = 0; n < divmaplen; n++) 255 1.7 jmcneill if (divmap[n] == div) 256 1.7 jmcneill return n; 257 1.7 jmcneill 258 1.7 jmcneill return -1; 259 1.7 jmcneill } 260 1.7 jmcneill 261 1.1 jmcneill static int 262 1.1 jmcneill sunxi_i2s_allocdma(struct sunxi_i2s_softc *sc, size_t size, 263 1.1 jmcneill size_t align, struct sunxi_i2s_dma *dma) 264 1.1 jmcneill { 265 1.1 jmcneill int error; 266 1.1 jmcneill 267 1.1 jmcneill dma->dma_size = size; 268 1.1 jmcneill error = bus_dmamem_alloc(sc->sc_dmat, dma->dma_size, align, 0, 269 1.1 jmcneill dma->dma_segs, 1, &dma->dma_nsegs, BUS_DMA_WAITOK); 270 1.1 jmcneill if (error) 271 1.1 jmcneill return error; 272 1.1 jmcneill 273 1.1 jmcneill error = bus_dmamem_map(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs, 274 1.1 jmcneill dma->dma_size, &dma->dma_addr, BUS_DMA_WAITOK | BUS_DMA_COHERENT); 275 1.1 jmcneill if (error) 276 1.1 jmcneill goto free; 277 1.1 jmcneill 278 1.1 jmcneill error = bus_dmamap_create(sc->sc_dmat, dma->dma_size, dma->dma_nsegs, 279 1.1 jmcneill dma->dma_size, 0, BUS_DMA_WAITOK, &dma->dma_map); 280 1.1 jmcneill if (error) 281 1.1 jmcneill goto unmap; 282 1.1 jmcneill 283 1.1 jmcneill error = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_addr, 284 1.1 jmcneill dma->dma_size, NULL, BUS_DMA_WAITOK); 285 1.1 jmcneill if (error) 286 1.1 jmcneill goto destroy; 287 1.1 jmcneill 288 1.1 jmcneill return 0; 289 1.1 jmcneill 290 1.1 jmcneill destroy: 291 1.1 jmcneill bus_dmamap_destroy(sc->sc_dmat, dma->dma_map); 292 1.1 jmcneill unmap: 293 1.1 jmcneill bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size); 294 1.1 jmcneill free: 295 1.1 jmcneill bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs); 296 1.1 jmcneill 297 1.1 jmcneill return error; 298 1.1 jmcneill } 299 1.1 jmcneill 300 1.1 jmcneill static void 301 1.1 jmcneill sunxi_i2s_freedma(struct sunxi_i2s_softc *sc, struct sunxi_i2s_dma *dma) 302 1.1 jmcneill { 303 1.1 jmcneill bus_dmamap_unload(sc->sc_dmat, dma->dma_map); 304 1.1 jmcneill bus_dmamap_destroy(sc->sc_dmat, dma->dma_map); 305 1.1 jmcneill bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size); 306 1.1 jmcneill bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs); 307 1.1 jmcneill } 308 1.1 jmcneill 309 1.1 jmcneill static int 310 1.1 jmcneill sunxi_i2s_transfer(struct sunxi_i2s_chan *ch) 311 1.1 jmcneill { 312 1.1 jmcneill bus_dma_segment_t seg; 313 1.1 jmcneill 314 1.1 jmcneill seg.ds_addr = ch->ch_cur_phys; 315 1.1 jmcneill seg.ds_len = ch->ch_blksize; 316 1.1 jmcneill ch->ch_req.dreq_segs = &seg; 317 1.1 jmcneill ch->ch_req.dreq_nsegs = 1; 318 1.1 jmcneill 319 1.1 jmcneill return fdtbus_dma_transfer(ch->ch_dma, &ch->ch_req); 320 1.1 jmcneill } 321 1.1 jmcneill 322 1.1 jmcneill static int 323 1.4 isaki sunxi_i2s_query_format(void *priv, audio_format_query_t *afp) 324 1.1 jmcneill { 325 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 326 1.1 jmcneill 327 1.4 isaki return audio_query_format(&sc->sc_format, 1, afp); 328 1.1 jmcneill } 329 1.1 jmcneill 330 1.1 jmcneill static int 331 1.4 isaki sunxi_i2s_set_format(void *priv, int setmode, 332 1.4 isaki const audio_params_t *play, const audio_params_t *rec, 333 1.4 isaki audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) 334 1.1 jmcneill { 335 1.1 jmcneill 336 1.1 jmcneill return 0; 337 1.1 jmcneill } 338 1.1 jmcneill 339 1.1 jmcneill static void * 340 1.1 jmcneill sunxi_i2s_allocm(void *priv, int dir, size_t size) 341 1.1 jmcneill { 342 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 343 1.1 jmcneill struct sunxi_i2s_dma *dma; 344 1.1 jmcneill int error; 345 1.1 jmcneill 346 1.1 jmcneill dma = kmem_alloc(sizeof(*dma), KM_SLEEP); 347 1.1 jmcneill 348 1.1 jmcneill error = sunxi_i2s_allocdma(sc, size, 16, dma); 349 1.1 jmcneill if (error) { 350 1.1 jmcneill kmem_free(dma, sizeof(*dma)); 351 1.1 jmcneill device_printf(sc->sc_dev, "couldn't allocate DMA memory (%d)\n", 352 1.1 jmcneill error); 353 1.1 jmcneill return NULL; 354 1.1 jmcneill } 355 1.1 jmcneill 356 1.1 jmcneill LIST_INSERT_HEAD(&sc->sc_dmalist, dma, dma_list); 357 1.1 jmcneill 358 1.1 jmcneill return dma->dma_addr; 359 1.1 jmcneill } 360 1.1 jmcneill 361 1.1 jmcneill static void 362 1.1 jmcneill sunxi_i2s_freem(void *priv, void *addr, size_t size) 363 1.1 jmcneill { 364 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 365 1.1 jmcneill struct sunxi_i2s_dma *dma; 366 1.1 jmcneill 367 1.1 jmcneill LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) 368 1.1 jmcneill if (dma->dma_addr == addr) { 369 1.1 jmcneill sunxi_i2s_freedma(sc, dma); 370 1.1 jmcneill LIST_REMOVE(dma, dma_list); 371 1.1 jmcneill kmem_free(dma, sizeof(*dma)); 372 1.1 jmcneill break; 373 1.1 jmcneill } 374 1.1 jmcneill } 375 1.1 jmcneill 376 1.1 jmcneill static int 377 1.1 jmcneill sunxi_i2s_get_props(void *priv) 378 1.1 jmcneill { 379 1.7 jmcneill struct sunxi_i2s_softc * const sc = priv; 380 1.7 jmcneill int props = 0; 381 1.6 isaki 382 1.7 jmcneill if (sc->sc_pchan.ch_dma != NULL) 383 1.7 jmcneill props |= AUDIO_PROP_PLAYBACK; 384 1.7 jmcneill if (sc->sc_rchan.ch_dma != NULL) 385 1.7 jmcneill props |= AUDIO_PROP_CAPTURE; 386 1.7 jmcneill if (sc->sc_pchan.ch_dma != NULL && sc->sc_rchan.ch_dma != NULL) 387 1.7 jmcneill props |= AUDIO_PROP_FULLDUPLEX; 388 1.7 jmcneill 389 1.7 jmcneill return props; 390 1.1 jmcneill } 391 1.1 jmcneill 392 1.1 jmcneill static int 393 1.1 jmcneill sunxi_i2s_trigger_output(void *priv, void *start, void *end, int blksize, 394 1.1 jmcneill void (*intr)(void *), void *intrarg, const audio_params_t *params) 395 1.1 jmcneill { 396 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 397 1.1 jmcneill struct sunxi_i2s_chan *ch = &sc->sc_pchan; 398 1.1 jmcneill struct sunxi_i2s_dma *dma; 399 1.1 jmcneill bus_addr_t pstart; 400 1.1 jmcneill bus_size_t psize; 401 1.1 jmcneill uint32_t val; 402 1.1 jmcneill int error; 403 1.1 jmcneill 404 1.7 jmcneill if (ch->ch_dma == NULL) 405 1.7 jmcneill return EIO; 406 1.7 jmcneill 407 1.1 jmcneill pstart = 0; 408 1.1 jmcneill psize = (uintptr_t)end - (uintptr_t)start; 409 1.1 jmcneill 410 1.1 jmcneill LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) 411 1.1 jmcneill if (dma->dma_addr == start) { 412 1.1 jmcneill pstart = dma->dma_map->dm_segs[0].ds_addr; 413 1.1 jmcneill break; 414 1.1 jmcneill } 415 1.1 jmcneill if (pstart == 0) { 416 1.1 jmcneill device_printf(sc->sc_dev, "bad addr %p\n", start); 417 1.1 jmcneill return EINVAL; 418 1.1 jmcneill } 419 1.1 jmcneill 420 1.1 jmcneill ch->ch_intr = intr; 421 1.1 jmcneill ch->ch_intrarg = intrarg; 422 1.1 jmcneill ch->ch_start_phys = ch->ch_cur_phys = pstart; 423 1.1 jmcneill ch->ch_end_phys = pstart + psize; 424 1.1 jmcneill ch->ch_blksize = blksize; 425 1.1 jmcneill 426 1.1 jmcneill /* Flush FIFO */ 427 1.1 jmcneill val = I2S_READ(sc, DA_FCTL); 428 1.1 jmcneill I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FTX); 429 1.1 jmcneill I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FTX); 430 1.1 jmcneill 431 1.1 jmcneill /* Reset TX sample counter */ 432 1.1 jmcneill I2S_WRITE(sc, DA_TXCNT, 0); 433 1.1 jmcneill 434 1.1 jmcneill /* Enable transmitter block */ 435 1.1 jmcneill val = I2S_READ(sc, DA_CTL); 436 1.1 jmcneill I2S_WRITE(sc, DA_CTL, val | DA_CTL_TXEN); 437 1.1 jmcneill 438 1.1 jmcneill /* Enable TX DRQ */ 439 1.1 jmcneill val = I2S_READ(sc, DA_INT); 440 1.1 jmcneill I2S_WRITE(sc, DA_INT, val | DA_INT_TX_DRQ); 441 1.1 jmcneill 442 1.1 jmcneill /* Start DMA transfer */ 443 1.1 jmcneill error = sunxi_i2s_transfer(ch); 444 1.1 jmcneill if (error != 0) { 445 1.1 jmcneill aprint_error_dev(sc->sc_dev, 446 1.1 jmcneill "failed to start DMA transfer: %d\n", error); 447 1.1 jmcneill return error; 448 1.1 jmcneill } 449 1.1 jmcneill 450 1.1 jmcneill return 0; 451 1.1 jmcneill } 452 1.1 jmcneill 453 1.1 jmcneill static int 454 1.1 jmcneill sunxi_i2s_trigger_input(void *priv, void *start, void *end, int blksize, 455 1.1 jmcneill void (*intr)(void *), void *intrarg, const audio_params_t *params) 456 1.1 jmcneill { 457 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 458 1.1 jmcneill struct sunxi_i2s_chan *ch = &sc->sc_rchan; 459 1.1 jmcneill struct sunxi_i2s_dma *dma; 460 1.1 jmcneill bus_addr_t pstart; 461 1.1 jmcneill bus_size_t psize; 462 1.1 jmcneill uint32_t val; 463 1.1 jmcneill int error; 464 1.1 jmcneill 465 1.7 jmcneill if (ch->ch_dma == NULL) 466 1.7 jmcneill return EIO; 467 1.7 jmcneill 468 1.1 jmcneill pstart = 0; 469 1.1 jmcneill psize = (uintptr_t)end - (uintptr_t)start; 470 1.1 jmcneill 471 1.1 jmcneill LIST_FOREACH(dma, &sc->sc_dmalist, dma_list) 472 1.1 jmcneill if (dma->dma_addr == start) { 473 1.1 jmcneill pstart = dma->dma_map->dm_segs[0].ds_addr; 474 1.1 jmcneill break; 475 1.1 jmcneill } 476 1.1 jmcneill if (pstart == 0) { 477 1.1 jmcneill device_printf(sc->sc_dev, "bad addr %p\n", start); 478 1.1 jmcneill return EINVAL; 479 1.1 jmcneill } 480 1.1 jmcneill 481 1.1 jmcneill ch->ch_intr = intr; 482 1.1 jmcneill ch->ch_intrarg = intrarg; 483 1.1 jmcneill ch->ch_start_phys = ch->ch_cur_phys = pstart; 484 1.1 jmcneill ch->ch_end_phys = pstart + psize; 485 1.1 jmcneill ch->ch_blksize = blksize; 486 1.1 jmcneill 487 1.1 jmcneill /* Flush FIFO */ 488 1.1 jmcneill val = I2S_READ(sc, DA_FCTL); 489 1.1 jmcneill I2S_WRITE(sc, DA_FCTL, val | DA_FCTL_FRX); 490 1.1 jmcneill I2S_WRITE(sc, DA_FCTL, val & ~DA_FCTL_FRX); 491 1.1 jmcneill 492 1.1 jmcneill /* Reset RX sample counter */ 493 1.1 jmcneill I2S_WRITE(sc, DA_RXCNT, 0); 494 1.1 jmcneill 495 1.1 jmcneill /* Enable receiver block */ 496 1.1 jmcneill val = I2S_READ(sc, DA_CTL); 497 1.1 jmcneill I2S_WRITE(sc, DA_CTL, val | DA_CTL_RXEN); 498 1.1 jmcneill 499 1.1 jmcneill /* Enable RX DRQ */ 500 1.1 jmcneill val = I2S_READ(sc, DA_INT); 501 1.1 jmcneill I2S_WRITE(sc, DA_INT, val | DA_INT_RX_DRQ); 502 1.1 jmcneill 503 1.1 jmcneill /* Start DMA transfer */ 504 1.1 jmcneill error = sunxi_i2s_transfer(ch); 505 1.1 jmcneill if (error != 0) { 506 1.1 jmcneill aprint_error_dev(sc->sc_dev, 507 1.1 jmcneill "failed to start DMA transfer: %d\n", error); 508 1.1 jmcneill return error; 509 1.1 jmcneill } 510 1.1 jmcneill 511 1.1 jmcneill return 0; 512 1.1 jmcneill } 513 1.1 jmcneill 514 1.1 jmcneill static int 515 1.1 jmcneill sunxi_i2s_halt_output(void *priv) 516 1.1 jmcneill { 517 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 518 1.1 jmcneill struct sunxi_i2s_chan *ch = &sc->sc_pchan; 519 1.1 jmcneill uint32_t val; 520 1.1 jmcneill 521 1.7 jmcneill if (ch->ch_dma == NULL) 522 1.7 jmcneill return EIO; 523 1.7 jmcneill 524 1.1 jmcneill /* Disable DMA channel */ 525 1.1 jmcneill fdtbus_dma_halt(ch->ch_dma); 526 1.1 jmcneill 527 1.1 jmcneill /* Disable transmitter block */ 528 1.1 jmcneill val = I2S_READ(sc, DA_CTL); 529 1.1 jmcneill I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_TXEN); 530 1.1 jmcneill 531 1.1 jmcneill /* Disable TX DRQ */ 532 1.1 jmcneill val = I2S_READ(sc, DA_INT); 533 1.1 jmcneill I2S_WRITE(sc, DA_INT, val & ~DA_INT_TX_DRQ); 534 1.1 jmcneill 535 1.1 jmcneill ch->ch_intr = NULL; 536 1.1 jmcneill ch->ch_intrarg = NULL; 537 1.1 jmcneill 538 1.1 jmcneill return 0; 539 1.1 jmcneill } 540 1.1 jmcneill 541 1.1 jmcneill static int 542 1.1 jmcneill sunxi_i2s_halt_input(void *priv) 543 1.1 jmcneill { 544 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 545 1.1 jmcneill struct sunxi_i2s_chan *ch = &sc->sc_rchan; 546 1.1 jmcneill uint32_t val; 547 1.1 jmcneill 548 1.7 jmcneill if (ch->ch_dma == NULL) 549 1.7 jmcneill return EIO; 550 1.7 jmcneill 551 1.1 jmcneill /* Disable DMA channel */ 552 1.1 jmcneill fdtbus_dma_halt(ch->ch_dma); 553 1.1 jmcneill 554 1.1 jmcneill /* Disable receiver block */ 555 1.1 jmcneill val = I2S_READ(sc, DA_CTL); 556 1.1 jmcneill I2S_WRITE(sc, DA_CTL, val & ~DA_CTL_RXEN); 557 1.1 jmcneill 558 1.1 jmcneill /* Disable RX DRQ */ 559 1.1 jmcneill val = I2S_READ(sc, DA_INT); 560 1.1 jmcneill I2S_WRITE(sc, DA_INT, val & ~DA_INT_RX_DRQ); 561 1.1 jmcneill 562 1.1 jmcneill return 0; 563 1.1 jmcneill } 564 1.1 jmcneill 565 1.1 jmcneill static void 566 1.1 jmcneill sunxi_i2s_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread) 567 1.1 jmcneill { 568 1.1 jmcneill struct sunxi_i2s_softc * const sc = priv; 569 1.1 jmcneill 570 1.1 jmcneill *intr = &sc->sc_intr_lock; 571 1.1 jmcneill *thread = &sc->sc_lock; 572 1.1 jmcneill } 573 1.1 jmcneill 574 1.1 jmcneill static const struct audio_hw_if sunxi_i2s_hw_if = { 575 1.4 isaki .query_format = sunxi_i2s_query_format, 576 1.4 isaki .set_format = sunxi_i2s_set_format, 577 1.1 jmcneill .allocm = sunxi_i2s_allocm, 578 1.1 jmcneill .freem = sunxi_i2s_freem, 579 1.1 jmcneill .get_props = sunxi_i2s_get_props, 580 1.1 jmcneill .trigger_output = sunxi_i2s_trigger_output, 581 1.1 jmcneill .trigger_input = sunxi_i2s_trigger_input, 582 1.1 jmcneill .halt_output = sunxi_i2s_halt_output, 583 1.1 jmcneill .halt_input = sunxi_i2s_halt_input, 584 1.1 jmcneill .get_locks = sunxi_i2s_get_locks, 585 1.1 jmcneill }; 586 1.1 jmcneill 587 1.1 jmcneill static void 588 1.1 jmcneill sunxi_i2s_dmaintr(void *priv) 589 1.1 jmcneill { 590 1.1 jmcneill struct sunxi_i2s_chan * const ch = priv; 591 1.1 jmcneill struct sunxi_i2s_softc * const sc = ch->ch_sc; 592 1.1 jmcneill 593 1.1 jmcneill mutex_enter(&sc->sc_intr_lock); 594 1.1 jmcneill ch->ch_cur_phys += ch->ch_blksize; 595 1.1 jmcneill if (ch->ch_cur_phys >= ch->ch_end_phys) 596 1.1 jmcneill ch->ch_cur_phys = ch->ch_start_phys; 597 1.1 jmcneill 598 1.1 jmcneill if (ch->ch_intr) { 599 1.1 jmcneill ch->ch_intr(ch->ch_intrarg); 600 1.1 jmcneill sunxi_i2s_transfer(ch); 601 1.1 jmcneill } 602 1.1 jmcneill mutex_exit(&sc->sc_intr_lock); 603 1.1 jmcneill } 604 1.1 jmcneill 605 1.1 jmcneill static int 606 1.1 jmcneill sunxi_i2s_chan_init(struct sunxi_i2s_softc *sc, 607 1.1 jmcneill struct sunxi_i2s_chan *ch, u_int mode, const char *dmaname) 608 1.1 jmcneill { 609 1.1 jmcneill ch->ch_sc = sc; 610 1.1 jmcneill ch->ch_mode = mode; 611 1.1 jmcneill ch->ch_dma = fdtbus_dma_get(sc->sc_phandle, dmaname, sunxi_i2s_dmaintr, ch); 612 1.7 jmcneill if (ch->ch_dma == NULL) 613 1.1 jmcneill return ENXIO; 614 1.1 jmcneill 615 1.1 jmcneill if (mode == AUMODE_PLAY) { 616 1.1 jmcneill ch->ch_req.dreq_dir = FDT_DMA_WRITE; 617 1.1 jmcneill ch->ch_req.dreq_dev_phys = 618 1.1 jmcneill sc->sc_baseaddr + DA_TXFIFO; 619 1.1 jmcneill } else { 620 1.1 jmcneill ch->ch_req.dreq_dir = FDT_DMA_READ; 621 1.1 jmcneill ch->ch_req.dreq_dev_phys = 622 1.1 jmcneill sc->sc_baseaddr + DA_RXFIFO; 623 1.1 jmcneill } 624 1.7 jmcneill ch->ch_req.dreq_mem_opt.opt_bus_width = 16; 625 1.1 jmcneill ch->ch_req.dreq_mem_opt.opt_burst_len = 8; 626 1.7 jmcneill ch->ch_req.dreq_dev_opt.opt_bus_width = 16; 627 1.1 jmcneill ch->ch_req.dreq_dev_opt.opt_burst_len = 8; 628 1.1 jmcneill 629 1.1 jmcneill return 0; 630 1.1 jmcneill } 631 1.1 jmcneill 632 1.1 jmcneill static int 633 1.1 jmcneill sunxi_i2s_dai_set_sysclk(audio_dai_tag_t dai, u_int rate, int dir) 634 1.1 jmcneill { 635 1.1 jmcneill struct sunxi_i2s_softc * const sc = audio_dai_private(dai); 636 1.7 jmcneill int bclk_val, mclk_val; 637 1.1 jmcneill uint32_t val; 638 1.7 jmcneill int error; 639 1.7 jmcneill 640 1.7 jmcneill error = clk_set_rate(sc->sc_clk, SUNXI_I2S_CLK_RATE); 641 1.7 jmcneill if (error != 0) { 642 1.7 jmcneill aprint_error_dev(sc->sc_dev, 643 1.7 jmcneill "couldn't set mod clock rate to %u Hz: %d\n", SUNXI_I2S_CLK_RATE, error); 644 1.7 jmcneill return error; 645 1.7 jmcneill } 646 1.7 jmcneill error = clk_enable(sc->sc_clk); 647 1.7 jmcneill if (error != 0) { 648 1.7 jmcneill aprint_error_dev(sc->sc_dev, 649 1.7 jmcneill "couldn't enable mod clock: %d\n", error); 650 1.7 jmcneill return error; 651 1.7 jmcneill } 652 1.1 jmcneill 653 1.7 jmcneill const u_int bclk_prate = I2S_TYPE(sc) == SUNXI_I2S_SUN4I ? rate : SUNXI_I2S_CLK_RATE; 654 1.1 jmcneill 655 1.7 jmcneill const u_int bclk_div = bclk_prate / (2 * 32 * SUNXI_I2S_SAMPLE_RATE); 656 1.7 jmcneill const u_int mclk_div = SUNXI_I2S_CLK_RATE / rate; 657 1.1 jmcneill 658 1.7 jmcneill if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { 659 1.7 jmcneill bclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_bclk_divmap, 660 1.7 jmcneill __arraycount(sun4i_i2s_bclk_divmap), bclk_div); 661 1.7 jmcneill mclk_val = sunxi_i2s_div_to_regval(sun4i_i2s_mclk_divmap, 662 1.7 jmcneill __arraycount(sun4i_i2s_mclk_divmap), mclk_div); 663 1.7 jmcneill } else { 664 1.7 jmcneill bclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap, 665 1.7 jmcneill __arraycount(sun8i_i2s_divmap), bclk_div); 666 1.7 jmcneill mclk_val = sunxi_i2s_div_to_regval(sun8i_i2s_divmap, 667 1.7 jmcneill __arraycount(sun8i_i2s_divmap), mclk_div); 668 1.7 jmcneill } 669 1.7 jmcneill if (bclk_val == -1 || mclk_val == -1) { 670 1.7 jmcneill aprint_error_dev(sc->sc_dev, "couldn't configure bclk/mclk dividers\n"); 671 1.7 jmcneill return EIO; 672 1.7 jmcneill } 673 1.7 jmcneill 674 1.7 jmcneill val = I2S_READ(sc, DA_CLKD); 675 1.7 jmcneill if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { 676 1.7 jmcneill val |= DA_CLKD_MCLKO_EN_SUN4I; 677 1.7 jmcneill val &= ~DA_CLKD_BCLKDIV_SUN4I; 678 1.7 jmcneill val |= __SHIFTIN(bclk_val, DA_CLKD_BCLKDIV_SUN4I); 679 1.7 jmcneill } else { 680 1.7 jmcneill val |= DA_CLKD_MCLKO_EN_SUN8I; 681 1.7 jmcneill val &= ~DA_CLKD_BCLKDIV_SUN8I; 682 1.7 jmcneill val |= __SHIFTIN(bclk_val, DA_CLKD_BCLKDIV_SUN8I); 683 1.7 jmcneill } 684 1.7 jmcneill val &= ~DA_CLKD_MCLKDIV; 685 1.7 jmcneill val |= __SHIFTIN(mclk_val, DA_CLKD_MCLKDIV); 686 1.1 jmcneill I2S_WRITE(sc, DA_CLKD, val); 687 1.1 jmcneill 688 1.1 jmcneill return 0; 689 1.1 jmcneill } 690 1.1 jmcneill 691 1.1 jmcneill static int 692 1.1 jmcneill sunxi_i2s_dai_set_format(audio_dai_tag_t dai, u_int format) 693 1.1 jmcneill { 694 1.1 jmcneill struct sunxi_i2s_softc * const sc = audio_dai_private(dai); 695 1.7 jmcneill uint32_t ctl, fat0, chsel; 696 1.7 jmcneill u_int offset; 697 1.1 jmcneill 698 1.1 jmcneill const u_int fmt = __SHIFTOUT(format, AUDIO_DAI_FORMAT_MASK); 699 1.1 jmcneill const u_int pol = __SHIFTOUT(format, AUDIO_DAI_POLARITY_MASK); 700 1.1 jmcneill const u_int clk = __SHIFTOUT(format, AUDIO_DAI_CLOCK_MASK); 701 1.1 jmcneill 702 1.1 jmcneill ctl = I2S_READ(sc, DA_CTL); 703 1.1 jmcneill fat0 = I2S_READ(sc, DA_FAT0); 704 1.1 jmcneill 705 1.7 jmcneill if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { 706 1.7 jmcneill fat0 &= ~DA_FAT0_FMT; 707 1.7 jmcneill switch (fmt) { 708 1.7 jmcneill case AUDIO_DAI_FORMAT_I2S: 709 1.7 jmcneill fat0 |= __SHIFTIN(DA_FMT_I2S, DA_FAT0_FMT); 710 1.7 jmcneill break; 711 1.7 jmcneill case AUDIO_DAI_FORMAT_RJ: 712 1.7 jmcneill fat0 |= __SHIFTIN(DA_FMT_RJ, DA_FAT0_FMT); 713 1.7 jmcneill break; 714 1.7 jmcneill case AUDIO_DAI_FORMAT_LJ: 715 1.7 jmcneill fat0 |= __SHIFTIN(DA_FMT_LJ, DA_FAT0_FMT); 716 1.7 jmcneill break; 717 1.7 jmcneill default: 718 1.7 jmcneill return EINVAL; 719 1.7 jmcneill } 720 1.7 jmcneill ctl &= ~DA_CTL_PCM; 721 1.7 jmcneill } else { 722 1.7 jmcneill ctl &= ~DA_CTL_MODE_SEL; 723 1.7 jmcneill switch (fmt) { 724 1.7 jmcneill case AUDIO_DAI_FORMAT_I2S: 725 1.7 jmcneill ctl |= __SHIFTIN(DA_CTL_MODE_SEL_LJ, DA_CTL_MODE_SEL); 726 1.7 jmcneill offset = 1; 727 1.7 jmcneill break; 728 1.7 jmcneill case AUDIO_DAI_FORMAT_LJ: 729 1.7 jmcneill ctl |= __SHIFTIN(DA_CTL_MODE_SEL_LJ, DA_CTL_MODE_SEL); 730 1.7 jmcneill offset = 0; 731 1.7 jmcneill break; 732 1.7 jmcneill case AUDIO_DAI_FORMAT_RJ: 733 1.7 jmcneill ctl |= __SHIFTIN(DA_CTL_MODE_SEL_RJ, DA_CTL_MODE_SEL); 734 1.7 jmcneill offset = 0; 735 1.7 jmcneill break; 736 1.7 jmcneill case AUDIO_DAI_FORMAT_DSPA: 737 1.7 jmcneill ctl |= __SHIFTIN(DA_CTL_MODE_SEL_PCM, DA_CTL_MODE_SEL); 738 1.7 jmcneill offset = 1; 739 1.7 jmcneill break; 740 1.7 jmcneill case AUDIO_DAI_FORMAT_DSPB: 741 1.7 jmcneill ctl |= __SHIFTIN(DA_CTL_MODE_SEL_PCM, DA_CTL_MODE_SEL); 742 1.7 jmcneill offset = 0; 743 1.7 jmcneill break; 744 1.7 jmcneill default: 745 1.7 jmcneill return EINVAL; 746 1.7 jmcneill } 747 1.7 jmcneill 748 1.7 jmcneill chsel = I2S_READ(sc, sc->sc_cfg->txchsel); 749 1.7 jmcneill chsel &= ~DA_CHSEL_OFFSET; 750 1.7 jmcneill chsel |= __SHIFTIN(offset, DA_CHSEL_OFFSET); 751 1.7 jmcneill I2S_WRITE(sc, sc->sc_cfg->txchsel, chsel); 752 1.7 jmcneill 753 1.7 jmcneill chsel = I2S_READ(sc, sc->sc_cfg->rxchsel); 754 1.7 jmcneill chsel &= ~DA_CHSEL_OFFSET; 755 1.7 jmcneill chsel |= __SHIFTIN(offset, DA_CHSEL_OFFSET); 756 1.7 jmcneill I2S_WRITE(sc, sc->sc_cfg->rxchsel, chsel); 757 1.1 jmcneill } 758 1.1 jmcneill 759 1.1 jmcneill fat0 &= ~(DA_FAT0_LRCP|DA_FAT0_BCP); 760 1.7 jmcneill if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { 761 1.7 jmcneill if (AUDIO_DAI_POLARITY_B(pol)) 762 1.7 jmcneill fat0 |= __SHIFTIN(DA_BCP_INVERTED, DA_FAT0_BCP); 763 1.7 jmcneill if (AUDIO_DAI_POLARITY_F(pol)) 764 1.7 jmcneill fat0 |= __SHIFTIN(DA_LRCP_INVERTED, DA_FAT0_LRCP); 765 1.7 jmcneill } else { 766 1.7 jmcneill if (AUDIO_DAI_POLARITY_B(pol)) 767 1.7 jmcneill fat0 |= __SHIFTIN(DA_BCP_INVERTED, DA_FAT0_BCP); 768 1.7 jmcneill if (!AUDIO_DAI_POLARITY_F(pol)) 769 1.7 jmcneill fat0 |= __SHIFTIN(DA_LRCP_INVERTED, DA_FAT0_LRCP); 770 1.7 jmcneill 771 1.7 jmcneill fat0 &= ~DA_FAT0_LRCK_PERIOD; 772 1.7 jmcneill fat0 |= __SHIFTIN(32 - 1, DA_FAT0_LRCK_PERIOD); 773 1.7 jmcneill } 774 1.1 jmcneill 775 1.1 jmcneill switch (clk) { 776 1.1 jmcneill case AUDIO_DAI_CLOCK_CBM_CFM: 777 1.7 jmcneill if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { 778 1.7 jmcneill ctl |= DA_CTL_MS; /* codec is master */ 779 1.7 jmcneill } else { 780 1.7 jmcneill ctl &= ~DA_CTL_BCLK_OUT; 781 1.7 jmcneill ctl &= ~DA_CLK_LRCK_OUT; 782 1.7 jmcneill } 783 1.1 jmcneill break; 784 1.1 jmcneill case AUDIO_DAI_CLOCK_CBS_CFS: 785 1.7 jmcneill if (I2S_TYPE(sc) == SUNXI_I2S_SUN4I) { 786 1.7 jmcneill ctl &= ~DA_CTL_MS; /* codec is slave */ 787 1.7 jmcneill } else { 788 1.7 jmcneill ctl |= DA_CTL_BCLK_OUT; 789 1.7 jmcneill ctl |= DA_CLK_LRCK_OUT; 790 1.7 jmcneill } 791 1.1 jmcneill break; 792 1.1 jmcneill default: 793 1.1 jmcneill return EINVAL; 794 1.1 jmcneill } 795 1.1 jmcneill 796 1.1 jmcneill I2S_WRITE(sc, DA_CTL, ctl); 797 1.1 jmcneill I2S_WRITE(sc, DA_FAT0, fat0); 798 1.1 jmcneill 799 1.1 jmcneill return 0; 800 1.1 jmcneill } 801 1.1 jmcneill 802 1.1 jmcneill static audio_dai_tag_t 803 1.1 jmcneill sunxi_i2s_dai_get_tag(device_t dev, const void *data, size_t len) 804 1.1 jmcneill { 805 1.1 jmcneill struct sunxi_i2s_softc * const sc = device_private(dev); 806 1.1 jmcneill 807 1.1 jmcneill if (len != 4) 808 1.1 jmcneill return NULL; 809 1.1 jmcneill 810 1.1 jmcneill return &sc->sc_dai; 811 1.1 jmcneill } 812 1.1 jmcneill 813 1.1 jmcneill static struct fdtbus_dai_controller_func sunxi_i2s_dai_funcs = { 814 1.1 jmcneill .get_tag = sunxi_i2s_dai_get_tag 815 1.1 jmcneill }; 816 1.1 jmcneill 817 1.1 jmcneill static int 818 1.7 jmcneill sunxi_i2s_clock_init(struct sunxi_i2s_softc *sc) 819 1.1 jmcneill { 820 1.7 jmcneill const int phandle = sc->sc_phandle; 821 1.1 jmcneill struct fdtbus_reset *rst; 822 1.1 jmcneill struct clk *clk; 823 1.1 jmcneill int error; 824 1.1 jmcneill 825 1.7 jmcneill sc->sc_clk = fdtbus_clock_get(phandle, "mod"); 826 1.7 jmcneill if (sc->sc_clk == NULL) { 827 1.1 jmcneill aprint_error(": couldn't find mod clock\n"); 828 1.1 jmcneill return ENXIO; 829 1.1 jmcneill } 830 1.1 jmcneill 831 1.1 jmcneill /* Enable APB clock */ 832 1.1 jmcneill clk = fdtbus_clock_get(phandle, "apb"); 833 1.1 jmcneill if (clk == NULL) { 834 1.1 jmcneill aprint_error(": couldn't find apb clock\n"); 835 1.1 jmcneill return ENXIO; 836 1.1 jmcneill } 837 1.1 jmcneill error = clk_enable(clk); 838 1.1 jmcneill if (error != 0) { 839 1.1 jmcneill aprint_error(": couldn't enable apb clock: %d\n", error); 840 1.1 jmcneill return error; 841 1.1 jmcneill } 842 1.1 jmcneill 843 1.1 jmcneill /* De-assert reset */ 844 1.7 jmcneill rst = fdtbus_reset_get_index(phandle, 0); 845 1.1 jmcneill if (rst == NULL) { 846 1.1 jmcneill aprint_error(": couldn't find reset\n"); 847 1.1 jmcneill return ENXIO; 848 1.1 jmcneill } 849 1.1 jmcneill error = fdtbus_reset_deassert(rst); 850 1.1 jmcneill if (error != 0) { 851 1.1 jmcneill aprint_error(": couldn't de-assert reset: %d\n", error); 852 1.1 jmcneill return error; 853 1.1 jmcneill } 854 1.1 jmcneill 855 1.1 jmcneill return 0; 856 1.1 jmcneill } 857 1.1 jmcneill 858 1.1 jmcneill static int 859 1.1 jmcneill sunxi_i2s_match(device_t parent, cfdata_t cf, void *aux) 860 1.1 jmcneill { 861 1.1 jmcneill struct fdt_attach_args * const faa = aux; 862 1.1 jmcneill 863 1.12 thorpej return of_compatible_match(faa->faa_phandle, compat_data); 864 1.1 jmcneill } 865 1.1 jmcneill 866 1.1 jmcneill static void 867 1.1 jmcneill sunxi_i2s_attach(device_t parent, device_t self, void *aux) 868 1.1 jmcneill { 869 1.1 jmcneill struct sunxi_i2s_softc * const sc = device_private(self); 870 1.1 jmcneill struct fdt_attach_args * const faa = aux; 871 1.1 jmcneill const int phandle = faa->faa_phandle; 872 1.1 jmcneill bus_addr_t addr; 873 1.1 jmcneill bus_size_t size; 874 1.1 jmcneill uint32_t val; 875 1.1 jmcneill 876 1.1 jmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 877 1.1 jmcneill aprint_error(": couldn't get registers\n"); 878 1.1 jmcneill return; 879 1.1 jmcneill } 880 1.1 jmcneill 881 1.1 jmcneill sc->sc_dev = self; 882 1.1 jmcneill sc->sc_phandle = phandle; 883 1.1 jmcneill sc->sc_baseaddr = addr; 884 1.1 jmcneill sc->sc_bst = faa->faa_bst; 885 1.1 jmcneill if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 886 1.1 jmcneill aprint_error(": couldn't map registers\n"); 887 1.1 jmcneill return; 888 1.1 jmcneill } 889 1.1 jmcneill sc->sc_dmat = faa->faa_dmat; 890 1.1 jmcneill LIST_INIT(&sc->sc_dmalist); 891 1.12 thorpej sc->sc_cfg = of_compatible_lookup(phandle, compat_data)->data; 892 1.1 jmcneill mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 893 1.1 jmcneill mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); 894 1.1 jmcneill 895 1.7 jmcneill if (sunxi_i2s_clock_init(sc) != 0) 896 1.7 jmcneill return; 897 1.7 jmcneill 898 1.7 jmcneill /* At least one of these needs to succeed */ 899 1.7 jmcneill sunxi_i2s_chan_init(sc, &sc->sc_pchan, AUMODE_PLAY, "tx"); 900 1.7 jmcneill sunxi_i2s_chan_init(sc, &sc->sc_rchan, AUMODE_RECORD, "rx"); 901 1.7 jmcneill if (sc->sc_pchan.ch_dma == NULL && sc->sc_rchan.ch_dma == NULL) { 902 1.1 jmcneill aprint_error(": couldn't setup channels\n"); 903 1.1 jmcneill return; 904 1.1 jmcneill } 905 1.1 jmcneill 906 1.1 jmcneill aprint_naive("\n"); 907 1.1 jmcneill aprint_normal(": %s\n", sc->sc_cfg->name); 908 1.1 jmcneill 909 1.1 jmcneill /* Reset */ 910 1.1 jmcneill val = I2S_READ(sc, DA_CTL); 911 1.1 jmcneill val &= ~(DA_CTL_TXEN|DA_CTL_RXEN|DA_CTL_GEN); 912 1.1 jmcneill I2S_WRITE(sc, DA_CTL, val); 913 1.1 jmcneill 914 1.1 jmcneill val = I2S_READ(sc, DA_FCTL); 915 1.1 jmcneill val &= ~(DA_FCTL_FTX|DA_FCTL_FRX); 916 1.1 jmcneill I2S_WRITE(sc, DA_FCTL, val); 917 1.1 jmcneill 918 1.1 jmcneill I2S_WRITE(sc, DA_TXCNT, 0); 919 1.1 jmcneill I2S_WRITE(sc, DA_RXCNT, 0); 920 1.1 jmcneill 921 1.1 jmcneill /* Enable */ 922 1.7 jmcneill val = I2S_READ(sc, DA_CTL); 923 1.7 jmcneill val |= DA_CTL_GEN; 924 1.7 jmcneill I2S_WRITE(sc, DA_CTL, val); 925 1.7 jmcneill val |= DA_CTL_SDO_EN; 926 1.7 jmcneill I2S_WRITE(sc, DA_CTL, val); 927 1.1 jmcneill 928 1.1 jmcneill /* Setup channels */ 929 1.1 jmcneill I2S_WRITE(sc, sc->sc_cfg->txchmap, 0x76543210); 930 1.7 jmcneill val = I2S_READ(sc, sc->sc_cfg->txchsel); 931 1.7 jmcneill val &= ~DA_CHSEL_EN; 932 1.7 jmcneill val |= __SHIFTIN(3, DA_CHSEL_EN); 933 1.7 jmcneill val &= ~DA_CHSEL_SEL; 934 1.7 jmcneill val |= __SHIFTIN(1, DA_CHSEL_SEL); 935 1.7 jmcneill I2S_WRITE(sc, sc->sc_cfg->txchsel, val); 936 1.1 jmcneill I2S_WRITE(sc, sc->sc_cfg->rxchmap, 0x76543210); 937 1.7 jmcneill val = I2S_READ(sc, sc->sc_cfg->rxchsel); 938 1.7 jmcneill val &= ~DA_CHSEL_EN; 939 1.7 jmcneill val |= __SHIFTIN(3, DA_CHSEL_EN); 940 1.7 jmcneill val &= ~DA_CHSEL_SEL; 941 1.7 jmcneill val |= __SHIFTIN(1, DA_CHSEL_SEL); 942 1.7 jmcneill I2S_WRITE(sc, sc->sc_cfg->rxchsel, val); 943 1.7 jmcneill 944 1.7 jmcneill if (I2S_TYPE(sc) == SUNXI_I2S_SUN8I) { 945 1.7 jmcneill val = I2S_READ(sc, DA_CHCFG); 946 1.7 jmcneill val &= ~DA_CHCFG_TX_SLOT_NUM; 947 1.7 jmcneill val |= __SHIFTIN(1, DA_CHCFG_TX_SLOT_NUM); 948 1.7 jmcneill val &= ~DA_CHCFG_RX_SLOT_NUM; 949 1.7 jmcneill val |= __SHIFTIN(1, DA_CHCFG_RX_SLOT_NUM); 950 1.7 jmcneill I2S_WRITE(sc, DA_CHCFG, val); 951 1.7 jmcneill } 952 1.1 jmcneill 953 1.1 jmcneill sc->sc_format.mode = AUMODE_PLAY|AUMODE_RECORD; 954 1.1 jmcneill sc->sc_format.encoding = AUDIO_ENCODING_SLINEAR_LE; 955 1.7 jmcneill sc->sc_format.validbits = 16; 956 1.7 jmcneill sc->sc_format.precision = 16; 957 1.1 jmcneill sc->sc_format.channels = 2; 958 1.1 jmcneill sc->sc_format.channel_mask = AUFMT_STEREO; 959 1.4 isaki sc->sc_format.frequency_type = 1; 960 1.7 jmcneill sc->sc_format.frequency[0] = SUNXI_I2S_SAMPLE_RATE; 961 1.1 jmcneill 962 1.1 jmcneill sc->sc_dai.dai_set_sysclk = sunxi_i2s_dai_set_sysclk; 963 1.1 jmcneill sc->sc_dai.dai_set_format = sunxi_i2s_dai_set_format; 964 1.1 jmcneill sc->sc_dai.dai_hw_if = &sunxi_i2s_hw_if; 965 1.1 jmcneill sc->sc_dai.dai_dev = self; 966 1.1 jmcneill sc->sc_dai.dai_priv = sc; 967 1.1 jmcneill fdtbus_register_dai_controller(self, phandle, &sunxi_i2s_dai_funcs); 968 1.1 jmcneill } 969 1.1 jmcneill 970 1.1 jmcneill CFATTACH_DECL_NEW(sunxi_i2s, sizeof(struct sunxi_i2s_softc), 971 1.1 jmcneill sunxi_i2s_match, sunxi_i2s_attach, NULL, NULL); 972