audio(9) has two different methods for managing DMA transfers.  the
original method is essentially a callback that gives the low level
driver the current kernel audio buffer position and block size
when each block is to be moved.  the newer (well, at least 12 yrs
old) method just gives the low level driver the blocksize and
kernel audio buffer start and end addresses when DMA is started.

the old method is useful in some circumstances, but is unnecessary
for most devices.  the drivers that use the AD1848 DMA engine
directly do not need to use the older method, but they were never
changed to the new method (in OpenBSD, NetBSD did change them >11
years ago).

so, this diff changes the ad1848 ISA drivers to use the newer method.

the CS4231 is very similar to the AD1848, and the CS4231 is also
supported by these drivers.  the biggest difference is that the
AD1848 only supports a single DMA channel, while the CS4231 supports
two DMA channels.  this means the AD1848 is not capable of full-
duplex but the CS4231 is.  single channel DMA operations is referred
to as "Mode 1" and two channel DMA operation is referred to as
"Mode 2".  currently, CS4231 chips are forced into Mode 2, even if
there is only one DMA channel available.  this makes recording
not work if there is only one DMA channel available.  so I also
made it so CS4231 can be used in Mode 1, and both playback and
recording work.  and in mode 2, full-duplex currently doesn't work
because of a couple reasons, but mainly because only one of the
capture or playback (instead of both) interrupt callbacks gets
called.  this diff fixes full-duplex mode for Mode 2 capable
devices as well.  but, there is a bandwidth limit.  for full-duplex
to work, you need to use either 8-bit samples, mono, or a sample
rate <= 24000.

and finally, the AD1848 and CS4231 recalibrate their sample clock
after mode changes.  the tests to check if calibration has ended are
currently wrong, causing busy loops in the driver.  when starting
audio playback, my machine is noticibly "stuck" for a second or two.
this diff fixes that issue as well.

what I'm mostly looking for are tests that this doesn't break any
current setup.  I imagine these devices probably aren't used much
these days, but maybe I'm wrong.

-- 
jake...@sdf.lonestar.org
SDF Public Access UNIX System - http://sdf.lonestar.org

Index: ic/cs4231reg.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/cs4231reg.h,v
retrieving revision 1.6
diff -u -p ic/cs4231reg.h
--- ic/cs4231reg.h      26 Jun 2008 05:42:15 -0000      1.6
+++ ic/cs4231reg.h      3 Jun 2010 07:42:30 -0000
@@ -77,26 +77,32 @@
 #define CS_ALT_FEATURE2                0x11
 #define CS_LEFT_LINE_CONTROL   0x12
 #define CS_RIGHT_LINE_CONTROL  0x13
+#define                LINE_INPUT_ATTEN_BITS   0x1f
+#define                LINE_INPUT_ATTEN_MASK   0xe0
+#define                LINE_INPUT_MUTE         0x80
+#define                LINE_INPUT_MUTE_MASK    0x7f
 #define CS_TIMER_LOW           0x14
 #define CS_TIMER_HIGH          0x15
 #define CS_UPPER_FREQUENCY_SEL 0x16
 #define CS_LOWER_FREQUENCY_SEL 0x17
 #define CS_IRQ_STATUS          0x18
+#define                CS_IRQ_PU               0x01    /* Playback Underrun */
+#define                CS_IRQ_PO               0x02    /* Playback Overrun */
+#define                CS_IRQ_CO               0x04    /* Capture Overrrun */
+#define                CS_IRQ_CU               0x08    /* Capture Underrun */
+#define                CS_IRQ_PI               0x10    /* Playback Interrupt */
+#define                CS_IRQ_CI               0x20    /* Capture Interrupt */
+#define                CS_IRQ_TI               0x40    /* Timer Interrupt */
+#define                CS_IRQ_RES              0x80    /* reserved */
 #define CS_VERSION_ID          0x19
 #define CS_MONO_IO_CONTROL     0x1A
+#define                MONO_INPUT_ATTEN_BITS   0x0f
+#define                MONO_INPUT_ATTEN_MASK   0xf0
+#define                MONO_OUTPUT_MUTE        0x40
+#define                MONO_INPUT_MUTE         0x80
+#define                MONO_INPUT_MUTE_MASK    0x7f
 #define CS_POWERDOWN_CONTROL   0x1B
 #define CS_REC_FORMAT          0x1C
 #define CS_XTAL_SELECT         0x1D
 #define CS_UPPER_REC_CNT       0x1E
 #define CS_LOWER_REC_CNT       0x1F
-
-#define MONO_INPUT_ATTEN_BITS  0x0f
-#define MONO_INPUT_ATTEN_MASK  0xf0
-#define MONO_OUTPUT_MUTE       0x40
-#define MONO_INPUT_MUTE                0x80
-#define MONO_INPUT_MUTE_MASK   0x7f
-
-#define LINE_INPUT_ATTEN_BITS  0x1f
-#define LINE_INPUT_ATTEN_MASK  0xe0
-#define LINE_INPUT_MUTE                0x80
-#define LINE_INPUT_MUTE_MASK   0x7f
Index: isa/ad1848.c
===================================================================
RCS file: /cvs/src/sys/dev/isa/ad1848.c,v
retrieving revision 1.33
diff -u -p isa/ad1848.c
--- isa/ad1848.c        5 Nov 2007 00:17:28 -0000       1.33
+++ isa/ad1848.c        3 Jun 2010 07:42:30 -0000
@@ -92,9 +92,11 @@
 #include <dev/isa/ad1848var.h>
 #include <dev/isa/cs4231var.h>
 
+/* #define AUDIO_DEBUG */
+
 #ifdef AUDIO_DEBUG
-#define DPRINTF(x)     if (ad1848debug) printf x
-int    ad1848debug = 0;
+#define DPRINTF(x)     do { if (ad1848debug) printf x; } while (0);
+int    ad1848debug = 3;
 #else
 #define DPRINTF(x)
 #endif
@@ -149,11 +151,13 @@ void      ad1848_reset(struct ad1848_softc *);
 int    ad1848_set_speed(struct ad1848_softc *, u_long *);
 void   ad1848_mute_monitor(void *, int);
 
+/* indirect register access */
 static int ad_read(struct ad1848_softc *, int);
 static void ad_write(struct ad1848_softc *, int, int);
 static void ad_set_MCE(struct ad1848_softc *, int);
 static void wait_for_calibration(struct ad1848_softc *);
 
+/* direct register (AD1848_{IADDR,IDATA,STATUS} only) access */
 #define ADREAD(sc, addr) bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, 
(sc)->sc_iooffs+(addr))
 #define ADWRITE(sc, addr, data) bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, 
(sc)->sc_iooffs+(addr), (data))
 
@@ -209,34 +213,32 @@ wait_for_calibration(sc)
     /*
      * Wait until the auto calibration process has finished.
      *
-     * 1) Wait until the chip becomes ready (reads don't return 0x80).
-     * 2) Wait until the ACI bit of I11 gets on and then off.
+     * 1) Wait until the chip becomes ready (reads don't return SP_IN_INIT).
+     * 2) Wait until the ACI bit of I11 goes hi and then lo.
+     *   a) With AD1848 alike, ACI goes hi within 5 sample cycles
+     *      and remains hi for ~384 sample periods.
+     *   b) With CS4231 alike, ACI goes hi immediately and remains
+     *      hi for at least 168 sample periods.
      */
-    timeout = 100000;
+    timeout = AD1848_TIMO;
     while (timeout > 0 && ADREAD(sc, AD1848_IADDR) == SP_IN_INIT)
        timeout--;
 
     if (ADREAD(sc, AD1848_IADDR) == SP_IN_INIT)
        DPRINTF(("ad1848: Auto calibration timed out(1).\n"));
 
-    ADWRITE(sc, AD1848_IADDR, SP_TEST_AND_INIT);
-    timeout = 100000;
-    while (timeout > 0 && ADREAD(sc, AD1848_IADDR) != SP_TEST_AND_INIT)
-       timeout--;
-
-    if (ADREAD(sc, AD1848_IADDR) == SP_TEST_AND_INIT)
-       DPRINTF(("ad1848: Auto calibration timed out(1.5).\n"));
-
-    if (!(ad_read(sc, SP_TEST_AND_INIT) & AUTO_CAL_IN_PROG)) {
-       timeout = 100000;
-       while (timeout > 0 && !(ad_read(sc, SP_TEST_AND_INIT) & 
AUTO_CAL_IN_PROG))
+    if (!(sc->sc_flags & AD1848_FLAG_32REGS)) {
+       timeout = AD1848_TIMO;
+       while (timeout > 0 &&
+        !(ad_read(sc, SP_TEST_AND_INIT) & AUTO_CAL_IN_PROG))
            timeout--;
 
-       if (!(ad_read(sc, SP_TEST_AND_INIT) & AUTO_CAL_IN_PROG))
+       if (!(ad_read(sc, SP_TEST_AND_INIT) & AUTO_CAL_IN_PROG)) {
            DPRINTF(("ad1848: Auto calibration timed out(2).\n"));
+       }
     }
 
-    timeout = 100000;
+    timeout = AD1848_TIMO;
     while (timeout > 0 && ad_read(sc, SP_TEST_AND_INIT) & AUTO_CAL_IN_PROG)
        timeout--;
     if (ad_read(sc, SP_TEST_AND_INIT) & AUTO_CAL_IN_PROG)
@@ -311,6 +313,7 @@ ad1848_probe(sc)
     /* Is there an ad1848 chip ? */
     sc->MCE_bit = MODE_CHANGE_ENABLE;
     sc->mode = 1;      /* MODE 1 = original ad1848/ad1846/cs4248 */
+    sc->sc_flags = 0;
     
     /*
      * Check that the I/O address is in use.
@@ -460,15 +463,18 @@ ad1848_probe(sc)
                    sc->chip_name = "CS4231 or AD1845";
                    break;
                case 0x82:
-               case 0xa2:
                    sc->chip_name = "CS4232";
                    break;
+               case 0xa2:
+                   sc->chip_name = "CS4232C";
+                   break;
                case 0x03:
                    sc->chip_name = "CS4236/CS4236B";
                    break;
                }
            }
            sc->mode = 2;
+           sc->sc_flags |= AD1848_FLAG_32REGS;
        }
     }
 
@@ -508,9 +514,8 @@ ad1848_attach(sc)
     struct audio_params pparams, rparams;
     int timeout;
     
-    sc->sc_locked = 0;
-    sc->sc_playrun = NOTRUNNING;
-    sc->sc_recrun = NOTRUNNING;
+    sc->sc_playrun = 0;
+    sc->sc_recrun = 0;
 
     if (sc->sc_drq != -1) {
        if (isa_dmamap_create(sc->sc_isa, sc->sc_drq, MAX_ISADMA,
@@ -523,7 +528,7 @@ ad1848_attach(sc)
     if (sc->sc_recdrq != -1 && sc->sc_recdrq != sc->sc_drq) {
        if (isa_dmamap_create(sc->sc_isa, sc->sc_recdrq, MAX_ISADMA,
            BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) {
-               printf("ad1848_attach: can't creape map for drq %d\n",
+               printf("ad1848_attach: can't create map for second drq %d\n",
                    sc->sc_recdrq);
                return;
        }
@@ -532,19 +537,26 @@ ad1848_attach(sc)
     /* Initialize the ad1848... */
     for (i = 0; i < 0x10; i++) {
        ad_write(sc, i, ad1848_init_values[i]);
-        timeout = 100000;
-        while (timeout > 0 && ad_read(sc, AD1848_IADDR) & SP_IN_INIT)
+        timeout = AD1848_TIMO;
+        while (timeout > 0 && ADREAD(sc, AD1848_IADDR) & SP_IN_INIT)
            timeout--;
     }
+    /* need 2 separate drqs for mode 2 */
+    if ((sc->mode == 2) &&
+      ((sc->sc_recdrq == -1) || (sc->sc_recdrq == sc->sc_drq))) {
+       ad_write(sc, SP_MISC_INFO, ad_read(sc, SP_MISC_INFO) & ~MODE2);
+       if (!(ad_read(sc, SP_MISC_INFO) & MODE2))
+           sc->mode = 1;
+    }
     /* ...and additional CS4231 stuff too */
     if (sc->mode == 2) {
            ad_write(sc, SP_INTERFACE_CONFIG, 0); /* disable SINGLE_DMA */
            for (i = 0x10; i < 0x20; i++)
                    if (ad1848_init_values[i] != 0) {
                            ad_write(sc, i, ad1848_init_values[i]);
-                           timeout = 100000;
+                           timeout = AD1848_TIMO;
                            while (timeout > 0 && 
-                                  ad_read(sc, AD1848_IADDR) & SP_IN_INIT)
+                                  ADREAD(sc, AD1848_IADDR) & SP_IN_INIT)
                                timeout--;
                    }
     }
@@ -572,7 +584,6 @@ ad1848_attach(sc)
 
     if (sc->chip_name)
        printf(": %s", sc->chip_name);
-#undef WAITREADY
 }
 
 /*
@@ -1053,29 +1064,29 @@ ad1848_set_params(addr, setmode, usemode, p, r)
     }
     switch (enc) {
     case AUDIO_ENCODING_ULAW:
-       bits = FMT_ULAW >> 5;
+       bits = FMT_ULAW;
        break;
     case AUDIO_ENCODING_ALAW:
-       bits = FMT_ALAW >> 5;
+       bits = FMT_ALAW;
        break;
     case AUDIO_ENCODING_ADPCM:
-       bits = FMT_ADPCM >> 5;
+       bits = FMT_ADPCM;
        break;
     case AUDIO_ENCODING_SLINEAR_LE:
        if (p->precision == 16)
-           bits = FMT_TWOS_COMP >> 5;
+           bits = FMT_TWOS_COMP;
        else
            return EINVAL;
        break;
     case AUDIO_ENCODING_SLINEAR_BE:
        if (p->precision == 16)
-           bits = FMT_TWOS_COMP_BE >> 5;
+           bits = FMT_TWOS_COMP_BE;
        else
            return EINVAL;
        break;
     case AUDIO_ENCODING_ULINEAR_LE:
        if (p->precision == 8)
-           bits = FMT_PCM8 >> 5;
+           bits = FMT_PCM8;
        else
            return EINVAL;
        break;
@@ -1151,10 +1162,6 @@ ad1848_round_blocksize(addr, blk)
     void *addr;
     int blk;
 {
-    struct ad1848_softc *sc = addr;
-
-    sc->sc_lastcc = -1;
-
     /* Round to a multiple of the biggest sample size. */
     blk = (blk + 3) & -4;
 
@@ -1170,16 +1177,15 @@ ad1848_open(addr, flags)
 
     DPRINTF(("ad1848_open: sc=%p\n", sc));
 
