Annotation of sys/arch/arm/xscale/pxa2x0_i2s.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: pxa2x0_i2s.c,v 1.7 2006/04/04 11:45:40 pascoe Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 Christopher Pascoe <pascoe@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/param.h>
! 20: #include <sys/systm.h>
! 21: #include <sys/device.h>
! 22: #include <sys/malloc.h>
! 23:
! 24: #include <arm/xscale/pxa2x0reg.h>
! 25: #include <arm/xscale/pxa2x0var.h>
! 26: #include <arm/xscale/pxa2x0_gpio.h>
! 27: #include <arm/xscale/pxa2x0_i2s.h>
! 28: #include <arm/xscale/pxa2x0_dmac.h>
! 29:
! 30: struct pxa2x0_i2s_dma {
! 31: struct pxa2x0_i2s_dma *next;
! 32: caddr_t addr;
! 33: size_t size;
! 34: bus_dmamap_t map;
! 35: bus_dma_segment_t seg;
! 36: };
! 37:
! 38: void
! 39: pxa2x0_i2s_init(struct pxa2x0_i2s_softc *sc)
! 40: {
! 41: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0, SACR0_RST);
! 42: delay(100);
! 43: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0,
! 44: SACR0_BCKD | SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7));
! 45: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR1, 0);
! 46: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADR, 0);
! 47: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADIV, sc->sc_sadiv);
! 48: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SACR0,
! 49: SACR0_BCKD | SACR0_SET_TFTH(7) | SACR0_SET_RFTH(7) | SACR0_ENB);
! 50: }
! 51:
! 52: int
! 53: pxa2x0_i2s_attach_sub(struct pxa2x0_i2s_softc *sc)
! 54: {
! 55: if (bus_space_map(sc->sc_iot, PXA2X0_I2S_BASE, PXA2X0_I2S_SIZE, 0,
! 56: &sc->sc_ioh)) {
! 57: sc->sc_size = 0;
! 58: return 1;
! 59: }
! 60: sc->sc_sadiv = SADIV_3_058MHz;
! 61:
! 62: bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, sc->sc_size,
! 63: BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
! 64:
! 65: pxa2x0_gpio_set_function(28, GPIO_ALT_FN_1_OUT); /* I2S_BITCLK */
! 66: pxa2x0_gpio_set_function(113, GPIO_ALT_FN_1_OUT); /* I2S_SYSCLK */
! 67: pxa2x0_gpio_set_function(31, GPIO_ALT_FN_1_OUT); /* I2S_SYNC */
! 68: pxa2x0_gpio_set_function(30, GPIO_ALT_FN_1_OUT); /* I2S_SDATA_OUT */
! 69: pxa2x0_gpio_set_function(29, GPIO_ALT_FN_2_IN); /* I2S_SDATA_IN */
! 70:
! 71: pxa2x0_i2s_init(sc);
! 72:
! 73: return 0;
! 74: }
! 75:
! 76: void pxa2x0_i2s_open(struct pxa2x0_i2s_softc *sc)
! 77: {
! 78: sc->sc_open++;
! 79: pxa2x0_clkman_config(CKEN_I2S, 1);
! 80: }
! 81:
! 82: void pxa2x0_i2s_close(struct pxa2x0_i2s_softc *sc)
! 83: {
! 84: pxa2x0_clkman_config(CKEN_I2S, 0);
! 85: sc->sc_open--;
! 86: }
! 87:
! 88: int
! 89: pxa2x0_i2s_detach_sub(struct pxa2x0_i2s_softc *sc)
! 90: {
! 91: if (sc->sc_size > 0) {
! 92: bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
! 93: sc->sc_size = 0;
! 94: }
! 95: pxa2x0_clkman_config(CKEN_I2S, 0);
! 96:
! 97: return (0);
! 98: }
! 99:
! 100: void pxa2x0_i2s_write(struct pxa2x0_i2s_softc *sc, u_int32_t data)
! 101: {
! 102: if (! sc->sc_open)
! 103: return;
! 104:
! 105: /* Clear intr and underrun bit if set. */
! 106: if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, I2S_SASR0) & SASR0_TUR)
! 107: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SAICR, SAICR_TUR);
! 108:
! 109: /* Wait for transmit fifo to have space. */
! 110: while ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, I2S_SASR0) & SASR0_TNF)
! 111: == 0)
! 112: ; /* nothing */
! 113:
! 114: /* Queue data */
! 115: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADR, data);
! 116: }
! 117:
! 118: void
! 119: pxa2x0_i2s_setspeed(struct pxa2x0_i2s_softc *sc, u_long *argp)
! 120: {
! 121: /*
! 122: * The available speeds are in the following table.
! 123: * Keep the speeds in increasing order.
! 124: */
! 125: typedef struct {
! 126: int speed;
! 127: int div;
! 128: } speed_struct;
! 129: u_long arg = *argp;
! 130:
! 131: static speed_struct speed_table[] = {
! 132: {8000, SADIV_513_25kHz},
! 133: {11025, SADIV_702_75kHz},
! 134: {16000, SADIV_1_026MHz},
! 135: {22050, SADIV_1_405MHz},
! 136: {44100, SADIV_2_836MHz},
! 137: {48000, SADIV_3_058MHz},
! 138: };
! 139:
! 140: int i, n, selected = -1;
! 141:
! 142: n = sizeof(speed_table) / sizeof(speed_struct);
! 143:
! 144: if (arg < speed_table[0].speed)
! 145: selected = 0;
! 146: if (arg > speed_table[n - 1].speed)
! 147: selected = n - 1;
! 148:
! 149: for (i = 1; selected == -1 && i < n; i++) {
! 150: if (speed_table[i].speed == arg)
! 151: selected = i;
! 152: else if (speed_table[i].speed > arg) {
! 153: int diff1, diff2;
! 154:
! 155: diff1 = arg - speed_table[i - 1].speed;
! 156: diff2 = speed_table[i].speed - arg;
! 157: if (diff1 < diff2)
! 158: selected = i - 1;
! 159: else
! 160: selected = i;
! 161: }
! 162: }
! 163:
! 164: if (selected == -1)
! 165: selected = 0;
! 166:
! 167: *argp = speed_table[selected].speed;
! 168:
! 169: sc->sc_sadiv = speed_table[selected].div;
! 170: bus_space_write_4(sc->sc_iot, sc->sc_ioh, I2S_SADIV, sc->sc_sadiv);
! 171: }
! 172:
! 173: void *
! 174: pxa2x0_i2s_allocm(void *hdl, int direction, size_t size, int type, int flags)
! 175: {
! 176: struct device *sc_dev = hdl;
! 177: struct pxa2x0_i2s_softc *sc =
! 178: (struct pxa2x0_i2s_softc *)((struct device *)hdl + 1);
! 179: struct pxa2x0_i2s_dma *p;
! 180: int error;
! 181: int rseg;
! 182:
! 183: p = malloc(sizeof(*p), type, flags);
! 184: if (!p)
! 185: return 0;
! 186:
! 187: p->size = size;
! 188: if ((error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &p->seg, 1,
! 189: &rseg, BUS_DMA_NOWAIT)) != 0) {
! 190: printf("%s: unable to allocate dma, error = %d\n",
! 191: sc_dev->dv_xname, error);
! 192: goto fail_alloc;
! 193: }
! 194:
! 195: if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr,
! 196: BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
! 197: printf("%s: unable to map dma, error = %d\n",
! 198: sc_dev->dv_xname, error);
! 199: goto fail_map;
! 200: }
! 201:
! 202: if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
! 203: BUS_DMA_NOWAIT, &p->map)) != 0) {
! 204: printf("%s: unable to create dma map, error = %d\n",
! 205: sc_dev->dv_xname, error);
! 206: goto fail_create;
! 207: }
! 208:
! 209: if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL,
! 210: BUS_DMA_NOWAIT)) != 0) {
! 211: printf("%s: unable to load dma map, error = %d\n",
! 212: sc_dev->dv_xname, error);
! 213: goto fail_load;
! 214: }
! 215:
! 216: p->next = sc->sc_dmas;
! 217: sc->sc_dmas = p;
! 218:
! 219: return p->addr;
! 220:
! 221: fail_load:
! 222: bus_dmamap_destroy(sc->sc_dmat, p->map);
! 223: fail_create:
! 224: bus_dmamem_unmap(sc->sc_dmat, p->addr, size);
! 225: fail_map:
! 226: bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
! 227: fail_alloc:
! 228: free(p, type);
! 229: return 0;
! 230: }
! 231:
! 232: void
! 233: pxa2x0_i2s_freem(void *hdl, void *ptr, int type)
! 234: {
! 235: struct pxa2x0_i2s_softc *sc =
! 236: (struct pxa2x0_i2s_softc *)((struct device *)hdl + 1);
! 237: struct pxa2x0_i2s_dma **pp, *p;
! 238:
! 239: for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next)
! 240: if (p->addr == ptr) {
! 241: bus_dmamap_unload(sc->sc_dmat, p->map);
! 242: bus_dmamap_destroy(sc->sc_dmat, p->map);
! 243: bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
! 244: bus_dmamem_free(sc->sc_dmat, &p->seg, 1);
! 245:
! 246: *pp = p->next;
! 247: free(p, type);
! 248: return;
! 249: }
! 250:
! 251: panic("pxa2x0_i2s_freem: trying to free unallocated memory");
! 252: }
! 253:
! 254: paddr_t
! 255: pxa2x0_i2s_mappage(void *hdl, void *mem, off_t off, int prot)
! 256: {
! 257: struct pxa2x0_i2s_softc *sc =
! 258: (struct pxa2x0_i2s_softc *)((struct device *)hdl + 1);
! 259: struct pxa2x0_i2s_dma *p;
! 260:
! 261: if (off < 0)
! 262: return -1;
! 263:
! 264: for (p = sc->sc_dmas; p && p->addr != mem; p = p->next)
! 265: ;
! 266: if (!p)
! 267: return -1;
! 268:
! 269: if (off > p->size)
! 270: return -1;
! 271:
! 272: return bus_dmamem_mmap(sc->sc_dmat, &p->seg, 1, off, prot,
! 273: BUS_DMA_WAITOK);
! 274: }
! 275:
! 276: int
! 277: pxa2x0_i2s_round_blocksize(void *hdl, int bs)
! 278: {
! 279: /* Enforce individual DMA block size limit */
! 280: if (bs > DCMD_LENGTH_MASK)
! 281: return (DCMD_LENGTH_MASK & ~0x03);
! 282:
! 283: return (bs + 0x03) & ~0x03; /* 32-bit multiples */
! 284: }
! 285:
! 286: size_t
! 287: pxa2x0_i2s_round_buffersize(void *hdl, int direction, size_t bufsize)
! 288: {
! 289: return bufsize;
! 290: }
! 291:
! 292: int
! 293: pxa2x0_i2s_start_output(struct pxa2x0_i2s_softc *sc, void *block, int bsize,
! 294: void (*intr)(void *), void *intrarg)
! 295: {
! 296: struct pxa2x0_i2s_dma *p;
! 297: int offset;
! 298:
! 299: /* Find mapping which contains block completely */
! 300: for (p = sc->sc_dmas; p && (((caddr_t)block < p->addr) ||
! 301: ((caddr_t)block + bsize > p->addr + p->size)); p = p->next)
! 302: ; /* Nothing */
! 303:
! 304: if (!p) {
! 305: printf("pxa2x0_i2s_start_output: request with bad start "
! 306: "address: %p, size: %d)\n", block, bsize);
! 307: return ENXIO;
! 308: }
! 309:
! 310: /* Offset into block to use in mapped block */
! 311: offset = (caddr_t)block - p->addr;
! 312:
! 313: /* Start DMA */
! 314: pxa2x0_dma_to_fifo(3, 1, 0x40400080, 4, 32,
! 315: p->map->dm_segs[0].ds_addr + offset, bsize, intr, intrarg);
! 316:
! 317: return 0;
! 318: }
! 319:
! 320: int
! 321: pxa2x0_i2s_start_input(struct pxa2x0_i2s_softc *sc, void *block, int bsize,
! 322: void (*intr)(void *), void *intrarg)
! 323: {
! 324: struct pxa2x0_i2s_dma *p;
! 325: int offset;
! 326:
! 327: /* Find mapping which contains block completely */
! 328: for (p = sc->sc_dmas; p && (((caddr_t)block < p->addr) ||
! 329: ((caddr_t)block + bsize > p->addr + p->size)); p = p->next)
! 330: ; /* Nothing */
! 331:
! 332: if (!p) {
! 333: printf("pxa2x0_i2s_start_input: request with bad start "
! 334: "address: %p, size: %d)\n", block, bsize);
! 335: return ENXIO;
! 336: }
! 337:
! 338: /* Offset into block to use in mapped block */
! 339: offset = (caddr_t)block - p->addr;
! 340:
! 341: /* Start DMA */
! 342: pxa2x0_dma_from_fifo(2, 2, 0x40400080, 4, 32,
! 343: p->map->dm_segs[0].ds_addr + offset, bsize, intr, intrarg);
! 344:
! 345: return 0;
! 346: }
CVSweb