1 /* $NetBSD: pxa2x0_i2s.c,v 1.7 2007/10/17 19:53:44 garbled Exp $ */ 2 /* $OpenBSD: pxa2x0_i2s.c,v 1.7 2006/04/04 11:45:40 pascoe Exp $ */ 3 4 /* 5 * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 __KERNEL_RCSID(0, "$NetBSD: pxa2x0_i2s.c,v 1.7 2007/10/17 19:53:44 garbled Exp $"); 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <sys/malloc.h> 27 28 #include <machine/bus.h> 29 30 #include <arm/xscale/pxa2x0reg.h> 31 #include <arm/xscale/pxa2x0var.h> 32 #include <arm/xscale/pxa2x0_gpio.h> 33 #include <arm/xscale/pxa2x0_i2s.h> 34 #include <arm/xscale/pxa2x0_dmac.h> 35 36 struct pxa2x0_i2s_dma { 37 struct pxa2x0_i2s_dma *next; 38 void *addr; 39 size_t size; 40 bus_dmamap_t map; 41 #define I2S_N_SEGS 1 42 bus_dma_segment_t segs[I2S_N_SEGS]; 43 int nsegs; 44 struct dmac_xfer *dx; 45 }; 46 47 static void pxa2x0_i2s_dmac_ointr(struct dmac_xfer *, int); 48 static void pxa2x0_i2s_dmac_iintr(struct dmac_xfer *, int); 49 50 void 51 pxa2x0_i2s_init(struct pxa2x0_i2s_softc *sc) 52 { 53 54 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0, SACR0_RST); 55 delay(100); 56 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0, 57 SACR0_BCKD | SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7)); 58 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR1, 0); 59 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADR, 0); 60 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADIV, sc->sc_sadiv); 61 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0, 62 SACR0_BCKD | SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7) | SACR0_ENB); 63 } 64 65 int 66 pxa2x0_i2s_attach_sub(struct pxa2x0_i2s_softc *sc) 67 { 68 int rv; 69 70 rv = bus_space_map(sc->sc_iot, PXA2X0_I2S_BASE, PXA2X0_I2S_SIZE, 0, 71 &sc->sc_ioh); 72 if (rv) { 73 sc->sc_size = 0; 74 return 1; 75 } 76 77 sc->sc_dr.ds_addr = PXA2X0_I2S_BASE + I2S_SADR; 78 sc->sc_dr.ds_len = 4; 79 80 sc->sc_sadiv = SADIV_3_058MHz; 81 82 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, sc->sc_size, 83 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE); 84 85 pxa2x0_i2s_init(sc); 86 87 return 0; 88 } 89 90 void 91 pxa2x0_i2s_open(struct pxa2x0_i2s_softc *sc) 92 { 93 94 if (sc->sc_open++ == 0) { 95 pxa2x0_clkman_config(CKEN_I2S, 1); 96 } 97 } 98 99 void 100 pxa2x0_i2s_close(struct pxa2x0_i2s_softc *sc) 101 { 102 103 if (--sc->sc_open == 0) { 104 pxa2x0_clkman_config(CKEN_I2S, 0); 105 } 106 } 107 108 int 109 pxa2x0_i2s_detach_sub(struct pxa2x0_i2s_softc *sc) 110 { 111 112 if (sc->sc_size > 0) { 113 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); 114 sc->sc_size = 0; 115 } 116 pxa2x0_clkman_config(CKEN_I2S, 0); 117 118 return 0; 119 } 120 121 void 122 pxa2x0_i2s_write(struct pxa2x0_i2s_softc *sc, uint32_t data) 123 { 124 125 if (sc->sc_open == 0) 126 return; 127 128 /* Clear intr and underrun bit if set. */ 129 if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, I2S_SASR0) & SASR0_TUR) 130 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SAICR, SAICR_TUR); 131 132 /* Wait for transmit fifo to have space. */ 133 while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, I2S_SASR0) & SASR0_TNF) 134 == 0) 135 continue; /* nothing */ 136 137 /* Queue data */ 138 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADR, data); 139 } 140 141 void 142 pxa2x0_i2s_setspeed(struct pxa2x0_i2s_softc *sc, u_int *argp) 143 { 144 /* 145 * The available speeds are in the following table. 146 * Keep the speeds in increasing order. 147 */ 148 static const struct speed_struct { 149 int speed; 150 int div; 151 } speed_table[] = { 152 {8000, SADIV_513_25kHz}, 153 {11025, SADIV_702_75kHz}, 154 {16000, SADIV_1_026MHz}, 155 {22050, SADIV_1_405MHz}, 156 {44100, SADIV_2_836MHz}, 157 {48000, SADIV_3_058MHz}, 158 }; 159 const int n = (int)__arraycount(speed_table); 160 u_int arg = (u_int)*argp; 161 int selected = -1; 162 int i; 163 164 if (arg < speed_table[0].speed) 165 selected = 0; 166 if (arg > speed_table[n - 1].speed) 167 selected = n - 1; 168 169 for (i = 1; selected == -1 && i < n; i++) { 170 if (speed_table[i].speed == arg) 171 selected = i; 172 else if (speed_table[i].speed > arg) { 173 int diff1, diff2; 174 175 diff1 = arg - speed_table[i - 1].speed; 176 diff2 = speed_table[i].speed - arg; 177 if (diff1 < diff2) 178 selected = i - 1; 179 else 180 selected = i; 181 } 182 } 183 184 if (selected == -1) 185 selected = 0; 186 187 *argp = speed_table[selected].speed; 188 189 sc->sc_sadiv = speed_table[selected].div; 190 bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADIV, sc->sc_sadiv); 191 } 192 193 void * 194 pxa2x0_i2s_allocm(void *hdl, int direction, size_t size, 195 struct malloc_type *type, int flags) 196 { 197 struct pxa2x0_i2s_softc *sc = hdl; 198 struct pxa2x0_i2s_dma *p; 199 struct dmac_xfer *dx; 200 int error; 201 202 p = malloc(sizeof(*p), type, flags); 203 if (p == NULL) 204 return NULL; 205 206 dx = pxa2x0_dmac_allocate_xfer(M_NOWAIT); 207 if (dx == NULL) { 208 goto fail_alloc; 209 } 210 p->dx = dx; 211 212 p->size = size; 213 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, p->segs, 214 I2S_N_SEGS, &p->nsegs, BUS_DMA_NOWAIT)) != 0) { 215 goto fail_xfer; 216 } 217 218 if ((error = bus_dmamem_map(sc->sc_dmat, p->segs, p->nsegs, size, 219 &p->addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { 220 goto fail_map; 221 } 222 223 if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 224 BUS_DMA_NOWAIT, &p->map)) != 0) { 225 goto fail_create; 226 } 227 228 if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL, 229 BUS_DMA_NOWAIT)) != 0) { 230 goto fail_load; 231 } 232 233 dx->dx_cookie = sc; 234 dx->dx_priority = DMAC_PRIORITY_NORMAL; 235 dx->dx_dev_width = DCMD_WIDTH_4; 236 dx->dx_burst_size = DCMD_SIZE_32; 237 238 p->next = sc->sc_dmas; 239 sc->sc_dmas = p; 240 241 return p->addr; 242 243 fail_load: 244 bus_dmamap_destroy(sc->sc_dmat, p->map); 245 fail_create: 246 bus_dmamem_unmap(sc->sc_dmat, p->addr, size); 247 fail_map: 248 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); 249 fail_xfer: 250 pxa2x0_dmac_free_xfer(dx); 251 fail_alloc: 252 free(p, type); 253 return NULL; 254 } 255 256 void 257 pxa2x0_i2s_freem(void *hdl, void *ptr, struct malloc_type *type) 258 { 259 struct pxa2x0_i2s_softc *sc = hdl; 260 struct pxa2x0_i2s_dma **pp, *p; 261 262 for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next) { 263 if (p->addr == ptr) { 264 pxa2x0_dmac_abort_xfer(p->dx); 265 pxa2x0_dmac_free_xfer(p->dx); 266 p->segs[0].ds_len = p->size; /* XXX */ 267 bus_dmamap_unload(sc->sc_dmat, p->map); 268 bus_dmamap_destroy(sc->sc_dmat, p->map); 269 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size); 270 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs); 271 272 *pp = p->next; 273 free(p, type); 274 return; 275 } 276 } 277 panic("pxa2x0_i2s_freem: trying to free unallocated memory"); 278 } 279 280 paddr_t 281 pxa2x0_i2s_mappage(void *hdl, void *mem, off_t off, int prot) 282 { 283 struct pxa2x0_i2s_softc *sc = hdl; 284 struct pxa2x0_i2s_dma *p; 285 286 if (off < 0) 287 return -1; 288 289 for (p = sc->sc_dmas; p && p->addr != mem; p = p->next) 290 continue; 291 if (p == NULL) 292 return -1; 293 294 if (off > p->size) 295 return -1; 296 297 return bus_dmamem_mmap(sc->sc_dmat, p->segs, p->nsegs, off, prot, 298 BUS_DMA_WAITOK); 299 } 300 301 int 302 pxa2x0_i2s_round_blocksize(void *hdl, int bs, int mode, 303 const struct audio_params *param) 304 { 305 306 /* Enforce individual DMA block size limit */ 307 if (bs > DCMD_LENGTH_MASK) 308 return (DCMD_LENGTH_MASK & ~0x03); 309 310 return (bs + 0x03) & ~0x03; /* 32-bit multiples */ 311 } 312 313 size_t 314 pxa2x0_i2s_round_buffersize(void *hdl, int direction, size_t bufsize) 315 { 316 317 return bufsize; 318 } 319 320 int 321 pxa2x0_i2s_halt_output(void *hdl) 322 { 323 struct pxa2x0_i2s_softc *sc = hdl; 324 int s; 325 326 s = splaudio(); 327 if (sc->sc_txdma) { 328 pxa2x0_dmac_abort_xfer(sc->sc_txdma->dx); 329 sc->sc_txdma = NULL; 330 } 331 splx(s); 332 333 return 0; 334 } 335 336 int 337 pxa2x0_i2s_halt_input(void *hdl) 338 { 339 struct pxa2x0_i2s_softc *sc = hdl; 340 int s; 341 342 s = splaudio(); 343 if (sc->sc_rxdma) { 344 pxa2x0_dmac_abort_xfer(sc->sc_rxdma->dx); 345 sc->sc_rxdma = NULL; 346 } 347 splx(s); 348 349 return 0; 350 } 351 352 int 353 pxa2x0_i2s_start_output(void *hdl, void *block, int bsize, 354 void (*tx_func)(void *), void *tx_arg) 355 { 356 struct pxa2x0_i2s_softc *sc = hdl; 357 struct pxa2x0_i2s_dma *p; 358 struct dmac_xfer *dx; 359 int rv; 360 361 if (sc->sc_txdma) 362 return EBUSY; 363 364 sc->sc_txfunc = tx_func; 365 sc->sc_txarg = tx_arg; 366 367 /* Find mapping which contains block completely */ 368 for (p = sc->sc_dmas; p != NULL && (((char *)block < (char *)p->addr) || 369 ((char *)block + bsize > (char *)p->addr + p->size)); p = p->next) 370 continue; /* Nothing */ 371 372 if (p == NULL) { 373 printf("pxa2x0_i2s_start_output: request with bad start " 374 "address: %p, size: %d)\n", block, bsize); 375 return ENXIO; 376 } 377 sc->sc_txdma = p; 378 379 p->segs[0].ds_addr = p->map->dm_segs[0].ds_addr 380 + ((char *)block - (char *)p->addr); 381 p->segs[0].ds_len = bsize; 382 383 dx = p->dx; 384 dx->dx_done = pxa2x0_i2s_dmac_ointr; 385 dx->dx_peripheral = DMAC_PERIPH_I2STX; 386 dx->dx_flow = DMAC_FLOW_CTRL_DEST; 387 dx->dx_loop_notify = DMAC_DONT_LOOP; 388 dx->dx_desc[DMAC_DESC_SRC].xd_addr_hold = false; 389 dx->dx_desc[DMAC_DESC_SRC].xd_nsegs = p->nsegs; 390 dx->dx_desc[DMAC_DESC_SRC].xd_dma_segs = p->segs; 391 dx->dx_desc[DMAC_DESC_DST].xd_addr_hold = true; 392 dx->dx_desc[DMAC_DESC_DST].xd_nsegs = 1; 393 dx->dx_desc[DMAC_DESC_DST].xd_dma_segs = &sc->sc_dr; 394 395 /* Start DMA */ 396 rv = pxa2x0_dmac_start_xfer(dx); 397 398 return rv; 399 } 400 401 int 402 pxa2x0_i2s_start_input(void *hdl, void *block, int bsize, 403 void (*rx_func)(void *), void *rx_arg) 404 { 405 struct pxa2x0_i2s_softc *sc = hdl; 406 struct pxa2x0_i2s_dma *p; 407 struct dmac_xfer *dx; 408 int rv; 409 410 if (sc->sc_rxdma) 411 return EBUSY; 412 413 sc->sc_rxfunc = rx_func; 414 sc->sc_rxarg = rx_arg; 415 416 /* Find mapping which contains block completely */ 417 for (p = sc->sc_dmas; p != NULL && (((char *)block < (char *)p->addr) || 418 ((char *)block + bsize > (char *)p->addr + p->size)); p = p->next) 419 continue; /* Nothing */ 420 421 if (p == NULL) { 422 printf("pxa2x0_i2s_start_input: request with bad start " 423 "address: %p, size: %d)\n", block, bsize); 424 return ENXIO; 425 } 426 427 sc->sc_rxdma = p; 428 p->segs[0].ds_addr = p->map->dm_segs[0].ds_addr 429 + ((char *)block - (char *)p->addr); 430 p->segs[0].ds_len = bsize; 431 432 dx = p->dx; 433 dx->dx_done = pxa2x0_i2s_dmac_iintr; 434 dx->dx_peripheral = DMAC_PERIPH_I2SRX; 435 dx->dx_flow = DMAC_FLOW_CTRL_SRC; 436 dx->dx_loop_notify = DMAC_DONT_LOOP; 437 dx->dx_desc[DMAC_DESC_SRC].xd_addr_hold = true; 438 dx->dx_desc[DMAC_DESC_SRC].xd_nsegs = 1; 439 dx->dx_desc[DMAC_DESC_SRC].xd_dma_segs = &sc->sc_dr; 440 dx->dx_desc[DMAC_DESC_DST].xd_addr_hold = false; 441 dx->dx_desc[DMAC_DESC_DST].xd_nsegs = p->nsegs; 442 dx->dx_desc[DMAC_DESC_DST].xd_dma_segs = p->segs; 443 444 /* Start DMA */ 445 rv = pxa2x0_dmac_start_xfer(dx); 446 447 return rv; 448 } 449 450 static void 451 pxa2x0_i2s_dmac_ointr(struct dmac_xfer *dx, int status) 452 { 453 struct pxa2x0_i2s_softc *sc = dx->dx_cookie; 454 struct pxa2x0_i2s_dma *p = sc->sc_txdma; 455 int s; 456 457 if (p == NULL) { 458 panic("pxa2x_i2s_dmac_ointr: bad TX DMA descriptor!"); 459 } 460 461 if (p->dx != dx) { 462 panic("pxa2x_i2s_dmac_ointr: xfer mismatch!"); 463 } 464 465 if (status) { 466 printf("%s: pxa2x0_i2s_dmac_ointr: " 467 "non-zero completion status %d\n", 468 sc->sc_dev.dv_xname, status); 469 } 470 471 s = splaudio(); 472 (sc->sc_txfunc)(sc->sc_txarg); 473 splx(s); 474 } 475 476 static void 477 pxa2x0_i2s_dmac_iintr(struct dmac_xfer *dx, int status) 478 { 479 struct pxa2x0_i2s_softc *sc = dx->dx_cookie; 480 struct pxa2x0_i2s_dma *p = sc->sc_rxdma; 481 int s; 482 483 if (p == NULL) { 484 panic("pxa2x_i2s_dmac_iintr: bad RX DMA descriptor!"); 485 } 486 487 if (p->dx != dx) { 488 panic("pxa2x_i2s_dmac_iintr: xfer mismatch!"); 489 } 490 491 if (status) { 492 printf("%s: pxa2x0_i2s_dmac_iintr: " 493 "non-zero completion status %d\n", 494 sc->sc_dev.dv_xname, status); 495 } 496 497 s = splaudio(); 498 (sc->sc_rxfunc)(sc->sc_rxarg); 499 splx(s); 500 } 501