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

📄 cs46xx_lib.c

📁 鼎力推荐!本程序是基于嵌入式LUNUX系统开发的源程序代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> *                   Abramo Bagnara <abramo@alsa-project.org> *                   Cirrus Logic, Inc. *  Routines for control of Cirrus Logic CS461x chips * *  KNOWN BUGS: *    - Sometimes the SPDIF input DSP tasks get's unsynchronized *      and the SPDIF get somewhat "distorcionated", or/and left right channel *      are swapped. To get around this problem when it happens, mute and unmute  *      the SPDIF input mixer controll. *    - On the Hercules Game Theater XP the amplifier are sometimes turned *      off on inadecuate moments which causes distorcions on sound. * *  TODO: *    - Secondary CODEC on some soundcards *    - SPDIF input support for other sample rates then 48khz *    - Posibility to mix the SPDIF output with analog sources. *    - PCM channels for Center and LFE on secondary codec * *  NOTE: with CONFIG_SND_CS46XX_NEW_DSP unset uses old DSP image (which *        is default configuration), no SPDIF, no secondary codec, no *        multi channel PCM.  But known to work. * *  FINALLY: A credit to the developers Tom and Jordan  *           at Cirrus for have helping me out with the DSP, however we *           still don't have sufficient documentation and technical *           references to be able to implement all fancy feutures *           supported by the cs46xx DSP's.  *           Benny <benny@hostmobility.com> *                 *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License as published by *   the Free Software Foundation; either version 2 of the License, 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 License for more details. * *   You should have received a copy of the GNU General Public License *   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 <linux/delay.h>#include <linux/pci.h>#include <linux/pm.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/gameport.h>#include <sound/core.h>#include <sound/control.h>#include <sound/info.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include <sound/cs46xx.h>#include <asm/io.h>#include "cs46xx_lib.h"#include "dsp_spos.h"static void amp_voyetra(cs46xx_t *chip, int change);#ifdef CONFIG_SND_CS46XX_NEW_DSPstatic snd_pcm_ops_t snd_cs46xx_playback_rear_ops;static snd_pcm_ops_t snd_cs46xx_playback_indirect_rear_ops;static snd_pcm_ops_t snd_cs46xx_playback_clfe_ops;static snd_pcm_ops_t snd_cs46xx_playback_indirect_clfe_ops;static snd_pcm_ops_t snd_cs46xx_playback_iec958_ops;static snd_pcm_ops_t snd_cs46xx_playback_indirect_iec958_ops;#endifstatic snd_pcm_ops_t snd_cs46xx_playback_ops;static snd_pcm_ops_t snd_cs46xx_playback_indirect_ops;static snd_pcm_ops_t snd_cs46xx_capture_ops;static snd_pcm_ops_t snd_cs46xx_capture_indirect_ops;static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip,					    unsigned short reg,					    int codec_index){	int count;	unsigned short result,tmp;	u32 offset = 0;	snd_assert ( (codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||		     (codec_index == CS46XX_SECONDARY_CODEC_INDEX),		     return -EINVAL);	chip->active_ctrl(chip, 1);	if (codec_index == CS46XX_SECONDARY_CODEC_INDEX)		offset = CS46XX_SECONDARY_CODEC_OFFSET;	/*	 *  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 write7---55	 *  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_cs46xx_peekBA0(chip, BA0_ACSDA + offset);	tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL);	if ((tmp & ACCTL_VFRM) == 0) {		snd_printk(KERN_WARNING  "cs46xx: ACCTL_VFRM not set 0x%x\n",tmp);		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM );		msleep(50);		tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset);		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, tmp | ACCTL_ESYN | ACCTL_VFRM );	}	/*	 *  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_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);	snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0);	if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {		snd_cs46xx_pokeBA0(chip, BA0_ACCTL,/* clear ACCTL_DCV */ ACCTL_CRW | 				   ACCTL_VFRM | ACCTL_ESYN |				   ACCTL_RSTN);		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |				   ACCTL_VFRM | ACCTL_ESYN |				   ACCTL_RSTN);	} else {		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |				   ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN |				   ACCTL_RSTN);	}	/*	 *  Wait for the read to occur.	 */	for (count = 0; count < 1000; 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_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV))			goto ok1;	}	snd_printk("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_cs46xx_peekBA0(chip, BA0_ACSTS + offset) & ACSTS_VSTS)			goto ok2;		udelay(10);	}		snd_printk("AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n", codec_index, reg);	result = 0xffff;	goto end; ok2:	/*	 *  Read the data returned from the AC97 register.	 *  ACSDA = Status Data Register = 474h	 */#if 0	printk("e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,			snd_cs46xx_peekBA0(chip, BA0_ACSDA),			snd_cs46xx_peekBA0(chip, BA0_ACCAD));#endif	//snd_cs46xx_peekBA0(chip, BA0_ACCAD);	result = snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset); end:	chip->active_ctrl(chip, -1);	return result;}static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97,					    unsigned short reg){	cs46xx_t *chip = ac97->private_data;	unsigned short val;	int codec_index = ac97->num;	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||		   codec_index == CS46XX_SECONDARY_CODEC_INDEX,		   return 0xffff);	val = snd_cs46xx_codec_read(chip, reg, codec_index);	return val;}static void snd_cs46xx_codec_write(cs46xx_t *chip,				   unsigned short reg,				   unsigned short val,				   int codec_index){	int count;	snd_assert ((codec_index == CS46XX_PRIMARY_CODEC_INDEX) ||		    (codec_index == CS46XX_SECONDARY_CODEC_INDEX),		    return);	chip->active_ctrl(chip, 1);	/*	 *  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	 */	/*	 *  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_cs46xx_pokeBA0(chip, BA0_ACCAD , reg);	snd_cs46xx_pokeBA0(chip, BA0_ACCDA , val);	snd_cs46xx_peekBA0(chip, BA0_ACCTL);	if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, /* clear ACCTL_DCV */ ACCTL_VFRM |				   ACCTL_ESYN | ACCTL_RSTN);		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM |				   ACCTL_ESYN | ACCTL_RSTN);	} else {		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |				   ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);	}	for (count = 0; count < 4000; 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_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) {			goto end;		}	}	snd_printk("AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n", codec_index, reg, val); end:	chip->active_ctrl(chip, -1);}static void snd_cs46xx_ac97_write(ac97_t *ac97,				   unsigned short reg,				   unsigned short val){	cs46xx_t *chip = ac97->private_data;	int codec_index = ac97->num;	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX ||		   codec_index == CS46XX_SECONDARY_CODEC_INDEX,		   return);	snd_cs46xx_codec_write(chip, reg, val, codec_index);}/* *  Chip initialization */int snd_cs46xx_download(cs46xx_t *chip,			u32 *src,                        unsigned long offset,                        unsigned long len){	void __iomem *dst;	unsigned int bank = offset >> 16;	offset = offset & 0xffff;	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);	dst = chip->region.idx[bank+1].remap_addr + offset;	len /= sizeof(u32);	/* writel already converts 32-bit value to right endianess */	while (len-- > 0) {		writel(*src++, dst);		dst += sizeof(u32);	}	return 0;}#ifdef CONFIG_SND_CS46XX_NEW_DSP#include "imgs/cwc4630.h"#include "imgs/cwcasync.h"#include "imgs/cwcsnoop.h"#include "imgs/cwcbinhack.h"#include "imgs/cwcdma.h"int snd_cs46xx_clear_BA1(cs46xx_t *chip,                         unsigned long offset,                         unsigned long len) {	void __iomem *dst;	unsigned int bank = offset >> 16;	offset = offset & 0xffff;	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL);	dst = chip->region.idx[bank+1].remap_addr + offset;	len /= sizeof(u32);	/* writel already converts 32-bit value to right endianess */	while (len-- > 0) {		writel(0, dst);		dst += sizeof(u32);	}	return 0;}#else /* old DSP image */#include "cs46xx_image.h"int snd_cs46xx_download_image(cs46xx_t *chip){	int idx, err;	unsigned long offset = 0;	for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {		if ((err = snd_cs46xx_download(chip,					       &BA1Struct.map[offset],					       BA1Struct.memory[idx].offset,					       BA1Struct.memory[idx].size)) < 0)			return err;		offset += BA1Struct.memory[idx].size >> 2;	}		return 0;}#endif /* CONFIG_SND_CS46XX_NEW_DSP *//* *  Chip reset */static void snd_cs46xx_reset(cs46xx_t *chip){	int idx;	/*	 *  Write the reset bit of the SP control register.	 */	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP);	/*	 *  Write the control register.	 */	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN);	/*	 *  Clear the trap registers.	 */	for (idx = 0; idx < 8; idx++) {		snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx);		snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF);	}	snd_cs46xx_poke(chip, BA1_DREG, 0);	/*	 *  Set the frame timer to reflect the number of cycles per frame.	 */	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);}static int cs46xx_wait_for_fifo(cs46xx_t * chip,int retry_timeout) {	u32 i, status = 0;	/*	 * Make sure the previous FIFO write operation has completed.	 */	for(i = 0; i < 50; i++){		status = snd_cs46xx_peekBA0(chip, BA0_SERBST);    		if( !(status & SERBST_WBSY) )			break;		mdelay(retry_timeout);	}  	if(status & SERBST_WBSY) {		snd_printk( KERN_ERR "cs46xx: failure waiting for FIFO command to complete\n");		return -EINVAL;	}	return 0;}static void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip){	int idx, powerdown = 0;	unsigned int tmp;	/*	 *  See if the devices are powered down.  If so, we must power them up first	 *  or they will not respond.	 */	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);	if (!(tmp & CLKCR1_SWCE)) {		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);		powerdown = 1;	}	/*	 *  We want to clear out the serial port FIFOs so we don't end up playing	 *  whatever random garbage happens to be in them.  We fill the sample FIFOS	 *  with zero (silence).	 */	snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0);	/*	 *  Fill all 256 sample FIFO locations.	 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -