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
*/
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;
}
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;
}
}
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);
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)
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,
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;
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];
}
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);
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;
}
}
/* 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++) {
/* 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);
}
}
*/
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;
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 */
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);
*/
/* 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;
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);
}
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 */
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 */
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)
{
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) {
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);