162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Universal interface for Audio Codec '97 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * For more details look to AC '97 component specification revision 2.2 762306a36Sopenharmony_ci * by Intel Corporation (http://developer.intel.com) and to datasheets 862306a36Sopenharmony_ci * for specific codecs. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/export.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/pcm.h> 1962306a36Sopenharmony_ci#include <sound/control.h> 2062306a36Sopenharmony_ci#include <sound/ac97_codec.h> 2162306a36Sopenharmony_ci#include <sound/asoundef.h> 2262306a36Sopenharmony_ci#include "ac97_id.h" 2362306a36Sopenharmony_ci#include "ac97_local.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * PCM support 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic const unsigned char rate_reg_tables[2][4][9] = { 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci /* standard rates */ 3262306a36Sopenharmony_ci { 3362306a36Sopenharmony_ci /* 3&4 front, 7&8 rear, 6&9 center/lfe */ 3462306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 3 */ 3562306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 4 */ 3662306a36Sopenharmony_ci 0xff, /* slot 5 */ 3762306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 6 */ 3862306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 7 */ 3962306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 8 */ 4062306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 9 */ 4162306a36Sopenharmony_ci 0xff, /* slot 10 */ 4262306a36Sopenharmony_ci 0xff, /* slot 11 */ 4362306a36Sopenharmony_ci }, 4462306a36Sopenharmony_ci { 4562306a36Sopenharmony_ci /* 7&8 front, 6&9 rear, 10&11 center/lfe */ 4662306a36Sopenharmony_ci 0xff, /* slot 3 */ 4762306a36Sopenharmony_ci 0xff, /* slot 4 */ 4862306a36Sopenharmony_ci 0xff, /* slot 5 */ 4962306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 6 */ 5062306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 7 */ 5162306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 8 */ 5262306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 9 */ 5362306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 10 */ 5462306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 11 */ 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci { 5762306a36Sopenharmony_ci /* 6&9 front, 10&11 rear, 3&4 center/lfe */ 5862306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 3 */ 5962306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 4 */ 6062306a36Sopenharmony_ci 0xff, /* slot 5 */ 6162306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 6 */ 6262306a36Sopenharmony_ci 0xff, /* slot 7 */ 6362306a36Sopenharmony_ci 0xff, /* slot 8 */ 6462306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 9 */ 6562306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 10 */ 6662306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 11 */ 6762306a36Sopenharmony_ci }, 6862306a36Sopenharmony_ci { 6962306a36Sopenharmony_ci /* 10&11 front, 3&4 rear, 7&8 center/lfe */ 7062306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 3 */ 7162306a36Sopenharmony_ci AC97_PCM_SURR_DAC_RATE, /* slot 4 */ 7262306a36Sopenharmony_ci 0xff, /* slot 5 */ 7362306a36Sopenharmony_ci 0xff, /* slot 6 */ 7462306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 7 */ 7562306a36Sopenharmony_ci AC97_PCM_LFE_DAC_RATE, /* slot 8 */ 7662306a36Sopenharmony_ci 0xff, /* slot 9 */ 7762306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 10 */ 7862306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 11 */ 7962306a36Sopenharmony_ci }, 8062306a36Sopenharmony_ci}, 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci /* double rates */ 8362306a36Sopenharmony_ci { 8462306a36Sopenharmony_ci /* 3&4 front, 7&8 front (t+1) */ 8562306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 3 */ 8662306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 4 */ 8762306a36Sopenharmony_ci 0xff, /* slot 5 */ 8862306a36Sopenharmony_ci 0xff, /* slot 6 */ 8962306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 7 */ 9062306a36Sopenharmony_ci AC97_PCM_FRONT_DAC_RATE, /* slot 8 */ 9162306a36Sopenharmony_ci 0xff, /* slot 9 */ 9262306a36Sopenharmony_ci 0xff, /* slot 10 */ 9362306a36Sopenharmony_ci 0xff, /* slot 11 */ 9462306a36Sopenharmony_ci }, 9562306a36Sopenharmony_ci { 9662306a36Sopenharmony_ci /* not specified in the specification */ 9762306a36Sopenharmony_ci 0xff, /* slot 3 */ 9862306a36Sopenharmony_ci 0xff, /* slot 4 */ 9962306a36Sopenharmony_ci 0xff, /* slot 5 */ 10062306a36Sopenharmony_ci 0xff, /* slot 6 */ 10162306a36Sopenharmony_ci 0xff, /* slot 7 */ 10262306a36Sopenharmony_ci 0xff, /* slot 8 */ 10362306a36Sopenharmony_ci 0xff, /* slot 9 */ 10462306a36Sopenharmony_ci 0xff, /* slot 10 */ 10562306a36Sopenharmony_ci 0xff, /* slot 11 */ 10662306a36Sopenharmony_ci }, 10762306a36Sopenharmony_ci { 10862306a36Sopenharmony_ci 0xff, /* slot 3 */ 10962306a36Sopenharmony_ci 0xff, /* slot 4 */ 11062306a36Sopenharmony_ci 0xff, /* slot 5 */ 11162306a36Sopenharmony_ci 0xff, /* slot 6 */ 11262306a36Sopenharmony_ci 0xff, /* slot 7 */ 11362306a36Sopenharmony_ci 0xff, /* slot 8 */ 11462306a36Sopenharmony_ci 0xff, /* slot 9 */ 11562306a36Sopenharmony_ci 0xff, /* slot 10 */ 11662306a36Sopenharmony_ci 0xff, /* slot 11 */ 11762306a36Sopenharmony_ci }, 11862306a36Sopenharmony_ci { 11962306a36Sopenharmony_ci 0xff, /* slot 3 */ 12062306a36Sopenharmony_ci 0xff, /* slot 4 */ 12162306a36Sopenharmony_ci 0xff, /* slot 5 */ 12262306a36Sopenharmony_ci 0xff, /* slot 6 */ 12362306a36Sopenharmony_ci 0xff, /* slot 7 */ 12462306a36Sopenharmony_ci 0xff, /* slot 8 */ 12562306a36Sopenharmony_ci 0xff, /* slot 9 */ 12662306a36Sopenharmony_ci 0xff, /* slot 10 */ 12762306a36Sopenharmony_ci 0xff, /* slot 11 */ 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci}}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* FIXME: more various mappings for ADC? */ 13262306a36Sopenharmony_cistatic const unsigned char rate_cregs[9] = { 13362306a36Sopenharmony_ci AC97_PCM_LR_ADC_RATE, /* 3 */ 13462306a36Sopenharmony_ci AC97_PCM_LR_ADC_RATE, /* 4 */ 13562306a36Sopenharmony_ci 0xff, /* 5 */ 13662306a36Sopenharmony_ci AC97_PCM_MIC_ADC_RATE, /* 6 */ 13762306a36Sopenharmony_ci 0xff, /* 7 */ 13862306a36Sopenharmony_ci 0xff, /* 8 */ 13962306a36Sopenharmony_ci 0xff, /* 9 */ 14062306a36Sopenharmony_ci 0xff, /* 10 */ 14162306a36Sopenharmony_ci 0xff, /* 11 */ 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic unsigned char get_slot_reg(struct ac97_pcm *pcm, unsigned short cidx, 14562306a36Sopenharmony_ci unsigned short slot, int dbl) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (slot < 3) 14862306a36Sopenharmony_ci return 0xff; 14962306a36Sopenharmony_ci if (slot > 11) 15062306a36Sopenharmony_ci return 0xff; 15162306a36Sopenharmony_ci if (pcm->spdif) 15262306a36Sopenharmony_ci return AC97_SPDIF; /* pseudo register */ 15362306a36Sopenharmony_ci if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) 15462306a36Sopenharmony_ci return rate_reg_tables[dbl][pcm->r[dbl].rate_table[cidx]][slot - 3]; 15562306a36Sopenharmony_ci else 15662306a36Sopenharmony_ci return rate_cregs[slot - 3]; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int set_spdif_rate(struct snd_ac97 *ac97, unsigned short rate) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci unsigned short old, bits, reg, mask; 16262306a36Sopenharmony_ci unsigned int sbits; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (! (ac97->ext_id & AC97_EI_SPDIF)) 16562306a36Sopenharmony_ci return -ENODEV; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* TODO: double rate support */ 16862306a36Sopenharmony_ci if (ac97->flags & AC97_CS_SPDIF) { 16962306a36Sopenharmony_ci switch (rate) { 17062306a36Sopenharmony_ci case 48000: bits = 0; break; 17162306a36Sopenharmony_ci case 44100: bits = 1 << AC97_SC_SPSR_SHIFT; break; 17262306a36Sopenharmony_ci default: /* invalid - disable output */ 17362306a36Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); 17462306a36Sopenharmony_ci return -EINVAL; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci reg = AC97_CSR_SPDIF; 17762306a36Sopenharmony_ci mask = 1 << AC97_SC_SPSR_SHIFT; 17862306a36Sopenharmony_ci } else { 17962306a36Sopenharmony_ci if (ac97->id == AC97_ID_CM9739 && rate != 48000) { 18062306a36Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci switch (rate) { 18462306a36Sopenharmony_ci case 44100: bits = AC97_SC_SPSR_44K; break; 18562306a36Sopenharmony_ci case 48000: bits = AC97_SC_SPSR_48K; break; 18662306a36Sopenharmony_ci case 32000: bits = AC97_SC_SPSR_32K; break; 18762306a36Sopenharmony_ci default: /* invalid - disable output */ 18862306a36Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci reg = AC97_SPDIF; 19262306a36Sopenharmony_ci mask = AC97_SC_SPSR_MASK; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci mutex_lock(&ac97->reg_mutex); 19662306a36Sopenharmony_ci old = snd_ac97_read(ac97, reg) & mask; 19762306a36Sopenharmony_ci if (old != bits) { 19862306a36Sopenharmony_ci snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); 19962306a36Sopenharmony_ci snd_ac97_update_bits_nolock(ac97, reg, mask, bits); 20062306a36Sopenharmony_ci /* update the internal spdif bits */ 20162306a36Sopenharmony_ci sbits = ac97->spdif_status; 20262306a36Sopenharmony_ci if (sbits & IEC958_AES0_PROFESSIONAL) { 20362306a36Sopenharmony_ci sbits &= ~IEC958_AES0_PRO_FS; 20462306a36Sopenharmony_ci switch (rate) { 20562306a36Sopenharmony_ci case 44100: sbits |= IEC958_AES0_PRO_FS_44100; break; 20662306a36Sopenharmony_ci case 48000: sbits |= IEC958_AES0_PRO_FS_48000; break; 20762306a36Sopenharmony_ci case 32000: sbits |= IEC958_AES0_PRO_FS_32000; break; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } else { 21062306a36Sopenharmony_ci sbits &= ~(IEC958_AES3_CON_FS << 24); 21162306a36Sopenharmony_ci switch (rate) { 21262306a36Sopenharmony_ci case 44100: sbits |= IEC958_AES3_CON_FS_44100<<24; break; 21362306a36Sopenharmony_ci case 48000: sbits |= IEC958_AES3_CON_FS_48000<<24; break; 21462306a36Sopenharmony_ci case 32000: sbits |= IEC958_AES3_CON_FS_32000<<24; break; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci ac97->spdif_status = sbits; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci snd_ac97_update_bits_nolock(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, AC97_EA_SPDIF); 22062306a36Sopenharmony_ci mutex_unlock(&ac97->reg_mutex); 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/** 22562306a36Sopenharmony_ci * snd_ac97_set_rate - change the rate of the given input/output. 22662306a36Sopenharmony_ci * @ac97: the ac97 instance 22762306a36Sopenharmony_ci * @reg: the register to change 22862306a36Sopenharmony_ci * @rate: the sample rate to set 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * Changes the rate of the given input/output on the codec. 23162306a36Sopenharmony_ci * If the codec doesn't support VAR, the rate must be 48000 (except 23262306a36Sopenharmony_ci * for SPDIF). 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * The valid registers are AC97_PCM_MIC_ADC_RATE, 23562306a36Sopenharmony_ci * AC97_PCM_FRONT_DAC_RATE, AC97_PCM_LR_ADC_RATE. 23662306a36Sopenharmony_ci * AC97_PCM_SURR_DAC_RATE and AC97_PCM_LFE_DAC_RATE are accepted 23762306a36Sopenharmony_ci * if the codec supports them. 23862306a36Sopenharmony_ci * AC97_SPDIF is accepted as a pseudo register to modify the SPDIF 23962306a36Sopenharmony_ci * status bits. 24062306a36Sopenharmony_ci * 24162306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_ciint snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int dbl; 24662306a36Sopenharmony_ci unsigned int tmp; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci dbl = rate > 48000; 24962306a36Sopenharmony_ci if (dbl) { 25062306a36Sopenharmony_ci if (!(ac97->flags & AC97_DOUBLE_RATE)) 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci if (reg != AC97_PCM_FRONT_DAC_RATE) 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci snd_ac97_update_power(ac97, reg, 1); 25762306a36Sopenharmony_ci switch (reg) { 25862306a36Sopenharmony_ci case AC97_PCM_MIC_ADC_RATE: 25962306a36Sopenharmony_ci if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */ 26062306a36Sopenharmony_ci if (rate != 48000) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci case AC97_PCM_FRONT_DAC_RATE: 26462306a36Sopenharmony_ci case AC97_PCM_LR_ADC_RATE: 26562306a36Sopenharmony_ci if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0) /* VRA */ 26662306a36Sopenharmony_ci if (rate != 48000 && rate != 96000) 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci case AC97_PCM_SURR_DAC_RATE: 27062306a36Sopenharmony_ci if (! (ac97->scaps & AC97_SCAP_SURROUND_DAC)) 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case AC97_PCM_LFE_DAC_RATE: 27462306a36Sopenharmony_ci if (! (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)) 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci case AC97_SPDIF: 27862306a36Sopenharmony_ci /* special case */ 27962306a36Sopenharmony_ci return set_spdif_rate(ac97, rate); 28062306a36Sopenharmony_ci default: 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci if (dbl) 28462306a36Sopenharmony_ci rate /= 2; 28562306a36Sopenharmony_ci tmp = (rate * ac97->bus->clock) / 48000; 28662306a36Sopenharmony_ci if (tmp > 65535) 28762306a36Sopenharmony_ci return -EINVAL; 28862306a36Sopenharmony_ci if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE) 28962306a36Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 29062306a36Sopenharmony_ci AC97_EA_DRA, dbl ? AC97_EA_DRA : 0); 29162306a36Sopenharmony_ci snd_ac97_update(ac97, reg, tmp & 0xffff); 29262306a36Sopenharmony_ci snd_ac97_read(ac97, reg); 29362306a36Sopenharmony_ci if ((ac97->ext_id & AC97_EI_DRA) && reg == AC97_PCM_FRONT_DAC_RATE) { 29462306a36Sopenharmony_ci /* Intel controllers require double rate data to be put in 29562306a36Sopenharmony_ci * slots 7+8 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci snd_ac97_update_bits(ac97, AC97_GENERAL_PURPOSE, 29862306a36Sopenharmony_ci AC97_GP_DRSS_MASK, 29962306a36Sopenharmony_ci dbl ? AC97_GP_DRSS_78 : 0); 30062306a36Sopenharmony_ci snd_ac97_read(ac97, AC97_GENERAL_PURPOSE); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_set_rate); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic unsigned short get_pslots(struct snd_ac97 *ac97, unsigned char *rate_table, unsigned short *spdif_slots) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci if (!ac97_is_audio(ac97)) 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci if (ac97_is_rev22(ac97) || ac97_can_amap(ac97)) { 31262306a36Sopenharmony_ci unsigned short slots = 0; 31362306a36Sopenharmony_ci if (ac97_is_rev22(ac97)) { 31462306a36Sopenharmony_ci /* Note: it's simply emulation of AMAP behaviour */ 31562306a36Sopenharmony_ci u16 es; 31662306a36Sopenharmony_ci es = ac97->regs[AC97_EXTENDED_ID] &= ~AC97_EI_DACS_SLOT_MASK; 31762306a36Sopenharmony_ci switch (ac97->addr) { 31862306a36Sopenharmony_ci case 1: 31962306a36Sopenharmony_ci case 2: es |= (1<<AC97_EI_DACS_SLOT_SHIFT); break; 32062306a36Sopenharmony_ci case 3: es |= (2<<AC97_EI_DACS_SLOT_SHIFT); break; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, es); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci switch (ac97->addr) { 32562306a36Sopenharmony_ci case 0: 32662306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT); 32762306a36Sopenharmony_ci if (ac97->scaps & AC97_SCAP_SURROUND_DAC) 32862306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT); 32962306a36Sopenharmony_ci if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) 33062306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE); 33162306a36Sopenharmony_ci if (ac97->ext_id & AC97_EI_SPDIF) { 33262306a36Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC)) 33362306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT); 33462306a36Sopenharmony_ci else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)) 33562306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1); 33662306a36Sopenharmony_ci else 33762306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci *rate_table = 0; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case 1: 34262306a36Sopenharmony_ci case 2: 34362306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT); 34462306a36Sopenharmony_ci if (ac97->scaps & AC97_SCAP_SURROUND_DAC) 34562306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE); 34662306a36Sopenharmony_ci if (ac97->ext_id & AC97_EI_SPDIF) { 34762306a36Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC)) 34862306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1); 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci *rate_table = 1; 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci case 3: 35562306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE); 35662306a36Sopenharmony_ci if (ac97->ext_id & AC97_EI_SPDIF) 35762306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2); 35862306a36Sopenharmony_ci *rate_table = 2; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci return slots; 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci unsigned short slots; 36462306a36Sopenharmony_ci slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT); 36562306a36Sopenharmony_ci if (ac97->scaps & AC97_SCAP_SURROUND_DAC) 36662306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_SLEFT)|(1<<AC97_SLOT_PCM_SRIGHT); 36762306a36Sopenharmony_ci if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) 36862306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_PCM_CENTER)|(1<<AC97_SLOT_LFE); 36962306a36Sopenharmony_ci if (ac97->ext_id & AC97_EI_SPDIF) { 37062306a36Sopenharmony_ci if (!(ac97->scaps & AC97_SCAP_SURROUND_DAC)) 37162306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT)|(1<<AC97_SLOT_SPDIF_RIGHT); 37262306a36Sopenharmony_ci else if (!(ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)) 37362306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT1)|(1<<AC97_SLOT_SPDIF_RIGHT1); 37462306a36Sopenharmony_ci else 37562306a36Sopenharmony_ci *spdif_slots = (1<<AC97_SLOT_SPDIF_LEFT2)|(1<<AC97_SLOT_SPDIF_RIGHT2); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci *rate_table = 0; 37862306a36Sopenharmony_ci return slots; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic unsigned short get_cslots(struct snd_ac97 *ac97) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci unsigned short slots; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!ac97_is_audio(ac97)) 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci slots = (1<<AC97_SLOT_PCM_LEFT)|(1<<AC97_SLOT_PCM_RIGHT); 38962306a36Sopenharmony_ci slots |= (1<<AC97_SLOT_MIC); 39062306a36Sopenharmony_ci return slots; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic unsigned int get_rates(struct ac97_pcm *pcm, unsigned int cidx, unsigned short slots, int dbl) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci int i, idx; 39662306a36Sopenharmony_ci unsigned int rates = ~0; 39762306a36Sopenharmony_ci unsigned char reg; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci for (i = 3; i < 12; i++) { 40062306a36Sopenharmony_ci if (!(slots & (1 << i))) 40162306a36Sopenharmony_ci continue; 40262306a36Sopenharmony_ci reg = get_slot_reg(pcm, cidx, i, dbl); 40362306a36Sopenharmony_ci switch (reg) { 40462306a36Sopenharmony_ci case AC97_PCM_FRONT_DAC_RATE: idx = AC97_RATES_FRONT_DAC; break; 40562306a36Sopenharmony_ci case AC97_PCM_SURR_DAC_RATE: idx = AC97_RATES_SURR_DAC; break; 40662306a36Sopenharmony_ci case AC97_PCM_LFE_DAC_RATE: idx = AC97_RATES_LFE_DAC; break; 40762306a36Sopenharmony_ci case AC97_PCM_LR_ADC_RATE: idx = AC97_RATES_ADC; break; 40862306a36Sopenharmony_ci case AC97_PCM_MIC_ADC_RATE: idx = AC97_RATES_MIC_ADC; break; 40962306a36Sopenharmony_ci default: idx = AC97_RATES_SPDIF; break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci rates &= pcm->r[dbl].codec[cidx]->rates[idx]; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci if (!dbl) 41462306a36Sopenharmony_ci rates &= ~(SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | 41562306a36Sopenharmony_ci SNDRV_PCM_RATE_96000); 41662306a36Sopenharmony_ci return rates; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * snd_ac97_pcm_assign - assign AC97 slots to given PCM streams 42162306a36Sopenharmony_ci * @bus: the ac97 bus instance 42262306a36Sopenharmony_ci * @pcms_count: count of PCMs to be assigned 42362306a36Sopenharmony_ci * @pcms: PCMs to be assigned 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * It assigns available AC97 slots for given PCMs. If none or only 42662306a36Sopenharmony_ci * some slots are available, pcm->xxx.slots and pcm->xxx.rslots[] members 42762306a36Sopenharmony_ci * are reduced and might be zero. 42862306a36Sopenharmony_ci * 42962306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ciint snd_ac97_pcm_assign(struct snd_ac97_bus *bus, 43262306a36Sopenharmony_ci unsigned short pcms_count, 43362306a36Sopenharmony_ci const struct ac97_pcm *pcms) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci int i, j, k; 43662306a36Sopenharmony_ci const struct ac97_pcm *pcm; 43762306a36Sopenharmony_ci struct ac97_pcm *rpcms, *rpcm; 43862306a36Sopenharmony_ci unsigned short avail_slots[2][4]; 43962306a36Sopenharmony_ci unsigned char rate_table[2][4]; 44062306a36Sopenharmony_ci unsigned short tmp, slots; 44162306a36Sopenharmony_ci unsigned short spdif_slots[4]; 44262306a36Sopenharmony_ci unsigned int rates; 44362306a36Sopenharmony_ci struct snd_ac97 *codec; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci rpcms = kcalloc(pcms_count, sizeof(struct ac97_pcm), GFP_KERNEL); 44662306a36Sopenharmony_ci if (rpcms == NULL) 44762306a36Sopenharmony_ci return -ENOMEM; 44862306a36Sopenharmony_ci memset(avail_slots, 0, sizeof(avail_slots)); 44962306a36Sopenharmony_ci memset(rate_table, 0, sizeof(rate_table)); 45062306a36Sopenharmony_ci memset(spdif_slots, 0, sizeof(spdif_slots)); 45162306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 45262306a36Sopenharmony_ci codec = bus->codec[i]; 45362306a36Sopenharmony_ci if (!codec) 45462306a36Sopenharmony_ci continue; 45562306a36Sopenharmony_ci avail_slots[0][i] = get_pslots(codec, &rate_table[0][i], &spdif_slots[i]); 45662306a36Sopenharmony_ci avail_slots[1][i] = get_cslots(codec); 45762306a36Sopenharmony_ci if (!(codec->scaps & AC97_SCAP_INDEP_SDIN)) { 45862306a36Sopenharmony_ci for (j = 0; j < i; j++) { 45962306a36Sopenharmony_ci if (bus->codec[j]) 46062306a36Sopenharmony_ci avail_slots[1][i] &= ~avail_slots[1][j]; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci /* first step - exclusive devices */ 46562306a36Sopenharmony_ci for (i = 0; i < pcms_count; i++) { 46662306a36Sopenharmony_ci pcm = &pcms[i]; 46762306a36Sopenharmony_ci rpcm = &rpcms[i]; 46862306a36Sopenharmony_ci /* low-level driver thinks that it's more clever */ 46962306a36Sopenharmony_ci if (pcm->copy_flag) { 47062306a36Sopenharmony_ci *rpcm = *pcm; 47162306a36Sopenharmony_ci continue; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci rpcm->stream = pcm->stream; 47462306a36Sopenharmony_ci rpcm->exclusive = pcm->exclusive; 47562306a36Sopenharmony_ci rpcm->spdif = pcm->spdif; 47662306a36Sopenharmony_ci rpcm->private_value = pcm->private_value; 47762306a36Sopenharmony_ci rpcm->bus = bus; 47862306a36Sopenharmony_ci rpcm->rates = ~0; 47962306a36Sopenharmony_ci slots = pcm->r[0].slots; 48062306a36Sopenharmony_ci for (j = 0; j < 4 && slots; j++) { 48162306a36Sopenharmony_ci if (!bus->codec[j]) 48262306a36Sopenharmony_ci continue; 48362306a36Sopenharmony_ci rates = ~0; 48462306a36Sopenharmony_ci if (pcm->spdif && pcm->stream == 0) 48562306a36Sopenharmony_ci tmp = spdif_slots[j]; 48662306a36Sopenharmony_ci else 48762306a36Sopenharmony_ci tmp = avail_slots[pcm->stream][j]; 48862306a36Sopenharmony_ci if (pcm->exclusive) { 48962306a36Sopenharmony_ci /* exclusive access */ 49062306a36Sopenharmony_ci tmp &= slots; 49162306a36Sopenharmony_ci for (k = 0; k < i; k++) { 49262306a36Sopenharmony_ci if (rpcm->stream == rpcms[k].stream) 49362306a36Sopenharmony_ci tmp &= ~rpcms[k].r[0].rslots[j]; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci } else { 49662306a36Sopenharmony_ci /* non-exclusive access */ 49762306a36Sopenharmony_ci tmp &= pcm->r[0].slots; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci if (tmp) { 50062306a36Sopenharmony_ci rpcm->r[0].rslots[j] = tmp; 50162306a36Sopenharmony_ci rpcm->r[0].codec[j] = bus->codec[j]; 50262306a36Sopenharmony_ci rpcm->r[0].rate_table[j] = rate_table[pcm->stream][j]; 50362306a36Sopenharmony_ci if (bus->no_vra) 50462306a36Sopenharmony_ci rates = SNDRV_PCM_RATE_48000; 50562306a36Sopenharmony_ci else 50662306a36Sopenharmony_ci rates = get_rates(rpcm, j, tmp, 0); 50762306a36Sopenharmony_ci if (pcm->exclusive) 50862306a36Sopenharmony_ci avail_slots[pcm->stream][j] &= ~tmp; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci slots &= ~tmp; 51162306a36Sopenharmony_ci rpcm->r[0].slots |= tmp; 51262306a36Sopenharmony_ci rpcm->rates &= rates; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci /* for double rate, we check the first codec only */ 51562306a36Sopenharmony_ci if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK && 51662306a36Sopenharmony_ci bus->codec[0] && (bus->codec[0]->flags & AC97_DOUBLE_RATE) && 51762306a36Sopenharmony_ci rate_table[pcm->stream][0] == 0) { 51862306a36Sopenharmony_ci tmp = (1<<AC97_SLOT_PCM_LEFT) | (1<<AC97_SLOT_PCM_RIGHT) | 51962306a36Sopenharmony_ci (1<<AC97_SLOT_PCM_LEFT_0) | (1<<AC97_SLOT_PCM_RIGHT_0); 52062306a36Sopenharmony_ci if ((tmp & pcm->r[1].slots) == tmp) { 52162306a36Sopenharmony_ci rpcm->r[1].slots = tmp; 52262306a36Sopenharmony_ci rpcm->r[1].rslots[0] = tmp; 52362306a36Sopenharmony_ci rpcm->r[1].rate_table[0] = 0; 52462306a36Sopenharmony_ci rpcm->r[1].codec[0] = bus->codec[0]; 52562306a36Sopenharmony_ci if (pcm->exclusive) 52662306a36Sopenharmony_ci avail_slots[pcm->stream][0] &= ~tmp; 52762306a36Sopenharmony_ci if (bus->no_vra) 52862306a36Sopenharmony_ci rates = SNDRV_PCM_RATE_96000; 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci rates = get_rates(rpcm, 0, tmp, 1); 53162306a36Sopenharmony_ci rpcm->rates |= rates; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci if (rpcm->rates == ~0) 53562306a36Sopenharmony_ci rpcm->rates = 0; /* not used */ 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci bus->pcms_count = pcms_count; 53862306a36Sopenharmony_ci bus->pcms = rpcms; 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_assign); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/** 54562306a36Sopenharmony_ci * snd_ac97_pcm_open - opens the given AC97 pcm 54662306a36Sopenharmony_ci * @pcm: the ac97 pcm instance 54762306a36Sopenharmony_ci * @rate: rate in Hz, if codec does not support VRA, this value must be 48000Hz 54862306a36Sopenharmony_ci * @cfg: output stream characteristics 54962306a36Sopenharmony_ci * @slots: a subset of allocated slots (snd_ac97_pcm_assign) for this pcm 55062306a36Sopenharmony_ci * 55162306a36Sopenharmony_ci * It locks the specified slots and sets the given rate to AC97 registers. 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ciint snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, 55662306a36Sopenharmony_ci enum ac97_pcm_cfg cfg, unsigned short slots) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct snd_ac97_bus *bus; 55962306a36Sopenharmony_ci int i, cidx, r, ok_flag; 56062306a36Sopenharmony_ci unsigned int reg_ok[4] = {0,0,0,0}; 56162306a36Sopenharmony_ci unsigned char reg; 56262306a36Sopenharmony_ci int err = 0; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci r = rate > 48000; 56562306a36Sopenharmony_ci bus = pcm->bus; 56662306a36Sopenharmony_ci if (cfg == AC97_PCM_CFG_SPDIF) { 56762306a36Sopenharmony_ci for (cidx = 0; cidx < 4; cidx++) 56862306a36Sopenharmony_ci if (bus->codec[cidx] && (bus->codec[cidx]->ext_id & AC97_EI_SPDIF)) { 56962306a36Sopenharmony_ci err = set_spdif_rate(bus->codec[cidx], rate); 57062306a36Sopenharmony_ci if (err < 0) 57162306a36Sopenharmony_ci return err; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci spin_lock_irq(&pcm->bus->bus_lock); 57562306a36Sopenharmony_ci for (i = 3; i < 12; i++) { 57662306a36Sopenharmony_ci if (!(slots & (1 << i))) 57762306a36Sopenharmony_ci continue; 57862306a36Sopenharmony_ci ok_flag = 0; 57962306a36Sopenharmony_ci for (cidx = 0; cidx < 4; cidx++) { 58062306a36Sopenharmony_ci if (bus->used_slots[pcm->stream][cidx] & (1 << i)) { 58162306a36Sopenharmony_ci spin_unlock_irq(&pcm->bus->bus_lock); 58262306a36Sopenharmony_ci err = -EBUSY; 58362306a36Sopenharmony_ci goto error; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci if (pcm->r[r].rslots[cidx] & (1 << i)) { 58662306a36Sopenharmony_ci bus->used_slots[pcm->stream][cidx] |= (1 << i); 58762306a36Sopenharmony_ci ok_flag++; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci if (!ok_flag) { 59162306a36Sopenharmony_ci spin_unlock_irq(&pcm->bus->bus_lock); 59262306a36Sopenharmony_ci dev_err(bus->card->dev, 59362306a36Sopenharmony_ci "cannot find configuration for AC97 slot %i\n", 59462306a36Sopenharmony_ci i); 59562306a36Sopenharmony_ci err = -EAGAIN; 59662306a36Sopenharmony_ci goto error; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci pcm->cur_dbl = r; 60062306a36Sopenharmony_ci spin_unlock_irq(&pcm->bus->bus_lock); 60162306a36Sopenharmony_ci for (i = 3; i < 12; i++) { 60262306a36Sopenharmony_ci if (!(slots & (1 << i))) 60362306a36Sopenharmony_ci continue; 60462306a36Sopenharmony_ci for (cidx = 0; cidx < 4; cidx++) { 60562306a36Sopenharmony_ci if (pcm->r[r].rslots[cidx] & (1 << i)) { 60662306a36Sopenharmony_ci reg = get_slot_reg(pcm, cidx, i, r); 60762306a36Sopenharmony_ci if (reg == 0xff) { 60862306a36Sopenharmony_ci dev_err(bus->card->dev, 60962306a36Sopenharmony_ci "invalid AC97 slot %i?\n", i); 61062306a36Sopenharmony_ci continue; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci if (reg_ok[cidx] & (1 << (reg - AC97_PCM_FRONT_DAC_RATE))) 61362306a36Sopenharmony_ci continue; 61462306a36Sopenharmony_ci dev_dbg(bus->card->dev, 61562306a36Sopenharmony_ci "setting ac97 reg 0x%x to rate %d\n", 61662306a36Sopenharmony_ci reg, rate); 61762306a36Sopenharmony_ci err = snd_ac97_set_rate(pcm->r[r].codec[cidx], reg, rate); 61862306a36Sopenharmony_ci if (err < 0) 61962306a36Sopenharmony_ci dev_err(bus->card->dev, 62062306a36Sopenharmony_ci "error in snd_ac97_set_rate: cidx=%d, reg=0x%x, rate=%d, err=%d\n", 62162306a36Sopenharmony_ci cidx, reg, rate, err); 62262306a36Sopenharmony_ci else 62362306a36Sopenharmony_ci reg_ok[cidx] |= (1 << (reg - AC97_PCM_FRONT_DAC_RATE)); 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci pcm->aslots = slots; 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci error: 63162306a36Sopenharmony_ci pcm->aslots = slots; 63262306a36Sopenharmony_ci snd_ac97_pcm_close(pcm); 63362306a36Sopenharmony_ci return err; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_open); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/** 63962306a36Sopenharmony_ci * snd_ac97_pcm_close - closes the given AC97 pcm 64062306a36Sopenharmony_ci * @pcm: the ac97 pcm instance 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * It frees the locked AC97 slots. 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * Return: Zero. 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_ciint snd_ac97_pcm_close(struct ac97_pcm *pcm) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct snd_ac97_bus *bus; 64962306a36Sopenharmony_ci unsigned short slots = pcm->aslots; 65062306a36Sopenharmony_ci int i, cidx; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci#ifdef CONFIG_SND_AC97_POWER_SAVE 65362306a36Sopenharmony_ci int r = pcm->cur_dbl; 65462306a36Sopenharmony_ci for (i = 3; i < 12; i++) { 65562306a36Sopenharmony_ci if (!(slots & (1 << i))) 65662306a36Sopenharmony_ci continue; 65762306a36Sopenharmony_ci for (cidx = 0; cidx < 4; cidx++) { 65862306a36Sopenharmony_ci if (pcm->r[r].rslots[cidx] & (1 << i)) { 65962306a36Sopenharmony_ci int reg = get_slot_reg(pcm, cidx, i, r); 66062306a36Sopenharmony_ci snd_ac97_update_power(pcm->r[r].codec[cidx], 66162306a36Sopenharmony_ci reg, 0); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci#endif 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci bus = pcm->bus; 66862306a36Sopenharmony_ci spin_lock_irq(&pcm->bus->bus_lock); 66962306a36Sopenharmony_ci for (i = 3; i < 12; i++) { 67062306a36Sopenharmony_ci if (!(slots & (1 << i))) 67162306a36Sopenharmony_ci continue; 67262306a36Sopenharmony_ci for (cidx = 0; cidx < 4; cidx++) 67362306a36Sopenharmony_ci bus->used_slots[pcm->stream][cidx] &= ~(1 << i); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci pcm->aslots = 0; 67662306a36Sopenharmony_ci pcm->cur_dbl = 0; 67762306a36Sopenharmony_ci spin_unlock_irq(&pcm->bus->bus_lock); 67862306a36Sopenharmony_ci return 0; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_close); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params, 68462306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 68762306a36Sopenharmony_ci if (channels->min > 2) { 68862306a36Sopenharmony_ci static const struct snd_interval single_rates = { 68962306a36Sopenharmony_ci .min = 1, 69062306a36Sopenharmony_ci .max = 48000, 69162306a36Sopenharmony_ci }; 69262306a36Sopenharmony_ci struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 69362306a36Sopenharmony_ci return snd_interval_refine(rate, &single_rates); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int double_rate_hw_constraint_channels(struct snd_pcm_hw_params *params, 69962306a36Sopenharmony_ci struct snd_pcm_hw_rule *rule) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 70262306a36Sopenharmony_ci if (rate->min > 48000) { 70362306a36Sopenharmony_ci static const struct snd_interval double_rate_channels = { 70462306a36Sopenharmony_ci .min = 2, 70562306a36Sopenharmony_ci .max = 2, 70662306a36Sopenharmony_ci }; 70762306a36Sopenharmony_ci struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 70862306a36Sopenharmony_ci return snd_interval_refine(channels, &double_rate_channels); 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci/** 71462306a36Sopenharmony_ci * snd_ac97_pcm_double_rate_rules - set double rate constraints 71562306a36Sopenharmony_ci * @runtime: the runtime of the ac97 front playback pcm 71662306a36Sopenharmony_ci * 71762306a36Sopenharmony_ci * Installs the hardware constraint rules to prevent using double rates and 71862306a36Sopenharmony_ci * more than two channels at the same time. 71962306a36Sopenharmony_ci * 72062306a36Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ciint snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int err; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 72762306a36Sopenharmony_ci double_rate_hw_constraint_rate, NULL, 72862306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, -1); 72962306a36Sopenharmony_ci if (err < 0) 73062306a36Sopenharmony_ci return err; 73162306a36Sopenharmony_ci err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 73262306a36Sopenharmony_ci double_rate_hw_constraint_channels, NULL, 73362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 73462306a36Sopenharmony_ci return err; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules); 738