-    sc->sc_intr = 0;
-    sc->sc_lastcc = -1;
-    sc->sc_locked = 0;
+    sc->sc_pintr = sc->sc_parg = NULL;
+    sc->sc_rintr = sc->sc_rarg = NULL;
 
     /* Enable interrupts */
     DPRINTF(("ad1848_open: enable intrs\n"));
     ad_write(sc, SP_PIN_CONTROL, INTERRUPT_ENABLE|ad_read(sc, SP_PIN_CONTROL));
 
 #ifdef AUDIO_DEBUG
-    if (ad1848debug)
+    if (ad1848debug > 2)
        ad1848_dump_regs(sc);
 #endif
 
@@ -1195,18 +1201,15 @@ ad1848_close(addr)
 {
     struct ad1848_softc *sc = addr;
     u_char r;
-    
-    sc->sc_intr = 0;
 
+    ad1848_halt_output(sc);
+    ad1848_halt_input(sc);
+
+    sc->sc_pintr = NULL;
+    sc->sc_rintr = NULL;
+
     DPRINTF(("ad1848_close: stop DMA\n"));
-    if (sc->sc_playrun != NOTRUNNING) {
-       isa_dmaabort(sc->sc_isa, sc->sc_drq);
-       sc->sc_playrun = NOTRUNNING;
-    }
-    if (sc->sc_recrun != NOTRUNNING) {
-       isa_dmaabort(sc->sc_isa, sc->sc_recdrq);
-       sc->sc_recrun = NOTRUNNING;
-    }
+
     ad_write(sc, SP_LOWER_BASE_COUNT, (u_char)0);
     ad_write(sc, SP_UPPER_BASE_COUNT, (u_char)0);
 
@@ -1221,7 +1224,7 @@ ad1848_close(addr)
     ad_write(sc, SP_INTERFACE_CONFIG, r);
 
 #ifdef AUDIO_DEBUG
-    if (ad1848debug)
+    if (ad1848debug > 2)
        ad1848_dump_regs(sc);
 #endif
 }
@@ -1247,7 +1250,7 @@ ad1848_commit_settings(addr)
     
     ad_set_MCE(sc, 1);         /* Enables changes to the format select reg */
 
-    fs = sc->speed_bits | (sc->format_bits << 5);
+    fs = sc->speed_bits | sc->format_bits;
 
     if (sc->channels == 2)
        fs |= FMT_STEREO;
@@ -1266,7 +1269,7 @@ ad1848_commit_settings(addr)
        /*
         * Write to I8 starts resynchronization. Wait until it completes.
         */
-       timeout = 100000;
+       timeout = AD1848_TIMO;
        while (timeout > 0 && ADREAD(sc, AD1848_IADDR) == SP_IN_INIT)
            timeout--;
 
@@ -1281,7 +1284,7 @@ ad1848_commit_settings(addr)
     /*
      * Write to I8 starts resynchronization. Wait until it completes.
      */
-    timeout = 100000;
+    timeout = AD1848_TIMO;
     while (timeout > 0 && ADREAD(sc, AD1848_IADDR) == SP_IN_INIT)
        timeout--;
 
@@ -1297,8 +1300,6 @@ ad1848_commit_settings(addr)
 
     ad1848_mute_monitor(sc, 0);
 
-    sc->sc_lastcc = -1;
-
     splx(s);
     
     sc->need_commit = 0;
@@ -1318,14 +1319,13 @@ ad1848_reset(sc)
     r &= ~(CAPTURE_ENABLE|PLAYBACK_ENABLE);
     ad_write(sc, SP_INTERFACE_CONFIG, r);
 
-    if (sc->mode == 2) {
-           ADWRITE(sc, AD1848_IADDR, CS_IRQ_STATUS);
-           ADWRITE(sc, AD1848_IDATA, 0);
-    }
     /* Clear interrupt status */
+    if (sc->mode == 2)
+       ad_write(sc, CS_IRQ_STATUS, 0);
     ADWRITE(sc, AD1848_STATUS, 0);
+
 #ifdef AUDIO_DEBUG
-    if (ad1848debug)
+    if (ad1848debug > 2)
        ad1848_dump_regs(sc);
 #endif
 }
@@ -1407,198 +1407,121 @@ ad1848_set_speed(sc, argp)
  * Halt a DMA in progress.
  */
 int
-ad1848_halt_out_dma(addr)
+ad1848_halt_output(addr)
     void *addr;
 {
     struct ad1848_softc *sc = addr;
     u_char reg;
-       
-    DPRINTF(("ad1848: ad1848_halt_out_dma\n"));
 
+    DPRINTF(("ad1848: ad1848_halt_output\n"));
+
     reg = ad_read(sc, SP_INTERFACE_CONFIG);
     ad_write(sc, SP_INTERFACE_CONFIG, (reg & ~PLAYBACK_ENABLE));
-    sc->sc_locked = 0;
 
+    if (sc->sc_playrun == 1) {
+       isa_dmaabort(sc->sc_isa, sc->sc_drq);
+       sc->sc_playrun = 0;
+    }
+
     return(0);
 }
 
 int
-ad1848_halt_in_dma(addr)
+ad1848_halt_input(addr)
     void *addr;
 {
     struct ad1848_softc *sc = addr;
     u_char reg;
     
-    DPRINTF(("ad1848: ad1848_halt_in_dma\n"));
+    DPRINTF(("ad1848: ad1848_halt_input\n"));
 
     reg = ad_read(sc, SP_INTERFACE_CONFIG);
     ad_write(sc, SP_INTERFACE_CONFIG, (reg & ~CAPTURE_ENABLE));
-    sc->sc_locked = 0;
 
+    if (sc->sc_recrun == 1) {
+       isa_dmaabort(sc->sc_isa, sc->sc_recdrq);
+       sc->sc_recrun = 0;
+    }
+
     return(0);
 }
 
 int
-ad1848_dma_init_input(addr, buf, cc)
+ad1848_trigger_input(addr, start, end, blksize, intr, arg, param)
     void *addr;
-    void *buf;
-    int cc;
-{
-    struct ad1848_softc *sc = addr;
-
-    sc->sc_recrun = DMARUNNING;
-    sc->sc_dma_flags = DMAMODE_READ | DMAMODE_LOOP;
-    sc->sc_dma_bp = buf;
-    sc->sc_dma_cnt = cc;
-    isa_dmastart(sc->sc_isa, sc->sc_recdrq, buf, cc, NULL,
-                sc->sc_dma_flags, BUS_DMA_NOWAIT);
-    DPRINTF(("ad1848_dma_init_input: %p %d\n", buf, cc));
-    return 0;
-}
-
-/*
- * DMA input/output are called at splaudio().
- */
-int
-ad1848_dma_input(addr, p, cc, intr, arg)
-    void *addr;
-    void *p;
-    int cc;
+    void *start, *end;
+    int blksize;
     void (*intr)(void *);
     void *arg;
+    struct audio_params *param;
 {
     struct ad1848_softc *sc = addr;
     u_char reg;
-    
-    if (sc->sc_locked) {
-       DPRINTF(("ad1848_dma_input: locked\n"));
-       return 0;
+
+    if (sc->sc_recdrq == -1) {
+       DPRINTF(("ad1848_trigger_input: invalid recording drq\n"));
+       return ENXIO;
     }
     
-#ifdef AUDIO_DEBUG
-    if (ad1848debug > 1)
-       printf("ad1848_dma_input: cc=%d %p (%p)\n", cc, intr, arg);
-#endif
-    sc->sc_locked = 1;
-    sc->sc_intr = intr;
-    sc->sc_arg = arg;
+    isa_dmastart(sc->sc_isa, sc->sc_recdrq, start, (char *)end - (char *)start,
+       NULL, DMAMODE_READ | DMAMODE_LOOP, BUS_DMA_NOWAIT);
 
-    switch (sc->sc_recrun) {
-    case NOTRUNNING:
-       sc->sc_dma_flags = DMAMODE_READ;
-       sc->sc_dma_bp = p;
-       sc->sc_dma_cnt = cc;
-       isa_dmastart(sc->sc_isa, sc->sc_recdrq, p, cc, NULL,
-                    DMAMODE_READ, BUS_DMA_NOWAIT);
-       goto startpcm;
-    case DMARUNNING:
-       sc->sc_recrun = PCMRUNNING;
-    startpcm:
-       if (sc->precision == 16)
-           cc >>= 1;
-       if (sc->channels == 2)
-           cc >>= 1;
-       cc--;
-    
-       if (sc->sc_lastcc != cc || sc->sc_mode != AUMODE_RECORD) {
-           ad_write(sc, SP_LOWER_BASE_COUNT, (u_char)(cc & 0xff));
-           ad_write(sc, SP_UPPER_BASE_COUNT, (u_char)((cc >> 8) & 0xff));
+    sc->sc_recrun = 1;
+    sc->sc_rintr = intr;
+    sc->sc_rarg = arg;
 
-           if (sc->mode == 2) {
-               ad_write(sc, CS_LOWER_REC_CNT, (u_char)(cc & 0xff));
-               ad_write(sc, CS_UPPER_REC_CNT, (u_char)((cc >> 8) & 0xff));
-           }
+    blksize = (blksize * NBBY) / (param->precision * param->channels) - 1;
 
-           reg = ad_read(sc, SP_INTERFACE_CONFIG);
-           ad_write(sc, SP_INTERFACE_CONFIG, (CAPTURE_ENABLE|reg));
+    if (sc->mode == 2) {
+       ad_write(sc, CS_LOWER_REC_CNT, (blksize & 0xff));
+       ad_write(sc, CS_UPPER_REC_CNT, ((blksize >> 8) & 0xff));
+    } else {
+        ad_write(sc, SP_LOWER_BASE_COUNT, blksize & 0xff);
+        ad_write(sc, SP_UPPER_BASE_COUNT, (blksize >> 8) & 0xff);
+    }
 
-           sc->sc_lastcc = cc;
-           sc->sc_mode = AUMODE_RECORD;
+    reg = ad_read(sc, SP_INTERFACE_CONFIG);
+    ad_write(sc, SP_INTERFACE_CONFIG, (CAPTURE_ENABLE|reg));
+
 #ifdef AUDIO_DEBUG
-           if (ad1848debug > 1)
-                   printf("ad1848_dma_input: started capture\n");
+    if (ad1848debug > 1)
+           printf("ad1848_trigger_input: started capture\n");
 #endif
-       }
-    case PCMRUNNING:
-       break;
-    }
     
     return 0;
 }
 
 int
-ad1848_dma_init_output(addr, buf, cc)
+ad1848_trigger_output(addr, start, end, blksize, intr, arg, param)
     void *addr;
-    void *buf;
-    int cc;
-{
-    struct ad1848_softc *sc = addr;
-
-    sc->sc_playrun = DMARUNNING;
-    sc->sc_dma_flags = DMAMODE_WRITE | DMAMODE_LOOP;
-    sc->sc_dma_bp = buf;
-    sc->sc_dma_cnt = cc;
-    isa_dmastart(sc->sc_isa, sc->sc_drq, buf, cc, NULL,
-                sc->sc_dma_flags, BUS_DMA_NOWAIT);
-    DPRINTF(("ad1848_dma_init_output: %p %d\n", buf, cc));
-    return 0;
-}
-
-int
-ad1848_dma_output(addr, p, cc, intr, arg)
-    void *addr;
-    void *p;
-    int cc;
+    void *start, *end;
+    int blksize;
     void (*intr)(void *);
     void *arg;
+    struct audio_params *param;
 {
     struct ad1848_softc *sc = addr;
     u_char reg;
     
-    if (sc->sc_locked) {
-       DPRINTF(("ad1848_dma_output: locked\n"));
-       return 0;
-    }
-    
-#ifdef AUDIO_DEBUG
-    if (ad1848debug > 0)
-       printf("ad1848_dma_output: cc=%d at %p %p (%p)\n", cc, p, intr, arg);
-#endif
-    sc->sc_locked = 1;
-    sc->sc_intr = intr;
-    sc->sc_arg = arg;
+    isa_dmastart(sc->sc_isa, sc->sc_drq, start, (char *)end - (char *)start,
+      NULL, DMAMODE_WRITE | DMAMODE_LOOP, BUS_DMA_NOWAIT);
 
-    switch (sc->sc_playrun) {
-    case NOTRUNNING:
-       sc->sc_dma_flags = DMAMODE_WRITE;
-       sc->sc_dma_bp = p;
-       sc->sc_dma_cnt = cc;
-       isa_dmastart(sc->sc_isa, sc->sc_drq, p, cc, NULL,
-                    DMAMODE_WRITE, BUS_DMA_NOWAIT);
-       goto startpcm;
-    case DMARUNNING:
-       sc->sc_playrun = PCMRUNNING;
-    startpcm:
-       if (sc->precision == 16)
-           cc >>= 1;
-       if (sc->channels == 2)
-           cc >>= 1;
-       cc--;
+    sc->sc_playrun = 1;
+    sc->sc_pintr = intr;
+    sc->sc_parg = arg;
 
-       if (sc->sc_lastcc != cc || sc->sc_mode != AUMODE_PLAY) {
-           ad_write(sc, SP_LOWER_BASE_COUNT, (u_char)(cc & 0xff));
-           ad_write(sc, SP_UPPER_BASE_COUNT, (u_char)((cc >> 8) & 0xff));
+    blksize = (blksize * NBBY) / (param->precision * param->channels) - 1;
 
-           reg = ad_read(sc, SP_INTERFACE_CONFIG);
-           ad_write(sc, SP_INTERFACE_CONFIG, (PLAYBACK_ENABLE|reg));
+    ad_write(sc, SP_LOWER_BASE_COUNT, blksize & 0xff);
+    ad_write(sc, SP_UPPER_BASE_COUNT, (blksize >> 8) & 0xff);
 
-           sc->sc_lastcc = cc;
-           sc->sc_mode = AUMODE_PLAY;
-       }
-       break;
-    case PCMRUNNING:
-       break;
-    }
+    reg = ad_read(sc, SP_INTERFACE_CONFIG);
+    ad_write(sc, SP_INTERFACE_CONFIG, (PLAYBACK_ENABLE|reg));
+
+#ifdef AUDIO_DEBUG
+    if (ad1848debug > 1)
+           printf("ad1848_trigger_output: started playback\n");
+#endif
     
     return 0;
 }
@@ -1616,25 +1539,40 @@ ad1848_intr(arg)
     
 #ifdef AUDIO_DEBUG
     if (ad1848debug > 1)
-       printf("ad1848_intr: intr=%p status=%x\n", sc->sc_intr, status);
+       printf("ad1848_intr: mode=%d pintr=%p prun=%d rintr=%p rrun=%d 
status=0x%x\n",
+           sc->mode, sc->sc_pintr, sc->sc_playrun, sc->sc_rintr, 
sc->sc_recrun, status);
 #endif
-    sc->sc_locked = 0;
-    sc->sc_interrupts++;
     
     /* Handle interrupt */
-    if (sc->sc_intr && (status & INTERRUPT_STATUS)) {
-       /* ACK DMA read because it may be in a bounce buffer */
-       /* XXX Do write to mask DMA ? */
-       if ((sc->sc_dma_flags & DMAMODE_READ) && sc->sc_recrun == NOTRUNNING)
-           isa_dmadone(sc->sc_isa, sc->sc_recdrq);
-       (*sc->sc_intr)(sc->sc_arg);
-       retval = 1;
+    if ((status & INTERRUPT_STATUS) != 0) {
+       if (sc->mode == 2) {
+               status = ad_read(sc, CS_IRQ_STATUS);
+#ifdef AUDIO_DEBUG
+               if (ad1848debug > 2)
+                       printf("ad1848_intr: cs_irq_status=0x%x (play=0x%x 
rec0x%x)\n",
+                           status, CS_IRQ_PI, CS_IRQ_CI);
+#endif
+               if ((status & CS_IRQ_PI) && sc->sc_playrun) {
+                       (*sc->sc_pintr)(sc->sc_parg);
+                       retval = 1;
+               }
+               if ((status & CS_IRQ_CI) && sc->sc_recrun) {
+                       (*sc->sc_rintr)(sc->sc_rarg);
+                       retval = 1;
+               }
+       } else {
+               if (sc->sc_playrun) {
+                       (*sc->sc_pintr)(sc->sc_parg);
+                       retval = 1;
+               } else if (sc->sc_recrun) {
+                       (*sc->sc_rintr)(sc->sc_rarg);
+                       retval = 1;
+               }
+       }
+       /* clear interrupt */
+       ADWRITE(sc, AD1848_STATUS, 0);
     }
 
-    /* clear interrupt */
-    if (status & INTERRUPT_STATUS)
-       ADWRITE(sc, AD1848_STATUS, 0);
-
     return(retval);
 }
 
@@ -1649,10 +1587,10 @@ ad1848_malloc(addr, direction, size, pool, flags)
        struct ad1848_softc *sc = addr;
        int drq;
 
-       if (sc->sc_mode == AUMODE_RECORD)
-               drq = sc->sc_recdrq == -1 ? sc->sc_drq : sc->sc_recdrq;
-       else
+       if (direction == AUMODE_PLAY)
                drq = sc->sc_drq;
+       else
+               drq = sc->sc_recdrq;
 
        return isa_malloc(sc->sc_isa, drq, size, pool, flags);
 }
@@ -1694,5 +1632,5 @@ ad1848_get_props(addr)
        struct ad1848_softc *sc = addr;
 
        return AUDIO_PROP_MMAP |
-              (sc->sc_drq != sc->sc_recdrq ? AUDIO_PROP_FULLDUPLEX : 0);
+              (sc->mode == 2 ? AUDIO_PROP_FULLDUPLEX : 0);
 }
Index: isa/ad1848var.h
===================================================================
RCS file: /cvs/src/sys/dev/isa/ad1848var.h,v
retrieving revision 1.12
diff -u -p isa/ad1848var.h
--- isa/ad1848var.h     14 Mar 2002 03:16:05 -0000      1.12
+++ isa/ad1848var.h     3 Jun 2010 07:42:30 -0000
@@ -37,6 +37,7 @@
  */
 
 #define AD1848_NPORT   4
