diff options
author | Jason Wright <jason@cvs.openbsd.org> | 2003-01-26 21:14:58 +0000 |
---|---|---|
committer | Jason Wright <jason@cvs.openbsd.org> | 2003-01-26 21:14:58 +0000 |
commit | 80f8b838bb789c213130e6d1ecdff1974bc0a3d4 (patch) | |
tree | a530a71d337ec0adc1583ee9df9da231f3deecf8 /sys/arch/hppa | |
parent | bb731899ef141d461ac99d7cb6737a4c732983c0 (diff) |
Ok, this now produces some noise (that's the best way I can describe it),
but the timing appears to get off some how and buffers just stay on the
chip. Oh well, at least its keeping me from playing in the traffic.
Diffstat (limited to 'sys/arch/hppa')
-rw-r--r-- | sys/arch/hppa/gsc/harmony.c | 185 |
1 files changed, 165 insertions, 20 deletions
diff --git a/sys/arch/hppa/gsc/harmony.c b/sys/arch/hppa/gsc/harmony.c index 8762b075365..66294788573 100644 --- a/sys/arch/hppa/gsc/harmony.c +++ b/sys/arch/hppa/gsc/harmony.c @@ -1,4 +1,4 @@ -/* $OpenBSD: harmony.c,v 1.1 2003/01/26 07:21:40 jason Exp $ */ +/* $OpenBSD: harmony.c,v 1.2 2003/01/26 21:14:57 jason Exp $ */ /* * Copyright (c) 2003 Jason L. Wright (jason@thought.net) @@ -122,6 +122,24 @@ struct harmony_empty { u_int8_t capture[CAPTURE_EMPTYS][HARMONY_BUFSIZE]; }; +struct harmony_dma { + struct harmony_dma *d_next; + bus_dmamap_t d_map; + bus_dma_segment_t d_seg; + caddr_t d_kva; + size_t d_size; +}; + +struct harmony_channel { + struct harmony_dma *c_current; + bus_size_t c_segsz; + bus_size_t c_cnt; + bus_size_t c_blksz; + bus_addr_t c_lastaddr; + void (*c_intr)(void *); + void *c_intrarg; +}; + struct harmony_softc { struct device sc_dv; bus_dma_tag_t sc_dmat; @@ -139,6 +157,9 @@ struct harmony_softc { bus_dma_segment_t sc_empty_seg; int sc_empty_rseg; struct harmony_empty *sc_empty_kva; + struct harmony_dma *sc_dmas; + int sc_playing, sc_capturing, sc_intr_enable; + struct harmony_channel sc_playback; }; int harmony_open(void *, int); @@ -154,15 +175,14 @@ int harmony_getdev(void *, struct audio_device *); int harmony_set_port(void *, mixer_ctrl_t *); int harmony_get_port(void *, mixer_ctrl_t *); int harmony_query_devinfo(void *addr, mixer_devinfo_t *); -void * harmony_alloc(void *, int, size_t, int, int); -void harmony_free(void *, void *, int); +void * harmony_allocm(void *, int, size_t, int, int); +void harmony_freem(void *, void *, int); size_t harmony_round_buffersize(void *, int, size_t); int harmony_get_props(void *); int harmony_trigger_output(void *, void *, void *, int, void (*intr)(void *), void *arg, struct audio_params *); int harmony_trigger_input(void *, void *, void *, int, void (*intr)(void *), void *arg, struct audio_params *); -u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *); struct audio_hw_if harmony_sa_hw_if = { harmony_open, @@ -184,8 +204,8 @@ struct audio_hw_if harmony_sa_hw_if = { harmony_set_port, harmony_get_port, harmony_query_devinfo, - NULL, - NULL, + harmony_allocm, + harmony_freem, harmony_round_buffersize, NULL, harmony_get_props, @@ -206,6 +226,7 @@ void harmony_intr_enable(struct harmony_softc *); void harmony_intr_disable(struct harmony_softc *); void harmony_wait(struct harmony_softc *); void harmony_set_gainctl(struct harmony_softc *, u_int32_t); +u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *); int harmony_match(parent, match, aux) @@ -319,25 +340,57 @@ harmony_intr(vsc) { struct harmony_softc *sc = vsc; u_int32_t dstatus; + int r = 0; + + if (sc->sc_intr_enable == 0) + return (0); harmony_intr_disable(sc); harmony_wait(sc); dstatus = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_DSTATUS) & (DSTATUS_PLAYNXT | DSTATUS_CAPTNXT); - if (dstatus == 0) - return (0); - - printf("%s: intr %x\n", sc->sc_dv.dv_xname, dstatus); if (dstatus & DSTATUS_PLAYNXT) { - bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_PLAYNXT, - sc->sc_playback_paddrs[sc->sc_playback_empty]); - if (++sc->sc_playback_empty == PLAYBACK_EMPTYS) - sc->sc_playback_empty = 0; + r = 1; + if (sc->sc_playing == 0) { + bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_PLAYNXT, + sc->sc_playback_paddrs[sc->sc_playback_empty]); + if (++sc->sc_playback_empty == PLAYBACK_EMPTYS) + sc->sc_playback_empty = 0; + } else { + struct harmony_channel *c = &sc->sc_playback; + struct harmony_dma *d; + bus_addr_t nextaddr; + bus_size_t togo; + + d = c->c_current; + togo = c->c_segsz - c->c_cnt; + if (togo == 0) { + nextaddr = d->d_map->dm_segs[0].ds_addr; + c->c_cnt = togo = c->c_blksz; + } else { + nextaddr = c->c_lastaddr; + if (togo > c->c_blksz) + togo = c->c_blksz; + c->c_cnt += togo; + } + + bus_dmamap_sync(sc->sc_dmat, d->d_map, + nextaddr - d->d_map->dm_segs[0].ds_addr, + c->c_blksz, BUS_DMASYNC_PREWRITE); + + bus_space_write_4(sc->sc_bt, sc->sc_bh, + HARMONY_PLAYNXT, nextaddr); + c->c_lastaddr = nextaddr + togo; + + if (c->c_intr != NULL) + (*c->c_intr)(c->c_intrarg); + } } if (dstatus & DSTATUS_CAPTNXT) { + r = 1; bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CAPTNXT, sc->sc_capture_paddrs[sc->sc_capture_empty]); if (++sc->sc_capture_empty == CAPTURE_EMPTYS) @@ -346,13 +399,14 @@ harmony_intr(vsc) harmony_intr_enable(sc); - return (1); + return (r); } void harmony_intr_enable(struct harmony_softc *sc) { harmony_wait(sc); + sc->sc_intr_enable = 1; bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_DSTATUS, DSTATUS_INTRENA); } @@ -362,6 +416,7 @@ harmony_intr_disable(struct harmony_softc *sc) { harmony_wait(sc); bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_DSTATUS, 0); + sc->sc_intr_enable = 0; } void @@ -521,7 +576,7 @@ harmony_set_params(void *vsc, int setmode, int usemode, int harmony_round_blocksize(void *vsc, int blk) { - return (blk & (-4)); + return (HARMONY_BUFSIZE); } int @@ -570,6 +625,7 @@ harmony_halt_output(void *vsc) struct harmony_softc *sc = vsc; harmony_intr_disable(sc); + sc->sc_playing = 0; return (0); } @@ -579,6 +635,7 @@ harmony_halt_input(void *vsc) struct harmony_softc *sc = vsc; harmony_intr_disable(sc); + sc->sc_capturing = 0; return (0); } @@ -778,10 +835,73 @@ harmony_query_devinfo(void *vsc, mixer_devinfo_t *dip) return (err); } +void * +harmony_allocm(void *vsc, int dir, size_t size, int pool, int flags) +{ + struct harmony_softc *sc = vsc; + struct harmony_dma *d; + int rseg; + + d = (struct harmony_dma *)malloc(sizeof(struct harmony_dma), pool, flags); + if (d == NULL) + goto fail; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT, + &d->d_map) != 0) + goto fail1; + + if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &d->d_seg, 1, + &rseg, BUS_DMA_NOWAIT) != 0) + goto fail2; + + if (bus_dmamem_map(sc->sc_dmat, &d->d_seg, 1, size, &d->d_kva, + BUS_DMA_NOWAIT) != 0) + goto fail3; + + if (bus_dmamap_load(sc->sc_dmat, d->d_map, d->d_kva, size, NULL, + BUS_DMA_NOWAIT) != 0) + goto fail4; + + d->d_next = sc->sc_dmas; + sc->sc_dmas = d; + d->d_size = size; + return (d->d_kva); + +fail4: + bus_dmamem_unmap(sc->sc_dmat, d->d_kva, size); +fail3: + bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1); +fail2: + bus_dmamap_destroy(sc->sc_dmat, d->d_map); +fail1: + free(d, pool); +fail: + return (NULL); +} + +void +harmony_freem(void *vsc, void *ptr, int pool) +{ + struct harmony_softc *sc = vsc; + struct harmony_dma *d, **dd; + + for (dd = &sc->sc_dmas; (d = *dd) != NULL; dd = &(*dd)->d_next) { + if (d->d_kva != ptr) + continue; + bus_dmamap_unload(sc->sc_dmat, d->d_map); + bus_dmamem_unmap(sc->sc_dmat, d->d_kva, d->d_size); + bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, d->d_map); + free(d, pool); + return; + } + printf("%s: free rogue pointer\n", sc->sc_dv.dv_xname); +} + size_t harmony_round_buffersize(void *vsc, int direction, size_t size) { - return (size); + return (size & (size_t)(-HARMONY_BUFSIZE)); } int @@ -795,11 +915,35 @@ harmony_trigger_output(void *vsc, void *start, void *end, int blksize, void (*intr)(void *), void *arg, struct audio_params *param) { struct harmony_softc *sc = vsc; + struct harmony_channel *c = &sc->sc_playback; + struct harmony_dma *d; + bus_size_t n; + + for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next) + /*EMPTY*/; + if (d == NULL) { + printf("%s: trigger_output: bad addr: %p\n", + sc->sc_dv.dv_xname, start); + return (EINVAL); + } + n = (caddr_t)end - (caddr_t)start; + + c->c_blksz = blksize; + c->c_current = d; + c->c_segsz = n; + + if (n > c->c_blksz) + n = c->c_blksz; + c->c_cnt = n; + + bus_dmamap_sync(sc->sc_dmat, d->d_map, 0, c->c_blksz, + BUS_DMASYNC_PREWRITE); bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_PLAYNXT, - sc->sc_playback_paddrs[sc->sc_playback_empty]); - if (++sc->sc_playback_empty == PLAYBACK_EMPTYS) - sc->sc_playback_empty = 0; + d->d_map->dm_segs[0].ds_addr); + c->c_lastaddr = d->d_map->dm_segs[0].ds_addr + n; + + sc->sc_playing = 1; harmony_intr_enable(sc); return (0); } @@ -814,6 +958,7 @@ harmony_trigger_input(void *vsc, void *start, void *end, int blksize, sc->sc_capture_paddrs[sc->sc_capture_empty]); if (++sc->sc_capture_empty == CAPTURE_EMPTYS) sc->sc_capture_empty = 0; + sc->sc_capturing = 1; harmony_intr_enable(sc); return (0); } |