X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sound%2Fpci%2Fac97%2Fac97_codec.c;h=8f43dd60dd1bf19ee2e071192b719cc57cf61001;hb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;hp=4044d0816c71679a9a2346df20cf70d10505cc81;hpb=c449269f45c2cdf53af08c8d0af37472f66539d9;p=linux-2.6.git diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 4044d0816..8f43dd60d 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ MODULE_LICENSE("GPL"); static int enable_loopback; -MODULE_PARM(enable_loopback, "i"); +module_param(enable_loopback, bool, 0444); MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC); @@ -299,6 +300,16 @@ unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg) return ac97->bus->read(ac97, reg); } +/* read a register - return the cached value if already read */ +static inline unsigned short snd_ac97_read_cache(ac97_t *ac97, unsigned short reg) +{ + if (! test_bit(reg, ac97->reg_accessed)) { + ac97->regs[reg] = ac97->bus->read(ac97, reg); + // set_bit(reg, ac97->reg_accessed); + } + return ac97->regs[reg]; +} + /** * snd_ac97_write_cache - write a value on the given register and update the cache * @ac97: the ac97 instance @@ -370,7 +381,7 @@ int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, if (!snd_ac97_valid_reg(ac97, reg)) return -EINVAL; spin_lock(&ac97->reg_lock); - old = ac97->regs[reg]; + old = snd_ac97_read_cache(ac97, reg); new = (old & ~mask) | value; change = old != new; if (change) { @@ -385,25 +396,26 @@ int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, static int snd_ac97_ad18xx_update_pcm_bits(ac97_t *ac97, int codec, unsigned short mask, unsigned short value) { int change; - unsigned short old, new; + unsigned short old, new, cfg; down(&ac97->spec.ad18xx.mutex); spin_lock(&ac97->reg_lock); old = ac97->spec.ad18xx.pcmreg[codec]; new = (old & ~mask) | value; + cfg = snd_ac97_read_cache(ac97, AC97_AD_SERIAL_CFG); change = old != new; if (change) { ac97->spec.ad18xx.pcmreg[codec] = new; spin_unlock(&ac97->reg_lock); /* select single codec */ ac97->bus->write(ac97, AC97_AD_SERIAL_CFG, - (ac97->regs[AC97_AD_SERIAL_CFG] & ~0x7000) | + (cfg & ~0x7000) | ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); /* update PCM bits */ ac97->bus->write(ac97, AC97_PCM, new); /* select all codecs */ ac97->bus->write(ac97, AC97_AD_SERIAL_CFG, - ac97->regs[AC97_AD_SERIAL_CFG] | 0x7000); + cfg | 0x7000); } else spin_unlock(&ac97->reg_lock); up(&ac97->spec.ad18xx.mutex); @@ -435,7 +447,7 @@ static int snd_ac97_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * uc ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; - val = ac97->regs[AC97_REC_SEL]; + val = snd_ac97_read_cache(ac97, AC97_REC_SEL); ucontrol->value.enumerated.item[0] = (val >> 8) & 7; ucontrol->value.enumerated.item[1] = (val >> 0) & 7; return 0; @@ -493,7 +505,7 @@ static int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_valu int shift = (kcontrol->private_value >> 8) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - val = (ac97->regs[reg] >> shift) & 1; + val = (snd_ac97_read_cache(ac97, reg) >> shift) & 1; if (invert) val ^= 1; ucontrol->value.enumerated.item[0] = val; @@ -535,7 +547,7 @@ int snd_ac97_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontr int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift) & mask; + ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; return 0; @@ -582,8 +594,8 @@ static int snd_ac97_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * int invert = (kcontrol->private_value >> 24) & 0xff; spin_lock(&ac97->reg_lock); - ucontrol->value.integer.value[0] = (ac97->regs[reg] >> shift_left) & mask; - ucontrol->value.integer.value[1] = (ac97->regs[reg] >> shift_right) & mask; + ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift_left) & mask; + ucontrol->value.integer.value[1] = (snd_ac97_read_cache(ac97, reg) >> shift_right) & mask; spin_unlock(&ac97->reg_lock); if (invert) { ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; @@ -796,7 +808,7 @@ static int snd_ac97_spdif_default_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_val AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT, v); } else { - unsigned short extst = ac97->regs[AC97_EXTENDED_STATUS]; + unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS); snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */ change |= snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val); @@ -822,13 +834,13 @@ static int snd_ac97_put_spsa(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uco mask <<= shift; value <<= shift; spin_lock(&ac97->reg_lock); - old = ac97->regs[reg]; + old = snd_ac97_read_cache(ac97, reg); new = (old & ~mask) | value; spin_unlock(&ac97->reg_lock); if (old != new) { int change; - unsigned short extst = ac97->regs[AC97_EXTENDED_STATUS]; + unsigned short extst = snd_ac97_read_cache(ac97, AC97_EXTENDED_STATUS); snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, AC97_EA_SPDIF, 0); /* turn off */ change = snd_ac97_update_bits(ac97, reg, mask, value); if (extst & AC97_EA_SPDIF) @@ -868,14 +880,14 @@ const snd_kcontrol_new_t snd_ac97_controls_spdif[5] = { .info = snd_ac97_info_single, .get = snd_ac97_get_single, .put = snd_ac97_put_spsa, - .private_value = AC97_EXTENDED_STATUS | (4 << 8) | (3 << 16) | (0 << 24), + .private_value = AC97_SINGLE_VALUE(AC97_EXTENDED_STATUS, 4, 3, 0) }, }; #define AD18XX_PCM_BITS(xname, codec, lshift, rshift, mask) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_bits, \ .get = snd_ac97_ad18xx_pcm_get_bits, .put = snd_ac97_ad18xx_pcm_put_bits, \ - .private_value = (codec) | ((lshift) << 8) | ((rshift) << 12) | ((mask) << 24) } + .private_value = (codec) | ((lshift) << 8) | ((rshift) << 12) | ((mask) << 16) } static int snd_ac97_ad18xx_pcm_info_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { @@ -987,6 +999,8 @@ AD18XX_PCM_BITS("LFE Playback Volume", 2, 0, 0, 31) * */ +static void snd_ac97_powerdown(ac97_t *ac97); + static int snd_ac97_bus_free(ac97_bus_t *bus) { if (bus) { @@ -1022,6 +1036,7 @@ static int snd_ac97_free(ac97_t *ac97) static int snd_ac97_dev_free(snd_device_t *device) { ac97_t *ac97 = snd_magic_cast(ac97_t, device->device_data, return -ENXIO); + snd_ac97_powerdown(ac97); /* for avoiding click noises during shut down */ return snd_ac97_free(ac97); } @@ -1572,6 +1587,26 @@ static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsi *r_result = result; } +/* check AC97_SPDIF register to accept which sample rates */ +static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97) +{ + unsigned int result = 0; + int i; + static unsigned short ctl_bits[] = { + AC97_SC_SPSR_44K, AC97_SC_SPSR_32K, AC97_SC_SPSR_48K + }; + static unsigned int rate_bits[] = { + SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_32000, SNDRV_PCM_RATE_48000 + }; + + for (i = 0; i < (int)ARRAY_SIZE(ctl_bits); i++) { + snd_ac97_update_bits(ac97, AC97_SPDIF, AC97_SC_SPSR_MASK, ctl_bits[i]); + if ((snd_ac97_read(ac97, AC97_SPDIF) & AC97_SC_SPSR_MASK) == ctl_bits[i]) + result |= rate_bits[i]; + } + return result; +} + void snd_ac97_get_name(ac97_t *ac97, unsigned int id, char *name, int modem) { const ac97_codec_id_t *pid; @@ -1889,9 +1924,7 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97) else if (ac97->id == AC97_ID_CM9739) ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; else - ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_32000; + ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97); } if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */ snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]); @@ -1949,12 +1982,12 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97) } /* make sure the proper powerdown bits are cleared */ if (ac97->scaps) { - reg = snd_ac97_read(ac97, AC97_EXTENDED_ID); + reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); if (ac97->scaps & AC97_SCAP_SURROUND_DAC) reg &= ~AC97_EA_PRJ; if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) reg &= ~(AC97_EA_PRI | AC97_EA_PRK); - snd_ac97_write_cache(ac97, AC97_EXTENDED_ID, reg); + snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg); } snd_ac97_proc_init(ac97); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ac97, &ops)) < 0) { @@ -1965,17 +1998,24 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97) return 0; } -#ifdef CONFIG_PM -/** - * snd_ac97_suspend - General suspend function for AC97 codec - * @ac97: the ac97 instance + +/* + * Power down the chip. * - * Suspends the codec, power down the chip. + * MASTER and HEADPHONE registers are muted but the register cache values + * are not changed, so that the values can be restored in snd_ac97_resume(). */ -void snd_ac97_suspend(ac97_t *ac97) +static void snd_ac97_powerdown(ac97_t *ac97) { - unsigned short power = (ac97->regs[AC97_POWERDOWN] ^ 0x8000) & ~0x8000; /* invert EAPD */ + unsigned short power; + + if (ac97_is_audio(ac97)) { + /* some codecs have stereo mute bits */ + snd_ac97_write(ac97, AC97_MASTER, 0x9f9f); + snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f); + } + power = ac97->regs[AC97_POWERDOWN] | 0x8000; /* EAPD */ power |= 0x4000; /* Headphone amplifier powerdown */ power |= 0x0300; /* ADC & DAC powerdown */ snd_ac97_write(ac97, AC97_POWERDOWN, power); @@ -1983,8 +2023,24 @@ void snd_ac97_suspend(ac97_t *ac97) power |= 0x0400; /* Analog Mixer powerdown (Vref on) */ snd_ac97_write(ac97, AC97_POWERDOWN, power); udelay(100); +#if 0 + /* FIXME: this causes click noises on some boards at resume */ power |= 0x3800; /* AC-link powerdown, internal Clk disable */ snd_ac97_write(ac97, AC97_POWERDOWN, power); +#endif +} + + +#ifdef CONFIG_PM +/** + * snd_ac97_suspend - General suspend function for AC97 codec + * @ac97: the ac97 instance + * + * Suspends the codec, power down the chip. + */ +void snd_ac97_suspend(ac97_t *ac97) +{ + snd_ac97_powerdown(ac97); } /** @@ -2267,6 +2323,7 @@ EXPORT_SYMBOL(snd_ac97_tune_hardware); EXPORT_SYMBOL(snd_ac97_set_rate); #ifdef CONFIG_PM EXPORT_SYMBOL(snd_ac97_resume); +EXPORT_SYMBOL(snd_ac97_suspend); #endif /*