summaryrefslogtreecommitdiff
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c262
1 files changed, 213 insertions, 49 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 14829210ef0b..08d81b873022 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -4372,6 +4372,34 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
}
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+ int type)
+{
+ if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+ cfg->inputs[cfg->num_inputs].pin = nid;
+ cfg->inputs[cfg->num_inputs].type = type;
+ cfg->num_inputs++;
+ }
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+ int i, j;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ for (j = i + 1; j < cfg->num_inputs; j++) {
+ if (cfg->inputs[i].type > cfg->inputs[j].type) {
+ struct auto_pin_cfg_item tmp;
+ tmp = cfg->inputs[i];
+ cfg->inputs[i] = cfg->inputs[j];
+ cfg->inputs[j] = tmp;
+ }
+ }
+ }
+}
+
/*
* Parse all pin widgets and store the useful pin nids to cfg
*
@@ -4385,7 +4413,7 @@ static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
* output, i.e. to line_out_pins[0]. So, line_outs is always positive
* if any analog output exists.
*
- * The analog input pins are assigned to input_pins array.
+ * The analog input pins are assigned to inputs array.
* The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
* respectively.
*/
@@ -4398,6 +4426,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+ int i;
memset(cfg, 0, sizeof(*cfg));
@@ -4468,33 +4497,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
cfg->hp_outs++;
break;
- case AC_JACK_MIC_IN: {
- int preferred, alt;
- if (loc == AC_JACK_LOC_FRONT ||
- (loc & 0x30) == AC_JACK_LOC_INTERNAL) {
- preferred = AUTO_PIN_FRONT_MIC;
- alt = AUTO_PIN_MIC;
- } else {
- preferred = AUTO_PIN_MIC;
- alt = AUTO_PIN_FRONT_MIC;
- }
- if (!cfg->input_pins[preferred])
- cfg->input_pins[preferred] = nid;
- else if (!cfg->input_pins[alt])
- cfg->input_pins[alt] = nid;
+ case AC_JACK_MIC_IN:
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
break;
- }
case AC_JACK_LINE_IN:
- if (loc == AC_JACK_LOC_FRONT)
- cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
- else
- cfg->input_pins[AUTO_PIN_LINE] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
break;
case AC_JACK_CD:
- cfg->input_pins[AUTO_PIN_CD] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
break;
case AC_JACK_AUX:
- cfg->input_pins[AUTO_PIN_AUX] = nid;
+ add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
break;
case AC_JACK_SPDIF_OUT:
case AC_JACK_DIG_OTHER_OUT:
@@ -4539,6 +4552,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
memmove(sequences_hp + i, sequences_hp + i + 1,
sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
}
+ memset(cfg->hp_pins + cfg->hp_outs, 0,
+ sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
}
/* sort by sequence */
@@ -4549,21 +4564,6 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
cfg->hp_outs);
- /* if we have only one mic, make it AUTO_PIN_MIC */
- if (!cfg->input_pins[AUTO_PIN_MIC] &&
- cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
- cfg->input_pins[AUTO_PIN_MIC] =
- cfg->input_pins[AUTO_PIN_FRONT_MIC];
- cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
- }
- /* ditto for line-in */
- if (!cfg->input_pins[AUTO_PIN_LINE] &&
- cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
- cfg->input_pins[AUTO_PIN_LINE] =
- cfg->input_pins[AUTO_PIN_FRONT_LINE];
- cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
- }
-
/*
* FIX-UP: if no line-outs are detected, try to use speaker or HP pin
* as a primary output
@@ -4602,6 +4602,8 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
break;
}
+ sort_autocfg_input_pins(cfg);
+
/*
* debug prints of the parsed results
*/
@@ -4621,14 +4623,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
- " cd=0x%x, aux=0x%x\n",
- cfg->input_pins[AUTO_PIN_MIC],
- cfg->input_pins[AUTO_PIN_FRONT_MIC],
- cfg->input_pins[AUTO_PIN_LINE],
- cfg->input_pins[AUTO_PIN_FRONT_LINE],
- cfg->input_pins[AUTO_PIN_CD],
- cfg->input_pins[AUTO_PIN_AUX]);
+ snd_printd(" inputs:");
+ for (i = 0; i < cfg->num_inputs; i++) {
+ snd_printdd(" %s=0x%x",
+ hda_get_autocfg_input_label(codec, cfg, i),
+ cfg->inputs[i].pin);
+ }
+ snd_printd("\n");
if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
@@ -4636,11 +4637,174 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
}
EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
-/* labels for input pins */
-const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
+enum {
+ MIC_ATTR_INT,
+ MIC_ATTR_DOCK,
+ MIC_ATTR_NORMAL,
+ MIC_ATTR_FRONT,
+ MIC_ATTR_REAR,
};
-EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
+
+static int get_mic_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ if (get_defcfg_connect(def_conf) == AC_JACK_PORT_FIXED ||
+ (loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return MIC_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return MIC_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return MIC_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return MIC_ATTR_FRONT;
+ return MIC_ATTR_NORMAL;
+}
+
+enum {
+ LINE_ATTR_DOCK,
+ LINE_ATTR_NORMAL,
+};
+
+static int get_line_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ if ((loc & 0xf0) == AC_JACK_LOC_SEPARATE)
+ return LINE_ATTR_DOCK;
+ return LINE_ATTR_NORMAL;
+}
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+ int check_location)
+{
+ unsigned int def_conf;
+ static const char *mic_names[] = {
+ "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+ };
+ static const char *line_names[] = {
+ "Dock Line", "Line",
+ };
+
+ def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+ switch (get_defcfg_device(def_conf)) {
+ case AC_JACK_MIC_IN:
+ if (!check_location)
+ return "Mic";
+ return mic_names[get_mic_pin_attr(def_conf)];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ return line_names[get_line_pin_attr(def_conf)];
+ case AC_JACK_AUX:
+ return "Aux";
+ case AC_JACK_CD:
+ return "CD";
+ case AC_JACK_SPDIF_IN:
+ return "SPDIF In";
+ case AC_JACK_DIG_OTHER_IN:
+ return "Digital In";
+ default:
+ return "Misc";
+ }
+}
+EXPORT_SYMBOL_HDA(hda_get_input_pin_label);
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label. In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ unsigned int defc;
+ int i, attr, attr2;
+
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+ attr = get_mic_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= MIC_ATTR_NORMAL)
+ return 1;
+
+ attr = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+ attr2 = get_mic_pin_attr(defc);
+ if (attr2 >= MIC_ATTR_NORMAL) {
+ if (attr && attr != attr2)
+ return 1; /* different locations found */
+ attr = attr2;
+ }
+ }
+ return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input)
+{
+ int type = cfg->inputs[input].type;
+ int has_multiple_pins = 0;
+
+ if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+ (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+ has_multiple_pins = 1;
+ if (has_multiple_pins && type == AUTO_PIN_MIC)
+ has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+ has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/**
+ * snd_hda_add_imux_item - Add an item to input_mux
+ *
+ * When the same label is used already in the existing items, the number
+ * suffix is appended to the label. This label index number is stored
+ * to type_idx when non-NULL pointer is given.
+ */
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_idx)
+{
+ int i, label_idx = 0;
+ if (imux->num_items >= HDA_MAX_NUM_INPUTS) {
+ snd_printd(KERN_ERR "hda_codec: Too many imux items!\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < imux->num_items; i++) {
+ if (!strncmp(label, imux->items[i].label, strlen(label)))
+ label_idx++;
+ }
+ if (type_idx)
+ *type_idx = label_idx;
+ if (label_idx > 0)
+ snprintf(imux->items[imux->num_items].label,
+ sizeof(imux->items[imux->num_items].label),
+ "%s %d", label, label_idx);
+ else
+ strlcpy(imux->items[imux->num_items].label, label,
+ sizeof(imux->items[imux->num_items].label));
+ imux->items[imux->num_items].index = index;
+ imux->num_items++;
+ return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_imux_item);
#ifdef CONFIG_PM