diff options
author | jmg <jmg@FreeBSD.org> | 1997-10-31 12:24:28 +0000 |
---|---|---|
committer | jmg <jmg@FreeBSD.org> | 1997-10-31 12:24:28 +0000 |
commit | 01dd9f96d63ad1b87adb5d7deb6ee3362baebbe2 (patch) | |
tree | e9c2dd7a7137068541454b8d30fef97a895a6541 | |
parent | 21bec5640c25cac4bb8e9ae59bd0290bc84a07ad (diff) | |
download | FreeBSD-src-01dd9f96d63ad1b87adb5d7deb6ee3362baebbe2.zip FreeBSD-src-01dd9f96d63ad1b87adb5d7deb6ee3362baebbe2.tar.gz |
This updates Luigi's sound code to the basic code in snd971023...
changes:
o rip the old select from his distribution to prevent extra pollution
o the code now uses audio dma, helps reduce clicks
o improved card support, should work in full duplex on sb16 cards
o add better voxware ioctl support pointed out by Joao Carlos Mendes
Luis <jonny@coppe.ufrj.br>
o remove an unused file that I included for more complete history
o and MANY other changes
I have personally tested this code with a CS4237 based card and an AWE32
(non-PnP). Both cards worked fine in 8bit and 16bit mode.
-rw-r--r-- | sys/dev/pcm/isa/mss.c | 436 | ||||
-rw-r--r-- | sys/dev/pcm/isa/mss.h | 109 | ||||
-rw-r--r-- | sys/dev/pcm/isa/sb.c | 159 | ||||
-rw-r--r-- | sys/dev/pcm/isa/sb.h | 45 | ||||
-rw-r--r-- | sys/dev/sound/isa/mss.c | 436 | ||||
-rw-r--r-- | sys/dev/sound/isa/mss.h | 109 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb.c | 159 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb.h | 45 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb16.c | 159 | ||||
-rw-r--r-- | sys/dev/sound/isa/sb8.c | 159 | ||||
-rw-r--r-- | sys/i386/isa/snd/README | 100 | ||||
-rw-r--r-- | sys/i386/isa/snd/ad1848.c | 436 | ||||
-rw-r--r-- | sys/i386/isa/snd/dmabuf.c | 773 | ||||
-rw-r--r-- | sys/i386/isa/snd/dmabuf_auto.c | 802 | ||||
-rw-r--r-- | sys/i386/isa/snd/mss.h | 109 | ||||
-rw-r--r-- | sys/i386/isa/snd/sb_dsp.c | 159 | ||||
-rw-r--r-- | sys/i386/isa/snd/sbcard.h | 45 | ||||
-rw-r--r-- | sys/i386/isa/snd/sound.c | 260 | ||||
-rw-r--r-- | sys/i386/isa/snd/sound.h | 91 |
19 files changed, 2173 insertions, 2418 deletions
diff --git a/sys/dev/pcm/isa/mss.c b/sys/dev/pcm/isa/mss.c index 1188d32..0cd2dfd 100644 --- a/sys/dev/pcm/isa/mss.c +++ b/sys/dev/pcm/isa/mss.c @@ -1,30 +1,24 @@ /* * sound/ad1848.c * - * Modified by Luigi Rizzo (luigi@iet.unipi.it) - * * Driver for Microsoft Sound System/Windows Sound System (mss) * -compatible boards. This includes: * - * AD1848, CS4248 - * - * CS4231, used in the GUS MAX and some other cards; - * AD1845, CS4231A (CS4231-like) - * CS4232 (CS4231+SB and MPU, PnP) - * CS4236 (upgrade of the CS4232, has a better mixer) - * OPTi931 (WSS compatible, full duplex, some differences from CS42xx) + * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others. * * Copyright Luigi Rizzo, 1997 * Copyright by Hannu Savolainen 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -39,14 +33,11 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * * Full data sheets in PDF format for the MSS-compatible chips * are available at * * http://www.crystal.com/ for the CS42XX series, or * http://www.opti.com/ for the OPTi931 - * - * The OPTi931 appears to be quite buggy. */ #include <i386/isa/snd/sound.h> @@ -114,11 +105,12 @@ snddev_info mss_op_desc = { AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */ /* * the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE + * but we do not use these modes. */ } ; /* - * this is the probe routine. Note, it is not necessary to + * mss_probe() is the probe routine. Note, it is not necessary to * go through this for PnP devices, since they are already * indentified precisely using their PnP id. * @@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev) tmp = inb(dev->id_iobase + 3); if (tmp == 0xff) { /* Bus float */ - DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); + DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); dev->id_flags &= ~DV_F_TRUE_MSS ; goto mss_probe_end; } tmp &= 0x3f ; if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) { - DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n", + DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n", dev->id_iobase, inb(dev->id_iobase + 3))); return 0; } @@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev) d->name, dev->id_unit, d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags); + dev->id_alive = 8 ; /* number of io ports */ + /* should be already set but just in case... */ + if ( dev->id_flags & DV_F_TRUE_MSS ) { /* has IRQ/DMA registers, set IRQ and DMA addr */ static char interrupt_bits[12] = { @@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev) printf("invalid dual dma config %d:%d\n", d->dma1, d->dma2); dev->id_irq = 0 ; + dev->id_alive = 0 ; /* this makes attach fail. */ return 0 ; } outb(dev->id_iobase, bits ); } } + if (d->dma1 != d->dma2) + d->audio_fmt |= AFMT_FULLDUPLEX ; mss_reinit(d); ad1848_mixer_reset(d); return 0; @@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; @@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_S16_LE ; break; } - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); - ask_init(d); + ask_init(d); /* and reset buffers... */ } splx(s); return 0 ; @@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p) /* * the callback routine to handle all dma ops etc. + * With the exception of INIT, all other callbacks are invoked + * with interrupts disabled. */ static int mss_callback(snddev_info *d, int reason) { - u_long s; u_char m; int retry, wr, cnt; @@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason) d->rec_fmt = d->play_fmt ; /* no split format on the WSS */ snd_set_blocksize(d); mss_reinit(d); + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1 ; break ; case SND_CB_START : - /* fallthrough */ - case SND_CB_RESTART : - s = spltty(); - cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ; + cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ; if (d->play_fmt == AFMT_S16_LE) cnt /= 2; if (d->flags & SND_F_STEREO) @@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason) DEB(printf("-- (re)start cnt %d\n", cnt)); m = ad_read(d,9) ; - DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); + DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */ /* * on the OPTi931 the enable bit seems hard to set... @@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason) else ad_write_cnt(d, 30, cnt); - splx(s); break ; case SND_CB_STOP : case SND_CB_ABORT : /* XXX check this... */ - s = spltty(); m = ad_read(d,9) ; m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */ /* @@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason) if (retry == 0) printf("start dma, failed to clear bit 0x%02x 0x%02x\n", m, ad_read(d, 9) ) ; - - /* disable DMA by clearing count registers. */ +#if 1 + /* + * try to disable DMA by clearing count registers. Not sure it + * is needed, and it might cause false interrupts when the + * DMA is re-enabled later. + */ if (wr || (d->dma1 == d->dma2) ) ad_write_cnt(d, 14, 0); else ad_write_cnt(d, 30, 0); - splx(s); break; +#endif } return 0 ; } @@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason) /* * main irq handler for the CS423x. The OPTi931 code is * a separate one. + * The correct way to operate for a device with multiple internal + * interrupt sources is to loop on the status register and ack + * interrupts until all interrupts are served and none are reported. At + * this point the IRQ line to the ISA IRQ controller should go low + * and be raised at the next interrupt. + * + * Since the ISA IRQ controller is sent EOI _before_ passing control + * to the isr, it might happen that we serve an interrupt early, in + * which case the status register at the next interrupt should just + * say that there are no more interrupts... */ static void mss_intr(int unit) { snddev_info *d = &pcm_info[unit]; - u_char i11, c; - u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ + u_char c, served = 0; + int i; - i11 = ad_read(d, 11); - reason = inb(io_Status(d)); + DEB(printf("mss_intr\n")); + ad_read(d, 11); /* fake read of status bits */ - if ( ! (reason & 1) ) /* no int, maybe a shared line ? */ - return; - /* get exact reason */ - c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24); - if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) ) - dsp_wrintr(d); - c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24); - if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) ) - dsp_rdintr(d); - /* XXX check this on the 4236... */ - if (d->dma1 == d->dma2) - outb(io_Status(d), 0); /* Clear interrupt status */ - else - ad_write(d, 24, ~c); /* ack selectively */ + /* + * loop until there are interrupts, but no more than 10 times. + */ + for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) { + /* get exact reason for full-duplex boards */ + c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24); + c &= ~served ; + if ( d->dbuf_out.dl && (c & 0x10) ) { + served |= 0x10 ; + dsp_wrintr(d); + } + if ( d->dbuf_in.dl && (c & 0x20) ) { + served |= 0x20 ; + dsp_rdintr(d); + } + /* + * now ack the interrupt + */ + if (d->dma1 == d->dma2) + outb(io_Status(d), 0); /* Clear interrupt status */ + else + ad_write(d, 24, ~c); /* ack selectively */ + } } /* * the opti931 seems to miss interrupts when working in full - * duplex. god know why... + * duplex, so we try some heuristics to catch them. */ static void opti931_intr(int unit) @@ -509,8 +523,6 @@ opti931_intr(int unit) u_char masked=0, i11, mc11, c=0; u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ int loops = 10; - int w_miss=0, r_miss=0; - static int misses=0; /* XXX kludge */ #if 0 reason = inb(io_Status(d)); @@ -525,11 +537,11 @@ again: c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11); mc11 &= 0x0c ; if (c & 0x10) { - printf("Warning CD interrupt\n"); + printf("Warning: CD interrupt\n"); mc11 |= 0x10 ; } if (c & 0x20) { - printf("Warning MPU interrupt\n"); + printf("Warning: MPU interrupt\n"); mc11 |= 0x20 ; } if (mc11 & masked) @@ -541,38 +553,18 @@ again: printf("one more try...\n"); goto again; } - if (w_miss || r_miss ) { - misses++; - if ( (misses & 0xff) == 1 ) - printf("opti931: %d missed irq (now %s%s)\n", - misses, w_miss?"play ":"", - r_miss?"capture ":""); - } - if (loops==10) + if (loops==10) { printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - if (w_miss) mc11 |= 4 ; - if (r_miss) mc11 |= 8 ; - if (mc11==0) - return; + } + return; } - if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) { + if ( d->dbuf_in.dl && (mc11 & 8) ) { dsp_rdintr(d); - r_miss = 0 ; } - else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) ) - r_miss = 1 ; - - if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) { - if (isa_dmastatus1(d->dma1) != 0) - printf("opti931_intr: wr dma %d\n", - isa_dmastatus1(d->dma1)); + if ( d->dbuf_out.dl && (mc11 & 4) ) { dsp_wrintr(d); - w_miss = 0 ; } - else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) ) - w_miss = 1 ; - opti_write(d->conf_base, 11, ~mc11); /* ack */ if (--loops) goto again; printf("xxx too many loops\n"); @@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg) outb(io_base, reg); return inb(io_base+1); } + +static void +gus_write(int io_base, u_char reg, u_char value) +{ + outb(io_base + 3, reg); + outb(io_base + 5, value); +} + +static void +gus_writew(int io_base, u_char reg, u_short value) +{ + outb(io_base + 3, reg); + outb(io_base + 4, value); +} + +static u_char +gus_read(int io_base, u_char reg) +{ + outb(io_base+3, reg); + return inb(io_base+5); +} + +static u_short +gus_readw(int io_base, u_char reg) +{ + outb(io_base+3, reg); + return inw(io_base+4); +} + + /* * AD_WAIT_INIT waits if we are initializing the board and * we cannot modify its settings @@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d) d->bd_flags |= BD_F_MCE_BIT; AD_WAIT_INIT(d, 100); prev = inb(io_Index_Addr(d)); - outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ; + prev &= ~IA_TRD ; + outb(io_Index_Addr(d), prev | IA_MCE ) ; } static void @@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d) d->bd_flags &= ~BD_F_MCE_BIT; prev = inb(io_Index_Addr(d)); - - outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */ + prev &= ~IA_TRD ; + outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */ wait_for_calibration(d); splx(flags); } /* * only one source can be set... - * - * fixed -- lr 970725 */ static int mss_set_recsrc(snddev_info *d, int mask) @@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value) mix_d = &(opti931_devices); if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { - DDB(printf("nbits = 0 for dev %d\n", dev) ); + DEB(printf("nbits = 0 for dev %d\n", dev) ); return EINVAL; } @@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value) val = old & 0x7f ; /* clear mute bit. */ change_bits(mix_d, &val, dev, LEFT_CHN, left); ad_write(d, regoffs, val); - DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n", + DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", dev, regoffs, old, val)); if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ @@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value) * Set the right channel */ regoffs = (*mix_d)[dev][RIGHT_CHN].regno; - val = ad_read(d, regoffs); + old = val = ad_read(d, regoffs); if (regoffs != 1) val = old & 0x7f ; /* clear mute bit. */ change_bits(mix_d, &val, dev, RIGHT_CHN, right); ad_write(d, regoffs, val); + DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); } return 0; /* success */ } @@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d) else d->mix_devs = MODE1_MIXER_DEVICES; - d->mix_rec_devs = MODE1_REC_DEVICES; + d->mix_rec_devs = MSS_REC_DEVICES; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (d->mix_devs & (1 << i)) mss_mixer_set(d, i, default_mixer_levels[i]); mss_set_recsrc(d, SOUND_MASK_MIC); + /* + * some device-specific things, mostly mute the mic to + * the output mixer so as to avoid hisses. In many cases this + * is the default after reset, this code is here mostly as a + * reminder that this might be necessary on other boards. + */ + switch(d->bd_id) { + case MD_OPTI931: + ad_write(d, 20, 0x88); + ad_write(d, 21, 0x88); + break; + case MD_GUSPNP: + /* this is only necessary in mode 3 ... */ + ad_write(d, 22, 0x88); + ad_write(d, 23, 0x88); + } } /* @@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d) * matching one. As a side effect, it returns the value to * be written in the speed bits of the codec. It does _NOT_ * set the speed of the device (but it should!) - * - * fixed lr970724 */ static int @@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev) else break ; if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */ - DDB(printf("mss_detect error, busy still set (0x%02x)\n", + DEB(printf("mss_detect error, busy still set (0x%02x)\n", inb(io_Index_Addr(d)))); return 0; } @@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev) tmp1 = ad_read(d, 0) ; tmp2 = ad_read(d, 1) ; if ( tmp1 != 0xaa || tmp2 != 0x45) { - DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", + DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", tmp1, tmp2)); - return 0; + return 0; } ad_write(d, 0, 0x45); @@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev) tmp2 = ad_read(d, 1) ; if (tmp1 != 0x45 || tmp2 != 0xaa) { - DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); + DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); return 0; } @@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev) tmp1 = ad_read(d, 12); if ((tmp & 0x0f) != (tmp1 & 0x0f)) { - DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", + DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", tmp1, tmp)); return 0; } @@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev) for (i = 0; i < 16; i++) if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) { - DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", + DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", i, tmp1, tmp2)); /* * note - this seems to fail on the 4232 on I11. So we just break @@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev) ad_write(d, 0, 0xaa); if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */ - DDB(printf("mss_detect error - step H(%x)\n", tmp1)); + DEB(printf("mss_detect error - step H(%x)\n", tmp1)); return 0; } /* * Verify that some bits of I25 are read only. */ - DDB(printf("mss_detect() - step I\n")); + DEB(printf("mss_detect() - step I\n")); tmp1 = ad_read(d, 25); /* Original bits */ ad_write(d, 25, ~tmp1); /* Invert all bits */ if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) { @@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev) break; case 0x83: /* CS4236 */ - case 0x03: /* CS4236 on Intel PR440FX motherboard */ + case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ name = "CS4236"; d->bd_id = MD_CS4236; break ; @@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev) } } - DDB(printf("mss_detect() - Detected %s\n", name)); + DEB(printf("mss_detect() - Detected %s\n", name)); strcpy(d->name, name); dev->id_flags &= ~DV_F_DEV_MASK ; dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ; @@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d) ad_write(d, 8, r) ; if (d->dma1 != d->dma2) { +#if 0 + if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) { + printf("warning, cannot do ulaw rec + play on the GUS\n"); + r = 0 ; /* move to U8 */ + } +#endif ad_write(d, 28, r & 0xf0 ) ; /* capture mode */ ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ; } else ad_write(d, 9, 4 /* no capture, no playback, single dma */) ; ad_leave_MCE(d); + /* + * not sure if this is really needed... + */ ad_write_cnt(d, 14, 0 ); /* playback count */ if (d->dma1 != d->dma2) ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */ @@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d) #if NPNP > 0 -static char * cs4236_probe(u_long csn, u_long vend_id); -static void cs4236_attach(u_long csn, u_long vend_id, char *name, +static char * cs423x_probe(u_long csn, u_long vend_id); +static void cs423x_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); -static struct pnp_device cs4236 = { - "cs423x", - cs4236_probe, - cs4236_attach, +static struct pnp_device cs423x = { + "cs423x/ymh0020", + cs423x_probe, + cs423x_attach, &nsnd, /* use this for all sound cards */ &tty_imask /* imask */ }; -DATA_SET (pnpdevice_set, cs4236); +DATA_SET (pnpdevice_set, cs423x); static char * -cs4236_probe(u_long csn, u_long vend_id) +cs423x_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x3742630e) + u_long id = vend_id & 0xff00ffff; + if ( id == 0x3700630e ) s = "CS4237" ; - else if (vend_id == 0x3642630e) + else if ( id == 0x3600630e ) s = "CS4236" ; - else if (vend_id == 0x360b630e) - s = "CS4236" ; - else if (vend_id == 0x3242630e) + else if ( id == 0x3200630e) s = "CS4232" ; + else if ( id == 0x2000a865) + s = "Yamaha SA2"; + else if (vend_id == 0x8140d315) + s = "SoundscapeVIVO"; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id) extern snddev_info sb_op_desc; static void -cs4236_attach(u_long csn, u_long vend_id, char *name, +cs423x_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d ; @@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name, return ; } snddev_last_probed = &tmp_d; - if (d.flags & DV_PNP_SBCODEC) { - printf("CS423x use sb-compatible codec\n"); - dev->id_iobase = d.port[2] ; + if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/ + dev->id_alive = 16 ; /* number of io ports ? */ tmp_d = sb_op_desc ; - tmp_d.alt_base = d.port[0] - 4; + if (vend_id == 0x2000a865 || vend_id == 0x8140d315) { + /* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */ + dev->id_iobase = d.port[0] ; + tmp_d.alt_base = d.port[1] ; + d.irq[1] = 0 ; /* only needed for the VIVO */ + } else { + dev->id_iobase = d.port[2] ; + tmp_d.alt_base = d.port[0] - 4; + } d.drq[1] = 4 ; /* disable, it is not used ... */ - } else { - /* mss-compatible codec */ - dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ + } else { /* mss-compatible codec */ + dev->id_alive = 8 ; /* number of io ports ? */ tmp_d = mss_op_desc ; - switch (vend_id) { - case 0x3742630e: /* CS4237 */ - case 0x3642630e: /* CS4236 */ - case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */ - tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */ + dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ + tmp_d.alt_base = d.port[2]; + switch (vend_id & 0xff00ffff) { + + case 0x2000a865: /* yamaha SA-2 */ + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + tmp_d.bd_id = MD_YM0020 ; + break; + + case 0x8100d315: /* ENSONIQ SoundscapeVIVO */ + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + tmp_d.bd_id = MD_VIVO ; + d.irq[1] = 0 ; break; - default: + + case 0x3700630e: /* CS4237 */ + tmp_d.bd_id = MD_CS4237 ; + break; + + case 0x3600630e: /* CS4236 */ + tmp_d.bd_id = MD_CS4236 ; + break; + + default: tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */ break; } - tmp_d.alt_base = d.port[2]; strcpy(tmp_d.name, name); tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; } @@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - dev->id_alive = 1; pcmattach(dev); } @@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name, dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */ tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */ tmp_d.alt_base = d.port[2]; - opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ ); + opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ ); ad_write (&tmp_d, 10, 2); /* enable interrupts */ if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ @@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } +static void gus_mem_cfg(snddev_info *tmp); + static char *guspnp_probe(u_long csn, u_long vend_id); static void guspnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); @@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name, struct pnp_cinfo d ; snddev_info tmp_d ; /* patched copy of the basic snddev_info */ + u_char tmp; + read_pnp_parms ( &d , 0 ) ; + + /* d.irq[1] = d.irq[0] ; */ + printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) ); + pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */ + write_pnp_parms ( &d , 0 ); enable_pnp_card(); @@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name, snddev_last_probed = &tmp_d; dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */ - dev->id_drq = d.drq[0] ; /* primary dma */ + dev->id_drq = d.drq[1] ; /* XXX PLAY dma */ dev->id_irq = (1 << d.irq[0] ) ; dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; + dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */ + + tmp_d.io_base = d.port[2] - 4; + tmp_d.alt_base = d.port[0]; /* 0x220 */ + tmp_d.conf_base = d.port[1]; /* gus control block... */ + tmp_d.bd_id = MD_GUSPNP ; + + /* reset */ + gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */ + DELAY(1000 * 30); + /* release reset and enable DAC */ + gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 ); + printf("resetting the gus...\n"); + DELAY(1000 * 30); + /* end of reset */ - tmp_d.alt_base = d.port[0]; + outb( tmp_d.alt_base, 0xC ); /* enable int and dma */ + + /* + * unmute left & right line. Need to go in mode3, unmute, + * and back to mode 2 + */ + tmp = ad_read(&tmp_d, 0x0c); + ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */ + ad_write(&tmp_d, 0x19, 0 ); /* unmute left */ + ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */ + ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */ + + /* send codec interrupts on irq1 and only use that one */ + gus_write(tmp_d.conf_base, 0x5a , 0x4f ); + + /* enable access to hidden regs */ + tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ ); + gus_write(tmp_d.conf_base, 0x5b , tmp | 1 ); + printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) ); + + strcpy(tmp_d.name, name); pcmattach(dev); } + +#if 0 +int +gus_mem_write(snddev_info *d, int addr, u_char data) +{ + gus_writew(d->conf_base, 0x43 , addr & 0xffff ); + gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); + outb(d->conf_base + 7, data); +} + +u_char +gus_mem_read(snddev_info *d, int addr) +{ + gus_writew(d->conf_base, 0x43 , addr & 0xffff ); + gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); + return inb(d->conf_base + 7); +} + +void +gus_mem_cfg(snddev_info *d) +{ + int base; + u_char old; + u_char a, b; + + printf("configuring gus memory...\n"); + gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/); + old = gus_read(d->conf_base, 0x19); + gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */ + for (base = 0; base < 1024; base++) { + a=gus_mem_read(d, base*1024); + a = ~a ; + gus_mem_write(d, base*1024, a); + b=gus_mem_read(d, base*1024); + if ( b != a ) + break ; + } + printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b); +} +#endif /* gus mem cfg... */ + #endif /* NPNP > 0 */ #endif /* NPCM > 0 */ diff --git a/sys/dev/pcm/isa/mss.h b/sys/dev/pcm/isa/mss.h index 4b34e51..994f323 100644 --- a/sys/dev/pcm/isa/mss.h +++ b/sys/dev/pcm/isa/mss.h @@ -143,90 +143,89 @@ ahead. * (Actually this is not a mapping but rather some kind of interleaving * solution). */ -#define GUSMAX_MIXER -#ifdef GUSMAX_MIXER -#define MODE1_REC_DEVICES \ - (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX) - -#define MODE1_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX) - -#define MODE2_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \ - SOUND_MASK_PCM | SOUND_MASK_IMIX) - -#else /* Generic mapping */ - -#define MODE1_REC_DEVICES \ - (SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX) -#define MODE1_MIXER_DEVICES \ - (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX) +#define MSS_REC_DEVICES \ + (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX) -#define MODE2_MIXER_DEVICES \ - (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \ - SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX) -#endif - -#define OPTI931_MIXER_DEVICES \ - (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ - SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN ) /* - * Most of the mixer entries work in backwards. Setting the polarity field - * makes them to work correctly. + * Table of mixer registers. There is a default table for the + * AD1848/CS423x clones, and one for the OPTI931. As more WSS + * clones come out, there ought to be more tables. + * + * Fields in the table are : polarity, register, offset, bits * * The channel numbering used by individual soundcards is not fixed. * Some cards have assigned different meanings for the AUX1, AUX2 * and LINE inputs. Some have different features... - * The current version doesn't try to compensate this. + * + * Following there is a macro ...MIXER_DEVICES which is a bitmap + * of all non-zero fields in the table. + * MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1 + * registers I0..I15) * */ -mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */ -MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5), +mixer_ent mix_devices[32][2] = { +MIX_NONE(SOUND_MIXER_VOLUME), +MIX_NONE(SOUND_MIXER_BASS), +MIX_NONE(SOUND_MIXER_TREBLE), +MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5), MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6), MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), -MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5), +MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5), MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_ALTPCM), +MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5) +MIX_NONE(SOUND_MIXER_OGAIN), +MIX_NONE(SOUND_MIXER_LINE1), +MIX_NONE(SOUND_MIXER_LINE2), +MIX_NONE(SOUND_MIXER_LINE3), }; +#define MODE2_MIXER_DEVICES \ + (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \ + SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ + SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) + +#define MODE1_MIXER_DEVICES \ + (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) + + +/* + * entries for the opti931... + */ + mixer_ent opti931_devices[32][2] = { /* for the opti931 */ MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_BASS), +MIX_NONE(SOUND_MIXER_TREBLE), MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4), MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5), -MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_SPEAKER), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_IMIX), +MIX_NONE(SOUND_MIXER_ALTPCM), +MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5) +MIX_NONE(SOUND_MIXER_OGAIN), +MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4), +MIX_NONE(SOUND_MIXER_LINE2), +MIX_NONE(SOUND_MIXER_LINE3), }; +#define OPTI931_MIXER_DEVICES \ + (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ + SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ + SOUND_MASK_IGAIN | SOUND_MASK_LINE1 ) + + static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = { 0x5a5a, /* Master Volume */ 0x3232, /* Bass */ diff --git a/sys/dev/pcm/isa/sb.c b/sys/dev/pcm/isa/sb.c index e786f5f..d4825c1 100644 --- a/sys/dev/pcm/isa/sb.c +++ b/sys/dev/pcm/isa/sb.c @@ -11,13 +11,14 @@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A @@ -112,7 +113,7 @@ snddev_info sb_op_desc = { * resetting the dsp and testing if it is there. * Version detection etc. will be done at attach time. * - * Remebber, isa probe routines are supposed to return the + * Remember, ISA probe routines are supposed to return the * size of io space used. */ @@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev) { snddev_info *d = &pcm_info[dev->id_unit] ; + dev->id_alive = 16 ; /* number of io ports */ + /* should be already set but just in case... */ sb_dsp_init(d, dev); return 0 ; } @@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - d->flags = 0 ; d->bd_flags &= ~BD_F_HISPEED ; @@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); sb_reset_dsp(d->io_base); ask_init(d); @@ -287,17 +285,18 @@ again: reason |= 2; } } + /* XXX previous location of ack... */ + DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); + if ( d->dbuf_out.dl && (reason & 1) ) + dsp_wrintr(d); + if ( d->dbuf_in.dl && (reason & 2) ) + dsp_rdintr(d); + if ( c & 2 ) inb(DSP_DATA_AVL16); /* 16-bit int ack */ if (c & 1) inb(DSP_DATA_AVAIL); /* 8-bit int ack */ -DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); - if ( (d->flags & SND_F_WR_DMA) && (reason & 1) ) - dsp_wrintr(d); - if ( (d->flags & SND_F_RD_DMA) && (reason & 2) ) - dsp_rdintr(d); - /* * the sb16 might have multiple sources etc. */ @@ -320,7 +319,7 @@ static int sb_callback(snddev_info *d, int reason) { int rd = reason & SND_CB_RD ; - int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ; + int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ; switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ @@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1; - break ; + break; case SND_CB_START : /* called with int disabled */ - sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON); - d->flags &= ~SND_F_INIT ; if (d->bd_flags & BD_F_SB16) { /* the SB16 can do full duplex using one 16-bit channel * and one 8-bit channel. It needs to be programmed to * use split format though. + * We use the following algorithm: + * 1. check which direction(s) are active; + * 2. check if we should swap dma channels + * 3. check if we can do the swap. */ - int b16 ; - int swap = 0 ; + int swap = 1 ; /* default... */ - b16 = (rd) ? d->rec_fmt : d->play_fmt ; - b16 = (b16 == AFMT_S16_LE) ? 1 : 0; - /* - * check if I have to swap dma channels. Swap if - * - !rd, dma1 <4, b16 - * - !rd, dma1 >=4, !b16 - * - rd, dma2 <4, b16 - * - rd, dma2 >=4, !b16 - */ - if (!rd) { - if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1; + if (rd) { + if (d->flags & SND_F_WRITING || d->dbuf_out.dl) + swap = 0; + if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4) + swap = 0; + if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4) + swap = 0; } else { - if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1; + if (d->flags & SND_F_READING || d->dbuf_in.dl) + swap = 0; + if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4) + swap = 0; + if (d->play_fmt != AFMT_S16_LE && d->dma1 <4) + swap = 0; } - /* - * before swapping should make sure that there is no - * pending DMA on the other channel... - */ + if (swap) { int c = d->dma2 ; d->dma2 = d->dma1; d->dma1 = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + DEB(printf("START dma chan: play %d, rec %d\n", + d->dma1, d->dma2)); } - DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n", - d->play_fmt, d->rec_fmt, d->dma1, d->dma2)); } - /* fallthrough */ - case SND_CB_RESTART: + if (!rd) + sb_cmd(d->io_base, DSP_CMD_SPKON); + if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; - /* - * SB16 support still not completely working!!! - * - * in principle, on the SB16, I could support simultaneous - * play & rec. - * However, there is no way to ask explicitly for 8 or - * 16 bit transfer. As a consequence, if we do 8-bit, - * we need to use the 8-bit channel, and if we do 16-bit, - * we need to use the other one. The only way I find to - * do this is to swap d->dma1 and d->dma2 ... - * - */ if (rd) { c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC ; - c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; - if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; + c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->rec_fmt == AFMT_S16_LE) l /= 2 ; } else { c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC ; - c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->play_fmt == AFMT_S16_LE) l /= 2 ; @@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, c ); sb_cmd3(d->io_base, c1 , l - 1) ; } else { + /* code for the SB2 and SB3 */ u_char c ; if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ; + c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ; else - c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ; + c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ; sb_cmd3(d->io_base, c , l - 1) ; } break; + case SND_CB_ABORT : /* XXX */ case SND_CB_STOP : - /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */ + { + int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ + if (d->bd_flags & BD_F_SB16) { + if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) ) + cmd = DSP_CMD_DMAPAUSE_16 ; + } + if (d->bd_flags & BD_F_HISPEED) { + sb_reset_dsp(d->io_base); + d->flags |= SND_F_INIT ; + } else { + sb_cmd(d->io_base, cmd); /* pause dma. */ + /* + * This seems to have the side effect of blocking the other + * side as well so I have to re-enable it :( + */ + if ( (rd && d->dbuf_out.dl) || + (!rd && d->dbuf_in.dl) ) + sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? + 0xd6 : 0xd4); /* continue other dma */ + } + } + DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; } @@ -443,7 +459,7 @@ sb_reset_dsp(int io_base) DELAY(30); if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp failed\n")); + DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); return 0; /* Sorry */ } return 1; @@ -753,13 +769,13 @@ dsp_speed(snddev_info *d) * Now the speed should be valid. Compute the value to be * programmed into the board. * - * XXX check this code... + * XXX stereo init is still missing... */ if (speed > 22050) { /* High speed mode on 2.01/3.xx */ int tmp; - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); + tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; d->bd_flags |= BD_F_HISPEED ; flags = spltty(); @@ -1022,19 +1038,18 @@ static char * sb16pnp_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - if (vend_id == 0x2b008c0e) - s = "SB16 Value PnP" ; + /* - * The SB16/AWE64 cards seem to differ in the fourth byte of + * The SB16/AWExx cards seem to differ in the fourth byte of * the vendor id, so I have just masked it for the time being... * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e */ if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB AWE64 PnP"; + s = "SB16 PnP"; + else if (vend_id == 0x01009305) + s = "Avance Asound 100" ; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); diff --git a/sys/dev/pcm/isa/sb.h b/sys/dev/pcm/isa/sb.h index 8548997..d7a24d5 100644 --- a/sys/dev/pcm/isa/sb.h +++ b/sys/dev/pcm/isa/sb.h @@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ; * DSP Commands. There are many, and in many cases they are used explicitly */ +/* these are not used except for programmed I/O (not in this driver) */ #define DSP_DAC8 0x10 /* direct DAC output */ +#define DSP_ADC8 0x20 /* direct ADC input */ + +/* these should be used in the SB 1.0 */ #define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */ +#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */ + +/* these should be used in the SB 2.0 and 2.01 */ +#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */ +#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */ + +#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ +#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */ +#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */ + +/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */ + + /* prepare for dma input */ +#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0)) + #define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */ #define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */ -#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */ -#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */ - -#define DSP_ADC8 0x20 /* direct ADC input */ +#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */ -#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */ -#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */ +/* SB16 commands */ #define DSP_CMD_O16 0xb0 #define DSP_CMD_I16 0xb8 #define DSP_CMD_O8 0xc0 @@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ; #define DSP_CMD_SPKON 0xD1 #define DSP_CMD_SPKOFF 0xD3 #define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2)) -#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */ -#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */ -#define DSP_CMD_DMAHALT 0xD0 +#define DSP_CMD_DMAPAUSE_8 0xD0 +#define DSP_CMD_DMAPAUSE_16 0xD5 +#define DSP_CMD_DMAEXIT_8 0xDA +#define DSP_CMD_DMAEXIT_16 0xD9 #define DSP_CMD_TCONST 0x40 /* set time constant */ -#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ #define DSP_CMD_HSDAC 0x91 /* high speed dac */ -#define DSP_CMD_HSADC 0x99 /* high speed adc */ +#define DSP_CMD_HSADC 0x99 /* high speed adc */ #define DSP_CMD_GETVER 0xE1 #define DSP_CMD_GETID 0xE7 /* return id bytes */ - /* prepare for dma input */ -#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0)) #define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */ #define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */ #if 0 /*** unknown ***/ - /* - * D9 and D5 are used on the sb16 on close... maybe a reset of - * some subsystem ? - */ -#define DSP_CMD_D9 0xD9 -#define DSP_CMD_D5 0xD5 #define DSP_CMD_FA 0xFA /* get version from prosonic*/ #define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/ #endif diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index 1188d32..0cd2dfd 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -1,30 +1,24 @@ /* * sound/ad1848.c * - * Modified by Luigi Rizzo (luigi@iet.unipi.it) - * * Driver for Microsoft Sound System/Windows Sound System (mss) * -compatible boards. This includes: * - * AD1848, CS4248 - * - * CS4231, used in the GUS MAX and some other cards; - * AD1845, CS4231A (CS4231-like) - * CS4232 (CS4231+SB and MPU, PnP) - * CS4236 (upgrade of the CS4232, has a better mixer) - * OPTi931 (WSS compatible, full duplex, some differences from CS42xx) + * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others. * * Copyright Luigi Rizzo, 1997 * Copyright by Hannu Savolainen 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -39,14 +33,11 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * * Full data sheets in PDF format for the MSS-compatible chips * are available at * * http://www.crystal.com/ for the CS42XX series, or * http://www.opti.com/ for the OPTi931 - * - * The OPTi931 appears to be quite buggy. */ #include <i386/isa/snd/sound.h> @@ -114,11 +105,12 @@ snddev_info mss_op_desc = { AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */ /* * the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE + * but we do not use these modes. */ } ; /* - * this is the probe routine. Note, it is not necessary to + * mss_probe() is the probe routine. Note, it is not necessary to * go through this for PnP devices, since they are already * indentified precisely using their PnP id. * @@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev) tmp = inb(dev->id_iobase + 3); if (tmp == 0xff) { /* Bus float */ - DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); + DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); dev->id_flags &= ~DV_F_TRUE_MSS ; goto mss_probe_end; } tmp &= 0x3f ; if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) { - DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n", + DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n", dev->id_iobase, inb(dev->id_iobase + 3))); return 0; } @@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev) d->name, dev->id_unit, d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags); + dev->id_alive = 8 ; /* number of io ports */ + /* should be already set but just in case... */ + if ( dev->id_flags & DV_F_TRUE_MSS ) { /* has IRQ/DMA registers, set IRQ and DMA addr */ static char interrupt_bits[12] = { @@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev) printf("invalid dual dma config %d:%d\n", d->dma1, d->dma2); dev->id_irq = 0 ; + dev->id_alive = 0 ; /* this makes attach fail. */ return 0 ; } outb(dev->id_iobase, bits ); } } + if (d->dma1 != d->dma2) + d->audio_fmt |= AFMT_FULLDUPLEX ; mss_reinit(d); ad1848_mixer_reset(d); return 0; @@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; @@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_S16_LE ; break; } - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); - ask_init(d); + ask_init(d); /* and reset buffers... */ } splx(s); return 0 ; @@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p) /* * the callback routine to handle all dma ops etc. + * With the exception of INIT, all other callbacks are invoked + * with interrupts disabled. */ static int mss_callback(snddev_info *d, int reason) { - u_long s; u_char m; int retry, wr, cnt; @@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason) d->rec_fmt = d->play_fmt ; /* no split format on the WSS */ snd_set_blocksize(d); mss_reinit(d); + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1 ; break ; case SND_CB_START : - /* fallthrough */ - case SND_CB_RESTART : - s = spltty(); - cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ; + cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ; if (d->play_fmt == AFMT_S16_LE) cnt /= 2; if (d->flags & SND_F_STEREO) @@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason) DEB(printf("-- (re)start cnt %d\n", cnt)); m = ad_read(d,9) ; - DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); + DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */ /* * on the OPTi931 the enable bit seems hard to set... @@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason) else ad_write_cnt(d, 30, cnt); - splx(s); break ; case SND_CB_STOP : case SND_CB_ABORT : /* XXX check this... */ - s = spltty(); m = ad_read(d,9) ; m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */ /* @@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason) if (retry == 0) printf("start dma, failed to clear bit 0x%02x 0x%02x\n", m, ad_read(d, 9) ) ; - - /* disable DMA by clearing count registers. */ +#if 1 + /* + * try to disable DMA by clearing count registers. Not sure it + * is needed, and it might cause false interrupts when the + * DMA is re-enabled later. + */ if (wr || (d->dma1 == d->dma2) ) ad_write_cnt(d, 14, 0); else ad_write_cnt(d, 30, 0); - splx(s); break; +#endif } return 0 ; } @@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason) /* * main irq handler for the CS423x. The OPTi931 code is * a separate one. + * The correct way to operate for a device with multiple internal + * interrupt sources is to loop on the status register and ack + * interrupts until all interrupts are served and none are reported. At + * this point the IRQ line to the ISA IRQ controller should go low + * and be raised at the next interrupt. + * + * Since the ISA IRQ controller is sent EOI _before_ passing control + * to the isr, it might happen that we serve an interrupt early, in + * which case the status register at the next interrupt should just + * say that there are no more interrupts... */ static void mss_intr(int unit) { snddev_info *d = &pcm_info[unit]; - u_char i11, c; - u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ + u_char c, served = 0; + int i; - i11 = ad_read(d, 11); - reason = inb(io_Status(d)); + DEB(printf("mss_intr\n")); + ad_read(d, 11); /* fake read of status bits */ - if ( ! (reason & 1) ) /* no int, maybe a shared line ? */ - return; - /* get exact reason */ - c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24); - if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) ) - dsp_wrintr(d); - c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24); - if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) ) - dsp_rdintr(d); - /* XXX check this on the 4236... */ - if (d->dma1 == d->dma2) - outb(io_Status(d), 0); /* Clear interrupt status */ - else - ad_write(d, 24, ~c); /* ack selectively */ + /* + * loop until there are interrupts, but no more than 10 times. + */ + for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) { + /* get exact reason for full-duplex boards */ + c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24); + c &= ~served ; + if ( d->dbuf_out.dl && (c & 0x10) ) { + served |= 0x10 ; + dsp_wrintr(d); + } + if ( d->dbuf_in.dl && (c & 0x20) ) { + served |= 0x20 ; + dsp_rdintr(d); + } + /* + * now ack the interrupt + */ + if (d->dma1 == d->dma2) + outb(io_Status(d), 0); /* Clear interrupt status */ + else + ad_write(d, 24, ~c); /* ack selectively */ + } } /* * the opti931 seems to miss interrupts when working in full - * duplex. god know why... + * duplex, so we try some heuristics to catch them. */ static void opti931_intr(int unit) @@ -509,8 +523,6 @@ opti931_intr(int unit) u_char masked=0, i11, mc11, c=0; u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ int loops = 10; - int w_miss=0, r_miss=0; - static int misses=0; /* XXX kludge */ #if 0 reason = inb(io_Status(d)); @@ -525,11 +537,11 @@ again: c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11); mc11 &= 0x0c ; if (c & 0x10) { - printf("Warning CD interrupt\n"); + printf("Warning: CD interrupt\n"); mc11 |= 0x10 ; } if (c & 0x20) { - printf("Warning MPU interrupt\n"); + printf("Warning: MPU interrupt\n"); mc11 |= 0x20 ; } if (mc11 & masked) @@ -541,38 +553,18 @@ again: printf("one more try...\n"); goto again; } - if (w_miss || r_miss ) { - misses++; - if ( (misses & 0xff) == 1 ) - printf("opti931: %d missed irq (now %s%s)\n", - misses, w_miss?"play ":"", - r_miss?"capture ":""); - } - if (loops==10) + if (loops==10) { printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - if (w_miss) mc11 |= 4 ; - if (r_miss) mc11 |= 8 ; - if (mc11==0) - return; + } + return; } - if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) { + if ( d->dbuf_in.dl && (mc11 & 8) ) { dsp_rdintr(d); - r_miss = 0 ; } - else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) ) - r_miss = 1 ; - - if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) { - if (isa_dmastatus1(d->dma1) != 0) - printf("opti931_intr: wr dma %d\n", - isa_dmastatus1(d->dma1)); + if ( d->dbuf_out.dl && (mc11 & 4) ) { dsp_wrintr(d); - w_miss = 0 ; } - else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) ) - w_miss = 1 ; - opti_write(d->conf_base, 11, ~mc11); /* ack */ if (--loops) goto again; printf("xxx too many loops\n"); @@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg) outb(io_base, reg); return inb(io_base+1); } + +static void +gus_write(int io_base, u_char reg, u_char value) +{ + outb(io_base + 3, reg); + outb(io_base + 5, value); +} + +static void +gus_writew(int io_base, u_char reg, u_short value) +{ + outb(io_base + 3, reg); + outb(io_base + 4, value); +} + +static u_char +gus_read(int io_base, u_char reg) +{ + outb(io_base+3, reg); + return inb(io_base+5); +} + +static u_short +gus_readw(int io_base, u_char reg) +{ + outb(io_base+3, reg); + return inw(io_base+4); +} + + /* * AD_WAIT_INIT waits if we are initializing the board and * we cannot modify its settings @@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d) d->bd_flags |= BD_F_MCE_BIT; AD_WAIT_INIT(d, 100); prev = inb(io_Index_Addr(d)); - outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ; + prev &= ~IA_TRD ; + outb(io_Index_Addr(d), prev | IA_MCE ) ; } static void @@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d) d->bd_flags &= ~BD_F_MCE_BIT; prev = inb(io_Index_Addr(d)); - - outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */ + prev &= ~IA_TRD ; + outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */ wait_for_calibration(d); splx(flags); } /* * only one source can be set... - * - * fixed -- lr 970725 */ static int mss_set_recsrc(snddev_info *d, int mask) @@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value) mix_d = &(opti931_devices); if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { - DDB(printf("nbits = 0 for dev %d\n", dev) ); + DEB(printf("nbits = 0 for dev %d\n", dev) ); return EINVAL; } @@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value) val = old & 0x7f ; /* clear mute bit. */ change_bits(mix_d, &val, dev, LEFT_CHN, left); ad_write(d, regoffs, val); - DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n", + DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", dev, regoffs, old, val)); if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ @@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value) * Set the right channel */ regoffs = (*mix_d)[dev][RIGHT_CHN].regno; - val = ad_read(d, regoffs); + old = val = ad_read(d, regoffs); if (regoffs != 1) val = old & 0x7f ; /* clear mute bit. */ change_bits(mix_d, &val, dev, RIGHT_CHN, right); ad_write(d, regoffs, val); + DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); } return 0; /* success */ } @@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d) else d->mix_devs = MODE1_MIXER_DEVICES; - d->mix_rec_devs = MODE1_REC_DEVICES; + d->mix_rec_devs = MSS_REC_DEVICES; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (d->mix_devs & (1 << i)) mss_mixer_set(d, i, default_mixer_levels[i]); mss_set_recsrc(d, SOUND_MASK_MIC); + /* + * some device-specific things, mostly mute the mic to + * the output mixer so as to avoid hisses. In many cases this + * is the default after reset, this code is here mostly as a + * reminder that this might be necessary on other boards. + */ + switch(d->bd_id) { + case MD_OPTI931: + ad_write(d, 20, 0x88); + ad_write(d, 21, 0x88); + break; + case MD_GUSPNP: + /* this is only necessary in mode 3 ... */ + ad_write(d, 22, 0x88); + ad_write(d, 23, 0x88); + } } /* @@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d) * matching one. As a side effect, it returns the value to * be written in the speed bits of the codec. It does _NOT_ * set the speed of the device (but it should!) - * - * fixed lr970724 */ static int @@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev) else break ; if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */ - DDB(printf("mss_detect error, busy still set (0x%02x)\n", + DEB(printf("mss_detect error, busy still set (0x%02x)\n", inb(io_Index_Addr(d)))); return 0; } @@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev) tmp1 = ad_read(d, 0) ; tmp2 = ad_read(d, 1) ; if ( tmp1 != 0xaa || tmp2 != 0x45) { - DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", + DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", tmp1, tmp2)); - return 0; + return 0; } ad_write(d, 0, 0x45); @@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev) tmp2 = ad_read(d, 1) ; if (tmp1 != 0x45 || tmp2 != 0xaa) { - DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); + DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); return 0; } @@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev) tmp1 = ad_read(d, 12); if ((tmp & 0x0f) != (tmp1 & 0x0f)) { - DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", + DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", tmp1, tmp)); return 0; } @@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev) for (i = 0; i < 16; i++) if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) { - DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", + DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", i, tmp1, tmp2)); /* * note - this seems to fail on the 4232 on I11. So we just break @@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev) ad_write(d, 0, 0xaa); if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */ - DDB(printf("mss_detect error - step H(%x)\n", tmp1)); + DEB(printf("mss_detect error - step H(%x)\n", tmp1)); return 0; } /* * Verify that some bits of I25 are read only. */ - DDB(printf("mss_detect() - step I\n")); + DEB(printf("mss_detect() - step I\n")); tmp1 = ad_read(d, 25); /* Original bits */ ad_write(d, 25, ~tmp1); /* Invert all bits */ if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) { @@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev) break; case 0x83: /* CS4236 */ - case 0x03: /* CS4236 on Intel PR440FX motherboard */ + case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ name = "CS4236"; d->bd_id = MD_CS4236; break ; @@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev) } } - DDB(printf("mss_detect() - Detected %s\n", name)); + DEB(printf("mss_detect() - Detected %s\n", name)); strcpy(d->name, name); dev->id_flags &= ~DV_F_DEV_MASK ; dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ; @@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d) ad_write(d, 8, r) ; if (d->dma1 != d->dma2) { +#if 0 + if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) { + printf("warning, cannot do ulaw rec + play on the GUS\n"); + r = 0 ; /* move to U8 */ + } +#endif ad_write(d, 28, r & 0xf0 ) ; /* capture mode */ ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ; } else ad_write(d, 9, 4 /* no capture, no playback, single dma */) ; ad_leave_MCE(d); + /* + * not sure if this is really needed... + */ ad_write_cnt(d, 14, 0 ); /* playback count */ if (d->dma1 != d->dma2) ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */ @@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d) #if NPNP > 0 -static char * cs4236_probe(u_long csn, u_long vend_id); -static void cs4236_attach(u_long csn, u_long vend_id, char *name, +static char * cs423x_probe(u_long csn, u_long vend_id); +static void cs423x_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); -static struct pnp_device cs4236 = { - "cs423x", - cs4236_probe, - cs4236_attach, +static struct pnp_device cs423x = { + "cs423x/ymh0020", + cs423x_probe, + cs423x_attach, &nsnd, /* use this for all sound cards */ &tty_imask /* imask */ }; -DATA_SET (pnpdevice_set, cs4236); +DATA_SET (pnpdevice_set, cs423x); static char * -cs4236_probe(u_long csn, u_long vend_id) +cs423x_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x3742630e) + u_long id = vend_id & 0xff00ffff; + if ( id == 0x3700630e ) s = "CS4237" ; - else if (vend_id == 0x3642630e) + else if ( id == 0x3600630e ) s = "CS4236" ; - else if (vend_id == 0x360b630e) - s = "CS4236" ; - else if (vend_id == 0x3242630e) + else if ( id == 0x3200630e) s = "CS4232" ; + else if ( id == 0x2000a865) + s = "Yamaha SA2"; + else if (vend_id == 0x8140d315) + s = "SoundscapeVIVO"; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id) extern snddev_info sb_op_desc; static void -cs4236_attach(u_long csn, u_long vend_id, char *name, +cs423x_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d ; @@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name, return ; } snddev_last_probed = &tmp_d; - if (d.flags & DV_PNP_SBCODEC) { - printf("CS423x use sb-compatible codec\n"); - dev->id_iobase = d.port[2] ; + if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/ + dev->id_alive = 16 ; /* number of io ports ? */ tmp_d = sb_op_desc ; - tmp_d.alt_base = d.port[0] - 4; + if (vend_id == 0x2000a865 || vend_id == 0x8140d315) { + /* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */ + dev->id_iobase = d.port[0] ; + tmp_d.alt_base = d.port[1] ; + d.irq[1] = 0 ; /* only needed for the VIVO */ + } else { + dev->id_iobase = d.port[2] ; + tmp_d.alt_base = d.port[0] - 4; + } d.drq[1] = 4 ; /* disable, it is not used ... */ - } else { - /* mss-compatible codec */ - dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ + } else { /* mss-compatible codec */ + dev->id_alive = 8 ; /* number of io ports ? */ tmp_d = mss_op_desc ; - switch (vend_id) { - case 0x3742630e: /* CS4237 */ - case 0x3642630e: /* CS4236 */ - case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */ - tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */ + dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ + tmp_d.alt_base = d.port[2]; + switch (vend_id & 0xff00ffff) { + + case 0x2000a865: /* yamaha SA-2 */ + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + tmp_d.bd_id = MD_YM0020 ; + break; + + case 0x8100d315: /* ENSONIQ SoundscapeVIVO */ + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + tmp_d.bd_id = MD_VIVO ; + d.irq[1] = 0 ; break; - default: + + case 0x3700630e: /* CS4237 */ + tmp_d.bd_id = MD_CS4237 ; + break; + + case 0x3600630e: /* CS4236 */ + tmp_d.bd_id = MD_CS4236 ; + break; + + default: tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */ break; } - tmp_d.alt_base = d.port[2]; strcpy(tmp_d.name, name); tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; } @@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - dev->id_alive = 1; pcmattach(dev); } @@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name, dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */ tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */ tmp_d.alt_base = d.port[2]; - opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ ); + opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ ); ad_write (&tmp_d, 10, 2); /* enable interrupts */ if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ @@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } +static void gus_mem_cfg(snddev_info *tmp); + static char *guspnp_probe(u_long csn, u_long vend_id); static void guspnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); @@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name, struct pnp_cinfo d ; snddev_info tmp_d ; /* patched copy of the basic snddev_info */ + u_char tmp; + read_pnp_parms ( &d , 0 ) ; + + /* d.irq[1] = d.irq[0] ; */ + printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) ); + pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */ + write_pnp_parms ( &d , 0 ); enable_pnp_card(); @@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name, snddev_last_probed = &tmp_d; dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */ - dev->id_drq = d.drq[0] ; /* primary dma */ + dev->id_drq = d.drq[1] ; /* XXX PLAY dma */ dev->id_irq = (1 << d.irq[0] ) ; dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; + dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */ + + tmp_d.io_base = d.port[2] - 4; + tmp_d.alt_base = d.port[0]; /* 0x220 */ + tmp_d.conf_base = d.port[1]; /* gus control block... */ + tmp_d.bd_id = MD_GUSPNP ; + + /* reset */ + gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */ + DELAY(1000 * 30); + /* release reset and enable DAC */ + gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 ); + printf("resetting the gus...\n"); + DELAY(1000 * 30); + /* end of reset */ - tmp_d.alt_base = d.port[0]; + outb( tmp_d.alt_base, 0xC ); /* enable int and dma */ + + /* + * unmute left & right line. Need to go in mode3, unmute, + * and back to mode 2 + */ + tmp = ad_read(&tmp_d, 0x0c); + ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */ + ad_write(&tmp_d, 0x19, 0 ); /* unmute left */ + ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */ + ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */ + + /* send codec interrupts on irq1 and only use that one */ + gus_write(tmp_d.conf_base, 0x5a , 0x4f ); + + /* enable access to hidden regs */ + tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ ); + gus_write(tmp_d.conf_base, 0x5b , tmp | 1 ); + printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) ); + + strcpy(tmp_d.name, name); pcmattach(dev); } + +#if 0 +int +gus_mem_write(snddev_info *d, int addr, u_char data) +{ + gus_writew(d->conf_base, 0x43 , addr & 0xffff ); + gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); + outb(d->conf_base + 7, data); +} + +u_char +gus_mem_read(snddev_info *d, int addr) +{ + gus_writew(d->conf_base, 0x43 , addr & 0xffff ); + gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); + return inb(d->conf_base + 7); +} + +void +gus_mem_cfg(snddev_info *d) +{ + int base; + u_char old; + u_char a, b; + + printf("configuring gus memory...\n"); + gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/); + old = gus_read(d->conf_base, 0x19); + gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */ + for (base = 0; base < 1024; base++) { + a=gus_mem_read(d, base*1024); + a = ~a ; + gus_mem_write(d, base*1024, a); + b=gus_mem_read(d, base*1024); + if ( b != a ) + break ; + } + printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b); +} +#endif /* gus mem cfg... */ + #endif /* NPNP > 0 */ #endif /* NPCM > 0 */ diff --git a/sys/dev/sound/isa/mss.h b/sys/dev/sound/isa/mss.h index 4b34e51..994f323 100644 --- a/sys/dev/sound/isa/mss.h +++ b/sys/dev/sound/isa/mss.h @@ -143,90 +143,89 @@ ahead. * (Actually this is not a mapping but rather some kind of interleaving * solution). */ -#define GUSMAX_MIXER -#ifdef GUSMAX_MIXER -#define MODE1_REC_DEVICES \ - (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX) - -#define MODE1_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX) - -#define MODE2_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \ - SOUND_MASK_PCM | SOUND_MASK_IMIX) - -#else /* Generic mapping */ - -#define MODE1_REC_DEVICES \ - (SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX) -#define MODE1_MIXER_DEVICES \ - (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX) +#define MSS_REC_DEVICES \ + (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX) -#define MODE2_MIXER_DEVICES \ - (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \ - SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX) -#endif - -#define OPTI931_MIXER_DEVICES \ - (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ - SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN ) /* - * Most of the mixer entries work in backwards. Setting the polarity field - * makes them to work correctly. + * Table of mixer registers. There is a default table for the + * AD1848/CS423x clones, and one for the OPTI931. As more WSS + * clones come out, there ought to be more tables. + * + * Fields in the table are : polarity, register, offset, bits * * The channel numbering used by individual soundcards is not fixed. * Some cards have assigned different meanings for the AUX1, AUX2 * and LINE inputs. Some have different features... - * The current version doesn't try to compensate this. + * + * Following there is a macro ...MIXER_DEVICES which is a bitmap + * of all non-zero fields in the table. + * MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1 + * registers I0..I15) * */ -mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */ -MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5), +mixer_ent mix_devices[32][2] = { +MIX_NONE(SOUND_MIXER_VOLUME), +MIX_NONE(SOUND_MIXER_BASS), +MIX_NONE(SOUND_MIXER_TREBLE), +MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5), MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6), MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), -MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5), +MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5), MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_ALTPCM), +MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5) +MIX_NONE(SOUND_MIXER_OGAIN), +MIX_NONE(SOUND_MIXER_LINE1), +MIX_NONE(SOUND_MIXER_LINE2), +MIX_NONE(SOUND_MIXER_LINE3), }; +#define MODE2_MIXER_DEVICES \ + (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \ + SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ + SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) + +#define MODE1_MIXER_DEVICES \ + (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) + + +/* + * entries for the opti931... + */ + mixer_ent opti931_devices[32][2] = { /* for the opti931 */ MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_BASS), +MIX_NONE(SOUND_MIXER_TREBLE), MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4), MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5), -MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_SPEAKER), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_IMIX), +MIX_NONE(SOUND_MIXER_ALTPCM), +MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5) +MIX_NONE(SOUND_MIXER_OGAIN), +MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4), +MIX_NONE(SOUND_MIXER_LINE2), +MIX_NONE(SOUND_MIXER_LINE3), }; +#define OPTI931_MIXER_DEVICES \ + (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ + SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ + SOUND_MASK_IGAIN | SOUND_MASK_LINE1 ) + + static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = { 0x5a5a, /* Master Volume */ 0x3232, /* Bass */ diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c index e786f5f..d4825c1 100644 --- a/sys/dev/sound/isa/sb.c +++ b/sys/dev/sound/isa/sb.c @@ -11,13 +11,14 @@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A @@ -112,7 +113,7 @@ snddev_info sb_op_desc = { * resetting the dsp and testing if it is there. * Version detection etc. will be done at attach time. * - * Remebber, isa probe routines are supposed to return the + * Remember, ISA probe routines are supposed to return the * size of io space used. */ @@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev) { snddev_info *d = &pcm_info[dev->id_unit] ; + dev->id_alive = 16 ; /* number of io ports */ + /* should be already set but just in case... */ sb_dsp_init(d, dev); return 0 ; } @@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - d->flags = 0 ; d->bd_flags &= ~BD_F_HISPEED ; @@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); sb_reset_dsp(d->io_base); ask_init(d); @@ -287,17 +285,18 @@ again: reason |= 2; } } + /* XXX previous location of ack... */ + DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); + if ( d->dbuf_out.dl && (reason & 1) ) + dsp_wrintr(d); + if ( d->dbuf_in.dl && (reason & 2) ) + dsp_rdintr(d); + if ( c & 2 ) inb(DSP_DATA_AVL16); /* 16-bit int ack */ if (c & 1) inb(DSP_DATA_AVAIL); /* 8-bit int ack */ -DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); - if ( (d->flags & SND_F_WR_DMA) && (reason & 1) ) - dsp_wrintr(d); - if ( (d->flags & SND_F_RD_DMA) && (reason & 2) ) - dsp_rdintr(d); - /* * the sb16 might have multiple sources etc. */ @@ -320,7 +319,7 @@ static int sb_callback(snddev_info *d, int reason) { int rd = reason & SND_CB_RD ; - int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ; + int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ; switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ @@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1; - break ; + break; case SND_CB_START : /* called with int disabled */ - sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON); - d->flags &= ~SND_F_INIT ; if (d->bd_flags & BD_F_SB16) { /* the SB16 can do full duplex using one 16-bit channel * and one 8-bit channel. It needs to be programmed to * use split format though. + * We use the following algorithm: + * 1. check which direction(s) are active; + * 2. check if we should swap dma channels + * 3. check if we can do the swap. */ - int b16 ; - int swap = 0 ; + int swap = 1 ; /* default... */ - b16 = (rd) ? d->rec_fmt : d->play_fmt ; - b16 = (b16 == AFMT_S16_LE) ? 1 : 0; - /* - * check if I have to swap dma channels. Swap if - * - !rd, dma1 <4, b16 - * - !rd, dma1 >=4, !b16 - * - rd, dma2 <4, b16 - * - rd, dma2 >=4, !b16 - */ - if (!rd) { - if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1; + if (rd) { + if (d->flags & SND_F_WRITING || d->dbuf_out.dl) + swap = 0; + if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4) + swap = 0; + if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4) + swap = 0; } else { - if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1; + if (d->flags & SND_F_READING || d->dbuf_in.dl) + swap = 0; + if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4) + swap = 0; + if (d->play_fmt != AFMT_S16_LE && d->dma1 <4) + swap = 0; } - /* - * before swapping should make sure that there is no - * pending DMA on the other channel... - */ + if (swap) { int c = d->dma2 ; d->dma2 = d->dma1; d->dma1 = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + DEB(printf("START dma chan: play %d, rec %d\n", + d->dma1, d->dma2)); } - DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n", - d->play_fmt, d->rec_fmt, d->dma1, d->dma2)); } - /* fallthrough */ - case SND_CB_RESTART: + if (!rd) + sb_cmd(d->io_base, DSP_CMD_SPKON); + if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; - /* - * SB16 support still not completely working!!! - * - * in principle, on the SB16, I could support simultaneous - * play & rec. - * However, there is no way to ask explicitly for 8 or - * 16 bit transfer. As a consequence, if we do 8-bit, - * we need to use the 8-bit channel, and if we do 16-bit, - * we need to use the other one. The only way I find to - * do this is to swap d->dma1 and d->dma2 ... - * - */ if (rd) { c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC ; - c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; - if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; + c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->rec_fmt == AFMT_S16_LE) l /= 2 ; } else { c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC ; - c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->play_fmt == AFMT_S16_LE) l /= 2 ; @@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, c ); sb_cmd3(d->io_base, c1 , l - 1) ; } else { + /* code for the SB2 and SB3 */ u_char c ; if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ; + c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ; else - c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ; + c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ; sb_cmd3(d->io_base, c , l - 1) ; } break; + case SND_CB_ABORT : /* XXX */ case SND_CB_STOP : - /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */ + { + int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ + if (d->bd_flags & BD_F_SB16) { + if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) ) + cmd = DSP_CMD_DMAPAUSE_16 ; + } + if (d->bd_flags & BD_F_HISPEED) { + sb_reset_dsp(d->io_base); + d->flags |= SND_F_INIT ; + } else { + sb_cmd(d->io_base, cmd); /* pause dma. */ + /* + * This seems to have the side effect of blocking the other + * side as well so I have to re-enable it :( + */ + if ( (rd && d->dbuf_out.dl) || + (!rd && d->dbuf_in.dl) ) + sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? + 0xd6 : 0xd4); /* continue other dma */ + } + } + DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; } @@ -443,7 +459,7 @@ sb_reset_dsp(int io_base) DELAY(30); if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp failed\n")); + DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); return 0; /* Sorry */ } return 1; @@ -753,13 +769,13 @@ dsp_speed(snddev_info *d) * Now the speed should be valid. Compute the value to be * programmed into the board. * - * XXX check this code... + * XXX stereo init is still missing... */ if (speed > 22050) { /* High speed mode on 2.01/3.xx */ int tmp; - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); + tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; d->bd_flags |= BD_F_HISPEED ; flags = spltty(); @@ -1022,19 +1038,18 @@ static char * sb16pnp_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - if (vend_id == 0x2b008c0e) - s = "SB16 Value PnP" ; + /* - * The SB16/AWE64 cards seem to differ in the fourth byte of + * The SB16/AWExx cards seem to differ in the fourth byte of * the vendor id, so I have just masked it for the time being... * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e */ if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB AWE64 PnP"; + s = "SB16 PnP"; + else if (vend_id == 0x01009305) + s = "Avance Asound 100" ; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); diff --git a/sys/dev/sound/isa/sb.h b/sys/dev/sound/isa/sb.h index 8548997..d7a24d5 100644 --- a/sys/dev/sound/isa/sb.h +++ b/sys/dev/sound/isa/sb.h @@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ; * DSP Commands. There are many, and in many cases they are used explicitly */ +/* these are not used except for programmed I/O (not in this driver) */ #define DSP_DAC8 0x10 /* direct DAC output */ +#define DSP_ADC8 0x20 /* direct ADC input */ + +/* these should be used in the SB 1.0 */ #define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */ +#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */ + +/* these should be used in the SB 2.0 and 2.01 */ +#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */ +#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */ + +#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ +#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */ +#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */ + +/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */ + + /* prepare for dma input */ +#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0)) + #define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */ #define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */ -#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */ -#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */ - -#define DSP_ADC8 0x20 /* direct ADC input */ +#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */ -#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */ -#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */ +/* SB16 commands */ #define DSP_CMD_O16 0xb0 #define DSP_CMD_I16 0xb8 #define DSP_CMD_O8 0xc0 @@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ; #define DSP_CMD_SPKON 0xD1 #define DSP_CMD_SPKOFF 0xD3 #define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2)) -#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */ -#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */ -#define DSP_CMD_DMAHALT 0xD0 +#define DSP_CMD_DMAPAUSE_8 0xD0 +#define DSP_CMD_DMAPAUSE_16 0xD5 +#define DSP_CMD_DMAEXIT_8 0xDA +#define DSP_CMD_DMAEXIT_16 0xD9 #define DSP_CMD_TCONST 0x40 /* set time constant */ -#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ #define DSP_CMD_HSDAC 0x91 /* high speed dac */ -#define DSP_CMD_HSADC 0x99 /* high speed adc */ +#define DSP_CMD_HSADC 0x99 /* high speed adc */ #define DSP_CMD_GETVER 0xE1 #define DSP_CMD_GETID 0xE7 /* return id bytes */ - /* prepare for dma input */ -#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0)) #define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */ #define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */ #if 0 /*** unknown ***/ - /* - * D9 and D5 are used on the sb16 on close... maybe a reset of - * some subsystem ? - */ -#define DSP_CMD_D9 0xD9 -#define DSP_CMD_D5 0xD5 #define DSP_CMD_FA 0xFA /* get version from prosonic*/ #define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/ #endif diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index e786f5f..d4825c1 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -11,13 +11,14 @@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A @@ -112,7 +113,7 @@ snddev_info sb_op_desc = { * resetting the dsp and testing if it is there. * Version detection etc. will be done at attach time. * - * Remebber, isa probe routines are supposed to return the + * Remember, ISA probe routines are supposed to return the * size of io space used. */ @@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev) { snddev_info *d = &pcm_info[dev->id_unit] ; + dev->id_alive = 16 ; /* number of io ports */ + /* should be already set but just in case... */ sb_dsp_init(d, dev); return 0 ; } @@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - d->flags = 0 ; d->bd_flags &= ~BD_F_HISPEED ; @@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); sb_reset_dsp(d->io_base); ask_init(d); @@ -287,17 +285,18 @@ again: reason |= 2; } } + /* XXX previous location of ack... */ + DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); + if ( d->dbuf_out.dl && (reason & 1) ) + dsp_wrintr(d); + if ( d->dbuf_in.dl && (reason & 2) ) + dsp_rdintr(d); + if ( c & 2 ) inb(DSP_DATA_AVL16); /* 16-bit int ack */ if (c & 1) inb(DSP_DATA_AVAIL); /* 8-bit int ack */ -DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); - if ( (d->flags & SND_F_WR_DMA) && (reason & 1) ) - dsp_wrintr(d); - if ( (d->flags & SND_F_RD_DMA) && (reason & 2) ) - dsp_rdintr(d); - /* * the sb16 might have multiple sources etc. */ @@ -320,7 +319,7 @@ static int sb_callback(snddev_info *d, int reason) { int rd = reason & SND_CB_RD ; - int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ; + int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ; switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ @@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1; - break ; + break; case SND_CB_START : /* called with int disabled */ - sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON); - d->flags &= ~SND_F_INIT ; if (d->bd_flags & BD_F_SB16) { /* the SB16 can do full duplex using one 16-bit channel * and one 8-bit channel. It needs to be programmed to * use split format though. + * We use the following algorithm: + * 1. check which direction(s) are active; + * 2. check if we should swap dma channels + * 3. check if we can do the swap. */ - int b16 ; - int swap = 0 ; + int swap = 1 ; /* default... */ - b16 = (rd) ? d->rec_fmt : d->play_fmt ; - b16 = (b16 == AFMT_S16_LE) ? 1 : 0; - /* - * check if I have to swap dma channels. Swap if - * - !rd, dma1 <4, b16 - * - !rd, dma1 >=4, !b16 - * - rd, dma2 <4, b16 - * - rd, dma2 >=4, !b16 - */ - if (!rd) { - if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1; + if (rd) { + if (d->flags & SND_F_WRITING || d->dbuf_out.dl) + swap = 0; + if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4) + swap = 0; + if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4) + swap = 0; } else { - if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1; + if (d->flags & SND_F_READING || d->dbuf_in.dl) + swap = 0; + if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4) + swap = 0; + if (d->play_fmt != AFMT_S16_LE && d->dma1 <4) + swap = 0; } - /* - * before swapping should make sure that there is no - * pending DMA on the other channel... - */ + if (swap) { int c = d->dma2 ; d->dma2 = d->dma1; d->dma1 = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + DEB(printf("START dma chan: play %d, rec %d\n", + d->dma1, d->dma2)); } - DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n", - d->play_fmt, d->rec_fmt, d->dma1, d->dma2)); } - /* fallthrough */ - case SND_CB_RESTART: + if (!rd) + sb_cmd(d->io_base, DSP_CMD_SPKON); + if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; - /* - * SB16 support still not completely working!!! - * - * in principle, on the SB16, I could support simultaneous - * play & rec. - * However, there is no way to ask explicitly for 8 or - * 16 bit transfer. As a consequence, if we do 8-bit, - * we need to use the 8-bit channel, and if we do 16-bit, - * we need to use the other one. The only way I find to - * do this is to swap d->dma1 and d->dma2 ... - * - */ if (rd) { c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC ; - c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; - if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; + c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->rec_fmt == AFMT_S16_LE) l /= 2 ; } else { c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC ; - c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->play_fmt == AFMT_S16_LE) l /= 2 ; @@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, c ); sb_cmd3(d->io_base, c1 , l - 1) ; } else { + /* code for the SB2 and SB3 */ u_char c ; if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ; + c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ; else - c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ; + c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ; sb_cmd3(d->io_base, c , l - 1) ; } break; + case SND_CB_ABORT : /* XXX */ case SND_CB_STOP : - /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */ + { + int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ + if (d->bd_flags & BD_F_SB16) { + if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) ) + cmd = DSP_CMD_DMAPAUSE_16 ; + } + if (d->bd_flags & BD_F_HISPEED) { + sb_reset_dsp(d->io_base); + d->flags |= SND_F_INIT ; + } else { + sb_cmd(d->io_base, cmd); /* pause dma. */ + /* + * This seems to have the side effect of blocking the other + * side as well so I have to re-enable it :( + */ + if ( (rd && d->dbuf_out.dl) || + (!rd && d->dbuf_in.dl) ) + sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? + 0xd6 : 0xd4); /* continue other dma */ + } + } + DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; } @@ -443,7 +459,7 @@ sb_reset_dsp(int io_base) DELAY(30); if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp failed\n")); + DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); return 0; /* Sorry */ } return 1; @@ -753,13 +769,13 @@ dsp_speed(snddev_info *d) * Now the speed should be valid. Compute the value to be * programmed into the board. * - * XXX check this code... + * XXX stereo init is still missing... */ if (speed > 22050) { /* High speed mode on 2.01/3.xx */ int tmp; - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); + tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; d->bd_flags |= BD_F_HISPEED ; flags = spltty(); @@ -1022,19 +1038,18 @@ static char * sb16pnp_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - if (vend_id == 0x2b008c0e) - s = "SB16 Value PnP" ; + /* - * The SB16/AWE64 cards seem to differ in the fourth byte of + * The SB16/AWExx cards seem to differ in the fourth byte of * the vendor id, so I have just masked it for the time being... * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e */ if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB AWE64 PnP"; + s = "SB16 PnP"; + else if (vend_id == 0x01009305) + s = "Avance Asound 100" ; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index e786f5f..d4825c1 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -11,13 +11,14 @@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A @@ -112,7 +113,7 @@ snddev_info sb_op_desc = { * resetting the dsp and testing if it is there. * Version detection etc. will be done at attach time. * - * Remebber, isa probe routines are supposed to return the + * Remember, ISA probe routines are supposed to return the * size of io space used. */ @@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev) { snddev_info *d = &pcm_info[dev->id_unit] ; + dev->id_alive = 16 ; /* number of io ports */ + /* should be already set but just in case... */ sb_dsp_init(d, dev); return 0 ; } @@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - d->flags = 0 ; d->bd_flags &= ~BD_F_HISPEED ; @@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); sb_reset_dsp(d->io_base); ask_init(d); @@ -287,17 +285,18 @@ again: reason |= 2; } } + /* XXX previous location of ack... */ + DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); + if ( d->dbuf_out.dl && (reason & 1) ) + dsp_wrintr(d); + if ( d->dbuf_in.dl && (reason & 2) ) + dsp_rdintr(d); + if ( c & 2 ) inb(DSP_DATA_AVL16); /* 16-bit int ack */ if (c & 1) inb(DSP_DATA_AVAIL); /* 8-bit int ack */ -DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); - if ( (d->flags & SND_F_WR_DMA) && (reason & 1) ) - dsp_wrintr(d); - if ( (d->flags & SND_F_RD_DMA) && (reason & 2) ) - dsp_rdintr(d); - /* * the sb16 might have multiple sources etc. */ @@ -320,7 +319,7 @@ static int sb_callback(snddev_info *d, int reason) { int rd = reason & SND_CB_RD ; - int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ; + int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ; switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ @@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1; - break ; + break; case SND_CB_START : /* called with int disabled */ - sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON); - d->flags &= ~SND_F_INIT ; if (d->bd_flags & BD_F_SB16) { /* the SB16 can do full duplex using one 16-bit channel * and one 8-bit channel. It needs to be programmed to * use split format though. + * We use the following algorithm: + * 1. check which direction(s) are active; + * 2. check if we should swap dma channels + * 3. check if we can do the swap. */ - int b16 ; - int swap = 0 ; + int swap = 1 ; /* default... */ - b16 = (rd) ? d->rec_fmt : d->play_fmt ; - b16 = (b16 == AFMT_S16_LE) ? 1 : 0; - /* - * check if I have to swap dma channels. Swap if - * - !rd, dma1 <4, b16 - * - !rd, dma1 >=4, !b16 - * - rd, dma2 <4, b16 - * - rd, dma2 >=4, !b16 - */ - if (!rd) { - if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1; + if (rd) { + if (d->flags & SND_F_WRITING || d->dbuf_out.dl) + swap = 0; + if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4) + swap = 0; + if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4) + swap = 0; } else { - if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1; + if (d->flags & SND_F_READING || d->dbuf_in.dl) + swap = 0; + if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4) + swap = 0; + if (d->play_fmt != AFMT_S16_LE && d->dma1 <4) + swap = 0; } - /* - * before swapping should make sure that there is no - * pending DMA on the other channel... - */ + if (swap) { int c = d->dma2 ; d->dma2 = d->dma1; d->dma1 = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + DEB(printf("START dma chan: play %d, rec %d\n", + d->dma1, d->dma2)); } - DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n", - d->play_fmt, d->rec_fmt, d->dma1, d->dma2)); } - /* fallthrough */ - case SND_CB_RESTART: + if (!rd) + sb_cmd(d->io_base, DSP_CMD_SPKON); + if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; - /* - * SB16 support still not completely working!!! - * - * in principle, on the SB16, I could support simultaneous - * play & rec. - * However, there is no way to ask explicitly for 8 or - * 16 bit transfer. As a consequence, if we do 8-bit, - * we need to use the 8-bit channel, and if we do 16-bit, - * we need to use the other one. The only way I find to - * do this is to swap d->dma1 and d->dma2 ... - * - */ if (rd) { c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC ; - c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; - if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; + c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->rec_fmt == AFMT_S16_LE) l /= 2 ; } else { c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC ; - c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->play_fmt == AFMT_S16_LE) l /= 2 ; @@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, c ); sb_cmd3(d->io_base, c1 , l - 1) ; } else { + /* code for the SB2 and SB3 */ u_char c ; if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ; + c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ; else - c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ; + c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ; sb_cmd3(d->io_base, c , l - 1) ; } break; + case SND_CB_ABORT : /* XXX */ case SND_CB_STOP : - /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */ + { + int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ + if (d->bd_flags & BD_F_SB16) { + if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) ) + cmd = DSP_CMD_DMAPAUSE_16 ; + } + if (d->bd_flags & BD_F_HISPEED) { + sb_reset_dsp(d->io_base); + d->flags |= SND_F_INIT ; + } else { + sb_cmd(d->io_base, cmd); /* pause dma. */ + /* + * This seems to have the side effect of blocking the other + * side as well so I have to re-enable it :( + */ + if ( (rd && d->dbuf_out.dl) || + (!rd && d->dbuf_in.dl) ) + sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? + 0xd6 : 0xd4); /* continue other dma */ + } + } + DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; } @@ -443,7 +459,7 @@ sb_reset_dsp(int io_base) DELAY(30); if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp failed\n")); + DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); return 0; /* Sorry */ } return 1; @@ -753,13 +769,13 @@ dsp_speed(snddev_info *d) * Now the speed should be valid. Compute the value to be * programmed into the board. * - * XXX check this code... + * XXX stereo init is still missing... */ if (speed > 22050) { /* High speed mode on 2.01/3.xx */ int tmp; - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); + tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; d->bd_flags |= BD_F_HISPEED ; flags = spltty(); @@ -1022,19 +1038,18 @@ static char * sb16pnp_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - if (vend_id == 0x2b008c0e) - s = "SB16 Value PnP" ; + /* - * The SB16/AWE64 cards seem to differ in the fourth byte of + * The SB16/AWExx cards seem to differ in the fourth byte of * the vendor id, so I have just masked it for the time being... * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e */ if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB AWE64 PnP"; + s = "SB16 PnP"; + else if (vend_id == 0x01009305) + s = "Avance Asound 100" ; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); diff --git a/sys/i386/isa/snd/README b/sys/i386/isa/snd/README index 70e3c11..ac45b22 100644 --- a/sys/i386/isa/snd/README +++ b/sys/i386/isa/snd/README @@ -1,8 +1,8 @@ - --- A new FreeBSD sound driver --- - by Luigi Rizzo (luigi@iet.unipi.it) + --- A new FreeBSD audio driver --- + by Luigi Rizzo (luigi@iet.unipi.it) -This is an experimental version of the sound driver for FreeBSD. +This is an experimental version of the audio driver for FreeBSD. I have almost completely rewritten the main parts of the code, starting from the Voxware 3.5-alpha release with patches from Amancio Hasty. The only file which is largely similar to the original @@ -20,15 +20,11 @@ For PnP cards, I also include the vendor_id and serial numbers of cards I have encountered. CS4236: PnP id 0x3642630e +CS4237: PnP id 0x3742630e works like a charm. All modes, including full duplex, supported in MSS mode. -CS4237: PnP id 0x3742630e - - I had early reports of success with this board, which is almost - the same as the CS4236. - CS4232: PnP id 0x3242630e this chip is reported as broken in the OSS documentation. As a @@ -39,32 +35,66 @@ CS4232: PnP id 0x3242630e OPTi931: PnP id 0x3109143e - The data sheets of this chip are very cryptic. I have it working - in full duplex in all modes _except_ capture of uLAW/ALAW data. - I am strongly convinced of a bug in the chip. I have sent email - to OPTI but got no reply so far. In SB emulation mode the - driver does not work yet (maybe I do not initialize it the - right way). - - Another bug seems to affect full duplex operation -- it appears - that at times DMA transfer are requested but not counted by - the device. In normal DMA mode this causes deadlocks. The only - solution I have found is to fetch the count from the ISA DMA - registers, but this does not seem to work very well either. + The data sheets of this chip are very cryptic and do not match + what the cards I have seem to do. I have it working + in WSS emulation, in full duplex and all modes. + In SB emulation mode the driver does not work yet (maybe I do + not initialize it the right way). It is not a major concern + anyways. + I am strongly convinced of a couple of bugs in the chip. I have + sent email to OPTI but got no reply so far. The bugs are: + - you cannot set both playback and capture format to use + a companded mode (ULAW, ALAW). If you do, the card will screw + up on the capture section. + The driver has a partial fix in software: when using ULAW, it + programs ULAW on the playback section, U8 on the capture, and + does a conversion in software (much like the SBPro). Of course + you lose 4-5 bits of dynamic range in the process. + - in full duplex (and single DMA mode), the card occasionally + misses interrupts, or generates spurious ones. Spurious ints are + not problematic since they can be ignored, but missed ones are + as you can imagine... This is fixed by auto-dma mode. SB16 PnP: PnP id 0xXX008c0e There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16, - etc. all differing in the PnP id (and with different synthesis - devices, which we do not support anyways). + etc.) all differing in the PnP id. They have different synthesis + devices, which we do not support, so we are not affected by these + differences. Don't worry if the driver identifies the card as a + different SB16 than the one you have. + + Full duplex support of this card is tricky since one channel can + work in 16-bit and the other in 8-bit mode. You will need to use + the new set of ioctl to use separate data formats on the two + channels (the vat driver does this). Of course, quality in 8-bit + is much lower than in 16-bit. + + Full duplex operation is unsupported by Creative. It seems to + work, although on my Vibra16 the command to stop DMA transfer + seems to erroneously affect both channels instead of the one + they are issued for. The driver has a workaround, but I cannot + guarantee that it works for everybody. I have had several + positive reports. + +Yamaha SA2 + + this card emulates a WSS or SB. Have reports that it works, although + it has mixer problems (maybe the driver has the wrong set of + parameters). - Since 970903 the driver supports them all, both capture and - playback, 8 and 16 bits. +Ensoniq Soundscape VIVO + + this card emulates a WSS or SB. Have reports that it works. GusPnP: PnP id 0x0100561e - I have code to recognize the board as MSS, but have not tested it - since I don't own the board. Hopefully someone will test it soon. + I have code to recognize the board as MSS, but some initializations + of the IW chip is still missing, so the card is not supported yet. + I am working on this problem... + +OPTI924: PnP + + I have this card but it is still unsupported. OPTI925: PnP id 0x2509143e @@ -76,3 +106,21 @@ OPTI930: should work as an MSS clone, but support for it is not implemented yet. +ESS1868 + + this card is not supported yet. It might work in SB emulation but + am not sure. + +ESS688 + + this card is used on many notebook. I don't have docs on this card + so I cannot support it. Pointers to the data sheets are welcome. + +PCI cards: + + some vendors have PCI cards. This code _cannot_ work on these + cards as it is now, since they cannot obviously use the ISA DMA + controller. As there are no data sheets available for these PCI + cards, none of them is supported at the moment, although support + should be easy as soon as I can put my hands on the data sheets + and cards. diff --git a/sys/i386/isa/snd/ad1848.c b/sys/i386/isa/snd/ad1848.c index 1188d32..0cd2dfd 100644 --- a/sys/i386/isa/snd/ad1848.c +++ b/sys/i386/isa/snd/ad1848.c @@ -1,30 +1,24 @@ /* * sound/ad1848.c * - * Modified by Luigi Rizzo (luigi@iet.unipi.it) - * * Driver for Microsoft Sound System/Windows Sound System (mss) * -compatible boards. This includes: * - * AD1848, CS4248 - * - * CS4231, used in the GUS MAX and some other cards; - * AD1845, CS4231A (CS4231-like) - * CS4232 (CS4231+SB and MPU, PnP) - * CS4236 (upgrade of the CS4232, has a better mixer) - * OPTi931 (WSS compatible, full duplex, some differences from CS42xx) + * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others. * * Copyright Luigi Rizzo, 1997 * Copyright by Hannu Savolainen 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -39,14 +33,11 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * * Full data sheets in PDF format for the MSS-compatible chips * are available at * * http://www.crystal.com/ for the CS42XX series, or * http://www.opti.com/ for the OPTi931 - * - * The OPTi931 appears to be quite buggy. */ #include <i386/isa/snd/sound.h> @@ -114,11 +105,12 @@ snddev_info mss_op_desc = { AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */ /* * the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE + * but we do not use these modes. */ } ; /* - * this is the probe routine. Note, it is not necessary to + * mss_probe() is the probe routine. Note, it is not necessary to * go through this for PnP devices, since they are already * indentified precisely using their PnP id. * @@ -158,13 +150,13 @@ mss_probe(struct isa_device *dev) tmp = inb(dev->id_iobase + 3); if (tmp == 0xff) { /* Bus float */ - DDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); + DEB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); dev->id_flags &= ~DV_F_TRUE_MSS ; goto mss_probe_end; } tmp &= 0x3f ; if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) { - DDB(printf("No MSS signature detected on port 0x%x (0x%x)\n", + DEB(printf("No MSS signature detected on port 0x%x (0x%x)\n", dev->id_iobase, inb(dev->id_iobase + 3))); return 0; } @@ -207,6 +199,9 @@ mss_attach(struct isa_device *dev) d->name, dev->id_unit, d->io_base, d->irq, d->dma1, d->dma2, dev->id_flags); + dev->id_alive = 8 ; /* number of io ports */ + /* should be already set but just in case... */ + if ( dev->id_flags & DV_F_TRUE_MSS ) { /* has IRQ/DMA registers, set IRQ and DMA addr */ static char interrupt_bits[12] = { @@ -238,11 +233,14 @@ mss_attach(struct isa_device *dev) printf("invalid dual dma config %d:%d\n", d->dma1, d->dma2); dev->id_irq = 0 ; + dev->id_alive = 0 ; /* this makes attach fail. */ return 0 ; } outb(dev->id_iobase, bits ); } } + if (d->dma1 != d->dma2) + d->audio_fmt |= AFMT_FULLDUPLEX ; mss_reinit(d); ad1848_mixer_reset(d); return 0; @@ -295,9 +293,6 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; @@ -313,9 +308,7 @@ mss_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_S16_LE ; break; } - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); - ask_init(d); + ask_init(d); /* and reset buffers... */ } splx(s); return 0 ; @@ -385,12 +378,13 @@ mss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p) /* * the callback routine to handle all dma ops etc. + * With the exception of INIT, all other callbacks are invoked + * with interrupts disabled. */ static int mss_callback(snddev_info *d, int reason) { - u_long s; u_char m; int retry, wr, cnt; @@ -405,14 +399,13 @@ mss_callback(snddev_info *d, int reason) d->rec_fmt = d->play_fmt ; /* no split format on the WSS */ snd_set_blocksize(d); mss_reinit(d); + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1 ; break ; case SND_CB_START : - /* fallthrough */ - case SND_CB_RESTART : - s = spltty(); - cnt = wr ? d->dbuf_out.dl0 : d->dbuf_in.dl0 ; + cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ; if (d->play_fmt == AFMT_S16_LE) cnt /= 2; if (d->flags & SND_F_STEREO) @@ -421,7 +414,7 @@ mss_callback(snddev_info *d, int reason) DEB(printf("-- (re)start cnt %d\n", cnt)); m = ad_read(d,9) ; - DDB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); + DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */ /* * on the OPTi931 the enable bit seems hard to set... @@ -438,11 +431,9 @@ mss_callback(snddev_info *d, int reason) else ad_write_cnt(d, 30, cnt); - splx(s); break ; case SND_CB_STOP : case SND_CB_ABORT : /* XXX check this... */ - s = spltty(); m = ad_read(d,9) ; m &= wr ? ~I9_PEN : ~I9_CEN ; /* Stop DMA */ /* @@ -455,14 +446,18 @@ mss_callback(snddev_info *d, int reason) if (retry == 0) printf("start dma, failed to clear bit 0x%02x 0x%02x\n", m, ad_read(d, 9) ) ; - - /* disable DMA by clearing count registers. */ +#if 1 + /* + * try to disable DMA by clearing count registers. Not sure it + * is needed, and it might cause false interrupts when the + * DMA is re-enabled later. + */ if (wr || (d->dma1 == d->dma2) ) ad_write_cnt(d, 14, 0); else ad_write_cnt(d, 30, 0); - splx(s); break; +#endif } return 0 ; } @@ -470,37 +465,56 @@ mss_callback(snddev_info *d, int reason) /* * main irq handler for the CS423x. The OPTi931 code is * a separate one. + * The correct way to operate for a device with multiple internal + * interrupt sources is to loop on the status register and ack + * interrupts until all interrupts are served and none are reported. At + * this point the IRQ line to the ISA IRQ controller should go low + * and be raised at the next interrupt. + * + * Since the ISA IRQ controller is sent EOI _before_ passing control + * to the isr, it might happen that we serve an interrupt early, in + * which case the status register at the next interrupt should just + * say that there are no more interrupts... */ static void mss_intr(int unit) { snddev_info *d = &pcm_info[unit]; - u_char i11, c; - u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ + u_char c, served = 0; + int i; - i11 = ad_read(d, 11); - reason = inb(io_Status(d)); + DEB(printf("mss_intr\n")); + ad_read(d, 11); /* fake read of status bits */ - if ( ! (reason & 1) ) /* no int, maybe a shared line ? */ - return; - /* get exact reason */ - c = (d->dma1 == d->dma2) ? 0x10 : ad_read(d, 24); - if ( (d->flags & SND_F_WR_DMA) && (c & 0x10) ) - dsp_wrintr(d); - c = (d->dma1 == d->dma2) ? 0x20 : ad_read(d, 24); - if ( (d->flags & SND_F_RD_DMA) && (c & 0x20) ) - dsp_rdintr(d); - /* XXX check this on the 4236... */ - if (d->dma1 == d->dma2) - outb(io_Status(d), 0); /* Clear interrupt status */ - else - ad_write(d, 24, ~c); /* ack selectively */ + /* + * loop until there are interrupts, but no more than 10 times. + */ + for (i=10 ; i && inb(io_Status(d)) & 1 ; i-- ) { + /* get exact reason for full-duplex boards */ + c = (d->dma1 == d->dma2) ? 0x30 : ad_read(d, 24); + c &= ~served ; + if ( d->dbuf_out.dl && (c & 0x10) ) { + served |= 0x10 ; + dsp_wrintr(d); + } + if ( d->dbuf_in.dl && (c & 0x20) ) { + served |= 0x20 ; + dsp_rdintr(d); + } + /* + * now ack the interrupt + */ + if (d->dma1 == d->dma2) + outb(io_Status(d), 0); /* Clear interrupt status */ + else + ad_write(d, 24, ~c); /* ack selectively */ + } } /* * the opti931 seems to miss interrupts when working in full - * duplex. god know why... + * duplex, so we try some heuristics to catch them. */ static void opti931_intr(int unit) @@ -509,8 +523,6 @@ opti931_intr(int unit) u_char masked=0, i11, mc11, c=0; u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ int loops = 10; - int w_miss=0, r_miss=0; - static int misses=0; /* XXX kludge */ #if 0 reason = inb(io_Status(d)); @@ -525,11 +537,11 @@ again: c=mc11 = (d->dma1 == d->dma2) ? 0xc : opti_read(d->conf_base, 11); mc11 &= 0x0c ; if (c & 0x10) { - printf("Warning CD interrupt\n"); + printf("Warning: CD interrupt\n"); mc11 |= 0x10 ; } if (c & 0x20) { - printf("Warning MPU interrupt\n"); + printf("Warning: MPU interrupt\n"); mc11 |= 0x20 ; } if (mc11 & masked) @@ -541,38 +553,18 @@ again: printf("one more try...\n"); goto again; } - if (w_miss || r_miss ) { - misses++; - if ( (misses & 0xff) == 1 ) - printf("opti931: %d missed irq (now %s%s)\n", - misses, w_miss?"play ":"", - r_miss?"capture ":""); - } - if (loops==10) + if (loops==10) { printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - if (w_miss) mc11 |= 4 ; - if (r_miss) mc11 |= 8 ; - if (mc11==0) - return; + } + return; } - if ( (d->flags & SND_F_RD_DMA) && (mc11 & 8) ) { + if ( d->dbuf_in.dl && (mc11 & 8) ) { dsp_rdintr(d); - r_miss = 0 ; } - else if (isa_dmastatus1(d->dma2) == 0 && (d->flags & SND_F_RD_DMA) ) - r_miss = 1 ; - - if ( (d->flags & SND_F_WR_DMA) && (mc11 & 4) ) { - if (isa_dmastatus1(d->dma1) != 0) - printf("opti931_intr: wr dma %d\n", - isa_dmastatus1(d->dma1)); + if ( d->dbuf_out.dl && (mc11 & 4) ) { dsp_wrintr(d); - w_miss = 0 ; } - else if (isa_dmastatus1(d->dma1) == 0 && (d->flags & SND_F_WR_DMA) ) - w_miss = 1 ; - opti_write(d->conf_base, 11, ~mc11); /* ack */ if (--loops) goto again; printf("xxx too many loops\n"); @@ -597,6 +589,36 @@ opti_read(int io_base, u_char reg) outb(io_base, reg); return inb(io_base+1); } + +static void +gus_write(int io_base, u_char reg, u_char value) +{ + outb(io_base + 3, reg); + outb(io_base + 5, value); +} + +static void +gus_writew(int io_base, u_char reg, u_short value) +{ + outb(io_base + 3, reg); + outb(io_base + 4, value); +} + +static u_char +gus_read(int io_base, u_char reg) +{ + outb(io_base+3, reg); + return inb(io_base+5); +} + +static u_short +gus_readw(int io_base, u_char reg) +{ + outb(io_base+3, reg); + return inw(io_base+4); +} + + /* * AD_WAIT_INIT waits if we are initializing the board and * we cannot modify its settings @@ -697,7 +719,8 @@ ad_enter_MCE(snddev_info *d) d->bd_flags |= BD_F_MCE_BIT; AD_WAIT_INIT(d, 100); prev = inb(io_Index_Addr(d)); - outb(io_Index_Addr(d), prev | IA_MCE | IA_TRD ) ; + prev &= ~IA_TRD ; + outb(io_Index_Addr(d), prev | IA_MCE ) ; } static void @@ -717,16 +740,14 @@ ad_leave_MCE(snddev_info *d) d->bd_flags &= ~BD_F_MCE_BIT; prev = inb(io_Index_Addr(d)); - - outb(io_Index_Addr(d), (prev & ~IA_MCE) | IA_TRD); /* Clear the MCE bit */ + prev &= ~IA_TRD ; + outb(io_Index_Addr(d), prev & ~IA_MCE ); /* Clear the MCE bit */ wait_for_calibration(d); splx(flags); } /* * only one source can be set... - * - * fixed -- lr 970725 */ static int mss_set_recsrc(snddev_info *d, int mask) @@ -803,7 +824,7 @@ mss_mixer_set(snddev_info *d, int dev, int value) mix_d = &(opti931_devices); if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { - DDB(printf("nbits = 0 for dev %d\n", dev) ); + DEB(printf("nbits = 0 for dev %d\n", dev) ); return EINVAL; } @@ -832,7 +853,7 @@ mss_mixer_set(snddev_info *d, int dev, int value) val = old & 0x7f ; /* clear mute bit. */ change_bits(mix_d, &val, dev, LEFT_CHN, left); ad_write(d, regoffs, val); - DEB(printf("dev %d reg %d old 0x%02x new 0x%02x\n", + DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", dev, regoffs, old, val)); if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ @@ -840,11 +861,13 @@ mss_mixer_set(snddev_info *d, int dev, int value) * Set the right channel */ regoffs = (*mix_d)[dev][RIGHT_CHN].regno; - val = ad_read(d, regoffs); + old = val = ad_read(d, regoffs); if (regoffs != 1) val = old & 0x7f ; /* clear mute bit. */ change_bits(mix_d, &val, dev, RIGHT_CHN, right); ad_write(d, regoffs, val); + DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); } return 0; /* success */ } @@ -861,12 +884,28 @@ ad1848_mixer_reset(snddev_info *d) else d->mix_devs = MODE1_MIXER_DEVICES; - d->mix_rec_devs = MODE1_REC_DEVICES; + d->mix_rec_devs = MSS_REC_DEVICES; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (d->mix_devs & (1 << i)) mss_mixer_set(d, i, default_mixer_levels[i]); mss_set_recsrc(d, SOUND_MASK_MIC); + /* + * some device-specific things, mostly mute the mic to + * the output mixer so as to avoid hisses. In many cases this + * is the default after reset, this code is here mostly as a + * reminder that this might be necessary on other boards. + */ + switch(d->bd_id) { + case MD_OPTI931: + ad_write(d, 20, 0x88); + ad_write(d, 21, 0x88); + break; + case MD_GUSPNP: + /* this is only necessary in mode 3 ... */ + ad_write(d, 22, 0x88); + ad_write(d, 23, 0x88); + } } /* @@ -874,8 +913,6 @@ ad1848_mixer_reset(snddev_info *d) * matching one. As a side effect, it returns the value to * be written in the speed bits of the codec. It does _NOT_ * set the speed of the device (but it should!) - * - * fixed lr970724 */ static int @@ -1009,7 +1046,7 @@ mss_detect(struct isa_device *dev) else break ; if ((inb(io_Index_Addr(d)) & IA_BUSY) != 0x00) { /* Not a AD1848 */ - DDB(printf("mss_detect error, busy still set (0x%02x)\n", + DEB(printf("mss_detect error, busy still set (0x%02x)\n", inb(io_Index_Addr(d)))); return 0; } @@ -1024,9 +1061,9 @@ mss_detect(struct isa_device *dev) tmp1 = ad_read(d, 0) ; tmp2 = ad_read(d, 1) ; if ( tmp1 != 0xaa || tmp2 != 0x45) { - DDB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", + DEB(printf("mss_detect error - IREG (0x%02x/0x%02x) want 0xaa/0x45\n", tmp1, tmp2)); - return 0; + return 0; } ad_write(d, 0, 0x45); @@ -1035,7 +1072,7 @@ mss_detect(struct isa_device *dev) tmp2 = ad_read(d, 1) ; if (tmp1 != 0x45 || tmp2 != 0xaa) { - DDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); + DEB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); return 0; } @@ -1049,7 +1086,7 @@ mss_detect(struct isa_device *dev) tmp1 = ad_read(d, 12); if ((tmp & 0x0f) != (tmp1 & 0x0f)) { - DDB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", + DEB(printf("mss_detect error - I12 (0x%02x was 0x%02x)\n", tmp1, tmp)); return 0; } @@ -1073,7 +1110,7 @@ mss_detect(struct isa_device *dev) for (i = 0; i < 16; i++) if ((tmp1 = ad_read(d, i)) != (tmp2 = ad_read(d, i + 16))) { - DDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", + DEB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", i, tmp1, tmp2)); /* * note - this seems to fail on the 4232 on I11. So we just break @@ -1110,14 +1147,14 @@ mss_detect(struct isa_device *dev) ad_write(d, 0, 0xaa); if ((tmp1 = ad_read(d, 16)) == 0xaa) { /* Rotten bits? */ - DDB(printf("mss_detect error - step H(%x)\n", tmp1)); + DEB(printf("mss_detect error - step H(%x)\n", tmp1)); return 0; } /* * Verify that some bits of I25 are read only. */ - DDB(printf("mss_detect() - step I\n")); + DEB(printf("mss_detect() - step I\n")); tmp1 = ad_read(d, 25); /* Original bits */ ad_write(d, 25, ~tmp1); /* Invert all bits */ if ((ad_read(d, 25) & 0xe7) == (tmp1 & 0xe7)) { @@ -1182,7 +1219,7 @@ mss_detect(struct isa_device *dev) break; case 0x83: /* CS4236 */ - case 0x03: /* CS4236 on Intel PR440FX motherboard */ + case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ name = "CS4236"; d->bd_id = MD_CS4236; break ; @@ -1197,7 +1234,7 @@ mss_detect(struct isa_device *dev) } } - DDB(printf("mss_detect() - Detected %s\n", name)); + DEB(printf("mss_detect() - Detected %s\n", name)); strcpy(d->name, name); dev->id_flags &= ~DV_F_DEV_MASK ; dev->id_flags |= (d->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK ; @@ -1246,11 +1283,20 @@ mss_reinit(snddev_info *d) ad_write(d, 8, r) ; if (d->dma1 != d->dma2) { +#if 0 + if (d->bd_id == MD_GUSPNP && d->play_fmt == AFMT_MU_LAW) { + printf("warning, cannot do ulaw rec + play on the GUS\n"); + r = 0 ; /* move to U8 */ + } +#endif ad_write(d, 28, r & 0xf0 ) ; /* capture mode */ ad_write(d, 9, 0 /* no capture, no playback, dual dma */) ; } else ad_write(d, 9, 4 /* no capture, no playback, single dma */) ; ad_leave_MCE(d); + /* + * not sure if this is really needed... + */ ad_write_cnt(d, 14, 0 ); /* playback count */ if (d->dma1 != d->dma2) ad_write_cnt(d, 30, 0 ); /* rec. count on dual dma */ @@ -1271,31 +1317,34 @@ mss_reinit(snddev_info *d) #if NPNP > 0 -static char * cs4236_probe(u_long csn, u_long vend_id); -static void cs4236_attach(u_long csn, u_long vend_id, char *name, +static char * cs423x_probe(u_long csn, u_long vend_id); +static void cs423x_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); -static struct pnp_device cs4236 = { - "cs423x", - cs4236_probe, - cs4236_attach, +static struct pnp_device cs423x = { + "cs423x/ymh0020", + cs423x_probe, + cs423x_attach, &nsnd, /* use this for all sound cards */ &tty_imask /* imask */ }; -DATA_SET (pnpdevice_set, cs4236); +DATA_SET (pnpdevice_set, cs423x); static char * -cs4236_probe(u_long csn, u_long vend_id) +cs423x_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x3742630e) + u_long id = vend_id & 0xff00ffff; + if ( id == 0x3700630e ) s = "CS4237" ; - else if (vend_id == 0x3642630e) + else if ( id == 0x3600630e ) s = "CS4236" ; - else if (vend_id == 0x360b630e) - s = "CS4236" ; - else if (vend_id == 0x3242630e) + else if ( id == 0x3200630e) s = "CS4232" ; + else if ( id == 0x2000a865) + s = "Yamaha SA2"; + else if (vend_id == 0x8140d315) + s = "SoundscapeVIVO"; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1312,7 +1361,7 @@ cs4236_probe(u_long csn, u_long vend_id) extern snddev_info sb_op_desc; static void -cs4236_attach(u_long csn, u_long vend_id, char *name, +cs423x_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d ; @@ -1324,27 +1373,51 @@ cs4236_attach(u_long csn, u_long vend_id, char *name, return ; } snddev_last_probed = &tmp_d; - if (d.flags & DV_PNP_SBCODEC) { - printf("CS423x use sb-compatible codec\n"); - dev->id_iobase = d.port[2] ; + if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/ + dev->id_alive = 16 ; /* number of io ports ? */ tmp_d = sb_op_desc ; - tmp_d.alt_base = d.port[0] - 4; + if (vend_id == 0x2000a865 || vend_id == 0x8140d315) { + /* Yamaha SA2 or ENSONIQ SoundscapeVIVO ENS4081 */ + dev->id_iobase = d.port[0] ; + tmp_d.alt_base = d.port[1] ; + d.irq[1] = 0 ; /* only needed for the VIVO */ + } else { + dev->id_iobase = d.port[2] ; + tmp_d.alt_base = d.port[0] - 4; + } d.drq[1] = 4 ; /* disable, it is not used ... */ - } else { - /* mss-compatible codec */ - dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ + } else { /* mss-compatible codec */ + dev->id_alive = 8 ; /* number of io ports ? */ tmp_d = mss_op_desc ; - switch (vend_id) { - case 0x3742630e: /* CS4237 */ - case 0x3642630e: /* CS4236 */ - case 0x360b630e: /* CS4236 on Intel PR440FX motherboard */ - tmp_d.bd_id = MD_CS4236 ; /* to short-circuit the detect routine */ + dev->id_iobase = d.port[0] -4 ; /* XXX old mss have 4 bytes before... */ + tmp_d.alt_base = d.port[2]; + switch (vend_id & 0xff00ffff) { + + case 0x2000a865: /* yamaha SA-2 */ + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + tmp_d.bd_id = MD_YM0020 ; + break; + + case 0x8100d315: /* ENSONIQ SoundscapeVIVO */ + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + tmp_d.bd_id = MD_VIVO ; + d.irq[1] = 0 ; break; - default: + + case 0x3700630e: /* CS4237 */ + tmp_d.bd_id = MD_CS4237 ; + break; + + case 0x3600630e: /* CS4236 */ + tmp_d.bd_id = MD_CS4236 ; + break; + + default: tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */ break; } - tmp_d.alt_base = d.port[2]; strcpy(tmp_d.name, name); tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; } @@ -1356,7 +1429,6 @@ cs4236_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - dev->id_alive = 1; pcmattach(dev); } @@ -1428,7 +1500,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name, dev->id_iobase = d.port[0] - 4 ; /* old mss have 4 bytes before... */ tmp_d.io_base = dev->id_iobase; /* needed for ad_write to work... */ tmp_d.alt_base = d.port[2]; - opti_write(p, 4, 0x56 /* fifo 1/2, OPL3, audio enable, SB3.2 */ ); + opti_write(p, 4, 0xd6 /* fifo empty, OPL3, audio enable, SB3.2 */ ); ad_write (&tmp_d, 10, 2); /* enable interrupts */ if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ @@ -1459,6 +1531,8 @@ opti931_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } +static void gus_mem_cfg(snddev_info *tmp); + static char *guspnp_probe(u_long csn, u_long vend_id); static void guspnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); @@ -1493,7 +1567,14 @@ guspnp_attach(u_long csn, u_long vend_id, char *name, struct pnp_cinfo d ; snddev_info tmp_d ; /* patched copy of the basic snddev_info */ + u_char tmp; + read_pnp_parms ( &d , 0 ) ; + + /* d.irq[1] = d.irq[0] ; */ + printf("pnp_read 0xf2 returns 0x%x\n", pnp_read(0xf2) ); + pnp_write ( 0xf2, 0xff ); /* enable power on the guspnp */ + write_pnp_parms ( &d , 0 ); enable_pnp_card(); @@ -1501,14 +1582,89 @@ guspnp_attach(u_long csn, u_long vend_id, char *name, snddev_last_probed = &tmp_d; dev->id_iobase = d.port[2] - 4 ; /* room for 4 mss registers */ - dev->id_drq = d.drq[0] ; /* primary dma */ + dev->id_drq = d.drq[1] ; /* XXX PLAY dma */ dev->id_irq = (1 << d.irq[0] ) ; dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; + dev->id_flags = DV_F_DUAL_DMA | d.drq[0] ; /* REC dma */ + + tmp_d.io_base = d.port[2] - 4; + tmp_d.alt_base = d.port[0]; /* 0x220 */ + tmp_d.conf_base = d.port[1]; /* gus control block... */ + tmp_d.bd_id = MD_GUSPNP ; + + /* reset */ + gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 0 );/* Pull reset */ + DELAY(1000 * 30); + /* release reset and enable DAC */ + gus_write(tmp_d.conf_base, 0x4c /* _URSTI */, 3 ); + printf("resetting the gus...\n"); + DELAY(1000 * 30); + /* end of reset */ - tmp_d.alt_base = d.port[0]; + outb( tmp_d.alt_base, 0xC ); /* enable int and dma */ + + /* + * unmute left & right line. Need to go in mode3, unmute, + * and back to mode 2 + */ + tmp = ad_read(&tmp_d, 0x0c); + ad_write(&tmp_d, 0x0c, 0x6c ); /* special value to enter mode 3 */ + ad_write(&tmp_d, 0x19, 0 ); /* unmute left */ + ad_write(&tmp_d, 0x1b, 0 ); /* unmute right */ + ad_write(&tmp_d, 0x0c, tmp ); /* restore old mode */ + + /* send codec interrupts on irq1 and only use that one */ + gus_write(tmp_d.conf_base, 0x5a , 0x4f ); + + /* enable access to hidden regs */ + tmp = gus_read(tmp_d.conf_base, 0x5b /* IVERI */ ); + gus_write(tmp_d.conf_base, 0x5b , tmp | 1 ); + printf("GUS: silicon rev %c\n", 'A' + ( ( tmp & 0xf ) >> 4) ); + + strcpy(tmp_d.name, name); pcmattach(dev); } + +#if 0 +int +gus_mem_write(snddev_info *d, int addr, u_char data) +{ + gus_writew(d->conf_base, 0x43 , addr & 0xffff ); + gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); + outb(d->conf_base + 7, data); +} + +u_char +gus_mem_read(snddev_info *d, int addr) +{ + gus_writew(d->conf_base, 0x43 , addr & 0xffff ); + gus_write(d->conf_base, 0x44 , (addr>>16) & 0xff ); + return inb(d->conf_base + 7); +} + +void +gus_mem_cfg(snddev_info *d) +{ + int base; + u_char old; + u_char a, b; + + printf("configuring gus memory...\n"); + gus_writew(d->conf_base, 0x52 /* LMCFI */, 1 /* 512K*/); + old = gus_read(d->conf_base, 0x19); + gus_write(d->conf_base, 0x19, old | 1); /* enable enhaced mode */ + for (base = 0; base < 1024; base++) { + a=gus_mem_read(d, base*1024); + a = ~a ; + gus_mem_write(d, base*1024, a); + b=gus_mem_read(d, base*1024); + if ( b != a ) + break ; + } + printf("Have found %d KB ( 0x%x != 0x%x)\n", base, a, b); +} +#endif /* gus mem cfg... */ + #endif /* NPNP > 0 */ #endif /* NPCM > 0 */ diff --git a/sys/i386/isa/snd/dmabuf.c b/sys/i386/isa/snd/dmabuf.c index e01ddd6..d9a5f37 100644 --- a/sys/i386/isa/snd/dmabuf.c +++ b/sys/i386/isa/snd/dmabuf.c @@ -1,56 +1,58 @@ /* * snd/dmabuf.c * - * New DMA routines -- Luigi Rizzo, 19 jul 97 * This file implements the new DMA routines for the sound driver. + * AUTO DMA MODE (ISA DMA SIDE). * * Copyright by Luigi Rizzo - 1997 * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. 2. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * */ #include <i386/isa/snd/sound.h> #include <i386/isa/snd/ulaw.h> #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ -#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */ -#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS) +#define DMA_ALIGN_THRESHOLD 4 #define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1)) static void dsp_wr_dmadone(snddev_info *d); static void dsp_rd_dmadone(snddev_info *d); /* - * - -SOUND OUTPUT + * SOUND OUTPUT We use a circular buffer to store samples directed to the DAC. -The buffer is split into three variable-size regions, each identified -by an offset in the buffer (dp,rp,fp) and a lenght (dl,rl,fl). +The buffer is split into two variable-size regions, each identified +by an offset in the buffer (rp,fp) and a lenght (rl,fl). dl contains +the length of the current DMA transfer, dl=0 means that the dma is +idle. - 0 dp,dl rp,rl fp,fl bufsize - |__________|_____>______|_____________|________| - FREE DMA READY FREE + 0 rp,rl fp,fl bufsize + |__________>____________>________| + FREE d READY w FREE READY region: contains data written from the process and ready to be sent to the DAC; @@ -58,190 +60,149 @@ by an offset in the buffer (dp,rp,fp) and a lenght (dl,rl,fl). FREE region: is the empty region of the buffer, where a process can write new data. - DMA region: contains data being sent to the DAC by the DMA engine. - the actual boundary between the FREE and READY regions is in - fact within the DMA region (indicated by the > symbol above), - and advances as the DMA engine makes progress. - Both the "READY" and "FREE" regions can wrap around the end of the -buffer. The "DMA" region can only wrap if AUTO DMA is used, otherwise -it cannot cross the end of the buffer. - -Since dl can be updated on the fly, dl0 marks the value used when the -operation was started. When using AUTO DMA, bufsize-(count in the ISA DMA -register) directly reflects the position of dp. - -At initialization, DMA and READY are empty, and FREE takes all the -available space: +buffer. At initialization, READY is empty, FREE takes all the +available space, and dma is idle. - dp = rp = fp = 0 ; -- beginning of buffer - dl0 = dl = 0 ; -- meaning no dma activity - rl = 0 ; -- meaning no data ready - fl = bufsize ; +The two boundaries (rp,fp) in the buffers are advanced by DMA [d] +and write() [w] operations. The first block of the READY region +is used for DMA transfers. The transfer is started at rp and with +chunks of length dl. During DMA operations, rp advances and rl,fl +are updated by dsp_wr_dmaupdate(). When a new block is written, +fp advances and rl,fl are updated accordingly. -The DMA region is also empty whenever there is no DMA activity, for -whatever reason (e.g. no ready data, or output is paused). The code works as follows: the user write routine dsp_write_body() fills up the READY region with new data (reclaiming space from the -FREE region) and starts the write DMA engine if inactive (activity -is indicated by d->flags & SND_F_WR_DMA ). The size of each -DMA transfer is chosen according to a series of rules which will be -discussed later. When a DMA transfer is complete, an interrupt causes -dsp_wrintr() to be called which empties the DMA region, extends -the FREE region and possibly starts the next transfer. +FREE region) and starts the write DMA engine if inactive. When a +DMA transfer is complete, an interrupt causes dsp_wrintr() to be +called which extends the FREE region and possibly starts the next +transfer. In some cases, the code tries to track the current status of DMA -operations by calling isa_dmastatus() and advancing the boundary -between FREE and DMA regions accordingly. +operations by calling dsp_wr_dmaupdate() which changes rp, rl and fl. -The size of a DMA transfer is selected according to the following -rules: +The sistem tries to make all DMA transfers use the same size, +play_blocksize or rec_blocksize. The size is either selected +by the user, or computed by the system to correspond to about .25s of +audio. The blocksize must be within a range which is currently: - 1. when not using AUTO DMA, do not wrap around the end of the - buffer, and do not let fp move too close to the end of the - buffer; + min(5ms, 40 bytes) ... 1/2 buffer size. - 2. do not use more than half of the buffer size. - This serves to allow room for a next write operation concurrent - with the dma transfer, and to reduce the time which is necessary - to wait before a pending dma will end (optionally, the max - size could be further limited to a fixed amount of play time, - depending on number of channels, sample size and sample speed); +When there aren't enough data (write) or space (read), a transfer +is started with a reduced size. - 3. use the current blocksize (either specified by the user, or - corresponding roughly to 0.25s of data); +To reduce problems in case of overruns, the routine which fills up the +buffer should initialize (e.g. by repeating the last value) a +reasonably long area after the last block so that no noise is +produced on overruns. * */ /* - * dsp_wr_dmadone moves the write DMA region into the FREE region. - * It is assumed to be called at spltty() and with a write dma - * previously started. + * dsp_wr_dmadone() updates pointers and wakes up any process sleeping + * or waiting on a select(). + * Must be called at spltty(). */ static void dsp_wr_dmadone(snddev_info *d) { snd_dbuf *b = & (d->dbuf_out) ; - isa_dmadone(B_WRITE, b->buf + b->dp, b->dl, d->dma1); - b->fl += b->dl ; /* make dl bytes free */ + dsp_wr_dmaupdate(b); /* * XXX here it would be more efficient to record if there * actually is a sleeping process, but this should still work. */ wakeup(b); /* wakeup possible sleepers */ - if (d->wsel.si_pid && + if (b->sel.si_pid && ( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) ) - selwakeup( & d->wsel ); - b->dp = b->rp ; - b->dl0 = b->dl = 0 ; + selwakeup( & b->sel ); } /* - * The following function tracks the status of a (write) dma transfer, - * and moves the boundary between the FREE and the DMA regions. - * It works under the following assumptions: - * - the DMA engine is running; - * - the routine is called with interrupts blocked. - * BEWARE: when using AUTO DMA, dl can go negative! We assume that it - * does not wrap! + * dsp_wr_dmaupdate() tracks the status of a (write) dma transfer, + * updating pointers. It must be called at spltty() and the ISA DMA must + * have been started. + * + * NOTE: when we are using auto dma in the device, rl might become + * negative. */ void -dsp_wr_dmaupdate(snddev_info *d) +dsp_wr_dmaupdate(snd_dbuf *b) { - snd_dbuf *b = & (d->dbuf_out) ; - int tmp; + int tmp, delta; - tmp = b->dl - isa_dmastatus1(d->dma1) ; + tmp = b->bufsize - isa_dmastatus1(b->chan) ; tmp &= DMA_ALIGN_MASK; /* align... */ - if (tmp > 0) { /* we have some new data */ - b->dp += tmp; - if (b->dp >= b->bufsize) - b->dp -= b->bufsize; - b->dl -= tmp ; - b->fl += tmp ; - } + delta = tmp - b->rp; + if (delta < 0) /* wrapped */ + delta += b->bufsize ; + b->rp = tmp; + b->rl -= delta ; + b->fl += delta ; } /* - * Write interrupt routine. Can be called from other places, but - * with interrupts disabled. + * Write interrupt routine. Can be called from other places (e.g. + * to start a paused transfer), but with interrupts disabled. */ void dsp_wrintr(snddev_info *d) { snd_dbuf *b = & (d->dbuf_out) ; - int cb_reason = SND_CB_WR ; - DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n", - b->dl, b->rl, b->fl)); - if (d->flags & SND_F_WR_DMA) { /* dma was active */ + if (b->dl) { /* dma was active */ b->int_count++; - d->flags &= ~SND_F_WR_DMA; - cb_reason = SND_CB_WR | SND_CB_RESTART ; dsp_wr_dmadone(d); - } else - cb_reason = SND_CB_WR | SND_CB_START ; + } + DEB(if (b->rl < 0) + printf("dsp_wrintr: dl %d, rp:rl %d:%d, fp:fl %d:%d\n", + b->dl, b->rp, b->rl, b->fp, b->fl)); /* - * start another dma operation only if have ready data in the - * buffer, there is no pending abort, have a full-duplex device - * (dma1 != dma2) or have half duplex device and there is no - * pending op on the other side. + * start another dma operation only if have ready data in the buffer, + * there is no pending abort, have a full-duplex device + * (dbuf_out.chan != dbuf_in.chan) or have half duplex device + * and there is no * pending op on the other side. * - * Force transfer to be aligned to a boundary of 4, which is + * Force transfers to be aligned to a boundary of 4, which is * needed when doing stereo and 16-bit. We could make this * adaptive, but why bother for now... */ if ( b->rl >= DMA_ALIGN_THRESHOLD && ! (d->flags & SND_F_ABORTING) && - ( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) { - b->dl = min(b->rl, b->bufsize - b->rp ) ; /* do not wrap */ - b->dl = min(b->dl, d->play_blocksize ); /* avoid too large transfer */ - b->dl &= DMA_ALIGN_MASK ; /* realign things */ - b->rl -= b->dl ; - b->rp += b->dl ; - if (b->rp == b->bufsize) - b->rp = 0 ; - /* - * now try to avoid too small dma transfers in the near future. - * This can happen if I let rp start too close to the end of - * the buffer. If this happens, and have enough data, try to - * split the available block in two approx. equal parts. - * XXX this code can go when we use auto dma. - */ - if (b->bufsize - b->rp < MIN_CHUNK_SIZE && - b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) { - b->dl = (b->bufsize - b->dp) / 2; - b->dl &= ~3 ; /* align to a boundary of 4 */ - b->rl += (b->rp - (b->dp + b->dl) ) ; - b->rp = b->dp + b->dl ; /* no need to check for wraps */ - } - /* - * how important is the order of operations ? - */ - if (b->dl == 0) { - printf("ouch... want to start for 0 bytes!\n"); - goto ferma; + ( (d->dbuf_out.chan != d->dbuf_in.chan) || + ! (d->flags & SND_F_READING) ) ) { + int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */ + l &= DMA_ALIGN_MASK ; /* realign things */ + + if (l != b->dl) { + /* for any reason, size has changed. Stop and restart */ + DEB(printf("wrintr: bsz change from %d to %d, rp %d rl %d\n", + b->dl, l, b->rp, b->rl)); + b->dl = l; /* record previous transfer size */ + d->callback(d, SND_CB_WR | SND_CB_STOP ); + d->callback(d, SND_CB_WR | SND_CB_START ); } - b->dl0 = b->dl ; /* XXX */ - if (d->callback) - d->callback(d, cb_reason ); /* start/restart dma */ - isa_dmastart( B_WRITE , b->buf + b->dp, b->dl, d->dma1); - d->flags |= SND_F_WR_DMA; } else { -ferma: - if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART ) + /* cannot start a new dma transfer */ + DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n", + d->flags, b->rp, b->rl)); + if (b->dl > 0) { /* was active */ + b->dl = 0; d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */ + if (d->flags & SND_F_WRITING) + DEB(printf("Race! got wrint while reloading...\n")); + else + reset_dbuf(b, SND_CHAN_WR); + } /* * if switching to read, should start the read dma... */ - if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) ) + if ( d->dbuf_out.chan == d->dbuf_in.chan && (d->flags & SND_F_READING) ) dsp_rdintr(d); - DEB(printf("cannot start wr-dma flags 0x%08lx dma_dl %d rl %d\n", - d->flags, isa_dmastatus1(d->dma1), b->rl)); } } @@ -259,65 +220,55 @@ ferma: * data, we reduce the latency to something proportional to the length * of the first piece, while keeping the overhead low and being able * to feed the DMA with large blocks. + * + * assume d->flags |= SND_F_WRITING ; has been done before */ int dsp_write_body(snddev_info *d, struct uio *buf) { - int timeout = 1, n, l, bsz, ret = 0 ; + int n, l, bsz, ret = 0 ; long s; snd_dbuf *b = & (d->dbuf_out) ; - /* assume d->flags |= SND_F_WRITING ; has been done before */ /* - * bsz is the max size for the next transfer. If the dma was - * idle, we want it as large as possible. Otherwise, start with + * bsz is the max size for the next transfer. If the dma was idle + * (dl == 0), we want it as large as possible. Otherwise, start with * a small block to avoid underruns if we are close to the end of * the previous operation. */ - bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ; + bsz = b->dl ? MIN_CHUNK_SIZE : b->bufsize ; while (( n = buf->uio_resid )) { l = min (n, bsz); /* at most n bytes ... */ s = spltty(); /* no interrupts here ... */ - /* - * if i) the dma engine is running, ii) we do not have enough space - * in the FREE region, and iii) the current DMA transfer might let - * us complete the _whole_ transfer without sleeping, or we are doing - * non-blocking I/O, then try to extend the FREE region. - * Otherwise do not bother, we will need to sleep anyways, and - * make the timeout longer. - */ - - if ( d->flags & SND_F_WR_DMA && b->fl < l && - ( b->fl + b->dl >= n || d->flags & SND_F_NBIO ) ) - dsp_wr_dmaupdate(d); /* should really change timeout... */ - else - timeout = hz; + dsp_wr_dmaupdate(b); l = min( l, b->fl ); /* no more than avail. space */ - l = min( l, b->bufsize - b->fp ); /* do not wrap ... */ DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n)); /* * at this point, we assume that if l==0 the dma engine - * must (or will, in cause it is paused) be running. + * must be running. */ if (l == 0) { /* no space, must sleep */ + int timeout; if (d->flags & SND_F_NBIO) { /* unless of course we are doing non-blocking i/o */ splx(s); break; } DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl)); + if ( b->fl < n ) + timeout = hz; + else + timeout = 1 ; ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout); if (ret == EINTR) d->flags |= SND_F_ABORTING ; splx(s); - if (ret == ERESTART || ret == EINTR) + if (ret == EINTR) break ; - timeout = min(2*timeout, hz); continue; } splx(s); - timeout = 1 ; /* we got some data... */ /* * copy data to the buffer, and possibly do format @@ -325,13 +276,19 @@ dsp_write_body(snddev_info *d, struct uio *buf) * NOTE: I can use fp here since it is not modified by the * interrupt routines. */ - ret = uiomove(b->buf + b->fp, l, buf) ; - if (ret !=0 ) { /* an error occurred ... */ - printf("uiomove error %d\n", ret); - break ; + if (b->fp + l > b->bufsize) { + int l1 = b->bufsize - b->fp ; + uiomove(b->buf + b->fp, l1, buf) ; + uiomove(b->buf, l - l1, buf) ; + if (d->flags & SND_F_XLAT8) { + translate_bytes(ulaw_dsp, b->buf + b->fp, l1); + translate_bytes(ulaw_dsp, b->buf , l - l1); + } + } else { + uiomove(b->buf + b->fp, l, buf) ; + if (d->flags & SND_F_XLAT8) + translate_bytes(ulaw_dsp, b->buf + b->fp, l); } - if (d->flags & SND_F_XLAT8) - translate_bytes(ulaw_dsp, b->buf + b->fp, l); s = spltty(); /* no interrupts here ... */ b->rl += l ; /* this more ready bytes */ @@ -339,17 +296,51 @@ dsp_write_body(snddev_info *d, struct uio *buf) b->fp += l ; if (b->fp >= b->bufsize) /* handle wraps */ b->fp -= b->bufsize ; - if ( !(d->flags & SND_F_WR_DMA) ) {/* dma was idle, restart it */ - if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) == - SND_F_INIT) { - /* want to init but no pending DMA activity */ - splx(s); - d->callback(d, SND_CB_INIT); /* this is slow! */ - s = spltty(); - } + if ( b->dl == 0 ) /* dma was idle, restart it */ dsp_wrintr(d) ; - } splx(s) ; + if (buf->uio_resid == 0 && (b->fp & (b->sample_size - 1)) == 0) { + /* + * If data is correctly aligned, pad the region with + * replicas of the last sample. l0 goes from current to + * the buffer end, l1 is the portion which wraps around. + */ + int l0, l1, i; + + l1 = min(/* b->dl */ d->play_blocksize, b->fl); + l0 = min (l1, b->bufsize - b->fp); + l1 = l1 - l0 ; + + i = b->fp - b->sample_size; + if (i < 0 ) i += b->bufsize ; + if (b->sample_size == 1) { + u_char *p= (u_char *)(b->buf + i), sample = *p; + + for ( ; l0 ; l0--) + *p++ = sample ; + for (p= (u_char *)(b->buf) ; l1 ; l1--) + *p++ = sample ; + } else if (b->sample_size == 2) { + u_short *p= (u_short *)(b->buf + i), sample = *p; + + l1 /= 2 ; + l0 /= 2 ; + for ( ; l0 ; l0--) + *p++ = sample ; + for (p= (u_short *)(b->buf) ; l1 ; l1--) + *p++ = sample ; + } else { /* must be 4 ... */ + u_long *p= (u_long *)(b->buf + i), sample = *p; + + l1 /= 4 ; + l0 /= 4 ; + for ( ; l0 ; l0--) + *p++ = sample ; + for (p= (u_long *)(b->buf) ; l1 ; l1--) + *p++ = sample ; + } + + } bsz = min(b->bufsize, bsz*2); } s = spltty(); /* no interrupts here ... */ @@ -357,7 +348,8 @@ dsp_write_body(snddev_info *d, struct uio *buf) if (d->flags & SND_F_ABORTING) { d->flags &= ~SND_F_ABORTING; splx(s); - dsp_wrabort(d); + dsp_wrabort(d, 1 /* restart */); + /* XXX return EINTR ? */ } splx(s) ; return ret ; @@ -367,45 +359,29 @@ dsp_write_body(snddev_info *d, struct uio *buf) * SOUND INPUT * -The input part is similar to the output one. The only difference is in -the ordering of regions, which is the following: - - 0 rp,rl dp,dl fp,fl bufsize - |__________|____________|______>______|________| - FREE READY DMA FREE - -and the fact that input data are in the READY region. +The input part is similar to the output one, with a circular buffer +split in two regions, and boundaries advancing because of read() calls +[r] or dma operation [d]. At initialization, as for the write +routine, READY is empty, and FREE takes all the space. -At initialization, as for the write routine, DMA and READY are empty, -and FREE takes all the space: - - dp = rp = fp = 0 ; -- beginning of buffer - dl0 = dl = 0 ; -- meaning no dma activity - rl = 0 ; -- meaning no data ready - fl = bufsize ; + 0 rp,rl fp,fl bufsize + |__________>____________>________| + FREE r READY d FREE Operation is as follows: upon user read (dsp_read_body()) a DMA read -is started if not already active (marked by d->flags & SND_F_RD_DMA), +is started if not already active (marked by b->dl > 0), then as soon as data are available in the READY region they are transferred to the user buffer, thus advancing the boundary between FREE and READY. Upon interrupts, caused by a completion of a DMA transfer, the READY region is extended and possibly a new transfer is started. -When necessary, isa_dmastatus() is called to advance the boundary -between READY and DMA to the real position. +When necessary, dsp_rd_dmaupdate() is called to advance fp (and update +rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are +updated accordingly. The rules to choose the size of the new DMA area are similar to -the other case, i.e: - - 1. if not using AUTO mode, do not wrap around the end of the - buffer, and do not let fp move too close to the end of the - buffer; - - 2. do not use more than half the buffer size; this serves to - leave room for the next dma operation. - - 3. use the default blocksize, either user-specified, or - corresponding to 0.25s of data; +the other case, with a preferred constant transfer size equal to +rec_blocksize, and fallback to smaller sizes if no space is available. * */ @@ -419,14 +395,11 @@ dsp_rd_dmadone(snddev_info *d) { snd_dbuf *b = & (d->dbuf_in) ; - isa_dmadone(B_READ, b->buf + b->dp, b->dl, d->dma2); - b->rl += b->dl ; /* make dl bytes available */ + dsp_rd_dmaupdate(b); wakeup(b) ; /* wakeup possibly sleeping processes */ - if (d->rsel.si_pid && + if (b->sel.si_pid && ( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) ) - selwakeup( & d->rsel ); - b->dp = b->fp ; - b->dl0 = b->dl = 0 ; + selwakeup( & b->sel ); } /* @@ -437,20 +410,18 @@ dsp_rd_dmadone(snddev_info *d) * - the function is called with interrupts blocked. */ void -dsp_rd_dmaupdate(snddev_info *d) +dsp_rd_dmaupdate(snd_dbuf *b) { - snd_dbuf *b = & (d->dbuf_in) ; - int tmp ; + int delta, tmp ; - tmp = b->dl - isa_dmastatus1(d->dma2) ; + tmp = b->bufsize - isa_dmastatus1(b->chan) ; tmp &= DMA_ALIGN_MASK; /* align... */ - if (tmp > 0) { /* we have some data */ - b->dp += tmp; - if (b->dp >= b->bufsize) - b->dp -= b->bufsize; - b->dl -= tmp ; - b->rl += tmp ; - } + delta = tmp - b->fp; + if (delta < 0) /* wrapped */ + delta += b->bufsize ; + b->fp = tmp; + b->fl -= delta ; + b->rl += delta ; } /* @@ -460,64 +431,41 @@ void dsp_rdintr(snddev_info *d) { snd_dbuf *b = & (d->dbuf_in) ; - int cb_reason = SND_CB_RD ; - DEB(printf("dsp_rdintr: start dl = %d fp %d blocksize %d\n", - b->dl, b->fp, d->rec_blocksize)); - if (d->flags & SND_F_RD_DMA) { /* dma was active */ + if (b->dl) { /* dma was active */ b->int_count++; - d->flags &= ~SND_F_RD_DMA; - cb_reason = SND_CB_RD | SND_CB_RESTART ; dsp_rd_dmadone(d); - } else - cb_reason = SND_CB_RD | SND_CB_START ; + } + + DEB(printf("dsp_rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n", + b->dl, b->rp, b->rl, b->fp, b->fl)); /* - * Same checks as in the write case (mutatis mutandis) to decide - * whether or not to restart a dma transfer. + * Restart if have enough free space to absorb overruns; */ - if ( b->fl >= DMA_ALIGN_THRESHOLD && - ((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) && - ( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) { - b->dl = min(b->fl, b->bufsize - b->fp ) ; /* do not wrap */ - b->dl = min(b->dl, d->rec_blocksize ); - b->dl &= DMA_ALIGN_MASK ; /* realign sizes */ - b->fl -= b->dl ; - b->fp += b->dl ; - if (b->fp == b->bufsize) - b->fp = 0 ; - /* - * now try to avoid too small dma transfers in the near future. - * This can happen if I let fp start too close to the end of - * the buffer. If this happens, and have enough data, try to - * split the available block in two approx. equal parts. - */ - if (b->bufsize - b->fp < MIN_CHUNK_SIZE && - b->bufsize - b->dp > 2*MIN_CHUNK_SIZE) { - b->dl = (b->bufsize - b->dp) / 2; - b->dl &= DMA_ALIGN_MASK ; /* align to multiples of 3 */ - b->fl += (b->fp - (b->dp + b->dl) ) ; - b->fp = b->dp + b->dl ; /* no need to check that fp wraps */ - } - if (b->dl == 0) { - printf("ouch! want to read 0 bytes\n"); - goto ferma; + if ( b->fl > 0x200 && + (d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0 && + ( d->dbuf_out.chan != d->dbuf_in.chan || + (d->flags & SND_F_WRITING) == 0 ) ) { + int l = min(b->fl - 0x100, d->rec_blocksize); + l &= DMA_ALIGN_MASK ; /* realign sizes */ + if (l != b->dl) { + /* for any reason, size has changed. Stop and restart */ + b->dl = l ; + d->callback(d, SND_CB_RD | SND_CB_STOP ); + d->callback(d, SND_CB_RD | SND_CB_START ); } - b->dl0 = b->dl ; /* XXX */ - if (d->callback) - d->callback(d, cb_reason); /* restart_dma(); */ - isa_dmastart( B_READ , b->buf + b->dp, b->dl, d->dma2); - d->flags |= SND_F_RD_DMA; } else { -ferma: - if (d->callback && (cb_reason & SND_CB_REASON_MASK) == SND_CB_RESTART) + if (b->dl > 0) { /* was active */ + b->dl = 0; d->callback(d, SND_CB_RD | SND_CB_STOP); + } /* * if switching to write, start write dma engine */ - if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) ) + if ( d->dbuf_out.chan == d->dbuf_in.chan && (d->flags & SND_F_WRITING) ) dsp_wrintr(d) ; - DEB(printf("cannot start rd-dma flags 0x%08lx dma_dl %d fl %d\n", - d->flags, isa_dmastatus1(d->dma2), b->fl)); + DEB(printf("cannot start rd-dma rl %d fl %d\n", + b->rl, b->fl)); } } @@ -549,8 +497,6 @@ dsp_read_body(snddev_info *d, struct uio *buf) long s; snd_dbuf *b = & (d->dbuf_in) ; - int timeout = 1 ; /* counter of how many ticks we sleep */ - /* * "limit" serves to return after at most one blocksize of data * (unless more are already available). Otherwise, things like @@ -569,116 +515,69 @@ dsp_read_body(snddev_info *d, struct uio *buf) bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */ while ( (n = buf->uio_resid) > limit ) { DEB(printf("dsp_read_body: start waiting for %d bytes\n", n)); - /* - * here compute how many bytes to transfer, enforcing various - * limitations: - */ - l = min (n, bsz); /* 1': at most bsz bytes ... */ + l = min (n, bsz); s = spltty(); /* no interrupts here ! */ - /* - * if i) the dma engine is running, ii) we do not have enough - * ready bytes, and iii) the current DMA transfer could give - * us what we need, or we are doing non-blocking IO, then try - * to extend the READY region. - * Otherwise do not bother, we will need to sleep anyways, - * and make the timeout longer. - */ - - if ( d->flags & SND_F_RD_DMA && b->rl < l && - ( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) ) - dsp_rd_dmaupdate(d); - else - timeout = hz ; - l = min( l, b->rl ); /* 2': no more than avail. data */ - l = min( l, b->bufsize - b->rp ); /* 3': do not wrap buffer. */ - /* the above limitation can be removed if we use auto DMA - * on the ISA controller. But then we have to make a check - * when doing the uiomove... - */ - if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */ - /* - * There are two reasons the dma can be idle: either this - * is the first read, or the buffer has become full. In - * the latter case, the dma cannot be restarted until - * we have removed some data, which will be true at the - * second round. - * - * Call dsp_rdintr to start the dma. It would be nice to - * have a "need" field in the snd_dbuf, so that we do - * not start a long operation unnecessarily. However, - * the restart code will ask for at most d->blocksize - * bytes, and since we are sure we are the only reader, - * and the routine is not interrupted, we patch and - * restore d->blocksize around the call. A bit dirty, - * but it works, and saves some overhead :) - */ - - int old_bs = d->rec_blocksize; - - if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) == - SND_F_INIT) { - /* want to init but no pending DMA activity */ - splx(s); - d->callback(d, SND_CB_INIT); /* this is slow! */ - s = spltty(); - } - if (l < MIN_CHUNK_SIZE) - d->rec_blocksize = MIN_CHUNK_SIZE ; - else if (l < d->rec_blocksize) - d->rec_blocksize = l ; - dsp_rdintr(d); - d->rec_blocksize = old_bs ; - } - + dsp_rd_dmaupdate(b); + l = min( l, b->rl ); /* no more than avail. data */ if (l == 0) { + int timeout; /* - * If, after all these efforts, we still have no data ready, - * then we must sleep (unless of course we have doing - * non-blocking i/o. But use exponential delays, starting - * at 1 tick and doubling each time. + * If there is no data ready, then we must sleep (unless + * of course we have doing non-blocking i/o). But also + * consider restarting the DMA engine. */ + if ( b->dl == 0 ) { /* dma was idle, start it */ + if ( d->flags & SND_F_INIT && d->dbuf_out.dl == 0 ) { + /* want to init and there is no pending DMA activity */ + splx(s); + d->callback(d, SND_CB_INIT); /* this is slow! */ + s = spltty(); + } + dsp_rdintr(d); + } if (d->flags & SND_F_NBIO) { splx(s); break; } - DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n", - timeout, buf->uio_resid)); + if (n-limit > b->dl) + timeout = hz; /* we need to wait for an int. */ + else + timeout = 1; /* maybe data will be ready earlier */ ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ; if (ret == EINTR) d->flags |= SND_F_ABORTING ; - splx(s); /* necessary before the goto again... */ - if (ret == ERESTART || ret == EINTR) + splx(s); + if (ret == EINTR) break ; - DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl)); - timeout = min(timeout*2, hz); continue; } splx(s); - timeout = 1 ; /* we got some data, so reset exp. wait */ - /* - * if we are using /dev/audio and the device does not - * support it natively, we should do a format conversion. - * (in this case from uLAW to natural format). - * This can be messy in that it can require an intermediate - * buffer, and also screw up the byte count. - */ /* + * Do any necessary format conversion, and copy to user space. * NOTE: I _can_ use rp here because it is not modified by the * interrupt routines. */ - if (d->flags & SND_F_XLAT8) - translate_bytes(dsp_ulaw, b->buf + b->rp, l); - ret = uiomove(b->buf + b->rp, l, buf) ; - if (ret !=0 ) /* an error occurred ... */ - break ; + if (b->rp + l > b->bufsize) { /* handle wraparounds */ + int l1 = b->bufsize - b->rp ; + if (d->flags & SND_F_XLAT8) { + translate_bytes(dsp_ulaw, b->buf + b->rp, l1); + translate_bytes(dsp_ulaw, b->buf , l - l1); + } + uiomove(b->buf + b->rp, l1, buf) ; + uiomove(b->buf, l - l1, buf) ; + } else { + if (d->flags & SND_F_XLAT8) + translate_bytes(dsp_ulaw, b->buf + b->rp, l); + uiomove(b->buf + b->rp, l, buf) ; + } s = spltty(); /* no interrupts here ... */ b->fl += l ; /* this more free bytes */ b->rl -= l ; /* this less ready bytes */ b->rp += l ; /* advance ready pointer */ - if (b->rp == b->bufsize) /* handle wraps */ - b->rp = 0 ; + if (b->rp >= b->bufsize) /* handle wraps */ + b->rp -= b->bufsize ; splx(s) ; bsz = min(b->bufsize, bsz*2); } @@ -687,7 +586,8 @@ dsp_read_body(snddev_info *d, struct uio *buf) if (d->flags & SND_F_ABORTING) { d->flags |= ~SND_F_ABORTING; splx(s); - dsp_rdabort(d); + dsp_rdabort(d, 1 /* restart */); + /* XXX return EINTR ? */ } splx(s) ; return ret ; @@ -697,30 +597,40 @@ dsp_read_body(snddev_info *d, struct uio *buf) /* * short routine to initialize a dma buffer descriptor (usually * located in the XXX_desc structure). The first parameter is - * the buffer size, the second one specifies the dma channel in use - * At the moment we do not support more than 64K, since for some - * cards I need to switch between dma1 and dma2. The channel is - * unused. + * the buffer size, the second one specifies that a 16-bit dma channel + * is used (hence the buffer must be properly aligned). */ void -alloc_dbuf(snd_dbuf *b, int size, int chan) +alloc_dbuf(snd_dbuf *b, int size) { if (size > 0x10000) panic("max supported size is 64k"); b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 1ul, 0x10000ul); - /* should check that it does not fail... */ - b->dp = b->rp = b->fp = 0 ; - b->dl0 = b->dl = b->rl = 0 ; + /* should check that malloc does not fail... */ + b->rp = b->fp = 0 ; + b->dl = b->rl = 0 ; b->bufsize = b->fl = size ; } +/* + * this resets a buffer and starts the isa dma on that channel. + * Must be called when the dma on the card is disabled (e.g. after init). + */ void -reset_dbuf(snd_dbuf *b) +reset_dbuf(snd_dbuf *b, int chan) { - b->dp = b->rp = b->fp = 0 ; - b->dl0 = b->dl = b->rl = 0 ; + DEB(printf("reset dbuf for chan %d\n", b->chan)); + b->rp = b->fp = 0 ; + b->dl = b->rl = 0 ; b->fl = b->bufsize ; + if (chan == SND_CHAN_NONE) + return ; + if (chan == SND_CHAN_WR) + chan = B_WRITE | B_RAW ; + else + chan = B_READ | B_RAW ; + isa_dmastart( chan , b->buf, b->bufsize, b->chan); } /* @@ -741,9 +651,9 @@ snd_sync(snddev_info *d, int chan, int threshold) for (;;) { s=spltty(); if ( chan==1 ) - dsp_wr_dmaupdate(d); + dsp_wr_dmaupdate(b); else - dsp_rd_dmaupdate(d); + dsp_rd_dmaupdate(b); if ( (chan == 1 && b->fl <= threshold) || (chan == 2 && b->rl <= threshold) ) { ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1); @@ -762,61 +672,53 @@ snd_sync(snddev_info *d, int chan, int threshold) /* * dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions * which abort a pending DMA transfer and flush the buffers. + * They return the number of bytes that has not been transferred. + * The second parameter is used to restart the engine if needed. */ int -dsp_wrabort(snddev_info *d) +dsp_wrabort(snddev_info *d, int restart) { long s; int missing = 0; snd_dbuf *b = & (d->dbuf_out) ; s = spltty(); - if ( d->flags & SND_F_WR_DMA ) { - d->flags &= ~(SND_F_WR_DMA | SND_F_WRITING); + if ( b->dl ) { + b->dl = 0 ; + d->flags &= ~ SND_F_WRITING ; if (d->callback) d->callback(d, SND_CB_WR | SND_CB_ABORT); - missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */ - - b->rl += missing ; /* which are part of the ready area */ - b->rp -= missing ; - if (b->rp < 0) - b->rp += b->bufsize; - DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n", - b->dl - missing, b->dl)); - b->dl -= missing; + isa_dmastop(b->chan) ; dsp_wr_dmadone(d); - missing = b->rl; + + DEB(printf("dsp_wrabort: stopped, %d bytes left\n", b->rl)); } - reset_dbuf(b); + missing = b->rl; + isa_dmadone(B_WRITE, b->buf, b->bufsize, b->chan); /*free chan */ + reset_dbuf(b, restart ? SND_CHAN_WR : SND_CHAN_NONE); splx(s); return missing; } int -dsp_rdabort(snddev_info *d) +dsp_rdabort(snddev_info *d, int restart) { long s; int missing = 0; snd_dbuf *b = & (d->dbuf_in) ; s = spltty(); - if ( d->flags & SND_F_RD_DMA ) { - d->flags &= ~(SND_F_RD_DMA | SND_F_READING); + if ( b->dl ) { + b->dl = 0 ; + d->flags &= ~ SND_F_READING ; if (d->callback) d->callback(d, SND_CB_RD | SND_CB_ABORT); - missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */ - - b->fl += missing ; /* which are part of the free area */ - b->fp -= missing ; - if (b->fp < 0) - b->fp += b->bufsize; - DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n", - b->dl - missing, b->dl)); - b->dl -= missing; + isa_dmastop(b->chan) ; dsp_rd_dmadone(d); - missing = b->rl ; } - reset_dbuf(b); + missing = b->rl ; + isa_dmadone(B_READ, b->buf, b->bufsize, b->chan); + reset_dbuf(b, restart ? SND_CHAN_RD : SND_CHAN_NONE); splx(s); return missing; } @@ -831,31 +733,34 @@ dsp_rdabort(snddev_info *d) int snd_flush(snddev_info *d) { - int ret; - int count=10; + int ret, count=10; + u_long s; + snd_dbuf *b = &(d->dbuf_out) ; -DEB(printf("snd_flush d->flags 0x%08lx\n", d->flags)); - dsp_rdabort(d); - if ( d->flags & SND_F_WR_DMA ) { - /* close write */ - while ( d->flags & SND_F_WR_DMA ) { - /* - * still pending output data. - */ - ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz); - if (ret == ERESTART || ret == EINTR) { - printf("tsleep returns %d\n", ret); - return -1 ; - } - if ( ret && --count == 0) { - printf("timeout flushing dma1, cnt 0x%x flags 0x%08lx\n", - isa_dmastatus1(d->dma1), d->flags); - return -1 ; - } + DEB(printf("snd_flush d->flags 0x%08x\n", d->flags)); + dsp_rdabort(d, 0 /* no restart */); + /* close write */ + while ( b->dl ) { + /* + * still pending output data. + */ + ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dmafl1", hz); + dsp_wr_dmaupdate(b); + DEB( printf("snd_sync: now rl : fl %d : %d\n", b->rl, b->fl ) ); + if (ret == EINTR) { + printf("tsleep returns %d\n", ret); + return -1 ; + } + if ( ret && --count == 0) { + printf("timeout flushing dbuf_out.chan, cnt 0x%x flags 0x%08lx\n", + b->rl, d->flags); + break; } - d->flags &= ~SND_F_CLOSING ; } - reset_dbuf(& (d->dbuf_out) ); + s = spltty(); /* should not be necessary... */ + d->flags &= ~SND_F_CLOSING ; + dsp_wrabort(d, 0 /* no restart */); + splx(s); return 0 ; } diff --git a/sys/i386/isa/snd/dmabuf_auto.c b/sys/i386/isa/snd/dmabuf_auto.c deleted file mode 100644 index 3972be8..0000000 --- a/sys/i386/isa/snd/dmabuf_auto.c +++ /dev/null @@ -1,802 +0,0 @@ -/* - * snd/dmabuf.c - * - * New DMA routines -- Luigi Rizzo, 05 sep 97 - * This file implements the new DMA routines for the sound driver. - * AUTO DMA MODE (ISA DMA SIDE). - * - * Copyright by Luigi Rizzo - 1997 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include <i386/isa/snd/sound.h> -#include <i386/isa/snd/ulaw.h> - -#define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ -#define DMA_ALIGN_BITS 2 /* i.e. 4 bytes */ -#define DMA_ALIGN_THRESHOLD (1<< DMA_ALIGN_BITS) -#define DMA_ALIGN_MASK (~ (DMA_ALIGN_THRESHOLD - 1)) - -static void dsp_wr_dmadone(snddev_info *d); -static void dsp_rd_dmadone(snddev_info *d); - -/* - * - -SOUND OUTPUT - -We use a circular buffer to store samples directed to the DAC. -The buffer is split into two variable-size regions, each identified -by an offset in the buffer (rp,fp) and a lenght (rl,fl). - - 0 rp,rl fp,fl bufsize - |__________|____________|________| - FREE d> READY w> FREE - - READY region: contains data written from the process and ready - to be sent to the DAC; - - FREE region: is the empty region of the buffer, where a process - can write new data. - -The first block of the READY region is used for DMA transfers. The -transfer is started at rp and with chunks of length dl0 computed -according to some rules. During DMA operations, rp advances (d>) -and rl,fl are updated by dsp_wr_dmaupdate(). When a new block is -written, fp advances (w>) and rl,fl are updated accordingly. - -Both the "READY" and "FREE" regions can wrap around the end of the -buffer. - -At initialization, READY is empty, FREE takes all the -available space, and dma is idle - -dl0 contains the new blocksize to be used in dma transfers (passed to -the callback). dl contains the previous blocksize (needed when the -codec uses AUTO DMA). - - rp = fp = 0 ; -- beginning of buffer - dl0 = dl = 0 ; - rl = 0 ; -- meaning no data ready - fl = bufsize ; - -The code works as follows: the user write routine dsp_write_body() -fills up the READY region with new data (reclaiming space from the -FREE region) and starts the write DMA engine if inactive (activity -is indicated by d->flags & SND_F_WR_DMA ). The size of each -DMA transfer is chosen according to a series of rules which will be -discussed later. When a DMA transfer is complete, an interrupt causes -dsp_wrintr() to be called which empties the DMA region, extends -the FREE region and possibly starts the next transfer. - -In some cases, the code tries to track the current status of DMA -operations by calling isa_dmastatus() and advancing the boundary -between FREE and DMA regions accordingly. - -The size of a DMA transfer is selected according to the following -rules: - - 1. when not using AUTO DMA, do not wrap around the end of the - buffer, and do not let fp move too close to the end of the - buffer; - - 2. do not use more than half of the buffer size. - This serves to allow room for a next write operation concurrent - with the dma transfer, and to reduce the time which is necessary - to wait before a pending dma will end (optionally, the max - size could be further limited to a fixed amount of play time, - depending on number of channels, sample size and sample speed); - - 3. use the current blocksize (either specified by the user, or - corresponding roughly to 0.25s of data); - - * - */ - - -/* - * dsp_wr_dmadone moves the write DMA region into the FREE region. - * It is assumed to be called at spltty() and with a write dma - * previously started. - */ -static void -dsp_wr_dmadone(snddev_info *d) -{ - snd_dbuf *b = & (d->dbuf_out) ; - - dsp_wr_dmaupdate(d); - /* - * XXX here it would be more efficient to record if there - * actually is a sleeping process, but this should still work. - */ - wakeup(b); /* wakeup possible sleepers */ - if (d->wsel.si_pid && - ( !(d->flags & SND_F_HAS_SIZE) || b->fl >= d->play_blocksize ) ) - selwakeup( & d->wsel ); -} - -/* - * The following function tracks the status of a (write) dma transfer, - * and moves the boundary between the FREE and the DMA regions. - * It works under the following assumptions: - * - the DMA engine is active; - * - the routine is called with interrupts blocked. - */ -void -dsp_wr_dmaupdate(snddev_info *d) -{ - snd_dbuf *b = & (d->dbuf_out) ; - int tmp, delta; - - tmp = b->bufsize - isa_dmastatus1(d->dma1) ; - tmp &= DMA_ALIGN_MASK; /* align... */ - delta = tmp - b->rp; - if (delta < 0) /* wrapped */ - delta += b->bufsize ; - b->rp = tmp; - b->rl -= delta ; - b->fl += delta ; -} - -/* - * Write interrupt routine. Can be called from other places, but - * with interrupts disabled. - */ -void -dsp_wrintr(snddev_info *d) -{ - snd_dbuf *b = & (d->dbuf_out) ; - - DEB(printf("dsp_wrintr: start on dl %d, rl %d, fl %d\n", - b->dl, b->rl, b->fl)); - if (d->flags & SND_F_WR_DMA) { /* dma was active */ - b->int_count++; - d->flags &= ~SND_F_WR_DMA; - dsp_wr_dmadone(d); - } else - b->dl = 0 ; - - /* - * start another dma operation only if have ready data in the - * buffer, there is no pending abort, have a full-duplex device - * (dma1 != dma2) or have half duplex device and there is no - * pending op on the other side. - * - * Force transfer to be aligned to a boundary of 4, which is - * needed when doing stereo and 16-bit. We could make this - * adaptive, but why bother for now... - */ - if ( b->rl >= DMA_ALIGN_THRESHOLD && - ! (d->flags & SND_F_ABORTING) && - ( (d->dma1 != d->dma2) || ! (d->flags & SND_F_READING) ) ) { - int l = min(b->rl, d->play_blocksize ); /* avoid too large transfer */ - l &= DMA_ALIGN_MASK ; /* realign things */ - - b->dl0 = l ; - if (d->callback) - d->callback(d, SND_CB_WR | SND_CB_START ); - d->flags |= SND_F_WR_DMA; - b->dl = b->dl0; - } else { - if (b->dl > 0) { /* was active */ - b->dl0 = 0; - d->callback(d, SND_CB_WR | SND_CB_STOP ); /* stop dma */ - } - /* - * if switching to read, should start the read dma... - */ - if ( d->dma1 == d->dma2 && (d->flags & SND_F_READING) ) - dsp_rdintr(d); - DEB(printf("cannot start wr-dma flags 0x%08x dma_dl %d rl %d\n", - d->flags, isa_dmastatus1(d->dma1), b->rl)); - } -} - -/* - * user write routine - * - * advance the boundary between READY and FREE, fill the space with - * uiomove(), and possibly start DMA. Do the above until the transfer - * is complete. - * - * To minimize latency in case a pending DMA transfer is about to end, - * we do the transfer in pieces of increasing sizes, extending the - * READY area at every checkpoint. In the (necessary) assumption that - * memory bandwidth is larger than the rate at which the dma consumes - * data, we reduce the latency to something proportional to the length - * of the first piece, while keeping the overhead low and being able - * to feed the DMA with large blocks. - */ - -int -dsp_write_body(snddev_info *d, struct uio *buf) -{ - int timeout = 1, n, l, bsz, ret = 0 ; - long s; - snd_dbuf *b = & (d->dbuf_out) ; - - /* assume d->flags |= SND_F_WRITING ; has been done before */ - /* - * bsz is the max size for the next transfer. If the dma was - * idle, we want it as large as possible. Otherwise, start with - * a small block to avoid underruns if we are close to the end of - * the previous operation. - */ - bsz = (d->flags & SND_F_WR_DMA) ? MIN_CHUNK_SIZE : b->bufsize ; - while ( n = buf->uio_resid ) { - l = min (n, bsz); /* at most n bytes ... */ - s = spltty(); /* no interrupts here ... */ - /* - * if i) the dma engine is running, ii) we do not have enough space - * in the FREE region, and iii) the current DMA transfer might let - * us complete the _whole_ transfer without sleeping, or we are doing - * non-blocking I/O, then try to extend the FREE region. - * Otherwise do not bother, we will need to sleep anyways, and - * make the timeout longer. - * Note that the check for iii) is only approximate since we - * might have fewer than dl0 residual bytes. - */ - - if ( d->flags & SND_F_WR_DMA && b->fl < l && - ( b->fl + b->dl0 >= n || d->flags & SND_F_NBIO ) ) - dsp_wr_dmaupdate(d); /* should really change timeout... */ - else - timeout = hz; - l = min( l, b->fl ); /* no more than avail. space */ - DEB(printf("dsp_write_body: prepare %d bytes out of %d\n", l,n)); - /* - * at this point, we assume that if l==0 the dma engine - * must (or will, in cause it is paused) be running. - */ - if (l == 0) { /* no space, must sleep */ - if (d->flags & SND_F_NBIO) { - /* unless of course we are doing non-blocking i/o */ - splx(s); - break; - } - DEB(printf("dsp_write_body: l=0, (fl %d) sleeping\n", b->fl)); - ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout); - if (ret == EINTR) - d->flags |= SND_F_ABORTING ; - splx(s); - if (ret == ERESTART || ret == EINTR) - break ; - timeout = min(2*timeout, hz); - continue; - } - splx(s); - timeout = 1 ; /* we got some data... */ - - /* - * copy data to the buffer, and possibly do format - * conversions (here, from ULAW to U8). - * NOTE: I can use fp here since it is not modified by the - * interrupt routines. - */ - if (b->fp + l > b->bufsize) { - int l1 = b->bufsize - b->fp ; - uiomove(b->buf + b->fp, l1, buf) ; - uiomove(b->buf, l - l1, buf) ; - if (d->flags & SND_F_XLAT8) { - translate_bytes(ulaw_dsp, b->buf + b->fp, l1); - translate_bytes(ulaw_dsp, b->buf , l - l1); - } - } else { - uiomove(b->buf + b->fp, l, buf) ; - if (d->flags & SND_F_XLAT8) - translate_bytes(ulaw_dsp, b->buf + b->fp, l); - } - - s = spltty(); /* no interrupts here ... */ - b->rl += l ; /* this more ready bytes */ - b->fl -= l ; /* this less free bytes */ - b->fp += l ; - if (b->fp >= b->bufsize) /* handle wraps */ - b->fp -= b->bufsize ; - if ( !(d->flags & SND_F_WR_DMA) ) /* dma was idle, restart it */ - dsp_wrintr(d) ; - splx(s) ; - bsz = min(b->bufsize, bsz*2); - } - s = spltty(); /* no interrupts here ... */ - d->flags &= ~SND_F_WRITING ; - if (d->flags & SND_F_ABORTING) { - d->flags &= ~SND_F_ABORTING; - splx(s); - dsp_wrabort(d); - } - splx(s) ; - return ret ; -} - -/* - * SOUND INPUT - * - -The input part is similar to the output one. The only difference is in -the ordering of regions, which is the following: - - 0 rp,rl fp,fl bufsize - |__________|____________|________| - FREE r> READY d> FREE - -and the fact that input data are in the READY region. - -At initialization, as for the write routine, READY is empty, -and FREE takes all the space: - - rp = fp = 0 ; -- beginning of buffer - dl0 = dl = 0 ; - rl = 0 ; -- meaning no data ready - fl = bufsize ; - -Operation is as follows: upon user read (dsp_read_body()) a DMA read -is started if not already active (marked by d->flags & SND_F_RD_DMA), -then as soon as data are available in the READY region they are -transferred to the user buffer, thus advancing the boundary between FREE -and READY. Upon interrupts, caused by a completion of a DMA transfer, -the READY region is extended and possibly a new transfer is started. - -When necessary, dsp_rd_dmaupdate() is called to advance fp (and update -rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are -updated accordingly. - - -The rules to choose the size of the new DMA area are similar to -the other case, i.e: - - 1. if not using AUTO mode, do not wrap around the end of the - buffer, and do not let fp move too close to the end of the - buffer; - - 2. do not use more than half the buffer size; this serves to - leave room for the next dma operation. - - 3. use the default blocksize, either user-specified, or - corresponding to 0.25s of data; - - * - */ - -/* - * dsp_rd_dmadone moves bytes in the input buffer from DMA region to - * READY region. We assume it is called at spltty() and with dl>0 - */ -static void -dsp_rd_dmadone(snddev_info *d) -{ - snd_dbuf *b = & (d->dbuf_in) ; - - dsp_rd_dmaupdate(d); - b->rl += b->dl ; /* make dl bytes available */ - wakeup(b) ; /* wakeup possibly sleeping processes */ - if (d->rsel.si_pid && - ( !(d->flags & SND_F_HAS_SIZE) || b->rl >= d->rec_blocksize ) ) - selwakeup( & d->rsel ); - b->dp = b->fp ; - b->dl0 = b->dl = 0 ; -} - -/* - * The following function tracks the status of a (read) dma transfer, - * and moves the boundary between the READY and the DMA regions. - * It works under the following assumptions: - * - the DMA engine is running; - * - the function is called with interrupts blocked. - */ -void -dsp_rd_dmaupdate(snddev_info *d) -{ - snd_dbuf *b = & (d->dbuf_in) ; - int tmp ; - - tmp = b->bufsize - isa_dmastatus1(d->dma2) ; - tmp &= DMA_ALIGN_MASK; /* align... */ - delta = tmp - b->fp; - if (delta < 0) /* wrapped */ - delta += b->bufsize ; - b->fp = tmp; - b->fl -= delta ; - b->rl += delta ; -} - -/* - * read interrupt routine. Must be called with interrupts blocked. - */ -void -dsp_rdintr(snddev_info *d) -{ - snd_dbuf *b = & (d->dbuf_in) ; - - if (d->flags & SND_F_RD_DMA) { /* dma was active */ - b->int_count++; - d->flags &= ~SND_F_RD_DMA; - dsp_rd_dmadone(d); - } else - b->dl = 0 ; - /* - * Same checks as in the write case (mutatis mutandis) to decide - * whether or not to restart a dma transfer. - */ - if ( b->fl >= DMA_ALIGN_THRESHOLD && - ((d->flags & (SND_F_ABORTING|SND_F_CLOSING)) == 0) && - ( (d->dma1 != d->dma2) || ( (d->flags & SND_F_WRITING) == 0) ) ) { - int l = min(b->fl, d->rec_blocksize); - l &= DMA_ALIGN_MASK ; /* realign sizes */ - b->dl0 = l ; - if (d->callback) - d->callback(d, SND_CB_RD | SND_CB_START ); - d->flags |= SND_F_RD_DMA; - b->dl = b->dl0; - } else { - if (b->dl > 0) { - b->dl0 = 0 ; /* was active */ - d->callback(d, SND_CB_RD | SND_CB_STOP); - } - /* - * if switching to write, start write dma engine - */ - if ( d->dma1 == d->dma2 && (d->flags & SND_F_WRITING) ) - dsp_wrintr(d) ; - DEB(printf("cannot start rd-dma flags 0x%08x dma_dl %d fl %d\n", - d->flags, isa_dmastatus1(d->dma2), b->fl)); - } -} - -/* - * body of user-read routine - * - * Start DMA if not active; wait for READY not empty. - * Transfer data from READY region using uiomove(), advance boundary - * between FREE and READY. Repeat until transfer is complete. - * - * To avoid excessive latency in freeing up space for the DMA - * engine, transfers are done in blocks of increasing size, so that - * the latency is proportional to the size of the smallest block, but - * we have a low overhead and are able to feed the dma engine with - * large blocks. - * - * When we enter this routine, we assume that d->flags |= SND_F_READING - * was done before. - * - * NOTE: in the current version, read will not return more than - * blocksize bytes at once (unless more are already available), to - * avoid that requests using very large buffers block for too long. - */ - -int -dsp_read_body(snddev_info *d, struct uio *buf) -{ - int limit, l, n, bsz, ret = 0 ; - long s; - snd_dbuf *b = & (d->dbuf_in) ; - - int timeout = 1 ; /* counter of how many ticks we sleep */ - - /* - * "limit" serves to return after at most one blocksize of data - * (unless more are already available). Otherwise, things like - * cat /dev/audio would use a 64K buffer and would start returning - * data after a _very_ long time... - * Note -- some applications depend on reads not returning short - * blocks. But I believe these apps are broken, since interrupted - * system calls might return short reads anyways, and the - * application should better check that. - */ - - if (buf->uio_resid > d->rec_blocksize) - limit = buf->uio_resid - d->rec_blocksize; - else - limit = 0; - bsz = MIN_CHUNK_SIZE ; /* the current transfer (doubles at each step) */ - while ( (n = buf->uio_resid) > limit ) { - DEB(printf("dsp_read_body: start waiting for %d bytes\n", n)); - /* - * here compute how many bytes to transfer, enforcing various - * limitations: - */ - l = min (n, bsz); /* 1': at most bsz bytes ... */ - s = spltty(); /* no interrupts here ! */ - /* - * if i) the dma engine is running, ii) we do not have enough - * ready bytes, and iii) the current DMA transfer could give - * us what we need, or we are doing non-blocking IO, then try - * to extend the READY region. - * Otherwise do not bother, we will need to sleep anyways, - * and make the timeout longer. - */ - - if ( d->flags & SND_F_RD_DMA && b->rl < l && - ( d->flags & SND_F_NBIO || b->rl + b->dl >= n - limit ) ) - dsp_rd_dmaupdate(d); - else - timeout = hz ; - l = min( l, b->rl ); /* 2': no more than avail. data */ - if ( !(d->flags & SND_F_RD_DMA) ) { /* dma was idle, start it */ - /* - * There are two reasons the dma can be idle: either this - * is the first read, or the buffer has become full. In - * the latter case, the dma cannot be restarted until - * we have removed some data, which will be true at the - * second round. - * - * Call dsp_rdintr to start the dma. It would be nice to - * have a "need" field in the snd_dbuf, so that we do - * not start a long operation unnecessarily. However, - * the restart code will ask for at most d->blocksize - * bytes, and since we are sure we are the only reader, - * and the routine is not interrupted, we patch and - * restore d->blocksize around the call. A bit dirty, - * but it works, and saves some overhead :) - */ - - int old_bs = d->rec_blocksize; - if ( (d->flags & (SND_F_INIT|SND_F_WR_DMA|SND_F_RD_DMA)) == - SND_F_INIT) { - /* want to init but no pending DMA activity */ - splx(s); - d->callback(d, SND_CB_INIT); /* this is slow! */ - s = spltty(); - } - if (l < MIN_CHUNK_SIZE) - d->rec_blocksize = MIN_CHUNK_SIZE ; - else if (l < d->rec_blocksize) - d->rec_blocksize = l ; - d->dl0 = d->rec_blocksize ; - dsp_rdintr(d); - d->rec_blocksize = old_bs ; - } - - if (l == 0) { - /* - * If, after all these efforts, we still have no data ready, - * then we must sleep (unless of course we have doing - * non-blocking i/o. But use exponential delays, starting - * at 1 tick and doubling each time. - */ - if (d->flags & SND_F_NBIO) { - splx(s); - break; - } - DEB(printf("dsp_read_body: sleeping %d waiting for %d bytes\n", - timeout, buf->uio_resid)); - ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ; - if (ret == EINTR) - d->flags |= SND_F_ABORTING ; - splx(s); /* necessary before the goto again... */ - if (ret == ERESTART || ret == EINTR) - break ; - DEB(printf("woke up, ret %d, rl %d\n", ret, b->rl)); - timeout = min(timeout*2, hz); - continue; - } - splx(s); - - timeout = 1 ; /* we got some data, so reset exp. wait */ - /* - * if we are using /dev/audio and the device does not - * support it natively, we should do a format conversion. - * (in this case from uLAW to natural format). - * This can be messy in that it can require an intermediate - * buffer, and also screw up the byte count. - */ - /* - * NOTE: I _can_ use rp here because it is not modified by the - * interrupt routines. - */ - if (d->flags & SND_F_XLAT8) - translate_bytes(dsp_ulaw, b->buf + b->rp, l); - ret = uiomove(b->buf + b->rp, l, buf) ; - if (ret !=0 ) /* an error occurred ... */ - break ; - - s = spltty(); /* no interrupts here ... */ - b->fl += l ; /* this more free bytes */ - b->rl -= l ; /* this less ready bytes */ - b->rp += l ; /* advance ready pointer */ - if (b->rp == b->bufsize) /* handle wraps */ - b->rp = 0 ; - splx(s) ; - bsz = min(b->bufsize, bsz*2); - } - s = spltty(); /* no interrupts here ... */ - d->flags &= ~SND_F_READING ; - if (d->flags & SND_F_ABORTING) { - d->flags |= ~SND_F_ABORTING; - splx(s); - dsp_rdabort(d); - } - splx(s) ; - return ret ; -} - - -/* - * short routine to initialize a dma buffer descriptor (usually - * located in the XXX_desc structure). The first parameter is - * the buffer size, the second one specifies that a 16-bit dma channel - * is used (hence the buffer must be properly aligned). - */ -void -alloc_dbuf(snd_dbuf *b, int size, int chan) -{ - if (size > 0x10000) - panic("max supported size is 64k"); - b->buf = contigmalloc(size, M_DEVBUF, M_NOWAIT, - 0ul, 0xfffffful, 1ul, 0x10000ul); - /* should check that it does not fail... */ - b->dp = b->rp = b->fp = 0 ; - b->dl0 = b->dl = b->rl = 0 ; - b->bufsize = b->fl = size ; -} - -void -reset_dbuf(snd_dbuf *b) -{ - b->dp = b->rp = b->fp = 0 ; - b->dl0 = b->dl = b->rl = 0 ; - b->fl = b->bufsize ; -} - -/* - * snd_sync waits until the space in the given channel goes above - * a threshold. chan = 1 : play, 2: capture. The threshold is - * checked against fl or rl respectively. - * Assume that the condition can become true, do not check here... - */ -int -snd_sync(snddev_info *d, int chan, int threshold) -{ - u_long s; - int ret; - snd_dbuf *b; - - b = (chan == 1) ? &(d->dbuf_out ) : &(d->dbuf_in ) ; - - for (;;) { - s=spltty(); - if ( chan==1 ) - dsp_wr_dmaupdate(d); - else - dsp_rd_dmaupdate(d); - if ( (chan == 1 && b->fl <= threshold) || - (chan == 2 && b->rl <= threshold) ) { - ret = tsleep((caddr_t)b, PRIBIO|PCATCH, "sndsyn", 1); - splx(s); - if (ret == ERESTART || ret == EINTR) { - printf("tsleep returns %d\n", ret); - return -1 ; - } - } else - break; - } - splx(s); - return 0 ; -} - -/* - * dsp_wrabort(d) and dsp_rdabort(d) are non-blocking functions - * which abort a pending DMA transfer and flush the buffers. - */ -int -dsp_wrabort(snddev_info *d) -{ - long s; - int missing = 0; - snd_dbuf *b = & (d->dbuf_out) ; - - s = spltty(); - if ( d->flags & SND_F_WR_DMA ) { - d->flags &= ~ ( SND_F_WR_DMA | SND_F_WRITING ) ; - if (d->callback) - d->callback(d, SND_CB_WR | SND_CB_ABORT); - missing = isa_dmastop(d->dma1) ; /* this many missing bytes... */ - - b->rl += missing ; /* which are part of the ready area */ - b->rp -= missing ; - if (b->rp < 0) - b->rp += b->bufsize; - DEB(printf("dsp_wrabort: stopped after %d bytes out of %d\n", - b->dl - missing, b->dl)); - b->dl -= missing; - dsp_wr_dmadone(d); - missing = b->rl; - } - reset_dbuf(b); - splx(s); - return missing; -} - -int -dsp_rdabort(snddev_info *d) -{ - long s; - int missing = 0; - snd_dbuf *b = & (d->dbuf_in) ; - - s = spltty(); - if ( d->flags & SND_F_RD_DMA ) { - d->flags &= ~ ( SND_F_RD_DMA | SND_F_READING ) ; - if (d->callback) - d->callback(d, SND_CB_RD | SND_CB_ABORT); - missing = isa_dmastop(d->dma2) ; /* this many missing bytes... */ - - b->fl += missing ; /* which are part of the free area */ - b->fp -= missing ; - if (b->fp < 0) - b->fp += b->bufsize; - DEB(printf("dsp_rdabort: stopped after %d bytes out of %d\n", - b->dl - missing, b->dl)); - b->dl -= missing; - dsp_rd_dmadone(d); - missing = b->rl ; - } - reset_dbuf(b); - splx(s); - return missing; -} - -/* - * this routine tries to flush the dma transfer. It is called - * on a close. The caller must set SND_F_CLOSING, and insure that - * interrupts are enabled. We immediately abort any read DMA - * operation, and then wait for the play buffer to drain. - */ - -int -snd_flush(snddev_info *d) -{ - int ret, res, res1; - int count=10; - - DEB(printf("snd_flush d->flags 0x%08x\n", d->flags)); - dsp_rdabort(d); - if ( d->flags & SND_F_WR_DMA ) { - /* close write */ - while ( d->flags & SND_F_WR_DMA ) { - /* - * still pending output data. - */ - ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO|PCATCH, "dmafl1", hz); - if (ret == ERESTART || ret == EINTR) { - printf("tsleep returns %d\n", ret); - return -1 ; - } - if ( ret && --count == 0) { - printf("timeout flushing dma1, cnt 0x%x flags 0x%08x\n", - isa_dmastatus1(d->dma1), d->flags); - return -1 ; - } - } - d->flags &= ~SND_F_CLOSING ; - } - reset_dbuf(& (d->dbuf_out) ); - return 0 ; -} - -/* - * end of new code for dma buffer handling - */ diff --git a/sys/i386/isa/snd/mss.h b/sys/i386/isa/snd/mss.h index 4b34e51..994f323 100644 --- a/sys/i386/isa/snd/mss.h +++ b/sys/i386/isa/snd/mss.h @@ -143,90 +143,89 @@ ahead. * (Actually this is not a mapping but rather some kind of interleaving * solution). */ -#define GUSMAX_MIXER -#ifdef GUSMAX_MIXER -#define MODE1_REC_DEVICES \ - (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX) - -#define MODE1_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_MIC | SOUND_MASK_CD | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM|SOUND_MASK_IMIX) - -#define MODE2_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | \ - SOUND_MASK_PCM | SOUND_MASK_IMIX) - -#else /* Generic mapping */ - -#define MODE1_REC_DEVICES \ - (SOUND_MASK_LINE3 | SOUND_MASK_MIC | SOUND_MASK_LINE1|SOUND_MASK_IMIX) -#define MODE1_MIXER_DEVICES \ - (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX) +#define MSS_REC_DEVICES \ + (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX) -#define MODE2_MIXER_DEVICES \ - (SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_LINE2 | \ - SOUND_MASK_LINE3 | SOUND_MASK_SPEAKER | \ - SOUND_MASK_IGAIN | SOUND_MASK_PCM | SOUND_MASK_IMIX) -#endif - -#define OPTI931_MIXER_DEVICES \ - (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ - SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN ) /* - * Most of the mixer entries work in backwards. Setting the polarity field - * makes them to work correctly. + * Table of mixer registers. There is a default table for the + * AD1848/CS423x clones, and one for the OPTI931. As more WSS + * clones come out, there ought to be more tables. + * + * Fields in the table are : polarity, register, offset, bits * * The channel numbering used by individual soundcards is not fixed. * Some cards have assigned different meanings for the AUX1, AUX2 * and LINE inputs. Some have different features... - * The current version doesn't try to compensate this. + * + * Following there is a macro ...MIXER_DEVICES which is a bitmap + * of all non-zero fields in the table. + * MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1 + * registers I0..I15) * */ -mixer_ent mix_devices[32][2] = { /* As used in GUS MAX */ -MIX_ENT(SOUND_MIXER_VOLUME, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5), +mixer_ent mix_devices[32][2] = { +MIX_NONE(SOUND_MIXER_VOLUME), +MIX_NONE(SOUND_MIXER_BASS), +MIX_NONE(SOUND_MIXER_TREBLE), +MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5), MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6), MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), -MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5), +MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5), MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_ALTPCM), +MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5) +MIX_NONE(SOUND_MIXER_OGAIN), +MIX_NONE(SOUND_MIXER_LINE1), +MIX_NONE(SOUND_MIXER_LINE2), +MIX_NONE(SOUND_MIXER_LINE3), }; +#define MODE2_MIXER_DEVICES \ + (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \ + SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ + SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) + +#define MODE1_MIXER_DEVICES \ + (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) + + +/* + * entries for the opti931... + */ + mixer_ent opti931_devices[32][2] = { /* for the opti931 */ MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5), -MIX_ENT(SOUND_MIXER_BASS, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_TREBLE, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_BASS), +MIX_NONE(SOUND_MIXER_TREBLE), MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4), MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5), -MIX_ENT(SOUND_MIXER_SPEAKER, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_SPEAKER), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4), -MIX_ENT(SOUND_MIXER_IMIX, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_ALTPCM, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_RECLEV, 0, 0, 0, 0, 0, 0, 0, 0), +MIX_NONE(SOUND_MIXER_IMIX), +MIX_NONE(SOUND_MIXER_ALTPCM), +MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), -MIX_ENT(SOUND_MIXER_OGAIN, 0, 0, 0, 0, 0, 0, 0, 0), -MIX_ENT(SOUND_MIXER_LINE1, 2, 1, 0, 5, 3, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE2, 4, 1, 0, 5, 5, 1, 0, 5), -MIX_ENT(SOUND_MIXER_LINE3, 18, 1, 0, 5, 19, 1, 0, 5) +MIX_NONE(SOUND_MIXER_OGAIN), +MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4), +MIX_NONE(SOUND_MIXER_LINE2), +MIX_NONE(SOUND_MIXER_LINE3), }; +#define OPTI931_MIXER_DEVICES \ + (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ + SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ + SOUND_MASK_IGAIN | SOUND_MASK_LINE1 ) + + static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = { 0x5a5a, /* Master Volume */ 0x3232, /* Bass */ diff --git a/sys/i386/isa/snd/sb_dsp.c b/sys/i386/isa/snd/sb_dsp.c index e786f5f..d4825c1 100644 --- a/sys/i386/isa/snd/sb_dsp.c +++ b/sys/i386/isa/snd/sb_dsp.c @@ -11,13 +11,14 @@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions - * are met: 1. Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. 2. Redistributions in binary form must reproduce the - * above copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A @@ -112,7 +113,7 @@ snddev_info sb_op_desc = { * resetting the dsp and testing if it is there. * Version detection etc. will be done at attach time. * - * Remebber, isa probe routines are supposed to return the + * Remember, ISA probe routines are supposed to return the * size of io space used. */ @@ -140,6 +141,8 @@ sb_attach(struct isa_device *dev) { snddev_info *d = &pcm_info[dev->id_unit] ; + dev->id_alive = 16 ; /* number of io ports */ + /* should be already set but just in case... */ sb_dsp_init(d, dev); return 0 ; } @@ -171,9 +174,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->rsel.si_pid = 0; d->rsel.si_flags = 0; - d->esel.si_pid = 0; - d->esel.si_flags = 0; - d->flags = 0 ; d->bd_flags &= ~BD_F_HISPEED ; @@ -200,8 +200,6 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; - reset_dbuf(& (d->dbuf_in) ); - reset_dbuf(& (d->dbuf_out) ); sb_reset_dsp(d->io_base); ask_init(d); @@ -287,17 +285,18 @@ again: reason |= 2; } } + /* XXX previous location of ack... */ + DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); + if ( d->dbuf_out.dl && (reason & 1) ) + dsp_wrintr(d); + if ( d->dbuf_in.dl && (reason & 2) ) + dsp_rdintr(d); + if ( c & 2 ) inb(DSP_DATA_AVL16); /* 16-bit int ack */ if (c & 1) inb(DSP_DATA_AVAIL); /* 8-bit int ack */ -DEB(printf("sbintr, flags 0x%08lx reason %d\n", d->flags, reason)); - if ( (d->flags & SND_F_WR_DMA) && (reason & 1) ) - dsp_wrintr(d); - if ( (d->flags & SND_F_RD_DMA) && (reason & 2) ) - dsp_rdintr(d); - /* * the sb16 might have multiple sources etc. */ @@ -320,7 +319,7 @@ static int sb_callback(snddev_info *d, int reason) { int rd = reason & SND_CB_RD ; - int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ; + int l = (rd) ? d->dbuf_in.dl : d->dbuf_out.dl ; switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ @@ -330,74 +329,68 @@ sb_callback(snddev_info *d, int reason) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1; - break ; + break; case SND_CB_START : /* called with int disabled */ - sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON); - d->flags &= ~SND_F_INIT ; if (d->bd_flags & BD_F_SB16) { /* the SB16 can do full duplex using one 16-bit channel * and one 8-bit channel. It needs to be programmed to * use split format though. + * We use the following algorithm: + * 1. check which direction(s) are active; + * 2. check if we should swap dma channels + * 3. check if we can do the swap. */ - int b16 ; - int swap = 0 ; + int swap = 1 ; /* default... */ - b16 = (rd) ? d->rec_fmt : d->play_fmt ; - b16 = (b16 == AFMT_S16_LE) ? 1 : 0; - /* - * check if I have to swap dma channels. Swap if - * - !rd, dma1 <4, b16 - * - !rd, dma1 >=4, !b16 - * - rd, dma2 <4, b16 - * - rd, dma2 >=4, !b16 - */ - if (!rd) { - if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1; + if (rd) { + if (d->flags & SND_F_WRITING || d->dbuf_out.dl) + swap = 0; + if (d->rec_fmt == AFMT_S16_LE && d->dma2 >=4) + swap = 0; + if (d->rec_fmt != AFMT_S16_LE && d->dma2 <4) + swap = 0; } else { - if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1; + if (d->flags & SND_F_READING || d->dbuf_in.dl) + swap = 0; + if (d->play_fmt == AFMT_S16_LE && d->dma1 >=4) + swap = 0; + if (d->play_fmt != AFMT_S16_LE && d->dma1 <4) + swap = 0; } - /* - * before swapping should make sure that there is no - * pending DMA on the other channel... - */ + if (swap) { int c = d->dma2 ; d->dma2 = d->dma1; d->dma1 = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + DEB(printf("START dma chan: play %d, rec %d\n", + d->dma1, d->dma2)); } - DEB(printf("sb_init: play %ld rec %ld dma1 %d dma2 %d\n", - d->play_fmt, d->rec_fmt, d->dma1, d->dma2)); } - /* fallthrough */ - case SND_CB_RESTART: + if (!rd) + sb_cmd(d->io_base, DSP_CMD_SPKON); + if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; - /* - * SB16 support still not completely working!!! - * - * in principle, on the SB16, I could support simultaneous - * play & rec. - * However, there is no way to ask explicitly for 8 or - * 16 bit transfer. As a consequence, if we do 8-bit, - * we need to use the 8-bit channel, and if we do 16-bit, - * we need to use the other one. The only way I find to - * do this is to swap d->dma1 and d->dma2 ... - * - */ if (rd) { c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC ; - c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; - if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; + c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + if (d->rec_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->rec_fmt == AFMT_S16_LE) l /= 2 ; } else { c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) | + DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC ; - c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; + c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ; if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ; if (d->play_fmt == AFMT_S16_LE) l /= 2 ; @@ -409,17 +402,40 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, c ); sb_cmd3(d->io_base, c1 , l - 1) ; } else { + /* code for the SB2 and SB3 */ u_char c ; if (d->bd_flags & BD_F_HISPEED) - c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ; + c = (rd) ? DSP_CMD_HSADC_AUTO : DSP_CMD_HSDAC_AUTO ; else - c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ; + c = (rd) ? DSP_CMD_ADC8_AUTO : DSP_CMD_DAC8_AUTO ; sb_cmd3(d->io_base, c , l - 1) ; } break; + case SND_CB_ABORT : /* XXX */ case SND_CB_STOP : - /* XXX ??? sb_cmd(d->io_base, DSP_CMD_SPKOFF);*/ /* speaker off */ + { + int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ + if (d->bd_flags & BD_F_SB16) { + if ( (rd && d->dbuf_in.chan>4) || (!rd && d->dbuf_out.chan>4) ) + cmd = DSP_CMD_DMAPAUSE_16 ; + } + if (d->bd_flags & BD_F_HISPEED) { + sb_reset_dsp(d->io_base); + d->flags |= SND_F_INIT ; + } else { + sb_cmd(d->io_base, cmd); /* pause dma. */ + /* + * This seems to have the side effect of blocking the other + * side as well so I have to re-enable it :( + */ + if ( (rd && d->dbuf_out.dl) || + (!rd && d->dbuf_in.dl) ) + sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? + 0xd6 : 0xd4); /* continue other dma */ + } + } + DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; } @@ -443,7 +459,7 @@ sb_reset_dsp(int io_base) DELAY(30); if (inb(DSP_READ) != 0xAA) { - DEB(printf("sb_reset_dsp failed\n")); + DEB(printf("sb_reset_dsp 0x%x failed\n", io_base)); return 0; /* Sorry */ } return 1; @@ -753,13 +769,13 @@ dsp_speed(snddev_info *d) * Now the speed should be valid. Compute the value to be * programmed into the board. * - * XXX check this code... + * XXX stereo init is still missing... */ if (speed > 22050) { /* High speed mode on 2.01/3.xx */ int tmp; - tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); + tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8) ; d->bd_flags |= BD_F_HISPEED ; flags = spltty(); @@ -1022,19 +1038,18 @@ static char * sb16pnp_probe(u_long csn, u_long vend_id) { char *s = NULL ; - if (vend_id == 0x01009305) - s = "Avance Asound 100" ; - if (vend_id == 0x2b008c0e) - s = "SB16 Value PnP" ; + /* - * The SB16/AWE64 cards seem to differ in the fourth byte of + * The SB16/AWExx cards seem to differ in the fourth byte of * the vendor id, so I have just masked it for the time being... * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e */ if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) - s = "SB AWE64 PnP"; + s = "SB16 PnP"; + else if (vend_id == 0x01009305) + s = "Avance Asound 100" ; if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); diff --git a/sys/i386/isa/snd/sbcard.h b/sys/i386/isa/snd/sbcard.h index 8548997..d7a24d5 100644 --- a/sys/i386/isa/snd/sbcard.h +++ b/sys/i386/isa/snd/sbcard.h @@ -28,19 +28,34 @@ extern int sbc_major, sbc_minor ; * DSP Commands. There are many, and in many cases they are used explicitly */ +/* these are not used except for programmed I/O (not in this driver) */ #define DSP_DAC8 0x10 /* direct DAC output */ +#define DSP_ADC8 0x20 /* direct ADC input */ + +/* these should be used in the SB 1.0 */ #define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */ +#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */ + +/* these should be used in the SB 2.0 and 2.01 */ +#define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */ +#define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */ + +#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ +#define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */ +#define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */ + +/* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */ + + /* prepare for dma input */ +#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0)) + #define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */ #define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */ -#define DSP_CMD_DAC8_A 0x1c /* auto 8-bit dma out */ -#define DSP_CMD_DAC2S_A 0x1f /* auto 2-bit adpcm dma out (start) */ - -#define DSP_ADC8 0x20 /* direct ADC input */ +#define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */ -#define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */ -#define DSP_CMD_ADC8_A 0x2c /* auto 8-bit dma out */ +/* SB16 commands */ #define DSP_CMD_O16 0xb0 #define DSP_CMD_I16 0xb8 #define DSP_CMD_O8 0xc0 @@ -54,30 +69,22 @@ extern int sbc_major, sbc_minor ; #define DSP_CMD_SPKON 0xD1 #define DSP_CMD_SPKOFF 0xD3 #define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2)) -#define DSP_CMD_DMAON 0xD0 /* ??? the comment says Halt DMA */ -#define DSP_CMD_DMAOFF 0xD4 /* ??? comment says continue dma */ -#define DSP_CMD_DMAHALT 0xD0 +#define DSP_CMD_DMAPAUSE_8 0xD0 +#define DSP_CMD_DMAPAUSE_16 0xD5 +#define DSP_CMD_DMAEXIT_8 0xDA +#define DSP_CMD_DMAEXIT_16 0xD9 #define DSP_CMD_TCONST 0x40 /* set time constant */ -#define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ #define DSP_CMD_HSDAC 0x91 /* high speed dac */ -#define DSP_CMD_HSADC 0x99 /* high speed adc */ +#define DSP_CMD_HSADC 0x99 /* high speed adc */ #define DSP_CMD_GETVER 0xE1 #define DSP_CMD_GETID 0xE7 /* return id bytes */ - /* prepare for dma input */ -#define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0)) #define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */ #define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */ #if 0 /*** unknown ***/ - /* - * D9 and D5 are used on the sb16 on close... maybe a reset of - * some subsystem ? - */ -#define DSP_CMD_D9 0xD9 -#define DSP_CMD_D5 0xD5 #define DSP_CMD_FA 0xFA /* get version from prosonic*/ #define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/ #endif diff --git a/sys/i386/isa/snd/sound.c b/sys/i386/isa/snd/sound.c index 1ed61ef..920680f 100644 --- a/sys/i386/isa/snd/sound.c +++ b/sys/i386/isa/snd/sound.c @@ -1,5 +1,5 @@ /* - * sound/sound.c + * snd/sound.c * * Main sound driver for FreeBSD. This file provides the main * entry points for probe/attach and all i/o demultiplexing, including @@ -31,15 +31,15 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * - * For each board type a template "snddev_info" structure contains + * For each card type a template "snddev_info" structure contains * all the relevant parameters, both for configuration and runtime. * * In this file we build tables of pointers to the descriptors for - * the various supported boards. The generic probe routine scans + * the various supported cards. The generic probe routine scans * the table(s) looking for a matching entry, then invokes the * board-specific probe routine. If successful, a pointer to the * correct snddev_info is stored in snddev_last_probed, for subsequent - * use in the attach routine. The generic attach routine copies + * use in the attach routine. The generic attach routine copies * the template to a permanent descriptor (pcm_info[unit] and * friends), initializes all generic parameters, and calls the * board-specific attach routine. @@ -51,15 +51,9 @@ */ #include <i386/isa/snd/sound.h> -#include <sys/poll.h> #if NPCM > 0 /* from "snd.h" */ -#include "snd.h" -#if NSND > 0 -#error Can't have both Luigi's (pcm0) and voxware (snd0) at the same time. -#endif - #define SNDSTAT_BUF_SIZE 4000 static char status_buf[SNDSTAT_BUF_SIZE] ; static int status_len = 0 ; @@ -97,12 +91,11 @@ u_long nsnd = NPCM ; /* total number of sound devices */ */ snddev_info *snddev_last_probed = NULL ; -static void print_isadev_info(struct isa_device *d, char *s); static snddev_info * generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s); /* - * here are the lists of known devices. Similar devices (e.g. all + * here are the lists of known cards. Similar cards (e.g. all * sb clones, all mss clones, ... are in the same array. * All lists of devices of the same type (eg. all pcm, all midi...) * are in the same array. @@ -116,19 +109,17 @@ generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s); extern snddev_info sb_op_desc; extern snddev_info mss_op_desc; -static snddev_info *sb_devs[] = { +static snddev_info *sb_devs[] = { /* all SB clones */ &sb_op_desc, NULL, } ; -static snddev_info *mss_devs[] = { - /* MSS-like devices */ +static snddev_info *mss_devs[] = { /* all WSS clones */ &mss_op_desc, NULL, } ; -static snddev_info **pcm_devslist[] = { - /* all pcm devices */ +static snddev_info **pcm_devslist[] = { /* all pcm devices */ mss_devs, sb_devs, NULL @@ -142,8 +133,7 @@ pcmprobe(struct isa_device * dev) return generic_snd_probe(dev, pcm_devslist, "pcm") ? 1 : 0 ; } -static snddev_info **midi_devslist[] = { - /* all midi devices */ +static snddev_info **midi_devslist[] = {/* all midi devices */ NULL } ; @@ -205,8 +195,8 @@ pcmattach(struct isa_device * dev) * revisions, if we see that we have a single dma, we might decide * to use a single buffer to save memory. */ - alloc_dbuf( &(d->dbuf_out), d->bufsize, d->dma1 ); - alloc_dbuf( &(d->dbuf_in), d->bufsize, d->dma2 ); + alloc_dbuf( &(d->dbuf_out), d->bufsize ); + alloc_dbuf( &(d->dbuf_in), d->bufsize ); isa_dma_acquire(d->dma1); if (d->dma2 != d->dma1) @@ -237,6 +227,7 @@ pcmattach(struct isa_device * dev) dev->id_id = dvp->id_id; } + d->magic = MAGIC(dev->id_unit); /* debugging... */ /* * and finally, call the device attach routine */ @@ -267,20 +258,6 @@ pcmintr(int unit) synth_info[unit].isr(unit); } -static void -print_isadev_info(struct isa_device *d, char *s) -{ - if (d == NULL ) - return ; - printf("%s%d at 0x%x irq %d drq %d mem %p flags 0x%x en %d confl %d\n", - d->id_driver ? d->id_driver->name : "NONAME", - d->id_unit, - (u_short)(d->id_iobase), ffs(d->id_irq) - 1 , - d->id_drq, d->id_maddr, d->id_flags, - d->id_enabled, d->id_conflicts); -} - - static snddev_info * generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s) { @@ -289,10 +266,11 @@ generic_snd_probe(struct isa_device * dev, snddev_info **p[], char *s) snddev_last_probed = NULL ; - print_isadev_info(dev, s); - saved_dev = *dev ; /* the probe routine might alter parameters */ + /* + * XXX todo: should try to match flags with device type. + */ for ( ; p[0] != NULL ; p++ ) for ( q = *p ; q[0] ; q++ ) if (q[0]->probe && q[0]->probe(dev)) @@ -474,13 +452,13 @@ sndread(dev_t i_dev, struct uio * buf, int flag) DDB(printf("read denied, half duplex and a writer is in\n")); return EBUSY ; } - while ( d->flags & SND_F_WR_DMA ) { + while ( d->dbuf_out.dl ) { /* * we have a pending dma operation, post a read request * and wait for the write to complete. */ d->flags |= SND_F_READING ; - DDB(printf("sndread: sleeping waiting for write to end\n")); + DEB(printf("sndread: sleeping waiting for write to end\n")); ret = tsleep( (caddr_t)&(d->dbuf_out), PRIBIO | PCATCH , "sndrdw", hz ) ; if (ret == ERESTART || ret == EINTR) { @@ -545,7 +523,7 @@ sndwrite(dev_t i_dev, struct uio * buf, int flag) splx(s); return EBUSY ; } - while ( d->flags & SND_F_RD_DMA ) { + while ( d->dbuf_in.dl ) { /* * we have a pending read dma. Post a write request * and wait for the read to complete (in fact I could @@ -636,8 +614,8 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) * we start with the new ioctl interface. */ case AIONWRITE : /* how many bytes can write ? */ - if (d->flags & SND_F_WR_DMA) - dsp_wr_dmaupdate(d); + if (d->dbuf_out.dl) + dsp_wr_dmaupdate(&(d->dbuf_out)); *(int *)arg = d->dbuf_out.fl; break; @@ -647,9 +625,9 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) if (p->play_size <= 1 && p->rec_size <= 1) { /* means no blocks */ d->flags &= ~SND_F_HAS_SIZE ; } else { - RANGE (p->play_size, 40, d->bufsize /4); + RANGE (p->play_size, 40, d->dbuf_out.bufsize /4); d->play_blocksize = p->play_size & ~3 ; - RANGE (p->rec_size, 40, d->bufsize /4); + RANGE (p->rec_size, 40, d->dbuf_in.bufsize /4); d->rec_blocksize = p->rec_size & ~3 ; d->flags |= SND_F_HAS_SIZE ; } @@ -668,9 +646,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) case AIOSFMT : { snd_chan_param *p = (snd_chan_param *)arg; - /* - * at the moment, only support same format on play & rec - */ d->play_speed = p->play_rate; d->rec_speed = p->play_rate; /* XXX */ if (p->play_format & SND_F_STEREO) @@ -715,9 +690,9 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) case AIOSTOP: if (*(int *)arg == AIOSYNC_PLAY) /* play */ - *(int *)arg = dsp_wrabort(d); + *(int *)arg = dsp_wrabort(d, 1 /* restart */); else if (*(int *)arg == AIOSYNC_CAPTURE) - *(int *)arg = dsp_rdabort(d); + *(int *)arg = dsp_rdabort(d, 1 /* restart */); else { splx(s); printf("AIOSTOP: bad channel 0x%x\n", *(int *)arg); @@ -734,13 +709,13 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) * here follow the standard ioctls (filio.h etc.) */ case FIONREAD : /* get # bytes to read */ - if ( d->flags & SND_F_RD_DMA ) - dsp_rd_dmaupdate(d); + if ( d->dbuf_in.dl ) + dsp_rd_dmaupdate(&(d->dbuf_in)); *(int *)arg = d->dbuf_in.rl; break; - case FIOASYNC: /* set/clear async i/o */ - /* do nothing, this is called from kern_descrip.c for fcntl() */ + case FIOASYNC: /*set/clear async i/o */ + printf("FIOASYNC\n"); break; case SNDCTL_DSP_NONBLOCK : @@ -764,7 +739,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) if (t <= 1) { /* means no blocks */ d->flags &= ~SND_F_HAS_SIZE ; } else { - RANGE (t, 40, d->bufsize /4); + RANGE (t, 40, d->dbuf_out.bufsize /4); d->play_blocksize = d->rec_blocksize = t & ~3 ; /* align to multiple of 4 */ d->flags |= SND_F_HAS_SIZE ; @@ -774,15 +749,15 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) ask_init(d); break ; case SNDCTL_DSP_RESET: - DDB(printf("dsp reset\n")); - dsp_wrabort(d); - dsp_rdabort(d); + DEB(printf("dsp reset\n")); + dsp_wrabort(d, 1 /* restart */); + dsp_rdabort(d, 1 /* restart */); break ; case SNDCTL_DSP_SYNC: - DDB(printf("dsp sync\n")); + DEB(printf("dsp sync\n")); splx(s); - snd_sync(d, 1, d->bufsize - 4); /* DMA does not start with <4 bytes */ + snd_sync(d, 1, d->dbuf_out.bufsize - 4); /* DMA does not start with <4 bytes */ break ; case SNDCTL_DSP_SPEED: @@ -793,6 +768,10 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) break ; case SNDCTL_DSP_STEREO: + if ( *(int *)arg == -1) { + *(int *)arg = (d->flags & SND_F_STEREO) ? 1 : 0 ; + break; + } if ( *(int *)arg == 0 ) d->flags &= ~SND_F_STEREO ; /* mono */ else if ( *(int *)arg == 1 ) @@ -807,7 +786,6 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) break ; case SOUND_PCM_WRITE_CHANNELS: - printf("dsp write channels %d\n", *(int *)arg); if ( *(int *)arg == 1) d->flags &= ~SND_F_STEREO ; /* mono */ else if ( *(int *)arg == 2) @@ -875,8 +853,8 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) ((audio_buf_info *)arg)->bytes = d->dbuf_in.fl ; ((audio_buf_info *)arg)->fragments = 1 ; ((audio_buf_info *)arg)->fragstotal = - d->bufsize / d->play_blocksize ; - ((audio_buf_info *)arg)->fragsize = d->play_blocksize ; + d->dbuf_in.bufsize / d->rec_blocksize ; + ((audio_buf_info *)arg)->fragsize = d->rec_blocksize ; break ; case SNDCTL_DSP_GETOSPACE: @@ -884,7 +862,7 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) ((audio_buf_info *)arg)->bytes = d->dbuf_out.fl ; ((audio_buf_info *)arg)->fragments = 1 ; ((audio_buf_info *)arg)->fragstotal = - d->bufsize / d->play_blocksize ; + d->dbuf_out.bufsize / d->play_blocksize ; ((audio_buf_info *)arg)->fragsize = d->play_blocksize ; break ; @@ -916,6 +894,14 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) *(int *)arg = d->mix_recsrc ; break; + case SOUND_MIXER_READ_CAPS : + /* + * XXX - This needs to be fixed when the sound driver actually + * supports multiple inputs. + */ + *(int *)arg = SOUND_CAP_EXCL_INPUT ; + break; + default: DEB(printf("default ioctl snd%d subdev %d fn 0x%08x fail\n", unit, dev & 0xf, cmd)); @@ -926,72 +912,80 @@ sndioctl(dev_t i_dev, int cmd, caddr_t arg, int mode, struct proc * p) return ret ; } +/* + * function to poll what is currently available. Used to be select. + */ int sndpoll(dev_t i_dev, int events, struct proc *p) { - int lim ; - int revents = 0; int dev, unit, c = 1 /* default: success */ ; snddev_info *d ; u_long flags; dev = minor(i_dev); d = get_snddev_info(dev, &unit); - DEB(printf("sndpoll dev 0x%04x rw 0x%08x\n",i_dev, events)); + DEB(printf("sndpoll dev 0x%04x events 0x%08x\n",i_dev, events)); if (d == NULL ) { - printf("select: unit %d not configured\n", unit ); - return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) - | POLLHUP); + printf("poll: unit %d not configured\n", unit ); + return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP); } if (d->poll == NULL) - /* is this correct hear? */ - return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) - | POLLHUP); + return ( (events & (POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM)) | POLLHUP); else if (d->poll != sndpoll ) return d->poll(i_dev, events, p); - - /* handle it here with the generic code */ - - /* - * if the user selected a block size, then we want to use the - * device as a block device, and select will return ready when - * we have a full block. - * In all other cases, select will return when 1 byte is ready. - */ - lim = 1; - flags = spltty(); - /* XXX fix the test here for half duplex devices */ - if (events & (POLLOUT | POLLWRNORM)) { - if ( d->flags & SND_F_HAS_SIZE ) - lim = d->play_blocksize ; - if (d->flags & SND_F_WR_DMA) - dsp_wr_dmaupdate(d); - c = d->dbuf_out.fl ; - if (c < lim) /* no space available */ - selrecord(p, & (d->wsel)); - else - revents |= events & (POLLOUT | POLLWRNORM); - } - - /* XXX fix the test here */ - if (events & (POLLIN | POLLRDNORM)) { - if ( d->flags & SND_F_HAS_SIZE ) - lim = d->rec_blocksize ; - if ( !(d->flags & SND_F_RD_DMA) ) /* dma idle, restart it */ - dsp_rdintr(d); - else - dsp_rd_dmaupdate(d); - c = d->dbuf_in.rl ; - if (c < lim) /* no data available */ - selrecord(p, & (d->rsel)); - else - revents |= events & (POLLIN | POLLRDNORM); - DEB(printf("sndpoll on read: %d >= %d flags 0x%08lx\n", c, lim, - d->flags)); + else { + /* handle it here with the generic code */ + + int lim ; + int revents = 0 ; + + /* + * if the user selected a block size, then we want to use the + * device as a block device, and select will return ready when + * we have a full block. + * In all other cases, select will return when 1 byte is ready. + */ + lim = 1; + if (events & (POLLOUT | POLLWRNORM) ) { + if ( d->flags & SND_F_HAS_SIZE ) + lim = d->play_blocksize ; + /* XXX fix the test here for half duplex devices */ + if (1 /* write is compatible with current mode */) { + flags = spltty(); + if (d->dbuf_out.dl) + dsp_wr_dmaupdate(&(d->dbuf_out)); + c = d->dbuf_out.fl ; + if (c < lim) /* no space available */ + selrecord(p, & (d->wsel)); + else + revents |= events & (POLLOUT | POLLWRNORM); + splx(flags); + } + } + if (events & (POLLIN | POLLRDNORM)) { + if ( d->flags & SND_F_HAS_SIZE ) + lim = d->rec_blocksize ; + /* XXX fix the test here */ + if (1 /* read is compatible with current mode */) { + flags = spltty(); + if ( d->dbuf_in.dl == 0 ) /* dma idle, restart it */ + dsp_rdintr(d); + else + dsp_rd_dmaupdate(&(d->dbuf_in)); + c = d->dbuf_in.rl ; + if (c < lim) /* no data available */ + selrecord(p, & (d->rsel)); + else + revents |= events & (POLLIN | POLLRDNORM); + splx(flags); + } + DEB(printf("sndpoll on read: %d >= %d flags 0x%08x\n", + c, lim, d->flags)); + return c < lim ? 0 : 1 ; + } + return revents; } - splx(flags); - - return revents; + return ENXIO ; /* notreached */ } /* @@ -1020,23 +1014,22 @@ sndmmap(dev_t dev, int offset, int nprot) { snddev_info *d = get_snddev_info(dev, NULL); - DEB(printf("sndmmap d %p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", + DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", d, dev, offset, nprot)); if (d == NULL || nprot & PROT_EXEC) return -1 ; /* forbidden */ - if (offset > d->bufsize && (nprot & PROT_WRITE) ) + if (offset >= d->dbuf_out.bufsize && (nprot & PROT_WRITE) ) return -1 ; /* can only write to the first block */ - if (offset < d->bufsize) + if (offset < d->dbuf_out.bufsize) return i386_btop(vtophys(d->dbuf_out.buf + offset)); offset -= 1 << 24; - if ( (offset >= 0) && (offset < d->bufsize)) + if ( (offset >= 0) && (offset < d->dbuf_in.bufsize)) return i386_btop(vtophys(d->dbuf_in.buf + offset)); offset -= 1 << 24; if ( (offset >= 0) && (offset < 0x2000)) { - printf("name %s\n", d->name); return i386_btop(vtophys( ((int)d & ~0xfff) + offset)); } return -1 ; @@ -1060,7 +1053,8 @@ ask_init(snddev_info *d) if ( d->callback == NULL ) return 0 ; s = spltty(); - if ( d->flags & SND_F_PENDING_IO ) { + if ( d->flags & SND_F_PENDING_IO || + d->dbuf_out.dl || d->dbuf_in.dl ) { /* cannot do it now, record the request and return */ d->flags |= SND_F_INIT ; splx(s); @@ -1093,7 +1087,7 @@ init_status(snddev_info *d) if (status_len != 0) /* only do init once */ return ; sprintf(status_buf, - "FreeBSD Sound Driver " __DATE__ " " __TIME__ "\n" + "FreeBSD Audio Driver (971023) " __DATE__ " " __TIME__ "\n" "Installed devices:\n"); for (i = 0; i < NPCM_MAX; i++) { @@ -1165,22 +1159,28 @@ snd_conflict(int io_base) void snd_set_blocksize(snddev_info *d) { + int tmp ; /* - * now set the blocksize so as to guarantee approx 1/4s + * compute the sample size, and possibly + * set the blocksize so as to guarantee approx 1/4s * between callbacks. */ + tmp = 1 ; + if (d->flags & SND_F_STEREO) tmp += tmp; + if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp; + d->dbuf_out.sample_size = tmp ; + tmp = tmp * d->play_speed; if ( (d->flags & SND_F_HAS_SIZE) == 0) { - /* make blocksize adaptive to 250ms or max 1/4 of the buf */ - int tmp ; - tmp = d->play_speed; - if (d->flags & SND_F_STEREO) tmp += tmp; - if (d->play_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp; d->play_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */ RANGE (d->play_blocksize, 1024, (d->bufsize / 4) & ~3); + } - tmp = d->rec_speed; - if (d->flags & SND_F_STEREO) tmp += tmp; - if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp; + tmp = 1 ; + if (d->flags & SND_F_STEREO) tmp += tmp; + if (d->rec_fmt & (AFMT_S16_LE|AFMT_U16_LE)) tmp += tmp; + tmp = tmp * d->rec_speed; + d->dbuf_in.sample_size = tmp ; + if ( (d->flags & SND_F_HAS_SIZE) == 0) { d->rec_blocksize = (tmp / 4) & ~3; /* 0.25s, aligned to 4 */ RANGE (d->rec_blocksize, 1024, (d->bufsize / 4) & ~3); } diff --git a/sys/i386/isa/snd/sound.h b/sys/i386/isa/snd/sound.h index ed4e0b3..55ed654 100644 --- a/sys/i386/isa/snd/sound.h +++ b/sys/i386/isa/snd/sound.h @@ -28,7 +28,11 @@ * */ +#ifdef KERNEL #include "pcm.h" +#else +#define NPCM 1 +#endif #if NPCM > 0 /* @@ -38,6 +42,7 @@ #ifndef _OS_H_ #define _OS_H_ +#ifdef KERNEL #include <sys/param.h> #include <sys/systm.h> #include <sys/ioccom.h> @@ -57,10 +62,20 @@ #include <sys/errno.h> #include <sys/malloc.h> #include <sys/buf.h> +#include <sys/poll.h> #include <i386/isa/isa_device.h> - #include <machine/clock.h> /* for DELAY */ + +#else +struct isa_device { int dummy ; } ; +#define d_open_t void +#define d_close_t void +#define d_read_t void +#define d_write_t void +#define d_ioctl_t void +#define d_poll_t void +#endif /* KERNEL */ typedef void (irq_proc_t) (int irq); #endif /* _OS_H_ */ @@ -68,19 +83,20 @@ typedef void (irq_proc_t) (int irq); /* * descriptor of a dma buffer. See dmabuf.c for documentation. * (rp,rl) and (fp,fl) identify the READY and FREE regions of the - * buffer. (dp,dl) identify the region currently used by the DMA. - * Only dmabuf.c should test the value of dl. The reason is that - * in auto-dma mode looking at dl alone does not make much sense, - * since the transfer potentially spans the entire buffer. + * buffer. dl contains the length used for dma transfer, dl>0 also + * means that the channel is busy and there is a DMA transfer in progress. */ typedef struct _snd_dbuf { char *buf; int bufsize ; - volatile int dp, rp, fp; - volatile int dl, rl, fl; - volatile int dl0; /* value used last time in dl */ + volatile int rp, fp; /* pointers to the ready and free area */ + volatile int dl; /* transfer size */ + volatile int rl, fl; /* lenght of ready and free areas. */ int int_count; + int chan; /* dma channel */ + int sample_size ; /* 1, 2, 4 */ + struct selinfo sel; } snd_dbuf ; /* @@ -125,7 +141,6 @@ struct _snddev_info { #define SND_CB_WR 0x200 /* write callback */ #define SND_CB_REASON_MASK 0xff #define SND_CB_START 0x01 /* start dma op */ -#define SND_CB_RESTART 0x02 /* restart dma op */ #define SND_CB_STOP 0x03 /* stop dma op */ #define SND_CB_ABORT 0x04 /* abort dma op */ #define SND_CB_INIT 0x05 /* init board parameters */ @@ -145,8 +160,8 @@ struct _snddev_info { int synth_base ; /* base for the synth */ int irq ; - int dma1, dma2 ; /* dma2=dma1 for half-duplex cards */ - +#define dma1 dbuf_out.chan +#define dma2 dbuf_in.chan int bd_id ; /* used to hold board-id info, eg. sb version, * mss codec type, etc. etc. */ @@ -198,12 +213,14 @@ struct _snddev_info { * you might get interrupts, so some manipulations of the * descriptors must be done with interrupts blocked. */ +#if 0 #define SND_F_RD_DMA 0x0010 /* read-dma active */ #define SND_F_WR_DMA 0x0020 /* write-dma active */ #define SND_F_PENDING_IN (SND_F_READING | SND_F_RD_DMA) #define SND_F_PENDING_OUT (SND_F_WRITING | SND_F_WR_DMA) -#define SND_F_PENDING_IO (SND_F_PENDING_IN | SND_F_PENDING_OUT) +#endif +#define SND_F_PENDING_IO (SND_F_READING | SND_F_WRITING) /* * flag used to mark a pending close. @@ -242,7 +259,6 @@ struct _snddev_info { * be done at the next convenient time. */ #define SND_F_INIT 0x4000 /* changed parameters. need init */ -#define SND_F_AUTO_DMA 0x8000 /* use auto-dma */ u_long bd_flags; /* board-specific flags */ int play_speed, rec_speed; @@ -258,8 +274,11 @@ struct _snddev_info { u_long mix_recsrc; /* current recording source(s) */ u_short mix_levels[32]; - struct selinfo wsel, rsel, esel ; +#define wsel dbuf_out.sel +#define rsel dbuf_in.sel u_long interrupts; /* counter of interrupts */ + u_long magic; +#define MAGIC(unit) ( 0xa4d10de0 + unit ) void *device_data ; /* just in case it is needed...*/ } ; @@ -310,13 +329,20 @@ struct _snddev_info { #define MD_CS4232 0xA4 #define MD_CS4232A 0xA5 #define MD_CS4236 0xA6 +#define MD_CS4237 0xA7 #define MD_OPTI931 0xB1 +#define MD_GUSPNP 0xB8 +#define MD_YM0020 0xC1 +#define MD_VIVO 0xD1 /* * TODO: add some card classes rather than specific types. */ +#ifdef KERNEL #include <i386/isa/snd/soundcard.h> - +#else +#include </sys/i386/isa/snd/soundcard.h> +#endif /* * many variables should be reduced to a range. Here define a macro */ @@ -327,14 +353,17 @@ struct _snddev_info { /* * finally, all default parameters */ -#define DSP_BUFFSIZE 65536 /* XXX */ +#define DSP_BUFFSIZE (65536 - 256) /* XXX */ +/* + * the last 256 bytes are room for buggy soundcard to overflow. + */ -#if 1 /* prepare for pnp support! */ +#ifdef KERNEL #include "pnp.h" #if NPNP > 0 #include <i386/isa/pnp.h> /* XXX pnp support */ #endif -#endif +#endif /* KERNEL */ /* * Minor numbers for the sound driver. @@ -395,11 +424,15 @@ struct mixer_def { typedef struct mixer_def mixer_ent; typedef struct mixer_def mixer_tab[32][2]; +#ifdef KERNEL + #define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \ {{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}} #define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \ {{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}} +#define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0) + #define DDB(x) x /* XXX */ #ifndef DEB @@ -431,14 +464,23 @@ void dsp_wrintr(snddev_info *d); void dsp_rdintr(snddev_info *d); int dsp_write_body(snddev_info *d, struct uio *buf); int dsp_read_body(snddev_info *d, struct uio *buf); -void alloc_dbuf(snd_dbuf *d, int size, int b16); -void reset_dbuf(snd_dbuf *b); +void alloc_dbuf(snd_dbuf *d, int size); + int snd_flush(snddev_info *d); + +/* the following parameters are used in snd_sync and reset_dbuf + * to decide whether or not to restart a channel + */ +#define SND_CHAN_NONE 0x0 +#define SND_CHAN_WR 0x1 +#define SND_CHAN_RD 0x2 + +void reset_dbuf(snd_dbuf *b, int chan); int snd_sync(snddev_info *d, int chan, int threshold); -int dsp_wrabort(snddev_info *d); -int dsp_rdabort(snddev_info *d); -void dsp_wr_dmaupdate(snddev_info *d); -void dsp_rd_dmaupdate(snddev_info *d); +int dsp_wrabort(snddev_info *d, int restart); +int dsp_rdabort(snddev_info *d, int restart); +void dsp_wr_dmaupdate(snd_dbuf *b); +void dsp_rd_dmaupdate(snd_dbuf *b); d_poll_t sndpoll; @@ -463,6 +505,7 @@ int sb_reset_dsp (int io_base); void sb_setmixer (int io_base, u_int port, u_int value); int sb_getmixer (int io_base, u_int port); +#endif /* KERNEL */ /* * usage of flags in device config entry (config file) |