diff options
Diffstat (limited to 'sys/dev/sound/isa/sb8.c')
-rw-r--r-- | sys/dev/sound/isa/sb8.c | 1080 |
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 |