📄 cs4281.c
字号:
/* JS GPIO */#define JSIO_DAX 0x00000001#define JSIO_DAY 0x00000002#define JSIO_DBX 0x00000004#define JSIO_DBY 0x00000008#define JSIO_AXOE 0x00000010#define JSIO_AYOE 0x00000020#define JSIO_BXOE 0x00000040#define JSIO_BYOE 0x00000080/* * */struct cs4281_dma { struct snd_pcm_substream *substream; unsigned int regDBA; /* offset to DBA register */ unsigned int regDCA; /* offset to DCA register */ unsigned int regDBC; /* offset to DBC register */ unsigned int regDCC; /* offset to DCC register */ unsigned int regDMR; /* offset to DMR register */ unsigned int regDCR; /* offset to DCR register */ unsigned int regHDSR; /* offset to HDSR register */ unsigned int regFCR; /* offset to FCR register */ unsigned int regFSIC; /* offset to FSIC register */ unsigned int valDMR; /* DMA mode */ unsigned int valDCR; /* DMA command */ unsigned int valFCR; /* FIFO control */ unsigned int fifo_offset; /* FIFO offset within BA1 */ unsigned char left_slot; /* FIFO left slot */ unsigned char right_slot; /* FIFO right slot */ int frag; /* period number */};#define SUSPEND_REGISTERS 20struct cs4281 { int irq; void __iomem *ba0; /* virtual (accessible) address */ void __iomem *ba1; /* virtual (accessible) address */ unsigned long ba0_addr; unsigned long ba1_addr; int dual_codec; struct snd_ac97_bus *ac97_bus; struct snd_ac97 *ac97; struct snd_ac97 *ac97_secondary; struct pci_dev *pci; struct snd_card *card; struct snd_pcm *pcm; struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *midi_input; struct snd_rawmidi_substream *midi_output; struct cs4281_dma dma[4]; unsigned char src_left_play_slot; unsigned char src_right_play_slot; unsigned char src_left_rec_slot; unsigned char src_right_rec_slot; unsigned int spurious_dhtc_irq; unsigned int spurious_dtc_irq; spinlock_t reg_lock; unsigned int midcr; unsigned int uartm; struct gameport *gameport;#ifdef CONFIG_PM u32 suspend_regs[SUSPEND_REGISTERS];#endif};static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id);static struct pci_device_id snd_cs4281_ids[] = { { 0x1013, 0x6005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4281 */ { 0, }};MODULE_DEVICE_TABLE(pci, snd_cs4281_ids);/* * constants */#define CS4281_FIFO_SIZE 32/* * common I/O routines */static inline void snd_cs4281_pokeBA0(struct cs4281 *chip, unsigned long offset, unsigned int val){ writel(val, chip->ba0 + offset);}static inline unsigned int snd_cs4281_peekBA0(struct cs4281 *chip, unsigned long offset){ return readl(chip->ba0 + offset);}static void snd_cs4281_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 * 3. Write ACCTL = Control Register = 460h for initiating the write * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h * 5. if DCV not cleared, break and return error */ struct cs4281 *chip = ac97->private_data; int count; /* * Setup the AC97 control registers on the CS461x to send the * appropriate command to the AC97 to perform the read. * ACCAD = Command Address Register = 46Ch * ACCDA = Command Data Register = 470h * ACCTL = Control Register = 460h * set DCV - will clear when process completed * reset CRW - Write command * set VFRM - valid frame enabled * set ESYN - ASYNC generation enabled * set RSTN - ARST# inactive, AC97 codec not reset */ snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); snd_cs4281_pokeBA0(chip, BA0_ACCDA, val); snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM | BA0_ACCTL_ESYN | (ac97->num ? BA0_ACCTL_TC : 0)); for (count = 0; count < 2000; count++) { /* * First, we want to wait for a short time. */ udelay(10); /* * Now, check to see if the write has completed. * ACCTL = 460h, DCV should be reset by now and 460h = 07h */ if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) { return; } } snd_printk(KERN_ERR "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);}static unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97, unsigned short reg){ struct cs4281 *chip = ac97->private_data; int count; unsigned short result; // FIXME: volatile is necessary in the following due to a bug of // some gcc versions volatile int ac97_num = ((volatile struct snd_ac97 *)ac97)->num; /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 * 3. Write ACCTL = Control Register = 460h for initiating the write * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h * 5. if DCV not cleared, break and return error * 6. Read ACSTS = Status Register = 464h, check VSTS bit */ snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); /* * Setup the AC97 control registers on the CS461x to send the * appropriate command to the AC97 to perform the read. * ACCAD = Command Address Register = 46Ch * ACCDA = Command Data Register = 470h * ACCTL = Control Register = 460h * set DCV - will clear when process completed * set CRW - Read command * set VFRM - valid frame enabled * set ESYN - ASYNC generation enabled * set RSTN - ARST# inactive, AC97 codec not reset */ snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0); snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW | BA0_ACCTL_VFRM | BA0_ACCTL_ESYN | (ac97_num ? BA0_ACCTL_TC : 0)); /* * Wait for the read to occur. */ for (count = 0; count < 500; count++) { /* * First, we want to wait for a short time. */ udelay(10); /* * Now, check to see if the read has completed. * ACCTL = 460h, DCV should be reset by now and 460h = 17h */ if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) goto __ok1; } snd_printk(KERN_ERR "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); result = 0xffff; goto __end; __ok1: /* * Wait for the valid status bit to go active. */ for (count = 0; count < 100; count++) { /* * Read the AC97 status register. * ACSTS = Status Register = 464h * VSTS - Valid Status */ if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS) goto __ok2; udelay(10); } snd_printk(KERN_ERR "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg); result = 0xffff; goto __end; __ok2: /* * Read the data returned from the AC97 register. * ACSDA = Status Data Register = 474h */ result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); __end: return result;}/* * PCM part */static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd){ struct cs4281_dma *dma = substream->runtime->private_data; struct cs4281 *chip = snd_pcm_substream_chip(substream); spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: dma->valDCR |= BA0_DCR_MSK; dma->valFCR |= BA0_FCR_FEN; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: dma->valDCR &= ~BA0_DCR_MSK; dma->valFCR &= ~BA0_FCR_FEN; break; case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA); dma->valDMR |= BA0_DMR_DMA; dma->valDCR &= ~BA0_DCR_MSK; dma->valFCR |= BA0_FCR_FEN; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL); dma->valDCR |= BA0_DCR_MSK; dma->valFCR &= ~BA0_FCR_FEN; /* Leave wave playback FIFO enabled for FM */ if (dma->regFCR != BA0_FCR0) dma->valFCR &= ~BA0_FCR_FEN; break; default: spin_unlock(&chip->reg_lock); return -EINVAL; } snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR); snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR); snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR); spin_unlock(&chip->reg_lock); return 0;}static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate){ unsigned int val = ~0; if (real_rate) *real_rate = rate; /* special "hardcoded" rates */ switch (rate) { case 8000: return 5; case 11025: return 4; case 16000: return 3; case 22050: return 2; case 44100: return 1; case 48000: return 0; default: goto __variable; } __variable: val = 1536000 / rate; if (real_rate) *real_rate = 1536000 / val; return val;}static void snd_cs4281_mode(struct cs4281 *chip, struct cs4281_dma *dma, struct snd_pcm_runtime *runtime, int capture, int src){ int rec_mono; dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO | (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ); if (runtime->channels == 1) dma->valDMR |= BA0_DMR_MONO; if (snd_pcm_format_unsigned(runtime->format) > 0) dma->valDMR |= BA0_DMR_USIGN; if (snd_pcm_format_big_endian(runtime->format) > 0) dma->valDMR |= BA0_DMR_BEND; switch (snd_pcm_format_width(runtime->format)) { case 8: dma->valDMR |= BA0_DMR_SIZE8; if (runtime->channels == 1) dma->valDMR |= BA0_DMR_SWAPC; break; case 32: dma->valDMR |= BA0_DMR_SIZE20; break; } dma->frag = 0; /* for workaround */ dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK; if (runtime->buffer_size != runtime->period_size) dma->valDCR |= BA0_DCR_HTCIE; /* Initialize DMA */ snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr); snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1); rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO; snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | (chip->src_right_play_slot << 8) | (chip->src_left_rec_slot << 16) | ((rec_mono ? 31 : chip->src_right_rec_slot) << 24)); if (!src) goto __skip_src; if (!capture) { if (dma->left_slot == chip->src_left_play_slot) { unsigned int val = snd_cs4281_rate(runtime->rate, NULL); snd_assert(dma->right_slot == chip->src_right_play_slot, ); snd_cs4281_pokeBA0(chip, BA0_DACSR, val); } } else { if (dma->left_slot == chip->src_left_rec_slot) { unsigned int val = snd_cs4281_rate(runtime->rate, NULL); snd_assert(dma->right_slot == chip->src_right_rec_slot, ); snd_cs4281_pokeBA0(chip, BA0_ADCSR, val); } } __skip_src: /* Deactivate wave playback FIFO before changing slot assignments */ if (dma->regFCR == BA0_FCR0) snd_cs4281_pokeBA0(chip, dma->regFCR, snd_cs4281_peekBA0(chip, dma->regFCR) & ~BA0_FCR_FEN); /* Initialize FIFO */ dma->valFCR = BA0_FCR_LS(dma->left_slot) | BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) | BA0_FCR_SZ(CS4281_FIFO_SIZE) | BA0_FCR_OF(dma->fifo_offset); snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0)); /* Activate FIFO again for FM playback */ if (dma->regFCR == BA0_FCR0) snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | BA0_FCR_FEN); /* Clear FIFO Status and Interrupt Control Register */ snd_cs4281_pokeBA0(chip, dma->regFSIC, 0);}static int snd_cs4281_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params){ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_cs4281_hw_free(struct snd_pcm_substream *substream){ return snd_pcm_lib_free_pages(substream);}static int snd_cs4281_playback_prepare(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct cs4281_dma *dma = runtime->private_data; struct cs4281 *chip = snd_pcm_substream_chip(substream); spin_lock_irq(&chip->reg_lock); snd_cs4281_mode(chip, dma, runtime, 0, 1); spin_unlock_irq(&chip->reg_lock); return 0;}static int snd_cs4281_capture_prepare(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime = substream->runtime; struct cs4281_dma *dma = runtime->private_data; struct cs4281 *chip = snd_pcm_substream_chip(substream); spin_lock_irq(&chip->reg_lock); snd_cs4281_mode(chip, dma, runtime, 1, 1); spin_unlock_irq(&chip->reg_lock); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -