📄 ac97_pcm.c
字号:
/* * 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 + -