ALSA: hda - Don't create multiple same volume/boost controls in Cxt auto-parser
[deliverable/linux.git] / sound / pci / hda / patch_conexant.c
index b575c9989f3ceb000a70041989bac5fb02066adf..ed983a0b0dc11b6251ffbb341959c9bbff10eb18 100644 (file)
@@ -108,8 +108,11 @@ struct conexant_spec {
        /* dynamic controls, init_verbs and input_mux */
        struct auto_pin_cfg autocfg;
        struct hda_input_mux private_imux;
+       int imux_cfg_idx[HDA_MAX_NUM_INPUTS]; /* corresponding autocfg.input */
+       hda_nid_t imux_boost_nid[HDA_MAX_NUM_INPUTS]; /* boost widget */
        hda_nid_t imux_adcs[HDA_MAX_NUM_INPUTS];
        hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS];
+       hda_nid_t private_adc_nids[HDA_MAX_NUM_INPUTS];
        hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
        struct pin_dac_pair dac_info[8];
        int dac_info_filled;
@@ -1103,8 +1106,10 @@ static int patch_cxt5045(struct hda_codec *codec)
        board_config = snd_hda_check_board_config(codec, CXT5045_MODELS,
                                                  cxt5045_models,
                                                  cxt5045_cfg_tbl);
+#if 0 /* use the old method just for safety */
        if (board_config < 0)
                board_config = CXT5045_AUTO;
+#endif
        if (board_config == CXT5045_AUTO)
                return patch_conexant_auto(codec);
 
@@ -1512,6 +1517,7 @@ enum {
 #ifdef CONFIG_SND_DEBUG
        CXT5047_TEST,
 #endif
+       CXT5047_AUTO,
        CXT5047_MODELS
 };
 
@@ -1522,6 +1528,7 @@ static const char * const cxt5047_models[CXT5047_MODELS] = {
 #ifdef CONFIG_SND_DEBUG
        [CXT5047_TEST]          = "test",
 #endif
+       [CXT5047_AUTO]          = "auto",
 };
 
 static const struct snd_pci_quirk cxt5047_cfg_tbl[] = {
@@ -1537,6 +1544,16 @@ static int patch_cxt5047(struct hda_codec *codec)
        struct conexant_spec *spec;
        int board_config;
 
+       board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
+                                                 cxt5047_models,
+                                                 cxt5047_cfg_tbl);
+#if 0 /* not enabled as default, as BIOS often broken for this codec */
+       if (board_config < 0)
+               board_config = CXT5047_AUTO;
+#endif
+       if (board_config == CXT5047_AUTO)
+               return patch_conexant_auto(codec);
+
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (!spec)
                return -ENOMEM;
@@ -1560,9 +1577,6 @@ static int patch_cxt5047(struct hda_codec *codec)
 
        codec->patch_ops = conexant_patch_ops;
 
-       board_config = snd_hda_check_board_config(codec, CXT5047_MODELS,
-                                                 cxt5047_models,
-                                                 cxt5047_cfg_tbl);
        switch (board_config) {
        case CXT5047_LAPTOP:
                spec->num_mixers = 2;
@@ -1962,8 +1976,10 @@ static int patch_cxt5051(struct hda_codec *codec)
        board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
                                                  cxt5051_models,
                                                  cxt5051_cfg_tbl);
+#if 0 /* use the old method just for safety */
        if (board_config < 0)
                board_config = CXT5051_AUTO;
+#endif
        if (board_config == CXT5051_AUTO)
                return patch_conexant_auto(codec);
 
@@ -3276,15 +3292,6 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
        return -1;
 }
 
