diff options
author | David Henningsson <david.henningsson@canonical.com> | 2013-07-16 05:48:10 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-07-16 05:57:37 -0400 |
commit | e4c3bce26de240457370d00ce396602cc98bb3cc (patch) | |
tree | 47aa906c59bf0f195405345b31c7515f438db94b | |
parent | 68593c9340847662ac1d337b3c5621a1f4ca05db (diff) |
ALSA: hda - Headphone mic support for an Asus/Conexant device
This Conexant codec has a single jack that can be used as either
headphone or mic (but not headset). The existing hp_mic functionality
does not apply here, because the mic and the HP are on separate pins.
Hence make a lighter version of what has been earlier done for Realtek
codecs.
BugLink: https://bugs.launchpad.net/bugs/1198030
Tested-by: Franz Hsieh <franz.hsieh@canonical.com>
Signed-off-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 79 |
1 files changed, 78 insertions, 1 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index de00ce166470..4edd2d0f9a3c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -66,6 +66,8 @@ struct conexant_spec { | |||
66 | hda_nid_t eapds[4]; | 66 | hda_nid_t eapds[4]; |
67 | bool dynamic_eapd; | 67 | bool dynamic_eapd; |
68 | 68 | ||
69 | unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */ | ||
70 | |||
69 | #ifdef ENABLE_CXT_STATIC_QUIRKS | 71 | #ifdef ENABLE_CXT_STATIC_QUIRKS |
70 | const struct snd_kcontrol_new *mixers[5]; | 72 | const struct snd_kcontrol_new *mixers[5]; |
71 | int num_mixers; | 73 | int num_mixers; |
@@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec) | |||
3200 | snd_hda_gen_init(codec); | 3202 | snd_hda_gen_init(codec); |
3201 | if (!spec->dynamic_eapd) | 3203 | if (!spec->dynamic_eapd) |
3202 | cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); | 3204 | cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); |
3205 | |||
3206 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); | ||
3207 | |||
3203 | return 0; | 3208 | return 0; |
3204 | } | 3209 | } |
3205 | 3210 | ||
@@ -3224,6 +3229,8 @@ enum { | |||
3224 | CXT_PINCFG_LEMOTE_A1205, | 3229 | CXT_PINCFG_LEMOTE_A1205, |
3225 | CXT_FIXUP_STEREO_DMIC, | 3230 | CXT_FIXUP_STEREO_DMIC, |
3226 | CXT_FIXUP_INC_MIC_BOOST, | 3231 | CXT_FIXUP_INC_MIC_BOOST, |
3232 | CXT_FIXUP_HEADPHONE_MIC_PIN, | ||
3233 | CXT_FIXUP_HEADPHONE_MIC, | ||
3227 | }; | 3234 | }; |
3228 | 3235 | ||
3229 | static void cxt_fixup_stereo_dmic(struct hda_codec *codec, | 3236 | static void cxt_fixup_stereo_dmic(struct hda_codec *codec, |
@@ -3246,6 +3253,59 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec, | |||
3246 | (0 << AC_AMPCAP_MUTE_SHIFT)); | 3253 | (0 << AC_AMPCAP_MUTE_SHIFT)); |
3247 | } | 3254 | } |
3248 | 3255 | ||
3256 | static void cxt_update_headset_mode(struct hda_codec *codec) | ||
3257 | { | ||
3258 | /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */ | ||
3259 | int i; | ||
3260 | bool mic_mode = false; | ||
3261 | struct conexant_spec *spec = codec->spec; | ||
3262 | struct auto_pin_cfg *cfg = &spec->gen.autocfg; | ||
3263 | |||
3264 | hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; | ||
3265 | |||
3266 | for (i = 0; i < cfg->num_inputs; i++) | ||
3267 | if (cfg->inputs[i].pin == mux_pin) { | ||
3268 | mic_mode = !!cfg->inputs[i].is_headphone_mic; | ||
3269 | break; | ||
3270 | } | ||
3271 | |||
3272 | if (mic_mode) { | ||
3273 | snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */ | ||
3274 | spec->gen.hp_jack_present = false; | ||
3275 | } else { | ||
3276 | snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */ | ||
3277 | spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]); | ||
3278 | } | ||
3279 | |||
3280 | snd_hda_gen_update_outputs(codec); | ||
3281 | } | ||
3282 | |||
3283 | static void cxt_update_headset_mode_hook(struct hda_codec *codec, | ||
3284 | struct snd_ctl_elem_value *ucontrol) | ||
3285 | { | ||
3286 | cxt_update_headset_mode(codec); | ||
3287 | } | ||
3288 | |||
3289 | static void cxt_fixup_headphone_mic(struct hda_codec *codec, | ||
3290 | const struct hda_fixup *fix, int action) | ||
3291 | { | ||
3292 | struct conexant_spec *spec = codec->spec; | ||
3293 | |||
3294 | switch (action) { | ||
3295 | case HDA_FIXUP_ACT_PRE_PROBE: | ||
3296 | spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC; | ||
3297 | break; | ||
3298 | case HDA_FIXUP_ACT_PROBE: | ||
3299 | spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; | ||
3300 | spec->gen.automute_hook = cxt_update_headset_mode; | ||
3301 | break; | ||
3302 | case HDA_FIXUP_ACT_INIT: | ||
3303 | cxt_update_headset_mode(codec); | ||
3304 | break; | ||
3305 | } | ||
3306 | } | ||
3307 | |||
3308 | |||
3249 | /* ThinkPad X200 & co with cxt5051 */ | 3309 | /* ThinkPad X200 & co with cxt5051 */ |
3250 | static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { | 3310 | static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { |
3251 | { 0x16, 0x042140ff }, /* HP (seq# overridden) */ | 3311 | { 0x16, 0x042140ff }, /* HP (seq# overridden) */ |
@@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = { | |||
3302 | .type = HDA_FIXUP_FUNC, | 3362 | .type = HDA_FIXUP_FUNC, |
3303 | .v.func = cxt5066_increase_mic_boost, | 3363 | .v.func = cxt5066_increase_mic_boost, |
3304 | }, | 3364 | }, |
3365 | [CXT_FIXUP_HEADPHONE_MIC_PIN] = { | ||
3366 | .type = HDA_FIXUP_PINS, | ||
3367 | .chained = true, | ||
3368 | .chain_id = CXT_FIXUP_HEADPHONE_MIC, | ||
3369 | .v.pins = (const struct hda_pintbl[]) { | ||
3370 | { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ | ||
3371 | { } | ||
3372 | } | ||
3373 | }, | ||
3374 | [CXT_FIXUP_HEADPHONE_MIC] = { | ||
3375 | .type = HDA_FIXUP_FUNC, | ||
3376 | .v.func = cxt_fixup_headphone_mic, | ||
3377 | }, | ||
3305 | }; | 3378 | }; |
3306 | 3379 | ||
3307 | static const struct snd_pci_quirk cxt5051_fixups[] = { | 3380 | static const struct snd_pci_quirk cxt5051_fixups[] = { |
@@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = { | |||
3311 | 3384 | ||
3312 | static const struct snd_pci_quirk cxt5066_fixups[] = { | 3385 | static const struct snd_pci_quirk cxt5066_fixups[] = { |
3313 | SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), | 3386 | SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), |
3387 | SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN), | ||
3314 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), | 3388 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), |
3315 | SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), | 3389 | SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), |
3316 | SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), | 3390 | SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), |
@@ -3395,7 +3469,8 @@ static int patch_conexant_auto(struct hda_codec *codec) | |||
3395 | 3469 | ||
3396 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); | 3470 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); |
3397 | 3471 | ||
3398 | err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); | 3472 | err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, |
3473 | spec->parse_flags); | ||
3399 | if (err < 0) | 3474 | if (err < 0) |
3400 | goto error; | 3475 | goto error; |
3401 | 3476 | ||
@@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec) | |||
3416 | codec->bus->allow_bus_reset = 1; | 3491 | codec->bus->allow_bus_reset = 1; |
3417 | } | 3492 | } |
3418 | 3493 | ||
3494 | snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); | ||
3495 | |||
3419 | return 0; | 3496 | return 0; |
3420 | 3497 | ||
3421 | error: | 3498 | error: |