From fd30400ec27180765773993cadf34442897550b0 Mon Sep 17 00:00:00 2001 From: jakemsr Date: Mon, 26 Jul 2010 23:17:19 +0000 Subject: [PATCH] recording/full-duplex support ok kettenis --- share/man/man4/man4.sparc64/audioce.4 | 3 +- sys/arch/sparc64/dev/ce4231.c | 188 ++++++++++++++++++++------ sys/arch/sparc64/dev/ce4231var.h | 20 +-- 3 files changed, 156 insertions(+), 55 deletions(-) diff --git a/share/man/man4/man4.sparc64/audioce.4 b/share/man/man4/man4.sparc64/audioce.4 index 9e2d6c9669a..42d0d2e8f6e 100644 --- a/share/man/man4/man4.sparc64/audioce.4 +++ b/share/man/man4/man4.sparc64/audioce.4 @@ -1,4 +1,4 @@ -.\" $OpenBSD: audioce.4,v 1.7 2010/07/26 20:06:12 jakemsr Exp $ +.\" $OpenBSD: audioce.4,v 1.8 2010/07/26 23:17:19 jakemsr Exp $ .\" .\" Copyright (c) 2001 Jason L. Wright (jason@thought.net) .\" All rights reserved. @@ -44,7 +44,6 @@ chip to implement the audio device interface described in .Xr audio 4 . This device is found onboard on some PCI/EBus based sparc64 models. .Pp -Audio capture is currently not supported. The .Nm has a maximum precision of 16 bits and has both stereo and monoaural outputs. diff --git a/sys/arch/sparc64/dev/ce4231.c b/sys/arch/sparc64/dev/ce4231.c index 67386919298..de987e3d28a 100644 --- a/sys/arch/sparc64/dev/ce4231.c +++ b/sys/arch/sparc64/dev/ce4231.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ce4231.c,v 1.27 2010/07/26 20:06:12 jakemsr Exp $ */ +/* $OpenBSD: ce4231.c,v 1.28 2010/07/26 23:17:19 jakemsr Exp $ */ /* * Copyright (c) 1999 Jason L. Wright (jason@thought.net) @@ -100,8 +100,6 @@ #define CS_TIMEOUT 90000 -#define CS_AFS_PI 0x10 - /* Read/write CS4231 direct registers */ #define CS_WRITE(sc,r,v) \ bus_space_write_1((sc)->sc_bustag, (sc)->sc_cshandle, (r) << 2, (v)) @@ -432,7 +430,6 @@ ce4231_open(addr, flags) return (EBUSY); sc->sc_open = 1; - sc->sc_locked = 0; sc->sc_rintr = 0; sc->sc_rarg = 0; sc->sc_pintr = 0; @@ -721,7 +718,6 @@ ce4231_halt_output(addr) P_READ(sc, EBDMA_DCSR) & ~EBDCSR_DMAEN); ce4231_write(sc, SP_INTERFACE_CONFIG, ce4231_read(sc, SP_INTERFACE_CONFIG) & (~PLAYBACK_ENABLE)); - sc->sc_locked = 0; return (0); } @@ -735,7 +731,6 @@ ce4231_halt_input(addr) C_READ(sc, EBDMA_DCSR) & ~EBDCSR_DMAEN); ce4231_write(sc, SP_INTERFACE_CONFIG, ce4231_read(sc, SP_INTERFACE_CONFIG) & (~CAPTURE_ENABLE)); - sc->sc_locked = 0; return (0); } @@ -1224,12 +1219,17 @@ ce4231_get_props(addr) /* * Hardware interrupt handler */ -int -ce4231_cintr(v) - void *v; -{ - return (0); -} +/* + * Don't bother with the AD1848_STATUS register. It's interrupt bit gets + * set for both recording and playback interrupts. But we have separate + * handlers for playback and recording, and if we clear the status in + * one handler while there is an interrupt pending for the other direction + * as well, we'll never notice the interrupt for the other direction. + * + * Instead rely solely on CS_IRQ_STATUS, which has separate bits for + * playback and recording interrupts. Also note that resetting + * AD1848_STATUS clears the interrupt bits in CS_IRQ_STATUS. + */ int ce4231_pintr(v) @@ -1237,19 +1237,18 @@ ce4231_pintr(v) { struct ce4231_softc *sc = (struct ce4231_softc *)v; u_int32_t csr; - u_int8_t reg, status; + u_int8_t reg; struct cs_dma *p; + struct cs_chdma *chdma = &sc->sc_pchdma; int r = 0; csr = P_READ(sc, EBDMA_DCSR); - status = CS_READ(sc, AD1848_STATUS); - if (status & (INTERRUPT_STATUS | SAMPLE_ERROR)) { - reg = ce4231_read(sc, CS_IRQ_STATUS); - if (reg & CS_AFS_PI) { - ce4231_write(sc, SP_LOWER_BASE_COUNT, 0xff); - ce4231_write(sc, SP_UPPER_BASE_COUNT, 0xff); - } - CS_WRITE(sc, AD1848_STATUS, 0); + + reg = ce4231_read(sc, CS_IRQ_STATUS); + if (reg & CS_IRQ_PI) { + ce4231_write(sc, SP_LOWER_BASE_COUNT, 0xff); + ce4231_write(sc, SP_UPPER_BASE_COUNT, 0xff); + ce4231_write(sc, CS_IRQ_STATUS, reg & ~CS_IRQ_PI); } P_WRITE(sc, EBDMA_DCSR, csr); @@ -1260,21 +1259,21 @@ ce4231_pintr(v) if ((csr & EBDCSR_TC) || ((csr & EBDCSR_A_LOADED) == 0)) { u_long nextaddr, togo; - p = sc->sc_nowplaying; - togo = sc->sc_playsegsz - sc->sc_playcnt; + p = chdma->cur_dma; + togo = chdma->segsz - chdma->count; if (togo == 0) { nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr; - sc->sc_playcnt = togo = sc->sc_blksz; + chdma->count = togo = chdma->blksz; } else { - nextaddr = sc->sc_lastaddr; - if (togo > sc->sc_blksz) - togo = sc->sc_blksz; - sc->sc_playcnt += togo; + nextaddr = chdma->lastaddr; + if (togo > chdma->blksz) + togo = chdma->blksz; + chdma->count += togo; } P_WRITE(sc, EBDMA_DCNT, togo); P_WRITE(sc, EBDMA_DADDR, nextaddr); - sc->sc_lastaddr = nextaddr + togo; + chdma->lastaddr = nextaddr + togo; if (sc->sc_pintr != NULL) (*sc->sc_pintr)(sc->sc_parg); @@ -1284,6 +1283,58 @@ ce4231_pintr(v) return (r); } +int +ce4231_cintr(v) + void *v; +{ + struct ce4231_softc *sc = (struct ce4231_softc *)v; + u_int32_t csr; + u_int8_t reg; + struct cs_dma *p; + struct cs_chdma *chdma = &sc->sc_rchdma; + int r = 0; + + csr = C_READ(sc, EBDMA_DCSR); + + reg = ce4231_read(sc, CS_IRQ_STATUS); + if (reg & CS_IRQ_CI) { + ce4231_write(sc, CS_LOWER_REC_CNT, 0xff); + ce4231_write(sc, CS_UPPER_REC_CNT, 0xff); + ce4231_write(sc, CS_IRQ_STATUS, reg & ~CS_IRQ_CI); + } + + C_WRITE(sc, EBDMA_DCSR, csr); + + if (csr & EBDCSR_INT) + r = 1; + + if ((csr & EBDCSR_TC) || ((csr & EBDCSR_A_LOADED) == 0)) { + u_long nextaddr, togo; + + p = chdma->cur_dma; + togo = chdma->segsz - chdma->count; + if (togo == 0) { + nextaddr = (u_int32_t)p->dmamap->dm_segs[0].ds_addr; + chdma->count = togo = chdma->blksz; + } else { + nextaddr = chdma->lastaddr; + if (togo > chdma->blksz) + togo = chdma->blksz; + chdma->count += togo; + } + + C_WRITE(sc, EBDMA_DCNT, togo); + C_WRITE(sc, EBDMA_DADDR, nextaddr); + chdma->lastaddr = nextaddr + togo; + + if (sc->sc_rintr != NULL) + (*sc->sc_rintr)(sc->sc_rarg); + r = 1; + } + + return (r); +} + void * ce4231_alloc(addr, direction, size, pool, flags) void *addr; @@ -1368,16 +1419,10 @@ ce4231_trigger_output(addr, start, end, blksize, intr, arg, param) { struct ce4231_softc *sc = addr; struct cs_dma *p; + struct cs_chdma *chdma = &sc->sc_pchdma; u_int32_t csr; vaddr_t n; - if (sc->sc_locked != 0) { - printf("%s: trigger_output: already running\n", - sc->sc_dev.dv_xname); - return (EINVAL); - } - - sc->sc_locked = 1; sc->sc_pintr = intr; sc->sc_parg = arg; @@ -1395,14 +1440,14 @@ ce4231_trigger_output(addr, start, end, blksize, intr, arg, param) * Do only `blksize' at a time, so audio_pint() is kept * synchronous with us... */ - sc->sc_blksz = blksize; - sc->sc_nowplaying = p; - sc->sc_playsegsz = n; + chdma->cur_dma = p; + chdma->blksz = blksize; + chdma->segsz = n; - if (n > sc->sc_blksz) - n = sc->sc_blksz; + if (n > chdma->blksz) + n = chdma->blksz; - sc->sc_playcnt = n; + chdma->count = n; csr = P_READ(sc, EBDMA_DCSR); if (csr & EBDCSR_DMAEN) { @@ -1425,7 +1470,7 @@ ce4231_trigger_output(addr, start, end, blksize, intr, arg, param) ce4231_write(sc, SP_INTERFACE_CONFIG, ce4231_read(sc, SP_INTERFACE_CONFIG) | PLAYBACK_ENABLE); } - sc->sc_lastaddr = p->dmamap->dm_segs[0].ds_addr + n; + chdma->lastaddr = p->dmamap->dm_segs[0].ds_addr + n; return (0); } @@ -1438,5 +1483,60 @@ ce4231_trigger_input(addr, start, end, blksize, intr, arg, param) void *arg; struct audio_params *param; { - return (ENXIO); + struct ce4231_softc *sc = addr; + struct cs_dma *p; + struct cs_chdma *chdma = &sc->sc_rchdma; + u_int32_t csr; + vaddr_t n; + + sc->sc_rintr = intr; + sc->sc_rarg = arg; + + for (p = sc->sc_dmas; p->addr != start; p = p->next) + /*EMPTY*/; + if (p == NULL) { + printf("%s: trigger_input: bad addr: %p\n", + sc->sc_dev.dv_xname, start); + return (EINVAL); + } + + n = (char *)end - (char *)start; + + /* + * Do only `blksize' at a time, so audio_rint() is kept + * synchronous with us... + */ + chdma->cur_dma = p; + chdma->blksz = blksize; + chdma->segsz = n; + + if (n > chdma->blksz) + n = chdma->blksz; + + chdma->count = n; + + csr = C_READ(sc, EBDMA_DCSR); + if (csr & EBDCSR_DMAEN) { + C_WRITE(sc, EBDMA_DCNT, (u_long)n); + C_WRITE(sc, EBDMA_DADDR, + (u_long)p->dmamap->dm_segs[0].ds_addr); + } else { + C_WRITE(sc, EBDMA_DCSR, EBDCSR_RESET); + C_WRITE(sc, EBDMA_DCSR, sc->sc_burst); + + C_WRITE(sc, EBDMA_DCNT, (u_long)n); + C_WRITE(sc, EBDMA_DADDR, + (u_long)p->dmamap->dm_segs[0].ds_addr); + + C_WRITE(sc, EBDMA_DCSR, sc->sc_burst | EBDCSR_WRITE | + EBDCSR_DMAEN | EBDCSR_INTEN | EBDCSR_CNTEN | EBDCSR_NEXTEN); + + ce4231_write(sc, CS_LOWER_REC_CNT, 0xff); + ce4231_write(sc, CS_UPPER_REC_CNT, 0xff); + ce4231_write(sc, SP_INTERFACE_CONFIG, + ce4231_read(sc, SP_INTERFACE_CONFIG) | CAPTURE_ENABLE); + } + chdma->lastaddr = p->dmamap->dm_segs[0].ds_addr + n; + + return (0); } diff --git a/sys/arch/sparc64/dev/ce4231var.h b/sys/arch/sparc64/dev/ce4231var.h index 12cd98fe984..37d43ff10c6 100644 --- a/sys/arch/sparc64/dev/ce4231var.h +++ b/sys/arch/sparc64/dev/ce4231var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ce4231var.h,v 1.10 2010/07/26 20:06:12 jakemsr Exp $ */ +/* $OpenBSD: ce4231var.h,v 1.11 2010/07/26 23:17:19 jakemsr Exp $ */ /* * Copyright (c) 1999 Jason L. Wright (jason@thought.net) @@ -52,8 +52,14 @@ struct cs_volume { u_int8_t right; }; -/* number of levels on the card, these relate to CSPORT_* */ -#define CS4231_LVLS 7 +/* DMA info container for each channel (play and record). */ +struct cs_chdma { + struct cs_dma *cur_dma; + u_int32_t blksz; + u_int32_t count; + u_int32_t segsz; + u_int32_t lastaddr; +}; struct ce4231_softc { struct device sc_dev; /* base device */ @@ -65,7 +71,6 @@ struct ce4231_softc { bus_space_handle_t sc_pdmahandle; /* playback DMA handle */ bus_space_handle_t sc_auxhandle; /* AUX handle */ int sc_open; /* already open? */ - int sc_locked; /* locked? */ void (*sc_rintr)(void *); /* input completion intr handler */ void *sc_rarg; /* arg for sc_rintr() */ @@ -78,12 +83,9 @@ struct ce4231_softc { int sc_need_commit; int sc_channels; u_int sc_last_format; - u_int32_t sc_blksz; - u_int32_t sc_playcnt; - u_int32_t sc_playsegsz; u_int32_t sc_burst; - u_int32_t sc_lastaddr; struct cs_dma *sc_dmas; /* dma list */ - struct cs_dma *sc_nowplaying; + struct cs_chdma sc_pchdma; + struct cs_chdma sc_rchdma; void *sc_pih, *sc_cih; }; -- 2.20.1