-static int has_multi_connection(struct hda_codec *codec, hda_nid_t mux)
-{
-       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
-       int nums;
-
-       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
-       return nums > 1;
-}
-
 /* get an unassigned DAC from the given list.
  * Return the nid if found and reduce the DAC list, or return zero if
  * not found
@@ -3455,6 +3462,63 @@ static int cx_auto_mux_enum_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+/* look for the route the given pin from mux and return the index;
+ * if do_select is set, actually select the route.
+ */
+static int __select_input_connection(struct hda_codec *codec, hda_nid_t mux,
+                                    hda_nid_t pin, hda_nid_t *srcp,
+                                    bool do_select, int depth)
+{
+       hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+       int i, nums;
+
+       switch (get_wcaps_type(get_wcaps(codec, mux))) {
+       case AC_WID_AUD_IN:
+       case AC_WID_AUD_SEL:
+       case AC_WID_AUD_MIX:
+               break;
+       default:
+               return -1;
+       }
+
+       nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+       for (i = 0; i < nums; i++)
+               if (conn[i] == pin) {
+                       if (do_select)
+                               snd_hda_codec_write(codec, mux, 0,
+                                                   AC_VERB_SET_CONNECT_SEL, i);
+                       if (srcp)
+                               *srcp = mux;
+                       return i;
+               }
+       depth++;
+       if (depth == 2)
+               return -1;
+       for (i = 0; i < nums; i++) {
+               int ret  = __select_input_connection(codec, conn[i], pin, srcp,
+                                                    do_select, depth);
+               if (ret >= 0) {
+                       if (do_select)
+                               snd_hda_codec_write(codec, mux, 0,
+                                                   AC_VERB_SET_CONNECT_SEL, i);
+                       return i;
+               }
+       }
+       return -1;
+}
+
+static void select_input_connection(struct hda_codec *codec, hda_nid_t mux,
+                                  hda_nid_t pin)
+{
+       __select_input_connection(codec, mux, pin, NULL, true, 0);
+}
+
+static int get_input_connection(struct hda_codec *codec, hda_nid_t mux,
+                               hda_nid_t pin)
+{
+       return __select_input_connection(codec, mux, pin, NULL, false, 0);
+}
+
 static int cx_auto_mux_enum_update(struct hda_codec *codec,
                                   const struct hda_input_mux *imux,
                                   unsigned int idx)
@@ -3469,10 +3533,8 @@ static int cx_auto_mux_enum_update(struct hda_codec *codec,
        if (spec->cur_mux[0] == idx)
                return 0;
        adc = spec->imux_adcs[idx];
-       if (has_multi_connection(codec, adc))
-               snd_hda_codec_write(codec, adc, 0,
-                                   AC_VERB_SET_CONNECT_SEL,
-                                   imux->items[idx].index);
+       select_input_connection(codec, spec->imux_adcs[idx],
+                               spec->imux_pins[idx]);
        if (spec->cur_adc && spec->cur_adc != adc) {
                /* stream is running, let's swap the current ADC */
                __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
@@ -3584,11 +3646,13 @@ static void cx_auto_parse_input(struct hda_codec *codec)
        for (i = 0; i < cfg->num_inputs; i++) {
                for (j = 0; j < spec->num_adc_nids; j++) {
                        hda_nid_t adc = spec->adc_nids[j];
-                       int idx = get_connection_index(codec, adc,
-                                              cfg->inputs[i].pin);
+                       int idx = get_input_connection(codec, adc,
+                                                      cfg->inputs[i].pin);
                        if (idx >= 0) {
                                const char *label;
                                label = hda_get_autocfg_input_label(codec, cfg, i);
+                               spec->imux_cfg_idx[imux->num_items] = i;
+                               spec->imux_boost_nid[imux->num_items] = 0;
                                spec->imux_adcs[imux->num_items] = adc;
                                spec->imux_pins[imux->num_items] =
                                        cfg->inputs[i].pin;
@@ -3791,11 +3855,8 @@ static void cx_auto_init_input(struct hda_codec *codec)
                                    AC_USRSP_EN | CONEXANT_MIC_EVENT);
                cx_auto_automic(codec);
        } else {
-               for (i = 0; i < spec->num_adc_nids; i++) {
-                       snd_hda_codec_write(codec, spec->adc_nids[i], 0,
-                                           AC_VERB_SET_CONNECT_SEL,
-                                           spec->private_imux.items[0].index);
-               }
+               select_input_connection(codec, spec->imux_adcs[0],
+                                       spec->imux_pins[0]);
        }
 }
 
@@ -3924,7 +3985,7 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
 
        for (i = 0; i < spec->num_adc_nids; i++) {
                hda_nid_t adc_nid = spec->adc_nids[i];
-               int idx = get_connection_index(codec, adc_nid, nid);
+               int idx = get_input_connection(codec, adc_nid, nid);
                if (idx < 0)
                        continue;
                return cx_auto_add_volume_idx(codec, label, pfx,
@@ -3933,35 +3994,73 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
        return 0;
 }
 
+static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
+                                   const char *label, int cidx)
+{
+       struct conexant_spec *spec = codec->spec;
+       hda_nid_t mux, nid;
+       int i, con;
+
+       nid = spec->imux_pins[idx];
+       if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
+               return cx_auto_add_volume(codec, label, " Boost", cidx,
+                                         nid, HDA_INPUT);
+       con = __select_input_connection(codec, spec->imux_adcs[idx], nid, &mux,
+                                       false, 0);
+       if (con < 0)
+               return 0;
+       for (i = 0; i < idx; i++) {
+               if (spec->imux_boost_nid[i] == mux)
+                       return 0; /* already present */
+       }
+
+       if (get_wcaps(codec, mux) & AC_WCAP_OUT_AMP) {
+               spec->imux_boost_nid[idx] = mux;
+               return cx_auto_add_volume(codec, label, " Boost", 0,
+                                         mux, HDA_OUTPUT);
+       }
+       return 0;
+}
+
 static int cx_auto_build_input_controls(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
-       struct auto_pin_cfg *cfg = &spec->autocfg;
-       static const char *prev_label;
+       struct hda_input_mux *imux = &spec->private_imux;
+       const char *prev_label;
+       int input_conn[HDA_MAX_NUM_INPUTS];
        int i, err, cidx;
+       int multi_connection;
+
+       multi_connection = 0;
+       for (i = 0; i < imux->num_items; i++) {
+               cidx = get_input_connection(codec, spec->imux_adcs[i],
+                                           spec->imux_pins[i]);
+               input_conn[i] = (spec->imux_adcs[i] << 8) | cidx;
+               if (i > 0 && input_conn[i] != input_conn[0])
+                       multi_connection = 1;
+       }
 
        prev_label = NULL;
        cidx = 0;
-       for (i = 0; i < cfg->num_inputs; i++) {
-               hda_nid_t nid = cfg->inputs[i].pin;
+       for (i = 0; i < imux->num_items; i++) {
+               hda_nid_t nid = spec->imux_pins[i];
                const char *label;
-               int pin_amp = get_wcaps(codec, nid) & AC_WCAP_IN_AMP;
 
-               label = hda_get_autocfg_input_label(codec, cfg, i);
+               label = hda_get_autocfg_input_label(codec, &spec->autocfg,
+                                                   spec->imux_cfg_idx[i]);
                if (label == prev_label)
                        cidx++;
                else
                        cidx = 0;
                prev_label = label;
 
-               if (pin_amp) {
-                       err = cx_auto_add_volume(codec, label, " Boost", cidx,
-                                                nid, HDA_INPUT);
-                       if (err < 0)
-                               return err;
-               }
+               err = cx_auto_add_boost_volume(codec, i, label, cidx);
+               if (err < 0)
+                       return err;
 
-               if (cfg->num_inputs == 1) {
+               if (!multi_connection) {
+                       if (i > 0)
+                               continue;
                        err = cx_auto_add_capture_volume(codec, nid,
                                                         "Capture", "", cidx);
                } else {
@@ -3994,6 +4093,28 @@ static int cx_auto_build_controls(struct hda_codec *codec)
        return conexant_build_controls(codec);
 }
 
+static int cx_auto_search_adcs(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       hda_nid_t nid, end_nid;
+
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               unsigned int caps = get_wcaps(codec, nid);
+               if (get_wcaps_type(caps) != AC_WID_AUD_IN)
+                       continue;
+               if (caps & AC_WCAP_DIGITAL)
+                       continue;
+               if (snd_BUG_ON(spec->num_adc_nids >=
+                              ARRAY_SIZE(spec->private_adc_nids)))
+                       break;
+               spec->private_adc_nids[spec->num_adc_nids++] = nid;
+       }
+       spec->adc_nids = spec->private_adc_nids;
+       return 0;
+}
+
+
 static const struct hda_codec_ops cx_auto_patch_ops = {
        .build_controls = cx_auto_build_controls,
        .build_pcms = conexant_build_pcms,
@@ -4018,25 +4139,10 @@ static int patch_conexant_auto(struct hda_codec *codec)
        if (!spec)
                return -ENOMEM;
        codec->spec = spec;
-       switch (codec->vendor_id) {
-       case 0x14f15051:
-               codec->pin_amp_workaround = 1;
-               spec->adc_nids = cxt5051_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(cxt5051_adc_nids);
-               spec->capsrc_nids = spec->adc_nids;
-               break;
-       case 0x14f15045:
-               codec->pin_amp_workaround = 1;
-               spec->adc_nids = cxt5045_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(cxt5045_adc_nids);
-               spec->capsrc_nids = spec->adc_nids;
-               break;
-       default:
-               spec->adc_nids = cx_auto_adc_nids;
-               spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
-               spec->capsrc_nids = spec->adc_nids;
-               break;
-       }
+       codec->pin_amp_workaround = 1;
+       err = cx_auto_search_adcs(codec);
+       if (err < 0)
+               return err;
        err = cx_auto_parse_auto_config(codec);
        if (err < 0) {
                kfree(codec->spec);
This page took 0.042219 seconds and 5 git commands to generate.