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: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
e581f3dba5
commit
4d7fbdbcb1
3 changed files with 56 additions and 41 deletions
|
@ -3203,51 +3203,30 @@ 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;
|
*/
|
||||||
/*
|
if (eapd_workaround && power_state == AC_PWRST_D3 &&
|
||||||
* don't power down the widget if it controls
|
get_wcaps_type(wcaps) == AC_WID_PIN &&
|
||||||
* eapd and EAPD_BTLENABLE is set.
|
(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
|
||||||
*/
|
int eapd = snd_hda_codec_read(codec, nid, 0,
|
||||||
pincap = snd_hda_query_pin_caps(codec, nid);
|
|
||||||
if (pincap & AC_PINCAP_EAPD) {
|
|
||||||
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,
|
|
||||||
power_state);
|
|
||||||
}
|
}
|
||||||
|
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
|
||||||
|
power_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (power_state == AC_PWRST_D0) {
|
if (power_state == AC_PWRST_D0) {
|
||||||
|
@ -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,
|
||||||
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 {
|
||||||
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,
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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 = {
|
||||||
.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
|
||||||
|
|
Loading…
Add table
Reference in a new issue