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

📄 cs46xx_lib.c

📁 是关于linux2.5.1的完全源码
💻 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 * *  BUGS: *    -- * *  TODO: *    We need a DSP code to support multichannel outputs and S/PDIF. *    Unfortunately, it seems that Cirrus Logic, Inc. is not willing *    to provide us sufficient information about the DSP processor, *    so we can't update the driver. * * *   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 * */#define __NO_VERSION__#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/pm.h>#include <linux/init.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/control.h>#include <sound/info.h>#include <sound/cs46xx.h>#ifndef LINUX_2_2#include <linux/gameport.h>#endif#define chip_t cs46xx_t/* *  constants */#define CS46XX_BA0_SIZE		0x1000#define CS46XX_BA1_DATA0_SIZE	0x3000#define CS46XX_BA1_DATA1_SIZE	0x3800#define CS46XX_BA1_PRG_SIZE	0x7000#define CS46XX_BA1_REG_SIZE	0x0100#define CS46XX_PERIOD_SIZE 2048#define CS46XX_FRAGS 2#define CS46XX_BUFFER_SIZE CS46XX_PERIOD_SIZE * CS46XX_FRAGSextern snd_pcm_ops_t snd_cs46xx_playback_ops;extern snd_pcm_ops_t snd_cs46xx_playback_indirect_ops;extern snd_pcm_ops_t snd_cs46xx_capture_ops;extern snd_pcm_ops_t snd_cs46xx_capture_indirect_ops;/* *  common I/O routines */static inline void snd_cs46xx_poke(cs46xx_t *chip, unsigned long reg, unsigned int val){	unsigned int bank = reg >> 16;	unsigned int offset = reg & 0xffff;	writel(val, chip->region.idx[bank+1].remap_addr + offset);}static inline unsigned int snd_cs46xx_peek(cs46xx_t *chip, unsigned long reg){	unsigned int bank = reg >> 16;	unsigned int offset = reg & 0xffff;	return readl(chip->region.idx[bank+1].remap_addr + offset);}static inline void snd_cs46xx_pokeBA0(cs46xx_t *chip, unsigned long offset, unsigned int val){	writel(val, chip->region.name.ba0.remap_addr + offset);}static inline unsigned int snd_cs46xx_peekBA0(cs46xx_t *chip, unsigned long offset){	return readl(chip->region.name.ba0.remap_addr + offset);}static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip,					    unsigned short reg){	int count;	unsigned short result;	/*	 *  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_cs46xx_peekBA0(chip, 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_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);	snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0);	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | 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) & ACSTS_VSTS)			goto ok2;		udelay(10);	}		snd_printk("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	 */#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	result = snd_cs46xx_peekBA0(chip, BA0_ACSDA); end:	return result;}static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97,					    unsigned short reg){	cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return -ENXIO);	unsigned short val;	chip->active_ctrl(chip, 1);	val = snd_cs46xx_codec_read(chip, reg);	chip->active_ctrl(chip, -1);	return val;}static void snd_cs46xx_codec_write(cs46xx_t *chip,				   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	 */	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_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);	snd_cs46xx_pokeBA0(chip, BA0_ACCDA, val);	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | 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)) {			return;		}	}	snd_printk("AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val);}static void snd_cs46xx_ac97_write(ac97_t *ac97,				   unsigned short reg,				   unsigned short val){	cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return);	int val2 = 0;	chip->active_ctrl(chip, 1);	if (reg == AC97_CD)		val2 = snd_cs46xx_codec_read(chip, AC97_CD);	snd_cs46xx_codec_write(chip, reg, val);		/*	 *	Adjust power if the mixer is selected/deselected according	 *	to the CD.	 *	 *	IF the CD is a valid input source (mixer or direct) AND	 *		the CD is not muted THEN power is needed	 *	 *	We do two things. When record select changes the input to	 *	add/remove the CD we adjust the power count if the CD is	 *	unmuted.	 *	 *	When the CD mute changes we adjust the power level if the	 *	CD was a valid input.	 *	 *      We also check for CD volume != 0, as the CD mute isn't	 *      normally tweaked from userspace.	 */	 	/* CD mute change ? */		if (reg == AC97_CD) {		/* Mute bit change ? */		if ((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) {			/* Mute on */			if(val&0x8000 || val == 0x1f1f)				chip->amplifier_ctrl(chip, -1);			else /* Mute off power on */				chip->amplifier_ctrl(chip, 1);		}	}	chip->active_ctrl(chip, -1);}/* *  Chip initialization */int snd_cs46xx_download(cs46xx_t *chip,			u32 *src,                        unsigned long offset,                        unsigned long len){	unsigned long 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;}/* 3*1024 parameter, 3.5*1024 sample, 2*3.5*1024 code */#define BA1_DWORD_SIZE		(13 * 1024 + 512)#define BA1_MEMORY_COUNT	3struct BA1struct {	struct {		unsigned long offset;		unsigned long size;	} memory[BA1_MEMORY_COUNT];	u32 map[BA1_DWORD_SIZE];};static#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;}/* *  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 void snd_cs46xx_clear_serial_FIFOs(cs46xx_t *chip){	int idx, loop, 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.	 */	for (idx = 0; idx < 256; idx++) {		/*		 *  Make sure the previous FIFO write operation has completed.		 */		for (loop = 0; loop < 5; loop++) {			udelay(50);			if (!(snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY))				break;		}		if (snd_cs46xx_peekBA0(chip, BA0_SERBST) & SERBST_WBSY) {			if (powerdown)				snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);		}		/*		 *  Write the serial port FIFO index.		 */		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);		/*		 *  Tell the serial port to load the new value into the FIFO location.		 */		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);	}	/*	 *  Now, if we powered up the devices, then power them back down again.	 *  This is kinda ugly, but should never happen.	 */	if (powerdown)		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);}static void snd_cs46xx_proc_start(cs46xx_t *chip){	int cnt;	/*	 *  Set the frame timer to reflect the number of cycles per frame.	 */	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);	/*	 *  Turn on the run, run at frame, and DMA enable bits in the local copy of	 *  the SP control register.	 */	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);	/*	 *  Wait until the run at frame bit resets itself in the SP control	 *  register.	 */	for (cnt = 0; cnt < 25; cnt++) {		udelay(50);		if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR))			break;	}	if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)		snd_printk("SPCR_RUNFR never reset\n");}static void snd_cs46xx_proc_stop(cs46xx_t *chip){	/*	 *  Turn off the run, run at frame, and DMA enable bits in the local copy of	 *  the SP control register.	 */	snd_cs46xx_poke(chip, BA1_SPCR, 0);

⌨️ 快捷键说明

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