📄 ac97_patch.c
字号:
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, spdif-in monitor off, spdif-in PCM off center on mic off, surround on line-in off duplicate front off */ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15); /* full DAC volume */ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808); return 0;}#define AC97_ALC850_JACK_SELECT 0x76#define AC97_ALC850_MISC1 0x7astatic int ac97_alc850_surround_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 12) & 7) == 2; return 0;}static int ac97_alc850_surround_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); /* SURR 1kOhm (bit4), Amp (bit5) */ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5), ucontrol->value.integer.value[0] ? (1<<5) : (1<<4)); /* LINE-IN = 0, SURROUND = 2 */ return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12, ucontrol->value.integer.value[0] ? (2<<12) : (0<<12));}static int ac97_alc850_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 4) & 7) == 2; return 0;}static int ac97_alc850_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); /* Vref disable (bit12), 1kOhm (bit13) */ snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13), ucontrol->value.integer.value[0] ? (1<<12) : (1<<13)); /* MIC-IN = 1, CENTER-LFE = 2 */ return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4, ucontrol->value.integer.value[0] ? (2<<4) : (1<<4));}static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = { AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line-In As Surround", .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 */ }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic As Center/LFE", .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 */ },};static int patch_alc850_specific(ac97_t *ac97){ int err; if ((err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850))) < 0) return err; if (ac97->ext_id & AC97_EI_SPDIF) { if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0) return err; } return 0;}static struct snd_ac97_build_ops patch_alc850_ops = { .build_specific = patch_alc850_specific};int patch_alc850(ac97_t *ac97){ ac97->build_ops = &patch_alc850_ops; ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */ /* assume only page 0 for writing cache */ snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR); /* adjust default values */ /* set default: spdif-in enabled, spdif-in monitor off, spdif-in PCM off center on mic off, surround on line-in off duplicate front off */ snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15); /* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off * Front Amp: on, Vref: enable, Center 1kOhm: on, Mix: on */ snd_ac97_write_cache(ac97, 0x7a, (1<<1)|(1<<4)|(0<<5)|(1<<6)| (1<<7)|(0<<12)|(1<<13)|(0<<14)); /* detection UIO2,3: all path floating, UIO3: MIC, Vref2: disable, * UIO1: FRONT, Vref3: disable, UIO3: LINE, Front-Mic: mute */ snd_ac97_write_cache(ac97, 0x76, (0<<0)|(0<<2)|(1<<4)|(1<<7)|(2<<8)| (1<<11)|(0<<12)|(1<<15)); /* full DAC volume */ snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808); return 0;}/* * C-Media CM97xx codecs */static const snd_kcontrol_new_t snd_ac97_cm9738_controls[] = { AC97_SINGLE("Line-In As Surround", AC97_CM9738_VENDOR_CTRL, 10, 1, 0), AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0),};static int patch_cm9738_specific(ac97_t * ac97){ return patch_build_controls(ac97, snd_ac97_cm9738_controls, ARRAY_SIZE(snd_ac97_cm9738_controls));}static struct snd_ac97_build_ops patch_cm9738_ops = { .build_specific = patch_cm9738_specific};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;}static int snd_ac97_cmedia_spdif_playback_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ static char *texts[] = { "Analog", "Digital" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 2; if (uinfo->value.enumerated.item > 1) uinfo->value.enumerated.item = 1; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_ac97_cmedia_spdif_playback_source_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); unsigned short val; val = ac97->regs[AC97_CM9739_SPDIF_CTRL]; ucontrol->value.enumerated.item[0] = (val >> 1) & 0x01; return 0;}static int snd_ac97_cmedia_spdif_playback_source_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); return snd_ac97_update_bits(ac97, AC97_CM9739_SPDIF_CTRL, 0x01 << 1, (ucontrol->value.enumerated.item[0] & 0x01) << 1);}static const snd_kcontrol_new_t snd_ac97_cm9739_controls_spdif[] = { /* BIT 0: SPDI_EN - always true */ { /* BIT 1: SPDIFS */ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", .info = snd_ac97_cmedia_spdif_playback_source_info, .get = snd_ac97_cmedia_spdif_playback_source_get, .put = snd_ac97_cmedia_spdif_playback_source_put, }, /* BIT 2: IG_SPIV */ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9739_SPDIF_CTRL, 2, 1, 0), /* BIT 3: SPI2F */ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9739_SPDIF_CTRL, 3, 1, 0), /* BIT 4: SPI2SDI */ AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9739_SPDIF_CTRL, 4, 1, 0), /* BIT 8: SPD32 - 32bit SPDIF - not supported yet */};static int snd_ac97_cm9739_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; return 0;}static int snd_ac97_cm9739_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000, ucontrol->value.integer.value[0] ? 0x1000 : 0x2000);}static const snd_kcontrol_new_t snd_ac97_cm9739_controls[] = { AC97_SINGLE("Line-In As Surround", AC97_CM9739_MULTI_CHAN, 10, 1, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic As Center/LFE", .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 */ },};static int patch_cm9739_specific(ac97_t * ac97){ return patch_build_controls(ac97, snd_ac97_cm9739_controls, ARRAY_SIZE(snd_ac97_cm9739_controls));}static int patch_cm9739_post_spdif(ac97_t * ac97){ return patch_build_controls(ac97, snd_ac97_cm9739_controls_spdif, ARRAY_SIZE(snd_ac97_cm9739_controls_spdif));}static struct snd_ac97_build_ops patch_cm9739_ops = { .build_specific = patch_cm9739_specific, .build_post_spdif = patch_cm9739_post_spdif};int patch_cm9739(ac97_t * ac97){ unsigned short val; 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) { /* enable spdif in */ snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL, snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) | 0x01); ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */ } else { ac97->ext_id &= ~AC97_EI_SPDIF; /* disable extended-id */ ac97->rates[AC97_RATES_SPDIF] = 0; } /* set-up multi channel */ /* bit 14: 0 = SPDIF, 1 = EAPD */ /* bit 13: enable internal vref output for mic */ /* 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 = 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 0x6cstatic 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); sn
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -