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

📄 cs4231.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * Driver for CS4231 sound chips found on Sparcs. * Copyright (C) 2002 David S. Miller <davem@redhat.com> * * Based entirely upon drivers/sbus/audio/cs4231.c which is: * Copyright (C) 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu) * and also sound/isa/cs423x/cs4231_lib.c which is: * Copyright (c) by Jaroslav Kysela <perex@perex.cz> */#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/moduleparam.h>#include <linux/irq.h>#include <linux/io.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/info.h>#include <sound/control.h>#include <sound/timer.h>#include <sound/initval.h>#include <sound/pcm_params.h>#ifdef CONFIG_SBUS#define SBUS_SUPPORT#include <asm/sbus.h>#endif#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64)#define EBUS_SUPPORT#include <linux/pci.h>#include <asm/ebus.h>#endifstatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card *//* Enable this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for Sun CS4231 soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for Sun CS4231 soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable Sun CS4231 soundcard.");MODULE_AUTHOR("Jaroslav Kysela, Derrick J. Brashear and David S. Miller");MODULE_DESCRIPTION("Sun CS4231");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Sun,CS4231}}");#ifdef SBUS_SUPPORTstruct sbus_dma_info {       spinlock_t	lock;	/* DMA access lock */       int		dir;       void __iomem	*regs;};#endifstruct snd_cs4231;struct cs4231_dma_control {	void		(*prepare)(struct cs4231_dma_control *dma_cont,				   int dir);	void		(*enable)(struct cs4231_dma_control *dma_cont, int on);	int		(*request)(struct cs4231_dma_control *dma_cont,				   dma_addr_t bus_addr, size_t len);	unsigned int	(*address)(struct cs4231_dma_control *dma_cont);	void		(*preallocate)(struct snd_cs4231 *chip,				       struct snd_pcm *pcm);#ifdef EBUS_SUPPORT	struct		ebus_dma_info	ebus_info;#endif#ifdef SBUS_SUPPORT	struct		sbus_dma_info	sbus_info;#endif};struct snd_cs4231 {	spinlock_t		lock;	/* registers access lock */	void __iomem		*port;	struct cs4231_dma_control	p_dma;	struct cs4231_dma_control	c_dma;	u32			flags;#define CS4231_FLAG_EBUS	0x00000001#define CS4231_FLAG_PLAYBACK	0x00000002#define CS4231_FLAG_CAPTURE	0x00000004	struct snd_card		*card;	struct snd_pcm		*pcm;	struct snd_pcm_substream	*playback_substream;	unsigned int		p_periods_sent;	struct snd_pcm_substream	*capture_substream;	unsigned int		c_periods_sent;	struct snd_timer	*timer;	unsigned short mode;#define CS4231_MODE_NONE	0x0000#define CS4231_MODE_PLAY	0x0001#define CS4231_MODE_RECORD	0x0002#define CS4231_MODE_TIMER	0x0004#define CS4231_MODE_OPEN	(CS4231_MODE_PLAY | CS4231_MODE_RECORD | \				 CS4231_MODE_TIMER)	unsigned char		image[32];	/* registers image */	int			mce_bit;	int			calibrate_mute;	struct mutex		mce_mutex;	/* mutex for mce register */	struct mutex		open_mutex;	/* mutex for ALSA open/close */	union {#ifdef SBUS_SUPPORT		struct sbus_dev		*sdev;#endif#ifdef EBUS_SUPPORT		struct pci_dev		*pdev;#endif	} dev_u;	unsigned int		irq[2];	unsigned int		regs_size;	struct snd_cs4231	*next;};static struct snd_cs4231 *cs4231_list;/* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for * now....  -DaveM *//* IO ports */#include <sound/cs4231-regs.h>/* XXX offsets are different than PC ISA chips... */#define CS4231U(chip, x)	((chip)->port + ((c_d_c_CS4231##x) << 2))/* SBUS DMA register defines.  */#define APCCSR	0x10UL	/* APC DMA CSR */#define APCCVA	0x20UL	/* APC Capture DMA Address */#define APCCC	0x24UL	/* APC Capture Count */#define APCCNVA	0x28UL	/* APC Capture DMA Next Address */#define APCCNC	0x2cUL	/* APC Capture Next Count */#define APCPVA	0x30UL	/* APC Play DMA Address */#define APCPC	0x34UL	/* APC Play Count */#define APCPNVA	0x38UL	/* APC Play DMA Next Address */#define APCPNC	0x3cUL	/* APC Play Next Count *//* Defines for SBUS DMA-routines */#define APCVA  0x0UL	/* APC DMA Address */#define APCC   0x4UL	/* APC Count */#define APCNVA 0x8UL	/* APC DMA Next Address */#define APCNC  0xcUL	/* APC Next Count */#define APC_PLAY 0x30UL	/* Play registers start at 0x30 */#define APC_RECORD 0x20UL /* Record registers start at 0x20 *//* APCCSR bits */#define APC_INT_PENDING 0x800000 /* Interrupt Pending */#define APC_PLAY_INT    0x400000 /* Playback interrupt */#define APC_CAPT_INT    0x200000 /* Capture interrupt */#define APC_GENL_INT    0x100000 /* General interrupt */#define APC_XINT_ENA    0x80000  /* General ext int. enable */#define APC_XINT_PLAY   0x40000  /* Playback ext intr */#define APC_XINT_CAPT   0x20000  /* Capture ext intr */#define APC_XINT_GENL   0x10000  /* Error ext intr */#define APC_XINT_EMPT   0x8000   /* Pipe empty interrupt (0 write to pva) */#define APC_XINT_PEMP   0x4000   /* Play pipe empty (pva and pnva not set) */#define APC_XINT_PNVA   0x2000   /* Playback NVA dirty */#define APC_XINT_PENA   0x1000   /* play pipe empty Int enable */#define APC_XINT_COVF   0x800    /* Cap data dropped on floor */#define APC_XINT_CNVA   0x400    /* Capture NVA dirty */#define APC_XINT_CEMP   0x200    /* Capture pipe empty (cva and cnva not set) */#define APC_XINT_CENA   0x100    /* Cap. pipe empty int enable */#define APC_PPAUSE      0x80     /* Pause the play DMA */#define APC_CPAUSE      0x40     /* Pause the capture DMA */#define APC_CDC_RESET   0x20     /* CODEC RESET */#define APC_PDMA_READY  0x08     /* Play DMA Go */#define APC_CDMA_READY  0x04     /* Capture DMA Go */#define APC_CHIP_RESET  0x01     /* Reset the chip *//* EBUS DMA register offsets  */#define EBDMA_CSR	0x00UL	/* Control/Status */#define EBDMA_ADDR	0x04UL	/* DMA Address */#define EBDMA_COUNT	0x08UL	/* DMA Count *//* *  Some variables */static unsigned char freq_bits[14] = {	/* 5510 */	0x00 | CS4231_XTAL2,	/* 6620 */	0x0E | CS4231_XTAL2,	/* 8000 */	0x00 | CS4231_XTAL1,	/* 9600 */	0x0E | CS4231_XTAL1,	/* 11025 */	0x02 | CS4231_XTAL2,	/* 16000 */	0x02 | CS4231_XTAL1,	/* 18900 */	0x04 | CS4231_XTAL2,	/* 22050 */	0x06 | CS4231_XTAL2,	/* 27042 */	0x04 | CS4231_XTAL1,	/* 32000 */	0x06 | CS4231_XTAL1,	/* 33075 */	0x0C | CS4231_XTAL2,	/* 37800 */	0x08 | CS4231_XTAL2,	/* 44100 */	0x0A | CS4231_XTAL2,	/* 48000 */	0x0C | CS4231_XTAL1};static unsigned int rates[14] = {	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,	27042, 32000, 33075, 37800, 44100, 48000};static struct snd_pcm_hw_constraint_list hw_constraints_rates = {	.count	= ARRAY_SIZE(rates),	.list	= rates,};static int snd_cs4231_xrate(struct snd_pcm_runtime *runtime){	return snd_pcm_hw_constraint_list(runtime, 0,					  SNDRV_PCM_HW_PARAM_RATE,					  &hw_constraints_rates);}static unsigned char snd_cs4231_original_image[32] ={	0x00,			/* 00/00 - lic */	0x00,			/* 01/01 - ric */	0x9f,			/* 02/02 - la1ic */	0x9f,			/* 03/03 - ra1ic */	0x9f,			/* 04/04 - la2ic */	0x9f,			/* 05/05 - ra2ic */	0xbf,			/* 06/06 - loc */	0xbf,			/* 07/07 - roc */	0x20,			/* 08/08 - pdfr */	CS4231_AUTOCALIB,	/* 09/09 - ic */	0x00,			/* 0a/10 - pc */	0x00,			/* 0b/11 - ti */	CS4231_MODE2,		/* 0c/12 - mi */	0x00,			/* 0d/13 - lbc */	0x00,			/* 0e/14 - pbru */	0x00,			/* 0f/15 - pbrl */	0x80,			/* 10/16 - afei */	0x01,			/* 11/17 - afeii */	0x9f,			/* 12/18 - llic */	0x9f,			/* 13/19 - rlic */	0x00,			/* 14/20 - tlb */	0x00,			/* 15/21 - thb */	0x00,			/* 16/22 - la3mic/reserved */	0x00,			/* 17/23 - ra3mic/reserved */	0x00,			/* 18/24 - afs */	0x00,			/* 19/25 - lamoc/version */	0x00,			/* 1a/26 - mioc */	0x00,			/* 1b/27 - ramoc/reserved */	0x20,			/* 1c/28 - cdfr */	0x00,			/* 1d/29 - res4 */	0x00,			/* 1e/30 - cbru */	0x00,			/* 1f/31 - cbrl */};static u8 __cs4231_readb(struct snd_cs4231 *cp, void __iomem *reg_addr){#ifdef EBUS_SUPPORT	if (cp->flags & CS4231_FLAG_EBUS)		return readb(reg_addr);	else#endif#ifdef SBUS_SUPPORT		return sbus_readb(reg_addr);#endif}static void __cs4231_writeb(struct snd_cs4231 *cp, u8 val,			    void __iomem *reg_addr){#ifdef EBUS_SUPPORT	if (cp->flags & CS4231_FLAG_EBUS)		return writeb(val, reg_addr);	else#endif#ifdef SBUS_SUPPORT		return sbus_writeb(val, reg_addr);#endif}/* *  Basic I/O functions */static void snd_cs4231_ready(struct snd_cs4231 *chip){	int timeout;	for (timeout = 250; timeout > 0; timeout--) {		int val = __cs4231_readb(chip, CS4231U(chip, REGSEL));		if ((val & CS4231_INIT) == 0)			break;		udelay(100);	}}static void snd_cs4231_dout(struct snd_cs4231 *chip, unsigned char reg,			    unsigned char value){	snd_cs4231_ready(chip);#ifdef CONFIG_SND_DEBUG	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)		snd_printdd("out: auto calibration time out - reg = 0x%x, "			    "value = 0x%x\n",			    reg, value);#endif	__cs4231_writeb(chip, chip->mce_bit | reg, CS4231U(chip, REGSEL));	wmb();	__cs4231_writeb(chip, value, CS4231U(chip, REG));	mb();}static inline void snd_cs4231_outm(struct snd_cs4231 *chip, unsigned char reg,		     unsigned char mask, unsigned char value){	unsigned char tmp = (chip->image[reg] & mask) | value;	chip->image[reg] = tmp;	if (!chip->calibrate_mute)		snd_cs4231_dout(chip, reg, tmp);}static void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg,			   unsigned char value){	snd_cs4231_dout(chip, reg, value);	chip->image[reg] = value;	mb();}static unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg){	snd_cs4231_ready(chip);#ifdef CONFIG_SND_DEBUG	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)		snd_printdd("in: auto calibration time out - reg = 0x%x\n",			    reg);#endif	__cs4231_writeb(chip, chip->mce_bit | reg, CS4231U(chip, REGSEL));	mb();	return __cs4231_readb(chip, CS4231U(chip, REG));}/* *  CS4231 detection / MCE routines */static void snd_cs4231_busy_wait(struct snd_cs4231 *chip){	int timeout;	/* looks like this sequence is proper for CS4231A chip (GUS MAX) */	for (timeout = 5; timeout > 0; timeout--)		__cs4231_readb(chip, CS4231U(chip, REGSEL));	/* end of cleanup sequence */	for (timeout = 500; timeout > 0; timeout--) {		int val = __cs4231_readb(chip, CS4231U(chip, REGSEL));		if ((val & CS4231_INIT) == 0)			break;		msleep(1);	}}static void snd_cs4231_mce_up(struct snd_cs4231 *chip){	unsigned long flags;	int timeout;	spin_lock_irqsave(&chip->lock, flags);	snd_cs4231_ready(chip);#ifdef CONFIG_SND_DEBUG	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)		snd_printdd("mce_up - auto calibration time out (0)\n");#endif	chip->mce_bit |= CS4231_MCE;	timeout = __cs4231_readb(chip, CS4231U(chip, REGSEL));	if (timeout == 0x80)		snd_printdd("mce_up [%p]: serious init problem - "			    "codec still busy\n",			    chip->port);	if (!(timeout & CS4231_MCE))		__cs4231_writeb(chip, chip->mce_bit | (timeout & 0x1f),				CS4231U(chip, REGSEL));	spin_unlock_irqrestore(&chip->lock, flags);}static void snd_cs4231_mce_down(struct snd_cs4231 *chip){	unsigned long flags, timeout;	int reg;	snd_cs4231_busy_wait(chip);	spin_lock_irqsave(&chip->lock, flags);#ifdef CONFIG_SND_DEBUG	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)		snd_printdd("mce_down [%p] - auto calibration time out (0)\n",			    CS4231U(chip, REGSEL));#endif	chip->mce_bit &= ~CS4231_MCE;	reg = __cs4231_readb(chip, CS4231U(chip, REGSEL));	__cs4231_writeb(chip, chip->mce_bit | (reg & 0x1f),			CS4231U(chip, REGSEL));	if (reg == 0x80)		snd_printdd("mce_down [%p]: serious init problem "			    "- codec still busy\n", chip->port);	if ((reg & CS4231_MCE) == 0) {		spin_unlock_irqrestore(&chip->lock, flags);		return;	}	/*	 * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low.	 */	timeout = jiffies + msecs_to_jiffies(250);	do {		spin_unlock_irqrestore(&chip->lock, flags);		msleep(1);		spin_lock_irqsave(&chip->lock, flags);		reg = snd_cs4231_in(chip, CS4231_TEST_INIT);		reg &= CS4231_CALIB_IN_PROGRESS;	} while (reg && time_before(jiffies, timeout));	spin_unlock_irqrestore(&chip->lock, flags);	if (reg)		snd_printk(KERN_ERR			   "mce_down - auto calibration time out (2)\n");}static void snd_cs4231_advance_dma(struct cs4231_dma_control *dma_cont,				   struct snd_pcm_substream *substream,				   unsigned int *periods_sent){	struct snd_pcm_runtime *runtime = substream->runtime;	while (1) {		unsigned int period_size = snd_pcm_lib_period_bytes(substream);		unsigned int offset = period_size * (*periods_sent);		BUG_ON(period_size >= (1 << 24));		if (dma_cont->request(dma_cont,				      runtime->dma_addr + offset, period_size))			return;		(*periods_sent) = ((*periods_sent) + 1) % runtime->periods;	}}static void cs4231_dma_trigger(struct snd_pcm_substream *substream,			       unsigned int what, int on){	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);	struct cs4231_dma_control *dma_cont;	if (what & CS4231_PLAYBACK_ENABLE) {		dma_cont = &chip->p_dma;		if (on) {			dma_cont->prepare(dma_cont, 0);			dma_cont->enable(dma_cont, 1);			snd_cs4231_advance_dma(dma_cont,				chip->playback_substream,				&chip->p_periods_sent);		} else {			dma_cont->enable(dma_cont, 0);		}	}	if (what & CS4231_RECORD_ENABLE) {		dma_cont = &chip->c_dma;		if (on) {			dma_cont->prepare(dma_cont, 1);			dma_cont->enable(dma_cont, 1);			snd_cs4231_advance_dma(dma_cont,				chip->capture_substream,				&chip->c_periods_sent);		} else {			dma_cont->enable(dma_cont, 0);		}	}}static int snd_cs4231_trigger(struct snd_pcm_substream *substream, int cmd){	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);	int result = 0;	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_STOP:	{		unsigned int what = 0;		struct snd_pcm_substream *s;		unsigned long flags;		snd_pcm_group_for_each_entry(s, substream) {			if (s == chip->playback_substream) {				what |= CS4231_PLAYBACK_ENABLE;				snd_pcm_trigger_done(s, substream);			} else if (s == chip->capture_substream) {				what |= CS4231_RECORD_ENABLE;				snd_pcm_trigger_done(s, substream);			}		}		spin_lock_irqsave(&chip->lock, flags);		if (cmd == SNDRV_PCM_TRIGGER_START) {			cs4231_dma_trigger(substream, what, 1);			chip->image[CS4231_IFACE_CTRL] |= what;		} else {			cs4231_dma_trigger(substream, what, 0);			chip->image[CS4231_IFACE_CTRL] &= ~what;		}		snd_cs4231_out(chip, CS4231_IFACE_CTRL,			       chip->image[CS4231_IFACE_CTRL]);		spin_unlock_irqrestore(&chip->lock, flags);		break;	}	default:		result = -EINVAL;		break;	}	return result;}/* *  CODEC I/O */static unsigned char snd_cs4231_get_rate(unsigned int rate){

⌨️ 快捷键说明

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