X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sound%2Fpci%2Fac97%2Fac97_patch.c;h=5bb7f24f4acf41aa64a543fecc928a43fb7f8dfb;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=1ad062a19e44d7302b3ba3ca03885fcacc80174b;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 1ad062a19..5bb7f24f4 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -55,12 +55,12 @@ static int ac97_update_bits_page(ac97_t *ac97, unsigned short reg, unsigned shor unsigned short page_save; int ret; - down(&ac97->mutex); + down(&ac97->page_mutex); page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK; snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page); ret = snd_ac97_update_bits(ac97, reg, mask, value); snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); - up(&ac97->mutex); /* unlock paging */ + up(&ac97->page_mutex); /* unlock paging */ return ret; } @@ -775,6 +775,71 @@ int patch_conexant(ac97_t * ac97) /* * Analog Device AD18xx, AD19xx codecs */ +#ifdef CONFIG_PM +static void ad18xx_resume(ac97_t *ac97) +{ + static unsigned short setup_regs[] = { + AC97_AD_MISC, AC97_AD_SERIAL_CFG, AC97_AD_JACK_SPDIF, + }; + int i, codec; + + for (i = 0; i < (int)ARRAY_SIZE(setup_regs); i++) { + unsigned short reg = setup_regs[i]; + if (test_bit(reg, ac97->reg_accessed)) { + snd_ac97_write(ac97, reg, ac97->regs[reg]); + snd_ac97_read(ac97, reg); + } + } + + if (! (ac97->flags & AC97_AD_MULTI)) + /* normal restore */ + snd_ac97_restore_status(ac97); + else { + /* restore the AD18xx codec configurations */ + for (codec = 0; codec < 3; codec++) { + if (! ac97->spec.ad18xx.id[codec]) + continue; + /* select single codec */ + snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, + ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); + ac97->bus->ops->write(ac97, AC97_AD_CODEC_CFG, ac97->spec.ad18xx.codec_cfg[codec]); + } + /* select all codecs */ + snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000); + + /* restore status */ + for (i = 2; i < 0x7c ; i += 2) { + if (i == AC97_POWERDOWN || i == AC97_EXTENDED_ID) + continue; + if (test_bit(i, ac97->reg_accessed)) { + /* handle multi codecs for AD18xx */ + if (i == AC97_PCM) { + for (codec = 0; codec < 3; codec++) { + if (! ac97->spec.ad18xx.id[codec]) + continue; + /* select single codec */ + snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, + ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); + /* update PCM bits */ + ac97->bus->ops->write(ac97, AC97_PCM, ac97->spec.ad18xx.pcmreg[codec]); + } + /* select all codecs */ + snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000); + continue; + } else if (i == AC97_AD_TEST || + i == AC97_AD_CODEC_CFG || + i == AC97_AD_SERIAL_CFG) + continue; /* ignore */ + } + snd_ac97_write(ac97, i, ac97->regs[i]); + snd_ac97_read(ac97, i); + } + } + + snd_ac97_restore_iec958(ac97); +} +#endif + int patch_ad1819(ac97_t * ac97) { unsigned short scfg; @@ -843,6 +908,12 @@ static void patch_ad1881_chained(ac97_t * ac97, int unchained_idx, int cidx1, in } } +static struct snd_ac97_build_ops patch_ad1881_build_ops = { +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif +}; + int patch_ad1881(ac97_t * ac97) { static const char cfg_idxs[3][2] = { @@ -897,6 +968,7 @@ int patch_ad1881(ac97_t * ac97) ac97->id &= 0xffff0000; ac97->id |= ac97->spec.ad18xx.id[0]; } + ac97->build_ops = &patch_ad1881_build_ops; return 0; } @@ -919,7 +991,10 @@ static int patch_ad1885_specific(ac97_t * ac97) } static struct snd_ac97_build_ops patch_ad1885_build_ops = { - .build_specific = &patch_ad1885_specific + .build_specific = &patch_ad1885_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif }; int patch_ad1885(ac97_t * ac97) @@ -1013,16 +1088,45 @@ static int patch_ad198x_post_spdif(ac97_t * ac97) return patch_build_controls(ac97, &snd_ac97_ad198x_spdif_source, 1); } +static const snd_kcontrol_new_t snd_ac97_ad1981x_jack_sense[] = { + AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 11, 1, 0), + AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0), +}; + +static int patch_ad1981a_specific(ac97_t * ac97) +{ + return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense, + ARRAY_SIZE(snd_ac97_ad1981x_jack_sense)); +} + static struct snd_ac97_build_ops patch_ad1981a_build_ops = { - .build_post_spdif = patch_ad198x_post_spdif + .build_post_spdif = patch_ad198x_post_spdif, + .build_specific = patch_ad1981a_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif }; +static void check_ad1981_hp_jack_sense(ac97_t *ac97) +{ + u32 subid = ((u32)ac97->subsystem_vendor << 16) | ac97->subsystem_device; + switch (subid) { + case 0x103c0890: /* HP nc6000 */ + case 0x103c006d: /* HP nx9105 */ + case 0x17340088: /* FSC Scenic-W */ + /* enable headphone jack sense */ + snd_ac97_update_bits(ac97, AC97_AD_JACK_SPDIF, 1<<11, 1<<11); + break; + } +} + int patch_ad1981a(ac97_t *ac97) { patch_ad1881(ac97); ac97->build_ops = &patch_ad1981a_build_ops; snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT); ac97->flags |= AC97_STEREO_MUTES; + check_ad1981_hp_jack_sense(ac97); return 0; } @@ -1031,12 +1135,20 @@ AC97_SINGLE("Stereo Mic", AC97_AD_MISC, 6, 1, 0); static int patch_ad1981b_specific(ac97_t *ac97) { - return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1); + int err; + + if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0) + return err; + return patch_build_controls(ac97, snd_ac97_ad1981x_jack_sense, + ARRAY_SIZE(snd_ac97_ad1981x_jack_sense)); } static struct snd_ac97_build_ops patch_ad1981b_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, - .build_specific = patch_ad1981b_specific + .build_specific = patch_ad1981b_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif }; int patch_ad1981b(ac97_t *ac97) @@ -1045,6 +1157,7 @@ int patch_ad1981b(ac97_t *ac97) ac97->build_ops = &patch_ad1981b_build_ops; snd_ac97_update_bits(ac97, AC97_AD_MISC, AC97_AD198X_MSPLT, AC97_AD198X_MSPLT); ac97->flags |= AC97_STEREO_MUTES; + check_ad1981_hp_jack_sense(ac97); return 0; } @@ -1150,7 +1263,10 @@ static int patch_ad1888_specific(ac97_t *ac97) static struct snd_ac97_build_ops patch_ad1888_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, - .build_specific = patch_ad1888_specific + .build_specific = patch_ad1888_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif }; int patch_ad1888(ac97_t * ac97) @@ -1184,7 +1300,10 @@ static int patch_ad1980_specific(ac97_t *ac97) static struct snd_ac97_build_ops patch_ad1980_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, - .build_specific = patch_ad1980_specific + .build_specific = patch_ad1980_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif }; int patch_ad1980(ac97_t * ac97) @@ -1210,7 +1329,10 @@ static int patch_ad1985_specific(ac97_t *ac97) static struct snd_ac97_build_ops patch_ad1985_build_ops = { .build_post_spdif = patch_ad198x_post_spdif, - .build_specific = patch_ad1985_specific + .build_specific = patch_ad1985_specific, +#ifdef CONFIG_PM + .resume = ad18xx_resume +#endif }; int patch_ad1985(ac97_t * ac97) @@ -1294,7 +1416,7 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic As Center/LFE", - .info = snd_ac97_info_single, + .info = snd_ac97_info_volsw, .get = snd_ac97_alc650_mic_get, .put = snd_ac97_alc650_mic_put, .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ @@ -1304,7 +1426,8 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = { AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0), AC97_SINGLE("Analog to IEC958 Output", AC97_ALC650_MULTICH, 12, 1, 0), - AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), + /* disable this controls since it doesn't work as expected */ + /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */ }; static int patch_alc650_specific(ac97_t * ac97) @@ -1405,7 +1528,7 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic As Center/LFE", - .info = snd_ac97_info_single, + .info = snd_ac97_info_volsw, .get = snd_ac97_alc655_mic_get, .put = snd_ac97_alc655_mic_put, .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ @@ -1454,7 +1577,8 @@ static int alc655_iec958_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_ static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc655[] = { AC97_PAGE_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0, 0), - AC97_PAGE_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0, 0), + /* disable this controls since it doesn't work as expected */ + /* AC97_PAGE_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0, 0), */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "IEC958 Playback Route", @@ -1494,7 +1618,10 @@ int patch_alc655(ac97_t * ac97) /* adjust default values */ val = snd_ac97_read(ac97, 0x7a); /* misc control */ - val |= (1 << 1); /* spdif input pin */ + if (ac97->id == 0x414c4780) /* ALC658 */ + val &= ~(1 << 1); /* Pin 47 is spdif input pin */ + else /* ALC655 */ + val |= (1 << 1); /* Pin 47 is spdif input pin */ val &= ~(1 << 12); /* vref enable */ snd_ac97_write_cache(ac97, 0x7a, val); /* set default: spdif-in enabled, @@ -1557,7 +1684,7 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line-In As Surround", - .info = snd_ac97_info_single, + .info = snd_ac97_info_volsw, .get = ac97_alc850_surround_get, .put = ac97_alc850_surround_put, .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ @@ -1565,7 +1692,7 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic As Center/LFE", - .info = snd_ac97_info_single, + .info = snd_ac97_info_volsw, .get = ac97_alc850_mic_get, .put = ac97_alc850_mic_put, .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ @@ -1644,6 +1771,11 @@ static struct snd_ac97_build_ops patch_cm9738_ops = { int patch_cm9738(ac97_t * ac97) { ac97->build_ops = &patch_cm9738_ops; + /* FIXME: can anyone confirm below? */ + /* CM9738 has no PCM volume although the register reacts */ + ac97->flags |= AC97_HAS_NO_PCM_VOL; + snd_ac97_write_cache(ac97, AC97_PCM, 0x8000); + return 0; } @@ -1720,7 +1852,7 @@ static const snd_kcontrol_new_t snd_ac97_cm9739_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic As Center/LFE", - .info = snd_ac97_info_single, + .info = snd_ac97_info_volsw, .get = snd_ac97_cm9739_center_mic_get, .put = snd_ac97_cm9739_center_mic_put, .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ @@ -1748,6 +1880,11 @@ int patch_cm9739(ac97_t * ac97) ac97->build_ops = &patch_cm9739_ops; + /* CM9739/A has no Master and PCM volume although the register reacts */ + ac97->flags |= AC97_HAS_NO_MASTER_VOL | AC97_HAS_NO_PCM_VOL; + snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000); + snd_ac97_write_cache(ac97, AC97_PCM, 0x8000); + /* check spdif */ val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); if (val & AC97_EA_SPCV) { @@ -1766,18 +1903,170 @@ int patch_cm9739(ac97_t * ac97) /* bit 12: disable center/lfe (swithable) */ /* bit 10: disable surround/line (switchable) */ /* bit 9: mix 2 surround off */ + /* bit 4: undocumented; 0 mutes the CM9739A, which defaults to 1 */ + /* bit 3: undocumented; surround? */ /* bit 0: dB */ - val = (1 << 13); + val = snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) & (1 << 4); + val |= (1 << 3); + val |= (1 << 13); if (! (ac97->ext_id & AC97_EI_SPDIF)) val |= (1 << 14); snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, val); + /* FIXME: set up GPIO */ + snd_ac97_write_cache(ac97, 0x70, 0x0100); + snd_ac97_write_cache(ac97, 0x72, 0x0020); + /* Special exception for ASUS W1000/CMI9739. It does not have an SPDIF in. */ + if (ac97->pci && + ac97->subsystem_vendor == 0x1043 && + ac97->subsystem_device == 0x1843) { + snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL, + snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) & ~0x01); + snd_ac97_write_cache(ac97, AC97_CM9739_MULTI_CHAN, + snd_ac97_read(ac97, AC97_CM9739_MULTI_CHAN) | (1 << 14)); + } + + return 0; +} + +#define AC97_CM9761_MULTI_CHAN 0x64 +#define AC97_CM9761_SPDIF_CTRL 0x6c + +static int snd_ac97_cm9761_linein_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x0400) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + return 0; +} + +static int snd_ac97_cm9761_linein_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short vals[2][2] = { + { 0x0008, 0x0400 }, /* off, on */ + { 0x0000, 0x0408 }, /* off, on (9761-82 rev.B) */ + }; + return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x0408, + vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]); +} + +static int snd_ac97_cm9761_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + if (ac97->spec.dev_flags) /* 9761-82 rev.B */ + ucontrol->value.integer.value[0] = !ucontrol->value.integer.value[0]; + return 0; +} + +static int snd_ac97_cm9761_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + unsigned short vals[2][2] = { + { 0x2000, 0x1880 }, /* off, on */ + { 0x1000, 0x2880 }, /* off, on (9761-82 rev.B) */ + }; + return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3880, + vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]); +} + +static const snd_kcontrol_new_t snd_ac97_cm9761_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line-In As Surround", + .info = snd_ac97_info_volsw, + .get = snd_ac97_cm9761_linein_rear_get, + .put = snd_ac97_cm9761_linein_rear_put, + .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic As Center/LFE", + .info = snd_ac97_info_volsw, + .get = snd_ac97_cm9761_center_mic_get, + .put = snd_ac97_cm9761_center_mic_put, + .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ + }, +}; + +static int patch_cm9761_specific(ac97_t * ac97) +{ + return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls)); +} + +static struct snd_ac97_build_ops patch_cm9761_ops = { + .build_specific = patch_cm9761_specific, + .build_post_spdif = patch_cm9739_post_spdif /* hope it's identical... */ +}; + +int patch_cm9761(ac97_t *ac97) +{ + unsigned short val; + + /* CM9761 has no Master and PCM volume although the register reacts */ + ac97->flags |= AC97_HAS_NO_MASTER_VOL | AC97_HAS_NO_PCM_VOL; + snd_ac97_write_cache(ac97, AC97_MASTER, 0x8000); + snd_ac97_write_cache(ac97, AC97_PCM, 0x8000); + + ac97->spec.dev_flags = 0; /* 1 = model 82 revision B */ + if (ac97->id == AC97_ID_CM9761_82) { + unsigned short tmp; + /* check page 1, reg 0x60 */ + val = snd_ac97_read(ac97, AC97_INT_PAGING); + snd_ac97_write_cache(ac97, AC97_INT_PAGING, (val & ~0x0f) | 0x01); + tmp = snd_ac97_read(ac97, 0x60); + ac97->spec.dev_flags = tmp & 1; /* revision B? */ + snd_ac97_write_cache(ac97, AC97_INT_PAGING, val); + } + + ac97->build_ops = &patch_cm9761_ops; + + /* enable spdif */ + /* force the SPDIF bit in ext_id - codec doesn't set this bit! */ + ac97->ext_id |= AC97_EI_SPDIF; + /* to be sure: we overwrite the ext status bits */ + snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, 0x05c0); + snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0209); + ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */ + + /* set-up multi channel */ + /* bit 15: pc master beep off + * bit 14: ?? + * bit 13: vref ctl [= cm9739] + * bit 12: center/mic [= cm9739] (reverted on rev B) + * bit 11: ?? (mic/center/lfe) (reverted on rev B) + * bit 10: suddound/line [= cm9739] + * bit 9: mix 2 surround + * bit 8: ? + * bit 7: ?? (mic/center/lfe) + * bit 4: ?? (front) + * bit 3: ?? (line-in/rear share) (revereted with rev B) + * bit 2: ?? (surround) + * bit 1: front mic + * bit 0: mic boost + */ + +#if 0 + if (ac97->spec.dev_flags) + val = 0x0214; + else + val = 0x321c; + snd_ac97_write_cache(ac97, AC97_CM9761_MULTI_CHAN, val); +#endif + /* FIXME: set up GPIO */ snd_ac97_write_cache(ac97, 0x70, 0x0100); snd_ac97_write_cache(ac97, 0x72, 0x0020); return 0; } + /* * VIA VT1616 codec