aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/hda_codec.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-08-10 11:21:45 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-16 09:58:46 -0400
commitcb53c626e1145edf1d619bc4953f6293d3a77ace (patch)
tree715c2ef3d56a5ac7c79498800e888f562c1aa961 /sound/pci/hda/hda_codec.c
parentcca3b3718ca96dca51daf1129ac03003bcede751 (diff)
[ALSA] hda-intel - Add POWER_SAVE option
Added CONFIG_SND_HDA_POWER_SAVE kconfig. It's an experimental option to achieve an aggressive power-saving. With this option, the driver will turn on/off the power of each codec and controller chip dynamically on demand. The patch introduces a new module option 'power_save'. It specifies the second of time-out for automatic power-down. As default, it's 10 seconds. Setting 0 means to suppress the power-saving feature. The codec may have analog-input loopbacks, which are usually represented by mixer elements such as 'Mic Playback Switch' or 'CD Playback Switch'. When these are on, we cannot turn off the mixer and the codec chip has to be kept on. For bookkeeping these states, a new codec-callback is introduced. For the bus-controller side, a new callback pm_notify is introduced, which can be used to turn on/off the contoller appropriately. Note that this power-saving might cause slight click-noise at power-on/off. Also, it might take some time to wake up the codec, and might even drop some tones at the very beginning. This seems to be the side-effect of turning off the controller chip. This turn-off of the controller can be disabled by undefining HDA_POWER_SAVE_RESET_CONTOLLER in hda_intel.c. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/pci/hda/hda_codec.c')
-rw-r--r--sound/pci/hda/hda_codec.c239
1 files changed, 197 insertions, 42 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 043529308676..9a3b72824f87 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -33,6 +33,13 @@
33#include "hda_local.h" 33#include "hda_local.h"
34#include <sound/hda_hwdep.h> 34#include <sound/hda_hwdep.h>
35 35
36#ifdef CONFIG_SND_HDA_POWER_SAVE
37/* define this option here to hide as static */
38static int power_save = 10;
39module_param(power_save, int, 0644);
40MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
41 "(in second, 0 = disable).");
42#endif
36 43
37/* 44/*
38 * vendor / preset table 45 * vendor / preset table
@@ -60,6 +67,13 @@ static struct hda_vendor_id hda_vendor_ids[] = {
60#include "hda_patch.h" 67#include "hda_patch.h"
61 68
62 69
70#ifdef CONFIG_SND_HDA_POWER_SAVE
71static void hda_power_work(struct work_struct *work);
72static void hda_keep_power_on(struct hda_codec *codec);
73#else
74static inline void hda_keep_power_on(struct hda_codec *codec) {}
75#endif
76
63/** 77/**
64 * snd_hda_codec_read - send a command and get the response 78 * snd_hda_codec_read - send a command and get the response
65 * @codec: the HDA codec 79 * @codec: the HDA codec
@@ -77,12 +91,14 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
77 unsigned int verb, unsigned int parm) 91 unsigned int verb, unsigned int parm)
78{ 92{
79 unsigned int res; 93 unsigned int res;
94 snd_hda_power_up(codec);
80 mutex_lock(&codec->bus->cmd_mutex); 95 mutex_lock(&codec->bus->cmd_mutex);
81 if (!codec->bus->ops.command(codec, nid, direct, verb, parm)) 96 if (!codec->bus->ops.command(codec, nid, direct, verb, parm))
82 res = codec->bus->ops.get_response(codec); 97 res = codec->bus->ops.get_response(codec);
83 else 98 else
84 res = (unsigned int)-1; 99 res = (unsigned int)-1;
85 mutex_unlock(&codec->bus->cmd_mutex); 100 mutex_unlock(&codec->bus->cmd_mutex);
101 snd_hda_power_down(codec);
86 return res; 102 return res;
87} 103}
88 104
@@ -102,9 +118,11 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
102 unsigned int verb, unsigned int parm) 118 unsigned int verb, unsigned int parm)
103{ 119{
104 int err; 120 int err;
121 snd_hda_power_up(codec);
105 mutex_lock(&codec->bus->cmd_mutex); 122 mutex_lock(&codec->bus->cmd_mutex);
106 err = codec->bus->ops.command(codec, nid, direct, verb, parm); 123 err = codec->bus->ops.command(codec, nid, direct, verb, parm);
107 mutex_unlock(&codec->bus->cmd_mutex); 124 mutex_unlock(&codec->bus->cmd_mutex);
125 snd_hda_power_down(codec);
108 return err; 126 return err;
109} 127}
110 128
@@ -505,6 +523,9 @@ static void snd_hda_codec_free(struct hda_codec *codec)
505{ 523{
506 if (!codec) 524 if (!codec)
507 return; 525 return;
526#ifdef CONFIG_SND_HDA_POWER_SAVE
527 cancel_delayed_work(&codec->power_work);
528#endif
508 list_del(&codec->list); 529 list_del(&codec->list);
509 codec->bus->caddr_tbl[codec->addr] = NULL; 530 codec->bus->caddr_tbl[codec->addr] = NULL;
510 if (codec->patch_ops.free) 531 if (codec->patch_ops.free)
@@ -551,6 +572,15 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
551 init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info)); 572 init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
552 init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head)); 573 init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
553 574
575#ifdef CONFIG_SND_HDA_POWER_SAVE
576 INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
577 /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
578 * the caller has to power down appropriatley after initialization
579 * phase.
580 */
581 hda_keep_power_on(codec);
582#endif
583
554 list_add_tail(&codec->list, &bus->codec_list); 584 list_add_tail(&codec->list, &bus->codec_list);
555 bus->caddr_tbl[codec_addr] = codec; 585 bus->caddr_tbl[codec_addr] = codec;
556 586
@@ -855,7 +885,7 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
855 return ret; 885 return ret;
856} 886}
857 887
858#ifdef CONFIG_PM 888#ifdef SND_HDA_NEEDS_RESUME
859/* resume the all amp commands from the cache */ 889/* resume the all amp commands from the cache */
860void snd_hda_codec_resume_amp(struct hda_codec *codec) 890void snd_hda_codec_resume_amp(struct hda_codec *codec)
861{ 891{
@@ -879,7 +909,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
879 } 909 }
880 } 910 }
881} 911}
882#endif /* CONFIG_PM */ 912#endif /* SND_HDA_NEEDS_RESUME */
883 913
884/* 914/*
885 * AMP control callbacks 915 * AMP control callbacks
@@ -945,6 +975,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
945 long *valp = ucontrol->value.integer.value; 975 long *valp = ucontrol->value.integer.value;
946 int change = 0; 976 int change = 0;
947 977
978 snd_hda_power_up(codec);
948 if (chs & 1) { 979 if (chs & 1) {
949 change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 980 change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
950 0x7f, *valp); 981 0x7f, *valp);
@@ -953,6 +984,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
953 if (chs & 2) 984 if (chs & 2)
954 change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, 985 change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
955 0x7f, *valp); 986 0x7f, *valp);
987 snd_hda_power_down(codec);
956 return change; 988 return change;
957} 989}
958 990
@@ -1025,6 +1057,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
1025 long *valp = ucontrol->value.integer.value; 1057 long *valp = ucontrol->value.integer.value;
1026 int change = 0; 1058 int change = 0;
1027 1059
1060 snd_hda_power_up(codec);
1028 if (chs & 1) { 1061 if (chs & 1) {
1029 change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 1062 change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
1030 HDA_AMP_MUTE, 1063 HDA_AMP_MUTE,
@@ -1035,7 +1068,11 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
1035 change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, 1068 change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
1036 HDA_AMP_MUTE, 1069 HDA_AMP_MUTE,
1037 *valp ? 0 : HDA_AMP_MUTE); 1070 *valp ? 0 : HDA_AMP_MUTE);
1038 1071#ifdef CONFIG_SND_HDA_POWER_SAVE
1072 if (codec->patch_ops.check_power_status)
1073 codec->patch_ops.check_power_status(codec, nid);
1074#endif
1075 snd_hda_power_down(codec);
1039 return change; 1076 return change;
1040} 1077}
1041 1078
@@ -1502,7 +1539,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
1502 return 0; 1539 return 0;
1503} 1540}
1504 1541
1505#ifdef CONFIG_PM 1542#ifdef SND_HDA_NEEDS_RESUME
1506/* 1543/*
1507 * command cache 1544 * command cache
1508 */ 1545 */
@@ -1528,6 +1565,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
1528 int direct, unsigned int verb, unsigned int parm) 1565 int direct, unsigned int verb, unsigned int parm)
1529{ 1566{
1530 int err; 1567 int err;
1568 snd_hda_power_up(codec);
1531 mutex_lock(&codec->bus->cmd_mutex); 1569 mutex_lock(&codec->bus->cmd_mutex);
1532 err = codec->bus->ops.command(codec, nid, direct, verb, parm); 1570 err = codec->bus->ops.command(codec, nid, direct, verb, parm);
1533 if (!err) { 1571 if (!err) {
@@ -1538,6 +1576,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
1538 c->val = parm; 1576 c->val = parm;
1539 } 1577 }
1540 mutex_unlock(&codec->bus->cmd_mutex); 1578 mutex_unlock(&codec->bus->cmd_mutex);
1579 snd_hda_power_down(codec);
1541 return err; 1580 return err;
1542} 1581}
1543 1582
@@ -1572,7 +1611,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
1572 snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, 1611 snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
1573 seq->param); 1612 seq->param);
1574} 1613}
1575#endif /* CONFIG_PM */ 1614#endif /* SND_HDA_NEEDS_RESUME */
1576 1615
1577/* 1616/*
1578 * set power state of the codec 1617 * set power state of the codec
@@ -1580,24 +1619,70 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
1580static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg, 1619static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
1581 unsigned int power_state) 1620 unsigned int power_state)
1582{ 1621{
1583 hda_nid_t nid, nid_start; 1622 hda_nid_t nid;
1584 int nodes; 1623 int i;
1585 1624
1586 snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE, 1625 snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
1587 power_state); 1626 power_state);
1588 1627
1589 nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start); 1628 nid = codec->start_nid;
1590 for (nid = nid_start; nid < nodes + nid_start; nid++) { 1629 for (i = 0; i < codec->num_nodes; i++, nid++) {
1591 if (get_wcaps(codec, nid) & AC_WCAP_POWER) 1630 if (get_wcaps(codec, nid) & AC_WCAP_POWER)
1592 snd_hda_codec_write(codec, nid, 0, 1631 snd_hda_codec_write(codec, nid, 0,
1593 AC_VERB_SET_POWER_STATE, 1632 AC_VERB_SET_POWER_STATE,
1594 power_state); 1633 power_state);
1595 } 1634 }
1596 1635
1597 if (power_state == AC_PWRST_D0) 1636 if (power_state == AC_PWRST_D0) {
1637 unsigned long end_time;
1638 int state;
1598 msleep(10); 1639 msleep(10);
1640 /* wait until the codec reachs to D0 */
1641 end_time = jiffies + msecs_to_jiffies(500);
1642 do {
1643 state = snd_hda_codec_read(codec, fg, 0,
1644 AC_VERB_GET_POWER_STATE, 0);
1645 if (state == power_state)
1646 break;
1647 msleep(1);
1648 } while (time_after_eq(end_time, jiffies));
1649 }
1650}
1651
1652#ifdef SND_HDA_NEEDS_RESUME
1653/*
1654 * call suspend and power-down; used both from PM and power-save
1655 */
1656static void hda_call_codec_suspend(struct hda_codec *codec)
1657{
1658 if (codec->patch_ops.suspend)
1659 codec->patch_ops.suspend(codec, PMSG_SUSPEND);
1660 hda_set_power_state(codec,
1661 codec->afg ? codec->afg : codec->mfg,
1662 AC_PWRST_D3);
1663#ifdef CONFIG_SND_HDA_POWER_SAVE
1664 cancel_delayed_work(&codec->power_work);
1665#endif
1599} 1666}
1600 1667
1668/*
1669 * kick up codec; used both from PM and power-save
1670 */
1671static void hda_call_codec_resume(struct hda_codec *codec)
1672{
1673 hda_set_power_state(codec,
1674 codec->afg ? codec->afg : codec->mfg,
1675 AC_PWRST_D0);
1676 if (codec->patch_ops.resume)
1677 codec->patch_ops.resume(codec);
1678 else {
1679 codec->patch_ops.init(codec);
1680 snd_hda_codec_resume_amp(codec);
1681 snd_hda_codec_resume_cache(codec);
1682 }
1683}
1684#endif /* SND_HDA_NEEDS_RESUME */
1685
1601 1686
1602/** 1687/**
1603 * snd_hda_build_controls - build mixer controls 1688 * snd_hda_build_controls - build mixer controls
@@ -1611,28 +1696,24 @@ int __devinit snd_hda_build_controls(struct hda_bus *bus)
1611{ 1696{
1612 struct hda_codec *codec; 1697 struct hda_codec *codec;
1613 1698
1614 /* build controls */
1615 list_for_each_entry(codec, &bus->codec_list, list) { 1699 list_for_each_entry(codec, &bus->codec_list, list) {
1616 int err; 1700 int err = 0;
1617 if (!codec->patch_ops.build_controls) 1701 /* fake as if already powered-on */
1618 continue; 1702 hda_keep_power_on(codec);
1619 err = codec->patch_ops.build_controls(codec); 1703 /* then fire up */
1620 if (err < 0)
1621 return err;
1622 }
1623
1624 /* initialize */
1625 list_for_each_entry(codec, &bus->codec_list, list) {
1626 int err;
1627 hda_set_power_state(codec, 1704 hda_set_power_state(codec,
1628 codec->afg ? codec->afg : codec->mfg, 1705 codec->afg ? codec->afg : codec->mfg,
1629 AC_PWRST_D0); 1706 AC_PWRST_D0);
1630 if (!codec->patch_ops.init) 1707 /* continue to initialize... */
1631 continue; 1708 if (codec->patch_ops.init)
1632 err = codec->patch_ops.init(codec); 1709 err = codec->patch_ops.init(codec);
1710 if (!err && codec->patch_ops.build_controls)
1711 err = codec->patch_ops.build_controls(codec);
1712 snd_hda_power_down(codec);
1633 if (err < 0) 1713 if (err < 0)
1634 return err; 1714 return err;
1635 } 1715 }
1716
1636 return 0; 1717 return 0;
1637} 1718}
1638 1719
@@ -2078,7 +2159,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
2078 */ 2159 */
2079int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew) 2160int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
2080{ 2161{
2081 int err; 2162 int err;
2082 2163
2083 for (; knew->name; knew++) { 2164 for (; knew->name; knew++) {
2084 struct snd_kcontrol *kctl; 2165 struct snd_kcontrol *kctl;
@@ -2101,6 +2182,89 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
2101 return 0; 2182 return 0;
2102} 2183}
2103 2184
2185#ifdef CONFIG_SND_HDA_POWER_SAVE
2186static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
2187 unsigned int power_state);
2188
2189static void hda_power_work(struct work_struct *work)
2190{
2191 struct hda_codec *codec =
2192 container_of(work, struct hda_codec, power_work.work);
2193
2194 if (!codec->power_on || codec->power_count)
2195 return;
2196
2197 hda_call_codec_suspend(codec);
2198 codec->power_on = 0;
2199 if (codec->bus->ops.pm_notify)
2200 codec->bus->ops.pm_notify(codec);
2201}
2202
2203static void hda_keep_power_on(struct hda_codec *codec)
2204{
2205 codec->power_count++;
2206 codec->power_on = 1;
2207}
2208
2209void snd_hda_power_up(struct hda_codec *codec)
2210{
2211 codec->power_count++;
2212 if (codec->power_on)
2213 return;
2214
2215 codec->power_on = 1;
2216 if (codec->bus->ops.pm_notify)
2217 codec->bus->ops.pm_notify(codec);
2218 hda_call_codec_resume(codec);
2219 cancel_delayed_work(&codec->power_work);
2220}
2221
2222void snd_hda_power_down(struct hda_codec *codec)
2223{
2224 --codec->power_count;
2225 if (!codec->power_on)
2226 return;
2227 if (power_save)
2228 schedule_delayed_work(&codec->power_work,
2229 msecs_to_jiffies(power_save * 1000));
2230}
2231
2232int snd_hda_check_amp_list_power(struct hda_codec *codec,
2233 struct hda_loopback_check *check,
2234 hda_nid_t nid)
2235{
2236 struct hda_amp_list *p;
2237 int ch, v;
2238
2239 if (!check->amplist)
2240 return 0;
2241 for (p = check->amplist; p->nid; p++) {
2242 if (p->nid == nid)
2243 break;
2244 }
2245 if (!p->nid)
2246 return 0; /* nothing changed */
2247
2248 for (p = check->amplist; p->nid; p++) {
2249 for (ch = 0; ch < 2; ch++) {
2250 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
2251 p->idx);
2252 if (!(v & HDA_AMP_MUTE) && v > 0) {
2253 if (!check->power_on) {
2254 check->power_on = 1;
2255 snd_hda_power_up(codec);
2256 }
2257 return 1;
2258 }
2259 }
2260 }
2261 if (check->power_on) {
2262 check->power_on = 0;
2263 snd_hda_power_down(codec);
2264 }
2265 return 0;
2266}
2267#endif
2104 2268
2105/* 2269/*
2106 * Channel mode helper 2270 * Channel mode helper
@@ -2605,41 +2769,32 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
2605{ 2769{
2606 struct hda_codec *codec; 2770 struct hda_codec *codec;
2607 2771
2608 /* FIXME: should handle power widget capabilities */
2609 list_for_each_entry(codec, &bus->codec_list, list) { 2772 list_for_each_entry(codec, &bus->codec_list, list) {
2610 if (codec->patch_ops.suspend) 2773 hda_call_codec_suspend(codec);
2611 codec->patch_ops.suspend(codec, state);
2612 hda_set_power_state(codec,
2613 codec->afg ? codec->afg : codec->mfg,
2614 AC_PWRST_D3);
2615 } 2774 }
2616 return 0; 2775 return 0;
2617} 2776}
2618 2777
2778#ifndef CONFIG_SND_HDA_POWER_SAVE
2619/** 2779/**
2620 * snd_hda_resume - resume the codecs 2780 * snd_hda_resume - resume the codecs
2621 * @bus: the HDA bus 2781 * @bus: the HDA bus
2622 * @state: resume state 2782 * @state: resume state
2623 * 2783 *
2624 * Returns 0 if successful. 2784 * Returns 0 if successful.
2785 *
2786 * This fucntion is defined only when POWER_SAVE isn't set.
2787 * In the power-save mode, the codec is resumed dynamically.
2625 */ 2788 */
2626int snd_hda_resume(struct hda_bus *bus) 2789int snd_hda_resume(struct hda_bus *bus)
2627{ 2790{
2628 struct hda_codec *codec; 2791 struct hda_codec *codec;
2629 2792
2630 list_for_each_entry(codec, &bus->codec_list, list) { 2793 list_for_each_entry(codec, &bus->codec_list, list) {
2631 hda_set_power_state(codec, 2794 hda_call_codec_resume(codec);
2632 codec->afg ? codec->afg : codec->mfg,
2633 AC_PWRST_D0);
2634 if (codec->patch_ops.resume)
2635 codec->patch_ops.resume(codec);
2636 else {
2637 codec->patch_ops.init(codec);
2638 snd_hda_codec_resume_amp(codec);
2639 snd_hda_codec_resume_cache(codec);
2640 }
2641 } 2795 }
2642 return 0; 2796 return 0;
2643} 2797}
2798#endif /* !CONFIG_SND_HDA_POWER_SAVE */
2644 2799
2645#endif 2800#endif