diff options
| author | Luigi Rizzo <luigi@FreeBSD.org> | 1998-10-02 17:26:37 +0000 |
|---|---|---|
| committer | Luigi Rizzo <luigi@FreeBSD.org> | 1998-10-02 17:26:37 +0000 |
| commit | fe854067c7036664227a88cea1e2436b67ac9723 (patch) | |
| tree | 6c7faf07ef58002971c63796aceb924a59522877 /sys | |
| parent | 2ebb50c0eb3af271ca6050459871ff072c3ca896 (diff) | |
Notes
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/dev/pcm/isa/mss.c | 162 | ||||
| -rw-r--r-- | sys/dev/pcm/isa/mss.h | 2 | ||||
| -rw-r--r-- | sys/dev/pcm/isa/sb.c | 274 | ||||
| -rw-r--r-- | sys/dev/pcm/isa/sb.h | 11 | ||||
| -rw-r--r-- | sys/dev/sound/isa/mss.c | 162 | ||||
| -rw-r--r-- | sys/dev/sound/isa/mss.h | 2 | ||||
| -rw-r--r-- | sys/dev/sound/isa/sb.c | 274 | ||||
| -rw-r--r-- | sys/dev/sound/isa/sb.h | 11 | ||||
| -rw-r--r-- | sys/dev/sound/isa/sb16.c | 274 | ||||
| -rw-r--r-- | sys/dev/sound/isa/sb8.c | 274 | ||||
| -rw-r--r-- | sys/i386/isa/snd/CARDS | 171 | ||||
| -rw-r--r-- | sys/i386/isa/snd/README | 33 | ||||
| -rw-r--r-- | sys/i386/isa/snd/ad1848.c | 162 | ||||
| -rw-r--r-- | sys/i386/isa/snd/dmabuf.c | 21 | ||||
| -rw-r--r-- | sys/i386/isa/snd/mss.h | 2 | ||||
| -rw-r--r-- | sys/i386/isa/snd/sb_dsp.c | 274 | ||||
| -rw-r--r-- | sys/i386/isa/snd/sbcard.h | 11 | ||||
| -rw-r--r-- | sys/i386/isa/snd/sound.c | 97 | ||||
| -rw-r--r-- | sys/i386/isa/snd/sound.h | 7 |
19 files changed, 1607 insertions, 617 deletions
diff --git a/sys/dev/pcm/isa/mss.c b/sys/dev/pcm/isa/mss.c index 095d83f0bccf9..326c5863f01ad 100644 --- a/sys/dev/pcm/isa/mss.c +++ b/sys/dev/pcm/isa/mss.c @@ -4,7 +4,7 @@ * Driver for Microsoft Sound System/Windows Sound System (mss) * -compatible boards. This includes: * - * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others. + * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others. * * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 @@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev) outb(dev->id_iobase, bits ); } } + if (1) { /* machine-specific code for the Toshiba Libretto */ + u_char r6, r9; + outb( 0x370, 6 /* dma config */ ); + outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */); + r6 = inb( 0x371 /* read */ ); + outb( 0x370, 0xa /* version */ ); + r9 = inb( 0x371 /* read */ ); + DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) + /* + * yamaha - set volume to max + */ + outb( 0x370, 7 /* volume left */ ); + outb( 0x371, 0 /* max level */ ); + outb( 0x370, 8 /* volume right */ ); + outb( 0x371, 0 /* max level */ ); + } if ( FULL_DUPLEX(d) ) d->audio_fmt |= AFMT_FULLDUPLEX ; + if (d->bd_id == MD_YM0020) { + DDB(printf("setting up yamaha registers\n")); + outb(0x370, 6 /* dma config */ ) ; + if (FULL_DUPLEX(d)) + outb(0x371, 0xa9 ); /* use both dma chans */ + else + outb(0x371, 0x8b ); /* use low dma chan */ + } mss_reinit(d); ad1848_mixer_reset(d); return 0; @@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason) /* * perform all necessary initializations for i/o */ - d->rec_fmt = d->play_fmt ; /* no split format on the WSS */ + d->rec_fmt = d->play_fmt ; /* no split format on the MSS */ snd_set_blocksize(d); mss_reinit(d); reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); @@ -561,15 +585,21 @@ again: if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked); masked |= mc11 ; + /* + * the nice OPTi931 sets the IRQ line before setting the bits in + * mc11. So, on some occasions I have to retry (max 10 times). + */ if ( mc11 == 0 ) { /* perhaps can return ... */ reason = inb(io_Status(d)); if (reason & 1) { - printf("one more try...\n"); - goto again; + DEB(printf("one more try...\n");) + if (--loops) + goto again; + else + DDB(printf("opti_intr: irq but mc11 not set!...\n");) } - if (loops==10) { + if (loops==10) printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - } return; } @@ -580,7 +610,8 @@ again: dsp_wrintr(d); } opti_write(d->conf_base, 11, ~mc11); /* ack */ - if (--loops) goto again; + if (--loops) + goto again; DEB(printf("xxx too many loops\n");) } @@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg) int x; flags = spltty(); - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 201); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; x = inb(io_Indexed_Data(d)); @@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data) int x ; flags = spltty(); - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 1002); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; outb(io_Indexed_Data(d), data); @@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d) int prev; d->bd_flags |= BD_F_MCE_BIT; - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 203); prev = inb(io_Index_Addr(d)); prev &= ~IA_TRD ; outb(io_Index_Addr(d), prev | IA_MCE ) ; @@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value) d->mix_levels[dev] = left | (right << 8); +#if 0 /* Scale volumes */ left = mix_cvt[left]; right = mix_cvt[right]; - +#endif /* * Set the left channel */ @@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d) ad_write(d, 20, 0x88); ad_write(d, 21, 0x88); break; + + case MD_YM0020: + /* set master volume to max */ + DDB(printf("set yamaha master volume to max"); ) + outb(0x370, 7) ; + outb(0x371, 0) ; + outb(0x370, 8) ; + outb(0x371, 0) ; + break; + case MD_GUSPNP: /* this is only necessary in mode 3 ... */ ad_write(d, 22, 0x88); @@ -997,7 +1039,7 @@ mss_format(snddev_info *d) arg = AFMT_U8 ; /* ulaw/alaw seems broken on the opti931... */ - if (d->bd_id == MD_OPTI931) { + if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) { if (arg == AFMT_MU_LAW) { arg = AFMT_U8 ; d->flags |= SND_F_XLAT8 ; @@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev) d->bd_id = MD_AD1845; } ad_write(d, 23, tmp); /* Restore */ + DDB(printf("... try to identify the yamaha\n") ;) + tmp = inb(0x370) ; + outb(0x370, 6 /* dma config */ ) ; + if (inb(0x370) != 6 ) /* not a yamaha... restore. */ + outb(0x370, tmp ) ; + else + d->bd_id = MD_YM0020 ; break; case 0x83: /* CS4236 */ @@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev) default: /* Assume CS4231 */ BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) d->bd_id = MD_CS4231; - } } ad_write(d, 25, tmp1); /* Restore bits */ @@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id) u_long id = vend_id & 0xff00ffff; if ( id == 0x3700630e ) s = "CS4237" ; - else if ( id == 0x3600630e ) + else if ( id == 0x3500630e || id == 0x3600630e ) s = "CS4236" ; else if ( id == 0x3500630e ) s = "CS4236B" ; @@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id) s = "Yamaha SA2"; else if ( id == 0x3000a865) s = "Yamaha SA3"; - else if ( id == 0x0000a865) + else if (vend_id == 0x0000a865) s = "Yamaha YMF719 OPL-SA3"; else if (vend_id == 0x8140d315) s = "SoundscapeVIVO"; @@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/ dev->id_alive = 16 ; /* number of io ports ? */ tmp_d = sb_op_desc ; - if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) { + if (vend_id==0x2000a865 || vend_id==0x3000a865 || + vend_id==0x0008a865 || vend_id==0x8140d315) { /* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */ dev->id_iobase = d.port[0] ; tmp_d.alt_base = d.port[1] ; @@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, case 0x2000a865: /* Yamaha SA2 */ case 0x3000a865: /* Yamaha SA3 */ - case 0x0000a865: /* Yamaha YMF719 */ + case 0x0000a865: /* Yamaha TMF719 SA3 */ dev->id_iobase = d.port[1]; tmp_d.alt_base = d.port[0]; tmp_d.conf_base = d.port[4]; @@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, tmp_d.bd_id = MD_CS4237 ; break; + case 0x3500630e: /* CS4236 */ case 0x3600630e: /* CS4236 */ case 0x3500630e: /* CS4236B */ tmp_d.bd_id = MD_CS4236 ; @@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, write_pnp_parms( &d, ldn ); enable_pnp_card(); - if ( (vend_id & 0x2000ffff) == 0x2000a865 ) { + if ( (vend_id & 0x0000ffff) == 0x0000a865 ) { /* special volume setting for the Yamaha... */ outb(tmp_d.conf_base, 7 /* volume, left */); outb(tmp_d.conf_base+1, 0 ); @@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name, if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ /* * the 931 is not a real SB, it has important pieces of - * hardware controlled by both the WSS and the SB port... + * hardware controlled by both the MSS and the SB port... */ printf("--- opti931 in sb mode ---\n"); - opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */ + opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */ /* * swap the main and alternate iobase address since we want * to work in sb mode. @@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name, dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; } else { /* mss-compatible codec */ tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */ - opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */ + opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */ opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */ @@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } +static char *opti925_probe(u_long csn, u_long vend_id); +static void opti925_attach(u_long csn, u_long vend_id, char *name, + struct isa_device *dev); + +static struct pnp_device opti925 = { + "opti925", + opti925_probe, + opti925_attach, + &nsnd, /* use this for all sound cards */ + &tty_imask /* imask */ +}; +DATA_SET (pnpdevice_set, opti925); + +static char * +opti925_probe(u_long csn, u_long vend_id) +{ + if (vend_id == 0x2509143e) { + struct pnp_cinfo d ; + read_pnp_parms ( &d , 1 ) ; + if (d.enable == 0) { + printf("This is an OPTi925, but LDN 1 is disabled\n"); + return NULL; + } + return "OPTi925" ; + } + return NULL ; +} + +static void +opti925_attach(u_long csn, u_long vend_id, char *name, + struct isa_device *dev) +{ + struct pnp_cinfo d ; + snddev_info tmp_d ; /* patched copy of the basic snddev_info */ + int the_irq = 0 ; + + tmp_d = mss_op_desc; + snddev_last_probed = &tmp_d; + + read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ + the_irq = d.irq[0]; + d.port[0] = 0 ; + d.enable = 0 ; + write_pnp_parms ( &d , 3 ); + + read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ + d.port[0] = 0 ; + d.enable = 0 ; + write_pnp_parms ( &d , 2 ); + + read_pnp_parms ( &d , 1 ) ; + d.irq[0] = the_irq ; + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + write_pnp_parms ( &d , 1 ); + enable_pnp_card(); + + tmp_d.conf_base = d.port[3]; + + dev->id_drq = d.drq[0] ; /* primary dma */ + dev->id_irq = (1 << d.irq[0] ) ; + dev->id_intr = pcmintr ; + dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; + tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; + + snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + + pcmattach(dev); +} + static void gus_mem_cfg(snddev_info *tmp); static char *guspnp_probe(u_long csn, u_long vend_id); diff --git a/sys/dev/pcm/isa/mss.h b/sys/dev/pcm/isa/mss.h index 994f323c2bef5..54efc3cc21950 100644 --- a/sys/dev/pcm/isa/mss.h +++ b/sys/dev/pcm/isa/mss.h @@ -150,7 +150,7 @@ ahead. /* * Table of mixer registers. There is a default table for the - * AD1848/CS423x clones, and one for the OPTI931. As more WSS + * AD1848/CS423x clones, and one for the OPTI931. As more MSS * clones come out, there ought to be more tables. * * Fields in the table are : polarity, register, offset, bits diff --git a/sys/dev/pcm/isa/sb.c b/sys/dev/pcm/isa/sb.c index 575980cc8821b..349a7e2102138 100644 --- a/sys/dev/pcm/isa/sb.c +++ b/sys/dev/pcm/isa/sb.c @@ -3,7 +3,7 @@ * * driver for the SoundBlaster and clones. * - * Copyright 1997 Luigi Rizzo. + * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d); static void sb_mixer_reset(snddev_info *d); u_int sb_get_byte(int io_base); +int ess_write(int io_base, u_char reg, int val); +int ess_read(int io_base, u_char reg); /* * Then put here the descriptors for the various boards supported @@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev) * here are the main routines from the switches. */ +/* + * Unlike MSS, the sb only supports a single open (does not mean + * that only a single process is using it, since it can fork + * afterwards, or pass the descriptor to another process). + * + */ static int sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) { @@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_U8 ; break ; } - if ( (flags & FREAD) == 0) + /* + * since the SB is not simmetric, I use the open mode to select + * which channel should be privileged, and disable I/O in the + * other direction. + * In case the board is opened RW, we don't have enough + * information on what to do. Temporarily, privilege the + * playback channel, which is used more often, and set the other + * one to U8. + */ + if ( (flags & FREAD) == 0) /* opened write only */ d->rec_fmt = 0 ; - if ( (flags & FWRITE) == 0) + else if ( (flags & FWRITE) == 0) /* opened read only */ d->play_fmt = 0 ; + else /* opened read/write */ + d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; d->flags |= SND_F_BUSY ; d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; @@ -275,7 +294,10 @@ sb_intr(int unit) * SB < 4.0 is half duplex and has only 1 bit for int source, * so we fake it. SB 4.x (SB16) has the int source in a separate * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... */ +#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) again: if (d->bd_flags & BD_F_SB16) { c = sb_getmixer(io_base, IRQ_STAT); @@ -284,25 +306,26 @@ again: */ reason = 0 ; if ( c & 1 ) { /* 8-bit dma */ - if (d->dbuf_out.chan < 4) + if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) reason |= 1; - if (d->dbuf_in.chan < 4) + if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) reason |= 2; } if ( c & 2 ) { /* 16-bit dma */ - if (d->dbuf_out.chan >= 4) + if (d->play_fmt == AFMT_S16_LE) reason |= 1; - if (d->dbuf_in.chan >= 4) + if (d->rec_fmt == AFMT_S16_LE) reason |= 2; } } /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason)); + DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", + d->flags, reason, c)); if ( reason & 1 ) { /* possibly a write interrupt */ if ( d->dbuf_out.dl ) dsp_wrintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: wrintr but write DMA inactive!\n"); } } @@ -310,7 +333,7 @@ again: if ( d->dbuf_in.dl ) dsp_rdintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: rdintr but read DMA inactive!\n"); } } @@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason) switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ + /* + * set the speed + */ dsp_speed(d); + /* + * set the desired DMA blocksize (influences select behaviour) + */ snd_set_blocksize(d); + /* + * since native mulaw is not present, emulate it. + */ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; - if (d->bd_flags & BD_F_SB16) { + /* + * there are too many flavours of SB for my taste... here i try to do + * the proper initialization for each one. + */ + if (PLAIN_SB16(d->bd_flags)) { u_char c, c1 ; - /* the SB16 can do full duplex using one 16-bit channel + /* the original SB16 (non-PnP, or PnP, or Vibra16C) + * 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: + * I DON'T do this for the Vibra16X because I have no idea + * of what needs to be done there... + * + * I 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. @@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason) if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) swap = 0; if ( d->rec_fmt ) { - /* check for possible config errors. */ + /* check for possible config errors. + * This cannot happen at open time since even in + * case of opening rw we privilege the play + * channel. + */ if (d->rec_fmt == d->play_fmt) { DDB(printf("sorry, read DMA channel unavailable\n")); } @@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason) d->dbuf_in.chan = d->dbuf_out.chan; d->dbuf_out.chan = c ; } + } else if (d->bd_flags & BD_F_ESS) { + u_char c ; + if (d->play_fmt == 0) { + /* initialize for record */ + static u_char cmd[] = { + 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc + }; + ess_write(d->io_base, 0xb8, 0x0e); + c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ; + if (d->flags & SND_F_STEREO) + c++ ; + ess_write(d->io_base, 0xa8, c); + ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */ + /* + * set format in b6, b7 + */ + } else { + /* initialize for play */ + static u_char cmd[] = { + 0x80,0x51,0xd0,0x00,0x71,0xf4, + 0x80,0x51,0x98,0x00,0x71,0xbc + }; + } } reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); @@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason) case SND_CB_START : /* called with int disabled */ if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; + + if (d->bd_flags & BD_F_SB16X) { + /* just a guess: on the Vibra16X, the first + * op started takes the first dma channel, + * the second one takes the next... + * The default is to be ready for play. + */ + int swap = 0 ; + DEB(printf("start %s -- now dma %d:%d\n", + rd ? "rd" : "wr", + d->dbuf_out.chan, d->dbuf_in.chan);); + /* swap only if both channels are idle + * play: dl=0, since there is no pause; + * rec: rl=0 + */ + if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { + /* must swap channels, but also save dl */ + int c = d->dbuf_in.chan ; + int dl = d->dbuf_in.dl ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + d->dbuf_in.dl = dl ; + printf("swapped -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } + /* * XXX note: c1 and l should be set basing on d->rec_fmt, * but there is no choice once a 16 or 8-bit channel * is assigned. This means that if the application * tries to use a bad format, the sound will not be nice. */ - if ( b->chan > 4 ) { + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) { c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; c1 = DSP_F16_SIGNED ; l /= 2 ; @@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason) case SND_CB_STOP : { int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - if ( d->bd_flags & BD_F_SB16 && b->chan > 4 ) + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) cmd = DSP_CMD_DMAPAUSE_16 ; if (d->bd_flags & BD_F_HISPEED) { sb_reset_dsp(d->io_base); @@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? 0xd6 : 0xd4); /* continue other dma */ } + if (d->bd_flags & BD_F_SB16X) { + /* restore possible swapped channels. + * The default is to be ready for play. + * XXX right now, it kills all input on overflow + */ + if ( rd && d->dbuf_out.dl == 0 ) { + /* must swap channels ? */ + int c = d->dbuf_in.chan ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + printf("restored -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } } DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; @@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev) printf("ESS1868 (rev %d)\n", rev); else printf("ESS688 (rev %d)\n", rev); + /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */ break ; /* XXX */ } else { printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", @@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d) /* * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd2 write a CMD + 1 byte arg + * sb_cmd3 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port + * + * ess_write is actually sb_cmd2 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ int @@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port) u_long flags; flags = spltty(); - outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */ + outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); - val = inb(io_base + 5); + val = inb(io_base + SB_MIX_DATA); DELAY(10); splx(flags); @@ -747,6 +874,19 @@ sb_get_byte(int io_base) return 0xffff; } +int +ess_write(int io_base, u_char reg, int val) +{ + return sb_cmd2(io_base, reg, val); +} + +int +ess_read(int io_base, u_char reg) +{ + if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) + return 0xffff ; + return sb_get_byte(io_base); +} /* @@ -785,17 +925,21 @@ dsp_speed(snddev_info *d) */ if (d->bd_flags & BD_F_ESS) { int t; - RANGE (speed, 4000, 48000); + RANGE (speed, 5000, 49000); if (speed > 22000) { t = (795500 + speed / 2) / speed; speed = (795500 + t / 2) / t ; - t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ; + t = (256 - t ) | 0x80 ; } else { t = (397700 + speed / 2) / speed; speed = (397700 + t / 2) / t ; - t = 128 - (397700 + speed / 2) / speed ; + t = 128 - t ; } - sb_cmd2(d->io_base, 0xa1, t); /* set time constant */ + ess_write(d->io_base, 0xa1, t); /* set time constant */ + d->play_speed = d->rec_speed = speed ; + speed = (speed * 9 ) / 20 ; + t = 256-7160000/(speed*82); + ess_write(d->io_base,0xa2,t); return speed ; } @@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } -static char *opti925_probe(u_long csn, u_long vend_id); -static void opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device opti925 = { - "opti925", - opti925_probe, - opti925_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, opti925); - -static char * -opti925_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x2509143e) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an OPTi925, but LDN 1 is disabled\n"); - return NULL; - } - return "OPTi925" ; - } - return NULL ; -} - -static void -opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int the_irq = 0 ; - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - the_irq = d.irq[0]; - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - d.irq[0] = the_irq ; - dev->id_iobase = d.port[0]; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - tmp_d.conf_base = d.port[3]; - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - pcmattach(dev); - -} - /* * A driver for some SB16pnp and compatibles... * * Avance Asound 100 -- 0x01009305 + * Avance Logic ALS100+ -- 0x10019305 * xxx -- 0x2b008c0e * */ @@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id) * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e + * Vibra16X: 0xf0008c0e */ - if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) + if (vend_id == 0xf0008c0e) + s = "Vibra16X" ; + else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) s = "SB16 PnP"; else if (vend_id == 0x01009305) s = "Avance Asound 100" ; + else if (vend_id == 0x10019305) + s = "Avance Logic 100+" ; /* Vibra16X-class */ if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - pcm_info[dev->id_unit] = tmp_d; + pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) { + /* + * XXX please add here the vend_id for other vibra16X cards... + * And remember, must change tmp_d, not + */ + tmp_d.bd_flags |= BD_F_SB16X ; + } pcmattach(dev); } #endif /* NPNP */ diff --git a/sys/dev/pcm/isa/sb.h b/sys/dev/pcm/isa/sb.h index 0bd42102d715b..92ec966107911 100644 --- a/sys/dev/pcm/isa/sb.h +++ b/sys/dev/pcm/isa/sb.h @@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ; #define DSP_DATA_AVAIL (io_base + 0xE) #define DSP_DATA_AVL16 (io_base + 0xF) +#define SB_MIX_ADDR 0x4 +#define SB_MIX_DATA 0x5 #if 0 -#define MIXER_ADDR (io_base + 0x4) -#define MIXER_DATA (io_base + 0x5) #define OPL3_LEFT (io_base + 0x0) #define OPL3_RIGHT (io_base + 0x2) #define OPL3_BOTH (io_base + 0x8) @@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ; #define BD_F_MIX_CT1745 0x0030 /* CT1745 */ #define BD_F_SB16 0x0100 /* this is a SB16 */ -#define BD_F_NOREC 0x0200 /* recording not supported on this board */ +#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */ #define BD_F_MIDIBUSY 0x0400 /* midi busy */ #define BD_F_ESS 0x0800 /* this is an ESS chip */ +/* + * on some SB16 cards, at times I swap DMA channels. Remember this + * so that they can be restored later. + */ +#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */ /* diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index 095d83f0bccf9..326c5863f01ad 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -4,7 +4,7 @@ * Driver for Microsoft Sound System/Windows Sound System (mss) * -compatible boards. This includes: * - * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others. + * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others. * * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 @@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev) outb(dev->id_iobase, bits ); } } + if (1) { /* machine-specific code for the Toshiba Libretto */ + u_char r6, r9; + outb( 0x370, 6 /* dma config */ ); + outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */); + r6 = inb( 0x371 /* read */ ); + outb( 0x370, 0xa /* version */ ); + r9 = inb( 0x371 /* read */ ); + DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) + /* + * yamaha - set volume to max + */ + outb( 0x370, 7 /* volume left */ ); + outb( 0x371, 0 /* max level */ ); + outb( 0x370, 8 /* volume right */ ); + outb( 0x371, 0 /* max level */ ); + } if ( FULL_DUPLEX(d) ) d->audio_fmt |= AFMT_FULLDUPLEX ; + if (d->bd_id == MD_YM0020) { + DDB(printf("setting up yamaha registers\n")); + outb(0x370, 6 /* dma config */ ) ; + if (FULL_DUPLEX(d)) + outb(0x371, 0xa9 ); /* use both dma chans */ + else + outb(0x371, 0x8b ); /* use low dma chan */ + } mss_reinit(d); ad1848_mixer_reset(d); return 0; @@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason) /* * perform all necessary initializations for i/o */ - d->rec_fmt = d->play_fmt ; /* no split format on the WSS */ + d->rec_fmt = d->play_fmt ; /* no split format on the MSS */ snd_set_blocksize(d); mss_reinit(d); reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); @@ -561,15 +585,21 @@ again: if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked); masked |= mc11 ; + /* + * the nice OPTi931 sets the IRQ line before setting the bits in + * mc11. So, on some occasions I have to retry (max 10 times). + */ if ( mc11 == 0 ) { /* perhaps can return ... */ reason = inb(io_Status(d)); if (reason & 1) { - printf("one more try...\n"); - goto again; + DEB(printf("one more try...\n");) + if (--loops) + goto again; + else + DDB(printf("opti_intr: irq but mc11 not set!...\n");) } - if (loops==10) { + if (loops==10) printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - } return; } @@ -580,7 +610,8 @@ again: dsp_wrintr(d); } opti_write(d->conf_base, 11, ~mc11); /* ack */ - if (--loops) goto again; + if (--loops) + goto again; DEB(printf("xxx too many loops\n");) } @@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg) int x; flags = spltty(); - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 201); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; x = inb(io_Indexed_Data(d)); @@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data) int x ; flags = spltty(); - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 1002); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; outb(io_Indexed_Data(d), data); @@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d) int prev; d->bd_flags |= BD_F_MCE_BIT; - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 203); prev = inb(io_Index_Addr(d)); prev &= ~IA_TRD ; outb(io_Index_Addr(d), prev | IA_MCE ) ; @@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value) d->mix_levels[dev] = left | (right << 8); +#if 0 /* Scale volumes */ left = mix_cvt[left]; right = mix_cvt[right]; - +#endif /* * Set the left channel */ @@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d) ad_write(d, 20, 0x88); ad_write(d, 21, 0x88); break; + + case MD_YM0020: + /* set master volume to max */ + DDB(printf("set yamaha master volume to max"); ) + outb(0x370, 7) ; + outb(0x371, 0) ; + outb(0x370, 8) ; + outb(0x371, 0) ; + break; + case MD_GUSPNP: /* this is only necessary in mode 3 ... */ ad_write(d, 22, 0x88); @@ -997,7 +1039,7 @@ mss_format(snddev_info *d) arg = AFMT_U8 ; /* ulaw/alaw seems broken on the opti931... */ - if (d->bd_id == MD_OPTI931) { + if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) { if (arg == AFMT_MU_LAW) { arg = AFMT_U8 ; d->flags |= SND_F_XLAT8 ; @@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev) d->bd_id = MD_AD1845; } ad_write(d, 23, tmp); /* Restore */ + DDB(printf("... try to identify the yamaha\n") ;) + tmp = inb(0x370) ; + outb(0x370, 6 /* dma config */ ) ; + if (inb(0x370) != 6 ) /* not a yamaha... restore. */ + outb(0x370, tmp ) ; + else + d->bd_id = MD_YM0020 ; break; case 0x83: /* CS4236 */ @@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev) default: /* Assume CS4231 */ BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) d->bd_id = MD_CS4231; - } } ad_write(d, 25, tmp1); /* Restore bits */ @@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id) u_long id = vend_id & 0xff00ffff; if ( id == 0x3700630e ) s = "CS4237" ; - else if ( id == 0x3600630e ) + else if ( id == 0x3500630e || id == 0x3600630e ) s = "CS4236" ; else if ( id == 0x3500630e ) s = "CS4236B" ; @@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id) s = "Yamaha SA2"; else if ( id == 0x3000a865) s = "Yamaha SA3"; - else if ( id == 0x0000a865) + else if (vend_id == 0x0000a865) s = "Yamaha YMF719 OPL-SA3"; else if (vend_id == 0x8140d315) s = "SoundscapeVIVO"; @@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/ dev->id_alive = 16 ; /* number of io ports ? */ tmp_d = sb_op_desc ; - if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) { + if (vend_id==0x2000a865 || vend_id==0x3000a865 || + vend_id==0x0008a865 || vend_id==0x8140d315) { /* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */ dev->id_iobase = d.port[0] ; tmp_d.alt_base = d.port[1] ; @@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, case 0x2000a865: /* Yamaha SA2 */ case 0x3000a865: /* Yamaha SA3 */ - case 0x0000a865: /* Yamaha YMF719 */ + case 0x0000a865: /* Yamaha TMF719 SA3 */ dev->id_iobase = d.port[1]; tmp_d.alt_base = d.port[0]; tmp_d.conf_base = d.port[4]; @@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, tmp_d.bd_id = MD_CS4237 ; break; + case 0x3500630e: /* CS4236 */ case 0x3600630e: /* CS4236 */ case 0x3500630e: /* CS4236B */ tmp_d.bd_id = MD_CS4236 ; @@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, write_pnp_parms( &d, ldn ); enable_pnp_card(); - if ( (vend_id & 0x2000ffff) == 0x2000a865 ) { + if ( (vend_id & 0x0000ffff) == 0x0000a865 ) { /* special volume setting for the Yamaha... */ outb(tmp_d.conf_base, 7 /* volume, left */); outb(tmp_d.conf_base+1, 0 ); @@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name, if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ /* * the 931 is not a real SB, it has important pieces of - * hardware controlled by both the WSS and the SB port... + * hardware controlled by both the MSS and the SB port... */ printf("--- opti931 in sb mode ---\n"); - opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */ + opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */ /* * swap the main and alternate iobase address since we want * to work in sb mode. @@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name, dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; } else { /* mss-compatible codec */ tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */ - opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */ + opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */ opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */ @@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } +static char *opti925_probe(u_long csn, u_long vend_id); +static void opti925_attach(u_long csn, u_long vend_id, char *name, + struct isa_device *dev); + +static struct pnp_device opti925 = { + "opti925", + opti925_probe, + opti925_attach, + &nsnd, /* use this for all sound cards */ + &tty_imask /* imask */ +}; +DATA_SET (pnpdevice_set, opti925); + +static char * +opti925_probe(u_long csn, u_long vend_id) +{ + if (vend_id == 0x2509143e) { + struct pnp_cinfo d ; + read_pnp_parms ( &d , 1 ) ; + if (d.enable == 0) { + printf("This is an OPTi925, but LDN 1 is disabled\n"); + return NULL; + } + return "OPTi925" ; + } + return NULL ; +} + +static void +opti925_attach(u_long csn, u_long vend_id, char *name, + struct isa_device *dev) +{ + struct pnp_cinfo d ; + snddev_info tmp_d ; /* patched copy of the basic snddev_info */ + int the_irq = 0 ; + + tmp_d = mss_op_desc; + snddev_last_probed = &tmp_d; + + read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ + the_irq = d.irq[0]; + d.port[0] = 0 ; + d.enable = 0 ; + write_pnp_parms ( &d , 3 ); + + read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ + d.port[0] = 0 ; + d.enable = 0 ; + write_pnp_parms ( &d , 2 ); + + read_pnp_parms ( &d , 1 ) ; + d.irq[0] = the_irq ; + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + write_pnp_parms ( &d , 1 ); + enable_pnp_card(); + + tmp_d.conf_base = d.port[3]; + + dev->id_drq = d.drq[0] ; /* primary dma */ + dev->id_irq = (1 << d.irq[0] ) ; + dev->id_intr = pcmintr ; + dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; + tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; + + snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + + pcmattach(dev); +} + static void gus_mem_cfg(snddev_info *tmp); static char *guspnp_probe(u_long csn, u_long vend_id); diff --git a/sys/dev/sound/isa/mss.h b/sys/dev/sound/isa/mss.h index 994f323c2bef5..54efc3cc21950 100644 --- a/sys/dev/sound/isa/mss.h +++ b/sys/dev/sound/isa/mss.h @@ -150,7 +150,7 @@ ahead. /* * Table of mixer registers. There is a default table for the - * AD1848/CS423x clones, and one for the OPTI931. As more WSS + * AD1848/CS423x clones, and one for the OPTI931. As more MSS * clones come out, there ought to be more tables. * * Fields in the table are : polarity, register, offset, bits diff --git a/sys/dev/sound/isa/sb.c b/sys/dev/sound/isa/sb.c index 575980cc8821b..349a7e2102138 100644 --- a/sys/dev/sound/isa/sb.c +++ b/sys/dev/sound/isa/sb.c @@ -3,7 +3,7 @@ * * driver for the SoundBlaster and clones. * - * Copyright 1997 Luigi Rizzo. + * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d); static void sb_mixer_reset(snddev_info *d); u_int sb_get_byte(int io_base); +int ess_write(int io_base, u_char reg, int val); +int ess_read(int io_base, u_char reg); /* * Then put here the descriptors for the various boards supported @@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev) * here are the main routines from the switches. */ +/* + * Unlike MSS, the sb only supports a single open (does not mean + * that only a single process is using it, since it can fork + * afterwards, or pass the descriptor to another process). + * + */ static int sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) { @@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_U8 ; break ; } - if ( (flags & FREAD) == 0) + /* + * since the SB is not simmetric, I use the open mode to select + * which channel should be privileged, and disable I/O in the + * other direction. + * In case the board is opened RW, we don't have enough + * information on what to do. Temporarily, privilege the + * playback channel, which is used more often, and set the other + * one to U8. + */ + if ( (flags & FREAD) == 0) /* opened write only */ d->rec_fmt = 0 ; - if ( (flags & FWRITE) == 0) + else if ( (flags & FWRITE) == 0) /* opened read only */ d->play_fmt = 0 ; + else /* opened read/write */ + d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; d->flags |= SND_F_BUSY ; d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; @@ -275,7 +294,10 @@ sb_intr(int unit) * SB < 4.0 is half duplex and has only 1 bit for int source, * so we fake it. SB 4.x (SB16) has the int source in a separate * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... */ +#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) again: if (d->bd_flags & BD_F_SB16) { c = sb_getmixer(io_base, IRQ_STAT); @@ -284,25 +306,26 @@ again: */ reason = 0 ; if ( c & 1 ) { /* 8-bit dma */ - if (d->dbuf_out.chan < 4) + if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) reason |= 1; - if (d->dbuf_in.chan < 4) + if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) reason |= 2; } if ( c & 2 ) { /* 16-bit dma */ - if (d->dbuf_out.chan >= 4) + if (d->play_fmt == AFMT_S16_LE) reason |= 1; - if (d->dbuf_in.chan >= 4) + if (d->rec_fmt == AFMT_S16_LE) reason |= 2; } } /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason)); + DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", + d->flags, reason, c)); if ( reason & 1 ) { /* possibly a write interrupt */ if ( d->dbuf_out.dl ) dsp_wrintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: wrintr but write DMA inactive!\n"); } } @@ -310,7 +333,7 @@ again: if ( d->dbuf_in.dl ) dsp_rdintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: rdintr but read DMA inactive!\n"); } } @@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason) switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ + /* + * set the speed + */ dsp_speed(d); + /* + * set the desired DMA blocksize (influences select behaviour) + */ snd_set_blocksize(d); + /* + * since native mulaw is not present, emulate it. + */ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; - if (d->bd_flags & BD_F_SB16) { + /* + * there are too many flavours of SB for my taste... here i try to do + * the proper initialization for each one. + */ + if (PLAIN_SB16(d->bd_flags)) { u_char c, c1 ; - /* the SB16 can do full duplex using one 16-bit channel + /* the original SB16 (non-PnP, or PnP, or Vibra16C) + * 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: + * I DON'T do this for the Vibra16X because I have no idea + * of what needs to be done there... + * + * I 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. @@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason) if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) swap = 0; if ( d->rec_fmt ) { - /* check for possible config errors. */ + /* check for possible config errors. + * This cannot happen at open time since even in + * case of opening rw we privilege the play + * channel. + */ if (d->rec_fmt == d->play_fmt) { DDB(printf("sorry, read DMA channel unavailable\n")); } @@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason) d->dbuf_in.chan = d->dbuf_out.chan; d->dbuf_out.chan = c ; } + } else if (d->bd_flags & BD_F_ESS) { + u_char c ; + if (d->play_fmt == 0) { + /* initialize for record */ + static u_char cmd[] = { + 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc + }; + ess_write(d->io_base, 0xb8, 0x0e); + c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ; + if (d->flags & SND_F_STEREO) + c++ ; + ess_write(d->io_base, 0xa8, c); + ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */ + /* + * set format in b6, b7 + */ + } else { + /* initialize for play */ + static u_char cmd[] = { + 0x80,0x51,0xd0,0x00,0x71,0xf4, + 0x80,0x51,0x98,0x00,0x71,0xbc + }; + } } reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); @@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason) case SND_CB_START : /* called with int disabled */ if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; + + if (d->bd_flags & BD_F_SB16X) { + /* just a guess: on the Vibra16X, the first + * op started takes the first dma channel, + * the second one takes the next... + * The default is to be ready for play. + */ + int swap = 0 ; + DEB(printf("start %s -- now dma %d:%d\n", + rd ? "rd" : "wr", + d->dbuf_out.chan, d->dbuf_in.chan);); + /* swap only if both channels are idle + * play: dl=0, since there is no pause; + * rec: rl=0 + */ + if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { + /* must swap channels, but also save dl */ + int c = d->dbuf_in.chan ; + int dl = d->dbuf_in.dl ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + d->dbuf_in.dl = dl ; + printf("swapped -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } + /* * XXX note: c1 and l should be set basing on d->rec_fmt, * but there is no choice once a 16 or 8-bit channel * is assigned. This means that if the application * tries to use a bad format, the sound will not be nice. */ - if ( b->chan > 4 ) { + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) { c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; c1 = DSP_F16_SIGNED ; l /= 2 ; @@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason) case SND_CB_STOP : { int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - if ( d->bd_flags & BD_F_SB16 && b->chan > 4 ) + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) cmd = DSP_CMD_DMAPAUSE_16 ; if (d->bd_flags & BD_F_HISPEED) { sb_reset_dsp(d->io_base); @@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? 0xd6 : 0xd4); /* continue other dma */ } + if (d->bd_flags & BD_F_SB16X) { + /* restore possible swapped channels. + * The default is to be ready for play. + * XXX right now, it kills all input on overflow + */ + if ( rd && d->dbuf_out.dl == 0 ) { + /* must swap channels ? */ + int c = d->dbuf_in.chan ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + printf("restored -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } } DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; @@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev) printf("ESS1868 (rev %d)\n", rev); else printf("ESS688 (rev %d)\n", rev); + /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */ break ; /* XXX */ } else { printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", @@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d) /* * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd2 write a CMD + 1 byte arg + * sb_cmd3 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port + * + * ess_write is actually sb_cmd2 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ int @@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port) u_long flags; flags = spltty(); - outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */ + outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); - val = inb(io_base + 5); + val = inb(io_base + SB_MIX_DATA); DELAY(10); splx(flags); @@ -747,6 +874,19 @@ sb_get_byte(int io_base) return 0xffff; } +int +ess_write(int io_base, u_char reg, int val) +{ + return sb_cmd2(io_base, reg, val); +} + +int +ess_read(int io_base, u_char reg) +{ + if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) + return 0xffff ; + return sb_get_byte(io_base); +} /* @@ -785,17 +925,21 @@ dsp_speed(snddev_info *d) */ if (d->bd_flags & BD_F_ESS) { int t; - RANGE (speed, 4000, 48000); + RANGE (speed, 5000, 49000); if (speed > 22000) { t = (795500 + speed / 2) / speed; speed = (795500 + t / 2) / t ; - t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ; + t = (256 - t ) | 0x80 ; } else { t = (397700 + speed / 2) / speed; speed = (397700 + t / 2) / t ; - t = 128 - (397700 + speed / 2) / speed ; + t = 128 - t ; } - sb_cmd2(d->io_base, 0xa1, t); /* set time constant */ + ess_write(d->io_base, 0xa1, t); /* set time constant */ + d->play_speed = d->rec_speed = speed ; + speed = (speed * 9 ) / 20 ; + t = 256-7160000/(speed*82); + ess_write(d->io_base,0xa2,t); return speed ; } @@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } -static char *opti925_probe(u_long csn, u_long vend_id); -static void opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device opti925 = { - "opti925", - opti925_probe, - opti925_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, opti925); - -static char * -opti925_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x2509143e) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an OPTi925, but LDN 1 is disabled\n"); - return NULL; - } - return "OPTi925" ; - } - return NULL ; -} - -static void -opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int the_irq = 0 ; - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - the_irq = d.irq[0]; - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - d.irq[0] = the_irq ; - dev->id_iobase = d.port[0]; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - tmp_d.conf_base = d.port[3]; - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - pcmattach(dev); - -} - /* * A driver for some SB16pnp and compatibles... * * Avance Asound 100 -- 0x01009305 + * Avance Logic ALS100+ -- 0x10019305 * xxx -- 0x2b008c0e * */ @@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id) * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e + * Vibra16X: 0xf0008c0e */ - if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) + if (vend_id == 0xf0008c0e) + s = "Vibra16X" ; + else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) s = "SB16 PnP"; else if (vend_id == 0x01009305) s = "Avance Asound 100" ; + else if (vend_id == 0x10019305) + s = "Avance Logic 100+" ; /* Vibra16X-class */ if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - pcm_info[dev->id_unit] = tmp_d; + pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) { + /* + * XXX please add here the vend_id for other vibra16X cards... + * And remember, must change tmp_d, not + */ + tmp_d.bd_flags |= BD_F_SB16X ; + } pcmattach(dev); } #endif /* NPNP */ diff --git a/sys/dev/sound/isa/sb.h b/sys/dev/sound/isa/sb.h index 0bd42102d715b..92ec966107911 100644 --- a/sys/dev/sound/isa/sb.h +++ b/sys/dev/sound/isa/sb.h @@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ; #define DSP_DATA_AVAIL (io_base + 0xE) #define DSP_DATA_AVL16 (io_base + 0xF) +#define SB_MIX_ADDR 0x4 +#define SB_MIX_DATA 0x5 #if 0 -#define MIXER_ADDR (io_base + 0x4) -#define MIXER_DATA (io_base + 0x5) #define OPL3_LEFT (io_base + 0x0) #define OPL3_RIGHT (io_base + 0x2) #define OPL3_BOTH (io_base + 0x8) @@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ; #define BD_F_MIX_CT1745 0x0030 /* CT1745 */ #define BD_F_SB16 0x0100 /* this is a SB16 */ -#define BD_F_NOREC 0x0200 /* recording not supported on this board */ +#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */ #define BD_F_MIDIBUSY 0x0400 /* midi busy */ #define BD_F_ESS 0x0800 /* this is an ESS chip */ +/* + * on some SB16 cards, at times I swap DMA channels. Remember this + * so that they can be restored later. + */ +#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */ /* diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index 575980cc8821b..349a7e2102138 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -3,7 +3,7 @@ * * driver for the SoundBlaster and clones. * - * Copyright 1997 Luigi Rizzo. + * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d); static void sb_mixer_reset(snddev_info *d); u_int sb_get_byte(int io_base); +int ess_write(int io_base, u_char reg, int val); +int ess_read(int io_base, u_char reg); /* * Then put here the descriptors for the various boards supported @@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev) * here are the main routines from the switches. */ +/* + * Unlike MSS, the sb only supports a single open (does not mean + * that only a single process is using it, since it can fork + * afterwards, or pass the descriptor to another process). + * + */ static int sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) { @@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_U8 ; break ; } - if ( (flags & FREAD) == 0) + /* + * since the SB is not simmetric, I use the open mode to select + * which channel should be privileged, and disable I/O in the + * other direction. + * In case the board is opened RW, we don't have enough + * information on what to do. Temporarily, privilege the + * playback channel, which is used more often, and set the other + * one to U8. + */ + if ( (flags & FREAD) == 0) /* opened write only */ d->rec_fmt = 0 ; - if ( (flags & FWRITE) == 0) + else if ( (flags & FWRITE) == 0) /* opened read only */ d->play_fmt = 0 ; + else /* opened read/write */ + d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; d->flags |= SND_F_BUSY ; d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; @@ -275,7 +294,10 @@ sb_intr(int unit) * SB < 4.0 is half duplex and has only 1 bit for int source, * so we fake it. SB 4.x (SB16) has the int source in a separate * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... */ +#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) again: if (d->bd_flags & BD_F_SB16) { c = sb_getmixer(io_base, IRQ_STAT); @@ -284,25 +306,26 @@ again: */ reason = 0 ; if ( c & 1 ) { /* 8-bit dma */ - if (d->dbuf_out.chan < 4) + if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) reason |= 1; - if (d->dbuf_in.chan < 4) + if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) reason |= 2; } if ( c & 2 ) { /* 16-bit dma */ - if (d->dbuf_out.chan >= 4) + if (d->play_fmt == AFMT_S16_LE) reason |= 1; - if (d->dbuf_in.chan >= 4) + if (d->rec_fmt == AFMT_S16_LE) reason |= 2; } } /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason)); + DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", + d->flags, reason, c)); if ( reason & 1 ) { /* possibly a write interrupt */ if ( d->dbuf_out.dl ) dsp_wrintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: wrintr but write DMA inactive!\n"); } } @@ -310,7 +333,7 @@ again: if ( d->dbuf_in.dl ) dsp_rdintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: rdintr but read DMA inactive!\n"); } } @@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason) switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ + /* + * set the speed + */ dsp_speed(d); + /* + * set the desired DMA blocksize (influences select behaviour) + */ snd_set_blocksize(d); + /* + * since native mulaw is not present, emulate it. + */ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; - if (d->bd_flags & BD_F_SB16) { + /* + * there are too many flavours of SB for my taste... here i try to do + * the proper initialization for each one. + */ + if (PLAIN_SB16(d->bd_flags)) { u_char c, c1 ; - /* the SB16 can do full duplex using one 16-bit channel + /* the original SB16 (non-PnP, or PnP, or Vibra16C) + * 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: + * I DON'T do this for the Vibra16X because I have no idea + * of what needs to be done there... + * + * I 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. @@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason) if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) swap = 0; if ( d->rec_fmt ) { - /* check for possible config errors. */ + /* check for possible config errors. + * This cannot happen at open time since even in + * case of opening rw we privilege the play + * channel. + */ if (d->rec_fmt == d->play_fmt) { DDB(printf("sorry, read DMA channel unavailable\n")); } @@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason) d->dbuf_in.chan = d->dbuf_out.chan; d->dbuf_out.chan = c ; } + } else if (d->bd_flags & BD_F_ESS) { + u_char c ; + if (d->play_fmt == 0) { + /* initialize for record */ + static u_char cmd[] = { + 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc + }; + ess_write(d->io_base, 0xb8, 0x0e); + c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ; + if (d->flags & SND_F_STEREO) + c++ ; + ess_write(d->io_base, 0xa8, c); + ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */ + /* + * set format in b6, b7 + */ + } else { + /* initialize for play */ + static u_char cmd[] = { + 0x80,0x51,0xd0,0x00,0x71,0xf4, + 0x80,0x51,0x98,0x00,0x71,0xbc + }; + } } reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); @@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason) case SND_CB_START : /* called with int disabled */ if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; + + if (d->bd_flags & BD_F_SB16X) { + /* just a guess: on the Vibra16X, the first + * op started takes the first dma channel, + * the second one takes the next... + * The default is to be ready for play. + */ + int swap = 0 ; + DEB(printf("start %s -- now dma %d:%d\n", + rd ? "rd" : "wr", + d->dbuf_out.chan, d->dbuf_in.chan);); + /* swap only if both channels are idle + * play: dl=0, since there is no pause; + * rec: rl=0 + */ + if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { + /* must swap channels, but also save dl */ + int c = d->dbuf_in.chan ; + int dl = d->dbuf_in.dl ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + d->dbuf_in.dl = dl ; + printf("swapped -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } + /* * XXX note: c1 and l should be set basing on d->rec_fmt, * but there is no choice once a 16 or 8-bit channel * is assigned. This means that if the application * tries to use a bad format, the sound will not be nice. */ - if ( b->chan > 4 ) { + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) { c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; c1 = DSP_F16_SIGNED ; l /= 2 ; @@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason) case SND_CB_STOP : { int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - if ( d->bd_flags & BD_F_SB16 && b->chan > 4 ) + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) cmd = DSP_CMD_DMAPAUSE_16 ; if (d->bd_flags & BD_F_HISPEED) { sb_reset_dsp(d->io_base); @@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? 0xd6 : 0xd4); /* continue other dma */ } + if (d->bd_flags & BD_F_SB16X) { + /* restore possible swapped channels. + * The default is to be ready for play. + * XXX right now, it kills all input on overflow + */ + if ( rd && d->dbuf_out.dl == 0 ) { + /* must swap channels ? */ + int c = d->dbuf_in.chan ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + printf("restored -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } } DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; @@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev) printf("ESS1868 (rev %d)\n", rev); else printf("ESS688 (rev %d)\n", rev); + /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */ break ; /* XXX */ } else { printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", @@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d) /* * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd2 write a CMD + 1 byte arg + * sb_cmd3 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port + * + * ess_write is actually sb_cmd2 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ int @@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port) u_long flags; flags = spltty(); - outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */ + outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); - val = inb(io_base + 5); + val = inb(io_base + SB_MIX_DATA); DELAY(10); splx(flags); @@ -747,6 +874,19 @@ sb_get_byte(int io_base) return 0xffff; } +int +ess_write(int io_base, u_char reg, int val) +{ + return sb_cmd2(io_base, reg, val); +} + +int +ess_read(int io_base, u_char reg) +{ + if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) + return 0xffff ; + return sb_get_byte(io_base); +} /* @@ -785,17 +925,21 @@ dsp_speed(snddev_info *d) */ if (d->bd_flags & BD_F_ESS) { int t; - RANGE (speed, 4000, 48000); + RANGE (speed, 5000, 49000); if (speed > 22000) { t = (795500 + speed / 2) / speed; speed = (795500 + t / 2) / t ; - t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ; + t = (256 - t ) | 0x80 ; } else { t = (397700 + speed / 2) / speed; speed = (397700 + t / 2) / t ; - t = 128 - (397700 + speed / 2) / speed ; + t = 128 - t ; } - sb_cmd2(d->io_base, 0xa1, t); /* set time constant */ + ess_write(d->io_base, 0xa1, t); /* set time constant */ + d->play_speed = d->rec_speed = speed ; + speed = (speed * 9 ) / 20 ; + t = 256-7160000/(speed*82); + ess_write(d->io_base,0xa2,t); return speed ; } @@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } -static char *opti925_probe(u_long csn, u_long vend_id); -static void opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device opti925 = { - "opti925", - opti925_probe, - opti925_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, opti925); - -static char * -opti925_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x2509143e) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an OPTi925, but LDN 1 is disabled\n"); - return NULL; - } - return "OPTi925" ; - } - return NULL ; -} - -static void -opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int the_irq = 0 ; - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - the_irq = d.irq[0]; - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - d.irq[0] = the_irq ; - dev->id_iobase = d.port[0]; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - tmp_d.conf_base = d.port[3]; - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - pcmattach(dev); - -} - /* * A driver for some SB16pnp and compatibles... * * Avance Asound 100 -- 0x01009305 + * Avance Logic ALS100+ -- 0x10019305 * xxx -- 0x2b008c0e * */ @@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id) * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e + * Vibra16X: 0xf0008c0e */ - if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) + if (vend_id == 0xf0008c0e) + s = "Vibra16X" ; + else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) s = "SB16 PnP"; else if (vend_id == 0x01009305) s = "Avance Asound 100" ; + else if (vend_id == 0x10019305) + s = "Avance Logic 100+" ; /* Vibra16X-class */ if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - pcm_info[dev->id_unit] = tmp_d; + pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) { + /* + * XXX please add here the vend_id for other vibra16X cards... + * And remember, must change tmp_d, not + */ + tmp_d.bd_flags |= BD_F_SB16X ; + } pcmattach(dev); } #endif /* NPNP */ diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index 575980cc8821b..349a7e2102138 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -3,7 +3,7 @@ * * driver for the SoundBlaster and clones. * - * Copyright 1997 Luigi Rizzo. + * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d); static void sb_mixer_reset(snddev_info *d); u_int sb_get_byte(int io_base); +int ess_write(int io_base, u_char reg, int val); +int ess_read(int io_base, u_char reg); /* * Then put here the descriptors for the various boards supported @@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev) * here are the main routines from the switches. */ +/* + * Unlike MSS, the sb only supports a single open (does not mean + * that only a single process is using it, since it can fork + * afterwards, or pass the descriptor to another process). + * + */ static int sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) { @@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_U8 ; break ; } - if ( (flags & FREAD) == 0) + /* + * since the SB is not simmetric, I use the open mode to select + * which channel should be privileged, and disable I/O in the + * other direction. + * In case the board is opened RW, we don't have enough + * information on what to do. Temporarily, privilege the + * playback channel, which is used more often, and set the other + * one to U8. + */ + if ( (flags & FREAD) == 0) /* opened write only */ d->rec_fmt = 0 ; - if ( (flags & FWRITE) == 0) + else if ( (flags & FWRITE) == 0) /* opened read only */ d->play_fmt = 0 ; + else /* opened read/write */ + d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; d->flags |= SND_F_BUSY ; d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; @@ -275,7 +294,10 @@ sb_intr(int unit) * SB < 4.0 is half duplex and has only 1 bit for int source, * so we fake it. SB 4.x (SB16) has the int source in a separate * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... */ +#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) again: if (d->bd_flags & BD_F_SB16) { c = sb_getmixer(io_base, IRQ_STAT); @@ -284,25 +306,26 @@ again: */ reason = 0 ; if ( c & 1 ) { /* 8-bit dma */ - if (d->dbuf_out.chan < 4) + if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) reason |= 1; - if (d->dbuf_in.chan < 4) + if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) reason |= 2; } if ( c & 2 ) { /* 16-bit dma */ - if (d->dbuf_out.chan >= 4) + if (d->play_fmt == AFMT_S16_LE) reason |= 1; - if (d->dbuf_in.chan >= 4) + if (d->rec_fmt == AFMT_S16_LE) reason |= 2; } } /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason)); + DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", + d->flags, reason, c)); if ( reason & 1 ) { /* possibly a write interrupt */ if ( d->dbuf_out.dl ) dsp_wrintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: wrintr but write DMA inactive!\n"); } } @@ -310,7 +333,7 @@ again: if ( d->dbuf_in.dl ) dsp_rdintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: rdintr but read DMA inactive!\n"); } } @@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason) switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ + /* + * set the speed + */ dsp_speed(d); + /* + * set the desired DMA blocksize (influences select behaviour) + */ snd_set_blocksize(d); + /* + * since native mulaw is not present, emulate it. + */ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; - if (d->bd_flags & BD_F_SB16) { + /* + * there are too many flavours of SB for my taste... here i try to do + * the proper initialization for each one. + */ + if (PLAIN_SB16(d->bd_flags)) { u_char c, c1 ; - /* the SB16 can do full duplex using one 16-bit channel + /* the original SB16 (non-PnP, or PnP, or Vibra16C) + * 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: + * I DON'T do this for the Vibra16X because I have no idea + * of what needs to be done there... + * + * I 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. @@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason) if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) swap = 0; if ( d->rec_fmt ) { - /* check for possible config errors. */ + /* check for possible config errors. + * This cannot happen at open time since even in + * case of opening rw we privilege the play + * channel. + */ if (d->rec_fmt == d->play_fmt) { DDB(printf("sorry, read DMA channel unavailable\n")); } @@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason) d->dbuf_in.chan = d->dbuf_out.chan; d->dbuf_out.chan = c ; } + } else if (d->bd_flags & BD_F_ESS) { + u_char c ; + if (d->play_fmt == 0) { + /* initialize for record */ + static u_char cmd[] = { + 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc + }; + ess_write(d->io_base, 0xb8, 0x0e); + c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ; + if (d->flags & SND_F_STEREO) + c++ ; + ess_write(d->io_base, 0xa8, c); + ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */ + /* + * set format in b6, b7 + */ + } else { + /* initialize for play */ + static u_char cmd[] = { + 0x80,0x51,0xd0,0x00,0x71,0xf4, + 0x80,0x51,0x98,0x00,0x71,0xbc + }; + } } reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); @@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason) case SND_CB_START : /* called with int disabled */ if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; + + if (d->bd_flags & BD_F_SB16X) { + /* just a guess: on the Vibra16X, the first + * op started takes the first dma channel, + * the second one takes the next... + * The default is to be ready for play. + */ + int swap = 0 ; + DEB(printf("start %s -- now dma %d:%d\n", + rd ? "rd" : "wr", + d->dbuf_out.chan, d->dbuf_in.chan);); + /* swap only if both channels are idle + * play: dl=0, since there is no pause; + * rec: rl=0 + */ + if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { + /* must swap channels, but also save dl */ + int c = d->dbuf_in.chan ; + int dl = d->dbuf_in.dl ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + d->dbuf_in.dl = dl ; + printf("swapped -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } + /* * XXX note: c1 and l should be set basing on d->rec_fmt, * but there is no choice once a 16 or 8-bit channel * is assigned. This means that if the application * tries to use a bad format, the sound will not be nice. */ - if ( b->chan > 4 ) { + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) { c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; c1 = DSP_F16_SIGNED ; l /= 2 ; @@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason) case SND_CB_STOP : { int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - if ( d->bd_flags & BD_F_SB16 && b->chan > 4 ) + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) cmd = DSP_CMD_DMAPAUSE_16 ; if (d->bd_flags & BD_F_HISPEED) { sb_reset_dsp(d->io_base); @@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? 0xd6 : 0xd4); /* continue other dma */ } + if (d->bd_flags & BD_F_SB16X) { + /* restore possible swapped channels. + * The default is to be ready for play. + * XXX right now, it kills all input on overflow + */ + if ( rd && d->dbuf_out.dl == 0 ) { + /* must swap channels ? */ + int c = d->dbuf_in.chan ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + printf("restored -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } } DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; @@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev) printf("ESS1868 (rev %d)\n", rev); else printf("ESS688 (rev %d)\n", rev); + /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */ break ; /* XXX */ } else { printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", @@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d) /* * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd2 write a CMD + 1 byte arg + * sb_cmd3 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port + * + * ess_write is actually sb_cmd2 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ int @@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port) u_long flags; flags = spltty(); - outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */ + outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); - val = inb(io_base + 5); + val = inb(io_base + SB_MIX_DATA); DELAY(10); splx(flags); @@ -747,6 +874,19 @@ sb_get_byte(int io_base) return 0xffff; } +int +ess_write(int io_base, u_char reg, int val) +{ + return sb_cmd2(io_base, reg, val); +} + +int +ess_read(int io_base, u_char reg) +{ + if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) + return 0xffff ; + return sb_get_byte(io_base); +} /* @@ -785,17 +925,21 @@ dsp_speed(snddev_info *d) */ if (d->bd_flags & BD_F_ESS) { int t; - RANGE (speed, 4000, 48000); + RANGE (speed, 5000, 49000); if (speed > 22000) { t = (795500 + speed / 2) / speed; speed = (795500 + t / 2) / t ; - t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ; + t = (256 - t ) | 0x80 ; } else { t = (397700 + speed / 2) / speed; speed = (397700 + t / 2) / t ; - t = 128 - (397700 + speed / 2) / speed ; + t = 128 - t ; } - sb_cmd2(d->io_base, 0xa1, t); /* set time constant */ + ess_write(d->io_base, 0xa1, t); /* set time constant */ + d->play_speed = d->rec_speed = speed ; + speed = (speed * 9 ) / 20 ; + t = 256-7160000/(speed*82); + ess_write(d->io_base,0xa2,t); return speed ; } @@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } -static char *opti925_probe(u_long csn, u_long vend_id); -static void opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device opti925 = { - "opti925", - opti925_probe, - opti925_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, opti925); - -static char * -opti925_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x2509143e) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an OPTi925, but LDN 1 is disabled\n"); - return NULL; - } - return "OPTi925" ; - } - return NULL ; -} - -static void -opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int the_irq = 0 ; - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - the_irq = d.irq[0]; - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - d.irq[0] = the_irq ; - dev->id_iobase = d.port[0]; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - tmp_d.conf_base = d.port[3]; - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - pcmattach(dev); - -} - /* * A driver for some SB16pnp and compatibles... * * Avance Asound 100 -- 0x01009305 + * Avance Logic ALS100+ -- 0x10019305 * xxx -- 0x2b008c0e * */ @@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id) * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e + * Vibra16X: 0xf0008c0e */ - if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) + if (vend_id == 0xf0008c0e) + s = "Vibra16X" ; + else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) s = "SB16 PnP"; else if (vend_id == 0x01009305) s = "Avance Asound 100" ; + else if (vend_id == 0x10019305) + s = "Avance Logic 100+" ; /* Vibra16X-class */ if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - pcm_info[dev->id_unit] = tmp_d; + pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) { + /* + * XXX please add here the vend_id for other vibra16X cards... + * And remember, must change tmp_d, not + */ + tmp_d.bd_flags |= BD_F_SB16X ; + } pcmattach(dev); } #endif /* NPNP */ diff --git a/sys/i386/isa/snd/CARDS b/sys/i386/isa/snd/CARDS index d0953578e8524..91ca79b682297 100644 --- a/sys/i386/isa/snd/CARDS +++ b/sys/i386/isa/snd/CARDS @@ -39,12 +39,20 @@ CS4232: PnP id 0x3242630e and working in playback mode in MSS emulation. Also have reports from some user that it works ok. -OPTi931: PnP id 0x3109143e +-------------------------- +CHIPSET: + OPTi931: PnP id 0x3109143e - http://www.opti.com/ opti931_21.pdf +MANUFACTURER: + ExpertColor MED931 (europe) +DATA SHEETS: + http://www.opti.com/ opti931_21.pdf + +PNP CONFIG: pnp 1 1 os enable port0 0x534 port2 0x220 port3 0xe0d irq0 10 drq0 1 drq1 6 +COMMENTS: 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. @@ -61,21 +69,45 @@ OPTi931: PnP id 0x3109143e 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. + misses interrupts, or generates spurious ones, or generates + interrupts before setting the status bits in the registers. + Spurious ints are not problematic since they can be easily + ignored, but missing interrupts are a nightmare to handle... + The only way to have this card work semi-reliably is to use + auto-dma mode (which is the default mode of operation in the + driver). + In any case, these cards are very cheap and widely available and + are a reasonable solution if you cannot find some other decent WSS + device. + +------------------------------- +CHIPSET: + SB16: PnP id 0x__008c0e + Vibra16X: PnP id 0xf0008c0e -SB16 PnP: PnP id 0x__008c0e +MANUFACTURER: + CreativeLabs - http://www.creative.com sbhwpg.pdf or SBHWPG.EXE +DATA SHEETS: + http://www.creative.com sbhwpg.pdf or SBHWPG.EXE +PNP CONFIG: pnp 1 0 os enable port0 0x220 irq0 5 drq0 1 drq1 5 - There are many such cards (plain SB16 PnP, AWE32, AWE64, Vibra16, - 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. +COMMENTS: + SB16 really refers to a large number of different cards, starting + from the original, non-PnP SB16, to more modern cards (SB16PnP, + Vibra16C) and other including Wavetable Synthesis (AWE32, AWE64, + etc.). All these cards have different 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. + + BEWARE -- all recent (1998 ?) SB16 use a new codec, Vibra16X, + which is sufficiently different from the old one to not work with + this driver in capture mode or in full duplex. Documentation is + not available from Creative. I suggest to stay away from these + cards (and from SB16 in general). 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 @@ -84,7 +116,7 @@ SB16 PnP: PnP id 0x__008c0e 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 + work, although on my Vibra16C 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 @@ -93,67 +125,111 @@ SB16 PnP: PnP id 0x__008c0e Some docs on how to use this card with the voxware driver are at http://multiverse.com/~rhh/awedrv - NOTE: 980122 I have had a couple of reports from people using - the Vibra16X (and clones, e.g. based on the Avance Logic - chipsets) which appears to use two 8-bit DMA channels. It - might be possible that new SB16 are able to do true full duplex! - The driver _does not_ support these boards although the fix - should be relatively easy. +-------------------------- +CHIPSET: + ALS100, ALS110, ... + Avance Asound 100: PnP id 0x01009305 -ASound: - Avance Logic makes SB clones, and apparently documentation - is available at +MANUFACTURER: + Realtek (also Avance Asound and possibly other names) + - http://www.realtek.com.tw/cp/cp.html +DOCUMENTATION: + + http://www.realtek.com.tw/cp/cp.html + but not very useful or detailed. - these card should be recognised as SB16 clones. +COMMENTS: + These card should be recognised as SB16 clones. Some of them + emulate the Vibra16X, so the comments above apply. -Yamaha SA2/SA3 - http://www.yamaha.com ? YM711.pdf - (this is a huge file containing a few pages scanned and converted - to pdf. Not very useful or detailed.) +------------------------------ +CHIPSET: + Yamaha SA2/SA3 . Both PnP and non-PnP versions. + OPL3-SA2 Sound Chip: PnP id 0x2000a865 + OPL/SA3 : PnP id 0x3000a865 - pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5 +MANUFACTURER: + no-name cards, and also used in various laptops, e.g. Toshiba + Libretto and others. I + +DATA SHEETS: + http://www.yamaha.com ? YM711.pdf + + This is a huge file containing a few pages scanned and converted + to pdf. Not very detailed. Luckily, this chipset appears to do a + good (i.e. bug-free) emulation of the WSS, so it is fully + supported by the driver. + + pnp 1 0 os enable port0 0x220 port1 0x530 port2 0x388 port3 0x370 irq0 5 drq0 1 drq1 0 this card emulates a WSS or SB. Have reports that it works, although it has incomplete mixer support (Yamaha defines an additional set of registers to control some mixer functions, such as the master - output volume). Currently we set the master volume to 100%. - Driver reported to work also on Toshiba DynaBook Portege 300CT - with OPL3SA-3(non-PNP) + output volume -- this is at 0x370 or 0x310). Currently we set + the master volume to 100% for the PnP card. Will put in code to do the + same for the non PnP card as soon as I find out how to tell + the Yamaha from other cards. + Driver reported to work also on + Toshiba DynaBook Portege 300CT with OPL3SA-3(non-PNP), and on the + Libretto50CT (has the non-PnP device). -Ensoniq Soundscape VIVO +------------------------ +CHIPSET: + ENSONIQ SoundscapeVIVO ENS4081: PnP id 0x8140d315 +MANUFACTURER: + Ensoniq Soundscape VIVO +PNP CONFIG: pnp 1 0 os enable port0 0x220 port1 0x530 irq0 5 drq0 1 drq1 5 +COMMENTS: this card emulates a WSS or SB. Have reports that it works. -GusPnP: PnP id 0x0100561e +------------------------- +CHIPSET: + AMD... + +MANUFACTURER: + GusPnP: PnP id 0x0100561e +PNP CONFIG: pnp 1 0 os enable port0 0x220 port1 0x320 port2 0x32c irq0 5 drq0 7 drq1 5 +COMMENTS: It works in U8 and S16 mode, ulaw support still not working. + The Gus has been discontinued, so support for this card is only + there for historical reasons (the Gus used to be the only card + well supported in full duplex mode). -NOT WORKING YET: +---- THE FOLLOWING CARDS ARE NOT FULLY SUPPORTED: ---- -OPTI925: PnP id 0x2509143e +CHIPSET: + OPTI925: PnP id 0x2509143e +COMMENTS: there is code to recognize it as a SB clone. I have reports that it probes ok, but not sure if it works. -OPTI924: PnP +------------------------- +CHIPSET: + OPTI924: PnP +COMMENT: I have this card but it is still unsupported. -OPTI930: +------------------------- +CHIPSET: + OPTI930: should work as an MSS clone, but support for it is not implemented yet. -ESS1868 -ESS688 +CHIPSET: + ESS1868 + ESS688 http://www.esstech.com @@ -168,12 +244,13 @@ ESS688 (the ESS688 is used on many notebooks. It is probably able to do 8 and 16-bit, half duplex). -PCI cards: +------------------------- +CHIPSET: + various PCI cards from Ensoniq, OPTI, CreativeLabs. - 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. + This code _cannot_ work on these cards as it is now, since I + don't think they can use the ISA DMA controller. As there are + no data sheets available for these PCI cards, none of them is + supported at the moment. +-------------------------------------------------------------------- diff --git a/sys/i386/isa/snd/README b/sys/i386/isa/snd/README index 256ae249d846e..289f0442ad2ec 100644 --- a/sys/i386/isa/snd/README +++ b/sys/i386/isa/snd/README @@ -13,15 +13,18 @@ of the Voxware ioctl() audio calls, so that many applications -- even commercial ones -- will run unmodified with this driver. On the other hand, at the moment this driver does not support /dev/midi and /dev/synth, or some ioctl() used in xquake. Do not expect -/dev/synth to be supported before summer'98. +/dev/synth to be supported anytime soon. I also have implemented a new software interface with an independent set of ioctl(), to support some functions which were not easy to express with the existing software interface (e.g. full duplex on -the SB16). To make an effective use of the new functionalities you -need to recompile applications by replacing the audio module(s). +old SB16 cards). To make an effective use of the new functionalities +you need to recompile applications by replacing the audio module(s). +Such modified driver modules are present in the misc/ directory +for several applications. -Updated versions of this code will available at the following URL: +This file gives quick information on how to install the driver. +Updated versions of this code will be available at the following URL: http://www.iet.unipi.it/~luigi/FreeBSD.html @@ -49,10 +52,18 @@ APPLICATIONS: - xanim - various mpeg players (mpg123, amp, ...); + WITH SPECIAL DRIVER MODULE (supplied) + - speak_freely, full duplex (requires removing the definition of + HALF_DUPLEX in the Makefile); + - the realaudio player (3.0, dynamically linked); + - vat, full duplex (the version in ports is already modified); + - timidity, a software midi-to-pcm converter; + NOT WORKING - xquake (we do not support mmapped buffers yet); - ---INSTALLATION--- + + ---INSTALLATION--- * add the following lines to your kernel configuration file: @@ -108,14 +119,14 @@ APPLICATIONS: "flags 0x13" specifies that you have a dual dma board with channel 3 as secondary DMA channel. - * build the kernel using the following steps + * build the kernel using the standard steps config MYKERNEL cd /sys/compile/MYKERNEL make depend make - * PnP support: + * note for PnP cards: For PnP cards, only the line for "pcm0" is needed (the code will allocate entries for more cards if found), but IT MUST @@ -145,8 +156,8 @@ APPLICATIONS: WHAT IF THIS DRIVER DOES NOT WORK: -If the driver does not work with your hardware, I am willing to -help but I need the following: +If the driver does not work with your hardware, I might to help +but I need the following information: - relevant lines in your config file; - dmesg output @@ -200,8 +211,8 @@ this driver. In particular I would like to thank: * Jim Lowe for his suggestion on the block-mode select; * Allison Mankin and Brad Karp at ISI-East for supplying a GUS PnP which allowed me to support this card; - * Eric J. Schwertfeger for donating sn ES1868 card for writing the - driver. + * Eric J. Schwertfeger for donating an ES1868 card for writing the + driver (which i haven't done yet...). * and many people who had the patience to try the driver on their cards and report success/fauilure and useful information. diff --git a/sys/i386/isa/snd/ad1848.c b/sys/i386/isa/snd/ad1848.c index 095d83f0bccf9..326c5863f01ad 100644 --- a/sys/i386/isa/snd/ad1848.c +++ b/sys/i386/isa/snd/ad1848.c @@ -4,7 +4,7 @@ * Driver for Microsoft Sound System/Windows Sound System (mss) * -compatible boards. This includes: * - * AD1848, CS4248, CS423x, OPTi931, Yamaha SA2 and many others. + * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others. * * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 @@ -239,8 +239,32 @@ mss_attach(struct isa_device *dev) outb(dev->id_iobase, bits ); } } + if (1) { /* machine-specific code for the Toshiba Libretto */ + u_char r6, r9; + outb( 0x370, 6 /* dma config */ ); + outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */); + r6 = inb( 0x371 /* read */ ); + outb( 0x370, 0xa /* version */ ); + r9 = inb( 0x371 /* read */ ); + DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) + /* + * yamaha - set volume to max + */ + outb( 0x370, 7 /* volume left */ ); + outb( 0x371, 0 /* max level */ ); + outb( 0x370, 8 /* volume right */ ); + outb( 0x371, 0 /* max level */ ); + } if ( FULL_DUPLEX(d) ) d->audio_fmt |= AFMT_FULLDUPLEX ; + if (d->bd_id == MD_YM0020) { + DDB(printf("setting up yamaha registers\n")); + outb(0x370, 6 /* dma config */ ) ; + if (FULL_DUPLEX(d)) + outb(0x371, 0xa9 ); /* use both dma chans */ + else + outb(0x371, 0x8b ); /* use low dma chan */ + } mss_reinit(d); ad1848_mixer_reset(d); return 0; @@ -401,7 +425,7 @@ mss_callback(snddev_info *d, int reason) /* * perform all necessary initializations for i/o */ - d->rec_fmt = d->play_fmt ; /* no split format on the WSS */ + d->rec_fmt = d->play_fmt ; /* no split format on the MSS */ snd_set_blocksize(d); mss_reinit(d); reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); @@ -561,15 +585,21 @@ again: if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, masked 0x%02x\n", mc11, masked); masked |= mc11 ; + /* + * the nice OPTi931 sets the IRQ line before setting the bits in + * mc11. So, on some occasions I have to retry (max 10 times). + */ if ( mc11 == 0 ) { /* perhaps can return ... */ reason = inb(io_Status(d)); if (reason & 1) { - printf("one more try...\n"); - goto again; + DEB(printf("one more try...\n");) + if (--loops) + goto again; + else + DDB(printf("opti_intr: irq but mc11 not set!...\n");) } - if (loops==10) { + if (loops==10) printf("ouch, intr but nothing in mcir11 0x%02x\n", mc11); - } return; } @@ -580,7 +610,8 @@ again: dsp_wrintr(d); } opti_write(d->conf_base, 11, ~mc11); /* ack */ - if (--loops) goto again; + if (--loops) + goto again; DEB(printf("xxx too many loops\n");) } @@ -657,7 +688,7 @@ ad_read(snddev_info *d, int reg) int x; flags = spltty(); - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 201); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; x = inb(io_Indexed_Data(d)); @@ -672,7 +703,7 @@ ad_write(snddev_info *d, int reg, u_char data) int x ; flags = spltty(); - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 1002); x = inb(io_Index_Addr(d)) & ~IA_AMASK ; outb(io_Index_Addr(d), (u_char) (reg & IA_AMASK) | x ) ; outb(io_Indexed_Data(d), data); @@ -731,7 +762,7 @@ ad_enter_MCE(snddev_info *d) int prev; d->bd_flags |= BD_F_MCE_BIT; - AD_WAIT_INIT(d, 100); + AD_WAIT_INIT(d, 203); prev = inb(io_Index_Addr(d)); prev &= ~IA_TRD ; outb(io_Index_Addr(d), prev | IA_MCE ) ; @@ -853,10 +884,11 @@ mss_mixer_set(snddev_info *d, int dev, int value) d->mix_levels[dev] = left | (right << 8); +#if 0 /* Scale volumes */ left = mix_cvt[left]; right = mix_cvt[right]; - +#endif /* * Set the left channel */ @@ -919,6 +951,16 @@ ad1848_mixer_reset(snddev_info *d) ad_write(d, 20, 0x88); ad_write(d, 21, 0x88); break; + + case MD_YM0020: + /* set master volume to max */ + DDB(printf("set yamaha master volume to max"); ) + outb(0x370, 7) ; + outb(0x371, 0) ; + outb(0x370, 8) ; + outb(0x371, 0) ; + break; + case MD_GUSPNP: /* this is only necessary in mode 3 ... */ ad_write(d, 22, 0x88); @@ -997,7 +1039,7 @@ mss_format(snddev_info *d) arg = AFMT_U8 ; /* ulaw/alaw seems broken on the opti931... */ - if (d->bd_id == MD_OPTI931) { + if (d->bd_id == MD_OPTI931 || d->bd_id == MD_GUSPNP) { if (arg == AFMT_MU_LAW) { arg = AFMT_U8 ; d->flags |= SND_F_XLAT8 ; @@ -1233,6 +1275,13 @@ mss_detect(struct isa_device *dev) d->bd_id = MD_AD1845; } ad_write(d, 23, tmp); /* Restore */ + DDB(printf("... try to identify the yamaha\n") ;) + tmp = inb(0x370) ; + outb(0x370, 6 /* dma config */ ) ; + if (inb(0x370) != 6 ) /* not a yamaha... restore. */ + outb(0x370, tmp ) ; + else + d->bd_id = MD_YM0020 ; break; case 0x83: /* CS4236 */ @@ -1244,7 +1293,6 @@ mss_detect(struct isa_device *dev) default: /* Assume CS4231 */ BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) d->bd_id = MD_CS4231; - } } ad_write(d, 25, tmp1); /* Restore bits */ @@ -1354,7 +1402,7 @@ cs423x_probe(u_long csn, u_long vend_id) u_long id = vend_id & 0xff00ffff; if ( id == 0x3700630e ) s = "CS4237" ; - else if ( id == 0x3600630e ) + else if ( id == 0x3500630e || id == 0x3600630e ) s = "CS4236" ; else if ( id == 0x3500630e ) s = "CS4236B" ; @@ -1364,7 +1412,7 @@ cs423x_probe(u_long csn, u_long vend_id) s = "Yamaha SA2"; else if ( id == 0x3000a865) s = "Yamaha SA3"; - else if ( id == 0x0000a865) + else if (vend_id == 0x0000a865) s = "Yamaha YMF719 OPL-SA3"; else if (vend_id == 0x8140d315) s = "SoundscapeVIVO"; @@ -1399,7 +1447,8 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/ dev->id_alive = 16 ; /* number of io ports ? */ tmp_d = sb_op_desc ; - if (vend_id == 0x0008a865 || vend_id==0x2000a865 || vend_id==0x3000a865 || vend_id==0x8140d315) { + if (vend_id==0x2000a865 || vend_id==0x3000a865 || + vend_id==0x0008a865 || vend_id==0x8140d315) { /* Yamaha SA2/SA3 or ENSONIQ SoundscapeVIVO ENS4081 */ dev->id_iobase = d.port[0] ; tmp_d.alt_base = d.port[1] ; @@ -1418,7 +1467,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, case 0x2000a865: /* Yamaha SA2 */ case 0x3000a865: /* Yamaha SA3 */ - case 0x0000a865: /* Yamaha YMF719 */ + case 0x0000a865: /* Yamaha TMF719 SA3 */ dev->id_iobase = d.port[1]; tmp_d.alt_base = d.port[0]; tmp_d.conf_base = d.port[4]; @@ -1436,6 +1485,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, tmp_d.bd_id = MD_CS4237 ; break; + case 0x3500630e: /* CS4236 */ case 0x3600630e: /* CS4236 */ case 0x3500630e: /* CS4236B */ tmp_d.bd_id = MD_CS4236 ; @@ -1451,7 +1501,7 @@ cs423x_attach(u_long csn, u_long vend_id, char *name, write_pnp_parms( &d, ldn ); enable_pnp_card(); - if ( (vend_id & 0x2000ffff) == 0x2000a865 ) { + if ( (vend_id & 0x0000ffff) == 0x0000a865 ) { /* special volume setting for the Yamaha... */ outb(tmp_d.conf_base, 7 /* volume, left */); outb(tmp_d.conf_base+1, 0 ); @@ -1542,10 +1592,10 @@ opti931_attach(u_long csn, u_long vend_id, char *name, if (d.flags & DV_PNP_SBCODEC) { /* sb-compatible codec */ /* * the 931 is not a real SB, it has important pieces of - * hardware controlled by both the WSS and the SB port... + * hardware controlled by both the MSS and the SB port... */ printf("--- opti931 in sb mode ---\n"); - opti_write(p, 6, 1); /* MCIR6 wss disable, sb enable */ + opti_write(p, 6, 1); /* MCIR6 mss disable, sb enable */ /* * swap the main and alternate iobase address since we want * to work in sb mode. @@ -1555,7 +1605,7 @@ opti931_attach(u_long csn, u_long vend_id, char *name, dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; } else { /* mss-compatible codec */ tmp_d.bd_id = MD_OPTI931 ; /* to short-circuit the detect routine */ - opti_write(p, 6 , 2); /* MCIR6: wss enable, sb disable */ + opti_write(p, 6 , 2); /* MCIR6: mss enable, sb disable */ opti_write(p, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; /* not really well... */ @@ -1567,6 +1617,76 @@ opti931_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } +static char *opti925_probe(u_long csn, u_long vend_id); +static void opti925_attach(u_long csn, u_long vend_id, char *name, + struct isa_device *dev); + +static struct pnp_device opti925 = { + "opti925", + opti925_probe, + opti925_attach, + &nsnd, /* use this for all sound cards */ + &tty_imask /* imask */ +}; +DATA_SET (pnpdevice_set, opti925); + +static char * +opti925_probe(u_long csn, u_long vend_id) +{ + if (vend_id == 0x2509143e) { + struct pnp_cinfo d ; + read_pnp_parms ( &d , 1 ) ; + if (d.enable == 0) { + printf("This is an OPTi925, but LDN 1 is disabled\n"); + return NULL; + } + return "OPTi925" ; + } + return NULL ; +} + +static void +opti925_attach(u_long csn, u_long vend_id, char *name, + struct isa_device *dev) +{ + struct pnp_cinfo d ; + snddev_info tmp_d ; /* patched copy of the basic snddev_info */ + int the_irq = 0 ; + + tmp_d = mss_op_desc; + snddev_last_probed = &tmp_d; + + read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ + the_irq = d.irq[0]; + d.port[0] = 0 ; + d.enable = 0 ; + write_pnp_parms ( &d , 3 ); + + read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ + d.port[0] = 0 ; + d.enable = 0 ; + write_pnp_parms ( &d , 2 ); + + read_pnp_parms ( &d , 1 ) ; + d.irq[0] = the_irq ; + dev->id_iobase = d.port[1]; + tmp_d.alt_base = d.port[0]; + write_pnp_parms ( &d , 1 ); + enable_pnp_card(); + + tmp_d.conf_base = d.port[3]; + + dev->id_drq = d.drq[0] ; /* primary dma */ + dev->id_irq = (1 << d.irq[0] ) ; + dev->id_intr = pcmintr ; + dev->id_flags = DV_F_DUAL_DMA | d.drq[1] ; + tmp_d.audio_fmt |= AFMT_FULLDUPLEX ; + + snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + + pcmattach(dev); +} + static void gus_mem_cfg(snddev_info *tmp); static char *guspnp_probe(u_long csn, u_long vend_id); diff --git a/sys/i386/isa/snd/dmabuf.c b/sys/i386/isa/snd/dmabuf.c index a19ccc48500e5..c69435c2cd5d4 100644 --- a/sys/i386/isa/snd/dmabuf.c +++ b/sys/i386/isa/snd/dmabuf.c @@ -184,7 +184,9 @@ dsp_wrintr(snddev_info *d) /* 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)); - d->callback(d, SND_CB_WR | SND_CB_STOP ); + DEB(printf("wrintr: dl %d -> %d\n", b->dl, l);) + if (b->dl != 0) + d->callback(d, SND_CB_WR | SND_CB_STOP ); /* * at high speed, it might well be that the count * changes in the meantime. So we try to update b->rl @@ -270,10 +272,10 @@ dsp_write_body(snddev_info *d, struct uio *buf) else timeout = 1 ; ret = tsleep( (caddr_t)b, PRIBIO|PCATCH, "dspwr", timeout); - if (ret == EINTR || ret == ERESTART) + if (ret == EINTR) d->flags |= SND_F_ABORTING ; splx(s); - if (ret == EINTR) + if (ret == EINTR || ret == ERESTART) break ; continue; } @@ -457,10 +459,12 @@ dsp_rdintr(snddev_info *d) ( FULL_DUPLEX(d) || (d->flags & SND_F_WRITING) == 0 ) ) { int l = min(b->fl - 0x100, d->rec_blocksize); l &= DMA_ALIGN_MASK ; /* realign sizes */ + DEB(printf("rdintr: dl %d -> %d\n", b->dl, l);) if (l != b->dl) { /* for any reason, size has changed. Stop and restart */ + if (b->dl > 0 ) + d->callback(d, SND_CB_RD | SND_CB_STOP ); b->dl = l ; - d->callback(d, SND_CB_RD | SND_CB_STOP ); d->callback(d, SND_CB_RD | SND_CB_START ); } } else { @@ -553,10 +557,10 @@ dsp_read_body(snddev_info *d, struct uio *buf) else timeout = 1; /* maybe data will be ready earlier */ ret = tsleep( (caddr_t)b, PRIBIO | PCATCH , "dsprd", timeout ) ; - if (ret == EINTR || ret == ERESTART) + if (ret == EINTR) d->flags |= SND_F_ABORTING ; splx(s); - if (ret == EINTR) + if (ret == EINTR || ret == ERESTART) break ; continue; } @@ -593,7 +597,7 @@ dsp_read_body(snddev_info *d, struct uio *buf) s = spltty(); /* no interrupts here ... */ d->flags &= ~SND_F_READING ; if (d->flags & SND_F_ABORTING) { - d->flags |= ~SND_F_ABORTING; + d->flags &= ~SND_F_ABORTING; /* XXX */ splx(s); dsp_rdabort(d, 1 /* restart */); /* XXX return EINTR ? */ @@ -761,7 +765,8 @@ snd_flush(snddev_info *d) return -1 ; } if ( ret && --count == 0) { - printf("timeout flushing dbuf_out.chan, cnt 0x%x flags 0x%08lx\n", + printf("timeout flushing dbuf_out, chan %d cnt 0x%x flags 0x%08lx\n", + b->chan, b->rl, d->flags); break; } diff --git a/sys/i386/isa/snd/mss.h b/sys/i386/isa/snd/mss.h index 994f323c2bef5..54efc3cc21950 100644 --- a/sys/i386/isa/snd/mss.h +++ b/sys/i386/isa/snd/mss.h @@ -150,7 +150,7 @@ ahead. /* * Table of mixer registers. There is a default table for the - * AD1848/CS423x clones, and one for the OPTI931. As more WSS + * AD1848/CS423x clones, and one for the OPTI931. As more MSS * clones come out, there ought to be more tables. * * Fields in the table are : polarity, register, offset, bits diff --git a/sys/i386/isa/snd/sb_dsp.c b/sys/i386/isa/snd/sb_dsp.c index 575980cc8821b..349a7e2102138 100644 --- a/sys/i386/isa/snd/sb_dsp.c +++ b/sys/i386/isa/snd/sb_dsp.c @@ -3,7 +3,7 @@ * * driver for the SoundBlaster and clones. * - * Copyright 1997 Luigi Rizzo. + * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright @@ -74,6 +74,8 @@ static int dsp_speed(snddev_info *d); static void sb_mixer_reset(snddev_info *d); u_int sb_get_byte(int io_base); +int ess_write(int io_base, u_char reg, int val); +int ess_read(int io_base, u_char reg); /* * Then put here the descriptors for the various boards supported @@ -151,6 +153,12 @@ sb_attach(struct isa_device *dev) * here are the main routines from the switches. */ +/* + * Unlike MSS, the sb only supports a single open (does not mean + * that only a single process is using it, since it can fork + * afterwards, or pass the descriptor to another process). + * + */ static int sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) { @@ -196,10 +204,21 @@ sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p) d->play_fmt = d->rec_fmt = AFMT_U8 ; break ; } - if ( (flags & FREAD) == 0) + /* + * since the SB is not simmetric, I use the open mode to select + * which channel should be privileged, and disable I/O in the + * other direction. + * In case the board is opened RW, we don't have enough + * information on what to do. Temporarily, privilege the + * playback channel, which is used more often, and set the other + * one to U8. + */ + if ( (flags & FREAD) == 0) /* opened write only */ d->rec_fmt = 0 ; - if ( (flags & FWRITE) == 0) + else if ( (flags & FWRITE) == 0) /* opened read only */ d->play_fmt = 0 ; + else /* opened read/write */ + d->rec_fmt = (d->play_fmt == AFMT_S16_LE) ? AFMT_U8 : AFMT_S16_LE ; d->flags |= SND_F_BUSY ; d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; @@ -275,7 +294,10 @@ sb_intr(int unit) * SB < 4.0 is half duplex and has only 1 bit for int source, * so we fake it. SB 4.x (SB16) has the int source in a separate * register. + * The Vibra16X has separate flags for 8 and 16 bit transfers, but + * I have no idea how to tell capture from playback interrupts... */ +#define PLAIN_SB16(x) ( ( (x) & (BD_F_SB16|BD_F_SB16X) ) == BD_F_SB16) again: if (d->bd_flags & BD_F_SB16) { c = sb_getmixer(io_base, IRQ_STAT); @@ -284,25 +306,26 @@ again: */ reason = 0 ; if ( c & 1 ) { /* 8-bit dma */ - if (d->dbuf_out.chan < 4) + if (d->play_fmt == AFMT_U8 || d->play_fmt == AFMT_MU_LAW ) reason |= 1; - if (d->dbuf_in.chan < 4) + if (d->rec_fmt == AFMT_U8 || d->rec_fmt == AFMT_MU_LAW ) reason |= 2; } if ( c & 2 ) { /* 16-bit dma */ - if (d->dbuf_out.chan >= 4) + if (d->play_fmt == AFMT_S16_LE) reason |= 1; - if (d->dbuf_in.chan >= 4) + if (d->rec_fmt == AFMT_S16_LE) reason |= 2; } } /* XXX previous location of ack... */ - DEB(printf("sb_intr, flags 0x%08lx reason %d\n", d->flags, reason)); + DEB(printf("sb_intr, flags 0x%08lx reason %d c 0x%x\n", + d->flags, reason, c)); if ( reason & 1 ) { /* possibly a write interrupt */ if ( d->dbuf_out.dl ) dsp_wrintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: wrintr but write DMA inactive!\n"); } } @@ -310,7 +333,7 @@ again: if ( d->dbuf_in.dl ) dsp_rdintr(d); else { - if (d->bd_flags & BD_F_SB16) + if (PLAIN_SB16(d->bd_flags)) printf("WARNING: rdintr but read DMA inactive!\n"); } } @@ -346,20 +369,37 @@ sb_callback(snddev_info *d, int reason) switch (reason & SND_CB_REASON_MASK) { case SND_CB_INIT : /* called with int enabled and no pending io */ + /* + * set the speed + */ dsp_speed(d); + /* + * set the desired DMA blocksize (influences select behaviour) + */ snd_set_blocksize(d); + /* + * since native mulaw is not present, emulate it. + */ if ( (d->play_fmt & AFMT_MU_LAW) || (d->rec_fmt & AFMT_MU_LAW) ) d->flags |= SND_F_XLAT8 ; else d->flags &= ~SND_F_XLAT8 ; - if (d->bd_flags & BD_F_SB16) { + /* + * there are too many flavours of SB for my taste... here i try to do + * the proper initialization for each one. + */ + if (PLAIN_SB16(d->bd_flags)) { u_char c, c1 ; - /* the SB16 can do full duplex using one 16-bit channel + /* the original SB16 (non-PnP, or PnP, or Vibra16C) + * 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: + * I DON'T do this for the Vibra16X because I have no idea + * of what needs to be done there... + * + * I 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. @@ -379,7 +419,11 @@ sb_callback(snddev_info *d, int reason) if ( d->play_fmt != AFMT_S16_LE && d->dbuf_out.chan < 4 ) swap = 0; if ( d->rec_fmt ) { - /* check for possible config errors. */ + /* check for possible config errors. + * This cannot happen at open time since even in + * case of opening rw we privilege the play + * channel. + */ if (d->rec_fmt == d->play_fmt) { DDB(printf("sorry, read DMA channel unavailable\n")); } @@ -392,6 +436,29 @@ sb_callback(snddev_info *d, int reason) d->dbuf_in.chan = d->dbuf_out.chan; d->dbuf_out.chan = c ; } + } else if (d->bd_flags & BD_F_ESS) { + u_char c ; + if (d->play_fmt == 0) { + /* initialize for record */ + static u_char cmd[] = { + 0x51,0xd0,0x71,0xf4,0x51,0x98,0x71,0xbc + }; + ess_write(d->io_base, 0xb8, 0x0e); + c = ( ess_read(d->io_base, 0xa8) & 0xfc ) | 1 ; + if (d->flags & SND_F_STEREO) + c++ ; + ess_write(d->io_base, 0xa8, c); + ess_write(d->io_base, 0xb9, 2); /* 4bytes/transfer */ + /* + * set format in b6, b7 + */ + } else { + /* initialize for play */ + static u_char cmd[] = { + 0x80,0x51,0xd0,0x00,0x71,0xf4, + 0x80,0x51,0x98,0x00,0x71,0xbc + }; + } } reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); @@ -400,13 +467,45 @@ sb_callback(snddev_info *d, int reason) case SND_CB_START : /* called with int disabled */ if (d->bd_flags & BD_F_SB16) { u_char c, c1 ; + + if (d->bd_flags & BD_F_SB16X) { + /* just a guess: on the Vibra16X, the first + * op started takes the first dma channel, + * the second one takes the next... + * The default is to be ready for play. + */ + int swap = 0 ; + DEB(printf("start %s -- now dma %d:%d\n", + rd ? "rd" : "wr", + d->dbuf_out.chan, d->dbuf_in.chan);); + /* swap only if both channels are idle + * play: dl=0, since there is no pause; + * rec: rl=0 + */ + if ( rd && d->dbuf_out.dl == 0 && d->dbuf_in.rl == 0 ) { + /* must swap channels, but also save dl */ + int c = d->dbuf_in.chan ; + int dl = d->dbuf_in.dl ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + d->dbuf_in.dl = dl ; + printf("swapped -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } + /* * XXX note: c1 and l should be set basing on d->rec_fmt, * but there is no choice once a 16 or 8-bit channel * is assigned. This means that if the application * tries to use a bad format, the sound will not be nice. */ - if ( b->chan > 4 ) { + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) { c = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_DMA16 ; c1 = DSP_F16_SIGNED ; l /= 2 ; @@ -455,7 +554,10 @@ sb_callback(snddev_info *d, int reason) case SND_CB_STOP : { int cmd = DSP_CMD_DMAPAUSE_8 ; /* default: halt 8 bit chan */ - if ( d->bd_flags & BD_F_SB16 && b->chan > 4 ) + if ( b->chan > 4 + || (rd && d->rec_fmt == AFMT_S16_LE) + || (!rd && d->play_fmt == AFMT_S16_LE) + ) cmd = DSP_CMD_DMAPAUSE_16 ; if (d->bd_flags & BD_F_HISPEED) { sb_reset_dsp(d->io_base); @@ -474,6 +576,22 @@ sb_callback(snddev_info *d, int reason) sb_cmd(d->io_base, cmd == DSP_CMD_DMAPAUSE_8 ? 0xd6 : 0xd4); /* continue other dma */ } + if (d->bd_flags & BD_F_SB16X) { + /* restore possible swapped channels. + * The default is to be ready for play. + * XXX right now, it kills all input on overflow + */ + if ( rd && d->dbuf_out.dl == 0 ) { + /* must swap channels ? */ + int c = d->dbuf_in.chan ; + d->dbuf_in.chan = d->dbuf_out.chan; + d->dbuf_out.chan = c ; + reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); + reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); + printf("restored -- now dma %d:%d\n", + d->dbuf_out.chan, d->dbuf_in.chan); + } + } } DEB( sb_cmd(d->io_base, DSP_CMD_SPKOFF) ); /* speaker off */ break ; @@ -618,6 +736,7 @@ sb_dsp_init(snddev_info *d, struct isa_device *dev) printf("ESS1868 (rev %d)\n", rev); else printf("ESS688 (rev %d)\n", rev); + /* d->audio_fmt |= AFMT_S16_LE; */ /* not yet... */ break ; /* XXX */ } else { printf("Unknown card 0x%x 0x%x -- hope it is SBPRO\n", @@ -660,6 +779,14 @@ sb_mix_init(snddev_info *d) /* * Common code for the midi and pcm functions + * + * sb_cmd write a single byte to the CMD port. + * sb_cmd2 write a CMD + 1 byte arg + * sb_cmd3 write a CMD + 2 byte arg + * sb_get_byte returns a single byte from the DSP data port + * + * ess_write is actually sb_cmd2 + * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte */ int @@ -725,9 +852,9 @@ sb_getmixer(int io_base, u_int port) u_long flags; flags = spltty(); - outb(io_base + 4, (u_char) (port & 0xff)); /* Select register */ + outb(io_base + SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); - val = inb(io_base + 5); + val = inb(io_base + SB_MIX_DATA); DELAY(10); splx(flags); @@ -747,6 +874,19 @@ sb_get_byte(int io_base) return 0xffff; } +int +ess_write(int io_base, u_char reg, int val) +{ + return sb_cmd2(io_base, reg, val); +} + +int +ess_read(int io_base, u_char reg) +{ + if (!sb_cmd(io_base, 0xc0) || !sb_cmd(io_base, reg) ) + return 0xffff ; + return sb_get_byte(io_base); +} /* @@ -785,17 +925,21 @@ dsp_speed(snddev_info *d) */ if (d->bd_flags & BD_F_ESS) { int t; - RANGE (speed, 4000, 48000); + RANGE (speed, 5000, 49000); if (speed > 22000) { t = (795500 + speed / 2) / speed; speed = (795500 + t / 2) / t ; - t = ( 256 - (795500 + speed / 2) / speed ) | 0x80 ; + t = (256 - t ) | 0x80 ; } else { t = (397700 + speed / 2) / speed; speed = (397700 + t / 2) / t ; - t = 128 - (397700 + speed / 2) / speed ; + t = 128 - t ; } - sb_cmd2(d->io_base, 0xa1, t); /* set time constant */ + ess_write(d->io_base, 0xa1, t); /* set time constant */ + d->play_speed = d->rec_speed = speed ; + speed = (speed * 9 ) / 20 ; + t = 256-7160000/(speed*82); + ess_write(d->io_base,0xa2,t); return speed ; } @@ -1080,79 +1224,11 @@ ess1868_attach(u_long csn, u_long vend_id, char *name, pcmattach(dev); } -static char *opti925_probe(u_long csn, u_long vend_id); -static void opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev); - -static struct pnp_device opti925 = { - "opti925", - opti925_probe, - opti925_attach, - &nsnd, /* use this for all sound cards */ - &tty_imask /* imask */ -}; -DATA_SET (pnpdevice_set, opti925); - -static char * -opti925_probe(u_long csn, u_long vend_id) -{ - if (vend_id == 0x2509143e) { - struct pnp_cinfo d ; - read_pnp_parms ( &d , 1 ) ; - if (d.enable == 0) { - printf("This is an OPTi925, but LDN 1 is disabled\n"); - return NULL; - } - return "OPTi925" ; - } - return NULL ; -} - -static void -opti925_attach(u_long csn, u_long vend_id, char *name, - struct isa_device *dev) -{ - struct pnp_cinfo d ; - snddev_info tmp_d ; /* patched copy of the basic snddev_info */ - int the_irq = 0 ; - - tmp_d = sb_op_desc; - snddev_last_probed = &tmp_d; - - read_pnp_parms ( &d , 3 ); /* disable LDN 3 */ - the_irq = d.irq[0]; - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 3 ); - - read_pnp_parms ( &d , 2 ); /* disable LDN 2 */ - d.port[0] = 0 ; - d.enable = 0 ; - write_pnp_parms ( &d , 2 ); - - read_pnp_parms ( &d , 1 ) ; - d.irq[0] = the_irq ; - dev->id_iobase = d.port[0]; - write_pnp_parms ( &d , 1 ); - enable_pnp_card(); - - tmp_d.conf_base = d.port[3]; - - dev->id_drq = d.drq[0] ; /* primary dma */ - dev->id_irq = (1 << d.irq[0] ) ; - dev->id_intr = pcmintr ; - dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - - snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ - - pcmattach(dev); - -} - /* * A driver for some SB16pnp and compatibles... * * Avance Asound 100 -- 0x01009305 + * Avance Logic ALS100+ -- 0x10019305 * xxx -- 0x2b008c0e * */ @@ -1181,11 +1257,16 @@ sb16pnp_probe(u_long csn, u_long vend_id) * Reported values are: * SB16 Value PnP: 0x2b008c0e * SB AWExx PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e + * Vibra16X: 0xf0008c0e */ - if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) + if (vend_id == 0xf0008c0e) + s = "Vibra16X" ; + else if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) ) s = "SB16 PnP"; else if (vend_id == 0x01009305) s = "Avance Asound 100" ; + else if (vend_id == 0x10019305) + s = "Avance Logic 100+" ; /* Vibra16X-class */ if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); @@ -1220,9 +1301,16 @@ sb16pnp_attach(u_long csn, u_long vend_id, char *name, dev->id_intr = pcmintr ; dev->id_flags = DV_F_DUAL_DMA | (d.drq[1] ) ; - pcm_info[dev->id_unit] = tmp_d; + pcm_info[dev->id_unit] = tmp_d; /* pcm_info[] will be reinitialized after */ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */ + if (vend_id == 0x10019305 || vend_id == 0xf0008c0e) { + /* + * XXX please add here the vend_id for other vibra16X cards... + * And remember, must change tmp_d, not + */ + tmp_d.bd_flags |= BD_F_SB16X ; + } pcmattach(dev); } #endif /* NPNP */ diff --git a/sys/i386/isa/snd/sbcard.h b/sys/i386/isa/snd/sbcard.h index 0bd42102d715b..92ec966107911 100644 --- a/sys/i386/isa/snd/sbcard.h +++ b/sys/i386/isa/snd/sbcard.h @@ -19,9 +19,9 @@ extern int sbc_major, sbc_minor ; #define DSP_DATA_AVAIL (io_base + 0xE) #define DSP_DATA_AVL16 (io_base + 0xF) +#define SB_MIX_ADDR 0x4 +#define SB_MIX_DATA 0x5 #if 0 -#define MIXER_ADDR (io_base + 0x4) -#define MIXER_DATA (io_base + 0x5) #define OPL3_LEFT (io_base + 0x0) #define OPL3_RIGHT (io_base + 0x2) #define OPL3_BOTH (io_base + 0x8) @@ -138,9 +138,14 @@ extern int sbc_major, sbc_minor ; #define BD_F_MIX_CT1745 0x0030 /* CT1745 */ #define BD_F_SB16 0x0100 /* this is a SB16 */ -#define BD_F_NOREC 0x0200 /* recording not supported on this board */ +#define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */ #define BD_F_MIDIBUSY 0x0400 /* midi busy */ #define BD_F_ESS 0x0800 /* this is an ESS chip */ +/* + * on some SB16 cards, at times I swap DMA channels. Remember this + * so that they can be restored later. + */ +#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */ /* diff --git a/sys/i386/isa/snd/sound.c b/sys/i386/isa/snd/sound.c index 51ea2f761b261..ef724cd5121b3 100644 --- a/sys/i386/isa/snd/sound.c +++ b/sys/i386/isa/snd/sound.c @@ -57,10 +57,10 @@ #include <sys/devfsext.h> #endif /* DEVFS */ -#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */ -extern struct isa_driver pcmdriver; +#if NPCM > 0 /* from "pcm.h" via disgusting #include in snd/sound.h */ +extern struct isa_driver pcmdriver ; #define SNDSTAT_BUF_SIZE 4000 static char status_buf[SNDSTAT_BUF_SIZE] ; static int status_len = 0 ; @@ -92,6 +92,75 @@ snddev_info synth_info[NPCM_MAX] ; u_long nsnd = NPCM ; /* total number of sound devices */ /* + * Hooks for APM support, but code not operational yet. + */ + +#include "apm.h" +#include <i386/include/apm_bios.h> +#if NAPM > 0 + +static int +sound_suspend(void *arg) +{ + /* + * I think i can safely do nothing here and + * reserve all the work for wakeup time + */ + printf("Called APM sound suspend hook for unit %d\n", (int)arg); + return 0 ; +} + +static int +sound_resume(void *arg) +{ + snddev_info *d = NULL ; + + d = &pcm_info[(int)arg] ; + /* + * reinitialize card registers. + * Flush buffers and reinitialize DMA channels. + * If a write was pending, pretend it is done + * (and issue any wakeup we need). + * If a read is pending, restart it. + */ + if (d->bd_id == MD_YM0020) { + DDB(printf("setting up yamaha registers\n")); + outb(0x370, 6 /* dma config */ ) ; + if (FULL_DUPLEX(d)) + outb(0x371, 0xa9 ); /* use both dma chans */ + else + outb(0x371, 0x8b ); /* use low dma chan */ + } + printf("Called APM sound resume hook for unit %d\n", (int)arg); + return 0 ; +} + +static void +init_sound_apm(int unit) +{ + struct apmhook *ap; + + ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT); + bzero(ap, sizeof *ap); + + ap->ah_fun = sound_resume; + ap->ah_arg = (void *)unit; + ap->ah_name = "pcm resume handler"; + ap->ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_RESUME, ap); + + ap = malloc(sizeof *ap, M_DEVBUF, M_NOWAIT); + bzero(ap, sizeof *ap); + + ap->ah_fun = sound_suspend; + ap->ah_arg = (void *)unit; + ap->ah_name = "pcm suspend handler"; + ap->ah_order = APM_MID_ORDER; + apm_hook_establish(APM_HOOK_SUSPEND, ap); +} +#endif /* NAPM */ + +/* * the probe routine can only return an int to the upper layer. Hence, * it leaves the pointer to the last successfully * probed device descriptor in snddev_last_probed @@ -121,7 +190,7 @@ static snddev_info *sb_devs[] = { /* all SB clones */ NULL, } ; -static snddev_info *mss_devs[] = { /* all WSS clones */ +static snddev_info *mss_devs[] = { /* all MSS clones */ &mss_op_desc, NULL, } ; @@ -194,7 +263,10 @@ pcmattach(struct isa_device * dev) d->dbuf_in.chan = dev->id_flags & DV_F_DRQ_MASK ; else d->dbuf_in.chan = d->dbuf_out.chan ; - /* XXX should also set bd_id from flags ? */ +#if 1 /* does this cause trouble with PnP cards ? */ + if (d->bd_id == 0) + d->bd_id = (dev->id_flags & DV_F_DEV_MASK) >> DV_F_DEV_SHIFT ; +#endif d->status_ptr = 0; /* @@ -224,7 +296,11 @@ pcmattach(struct isa_device * dev) cdevsw_add(&isadev, &snd_cdevsw, NULL); #ifdef DEVFS -#define GID_SND GID_GAMES +#ifndef GID_GAMES +#define GID_SND UID_ROOT +#else +#define GID_SND GID_GAMES /* i am not really sure this is a good one. */ +#endif #define UID_SND UID_ROOT #define PERM_SND 0660 /* @@ -294,6 +370,9 @@ pcmattach(struct isa_device * dev) #endif snddev_last_probed = NULL ; +#if NAPM > 0 + init_sound_apm(dev->id_unit); +#endif return stat ; } @@ -738,7 +817,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) snd_chan_param *p = (snd_chan_param *)arg; d->play_speed = p->play_rate; d->rec_speed = p->play_rate; /* XXX one speed allowed */ - if (p->play_format & SND_F_STEREO) + if (p->play_format & AFMT_STEREO) d->flags |= SND_F_STEREO ; else d->flags &= ~SND_F_STEREO ; @@ -805,7 +884,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) break; case FIOASYNC: /*set/clear async i/o */ - printf("FIOASYNC\n"); + DEB( printf("FIOASYNC\n") ; ) break; case SNDCTL_DSP_NONBLOCK : @@ -1055,7 +1134,7 @@ sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) * really sndpoll. Second arg for poll is not "rw" but "events" */ int -sndselect(dev_t i_dev, int rw, struct proc *p) +sndselect(dev_t i_dev, int rw, struct proc * p) { int dev, unit, c = 1 /* default: success */ ; snddev_info *d ; @@ -1220,7 +1299,7 @@ init_status(snddev_info *d) if (status_len != 0) /* only do init once */ return ; sprintf(status_buf, - "FreeBSD Audio Driver (980215) " __DATE__ " " __TIME__ "\n" + "FreeBSD Audio Driver (981002) " __DATE__ " " __TIME__ "\n" "Installed devices:\n"); for (i = 0; i < NPCM_MAX; i++) { diff --git a/sys/i386/isa/snd/sound.h b/sys/i386/isa/snd/sound.h index fbcd3584c8ee9..eea64c639f4c2 100644 --- a/sys/i386/isa/snd/sound.h +++ b/sys/i386/isa/snd/sound.h @@ -65,7 +65,10 @@ #include <i386/isa/isa_device.h> #include <machine/clock.h> /* for DELAY */ -/* To minimize changes with the code in 2.2.X */ +/* + * the following assumes that FreeBSD 3.X uses poll(2) instead of select(2). + * This change dates to late 1997. + */ #include <sys/poll.h> #define d_select_t d_poll_t @@ -158,7 +161,7 @@ struct _snddev_info { */ int io_base ; /* primary I/O address for the board */ - int alt_base ; /* some codecs are accessible as SB+WSS... */ + int alt_base ; /* some codecs are accessible as SB+MSS... */ int conf_base ; /* and the opti931 also has a config space */ int mix_base ; /* base for the mixer... */ int midi_base ; /* base for the midi */ |
