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

📄 ac97_pcm.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  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"/* *  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 */  },},{  /* double rates */  {  	/* 3&4 front, 7&8 front (t+1) */	AC97_PCM_FRONT_DAC_RATE,	/* slot 3 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 4 */	0xff,				/* slot 5 */	0xff,				/* slot 6 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 7 */	AC97_PCM_FRONT_DAC_RATE,	/* slot 8 */	0xff,				/* slot 9 */	0xff,				/* slot 10 */	0xff,				/* slot 11 */  },  {	/* not specified in the specification */	0xff,				/* slot 3 */	0xff,				/* slot 4 */	0xff,				/* slot 5 */	0xff,				/* slot 6 */	0xff,				/* slot 7 */	0xff,				/* slot 8 */	0xff,				/* slot 9 */	0xff,				/* slot 10 */	0xff,				/* slot 11 */  },  {	0xff,				/* slot 3 */	0xff,				/* slot 4 */	0xff,				/* slot 5 */	0xff,				/* slot 6 */	0xff,				/* slot 7 */	0xff,				/* slot 8 */	0xff,				/* slot 9 */	0xff,				/* slot 10 */	0xff,				/* slot 11 */  },  {	0xff,				/* slot 3 */	0xff,				/* slot 4 */	0xff,				/* slot 5 */	0xff,				/* slot 6 */	0xff,				/* slot 7 */	0xff,				/* slot 8 */	0xff,				/* slot 9 */	0xff,				/* slot 10 */	0xff,				/* 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;	/* TODO: double rate support */	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;	}	down(&ac97->reg_mutex);	old = snd_ac97_read(ac97, reg) & mask;	if (old != bits) {		snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0);		snd_ac97_update_bits_nolock(ac97, reg, mask, bits);		/* update the internal spdif bits */		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;	}	snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF);	up(&ac97->reg_mutex);	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 int rate){	int dbl;	unsigned int tmp;		dbl = rate > 48000;	if (dbl) {		if (!(ac97->flags & AC97_DOUBLE_RATE))			return -EINVAL;		if (reg != AC97_PCM_FRONT_DAC_RATE)			return -EINVAL;	}	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 && rate != 96000)				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;	}	if (dbl)		rate /= 2;	tmp = (rate * ac97->bus->clock) / 48000;	if (tmp > 65535)		return -EINVAL;	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE)		snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,				     AC97_EA_DRA, dbl ? AC97_EA_DRA : 0);	snd_ac97_update(ac97, reg, tmp & 0xffff);	snd_ac97_read(ac97, reg);	if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE) {		/* Intel controllers require double rate data to be put in		 * slots 7+8		 */		snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE,				     AC97_GP_DRSS_MASK,				     dbl ? AC97_GP_DRSS_78 : 0);		snd_ac97_read(ac97, AC97_GENERAL_PURPOSE);	}	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;			es = ac97->regs[AC97_EXTENDED_ID] &= ~AC97_EI_DACS_SLOT_MASK;			switch (ac97->addr) {			case 1:			case 2: es |= (1<<AC97_EI_DACS_SLOT_SHIFT); break;			case 3: es |= (2<<AC97_EI_DACS_SLOT_SHIFT); break;			}			snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, es);		}		switch (ac97->addr) {		case 0:			slots |= (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT);			if (ac97->scaps & AC97_SCAP_SURROUND_DAC)				slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);			if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)				slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE);			if (ac97->ext_id & AC97_EI_SPDIF) {				if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC))					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT);				else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC))					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1);				else					*spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2);			}			*rate_table = 0;			break;		case 1:		case 2:			slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT);			if (ac97->scaps & AC97_SCAP_SURROUND_DAC)

⌨️ 快捷键说明

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