diff options
Diffstat (limited to 'sys/i386/isa/sound/sb16_dsp.c')
-rw-r--r-- | sys/i386/isa/sound/sb16_dsp.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/sys/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c new file mode 100644 index 0000000..e923176 --- /dev/null +++ b/sys/i386/isa/sound/sb16_dsp.c @@ -0,0 +1,535 @@ +/* + * sound/sb16_dsp.c + * + * The low level driver for the SoundBlaster DSP chip. + * + * (C) 1993 J. Schubert (jsb@sth.ruhr-uni-bochum.de) + * + * based on SB-driver by (C) Hannu Savolainen + * + * 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. + * + */ + +#define DEB(x) +#define DEB1(x) +#include <i386/isa/sound/sound_config.h> +#include "sb.h" +#include <i386/isa/sound/sb_mixer.h> +#include <i386/isa/sound/sbcard.h> + +#if defined(CONFIG_SB16) && (NSB > 0) && defined(CONFIG_AUDIO) && defined(CONFIG_SBPRO) + + +extern sound_os_info *sb_osp; +extern int sbc_base; + +static int sb16_dsp_ok = 0; +static int dsp_16bit = 0; +static int dsp_stereo = 0; +static int dsp_current_speed = 8000; +static int dsp_busy = 0; +static int dma16, dma8; + + +static int trigger_bits = 0; +static u_long dsp_count = 0; + +static int irq_mode = IMODE_NONE; +static int my_dev = 0; + +static volatile int intr_active = 0; + +static int sb16_dsp_open(int dev, int mode); +static void sb16_dsp_close(int dev); +static void sb16_dsp_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart); +static void sb16_dsp_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart); +static int sb16_dsp_ioctl(int dev, u_int cmd, ioctl_arg arg, int local); +static int sb16_dsp_prepare_for_input(int dev, int bsize, int bcount); +static int sb16_dsp_prepare_for_output(int dev, int bsize, int bcount); +static void sb16_dsp_reset(int dev); +static void sb16_dsp_halt(int dev); +static void sb16_dsp_trigger(int dev, int bits); +static int dsp_set_speed(int); +static int dsp_set_stereo(int); +static void dsp_cleanup(void); + +static struct audio_operations sb16_dsp_operations = +{ + "SoundBlaster 16", + DMA_AUTOMODE, + AFMT_U8 | AFMT_S16_LE, + NULL, + sb16_dsp_open, + sb16_dsp_close, + sb16_dsp_output_block, + sb16_dsp_start_input, + sb16_dsp_ioctl, + sb16_dsp_prepare_for_input, + sb16_dsp_prepare_for_output, + sb16_dsp_reset, + sb16_dsp_halt, + NULL, + NULL, + NULL, + NULL, + sb16_dsp_trigger +}; + +static int +sb_dsp_command01(u_char val) +{ + int i = 1 << 16; + + while (--i & (!inb(DSP_STATUS) & 0x80)); + if (!i) + printf("SB16 sb_dsp_command01 Timeout\n"); + return sb_dsp_command(val); +} + +static int +dsp_set_speed(int mode) +{ + DEB(printf("dsp_set_speed(%d)\n", mode)); + if (mode) { + RANGE (mode, 5000, 44100); + dsp_current_speed = mode; + } + return mode; +} + +static int +dsp_set_stereo(int mode) +{ + DEB(printf("dsp_set_stereo(%d)\n", mode)); + dsp_stereo = mode; + return mode; +} + +static int +dsp_set_bits(int arg) +{ + DEB(printf("dsp_set_bits(%d)\n", arg)); + + if (arg) + dsp_16bit = (arg == 16) ? 1 : 0 ; + return dsp_16bit ? 16 : 8; +} + +static int +sb16_dsp_ioctl(int dev, u_int cmd, ioctl_arg arg, int local) +{ + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return dsp_set_speed((int) arg); + return *(int *) arg = dsp_set_speed((*(int *) arg)); + + case SOUND_PCM_READ_RATE: + if (local) + return dsp_current_speed; + return *(int *) arg = dsp_current_speed; + + case SNDCTL_DSP_STEREO: + if (local) + return dsp_set_stereo((int) arg); + return *(int *) arg = dsp_set_stereo((*(int *) arg)); + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return dsp_set_stereo((int) arg - 1) + 1; + return *(int *) arg = dsp_set_stereo((*(int *) arg) - 1) + 1; + + case SOUND_PCM_READ_CHANNELS: + if (local) + return dsp_stereo + 1; + return *(int *) arg = dsp_stereo + 1; + + case SNDCTL_DSP_SETFMT: + if (local) + return dsp_set_bits((int) arg); + return *(int *) arg = dsp_set_bits((*(int *) arg)); + + case SOUND_PCM_READ_BITS: + if (local) + return dsp_16bit ? 16 : 8; + return *(int *) arg = dsp_16bit ? 16 : 8; + + case SOUND_PCM_WRITE_FILTER: /* NOT YET IMPLEMENTED */ + if ((*(int *) arg) > 1) + return *(int *) arg = -(EINVAL); + + case FIOASYNC: + if (local) + return 1; + return *(int *) arg = 1; + + case FIONBIO: + if (local) + return 1; + return *(int *) arg = 1; + + default: + return -(EINVAL); + } + + return -(EINVAL); +} + +static int +sb16_dsp_open(int dev, int mode) +{ + DEB(printf("sb16_dsp_open()\n")); + + if (!sb16_dsp_ok) { + printf("SB16 Error: SoundBlaster board not installed\n"); + return -(ENXIO); + } + if (intr_active) + return -(EBUSY); + + sb_reset_dsp(); + + + irq_mode = IMODE_NONE; + dsp_busy = 1; + trigger_bits = 0; + + return 0; +} + +static void +sb16_dsp_close(int dev) +{ + u_long flags; + + DEB(printf("sb16_dsp_close()\n")); + sb_dsp_command01(0xd9); + sb_dsp_command01(0xd5); + + flags = splhigh(); + + audio_devs[dev]->dmachan1 = dma8; + + dsp_cleanup(); + dsp_busy = 0; + + + splx(flags); +} + +static void +sb16_dsp_output_block(int dev, u_long buf, int count, int intrflag, int dma_restart) +{ + u_long flags, cnt; + + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt==dsp_count) { + irq_mode = IMODE_OUTPUT; + intr_active = 1; + return; /* Auto mode on. No need to react */ + } + flags = splhigh(); + + if (dma_restart) { + + sb16_dsp_halt(dev); + DMAbuf_start_dma(dev, buf, count, 1); + } + + + sb_dsp_command(0x41); + sb_dsp_command((u_char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command((u_char) (dsp_current_speed & 0xff)); + sb_dsp_command((u_char) (dsp_16bit ? 0xb6 : 0xc6)); + dsp_count = cnt; + sb_dsp_command((u_char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command((u_char) (cnt & 0xff)); + sb_dsp_command((u_char) (cnt >> 8)); + + irq_mode = IMODE_OUTPUT; + intr_active = 1; + splx(flags); +} + +static void +sb16_dsp_start_input(int dev, u_long buf, int count, int intrflag, int dma_restart) +{ + u_long flags, cnt; + + cnt = count; + if (dsp_16bit) + cnt >>= 1; + cnt--; + + if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == dsp_count) { + irq_mode = IMODE_INPUT; + intr_active = 1; + return; /* Auto mode on. No need to react */ + } + flags = splhigh(); + + if (dma_restart) { + sb_reset_dsp(); + DMAbuf_start_dma(dev, buf, count, 0); + } + sb_dsp_command(0x42); + sb_dsp_command((u_char) ((dsp_current_speed >> 8) & 0xff)); + sb_dsp_command((u_char) (dsp_current_speed & 0xff)); + sb_dsp_command((u_char) (dsp_16bit ? 0xbe : 0xce)); + dsp_count = cnt; + sb_dsp_command((u_char) ((dsp_stereo ? 0x20 : 0) + + (dsp_16bit ? 0x10 : 0))); + sb_dsp_command01((u_char) (cnt & 0xff)); + sb_dsp_command((u_char) (cnt >> 8)); + + irq_mode = IMODE_INPUT; + intr_active = 1; + splx(flags); +} + +static int +sb16_dsp_prepare_for_input(int dev, int bsize, int bcount) +{ + int fudge; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; + + audio_devs[my_dev]->dmachan2 = dsp_16bit ? dma16 : dma8; + + + fudge = audio_devs[my_dev]->dmachan2 ; + + if (dmap->dma_chan != fudge ) { + isa_dma_release( dmap->dma_chan); + isa_dma_acquire(fudge); + dmap->dma_chan = fudge; + } + + dsp_count = 0; + dsp_cleanup(); + if (dsp_16bit) + sb_dsp_command(0xd5); /* Halt DMA until trigger() is called */ + else + sb_dsp_command(0xd0); /* Halt DMA until trigger() is called */ + + trigger_bits = 0; + return 0; +} + +static int +sb16_dsp_prepare_for_output(int dev, int bsize, int bcount) +{ + int fudge = dsp_16bit ? dma16 : dma8; + struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; + + if (dmap->dma_chan != fudge ) { + isa_dma_release( dmap->dma_chan); + isa_dma_acquire(fudge); + dmap->dma_chan = fudge; + } + + audio_devs[my_dev]->dmachan1 = fudge; + + dsp_count = 0; + dsp_cleanup(); + if (dsp_16bit) + sb_dsp_command(0xd5); /* Halt DMA until trigger() is called */ + else + sb_dsp_command(0xd0); /* Halt DMA until trigger() is called */ + + trigger_bits = 0; + return 0; +} + +static void +sb16_dsp_trigger(int dev, int bits) +{ + if (bits != 0) + bits = 1; + + if (bits == trigger_bits) /* No change */ + return; + + trigger_bits = bits; + + if (!bits) + sb_dsp_command(0xd0); /* Halt DMA */ + else if (bits & irq_mode) + sb_dsp_command(0xd4); /* Continue DMA */ +} + +static void +dsp_cleanup(void) +{ + irq_mode = IMODE_NONE; + intr_active = 0; +} + +static void +sb16_dsp_reset(int dev) +{ + u_long flags; + + flags = splhigh(); + + sb_reset_dsp(); + dsp_cleanup(); + + splx(flags); +} + +static void +sb16_dsp_halt(int dev) +{ + + if (dsp_16bit) { + sb_dsp_command01(0xd9); + sb_dsp_command01(0xd5); + } else { + sb_dsp_command01(0xda); + sb_dsp_command01(0xd0); + } + + +} + +static void +set_irq_hw(int level) +{ + int ival; + + switch (level) { + case 5: + ival = 2; + break; + case 7: + ival = 4; + break; + case 9: + ival = 1; + break; + case 10: + ival = 8; + break; + default: + printf("SB16_IRQ_LEVEL %d does not exist\n", level); + return; + } + sb_setmixer(IRQ_NR, ival); +} + +void +sb16_dsp_init(struct address_info * hw_config) +{ + if (sbc_major < 4) + return; /* Not a SB16 */ + + snprintf(sb16_dsp_operations.name, sizeof(sb16_dsp_operations.name), + "SoundBlaster 16 %d.%d", sbc_major, sbc_minor); + + conf_printf(sb16_dsp_operations.name, hw_config); + + if (num_audiodevs < MAX_AUDIO_DEV) { + audio_devs[my_dev = num_audiodevs++] = &sb16_dsp_operations; + audio_devs[my_dev]->dmachan1 = dma8; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + + } else + printf("SB: Too many DSP devices available\n"); + sb16_dsp_ok = 1; + return; +} + +int +sb16_dsp_detect(struct address_info * hw_config) +{ + struct address_info *sb_config; + + if (sb16_dsp_ok) + return 1; /* Can't drive two cards */ + + if (!(sb_config = sound_getconf(SNDCARD_SB))) { + printf("SB16 Error: Plain SB not configured\n"); + return 0; + } + /* + * sb_setmixer(OPSW,0xf); if(sb_getmixer(OPSW)!=0xf) return 0; + */ + + if (!sb_reset_dsp()) + return 0; + + if (sbc_major < 4) /* Set by the plain SB driver */ + return 0; /* Not a SB16 */ + + if (hw_config->dma < 4) + if (hw_config->dma != sb_config->dma) { + printf("SB16 Error: Invalid DMA channel %d/%d\n", + sb_config->dma, hw_config->dma); + return 0; + } + dma16 = hw_config->dma; + dma8 = sb_config->dma; + /* hw_config->irq = 0; sb_config->irq; + hw_config->io_base = sb_config->io_base; + */ + set_irq_hw(sb_config->irq); + + sb_setmixer(DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma)); + + DEB(printf("SoundBlaster 16: IRQ %d DMA %d OK\n", + sb_config->irq, hw_config->dma)); + + /* + * dsp_showmessage(0xe3,99); + */ + sb16_dsp_ok = 1; + return 1; +} + +void +sb16_dsp_interrupt(int unused) +{ + int data; + + data = inb(DSP_DATA_AVL16); /* Interrupt acknowledge */ + + if (intr_active) + switch (irq_mode) { + case IMODE_OUTPUT: + DMAbuf_outputintr(my_dev, 1); + break; + + case IMODE_INPUT: + DMAbuf_inputintr(my_dev); + break; + + default: + printf("SoundBlaster: Unexpected interrupt\n"); + } +} +#endif |