📄 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. * * CS4231A and AD1845 are upward compatible with CS4231. However * the new features of these chips are different. * * CS4232 is a PnP audio chip which contains a CS4231A (and SB, MPU). * CS4232A is an improved version of CS4232. * * * * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. * * * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) * general sleep/wakeup clean up. * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free * of irqs. Use dev_id. * Christoph Hellwig : adapted to module_init/module_exit * Aki Laukkanen : added power management support * * Status: * Tested. Believed fully functional. */#include <linux/config.h>#include <linux/init.h>#include <linux/module.h>#include <linux/stddef.h>#include <linux/pm.h>#define DEB(x)#define DEB1(x)#include "sound_config.h"#include "ad1848.h"#include "ad1848_mixer.h"typedef struct{ int base; int irq; int dma1, dma2; int dual_dma; /* 1, when two DMA channels allocated */ int subtype; unsigned char MCE_bit; unsigned char saved_regs[32]; int debug_flag; int audio_flags; int record_dev, playback_dev; int xfer_count; int audio_mode; int open_mode; int intr_active; char *chip_name, *name; int model;#define MD_1848 1#define MD_4231 2#define MD_4231A 3#define MD_1845 4#define MD_4232 5#define MD_C930 6#define MD_IWAVE 7#define MD_4235 8 /* Crystal Audio CS4235 */#define MD_1845_SSCAPE 9 /* Ensoniq Soundscape PNP*/ /* Mixer parameters */ int recmask; int supported_devices, orig_devices; int supported_rec_devices, orig_rec_devices; int *levels; short mixer_reroute[32]; int dev_no; volatile unsigned long timer_ticks; int timer_running; int irq_ok; mixer_ents *mix_devices; int mixer_output_port; /* Power management */ struct pm_dev *pmdev;} ad1848_info;typedef struct ad1848_port_info{ int open_mode; int speed; unsigned char speed_bits; int channels; int audio_format; unsigned char format_bits;}ad1848_port_info;static struct address_info cfg;static int nr_ad1848_devs = 0;int deskpro_xl = 0;int deskpro_m = 0;int soundpro = 0;static volatile signed char irq2dev[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};#ifndef EXCLUDE_TIMERSstatic int timer_installed = -1;#endifstatic int loaded = 0;static int ad_format_mask[10 /*devc->model */ ] ={ 0, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* AD1845 */ AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_S16_BE | AFMT_IMA_ADPCM, AFMT_U8 | AFMT_S16_LE /* CS4235 */, AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW /* Ensoniq Soundscape*/};static ad1848_info adev_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 struct { unsigned char flags;#define CAP_F_TIMER 0x01 } capabilities [10 /*devc->model */ ] = { {0} ,{0} /* MD_1848 */ ,{CAP_F_TIMER} /* MD_4231 */ ,{CAP_F_TIMER} /* MD_4231A */ ,{CAP_F_TIMER} /* MD_1845 */ ,{CAP_F_TIMER} /* MD_4232 */ ,{0} /* MD_C930 */ ,{CAP_F_TIMER} /* MD_IWAVE */ ,{0} /* MD_4235 */ ,{CAP_F_TIMER} /* MD_1845_SSCAPE */};static int ad1848_open(int dev, int mode);static void ad1848_close(int dev);static void ad1848_output_block(int dev, unsigned long buf, int count, int intrflag);static void ad1848_start_input(int dev, unsigned long buf, int count, int intrflag);static int ad1848_prepare_for_output(int dev, int bsize, int bcount);static int ad1848_prepare_for_input(int dev, int bsize, int bcount);static void ad1848_halt(int dev);static void ad1848_halt_input(int dev);static void ad1848_halt_output(int dev);static void ad1848_trigger(int dev, int bits);static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data);#ifndef EXCLUDE_TIMERSstatic int ad1848_tmr_install(int dev);static void ad1848_tmr_reprogram(int dev);#endifstatic int ad_read(ad1848_info * devc, int reg){ unsigned long flags; int x; int timeout = 900000; while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ timeout--; save_flags(flags); cli(); 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_flags(flags); return x;}static void ad_write(ad1848_info * devc, int reg, int data){ unsigned long flags; int timeout = 900000; while (timeout > 0 && inb(devc->base) == 0x80) /* Are we initializing */ timeout--; save_flags(flags); cli(); 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_flags(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(KERN_WARNING "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 = 80000; while (timeout > 0 && (ad_read(devc, 11) & 0x20)) timeout--; if (ad_read(devc, 11) & 0x20) if ( (devc->model != MD_1845) || (devc->model != MD_1845_SSCAPE)) printk(KERN_WARNING "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); }}static void ad_unmute(ad1848_info * devc){}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--; save_flags(flags); cli(); devc->MCE_bit = 0x40; prev = inb(io_Index_Addr(devc)); if (prev & 0x40) { restore_flags(flags); return; } outb((devc->MCE_bit), io_Index_Addr(devc)); restore_flags(flags);}static void ad_leave_MCE(ad1848_info * devc){ unsigned long flags; unsigned char prev, acal; int timeout = 1000; while (timeout > 0 && inb(devc->base) == 0x80) /*Are we initializing */ timeout--; save_flags(flags); cli(); acal = ad_read(devc, 9); 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_flags(flags); return; } outb((0x00), io_Index_Addr(devc)); /* Clear the MCE bit */ if (acal & 0x08) /* Auto calibration is enabled */ wait_for_calibration(devc); restore_flags(flags);}static int ad1848_set_recmask(ad1848_info * devc, int mask){ unsigned char recdev; int i, n; mask &= devc->supported_rec_devices; /* Rename the mixer bits if necessary */ for (i = 0; i < 32; i++) { if (devc->mixer_reroute[i] != i) { if (mask & (1 << i)) { mask &= ~(1 << i); mask |= (1 << devc->mixer_reroute[i]); } } } n = 0; for (i = 0; i < 32; i++) /* Count selected device bits */ if (mask & (1 << i)) n++; if (!soundpro) { 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; case SOUND_MASK_IMIX: recdev = 3; 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); } else { /* soundpro */ unsigned char val; int set_rec_bit; int j; for (i = 0; i < 32; i++) { /* For each bit */ if ((devc->supported_rec_devices & (1 << i)) == 0) continue; /* Device not supported */ for (j = LEFT_CHN; j <= RIGHT_CHN; j++) { if (devc->mix_devices[i][j].nbits == 0) /* Inexistent channel */ continue; /* * This is tricky: * set_rec_bit becomes 1 if the corresponding bit in mask is set * then it gets flipped if the polarity is inverse */ set_rec_bit = ((mask & (1 << i)) != 0) ^ devc->mix_devices[i][j].recpol; val = ad_read(devc, devc->mix_devices[i][j].recreg); val &= ~(1 << devc->mix_devices[i][j].recpos); val |= (set_rec_bit << devc->mix_devices[i][j].recpos); ad_write(devc, devc->mix_devices[i][j].recreg, val); } } } /* Rename the mixer bits back if necessary */ for (i = 0; i < 32; i++) { if (devc->mixer_reroute[i] != i) { if (mask & (1 << devc->mixer_reroute[i])) { mask &= ~(1 << devc->mixer_reroute[i]); mask |= (1 << i); } } } devc->recmask = mask; return mask;}static void change_bits(ad1848_info * devc, unsigned char *regval, unsigned char *muteval, int dev, int chn, int newval){ unsigned char mask; int shift; int mute; int mutemask; int set_mute_bit; set_mute_bit = (newval == 0) ^ devc->mix_devices[dev][chn].mutepol; if (devc->mix_devices[dev][chn].polarity == 1) /* Reverse */ newval = 100 - newval; mask = (1 << devc->mix_devices[dev][chn].nbits) - 1; shift = devc->mix_devices[dev][chn].bitpos; if (devc->mix_devices[dev][chn].mutepos == 8) { /* if there is no mute bit */ mute = 0; /* No mute bit; do nothing special */ mutemask = ~0; /* No mute bit; do nothing special */ } else { mute = (set_mute_bit << devc->mix_devices[dev][chn].mutepos); mutemask = ~(1 << devc->mix_devices[dev][chn].mutepos); } newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ *regval &= ~(mask << shift); /* Clear bits */ *regval |= (newval & mask) << shift; /* Set new value */ *muteval &= mutemask; *muteval |= mute;}static int ad1848_mixer_get(ad1848_info * devc, int dev){ if (!((1 << dev) & devc->supported_devices)) return -EINVAL; dev = devc->mixer_reroute[dev]; return devc->levels[dev];}static void ad1848_mixer_set_channel(ad1848_info *devc, int dev, int value, int channel){ int regoffs, muteregoffs; unsigned char val, muteval; regoffs = devc->mix_devices[dev][channel].regno; muteregoffs = devc->mix_devices[dev][channel].mutereg; val = ad_read(devc, regoffs); if (muteregoffs != regoffs) { muteval = ad_read(devc, muteregoffs); change_bits(devc, &val, &muteval, dev, channel, value); } else change_bits(devc, &val, &val, dev, channel, value); ad_write(devc, regoffs, val); devc->saved_regs[regoffs] = val; if (muteregoffs != regoffs) { ad_write(devc, muteregoffs, muteval); devc->saved_regs[muteregoffs] = muteval; }}static int ad1848_mixer_set(ad1848_info * devc, int dev, int value){ int left = value & 0x000000ff; int right = (value & 0x0000ff00) >> 8; int retvol; if (dev > 31) return -EINVAL; if (!(devc->supported_devices & (1 << dev))) return -EINVAL; dev = devc->mixer_reroute[dev]; if (devc->mix_devices[dev][LEFT_CHN].nbits == 0) return -EINVAL; if (left > 100) left = 100; if (right > 100) right = 100; if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) /* Mono control */ right = left; retvol = left | (right << 8); /* Scale volumes */ left = mix_cvt[left]; right = mix_cvt[right]; devc->levels[dev] = retvol; /* * Set the left channel */ ad1848_mixer_set_channel(devc, dev, left, LEFT_CHN); /* * Set the right channel */ if (devc->mix_devices[dev][RIGHT_CHN].nbits == 0) goto out; ad1848_mixer_set_channel(devc, dev, right, RIGHT_CHN); out: return retvol;}static void ad1848_mixer_reset(ad1848_info * devc){ int i; char name[32];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -