⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cs4281.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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 + -