const struct snd_kcontrol_new *mixers[5];
int num_mixers;
hda_nid_t vmaster_nid;
+ struct hda_vmaster_mute_hook vmaster_mute;
+ bool vmaster_mute_led;
const struct hda_verb *init_verbs[5]; /* initialization verbs
* don't forget NULL
unsigned int hp_laptop:1;
unsigned int asus:1;
unsigned int pin_eapd_ctrls:1;
- unsigned int single_adc_amp:1;
unsigned int adc_switching:1;
};
#endif
-static const char * const slave_vols[] = {
- "Headphone Playback Volume",
- "Speaker Playback Volume",
- "Front Playback Volume",
- "Surround Playback Volume",
- "CLFE Playback Volume",
- NULL
-};
-
-static const char * const slave_sws[] = {
- "Headphone Playback Switch",
- "Speaker Playback Switch",
- "Front Playback Switch",
- "Surround Playback Switch",
- "CLFE Playback Switch",
+static const char * const slave_pfxs[] = {
+ "Headphone", "Speaker", "Front", "Surround", "CLFE",
NULL
};
snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
HDA_OUTPUT, vmaster_tlv);
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
- vmaster_tlv, slave_vols);
+ vmaster_tlv, slave_pfxs,
+ "Playback Volume");
if (err < 0)
return err;
}
if (spec->vmaster_nid &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
- err = snd_hda_add_vmaster(codec, "Master Playback Switch",
- NULL, slave_sws);
+ err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
+ NULL, slave_pfxs,
+ "Playback Switch", true,
+ &spec->vmaster_mute.sw_kctl);
if (err < 0)
return err;
}
static const struct hda_input_mux cxt5045_capture_source = {
.num_items = 2,
.items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
+ { "Internal Mic", 0x1 },
+ { "Mic", 0x2 },
}
};
static const struct hda_input_mux cxt5045_capture_source_benq = {
.num_items = 5,
.items = {
- { "IntMic", 0x1 },
- { "ExtMic", 0x2 },
- { "LineIn", 0x3 },
- { "CD", 0x4 },
- { "Mixer", 0x0 },
+ { "CD", 0x4 },
+ { "Internal Mic", 0x1 },
+ { "Mic", 0x2 },
+ { "Line", 0x3 },
+ { "Mixer", 0x0 },
}
};
static const struct hda_input_mux cxt5045_capture_source_hp530 = {
.num_items = 2,
.items = {
- { "ExtMic", 0x1 },
- { "IntMic", 0x2 },
+ { "Mic", 0x1 },
+ { "Internal Mic", 0x2 },
}
};
}
static const struct snd_kcontrol_new cxt5045_mixers[] = {
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x1, HDA_INPUT),
};
static const struct snd_kcontrol_new cxt5045_benq_mixers[] = {
- HDA_CODEC_VOLUME("CD Capture Volume", 0x1a, 0x04, HDA_INPUT),
- HDA_CODEC_MUTE("CD Capture Switch", 0x1a, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x17, 0x4, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x17, 0x4, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
-
- HDA_CODEC_VOLUME("Mixer Capture Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Mixer Capture Switch", 0x1a, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x3, HDA_INPUT),
{}
};
static const struct snd_kcontrol_new cxt5045_mixers_hp530[] = {
- HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE("Internal Mic Capture Switch", 0x1a, 0x02, HDA_INPUT),
- HDA_CODEC_VOLUME("Mic Capture Volume", 0x1a, 0x01, HDA_INPUT),
- HDA_CODEC_MUTE("Mic Capture Switch", 0x1a, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("PCM Playback Volume", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("PCM Playback Switch", 0x17, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x2, HDA_INPUT),
.put = conexant_mux_enum_put,
},
/* Audio input controls */
- HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
- HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
- HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x1a, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x1a, 0x0, HDA_INPUT),
{ } /* end */
};
return -ENOMEM;
codec->spec = spec;
codec->pin_amp_workaround = 1;
+ codec->single_adc_amp = 1;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = ARRAY_SIZE(cxt5045_dac_nids);
"Disabled", "Enabled"
};
static const char * const texts3[] = {
- "Disabled", "Speaker Only", "Line-Out+Speaker"
+ "Disabled", "Speaker Only", "Line Out+Speaker"
};
const char * const *texts;
snd_hda_jack_detect_enable(codec, pins[i], action);
}
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return true;
+ return false;
+}
+
+/* is the given NID found in any of autocfg items? */
+static bool found_in_autocfg(struct auto_pin_cfg *cfg, hda_nid_t nid)
+{
+ int i;
+
+ if (found_in_nid_list(nid, cfg->line_out_pins, cfg->line_outs) ||
+ found_in_nid_list(nid, cfg->hp_pins, cfg->hp_outs) ||
+ found_in_nid_list(nid, cfg->speaker_pins, cfg->speaker_outs) ||
+ found_in_nid_list(nid, cfg->dig_out_pins, cfg->dig_outs))
+ return true;
+ for (i = 0; i < cfg->num_inputs; i++)
+ if (cfg->inputs[i].pin == nid)
+ return true;
+ if (cfg->dig_in_pin == nid)
+ return true;
+ return false;
+}
+
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
+ * invalid unsol tags by some reason
+ */
+static void clear_unsol_on_unused_pins(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < codec->init_pins.used; i++) {
+ struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+ if (!found_in_autocfg(cfg, pin->nid))
+ snd_hda_codec_write(codec, pin->nid, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE, 0);
+ }
+}
+
+/* turn on/off EAPD according to Master switch */
+static void cx_auto_vmaster_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct conexant_spec *spec = codec->spec;
+
+ if (enabled && spec->pin_eapd_ctrls) {
+ cx_auto_update_speakers(codec);
+ return;
+ }
+ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
+}
+
static void cx_auto_init_output(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
/* turn on all EAPDs if no individual EAPD control is available */
if (!spec->pin_eapd_ctrls)
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
+ clear_unsol_on_unused_pins(codec);
}
static void cx_auto_init_input(struct hda_codec *codec)
static int cx_auto_init(struct hda_codec *codec)
{
+ struct conexant_spec *spec = codec->spec;
/*snd_hda_sequence_write(codec, cx_auto_init_verbs);*/
cx_auto_init_output(codec);
cx_auto_init_input(codec);
cx_auto_init_digital(codec);
snd_hda_jack_report_sync(codec);
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
return 0;
}
err = snd_hda_ctl_add(codec, nid, kctl);
if (err < 0)
return err;
- if (!(query_amp_caps(codec, nid, hda_dir) & AC_AMPCAP_MUTE))
+ if (!(query_amp_caps(codec, nid, hda_dir) &
+ (AC_AMPCAP_MUTE | AC_AMPCAP_MIN_MUTE)))
break;
}
return 0;
int idx = get_input_connection(codec, adc_nid, nid);
if (idx < 0)
continue;
- if (spec->single_adc_amp)
+ if (codec->single_adc_amp)
idx = 0;
return cx_auto_add_volume_idx(codec, label, pfx,
cidx, adc_nid, HDA_INPUT, idx);
if (cidx < 0)
continue;
input_conn[i] = spec->imux_info[i].adc;
- if (!spec->single_adc_amp)
+ if (!codec->single_adc_amp)
input_conn[i] |= cidx << 8;
if (i > 0 && input_conn[i] != input_conn[0])
multi_connection = 1;
err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
if (err < 0)
return err;
+ if (spec->vmaster_mute.sw_kctl) {
+ spec->vmaster_mute.hook = cx_auto_vmaster_hook;
+ err = snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute,
+ spec->vmaster_mute_led);
+ if (err < 0)
+ return err;
+ }
return 0;
}
return 0;
}
-
static const struct hda_codec_ops cx_auto_patch_ops = {
.build_controls = cx_auto_build_controls,
.build_pcms = conexant_build_pcms,
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
{ 0x17, 0x21a11000 }, /* dock-mic */
{ 0x19, 0x2121103f }, /* dock-HP */
+ { 0x1c, 0x21440100 }, /* dock SPDIF out */
{}
};
{}
};
+/* add "fake" mute amp-caps to DACs on cx5051 so that mixer mute switches
+ * can be created (bko#42825)
+ */
+static void add_cx5051_fake_mutes(struct hda_codec *codec)
+{
+ static hda_nid_t out_nids[] = {
+ 0x10, 0x11, 0
+ };
+ hda_nid_t *p;
+
+ for (p = out_nids; *p; p++)
+ snd_hda_override_amp_caps(codec, *p, HDA_OUTPUT,
+ AC_AMPCAP_MIN_MUTE |
+ query_amp_caps(codec, *p, HDA_OUTPUT));
+}
+
static int patch_conexant_auto(struct hda_codec *codec)
{
struct conexant_spec *spec;
switch (codec->vendor_id) {
case 0x14f15045:
- spec->single_adc_amp = 1;
+ codec->single_adc_amp = 1;
+ break;
+ case 0x14f15051:
+ add_cx5051_fake_mutes(codec);
break;
}
apply_pin_fixup(codec, cxt_fixups, cxt_pincfg_tbl);
+ /* Show mute-led control only on HP laptops
+ * This is a sort of white-list: on HP laptops, EAPD corresponds
+ * only to the mute-LED without actualy amp function. Meanwhile,
+ * others may use EAPD really as an amp switch, so it might be
+ * not good to expose it blindly.
+ */
+ switch (codec->subsystem_id >> 16) {
+ case 0x103c:
+ spec->vmaster_mute_led = 1;
+ break;
+ }
+
err = cx_auto_search_adcs(codec);
if (err < 0)
return err;