aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_generic.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/hda_generic.c')
-rw-r--r--sound/pci/hda/hda_generic.c620
1 files changed, 479 insertions, 141 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index fe18071bf93a..3d2597b7037b 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -140,6 +140,9 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "single_adc_amp");
if (val >= 0)
codec->single_adc_amp = !!val;
+ val = snd_hda_get_bool_hint(codec, "power_save_node");
+ if (val >= 0)
+ codec->power_save_node = !!val;
val = snd_hda_get_bool_hint(codec, "auto_mute");
if (val >= 0)
@@ -648,12 +651,24 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
unsigned int dir, unsigned int idx)
{
struct hda_gen_spec *spec = codec->spec;
+ int type = get_wcaps_type(get_wcaps(codec, nid));
int i, n;
+ if (nid == codec->core.afg)
+ return true;
+
for (n = 0; n < spec->paths.used; n++) {
struct nid_path *path = snd_array_elem(&spec->paths, n);
if (!path->active)
continue;
+ if (codec->power_save_node) {
+ if (!path->stream_enabled)
+ continue;
+ /* ignore unplugged paths except for DAC/ADC */
+ if (!(path->pin_enabled || path->pin_fixed) &&
+ type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN)
+ continue;
+ }
for (i = 0; i < path->depth; i++) {
if (path->path[i] == nid) {
if (dir == HDA_OUTPUT || path->idx[i] == idx)
@@ -687,13 +702,30 @@ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
return val;
}
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ hda_nid_t conn;
+
+ if (wcaps & AC_WCAP_STEREO)
+ return true;
+ if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+ return false;
+ if (snd_hda_get_num_conns(codec, nid) != 1)
+ return false;
+ if (snd_hda_get_connections(codec, nid, &conn, 1) < 0)
+ return false;
+ return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO);
+}
+
/* initialize the amp value (only at the first time) */
static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
{
unsigned int caps = query_amp_caps(codec, nid, dir);
int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
- if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
+ if (is_stereo_amps(codec, nid, dir))
snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
else
snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val);
@@ -703,7 +735,7 @@ static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx,
unsigned int mask, unsigned int val)
{
- if (get_wcaps(codec, nid) & AC_WCAP_STEREO)
+ if (is_stereo_amps(codec, nid, dir))
return snd_hda_codec_amp_stereo(codec, nid, dir, idx,
mask, val);
else
@@ -790,6 +822,44 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
}
}
+/* sync power of each widget in the the given path */
+static hda_nid_t path_power_update(struct hda_codec *codec,
+ struct nid_path *path,
+ bool allow_powerdown)
+{
+ hda_nid_t nid, changed = 0;
+ int i, state;
+
+ for (i = 0; i < path->depth; i++) {
+ nid = path->path[i];
+ if (!(get_wcaps(codec, nid) & AC_WCAP_POWER))
+ continue;
+ if (nid == codec->core.afg)
+ continue;
+ if (!allow_powerdown || is_active_nid_for_any(codec, nid))
+ state = AC_PWRST_D0;
+ else
+ state = AC_PWRST_D3;
+ if (!snd_hda_check_power_state(codec, nid, state)) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_POWER_STATE, state);
+ changed = nid;
+ if (state == AC_PWRST_D0)
+ snd_hdac_regmap_sync_node(&codec->core, nid);
+ }
+ }
+ return changed;
+}
+
+/* do sync with the last power state change */
+static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (nid) {
+ msleep(10);
+ snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0);
+ }
+}
+
/**
* snd_hda_activate_path - activate or deactivate the given path
* @codec: the HDA codec
@@ -808,15 +878,13 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path,
if (!enable)
path->active = false;
+ /* make sure the widget is powered up */
+ if (enable && (spec->power_down_unused || codec->power_save_node))
+ path_power_update(codec, path, codec->power_save_node);
+
for (i = path->depth - 1; i >= 0; i--) {
hda_nid_t nid = path->path[i];
- if (enable && spec->power_down_unused) {
- /* make sure the widget is powered up */
- if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D0);
- }
+
if (enable && path->multi[i])
snd_hda_codec_update_cache(codec, nid, 0,
AC_VERB_SET_CONNECT_SEL,
@@ -836,28 +904,10 @@ EXPORT_SYMBOL_GPL(snd_hda_activate_path);
static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path)
{
struct hda_gen_spec *spec = codec->spec;
- bool changed = false;
- int i;
- if (!spec->power_down_unused || path->active)
+ if (!(spec->power_down_unused || codec->power_save_node) || path->active)
return;
-
- for (i = 0; i < path->depth; i++) {
- hda_nid_t nid = path->path[i];
- if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3) &&
- !is_active_nid_for_any(codec, nid)) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_POWER_STATE,
- AC_PWRST_D3);
- changed = true;
- }
- }
-
- if (changed) {
- msleep(10);
- snd_hda_codec_read(codec, path->path[0], 0,
- AC_VERB_GET_POWER_STATE, 0);
- }
+ sync_power_state_change(codec, path_power_update(codec, path, true));
}
/* turn on/off EAPD on the given pin */
@@ -1557,6 +1607,7 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
return 0;
/* print_nid_path(codec, "output-aamix", path); */
path->active = false; /* unused as default */
+ path->pin_fixed = true; /* static route */
return snd_hda_get_path_idx(codec, path);
}
@@ -1846,12 +1897,11 @@ static void debug_show_configs(struct hda_codec *codec,
static void fill_all_dac_nids(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
- int i;
- hda_nid_t nid = codec->start_nid;
+ hda_nid_t nid;
spec->num_all_dacs = 0;
memset(spec->all_dacs, 0, sizeof(spec->all_dacs));
- for (i = 0; i < codec->num_nodes; i++, nid++) {
+ for_each_hda_codec_node(nid, codec) {
if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT)
continue;
if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) {
@@ -2981,6 +3031,7 @@ static int new_analog_input(struct hda_codec *codec, int input_idx,
}
path->active = true;
+ path->stream_enabled = true; /* no DAC/ADC involved */
err = add_loopback_list(spec, mix_nid, idx);
if (err < 0)
return err;
@@ -2992,6 +3043,8 @@ static int new_analog_input(struct hda_codec *codec, int input_idx,
if (path) {
print_nid_path(codec, "loopback-merge", path);
path->active = true;
+ path->pin_fixed = true; /* static route */
+ path->stream_enabled = true; /* no DAC/ADC involved */
spec->loopback_merge_path =
snd_hda_get_path_idx(codec, path);
}
@@ -3013,10 +3066,9 @@ static int fill_adc_nids(struct hda_codec *codec)
hda_nid_t nid;
hda_nid_t *adc_nids = spec->adc_nids;
int max_nums = ARRAY_SIZE(spec->adc_nids);
- int i, nums = 0;
+ int nums = 0;
- nid = codec->start_nid;
- for (i = 0; i < codec->num_nodes; i++, nid++) {
+ for_each_hda_codec_node(nid, codec) {
unsigned int caps = get_wcaps(codec, nid);
int type = get_wcaps_type(caps);
@@ -3329,11 +3381,6 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
imux = &spec->input_mux;
adc_idx = kcontrol->id.index;
mutex_lock(&codec->control_mutex);
- /* we use the cache-only update at first since multiple input paths
- * may shared the same amp; by updating only caches, the redundant
- * writes to hardware can be reduced.
- */
- codec->cached_write = 1;
for (i = 0; i < imux->num_items; i++) {
path = get_input_path(codec, adc_idx, i);
if (!path || !path->ctls[type])
@@ -3341,12 +3388,9 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
kcontrol->private_value = path->ctls[type];
err = func(kcontrol, ucontrol);
if (err < 0)
- goto error;
+ break;
}
- error:
- codec->cached_write = 0;
mutex_unlock(&codec->control_mutex);
- snd_hda_codec_flush_cache(codec); /* flush the updates */
if (err >= 0 && spec->cap_sync_hook)
spec->cap_sync_hook(codec, kcontrol, ucontrol);
return err;
@@ -3793,6 +3837,7 @@ static void parse_digital(struct hda_codec *codec)
continue;
print_nid_path(codec, "digout", path);
path->active = true;
+ path->pin_fixed = true; /* no jack detection */
spec->digout_paths[i] = snd_hda_get_path_idx(codec, path);
set_pin_target(codec, pin, PIN_OUT, false);
if (!nums) {
@@ -3809,8 +3854,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++) {
+ for_each_hda_codec_node(dig_nid, codec) {
unsigned int wcaps = get_wcaps(codec, dig_nid);
if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
continue;
@@ -3820,6 +3864,7 @@ static void parse_digital(struct hda_codec *codec)
if (path) {
print_nid_path(codec, "digin", path);
path->active = true;
+ path->pin_fixed = true; /* no jack */
spec->dig_in_nid = dig_nid;
spec->digin_path = snd_hda_get_path_idx(codec, path);
set_pin_target(codec, pin, PIN_IN, false);
@@ -3879,6 +3924,238 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
return 1;
}
+/* power up/down widgets in the all paths that match with the given NID
+ * as terminals (either start- or endpoint)
+ *
+ * returns the last changed NID, or zero if unchanged.
+ */
+static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
+ int pin_state, int stream_state)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ hda_nid_t last, changed = 0;
+ struct nid_path *path;
+ int n;
+
+ for (n = 0; n < spec->paths.used; n++) {
+ path = snd_array_elem(&spec->paths, n);
+ if (path->path[0] == nid ||
+ path->path[path->depth - 1] == nid) {
+ bool pin_old = path->pin_enabled;
+ bool stream_old = path->stream_enabled;
+
+ if (pin_state >= 0)
+ path->pin_enabled = pin_state;
+ if (stream_state >= 0)
+ path->stream_enabled = stream_state;
+ if ((!path->pin_fixed && path->pin_enabled != pin_old)
+ || path->stream_enabled != stream_old) {
+ last = path_power_update(codec, path, true);
+ if (last)
+ changed = last;
+ }
+ }
+ }
+ return changed;
+}
+
+/* check the jack status for power control */
+static bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin)
+{
+ if (!is_jack_detectable(codec, pin))
+ return true;
+ return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT;
+}
+
+/* power up/down the paths of the given pin according to the jack state;
+ * power = 0/1 : only power up/down if it matches with the jack state,
+ * < 0 : force power up/down to follow the jack sate
+ *
+ * returns the last changed NID, or zero if unchanged.
+ */
+static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin,
+ int power)
+{
+ bool on;
+
+ if (!codec->power_save_node)
+ return 0;
+
+ on = detect_pin_state(codec, pin);
+
+ if (power >= 0 && on != power)
+ return 0;
+ return set_path_power(codec, pin, on, -1);
+}
+
+static void pin_power_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack,
+ bool on)
+{
+ if (jack && jack->tbl->nid)
+ sync_power_state_change(codec,
+ set_pin_power_jack(codec, jack->tbl->nid, on));
+}
+
+/* callback only doing power up -- called at first */
+static void pin_power_up_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ pin_power_callback(codec, jack, true);
+}
+
+/* callback only doing power down -- called at last */
+static void pin_power_down_callback(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ pin_power_callback(codec, jack, false);
+}
+
+/* set up the power up/down callbacks */
+static void add_pin_power_ctls(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins, bool on)
+{
+ int i;
+ hda_jack_callback_fn cb =
+ on ? pin_power_up_callback : pin_power_down_callback;
+
+ for (i = 0; i < num_pins && pins[i]; i++) {
+ if (is_jack_detectable(codec, pins[i]))
+ snd_hda_jack_detect_enable_callback(codec, pins[i], cb);
+ else
+ set_path_power(codec, pins[i], true, -1);
+ }
+}
+
+/* enabled power callback to each available I/O pin with jack detections;
+ * the digital I/O pins are excluded because of the unreliable detectsion
+ */
+static void add_all_pin_power_ctls(struct hda_codec *codec, bool on)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ if (!codec->power_save_node)
+ return;
+ add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on);
+ for (i = 0; i < cfg->num_inputs; i++)
+ add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on);
+}
+
+/* sync path power up/down with the jack states of given pins */
+static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins)
+{
+ int i;
+
+ for (i = 0; i < num_pins && pins[i]; i++)
+ if (is_jack_detectable(codec, pins[i]))
+ set_pin_power_jack(codec, pins[i], -1);
+}
+
+/* sync path power up/down with pins; called at init and resume */
+static void sync_all_pin_power_ctls(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ if (!codec->power_save_node)
+ return;
+ sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins);
+ for (i = 0; i < cfg->num_inputs; i++)
+ sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin);
+}
+
+/* add fake paths if not present yet */
+static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid,
+ int num_pins, const hda_nid_t *pins)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_pins; i++) {
+ if (!pins[i])
+ break;
+ if (get_nid_path(codec, nid, pins[i], 0))
+ continue;
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return -ENOMEM;
+ memset(path, 0, sizeof(*path));
+ path->depth = 2;
+ path->path[0] = nid;
+ path->path[1] = pins[i];
+ path->active = true;
+ }
+ return 0;
+}
+
+/* create fake paths to all outputs from beep */
+static int add_fake_beep_paths(struct hda_codec *codec)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t nid = spec->beep_nid;
+ int err;
+
+ if (!codec->power_save_node || !nid)
+ return 0;
+ err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins);
+ if (err < 0)
+ return err;
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT) {
+ err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins);
+ if (err < 0)
+ return err;
+ }
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+ err = add_fake_paths(codec, nid, cfg->speaker_outs,
+ cfg->speaker_pins);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
+/* power up/down beep widget and its output paths */
+static void beep_power_hook(struct hda_beep *beep, bool on)
+{
+ set_path_power(beep->codec, beep->nid, -1, on);
+}
+
+/**
+ * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0
+ * @codec: the HDA codec
+ * @pin: NID of pin to fix
+ */
+int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+
+ path = snd_array_new(&spec->paths);
+ if (!path)
+ return -ENOMEM;
+ memset(path, 0, sizeof(*path));
+ path->depth = 1;
+ path->path[0] = pin;
+ path->active = true;
+ path->pin_fixed = true;
+ path->stream_enabled = true;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power);
/*
* Jack detections for HP auto-mute and mic-switch
@@ -3916,6 +4193,10 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
if (!nid)
break;
+ oldval = snd_hda_codec_get_pin_target(codec, nid);
+ if (oldval & PIN_IN)
+ continue; /* no mute for inputs */
+
if (spec->auto_mute_via_amp) {
struct nid_path *path;
hda_nid_t mute_nid;
@@ -3930,29 +4211,32 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
spec->mute_bits |= (1ULL << mute_nid);
else
spec->mute_bits &= ~(1ULL << mute_nid);
- set_pin_eapd(codec, nid, !mute);
continue;
+ } else {
+ /* 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 = oldval & ~PIN_HP;
+ else
+ val = 0;
+ if (!mute)
+ val |= oldval;
+ /* 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);
}
- oldval = snd_hda_codec_get_pin_target(codec, nid);
- if (oldval & PIN_IN)
- continue; /* no mute for inputs */
- /* 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 = oldval & ~PIN_HP;
- else
- val = 0;
- if (!mute)
- val |= oldval;
- /* 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 (codec->power_save_node) {
+ bool on = !mute;
+ if (on)
+ on = detect_pin_state(codec, nid);
+ set_path_power(codec, nid, on, -1);
+ }
}
}
@@ -4419,7 +4703,11 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
hda_nid_t nid,
unsigned int power_state)
{
- if (power_state != AC_PWRST_D0 || nid == codec->afg)
+ struct hda_gen_spec *spec = codec->spec;
+
+ if (!spec->power_down_unused && !codec->power_save_node)
+ return power_state;
+ if (power_state != AC_PWRST_D0 || nid == codec->core.afg)
return power_state;
if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER)
return power_state;
@@ -4449,6 +4737,21 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix)
}
/**
+ * snd_hda_gen_stream_pm - Stream power management callback
+ * @codec: the HDA codec
+ * @nid: audio widget
+ * @on: power on/off flag
+ *
+ * Set this in patch_ops.stream_pm. Only valid with power_save_node flag.
+ */
+void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on)
+{
+ if (codec->power_save_node)
+ set_path_power(codec, nid, -1, on);
+}
+EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm);
+
+/**
* snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and
* set up the hda_gen_spec
* @codec: the HDA codec
@@ -4532,6 +4835,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
if (err < 0)
return err;
+ /* add power-down pin callbacks at first */
+ add_all_pin_power_ctls(codec, false);
+
spec->const_channel_count = spec->ext_channel_count;
/* check the multiple speaker and headphone pins */
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
@@ -4601,6 +4907,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
}
}
+ /* add power-up pin callbacks at last */
+ add_all_pin_power_ctls(codec, true);
+
/* mute all aamix input initially */
if (spec->mixer_nid)
mute_all_mixer_nid(codec, spec->mixer_nid);
@@ -4608,13 +4917,20 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
dig_only:
parse_digital(codec);
- if (spec->power_down_unused)
- codec->power_filter = snd_hda_gen_path_power_filter;
+ if (spec->power_down_unused || codec->power_save_node)
+ if (!codec->power_filter)
+ codec->power_filter = snd_hda_gen_path_power_filter;
if (!spec->no_analog && spec->beep_nid) {
err = snd_hda_attach_beep_device(codec, spec->beep_nid);
if (err < 0)
return err;
+ if (codec->beep && codec->power_save_node) {
+ err = add_fake_beep_paths(codec);
+ if (err < 0)
+ return err;
+ codec->beep->power_hook = beep_power_hook;
+ }
}
return 1;
@@ -4658,7 +4974,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
err = snd_hda_create_dig_out_ctls(codec,
spec->multiout.dig_out_nid,
spec->multiout.dig_out_nid,
- spec->pcm_rec[1].pcm_type);
+ spec->pcm_rec[1]->pcm_type);
if (err < 0)
return err;
if (!spec->no_analog) {
@@ -5120,6 +5436,33 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
strlcat(str, sfx, len);
}
+/* copy PCM stream info from @default_str, and override non-NULL entries
+ * from @spec_str and @nid
+ */
+static void setup_pcm_stream(struct hda_pcm_stream *str,
+ const struct hda_pcm_stream *default_str,
+ const struct hda_pcm_stream *spec_str,
+ hda_nid_t nid)
+{
+ *str = *default_str;
+ if (nid)
+ str->nid = nid;
+ if (spec_str) {
+ if (spec_str->substreams)
+ str->substreams = spec_str->substreams;
+ if (spec_str->channels_min)
+ str->channels_min = spec_str->channels_min;
+ if (spec_str->channels_max)
+ str->channels_max = spec_str->channels_max;
+ if (spec_str->rates)
+ str->rates = spec_str->rates;
+ if (spec_str->formats)
+ str->formats = spec_str->formats;
+ if (spec_str->maxbps)
+ str->maxbps = spec_str->maxbps;
+ }
+}
+
/**
* snd_hda_gen_build_pcms - build PCM streams based on the parsed results
* @codec: the HDA codec
@@ -5129,27 +5472,25 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx,
int snd_hda_gen_build_pcms(struct hda_codec *codec)
{
struct hda_gen_spec *spec = codec->spec;
- struct hda_pcm *info = spec->pcm_rec;
- const struct hda_pcm_stream *p;
+ struct hda_pcm *info;
bool have_multi_adcs;
- codec->num_pcms = 1;
- codec->pcm_info = info;
-
if (spec->no_analog)
goto skip_analog;
fill_pcm_stream_name(spec->stream_name_analog,
sizeof(spec->stream_name_analog),
- " Analog", codec->chip_name);
- info->name = spec->stream_name_analog;
+ " Analog", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog);
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[0] = info;
if (spec->multiout.num_dacs > 0) {
- p = spec->stream_analog_playback;
- if (!p)
- p = &pcm_analog_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_analog_playback,
+ spec->stream_analog_playback,
+ spec->multiout.dac_nids[0]);
info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
spec->multiout.max_channels;
if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
@@ -5158,15 +5499,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
snd_pcm_2_1_chmaps;
}
if (spec->num_adc_nids) {
- p = spec->stream_analog_capture;
- if (!p) {
- if (spec->dyn_adc_switch)
- p = &dyn_adc_pcm_analog_capture;
- else
- p = &pcm_analog_capture;
- }
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ (spec->dyn_adc_switch ?
+ &dyn_adc_pcm_analog_capture : &pcm_analog_capture),
+ spec->stream_analog_capture,
+ spec->adc_nids[0]);
}
skip_analog:
@@ -5174,29 +5511,27 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
fill_pcm_stream_name(spec->stream_name_digital,
sizeof(spec->stream_name_digital),
- " Digital", codec->chip_name);
- codec->num_pcms = 2;
+ " Digital", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s",
+ spec->stream_name_digital);
+ if (!info)
+ return -ENOMEM;
codec->slave_dig_outs = spec->multiout.slave_dig_outs;
- info = spec->pcm_rec + 1;
- info->name = spec->stream_name_digital;
+ spec->pcm_rec[1] = info;
if (spec->dig_out_type)
info->pcm_type = spec->dig_out_type;
else
info->pcm_type = HDA_PCM_TYPE_SPDIF;
- if (spec->multiout.dig_out_nid) {
- p = spec->stream_digital_playback;
- if (!p)
- p = &pcm_digital_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
- }
- if (spec->dig_in_nid) {
- p = spec->stream_digital_capture;
- if (!p)
- p = &pcm_digital_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
- }
+ if (spec->multiout.dig_out_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_digital_playback,
+ spec->stream_digital_playback,
+ spec->multiout.dig_out_nid);
+ if (spec->dig_in_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_digital_capture,
+ spec->stream_digital_capture,
+ spec->dig_in_nid);
}
if (spec->no_analog)
@@ -5211,35 +5546,30 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec)
if (spec->alt_dac_nid || have_multi_adcs) {
fill_pcm_stream_name(spec->stream_name_alt_analog,
sizeof(spec->stream_name_alt_analog),
- " Alt Analog", codec->chip_name);
- codec->num_pcms = 3;
- info = spec->pcm_rec + 2;
- info->name = spec->stream_name_alt_analog;
- if (spec->alt_dac_nid) {
- p = spec->stream_analog_alt_playback;
- if (!p)
- p = &pcm_analog_alt_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
- spec->alt_dac_nid;
- } else {
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
- pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- }
+ " Alt Analog", codec->core.chip_name);
+ info = snd_hda_codec_pcm_new(codec, "%s",
+ spec->stream_name_alt_analog);
+ if (!info)
+ return -ENOMEM;
+ spec->pcm_rec[2] = info;
+ if (spec->alt_dac_nid)
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_analog_alt_playback,
+ spec->stream_analog_alt_playback,
+ spec->alt_dac_nid);
+ else
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK],
+ &pcm_null_stream, NULL, 0);
if (have_multi_adcs) {
- p = spec->stream_analog_alt_capture;
- if (!p)
- p = &pcm_analog_alt_capture;
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
- spec->adc_nids[1];
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_analog_alt_capture,
+ spec->stream_analog_alt_capture,
+ spec->adc_nids[1]);
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
spec->num_adc_nids - 1;
} else {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] =
- pcm_null_stream;
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
+ setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE],
+ &pcm_null_stream, NULL, 0);
}
}
@@ -5435,8 +5765,6 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
- codec->cached_write = 1;
-
init_multi_out(codec);
init_extra_out(codec);
init_multi_io(codec);
@@ -5447,10 +5775,12 @@ int snd_hda_gen_init(struct hda_codec *codec)
clear_unsol_on_unused_pins(codec);
+ sync_all_pin_power_ctls(codec);
+
/* call init functions of standard auto-mute helpers */
update_automute_all(codec);
- snd_hda_codec_flush_cache(codec);
+ regcache_sync(codec->core.regmap);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
@@ -5507,13 +5837,11 @@ static const struct hda_codec_ops generic_patch_ops = {
#endif
};
-/**
+/*
* snd_hda_parse_generic_codec - Generic codec parser
* @codec: the HDA codec
- *
- * This should be called from the HDA codec core.
*/
-int snd_hda_parse_generic_codec(struct hda_codec *codec)
+static int snd_hda_parse_generic_codec(struct hda_codec *codec)
{
struct hda_gen_spec *spec;
int err;
@@ -5539,7 +5867,17 @@ error:
snd_hda_gen_free(codec);
return err;
}
-EXPORT_SYMBOL_GPL(snd_hda_parse_generic_codec);
+
+static const struct hda_codec_preset snd_hda_preset_generic[] = {
+ { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+ {} /* terminator */
+};
+
+static struct hda_codec_driver generic_driver = {
+ .preset = snd_hda_preset_generic,
+};
+
+module_hda_codec_driver(generic_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Generic HD-audio codec parser");