summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/isa/sb8.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/isa/sb8.c')
-rw-r--r--sys/dev/sound/isa/sb8.c1080
1 files changed, 1080 insertions, 0 deletions
diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c
new file mode 100644
index 0000000..4ca1f1f
--- /dev/null
+++ b/sys/dev/sound/isa/sb8.c
@@ -0,0 +1,1080 @@
+/*
+ * sound/sb_dsp.c
+ *
+ * driver for the SoundBlaster and clones.
+ *
+ * Copyright 1997 Luigi Rizzo.
+ *
+ * Derived from files in the Voxware 3.5 distribution,
+ * Copyright by Hannu Savolainen 1994, under the same copyright
+ * conditions.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met: 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer. 2. Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * use this as a template file for board-specific drivers.
+ * The next two lines (and the final #endif) are in all drivers:
+ */
+
+#include <i386/isa/snd/sound.h>
+#if NPCM > 0
+
+/*
+ * Begin with the board-specific include files...
+ */
+
+#define __SB_MIXER_C__ /* XXX warning... */
+#include <i386/isa/snd/sbcard.h>
+
+/*
+ * then prototypes of functions which go in the snddev_info
+ * (usually static, unless they are shared by other modules)...
+ */
+
+static int sb_probe(struct isa_device *dev);
+static int sb_attach(struct isa_device *dev);
+
+static d_open_t sb_dsp_open;
+static d_close_t sb_dsp_close;
+static d_ioctl_t sb_dsp_ioctl;
+static irq_proc_t sbintr;
+static snd_callback_t sb_callback;
+
+/*
+ * and prototypes for other private functions defined in this module.
+ */
+
+static void sb_dsp_init(snddev_info *d, struct isa_device *dev);
+static void sb_mix_init(snddev_info *d);
+static int sb_mixer_set(snddev_info *d, int dev, int value);
+static int dsp_speed(snddev_info *d);
+static void sb_mixer_reset(snddev_info *d);
+
+u_int sb_get_byte(int io_base);
+
+/*
+ * Then put here the descriptors for the various boards supported
+ * by this module, properly initialized.
+ */
+
+snddev_info sb_op_desc = {
+ "basic soundblaster",
+
+ SNDCARD_SB,
+ sb_probe,
+ sb_attach,
+
+ sb_dsp_open,
+ sb_dsp_close /* sb_close */,
+ NULL /* use generic sndread */,
+ NULL /* use generic sndwrite */,
+ sb_dsp_ioctl,
+ sndselect,
+
+ sbintr,
+ sb_callback,
+
+ DSP_BUFFSIZE, /* bufsize */
+
+ AFMT_STEREO | AFMT_U8, /* audio format */
+
+} ;
+
+/*
+ * Then the file continues with the body of all functions
+ * directly referenced in the descriptor.
+ */
+
+/*
+ * the probe routine for the SoundBlaster only consists in
+ * resetting the dsp and testing if it is there.
+ * Version detection etc. will be done at attach time.
+ *
+ * Remebber, isa probe routines are supposed to return the
+ * size of io space used.
+ */
+
+static int
+sb_probe(struct isa_device *dev)
+{
+ if (dev->id_iobase == -1) {
+ dev->id_iobase = 0x220;
+ printf("sb_probe: no address supplied, try defaults (0x220,0x240)\n");
+ if (snd_conflict(dev->id_iobase))
+ dev->id_iobase = 0x240;
+ }
+ if (snd_conflict(dev->id_iobase))
+ return 0 ;
+
+ if (sb_reset_dsp(dev->id_iobase))
+ return 16 ; /* the SB uses 16 registers... */
+ else
+ return 0;
+}
+
+static int
+sb_attach(struct isa_device *dev)
+{
+ snddev_info *d = &pcm_info[dev->id_unit] ;
+
+ sb_dsp_init(d, dev);
+ return 0 ;
+}
+
+/*
+ * here are the main routines from the switches.
+ */
+
+static int
+sb_dsp_open(dev_t dev, int flags, int mode, struct proc * p)
+{
+ snddev_info *d;
+ int unit ;
+
+ dev = minor(dev);
+ unit = dev >> 4 ;
+ d = &pcm_info[unit] ;
+
+ DEB(printf("<%s>%d : open\n", d->name, unit));
+
+ if (d->flags & SND_F_BUSY) {
+ printf("<%s>%d open: device busy\n", d->name, unit);
+ return EBUSY ;
+ }
+
+ d->wsel.si_pid = 0;
+ d->wsel.si_flags = 0;
+
+ d->rsel.si_pid = 0;
+ d->rsel.si_flags = 0;
+
+ d->esel.si_pid = 0;
+ d->esel.si_flags = 0;
+
+ d->flags = 0 ;
+ d->bd_flags &= ~BD_F_HISPEED ;
+
+ switch ( dev & 0xf ) {
+ case SND_DEV_DSP16 :
+ if ((d->audio_fmt & AFMT_S16_LE) == 0) {
+ printf("sorry, 16-bit not supported on SB %d.%02d\n",
+ (d->bd_id >>8) & 0xff, d->bd_id & 0xff);
+ return ENXIO;
+ }
+ d->play_fmt = d->rec_fmt = AFMT_S16_LE ;
+ break;
+ case SND_DEV_AUDIO :
+ d->play_fmt = d->rec_fmt = AFMT_MU_LAW ;
+ break ;
+ case SND_DEV_DSP :
+ d->play_fmt = d->rec_fmt = AFMT_U8 ;
+ break ;
+ }
+
+ d->flags |= SND_F_BUSY ;
+ d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ;
+
+ if (flags & O_NONBLOCK)
+ d->flags |= SND_F_NBIO ;
+
+ reset_dbuf(& (d->dbuf_in) );
+ reset_dbuf(& (d->dbuf_out) );
+ sb_reset_dsp(d->io_base);
+ ask_init(d);
+
+ return 0;
+}
+
+static int
+sb_dsp_close(dev_t dev, int flags, int mode, struct proc * p)
+{
+ int unit;
+ snddev_info *d;
+ u_long s;
+
+ dev = minor(dev);
+ unit = dev >> 4 ;
+ d = &pcm_info[unit] ;
+
+ s = spltty();
+ d->flags |= SND_F_CLOSING ;
+ splx(s);
+ snd_flush(d);
+
+ sb_cmd(d->io_base, DSP_CMD_SPKOFF ); /* XXX useless ? */
+
+ d->flags = 0 ;
+ return 0 ;
+}
+
+static int
+sb_dsp_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p)
+{
+ int unit;
+ snddev_info *d;
+
+ dev = minor(dev);
+ unit = dev >> 4 ;
+ d = &pcm_info[unit] ;
+
+ /*
+ * handle mixer calls first. Reads are in the default handler,
+ * so do not bother about them.
+ */
+ if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) )
+ return sb_mixer_set(d, cmd & 0xff, *(int *)arg) ;
+
+ /*
+ * for the remaining functions, use the default handler.
+ */
+
+ return ENOSYS ;
+}
+
+static void
+sbintr(int unit)
+{
+ snddev_info *d = &pcm_info[unit];
+ int reason = 3, c=1, io_base = d->io_base;
+
+ DEB(printf("got sbintr for unit %d, flags 0x%08x\n", unit, d->flags));
+
+ /*
+ * 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.
+ */
+again:
+ if (d->bd_flags & BD_F_SB16) {
+ c = sb_getmixer(io_base, IRQ_STAT);
+ /* this tells us if the source is 8-bit or 16-bit dma. We
+ * have to check the io channel to map it to read or write...
+ */
+ reason = 0 ;
+ if ( c & 1 ) { /* 8-bit dma */
+ if (d->dma1 < 4)
+ reason |= 1;
+ if (d->dma2 < 4)
+ reason |= 2;
+ }
+ if ( c & 2 ) { /* 16-bit dma */
+ if (d->dma1 >= 4)
+ reason |= 1;
+ if (d->dma2 >= 4)
+ reason |= 2;
+ }
+ }
+ if ( c & 2 )
+ inb(DSP_DATA_AVL16); /* 16-bit int ack */
+ if (c & 1)
+ inb(DSP_DATA_AVAIL); /* 8-bit int ack */
+
+DEB(printf("sbintr, flags 0x%08x reason %d\n", d->flags, reason));
+ if ( (d->flags & SND_F_WR_DMA) && (reason & 1) )
+ dsp_wrintr(d);
+ if ( (d->flags & SND_F_RD_DMA) && (reason & 2) )
+ dsp_rdintr(d);
+
+ /*
+ * the sb16 might have multiple sources etc.
+ */
+ if (d->bd_flags & BD_F_SB16 && (c & 3) )
+ goto again;
+}
+
+/*
+ * device-specific function called back from the dma module.
+ * The reason of the callback is the second argument.
+ * NOTE: during operations, some ioctl can be done to change
+ * settings (e.g. speed, channels, format), and the default
+ * ioctl handler will just record the change and set the
+ * flag SND_F_INIT. The callback routine is in charge of applying
+ * the changes at the next convenient time (typically, at the
+ * start of operations). For full duplex devices, in some cases the
+ * init requires both channels to be idle.
+ */
+static int
+sb_callback(snddev_info *d, int reason)
+{
+ u_long s ;
+ int rd = reason & SND_CB_RD ;
+ int l = (rd) ? d->dbuf_in.dl0 : d->dbuf_out.dl0 ;
+
+ switch (reason & SND_CB_REASON_MASK) {
+ case SND_CB_INIT : /* called with int enabled and no pending io */
+ dsp_speed(d);
+ snd_set_blocksize(d);
+ if (d->play_fmt & AFMT_MU_LAW)
+ d->flags |= SND_F_XLAT8 ;
+ else
+ d->flags &= ~SND_F_XLAT8 ;
+ return 1;
+ break ;
+
+ case SND_CB_START : /* called with int disabled */
+ sb_cmd(d->io_base, rd ? DSP_CMD_SPKOFF : DSP_CMD_SPKON);
+ d->flags &= ~SND_F_INIT ;
+ if (d->bd_flags & BD_F_SB16) {
+ /* the SB16 can do full duplex using one 16-bit channel
+ * and one 8-bit channel. It needs to be programmed to
+ * use split format though.
+ */
+ int b16 ;
+ int swap = 0 ;
+
+ b16 = (rd) ? d->rec_fmt : d->play_fmt ;
+ b16 = (b16 == AFMT_S16_LE) ? 1 : 0;
+ /*
+ * check if I have to swap dma channels. Swap if
+ * - !rd, dma1 <4, b16
+ * - !rd, dma1 >=4, !b16
+ * - rd, dma2 <4, b16
+ * - rd, dma2 >=4, !b16
+ */
+ if (!rd) {
+ if ( (d->dma1 <4 && b16) || (d->dma1 >=4 && !b16) ) swap = 1;
+ } else {
+ if ( (d->dma2 <4 && b16) || (d->dma2 >=4 && !b16) ) swap = 1;
+ }
+ /*
+ * before swapping should make sure that there is no
+ * pending DMA on the other channel...
+ */
+ if (swap) {
+ int c = d->dma2 ;
+ d->dma2 = d->dma1;
+ d->dma1 = c ;
+ }
+ DEB(printf("sb_init: play %d rec %d dma1 %d dma2 %d\n",
+ d->play_fmt, d->rec_fmt, d->dma1, d->dma2));
+ }
+ /* fallthrough */
+ case SND_CB_RESTART:
+ if (d->bd_flags & BD_F_SB16) {
+ u_char c, c1 ;
+ /*
+ * SB16 support still not completely working!!!
+ *
+ * in principle, on the SB16, I could support simultaneous
+ * play & rec.
+ * However, there is no way to ask explicitly for 8 or
+ * 16 bit transfer. As a consequence, if we do 8-bit,
+ * we need to use the 8-bit channel, and if we do 16-bit,
+ * we need to use the other one. The only way I find to
+ * do this is to swap d->dma1 and d->dma2 ...
+ *
+ */
+
+ if (rd) {
+ c = ((d->dma2 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_FIFO_ON | DSP_F16_ADC ;
+ c1 = (d->play_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ if (d->rec_fmt == AFMT_S16_LE)
+ l /= 2 ;
+ } else {
+ c = ((d->dma1 > 3) ? DSP_DMA16 : DSP_DMA8) |
+ DSP_F16_FIFO_ON | DSP_F16_DAC ;
+ c1 = (d->rec_fmt == AFMT_U8) ? 0 : DSP_F16_SIGNED ;
+ if (d->play_fmt == AFMT_MU_LAW) c1 = 0 ;
+ if (d->play_fmt == AFMT_S16_LE)
+ l /= 2 ;
+ }
+
+ if (d->flags & SND_F_STEREO)
+ c1 |= DSP_F16_STEREO ;
+
+ sb_cmd(d->io_base, c );
+ sb_cmd3(d->io_base, c1 , l - 1) ;
+ } else {
+ u_char c ;
+ if (d->bd_flags & BD_F_HISPEED)
+ c = (rd) ? DSP_CMD_HSADC : DSP_CMD_HSDAC ;
+ else
+ c = (rd) ? DSP_CMD_ADC8 : DSP_CMD_DAC8 ;
+ sb_cmd3(d->io_base, c , l - 1) ;
+ }
+ break;
+
+ case SND_CB_STOP :
+ /* sb_cmd(d->io_base, DSP_CMD_SPKOFF); /* speaker off */
+ break ;
+
+ }
+ return 0 ;
+}
+
+/*
+ * The second part of the file contains all functions specific to
+ * the board and (usually) not exported to other modules.
+ */
+
+int
+sb_reset_dsp(int io_base)
+{
+ int loopc;
+
+ outb(DSP_RESET, 1);
+ DELAY(100);
+ outb(DSP_RESET, 0);
+ for (loopc = 0; loopc<100 && !(inb(DSP_DATA_AVAIL) & 0x80); loopc++)
+ DELAY(30);
+
+ if (inb(DSP_READ) != 0xAA) {
+ DEB(printf("sb_reset_dsp failed\n"));
+ return 0; /* Sorry */
+ }
+ return 1;
+}
+
+/*
+ * only used in sb_attach from here.
+ */
+
+static void
+sb_dsp_init(snddev_info *d, struct isa_device *dev)
+{
+ int i, x;
+ char *fmt = NULL ;
+ int io_base = dev->id_iobase ;
+
+ d->bd_id = 0 ;
+
+ sb_reset_dsp(io_base);
+ sb_cmd(io_base, DSP_CMD_GETVER); /* Get version */
+
+ for (i = 10000; i; i--) { /* perhaps wait longer on a fast machine ? */
+ if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */
+ if ( (d->bd_id & 0xff00) == 0)
+ d->bd_id = inb(DSP_READ) << 8; /* major */
+ else {
+ d->bd_id |= inb(DSP_READ); /* minor */
+ break;
+ }
+ } else
+ DELAY(20);
+ }
+
+ /*
+ * now do various initializations depending on board id.
+ */
+
+ fmt = "SoundBlaster %d.%d" ; /* default */
+
+ switch ( d->bd_id >> 8 ) {
+ case 0 :
+ printf("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n",
+ inb(DSP_DATA_AVAIL));
+ d->bd_id = 0x100;
+ case 1 : /* old sound blaster has nothing... */
+ break ;
+
+ case 2 :
+ d->dma2 = d->dma1 ; /* half duplex */
+ d->bd_flags |= BD_F_DUP_MIDI ;
+
+ if (d->bd_id == 0x200)
+ break ; /* no mixer on the 2.0 */
+ d->bd_flags &= ~BD_F_MIX_MASK ;
+ d->bd_flags |= BD_F_MIX_CT1335 ;
+
+ break ;
+ case 4 :
+ fmt = "SoundBlaster 16 %d.%d";
+ d->audio_fmt |= AFMT_FULLDUPLEX | AFMT_WEIRD | AFMT_S8 | AFMT_S16_LE;
+ d->bd_flags |= BD_F_SB16;
+ d->bd_flags &= ~BD_F_MIX_MASK ;
+ d->bd_flags |= BD_F_MIX_CT1745 ;
+
+ /* soft irq/dma configuration */
+ x = -1 ;
+ if (d->irq == 5) x = 2;
+ else if (d->irq == 7) x = 4;
+ else if (d->irq == 9) x = 1;
+ else if (d->irq == 10) x = 8;
+ if (x == -1)
+ printf("<%s>%d: bad irq %d (only 5,7,9,10 allowed)\n",
+ d->name, dev->id_unit, d->irq);
+ else
+ sb_setmixer(io_base, IRQ_NR, x);
+
+ sb_setmixer(io_base, DMA_NR, (1 << d->dma1) | (1 << d->dma2));
+ break ;
+
+ case 3 :
+ d->dma2 = d->dma1 ; /* half duplex */
+ fmt = "SoundBlaster Pro %d.%d";
+ d->bd_flags |= BD_F_DUP_MIDI ;
+ d->bd_flags &= ~BD_F_MIX_MASK ;
+ d->bd_flags |= BD_F_MIX_CT1345 ;
+ if (d->bd_id == 0x301) {
+ int ess_major = 0, ess_minor = 0;
+
+ /*
+ * Try to detect ESS chips.
+ */
+
+ sb_cmd(io_base, DSP_CMD_GETID); /* Return ident. bytes. */
+
+ for (i = 1000; i; i--) {
+ if (inb(DSP_DATA_AVAIL) & 0x80) { /* wait for Data Ready */
+ if (ess_major == 0)
+ ess_major = inb(DSP_READ);
+ else {
+ ess_minor = inb(DSP_READ);
+ break;
+ }
+ }
+ }
+
+ if (ess_major == 0x48 && (ess_minor & 0xf0) == 0x80)
+ printf("Hmm... Could this be an ESS488 based card (rev %d)\n",
+ ess_minor & 0x0f);
+ else if (ess_major == 0x68 && (ess_minor & 0xf0) == 0x80)
+ printf("Hmm... Could this be an ESS688 based card (rev %d)\n",
+ ess_minor & 0x0f);
+ }
+
+ if (d->bd_flags & BD_F_JAZZ16) {
+ if (d->bd_flags & BD_F_JAZZ16_2)
+ fmt = "SoundMan Wave %d.%d";
+ else
+ fmt = "MV Jazz16 %d.%d";
+ d->audio_fmt |= AFMT_S16_LE; /* 16 bits */
+ }
+ }
+
+ sprintf(d->name, fmt, (d->bd_id >> 8) &0xff, d->bd_id & 0xff);
+
+ sb_mix_init(d);
+}
+
+static void
+sb_mix_init(snddev_info *d)
+{
+ switch (d->bd_flags & BD_F_MIX_MASK) {
+ case BD_F_MIX_CT1345 : /* SB 3.0 has 1345 mixer */
+
+ d->mix_devs = SBPRO_MIXER_DEVICES ;
+ d->mix_rec_devs = SBPRO_RECORDING_DEVICES ;
+ d->mix_recsrc = SOUND_MASK_MIC ;
+
+ sb_setmixer(d->io_base, 0, 1 ); /* reset mixer */
+ sb_setmixer(d->io_base, MIC_VOL , 0x6 ); /* mic volume max */
+ sb_setmixer(d->io_base, RECORD_SRC , 0x0 ); /* mic source */
+ sb_setmixer(d->io_base, FM_VOL , 0x0 ); /* no midi */
+ break ;
+
+ case BD_F_MIX_CT1745 : /* SB16 mixer ... */
+
+ d->mix_devs = SB16_MIXER_DEVICES ;
+ d->mix_rec_devs = SB16_RECORDING_DEVICES ;
+ d->mix_recsrc = SOUND_MASK_MIC ;
+ }
+ sb_mixer_reset(d);
+}
+
+/*
+ * Common code for the midi and pcm functions
+ */
+
+int
+sb_cmd(int io_base, u_char val)
+{
+ int i;
+
+ for (i = 0; i < 1000 ; i++) {
+ if ((inb(DSP_STATUS) & 0x80) == 0) {
+ outb(DSP_COMMAND, val);
+ return 1;
+ }
+ if (i > 10)
+ DELAY (i > 100 ? 1000 : 10 );
+ }
+
+ printf("SoundBlaster: DSP Command(0x%02x) timeout. IRQ conflict ?\n", val);
+ return 0;
+}
+
+int
+sb_cmd3(int io_base, u_char cmd, int val)
+{
+ if (sb_cmd(io_base, cmd)) {
+ sb_cmd(io_base, val & 0xff );
+ sb_cmd(io_base, (val>>8) & 0xff );
+ return 1 ;
+ } else
+ return 0;
+}
+
+int
+sb_cmd2(int io_base, u_char cmd, int val)
+{
+ if (sb_cmd(io_base, cmd)) {
+ sb_cmd(io_base, val & 0xff );
+ return 1 ;
+ } else
+ return 0;
+}
+
+void
+sb_setmixer(int io_base, u_int port, u_int value)
+{
+ u_long flags;
+
+ flags = spltty();
+ outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ DELAY(10);
+ outb(MIXER_DATA, (u_char) (value & 0xff));
+ DELAY(10);
+ splx(flags);
+}
+
+u_int
+sb_get_byte(int io_base)
+{
+ int i;
+
+ for (i = 1000; i; i--)
+ if (inb(DSP_DATA_AVAIL) & 0x80)
+ return inb(DSP_READ);
+ else
+ DELAY(20);
+ return 0xffff;
+}
+
+int
+sb_getmixer(int io_base, u_int port)
+{
+ int val;
+ u_long flags;
+
+ flags = spltty();
+ outb(MIXER_ADDR, (u_char) (port & 0xff)); /* Select register */
+ DELAY(10);
+ val = inb(MIXER_DATA);
+ DELAY(10);
+ splx(flags);
+
+ return val;
+}
+
+
+/*
+ * various utility functions for the DSP
+ */
+
+/*
+ * dsp_speed updates the speed setting from the descriptor. make sure
+ * it is called at spltty().
+ * Besides, it takes care of stereo setting.
+ */
+static int
+dsp_speed(snddev_info *d)
+{
+ u_char tconst;
+ u_long flags;
+ int max_speed = 44100, speed = d->play_speed ;
+
+ if (d->bd_flags & BD_F_SB16) {
+ RANGE (speed, 5000, 45000);
+ d->play_speed = d->rec_speed = speed ;
+ sb_cmd(d->io_base, 0x41);
+ sb_cmd(d->io_base, d->play_speed >> 8 );
+ sb_cmd(d->io_base, d->play_speed & 0xff );
+ sb_cmd(d->io_base, 0x42);
+ sb_cmd(d->io_base, d->rec_speed >> 8 );
+ sb_cmd(d->io_base, d->rec_speed & 0xff );
+ return speed ;
+ }
+ /*
+ * only some models can do stereo, and only if not
+ * simultaneously using midi.
+ */
+ if ( (d->bd_id & 0xff00) < 0x300 || d->bd_flags & BD_F_MIDIBUSY)
+ d->flags &= ~SND_F_STEREO;
+
+ /*
+ * here enforce speed limitations.
+ */
+ if (d->bd_id <= 0x200)
+ max_speed = 22050; /* max 22050 on SB 1.X */
+
+ /*
+ * SB models earlier than SB Pro have low limit for the
+ * input rate. Note that this is only for input, but since
+ * we do not support separate values for rec & play....
+ */
+ if (d->bd_id <= 0x200)
+ max_speed = 13000;
+ else if (d->bd_id < 0x300)
+ max_speed = 15000;
+
+ RANGE(speed, 4000, max_speed);
+
+ /*
+ * Logitech SoundMan Games and Jazz16 cards can support 44.1kHz
+ * stereo
+ */
+#if !defined (SM_GAMES)
+ /*
+ * Max. stereo speed is 22050
+ */
+ if (d->flags & SND_F_STEREO && speed > 22050 && !(d->bd_flags & BD_F_JAZZ16))
+ speed = 22050;
+#endif
+
+ if ((speed > 22050) && d->bd_flags & BD_F_MIDIBUSY)
+ speed = 22050;
+
+ if (d->flags & SND_F_STEREO)
+ speed *= 2;
+
+ /*
+ * Now the speed should be valid. Compute the value to be
+ * programmed into the board.
+ */
+
+ if (speed > 22050) { /* High speed mode on 2.01/3.xx */
+ int tmp;
+
+ tconst = (u_char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8);
+ d->bd_flags |= BD_F_HISPEED ;
+
+ flags = spltty();
+ sb_cmd2(d->io_base, DSP_CMD_TCONST, tconst);
+ splx(flags);
+
+ tmp = 65536 - (tconst << 8);
+ speed = (256000000 + tmp / 2) / tmp;
+ } else {
+ int tmp;
+
+ d->bd_flags &= ~BD_F_HISPEED ;
+ tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff;
+
+ flags = spltty();
+ sb_cmd2(d->io_base, DSP_CMD_TCONST, tconst);
+ splx(flags);
+
+ tmp = 256 - tconst;
+ speed = (1000000 + tmp / 2) / tmp;
+ }
+
+ if (d->flags & SND_F_STEREO)
+ speed /= 2;
+
+ d->play_speed = d->rec_speed = speed;
+ return speed;
+}
+
+/*
+ * mixer support, originally in sb_mixer.c
+ */
+
+static void
+sb_set_recsrc(snddev_info *d, int mask)
+{
+ u_char outmask;
+ u_char recdev ;
+
+ mask &= d->mix_rec_devs;
+ switch (d->bd_flags & BD_F_MIX_MASK) {
+ case BD_F_MIX_CT1345 :
+ if (mask == SOUND_MASK_LINE)
+ recdev = 6 ;
+ else if (mask == SOUND_MASK_CD)
+ recdev = 2 ;
+ else { /* default: mic */
+ mask = SOUND_MASK_MIC ;
+ recdev = 0 ;
+ }
+ sb_setmixer(d->io_base, RECORD_SRC,
+ recdev | (sb_getmixer(d->io_base, RECORD_SRC) & ~7 ));
+ break ;
+ case BD_F_MIX_CT1745 : /* sb16 */
+ if (mask == 0)
+ mask = SOUND_MASK_MIC ; /* XXX For compatibility. Bug ? */
+ recdev = 0 ;
+ if (mask & SOUND_MASK_MIC)
+ recdev |= 1 ;
+ if (mask & SOUND_MASK_CD)
+ recdev |= 6 ; /* l+r cd */
+ if (mask & SOUND_MASK_LINE)
+ recdev |= 0x18 ; /* l+r line */
+ if (mask & SOUND_MASK_SYNTH)
+ recdev |= 0x60 ; /* l+r midi */
+ sb_setmixer(d->io_base, SB16_IMASK_L, recdev);
+ sb_setmixer(d->io_base, SB16_IMASK_R, recdev);
+ /*
+ * since the same volume controls apply to the input and
+ * output sections, the best approach to have a consistent
+ * behaviour among cards would be to disable the output path
+ * on devices which are used to record.
+ * However, since users like to have feedback, we only disable
+ * the mike -- permanently.
+ */
+ sb_setmixer(d->io_base, SB16_OMASK, 0x1f & ~1);
+ break ;
+ }
+ d->mix_recsrc = mask;
+}
+
+static void
+sb_mixer_reset(snddev_info *d)
+{
+ int i;
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ sb_mixer_set(d, i, levels[i]);
+ if (d->bd_flags & BD_F_SB16) {
+ sb_setmixer(d->io_base, 0x3c, 0x1f); /* make all output active */
+ sb_setmixer(d->io_base, 0x3d, 0); /* make all inputs-l off */
+ sb_setmixer(d->io_base, 0x3e, 0); /* make all inputs-r off */
+ }
+ sb_set_recsrc(d, SOUND_MASK_MIC);
+}
+
+static int
+sb_mixer_set(snddev_info *d, int dev, int value)
+{
+ int left = value & 0x000000ff;
+ int right = (value & 0x0000ff00) >> 8;
+ int regoffs;
+ u_char val;
+ mixer_tab *iomap;
+
+#ifdef JAZZ16
+ if (d->bd_flags & BD_F_JAZZ16 && d->bd_flags & BD_F_JAZZ16_2)
+ return smw_mixer_set(dev, value);
+#endif
+
+ if (dev == SOUND_MIXER_RECSRC) {
+ sb_set_recsrc(d, value);
+ return 0 ;
+ }
+ if (left > 100)
+ left = 100;
+ if (right > 100)
+ right = 100;
+
+ if (dev > 31)
+ return EINVAL ;
+
+ if (!(d->mix_devs & (1 << dev))) /* Not supported */
+ return EINVAL;
+
+ switch ( d->bd_flags & BD_F_MIX_MASK ) {
+ default:
+ /* mixer unknown, fail... */
+ return EINVAL ;/* XXX change this */
+ case BD_F_MIX_CT1345 :
+ iomap = &sbpro_mix ;
+ break;
+ case BD_F_MIX_CT1745 :
+ iomap = &sb16_mix ;
+ break;
+ /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */
+ }
+ regoffs = (*iomap)[dev][LEFT_CHN].regno;
+ if (regoffs == 0)
+ return EINVAL;
+
+ val = sb_getmixer(d->io_base, regoffs);
+
+ change_bits(iomap, &val, dev, LEFT_CHN, left);
+
+ d->mix_levels[dev] = left | (left << 8);
+
+ if ((*iomap)[dev][RIGHT_CHN].regno != regoffs) { /* Change register */
+ sb_setmixer(d->io_base, regoffs, val); /* Save the old one */
+ regoffs = (*iomap)[dev][RIGHT_CHN].regno;
+
+ if (regoffs == 0)
+ return 0 ; /* Just left channel present */
+
+ val = sb_getmixer(d->io_base, regoffs); /* Read the new one */
+ }
+ change_bits(iomap, &val, dev, RIGHT_CHN, right);
+
+ sb_setmixer(d->io_base, regoffs, val);
+
+ d->mix_levels[dev] = left | (right << 8);
+ return 0 ; /* ok */
+}
+
+/*
+ * now support for some PnP boards.
+ */
+
+#if NPNP > 0
+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
+ * xxx -- 0x2b008c0e
+ *
+ */
+
+static char *sb16pnp_probe(u_long csn, u_long vend_id);
+static void sb16pnp_attach(u_long csn, u_long vend_id, char *name,
+ struct isa_device *dev);
+
+static struct pnp_device sb16pnp = {
+ "SB16pnp",
+ sb16pnp_probe,
+ sb16pnp_attach,
+ &nsnd, /* use this for all sound cards */
+ &tty_imask /* imask */
+};
+DATA_SET (pnpdevice_set, sb16pnp);
+
+static char *
+sb16pnp_probe(u_long csn, u_long vend_id)
+{
+ char *s = NULL ;
+ if (vend_id == 0x01009305)
+ s = "Avance Asound 100" ;
+ if (vend_id == 0x2b008c0e)
+ s = "SB16 Value PnP" ;
+ /*
+ * The SB16/AWE64 cards seem to differ in the fourth byte of
+ * the vendor id, so I have just masked it for the time being...
+ * Reported values are:
+ * SB16 Value PnP: 0x2b008c0e
+ * SB AWE64 PnP: 0x39008c0e 0x9d008c0e 0xc3008c0e
+ */
+ if ( (vend_id & 0xffffff) == (0x9d008c0e & 0xffffff) )
+ s = "SB AWE64 PnP";
+ if (s) {
+ struct pnp_cinfo d;
+ read_pnp_parms(&d, 0);
+ if (d.enable == 0) {
+ printf("This is a %s, but LDN 0 is disabled\n", s);
+ return NULL ;
+ }
+ return s ;
+ }
+ return NULL ;
+}
+
+static void
+sb16pnp_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 */
+
+ tmp_d = sb_op_desc;
+ snddev_last_probed = &tmp_d;
+
+ read_pnp_parms ( &d , 0 ) ;
+ d.port[1] = 0 ; /* only the first address is used */
+ dev->id_iobase = d.port[0];
+ write_pnp_parms ( &d , 0 );
+ enable_pnp_card();
+
+ 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] ) ;
+
+ pcm_info[dev->id_unit] = tmp_d;
+ snddev_last_probed->probe(dev); /* not really necessary but doesn't harm */
+
+ pcmattach(dev);
+}
+#endif /* NPNP */
+
+#endif
OpenPOWER on IntegriCloud