diff --git a/sys/i386/isa/sound/ad1848.c b/sys/i386/isa/sound/ad1848.c index 8167c0de884c..024b62f23843 100644 --- a/sys/i386/isa/sound/ad1848.c +++ b/sys/i386/isa/sound/ad1848.c @@ -1,1473 +1,1473 @@ /* * sound/ad1848.c * * The low level driver for the AD1848/CS4248 codec chip which * is used for example in the MS Sound System. * * The CS4231 which is used in the GUS MAX and some other cards is * upwards compatible with AD1848 and this driver is able to drive it. * * Copyright by Hannu Savolainen 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 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. * * Modified: * Riccardo Facchetti 24 Mar 1995 * - Added the Audio Excel DSP 16 initialization routine. */ #define DEB(x) #define DEB1(x) #include "sound_config.h" #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848) #include "ad1848_mixer.h" #define IMODE_NONE 0 #define IMODE_OUTPUT 1 #define IMODE_INPUT 2 #define IMODE_INIT 3 #define IMODE_MIDI 4 typedef struct { int base; int irq; int dma_capture, dma_playback; unsigned char MCE_bit; unsigned char saved_regs[16]; int speed; unsigned char speed_bits; int channels; int audio_format; unsigned char format_bits; int xfer_count; int irq_mode; int intr_active; int opened; char *chip_name; int mode; /* Mixer parameters */ int recmask; int supported_devices; int supported_rec_devices; unsigned short levels[32]; } ad1848_info; static int nr_ad1848_devs = 0; static char irq2dev[16] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; static char mixer2codec[MAX_MIXER_DEV] = {0}; static int ad_format_mask[3 /*devc->mode */ ] = { 0, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM }; static ad1848_info dev_info[MAX_AUDIO_DEV]; #define io_Index_Addr(d) ((d)->base) #define io_Indexed_Data(d) ((d)->base+1) #define io_Status(d) ((d)->base+2) #define io_Polled_IO(d) ((d)->base+3) static int ad1848_open (int dev, int mode); static void ad1848_close (int dev); static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local); static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart); static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart); static int ad1848_prepare_for_IO (int dev, int bsize, int bcount); static void ad1848_reset (int dev); static void ad1848_halt (int dev); static int ad_read (ad1848_info * devc, int reg) { unsigned long flags; int x; int timeout = 100; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); x = INB (io_Indexed_Data (devc)); /* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */ RESTORE_INTR (flags); return x; } static void ad_write (ad1848_info * devc, int reg, int data) { unsigned long flags; int timeout = 100; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc)); OUTB ((unsigned char) (data & 0xff), io_Indexed_Data (devc)); /* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */ RESTORE_INTR (flags); } static void wait_for_calibration (ad1848_info * devc) { int timeout = 0; /* * Wait until the auto calibration process has finished. * * 1) Wait until the chip becomes ready (reads don't return 0x80). * 2) Wait until the ACI bit of I11 gets on and then off. */ timeout = 100000; while (timeout > 0 && INB (devc->base) & 0x80) timeout--; if (INB (devc->base) & 0x80) printk ("ad1848: Auto calibration timed out(1).\n"); timeout = 100; while (timeout > 0 && !(ad_read (devc, 11) & 0x20)) timeout--; if (!(ad_read (devc, 11) & 0x20)) return; timeout = 10000; while (timeout > 0 && ad_read (devc, 11) & 0x20) timeout--; if (ad_read (devc, 11) & 0x20) printk ("ad1848: Auto calibration timed out(3).\n"); } static void ad_mute (ad1848_info * devc) { int i; unsigned char prev; /* * Save old register settings and mute output channels */ for (i = 6; i < 8; i++) { prev = devc->saved_regs[i] = ad_read (devc, i); ad_write (devc, i, prev | 0x80); } } static void ad_unmute (ad1848_info * devc) { int i; /* * Restore back old volume registers (unmute) */ for (i = 6; i < 8; i++) { ad_write (devc, i, devc->saved_regs[i] & ~0x80); } } static void ad_enter_MCE (ad1848_info * devc) { unsigned long flags; int timeout = 1000; unsigned short prev; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); devc->MCE_bit = 0x40; prev = INB (io_Index_Addr (devc)); if (prev & 0x40) { RESTORE_INTR (flags); return; } OUTB (devc->MCE_bit, io_Index_Addr (devc)); RESTORE_INTR (flags); } static void ad_leave_MCE (ad1848_info * devc) { unsigned long flags; unsigned char prev; int timeout = 1000; while (timeout > 0 && INB (devc->base) == 0x80) /*Are we initializing */ timeout--; DISABLE_INTR (flags); devc->MCE_bit = 0x00; prev = INB (io_Index_Addr (devc)); OUTB (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */ if (prev & 0x40 == 0) /* Not in MCE mode */ { RESTORE_INTR (flags); return; } OUTB (0x00, io_Index_Addr (devc)); /* Clear the MCE bit */ wait_for_calibration (devc); RESTORE_INTR (flags); } static int ad1848_set_recmask (ad1848_info * devc, int mask) { unsigned char recdev; int i, n; mask &= devc->supported_rec_devices; n = 0; for (i = 0; i < 32; i++) /* Count selected device bits */ if (mask & (1 << i)) n++; if (n == 0) mask = SOUND_MASK_MIC; else if (n != 1) /* Too many devices selected */ { mask &= ~devc->recmask; /* Filter out active settings */ n = 0; for (i = 0; i < 32; i++) /* Count selected device bits */ if (mask & (1 << i)) n++; if (n != 1) mask = SOUND_MASK_MIC; } switch (mask) { case SOUND_MASK_MIC: recdev = 2; break; case SOUND_MASK_LINE: case SOUND_MASK_LINE3: recdev = 0; break; case SOUND_MASK_CD: case SOUND_MASK_LINE1: recdev = 1; break; default: mask = SOUND_MASK_MIC; recdev = 2; } recdev <<= 6; ad_write (devc, 0, (ad_read (devc, 0) & 0x3f) | recdev); ad_write (devc, 1, (ad_read (devc, 1) & 0x3f) | recdev); devc->recmask = mask; return mask; } static void change_bits (unsigned char *regval, int dev, int chn, int newval) { unsigned char mask; int shift; if (mix_devices[dev][chn].polarity == 1) /* Reverse */ newval = 100 - newval; mask = (1 << mix_devices[dev][chn].nbits) - 1; shift = mix_devices[dev][chn].bitpos; newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ *regval &= ~(mask << shift); /* Clear bits */ *regval |= (newval & mask) << shift; /* Set new value */ } static int ad1848_mixer_get (ad1848_info * devc, int dev) { if (!((1 << dev) & devc->supported_devices)) return RET_ERROR (EINVAL); return devc->levels[dev]; } static int ad1848_mixer_set (ad1848_info * devc, int dev, int value) { int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int regoffs; unsigned char val; if (left > 100) left = 100; if (right > 100) right = 100; if (dev > 31) return RET_ERROR (EINVAL); if (!(devc->supported_devices & (1 << dev))) return RET_ERROR (EINVAL); if (mix_devices[dev][LEFT_CHN].nbits == 0) return RET_ERROR (EINVAL); /* * Set the left channel */ regoffs = mix_devices[dev][LEFT_CHN].regno; val = ad_read (devc, regoffs); change_bits (&val, dev, LEFT_CHN, left); devc->levels[dev] = left | (left << 8); ad_write (devc, regoffs, val); devc->saved_regs[regoffs] = val; /* * Set the left right */ if (mix_devices[dev][RIGHT_CHN].nbits == 0) return left | (left << 8); /* Was just a mono channel */ regoffs = mix_devices[dev][RIGHT_CHN].regno; val = ad_read (devc, regoffs); change_bits (&val, dev, RIGHT_CHN, right); ad_write (devc, regoffs, val); devc->saved_regs[regoffs] = val; devc->levels[dev] = left | (right << 8); return left | (right << 8); } static void ad1848_mixer_reset (ad1848_info * devc) { int i; devc->recmask = 0; if (devc->mode == 2) devc->supported_devices = MODE2_MIXER_DEVICES; else devc->supported_devices = MODE1_MIXER_DEVICES; devc->supported_rec_devices = MODE1_REC_DEVICES; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) ad1848_mixer_set (devc, i, devc->levels[i] = default_mixer_levels[i]); ad1848_set_recmask (devc, SOUND_MASK_MIC); } static int ad1848_mixer_ioctl (int dev, unsigned int cmd, unsigned int arg) { ad1848_info *devc; int codec_dev = mixer2codec[dev]; if (!codec_dev) return RET_ERROR (ENXIO); codec_dev--; devc = (ad1848_info *) audio_devs[codec_dev]->devc; if (((cmd >> 8) & 0xff) == 'M') { if (cmd & IOC_IN) switch (cmd & 0xff) { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, ad1848_set_recmask (devc, IOCTL_IN (arg))); break; default: return IOCTL_OUT (arg, ad1848_mixer_set (devc, cmd & 0xff, IOCTL_IN (arg))); } else switch (cmd & 0xff) /* * Return parameters */ { case SOUND_MIXER_RECSRC: return IOCTL_OUT (arg, devc->recmask); break; case SOUND_MIXER_DEVMASK: return IOCTL_OUT (arg, devc->supported_devices); break; case SOUND_MIXER_STEREODEVS: return IOCTL_OUT (arg, devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX)); break; case SOUND_MIXER_RECMASK: return IOCTL_OUT (arg, devc->supported_rec_devices); break; case SOUND_MIXER_CAPS: return IOCTL_OUT (arg, SOUND_CAP_EXCL_INPUT); break; default: return IOCTL_OUT (arg, ad1848_mixer_get (devc, cmd & 0xff)); } } else return RET_ERROR (EINVAL); } static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] = { { "Generic AD1848 codec", DMA_AUTOMODE, AFMT_U8, /* Will be set later */ NULL, ad1848_open, ad1848_close, ad1848_output_block, ad1848_start_input, ad1848_ioctl, ad1848_prepare_for_IO, ad1848_prepare_for_IO, ad1848_reset, ad1848_halt, NULL, NULL }}; static struct mixer_operations ad1848_mixer_operations = { "AD1848/CS4248/CS4231", ad1848_mixer_ioctl }; static int ad1848_open (int dev, int mode) { int err; ad1848_info *devc = NULL; unsigned long flags; DEB (printk ("ad1848_open(int mode = %X)\n", mode)); if (dev < 0 || dev >= num_audiodevs) return RET_ERROR (ENXIO); devc = (ad1848_info *) audio_devs[dev]->devc; DISABLE_INTR (flags); if (devc->opened) { RESTORE_INTR (flags); printk ("ad1848: Already opened\n"); return RET_ERROR (EBUSY); } if (devc->irq) /* Not managed by another driver */ if ((err = snd_set_irq_handler (devc->irq, ad1848_interrupt, audio_devs[dev]->name)) < 0) { printk ("ad1848: IRQ in use\n"); RESTORE_INTR (flags); return err; } if (DMAbuf_open_dma (dev) < 0) { RESTORE_INTR (flags); printk ("ad1848: DMA in use\n"); return RET_ERROR (EBUSY); } devc->intr_active = 0; devc->opened = 1; RESTORE_INTR (flags); return 0; } static void ad1848_close (int dev) { unsigned long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; DEB (printk ("ad1848_close(void)\n")); DISABLE_INTR (flags); devc->intr_active = 0; if (devc->irq) /* Not managed by another driver */ snd_release_irq (devc->irq); ad1848_reset (dev); DMAbuf_close_dma (dev); devc->opened = 0; RESTORE_INTR (flags); } static int set_speed (ad1848_info * devc, int arg) { /* * The sampling speed is encoded in the least significant nible of I8. The * LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) and other * three bits select the divisor (indirectly): * * The available speeds are in the following table. Keep the speeds in * the increasing order. */ typedef struct { int speed; unsigned char bits; } speed_struct; static speed_struct speed_table[] = { {5510, (0 << 1) | 1}, {5510, (0 << 1) | 1}, {6620, (7 << 1) | 1}, {8000, (0 << 1) | 0}, {9600, (7 << 1) | 0}, {11025, (1 << 1) | 1}, {16000, (1 << 1) | 0}, {18900, (2 << 1) | 1}, {22050, (3 << 1) | 1}, {27420, (2 << 1) | 0}, {32000, (3 << 1) | 0}, {33075, (6 << 1) | 1}, {37800, (4 << 1) | 1}, {44100, (5 << 1) | 1}, {48000, (6 << 1) | 0} }; int i, n, selected = -1; n = sizeof (speed_table) / sizeof (speed_struct); if (arg < speed_table[0].speed) selected = 0; if (arg > speed_table[n - 1].speed) selected = n - 1; for (i = 1 /*really */ ; selected == -1 && i < n; i++) if (speed_table[i].speed == arg) selected = i; else if (speed_table[i].speed > arg) { int diff1, diff2; diff1 = arg - speed_table[i - 1].speed; diff2 = speed_table[i].speed - arg; if (diff1 < diff2) selected = i - 1; else selected = i; } if (selected == -1) { printk ("ad1848: Can't find speed???\n"); selected = 3; } devc->speed = speed_table[selected].speed; devc->speed_bits = speed_table[selected].bits; return devc->speed; } static int set_channels (ad1848_info * devc, int arg) { if (arg != 1 && arg != 2) return devc->channels; devc->channels = arg; return arg; } static int set_format (ad1848_info * devc, int arg) { static struct format_tbl { int format; unsigned char bits; } format2bits[] = { { 0, 0 } , { AFMT_MU_LAW, 1 } , { AFMT_A_LAW, 3 } , { AFMT_IMA_ADPCM, 5 } , { AFMT_U8, 0 } , { AFMT_S16_LE, 2 } , { AFMT_S16_BE, 6 } , { AFMT_S8, 0 } , { AFMT_U16_LE, 0 } , { AFMT_U16_BE, 0 } }; int i, n = sizeof (format2bits) / sizeof (struct format_tbl); if (!(arg & ad_format_mask[devc->mode])) arg = AFMT_U8; devc->audio_format = arg; for (i = 0; i < n; i++) if (format2bits[i].format == arg) { if ((devc->format_bits = format2bits[i].bits) == 0) return devc->audio_format = AFMT_U8; /* Was not supported */ return arg; } /* Still hanging here. Something must be terribly wrong */ devc->format_bits = 0; return devc->audio_format = AFMT_U8; } static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return set_speed (devc, arg); return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_RATE: if (local) return devc->speed; return IOCTL_OUT (arg, devc->speed); case SNDCTL_DSP_STEREO: if (local) return set_channels (devc, arg + 1) - 1; return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1); case SOUND_PCM_WRITE_CHANNELS: if (local) return set_channels (devc, arg); return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_CHANNELS: if (local) return devc->channels; return IOCTL_OUT (arg, devc->channels); case SNDCTL_DSP_SAMPLESIZE: if (local) return set_format (devc, arg); return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg))); case SOUND_PCM_READ_BITS: if (local) return devc->audio_format; return IOCTL_OUT (arg, devc->audio_format); default:; } return RET_ERROR (EINVAL); } static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { unsigned long flags, cnt; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; cnt = count; if (devc->audio_format == AFMT_IMA_ADPCM) { cnt /= 4; } else { if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; } if (devc->channels > 1) cnt >>= 1; cnt--; if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->irq_mode = IMODE_OUTPUT; devc->intr_active = 1; return; /* * Auto DMA mode on. No need to react */ } DISABLE_INTR (flags); if (dma_restart) { /* ad1848_halt (dev); */ DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); } ad_enter_MCE (devc); ad_write (devc, 15, (unsigned char) (cnt & 0xff)); ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff)); ad_write (devc, 9, 0x0d); /* * Playback enable, single DMA channel mode, * auto calibration on. */ ad_leave_MCE (devc); /* * Starts the calibration process and * enters playback mode after it. */ ad_unmute (devc); devc->xfer_count = cnt; devc->irq_mode = IMODE_OUTPUT; devc->intr_active = 1; INB (io_Status (devc)); OUTB (0, io_Status (devc)); /* Clear pending interrupts */ RESTORE_INTR (flags); } static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart) { unsigned long flags, cnt; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; int count_reg = 14; /* (devc->mode == 1) ? 14 : 30; */ cnt = count; if (devc->audio_format == AFMT_IMA_ADPCM) { cnt /= 4; } else { if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */ cnt >>= 1; } if (devc->channels > 1) cnt >>= 1; cnt--; if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == devc->xfer_count) { devc->irq_mode = IMODE_INPUT; devc->intr_active = 1; return; /* * Auto DMA mode on. No need to react */ } DISABLE_INTR (flags); if (dma_restart) { /* ad1848_halt (dev); */ DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); } ad_enter_MCE (devc); ad_write (devc, count_reg + 1, (unsigned char) (cnt & 0xff)); ad_write (devc, count_reg, (unsigned char) ((cnt >> 8) & 0xff)); ad_write (devc, 9, 0x0e); /* * Capture enable, single DMA channel mode, * auto calibration on. */ ad_leave_MCE (devc); /* * Starts the calibration process and * enters playback mode after it. */ ad_unmute (devc); devc->xfer_count = cnt; devc->irq_mode = IMODE_INPUT; devc->intr_active = 1; INB (io_Status (devc)); OUTB (0, io_Status (devc)); /* Clear interrupt status */ RESTORE_INTR (flags); } static int ad1848_prepare_for_IO (int dev, int bsize, int bcount) { int timeout; unsigned char fs; unsigned long flags; ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; DISABLE_INTR (flags); ad_enter_MCE (devc); /* Enables changes to the format select reg */ fs = devc->speed_bits | (devc->format_bits << 5); if (devc->channels > 1) fs |= 0x10; ad_write (devc, 8, fs); /* * Write to I8 starts resyncronization. Wait until it completes. */ timeout = 10000; while (timeout > 0 && INB (devc->base) == 0x80) timeout--; /* * If mode == 2 (CS4231), set I28 also. It's the capture format register. */ if (devc->mode == 2) { ad_write (devc, 28, fs); /* * Write to I28 starts resyncronization. Wait until it completes. */ timeout = 10000; while (timeout > 0 && INB (devc->base) == 0x80) timeout--; } ad_leave_MCE (devc); /* * Starts the calibration process and * enters playback mode after it. */ RESTORE_INTR (flags); devc->xfer_count = 0; return 0; } static void ad1848_reset (int dev) { ad1848_halt (dev); } static void ad1848_halt (int dev) { ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; ad_mute (devc); ad_write (devc, 9, ad_read (devc, 9) & ~0x03); /* Stop DMA */ OUTB (0, io_Status (devc)); /* Clear interrupt status */ ad_enter_MCE (devc); OUTB (0, io_Status (devc)); /* Clear interrupt status */ ad_write (devc, 15, 0); /* Clear DMA counter */ ad_write (devc, 14, 0); /* Clear DMA counter */ if (devc->mode == 2) { ad_write (devc, 30, 0); /* Clear DMA counter */ ad_write (devc, 31, 0); /* Clear DMA counter */ } ad_write (devc, 9, ad_read (devc, 9) & ~0x03); /* Stop DMA */ OUTB (0, io_Status (devc)); /* Clear interrupt status */ OUTB (0, io_Status (devc)); /* Clear interrupt status */ ad_leave_MCE (devc); DMAbuf_reset_dma (dev); } int ad1848_detect (int io_base) { unsigned char tmp; int i; ad1848_info *devc = &dev_info[nr_ad1848_devs]; unsigned char tmp1 = 0xff, tmp2 = 0xff; if (nr_ad1848_devs >= MAX_AUDIO_DEV) { AUDIO_DDB (printk ("ad1848 detect error - step 0\n")); return 0; } devc->base = io_base; devc->MCE_bit = 0x40; devc->irq = 0; devc->dma_capture = 0; devc->dma_playback = 0; devc->opened = 0; devc->chip_name = "AD1848"; devc->mode = 1; /* MODE1 = original AD1848 */ /* * Check that the I/O address is in use. * * The bit 0x80 of the base I/O port is known to be 0 after the * chip has performed it's power on initialization. Just assume * this has happened before the OS is starting. * * If the I/O address is unused, it typically returns 0xff. */ if ((INB (devc->base) & 0x80) != 0x00) /* Not a AD1884 */ { AUDIO_DDB (printk ("ad1848 detect error - step A\n")); return 0; } /* * Test if it's possible to change contents of the indirect registers. * Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only * so try to avoid using it. */ ad_write (devc, 0, 0xaa); ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */ if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45) { AUDIO_DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2)); return 0; } ad_write (devc, 0, 0x45); ad_write (devc, 1, 0xaa); if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa) { AUDIO_DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2)); return 0; } /* * The indirect register I12 has some read only bits. Lets * try to change them. */ tmp = ad_read (devc, 12); ad_write (devc, 12, (~tmp) & 0x0f); if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f)) { AUDIO_DDB (printk ("ad1848 detect error - step D (%x)\n", tmp1)); return 0; } /* * NOTE! Last 4 bits of the reg I12 tell the chip revision. * 0x01=RevB and 0x0A=RevC. */ /* * The original AD1848/CS4248 has just 15 indirect registers. This means * that I0 and I16 should return the same value (etc.). * Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails * with CS4231. */ ad_write (devc, 12, 0); /* Mode2=disabled */ for (i = 0; i < 16; i++) if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16))) { AUDIO_DDB (printk ("ad1848 detect error - step F(%d/%x/%x)\n", i, tmp1, tmp2)); return 0; } /* * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40). * The bit 0x80 is always 1 in CS4248 and CS4231. */ ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */ tmp1 = ad_read (devc, 12); if (tmp1 & 0x80) devc->chip_name = "CS4248"; /* Our best knowledge just now */ if ((tmp1 & 0xc0) == (0x80 | 0x40)) { /* * CS4231 detected - is it? * * Verify that setting I0 doesn't change I16. */ ad_write (devc, 16, 0); /* Set I16 to known value */ ad_write (devc, 0, 0x45); if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */ { ad_write (devc, 0, 0xaa); if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */ { AUDIO_DDB (printk ("ad1848 detect error - step H(%x)\n", tmp1)); return 0; } /* * Verify that some bits of I25 are read only. */ tmp1 = ad_read (devc, 25); /* Original bits */ ad_write (devc, 25, ~tmp1); /* Invert all bits */ if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7)) { /* * It's a CS4231 */ devc->chip_name = "CS4231"; #ifdef MOZART_PORT if (devc->base != MOZART_PORT) #endif devc->mode = 2; } ad_write (devc, 25, tmp1); /* Restore bits */ } } return 1; } void ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture) { /* * NOTE! If irq < 0, there is another driver which has allocated the IRQ * so that this driver doesn't need to allocate/deallocate it. * The actually used IRQ is ABS(irq). */ /* * Initial values for the indirect registers of CS4248/AD1848. */ static int init_values[] = { 0xa8, 0xa8, 0x08, 0x08, 0x08, 0x08, 0x80, 0x80, 0x00, 0x08, 0x02, 0x00, 0x8a, 0x01, 0x00, 0x00, /* Positions 16 to 31 just for CS4231 */ 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int i, my_dev; ad1848_info *devc = &dev_info[nr_ad1848_devs]; if (!ad1848_detect (io_base)) return; devc->irq = (irq > 0) ? irq : 0; devc->dma_capture = dma_playback; devc->dma_playback = dma_capture; devc->opened = 0; if (nr_ad1848_devs != 0) { memcpy ((char *) &ad1848_pcm_operations[nr_ad1848_devs], (char *) &ad1848_pcm_operations[0], sizeof (struct audio_operations)); } for (i = 0; i < 16; i++) ad_write (devc, i, init_values[i]); ad_mute (devc); if (devc->mode == 2) { ad_write (devc, 12, ad_read (devc, 12) | 0x40); /* Mode2 = enabled */ for (i = 16; i < 32; i++) ad_write (devc, i, init_values[i]); } OUTB (0, io_Status (devc)); /* Clear pending interrupts */ if (name[0] != 0) sprintf (ad1848_pcm_operations[nr_ad1848_devs].name, "%s (%s)", name, devc->chip_name); else sprintf (ad1848_pcm_operations[nr_ad1848_devs].name, "Generic audio codec (%s)", devc->chip_name); #if defined(__FreeBSD__) if (strcmp(name, "MS Sound System")) /* *sigh* */ printk ("\ngus0: <%s>", ad1848_pcm_operations[nr_ad1848_devs].name); else printk ("mss0: <%s>", ad1848_pcm_operations[nr_ad1848_devs].name); #else printk (" <%s>", ad1848_pcm_operations[nr_ad1848_devs].name); #endif if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs]; if (irq > 0) irq2dev[irq] = my_dev; else if (irq < 0) irq2dev[-irq] = my_dev; audio_devs[my_dev]->dmachan = dma_playback; audio_devs[my_dev]->buffcount = 1; - audio_devs[my_dev]->buffsize = DSP_BUFFSIZE * 2; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; audio_devs[my_dev]->devc = devc; audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode]; nr_ad1848_devs++; /* * Toggle the MCE bit. It completes the initialization phase. */ ad_enter_MCE (devc); /* In case the bit was off */ ad_leave_MCE (devc); if (num_mixers < MAX_MIXER_DEV) { mixer2codec[num_mixers] = my_dev + 1; audio_devs[my_dev]->mixer_dev = num_mixers; mixer_devs[num_mixers++] = &ad1848_mixer_operations; ad1848_mixer_reset (devc); } } else printk ("AD1848: Too many PCM devices available\n"); } void ad1848_interrupt (INT_HANDLER_PARMS (irq, dummy)) { unsigned char status; ad1848_info *devc; int dev; if (irq < 0 || irq > 15) return; /* Bogus irq */ dev = irq2dev[irq]; if (dev < 0 || dev >= num_audiodevs) return; /* Bogus dev */ devc = (ad1848_info *) audio_devs[dev]->devc; status = INB (io_Status (devc)); if (status == 0x80) printk ("ad1848_interrupt: Why?\n"); if (status & 0x01) { if (devc->opened && devc->irq_mode == IMODE_OUTPUT) { DMAbuf_outputintr (dev, 1); } if (devc->opened && devc->irq_mode == IMODE_INPUT) DMAbuf_inputintr (dev); } OUTB (0, io_Status (devc)); /* Clear interrupt status */ status = INB (io_Status (devc)); if (status == 0x80 || status & 0x01) { printk ("ad1848: Problems when clearing interrupt, status=%x\n", status); OUTB (0, io_Status (devc)); /* Try again */ } } #ifdef MOZART_PORT /* * Experimental initialization sequence for Mozart soundcard * (OAK OTI-601 sound chip). * by Gregor Hoffleit * Some comments by Hannu Savolainen. */ int mozart_init (int io_base) { int i; unsigned char byte; static int mozart_detected_here = 0; /* * Valid ports are 0x530 and 0xf40. The DOS based software doesn't allow * other ports. The OTI-601 preliminary specification says that * 0xe80 and 0x604 are also possible but it's safest to ignore them. */ if ((io_base != 0x530) && (io_base != 0xf40)) { printk ("Mozart: invalid io_base(%x)\n", io_base); return 0; } if (mozart_detected_here == io_base) /* Already detected this card */ return 1; if (mozart_detected_here != 0) return 0; /* Don't allow detecting another Mozart card. */ /* * The Mozart chip (OAK OTI-601) must be enabled before _each_ write * by writing a secret password (0xE2) to the password register (0xf8f). * Any I/O cycle after writing the password closes the gate and disbles * further access. */ if (INB (0xf88) != 0) /* Appears to return 0 while the gate is closed */ { AUDIO_DDB (printk ("No Mozart signature detected on port 0xf88\n")); return 0; } OUTB (0xe2, 0xf8f); /* A secret password which opens the gate */ OUTB (0x10, 0xf91); /* Enable access to codec registers during SB mode */ for (i = 0; i < 100; i++) /* Delay */ tenmicrosec (); OUTB (0xe2, 0xf8f); /* Sesam */ byte = INB (0xf8d); /* Read MC1 (Mode control register) */ /* Read the same register again but with gate closed at this time. */ if (INB (0xf8d) == 0xff) /* Bus float. Should be 0 if Mozart present */ { AUDIO_DDB (printk ("Seems to be no Mozart chip set\n")); return 0; } AUDIO_DDB (printk ("mozart_init: read 0x%x on 0xf8d\n", byte)); byte = byte | 0x80; /* Switch to WSS mode (disables SB) */ byte = byte & 0xcf; /* Clear sound base, disable CD, enable joystick */ if (io_base == 0xf40) byte = byte | 0x20; for (i = 0; i < 100; i++) tenmicrosec (); OUTB (0xe2, 0xf8f); /* Open the gate again */ OUTB (byte, 0xf8d); /* Write the modified value back to MC1 */ AUDIO_DDB (printk ("mozart_init: wrote 0x%x on 0xf8d\n", byte)); OUTB (0xe2, 0xf8f); /* Here we come again */ OUTB (0x20, 0xf91); /* Protect WSS shadow registers against write */ for (i = 0; i < 1000; i++) tenmicrosec (); return 1; } #endif /* MOZART_PORT */ #ifdef OPTI_MAD16_PORT #include "mad16.h" #endif /* * Some extra code for the MS Sound System */ int probe_ms_sound (struct address_info *hw_config) { #if !defined(EXCLUDE_AEDSP16) && defined(AEDSP16_MSS) /* * Initialize Audio Excel DSP 16 to MSS: before any operation * we must enable MSS I/O ports. */ InitAEDSP16_MSS (hw_config); #endif /* * Check if the IO port returns valid signature. The original MS Sound * system returns 0x04 while some cards (AudioTriX Pro for example) * return 0x00. */ #ifdef MOZART_PORT if (hw_config->io_base == MOZART_PORT) mozart_init (hw_config->io_base); #endif #ifdef OPTI_MAD16_PORT if (hw_config->io_base == OPTI_MAD16_PORT) mad16init (hw_config->io_base); #endif if ((INB (hw_config->io_base + 3) & 0x3f) != 0x04 && (INB (hw_config->io_base + 3) & 0x3f) != 0x00) { AUDIO_DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, INB (hw_config->io_base + 3))); return 0; } if (hw_config->irq > 11) { printk ("MSS: Bad IRQ %d\n", hw_config->irq); return 0; } if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) { printk ("MSS: Bad DMA %d\n", hw_config->dma); return 0; } /* * Check that DMA0 is not in use with a 8 bit board. */ if (hw_config->dma == 0 && INB (hw_config->io_base + 3) & 0x80) { printk ("MSS: Can't use DMA0 with a 8 bit card/slot\n"); return 0; } if (hw_config->irq > 7 && hw_config->irq != 9 && INB (hw_config->io_base + 3) & 0x80) { printk ("MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); return 0; } return ad1848_detect (hw_config->io_base + 4); } long attach_ms_sound (long mem_start, struct address_info *hw_config) { static char interrupt_bits[12] = { -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 }; char bits; static char dma_bits[4] = { 1, 2, 0, 3 }; int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; if (!ad1848_detect (hw_config->io_base + 4)) return mem_start; /* * Set the IRQ and DMA addresses. */ bits = interrupt_bits[hw_config->irq]; if (bits == -1) return mem_start; OUTB (bits | 0x40, config_port); if ((INB (version_port) & 0x40) == 0) printk ("[IRQ Conflict?]"); OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */ ad1848_init ("MS Sound System", hw_config->io_base + 4, hw_config->irq, hw_config->dma, hw_config->dma); return mem_start; } #endif diff --git a/sys/i386/isa/sound/dmabuf.c b/sys/i386/isa/sound/dmabuf.c index 72ff2f794d17..21d54cdbc53f 100644 --- a/sys/i386/isa/sound/dmabuf.c +++ b/sys/i386/isa/sound/dmabuf.c @@ -1,1105 +1,1108 @@ /* * sound/dmabuf.c * * The DMA buffer manager for digitized voice applications * * Copyright by Hannu Savolainen 1993, 1994, 1995 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 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. * */ #include "sound_config.h" #ifdef CONFIGURE_SOUNDCARD #if !defined(EXCLUDE_AUDIO) || !defined(EXCLUDE_GUS) DEFINE_WAIT_QUEUES (dev_sleeper[MAX_AUDIO_DEV], dev_sleep_flag[MAX_AUDIO_DEV]); static struct dma_buffparms dmaps[MAX_AUDIO_DEV] = { {0}}; /* * Primitive way to allocate * such a large array. * Needs dynamic run-time alloction. */ static void reorganize_buffers (int dev) { /* * This routine breaks the physical device buffers to logical ones. */ struct dma_buffparms *dmap = audio_devs[dev]->dmap; struct audio_operations *dsp_dev = audio_devs[dev]; unsigned i, p, n; unsigned sr, nc, sz, bsz; if (dmap->fragment_size == 0) { /* Compute the fragment size using the default algorithm */ sr = dsp_dev->ioctl (dev, SOUND_PCM_READ_RATE, 0, 1); nc = dsp_dev->ioctl (dev, SOUND_PCM_READ_CHANNELS, 0, 1); sz = dsp_dev->ioctl (dev, SOUND_PCM_READ_BITS, 0, 1); if (sr < 1 || nc < 1 || sz < 1) { printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz); sr = DSP_DEFAULT_SPEED; nc = 1; sz = 8; } - sz /= 8; /* #bits -> #bytes */ - sz = sr * nc * sz; + sz /= 8; /* #bits -> #bytes */ + /* * Compute a buffer size for time not exeeding 1 second. * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds * of sound (using the current speed, sample size and #channels). */ bsz = dsp_dev->buffsize; while (bsz > sz) bsz /= 2; if (dsp_dev->buffcount == 1 && bsz == dsp_dev->buffsize) bsz /= 2; /* Needs at least 2 buffers */ if (dmap->subdivision == 0) /* Not already set */ dmap->subdivision = 1; /* Init to default value */ + else + bsz /= dmap->subdivision; - bsz /= dmap->subdivision; - - if (bsz < 64) - bsz = 4096; /* Just a sanity check */ + if (bsz < 16) + bsz = 16; /* Just a sanity check */ while ((dsp_dev->buffsize * dsp_dev->buffcount) / bsz > MAX_SUB_BUFFERS) bsz *= 2; dmap->fragment_size = bsz; } else { /* * The process has specified the buffer sice with SNDCTL_DSP_SETFRAGMENT or * the buffer sice computation has already been done. */ - if (dmap->fragment_size > audio_devs[dev]->buffsize) - dmap->fragment_size = audio_devs[dev]->buffsize; + if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2)) + dmap->fragment_size = (audio_devs[dev]->buffsize / 2); bsz = dmap->fragment_size; } + bsz &= ~0x03; /* Force size which is multiple of 4 bytes */ + /* * Now computing addresses for the logical buffers */ n = 0; for (i = 0; i < dmap->raw_count && n < dmap->max_fragments && n < MAX_SUB_BUFFERS; i++) { p = 0; while ((p + bsz) <= dsp_dev->buffsize && n < dmap->max_fragments && n < MAX_SUB_BUFFERS) { dmap->buf[n] = dmap->raw_buf[i] + p; dmap->buf_phys[n] = dmap->raw_buf_phys[i] + p; p += bsz; n++; } } dmap->nbufs = n; dmap->bytes_in_use = n * bsz; for (i = 0; i < dmap->nbufs; i++) { dmap->counts[i] = 0; } dmap->flags |= DMA_ALLOC_DONE; } static void dma_init_buffers (int dev) { struct dma_buffparms *dmap = audio_devs[dev]->dmap = &dmaps[dev]; RESET_WAIT_QUEUE (dev_sleeper[dev], dev_sleep_flag[dev]); dmap->flags = DMA_BUSY; /* Other flags off */ dmap->qlen = dmap->qhead = dmap->qtail = 0; + dmap->nbufs = 1; + dmap->bytes_in_use = audio_devs[dev]->buffsize; dmap->dma_mode = DMODE_NONE; } int DMAbuf_open (int dev, int mode) { int retval; struct dma_buffparms *dmap = NULL; if (dev >= num_audiodevs) { printk ("PCM device %d not installed.\n", dev); return RET_ERROR (ENXIO); } if (!audio_devs[dev]) { printk ("PCM device %d not initialized\n", dev); return RET_ERROR (ENXIO); } dmap = audio_devs[dev]->dmap = &dmaps[dev]; if (dmap->flags & DMA_BUSY) return RET_ERROR (EBUSY); #ifdef USE_RUNTIME_DMAMEM dmap->raw_buf[0] = NULL; sound_dma_malloc (dev); #endif if (dmap->raw_buf[0] == NULL) return RET_ERROR (ENOSPC); /* Memory allocation failed during boot */ if ((retval = audio_devs[dev]->open (dev, mode)) < 0) return retval; dmap->open_mode = mode; dmap->subdivision = dmap->underrun_count = 0; dmap->fragment_size = 0; dmap->max_fragments = 65536; /* Just a large value */ dma_init_buffers (dev); audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_BITS, 8, 1); audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_CHANNELS, 1, 1); audio_devs[dev]->ioctl (dev, SOUND_PCM_WRITE_RATE, DSP_DEFAULT_SPEED, 1); return 0; } static void dma_reset (int dev) { int retval; unsigned long flags; DISABLE_INTR (flags); audio_devs[dev]->reset (dev); audio_devs[dev]->close (dev); if ((retval = audio_devs[dev]->open (dev, audio_devs[dev]->dmap->open_mode)) < 0) printk ("Sound: Reset failed - Can't reopen device\n"); RESTORE_INTR (flags); dma_init_buffers (dev); reorganize_buffers (dev); } static int dma_sync (int dev) { unsigned long flags; if (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT) { DISABLE_INTR (flags); while (!PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev]) && audio_devs[dev]->dmap->qlen) { DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 10 * HZ); if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) { RESTORE_INTR (flags); return audio_devs[dev]->dmap->qlen; } } RESTORE_INTR (flags); /* * Some devices such as GUS have huge amount of on board RAM for the * audio data. We have to wait until the device has finished playing. */ DISABLE_INTR (flags); if (audio_devs[dev]->local_qlen) /* Device has hidden buffers */ { while (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) && audio_devs[dev]->local_qlen (dev)) { DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], HZ); } } RESTORE_INTR (flags); } return audio_devs[dev]->dmap->qlen; } int DMAbuf_release (int dev, int mode) { unsigned long flags; if (!(PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) && (audio_devs[dev]->dmap->dma_mode == DMODE_OUTPUT)) { dma_sync (dev); } #ifdef USE_RUNTIME_DMAMEM sound_dma_free (dev); #endif DISABLE_INTR (flags); audio_devs[dev]->reset (dev); audio_devs[dev]->close (dev); audio_devs[dev]->dmap->dma_mode = DMODE_NONE; audio_devs[dev]->dmap->flags &= ~DMA_BUSY; RESTORE_INTR (flags); return 0; } int DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) { unsigned long flags; int err = EIO; struct dma_buffparms *dmap = audio_devs[dev]->dmap; DISABLE_INTR (flags); if (!dmap->qlen) { if (dmap->flags & DMA_RESTART) { dma_reset (dev); dmap->flags &= ~DMA_RESTART; } if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ { dma_sync (dev); dma_reset (dev); dmap->dma_mode = DMODE_NONE; } if (!(dmap->flags & DMA_ALLOC_DONE)) reorganize_buffers (dev); if (!dmap->dma_mode) { int err; if ((err = audio_devs[dev]->prepare_for_input (dev, dmap->fragment_size, dmap->nbufs)) < 0) { RESTORE_INTR (flags); return err; } dmap->dma_mode = DMODE_INPUT; } if (!(dmap->flags & DMA_ACTIVE)) { audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail], dmap->fragment_size, 0, !(audio_devs[dev]->flags & DMA_AUTOMODE) || !(dmap->flags & DMA_STARTED)); dmap->flags |= DMA_ACTIVE | DMA_STARTED; } if (dontblock) { RESTORE_INTR (flags); #if defined(__FreeBSD__) return RET_ERROR (EWOULDBLOCK); #else return RET_ERROR (EAGAIN); #endif } /* Wait for the next block */ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) { printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); dma_reset (dev); err = EIO; SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); } else err = EINTR; } RESTORE_INTR (flags); if (!dmap->qlen) return RET_ERROR (err); *buf = &dmap->buf[dmap->qhead][dmap->counts[dmap->qhead]]; *len = dmap->fragment_size - dmap->counts[dmap->qhead]; return dmap->qhead; } int DMAbuf_rmchars (int dev, int buff_no, int c) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; int p = dmap->counts[dmap->qhead] + c; if (p >= dmap->fragment_size) { /* This buffer is completely empty */ dmap->counts[dmap->qhead] = 0; if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); dmap->qlen--; dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; } else dmap->counts[dmap->qhead] = p; return 0; } int DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; switch (cmd) { case SNDCTL_DSP_RESET: dma_reset (dev); return 0; break; case SNDCTL_DSP_SYNC: dma_sync (dev); dma_reset (dev); return 0; break; case SNDCTL_DSP_GETBLKSIZE: if (!(dmap->flags & DMA_ALLOC_DONE)) reorganize_buffers (dev); return IOCTL_OUT (arg, dmap->fragment_size); break; case SNDCTL_DSP_SETBLKSIZE: { int size = IOCTL_IN(arg); if(!(dmap->flags & DMA_ALLOC_DONE) && size) { dmap->fragment_size = size; return 0; } else return RET_ERROR (EINVAL); /* Too late to change */ } break; case SNDCTL_DSP_SUBDIVIDE: { int fact = IOCTL_IN (arg); if (fact == 0) { fact = dmap->subdivision; if (fact == 0) fact = 1; return IOCTL_OUT (arg, fact); } if (dmap->subdivision != 0 || dmap->fragment_size) /* Loo late to change */ return RET_ERROR (EINVAL); if (fact > MAX_REALTIME_FACTOR) return RET_ERROR (EINVAL); if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16) return RET_ERROR (EINVAL); dmap->subdivision = fact; return IOCTL_OUT (arg, fact); } break; case SNDCTL_DSP_SETFRAGMENT: { int fact = IOCTL_IN (arg); int bytes, count; if (fact == 0) return RET_ERROR (EIO); if (dmap->subdivision != 0 || dmap->fragment_size) /* Loo late to change */ return RET_ERROR (EINVAL); bytes = fact & 0xffff; count = (fact >> 16) & 0xffff; if (count == 0) count = MAX_SUB_BUFFERS; if (bytes < 7 || bytes > 17) /* <64 || > 128k */ return RET_ERROR (EINVAL); if (count < 2) return RET_ERROR (EINVAL); dmap->fragment_size = (1 << bytes); dmap->max_fragments = count; if (dmap->fragment_size > audio_devs[dev]->buffsize) dmap->fragment_size = audio_devs[dev]->buffsize; if (dmap->fragment_size == audio_devs[dev]->buffsize && audio_devs[dev]->flags & DMA_AUTOMODE) dmap->fragment_size /= 2; /* Needs at least 2 buffers */ dmap->subdivision = 1; /* Disable SNDCTL_DSP_SUBDIVIDE */ return IOCTL_OUT (arg, bytes | (count << 16)); } break; case SNDCTL_DSP_GETISPACE: case SNDCTL_DSP_GETOSPACE: if (!local) return RET_ERROR (EINVAL); { audio_buf_info *info = (audio_buf_info *) arg; info->fragments = dmap->qlen; info->fragsize = dmap->fragment_size; info->bytes = dmap->qlen * dmap->fragment_size; } return 0; default: return audio_devs[dev]->ioctl (dev, cmd, arg, local); } } static int space_in_queue (int dev) { int len, max, tmp; struct dma_buffparms *dmap = audio_devs[dev]->dmap; if (dmap->qlen >= dmap->nbufs) /* No space at all */ return 0; /* * Verify that there are no more pending buffers than the limit * defined by the process. */ max = dmap->max_fragments; len = dmap->qlen; if (audio_devs[dev]->local_qlen) { tmp = audio_devs[dev]->local_qlen (dev); if (tmp & len) tmp--; /* * This buffer has been counted twice */ len += tmp; } if (len >= max) return 0; return 1; } int DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) { unsigned long flags; int abort, err = EIO; struct dma_buffparms *dmap = audio_devs[dev]->dmap; if (dmap->dma_mode == DMODE_INPUT) /* Direction change */ { dma_reset (dev); dmap->dma_mode = DMODE_NONE; } else if (dmap->flags & DMA_RESTART) /* Restart buffering */ { dma_sync (dev); dma_reset (dev); } dmap->flags &= ~DMA_RESTART; if (!(dmap->flags & DMA_ALLOC_DONE)) reorganize_buffers (dev); if (!dmap->dma_mode) { int err; dmap->dma_mode = DMODE_OUTPUT; if ((err = audio_devs[dev]->prepare_for_output (dev, dmap->fragment_size, dmap->nbufs)) < 0) return err; } - - if (dontblock && !space_in_queue (dev)) /* XXX */ -#if defined(__FreeBSD__) - return RET_ERROR (EWOULDBLOCK); -#else - return RET_ERROR (EAGAIN); -#endif - DISABLE_INTR (flags); abort = 0; while (!space_in_queue (dev) && !abort) { + + if (dontblock) + { + RESTORE_INTR (flags); + return RET_ERROR (EAGAIN); + } + /* * Wait for free space */ DO_SLEEP (dev_sleeper[dev], dev_sleep_flag[dev], 2 * HZ); if (TIMED_OUT (dev_sleeper[dev], dev_sleep_flag[dev])) { printk ("Sound: DMA timed out - IRQ/DRQ config error?\n"); dma_reset (dev); err = EIO; abort = 1; SET_ABORT_FLAG (dev_sleeper[dev], dev_sleep_flag[dev]); } else if (PROCESS_ABORTING (dev_sleeper[dev], dev_sleep_flag[dev])) { err = EINTR; abort = 1; } } RESTORE_INTR (flags); if (!space_in_queue (dev)) { return RET_ERROR (err); /* Caught a signal ? */ } *buf = dmap->buf[dmap->qtail]; *size = dmap->fragment_size; dmap->counts[dmap->qtail] = 0; return dmap->qtail; } int DMAbuf_start_output (int dev, int buff_no, int l) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; if (buff_no != dmap->qtail) printk ("Sound warning: DMA buffers out of sync %d != %d\n", buff_no, dmap->qtail); dmap->qlen++; if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) printk ("\nSound: Audio queue2 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); dmap->counts[dmap->qtail] = l; if ((l != dmap->fragment_size) && ((audio_devs[dev]->flags & DMA_AUTOMODE) && audio_devs[dev]->flags & NEEDS_RESTART)) dmap->flags |= DMA_RESTART; else dmap->flags &= ~DMA_RESTART; dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; if (!(dmap->flags & DMA_ACTIVE)) { dmap->flags |= DMA_ACTIVE; audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead], dmap->counts[dmap->qhead], 0, !(audio_devs[dev]->flags & DMA_AUTOMODE) || !(dmap->flags & DMA_STARTED)); dmap->flags |= DMA_STARTED; } return 0; } int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) { int chan = audio_devs[dev]->dmachan; struct dma_buffparms *dmap = audio_devs[dev]->dmap; unsigned long flags; /* * This function is not as portable as it should be. */ /* * The count must be one less than the actual size. This is handled by * set_dma_addr() */ if (audio_devs[dev]->flags & DMA_AUTOMODE) { /* * Auto restart mode. Transfer the whole * * buffer */ #ifdef linux DISABLE_INTR (flags); disable_dma (chan); clear_dma_ff (chan); set_dma_mode (chan, dma_mode | DMA_AUTOINIT); set_dma_addr (chan, dmap->raw_buf_phys[0]); set_dma_count (chan, dmap->bytes_in_use); enable_dma (chan); RESTORE_INTR (flags); #else #if defined(__FreeBSD__) isa_dmastart (B_RAW | ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE), (caddr_t)dmap->raw_buf_phys[0], dmap->bytes_in_use, chan); #else /* else __FreeBSD__ */ #if defined(GENERIC_SYSV) #ifndef DMAMODE_AUTO printk ("sound: Invalid DMA mode for device %d\n", dev); #endif #if defined(SVR42) /* ** send full count to snd_dma_prog, it will take care of subtracting ** one if it is required. */ snd_dma_prog (chan, dmap->raw_buf_phys[0], dmap->bytes_in_use, dma_mode, TRUE); #else /* !SVR42 */ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode) #ifdef DMAMODE_AUTO | DMAMODE_AUTO #endif , dmap->raw_buf_phys[0], dmap->bytes_in_use - 1); dma_enable (chan); #endif /* ! SVR42 */ #else #error This routine is not valid for this OS. #endif #endif #endif } else { #ifdef linux DISABLE_INTR (flags); disable_dma (chan); clear_dma_ff (chan); set_dma_mode (chan, dma_mode); set_dma_addr (chan, physaddr); set_dma_count (chan, count); enable_dma (chan); RESTORE_INTR (flags); #else #if defined(__FreeBSD__) isa_dmastart ((dma_mode == DMA_MODE_READ) ? B_READ : B_WRITE, (caddr_t)physaddr, count, chan); #else /* FreeBSD */ #if defined(GENERIC_SYSV) #if defined(SVR42) snd_dma_prog (chan, physaddr, count, dma_mode, FALSE); #else /* ! SVR42 */ dma_param (chan, ((dma_mode == DMA_MODE_READ) ? DMA_Rdmode : DMA_Wrmode), physaddr, count); dma_enable (chan); #endif /* SVR42 */ #else #error This routine is not valid for this OS. #endif /* GENERIC_SYSV */ #endif #endif } return count; } long DMAbuf_init (long mem_start) { int dev; #if defined(SVR42) snd_dma_init (); #endif /* SVR42 */ /* * NOTE! This routine could be called several times. */ for (dev = 0; dev < num_audiodevs; dev++) audio_devs[dev]->dmap = &dmaps[dev]; return mem_start; } void DMAbuf_outputintr (int dev, int event_type) { /* * Event types: * 0 = DMA transfer done. Device still has more data in the local * buffer. * 1 = DMA transfer done. Device doesn't have local buffer or it's * empty now. * 2 = No DMA transfer but the device has now more space in it's local * buffer. */ unsigned long flags; struct dma_buffparms *dmap = audio_devs[dev]->dmap; #if defined(SVR42) snd_dma_intr (audio_devs[dev]->dmachan); #endif /* SVR42 */ if (event_type != 2) { if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) { printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); return; } dmap->qlen--; dmap->qhead = (dmap->qhead + 1) % dmap->nbufs; dmap->flags &= ~DMA_ACTIVE; if (dmap->qlen) { audio_devs[dev]->output_block (dev, dmap->buf_phys[dmap->qhead], dmap->counts[dmap->qhead], 1, !(audio_devs[dev]->flags & DMA_AUTOMODE)); dmap->flags |= DMA_ACTIVE; } else if (event_type == 1) { dmap->underrun_count++; audio_devs[dev]->halt_xfer (dev); if ((audio_devs[dev]->flags & DMA_AUTOMODE) && audio_devs[dev]->flags & NEEDS_RESTART) dmap->flags |= DMA_RESTART; else dmap->flags &= ~DMA_RESTART; } } /* event_type != 2 */ DISABLE_INTR (flags); if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) { WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); } RESTORE_INTR (flags); #if defined(__FreeBSD__) if(selinfo[dev].si_pid) selwakeup(&selinfo[dev]); #endif } void DMAbuf_inputintr (int dev) { unsigned long flags; struct dma_buffparms *dmap = audio_devs[dev]->dmap; #if defined(SVR42) snd_dma_intr (audio_devs[dev]->dmachan); #endif /* SVR42 */ if (dmap->qlen == (dmap->nbufs - 1)) { #if !defined(__FreeBSD__) /* ignore console message. */ printk ("Sound: Recording overrun\n"); #endif dmap->underrun_count++; audio_devs[dev]->halt_xfer (dev); dmap->flags &= ~DMA_ACTIVE; if (audio_devs[dev]->flags & DMA_AUTOMODE) dmap->flags |= DMA_RESTART; else dmap->flags &= ~DMA_RESTART; } else { dmap->qlen++; if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) printk ("\nSound: Audio queue4 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs); dmap->qtail = (dmap->qtail + 1) % dmap->nbufs; audio_devs[dev]->start_input (dev, dmap->buf_phys[dmap->qtail], dmap->fragment_size, 1, !(audio_devs[dev]->flags & DMA_AUTOMODE)); dmap->flags |= DMA_ACTIVE; } DISABLE_INTR (flags); if (SOMEONE_WAITING (dev_sleeper[dev], dev_sleep_flag[dev])) { WAKE_UP (dev_sleeper[dev], dev_sleep_flag[dev]); } RESTORE_INTR (flags); #if defined(__FreeBSD__) if(selinfo[dev].si_pid) selwakeup(&selinfo[dev]); #endif } int DMAbuf_open_dma (int dev) { unsigned long flags; int chan = audio_devs[dev]->dmachan; if (ALLOC_DMA_CHN (chan, audio_devs[dev]->name)) { printk ("Unable to grab DMA%d for the audio driver\n", chan); return RET_ERROR (EBUSY); } DISABLE_INTR (flags); #ifdef linux disable_dma (chan); clear_dma_ff (chan); #endif RESTORE_INTR (flags); return 0; } void DMAbuf_close_dma (int dev) { int chan = audio_devs[dev]->dmachan; DMAbuf_reset_dma (dev); RELEASE_DMA_CHN (chan); } void DMAbuf_reset_dma (int dev) { #if 0 int chan = audio_devs[dev]->dmachan; disable_dma (chan); #endif } #ifdef ALLOW_SELECT int DMAbuf_select (int dev, struct fileinfo *file, int sel_type, select_table * wait) { struct dma_buffparms *dmap = audio_devs[dev]->dmap; unsigned long flags; switch (sel_type) { case SEL_IN: if (dmap->dma_mode != DMODE_INPUT) return 0; DISABLE_INTR (flags); if (!dmap->qlen) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else dev_sleep_flag[dev].mode = WK_SLEEP; select_wait (&dev_sleeper[dev], wait); #endif RESTORE_INTR (flags); return 0; } RESTORE_INTR (flags); return 1; break; case SEL_OUT: if (dmap->dma_mode == DMODE_INPUT) return 0; if (dmap->dma_mode == DMODE_NONE) return 1; DISABLE_INTR (flags); if (!space_in_queue (dev)) { #if defined(__FreeBSD__) selrecord(wait, &selinfo[dev]); #else dev_sleep_flag[dev].mode = WK_SLEEP; select_wait (&dev_sleeper[dev], wait); #endif RESTORE_INTR (flags); return 0; } RESTORE_INTR (flags); return 1; break; case SEL_EX: return 0; } return 0; } #endif /* ALLOW_SELECT */ #else /* EXCLUDE_AUDIO */ /* * Stub versions if audio services not included */ int DMAbuf_open (int dev, int mode) { return RET_ERROR (ENXIO); } int DMAbuf_release (int dev, int mode) { return 0; } int DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock) { return RET_ERROR (EIO); } int DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock) { return RET_ERROR (EIO); } int DMAbuf_rmchars (int dev, int buff_no, int c) { return RET_ERROR (EIO); } int DMAbuf_start_output (int dev, int buff_no, int l) { return RET_ERROR (EIO); } int DMAbuf_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { return RET_ERROR (EIO); } long DMAbuf_init (long mem_start) { return mem_start; } int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode) { return RET_ERROR (EIO); } int DMAbuf_open_dma (int dev) { return RET_ERROR (ENXIO); } void DMAbuf_close_dma (int dev) { return; } void DMAbuf_reset_dma (int dev) { return; } void DMAbuf_inputintr (int dev) { return; } void DMAbuf_outputintr (int dev, int underrun_flag) { return; } #endif #endif diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c index 67a3199ff425..8a9c17290d40 100644 --- a/sys/i386/isa/sound/pas2_pcm.c +++ b/sys/i386/isa/sound/pas2_pcm.c @@ -1,457 +1,457 @@ #define _PAS2_PCM_C_ /* * sound/pas2_pcm.c * * The low level driver for the Pro Audio Spectrum ADC/DAC. * * Copyright by Hannu Savolainen 1993 * * 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. * */ #include "sound_config.h" #ifdef CONFIGURE_SOUNDCARD #include "pas.h" static int pcm_set_bits __P((int arg)); static int pcm_set_channels __P((int arg)); static int pcm_set_speed __P((int arg)); #if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO) #define TRACE(WHAT) /* * * * (WHAT) */ #define PAS_PCM_INTRBITS (0x08) /* * Sample buffer timer interrupt enable */ #define PCM_NON 0 #define PCM_DAC 1 #define PCM_ADC 2 static unsigned long pcm_speed = 0; /* sampling rate */ static unsigned char pcm_channels = 1; /* channels (1 or 2) */ static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ static unsigned char pcm_filter = 0; /* filter FLAG */ static unsigned char pcm_mode = PCM_NON; static unsigned long pcm_count = 0; static unsigned short pcm_bitsok = 8; /* mask of OK bits */ static int my_devnum = 0; static int pcm_set_speed (int arg) { int foo, tmp; unsigned long flags; if (arg > 44100) arg = 44100; if (arg < 5000) arg = 5000; foo = (1193180 + (arg / 2)) / arg; arg = 1193180 / foo; if (pcm_channels & 2) foo = foo >> 1; pcm_speed = arg; tmp = pas_read (FILTER_FREQUENCY); /* * Set anti-aliasing filters according to sample rate. You reall *NEED* * to enable this feature for all normal recording unless you want to * experiment with aliasing effects. * These filters apply to the selected "recording" source. * I (pfw) don't know the encoding of these 5 bits. The values shown * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. */ #if !defined NO_AUTO_FILTER_SET tmp &= 0xe0; if (pcm_speed >= 2 * 17897) tmp |= 0x21; else if (pcm_speed >= 2 * 15909) tmp |= 0x22; else if (pcm_speed >= 2 * 11931) tmp |= 0x29; else if (pcm_speed >= 2 * 8948) tmp |= 0x31; else if (pcm_speed >= 2 * 5965) tmp |= 0x39; else if (pcm_speed >= 2 * 2982) tmp |= 0x24; pcm_filter = tmp; #endif DISABLE_INTR (flags); pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY); pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); pas_write (foo & 0xff, SAMPLE_RATE_TIMER); pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER); pas_write (tmp, FILTER_FREQUENCY); RESTORE_INTR (flags); return pcm_speed; } static int pcm_set_channels (int arg) { if ((arg != 1) && (arg != 2)) return pcm_channels; if (arg != pcm_channels) { pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL); pcm_channels = arg; pcm_set_speed (pcm_speed); /* * The speed must be reinitialized */ } return pcm_channels; } static int pcm_set_bits (int arg) { if ((arg & pcm_bitsok) != arg) return pcm_bits; if (arg != pcm_bits) { pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2); pcm_bits = arg; } return pcm_bits; } static int pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg)); switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return pcm_set_speed (arg); return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg))); break; case SOUND_PCM_READ_RATE: if (local) return pcm_speed; return IOCTL_OUT (arg, pcm_speed); break; case SNDCTL_DSP_STEREO: if (local) return pcm_set_channels (arg + 1) - 1; return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1); break; case SOUND_PCM_WRITE_CHANNELS: if (local) return pcm_set_channels (arg); return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg))); break; case SOUND_PCM_READ_CHANNELS: if (local) return pcm_channels; return IOCTL_OUT (arg, pcm_channels); break; case SNDCTL_DSP_SETFMT: if (local) return pcm_set_bits (arg); return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg))); break; case SOUND_PCM_READ_BITS: if (local) return pcm_bits; return IOCTL_OUT (arg, pcm_bits); case SOUND_PCM_WRITE_FILTER: /* * NOT YET IMPLEMENTED */ if (IOCTL_IN (arg) > 1) return IOCTL_OUT (arg, RET_ERROR (EINVAL)); break; pcm_filter = IOCTL_IN (arg); case SOUND_PCM_READ_FILTER: return IOCTL_OUT (arg, pcm_filter); break; default: return RET_ERROR (EINVAL); } return RET_ERROR (EINVAL); } static void pas_pcm_reset (int dev) { TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n")); pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); } static int pas_pcm_open (int dev, int mode) { int err; TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode)); if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0) return err; if (DMAbuf_open_dma (dev) < 0) { pas_remove_intr (PAS_PCM_INTRBITS); return RET_ERROR (EBUSY); } pcm_count = 0; return 0; } static void pas_pcm_close (int dev) { unsigned long flags; TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n")); DISABLE_INTR (flags); pas_pcm_reset (dev); DMAbuf_close_dma (dev); pas_remove_intr (PAS_PCM_INTRBITS); pcm_mode = PCM_NON; RESTORE_INTR (flags); } static void pas_pcm_output_block (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags, cnt; TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count)); cnt = count; if (audio_devs[dev]->dmachan > 3) cnt >>= 1; if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == pcm_count) return; /* * Auto mode on. No need to react */ DISABLE_INTR (flags); pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); if (restart_dma) DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); if (audio_devs[dev]->dmachan > 3) count >>= 1; if (count != pcm_count) { pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pcm_count = count; } pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL); pcm_mode = PCM_DAC; RESTORE_INTR (flags); } static void pas_pcm_start_input (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags; int cnt; TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count)); cnt = count; if (audio_devs[dev]->dmachan > 3) cnt >>= 1; if (audio_devs[my_devnum]->flags & DMA_AUTOMODE && intrflag && cnt == pcm_count) return; /* * Auto mode on. No need to react */ DISABLE_INTR (flags); if (restart_dma) DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); if (audio_devs[dev]->dmachan > 3) count >>= 1; if (count != pcm_count) { pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL); pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER); pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER); pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY); pcm_count = count; } pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY); pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL); pcm_mode = PCM_ADC; RESTORE_INTR (flags); } static int pas_pcm_prepare_for_input (int dev, int bsize, int bcount) { return 0; } static int pas_pcm_prepare_for_output (int dev, int bsize, int bcount) { return 0; } static struct audio_operations pas_pcm_operations = { "Pro Audio Spectrum", DMA_AUTOMODE, AFMT_U8 | AFMT_S16_LE, NULL, pas_pcm_open, pas_pcm_close, pas_pcm_output_block, pas_pcm_start_input, pas_pcm_ioctl, pas_pcm_prepare_for_input, pas_pcm_prepare_for_output, pas_pcm_reset, pas_pcm_reset, NULL, NULL }; long pas_pcm_init (long mem_start, struct address_info *hw_config) { TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start)); pcm_bitsok = 8; if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE) pcm_bitsok |= 16; pcm_set_speed (DSP_DEFAULT_SPEED); if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations; audio_devs[my_devnum]->dmachan = hw_config->dma; audio_devs[my_devnum]->buffcount = 1; - audio_devs[my_devnum]->buffsize = 2 * DSP_BUFFSIZE; + audio_devs[my_devnum]->buffsize = DSP_BUFFSIZE; } else printk ("PAS2: Too many PCM devices available\n"); return mem_start; } void pas_pcm_interrupt (unsigned char status, int cause) { if (cause == 1) /* * PCM buffer done */ { /* * Halt the PCM first. Otherwise we don't have time to start a new * block before the PCM chip proceeds to the next sample */ if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE)) { pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL); } switch (pcm_mode) { case PCM_DAC: DMAbuf_outputintr (my_devnum, 1); break; case PCM_ADC: DMAbuf_inputintr (my_devnum); break; default: printk ("PAS: Unexpected PCM interrupt\n"); } } } #endif #endif diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c index 8bffc599636e..f5507e9bd9ab 100644 --- a/sys/i386/isa/sound/sb_dsp.c +++ b/sys/i386/isa/sound/sb_dsp.c @@ -1,1221 +1,1223 @@ /* * sound/sb_dsp.c * * The low level driver for the SoundBlaster DSP chip (SB1.0 to 2.1, SB Pro). * * Copyright by Hannu Savolainen 1994 * * 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. * * Modified: * Hunyue Yau Jan 6 1994 * Added code to support Sound Galaxy NX Pro * * JRA Gibson April 1995 * Code added for MV ProSonic/Jazz 16 in 16 bit mode */ #include "sound_config.h" #if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SB) #include "sb.h" #include "sb_mixer.h" #undef SB_TEST_IRQ int sbc_base = 0; static int sbc_irq = 0; static int open_mode = 0; /* Read, write or both */ int Jazz16_detected = 0; /* * The DSP channel can be used either for input or output. Variable * 'sb_irq_mode' will be set when the program calls read or write first time * after open. Current version doesn't support mode changes without closing * and reopening the device. Support for this feature may be implemented in a * future version of this driver. */ int sb_dsp_ok = 0; /* * * * * Set to 1 after successful * initialization * */ static int midi_disabled = 0; int sb_dsp_highspeed = 0; int sbc_major = 1, sbc_minor = 0; /* * * * * DSP version */ static int dsp_stereo = 0; static int dsp_current_speed = DSP_DEFAULT_SPEED; static int sb16 = 0; static int irq_verified = 0; int sb_midi_mode = NORMAL_MIDI; int sb_midi_busy = 0; /* * * * * 1 if the process has output * to * * MIDI */ int sb_dsp_busy = 0; volatile int sb_irq_mode = IMODE_NONE; /* * * * * IMODE_INPUT, * * IMODE_OUTPUT * * or * * IMODE_NONE */ static volatile int irq_ok = 0; #ifdef JAZZ16 /* 16 bit support */ static int dsp_16bit = 0; static int dma8 = 1; static int dma16 = 5; static int dsp_set_bits (int arg); static int initialize_ProSonic16 (void); /* end of 16 bit support */ #endif int sb_duplex_midi = 0; static int my_dev = 0; volatile int sb_intr_active = 0; static int dsp_speed (int); static int dsp_set_stereo (int mode); #if !defined(EXCLUDE_MIDI) || !defined(EXCLUDE_AUDIO) /* * Common code for the midi and pcm functions */ int sb_dsp_command (unsigned char val) { int i; unsigned long limit; limit = GET_TIME () + HZ / 10; /* * The timeout is 0.1 secods */ /* * Note! the i<500000 is an emergency exit. The sb_dsp_command() is sometimes * called while interrupts are disabled. This means that the timer is * disabled also. However the timeout situation is a abnormal condition. * Normally the DSP should be ready to accept commands after just couple of * loops. */ for (i = 0; i < 500000 && GET_TIME () < limit; i++) { if ((INB (DSP_STATUS) & 0x80) == 0) { OUTB (val, DSP_COMMAND); return 1; } } printk ("SoundBlaster: DSP Command(%x) Timeout.\n", val); printk ("IRQ conflict???\n"); return 0; } void sbintr (INT_HANDLER_PARMS (irq, dummy)) { int status; #ifndef EXCLUDE_SBPRO if (sb16) { unsigned char src = sb_getmixer (IRQ_STAT); /* Interrupt source register */ #ifndef EXCLUDE_SB16 if (src & 3) sb16_dsp_interrupt (irq); #ifndef EXCLUDE_MIDI if (src & 4) sb16midiintr (irq); /* * SB MPU401 interrupt */ #endif #endif if (!(src & 1)) return; /* * Not a DSP interupt */ } #endif status = INB (DSP_DATA_AVAIL); /* * Clear interrupt */ if (sb_intr_active) switch (sb_irq_mode) { case IMODE_OUTPUT: sb_intr_active = 0; DMAbuf_outputintr (my_dev, 1); break; case IMODE_INPUT: sb_intr_active = 0; DMAbuf_inputintr (my_dev); /* * A complete buffer has been input. Let's start new one */ break; case IMODE_INIT: sb_intr_active = 0; irq_ok = 1; break; case IMODE_MIDI: #ifndef EXCLUDE_MIDI sb_midi_interrupt (irq); #endif break; default: printk ("SoundBlaster: Unexpected interrupt\n"); } } static int sb_irq_usecount = 0; int sb_get_irq (void) { int ok; if (!sb_irq_usecount) if ((ok = snd_set_irq_handler (sbc_irq, sbintr, "SoundBlaster")) < 0) return ok; sb_irq_usecount++; return 0; } void sb_free_irq (void) { if (!sb_irq_usecount) return; sb_irq_usecount--; if (!sb_irq_usecount) snd_release_irq (sbc_irq); } int sb_reset_dsp (void) { int loopc; OUTB (1, DSP_RESET); tenmicrosec (); OUTB (0, DSP_RESET); tenmicrosec (); tenmicrosec (); tenmicrosec (); for (loopc = 0; loopc < 1000 && !(INB (DSP_DATA_AVAIL) & 0x80); loopc++); /* * Wait * for * data * * * available * status */ if (INB (DSP_READ) != 0xAA) return 0; /* * Sorry */ return 1; } #endif #ifndef EXCLUDE_AUDIO static void dsp_speaker (char state) { if (state) sb_dsp_command (DSP_CMD_SPKON); else sb_dsp_command (DSP_CMD_SPKOFF); } static int dsp_speed (int speed) { unsigned char tconst; unsigned long flags; int max_speed = 44100; if (speed < 4000) speed = 4000; /* * Older SB models don't support higher speeds than 22050. */ if (sbc_major < 2 || (sbc_major == 2 && sbc_minor == 0)) max_speed = 22050; /* * SB models earlier than SB Pro have low limit for the input speed. */ if (open_mode != OPEN_WRITE) /* Recording is possible */ if (sbc_major < 3) /* Limited input speed with these cards */ if (sbc_major == 2 && sbc_minor > 0) max_speed = 15000; else max_speed = 13000; if (speed > max_speed) speed = max_speed; /* * Invalid speed */ /* Logitech SoundMan Games and Jazz16 cards can support 44.1kHz stereo */ #if !defined (SM_GAMES) /* * Max. stereo speed is 22050 */ if (dsp_stereo && speed > 22050 && Jazz16_detected == 0) speed = 22050; #endif if ((speed > 22050) && sb_midi_busy) { printk ("SB Warning: High speed DSP not possible simultaneously with MIDI output\n"); speed = 22050; } if (dsp_stereo) speed *= 2; /* * Now the speed should be valid */ if (speed > 22050) { /* * High speed mode */ int tmp; tconst = (unsigned char) ((65536 - ((256000000 + speed / 2) / speed)) >> 8); sb_dsp_highspeed = 1; DISABLE_INTR (flags); if (sb_dsp_command (0x40)) sb_dsp_command (tconst); RESTORE_INTR (flags); tmp = 65536 - (tconst << 8); speed = (256000000 + tmp / 2) / tmp; } else { int tmp; sb_dsp_highspeed = 0; tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; DISABLE_INTR (flags); if (sb_dsp_command (0x40)) /* * Set time constant */ sb_dsp_command (tconst); RESTORE_INTR (flags); tmp = 256 - tconst; speed = (1000000 + tmp / 2) / tmp; } if (dsp_stereo) speed /= 2; dsp_current_speed = speed; return speed; } static int dsp_set_stereo (int mode) { dsp_stereo = 0; #ifdef EXCLUDE_SBPRO return 0; #else if (sbc_major < 3 || sb16) return 0; /* * Sorry no stereo */ if (mode && sb_midi_busy) { printk ("SB Warning: Stereo DSP not possible simultaneously with MIDI output\n"); return 0; } dsp_stereo = !!mode; return dsp_stereo; #endif } static void sb_dsp_output_block (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { unsigned long flags; if (!sb_irq_mode) dsp_speaker (ON); sb_irq_mode = IMODE_OUTPUT; DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); if (audio_devs[dev]->dmachan > 3) count >>= 1; count--; if (sb_dsp_highspeed) { DISABLE_INTR (flags); if (sb_dsp_command (0x48)) /* * High speed size */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); sb_dsp_command (0x91); /* * High speed 8 bit DAC */ } else printk ("SB Error: Unable to start (high speed) DAC\n"); RESTORE_INTR (flags); } else { DISABLE_INTR (flags); if (sb_dsp_command (0x14)) /* * 8-bit DAC (DMA) */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); } else printk ("SB Error: Unable to start DAC\n"); RESTORE_INTR (flags); } sb_intr_active = 1; } static void sb_dsp_start_input (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { /* * Start a DMA input to the buffer pointed by dmaqtail */ unsigned long flags; if (!sb_irq_mode) dsp_speaker (OFF); sb_irq_mode = IMODE_INPUT; DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); if (audio_devs[dev]->dmachan > 3) count >>= 1; count--; if (sb_dsp_highspeed) { DISABLE_INTR (flags); if (sb_dsp_command (0x48)) /* * High speed size */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); sb_dsp_command (0x99); /* * High speed 8 bit ADC */ } else printk ("SB Error: Unable to start (high speed) ADC\n"); RESTORE_INTR (flags); } else { DISABLE_INTR (flags); if (sb_dsp_command (0x24)) /* * 8-bit ADC (DMA) */ { sb_dsp_command ((unsigned char) (count & 0xff)); sb_dsp_command ((unsigned char) ((count >> 8) & 0xff)); } else printk ("SB Error: Unable to start ADC\n"); RESTORE_INTR (flags); } sb_intr_active = 1; } static void dsp_cleanup (void) { sb_intr_active = 0; } static int sb_dsp_prepare_for_input (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (OFF); if (sbc_major == 3) /* * SB Pro */ { #ifdef JAZZ16 /* Select correct dma channel * for 16/8 bit acccess */ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; if (dsp_stereo) sb_dsp_command (dsp_16bit ? 0xac : 0xa8); else sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0); #else /* 8 bit only cards use this */ if (dsp_stereo) sb_dsp_command (0xa8); else sb_dsp_command (0xa0); #endif dsp_speed (dsp_current_speed); /* * Speed must be recalculated if * #channels * changes */ } return 0; } static int sb_dsp_prepare_for_output (int dev, int bsize, int bcount) { dsp_cleanup (); dsp_speaker (ON); #ifndef EXCLUDE_SBPRO if (sbc_major == 3) /* * SB Pro */ { #ifdef JAZZ16 /* 16 bit specific instructions */ audio_devs[my_dev]->dmachan = dsp_16bit ? dma16 : dma8; if (Jazz16_detected != 2) /* SM Wave */ sb_mixer_set_stereo (dsp_stereo); if (dsp_stereo) sb_dsp_command (dsp_16bit ? 0xac : 0xa8); else sb_dsp_command (dsp_16bit ? 0xa4 : 0xa0); #else sb_mixer_set_stereo (dsp_stereo); #endif dsp_speed (dsp_current_speed); /* * Speed must be recalculated if * #channels * changes */ } #endif return 0; } static void sb_dsp_halt_xfer (int dev) { } static int verify_irq (void) { #if 0 DEFINE_WAIT_QUEUE (testq, testf); irq_ok = 0; if (sb_get_irq () == -1) { printk ("*** SB Error: Irq %d already in use\n", sbc_irq); return 0; } sb_irq_mode = IMODE_INIT; sb_dsp_command (0xf2); /* * This should cause immediate interrupt */ DO_SLEEP (testq, testf, HZ / 5); sb_free_irq (); if (!irq_ok) { printk ("SB Warning: IRQ%d test not passed!", sbc_irq); irq_ok = 1; } #else irq_ok = 1; #endif return irq_ok; } static int sb_dsp_open (int dev, int mode) { int retval; if (!sb_dsp_ok) { printk ("SB Error: SoundBlaster board not installed\n"); return RET_ERROR (ENXIO); } if (sb_intr_active || (sb_midi_busy && sb_midi_mode == UART_MIDI)) { printk ("SB: PCM not possible during MIDI input\n"); return RET_ERROR (EBUSY); } if (!irq_verified) { verify_irq (); irq_verified = 1; } else if (!irq_ok) printk ("SB Warning: Incorrect IRQ setting %d\n", sbc_irq); retval = sb_get_irq (); if (retval) return retval; /* Allocate 8 bit dma */ if (DMAbuf_open_dma (dev) < 0) { sb_free_irq (); printk ("SB: DMA Busy\n"); return RET_ERROR (EBUSY); } #ifdef JAZZ16 /* Allocate 16 bit dma */ if (Jazz16_detected != 0) if (dma16 != dma8) { if (ALLOC_DMA_CHN (dma16, "Jazz16 16 bit")) { sb_free_irq (); RELEASE_DMA_CHN (dma8); return RET_ERROR (EBUSY); } } #endif sb_irq_mode = IMODE_NONE; sb_dsp_busy = 1; open_mode = mode; return 0; } static void sb_dsp_close (int dev) { #ifdef JAZZ16 /* Release 16 bit dma channel */ if (Jazz16_detected) RELEASE_DMA_CHN (dma16); #endif DMAbuf_close_dma (dev); sb_free_irq (); dsp_cleanup (); dsp_speaker (OFF); sb_dsp_busy = 0; sb_dsp_highspeed = 0; open_mode = 0; } #ifdef JAZZ16 /* Function dsp_set_bits() only required for 16 bit cards */ static int dsp_set_bits (int arg) { if (arg) if (Jazz16_detected == 0) dsp_16bit = 0; else switch (arg) { case 8: dsp_16bit = 0; break; case 16: dsp_16bit = 1; break; default: dsp_16bit = 0; } return dsp_16bit ? 16 : 8; } #endif /* ifdef JAZZ16 */ static int sb_dsp_ioctl (int dev, unsigned int cmd, unsigned int arg, int local) { switch (cmd) { case SOUND_PCM_WRITE_RATE: if (local) return dsp_speed (arg); return IOCTL_OUT (arg, dsp_speed (IOCTL_IN (arg))); break; case SOUND_PCM_READ_RATE: if (local) return dsp_current_speed; return IOCTL_OUT (arg, dsp_current_speed); break; case SOUND_PCM_WRITE_CHANNELS: if (local) return dsp_set_stereo (arg - 1) + 1; return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg) - 1) + 1); break; case SOUND_PCM_READ_CHANNELS: if (local) return dsp_stereo + 1; return IOCTL_OUT (arg, dsp_stereo + 1); break; case SNDCTL_DSP_STEREO: if (local) return dsp_set_stereo (arg); return IOCTL_OUT (arg, dsp_set_stereo (IOCTL_IN (arg))); break; #ifdef JAZZ16 /* Word size specific cases here. * SNDCTL_DSP_SETFMT=SOUND_PCM_WRITE_BITS */ case SNDCTL_DSP_SETFMT: if (local) return dsp_set_bits (arg); return IOCTL_OUT (arg, dsp_set_bits (IOCTL_IN (arg))); break; case SOUND_PCM_READ_BITS: if (local) return dsp_16bit ? 16 : 8; return IOCTL_OUT (arg, dsp_16bit ? 16 : 8); break; #else case SOUND_PCM_WRITE_BITS: case SOUND_PCM_READ_BITS: if (local) return 8; return IOCTL_OUT (arg, 8); /* * Only 8 bits/sample supported */ break; #endif /* ifdef JAZZ16 */ case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: return RET_ERROR (EINVAL); break; default: return RET_ERROR (EINVAL); } return RET_ERROR (EINVAL); } static void sb_dsp_reset (int dev) { unsigned long flags; DISABLE_INTR (flags); sb_reset_dsp (); dsp_speed (dsp_current_speed); dsp_cleanup (); RESTORE_INTR (flags); } #endif #ifdef JAZZ16 /* * Initialization of a Media Vision ProSonic 16 Soundcard. * The function initializes a ProSonic 16 like PROS.EXE does for DOS. It sets * the base address, the DMA-channels, interrupts and enables the joystickport. * * Also used by Jazz 16 (same card, different name) * * written 1994 by Rainer Vranken * E-Mail: rvranken@polaris.informatik.uni-essen.de */ #ifndef MPU_BASE /* take default values if not specified */ #define MPU_BASE 0x330 #endif #ifndef MPU_IRQ #define MPU_IRQ 9 #endif unsigned int get_sb_byte (void) { int i; for (i = 1000; i; i--) if (INB (DSP_DATA_AVAIL) & 0x80) { return INB (DSP_READ); } return 0xffff; } #ifdef SM_WAVE /* * Logitech Soundman Wave detection and initialization by Hannu Savolainen. * * There is a microcontroller (8031) in the SM Wave card for MIDI emulation. * it's located at address MPU_BASE+4. MPU_BASE+7 is a SM Wave specific * control register for MC reset, SCSI, OPL4 and DSP (future expansion) * address decoding. Otherwise the SM Wave is just a ordinary MV Jazz16 * based soundcard. */ static void smw_putmem (int base, int addr, unsigned char val) { unsigned long flags; DISABLE_INTR (flags); OUTB (addr & 0xff, base + 1); /* Low address bits */ OUTB (addr >> 8, base + 2); /* High address bits */ OUTB (val, base); /* Data */ RESTORE_INTR (flags); } static unsigned char smw_getmem (int base, int addr) { unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (addr & 0xff, base + 1); /* Low address bits */ OUTB (addr >> 8, base + 2); /* High address bits */ val = INB (base); /* Data */ RESTORE_INTR (flags); return val; } static int initialize_smw (void) { #ifdef SMW_MIDI0001_INCLUDED #include "smw-midi0001.h" #else unsigned char smw_ucode[1]; int smw_ucodeLen = 0; #endif int mp_base = MPU_BASE + 4; /* Microcontroller base */ int i; unsigned char control; /* * Reset the microcontroller so that the RAM can be accessed */ control = INB (MPU_BASE + 7); OUTB (control | 3, MPU_BASE + 7); /* Set last two bits to 1 (?) */ OUTB ((control & 0xfe) | 2, MPU_BASE + 7); /* xxxxxxx0 resets the mc */ for (i = 0; i < 300; i++) /* Wait at least 1ms */ tenmicrosec (); OUTB (control & 0xfc, MPU_BASE + 7); /* xxxxxx00 enables RAM */ /* * Detect microcontroller by probing the 8k RAM area */ smw_putmem (mp_base, 0, 0x00); smw_putmem (mp_base, 1, 0xff); tenmicrosec (); if (smw_getmem (mp_base, 0) != 0x00 || smw_getmem (mp_base, 1) != 0xff) { printk ("\nSM Wave: No microcontroller RAM detected (%02x, %02x)\n", smw_getmem (mp_base, 0), smw_getmem (mp_base, 1)); return 0; /* No RAM */ } /* * There is RAM so assume it's really a SM Wave */ #ifdef SMW_MIDI0001_INCLUDED if (smw_ucodeLen != 8192) { printk ("\nSM Wave: Invalid microcode (MIDI0001.BIN) length\n"); return 1; } #endif /* * Download microcode */ for (i = 0; i < 8192; i++) smw_putmem (mp_base, i, smw_ucode[i]); /* * Verify microcode */ for (i = 0; i < 8192; i++) if (smw_getmem (mp_base, i) != smw_ucode[i]) { printk ("SM Wave: Microcode verification failed\n"); return 0; } control = 0; #ifdef SMW_SCSI_IRQ /* * Set the SCSI interrupt (IRQ2/9, IRQ3 or IRQ10). The SCSI interrupt * is disabled by default. * * Btw the Zilog 5380 SCSI controller is located at MPU base + 0x10. */ { static unsigned char scsi_irq_bits[] = {0, 0, 3, 1, 0, 0, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0}; control |= scsi_irq_bits[SMW_SCSI_IRQ] << 6; } #endif #ifdef SMW_OPL4_ENABLE /* * Make the OPL4 chip visible on the PC bus at 0x380. * * There is no need to enable this feature since VoxWare * doesn't support OPL4 yet. Also there is no RAM in SM Wave so * enabling OPL4 is pretty useless. */ control |= 0x10; /* Uses IRQ12 if bit 0x20 == 0 */ /* control |= 0x20; Uncomment this if you want to use IRQ7 */ #endif OUTB (control | 0x03, MPU_BASE + 7); /* xxxxxx11 restarts */ return 1; } #endif static int initialize_ProSonic16 (void) { int x; static unsigned char int_translat[16] = {0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6}, dma_translat[8] = {0, 1, 0, 2, 0, 3, 0, 4}; OUTB (0xAF, 0x201); /* ProSonic/Jazz16 wakeup */ for (x = 0; x < 1000; ++x) /* wait 10 milliseconds */ tenmicrosec (); OUTB (0x50, 0x201); OUTB ((sbc_base & 0x70) | ((MPU_BASE & 0x30) >> 4), 0x201); if (sb_reset_dsp ()) { /* OK. We have at least a SB */ /* Check the version number of ProSonic (I guess) */ if (!sb_dsp_command (0xFA)) return 1; if (get_sb_byte () != 0x12) return 1; if (sb_dsp_command (0xFB) && /* set DMA-channels and Interrupts */ sb_dsp_command ((dma_translat[JAZZ_DMA16] << 4) | dma_translat[SBC_DMA]) && sb_dsp_command ((int_translat[MPU_IRQ] << 4) | int_translat[sbc_irq])) { Jazz16_detected = 1; #ifdef SM_WAVE if (initialize_smw ()) Jazz16_detected = 2; #endif sb_dsp_disable_midi (); } return 1; /* There was at least a SB */ } return 0; /* No SB or ProSonic16 detected */ } #endif /* ifdef JAZZ16 */ int sb_dsp_detect (struct address_info *hw_config) { sbc_base = hw_config->io_base; sbc_irq = hw_config->irq; if (sb_dsp_ok) return 0; /* * Already initialized */ #ifdef JAZZ16 dma8 = hw_config->dma; dma16 = JAZZ_DMA16; if (!initialize_ProSonic16 ()) return 0; #else if (!sb_reset_dsp ()) return 0; #endif return 1; /* * Detected */ } #ifndef EXCLUDE_AUDIO static struct audio_operations sb_dsp_operations = { "SoundBlaster", NOTHING_SPECIAL, AFMT_U8, /* Just 8 bits. Poor old SB */ NULL, sb_dsp_open, sb_dsp_close, sb_dsp_output_block, sb_dsp_start_input, sb_dsp_ioctl, sb_dsp_prepare_for_input, sb_dsp_prepare_for_output, sb_dsp_reset, sb_dsp_halt_xfer, NULL, /* local_qlen */ NULL /* copy_from_user */ }; #endif long sb_dsp_init (long mem_start, struct address_info *hw_config) { int i; int mixer_type = 0; sbc_major = sbc_minor = 0; sb_dsp_command (0xe1); /* * Get version */ for (i = 1000; i; i--) { if (INB (DSP_DATA_AVAIL) & 0x80) { /* * wait for Data Ready */ if (sbc_major == 0) sbc_major = INB (DSP_READ); else { sbc_minor = INB (DSP_READ); break; } } } if (sbc_major == 2 || sbc_major == 3) sb_duplex_midi = 1; if (sbc_major == 4) sb16 = 1; #ifndef EXCLUDE_SBPRO if (sbc_major >= 3) mixer_type = sb_mixer_init (sbc_major); #else if (sbc_major >= 3) printk ("\n\n\n\nNOTE! SB Pro support is required with your soundcard!\n\n\n"); #endif #ifndef EXCLUDE_YM3812 if (sbc_major > 3 || (sbc_major == 3 && INB (0x388) == 0x00)) /* Should be 0x06 if not OPL-3 */ enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); #endif #ifndef EXCLUDE_AUDIO if (sbc_major >= 3) { if (Jazz16_detected) { if (Jazz16_detected == 2) sprintf (sb_dsp_operations.name, "SoundMan Wave %d.%d", sbc_major, sbc_minor); else sprintf (sb_dsp_operations.name, "MV Jazz16 %d.%d", sbc_major, sbc_minor); sb_dsp_operations.format_mask |= AFMT_S16_LE; /* Hurrah, 16 bits */ } else #ifdef __SGNXPRO__ if (mixer_type == 2) { sprintf (sb_dsp_operations.name, "Sound Galaxy NX Pro %d.%d", sbc_major, sbc_minor); } else #endif if (sbc_major == 4) { sprintf (sb_dsp_operations.name, "SoundBlaster 16 %d.%d", sbc_major, sbc_minor); } else { sprintf (sb_dsp_operations.name, "SoundBlaster Pro %d.%d", sbc_major, sbc_minor); } } else { sprintf (sb_dsp_operations.name, "SoundBlaster %d.%d", sbc_major, sbc_minor); } #if defined(__FreeBSD__) printk ("sb0: <%s>", sb_dsp_operations.name); #else printk (" <%s>", sb_dsp_operations.name); #endif #if !defined(EXCLUDE_SB16) && !defined(EXCLUDE_SBPRO) if (!sb16) /* * There is a better driver for SB16 */ #endif if (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[my_dev = num_audiodevs++] = &sb_dsp_operations; audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT; - audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; + audio_devs[my_dev]->buffsize = ( + (sbc_major > 2 || sbc_major == 2 && sbc_minor > 0) ? + 16 : 8) * 1024; audio_devs[my_dev]->dmachan = hw_config->dma; } else printk ("SB: Too many DSP devices available\n"); #else printk (" "); #endif #ifndef EXCLUDE_MIDI if (!midi_disabled && !sb16) /* * Midi don't work in the SB emulation mode * * of PAS, SB16 has better midi interface */ sb_midi_init (sbc_major); #endif sb_dsp_ok = 1; return mem_start; } void sb_dsp_disable_midi (void) { midi_disabled = 1; } #endif diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c index 2a8525d286da..be0c1ccac604 100644 --- a/sys/i386/isa/sound/soundcard.c +++ b/sys/i386/isa/sound/soundcard.c @@ -1,550 +1,551 @@ /* * sound/386bsd/soundcard.c * * Soundcard driver for FreeBSD. * * Copyright by Hannu Savolainen 1993 * * 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. * - * $Id: soundcard.c,v 1.39 1995/12/10 02:53:07 bde Exp $ + * $Id: soundcard.c,v 1.40 1995/12/11 09:26:16 phk Exp $ */ #include "sound_config.h" #include #include #ifdef CONFIGURE_SOUNDCARD #include "dev_table.h" #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #define FIX_RETURN(ret) { \ int tmp_ret = (ret); \ if (tmp_ret<0) return -tmp_ret; else return 0; \ } static int timer_running = 0; static int soundcards_installed = 0; /* Number of installed * soundcards */ static int soundcard_configured = 0; static struct fileinfo files[SND_NDEVS]; static void * snd_devfs_token[SND_NDEVS]; static void * sndstat_devfs_token; struct selinfo selinfo[SND_NDEVS >> 4]; static int sndprobe (struct isa_device *dev); static int sndattach (struct isa_device *dev); static void sound_mem_init(void); static d_open_t sndopen; static d_close_t sndclose; static d_read_t sndread; static d_write_t sndwrite; static d_ioctl_t sndioctl; static d_select_t sndselect; #define CDEV_MAJOR 30 static struct cdevsw snd_cdevsw = { sndopen, sndclose, sndread, sndwrite, /*30*/ sndioctl, nostop, nullreset, nodevtotty,/* sound */ sndselect, nommap, NULL, "snd", NULL, -1 }; struct isa_driver opldriver = {sndprobe, sndattach, "opl"}; struct isa_driver sbdriver = {sndprobe, sndattach, "sb"}; struct isa_driver sbxvidriver = {sndprobe, sndattach, "sbxvi"}; struct isa_driver sbmididriver = {sndprobe, sndattach, "sbmidi"}; struct isa_driver pasdriver = {sndprobe, sndattach, "pas"}; struct isa_driver mpudriver = {sndprobe, sndattach, "mpu"}; struct isa_driver gusdriver = {sndprobe, sndattach, "gus"}; struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"}; struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"}; struct isa_driver uartdriver = {sndprobe, sndattach, "uart"}; struct isa_driver mssdriver = {sndprobe, sndattach, "mss"}; static unsigned short ipri_to_irq (unsigned short ipri); void adintr(INT_HANDLER_PARMS(unit,dummy)) { #ifndef EXCLUDE_AD1848 static short unit_to_irq[4] = { -1, -1, -1, -1 }; struct isa_device *dev; if (unit_to_irq [unit] > 0) ad1848_interrupt(INT_HANDLER_CALL (unit_to_irq [unit])); else { dev = find_isadev (isa_devtab_null, &mssdriver, unit); if (!dev) printk ("ad1848: Couldn't determine unit\n"); else { unit_to_irq [unit] = ipri_to_irq (dev->id_irq); ad1848_interrupt(INT_HANDLER_CALL (unit_to_irq [unit])); } } #endif } unsigned long get_time(void) { struct timeval timecopy; int x; x = splclock(); timecopy = time; splx(x); return timecopy.tv_usec/(1000000/HZ) + (unsigned long)timecopy.tv_sec*HZ; } static int sndread (dev_t dev, struct uio *buf, int ioflag) { int count = buf->uio_resid; dev = minor (dev); FIX_RETURN (sound_read_sw (dev, &files[dev], buf, count)); } static int sndwrite (dev_t dev, struct uio *buf, int ioflag) { int count = buf->uio_resid; dev = minor (dev); FIX_RETURN (sound_write_sw (dev, &files[dev], buf, count)); } static int sndopen (dev_t dev, int flags, int fmt, struct proc *p) { dev = minor (dev); if (!soundcard_configured && dev) { printk ("SoundCard Error: The soundcard system has not been configured\n"); FIX_RETURN (-ENODEV); } files[dev].mode = 0; if (flags & FREAD && flags & FWRITE) files[dev].mode = OPEN_READWRITE; else if (flags & FREAD) files[dev].mode = OPEN_READ; else if (flags & FWRITE) files[dev].mode = OPEN_WRITE; selinfo[dev >> 4].si_pid = 0; selinfo[dev >> 4].si_flags = 0; FIX_RETURN(sound_open_sw (dev, &files[dev])); } static int sndclose (dev_t dev, int flags, int fmt, struct proc *p) { dev = minor (dev); sound_release_sw(dev, &files[dev]); FIX_RETURN (0); } static int sndioctl (dev_t dev, int cmd, caddr_t arg, int flags, struct proc *p) { dev = minor (dev); FIX_RETURN (sound_ioctl_sw (dev, &files[dev], cmd, (unsigned int) arg)); } static int sndselect (dev_t dev, int rw, struct proc *p) { dev = minor (dev); DEB (printk ("snd_select(dev=%d, rw=%d, pid=%d)\n", dev, rw, p->p_pid)); #ifdef ALLOW_SELECT switch (dev & 0x0f) { #ifndef EXCLUDE_SEQUENCER case SND_DEV_SEQ: case SND_DEV_SEQ2: return sequencer_select (dev, &files[dev], rw, p); break; #endif #ifndef EXCLUDE_MIDI case SND_DEV_MIDIN: return MIDIbuf_select (dev, &files[dev], rw, p); break; #endif #ifndef EXCLUDE_AUDIO case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return audio_select (dev, &files[dev], rw, p); break; #endif default: return 0; } #endif return 0; } static unsigned short ipri_to_irq (unsigned short ipri) { /* * Converts the ipri (bitmask) to the corresponding irq number */ int irq; for (irq = 0; irq < 16; irq++) if (ipri == (1 << irq)) return irq; return -1; /* Invalid argument */ } static int driver_to_voxunit(struct isa_driver *driver) { /* converts a sound driver pointer into the equivalent VoxWare device unit number */ if(driver == &opldriver) return(SNDCARD_ADLIB); else if(driver == &sbdriver) return(SNDCARD_SB); else if(driver == &pasdriver) return(SNDCARD_PAS); else if(driver == &gusdriver) return(SNDCARD_GUS); else if(driver == &mpudriver) return(SNDCARD_MPU401); else if(driver == &sbxvidriver) return(SNDCARD_SB16); else if(driver == &sbmididriver) return(SNDCARD_SB16MIDI); else if(driver == &uartdriver) return(SNDCARD_UART6850); else if(driver == &gusdriver) return(SNDCARD_GUS16); else if(driver == &mssdriver) return(SNDCARD_MSS); else return(0); } static int sndprobe (struct isa_device *dev) { struct address_info hw_config; int unit; unit = driver_to_voxunit(dev->id_driver); hw_config.io_base = dev->id_iobase; hw_config.irq = ipri_to_irq (dev->id_irq); hw_config.dma = dev->id_drq; hw_config.dma_read = dev->id_flags; /* misuse the flags field for read dma*/ if(unit) return sndtable_probe (unit, &hw_config); else return 0; } static int sndattach (struct isa_device *dev) { int unit; static int midi_initialized = 0; static int seq_initialized = 0; unsigned long mem_start = 0xefffffffUL; struct address_info hw_config; char name[32]; unit = driver_to_voxunit(dev->id_driver); hw_config.io_base = dev->id_iobase; hw_config.irq = ipri_to_irq (dev->id_irq); hw_config.dma = dev->id_drq; hw_config.dma_read = dev->id_flags; /* misuse the flags field for read dma*/ if(!unit) return FALSE; if (!sndtable_init_card (unit, &hw_config)) { printf (" "); return FALSE; } /* * Init the high level sound driver */ if (!(soundcards_installed = sndtable_get_cardcount ())) { printf (" "); return FALSE; /* No cards detected */ } printf("\n"); #ifndef EXCLUDE_AUDIO if (num_audiodevs) /* Audio devices present */ { mem_start = DMAbuf_init (mem_start); mem_start = audio_init (mem_start); sound_mem_init (); } soundcard_configured = 1; #endif #ifndef EXCLUDE_MIDI if (num_midis && !midi_initialized) { midi_initialized = 1; mem_start = MIDIbuf_init (mem_start); } #endif #ifndef EXCLUDE_SEQUENCER if ((num_midis + num_synths) && !seq_initialized) { seq_initialized = 1; mem_start = sequencer_init (mem_start); } #endif #ifdef DEVFS /* XXX */ /* find out where to store the tokens.. */ /* XXX */ /* should only create devices if that card has them */ #define SND_UID 0 #define SND_GID 13 sprintf(name,"mixer%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_CTL, DV_CHR, SND_UID, SND_GID, 0660); #ifndef EXCLUDE_SEQUENCER sprintf(name,"sequencer%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_SEQ, DV_CHR, SND_UID, SND_GID, 0660); sprintf(name,"music%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_SEQ2, DV_CHR, SND_UID, SND_GID, 0660); #endif #ifndef EXCLUDE_MIDI sprintf(name,"midi%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_MIDIN, DV_CHR, SND_UID, SND_GID, 0660); #endif #ifndef EXCLUDE_AUDIO sprintf(name,"dsp%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_DSP, DV_CHR, SND_UID, SND_GID, 0660); sprintf(name,"audio%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_AUDIO, DV_CHR, SND_UID, SND_GID, 0660); sprintf(name,"dspW%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_DSP16, DV_CHR, SND_UID, SND_GID, 0660); #endif sprintf(name,"pss%d",unit); snd_devfs_token[unit]=devfs_add_devsw( "/", name, &snd_cdevsw, (unit << 4)+SND_DEV_SNDPROC, DV_CHR, SND_UID, SND_GID, 0660); if ( ! sndstat_devfs_token) { sndstat_devfs_token = devfs_add_devsw( "/", "sndstat", &snd_cdevsw, 6, DV_CHR, SND_UID, SND_GID, 0660); } #endif /* DEVFS */ return TRUE; } void tenmicrosec (void) { int i; for (i = 0; i < 16; i++) inb (0x80); } #ifndef EXCLUDE_SEQUENCER void request_sound_timer (int count) { static int current = 0; int tmp = count; if (count < 0) timeout ((timeout_func_t)sequencer_timer, 0, -count); else { if (count < current) current = 0; /* Timer restarted */ count = count - current; current = tmp; if (!count) count = 1; timeout ((timeout_func_t)sequencer_timer, 0, count); } timer_running = 1; } void sound_stop_timer (void) { if (timer_running) untimeout ((timeout_func_t)sequencer_timer, 0); timer_running = 0; } #endif #ifndef EXCLUDE_AUDIO static void sound_mem_init (void) { int dev; unsigned long dma_pagesize; struct dma_buffparms *dmap; static unsigned long dsp_init_mask = 0; for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */ if (!(dsp_init_mask & (1 << dev))) /* Not already done */ if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan > 0) { dsp_init_mask |= (1 << dev); dmap = audio_devs[dev]->dmap; if (audio_devs[dev]->flags & DMA_AUTOMODE) audio_devs[dev]->buffcount = 1; + audio_devs[dev]->buffsize &= ~0xfff; /* Truncate to n*4k */ + if (audio_devs[dev]->dmachan > 3 && audio_devs[dev]->buffsize > 65536) dma_pagesize = 131072; /* 128k */ else dma_pagesize = 65536; /* More sanity checks */ if (audio_devs[dev]->buffsize > dma_pagesize) audio_devs[dev]->buffsize = dma_pagesize; - audio_devs[dev]->buffsize &= ~0xfff; /* Truncate to n*4k */ if (audio_devs[dev]->buffsize < 4096) audio_devs[dev]->buffsize = 4096; /* Now allocate the buffers */ for (dmap->raw_count = 0; dmap->raw_count < audio_devs[dev]->buffcount; dmap->raw_count++) { char *tmpbuf = (char *)vm_page_alloc_contig(audio_devs[dev]->buffsize, 0ul, 0xfffffful, dma_pagesize); if (tmpbuf == NULL) { printk ("snd: Unable to allocate %ld bytes of buffer\n", audio_devs[dev]->buffsize); return; } dmap->raw_buf[dmap->raw_count] = tmpbuf; /* * Use virtual address as the physical address, since * isa_dmastart performs the phys address computation. */ dmap->raw_buf_phys[dmap->raw_count] = (unsigned long) dmap->raw_buf[dmap->raw_count]; } } /* for dev */ } #endif int snd_set_irq_handler (int interrupt_level, INT_HANDLER_PROTO(), char *name) { return 1; } void snd_release_irq(int vect) { } static snd_devsw_installed = 0; static void snd_drvinit(void *unused) { dev_t dev; if( ! snd_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&snd_cdevsw, NULL); snd_devsw_installed = 1; } } SYSINIT(snddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snd_drvinit,NULL) #endif