X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=blobdiff_plain;f=sound%2Fpci%2Fhda%2Fpatch_conexant.c;h=0de21193a2b025ba58363c0e2a509ec4545b495d;hp=7696d05b935629f354c81866fdb89e43222f415f;hb=ecb40a3adadfabb13c7a1548944ff6a0442f1c01;hpb=efa62e1355f0495f37f1296754b8880947c8da72 diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 7696d05b9356..707225147111 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -136,6 +137,8 @@ struct conexant_spec { unsigned int thinkpad:1; unsigned int hp_laptop:1; unsigned int asus:1; + unsigned int pin_eapd_ctrls:1; + unsigned int single_adc_amp:1; unsigned int adc_switching:1; @@ -1117,8 +1120,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = { static const struct snd_pci_quirk cxt5045_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530), - SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series", - CXT5045_LAPTOP_HPSENSE), SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE), SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ), SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE), @@ -1867,39 +1868,6 @@ static const struct hda_verb cxt5051_hp_dv6736_init_verbs[] = { { } /* end */ }; -static const struct hda_verb cxt5051_lenovo_x200_init_verbs[] = { - /* Line in, Mic */ - {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, - {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, - {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, - /* SPK */ - {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, - {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* HP, Amp */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* Docking HP */ - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, - {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, - /* DAC1 */ - {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, - /* Record selector: Internal mic */ - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, - {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44}, - {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44}, - /* SPDIF route: PCM */ - {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* needed for W500 Advanced Mini Dock 250410 */ - {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0}, - /* EAPD */ - {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ - {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT}, - { } /* end */ -}; - static const struct hda_verb cxt5051_f700_init_verbs[] = { /* Line in, Mic */ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03}, @@ -1968,7 +1936,6 @@ enum { CXT5051_LAPTOP, /* Laptops w/ EAPD support */ CXT5051_HP, /* no docking */ CXT5051_HP_DV6736, /* HP without mic switch */ - CXT5051_LENOVO_X200, /* Lenovo X200 laptop, also used for Advanced Mini Dock 250410 */ CXT5051_F700, /* HP Compaq Presario F700 */ CXT5051_TOSHIBA, /* Toshiba M300 & co */ CXT5051_IDEAPAD, /* Lenovo IdeaPad Y430 */ @@ -1980,7 +1947,6 @@ static const char *const cxt5051_models[CXT5051_MODELS] = { [CXT5051_LAPTOP] = "laptop", [CXT5051_HP] = "hp", [CXT5051_HP_DV6736] = "hp-dv6736", - [CXT5051_LENOVO_X200] = "lenovo-x200", [CXT5051_F700] = "hp-700", [CXT5051_TOSHIBA] = "toshiba", [CXT5051_IDEAPAD] = "ideapad", @@ -1995,7 +1961,6 @@ static const struct snd_pci_quirk cxt5051_cfg_tbl[] = { SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5051_LAPTOP), SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP), - SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo IdeaPad", CXT5051_IDEAPAD), {} }; @@ -2053,13 +2018,6 @@ static int patch_cxt5051(struct hda_codec *codec) spec->mixers[0] = cxt5051_hp_dv6736_mixers; spec->auto_mic = 0; break; - case CXT5051_LENOVO_X200: - spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs; - /* Thinkpad X301 does not have S/PDIF wired and no ability - to use a docking station. */ - if (codec->subsystem_id == 0x17aa211f) - spec->multiout.dig_out_nid = 0; - break; case CXT5051_F700: spec->init_verbs[0] = cxt5051_f700_init_verbs; spec->mixers[0] = cxt5051_f700_mixers; @@ -3102,7 +3060,6 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1993, "Asus U50F", CXT5066_ASUS), SND_PCI_QUIRK(0x1179, 0xff1e, "Toshiba Satellite C650D", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba Satellite P500-PSPGSC-01800T", CXT5066_OLPC_XO_1_5), - SND_PCI_QUIRK(0x1179, 0xffe0, "Toshiba Satellite Pro T130-15F", CXT5066_OLPC_XO_1_5), SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board", CXT5066_LAPTOP), SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5), @@ -3110,6 +3067,7 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo Thinkpad", CXT5066_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO), SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS), @@ -3472,12 +3430,14 @@ static void cx_auto_turn_eapd(struct hda_codec *codec, int num_pins, static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, bool on) { + struct conexant_spec *spec = codec->spec; int i; for (i = 0; i < num_pins; i++) snd_hda_codec_write(codec, pins[i], 0, AC_VERB_SET_PIN_WIDGET_CONTROL, on ? PIN_OUT : 0); - cx_auto_turn_eapd(codec, num_pins, pins, on); + if (spec->pin_eapd_ctrls) + cx_auto_turn_eapd(codec, num_pins, pins, on); } static int detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) @@ -3502,9 +3462,12 @@ static void cx_auto_update_speakers(struct hda_codec *codec) int on = 1; /* turn on HP EAPD when HP jacks are present */ - if (spec->auto_mute) - on = spec->hp_present; - cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); + if (spec->pin_eapd_ctrls) { + if (spec->auto_mute) + on = spec->hp_present; + cx_auto_turn_eapd(codec, cfg->hp_outs, cfg->hp_pins, on); + } + /* mute speakers in auto-mode if HP or LO jacks are plugged */ if (spec->auto_mute) on = !(spec->hp_present || @@ -3931,20 +3894,10 @@ static void cx_auto_parse_beep(struct hda_codec *codec) #define cx_auto_parse_beep(codec) #endif -static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return true; - return false; -} - -/* parse extra-EAPD that aren't assigned to any pins */ +/* parse EAPDs */ static void cx_auto_parse_eapd(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t nid, end_nid; end_nid = codec->start_nid + codec->num_nodes; @@ -3953,14 +3906,18 @@ static void cx_auto_parse_eapd(struct hda_codec *codec) continue; if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) continue; - if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) || - found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) || - found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs)) - continue; spec->eapds[spec->num_eapds++] = nid; if (spec->num_eapds >= ARRAY_SIZE(spec->eapds)) break; } + + /* NOTE: below is a wild guess; if we have more than two EAPDs, + * it's a new chip, where EAPDs are supposed to be associated to + * pins, and we can control EAPD per pin. + * OTOH, if only one or two EAPDs are found, it's an old chip, + * thus it might control over all pins. + */ + spec->pin_eapd_ctrls = spec->num_eapds > 2; } static int cx_auto_parse_auto_config(struct hda_codec *codec) @@ -4066,8 +4023,9 @@ static void cx_auto_init_output(struct hda_codec *codec) } } cx_auto_update_speakers(codec); - /* turn on/off extra EAPDs, too */ - cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); + /* turn on all EAPDs if no individual EAPD control is available */ + if (!spec->pin_eapd_ctrls) + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); } static void cx_auto_init_input(struct hda_codec *codec) @@ -4254,6 +4212,8 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid, int idx = get_input_connection(codec, adc_nid, nid); if (idx < 0) continue; + if (spec->single_adc_amp) + idx = 0; return cx_auto_add_volume_idx(codec, label, pfx, cidx, adc_nid, HDA_INPUT, idx); } @@ -4294,14 +4254,21 @@ static int cx_auto_build_input_controls(struct hda_codec *codec) struct hda_input_mux *imux = &spec->private_imux; const char *prev_label; int input_conn[HDA_MAX_NUM_INPUTS]; - int i, err, cidx; + int i, j, err, cidx; int multi_connection; + if (!imux->num_items) + return 0; + multi_connection = 0; for (i = 0; i < imux->num_items; i++) { cidx = get_input_connection(codec, spec->imux_info[i].adc, spec->imux_info[i].pin); - input_conn[i] = (spec->imux_info[i].adc << 8) | cidx; + if (cidx < 0) + continue; + input_conn[i] = spec->imux_info[i].adc; + if (!spec->single_adc_amp) + input_conn[i] |= cidx << 8; if (i > 0 && input_conn[i] != input_conn[0]) multi_connection = 1; } @@ -4330,6 +4297,15 @@ static int cx_auto_build_input_controls(struct hda_codec *codec) err = cx_auto_add_capture_volume(codec, nid, "Capture", "", cidx); } else { + bool dup_found = false; + for (j = 0; j < i; j++) { + if (input_conn[j] == input_conn[i]) { + dup_found = true; + break; + } + } + if (dup_found) + continue; err = cx_auto_add_capture_volume(codec, nid, label, " Capture", cidx); } @@ -4393,6 +4369,53 @@ static const struct hda_codec_ops cx_auto_patch_ops = { .reboot_notify = snd_hda_shutup_pins, }; +/* + * pin fix-up + */ +struct cxt_pincfg { + hda_nid_t nid; + u32 val; +}; + +static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg) +{ + for (; cfg->nid; cfg++) + snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val); + +} + +static void apply_pin_fixup(struct hda_codec *codec, + const struct snd_pci_quirk *quirk, + const struct cxt_pincfg **table) +{ + quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk); + if (quirk) { + snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n", + quirk->name); + apply_pincfg(codec, table[quirk->value]); + } +} + +enum { + CXT_PINCFG_LENOVO_X200, +}; + +static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = { + { 0x16, 0x042140ff }, /* HP (seq# overridden) */ + { 0x17, 0x21a11000 }, /* dock-mic */ + { 0x19, 0x2121103f }, /* dock-HP */ + {} +}; + +static const struct cxt_pincfg *cxt_pincfg_tbl[] = { + [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200, +}; + +static const struct snd_pci_quirk cxt_fixups[] = { + SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200), + {} +}; + static int patch_conexant_auto(struct hda_codec *codec) { struct conexant_spec *spec; @@ -4406,6 +4429,15 @@ static int patch_conexant_auto(struct hda_codec *codec) return -ENOMEM; codec->spec = spec; codec->pin_amp_workaround = 1; + + switch (codec->vendor_id) { + case 0x14f15045: + spec->single_adc_amp = 1; + break; + } + + apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl); + err = cx_auto_search_adcs(codec); if (err < 0) return err;