|
@@ -83,6 +83,41 @@ void snd_hda_gen_spec_free(struct hda_gen_spec *spec)
|
|
|
}
|
|
|
EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
|
|
|
|
|
|
+/*
|
|
|
+ * pin control value accesses
|
|
|
+ */
|
|
|
+
|
|
|
+#define update_pin_ctl(codec, pin, val) \
|
|
|
+ snd_hda_codec_update_cache(codec, pin, 0, \
|
|
|
+ AC_VERB_SET_PIN_WIDGET_CONTROL, val)
|
|
|
+
|
|
|
+/* restore the pinctl based on the cached value */
|
|
|
+static inline void restore_pin_ctl(struct hda_codec *codec, hda_nid_t pin)
|
|
|
+{
|
|
|
+ update_pin_ctl(codec, pin, snd_hda_codec_get_pin_target(codec, pin));
|
|
|
+}
|
|
|
+
|
|
|
+/* set the pinctl target value and write it if requested */
|
|
|
+static void set_pin_target(struct hda_codec *codec, hda_nid_t pin,
|
|
|
+ unsigned int val, bool do_write)
|
|
|
+{
|
|
|
+ if (!pin)
|
|
|
+ return;
|
|
|
+ val = snd_hda_correct_pin_ctl(codec, pin, val);
|
|
|
+ snd_hda_codec_set_pin_target(codec, pin, val);
|
|
|
+ if (do_write)
|
|
|
+ update_pin_ctl(codec, pin, val);
|
|
|
+}
|
|
|
+
|
|
|
+/* set pinctl target values for all given pins */
|
|
|
+static void set_pin_targets(struct hda_codec *codec, int num_pins,
|
|
|
+ hda_nid_t *pins, unsigned int val)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < num_pins; i++)
|
|
|
+ set_pin_target(codec, pins[i], val, false);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* parsing paths
|
|
|
*/
|
|
@@ -1317,6 +1352,15 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
|
|
|
spec->multiout.extra_out_nid,
|
|
|
spec->speaker_paths);
|
|
|
|
|
|
+ /* set initial pinctl targets */
|
|
|
+ set_pin_targets(codec, cfg->line_outs, cfg->line_out_pins,
|
|
|
+ cfg->line_out_type == AUTO_PIN_HP_OUT ? PIN_HP : PIN_OUT);
|
|
|
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
|
|
|
+ set_pin_targets(codec, cfg->hp_outs, cfg->hp_pins, PIN_HP);
|
|
|
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
|
|
|
+ set_pin_targets(codec, cfg->speaker_outs,
|
|
|
+ cfg->speaker_pins, PIN_OUT);
|
|
|
+
|
|
|
return badness;
|
|
|
}
|
|
|
|
|
@@ -1715,14 +1759,13 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output)
|
|
|
return 0;
|
|
|
|
|
|
if (output) {
|
|
|
- snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
|
|
|
+ set_pin_target(codec, nid, PIN_OUT, true);
|
|
|
snd_hda_activate_path(codec, path, true, true);
|
|
|
set_pin_eapd(codec, nid, true);
|
|
|
} else {
|
|
|
set_pin_eapd(codec, nid, false);
|
|
|
snd_hda_activate_path(codec, path, false, true);
|
|
|
- snd_hda_set_pin_ctl_cache(codec, nid,
|
|
|
- spec->multi_io[idx].ctl_in);
|
|
|
+ set_pin_target(codec, nid, spec->multi_io[idx].ctl_in, true);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -1871,7 +1914,7 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
|
|
|
}
|
|
|
|
|
|
val = set_as_mic ? val | PIN_IN : PIN_HP;
|
|
|
- snd_hda_set_pin_ctl_cache(codec, pin, val);
|
|
|
+ set_pin_target(codec, pin, val, true);
|
|
|
|
|
|
spec->automute_speaker = !set_as_mic;
|
|
|
call_update_outputs(codec);
|
|
@@ -2126,6 +2169,7 @@ static int create_input_ctls(struct hda_codec *codec)
|
|
|
int num_adcs;
|
|
|
int i, err, type_idx = 0;
|
|
|
const char *prev_label = NULL;
|
|
|
+ unsigned int val;
|
|
|
|
|
|
num_adcs = fill_adc_nids(codec);
|
|
|
if (num_adcs < 0)
|
|
@@ -2146,6 +2190,11 @@ static int create_input_ctls(struct hda_codec *codec)
|
|
|
type_idx = 0;
|
|
|
prev_label = label;
|
|
|
|
|
|
+ val = PIN_IN;
|
|
|
+ if (cfg->inputs[i].type == AUTO_PIN_MIC)
|
|
|
+ val |= snd_hda_get_default_vref(codec, pin);
|
|
|
+ set_pin_target(codec, pin, val, false);
|
|
|
+
|
|
|
if (mixer) {
|
|
|
if (is_reachable_path(codec, pin, mixer)) {
|
|
|
err = new_analog_input(codec, i, pin,
|
|
@@ -2611,12 +2660,12 @@ static void parse_digital(struct hda_codec *codec)
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
struct nid_path *path;
|
|
|
int i, nums;
|
|
|
- hda_nid_t dig_nid;
|
|
|
+ hda_nid_t dig_nid, pin;
|
|
|
|
|
|
/* support multiple SPDIFs; the secondary is set up as a slave */
|
|
|
nums = 0;
|
|
|
for (i = 0; i < spec->autocfg.dig_outs; i++) {
|
|
|
- hda_nid_t pin = spec->autocfg.dig_out_pins[i];
|
|
|
+ pin = spec->autocfg.dig_out_pins[i];
|
|
|
dig_nid = look_for_dac(codec, pin, true);
|
|
|
if (!dig_nid)
|
|
|
continue;
|
|
@@ -2626,6 +2675,7 @@ static void parse_digital(struct hda_codec *codec)
|
|
|
print_nid_path("digout", path);
|
|
|
path->active = true;
|
|
|
spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
|
|
|
+ set_pin_target(codec, pin, PIN_OUT, false);
|
|
|
if (!nums) {
|
|
|
spec->multiout.dig_out_nid = dig_nid;
|
|
|
spec->dig_out_type = spec->autocfg.dig_out_type[0];
|
|
@@ -2639,6 +2689,7 @@ static void parse_digital(struct hda_codec *codec)
|
|
|
}
|
|
|
|
|
|
if (spec->autocfg.dig_in_pin) {
|
|
|
+ pin = spec->autocfg.dig_in_pin;
|
|
|
dig_nid = codec->start_nid;
|
|
|
for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
|
|
|
unsigned int wcaps = get_wcaps(codec, dig_nid);
|
|
@@ -2646,14 +2697,13 @@ static void parse_digital(struct hda_codec *codec)
|
|
|
continue;
|
|
|
if (!(wcaps & AC_WCAP_DIGITAL))
|
|
|
continue;
|
|
|
- path = snd_hda_add_new_path(codec,
|
|
|
- spec->autocfg.dig_in_pin,
|
|
|
- dig_nid, 0);
|
|
|
+ path = snd_hda_add_new_path(codec, pin, dig_nid, 0);
|
|
|
if (path) {
|
|
|
print_nid_path("digin", path);
|
|
|
path->active = true;
|
|
|
spec->dig_in_nid = dig_nid;
|
|
|
spec->digin_path = snd_hda_get_path_idx(codec, path);
|
|
|
+ set_pin_target(codec, pin, PIN_IN, false);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
@@ -2730,10 +2780,9 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
|
|
|
|
|
|
/* standard HP/line-out auto-mute helper */
|
|
|
static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
|
|
|
- bool mute, bool hp_out)
|
|
|
+ bool mute)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
- unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT);
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < num_pins; i++) {
|
|
@@ -2744,14 +2793,18 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
|
|
|
/* don't reset VREF value in case it's controlling
|
|
|
* the amp (see alc861_fixup_asus_amp_vref_0f())
|
|
|
*/
|
|
|
- if (spec->keep_vref_in_automute) {
|
|
|
- val = snd_hda_codec_read(codec, nid, 0,
|
|
|
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
|
- val &= ~PIN_HP;
|
|
|
- } else
|
|
|
+ if (spec->keep_vref_in_automute)
|
|
|
+ val = snd_hda_codec_get_pin_target(codec, nid) & ~PIN_HP;
|
|
|
+ else
|
|
|
val = 0;
|
|
|
- val |= pin_bits;
|
|
|
- snd_hda_set_pin_ctl_cache(codec, nid, val);
|
|
|
+ if (!mute)
|
|
|
+ val |= snd_hda_codec_get_pin_target(codec, nid);
|
|
|
+ /* here we call update_pin_ctl() so that the pinctl is changed
|
|
|
+ * without changing the pinctl target value;
|
|
|
+ * the original target value will be still referred at the
|
|
|
+ * init / resume again
|
|
|
+ */
|
|
|
+ update_pin_ctl(codec, nid, val);
|
|
|
set_pin_eapd(codec, nid, !mute);
|
|
|
}
|
|
|
}
|
|
@@ -2768,7 +2821,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
|
|
|
*/
|
|
|
if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */
|
|
|
do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
|
|
|
- spec->autocfg.hp_pins, spec->master_mute, true);
|
|
|
+ spec->autocfg.hp_pins, spec->master_mute);
|
|
|
|
|
|
if (!spec->automute_speaker)
|
|
|
on = 0;
|
|
@@ -2776,7 +2829,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
|
|
|
on = spec->hp_jack_present | spec->line_jack_present;
|
|
|
on |= spec->master_mute;
|
|
|
do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
|
|
|
- spec->autocfg.speaker_pins, on, false);
|
|
|
+ spec->autocfg.speaker_pins, on);
|
|
|
|
|
|
/* toggle line-out mutes if needed, too */
|
|
|
/* if LO is a copy of either HP or Speaker, don't need to handle it */
|
|
@@ -2789,7 +2842,7 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
|
|
|
on = spec->hp_jack_present;
|
|
|
on |= spec->master_mute;
|
|
|
do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
|
|
|
- spec->autocfg.line_out_pins, on, false);
|
|
|
+ spec->autocfg.line_out_pins, on);
|
|
|
}
|
|
|
EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
|
|
|
|
|
@@ -3806,8 +3859,7 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
|
|
|
*/
|
|
|
|
|
|
/* configure the given path as a proper output */
|
|
|
-static void set_output_and_unmute(struct hda_codec *codec,
|
|
|
- int pin_type, int path_idx)
|
|
|
+static void set_output_and_unmute(struct hda_codec *codec, int path_idx)
|
|
|
{
|
|
|
struct nid_path *path;
|
|
|
hda_nid_t pin;
|
|
@@ -3816,7 +3868,7 @@ static void set_output_and_unmute(struct hda_codec *codec,
|
|
|
if (!path || !path->depth)
|
|
|
return;
|
|
|
pin = path->path[path->depth - 1];
|
|
|
- snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
|
|
|
+ restore_pin_ctl(codec, pin);
|
|
|
snd_hda_activate_path(codec, path, path->active, true);
|
|
|
set_pin_eapd(codec, pin, path->active);
|
|
|
}
|
|
@@ -3825,26 +3877,19 @@ static void set_output_and_unmute(struct hda_codec *codec,
|
|
|
static void init_multi_out(struct hda_codec *codec)
|
|
|
{
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
- int pin_type;
|
|
|
int i;
|
|
|
|
|
|
- if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
|
|
|
- pin_type = PIN_HP;
|
|
|
- else
|
|
|
- pin_type = PIN_OUT;
|
|
|
-
|
|
|
for (i = 0; i < spec->autocfg.line_outs; i++)
|
|
|
- set_output_and_unmute(codec, pin_type, spec->out_paths[i]);
|
|
|
+ set_output_and_unmute(codec, spec->out_paths[i]);
|
|
|
}
|
|
|
|
|
|
|
|
|
-static void __init_extra_out(struct hda_codec *codec, int num_outs,
|
|
|
- int *paths, int type)
|
|
|
+static void __init_extra_out(struct hda_codec *codec, int num_outs, int *paths)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
for (i = 0; i < num_outs; i++)
|
|
|
- set_output_and_unmute(codec, type, paths[i]);
|
|
|
+ set_output_and_unmute(codec, paths[i]);
|
|
|
}
|
|
|
|
|
|
/* initialize hp and speaker paths */
|
|
@@ -3853,11 +3898,10 @@ static void init_extra_out(struct hda_codec *codec)
|
|
|
struct hda_gen_spec *spec = codec->spec;
|
|
|
|
|
|
if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT)
|
|
|
- __init_extra_out(codec, spec->autocfg.hp_outs,
|
|
|
- spec->hp_paths, PIN_HP);
|
|
|
+ __init_extra_out(codec, spec->autocfg.hp_outs, spec->hp_paths);
|
|
|
if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT)
|
|
|
__init_extra_out(codec, spec->autocfg.speaker_outs,
|
|
|
- spec->speaker_paths, PIN_OUT);
|
|
|
+ spec->speaker_paths);
|
|
|
}
|
|
|
|
|
|
/* initialize multi-io paths */
|
|
@@ -3874,22 +3918,11 @@ static void init_multi_io(struct hda_codec *codec)
|
|
|
continue;
|
|
|
if (!spec->multi_io[i].ctl_in)
|
|
|
spec->multi_io[i].ctl_in =
|
|
|
- snd_hda_codec_update_cache(codec, pin, 0,
|
|
|
- AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
|
|
|
+ snd_hda_codec_get_pin_target(codec, pin);
|
|
|
snd_hda_activate_path(codec, path, path->active, true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* set up the input pin config, depending on the given auto-pin type */
|
|
|
-static void set_input_pin(struct hda_codec *codec, hda_nid_t nid,
|
|
|
- int auto_pin_type)
|
|
|
-{
|
|
|
- unsigned int val = PIN_IN;
|
|
|
- if (auto_pin_type == AUTO_PIN_MIC)
|
|
|
- val |= snd_hda_get_default_vref(codec, nid);
|
|
|
- snd_hda_set_pin_ctl_cache(codec, nid, val);
|
|
|
-}
|
|
|
-
|
|
|
/* set up input pins and loopback paths */
|
|
|
static void init_analog_input(struct hda_codec *codec)
|
|
|
{
|
|
@@ -3900,7 +3933,7 @@ static void init_analog_input(struct hda_codec *codec)
|
|
|
for (i = 0; i < cfg->num_inputs; i++) {
|
|
|
hda_nid_t nid = cfg->inputs[i].pin;
|
|
|
if (is_input_pin(codec, nid))
|
|
|
- set_input_pin(codec, nid, cfg->inputs[i].type);
|
|
|
+ restore_pin_ctl(codec, nid);
|
|
|
|
|
|
/* init loopback inputs */
|
|
|
if (spec->mixer_nid) {
|
|
@@ -3953,11 +3986,11 @@ static void init_digital(struct hda_codec *codec)
|
|
|
hda_nid_t pin;
|
|
|
|
|
|
for (i = 0; i < spec->autocfg.dig_outs; i++)
|
|
|
- set_output_and_unmute(codec, PIN_OUT, spec->digout_paths[i]);
|
|
|
+ set_output_and_unmute(codec, spec->digout_paths[i]);
|
|
|
pin = spec->autocfg.dig_in_pin;
|
|
|
if (pin) {
|
|
|
struct nid_path *path;
|
|
|
- snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN);
|
|
|
+ restore_pin_ctl(codec, pin);
|
|
|
path = snd_hda_get_path_from_idx(codec, spec->digin_path);
|
|
|
if (path)
|
|
|
snd_hda_activate_path(codec, path, path->active, false);
|