Commit e6feb5d0 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Support advanced power state controls

This patch enables the finer power state control of each widget
depending on the jack plug state and streaming state in addition to
the existing power_down_unused power optimization.  The new feature is
enabled only when codec->power_mgmt flag is set.

Two new flags, pin_enabled and stream_enabled, are introduced in
nid_path struct for marking the two individual power states: the pin
plug/unplug and DAC/ADC stream, respectively.  They can be set
statically in case they are static routes (e.g. some mixer paths),
too.

The power up and down events for each pin are triggered via the
standard hda_jack table.  The call order is hard-coded, relying on the
current implementation of jack event chain (a la FILO/stack order).

One point to be dealt carefully is that DAC/ADC cannot be powered
on/off while streaming.  They are pinned as long as the stream is
running.  For controlling the power of DAC/ADC, a new patch_ops is
added.  The generic parser provides the default callback for that.

As of this patch, only IDT/Sigmatel codec driver enables the flag.
The support on other codecs will follow.

An assumption we made in this code is that the widget state (e.g. amp,
pinctl, connections) remains after the widget power transition (not
about FG power transition).  This is true for IDT codecs, at least.
But if the widget state is lost at widget power transition, we'd need
to implement additional code to sync the cached amp/verbs for the
specific NID.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent fb83b635
......@@ -1502,6 +1502,8 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
if (!p)
return;
if (codec->patch_ops.stream_pm)
codec->patch_ops.stream_pm(codec, nid, true);
if (codec->pcm_format_first)
update_pcm_format(codec, p, nid, format);
update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
......@@ -1570,6 +1572,8 @@ static void really_cleanup_stream(struct hda_codec *codec,
);
memset(q, 0, sizeof(*q));
q->nid = nid;
if (codec->patch_ops.stream_pm)
codec->patch_ops.stream_pm(codec, nid, false);
}
/* clean up the all conflicting obsolete streams */
......
......@@ -200,6 +200,7 @@ struct hda_codec_ops {
int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
#endif
void (*reboot_notify)(struct hda_codec *codec);
void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on);
};
/* record for amp information cache */
......@@ -370,6 +371,7 @@ struct hda_codec {
unsigned int cached_write:1; /* write only to caches */
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
unsigned int power_mgmt:1; /* advanced PM for each widget */
#ifdef CONFIG_PM
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
atomic_t in_pm; /* suspend/resume being performed */
......
This diff is collapsed.
......@@ -46,7 +46,9 @@ struct nid_path {
unsigned char idx[MAX_NID_PATH_DEPTH];
unsigned char multi[MAX_NID_PATH_DEPTH];
unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */
bool active;
bool active:1; /* activated by driver */
bool pin_enabled:1; /* pins are enabled */
bool stream_enabled:1; /* stream is active */
};
/* mic/line-in auto switching entry */
......@@ -340,5 +342,6 @@ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid);
unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec,
hda_nid_t nid,
unsigned int power_state);
void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on);
#endif /* __SOUND_HDA_GENERIC_H */
......@@ -4394,6 +4394,7 @@ static const struct hda_codec_ops stac_patch_ops = {
#ifdef CONFIG_PM
.suspend = stac_suspend,
#endif
.stream_pm = snd_hda_gen_stream_pm,
.reboot_notify = stac_shutup,
};
......@@ -4487,6 +4488,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
return err;
spec = codec->spec;
codec->power_mgmt = 1;
spec->linear_tone_beep = 0;
spec->gen.mixer_nid = 0x1d;
spec->have_spdif_mux = 1;
......@@ -4592,6 +4594,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
codec->epss = 0; /* longer delay needed for D3 */
spec = codec->spec;
codec->power_mgmt = 1;
spec->linear_tone_beep = 0;
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
......@@ -4641,6 +4644,7 @@ static int patch_stac92hd95(struct hda_codec *codec)
codec->epss = 0; /* longer delay needed for D3 */
spec = codec->spec;
codec->power_mgmt = 1;
spec->linear_tone_beep = 0;
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
......@@ -4682,6 +4686,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
return err;
spec = codec->spec;
codec->power_mgmt = 1;
spec->linear_tone_beep = 0;
spec->gen.own_eapd_ctl = 1;
spec->gen.power_down_unused = 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment