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 /sys/dev/sound | |
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.
Diffstat (limited to 'sys/dev/sound')
-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 |
6 files changed, 637 insertions, 430 deletions
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); |