📄 ali5451.c
字号:
/* * Matt Wu <Matt_Wu@acersoftech.com.cn> * Apr 26, 2001 * Routines for control of ALi pci audio M5451 * * BUGS: * -- * * TODO: * -- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public Lcodecnse as published by * the Free Software Foundation; either version 2 of the Lcodecnse, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public Lcodecnse for more details. * * You should have received a copy of the GNU General Public Lcodecnse * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/info.h>#include <sound/ac97_codec.h>#include <sound/mpu401.h>#include <sound/initval.h>MODULE_AUTHOR("Matt Wu <Matt_Wu@acersoftech.com.cn>");MODULE_DESCRIPTION("ALI M5451");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}");static int index = SNDRV_DEFAULT_IDX1; /* Index */static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */static int pcm_channels = 32;static int spdif = 0;module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio.");module_param(pcm_channels, int, 0444);MODULE_PARM_DESC(pcm_channels, "PCM Channels");module_param(spdif, bool, 0444);MODULE_PARM_DESC(spdif, "Support SPDIF I/O");/* just for backward compatibility */static int enable;module_param(enable, bool, 0444);/* * Debug part definitions *///#define ALI_DEBUG#ifdef ALI_DEBUG#define snd_ali_printk(format, args...) printk(format, ##args);#else#define snd_ali_printk(format, args...)#endif/* * Constants definition */#define DEVICE_ID_ALI5451 ((PCI_VENDOR_ID_AL<<16)|PCI_DEVICE_ID_AL_M5451)#define ALI_CHANNELS 32#define ALI_PCM_IN_CHANNEL 31#define ALI_SPDIF_IN_CHANNEL 19#define ALI_SPDIF_OUT_CHANNEL 15#define ALI_CENTER_CHANNEL 24#define ALI_LEF_CHANNEL 23#define ALI_SURR_LEFT_CHANNEL 26#define ALI_SURR_RIGHT_CHANNEL 25#define ALI_MODEM_IN_CHANNEL 21#define ALI_MODEM_OUT_CHANNEL 20#define SNDRV_ALI_VOICE_TYPE_PCM 01#define SNDRV_ALI_VOICE_TYPE_OTH 02#define ALI_5451_V02 0x02/* * Direct Registers */#define ALI_LEGACY_DMAR0 0x00 // ADR0#define ALI_LEGACY_DMAR4 0x04 // CNT0#define ALI_LEGACY_DMAR11 0x0b // MOD #define ALI_LEGACY_DMAR15 0x0f // MMR #define ALI_MPUR0 0x20#define ALI_MPUR1 0x21#define ALI_MPUR2 0x22#define ALI_MPUR3 0x23#define ALI_AC97_WRITE 0x40#define ALI_AC97_READ 0x44#define ALI_SCTRL 0x48#define ALI_SPDIF_OUT_ENABLE 0x20#define ALI_SCTRL_LINE_IN2 (1 << 9)#define ALI_SCTRL_GPIO_IN2 (1 << 13)#define ALI_SCTRL_LINE_OUT_EN (1 << 20)#define ALI_SCTRL_GPIO_OUT_EN (1 << 23)#define ALI_SCTRL_CODEC1_READY (1 << 24)#define ALI_SCTRL_CODEC2_READY (1 << 25)#define ALI_AC97_GPIO 0x4c#define ALI_AC97_GPIO_ENABLE 0x8000#define ALI_AC97_GPIO_DATA_SHIFT 16#define ALI_SPDIF_CS 0x70#define ALI_SPDIF_CTRL 0x74#define ALI_SPDIF_IN_FUNC_ENABLE 0x02#define ALI_SPDIF_IN_CH_STATUS 0x40#define ALI_SPDIF_OUT_CH_STATUS 0xbf#define ALI_START 0x80#define ALI_STOP 0x84#define ALI_CSPF 0x90#define ALI_AINT 0x98#define ALI_GC_CIR 0xa0 #define ENDLP_IE 0x00001000 #define MIDLP_IE 0x00002000#define ALI_AINTEN 0xa4#define ALI_VOLUME 0xa8#define ALI_SBDELTA_DELTA_R 0xac#define ALI_MISCINT 0xb0 #define ADDRESS_IRQ 0x00000020 #define TARGET_REACHED 0x00008000 #define MIXER_OVERFLOW 0x00000800 #define MIXER_UNDERFLOW 0x00000400 #define GPIO_IRQ 0x01000000#define ALI_SBBL_SBCL 0xc0#define ALI_SBCTRL_SBE2R_SBDD 0xc4#define ALI_STIMER 0xc8#define ALI_GLOBAL_CONTROL 0xd4#define ALI_SPDIF_OUT_SEL_PCM 0x00000400 /* bit 10 */#define ALI_SPDIF_IN_SUPPORT 0x00000800 /* bit 11 */#define ALI_SPDIF_OUT_CH_ENABLE 0x00008000 /* bit 15 */#define ALI_SPDIF_IN_CH_ENABLE 0x00080000 /* bit 19 */#define ALI_PCM_IN_ENABLE 0x80000000 /* bit 31 */#define ALI_CSO_ALPHA_FMS 0xe0#define ALI_LBA 0xe4#define ALI_ESO_DELTA 0xe8#define ALI_GVSEL_PAN_VOC_CTRL_EC 0xf0#define ALI_EBUF1 0xf4#define ALI_EBUF2 0xf8#define ALI_REG(codec, x) ((codec)->port + x)#define MAX_CODECS 2typedef struct snd_stru_ali ali_t;typedef struct snd_ali_stru_voice snd_ali_voice_t;typedef struct snd_ali_channel_control { // register data struct REGDATA { unsigned int start; unsigned int stop; unsigned int aint; unsigned int ainten; } data; // register addresses struct REGS { unsigned int start; unsigned int stop; unsigned int aint; unsigned int ainten; unsigned int ac97read; unsigned int ac97write; } regs;} snd_ali_channel_control_t;struct snd_ali_stru_voice { unsigned int number; unsigned int use: 1, pcm: 1, midi: 1, mode: 1, synth: 1; /* PCM data */ ali_t *codec; snd_pcm_substream_t *substream; snd_ali_voice_t *extra; unsigned int running: 1; int eso; /* final ESO value for channel */ int count; /* runtime->period_size */ /* --- */ void *private_data; void (*private_free)(void *private_data);};typedef struct snd_stru_alidev { snd_ali_voice_t voices[ALI_CHANNELS]; unsigned int chcnt; /* num of opened channels */ unsigned int chmap; /* bitmap for opened channels */ unsigned int synthcount;} alidev_t;#ifdef CONFIG_PM#define ALI_GLOBAL_REGS 56#define ALI_CHANNEL_REGS 8typedef struct snd_ali_image { unsigned long regs[ALI_GLOBAL_REGS]; unsigned long channel_regs[ALI_CHANNELS][ALI_CHANNEL_REGS];} ali_image_t;#endifstruct snd_stru_ali { unsigned long irq; unsigned long port; unsigned char revision; unsigned int hw_initialized: 1; unsigned int spdif_support: 1; struct pci_dev *pci; struct pci_dev *pci_m1533; struct pci_dev *pci_m7101; snd_card_t *card; snd_pcm_t *pcm[MAX_CODECS]; alidev_t synth; snd_ali_channel_control_t chregs; /* S/PDIF Mask */ unsigned int spdif_mask; unsigned int spurious_irq_count; unsigned int spurious_irq_max_delta; unsigned int num_of_codecs; ac97_bus_t *ac97_bus; ac97_t *ac97[MAX_CODECS]; unsigned short ac97_ext_id; unsigned short ac97_ext_status; spinlock_t reg_lock; spinlock_t voice_alloc;#ifdef CONFIG_PM ali_image_t *image;#endif};static struct pci_device_id snd_ali_ids[] = { {0x10b9, 0x5451, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, {0, }};MODULE_DEVICE_TABLE(pci, snd_ali_ids);static void snd_ali_clear_voices(ali_t *, unsigned int, unsigned int);static unsigned short snd_ali_codec_peek(ali_t *, int, unsigned short);static void snd_ali_codec_poke(ali_t *, int, unsigned short, unsigned short);/* * Debug Part */#ifdef ALI_DEBUGstatic void ali_read_regs(ali_t *codec, int channel){ int i,j; unsigned int dwVal; printk("channel %d registers map:\n", channel); outb((unsigned char)(channel & 0x001f), ALI_REG(codec,ALI_GC_CIR)); printk(" "); for(j=0;j<8;j++) printk("%2.2x ", j*4); printk("\n"); for (i=0; i<=0xf8/4;i++) { if(i%8 == 0) printk("%2.2x ", (i*4/0x10)*0x10); dwVal = inl(ALI_REG(codec,i*4)); printk("%8.8x ", dwVal); if ((i+1)%8 == 0) printk("\n"); } printk("\n");}static void ali_read_cfg(unsigned int vendor, unsigned deviceid){ unsigned int dwVal; struct pci_dev *pci_dev; int i,j; pci_dev = pci_get_device(vendor, deviceid, NULL); if (pci_dev == NULL) return ; printk("\nM%x PCI CFG\n", deviceid); printk(" "); for(j=0;j<8;j++) printk("%d ",j); printk("\n"); for(i=0;i<8;i++) { printk("%d ",i); for(j=0;j<8;j++) { pci_read_config_dword(pci_dev, i*0x20+j*4, &dwVal); printk("%8.8x ", dwVal); } printk("\n"); } pci_dev_put(pci_dev); }static void ali_read_ac97regs(ali_t *codec, int secondary){ unsigned short i,j; unsigned short wVal; printk("\ncodec %d registers map:\n", secondary); printk(" "); for(j=0;j<8;j++) printk("%2.2x ",j*2); printk("\n"); for (i=0; i<64;i++) { if(i%8 == 0) printk("%2.2x ", (i/8)*0x10); wVal = snd_ali_codec_peek(codec, secondary, i*2); printk("%4.4x ", wVal); if ((i+1)%8 == 0) printk("\n"); } printk("\n");}#endif/* * AC97 ACCESS */static inline unsigned int snd_ali_5451_peek(ali_t *codec, unsigned int port ){ return (unsigned int)inl(ALI_REG(codec, port)); }static inline void snd_ali_5451_poke( ali_t *codec, unsigned int port, unsigned int val ){ outl((unsigned int)val, ALI_REG(codec, port));}static int snd_ali_codec_ready( ali_t *codec, unsigned int port, int sched ){ unsigned long end_time; unsigned int res; end_time = jiffies + 10 * msecs_to_jiffies(250); do { res = snd_ali_5451_peek(codec,port); if (! (res & 0x8000)) return 0; if (sched) schedule_timeout_uninterruptible(1); } while (time_after_eq(end_time, jiffies)); snd_ali_5451_poke(codec, port, res & ~0x8000); snd_printdd("ali_codec_ready: codec is not ready.\n "); return -EIO;}static int snd_ali_stimer_ready(ali_t *codec, int sched){ unsigned long end_time; unsigned long dwChk1,dwChk2; dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); end_time = jiffies + 10 * msecs_to_jiffies(250); do { dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); if (dwChk2 != dwChk1) return 0; if (sched) schedule_timeout_uninterruptible(1); } while (time_after_eq(end_time, jiffies)); snd_printk(KERN_ERR "ali_stimer_read: stimer is not ready.\n"); return -EIO;}static void snd_ali_codec_poke(ali_t *codec,int secondary, unsigned short reg, unsigned short val){ unsigned int dwVal = 0; unsigned int port = 0; if (reg >= 0x80) { snd_printk(KERN_ERR "ali_codec_poke: reg(%xh) invalid.\n", reg); return; } port = codec->chregs.regs.ac97write; if (snd_ali_codec_ready(codec, port, 0) < 0) return; if (snd_ali_stimer_ready(codec, 0) < 0) return; dwVal = (unsigned int) (reg & 0xff); dwVal |= 0x8000 | (val << 16); if (secondary) dwVal |= 0x0080; if (codec->revision == ALI_5451_V02) dwVal |= 0x0100; snd_ali_5451_poke(codec,port,dwVal); return ;}static unsigned short snd_ali_codec_peek( ali_t *codec, int secondary, unsigned short reg){ unsigned int dwVal = 0; unsigned int port = 0; if (reg >= 0x80) { snd_printk(KERN_ERR "ali_codec_peek: reg(%xh) invalid.\n", reg); return ~0; } port = codec->chregs.regs.ac97read; if (snd_ali_codec_ready(codec, port, 0) < 0) return ~0; if (snd_ali_stimer_ready(codec, 0) < 0) return ~0; dwVal = (unsigned int) (reg & 0xff); dwVal |= 0x8000; /* bit 15*/ if (secondary) dwVal |= 0x0080; snd_ali_5451_poke(codec, port, dwVal); if (snd_ali_stimer_ready(codec, 0) < 0) return ~0; if (snd_ali_codec_ready(codec, port, 0) < 0) return ~0; return (snd_ali_5451_peek(codec, port) & 0xffff0000)>>16;}static void snd_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val ){ ali_t *codec = ac97->private_data; snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val); if(reg == AC97_GPIO_STATUS) { outl((val << ALI_AC97_GPIO_DATA_SHIFT)|ALI_AC97_GPIO_ENABLE, ALI_REG(codec, ALI_AC97_GPIO)); return; } snd_ali_codec_poke(codec, ac97->num, reg, val); return ;}static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg){ ali_t *codec = ac97->private_data; snd_ali_printk("codec_read reg=%xh.\n", reg); return (snd_ali_codec_peek(codec, ac97->num, reg));}/* * AC97 Reset */static int snd_ali_reset_5451(ali_t *codec){ struct pci_dev *pci_dev = NULL; unsigned short wCount, wReg; unsigned int dwVal; if ((pci_dev = codec->pci_m1533) != NULL) { pci_read_config_dword(pci_dev, 0x7c, &dwVal); pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); udelay(5000); pci_read_config_dword(pci_dev, 0x7c, &dwVal); pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); udelay(5000); } pci_dev = codec->pci; pci_read_config_dword(pci_dev, 0x44, &dwVal); pci_write_config_dword(pci_dev, 0x44, dwVal | 0x000c0000); udelay(500); pci_read_config_dword(pci_dev, 0x44, &dwVal); pci_write_config_dword(pci_dev, 0x44, dwVal & 0xfffbffff); udelay(5000); wCount = 200; while(wCount--) { wReg = snd_ali_codec_peek(codec, 0, AC97_POWERDOWN); if((wReg & 0x000f) == 0x000f) return 0; udelay(5000); } /* non-fatal if you have a non PM capable codec */ /* snd_printk(KERN_WARNING "ali5451: reset time out\n"); */ return 0;}#ifdef CODEC_RESETstatic int snd_ali_reset_codec(ali_t *codec){ struct pci_dev *pci_dev = NULL; unsigned char bVal = 0; unsigned int dwVal; unsigned short wCount, wReg; pci_dev = codec->pci_m1533; pci_read_config_dword(pci_dev, 0x7c, &dwVal); pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000); udelay(5000); pci_read_config_dword(pci_dev, 0x7c, &dwVal); pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff); udelay(5000); bVal = inb(ALI_REG(codec,ALI_SCTRL)); bVal |= 0x02; outb(ALI_REG(codec,ALI_SCTRL),bVal); udelay(5000); bVal = inb(ALI_REG(codec,ALI_SCTRL)); bVal &= 0xfd; outb(ALI_REG(codec,ALI_SCTRL),bVal); udelay(15000); wCount = 200; while(wCount--) { wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN); if((wReg & 0x000f) == 0x000f) return 0; udelay(5000); } return -1;}#endif/* * ALI 5451 Controller */static void snd_ali_enable_special_channel(ali_t *codec, unsigned int channel){ unsigned long dwVal = 0; dwVal = inl(ALI_REG(codec,ALI_GLOBAL_CONTROL)); dwVal |= 1 << (channel & 0x0000001f); outl(dwVal, ALI_REG(codec,ALI_GLOBAL_CONTROL));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -