Commit 4d7fbdbc authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Allow codec-specific set_power_state ops

The procedure for codec D-state change may have exceptional cases
depending on the codec chip, such as a longer delay or suppressing D3.

This patch adds a new codec ops, set_power_state() to override the system
default function.  For ease of porting, snd_hda_codec_set_power_to_all()
helper function is extracted from the default set_power_state() function.

As an example, the Conexant codec-specific delay is removed from the
default routine but moved to patch_conexant.c.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent e581f3db
...@@ -3203,52 +3203,31 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, ...@@ -3203,52 +3203,31 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
/* void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
* set power state of the codec unsigned int power_state,
*/ bool eapd_workaround)
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state)
{ {
hda_nid_t nid; hda_nid_t nid = codec->start_nid;
int i; int i;
/* this delay seems necessary to avoid click noise at power-down */
if (power_state == AC_PWRST_D3)
msleep(100);
snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
power_state);
/* partial workaround for "azx_get_response timeout" */
if (power_state == AC_PWRST_D0 &&
(codec->vendor_id & 0xffff0000) == 0x14f10000)
msleep(10);
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) { for (i = 0; i < codec->num_nodes; i++, nid++) {
unsigned int wcaps = get_wcaps(codec, nid); unsigned int wcaps = get_wcaps(codec, nid);
if (wcaps & AC_WCAP_POWER) { if (!(wcaps & AC_WCAP_POWER))
unsigned int wid_type = get_wcaps_type(wcaps); continue;
if (power_state == AC_PWRST_D3 && /* don't power down the widget if it controls eapd and
wid_type == AC_WID_PIN) { * EAPD_BTLENABLE is set.
unsigned int pincap;
/*
* don't power down the widget if it controls
* eapd and EAPD_BTLENABLE is set.
*/ */
pincap = snd_hda_query_pin_caps(codec, nid); if (eapd_workaround && power_state == AC_PWRST_D3 &&
if (pincap & AC_PINCAP_EAPD) { get_wcaps_type(wcaps) == AC_WID_PIN &&
int eapd = snd_hda_codec_read(codec, (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
nid, 0, int eapd = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_EAPD_BTLENABLE, 0); AC_VERB_GET_EAPD_BTLENABLE, 0);
eapd &= 0x02; if (eapd & 0x02)
if (eapd)
continue; continue;
} }
} snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_POWER_STATE,
power_state); power_state);
} }
}
if (power_state == AC_PWRST_D0) { if (power_state == AC_PWRST_D0) {
unsigned long end_time; unsigned long end_time;
...@@ -3264,6 +3243,26 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, ...@@ -3264,6 +3243,26 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
} while (time_after_eq(end_time, jiffies)); } while (time_after_eq(end_time, jiffies));
} }
} }
EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);
/*
* set power state of the codec
*/
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state)
{
if (codec->patch_ops.set_power_state) {
codec->patch_ops.set_power_state(codec, fg, power_state);
return;
}
/* this delay seems necessary to avoid click noise at power-down */
if (power_state == AC_PWRST_D3)
msleep(100);
snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
power_state);
snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
}
#ifdef CONFIG_SND_HDA_HWDEP #ifdef CONFIG_SND_HDA_HWDEP
/* execute additional init verbs */ /* execute additional init verbs */
...@@ -4073,9 +4072,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, ...@@ -4073,9 +4072,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
static void hda_power_work(struct work_struct *work) static void hda_power_work(struct work_struct *work)
{ {
struct hda_codec *codec = struct hda_codec *codec =
......
...@@ -700,6 +700,8 @@ struct hda_codec_ops { ...@@ -700,6 +700,8 @@ struct hda_codec_ops {
int (*init)(struct hda_codec *codec); int (*init)(struct hda_codec *codec);
void (*free)(struct hda_codec *codec); void (*free)(struct hda_codec *codec);
void (*unsol_event)(struct hda_codec *codec, unsigned int res); void (*unsol_event)(struct hda_codec *codec, unsigned int res);
void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
#ifdef CONFIG_PM #ifdef CONFIG_PM
int (*suspend)(struct hda_codec *codec, pm_message_t state); int (*suspend)(struct hda_codec *codec, pm_message_t state);
int (*post_suspend)(struct hda_codec *codec); int (*post_suspend)(struct hda_codec *codec);
...@@ -1006,6 +1008,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, ...@@ -1006,6 +1008,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
*/ */
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_bus_reboot_notify(struct hda_bus *bus); void snd_hda_bus_reboot_notify(struct hda_bus *bus);
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state,
bool eapd_workaround);
/* /*
* power management * power management
......
...@@ -446,6 +446,19 @@ static int conexant_init_jacks(struct hda_codec *codec) ...@@ -446,6 +446,19 @@ static int conexant_init_jacks(struct hda_codec *codec)
return 0; return 0;
} }
static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state)
{
if (power_state == AC_PWRST_D3)
msleep(100);
snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
power_state);
/* partial workaround for "azx_get_response timeout" */
if (power_state == AC_PWRST_D0)
msleep(10);
snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
}
static int conexant_init(struct hda_codec *codec) static int conexant_init(struct hda_codec *codec)
{ {
struct conexant_spec *spec = codec->spec; struct conexant_spec *spec = codec->spec;
...@@ -588,6 +601,7 @@ static const struct hda_codec_ops conexant_patch_ops = { ...@@ -588,6 +601,7 @@ static const struct hda_codec_ops conexant_patch_ops = {
.build_pcms = conexant_build_pcms, .build_pcms = conexant_build_pcms,
.init = conexant_init, .init = conexant_init,
.free = conexant_free, .free = conexant_free,
.set_power_state = conexant_set_power,
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
.suspend = conexant_suspend, .suspend = conexant_suspend,
#endif #endif
......
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