ad1848_lib.c

来自「linux 内核源代码」· C语言 代码 · 共 1,269 行 · 第 1/3 页

C
1,269
字号
/* *  Copyright (c) by Jaroslav Kysela <perex@perex.cz> *  Routines for control of AD1848/AD1847/CS4248 * * *   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 SNDRV_MAIN_OBJECT_FILE#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/ioport.h>#include <sound/core.h>#include <sound/ad1848.h>#include <sound/control.h>#include <sound/tlv.h>#include <sound/pcm_params.h>#include <asm/io.h>#include <asm/dma.h>MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248");MODULE_LICENSE("GPL");#if 0#define SNDRV_DEBUG_MCE#endif/* *  Some variables */static unsigned char freq_bits[14] = {	/* 5510 */	0x00 | AD1848_XTAL2,	/* 6620 */	0x0E | AD1848_XTAL2,	/* 8000 */	0x00 | AD1848_XTAL1,	/* 9600 */	0x0E | AD1848_XTAL1,	/* 11025 */	0x02 | AD1848_XTAL2,	/* 16000 */	0x02 | AD1848_XTAL1,	/* 18900 */	0x04 | AD1848_XTAL2,	/* 22050 */	0x06 | AD1848_XTAL2,	/* 27042 */	0x04 | AD1848_XTAL1,	/* 32000 */	0x06 | AD1848_XTAL1,	/* 33075 */	0x0C | AD1848_XTAL2,	/* 37800 */	0x08 | AD1848_XTAL2,	/* 44100 */	0x0A | AD1848_XTAL2,	/* 48000 */	0x0C | AD1848_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,	.mask = 0,};static unsigned char snd_ad1848_original_image[16] ={	0x00,			/* 00 - lic */	0x00,			/* 01 - ric */	0x9f,			/* 02 - la1ic */	0x9f,			/* 03 - ra1ic */	0x9f,			/* 04 - la2ic */	0x9f,			/* 05 - ra2ic */	0xbf,			/* 06 - loc */	0xbf,			/* 07 - roc */	0x20,			/* 08 - dfr */	AD1848_AUTOCALIB,	/* 09 - ic */	0x00,			/* 0a - pc */	0x00,			/* 0b - ti */	0x00,			/* 0c - mi */	0x00,			/* 0d - lbc */	0x00,			/* 0e - dru */	0x00,			/* 0f - drl */};/* *  Basic I/O functions */static void snd_ad1848_wait(struct snd_ad1848 *chip){	int timeout;	for (timeout = 250; timeout > 0; timeout--) {		if ((inb(AD1848P(chip, REGSEL)) & AD1848_INIT) == 0)			break;		udelay(100);	}}void snd_ad1848_out(struct snd_ad1848 *chip,			   unsigned char reg,			   unsigned char value){	snd_ad1848_wait(chip);#ifdef CONFIG_SND_DEBUG	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)		snd_printk(KERN_WARNING "auto calibration time out - "			   "reg = 0x%x, value = 0x%x\n", reg, value);#endif	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));	outb(chip->image[reg] = value, AD1848P(chip, REG));	mb();	snd_printdd("codec out - reg 0x%x = 0x%x\n",			chip->mce_bit | reg, value);}EXPORT_SYMBOL(snd_ad1848_out);static void snd_ad1848_dout(struct snd_ad1848 *chip,			    unsigned char reg, unsigned char value){	snd_ad1848_wait(chip);	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));	outb(value, AD1848P(chip, REG));	mb();}static unsigned char snd_ad1848_in(struct snd_ad1848 *chip, unsigned char reg){	snd_ad1848_wait(chip);#ifdef CONFIG_SND_DEBUG	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)		snd_printk(KERN_WARNING "auto calibration time out - "			   "reg = 0x%x\n", reg);#endif	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL));	mb();	return inb(AD1848P(chip, REG));}#if 0static void snd_ad1848_debug(struct snd_ad1848 *chip){	printk("AD1848 REGS:      INDEX = 0x%02x  ", inb(AD1848P(chip, REGSEL)));	printk("                 STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS)));	printk("  0x00: left input      = 0x%02x  ", snd_ad1848_in(chip, 0x00));	printk("  0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08));	printk("  0x01: right input     = 0x%02x  ", snd_ad1848_in(chip, 0x01));	printk("  0x09: iface (CFIG 1)  = 0x%02x\n", snd_ad1848_in(chip, 0x09));	printk("  0x02: AUXA left       = 0x%02x  ", snd_ad1848_in(chip, 0x02));	printk("  0x0a: pin control     = 0x%02x\n", snd_ad1848_in(chip, 0x0a));	printk("  0x03: AUXA right      = 0x%02x  ", snd_ad1848_in(chip, 0x03));	printk("  0x0b: init & status   = 0x%02x\n", snd_ad1848_in(chip, 0x0b));	printk("  0x04: AUXB left       = 0x%02x  ", snd_ad1848_in(chip, 0x04));	printk("  0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c));	printk("  0x05: AUXB right      = 0x%02x  ", snd_ad1848_in(chip, 0x05));	printk("  0x0d: loopback        = 0x%02x\n", snd_ad1848_in(chip, 0x0d));	printk("  0x06: left output     = 0x%02x  ", snd_ad1848_in(chip, 0x06));	printk("  0x0e: data upr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0e));	printk("  0x07: right output    = 0x%02x  ", snd_ad1848_in(chip, 0x07));	printk("  0x0f: data lwr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0f));}#endif/* *  AD1848 detection / MCE routines */static void snd_ad1848_mce_up(struct snd_ad1848 *chip){	unsigned long flags;	int timeout;	snd_ad1848_wait(chip);#ifdef CONFIG_SND_DEBUG	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)		snd_printk(KERN_WARNING "mce_up - auto calibration time out (0)\n");#endif	spin_lock_irqsave(&chip->reg_lock, flags);	chip->mce_bit |= AD1848_MCE;	timeout = inb(AD1848P(chip, REGSEL));	if (timeout == 0x80)		snd_printk(KERN_WARNING "mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port);	if (!(timeout & AD1848_MCE))		outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL));	spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_ad1848_mce_down(struct snd_ad1848 *chip){	unsigned long flags, timeout;	int reg;	spin_lock_irqsave(&chip->reg_lock, flags);	for (timeout = 5; timeout > 0; timeout--)		inb(AD1848P(chip, REGSEL));	/* end of cleanup sequence */	for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--)		udelay(100);	snd_printdd("(1) timeout = %d\n", timeout);#ifdef CONFIG_SND_DEBUG	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT)		snd_printk(KERN_WARNING "mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL));#endif	chip->mce_bit &= ~AD1848_MCE;	reg = inb(AD1848P(chip, REGSEL));	outb(chip->mce_bit | (reg & 0x1f), AD1848P(chip, REGSEL));	if (reg == 0x80)		snd_printk(KERN_WARNING "mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port);	if ((reg & AD1848_MCE) == 0) {		spin_unlock_irqrestore(&chip->reg_lock, flags);		return;	}	/*	 * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low.	 * It may take up to 5 sample periods (at most 907 us @ 5.5125 kHz) for	 * the process to _start_, so it is important to wait at least that long	 * before checking.  Otherwise we might think AC has finished when it	 * has in fact not begun.  It could take 128 (no AC) or 384 (AC) cycles	 * for ACI to drop.  This gives a wait of at most 70 ms with a more	 * typical value of 3-9 ms.	 */	timeout = jiffies + msecs_to_jiffies(250);	do {		spin_unlock_irqrestore(&chip->reg_lock, flags);		msleep(1);		spin_lock_irqsave(&chip->reg_lock, flags);		reg = snd_ad1848_in(chip, AD1848_TEST_INIT) &		      AD1848_CALIB_IN_PROGRESS;	} while (reg && time_before(jiffies, timeout));	spin_unlock_irqrestore(&chip->reg_lock, flags);	if (reg)		snd_printk(KERN_ERR			   "mce_down - auto calibration time out (2)\n");	snd_printdd("(4) jiffies = %lu\n", jiffies);	snd_printd("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL)));}static unsigned int snd_ad1848_get_count(unsigned char format,				         unsigned int size){	switch (format & 0xe0) {	case AD1848_LINEAR_16:		size >>= 1;		break;	}	if (format & AD1848_STEREO)		size >>= 1;	return size;}static int snd_ad1848_trigger(struct snd_ad1848 *chip, unsigned char what,			      int channel, int cmd){	int result = 0;#if 0	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS)));#endif	spin_lock(&chip->reg_lock);	if (cmd == SNDRV_PCM_TRIGGER_START) {		if (chip->image[AD1848_IFACE_CTRL] & what) {			spin_unlock(&chip->reg_lock);			return 0;		}		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what);		chip->mode |= AD1848_MODE_RUNNING;	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {		if (!(chip->image[AD1848_IFACE_CTRL] & what)) {			spin_unlock(&chip->reg_lock);			return 0;		}		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what);		chip->mode &= ~AD1848_MODE_RUNNING;	} else {		result = -EINVAL;	}	spin_unlock(&chip->reg_lock);	return result;}/* *  CODEC I/O */static unsigned char snd_ad1848_get_rate(unsigned int rate){	int i;	for (i = 0; i < ARRAY_SIZE(rates); i++)		if (rate == rates[i])			return freq_bits[i];	snd_BUG();	return freq_bits[ARRAY_SIZE(rates) - 1];}static int snd_ad1848_ioctl(struct snd_pcm_substream *substream,			    unsigned int cmd, void *arg){	return snd_pcm_lib_ioctl(substream, cmd, arg);}static unsigned char snd_ad1848_get_format(int format, int channels){	unsigned char rformat;	rformat = AD1848_LINEAR_8;	switch (format) {	case SNDRV_PCM_FORMAT_A_LAW:	rformat = AD1848_ALAW_8; break;	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = AD1848_ULAW_8; break;	case SNDRV_PCM_FORMAT_S16_LE:	rformat = AD1848_LINEAR_16; break;	}	if (channels > 1)		rformat |= AD1848_STEREO;#if 0	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode);#endif	return rformat;}static void snd_ad1848_calibrate_mute(struct snd_ad1848 *chip, int mute){	unsigned long flags;		mute = mute ? 1 : 0;	spin_lock_irqsave(&chip->reg_lock, flags);	if (chip->calibrate_mute == mute) {		spin_unlock_irqrestore(&chip->reg_lock, flags);		return;	}	if (!mute) {		snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]);		snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]);	}	snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]);	snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]);	snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]);	snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]);	snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]);	snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]);	chip->calibrate_mute = mute;	spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_ad1848_set_data_format(struct snd_ad1848 *chip, struct snd_pcm_hw_params *hw_params){	if (hw_params == NULL) {		chip->image[AD1848_DATA_FORMAT] = 0x20;	} else {		chip->image[AD1848_DATA_FORMAT] =		    snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) |		    snd_ad1848_get_rate(params_rate(hw_params));	}	// snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]);}static int snd_ad1848_open(struct snd_ad1848 *chip, unsigned int mode){	unsigned long flags;	if (chip->mode & AD1848_MODE_OPEN)		return -EAGAIN;	snd_ad1848_mce_down(chip);#ifdef SNDRV_DEBUG_MCE	snd_printk("open: (1)\n");#endif	snd_ad1848_mce_up(chip);	spin_lock_irqsave(&chip->reg_lock, flags);	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO |			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO |			     AD1848_CALIB_MODE);	chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB;	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]);	spin_unlock_irqrestore(&chip->reg_lock, flags);	snd_ad1848_mce_down(chip);#ifdef SNDRV_DEBUG_MCE	snd_printk("open: (2)\n");#endif	snd_ad1848_set_data_format(chip, NULL);	snd_ad1848_mce_up(chip);	spin_lock_irqsave(&chip->reg_lock, flags);	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]);	spin_unlock_irqrestore(&chip->reg_lock, flags);	snd_ad1848_mce_down(chip);#ifdef SNDRV_DEBUG_MCE	snd_printk("open: (3)\n");#endif	/* ok. now enable and ack CODEC IRQ */	spin_lock_irqsave(&chip->reg_lock, flags);	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */	chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE;	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]);	spin_unlock_irqrestore(&chip->reg_lock, flags);	chip->mode = mode;

⌨️ 快捷键说明

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