📄 ad1848.c
字号:
/* * 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 <i386/isa/sound/sound_config.h>#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848)#include <i386/isa/sound/ad1848_mixer.h>#define IMODE_NONE 0#define IMODE_OUTPUT 1#define IMODE_INPUT 2#define IMODE_INIT 3#define IMODE_MIDI 4typedef 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 intad_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 voidad_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 voidwait_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;#ifdef PC98 while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) timeout--; if ((INB (devc->base) & 0x80) == 0x80)#else while (timeout > 0 && INB (devc->base) & 0x80) timeout--; if (INB (devc->base) & 0x80)#endif 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 voidad_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 voidad_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 voidad_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 voidad_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 intad1848_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 voidchange_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 intad1848_mixer_get (ad1848_info * devc, int dev){ if (!((1 << dev) & devc->supported_devices)) return RET_ERROR (EINVAL); return devc->levels[dev];}static intad1848_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 voidad1848_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 intad1848_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 intad1848_open (int dev, int mode){ int err; ad1848_info *devc = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -