diff options
author | Takashi Iwai <tiwai@suse.de> | 2011-07-26 04:33:10 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-07-26 11:21:24 -0400 |
commit | 4d7fbdbcb1d563b1822c74da3c9e4aa4523d8d6d (patch) | |
tree | e8b38eb8d7c67388595f11b140c97382a1384b1d /sound/pci | |
parent | e581f3dba509f6d48e4939aa70e9b768aa5fd4f3 (diff) |
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>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/hda/hda_codec.c | 78 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 5 | ||||
-rw-r--r-- | sound/pci/hda/patch_conexant.c | 14 |
3 files changed, 56 insertions, 41 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 056cd9ade1fb..3e7850c238c3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c | |||
@@ -3203,51 +3203,30 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, | |||
3203 | EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); | 3203 | EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); |
3204 | #endif /* CONFIG_PM */ | 3204 | #endif /* CONFIG_PM */ |
3205 | 3205 | ||
3206 | /* | 3206 | void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, |
3207 | * set power state of the codec | 3207 | unsigned int power_state, |
3208 | */ | 3208 | bool eapd_workaround) |
3209 | static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | ||
3210 | unsigned int power_state) | ||
3211 | { | 3209 | { |
3212 | hda_nid_t nid; | 3210 | hda_nid_t nid = codec->start_nid; |
3213 | int i; | 3211 | int i; |
3214 | 3212 | ||
3215 | /* this delay seems necessary to avoid click noise at power-down */ | ||
3216 | if (power_state == AC_PWRST_D3) | ||
3217 | msleep(100); | ||
3218 | snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, | ||
3219 | power_state); | ||
3220 | /* partial workaround for "azx_get_response timeout" */ | ||
3221 | if (power_state == AC_PWRST_D0 && | ||
3222 | (codec->vendor_id & 0xffff0000) == 0x14f10000) | ||
3223 | msleep(10); | ||
3224 | |||
3225 | nid = codec->start_nid; | ||
3226 | for (i = 0; i < codec->num_nodes; i++, nid++) { | 3213 | for (i = 0; i < codec->num_nodes; i++, nid++) { |
3227 | unsigned int wcaps = get_wcaps(codec, nid); | 3214 | unsigned int wcaps = get_wcaps(codec, nid); |
3228 | if (wcaps & AC_WCAP_POWER) { | 3215 | if (!(wcaps & AC_WCAP_POWER)) |
3229 | unsigned int wid_type = get_wcaps_type(wcaps); | 3216 | continue; |
3230 | if (power_state == AC_PWRST_D3 && | 3217 | /* don't power down the widget if it controls eapd and |
3231 | wid_type == AC_WID_PIN) { | 3218 | * EAPD_BTLENABLE is set. |
3232 | unsigned int pincap; | 3219 | */ |
3233 | /* | 3220 | if (eapd_workaround && power_state == AC_PWRST_D3 && |
3234 | * don't power down the widget if it controls | 3221 | get_wcaps_type(wcaps) == AC_WID_PIN && |
3235 | * eapd and EAPD_BTLENABLE is set. | 3222 | (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) { |
3236 | */ | 3223 | int eapd = snd_hda_codec_read(codec, nid, 0, |
3237 | pincap = snd_hda_query_pin_caps(codec, nid); | ||
3238 | if (pincap & AC_PINCAP_EAPD) { | ||
3239 | int eapd = snd_hda_codec_read(codec, | ||
3240 | nid, 0, | ||
3241 | AC_VERB_GET_EAPD_BTLENABLE, 0); | 3224 | AC_VERB_GET_EAPD_BTLENABLE, 0); |
3242 | eapd &= 0x02; | 3225 | if (eapd & 0x02) |
3243 | if (eapd) | 3226 | continue; |
3244 | continue; | ||
3245 | } | ||
3246 | } | ||
3247 | snd_hda_codec_write(codec, nid, 0, | ||
3248 | AC_VERB_SET_POWER_STATE, | ||
3249 | power_state); | ||
3250 | } | 3227 | } |
3228 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, | ||
3229 | power_state); | ||
3251 | } | 3230 | } |
3252 | 3231 | ||
3253 | if (power_state == AC_PWRST_D0) { | 3232 | if (power_state == AC_PWRST_D0) { |
@@ -3264,6 +3243,26 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | |||
3264 | } while (time_after_eq(end_time, jiffies)); | 3243 | } while (time_after_eq(end_time, jiffies)); |
3265 | } | 3244 | } |
3266 | } | 3245 | } |
3246 | EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all); | ||
3247 | |||
3248 | /* | ||
3249 | * set power state of the codec | ||
3250 | */ | ||
3251 | static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | ||
3252 | unsigned int power_state) | ||
3253 | { | ||
3254 | if (codec->patch_ops.set_power_state) { | ||
3255 | codec->patch_ops.set_power_state(codec, fg, power_state); | ||
3256 | return; | ||
3257 | } | ||
3258 | |||
3259 | /* this delay seems necessary to avoid click noise at power-down */ | ||
3260 | if (power_state == AC_PWRST_D3) | ||
3261 | msleep(100); | ||
3262 | snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, | ||
3263 | power_state); | ||
3264 | snd_hda_codec_set_power_to_all(codec, fg, power_state, true); | ||
3265 | } | ||
3267 | 3266 | ||
3268 | #ifdef CONFIG_SND_HDA_HWDEP | 3267 | #ifdef CONFIG_SND_HDA_HWDEP |
3269 | /* execute additional init verbs */ | 3268 | /* execute additional init verbs */ |
@@ -4073,9 +4072,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, | |||
4073 | EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); | 4072 | EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls); |
4074 | 4073 | ||
4075 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 4074 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
4076 | static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, | ||
4077 | unsigned int power_state); | ||
4078 | |||
4079 | static void hda_power_work(struct work_struct *work) | 4075 | static void hda_power_work(struct work_struct *work) |
4080 | { | 4076 | { |
4081 | struct hda_codec *codec = | 4077 | struct hda_codec *codec = |
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c7ca753d94ee..755f2b0f9d8e 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h | |||
@@ -700,6 +700,8 @@ struct hda_codec_ops { | |||
700 | int (*init)(struct hda_codec *codec); | 700 | int (*init)(struct hda_codec *codec); |
701 | void (*free)(struct hda_codec *codec); | 701 | void (*free)(struct hda_codec *codec); |
702 | void (*unsol_event)(struct hda_codec *codec, unsigned int res); | 702 | void (*unsol_event)(struct hda_codec *codec, unsigned int res); |
703 | void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg, | ||
704 | unsigned int power_state); | ||
703 | #ifdef CONFIG_PM | 705 | #ifdef CONFIG_PM |
704 | int (*suspend)(struct hda_codec *codec, pm_message_t state); | 706 | int (*suspend)(struct hda_codec *codec, pm_message_t state); |
705 | int (*post_suspend)(struct hda_codec *codec); | 707 | 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 | */ | 1008 | */ |
1007 | void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); | 1009 | void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); |
1008 | void snd_hda_bus_reboot_notify(struct hda_bus *bus); | 1010 | void snd_hda_bus_reboot_notify(struct hda_bus *bus); |
1011 | void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, | ||
1012 | unsigned int power_state, | ||
1013 | bool eapd_workaround); | ||
1009 | 1014 | ||
1010 | /* | 1015 | /* |
1011 | * power management | 1016 | * power management |
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 884f67b8f4e0..502fc9499453 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c | |||
@@ -446,6 +446,19 @@ static int conexant_init_jacks(struct hda_codec *codec) | |||
446 | return 0; | 446 | return 0; |
447 | } | 447 | } |
448 | 448 | ||
449 | static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg, | ||
450 | unsigned int power_state) | ||
451 | { | ||
452 | if (power_state == AC_PWRST_D3) | ||
453 | msleep(100); | ||
454 | snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, | ||
455 | power_state); | ||
456 | /* partial workaround for "azx_get_response timeout" */ | ||
457 | if (power_state == AC_PWRST_D0) | ||
458 | msleep(10); | ||
459 | snd_hda_codec_set_power_to_all(codec, fg, power_state, true); | ||
460 | } | ||
461 | |||
449 | static int conexant_init(struct hda_codec *codec) | 462 | static int conexant_init(struct hda_codec *codec) |
450 | { | 463 | { |
451 | struct conexant_spec *spec = codec->spec; | 464 | struct conexant_spec *spec = codec->spec; |
@@ -588,6 +601,7 @@ static const struct hda_codec_ops conexant_patch_ops = { | |||
588 | .build_pcms = conexant_build_pcms, | 601 | .build_pcms = conexant_build_pcms, |
589 | .init = conexant_init, | 602 | .init = conexant_init, |
590 | .free = conexant_free, | 603 | .free = conexant_free, |
604 | .set_power_state = conexant_set_power, | ||
591 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 605 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
592 | .suspend = conexant_suspend, | 606 | .suspend = conexant_suspend, |
593 | #endif | 607 | #endif |