summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-10-25 10:40:05 +0200
committerTakashi Iwai <tiwai@suse.de>2010-10-25 10:40:05 +0200
commit506ecbca71d07fa327dd986be1682e90885678ee (patch)
treeeba675a57b0201c8f30436d08cab03c5dcd235ba
parentaa5c14d5c0d3e4c587db4a1b220b9c86415c538f (diff)
parent0e7adbe263f89ea2ef15b5af5e80a812b2a85025 (diff)
Merge branch 'topic/hda' into for-linus
-rw-r--r--Documentation/sound/alsa/HD-Audio.txt8
-rw-r--r--include/sound/tlv.h4
-rw-r--r--sound/pci/hda/Kconfig39
-rw-r--r--sound/pci/hda/Makefile15
-rw-r--r--sound/pci/hda/hda_codec.c271
-rw-r--r--sound/pci/hda/hda_codec.h13
-rw-r--r--sound/pci/hda/hda_eld.c7
-rw-r--r--sound/pci/hda/hda_generic.c41
-rw-r--r--sound/pci/hda/hda_intel.c101
-rw-r--r--sound/pci/hda/hda_local.h51
-rw-r--r--sound/pci/hda/patch_analog.c48
-rw-r--r--sound/pci/hda/patch_atihdmi.c224
-rw-r--r--sound/pci/hda/patch_ca0110.c10
-rw-r--r--sound/pci/hda/patch_cirrus.c94
-rw-r--r--sound/pci/hda/patch_conexant.c651
-rw-r--r--sound/pci/hda/patch_hdmi.c797
-rw-r--r--sound/pci/hda/patch_intelhdmi.c220
-rw-r--r--sound/pci/hda/patch_nvhdmi.c608
-rw-r--r--sound/pci/hda/patch_realtek.c908
-rw-r--r--sound/pci/hda/patch_sigmatel.c379
-rw-r--r--sound/pci/hda/patch_via.c587
21 files changed, 2837 insertions, 2239 deletions
diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt
index 278cc2122ea..c82beb00763 100644
--- a/Documentation/sound/alsa/HD-Audio.txt
+++ b/Documentation/sound/alsa/HD-Audio.txt
@@ -57,9 +57,11 @@ dead. However, this detection isn't perfect on some devices. In such
a case, you can change the default method via `position_fix` option.
`position_fix=1` means to use LPIB method explicitly.
-`position_fix=2` means to use the position-buffer. 0 is the default
-value, the automatic check and fallback to LPIB as described in the
-above. If you get a problem of repeated sounds, this option might
+`position_fix=2` means to use the position-buffer.
+`position_fix=3` means to use a combination of both methods, needed
+for some VIA and ATI controllers. 0 is the default value for all other
+controllers, the automatic check and fallback to LPIB as described in
+the above. If you get a problem of repeated sounds, this option might
help.
In addition to that, every controller is known to be broken regarding
diff --git a/include/sound/tlv.h b/include/sound/tlv.h
index 9fd5b19ccf5..7067e2dfb0b 100644
--- a/include/sound/tlv.h
+++ b/include/sound/tlv.h
@@ -38,9 +38,11 @@
#define SNDRV_CTL_TLVT_DB_MINMAX 4 /* dB scale with min/max */
#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5 /* dB scale with min/max with mute */
+#define TLV_DB_SCALE_MASK 0xffff
+#define TLV_DB_SCALE_MUTE 0x10000
#define TLV_DB_SCALE_ITEM(min, step, mute) \
SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int), \
- (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0)
+ (min), ((step) & TLV_DB_SCALE_MASK) | ((mute) ? TLV_DB_SCALE_MUTE : 0)
#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 9194c3c1d04..0ea5cc60ac7 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -119,47 +119,20 @@ config SND_HDA_CODEC_VIA
snd-hda-codec-via.
This module is automatically loaded at probing.
-config SND_HDA_CODEC_ATIHDMI
- bool "Build ATI HDMI HD-audio codec support"
- default y
- help
- Say Y here to include ATI HDMI HD-audio codec support in
- snd-hda-intel driver, such as ATI RS600 HDMI.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-atihdmi.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_NVHDMI
- bool "Build NVIDIA HDMI HD-audio codec support"
- default y
- help
- Say Y here to include NVIDIA HDMI HD-audio codec support in
- snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
-
- When the HD-audio driver is built as a module, the codec
- support code is also built as another module,
- snd-hda-codec-nvhdmi.
- This module is automatically loaded at probing.
-
-config SND_HDA_CODEC_INTELHDMI
- bool "Build INTEL HDMI HD-audio codec support"
+config SND_HDA_CODEC_HDMI
+ bool "Build HDMI/DisplayPort HD-audio codec support"
select SND_DYNAMIC_MINORS
default y
help
- Say Y here to include INTEL HDMI HD-audio codec support in
- snd-hda-intel driver, such as Eaglelake integrated HDMI.
+ Say Y here to include HDMI and DisplayPort HD-audio codec
+ support in snd-hda-intel driver. This includes all AMD/ATI,
+ Intel and Nvidia HDMI/DisplayPort codecs.
When the HD-audio driver is built as a module, the codec
support code is also built as another module,
- snd-hda-codec-intelhdmi.
+ snd-hda-codec-hdmi.
This module is automatically loaded at probing.
-config SND_HDA_ELD
- def_bool y
- depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
-
config SND_HDA_CODEC_CIRRUS
bool "Build Cirrus Logic codec support"
depends on SND_HDA_INTEL
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 24bc195b02d..17ef3658f34 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -3,7 +3,6 @@ snd-hda-intel-objs := hda_intel.o
snd-hda-codec-y := hda_codec.o
snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
@@ -12,13 +11,11 @@ snd-hda-codec-cmedia-objs := patch_cmedia.o
snd-hda-codec-analog-objs := patch_analog.o
snd-hda-codec-idt-objs := patch_sigmatel.o
snd-hda-codec-si3054-objs := patch_si3054.o
-snd-hda-codec-atihdmi-objs := patch_atihdmi.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
-snd-hda-codec-nvhdmi-objs := patch_nvhdmi.o
-snd-hda-codec-intelhdmi-objs := patch_intelhdmi.o
+snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# common driver
obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
@@ -39,9 +36,6 @@ endif
ifdef CONFIG_SND_HDA_CODEC_SI3054
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
endif
-ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
-endif
ifdef CONFIG_SND_HDA_CODEC_CIRRUS
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
endif
@@ -54,11 +48,8 @@ endif
ifdef CONFIG_SND_HDA_CODEC_VIA
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
endif
-ifdef CONFIG_SND_HDA_CODEC_NVHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
-endif
-ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
-obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
+ifdef CONFIG_SND_HDA_CODEC_HDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-hdmi.o
endif
# this must be the last entry after codec drivers;
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 14829210ef0..644e3f14f8c 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1216,6 +1216,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
struct hda_codec *c;
struct hda_cvt_setup *p;
unsigned int oldval, newval;
+ int type;
int i;
if (!nid)
@@ -1254,10 +1255,12 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
p->dirty = 0;
/* make other inactive cvts with the same stream-tag dirty */
+ type = get_wcaps_type(get_wcaps(codec, nid));
list_for_each_entry(c, &codec->bus->codec_list, list) {
for (i = 0; i < c->cvt_setups.used; i++) {
p = snd_array_elem(&c->cvt_setups, i);
- if (!p->active && p->stream_tag == stream_tag)
+ if (!p->active && p->stream_tag == stream_tag &&
+ get_wcaps_type(get_wcaps(codec, p->nid)) == type)
p->dirty = 1;
}
}
@@ -1281,6 +1284,9 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
if (!nid)
return;
+ if (codec->no_sticky_stream)
+ do_now = 1;
+
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid);
p = get_hda_cvt_setup(codec, nid);
if (p) {
@@ -1831,6 +1837,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
hda_nid_t nid = get_amp_nid(kcontrol);
int dir = get_amp_direction(kcontrol);
unsigned int ofs = get_amp_offset(kcontrol);
+ bool min_mute = get_amp_min_mute(kcontrol);
u32 caps, val1, val2;
if (size < 4 * sizeof(unsigned int))
@@ -1841,6 +1848,8 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
val1 = -((caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT);
val1 += ofs;
val1 = ((int)val1) * ((int)val2);
+ if (min_mute)
+ val2 |= TLV_DB_SCALE_MUTE;
if (put_user(SNDRV_CTL_TLVT_DB_SCALE, _tlv))
return -EFAULT;
if (put_user(2 * sizeof(unsigned int), _tlv + 1))
@@ -2228,10 +2237,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, nid);
-#endif
+ hda_call_check_power_status(codec, nid);
snd_hda_power_down(codec);
return change;
}
@@ -4372,6 +4378,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 +4419,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 +4432,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 +4503,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 +4558,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 +4570,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 +4608,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 +4629,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 +4643,165 @@ 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"
-};
-EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+ unsigned int loc = get_defcfg_location(def_conf);
+ unsigned int conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
+ return INPUT_PIN_ATTR_UNUSED;
+ /* Windows may claim the internal mic to be BOTH, too */
+ if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+ return INPUT_PIN_ATTR_INT;
+ if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+ return INPUT_PIN_ATTR_DOCK;
+ if (loc == AC_JACK_LOC_REAR)
+ return INPUT_PIN_ATTR_REAR;
+ if (loc == AC_JACK_LOC_FRONT)
+ return INPUT_PIN_ATTR_FRONT;
+ return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+
+/**
+ * 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",
+ };
+ int attr;
+
+ 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";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ return mic_names[attr - 1];
+ case AC_JACK_LINE_IN:
+ if (!check_location)
+ return "Line";
+ attr = snd_hda_get_input_pin_attr(def_conf);
+ if (!attr)
+ return "None";
+ if (attr == INPUT_PIN_ATTR_DOCK)
+ return "Dock Line";
+ return "Line";
+ 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 = snd_hda_get_input_pin_attr(defc);
+ /* for internal or docking mics, we need locations */
+ if (attr <= INPUT_PIN_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 = snd_hda_get_input_pin_attr(defc);
+ if (attr2 >= INPUT_PIN_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
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 62c70224010..fdf8d44f8b6 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -850,6 +850,7 @@ struct hda_codec {
unsigned int pin_amp_workaround:1; /* pin out-amp takes index
* (e.g. Conexant codecs)
*/
+ unsigned int no_sticky_stream:1; /* no sticky-PCM stream assignment */
unsigned int pins_shutup:1; /* pins are shut up */
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -989,6 +990,18 @@ int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static inline
+int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (codec->patch_ops.check_power_status)
+ return codec->patch_ops.check_power_status(codec, nid);
+ return 0;
+}
+#else
+#define hda_call_check_power_status(codec, nid) 0
+#endif
+
/*
* get widget information
*/
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 26c3ade7358..cb0c23a6b47 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -332,7 +332,6 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
AC_DIPSIZE_ELD_BUF);
}
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
int snd_hdmi_get_eld(struct hdmi_eld *eld,
struct hda_codec *codec, hda_nid_t nid)
@@ -368,7 +367,6 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
kfree(buf);
return ret;
}
-EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
static void hdmi_show_short_audio_desc(struct cea_sad *a)
{
@@ -407,7 +405,6 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
}
buf[j] = '\0'; /* necessary when j == 0 */
}
-EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
void snd_hdmi_show_eld(struct hdmi_eld *e)
{
@@ -426,7 +423,6 @@ void snd_hdmi_show_eld(struct hdmi_eld *e)
for (i = 0; i < e->sad_count; i++)
hdmi_show_short_audio_desc(e->sad + i);
}
-EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
#ifdef CONFIG_PROC_FS
@@ -585,7 +581,6 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
return 0;
}
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
{
@@ -594,7 +589,6 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
eld->proc_entry = NULL;
}
}
-EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
#endif /* CONFIG_PROC_FS */
@@ -645,4 +639,3 @@ void hdmi_eld_update_pcm_info(struct hdmi_eld *eld, struct hda_pcm_stream *pcm,
pcm->channels_max = min(pcm->channels_max, codec_pars->channels_max);
pcm->maxbps = min(pcm->maxbps, codec_pars->maxbps);
}
-EXPORT_SYMBOL_HDA(hdmi_eld_update_pcm_info);
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 5ea21285ee1..fb0582f8d72 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -61,7 +61,6 @@ struct hda_gspec {
struct hda_gnode *cap_vol_node; /* Node for capture volume */
unsigned int cur_cap_src; /* current capture source */
struct hda_input_mux input_mux;
- char cap_labels[HDA_MAX_NUM_INPUTS][16];
unsigned int def_amp_in_caps;
unsigned int def_amp_out_caps;
@@ -506,11 +505,10 @@ static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
* returns 0 if not found, 1 if found, or a negative error code.
*/
static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
- struct hda_gnode *node)
+ struct hda_gnode *node, int idx)
{
int i, err;
unsigned int pinctl;
- char *label;
const char *type;
if (node->checked)
@@ -523,7 +521,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
child = hda_get_node(spec, node->conn_list[i]);
if (! child)
continue;
- err = parse_adc_sub_nodes(codec, spec, child);
+ err = parse_adc_sub_nodes(codec, spec, child, idx);
if (err < 0)
return err;
if (err > 0) {
@@ -564,9 +562,7 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 0;
type = "Input";
}
- label = spec->cap_labels[spec->input_mux.num_items];
- strcpy(label, type);
- spec->input_mux.items[spec->input_mux.num_items].label = label;
+ snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL);
/* unmute the PIN external input */
unmute_input(codec, node, 0); /* index = 0? */
@@ -577,29 +573,6 @@ static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec,
return 1; /* found */
}
-/* add a capture source element */
-static void add_cap_src(struct hda_gspec *spec, int idx)
-{
- struct hda_input_mux_item *csrc;
- char *buf;
- int num, ocap;
-
- num = spec->input_mux.num_items;
- csrc = &spec->input_mux.items[num];
- buf = spec->cap_labels[num];
- for (ocap = 0; ocap < num; ocap++) {
- if (! strcmp(buf, spec->cap_labels[ocap])) {
- /* same label already exists,
- * put the index number to be unique
- */
- sprintf(buf, "%s %d", spec->cap_labels[ocap], num);
- break;
- }
- }
- csrc->index = idx;
- spec->input_mux.num_items++;
-}
-
/*
* parse input
*/
@@ -624,22 +597,18 @@ static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node)
for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type == AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node);
+ err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0)
return err;
- else if (err > 0)
- add_cap_src(spec, i);
}
}
/* ... then check the rests, more complicated connections */
for (i = 0; i < adc_node->nconns; i++) {
node = hda_get_node(spec, adc_node->conn_list[i]);
if (node && node->type != AC_WID_PIN) {
- err = parse_adc_sub_nodes(codec, spec, node);
+ err = parse_adc_sub_nodes(codec, spec, node, i);
if (err < 0)
return err;
- else if (err > 0)
- add_cap_src(spec, i);
}
}
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 34940a07905..21aa9b0e28f 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -78,8 +78,8 @@ MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
-MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
- "(0 = auto, 1 = none, 2 = POSBUF).");
+MODULE_PARM_DESC(position_fix, "DMA pointer read method."
+ "(0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
@@ -305,6 +305,7 @@ enum {
POS_FIX_AUTO,
POS_FIX_LPIB,
POS_FIX_POSBUF,
+ POS_FIX_VIACOMBO,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
@@ -433,7 +434,6 @@ struct azx {
unsigned int polling_mode :1;
unsigned int msi :1;
unsigned int irq_pending_warned :1;
- unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
unsigned int probing :1; /* codec probing phase */
/* for debugging */
@@ -458,6 +458,7 @@ enum {
AZX_DRIVER_ULI,
AZX_DRIVER_NVIDIA,
AZX_DRIVER_TERA,
+ AZX_DRIVER_CTX,
AZX_DRIVER_GENERIC,
AZX_NUM_DRIVERS, /* keep this as last entry */
};
@@ -473,6 +474,7 @@ static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ULI] = "HDA ULI M5461",
[AZX_DRIVER_NVIDIA] = "HDA NVidia",
[AZX_DRIVER_TERA] = "HDA Teradici",
+ [AZX_DRIVER_CTX] = "HDA Creative",
[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
};
@@ -563,7 +565,10 @@ static void azx_init_cmd_io(struct azx *chip)
/* reset the rirb hw write pointer */
azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
/* set N=1, get RIRB response interrupt for new entry */
- azx_writew(chip, RINTCNT, 1);
+ if (chip->driver_type == AZX_DRIVER_CTX)
+ azx_writew(chip, RINTCNT, 0xc0);
+ else
+ azx_writew(chip, RINTCNT, 1);
/* enable rirb dma and response irq */
azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
spin_unlock_irq(&chip->reg_lock);
@@ -1136,8 +1141,11 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
/* clear rirb int */
status = azx_readb(chip, RIRBSTS);
if (status & RIRB_INT_MASK) {
- if (status & RIRB_INT_RESPONSE)
+ if (status & RIRB_INT_RESPONSE) {
+ if (chip->driver_type == AZX_DRIVER_CTX)
+ udelay(80);
azx_update_rirb(chip);
+ }
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
}
@@ -1309,11 +1317,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
azx_sd_writel(azx_dev, SD_BDLPU, upper_32_bits(azx_dev->bdl.addr));
/* enable the position buffer */
- if (chip->position_fix[0] == POS_FIX_POSBUF ||
- chip->position_fix[0] == POS_FIX_AUTO ||
- chip->position_fix[1] == POS_FIX_POSBUF ||
- chip->position_fix[1] == POS_FIX_AUTO ||
- chip->via_dmapos_patch) {
+ if (chip->position_fix[0] != POS_FIX_LPIB ||
+ chip->position_fix[1] != POS_FIX_LPIB) {
if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
azx_writel(chip, DPLBASE,
(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
@@ -1647,7 +1652,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
struct azx_dev *azx_dev = get_azx_dev(substream);
struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream];
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int bufsize, period_bytes, format_val;
+ unsigned int bufsize, period_bytes, format_val, stream_tag;
int err;
azx_stream_reset(chip, azx_dev);
@@ -1689,7 +1694,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
else
azx_dev->fifo_size = 0;
- return snd_hda_codec_prepare(apcm->codec, hinfo, azx_dev->stream_tag,
+ stream_tag = azx_dev->stream_tag;
+ /* CA-IBG chips need the playback stream starting from 1 */
+ if (chip->driver_type == AZX_DRIVER_CTX &&
+ stream_tag > chip->capture_streams)
+ stream_tag -= chip->capture_streams;
+ return snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag,
azx_dev->format_val, substream);
}
@@ -1852,20 +1862,21 @@ static unsigned int azx_get_position(struct azx *chip,
struct azx_dev *azx_dev)
{
unsigned int pos;
+ int stream = azx_dev->substream->stream;
- if (chip->via_dmapos_patch)
+ switch (chip->position_fix[stream]) {
+ case POS_FIX_LPIB:
+ /* read LPIB */
+ pos = azx_sd_readl(azx_dev, SD_LPIB);
+ break;
+ case POS_FIX_VIACOMBO:
pos = azx_via_get_position(chip, azx_dev);
- else {
- int stream = azx_dev->substream->stream;
- if (chip->position_fix[stream] == POS_FIX_POSBUF ||
- chip->position_fix[stream] == POS_FIX_AUTO) {
- /* use the position buffer */
- pos = le32_to_cpu(*azx_dev->posbuf);
- } else {
- /* read LPIB */
- pos = azx_sd_readl(azx_dev, SD_LPIB);
- }
+ break;
+ default:
+ /* use the position buffer */
+ pos = le32_to_cpu(*azx_dev->posbuf);
}
+
if (pos >= azx_dev->bufsize)
pos = 0;
return pos;
@@ -2313,19 +2324,10 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
switch (fix) {
case POS_FIX_LPIB:
case POS_FIX_POSBUF:
+ case POS_FIX_VIACOMBO:
return fix;
}
- /* Check VIA/ATI HD Audio Controller exist */
- switch (chip->driver_type) {
- case AZX_DRIVER_VIA:
- case AZX_DRIVER_ATI:
- chip->via_dmapos_patch = 1;
- /* Use link position directly, avoid any transfer problem. */
- return POS_FIX_LPIB;
- }
- chip->via_dmapos_patch = 0;
-
q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
if (q) {
printk(KERN_INFO
@@ -2334,6 +2336,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
q->value, q->subvendor, q->subdevice);
return q->value;
}
+
+ /* Check VIA/ATI HD Audio Controller exist */
+ switch (chip->driver_type) {
+ case AZX_DRIVER_VIA:
+ case AZX_DRIVER_ATI:
+ /* Use link position directly, avoid any transfer problem. */
+ return POS_FIX_VIACOMBO;
+ }
+
return POS_FIX_AUTO;
}
@@ -2735,25 +2746,17 @@ static void __devexit azx_remove(struct pci_dev *pci)
/* PCI IDs */
static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
- /* ICH 6..10 */
- { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x269a), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x284b), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x2911), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x293e), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x293f), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x3a3e), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
- /* PCH */
- { PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
- { PCI_DEVICE(0x8086, 0x3b57), .driver_data = AZX_DRIVER_ICH },
/* CPT */
{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
/* PBG */
{ PCI_DEVICE(0x8086, 0x1d20), .driver_data = AZX_DRIVER_PCH },
/* SCH */
{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
+ /* Generic Intel */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
+ .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+ .class_mask = 0xffffff,
+ .driver_data = AZX_DRIVER_ICH },
/* ATI SB 450/600 */
{ PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI },
{ PCI_DEVICE(0x1002, 0x4383), .driver_data = AZX_DRIVER_ATI },
@@ -2794,11 +2797,13 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
.class_mask = 0xffffff,
- .driver_data = AZX_DRIVER_GENERIC },
+ .driver_data = AZX_DRIVER_CTX },
#else
/* this entry seems still valid -- i.e. without emu20kx chip */
- { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
+ { PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_CTX },
#endif
+ /* Vortex86MX */
+ { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
.class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 28ab4aead48..46bbefe2e4a 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -38,10 +38,11 @@
*/
#define HDA_COMPOSE_AMP_VAL_OFS(nid,chs,idx,dir,ofs) \
((nid) | ((chs)<<16) | ((dir)<<18) | ((idx)<<19) | ((ofs)<<23))
+#define HDA_AMP_VAL_MIN_MUTE (1<<29)
#define HDA_COMPOSE_AMP_VAL(nid,chs,idx,dir) \
HDA_COMPOSE_AMP_VAL_OFS(nid, chs, idx, dir, 0)
/* mono volume with index (index=0,1,...) (channel=1,2) */
-#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+#define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, dir, flags) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
.subdevice = HDA_SUBDEV_AMP_FLAG, \
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
@@ -51,16 +52,20 @@
.get = snd_hda_mixer_amp_volume_get, \
.put = snd_hda_mixer_amp_volume_put, \
.tlv = { .c = snd_hda_mixer_amp_tlv }, \
- .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+ .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, dir) | flags }
/* stereo volume with index */
#define HDA_CODEC_VOLUME_IDX(xname, xcidx, nid, xindex, direction) \
- HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction)
+ HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, 3, xindex, direction, 0)
/* mono volume */
#define HDA_CODEC_VOLUME_MONO(xname, nid, channel, xindex, direction) \
- HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+ HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, channel, xindex, direction, 0)
/* stereo volume */
#define HDA_CODEC_VOLUME(xname, nid, xindex, direction) \
HDA_CODEC_VOLUME_MONO(xname, nid, 3, xindex, direction)
+/* stereo volume with min=mute */
+#define HDA_CODEC_VOLUME_MIN_MUTE(xname, nid, xindex, direction) \
+ HDA_CODEC_VOLUME_MONO_IDX(xname, 0, nid, 3, xindex, direction, \
+ HDA_AMP_VAL_MIN_MUTE)
/* mono mute switch with index (index=0,1,...) (channel=1,2) */
#define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
@@ -215,7 +220,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid);
*/
#define HDA_MAX_NUM_INPUTS 16
struct hda_input_mux_item {
- const char *label;
+ char label[32];
unsigned int index;
};
struct hda_input_mux {
@@ -366,9 +371,7 @@ struct hda_bus_unsolicited {
enum {
AUTO_PIN_MIC,
- AUTO_PIN_FRONT_MIC,
- AUTO_PIN_LINE,
- AUTO_PIN_FRONT_LINE,
+ AUTO_PIN_LINE_IN,
AUTO_PIN_CD,
AUTO_PIN_AUX,
AUTO_PIN_LAST
@@ -380,9 +383,33 @@ enum {
AUTO_PIN_HP_OUT
};
-extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
-
#define AUTO_CFG_MAX_OUTS 5
+#define AUTO_CFG_MAX_INS 8
+
+struct auto_pin_cfg_item {
+ hda_nid_t pin;
+ int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_input_pin_label(struct hda_codec *codec, hda_nid_t pin,
+ int check_location);
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ int input);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+ int index, int *type_index_ret);
+
+enum {
+ INPUT_PIN_ATTR_UNUSED, /* pin not connected */
+ INPUT_PIN_ATTR_INT, /* internal mic/line-in */
+ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */
+ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */
+ INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */
+ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
struct auto_pin_cfg {
int line_outs;
@@ -393,7 +420,8 @@ struct auto_pin_cfg {
int hp_outs;
int line_out_type; /* AUTO_PIN_XXX_OUT */
hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
- hda_nid_t input_pins[AUTO_PIN_LAST];
+ int num_inputs;
+ struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
int dig_outs;
hda_nid_t dig_out_pins[2];
hda_nid_t dig_in_pin;
@@ -558,6 +586,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_direction(kc) (((kc)->private_value >> 18) & 0x1)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
+#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
/*
* CEA Short Audio Descriptor data
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 10bbbaf6ebc..f7ff3f7ccb8 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1276,6 +1276,7 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.no_share_stream = 1;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -1463,6 +1464,7 @@ static int patch_ad1983(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -1917,6 +1919,7 @@ static int patch_ad1981(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -2880,7 +2883,7 @@ static int ad1988_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
/* create input playback/capture controls for the given pin */
static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
- const char *ctlname, int boost)
+ const char *ctlname, int ctlidx, int boost)
{
char name[32];
int err, idx;
@@ -2909,25 +2912,27 @@ static int new_analog_input(struct ad198x_spec *spec, hda_nid_t pin,
}
/* create playback/capture controls for input pins */
-static int ad1988_auto_create_analog_input_ctls(struct ad198x_spec *spec,
+static int ad1988_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
+ struct ad198x_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
- int i, err;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- err = new_analog_input(spec, cfg->input_pins[i],
- auto_pin_cfg_labels[i],
- i <= AUTO_PIN_FRONT_MIC);
+ int i, err, type, type_idx;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ const char *label;
+ type = cfg->inputs[i].type;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label,
+ ad1988_pin_to_adc_idx(cfg->inputs[i].pin),
+ &type_idx);
+ err = new_analog_input(spec, cfg->inputs[i].pin,
+ label, type_idx,
+ type == AUTO_PIN_MIC);
if (err < 0)
return err;
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = ad1988_pin_to_adc_idx(cfg->input_pins[i]);
- imux->num_items++;
}
- imux->items[imux->num_items].label = "Mix";
- imux->items[imux->num_items].index = 9;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Mix", 9, NULL);
if ((err = add_control(spec, AD_CTL_WIDGET_VOL,
"Analog Mix Playback Volume",
@@ -2994,12 +2999,11 @@ static void ad1988_auto_init_extra_out(struct hda_codec *codec)
static void ad1988_auto_init_analog_input(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int i, idx;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (! nid)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
switch (nid) {
case 0x15: /* port-C */
snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3009,7 +3013,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
break;
}
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
- i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+ i == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
if (nid != AD1988_PIN_CD_NID)
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
@@ -3040,7 +3044,7 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
"Speaker")) < 0 ||
(err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
"Headphone")) < 0 ||
- (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+ (err = ad1988_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
return err;
spec->multiout.max_channels = spec->multiout.num_dacs * 2;
@@ -3235,6 +3239,7 @@ static int patch_ad1988(struct hda_codec *codec)
spec->vmaster_nid = 0x04;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -3449,6 +3454,7 @@ static int patch_ad1884(struct hda_codec *codec)
codec->patch_ops = ad198x_patch_ops;
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -4422,6 +4428,7 @@ static int patch_ad1884a(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
@@ -4761,6 +4768,7 @@ static int patch_ad1882(struct hda_codec *codec)
}
codec->no_trigger_sense = 1;
+ codec->no_sticky_stream = 1;
return 0;
}
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
deleted file mode 100644
index fb684f00156..00000000000
--- a/sound/pci/hda/patch_atihdmi.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for ATI HDMI codecs
- *
- * Copyright (c) 2006 ATI Technologies Inc.
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-struct atihdmi_spec {
- struct hda_multi_out multiout;
-
- struct hda_pcm pcm_rec;
-};
-
-#define CVT_NID 0x02 /* audio converter */
-#define PIN_NID 0x03 /* HDMI output pin */
-
-static struct hda_verb atihdmi_basic_init[] = {
- /* enable digital output on pin widget */
- { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
- {} /* terminator */
-};
-
-/*
- * Controls
- */
-static int atihdmi_build_controls(struct hda_codec *codec)
-{
- struct atihdmi_spec *spec = codec->spec;
- int err;
-
- err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-static int atihdmi_init(struct hda_codec *codec)
-{
- snd_hda_sequence_write(codec, atihdmi_basic_init);
- /* SI codec requires to unmute the pin */
- if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, PIN_NID, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- return 0;
-}
-
-/*
- * Digital out
- */
-static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct atihdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct atihdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct atihdmi_spec *spec = codec->spec;
- int chans = substream->runtime->channels;
- int i, err;
-
- err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
- if (err < 0)
- return err;
- snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT,
- chans - 1);
- /* FIXME: XXX */
- for (i = 0; i < chans; i++) {
- snd_hda_codec_write(codec, CVT_NID, 0,
- AC_VERB_SET_HDMI_CHAN_SLOT,
- (i << 4) | i);
- }
- return 0;
-}
-
-static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = CVT_NID, /* NID to query formats and rates and setup streams */
- .ops = {
- .open = atihdmi_dig_playback_pcm_open,
- .close = atihdmi_dig_playback_pcm_close,
- .prepare = atihdmi_dig_playback_pcm_prepare
- },
-};
-
-static int atihdmi_build_pcms(struct hda_codec *codec)
-{
- struct atihdmi_spec *spec = codec->spec;
- struct hda_pcm *info = &spec->pcm_rec;
- unsigned int chans;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "ATI HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback;
-
- /* FIXME: we must check ELD and change the PCM parameters dynamically
- */
- chans = get_wcaps(codec, CVT_NID);
- chans = get_wcaps_channels(chans);
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
-
- return 0;
-}
-
-static void atihdmi_free(struct hda_codec *codec)
-{
- kfree(codec->spec);
-}
-
-static struct hda_codec_ops atihdmi_patch_ops = {
- .build_controls = atihdmi_build_controls,
- .build_pcms = atihdmi_build_pcms,
- .init = atihdmi_init,
- .free = atihdmi_free,
-};
-
-static int patch_atihdmi(struct hda_codec *codec)
-{
- struct atihdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- /* NID for copying analog to digital,
- * seems to be unused in pure-digital
- * case.
- */
- spec->multiout.dig_out_nid = CVT_NID;
-
- codec->patch_ops = atihdmi_patch_ops;
-
- return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
- { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
- { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
- { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
- { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_atihdmi },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
-
-static struct hda_codec_preset_list atihdmi_list = {
- .preset = snd_hda_preset_atihdmi,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_atihdmi_init(void)
-{
- return snd_hda_add_codec_preset(&atihdmi_list);
-}
-
-static void __exit patch_atihdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&atihdmi_list);
-}
-
-module_init(patch_atihdmi_init)
-module_exit(patch_atihdmi_exit)
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
index af478019088..46c8bf48c31 100644
--- a/sound/pci/hda/patch_ca0110.c
+++ b/sound/pci/hda/patch_ca0110.c
@@ -468,13 +468,13 @@ static void parse_input(struct hda_codec *codec)
spec->dig_in = nid;
continue;
}
- for (j = 0; j < AUTO_PIN_LAST; j++)
- if (cfg->input_pins[j] == pin)
+ for (j = 0; j < cfg->num_inputs; j++)
+ if (cfg->inputs[j].pin == pin)
break;
- if (j >= AUTO_PIN_LAST)
+ if (j >= cfg->num_inputs)
continue;
spec->input_pins[n] = pin;
- spec->input_labels[n] = auto_pin_cfg_labels[j];
+ spec->input_labels[n] = hda_get_input_pin_label(codec, pin, 1);
spec->adcs[n] = nid;
n++;
}
@@ -489,7 +489,7 @@ static void parse_digital(struct hda_codec *codec)
if (cfg->dig_outs &&
snd_hda_get_connections(codec, cfg->dig_out_pins[0],
&spec->dig_out, 1) == 1)
- spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
+ spec->multiout.dig_out_nid = spec->dig_out;
}
static int ca0110_parse_auto_config(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 488fd9ade1b..460fb2ef7e3 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -65,6 +65,7 @@ struct cs_spec {
/* available models */
enum {
+ CS420X_MBP53,
CS420X_MBP55,
CS420X_IMAC27,
CS420X_AUTO,
@@ -329,12 +330,12 @@ static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
{
struct cs_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t pin = cfg->input_pins[idx];
+ hda_nid_t pin = cfg->inputs[idx].pin;
unsigned int val = snd_hda_query_pin_caps(codec, pin);
if (!(val & AC_PINCAP_PRES_DETECT))
return 0;
val = snd_hda_codec_get_pincfg(codec, pin);
- return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX);
+ return (snd_hda_get_input_pin_attr(val) != INPUT_PIN_ATTR_INT);
}
static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
@@ -424,10 +425,8 @@ static int parse_input(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t pin = cfg->input_pins[i];
- if (!pin)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t pin = cfg->inputs[i].pin;
spec->input_idx[spec->num_inputs] = i;
spec->capsrc_idx[i] = spec->num_inputs++;
spec->cur_input = i;
@@ -438,16 +437,17 @@ static int parse_input(struct hda_codec *codec)
/* check whether the automatic mic switch is available */
if (spec->num_inputs == 2 &&
- spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
- if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
- if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ cfg->inputs[0].type == AUTO_PIN_MIC &&
+ cfg->inputs[1].type == AUTO_PIN_MIC) {
+ if (is_ext_mic(codec, cfg->inputs[0].pin)) {
+ if (!is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1;
- spec->automic_idx = AUTO_PIN_FRONT_MIC;
+ spec->automic_idx = 0;
}
} else {
- if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+ if (is_ext_mic(codec, cfg->inputs[1].pin)) {
spec->mic_detect = 1;
- spec->automic_idx = AUTO_PIN_MIC;
+ spec->automic_idx = 1;
}
}
}
@@ -674,6 +674,7 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct cs_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int idx;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
@@ -682,7 +683,8 @@ static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
if (uinfo->value.enumerated.item >= spec->num_inputs)
uinfo->value.enumerated.item = spec->num_inputs - 1;
idx = spec->input_idx[uinfo->value.enumerated.item];
- strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]);
+ strcpy(uinfo->value.enumerated.name,
+ hda_get_input_pin_label(codec, cfg->inputs[idx].pin, 1));
return 0;
}
@@ -740,6 +742,27 @@ static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
return bind;
}
+/* add a (input-boost) volume control to the given input pin */
+static int add_input_volume_control(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg,
+ int item)
+{
+ hda_nid_t pin = cfg->inputs[item].pin;
+ u32 caps;
+ const char *label;
+ struct snd_kcontrol *kctl;
+
+ if (!(get_wcaps(codec, pin) & AC_WCAP_IN_AMP))
+ return 0;
+ caps = query_amp_caps(codec, pin, HDA_INPUT);
+ caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ if (caps <= 1)
+ return 0;
+ label = hda_get_autocfg_input_label(codec, cfg, item);
+ return add_volume(codec, label, 0,
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT), 1, &kctl);
+}
+
static int build_input(struct hda_codec *codec)
{
struct cs_spec *spec = codec->spec;
@@ -779,6 +802,12 @@ static int build_input(struct hda_codec *codec)
return err;
}
+ for (i = 0; i < spec->num_inputs; i++) {
+ err = add_input_volume_control(codec, &spec->autocfg, i);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -838,7 +867,8 @@ static void cs_automute(struct hda_codec *codec)
AC_VERB_SET_PIN_WIDGET_CONTROL,
hp_present ? 0 : PIN_OUT);
}
- if (spec->board_config == CS420X_MBP55 ||
+ if (spec->board_config == CS420X_MBP53 ||
+ spec->board_config == CS420X_MBP55 ||
spec->board_config == CS420X_IMAC27) {
unsigned int gpio = hp_present ? 0x02 : 0x08;
snd_hda_codec_write(codec, 0x01, 0,
@@ -853,15 +883,12 @@ static void cs_automic(struct hda_codec *codec)
hda_nid_t nid;
unsigned int present;
- nid = cfg->input_pins[spec->automic_idx];
+ nid = cfg->inputs[spec->automic_idx].pin;
present = snd_hda_jack_detect(codec, nid);
if (present)
change_cur_input(codec, spec->automic_idx, 0);
- else {
- unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
- AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
- change_cur_input(codec, imic, 0);
- }
+ else
+ change_cur_input(codec, !spec->automic_idx, 0);
}
/*
@@ -918,14 +945,14 @@ static void init_input(struct hda_codec *codec)
unsigned int coef;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
+ for (i = 0; i < cfg->num_inputs; i++) {
unsigned int ctl;
- hda_nid_t pin = cfg->input_pins[i];
- if (!pin || !spec->adc_nid[i])
+ hda_nid_t pin = cfg->inputs[i].pin;
+ if (!spec->adc_nid[i])
continue;
/* set appropriate pin control and mute first */
ctl = PIN_IN;
- if (i <= AUTO_PIN_FRONT_MIC) {
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
unsigned int caps = snd_hda_query_pin_caps(codec, pin);
caps >>= AC_PINCAP_VREF_SHIFT;
if (caps & AC_PINCAP_VREF_80)
@@ -1130,6 +1157,7 @@ static int cs_parse_auto_config(struct hda_codec *codec)
}
static const char *cs420x_models[CS420X_MODELS] = {
+ [CS420X_MBP53] = "mbp53",
[CS420X_MBP55] = "mbp55",
[CS420X_IMAC27] = "imac27",
[CS420X_AUTO] = "auto",
@@ -1137,7 +1165,9 @@ static const char *cs420x_models[CS420X_MODELS] = {
static struct snd_pci_quirk cs420x_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x10de, 0x0ac0, "MacBookPro 5,3", CS420X_MBP53),
SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+ SND_PCI_QUIRK(0x10de, 0xcb89, "MacBookPro 7,1", CS420X_MBP55),
SND_PCI_QUIRK(0x8086, 0x7270, "IMac 27 Inch", CS420X_IMAC27),
{} /* terminator */
};
@@ -1147,6 +1177,20 @@ struct cs_pincfg {
u32 val;
};
+static struct cs_pincfg mbp53_pincfgs[] = {
+ { 0x09, 0x012b4050 },
+ { 0x0a, 0x90100141 },
+ { 0x0b, 0x90100140 },
+ { 0x0c, 0x018b3020 },
+ { 0x0d, 0x90a00110 },
+ { 0x0e, 0x400000f0 },
+ { 0x0f, 0x01cbe030 },
+ { 0x10, 0x014be060 },
+ { 0x12, 0x400000f0 },
+ { 0x15, 0x400000f0 },
+ {} /* terminator */
+};
+
static struct cs_pincfg mbp55_pincfgs[] = {
{ 0x09, 0x012b4030 },
{ 0x0a, 0x90100121 },
@@ -1176,6 +1220,7 @@ static struct cs_pincfg imac27_pincfgs[] = {
};
static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
+ [CS420X_MBP53] = mbp53_pincfgs,
[CS420X_MBP55] = mbp55_pincfgs,
[CS420X_IMAC27] = imac27_pincfgs,
};
@@ -1208,6 +1253,7 @@ static int patch_cs420x(struct hda_codec *codec)
switch (spec->board_config) {
case CS420X_IMAC27:
+ case CS420X_MBP53:
case CS420X_MBP55:
/* GPIO1 = headphones */
/* GPIO3 = speakers */
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 972e7c453b3..6361f752b5f 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -57,6 +57,12 @@ struct conexant_jack {
};
+struct pin_dac_pair {
+ hda_nid_t pin;
+ hda_nid_t dac;
+ int type;
+};
+
struct conexant_spec {
struct snd_kcontrol_new *mixers[5];
@@ -77,6 +83,7 @@ struct conexant_spec {
unsigned int cur_eapd;
unsigned int hp_present;
unsigned int auto_mic;
+ int auto_mic_ext; /* autocfg.inputs[] index for ext mic */
unsigned int need_dac_fix;
/* capture */
@@ -110,9 +117,12 @@ struct conexant_spec {
struct auto_pin_cfg autocfg;
struct hda_input_mux private_imux;
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+ struct pin_dac_pair dac_info[8];
+ int dac_info_filled;
- unsigned int dell_automute;
unsigned int port_d_mode;
+ unsigned int auto_mute:1; /* used in auto-parser */
+ unsigned int dell_automute:1;
unsigned int dell_vostro:1;
unsigned int ideapad:1;
unsigned int thinkpad:1;
@@ -3065,7 +3075,7 @@ enum {
CXT5066_LAPTOP, /* Laptops w/ EAPD support */
CXT5066_DELL_LAPTOP, /* Dell Laptop */
CXT5066_OLPC_XO_1_5, /* OLPC XO 1.5 */
- CXT5066_DELL_VOSTO, /* Dell Vostro 1015i */
+ CXT5066_DELL_VOSTRO, /* Dell Vostro 1015i */
CXT5066_IDEAPAD, /* Lenovo IdeaPad U150 */
CXT5066_THINKPAD, /* Lenovo ThinkPad T410s, others? */
CXT5066_HP_LAPTOP, /* HP Laptop */
@@ -3076,25 +3086,26 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
[CXT5066_LAPTOP] = "laptop",
[CXT5066_DELL_LAPTOP] = "dell-laptop",
[CXT5066_OLPC_XO_1_5] = "olpc-xo-1_5",
- [CXT5066_DELL_VOSTO] = "dell-vostro",
+ [CXT5066_DELL_VOSTRO] = "dell-vostro",
[CXT5066_IDEAPAD] = "ideapad",
[CXT5066_THINKPAD] = "thinkpad",
[CXT5066_HP_LAPTOP] = "hp-laptop",
};
static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
- SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
- CXT5066_LAPTOP),
+ SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
+ SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
CXT5066_DELL_LAPTOP),
- SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
- SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTO),
- SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
+ SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP),
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),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21b2, "Thinkpad X100e", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x21b3, "Thinkpad Edge 13 (197)", CXT5066_IDEAPAD),
@@ -3196,7 +3207,7 @@ static int patch_cxt5066(struct hda_codec *codec)
spec->capture_prepare = cxt5066_olpc_capture_prepare;
spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
break;
- case CXT5066_DELL_VOSTO:
+ case CXT5066_DELL_VOSTRO:
codec->patch_ops.init = cxt5066_init;
codec->patch_ops.unsol_event = cxt5066_vostro_event;
spec->init_verbs[0] = cxt5066_init_verbs_vostro;
@@ -3254,6 +3265,604 @@ static int patch_cxt5066(struct hda_codec *codec)
}
/*
+ * Automatic parser for CX20641 & co
+ */
+
+static hda_nid_t cx_auto_adc_nids[] = { 0x14 };
+
+/* get the connection index of @nid in the widget @mux */
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+ hda_nid_t nid)
+{
+ hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+ int i, nums;
+
+ nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+ for (i = 0; i < nums; i++)
+ if (conn[i] == nid)
+ return i;
+ return -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
+ */
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t *dacs, int *num_dacs)
+{
+ int i, nums = *num_dacs;
+ hda_nid_t ret = 0;
+
+ for (i = 0; i < nums; i++) {
+ if (get_connection_index(codec, pin, dacs[i]) >= 0) {
+ ret = dacs[i];
+ break;
+ }
+ }
+ if (!ret)
+ return 0;
+ if (--nums > 0)
+ memmove(dacs, dacs + 1, nums * sizeof(hda_nid_t));
+ *num_dacs = nums;
+ return ret;
+}
+
+#define MAX_AUTO_DACS 5
+
+/* fill analog DAC list from the widget tree */
+static int fill_cx_auto_dacs(struct hda_codec *codec, hda_nid_t *dacs)
+{
+ hda_nid_t nid, end_nid;
+ int nums = 0;
+
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
+ unsigned int wcaps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(wcaps);
+ if (type == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) {
+ dacs[nums++] = nid;
+ if (nums >= MAX_AUTO_DACS)
+ break;
+ }
+ }
+ return nums;
+}
+
+/* fill pin_dac_pair list from the pin and dac list */
+static int fill_dacs_for_pins(struct hda_codec *codec, hda_nid_t *pins,
+ int num_pins, hda_nid_t *dacs, int *rest,
+ struct pin_dac_pair *filled, int type)
+{
+ int i, nums;
+
+ nums = 0;
+ for (i = 0; i < num_pins; i++) {
+ filled[nums].pin = pins[i];
+ filled[nums].type = type;
+ filled[nums].dac = get_unassigned_dac(codec, pins[i], dacs, rest);
+ nums++;
+ }
+ return nums;
+}
+
+/* parse analog output paths */
+static void cx_auto_parse_output(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t dacs[MAX_AUTO_DACS];
+ int i, j, nums, rest;
+
+ rest = fill_cx_auto_dacs(codec, dacs);
+ /* parse all analog output pins */
+ nums = fill_dacs_for_pins(codec, cfg->line_out_pins, cfg->line_outs,
+ dacs, &rest, spec->dac_info,
+ AUTO_PIN_LINE_OUT);
+ nums += fill_dacs_for_pins(codec, cfg->hp_pins, cfg->hp_outs,
+ dacs, &rest, spec->dac_info + nums,
+ AUTO_PIN_HP_OUT);
+ nums += fill_dacs_for_pins(codec, cfg->speaker_pins, cfg->speaker_outs,
+ dacs, &rest, spec->dac_info + nums,
+ AUTO_PIN_SPEAKER_OUT);
+ spec->dac_info_filled = nums;
+ /* fill multiout struct */
+ for (i = 0; i < nums; i++) {
+ hda_nid_t dac = spec->dac_info[i].dac;
+ if (!dac)
+ continue;
+ switch (spec->dac_info[i].type) {
+ case AUTO_PIN_LINE_OUT:
+ spec->private_dac_nids[spec->multiout.num_dacs] = dac;
+ spec->multiout.num_dacs++;
+ break;
+ case AUTO_PIN_HP_OUT:
+ case AUTO_PIN_SPEAKER_OUT:
+ if (!spec->multiout.hp_nid) {
+ spec->multiout.hp_nid = dac;
+ break;
+ }
+ for (j = 0; j < ARRAY_SIZE(spec->multiout.extra_out_nid); j++)
+ if (!spec->multiout.extra_out_nid[j]) {
+ spec->multiout.extra_out_nid[j] = dac;
+ break;
+ }
+ break;
+ }
+ }
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.max_channels = nums * 2;
+
+ if (cfg->hp_outs > 0)
+ spec->auto_mute = 1;
+ spec->vmaster_nid = spec->private_dac_nids[0];
+}
+
+/* auto-mute/unmute speaker and line outs according to headphone jack */
+static void cx_auto_hp_automute(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, present;
+
+ if (!spec->auto_mute)
+ return;
+ present = 0;
+ for (i = 0; i < cfg->hp_outs; i++) {
+ if (snd_hda_jack_detect(codec, cfg->hp_pins[i])) {
+ present = 1;
+ break;
+ }
+ }
+ for (i = 0; i < cfg->line_outs; i++) {
+ snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ present ? 0 : PIN_OUT);
+ }
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ present ? 0 : PIN_OUT);
+ }
+}
+
+/* automatic switch internal and external mic */
+static void cx_auto_automic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux = &spec->private_imux;
+ int ext_idx = spec->auto_mic_ext;
+
+ if (!spec->auto_mic)
+ return;
+ if (snd_hda_jack_detect(codec, cfg->inputs[ext_idx].pin)) {
+ snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[ext_idx].index);
+ } else {
+ snd_hda_codec_write(codec, spec->adc_nids[0], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ imux->items[!ext_idx].index);
+ }
+}
+
+static void cx_auto_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ int nid = (res & AC_UNSOL_RES_SUBTAG) >> 20;
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cx_auto_hp_automute(codec);
+ conexant_report_jack(codec, nid);
+ break;
+ case CONEXANT_MIC_EVENT:
+ cx_auto_automic(codec);
+ conexant_report_jack(codec, nid);
+ break;
+ }
+}
+
+/* return true if it's an internal-mic pin */
+static int is_int_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+ return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+ snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT;
+}
+
+/* return true if it's an external-mic pin */
+static int is_ext_mic(struct hda_codec *codec, hda_nid_t pin)
+{
+ unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin);
+ return get_defcfg_device(def_conf) == AC_JACK_MIC_IN &&
+ snd_hda_get_input_pin_attr(def_conf) >= INPUT_PIN_ATTR_NORMAL &&
+ (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_PRES_DETECT);
+}
+
+/* check whether the pin config is suitable for auto-mic switching;
+ * auto-mic is enabled only when one int-mic and one-ext mic exist
+ */
+static void cx_auto_check_auto_mic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (is_ext_mic(codec, cfg->inputs[0].pin) &&
+ is_int_mic(codec, cfg->inputs[1].pin)) {
+ spec->auto_mic = 1;
+ spec->auto_mic_ext = 1;
+ return;
+ }
+ if (is_int_mic(codec, cfg->inputs[1].pin) &&
+ is_ext_mic(codec, cfg->inputs[0].pin)) {
+ spec->auto_mic = 1;
+ spec->auto_mic_ext = 0;
+ return;
+ }
+}
+
+static void cx_auto_parse_input(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct hda_input_mux *imux;
+ int i;
+
+ imux = &spec->private_imux;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ int idx = get_connection_index(codec, spec->adc_nids[0],
+ cfg->inputs[i].pin);
+ if (idx >= 0) {
+ const char *label;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label, idx, NULL);
+ }
+ }
+ if (imux->num_items == 2 && cfg->num_inputs == 2)
+ cx_auto_check_auto_mic(codec);
+ if (imux->num_items > 1 && !spec->auto_mic)
+ spec->input_mux = imux;
+}
+
+/* get digital-input audio widget corresponding to the given pin */
+static hda_nid_t cx_auto_get_dig_in(struct hda_codec *codec, hda_nid_t pin)
+{
+ 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 wcaps = get_wcaps(codec, nid);
+ unsigned int type = get_wcaps_type(wcaps);
+ if (type == AC_WID_AUD_IN && (wcaps & AC_WCAP_DIGITAL)) {
+ if (get_connection_index(codec, nid, pin) >= 0)
+ return nid;
+ }
+ }
+ return 0;
+}
+
+static void cx_auto_parse_digital(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+
+ if (cfg->dig_outs &&
+ snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) == 1)
+ spec->multiout.dig_out_nid = nid;
+ if (cfg->dig_in_pin)
+ spec->dig_in_nid = cx_auto_get_dig_in(codec, cfg->dig_in_pin);
+}
+
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static void cx_auto_parse_beep(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++)
+ if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
+ set_beep_amp(spec, nid, 0, HDA_OUTPUT);
+ break;
+ }
+}
+#else
+#define cx_auto_parse_beep(codec)
+#endif
+
+static int cx_auto_parse_auto_config(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+
+ cx_auto_parse_output(codec);
+ cx_auto_parse_input(codec);
+ cx_auto_parse_digital(codec);
+ cx_auto_parse_beep(codec);
+ return 0;
+}
+
+static void cx_auto_turn_on_eapd(struct hda_codec *codec, int num_pins,
+ hda_nid_t *pins)
+{
+ int i;
+ for (i = 0; i < num_pins; i++) {
+ if (snd_hda_query_pin_caps(codec, pins[i]) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pins[i], 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ }
+}
+
+static void select_connection(struct hda_codec *codec, hda_nid_t pin,
+ hda_nid_t src)
+{
+ int idx = get_connection_index(codec, pin, src);
+ if (idx >= 0)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+static void cx_auto_init_output(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid;
+ int i;
+
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+ for (i = 0; i < cfg->hp_outs; i++)
+ snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ if (spec->auto_mute) {
+ for (i = 0; i < cfg->hp_outs; i++) {
+ snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | CONEXANT_HP_EVENT);
+ }
+ cx_auto_hp_automute(codec);
+ } else {
+ for (i = 0; i < cfg->line_outs; i++)
+ snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ for (i = 0; i < cfg->speaker_outs; i++)
+ snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ }
+
+ for (i = 0; i < spec->dac_info_filled; i++) {
+ nid = spec->dac_info[i].dac;
+ if (!nid)
+ nid = spec->multiout.dac_nids[0];
+ select_connection(codec, spec->dac_info[i].pin, nid);
+ }
+
+ /* turn on EAPD */
+ cx_auto_turn_on_eapd(codec, cfg->line_outs, cfg->line_out_pins);
+ cx_auto_turn_on_eapd(codec, cfg->hp_outs, cfg->hp_pins);
+ cx_auto_turn_on_eapd(codec, cfg->speaker_outs, cfg->speaker_pins);
+}
+
+static void cx_auto_init_input(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < spec->num_adc_nids; i++)
+ snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0));
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ unsigned int type;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
+ type = PIN_VREF80;
+ else
+ type = PIN_IN;
+ snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, type);
+ }
+
+ if (spec->auto_mic) {
+ int ext_idx = spec->auto_mic_ext;
+ snd_hda_codec_write(codec, cfg->inputs[ext_idx].pin, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ 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);
+ }
+ }
+}
+
+static void cx_auto_init_digital(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (spec->multiout.dig_out_nid)
+ snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (spec->dig_in_nid)
+ snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+}
+
+static int cx_auto_init(struct hda_codec *codec)
+{
+ /*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
+ cx_auto_init_output(codec);
+ cx_auto_init_input(codec);
+ cx_auto_init_digital(codec);
+ return 0;
+}
+
+static int cx_auto_add_volume(struct hda_codec *codec, const char *basename,
+ const char *dir, int cidx,
+ hda_nid_t nid, int hda_dir)
+{
+ static char name[32];
+ static struct snd_kcontrol_new knew[] = {
+ HDA_CODEC_VOLUME(name, 0, 0, 0),
+ HDA_CODEC_MUTE(name, 0, 0, 0),
+ };
+ static char *sfx[2] = { "Volume", "Switch" };
+ int i, err;
+
+ for (i = 0; i < 2; i++) {
+ struct snd_kcontrol *kctl;
+ knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir);
+ knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
+ knew[i].index = cidx;
+ snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]);
+ kctl = snd_ctl_new1(&knew[i], codec);
+ if (!kctl)
+ return -ENOMEM;
+ err = snd_hda_ctl_add(codec, nid, kctl);
+ if (err < 0)
+ return err;
+ if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
+ break;
+ }
+ return 0;
+}
+
+#define cx_auto_add_pb_volume(codec, nid, str, idx) \
+ cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
+
+static int cx_auto_build_output_controls(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ int i, err;
+ int num_line = 0, num_hp = 0, num_spk = 0;
+ static const char *texts[3] = { "Front", "Surround", "CLFE" };
+
+ if (spec->dac_info_filled == 1)
+ return cx_auto_add_pb_volume(codec, spec->dac_info[0].dac,
+ "Master", 0);
+ for (i = 0; i < spec->dac_info_filled; i++) {
+ const char *label;
+ int idx, type;
+ if (!spec->dac_info[i].dac)
+ continue;
+ type = spec->dac_info[i].type;
+ if (type == AUTO_PIN_LINE_OUT)
+ type = spec->autocfg.line_out_type;
+ switch (type) {
+ case AUTO_PIN_LINE_OUT:
+ default:
+ label = texts[num_line++];
+ idx = 0;
+ break;
+ case AUTO_PIN_HP_OUT:
+ label = "Headphone";
+ idx = num_hp++;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ label = "Speaker";
+ idx = num_spk++;
+ break;
+ }
+ err = cx_auto_add_pb_volume(codec, spec->dac_info[i].dac,
+ label, idx);
+ if (err < 0)
+ return err;
+ }
+ 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;
+ int i, err, cidx;
+
+ err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0],
+ HDA_INPUT);
+ if (err < 0)
+ return err;
+ prev_label = NULL;
+ cidx = 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ const char *label;
+ if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP))
+ continue;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ if (label == prev_label)
+ cidx++;
+ else
+ cidx = 0;
+ prev_label = label;
+ err = cx_auto_add_volume(codec, label, " Capture", cidx,
+ nid, HDA_INPUT);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+static int cx_auto_build_controls(struct hda_codec *codec)
+{
+ int err;
+
+ err = cx_auto_build_output_controls(codec);
+ if (err < 0)
+ return err;
+ err = cx_auto_build_input_controls(codec);
+ if (err < 0)
+ return err;
+ return conexant_build_controls(codec);
+}
+
+static struct hda_codec_ops cx_auto_patch_ops = {
+ .build_controls = cx_auto_build_controls,
+ .build_pcms = conexant_build_pcms,
+ .init = cx_auto_init,
+ .free = conexant_free,
+ .unsol_event = cx_auto_unsol_event,
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ .suspend = conexant_suspend,
+#endif
+ .reboot_notify = snd_hda_shutup_pins,
+};
+
+static int patch_conexant_auto(struct hda_codec *codec)
+{
+ struct conexant_spec *spec;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+ spec->adc_nids = cx_auto_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(cx_auto_adc_nids);
+ spec->capsrc_nids = spec->adc_nids;
+ err = cx_auto_parse_auto_config(codec);
+ if (err < 0) {
+ kfree(codec->spec);
+ codec->spec = NULL;
+ return err;
+ }
+ codec->patch_ops = cx_auto_patch_ops;
+ if (spec->beep_amp)
+ snd_hda_attach_beep_device(codec, spec->beep_amp);
+ return 0;
+}
+
+/*
*/
static struct hda_codec_preset snd_hda_preset_conexant[] = {
@@ -3271,6 +3880,22 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
.patch = patch_cxt5066 },
{ .id = 0x14f15069, .name = "CX20585",
.patch = patch_cxt5066 },
+ { .id = 0x14f15097, .name = "CX20631",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f15098, .name = "CX20632",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150a1, .name = "CX20641",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150a2, .name = "CX20642",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150ab, .name = "CX20651",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150ac, .name = "CX20652",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150b8, .name = "CX20664",
+ .patch = patch_conexant_auto },
+ { .id = 0x14f150b9, .name = "CX20665",
+ .patch = patch_conexant_auto },
{} /* terminator */
};
@@ -3281,6 +3906,14 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066");
MODULE_ALIAS("snd-hda-codec-id:14f15067");
MODULE_ALIAS("snd-hda-codec-id:14f15068");
MODULE_ALIAS("snd-hda-codec-id:14f15069");
+MODULE_ALIAS("snd-hda-codec-id:14f15097");
+MODULE_ALIAS("snd-hda-codec-id:14f15098");
+MODULE_ALIAS("snd-hda-codec-id:14f150a1");
+MODULE_ALIAS("snd-hda-codec-id:14f150a2");
+MODULE_ALIAS("snd-hda-codec-id:14f150ab");
+MODULE_ALIAS("snd-hda-codec-id:14f150ac");
+MODULE_ALIAS("snd-hda-codec-id:14f150b8");
+MODULE_ALIAS("snd-hda-codec-id:14f150b9");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Conexant HD-audio codec");
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index afd6022a96a..d3e49aa5b9e 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3,6 +3,9 @@
* patch_hdmi.c - routines for HDMI/DisplayPort codecs
*
* Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
+ * Copyright (c) 2006 ATI Technologies Inc.
+ * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
+ * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
*
* Authors:
* Wu Fengguang <wfg@linux.intel.com>
@@ -25,6 +28,22 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
+ * could support two independent pipes, each of them can be connected to one or
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define MAX_HDMI_CVTS 3
+#define MAX_HDMI_PINS 3
struct hdmi_spec {
int num_cvts;
@@ -49,10 +68,10 @@ struct hdmi_spec {
struct hda_pcm_stream codec_pcm_pars[MAX_HDMI_CVTS];
/*
- * nvhdmi specific
+ * ati/nvhdmi specific
*/
struct hda_multi_out multiout;
- unsigned int codec_type;
+ struct hda_pcm_stream *pcm_playback;
/* misc flags */
/* PD bit indicates only the update, not the current state */
@@ -65,13 +84,25 @@ struct hdmi_audio_infoframe {
u8 ver; /* 0x01 */
u8 len; /* 0x0a */
- u8 checksum; /* PB0 */
+ u8 checksum;
+
u8 CC02_CT47; /* CC in bits 0:2, CT in 4:7 */
u8 SS01_SF24;
u8 CXT04;
u8 CA;
u8 LFEPBL01_LSV36_DM_INH7;
- u8 reserved[5]; /* PB6 - PB10 */
+};
+
+struct dp_audio_infoframe {
+ u8 type; /* 0x84 */
+ u8 len; /* 0x1b */
+ u8 ver; /* 0x11 << 2 */
+
+ u8 CC02_CT47; /* match with HDMI infoframe from this on */
+ u8 SS01_SF24;
+ u8 CXT04;
+ u8 CA;
+ u8 LFEPBL01_LSV36_DM_INH7;
};
/*
@@ -162,7 +193,7 @@ static int hdmi_channel_mapping[0x32][8] = {
/* 4ch */
[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
/* surround41 */
- [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
+ [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
/* surround50 */
[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
/* surround51 */
@@ -175,7 +206,7 @@ static int hdmi_channel_mapping[0x32][8] = {
* This is an ordered list!
*
* The preceding ones have better chances to be selected by
- * hdmi_setup_channel_allocation().
+ * hdmi_channel_allocation().
*/
static struct cea_channel_speaker_allocation channel_allocations[] = {
/* channel: 7 6 5 4 3 2 1 0 */
@@ -352,14 +383,14 @@ static void init_channel_allocations(void)
*
* TODO: it could select the wrong CA from multiple candidates.
*/
-static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
- struct hdmi_audio_infoframe *ai)
+static int hdmi_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
+ int channels)
{
struct hdmi_spec *spec = codec->spec;
struct hdmi_eld *eld;
int i;
+ int ca = 0;
int spk_mask = 0;
- int channels = 1 + (ai->CC02_CT47 & 0x7);
char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
/*
@@ -397,16 +428,16 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
if (channels == channel_allocations[i].channels &&
(spk_mask & channel_allocations[i].spk_mask) ==
channel_allocations[i].spk_mask) {
- ai->CA = channel_allocations[i].ca_index;
+ ca = channel_allocations[i].ca_index;
break;
}
}
snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
- ai->CA, channels, buf);
+ ca, channels, buf);
- return ai->CA;
+ return ca;
}
static void hdmi_debug_channel_mapping(struct hda_codec *codec,
@@ -428,10 +459,9 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec,
static void hdmi_setup_channel_mapping(struct hda_codec *codec,
hda_nid_t pin_nid,
- struct hdmi_audio_infoframe *ai)
+ int ca)
{
int i;
- int ca = ai->CA;
int err;
if (hdmi_channel_mapping[ca][1] == 0) {
@@ -528,41 +558,37 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
#endif
}
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *hdmi_ai)
{
- u8 *bytes = (u8 *)ai;
+ u8 *bytes = (u8 *)hdmi_ai;
u8 sum = 0;
int i;
- ai->checksum = 0;
+ hdmi_ai->checksum = 0;
- for (i = 0; i < sizeof(*ai); i++)
+ for (i = 0; i < sizeof(*hdmi_ai); i++)
sum += bytes[i];
- ai->checksum = -sum;
+ hdmi_ai->checksum = -sum;
}
static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
hda_nid_t pin_nid,
- struct hdmi_audio_infoframe *ai)
+ u8 *dip, int size)
{
- u8 *bytes = (u8 *)ai;
int i;
hdmi_debug_dip_size(codec, pin_nid);
hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
- hdmi_checksum_audio_infoframe(ai);
-
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(*ai); i++)
- hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
+ for (i = 0; i < size; i++)
+ hdmi_write_dip_byte(codec, pin_nid, dip[i]);
}
static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
- struct hdmi_audio_infoframe *ai)
+ u8 *dip, int size)
{
- u8 *bytes = (u8 *)ai;
u8 val;
int i;
@@ -571,10 +597,10 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
return false;
hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
- for (i = 0; i < sizeof(*ai); i++) {
+ for (i = 0; i < size; i++) {
val = snd_hda_codec_read(codec, pin_nid, 0,
AC_VERB_GET_HDMI_DIP_DATA, 0);
- if (val != bytes[i])
+ if (val != dip[i])
return false;
}
@@ -586,15 +612,13 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
{
struct hdmi_spec *spec = codec->spec;
hda_nid_t pin_nid;
+ int channels = substream->runtime->channels;
+ int ca;
int i;
- struct hdmi_audio_infoframe ai = {
- .type = 0x84,
- .ver = 0x01,
- .len = 0x0a,
- .CC02_CT47 = substream->runtime->channels - 1,
- };
+ u8 ai[max(sizeof(struct hdmi_audio_infoframe),
+ sizeof(struct dp_audio_infoframe))];
- hdmi_setup_channel_allocation(codec, nid, &ai);
+ ca = hdmi_channel_allocation(codec, nid, channels);
for (i = 0; i < spec->num_pins; i++) {
if (spec->pin_cvt[i] != nid)
@@ -603,14 +627,45 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
continue;
pin_nid = spec->pin[i];
- if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+
+ memset(ai, 0, sizeof(ai));
+ if (spec->sink_eld[i].conn_type == 0) { /* HDMI */
+ struct hdmi_audio_infoframe *hdmi_ai;
+
+ hdmi_ai = (struct hdmi_audio_infoframe *)ai;
+ hdmi_ai->type = 0x84;
+ hdmi_ai->ver = 0x01;
+ hdmi_ai->len = 0x0a;
+ hdmi_ai->CC02_CT47 = channels - 1;
+ hdmi_checksum_audio_infoframe(hdmi_ai);
+ } else if (spec->sink_eld[i].conn_type == 1) { /* DisplayPort */
+ struct dp_audio_infoframe *dp_ai;
+
+ dp_ai = (struct dp_audio_infoframe *)ai;
+ dp_ai->type = 0x84;
+ dp_ai->len = 0x1b;
+ dp_ai->ver = 0x11 << 2;
+ dp_ai->CC02_CT47 = channels - 1;
+ } else {
+ snd_printd("HDMI: unknown connection type at pin %d\n",
+ pin_nid);
+ continue;
+ }
+
+ /*
+ * sizeof(ai) is used instead of sizeof(*hdmi_ai) or
+ * sizeof(*dp_ai) to avoid partial match/update problems when
+ * the user switches between HDMI/DP monitors.
+ */
+ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai, sizeof(ai))) {
snd_printdd("hdmi_setup_audio_infoframe: "
"cvt=%d pin=%d channels=%d\n",
nid, pin_nid,
- substream->runtime->channels);
- hdmi_setup_channel_mapping(codec, pin_nid, &ai);
+ channels);
+ hdmi_setup_channel_mapping(codec, pin_nid, ca);
hdmi_stop_infoframe_trans(codec, pin_nid);
- hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+ hdmi_fill_audio_infoframe(codec, pin_nid,
+ ai, sizeof(ai));
hdmi_start_infoframe_trans(codec, pin_nid);
}
}
@@ -791,7 +846,6 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
/*
* HDA/HDMI auto parsing
*/
-
static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
{
struct hdmi_spec *spec = codec->spec;
@@ -922,3 +976,664 @@ static int hdmi_parse_codec(struct hda_codec *codec)
return 0;
}
+/*
+ */
+static char *generic_hdmi_pcm_names[MAX_HDMI_CVTS] = {
+ "HDMI 0",
+ "HDMI 1",
+ "HDMI 2",
+};
+
+/*
+ * HDMI callbacks
+ */
+
+static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ hdmi_set_channel_count(codec, hinfo->nid,
+ substream->runtime->channels);
+
+ hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+
+ return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+}
+
+static struct hda_pcm_stream generic_hdmi_pcm_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .ops = {
+ .open = hdmi_pcm_open,
+ .prepare = generic_hdmi_playback_pcm_prepare,
+ },
+};
+
+static int generic_hdmi_build_pcms(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+ int i;
+
+ codec->num_pcms = spec->num_cvts;
+ codec->pcm_info = info;
+
+ for (i = 0; i < codec->num_pcms; i++, info++) {
+ unsigned int chans;
+ struct hda_pcm_stream *pstr;
+
+ chans = get_wcaps(codec, spec->cvt[i]);
+ chans = get_wcaps_channels(chans);
+
+ info->name = generic_hdmi_pcm_names[i];
+ info->pcm_type = HDA_PCM_TYPE_HDMI;
+ pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
+ if (spec->pcm_playback)
+ *pstr = *spec->pcm_playback;
+ else
+ *pstr = generic_hdmi_pcm_playback;
+ pstr->nid = spec->cvt[i];
+ if (pstr->channels_max <= 2 && chans && chans <= 16)
+ pstr->channels_max = chans;
+ }
+
+ return 0;
+}
+
+static int generic_hdmi_build_controls(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int err;
+ int i;
+
+ for (i = 0; i < codec->num_pcms; i++) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int generic_hdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; spec->pin[i]; i++) {
+ hdmi_enable_output(codec, spec->pin[i]);
+ snd_hda_codec_write(codec, spec->pin[i], 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ AC_USRSP_EN | spec->pin[i]);
+ }
+ return 0;
+}
+
+static void generic_hdmi_free(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+
+ kfree(spec);
+}
+
+static struct hda_codec_ops generic_hdmi_patch_ops = {
+ .init = generic_hdmi_init,
+ .free = generic_hdmi_free,
+ .build_pcms = generic_hdmi_build_pcms,
+ .build_controls = generic_hdmi_build_controls,
+ .unsol_event = hdmi_unsol_event,
+};
+
+static int patch_generic_hdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int i;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+ if (hdmi_parse_codec(codec) < 0) {
+ codec->spec = NULL;
+ kfree(spec);
+ return -EINVAL;
+ }
+ codec->patch_ops = generic_hdmi_patch_ops;
+
+ for (i = 0; i < spec->num_pins; i++)
+ snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+
+ init_channel_allocations();
+
+ return 0;
+}
+
+/*
+ * Nvidia specific implementations
+ */
+
+#define Nv_VERB_SET_Channel_Allocation 0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
+#define Nv_VERB_SET_Audio_Protection_On 0xF98
+#define Nv_VERB_SET_Audio_Protection_Off 0xF99
+
+#define nvhdmi_master_con_nid_7x 0x04
+#define nvhdmi_master_pin_nid_7x 0x05
+
+static hda_nid_t nvhdmi_con_nids_7x[4] = {
+ /*front, rear, clfe, rear_surr */
+ 0x6, 0x8, 0xa, 0xc,
+};
+
+static struct hda_verb nvhdmi_basic_init_7x[] = {
+ /* set audio protect on */
+ { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
+ /* enable digital output on pin widget */
+ { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+ {} /* terminator */
+};
+
+#ifdef LIMITED_RATE_FMT_SUPPORT
+/* support only the safe format and rate */
+#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
+#define SUPPORTED_MAXBPS 16
+#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#else
+/* support all rates and formats */
+#define SUPPORTED_RATES \
+ (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000)
+#define SUPPORTED_MAXBPS 24
+#define SUPPORTED_FORMATS \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+#endif
+
+static int nvhdmi_7x_init(struct hda_codec *codec)
+{
+ snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+ return 0;
+}
+
+static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int simple_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
+}
+
+static int nvhdmi_8ch_7x_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int i;
+
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
+ 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ for (i = 0; i < 4; i++) {
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ AC_VERB_SET_CHANNEL_STREAMID, 0);
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
+ AC_VERB_SET_STREAM_FORMAT, 0);
+ }
+
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ int chs;
+ unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+ int i;
+
+ mutex_lock(&codec->spdif_mutex);
+
+ chs = substream->runtime->channels;
+ chan = chs ? (chs - 1) : 1;
+
+ switch (chs) {
+ default:
+ case 0:
+ case 2:
+ chanmask = 0x00;
+ break;
+ case 4:
+ chanmask = 0x08;
+ break;
+ case 6:
+ chanmask = 0x0b;
+ break;
+ case 8:
+ chanmask = 0x13;
+ break;
+ }
+ dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
+ dataDCC2 = 0x2;
+
+ /* set the Audio InforFrame Channel Allocation */
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Channel_Allocation, chanmask);
+
+ /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+
+ /* set the stream id */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+ /* set the stream format */
+ snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
+ AC_VERB_SET_STREAM_FORMAT, format);
+
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_master_con_nid_7x,
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (chs == 2)
+ channel_id = 0;
+ else
+ channel_id = i * 2;
+
+ /* turn off SPDIF once;
+ *otherwise the IEC958 bits won't be updated
+ */
+ if (codec->spdif_status_reset &&
+ (codec->spdif_ctls & AC_DIG1_ENABLE))
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+ /* set the stream id */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ (stream_tag << 4) | channel_id);
+ /* set the stream format */
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ /* turn on again (if needed) */
+ /* enable and set the channel status audio/data flag */
+ if (codec->spdif_status_reset &&
+ (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_1,
+ codec->spdif_ctls & 0xff);
+ snd_hda_codec_write(codec,
+ nvhdmi_con_nids_7x[i],
+ 0,
+ AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+ }
+ }
+
+ /* set the Audio Info Frame Checksum */
+ snd_hda_codec_write(codec, 0x1, 0,
+ Nv_VERB_SET_Info_Frame_Checksum,
+ (0x71 - chan - chanmask));
+
+ mutex_unlock(&codec->spdif_mutex);
+ return 0;
+}
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_8ch_7x = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = nvhdmi_master_con_nid_7x,
+ .rates = SUPPORTED_RATES,
+ .maxbps = SUPPORTED_MAXBPS,
+ .formats = SUPPORTED_FORMATS,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = nvhdmi_8ch_7x_pcm_close,
+ .prepare = nvhdmi_8ch_7x_pcm_prepare
+ },
+};
+
+static struct hda_pcm_stream nvhdmi_pcm_playback_2ch = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = nvhdmi_master_con_nid_7x,
+ .rates = SUPPORTED_RATES,
+ .maxbps = SUPPORTED_MAXBPS,
+ .formats = SUPPORTED_FORMATS,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = simple_playback_pcm_prepare
+ },
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
+ .build_controls = generic_hdmi_build_controls,
+ .build_pcms = generic_hdmi_build_pcms,
+ .init = nvhdmi_7x_init,
+ .free = generic_hdmi_free,
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
+ .build_controls = generic_hdmi_build_controls,
+ .build_pcms = generic_hdmi_build_pcms,
+ .init = nvhdmi_7x_init,
+ .free = generic_hdmi_free,
+};
+
+static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err = patch_generic_hdmi(codec);
+
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+ spec->old_pin_detect = 1;
+ return 0;
+}
+
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+ spec->old_pin_detect = 1;
+ spec->num_cvts = 1;
+ spec->cvt[0] = nvhdmi_master_con_nid_7x;
+ spec->pcm_playback = &nvhdmi_pcm_playback_2ch;
+
+ codec->patch_ops = nvhdmi_patch_ops_2ch;
+
+ return 0;
+}
+
+static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+ int err = patch_nvhdmi_2ch(codec);
+
+ if (err < 0)
+ return err;
+ spec = codec->spec;
+ spec->multiout.max_channels = 8;
+ spec->pcm_playback = &nvhdmi_pcm_playback_8ch_7x;
+ codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
+ return 0;
+}
+
+/*
+ * ATI-specific implementations
+ *
+ * FIXME: we may omit the whole this and use the generic code once after
+ * it's confirmed to work.
+ */
+
+#define ATIHDMI_CVT_NID 0x02 /* audio converter */
+#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */
+
+static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct hdmi_spec *spec = codec->spec;
+ int chans = substream->runtime->channels;
+ int i, err;
+
+ err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format,
+ substream);
+ if (err < 0)
+ return err;
+ snd_hda_codec_write(codec, spec->cvt[0], 0, AC_VERB_SET_CVT_CHAN_COUNT,
+ chans - 1);
+ /* FIXME: XXX */
+ for (i = 0; i < chans; i++) {
+ snd_hda_codec_write(codec, spec->cvt[0], 0,
+ AC_VERB_SET_HDMI_CHAN_SLOT,
+ (i << 4) | i);
+ }
+ return 0;
+}
+
+static struct hda_pcm_stream atihdmi_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = ATIHDMI_CVT_NID,
+ .ops = {
+ .open = simple_playback_pcm_open,
+ .close = simple_playback_pcm_close,
+ .prepare = atihdmi_playback_pcm_prepare
+ },
+};
+
+static struct hda_verb atihdmi_basic_init[] = {
+ /* enable digital output on pin widget */
+ { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+ {} /* terminator */
+};
+
+static int atihdmi_init(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec = codec->spec;
+
+ snd_hda_sequence_write(codec, atihdmi_basic_init);
+ /* SI codec requires to unmute the pin */
+ if (get_wcaps(codec, spec->pin[0]) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, spec->pin[0], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ return 0;
+}
+
+static struct hda_codec_ops atihdmi_patch_ops = {
+ .build_controls = generic_hdmi_build_controls,
+ .build_pcms = generic_hdmi_build_pcms,
+ .init = atihdmi_init,
+ .free = generic_hdmi_free,
+};
+
+
+static int patch_atihdmi(struct hda_codec *codec)
+{
+ struct hdmi_spec *spec;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ spec->multiout.num_dacs = 0; /* no analog */
+ spec->multiout.max_channels = 2;
+ spec->multiout.dig_out_nid = ATIHDMI_CVT_NID;
+ spec->num_cvts = 1;
+ spec->cvt[0] = ATIHDMI_CVT_NID;
+ spec->pin[0] = ATIHDMI_PIN_NID;
+ spec->pcm_playback = &atihdmi_pcm_digital_playback;
+
+ codec->patch_ops = atihdmi_patch_ops;
+
+ return 0;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_hdmi[] = {
+{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
+{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
+{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
+{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
+{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
+{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
+{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_generic_hdmi },
+{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi },
+{} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:1002793c");
+MODULE_ALIAS("snd-hda-codec-id:10027919");
+MODULE_ALIAS("snd-hda-codec-id:1002791a");
+MODULE_ALIAS("snd-hda-codec-id:1002aa01");
+MODULE_ALIAS("snd-hda-codec-id:10951390");
+MODULE_ALIAS("snd-hda-codec-id:10951392");
+MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0003");
+MODULE_ALIAS("snd-hda-codec-id:10de0005");
+MODULE_ALIAS("snd-hda-codec-id:10de0006");
+MODULE_ALIAS("snd-hda-codec-id:10de0007");
+MODULE_ALIAS("snd-hda-codec-id:10de000a");
+MODULE_ALIAS("snd-hda-codec-id:10de000b");
+MODULE_ALIAS("snd-hda-codec-id:10de000c");
+MODULE_ALIAS("snd-hda-codec-id:10de000d");
+MODULE_ALIAS("snd-hda-codec-id:10de0010");
+MODULE_ALIAS("snd-hda-codec-id:10de0011");
+MODULE_ALIAS("snd-hda-codec-id:10de0012");
+MODULE_ALIAS("snd-hda-codec-id:10de0013");
+MODULE_ALIAS("snd-hda-codec-id:10de0014");
+MODULE_ALIAS("snd-hda-codec-id:10de0018");
+MODULE_ALIAS("snd-hda-codec-id:10de0019");
+MODULE_ALIAS("snd-hda-codec-id:10de001a");
+MODULE_ALIAS("snd-hda-codec-id:10de001b");
+MODULE_ALIAS("snd-hda-codec-id:10de001c");
+MODULE_ALIAS("snd-hda-codec-id:10de0040");
+MODULE_ALIAS("snd-hda-codec-id:10de0041");
+MODULE_ALIAS("snd-hda-codec-id:10de0042");
+MODULE_ALIAS("snd-hda-codec-id:10de0043");
+MODULE_ALIAS("snd-hda-codec-id:10de0044");
+MODULE_ALIAS("snd-hda-codec-id:10de0067");
+MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:17e80047");
+MODULE_ALIAS("snd-hda-codec-id:80860054");
+MODULE_ALIAS("snd-hda-codec-id:80862801");
+MODULE_ALIAS("snd-hda-codec-id:80862802");
+MODULE_ALIAS("snd-hda-codec-id:80862803");
+MODULE_ALIAS("snd-hda-codec-id:80862804");
+MODULE_ALIAS("snd-hda-codec-id:80862805");
+MODULE_ALIAS("snd-hda-codec-id:808629fb");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI HD-audio codec");
+MODULE_ALIAS("snd-hda-codec-intelhdmi");
+MODULE_ALIAS("snd-hda-codec-nvhdmi");
+MODULE_ALIAS("snd-hda-codec-atihdmi");
+
+static struct hda_codec_preset_list intel_list = {
+ .preset = snd_hda_preset_hdmi,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_hdmi_init(void)
+{
+ return snd_hda_add_codec_preset(&intel_list);
+}
+
+static void __exit patch_hdmi_exit(void)
+{
+ snd_hda_delete_codec_preset(&intel_list);
+}
+
+module_init(patch_hdmi_init)
+module_exit(patch_hdmi_exit)
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
deleted file mode 100644
index 36a9b83a617..00000000000
--- a/sound/pci/hda/patch_intelhdmi.c
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- *
- * patch_intelhdmi.c - Patch for Intel HDMI codecs
- *
- * Copyright(c) 2008 Intel Corporation. All rights reserved.
- *
- * Authors:
- * Jiang Zhe <zhe.jiang@intel.com>
- * Wu Fengguang <wfg@linux.intel.com>
- *
- * Maintained by:
- * Wu Fengguang <wfg@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-/*
- * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
- * could support two independent pipes, each of them can be connected to one or
- * more ports (DVI, HDMI or DisplayPort).
- *
- * The HDA correspondence of pipes/ports are converter/pin nodes.
- */
-#define MAX_HDMI_CVTS 3
-#define MAX_HDMI_PINS 3
-
-#include "patch_hdmi.c"
-
-static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
- "INTEL HDMI 0",
- "INTEL HDMI 1",
- "INTEL HDMI 2",
-};
-
-/*
- * HDMI callbacks
- */
-
-static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
-
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static struct hda_pcm_stream intel_hdmi_pcm_playback = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = intel_hdmi_playback_pcm_prepare,
- },
-};
-
-static int intel_hdmi_build_pcms(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
-
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
-
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
-
- info->name = intel_hdmi_pcm_names[i];
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- intel_hdmi_pcm_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
- }
-
- return 0;
-}
-
-static int intel_hdmi_build_controls(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int err;
- int i;
-
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int intel_hdmi_init(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
- }
- return 0;
-}
-
-static void intel_hdmi_free(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
-
- kfree(spec);
-}
-
-static struct hda_codec_ops intel_hdmi_patch_ops = {
- .init = intel_hdmi_init,
- .free = intel_hdmi_free,
- .build_pcms = intel_hdmi_build_pcms,
- .build_controls = intel_hdmi_build_controls,
- .unsol_event = hdmi_unsol_event,
-};
-
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int i;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
- if (hdmi_parse_codec(codec) < 0) {
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
- }
- codec->patch_ops = intel_hdmi_patch_ops;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
- init_channel_allocations();
-
- return 0;
-}
-
-static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
-{ .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI", .patch = patch_intel_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_intel_hdmi },
-{} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
-
-static struct hda_codec_preset_list intel_list = {
- .preset = snd_hda_preset_intelhdmi,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_intelhdmi_init(void)
-{
- return snd_hda_add_codec_preset(&intel_list);
-}
-
-static void __exit patch_intelhdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&intel_list);
-}
-
-module_init(patch_intelhdmi_init)
-module_exit(patch_intelhdmi_exit)
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
deleted file mode 100644
index baa108b9d6a..00000000000
--- a/sound/pci/hda/patch_nvhdmi.c
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Universal Interface for Intel High Definition Audio Codec
- *
- * HD audio interface patch for NVIDIA HDMI codecs
- *
- * Copyright (c) 2008 NVIDIA Corp. All rights reserved.
- * Copyright (c) 2008 Wei Ni <wni@nvidia.com>
- *
- *
- * This driver is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This driver is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include "hda_codec.h"
-#include "hda_local.h"
-
-#define MAX_HDMI_CVTS 1
-#define MAX_HDMI_PINS 1
-
-#include "patch_hdmi.c"
-
-static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
- "NVIDIA HDMI",
-};
-
-/* define below to restrict the supported rates and formats */
-/* #define LIMITED_RATE_FMT_SUPPORT */
-
-enum HDACodec {
- HDA_CODEC_NVIDIA_MCP7X,
- HDA_CODEC_NVIDIA_MCP89,
- HDA_CODEC_NVIDIA_GT21X,
- HDA_CODEC_INVALID
-};
-
-#define Nv_VERB_SET_Channel_Allocation 0xF79
-#define Nv_VERB_SET_Info_Frame_Checksum 0xF7A
-#define Nv_VERB_SET_Audio_Protection_On 0xF98
-#define Nv_VERB_SET_Audio_Protection_Off 0xF99
-
-#define nvhdmi_master_con_nid_7x 0x04
-#define nvhdmi_master_pin_nid_7x 0x05
-
-#define nvhdmi_master_con_nid_89 0x04
-#define nvhdmi_master_pin_nid_89 0x05
-
-static hda_nid_t nvhdmi_con_nids_7x[4] = {
- /*front, rear, clfe, rear_surr */
- 0x6, 0x8, 0xa, 0xc,
-};
-
-static struct hda_verb nvhdmi_basic_init_7x[] = {
- /* set audio protect on */
- { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
- /* enable digital output on pin widget */
- { 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- { 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
- {} /* terminator */
-};
-
-#ifdef LIMITED_RATE_FMT_SUPPORT
-/* support only the safe format and rate */
-#define SUPPORTED_RATES SNDRV_PCM_RATE_48000
-#define SUPPORTED_MAXBPS 16
-#define SUPPORTED_FORMATS SNDRV_PCM_FMTBIT_S16_LE
-#else
-/* support all rates and formats */
-#define SUPPORTED_RATES \
- (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
- SNDRV_PCM_RATE_192000)
-#define SUPPORTED_MAXBPS 24
-#define SUPPORTED_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
-#endif
-
-/*
- * Controls
- */
-static int nvhdmi_build_controls(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int err;
- int i;
-
- if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
- || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
- for (i = 0; i < codec->num_pcms; i++) {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->cvt[i]);
- if (err < 0)
- return err;
- }
- } else {
- err = snd_hda_create_spdif_out_ctls(codec,
- spec->multiout.dig_out_nid);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-
-static int nvhdmi_init(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
- if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
- || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
- for (i = 0; spec->pin[i]; i++) {
- hdmi_enable_output(codec, spec->pin[i]);
- snd_hda_codec_write(codec, spec->pin[i], 0,
- AC_VERB_SET_UNSOLICITED_ENABLE,
- AC_USRSP_EN | spec->pin[i]);
- }
- } else {
- snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
- }
- return 0;
-}
-
-static void nvhdmi_free(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
- || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
- }
-
- kfree(spec);
-}
-
-/*
- * Digital out
- */
-static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- int i;
-
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
- 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
- for (i = 0; i < 4; i++) {
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- AC_VERB_SET_CHANNEL_STREAMID, 0);
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
- AC_VERB_SET_STREAM_FORMAT, 0);
- }
-
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- hdmi_set_channel_count(codec, hinfo->nid,
- substream->runtime->channels);
-
- hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
-
- return hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- int chs;
- unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
- int i;
-
- mutex_lock(&codec->spdif_mutex);
-
- chs = substream->runtime->channels;
- chan = chs ? (chs - 1) : 1;
-
- switch (chs) {
- default:
- case 0:
- case 2:
- chanmask = 0x00;
- break;
- case 4:
- chanmask = 0x08;
- break;
- case 6:
- chanmask = 0x0b;
- break;
- case 8:
- chanmask = 0x13;
- break;
- }
- dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
- dataDCC2 = 0x2;
-
- /* set the Audio InforFrame Channel Allocation */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Channel_Allocation, chanmask);
-
- /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-
- /* set the stream id */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
-
- /* set the stream format */
- snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
- AC_VERB_SET_STREAM_FORMAT, format);
-
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_master_con_nid_7x,
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
-
- for (i = 0; i < 4; i++) {
- if (chs == 2)
- channel_id = 0;
- else
- channel_id = i * 2;
-
- /* turn off SPDIF once;
- *otherwise the IEC958 bits won't be updated
- */
- if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE))
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
- /* set the stream id */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- (stream_tag << 4) | channel_id);
- /* set the stream format */
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- /* turn on again (if needed) */
- /* enable and set the channel status audio/data flag */
- if (codec->spdif_status_reset &&
- (codec->spdif_ctls & AC_DIG1_ENABLE)) {
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_1,
- codec->spdif_ctls & 0xff);
- snd_hda_codec_write(codec,
- nvhdmi_con_nids_7x[i],
- 0,
- AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
- }
- }
-
- /* set the Audio Info Frame Checksum */
- snd_hda_codec_write(codec, 0x1, 0,
- Nv_VERB_SET_Info_Frame_Checksum,
- (0x71 - chan - chanmask));
-
- mutex_unlock(&codec->spdif_mutex);
- return 0;
-}
-
-static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct hdmi_spec *spec = codec->spec;
- return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
- format, substream);
-}
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
- .substreams = 1,
- .channels_min = 2,
- .ops = {
- .open = hdmi_pcm_open,
- .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
- },
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 8,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = nvhdmi_dig_playback_pcm_open,
- .close = nvhdmi_dig_playback_pcm_close_8ch_7x,
- .prepare = nvhdmi_dig_playback_pcm_prepare_8ch
- },
-};
-
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
- .nid = nvhdmi_master_con_nid_7x,
- .rates = SUPPORTED_RATES,
- .maxbps = SUPPORTED_MAXBPS,
- .formats = SUPPORTED_FORMATS,
- .ops = {
- .open = nvhdmi_dig_playback_pcm_open,
- .close = nvhdmi_dig_playback_pcm_close_2ch,
- .prepare = nvhdmi_dig_playback_pcm_prepare_2ch
- },
-};
-
-static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- int i;
-
- codec->num_pcms = spec->num_cvts;
- codec->pcm_info = info;
-
- for (i = 0; i < codec->num_pcms; i++, info++) {
- unsigned int chans;
-
- chans = get_wcaps(codec, spec->cvt[i]);
- chans = get_wcaps_channels(chans);
-
- info->name = nvhdmi_pcm_names[i];
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK]
- = nvhdmi_pcm_digital_playback_8ch_89;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
- }
-
- return 0;
-}
-
-static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "NVIDIA HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK]
- = nvhdmi_pcm_digital_playback_8ch_7x;
-
- return 0;
-}
-
-static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
-{
- struct hdmi_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
-
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
- info->name = "NVIDIA HDMI";
- info->pcm_type = HDA_PCM_TYPE_HDMI;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK]
- = nvhdmi_pcm_digital_playback_2ch;
-
- return 0;
-}
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
- .build_controls = nvhdmi_build_controls,
- .build_pcms = nvhdmi_build_pcms_8ch_89,
- .init = nvhdmi_init,
- .free = nvhdmi_free,
- .unsol_event = hdmi_unsol_event,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
- .build_controls = nvhdmi_build_controls,
- .build_pcms = nvhdmi_build_pcms_8ch_7x,
- .init = nvhdmi_init,
- .free = nvhdmi_free,
-};
-
-static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
- .build_controls = nvhdmi_build_controls,
- .build_pcms = nvhdmi_build_pcms_2ch,
- .init = nvhdmi_init,
- .free = nvhdmi_free,
-};
-
-static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
- int i;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
- spec->codec_type = HDA_CODEC_NVIDIA_MCP89;
- spec->old_pin_detect = 1;
-
- if (hdmi_parse_codec(codec) < 0) {
- codec->spec = NULL;
- kfree(spec);
- return -EINVAL;
- }
- codec->patch_ops = nvhdmi_patch_ops_8ch_89;
-
- for (i = 0; i < spec->num_pins; i++)
- snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
-
- init_channel_allocations();
-
- return 0;
-}
-
-static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 8;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
- spec->old_pin_detect = 1;
-
- codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
-
- return 0;
-}
-
-static int patch_nvhdmi_2ch(struct hda_codec *codec)
-{
- struct hdmi_spec *spec;
-
- spec = kzalloc(sizeof(*spec), GFP_KERNEL);
- if (spec == NULL)
- return -ENOMEM;
-
- codec->spec = spec;
-
- spec->multiout.num_dacs = 0; /* no analog */
- spec->multiout.max_channels = 2;
- spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
- spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
- spec->old_pin_detect = 1;
-
- codec->patch_ops = nvhdmi_patch_ops_2ch;
-
- return 0;
-}
-
-/*
- * patch entries
- */
-static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
- { .id = 0x10de0002, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0003, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0005, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0006, .name = "MCP77/78 HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de0007, .name = "MCP79/7A HDMI", .patch = patch_nvhdmi_8ch_7x },
- { .id = 0x10de000a, .name = "GPU 0a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000b, .name = "GPU 0b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000c, .name = "MCP89 HDMI", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de000d, .name = "GPU 0d HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0010, .name = "GPU 10 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0011, .name = "GPU 11 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0012, .name = "GPU 12 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0013, .name = "GPU 13 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0014, .name = "GPU 14 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0018, .name = "GPU 18 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0019, .name = "GPU 19 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de001a, .name = "GPU 1a HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de001b, .name = "GPU 1b HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de001c, .name = "GPU 1c HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0040, .name = "GPU 40 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0041, .name = "GPU 41 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0042, .name = "GPU 42 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0043, .name = "GPU 43 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0044, .name = "GPU 44 HDMI/DP", .patch = patch_nvhdmi_8ch_89 },
- { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
- { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
- {} /* terminator */
-};
-
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
-
-static struct hda_codec_preset_list nvhdmi_list = {
- .preset = snd_hda_preset_nvhdmi,
- .owner = THIS_MODULE,
-};
-
-static int __init patch_nvhdmi_init(void)
-{
- return snd_hda_add_codec_preset(&nvhdmi_list);
-}
-
-static void __exit patch_nvhdmi_exit(void)
-{
- snd_hda_delete_codec_preset(&nvhdmi_list);
-}
-
-module_init(patch_nvhdmi_init)
-module_exit(patch_nvhdmi_exit)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a432e6efd19..5f00589cb79 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/pci.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_beep.h"
@@ -282,6 +283,12 @@ struct alc_mic_route {
unsigned char amix_idx;
};
+struct alc_jack {
+ hda_nid_t nid;
+ int type;
+ struct snd_jack *jack;
+};
+
#define MUX_IDX_UNDEF ((unsigned char)-1)
struct alc_customize_define {
@@ -294,6 +301,7 @@ struct alc_customize_define {
unsigned int platform_type:1;
unsigned int swap:1;
unsigned int override:1;
+ unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */
};
struct alc_spec {
@@ -357,6 +365,9 @@ struct alc_spec {
/* PCM information */
struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
+ /* jack detection */
+ struct snd_array jacks;
+
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
struct alc_customize_define cdefine;
@@ -383,6 +394,7 @@ struct alc_spec {
unsigned int no_analog :1; /* digital I/O only */
unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
int init_amp;
+ int codec_variant; /* flag for other variants */
/* for virtual master */
hda_nid_t vmaster_nid;
@@ -846,7 +858,7 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
{
unsigned int val = PIN_IN;
- if (auto_pin_type <= AUTO_PIN_FRONT_MIC) {
+ if (auto_pin_type == AUTO_PIN_MIC) {
unsigned int pincap;
unsigned int oldval;
oldval = snd_hda_codec_read(codec, nid, 0,
@@ -866,6 +878,28 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
}
+static void alc_fixup_autocfg_pin_nums(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ if (!cfg->line_outs) {
+ while (cfg->line_outs < AUTO_CFG_MAX_OUTS &&
+ cfg->line_out_pins[cfg->line_outs])
+ cfg->line_outs++;
+ }
+ if (!cfg->speaker_outs) {
+ while (cfg->speaker_outs < AUTO_CFG_MAX_OUTS &&
+ cfg->speaker_pins[cfg->speaker_outs])
+ cfg->speaker_outs++;
+ }
+ if (!cfg->hp_outs) {
+ while (cfg->hp_outs < AUTO_CFG_MAX_OUTS &&
+ cfg->hp_pins[cfg->hp_outs])
+ cfg->hp_outs++;
+ }
+}
+
/*
*/
static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
@@ -934,6 +968,8 @@ static void setup_preset(struct hda_codec *codec,
if (preset->setup)
preset->setup(codec);
+
+ alc_fixup_autocfg_pin_nums(codec);
}
/* Enable GPIO mask and set output */
@@ -990,25 +1026,136 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
alc_fix_pll(codec);
}
-static void alc_automute_pin(struct hda_codec *codec)
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+static void alc_free_jack_priv(struct snd_jack *jack)
+{
+ struct alc_jack *jacks = jack->private_data;
+ jacks->nid = 0;
+ jacks->jack = NULL;
+}
+
+static int alc_add_jack(struct hda_codec *codec,
+ hda_nid_t nid, int type)
+{
+ struct alc_spec *spec;
+ struct alc_jack *jack;
+ const char *name;
+ int err;
+
+ spec = codec->spec;
+ snd_array_init(&spec->jacks, sizeof(*jack), 32);
+ jack = snd_array_new(&spec->jacks);
+ if (!jack)
+ return -ENOMEM;
+
+ jack->nid = nid;
+ jack->type = type;
+ name = (type == SND_JACK_HEADPHONE) ? "Headphone" : "Mic" ;
+
+ err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+ if (err < 0)
+ return err;
+ jack->jack->private_data = jack;
+ jack->jack->private_free = alc_free_jack_priv;
+ return 0;
+}
+
+static void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
{
struct alc_spec *spec = codec->spec;
- unsigned int nid = spec->autocfg.hp_pins[0];
+ struct alc_jack *jacks = spec->jacks.list;
+
+ if (jacks) {
+ int i;
+ for (i = 0; i < spec->jacks.used; i++) {
+ if (jacks->nid == nid) {
+ unsigned int present;
+ present = snd_hda_jack_detect(codec, nid);
+
+ present = (present) ? jacks->type : 0;
+
+ snd_jack_report(jacks->jack, present);
+ }
+ jacks++;
+ }
+ }
+}
+
+static int alc_init_jacks(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ unsigned int hp_nid = spec->autocfg.hp_pins[0];
+ unsigned int mic_nid = spec->ext_mic.pin;
+
+ if (hp_nid) {
+ err = alc_add_jack(codec, hp_nid, SND_JACK_HEADPHONE);
+ if (err < 0)
+ return err;
+ alc_report_jack(codec, hp_nid);
+ }
+
+ if (mic_nid) {
+ err = alc_add_jack(codec, mic_nid, SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
+ alc_report_jack(codec, mic_nid);
+ }
+
+ return 0;
+}
+#else
+static inline void alc_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+}
+
+static inline int alc_init_jacks(struct hda_codec *codec)
+{
+ return 0;
+}
+#endif
+
+static void alc_automute_speaker(struct hda_codec *codec, int pinctl)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute;
+ hda_nid_t nid;
int i;
- if (!nid)
- return;
- spec->jack_present = snd_hda_jack_detect(codec, nid);
+ spec->jack_present = 0;
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+ nid = spec->autocfg.hp_pins[i];
+ if (!nid)
+ break;
+ if (snd_hda_jack_detect(codec, nid)) {
+ spec->jack_present = 1;
+ break;
+ }
+ alc_report_jack(codec, spec->autocfg.hp_pins[i]);
+ }
+
+ mute = spec->jack_present ? HDA_AMP_MUTE : 0;
+ /* Toggle internal speakers muting */
for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
nid = spec->autocfg.speaker_pins[i];
if (!nid)
break;
- snd_hda_codec_write(codec, nid, 0,
+ if (pinctl) {
+ snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL,
spec->jack_present ? 0 : PIN_OUT);
+ } else {
+ snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
}
}
+static void alc_automute_pin(struct hda_codec *codec)
+{
+ alc_automute_speaker(codec, 1);
+}
+
static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid)
{
@@ -1090,6 +1237,7 @@ static void alc_mic_automute(struct hda_codec *codec)
AC_VERB_SET_CONNECT_SEL,
alive->mux_idx);
}
+ alc_report_jack(codec, spec->ext_mic.pin);
/* FIXME: analog mixer */
}
@@ -1236,24 +1384,35 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
static void alc_init_auto_hp(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
- if (!spec->autocfg.hp_pins[0])
- return;
+ if (!cfg->hp_pins[0]) {
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ return;
+ }
- if (!spec->autocfg.speaker_pins[0]) {
- if (spec->autocfg.line_out_pins[0] &&
- spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
- spec->autocfg.speaker_pins[0] =
- spec->autocfg.line_out_pins[0];
- else
+ if (!cfg->speaker_pins[0]) {
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
return;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = cfg->line_outs;
}
- snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
- spec->autocfg.hp_pins[0]);
- snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+ if (!cfg->hp_pins[0]) {
+ memcpy(cfg->hp_pins, cfg->line_out_pins,
+ sizeof(cfg->hp_pins));
+ cfg->hp_outs = cfg->line_outs;
+ }
+
+ for (i = 0; i < cfg->hp_outs; i++) {
+ snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+ cfg->hp_pins[i]);
+ snd_hda_codec_write_cache(codec, cfg->hp_pins[i], 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
AC_USRSP_EN | ALC880_HP_EVENT);
+ }
spec->unsol_event = alc_sku_unsol_event;
}
@@ -1265,30 +1424,28 @@ static void alc_init_auto_mic(struct hda_codec *codec)
int i;
/* there must be only two mic inputs exclusively */
- for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
- if (cfg->input_pins[i])
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
return;
fixed = ext = 0;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
- hda_nid_t nid = cfg->input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
unsigned int defcfg;
- if (!nid)
- return;
defcfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (get_defcfg_connect(defcfg)) {
- case AC_JACK_PORT_FIXED:
+ switch (snd_hda_get_input_pin_attr(defcfg)) {
+ case INPUT_PIN_ATTR_INT:
if (fixed)
return; /* already occupied */
fixed = nid;
break;
- case AC_JACK_PORT_COMPLEX:
+ case INPUT_PIN_ATTR_UNUSED:
+ return; /* invalid entry */
+ default:
if (ext)
return; /* already occupied */
ext = nid;
break;
- default:
- return; /* invalid entry */
}
}
if (!ext || !fixed)
@@ -1308,6 +1465,11 @@ static void alc_init_auto_mic(struct hda_codec *codec)
spec->unsol_event = alc_sku_unsol_event;
}
+/* Could be any non-zero and even value. When used as fixup, tells
+ * the driver to ignore any present sku defines.
+ */
+#define ALC_FIXUP_SKU_IGNORE (2)
+
static int alc_auto_parse_customize_define(struct hda_codec *codec)
{
unsigned int ass, tmp, i;
@@ -1316,6 +1478,13 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec)
spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return -1;
+ goto do_sku;
+ }
+
ass = codec->subsystem_id & 0xffff;
if (ass != codec->bus->pci->subsystem_device && (ass & 1))
goto do_sku;
@@ -1383,6 +1552,13 @@ static int alc_subsystem_id(struct hda_codec *codec,
unsigned nid;
struct alc_spec *spec = codec->spec;
+ if (spec->cdefine.fixup) {
+ ass = spec->cdefine.sku_cfg;
+ if (ass == ALC_FIXUP_SKU_IGNORE)
+ return 0;
+ goto do_sku;
+ }
+
ass = codec->subsystem_id & 0xffff;
if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
goto do_sku;
@@ -1502,6 +1678,7 @@ struct alc_pincfg {
};
struct alc_fixup {
+ unsigned int sku;
const struct alc_pincfg *pins;
const struct hda_verb *verbs;
};
@@ -1512,12 +1689,22 @@ static void alc_pick_fixup(struct hda_codec *codec,
int pre_init)
{
const struct alc_pincfg *cfg;
+ struct alc_spec *spec;
quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
if (!quirk)
return;
fix += quirk->value;
cfg = fix->pins;
+ if (pre_init && fix->sku) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+ snd_printdd(KERN_INFO "hda_codec: %s: Apply sku override for %s\n",
+ codec->chip_name, quirk->name);
+#endif
+ spec = codec->spec;
+ spec->cdefine.sku_cfg = fix->sku;
+ spec->cdefine.fixup = 1;
+ }
if (pre_init && cfg) {
#ifdef CONFIG_SND_DEBUG_VERBOSE
snd_printdd(KERN_INFO "hda_codec: %s: Apply pincfg for %s\n",
@@ -1546,6 +1733,15 @@ static int alc_read_coef_idx(struct hda_codec *codec,
return val;
}
+static void alc_write_coef_idx(struct hda_codec *codec, unsigned int coef_idx,
+ unsigned int coef_val)
+{
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+ coef_idx);
+ snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF,
+ coef_val);
+}
+
/* set right pin controls for digital I/O */
static void alc_auto_init_digital(struct hda_codec *codec)
{
@@ -1723,31 +1919,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
static void alc_automute_amp(struct hda_codec *codec)
{
- struct alc_spec *spec = codec->spec;
- unsigned int mute;
- hda_nid_t nid;
- int i;
-
- spec->jack_present = 0;
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
- nid = spec->autocfg.hp_pins[i];
- if (!nid)
- break;
- if (snd_hda_jack_detect(codec, nid)) {
- spec->jack_present = 1;
- break;
- }
- }
-
- mute = spec->jack_present ? HDA_AMP_MUTE : 0;
- /* Toggle internal speakers muting */
- for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
- nid = spec->autocfg.speaker_pins[i];
- if (!nid)
- break;
- snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
- HDA_AMP_MUTE, mute);
- }
+ alc_automute_speaker(codec, 0);
}
static void alc_automute_amp_unsol_event(struct hda_codec *codec,
@@ -3602,10 +3774,7 @@ static int alc_init(struct hda_codec *codec)
if (spec->init_hook)
spec->init_hook(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -4001,10 +4170,7 @@ static int alc_resume(struct hda_codec *codec)
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
- if (codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
#endif
@@ -4729,7 +4895,7 @@ static struct snd_kcontrol_new alc880_control_templates[] = {
/* add dynamic controls */
static int add_control(struct alc_spec *spec, int type, const char *name,
- unsigned long val)
+ int cidx, unsigned long val)
{
struct snd_kcontrol_new *knew;
@@ -4741,6 +4907,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
knew->name = kstrdup(name, GFP_KERNEL);
if (!knew->name)
return -ENOMEM;
+ knew->index = cidx;
if (get_amp_nid_(val))
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
knew->private_value = val;
@@ -4749,17 +4916,21 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
static int add_control_with_pfx(struct alc_spec *spec, int type,
const char *pfx, const char *dir,
- const char *sfx, unsigned long val)
+ const char *sfx, int cidx, unsigned long val)
{
char name[32];
snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
- return add_control(spec, type, name, val);
+ return add_control(spec, type, name, cidx, val);
}
-#define add_pb_vol_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
-#define add_pb_sw_ctrl(spec, type, pfx, val) \
- add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val)
+#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val)
+#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \
+ add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val)
#define alc880_is_fixed_pin(nid) ((nid) >= 0x14 && (nid) <= 0x17)
#define alc880_fixed_pin_idx(nid) ((nid) - 0x14)
@@ -4912,16 +5083,16 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
/* create input playback/capture controls for the given pin */
static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
- const char *ctlname,
+ const char *ctlname, int ctlidx,
int idx, hda_nid_t mix_nid)
{
int err;
- err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
+ err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
- err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
+ err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -4942,20 +5113,27 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
{
struct alc_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx;
+ int i, err, idx, type, type_idx = 0;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
+ for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t pin;
+ const char *label;
- pin = cfg->input_pins[i];
+ pin = cfg->inputs[i].pin;
if (!alc_is_input_pin(codec, pin))
continue;
+ type = cfg->inputs[i].type;
+ if (i > 0 && type == cfg->inputs[i - 1].type)
+ type_idx++;
+ else
+ type_idx = 0;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
if (mixer) {
idx = get_connection_index(codec, mixer, pin);
if (idx >= 0) {
err = new_analog_input(spec, pin,
- auto_pin_cfg_labels[i],
+ label, type_idx,
idx, mixer);
if (err < 0)
return err;
@@ -4967,12 +5145,8 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec,
idx = get_connection_index(codec, cap1, pin);
if (idx < 0 && cap2)
idx = get_connection_index(codec, cap2, pin);
- if (idx >= 0) {
- imux->items[imux->num_items].label =
- auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
+ if (idx >= 0)
+ snd_hda_add_imux_item(imux, label, idx, NULL);
}
return 0;
}
@@ -5044,12 +5218,13 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec)
static void alc880_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC880_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -5214,19 +5389,13 @@ static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
static void fixup_single_adc(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t pin = 0;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
/* search for the input pin; there must be only one */
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (spec->autocfg.input_pins[i]) {
- pin = spec->autocfg.input_pins[i];
- break;
- }
- }
- if (!pin)
+ if (cfg->num_inputs != 1)
return;
- i = init_capsrc_for_pin(codec, pin);
+ i = init_capsrc_for_pin(codec, cfg->inputs[0].pin);
if (i >= 0) {
/* use only this ADC */
if (spec->capsrc_nids)
@@ -5279,6 +5448,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
int num_nids)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int n;
hda_nid_t fallback_adc = 0, fallback_cap = 0;
@@ -5304,10 +5474,8 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
fallback_adc = adc;
fallback_cap = cap;
}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
for (j = 0; j < nconns; j++) {
if (conn[j] == nid)
break;
@@ -5315,7 +5483,7 @@ static void fillup_priv_adc_nids(struct hda_codec *codec, hda_nid_t *nids,
if (j >= nconns)
break;
}
- if (i >= AUTO_PIN_LAST) {
+ if (i >= cfg->num_inputs) {
int num_adcs = spec->num_adc_nids;
spec->private_adc_nids[num_adcs] = adc;
spec->private_capsrc_nids[num_adcs] = cap;
@@ -6683,12 +6851,13 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)
static void alc260_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (nid >= 0x12) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC260_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -6810,14 +6979,12 @@ enum {
PINFIX_HP_DC5750,
};
-static struct alc_pincfg alc260_hp_dc5750_pinfix[] = {
- { 0x11, 0x90130110 }, /* speaker */
- { }
-};
-
static const struct alc_fixup alc260_fixups[] = {
[PINFIX_HP_DC5750] = {
- .pins = alc260_hp_dc5750_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x11, 0x90130110 }, /* speaker */
+ { }
+ }
},
};
@@ -10461,32 +10628,33 @@ static struct alc_config_preset alc882_presets[] = {
enum {
PINFIX_ABIT_AW9D_MAX,
PINFIX_PB_M5210,
-};
-
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
- { 0x15, 0x01080104 }, /* side */
- { 0x16, 0x01011012 }, /* rear */
- { 0x17, 0x01016011 }, /* clfe */
- { }
-};
-
-static const struct hda_verb pb_m5210_verbs[] = {
- { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
- {}
+ PINFIX_ACER_ASPIRE_7736,
};
static const struct alc_fixup alc882_fixups[] = {
[PINFIX_ABIT_AW9D_MAX] = {
- .pins = alc882_abit_aw9d_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x15, 0x01080104 }, /* side */
+ { 0x16, 0x01011012 }, /* rear */
+ { 0x17, 0x01016011 }, /* clfe */
+ { }
+ }
},
[PINFIX_PB_M5210] = {
- .verbs = pb_m5210_verbs
+ .verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ {}
+ }
+ },
+ [PINFIX_ACER_ASPIRE_7736] = {
+ .sku = ALC_FIXUP_SKU_IGNORE,
},
};
static struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x0155, "Packard-Bell M5120", PINFIX_PB_M5210),
SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+ SND_PCI_QUIRK(0x1025, 0x0296, "Acer Aspire 7736z", PINFIX_ACER_ASPIRE_7736),
{}
};
@@ -10535,16 +10703,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t pin, dac;
+ int i;
- pin = spec->autocfg.hp_pins[0];
- if (pin) {
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+ pin = spec->autocfg.hp_pins[i];
+ if (!pin)
+ break;
dac = spec->multiout.hp_nid;
if (!dac)
dac = spec->multiout.dac_nids[0]; /* to front */
alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
}
- pin = spec->autocfg.speaker_pins[0];
- if (pin) {
+ for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+ pin = spec->autocfg.speaker_pins[i];
+ if (!pin)
+ break;
dac = spec->multiout.extra_out_nid[0];
if (!dac)
dac = spec->multiout.dac_nids[0]; /* to front */
@@ -10555,13 +10728,12 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec)
static void alc882_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
- alc_set_input_pin(codec, nid, i);
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
@@ -10623,24 +10795,23 @@ static void alc882_auto_init_input_src(struct hda_codec *codec)
static int alc_auto_add_mic_boost(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- int err;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
hda_nid_t nid;
- nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Mic Boost",
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
- }
- nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
- err = add_control(spec, ALC_CTL_WIDGET_VOL,
- "Front Mic Boost",
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type > AUTO_PIN_MIC)
+ break;
+ nid = cfg->inputs[i].pin;
+ if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+ char label[32];
+ snprintf(label, sizeof(label), "%s Boost",
+ hda_get_autocfg_input_label(codec, cfg, i));
+ err = add_control(spec, ALC_CTL_WIDGET_VOL, label, 0,
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
- if (err < 0)
- return err;
+ if (err < 0)
+ return err;
+ }
}
return 0;
}
@@ -10726,8 +10897,6 @@ static int patch_alc882(struct hda_codec *codec)
codec->spec = spec;
- alc_auto_parse_customize_define(codec);
-
switch (codec->vendor_id) {
case 0x10ec0882:
case 0x10ec0885:
@@ -10755,6 +10924,8 @@ static int patch_alc882(struct hda_codec *codec)
if (board_config == ALC882_AUTO)
alc_pick_fixup(codec, alc882_fixup_tbl, alc882_fixups, 1);
+ alc_auto_parse_customize_define(codec);
+
if (board_config == ALC882_AUTO) {
/* automatic parse from the BIOS config */
err = alc882_parse_auto_config(codec);
@@ -10835,6 +11006,8 @@ static int patch_alc882(struct hda_codec *codec)
codec->patch_ops = alc_patch_ops;
if (board_config == ALC882_AUTO)
spec->init_hook = alc882_auto_init;
+
+ alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc882_loopbacks;
@@ -11831,7 +12004,7 @@ static int alc262_check_volbit(hda_nid_t nid)
}
static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx, int *vbits)
+ const char *pfx, int *vbits, int idx)
{
unsigned long val;
int vbit;
@@ -11846,11 +12019,11 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
- return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
+ return __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, idx, val);
}
static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
- const char *pfx)
+ const char *pfx, int idx)
{
unsigned long val;
@@ -11860,7 +12033,7 @@ static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
else
val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
- return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
+ return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, idx, val);
}
/* add playback controls from the parsed DAC table */
@@ -11869,7 +12042,7 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
{
const char *pfx;
int vbits;
- int err;
+ int i, err;
spec->multiout.num_dacs = 1; /* only use one dac */
spec->multiout.dac_nids = spec->private_dac_nids;
@@ -11879,39 +12052,52 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
pfx = "Master";
else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
pfx = "Speaker";
+ else if (cfg->line_out_type == AUTO_PIN_HP_OUT)
+ pfx = "Headphone";
else
pfx = "Front";
- err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx);
- if (err < 0)
- return err;
- err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker");
- if (err < 0)
- return err;
- err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone");
- if (err < 0)
- return err;
+ for (i = 0; i < 2; i++) {
+ err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[i],
+ "Speaker", i);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[i],
+ "Headphone", i);
+ if (err < 0)
+ return err;
+ }
+ }
vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
alc262_check_volbit(cfg->speaker_pins[0]) |
alc262_check_volbit(cfg->hp_pins[0]);
if (vbits == 1 || vbits == 2)
pfx = "Master"; /* only one mixer is used */
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = "Front";
vbits = 0;
- err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits);
- if (err < 0)
- return err;
- err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker",
- &vbits);
- if (err < 0)
- return err;
- err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone",
- &vbits);
- if (err < 0)
- return err;
+ for (i = 0; i < 2; i++) {
+ err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
+ &vbits, i);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[i],
+ "Speaker", &vbits, i);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[i],
+ "Headphone", &vbits, i);
+ if (err < 0)
+ return err;
+ }
+ }
return 0;
}
@@ -12199,6 +12385,35 @@ static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = {
{}
};
+/*
+ * Pin config fixes
+ */
+enum {
+ PINFIX_FSC_H270,
+};
+
+static const struct alc_fixup alc262_fixups[] = {
+ [PINFIX_FSC_H270] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x14, 0x99130110 }, /* speaker */
+ { 0x15, 0x0221142f }, /* front HP */
+ { 0x1b, 0x0121141f }, /* rear HP */
+ { }
+ }
+ },
+ [PINFIX_PB_M5210] = {
+ .verbs = (const struct hda_verb[]) {
+ { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 },
+ {}
+ }
+ },
+};
+
+static struct snd_pci_quirk alc262_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
+ {}
+};
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc262_loopbacks alc880_loopbacks
@@ -12622,6 +12837,9 @@ static int patch_alc262(struct hda_codec *codec)
board_config = ALC262_AUTO;
}
+ if (board_config == ALC262_AUTO)
+ alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 1);
+
if (board_config == ALC262_AUTO) {
/* automatic parse from the BIOS config */
err = alc262_parse_auto_config(codec);
@@ -12690,11 +12908,16 @@ static int patch_alc262(struct hda_codec *codec)
if (!spec->no_analog && has_cdefine_beep(codec))
set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+ if (board_config == ALC262_AUTO)
+ alc_pick_fixup(codec, alc262_fixup_tbl, alc262_fixups, 0);
+
spec->vmaster_nid = 0x0c;
codec->patch_ops = alc_patch_ops;
if (board_config == ALC262_AUTO)
spec->init_hook = alc262_auto_init;
+
+ alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc262_loopbacks;
@@ -13310,8 +13533,10 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
static void alc268_auto_init_multi_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t nid = spec->autocfg.line_out_pins[0];
- if (nid) {
+ int i;
+
+ for (i = 0; i < spec->autocfg.line_outs; i++) {
+ hda_nid_t nid = spec->autocfg.line_out_pins[i];
int pin_type = get_pin_type(spec->autocfg.line_out_type);
alc268_auto_set_output_and_unmute(codec, nid, pin_type);
}
@@ -13321,13 +13546,19 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t pin;
+ int i;
- pin = spec->autocfg.hp_pins[0];
- if (pin)
+ for (i = 0; i < spec->autocfg.hp_outs; i++) {
+ pin = spec->autocfg.hp_pins[i];
alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
+ }
+ for (i = 0; i < spec->autocfg.speaker_outs; i++) {
+ pin = spec->autocfg.speaker_pins[i];
alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
+ }
+ if (spec->autocfg.mono_out_pin)
+ snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
}
static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -13766,6 +13997,8 @@ static int patch_alc268(struct hda_codec *codec)
if (board_config == ALC268_AUTO)
spec->init_hook = alc268_auto_init;
+ alc_init_jacks(codec);
+
return 0;
}
@@ -14132,6 +14365,7 @@ static void alc269_speaker_automute(struct hda_codec *codec)
HDA_AMP_MUTE, bits);
snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
HDA_AMP_MUTE, bits);
+ alc_report_jack(codec, nid);
}
/* unsolicited event for HP jack sensing */
@@ -14386,6 +14620,13 @@ static int alc275_setup_dual_adc(struct hda_codec *codec)
return 0;
}
+/* different alc269-variants */
+enum {
+ ALC269_TYPE_NORMAL,
+ ALC269_TYPE_ALC259,
+ ALC269_TYPE_ALC271X,
+};
+
/*
* BIOS auto configuration
*/
@@ -14403,7 +14644,11 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
if (err < 0)
return err;
- err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+ if (spec->codec_variant == ALC269_TYPE_NORMAL)
+ err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
+ else
+ err = alc_auto_create_input_ctls(codec, &spec->autocfg, 0,
+ 0x22, 0);
if (err < 0)
return err;
@@ -14414,7 +14659,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) {
+ if (spec->codec_variant != ALC269_TYPE_NORMAL) {
add_verb(spec, alc269vb_init_verbs);
alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
} else {
@@ -14461,19 +14706,71 @@ static void alc269_auto_init(struct hda_codec *codec)
alc_inithook(codec);
}
+#ifdef SND_HDA_NEEDS_RESUME
+static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
+{
+ int val = alc_read_coef_idx(codec, 0x04);
+ if (power_up)
+ val |= 1 << 11;
+ else
+ val &= ~(1 << 11);
+ alc_write_coef_idx(codec, 0x04, val);
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int alc269_suspend(struct hda_codec *codec, pm_message_t state)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017)
+ alc269_toggle_power_output(codec, 0);
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ alc269_toggle_power_output(codec, 0);
+ msleep(150);
+ }
+
+ alc_shutup(codec);
+ if (spec && spec->power_hook)
+ spec->power_hook(codec);
+ return 0;
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
+static int alc269_resume(struct hda_codec *codec)
+{
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ alc269_toggle_power_output(codec, 0);
+ msleep(150);
+ }
+
+ codec->patch_ops.init(codec);
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+ alc269_toggle_power_output(codec, 1);
+ msleep(200);
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018)
+ alc269_toggle_power_output(codec, 1);
+
+ snd_hda_codec_resume_amp(codec);
+ snd_hda_codec_resume_cache(codec);
+ hda_call_check_power_status(codec, 0x01);
+ return 0;
+}
+#endif /* SND_HDA_NEEDS_RESUME */
+
enum {
ALC269_FIXUP_SONY_VAIO,
ALC269_FIXUP_DELL_M101Z,
};
-static const struct hda_verb alc269_sony_vaio_fixup_verbs[] = {
- {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
- {}
-};
-
static const struct alc_fixup alc269_fixups[] = {
[ALC269_FIXUP_SONY_VAIO] = {
- .verbs = alc269_sony_vaio_fixup_verbs
+ .verbs = (const struct hda_verb[]) {
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD},
+ {}
+ }
},
[ALC269_FIXUP_DELL_M101Z] = {
.verbs = (const struct hda_verb[]) {
@@ -14486,8 +14783,7 @@ static const struct alc_fixup alc269_fixups[] = {
};
static struct snd_pci_quirk alc269_fixup_tbl[] = {
- SND_PCI_QUIRK(0x104d, 0x9071, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
- SND_PCI_QUIRK(0x104d, 0x9077, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
+ SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
{}
};
@@ -14689,12 +14985,46 @@ static struct alc_config_preset alc269_presets[] = {
},
};
+static int alc269_fill_coef(struct hda_codec *codec)
+{
+ int val;
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) < 0x015) {
+ alc_write_coef_idx(codec, 0xf, 0x960b);
+ alc_write_coef_idx(codec, 0xe, 0x8817);
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x016) {
+ alc_write_coef_idx(codec, 0xf, 0x960b);
+ alc_write_coef_idx(codec, 0xe, 0x8814);
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x017) {
+ val = alc_read_coef_idx(codec, 0x04);
+ /* Power up output pin */
+ alc_write_coef_idx(codec, 0x04, val | (1<<11));
+ }
+
+ if ((alc_read_coef_idx(codec, 0) & 0x00ff) == 0x018) {
+ val = alc_read_coef_idx(codec, 0xd);
+ if ((val & 0x0c00) >> 10 != 0x1) {
+ /* Capless ramp up clock control */
+ alc_write_coef_idx(codec, 0xd, val | 1<<10);
+ }
+ val = alc_read_coef_idx(codec, 0x17);
+ if ((val & 0x01c0) >> 6 != 0x4) {
+ /* Class D power on reset */
+ alc_write_coef_idx(codec, 0x17, val | 1<<7);
+ }
+ }
+ return 0;
+}
+
static int patch_alc269(struct hda_codec *codec)
{
struct alc_spec *spec;
int board_config;
int err;
- int is_alc269vb = 0;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (spec == NULL)
@@ -14706,14 +15036,18 @@ static int patch_alc269(struct hda_codec *codec)
if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
if (codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1)
+ spec->cdefine.platform_type == 1) {
alc_codec_rename(codec, "ALC271X");
- else
+ spec->codec_variant = ALC269_TYPE_ALC271X;
+ } else {
alc_codec_rename(codec, "ALC259");
- is_alc269vb = 1;
+ spec->codec_variant = ALC269_TYPE_ALC259;
+ }
} else
alc_fix_pll_init(codec, 0x20, 0x04, 15);
+ alc269_fill_coef(codec);
+
board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
alc269_models,
alc269_cfg_tbl);
@@ -14770,7 +15104,7 @@ static int patch_alc269(struct hda_codec *codec)
spec->stream_digital_capture = &alc269_pcm_digital_capture;
if (!spec->adc_nids) { /* wasn't filled automatically? use default */
- if (!is_alc269vb) {
+ if (spec->codec_variant != ALC269_TYPE_NORMAL) {
spec->adc_nids = alc269_adc_nids;
spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
spec->capsrc_nids = alc269_capsrc_nids;
@@ -14792,8 +15126,16 @@ static int patch_alc269(struct hda_codec *codec)
spec->vmaster_nid = 0x02;
codec->patch_ops = alc_patch_ops;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ codec->patch_ops.suspend = alc269_suspend;
+#endif
+#ifdef SND_HDA_NEEDS_RESUME
+ codec->patch_ops.resume = alc269_resume;
+#endif
if (board_config == ALC269_AUTO)
spec->init_hook = alc269_auto_init;
+
+ alc_init_jacks(codec);
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc269_loopbacks;
@@ -15606,12 +15948,13 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
static void alc861_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (nid >= 0x0c && nid <= 0x11)
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
}
}
@@ -15840,15 +16183,13 @@ enum {
PINFIX_FSC_AMILO_PI1505,
};
-static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
- { 0x0b, 0x0221101f }, /* HP */
- { 0x0f, 0x90170310 }, /* speaker */
- { }
-};
-
static const struct alc_fixup alc861_fixups[] = {
[PINFIX_FSC_AMILO_PI1505] = {
- .pins = alc861_fsc_amilo_pi1505_pinfix
+ .pins = (const struct alc_pincfg[]) {
+ { 0x0b, 0x0221101f }, /* HP */
+ { 0x0f, 0x90170310 }, /* speaker */
+ { }
+ }
},
};
@@ -16600,12 +16941,13 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC861VD_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -16815,16 +17157,14 @@ enum {
};
/* reset GPIO1 */
-static const struct hda_verb alc660vd_fix_asus_gpio1_verbs[] = {
- {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
- {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
- {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
- { }
-};
-
static const struct alc_fixup alc861vd_fixups[] = {
[ALC660VD_FIX_ASUS_GPIO1] = {
- .verbs = alc660vd_fix_asus_gpio1_verbs,
+ .verbs = (const struct hda_verb[]) {
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x03},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+ { }
+ }
},
};
@@ -18838,12 +19178,13 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec)
static void alc662_auto_init_analog_input(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, i);
+ alc_set_input_pin(codec, nid, cfg->inputs[i].type);
if (nid != ALC662_PIN_CD_NID &&
(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, nid, 0,
@@ -18935,10 +19276,40 @@ static void alc662_auto_init(struct hda_codec *codec)
alc_inithook(codec);
}
+enum {
+ ALC662_FIXUP_ASPIRE,
+ ALC662_FIXUP_IDEAPAD,
+};
+
+static const struct alc_fixup alc662_fixups[] = {
+ [ALC662_FIXUP_ASPIRE] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x15, 0x99130112 }, /* subwoofer */
+ { }
+ }
+ },
+ [ALC662_FIXUP_IDEAPAD] = {
+ .pins = (const struct alc_pincfg[]) {
+ { 0x17, 0x99130112 }, /* subwoofer */
+ { }
+ }
+ },
+};
+
+static struct snd_pci_quirk alc662_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
+ SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
+ {}
+};
+
+
+
static int patch_alc662(struct hda_codec *codec)
{
struct alc_spec *spec;
int err, board_config;
+ int coef;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
if (!spec)
@@ -18950,12 +19321,15 @@ static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- if (alc_read_coef_idx(codec, 0) == 0x8020)
+ coef = alc_read_coef_idx(codec, 0);
+ if (coef == 0x8020 || coef == 0x8011)
alc_codec_rename(codec, "ALC661");
- else if ((alc_read_coef_idx(codec, 0) & (1 << 14)) &&
- codec->bus->pci->subsystem_vendor == 0x1025 &&
- spec->cdefine.platform_type == 1)
+ else if (coef & (1 << 14) &&
+ codec->bus->pci->subsystem_vendor == 0x1025 &&
+ spec->cdefine.platform_type == 1)
alc_codec_rename(codec, "ALC272X");
+ else if (coef == 0x4011)
+ alc_codec_rename(codec, "ALC656");
board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
alc662_models,
@@ -18967,6 +19341,7 @@ static int patch_alc662(struct hda_codec *codec)
}
if (board_config == ALC662_AUTO) {
+ alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 1);
/* automatic parse from the BIOS config */
err = alc662_parse_auto_config(codec);
if (err < 0) {
@@ -19025,8 +19400,13 @@ static int patch_alc662(struct hda_codec *codec)
spec->vmaster_nid = 0x02;
codec->patch_ops = alc_patch_ops;
- if (board_config == ALC662_AUTO)
+ if (board_config == ALC662_AUTO) {
spec->init_hook = alc662_auto_init;
+ alc_pick_fixup(codec, alc662_fixup_tbl, alc662_fixups, 0);
+ }
+
+ alc_init_jacks(codec);
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
if (!spec->loopback.amplist)
spec->loopback.amplist = alc662_loopbacks;
@@ -19070,6 +19450,39 @@ static hda_nid_t alc680_adc_nids[3] = {
/*
* Analog capture ADC cgange
*/
+static void alc680_rec_autoswitch(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int pin_found = 0;
+ int type_found = AUTO_PIN_LAST;
+ hda_nid_t nid;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ if (!(snd_hda_query_pin_caps(codec, nid) &
+ AC_PINCAP_PRES_DETECT))
+ continue;
+ if (snd_hda_jack_detect(codec, nid)) {
+ if (cfg->inputs[i].type < type_found) {
+ type_found = cfg->inputs[i].type;
+ pin_found = nid;
+ }
+ }
+ }
+
+ nid = 0x07;
+ if (pin_found)
+ snd_hda_get_connections(codec, pin_found, &nid, 1);
+
+ if (nid != spec->cur_adc)
+ __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
+ spec->cur_adc = nid;
+ snd_hda_codec_setup_stream(codec, nid, spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+}
+
static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
@@ -19077,24 +19490,12 @@ static int alc680_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int pre_mic, pre_line;
-
- pre_mic = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
- pre_line = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_LINE]);
+ spec->cur_adc = 0x07;
spec->cur_adc_stream_tag = stream_tag;
spec->cur_adc_format = format;
- if (pre_mic || pre_line) {
- if (pre_mic)
- snd_hda_codec_setup_stream(codec, 0x08, stream_tag, 0,
- format);
- else
- snd_hda_codec_setup_stream(codec, 0x09, stream_tag, 0,
- format);
- } else
- snd_hda_codec_setup_stream(codec, 0x07, stream_tag, 0, format);
+ alc680_rec_autoswitch(codec);
return 0;
}
@@ -19180,6 +19581,7 @@ static struct hda_verb alc680_init_verbs[] = {
{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+ {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
{ }
};
@@ -19192,25 +19594,11 @@ static void alc680_base_setup(struct hda_codec *codec)
spec->autocfg.hp_pins[0] = 0x16;
spec->autocfg.speaker_pins[0] = 0x14;
spec->autocfg.speaker_pins[1] = 0x15;
- spec->autocfg.input_pins[AUTO_PIN_MIC] = 0x18;
- spec->autocfg.input_pins[AUTO_PIN_LINE] = 0x19;
-}
-
-static void alc680_rec_autoswitch(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- unsigned int present;
- hda_nid_t new_adc;
-
- present = snd_hda_jack_detect(codec, cfg->input_pins[AUTO_PIN_MIC]);
-
- new_adc = present ? 0x8 : 0x7;
- __snd_hda_codec_cleanup_stream(codec, !present ? 0x8 : 0x7, 1);
- snd_hda_codec_setup_stream(codec, new_adc,
- spec->cur_adc_stream_tag, 0,
- spec->cur_adc_format);
-
+ spec->autocfg.num_inputs = 2;
+ spec->autocfg.inputs[0].pin = 0x18;
+ spec->autocfg.inputs[0].type = AUTO_PIN_MIC;
+ spec->autocfg.inputs[1].pin = 0x19;
+ spec->autocfg.inputs[1].type = AUTO_PIN_LINE_IN;
}
static void alc680_unsol_event(struct hda_codec *codec,
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index c16c5ba0fda..82ebeb9544f 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -32,6 +32,7 @@
#include <sound/core.h>
#include <sound/asoundef.h>
#include <sound/jack.h>
+#include <sound/tlv.h>
#include "hda_codec.h"
#include "hda_local.h"
#include "hda_beep.h"
@@ -263,6 +264,7 @@ struct sigmatel_spec {
struct sigmatel_mic_route ext_mic;
struct sigmatel_mic_route int_mic;
+ struct sigmatel_mic_route dock_mic;
const char **spdif_labels;
@@ -382,6 +384,11 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
0x03, 0x0c, 0x20, 0x40,
};
+#define STAC92HD83XXX_NUM_DMICS 2
+static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
+ 0x11, 0x20, 0
+};
+
#define STAC92HD83XXX_NUM_CAPS 2
static unsigned long stac92hd83xxx_capvols[] = {
HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
@@ -986,7 +993,7 @@ static struct hda_verb stac9205_core_init[] = {
}
static struct snd_kcontrol_new stac9200_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
@@ -1014,7 +1021,7 @@ static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
};
static struct snd_kcontrol_new stac925x_mixer[] = {
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MIN_MUTE("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
{ } /* end */
};
@@ -1105,9 +1112,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
struct hda_input_mux *smux = &spec->private_smux;
/* check for mute support on SPDIF out */
if (wcaps & AC_WCAP_OUT_AMP) {
- smux->items[smux->num_items].label = "Off";
- smux->items[smux->num_items].index = 0;
- smux->num_items++;
+ snd_hda_add_imux_item(smux, "Off", 0, NULL);
spec->spdif_mute = 1;
}
stac_smux_mixer.count = spec->num_smuxes;
@@ -1140,6 +1145,8 @@ static int stac92xx_build_controls(struct hda_codec *codec)
HDA_OUTPUT, vmaster_tlv);
/* correct volume offset */
vmaster_tlv[2] += vmaster_tlv[3] * spec->volume_offset;
+ /* minimum value is actually mute */
+ vmaster_tlv[3] |= TLV_DB_SCALE_MUTE;
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
vmaster_tlv, slave_vols);
if (err < 0)
@@ -1180,14 +1187,11 @@ static int stac92xx_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- nid = cfg->input_pins[i];
- if (nid) {
- err = stac92xx_add_jack(codec, nid,
- SND_JACK_MICROPHONE);
- if (err < 0)
- return err;
- }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ err = stac92xx_add_jack(codec, nid, SND_JACK_MICROPHONE);
+ if (err < 0)
+ return err;
}
return 0;
@@ -2779,7 +2783,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec;
char name[22];
- if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
&& nid == spec->line_switch)
control = STAC_CTL_WIDGET_IO_SWITCH;
@@ -2791,7 +2795,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
}
if (control) {
- strcpy(name, auto_pin_cfg_labels[idx]);
+ strcpy(name, hda_get_input_pin_label(codec, nid, 1));
return stac92xx_add_control(codec->spec, control,
strcat(name, " Jack Mode"), nid);
}
@@ -2823,41 +2827,49 @@ static hda_nid_t check_line_out_switch(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
unsigned int pincap;
+ int i;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
- nid = cfg->input_pins[AUTO_PIN_LINE];
- pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type == AUTO_PIN_LINE_IN) {
+ nid = cfg->inputs[i].pin;
+ pincap = snd_hda_query_pin_caps(codec, nid);
+ if (pincap & AC_PINCAP_OUT)
+ return nid;
+ }
+ }
return 0;
}
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid);
+
/* check whether the mic-input can be used as line-out */
-static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec, hda_nid_t *dac)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int def_conf, pincap;
- unsigned int mic_pin;
+ int i;
+ *dac = 0;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
- mic_pin = AUTO_PIN_MIC;
- for (;;) {
- hda_nid_t nid = cfg->input_pins[mic_pin];
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ if (cfg->inputs[i].type != AUTO_PIN_MIC)
+ continue;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
/* some laptops have an internal analog microphone
* which can't be used as a output */
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+ if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
pincap = snd_hda_query_pin_caps(codec, nid);
- if (pincap & AC_PINCAP_OUT)
- return nid;
+ if (pincap & AC_PINCAP_OUT) {
+ *dac = get_unassigned_dac(codec, nid);
+ if (*dac)
+ return nid;
+ }
}
- if (mic_pin == AUTO_PIN_MIC)
- mic_pin = AUTO_PIN_FRONT_MIC;
- else
- break;
}
return 0;
}
@@ -3004,17 +3016,14 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
}
}
/* add mic as output */
- nid = check_mic_out_switch(codec);
- if (nid) {
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
- nid, cfg->line_outs);
- cfg->line_out_pins[cfg->line_outs] = nid;
- cfg->line_outs++;
- spec->mic_switch = nid;
- add_spec_dacs(spec, dac);
- }
+ nid = check_mic_out_switch(codec, &dac);
+ if (nid && dac) {
+ snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+ nid, cfg->line_outs);
+ cfg->line_out_pins[cfg->line_outs] = nid;
+ cfg->line_outs++;
+ spec->mic_switch = nid;
+ add_spec_dacs(spec, dac);
}
snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
@@ -3204,13 +3213,13 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
return err;
}
- for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
- nid = cfg->input_pins[idx];
- if (nid) {
- err = stac92xx_add_jack_mode_control(codec, nid, idx);
- if (err < 0)
- return err;
- }
+ for (idx = 0; idx < cfg->num_inputs; idx++) {
+ if (cfg->inputs[idx].type > AUTO_PIN_LINE_IN)
+ break;
+ nid = cfg->inputs[idx].pin;
+ err = stac92xx_add_jack_mode_control(codec, nid, idx);
+ if (err < 0)
+ return err;
}
return 0;
@@ -3256,12 +3265,9 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
return -EINVAL;
- for (i = 0; i < num_cons; i++) {
- mono_mux->items[mono_mux->num_items].label =
- stac92xx_mono_labels[i];
- mono_mux->items[mono_mux->num_items].index = i;
- mono_mux->num_items++;
- }
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(mono_mux, stac92xx_mono_labels[i], i,
+ NULL);
return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX,
"Mono Mux", spec->mono_nid);
@@ -3386,11 +3392,8 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
if (!labels)
labels = stac92xx_spdif_labels;
- for (i = 0; i < num_cons; i++) {
- spdif_mux->items[spdif_mux->num_items].label = labels[i];
- spdif_mux->items[spdif_mux->num_items].index = i;
- spdif_mux->num_items++;
- }
+ for (i = 0; i < num_cons; i++)
+ snd_hda_add_imux_item(spdif_mux, labels[i], i, NULL);
return 0;
}
@@ -3417,7 +3420,7 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
/* create a volume assigned to the given pin (only if supported) */
/* return 1 if the volume control is created */
static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
- const char *label, int direction)
+ const char *label, int idx, int direction)
{
unsigned int caps, nums;
char name[32];
@@ -3434,8 +3437,8 @@ static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
if (!nums)
return 0;
snprintf(name, sizeof(name), "%s Capture Volume", label);
- err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+ err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
if (err < 0)
return err;
return 1;
@@ -3448,27 +3451,14 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
struct hda_input_mux *dimux = &spec->private_dimux;
- int err, i, active_mics;
+ int err, i;
unsigned int def_conf;
- dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
- dimux->items[dimux->num_items].index = 0;
- dimux->num_items++;
-
- active_mics = 0;
- for (i = 0; i < spec->num_dmics; i++) {
- /* check the validity: sometimes it's a dead vendor-spec node */
- if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
- != AC_WID_PIN)
- continue;
- def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
- if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
- active_mics++;
- }
+ snd_hda_add_imux_item(dimux, stac92xx_dmic_labels[0], 0, NULL);
for (i = 0; i < spec->num_dmics; i++) {
hda_nid_t nid;
- int index;
+ int index, type_idx;
const char *label;
nid = spec->dmic_nids[i];
@@ -3482,28 +3472,23 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
if (index < 0)
continue;
- if (active_mics == 1)
- label = "Digital Mic";
- else
- label = stac92xx_dmic_labels[dimux->num_items];
+ label = hda_get_input_pin_label(codec, nid, 1);
+ snd_hda_add_imux_item(dimux, label, index, &type_idx);
- err = create_elem_capture_vol(codec, nid, label, HDA_INPUT);
+ err = create_elem_capture_vol(codec, nid, label, type_idx,
+ HDA_INPUT);
if (err < 0)
return err;
if (!err) {
err = create_elem_capture_vol(codec, nid, label,
- HDA_OUTPUT);
+ type_idx, HDA_OUTPUT);
if (err < 0)
return err;
}
- dimux->items[dimux->num_items].label = label;
- dimux->items[dimux->num_items].index = index;
- dimux->num_items++;
if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
- imux->items[imux->num_items].label = label;
- imux->items[imux->num_items].index = index;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, label, index, NULL);
+ spec->num_analog_muxes++;
}
}
@@ -3511,20 +3496,27 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
}
static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *fixed, hda_nid_t *ext)
+ hda_nid_t *fixed, hda_nid_t *ext, hda_nid_t *dock)
{
unsigned int cfg;
if (!nid)
return 0;
cfg = snd_hda_codec_get_pincfg(codec, nid);
- switch (get_defcfg_connect(cfg)) {
- case AC_JACK_PORT_FIXED:
+ switch (snd_hda_get_input_pin_attr(cfg)) {
+ case INPUT_PIN_ATTR_INT:
if (*fixed)
return 1; /* already occupied */
*fixed = nid;
break;
- case AC_JACK_PORT_COMPLEX:
+ case INPUT_PIN_ATTR_UNUSED:
+ break;
+ case INPUT_PIN_ATTR_DOCK:
+ if (*dock)
+ return 1; /* already occupied */
+ *dock = nid;
+ break;
+ default:
if (*ext)
return 1; /* already occupied */
*ext = nid;
@@ -3542,10 +3534,13 @@ static int set_mic_route(struct hda_codec *codec,
int i;
mic->pin = pin;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
- if (pin == cfg->input_pins[i])
+ if (pin == 0)
+ return 0;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (pin == cfg->inputs[i].pin)
break;
- if (i <= AUTO_PIN_FRONT_MIC) {
+ }
+ if (i < cfg->num_inputs && cfg->inputs[i].type == AUTO_PIN_MIC) {
/* analog pin */
i = get_connection_index(codec, spec->mux_nids[0], pin);
if (i < 0)
@@ -3576,26 +3571,29 @@ static int stac_check_auto_mic(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
- hda_nid_t fixed, ext;
+ hda_nid_t fixed, ext, dock;
int i;
- for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
- if (cfg->input_pins[i])
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (cfg->inputs[i].type >= AUTO_PIN_LINE_IN)
return 0; /* must be exclusively mics */
}
- fixed = ext = 0;
- for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
- if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
+ fixed = ext = dock = 0;
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (check_mic_pin(codec, cfg->inputs[i].pin,
+ &fixed, &ext, &dock))
return 0;
for (i = 0; i < spec->num_dmics; i++)
- if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
+ if (check_mic_pin(codec, spec->dmic_nids[i],
+ &fixed, &ext, &dock))
return 0;
- if (!fixed || !ext)
- return 0;
+ if (!fixed && !ext && !dock)
+ return 0; /* no input to switch */
if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
return 0; /* no unsol support */
if (set_mic_route(codec, &spec->ext_mic, ext) ||
- set_mic_route(codec, &spec->int_mic, fixed))
+ set_mic_route(codec, &spec->int_mic, fixed) ||
+ set_mic_route(codec, &spec->dock_mic, dock))
return 0; /* something is wrong */
return 1;
}
@@ -3606,13 +3604,12 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
struct sigmatel_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux;
int i, j;
+ const char *label;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = cfg->input_pins[i];
- int index, err;
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int index, err, type_idx;
- if (!nid)
- continue;
index = -1;
for (j = 0; j < spec->num_muxes; j++) {
index = get_connection_index(codec, spec->mux_nids[j],
@@ -3623,15 +3620,14 @@ static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const
if (index < 0)
continue;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ snd_hda_add_imux_item(imux, label, index, &type_idx);
+
err = create_elem_capture_vol(codec, nid,
- auto_pin_cfg_labels[i],
+ label, type_idx,
HDA_INPUT);
if (err < 0)
return err;
-
- imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
- imux->items[imux->num_items].index = index;
- imux->num_items++;
}
spec->num_analog_muxes = imux->num_items;
@@ -4305,38 +4301,38 @@ static int stac92xx_init(struct hda_codec *codec)
AC_VERB_SET_CONNECT_SEL, 0);
if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
stac_issue_unsol_event(codec, spec->ext_mic.pin);
- }
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = cfg->input_pins[i];
- if (nid) {
- unsigned int pinctl, conf;
- if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
- /* for mic pins, force to initialize */
- pinctl = stac92xx_get_default_vref(codec, nid);
+ if (enable_pin_detect(codec, spec->dock_mic.pin,
+ STAC_MIC_EVENT))
+ stac_issue_unsol_event(codec, spec->dock_mic.pin);
+ }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int type = cfg->inputs[i].type;
+ unsigned int pinctl, conf;
+ if (type == AUTO_PIN_MIC) {
+ /* for mic pins, force to initialize */
+ pinctl = stac92xx_get_default_vref(codec, nid);
+ pinctl |= AC_PINCTL_IN_EN;
+ stac92xx_auto_set_pinctl(codec, nid, pinctl);
+ } else {
+ pinctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ /* if PINCTL already set then skip */
+ /* Also, if both INPUT and OUTPUT are set,
+ * it must be a BIOS bug; need to override, too
+ */
+ if (!(pinctl & AC_PINCTL_IN_EN) ||
+ (pinctl & AC_PINCTL_OUT_EN)) {
+ pinctl &= ~AC_PINCTL_OUT_EN;
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
- } else {
- pinctl = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- /* if PINCTL already set then skip */
- /* Also, if both INPUT and OUTPUT are set,
- * it must be a BIOS bug; need to override, too
- */
- if (!(pinctl & AC_PINCTL_IN_EN) ||
- (pinctl & AC_PINCTL_OUT_EN)) {
- pinctl &= ~AC_PINCTL_OUT_EN;
- pinctl |= AC_PINCTL_IN_EN;
- stac92xx_auto_set_pinctl(codec, nid,
- pinctl);
- }
- }
- conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
- if (enable_pin_detect(codec, nid,
- STAC_INSERT_EVENT))
- stac_issue_unsol_event(codec, nid);
}
}
+ conf = snd_hda_codec_get_pincfg(codec, nid);
+ if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
+ if (enable_pin_detect(codec, nid, STAC_INSERT_EVENT))
+ stac_issue_unsol_event(codec, nid);
+ }
}
for (i = 0; i < spec->num_dmics; i++)
stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
@@ -4383,11 +4379,9 @@ static int stac92xx_init(struct hda_codec *codec)
stac_issue_unsol_event(codec, nid);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
/* sync mute LED */
- if (spec->gpio_led && codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ if (spec->gpio_led)
+ hda_call_check_power_status(codec, 0x01);
if (spec->dac_list)
stac92xx_power_down(codec);
return 0;
@@ -4688,6 +4682,36 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
}
}
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int cfg;
+
+ cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+ return get_defcfg_connect(cfg);
+}
+
+static int stac92xx_connected_ports(struct hda_codec *codec,
+ hda_nid_t *nids, int num_nids)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ int idx, num;
+ unsigned int def_conf;
+
+ for (num = 0; num < num_nids; num++) {
+ for (idx = 0; idx < spec->num_pins; idx++)
+ if (spec->pin_nids[idx] == nids[num])
+ break;
+ if (idx >= spec->num_pins)
+ break;
+ def_conf = stac_get_defcfg_connect(codec, idx);
+ if (def_conf == AC_JACK_PORT_NONE)
+ break;
+ }
+ return num;
+}
+
static void stac92xx_mic_detect(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4695,6 +4719,8 @@ static void stac92xx_mic_detect(struct hda_codec *codec)
if (get_pin_presence(codec, spec->ext_mic.pin))
mic = &spec->ext_mic;
+ else if (get_pin_presence(codec, spec->dock_mic.pin))
+ mic = &spec->dock_mic;
else
mic = &spec->int_mic;
if (mic->dmux_idx >= 0)
@@ -4937,11 +4963,9 @@ static int stac92xx_resume(struct hda_codec *codec)
stac_issue_unsol_event(codec,
spec->autocfg.line_out_pins[0]);
}
-#ifdef CONFIG_SND_HDA_POWER_SAVE
/* sync mute LED */
- if (spec->gpio_led && codec->patch_ops.check_power_status)
- codec->patch_ops.check_power_status(codec, 0x01);
-#endif
+ if (spec->gpio_led)
+ hda_call_check_power_status(codec, 0x01);
return 0;
}
@@ -5313,11 +5337,16 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
if (spec == NULL)
return -ENOMEM;
+ /* reset pin power-down; Windows may leave these bits after reboot */
+ snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7EC, 0);
+ snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7ED, 0);
codec->no_trigger_sense = 1;
codec->spec = spec;
spec->linear_tone_beep = 1;
codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
spec->digbeep_nid = 0x21;
+ spec->dmic_nids = stac92hd83xxx_dmic_nids;
+ spec->dmux_nids = stac92hd83xxx_mux_nids;
spec->mux_nids = stac92hd83xxx_mux_nids;
spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
spec->adc_nids = stac92hd83xxx_adc_nids;
@@ -5363,9 +5392,13 @@ again:
case 0x111d76d4:
case 0x111d7605:
case 0x111d76d5:
+ case 0x111d76e7:
if (spec->board_config == STAC_92HD83XXX_PWR_REF)
break;
spec->num_pwrs = 0;
+ spec->num_dmics = stac92xx_connected_ports(codec,
+ stac92hd83xxx_dmic_nids,
+ STAC92HD83XXX_NUM_DMICS);
break;
}
@@ -5424,36 +5457,6 @@ again:
return 0;
}
-/* get the pin connection (fixed, none, etc) */
-static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
-{
- struct sigmatel_spec *spec = codec->spec;
- unsigned int cfg;
-
- cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
- return get_defcfg_connect(cfg);
-}
-
-static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
- hda_nid_t *nids, int num_nids)
-{
- struct sigmatel_spec *spec = codec->spec;
- int idx, num;
- unsigned int def_conf;
-
- for (num = 0; num < num_nids; num++) {
- for (idx = 0; idx < spec->num_pins; idx++)
- if (spec->pin_nids[idx] == nids[num])
- break;
- if (idx >= spec->num_pins)
- break;
- def_conf = stac_get_defcfg_connect(codec, idx);
- if (def_conf == AC_JACK_PORT_NONE)
- break;
- }
- return num;
-}
-
static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
hda_nid_t dig0pin)
{
@@ -5592,7 +5595,7 @@ again:
case 0x111d76b5:
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
break;
@@ -5624,7 +5627,7 @@ again:
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS - 1);
break;
@@ -5638,7 +5641,7 @@ again:
default:
spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
- spec->num_dmics = stac92hd71bxx_connected_ports(codec,
+ spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
STAC92HD71BXX_NUM_DMICS);
break;
@@ -6320,6 +6323,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
{ .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
{ .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
{ .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
+ { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index ae3acb2b42d..d1c3f8defc4 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -444,8 +444,8 @@ static hda_nid_t vt1812_adc_nids[2] = {
/* add dynamic controls */
-static int via_add_control(struct via_spec *spec, int type, const char *name,
- unsigned long val)
+static int __via_add_control(struct via_spec *spec, int type, const char *name,
+ int idx, unsigned long val)
{
struct snd_kcontrol_new *knew;
@@ -463,6 +463,9 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
return 0;
}
+#define via_add_control(spec, type, name, val) \
+ __via_add_control(spec, type, name, 0, val)
+
static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
struct snd_kcontrol_new *tmpl)
{
@@ -494,18 +497,18 @@ static void via_free_kctls(struct hda_codec *codec)
/* create input playback/capture controls for the given pin */
static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
- int idx, int mix_nid)
+ int type_idx, int idx, int mix_nid)
{
char name[32];
int err;
sprintf(name, "%s Playback Volume", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", ctlname);
- err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
+ err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
if (err < 0)
return err;
@@ -557,17 +560,15 @@ static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
static void via_auto_init_analog_input(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int ctl;
int i;
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- hda_nid_t nid = spec->autocfg.input_pins[i];
- if (!nid)
- continue;
-
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
if (spec->smart51_enabled && is_smart51_pins(spec, nid))
ctl = PIN_OUT;
- else if (i <= AUTO_PIN_FRONT_MIC)
+ else if (i == AUTO_PIN_MIC)
ctl = PIN_VREF50;
else
ctl = PIN_IN;
@@ -1322,15 +1323,14 @@ static void mute_aa_path(struct hda_codec *codec, int mute)
}
static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
{
- int res = 0;
- int index;
- for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
- if (pin == spec->autocfg.input_pins[index]) {
- res = 1;
- break;
- }
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ if (pin == cfg->inputs[i].pin)
+ return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
}
- return res;
+ return 0;
}
static int via_smart51_info(struct snd_kcontrol *kcontrol,
@@ -1348,25 +1348,21 @@ static int via_smart51_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int on = 1;
int i;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- hda_nid_t nid = spec->autocfg.input_pins[index[i]];
- if (nid) {
- int ctl =
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0);
- if (i == AUTO_PIN_FRONT_MIC
- && spec->hp_independent_mode
- && spec->codec_type != VT1718S)
- continue; /* ignore FMic for independent HP */
- if (ctl & AC_PINCTL_IN_EN
- && !(ctl & AC_PINCTL_OUT_EN))
- on = 0;
- }
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ int ctl = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ continue;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+ spec->hp_independent_mode && spec->codec_type != VT1718S)
+ continue; /* ignore FMic for independent HP */
+ if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
+ on = 0;
}
*ucontrol->value.integer.value = on;
return 0;
@@ -1377,36 +1373,38 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int out_in = *ucontrol->value.integer.value
? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
int i;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- hda_nid_t nid = spec->autocfg.input_pins[index[i]];
- if (i == AUTO_PIN_FRONT_MIC
- && spec->hp_independent_mode
- && spec->codec_type != VT1718S)
+ for (i = 0; i < cfg->num_inputs; i++) {
+ hda_nid_t nid = cfg->inputs[i].pin;
+ unsigned int parm;
+
+ if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
+ continue;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC &&
+ spec->hp_independent_mode && spec->codec_type != VT1718S)
continue; /* don't retask FMic for independent HP */
- if (nid) {
- unsigned int parm = snd_hda_codec_read(
- codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
- parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
- parm |= out_in;
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- parm);
- if (out_in == AC_PINCTL_OUT_EN) {
- mute_aa_path(codec, 1);
- notify_aa_path_ctls(codec);
- }
- if (spec->codec_type == VT1718S)
- snd_hda_codec_amp_stereo(
+
+ parm = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+ parm |= out_in;
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ parm);
+ if (out_in == AC_PINCTL_OUT_EN) {
+ mute_aa_path(codec, 1);
+ notify_aa_path_ctls(codec);
+ }
+ if (spec->codec_type == VT1718S) {
+ snd_hda_codec_amp_stereo(
codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
HDA_AMP_UNMUTE);
}
- if (i == AUTO_PIN_FRONT_MIC) {
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
if (spec->codec_type == VT1708S
|| spec->codec_type == VT1716S) {
/* input = index 1 (AOW3) */
@@ -1442,7 +1440,7 @@ static struct snd_kcontrol_new via_smart51_mixer[2] = {
static int via_smart51_build(struct via_spec *spec)
{
struct snd_kcontrol_new *knew;
- int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
int i;
@@ -1450,13 +1448,14 @@ static int via_smart51_build(struct via_spec *spec)
if (knew == NULL)
return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(index); i++) {
- nid = spec->autocfg.input_pins[index[i]];
- if (nid) {
+ for (i = 0; i < cfg->num_inputs; i++) {
+ nid = cfg->inputs[i].pin;
+ if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
knew = via_clone_control(spec, &via_smart51_mixer[1]);
if (knew == NULL)
return -ENOMEM;
knew->subdevice = nid;
+ break;
}
}
@@ -2375,13 +2374,8 @@ static void create_hp_imux(struct via_spec *spec)
static const char *texts[] = { "OFF", "ON", NULL};
/* for hp mode select */
- i = 0;
- while (texts[i] != NULL) {
- imux->items[imux->num_items].label = texts[i];
- imux->items[imux->num_items].index = i;
- imux->num_items++;
- i++;
- }
+ for (i = 0; texts[i]; i++)
+ snd_hda_add_imux_item(imux, texts[i], i, NULL);
spec->hp_mux = &spec->private_imux[1];
}
@@ -2413,51 +2407,53 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg,
+ hda_nid_t cap_nid,
+ hda_nid_t pin_idxs[], int num_idxs)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
+ int i, err, idx, type, type_idx = 0;
/* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1d: /* Mic */
- idx = 2;
- break;
-
- case 0x1e: /* Line In */
- idx = 3;
- break;
-
- case 0x21: /* Front Mic */
- idx = 4;
- break;
-
- case 0x24: /* CD */
- idx = 1;
+ for (idx = 0; idx < num_idxs; idx++) {
+ if (pin_idxs[idx] == 0xff) {
+ snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
break;
}
- err = via_new_analog_input(spec, labels[i], idx, 0x17);
+ }
+
+ for (i = 0; i < cfg->num_inputs; i++) {
+ const char *label;
+ type = cfg->inputs[i].type;
+ for (idx = 0; idx < num_idxs; idx++)
+ if (pin_idxs[idx] == cfg->inputs[i].pin)
+ break;
+ if (idx >= num_idxs)
+ continue;
+ if (i > 0 && type == cfg->inputs[i - 1].type)
+ type_idx++;
+ else
+ type_idx = 0;
+ label = hda_get_autocfg_input_label(codec, cfg, i);
+ err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
if (err < 0)
return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, label, idx, NULL);
}
return 0;
}
+/* create playback/capture controls for input pins */
+static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+}
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
static struct hda_amp_list vt1708_loopbacks[] = {
{ 0x17, HDA_INPUT, 1 },
@@ -2554,7 +2550,7 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
/* add jack detect on/off control */
@@ -3021,49 +3017,12 @@ static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1d: /* Mic */
- idx = 2;
- break;
-
- case 0x1e: /* Line In */
- idx = 3;
- break;
-
- case 0x21: /* Front Mic */
- idx = 4;
- break;
-
- case 0x23: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x18);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1709_parse_auto_config(struct hda_codec *codec)
@@ -3086,7 +3045,7 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -3588,49 +3547,12 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1708B_parse_auto_config(struct hda_codec *codec)
@@ -3653,7 +3575,7 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4061,49 +3983,12 @@ static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
/* fill out digital output widgets; one for master and one for slave outputs */
@@ -4151,7 +4036,7 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4441,58 +4326,20 @@ static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
imux = &spec->private_imux[1];
/* for hp mode select */
- i = 0;
- while (texts[i] != NULL) {
- imux->items[imux->num_items].label = texts[i];
- imux->items[imux->num_items].index = i;
- imux->num_items++;
- i++;
- }
+ for (i = 0; texts[i]; i++)
+ snd_hda_add_imux_item(imux, texts[i], i, NULL);
spec->hp_mux = &spec->private_imux[1];
return 0;
}
/* create playback/capture controls for input pins */
-static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 3;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x14: /* Mic */
- idx = 1;
- break;
-
- case 0x15: /* Line In */
- idx = 2;
- break;
-
- case 0x18: /* Front Mic */
- idx = 3;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x1A);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1702_parse_auto_config(struct hda_codec *codec)
@@ -4521,7 +4368,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
(0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
(0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
(1 << AC_AMPCAP_MUTE_SHIFT));
- err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -4872,49 +4719,12 @@ static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 1;
- break;
-
- case 0x2a: /* Line In */
- idx = 2;
- break;
-
- case 0x29: /* Front Mic */
- idx = 3;
- break;
-
- case 0x2c: /* CD */
- idx = 0;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1718S_parse_auto_config(struct hda_codec *codec)
@@ -4938,7 +4748,7 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -5371,49 +5181,12 @@ static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
- struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x1a: /* Mic */
- idx = 2;
- break;
-
- case 0x1b: /* Line In */
- idx = 3;
- break;
-
- case 0x1e: /* Front Mic */
- idx = 4;
- break;
-
- case 0x1f: /* CD */
- idx = 1;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x16);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx-1;
- imux->num_items++;
- }
- return 0;
+ static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
+ return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
}
static int vt1716S_parse_auto_config(struct hda_codec *codec)
@@ -5436,7 +5209,7 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -5717,54 +5490,25 @@ static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 0;
- break;
-
- case 0x2a: /* Line In */
- idx = 1;
- break;
-
- case 0x29: /* Front Mic */
- idx = 2;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
+ static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
+ int err;
+ err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+ if (err < 0)
+ return err;
/* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
+ err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
if (err < 0)
return err;
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 3;
- imux->num_items++;
-
/* for digital mic select */
- imux->items[imux->num_items].label = "Digital Mic";
- imux->items[imux->num_items].index = 4;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
return 0;
}
@@ -5792,7 +5536,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -6067,53 +5811,26 @@ static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
}
/* create playback/capture controls for input pins */
-static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
+static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
const struct auto_pin_cfg *cfg)
{
- static char *labels[] = {
- "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
- };
+ struct via_spec *spec = codec->spec;
struct hda_input_mux *imux = &spec->private_imux[0];
- int i, err, idx = 0;
-
- for (i = 0; i < AUTO_PIN_LAST; i++) {
- if (!cfg->input_pins[i])
- continue;
-
- switch (cfg->input_pins[i]) {
- case 0x2b: /* Mic */
- idx = 0;
- break;
+ static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
+ int err;
- case 0x2a: /* Line In */
- idx = 1;
- break;
+ err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
+ ARRAY_SIZE(pin_idxs));
+ if (err < 0)
+ return err;
- case 0x29: /* Front Mic */
- idx = 2;
- break;
- }
- err = via_new_analog_input(spec, labels[i], idx, 0x21);
- if (err < 0)
- return err;
- imux->items[imux->num_items].label = labels[i];
- imux->items[imux->num_items].index = idx;
- imux->num_items++;
- }
/* build volume/mute control of loopback */
- err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
+ err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
if (err < 0)
return err;
- /* for internal loopback recording select */
- imux->items[imux->num_items].label = "Stereo Mixer";
- imux->items[imux->num_items].index = 5;
- imux->num_items++;
-
/* for digital mic select */
- imux->items[imux->num_items].label = "Digital Mic";
- imux->items[imux->num_items].index = 6;
- imux->num_items++;
+ snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
return 0;
}
@@ -6141,7 +5858,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
if (err < 0)
return err;
- err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;