Skip to content

Commit

Permalink
ALSA: hda - Manage current pinctl values in generic parser
Browse files Browse the repository at this point in the history
Use the new pin target accessors for managing the current pinctl
values in the generic parser.  The pinctl values of all active pins
are once determined at the initialization phase, and stored via
snd_hda_codec_set_pin_target().  This will be referred again in the
codec init or resume phase to set the actual pinctl.

This value is kept while the auto-mute.  When a line-out or a speaker
pin is muted by auto-mute, the driver simply disables the pin, but it
doesn't touch the cached pinctl target value.  Upon unmute, this value
is used to restore the original pinctl in return.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Iwai committed Jan 12, 2013
1 parent 62f3a2f commit 2c12c30
Showing 1 changed file with 85 additions and 52 deletions.
137 changes: 85 additions & 52 deletions sound/pci/hda/hda_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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];
Expand All @@ -2639,21 +2689,21 @@ 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);
if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
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;
}
}
Expand Down Expand Up @@ -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++) {
Expand All @@ -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);
}
}
Expand All @@ -2768,15 +2821,15 @@ 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;
else
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 */
Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand All @@ -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 */
Expand All @@ -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 */
Expand All @@ -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)
{
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 2c12c30

Please sign in to comment.