diff options
Diffstat (limited to 'sound/pci/hda/patch_conexant.c')
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 165 |
1 files changed, 99 insertions, 66 deletions
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 76752d8ea733..0c8b5a1993ed 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -136,6 +136,8 @@ struct conexant_spec { | |||
136 | unsigned int thinkpad:1; | 136 | unsigned int thinkpad:1; |
137 | unsigned int hp_laptop:1; | 137 | unsigned int hp_laptop:1; |
138 | unsigned int asus:1; | 138 | unsigned int asus:1; |
139 | unsigned int pin_eapd_ctrls:1; | ||
140 | unsigned int single_adc_amp:1; | ||
139 | 141 | ||
140 | unsigned int adc_switching:1; | 142 | unsigned int adc_switching:1; |
141 | 143 | ||
@@ -1867,39 +1869,6 @@ static const struct hda_verb cxt5051_hp_dv6736_init_verbs[] = { | |||
1867 | { } /* end */ | 1869 | { } /* end */ |
1868 | }; | 1870 | }; |
1869 | 1871 | ||
1870 | static const struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { | ||
1871 | /* Line in, Mic */ | ||
1872 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, | ||
1873 | {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
1874 | {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, | ||
1875 | {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
1876 | {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
1877 | {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, | ||
1878 | /* SPK */ | ||
1879 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
1880 | {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
1881 | /* HP, Amp */ | ||
1882 | {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
1883 | {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
1884 | /* Docking HP */ | ||
1885 | {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
1886 | {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
1887 | /* DAC1 */ | ||
1888 | {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
1889 | /* Record selector: Internal mic */ | ||
1890 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, | ||
1891 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, | ||
1892 | {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, | ||
1893 | /* SPDIF route: PCM */ | ||
1894 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */ | ||
1895 | {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, | ||
1896 | /* EAPD */ | ||
1897 | {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ | ||
1898 | {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, | ||
1899 | {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, | ||
1900 | { } /* end */ | ||
1901 | }; | ||
1902 | |||
1903 | static const struct hda_verb cxt5051_f700_init_verbs[] = { | 1872 | static const struct hda_verb cxt5051_f700_init_verbs[] = { |
1904 | /* Line in, Mic */ | 1873 | /* Line in, Mic */ |
1905 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, | 1874 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, |
@@ -1968,7 +1937,6 @@ enum { | |||
1968 | CXT5051_LAPTOP, /* Laptops w/ EAPD support */ | 1937 | CXT5051_LAPTOP, /* Laptops w/ EAPD support */ |
1969 | CXT5051_HP, /* no docking */ | 1938 | CXT5051_HP, /* no docking */ |
1970 | CXT5051_HP_DV6736, /* HP without mic switch */ | 1939 | CXT5051_HP_DV6736, /* HP without mic switch */ |
1971 | CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */ | ||
1972 | CXT5051_F700, /* HP Compaq Presario F700 */ | 1940 | CXT5051_F700, /* HP Compaq Presario F700 */ |
1973 | CXT5051_TOSHIBA, /* Toshiba M300 & co */ | 1941 | CXT5051_TOSHIBA, /* Toshiba M300 & co */ |
1974 | CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */ | 1942 | CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */ |
@@ -1980,7 +1948,6 @@ static const char *const cxt5051_models[CXT5051_MODELS] = { | |||
1980 | [CXT5051_LAPTOP] = "laptop", | 1948 | [CXT5051_LAPTOP] = "laptop", |
1981 | [CXT5051_HP] = "hp", | 1949 | [CXT5051_HP] = "hp", |
1982 | [CXT5051_HP_DV6736] = "hp-dv6736", | 1950 | [CXT5051_HP_DV6736] = "hp-dv6736", |
1983 | [CXT5051_LENOVO_X200] = "lenovo-x200", | ||
1984 | [CXT5051_F700] = "hp-700", | 1951 | [CXT5051_F700] = "hp-700", |
1985 | [CXT5051_TOSHIBA] = "toshiba", | 1952 | [CXT5051_TOSHIBA] = "toshiba", |
1986 | [CXT5051_IDEAPAD] = "ideapad", | 1953 | [CXT5051_IDEAPAD] = "ideapad", |
@@ -1995,7 +1962,6 @@ static const struct snd_pci_quirk cxt5051_cfg_tbl[] = { | |||
1995 | SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", | 1962 | SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", |
1996 | CXT5051_LAPTOP), | 1963 | CXT5051_LAPTOP), |
1997 | SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), | 1964 | SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), |
1998 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), | ||
1999 | SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD), | 1965 | SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD), |
2000 | {} | 1966 | {} |
2001 | }; | 1967 | }; |
@@ -2053,13 +2019,6 @@ static int patch_cxt5051(struct hda_codec *codec) | |||
2053 | spec->mixers[0] = cxt5051_hp_dv6736_mixers; | 2019 | spec->mixers[0] = cxt5051_hp_dv6736_mixers; |
2054 | spec->auto_mic = 0; | 2020 | spec->auto_mic = 0; |
2055 | break; | 2021 | break; |
2056 | case CXT5051_LENOVO_X200: | ||
2057 | spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; | ||
2058 | /* Thinkpad X301 does not have S/PDIF wired and no ability | ||
2059 | to use a docking station. */ | ||
2060 | if (codec->subsystem_id == 0x17aa211f) | ||
2061 | spec->multiout.dig_out_nid = 0; | ||
2062 | break; | ||
2063 | case CXT5051_F700: | 2022 | case CXT5051_F700: |
2064 | spec->init_verbs[0] = cxt5051_f700_init_verbs; | 2023 | spec->init_verbs[0] = cxt5051_f700_init_verbs; |
2065 | spec->mixers[0] = cxt5051_f700_mixers; | 2024 | spec->mixers[0] = cxt5051_f700_mixers; |
@@ -3473,12 +3432,14 @@ static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, | |||
3473 | static void do_automute(struct hda_codec *codec, int num_pins, | 3432 | static void do_automute(struct hda_codec *codec, int num_pins, |
3474 | hda_nid_t *pins, bool on) | 3433 | hda_nid_t *pins, bool on) |
3475 | { | 3434 | { |
3435 | struct conexant_spec *spec = codec->spec; | ||
3476 | int i; | 3436 | int i; |
3477 | for (i = 0; i < num_pins; i++) | 3437 | for (i = 0; i < num_pins; i++) |
3478 | snd_hda_codec_write(codec, pins[i], 0, | 3438 | snd_hda_codec_write(codec, pins[i], 0, |
3479 | AC_VERB_SET_PIN_WIDGET_CONTROL, | 3439 | AC_VERB_SET_PIN_WIDGET_CONTROL, |
3480 | on ? PIN_OUT : 0); | 3440 | on ? PIN_OUT : 0); |
3481 | cx_auto_turn_eapd(codec, num_pins, pins, on); | 3441 | if (spec->pin_eapd_ctrls) |
3442 | cx_auto_turn_eapd(codec, num_pins, pins, on); | ||
3482 | } | 3443 | } |
3483 | 3444 | ||
3484 | static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) | 3445 | static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) |
@@ -3503,9 +3464,12 @@ static void cx_auto_update_speakers(struct hda_codec *codec) | |||
3503 | int on = 1; | 3464 | int on = 1; |
3504 | 3465 | ||
3505 | /* turn on HP EAPD when HP jacks are present */ | 3466 | /* turn on HP EAPD when HP jacks are present */ |
3506 | if (spec->auto_mute) | 3467 | if (spec->pin_eapd_ctrls) { |
3507 | on = spec->hp_present; | 3468 | if (spec->auto_mute) |
3508 | cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); | 3469 | on = spec->hp_present; |
3470 | cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); | ||
3471 | } | ||
3472 | |||
3509 | /* mute speakers in auto-mode if HP or LO jacks are plugged */ | 3473 | /* mute speakers in auto-mode if HP or LO jacks are plugged */ |
3510 | if (spec->auto_mute) | 3474 | if (spec->auto_mute) |
3511 | on = !(spec->hp_present || | 3475 | on = !(spec->hp_present || |
@@ -3932,20 +3896,10 @@ static void cx_auto_parse_beep(struct hda_codec *codec) | |||
3932 | #define cx_auto_parse_beep(codec) | 3896 | #define cx_auto_parse_beep(codec) |
3933 | #endif | 3897 | #endif |
3934 | 3898 | ||
3935 | static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) | 3899 | /* parse EAPDs */ |
3936 | { | ||
3937 | int i; | ||
3938 | for (i = 0; i < nums; i++) | ||
3939 | if (list[i] == nid) | ||
3940 | return true; | ||
3941 | return false; | ||
3942 | } | ||
3943 | |||
3944 | /* parse extra-EAPD that aren't assigned to any pins */ | ||
3945 | static void cx_auto_parse_eapd(struct hda_codec *codec) | 3900 | static void cx_auto_parse_eapd(struct hda_codec *codec) |
3946 | { | 3901 | { |
3947 | struct conexant_spec *spec = codec->spec; | 3902 | struct conexant_spec *spec = codec->spec; |
3948 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3949 | hda_nid_t nid, end_nid; | 3903 | hda_nid_t nid, end_nid; |
3950 | 3904 | ||
3951 | end_nid = codec->start_nid + codec->num_nodes; | 3905 | end_nid = codec->start_nid + codec->num_nodes; |
@@ -3954,14 +3908,18 @@ static void cx_auto_parse_eapd(struct hda_codec *codec) | |||
3954 | continue; | 3908 | continue; |
3955 | if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) | 3909 | if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) |
3956 | continue; | 3910 | continue; |
3957 | if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || | ||
3958 | found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || | ||
3959 | found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs)) | ||
3960 | continue; | ||
3961 | spec->eapds[spec->num_eapds++] = nid; | 3911 | spec->eapds[spec->num_eapds++] = nid; |
3962 | if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) | 3912 | if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) |
3963 | break; | 3913 | break; |
3964 | } | 3914 | } |
3915 | |||
3916 | /* NOTE: below is a wild guess; if we have more than two EAPDs, | ||
3917 | * it's a new chip, where EAPDs are supposed to be associated to | ||
3918 | * pins, and we can control EAPD per pin. | ||
3919 | * OTOH, if only one or two EAPDs are found, it's an old chip, | ||
3920 | * thus it might control over all pins. | ||
3921 | */ | ||
3922 | spec->pin_eapd_ctrls = spec->num_eapds > 2; | ||
3965 | } | 3923 | } |
3966 | 3924 | ||
3967 | static int cx_auto_parse_auto_config(struct hda_codec *codec) | 3925 | static int cx_auto_parse_auto_config(struct hda_codec *codec) |
@@ -4067,8 +4025,9 @@ static void cx_auto_init_output(struct hda_codec *codec) | |||
4067 | } | 4025 | } |
4068 | } | 4026 | } |
4069 | cx_auto_update_speakers(codec); | 4027 | cx_auto_update_speakers(codec); |
4070 | /* turn on/off extra EAPDs, too */ | 4028 | /* turn on all EAPDs if no individual EAPD control is available */ |
4071 | cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); | 4029 | if (!spec->pin_eapd_ctrls) |
4030 | cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); | ||
4072 | } | 4031 | } |
4073 | 4032 | ||
4074 | static void cx_auto_init_input(struct hda_codec *codec) | 4033 | static void cx_auto_init_input(struct hda_codec *codec) |
@@ -4255,6 +4214,8 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, | |||
4255 | int idx = get_input_connection(codec, adc_nid, nid); | 4214 | int idx = get_input_connection(codec, adc_nid, nid); |
4256 | if (idx < 0) | 4215 | if (idx < 0) |
4257 | continue; | 4216 | continue; |
4217 | if (spec->single_adc_amp) | ||
4218 | idx = 0; | ||
4258 | return cx_auto_add_volume_idx(codec, label, pfx, | 4219 | return cx_auto_add_volume_idx(codec, label, pfx, |
4259 | cidx, adc_nid, HDA_INPUT, idx); | 4220 | cidx, adc_nid, HDA_INPUT, idx); |
4260 | } | 4221 | } |
@@ -4295,14 +4256,21 @@ static int cx_auto_build_input_controls(struct hda_codec *codec) | |||
4295 | struct hda_input_mux *imux = &spec->private_imux; | 4256 | struct hda_input_mux *imux = &spec->private_imux; |
4296 | const char *prev_label; | 4257 | const char *prev_label; |
4297 | int input_conn[HDA_MAX_NUM_INPUTS]; | 4258 | int input_conn[HDA_MAX_NUM_INPUTS]; |
4298 | int i, err, cidx; | 4259 | int i, j, err, cidx; |
4299 | int multi_connection; | 4260 | int multi_connection; |
4300 | 4261 | ||
4262 | if (!imux->num_items) | ||
4263 | return 0; | ||
4264 | |||
4301 | multi_connection = 0; | 4265 | multi_connection = 0; |
4302 | for (i = 0; i < imux->num_items; i++) { | 4266 | for (i = 0; i < imux->num_items; i++) { |
4303 | cidx = get_input_connection(codec, spec->imux_info[i].adc, | 4267 | cidx = get_input_connection(codec, spec->imux_info[i].adc, |
4304 | spec->imux_info[i].pin); | 4268 | spec->imux_info[i].pin); |
4305 | input_conn[i] = (spec->imux_info[i].adc << 8) | cidx; | 4269 | if (cidx < 0) |
4270 | continue; | ||
4271 | input_conn[i] = spec->imux_info[i].adc; | ||
4272 | if (!spec->single_adc_amp) | ||
4273 | input_conn[i] |= cidx << 8; | ||
4306 | if (i > 0 && input_conn[i] != input_conn[0]) | 4274 | if (i > 0 && input_conn[i] != input_conn[0]) |
4307 | multi_connection = 1; | 4275 | multi_connection = 1; |
4308 | } | 4276 | } |
@@ -4331,6 +4299,15 @@ static int cx_auto_build_input_controls(struct hda_codec *codec) | |||
4331 | err = cx_auto_add_capture_volume(codec, nid, | 4299 | err = cx_auto_add_capture_volume(codec, nid, |
4332 | "Capture", "", cidx); | 4300 | "Capture", "", cidx); |
4333 | } else { | 4301 | } else { |
4302 | bool dup_found = false; | ||
4303 | for (j = 0; j < i; j++) { | ||
4304 | if (input_conn[j] == input_conn[i]) { | ||
4305 | dup_found = true; | ||
4306 | break; | ||
4307 | } | ||
4308 | } | ||
4309 | if (dup_found) | ||
4310 | continue; | ||
4334 | err = cx_auto_add_capture_volume(codec, nid, | 4311 | err = cx_auto_add_capture_volume(codec, nid, |
4335 | label, " Capture", cidx); | 4312 | label, " Capture", cidx); |
4336 | } | 4313 | } |
@@ -4394,6 +4371,53 @@ static const struct hda_codec_ops cx_auto_patch_ops = { | |||
4394 | .reboot_notify = snd_hda_shutup_pins, | 4371 | .reboot_notify = snd_hda_shutup_pins, |
4395 | }; | 4372 | }; |
4396 | 4373 | ||
4374 | /* | ||
4375 | * pin fix-up | ||
4376 | */ | ||
4377 | struct cxt_pincfg { | ||
4378 | hda_nid_t nid; | ||
4379 | u32 val; | ||
4380 | }; | ||
4381 | |||
4382 | static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg) | ||
4383 | { | ||
4384 | for (; cfg->nid; cfg++) | ||
4385 | snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); | ||
4386 | |||
4387 | } | ||
4388 | |||
4389 | static void apply_pin_fixup(struct hda_codec *codec, | ||
4390 | const struct snd_pci_quirk *quirk, | ||
4391 | const struct cxt_pincfg **table) | ||
4392 | { | ||
4393 | quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); | ||
4394 | if (quirk) { | ||
4395 | snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", | ||
4396 | quirk->name); | ||
4397 | apply_pincfg(codec, table[quirk->value]); | ||
4398 | } | ||
4399 | } | ||
4400 | |||
4401 | enum { | ||
4402 | CXT_PINCFG_LENOVO_X200, | ||
4403 | }; | ||
4404 | |||
4405 | static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { | ||
4406 | { 0x16, 0x042140ff }, /* HP (seq# overridden) */ | ||
4407 | { 0x17, 0x21a11000 }, /* dock-mic */ | ||
4408 | { 0x19, 0x2121103f }, /* dock-HP */ | ||
4409 | {} | ||
4410 | }; | ||
4411 | |||
4412 | static const struct cxt_pincfg *cxt_pincfg_tbl[] = { | ||
4413 | [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, | ||
4414 | }; | ||
4415 | |||
4416 | static const struct snd_pci_quirk cxt_fixups[] = { | ||
4417 | SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), | ||
4418 | {} | ||
4419 | }; | ||
4420 | |||
4397 | static int patch_conexant_auto(struct hda_codec *codec) | 4421 | static int patch_conexant_auto(struct hda_codec *codec) |
4398 | { | 4422 | { |
4399 | struct conexant_spec *spec; | 4423 | struct conexant_spec *spec; |
@@ -4407,6 +4431,15 @@ static int patch_conexant_auto(struct hda_codec *codec) | |||
4407 | return -ENOMEM; | 4431 | return -ENOMEM; |
4408 | codec->spec = spec; | 4432 | codec->spec = spec; |
4409 | codec->pin_amp_workaround = 1; | 4433 | codec->pin_amp_workaround = 1; |
4434 | |||
4435 | switch (codec->vendor_id) { | ||
4436 | case 0x14f15045: | ||
4437 | spec->single_adc_amp = 1; | ||
4438 | break; | ||
4439 | } | ||
4440 | |||
4441 | apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); | ||
4442 | |||
4410 | err = cx_auto_search_adcs(codec); | 4443 | err = cx_auto_search_adcs(codec); |
4411 | if (err < 0) | 4444 | if (err < 0) |
4412 | return err; | 4445 | return err; |