/* 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;
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);
#ifdef CONFIG_SND_DEBUG
CXT5047_TEST,
#endif
+ CXT5047_AUTO,
CXT5047_MODELS
};
#ifdef CONFIG_SND_DEBUG
[CXT5047_TEST] = "test",
#endif
+ [CXT5047_AUTO] = "auto",
};
static const struct snd_pci_quirk cxt5047_cfg_tbl[] = {
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;
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;
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);
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
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)
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);
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;
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]);
}
}
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,
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 {
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,
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);