ac97_pcm.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 616 行 · 第 1/2 页

C
616
字号
/* *  Copyright (c) by Jaroslav Kysela <perex@suse.cz> *  Universal interface for Audio Codec '97 * *  For more details look to AC '97 component specification revision 2.2 *  by Intel Corporation (http://developer.intel.com) and to datasheets *  for specific codecs. * * *   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/init.h>#include <linux/slab.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/control.h>#include <sound/ac97_codec.h>#include <sound/asoundef.h>#include "ac97_patch.h"#include "ac97_id.h"#include "ac97_local.h"#define chip_t ac97_t/* *  PCM support */static unsigned char rate_reg_tables[2][4][9] = {{  /* standard rates */  {  	/* 3&4 front, 7&8 rear, 6&9 center/lfe */	AC97_PCM_FRONT_DAC_RATE,	/* slot 3 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 4 */	0xff,				/* slot 5 */	AC97_PCM_LFE_DAC_RATE,		/* slot 6 */	AC97_PCM_SURR_DAC_RATE,		/* slot 7 */	AC97_PCM_SURR_DAC_RATE,		/* slot 8 */	AC97_PCM_LFE_DAC_RATE,		/* slot 9 */	0xff,				/* slot 10 */	0xff,				/* slot 11 */  },  {  	/* 7&8 front, 6&9 rear, 10&11 center/lfe */	0xff,				/* slot 3 */	0xff,				/* slot 4 */	0xff,				/* slot 5 */	AC97_PCM_SURR_DAC_RATE,		/* slot 6 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 7 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 8 */	AC97_PCM_SURR_DAC_RATE,		/* slot 9 */	AC97_PCM_LFE_DAC_RATE,		/* slot 10 */	AC97_PCM_LFE_DAC_RATE,		/* slot 11 */  },  {  	/* 6&9 front, 10&11 rear, 3&4 center/lfe */	AC97_PCM_LFE_DAC_RATE,		/* slot 3 */	AC97_PCM_LFE_DAC_RATE,		/* slot 4 */	0xff,				/* slot 5 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 6 */	0xff,				/* slot 7 */	0xff,				/* slot 8 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 9 */	AC97_PCM_SURR_DAC_RATE,		/* slot 10 */	AC97_PCM_SURR_DAC_RATE,		/* slot 11 */  },  {  	/* 10&11 front, 3&4 rear, 7&8 center/lfe */	AC97_PCM_SURR_DAC_RATE,		/* slot 3 */	AC97_PCM_SURR_DAC_RATE,		/* slot 4 */	0xff,				/* slot 5 */	0xff,				/* slot 6 */	AC97_PCM_LFE_DAC_RATE,		/* slot 7 */	AC97_PCM_LFE_DAC_RATE,		/* slot 8 */	0xff,				/* slot 9 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 10 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 11 */  },},{  /* FIXME: double rates */  {  	/* 3&4 front, 7&8 rear, 6&9 center/lfe */	AC97_PCM_FRONT_DAC_RATE,	/* slot 3 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 4 */	0xff,				/* slot 5 */	AC97_PCM_LFE_DAC_RATE,		/* slot 6 */	AC97_PCM_SURR_DAC_RATE,		/* slot 7 */	AC97_PCM_SURR_DAC_RATE,		/* slot 8 */	AC97_PCM_LFE_DAC_RATE,		/* slot 9 */	0xff,				/* slot 10 */	0xff,				/* slot 11 */  },  {  	/* 7&8 front, 6&9 rear, 10&11 center/lfe */	0xff,				/* slot 3 */	0xff,				/* slot 4 */	0xff,				/* slot 5 */	AC97_PCM_SURR_DAC_RATE,		/* slot 6 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 7 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 8 */	AC97_PCM_SURR_DAC_RATE,		/* slot 9 */	AC97_PCM_LFE_DAC_RATE,		/* slot 10 */	AC97_PCM_LFE_DAC_RATE,		/* slot 11 */  },  {  	/* 6&9 front, 10&11 rear, 3&4 center/lfe */	AC97_PCM_LFE_DAC_RATE,		/* slot 3 */	AC97_PCM_LFE_DAC_RATE,		/* slot 4 */	0xff,				/* slot 5 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 6 */	0xff,				/* slot 7 */	0xff,				/* slot 8 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 9 */	AC97_PCM_SURR_DAC_RATE,		/* slot 10 */	AC97_PCM_SURR_DAC_RATE,		/* slot 11 */  },  {  	/* 10&11 front, 3&4 rear, 7&8 center/lfe */	AC97_PCM_SURR_DAC_RATE,		/* slot 3 */	AC97_PCM_SURR_DAC_RATE,		/* slot 4 */	0xff,				/* slot 5 */	0xff,				/* slot 6 */	AC97_PCM_LFE_DAC_RATE,		/* slot 7 */	AC97_PCM_LFE_DAC_RATE,		/* slot 8 */	0xff,				/* slot 9 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 10 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 11 */  }}};/* FIXME: more various mappings for ADC? */static unsigned char rate_cregs[9] = {	AC97_PCM_LR_ADC_RATE,	/* 3 */	AC97_PCM_LR_ADC_RATE,	/* 4 */	0xff,			/* 5 */	AC97_PCM_MIC_ADC_RATE,	/* 6 */	0xff,			/* 7 */	0xff,			/* 8 */	0xff,			/* 9 */	0xff,			/* 10 */	0xff,			/* 11 */};static unsigned char get_slot_reg(struct ac97_pcm *pcm, unsigned short cidx,				  unsigned short slot, int dbl){	if (slot < 3)		return 0xff;	if (slot > 11)		return 0xff;	if (pcm->spdif)		return AC97_SPDIF; /* pseudo register */	if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK)		return rate_reg_tables[dbl][pcm->r[dbl].rate_table[cidx]][slot - 3];	else		return rate_cregs[slot - 3];}static int set_spdif_rate(ac97_t *ac97, unsigned short rate){	unsigned short old, bits, reg, mask;	unsigned int sbits;	if (! (ac97->ext_id & AC97_EI_SPDIF))		return -ENODEV;	if (ac97->flags & AC97_CS_SPDIF) {		switch (rate) {		case 48000: bits = 0; break;		case 44100: bits = 1 << AC97_SC_SPSR_SHIFT; break;		default: /* invalid - disable output */			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);			return -EINVAL;		}		reg = AC97_CSR_SPDIF;		mask = 1 << AC97_SC_SPSR_SHIFT;	} else {		if (ac97->id == AC97_ID_CM9739 && rate != 48000) {			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);			return -EINVAL;		}		switch (rate) {		case 44100: bits = AC97_SC_SPSR_44K; break;		case 48000: bits = AC97_SC_SPSR_48K; break;		case 32000: bits = AC97_SC_SPSR_32K; break;		default: /* invalid - disable output */			snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);			return -EINVAL;		}		reg = AC97_SPDIF;		mask = AC97_SC_SPSR_MASK;	}	spin_lock(&ac97->reg_lock);	old = snd_ac97_read(ac97, reg) & mask;	spin_unlock(&ac97->reg_lock);	if (old != bits) {		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);		snd_ac97_update_bits(ac97, reg, mask, bits);		/* update the internal spdif bits */		spin_lock(&ac97->reg_lock);		sbits = ac97->spdif_status;		if (sbits & IEC958_AES0_PROFESSIONAL) {			sbits &= ~IEC958_AES0_PRO_FS;			switch (rate) {			case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break;			case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break;			case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break;			}		} else {			sbits &= ~(IEC958_AES3_CON_FS << 24);			switch (rate) {			case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break;			case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break;			case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break;			}		}		ac97->spdif_status = sbits;		spin_unlock(&ac97->reg_lock);	}	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);	return 0;}/** * snd_ac97_set_rate - change the rate of the given input/output. * @ac97: the ac97 instance * @reg: the register to change * @rate: the sample rate to set * * Changes the rate of the given input/output on the codec. * If the codec doesn't support VAR, the rate must be 48000 (except * for SPDIF). * * The valid registers are AC97_PMC_MIC_ADC_RATE, * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE. * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted * if the codec supports them. * AC97_SPDIF is accepted as a pseudo register to modify the SPDIF * status bits. * * Returns zero if successful, or a negative error code on failure. */int snd_ac97_set_rate(ac97_t *ac97, int reg, unsigned short rate){	unsigned int tmp;		switch (reg) {	case AC97_PCM_MIC_ADC_RATE:		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0)	/* MIC VRA */			if (rate != 48000)				return -EINVAL;		break;	case AC97_PCM_FRONT_DAC_RATE:	case AC97_PCM_LR_ADC_RATE:		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0)	/* VRA */			if (rate != 48000)				return -EINVAL;		break;	case AC97_PCM_SURR_DAC_RATE:		if (! (ac97->scaps & AC97_SCAP_SURROUND_DAC))			return -EINVAL;		break;	case AC97_PCM_LFE_DAC_RATE:		if (! (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))			return -EINVAL;		break;	case AC97_SPDIF:		/* special case */		return set_spdif_rate(ac97, rate);	default:		return -EINVAL;	}	tmp = ((unsigned int)rate * ac97->bus->clock) / 48000;	if (tmp > 65535)		return -EINVAL;	snd_ac97_update(ac97, reg, tmp & 0xffff);	snd_ac97_read(ac97, reg);	return 0;}static unsigned short get_pslots(ac97_t *ac97, unsigned char *rate_table, unsigned short *spdif_slots){	if (!ac97_is_audio(ac97))		return 0;	if (ac97_is_rev22(ac97) || ac97_can_amap(ac97)) {		unsigned short slots = 0;		if (ac97_is_rev22(ac97)) {			/* Note: it's simply emulation of AMAP behaviour */			u16 es;

⌨️ 快捷键说明

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