+#define        AD1848_TIMO     1000000
 
 struct ad1848_volume {
        u_char  left;
@@ -54,24 +55,16 @@ struct ad1848_softc {
        void    *parent;
        struct  device *sc_isa;         /* ISA bus's device */
 
-       u_short sc_locked;              /* true when doing HS DMA  */
-       u_int   sc_lastcc;              /* size of last DMA xfer */
-       int     sc_mode;                /* half-duplex record/play */
-       
-       int     sc_dma_flags;
-       void    *sc_dma_bp;
-       u_int   sc_dma_cnt;
-
        char    sc_playrun;             /* running in continuous mode */
        char    sc_recrun;              /* running in continuous mode */
-#define NOTRUNNING 0
-#define DMARUNNING 1
-#define PCMRUNNING 2
 
        int     sc_irq;                 /* interrupt */
        int     sc_drq;                 /* DMA */
        int     sc_recdrq;              /* record/capture DMA */
        
+       int     sc_flags;
+#define AD1848_FLAG_32REGS     0x01    /* newer chip (cs4231 compatible) */
+
        /* We keep track of these */
         struct ad1848_volume gains[6];
 
@@ -94,9 +87,10 @@ struct ad1848_softc {
        u_char  format_bits;
        u_char  need_commit;
 
-       u_long  sc_interrupts;          /* number of interrupts taken */
-       void    (*sc_intr)(void *);     /* dma completion intr handler */
-       void    *sc_arg;                /* arg for sc_intr() */
+       void    (*sc_pintr)(void *);    /* play dma completion intr handler */
+       void    (*sc_rintr)(void *);    /* rec dma completion intr handler */
+       void    *sc_parg;               /* play arg for sc_intr() */
+       void    *sc_rarg;               /* rec arg for sc_intr() */
 
        /* Only used by pss XXX */
        int     sc_iobase;
@@ -194,15 +188,15 @@ int       ad1848_set_params(void *, int, int, struct 
audio_p
 
 int    ad1848_round_blocksize(void *, int);
 
-int    ad1848_dma_init_output(void *, void *, int);
-int    ad1848_dma_init_input(void *, void *, int);
-int    ad1848_dma_output(void *, void *, int, void (*)(void *), void *);
-int    ad1848_dma_input(void *, void *, int, void (*)(void *), void *);
+int    ad1848_trigger_input(void *, void *, void *, int, void (*)(void *),
+       void *, struct audio_params *);
+int    ad1848_trigger_output(void *, void *, void *, int, void (*)(void *),
+       void *, struct audio_params *);
 
 int    ad1848_commit_settings(void *);
 
-int    ad1848_halt_in_dma(void *);
-int    ad1848_halt_out_dma(void *);
+int    ad1848_halt_input(void *);
+int    ad1848_halt_output(void *);
 
 int    ad1848_intr(void *);
 
Index: isa/pss.c
===================================================================
RCS file: /cvs/src/sys/dev/isa/pss.c,v
retrieving revision 1.23
diff -u -p isa/pss.c
--- isa/pss.c   21 Apr 2008 00:32:42 -0000      1.23
+++ isa/pss.c   3 Jun 2010 07:42:30 -0000
@@ -219,12 +219,12 @@ struct audio_hw_if pss_audio_if = {
        ad1848_set_params,
        ad1848_round_blocksize,
        ad1848_commit_settings,
-       ad1848_dma_init_output,
-       ad1848_dma_init_input,
-       ad1848_dma_output,
-       ad1848_dma_input,
-       ad1848_halt_out_dma,
-       ad1848_halt_in_dma,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       ad1848_halt_output,
+       ad1848_halt_input,
        pss_speaker_ctl,
        pss_getdev,
        NULL,
@@ -236,8 +236,8 @@ struct audio_hw_if pss_audio_if = {
        ad1848_round,
        ad1848_mappage,
        ad1848_get_props,
-       NULL,
-       NULL,
+       ad1848_trigger_output,
+       ad1848_trigger_input,
        NULL
 };
 
Index: isa/wss.c
===================================================================
RCS file: /cvs/src/sys/dev/isa/wss.c,v
retrieving revision 1.23
diff -u -p isa/wss.c
--- isa/wss.c   21 Apr 2008 00:32:42 -0000      1.23
+++ isa/wss.c   3 Jun 2010 07:42:30 -0000
@@ -92,13 +92,13 @@ struct audio_hw_if wss_hw_if = {
        ad1848_set_params,
        ad1848_round_blocksize,
        ad1848_commit_settings,
-       ad1848_dma_init_output,
-       ad1848_dma_init_input,
-       ad1848_dma_output,
-       ad1848_dma_input,
-       ad1848_halt_out_dma,
-       ad1848_halt_in_dma,
        NULL,
+       NULL,
+       NULL,
+       NULL,
+       ad1848_halt_output,
+       ad1848_halt_input,
+       NULL,
        wss_getdev,
        NULL,
        wss_mixer_set_port,
@@ -109,8 +109,8 @@ struct audio_hw_if wss_hw_if = {
        ad1848_round,
        ad1848_mappage,
        ad1848_get_props,
-       NULL,
-       NULL,
+       ad1848_trigger_output,
+       ad1848_trigger_input,
        NULL
 };
 
Index: isa/ym.c
===================================================================
RCS file: /cvs/src/sys/dev/isa/ym.c,v
retrieving revision 1.14
diff -u -p isa/ym.c
--- isa/ym.c    21 Apr 2008 00:32:42 -0000      1.14
+++ isa/ym.c    3 Jun 2010 07:42:30 -0000
@@ -75,13 +75,13 @@ struct audio_hw_if ym_hw_if = {
        ad1848_set_params,
        ad1848_round_blocksize,
        ad1848_commit_settings,
-       ad1848_dma_init_output,
-       ad1848_dma_init_input,
-       ad1848_dma_output,
-       ad1848_dma_input,
-       ad1848_halt_out_dma,
-       ad1848_halt_in_dma,
        NULL,
+       NULL,
+       NULL,
+       NULL,
+       ad1848_halt_output,
+       ad1848_halt_input,
+       NULL,
        ym_getdev,
        NULL,
        ym_mixer_set_port,
@@ -92,8 +92,8 @@ struct audio_hw_if ym_hw_if = {
        ad1848_round,
        ad1848_mappage,
        ad1848_get_props,
-       NULL,
-       NULL,
+       ad1848_trigger_output,
+       ad1848_trigger_input,
        NULL
 };

Reply via email to