[BACK]Return to pxa2x0_i2s.c CVS log [TXT][DIR] Up to [local] / sys / arch / arm / xscale

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