aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2005-04-16 18:24:32 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:24:32 -0400
commitb20af5f59797796d28b701f5d337e47c8a142eb2 (patch)
tree24f01577a2749499249cce2aa89e57bebedd4bd6
parentb75550e1bc6b3b2c80b628e68628fca015634071 (diff)
[PATCH] pmac: Improve sleep code of tumbler driver
This patch improves the behaviour of the "tumbler/snapper" driver used on newer PowerMacs during sleep. It properly set the HW mutes to shut down amplifiers and does an analog shutdown of the codec. That might improve power consumption during sleep on a number of machines. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--sound/ppc/tumbler.c142
1 files changed, 106 insertions, 36 deletions
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 72a2219f56b5..cb6916e9b74f 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -94,12 +94,17 @@ typedef struct pmac_tumbler_t {
94 pmac_gpio_t hp_detect; 94 pmac_gpio_t hp_detect;
95 int headphone_irq; 95 int headphone_irq;
96 unsigned int master_vol[2]; 96 unsigned int master_vol[2];
97 unsigned int save_master_switch[2];
97 unsigned int master_switch[2]; 98 unsigned int master_switch[2];
98 unsigned int mono_vol[VOL_IDX_LAST_MONO]; 99 unsigned int mono_vol[VOL_IDX_LAST_MONO];
99 unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */ 100 unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */
100 int drc_range; 101 int drc_range;
101 int drc_enable; 102 int drc_enable;
102 int capture_source; 103 int capture_source;
104 int anded_reset;
105 int auto_mute_notify;
106 int reset_on_sleep;
107 u8 acs;
103} pmac_tumbler_t; 108} pmac_tumbler_t;
104 109
105 110
@@ -654,7 +659,8 @@ static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucont
654 659
655 660
656/* 661/*
657 * mute switches 662 * mute switches. FIXME: Turn that into software mute when both outputs are muted
663 * to avoid codec reset on ibook M7
658 */ 664 */
659 665
660enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP }; 666enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP };
@@ -696,8 +702,11 @@ static int snapper_set_capture_source(pmac_tumbler_t *mix)
696{ 702{
697 if (! mix->i2c.client) 703 if (! mix->i2c.client)
698 return -ENODEV; 704 return -ENODEV;
699 return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, 705 if (mix->capture_source)
700 mix->capture_source ? 2 : 0); 706 mix->acs = mix->acs |= 2;
707 else
708 mix->acs &= ~2;
709 return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs);
701} 710}
702 711
703static int snapper_info_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) 712static int snapper_info_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
@@ -855,8 +864,7 @@ static void check_mute(pmac_t *chip, pmac_gpio_t *gp, int val, int do_notify, sn
855 864
856static struct work_struct device_change; 865static struct work_struct device_change;
857 866
858static void 867static void device_change_handler(void *self)
859device_change_handler(void *self)
860{ 868{
861 pmac_t *chip = (pmac_t*) self; 869 pmac_t *chip = (pmac_t*) self;
862 pmac_tumbler_t *mix; 870 pmac_tumbler_t *mix;
@@ -865,6 +873,33 @@ device_change_handler(void *self)
865 return; 873 return;
866 874
867 mix = chip->mixer_data; 875 mix = chip->mixer_data;
876 snd_assert(mix, return);
877
878 if (tumbler_detect_headphone(chip)) {
879 /* mute speaker */
880 check_mute(chip, &mix->hp_mute, 0, mix->auto_mute_notify,
881 chip->master_sw_ctl);
882 if (mix->anded_reset)
883 big_mdelay(10);
884 check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify,
885 chip->speaker_sw_ctl);
886 mix->drc_enable = 0;
887 } else {
888 /* unmute speaker */
889 check_mute(chip, &mix->amp_mute, 0, mix->auto_mute_notify,
890 chip->speaker_sw_ctl);
891 if (mix->anded_reset)
892 big_mdelay(10);
893 check_mute(chip, &mix->hp_mute, 1, mix->auto_mute_notify,
894 chip->master_sw_ctl);
895 mix->drc_enable = 1;
896 }
897 if (mix->auto_mute_notify) {
898 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
899 &chip->hp_detect_ctl->id);
900 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
901 &chip->drc_sw_ctl->id);
902 }
868 903
869 /* first set the DRC so the speaker do not explode -ReneR */ 904 /* first set the DRC so the speaker do not explode -ReneR */
870 if (chip->model == PMAC_TUMBLER) 905 if (chip->model == PMAC_TUMBLER)
@@ -879,31 +914,11 @@ device_change_handler(void *self)
879static void tumbler_update_automute(pmac_t *chip, int do_notify) 914static void tumbler_update_automute(pmac_t *chip, int do_notify)
880{ 915{
881 if (chip->auto_mute) { 916 if (chip->auto_mute) {
882 pmac_tumbler_t *mix = chip->mixer_data; 917 pmac_tumbler_t *mix;
918 mix = chip->mixer_data;
883 snd_assert(mix, return); 919 snd_assert(mix, return);
884 if (tumbler_detect_headphone(chip)) { 920 mix->auto_mute_notify = do_notify;
885 /* mute speaker */
886 check_mute(chip, &mix->amp_mute, 1, do_notify, chip->speaker_sw_ctl);
887 check_mute(chip, &mix->hp_mute, 0, do_notify, chip->master_sw_ctl);
888 mix->drc_enable = 0;
889
890 } else {
891 /* unmute speaker */
892 check_mute(chip, &mix->amp_mute, 0, do_notify, chip->speaker_sw_ctl);
893 check_mute(chip, &mix->hp_mute, 1, do_notify, chip->master_sw_ctl);
894 mix->drc_enable = 1;
895 }
896 if (do_notify) {
897 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
898 &chip->hp_detect_ctl->id);
899 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
900 &chip->drc_sw_ctl->id);
901 }
902
903 /* finally we need to schedule an update of the mixer values
904 (master and DRC are enough for now) -ReneR */
905 schedule_work(&device_change); 921 schedule_work(&device_change);
906
907 } 922 }
908} 923}
909#endif /* PMAC_SUPPORT_AUTOMUTE */ 924#endif /* PMAC_SUPPORT_AUTOMUTE */
@@ -1002,15 +1017,53 @@ static void tumbler_reset_audio(pmac_t *chip)
1002{ 1017{
1003 pmac_tumbler_t *mix = chip->mixer_data; 1018 pmac_tumbler_t *mix = chip->mixer_data;
1004 1019
1005 write_audio_gpio(&mix->audio_reset, 0); 1020 if (mix->anded_reset) {
1006 big_mdelay(200); 1021 write_audio_gpio(&mix->hp_mute, 0);
1007 write_audio_gpio(&mix->audio_reset, 1); 1022 write_audio_gpio(&mix->amp_mute, 0);
1008 big_mdelay(100); 1023 big_mdelay(200);
1009 write_audio_gpio(&mix->audio_reset, 0); 1024 write_audio_gpio(&mix->hp_mute, 1);
1010 big_mdelay(100); 1025 write_audio_gpio(&mix->amp_mute, 1);
1026 big_mdelay(100);
1027 write_audio_gpio(&mix->hp_mute, 0);
1028 write_audio_gpio(&mix->amp_mute, 0);
1029 big_mdelay(100);
1030 } else {
1031 write_audio_gpio(&mix->audio_reset, 0);
1032 big_mdelay(200);
1033 write_audio_gpio(&mix->audio_reset, 1);
1034 big_mdelay(100);
1035 write_audio_gpio(&mix->audio_reset, 0);
1036 big_mdelay(100);
1037 }
1011} 1038}
1012 1039
1013#ifdef CONFIG_PMAC_PBOOK 1040#ifdef CONFIG_PMAC_PBOOK
1041/* suspend mixer */
1042static void tumbler_suspend(pmac_t *chip)
1043{
1044 pmac_tumbler_t *mix = chip->mixer_data;
1045
1046 if (mix->headphone_irq >= 0)
1047 disable_irq(mix->headphone_irq);
1048 mix->save_master_switch[0] = mix->master_switch[0];
1049 mix->save_master_switch[1] = mix->master_switch[1];
1050 mix->master_switch[0] = mix->master_switch[1] = 0;
1051 tumbler_set_master_volume(mix);
1052 if (!mix->anded_reset) {
1053 write_audio_gpio(&mix->amp_mute, 1);
1054 write_audio_gpio(&mix->hp_mute, 1);
1055 }
1056 if (chip->model == PMAC_SNAPPER) {
1057 mix->acs |= 1;
1058 i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs);
1059 }
1060 if (mix->anded_reset) {
1061 write_audio_gpio(&mix->amp_mute, 1);
1062 write_audio_gpio(&mix->hp_mute, 1);
1063 } else
1064 write_audio_gpio(&mix->audio_reset, 1);
1065}
1066
1014/* resume mixer */ 1067/* resume mixer */
1015static void tumbler_resume(pmac_t *chip) 1068static void tumbler_resume(pmac_t *chip)
1016{ 1069{
@@ -1018,6 +1071,9 @@ static void tumbler_resume(pmac_t *chip)
1018 1071
1019 snd_assert(mix, return); 1072 snd_assert(mix, return);
1020 1073
1074 mix->acs &= ~1;
1075 mix->master_switch[0] = mix->save_master_switch[0];
1076 mix->master_switch[1] = mix->save_master_switch[1];
1021 tumbler_reset_audio(chip); 1077 tumbler_reset_audio(chip);
1022 if (mix->i2c.client && mix->i2c.init_client) { 1078 if (mix->i2c.client && mix->i2c.init_client) {
1023 if (mix->i2c.init_client(&mix->i2c) < 0) 1079 if (mix->i2c.init_client(&mix->i2c) < 0)
@@ -1041,6 +1097,8 @@ static void tumbler_resume(pmac_t *chip)
1041 tumbler_set_master_volume(mix); 1097 tumbler_set_master_volume(mix);
1042 if (chip->update_automute) 1098 if (chip->update_automute)
1043 chip->update_automute(chip, 0); 1099 chip->update_automute(chip, 0);
1100 if (mix->headphone_irq >= 0)
1101 enable_irq(mix->headphone_irq);
1044} 1102}
1045#endif 1103#endif
1046 1104
@@ -1103,7 +1161,7 @@ int __init snd_pmac_tumbler_init(pmac_t *chip)
1103 int i, err; 1161 int i, err;
1104 pmac_tumbler_t *mix; 1162 pmac_tumbler_t *mix;
1105 u32 *paddr; 1163 u32 *paddr;
1106 struct device_node *tas_node; 1164 struct device_node *tas_node, *np;
1107 char *chipname; 1165 char *chipname;
1108 1166
1109#ifdef CONFIG_KMOD 1167#ifdef CONFIG_KMOD
@@ -1119,7 +1177,18 @@ int __init snd_pmac_tumbler_init(pmac_t *chip)
1119 1177
1120 chip->mixer_data = mix; 1178 chip->mixer_data = mix;
1121 chip->mixer_free = tumbler_cleanup; 1179 chip->mixer_free = tumbler_cleanup;
1122 1180 mix->anded_reset = 0;
1181 mix->reset_on_sleep = 1;
1182
1183 for (np = chip->node->child; np; np = np->sibling) {
1184 if (!strcmp(np->name, "sound")) {
1185 if (get_property(np, "has-anded-reset", NULL))
1186 mix->anded_reset = 1;
1187 if (get_property(np, "layout-id", NULL))
1188 mix->reset_on_sleep = 0;
1189 break;
1190 }
1191 }
1123 if ((err = tumbler_init(chip)) < 0) 1192 if ((err = tumbler_init(chip)) < 0)
1124 return err; 1193 return err;
1125 1194
@@ -1178,6 +1247,7 @@ int __init snd_pmac_tumbler_init(pmac_t *chip)
1178 return err; 1247 return err;
1179 1248
1180#ifdef CONFIG_PMAC_PBOOK 1249#ifdef CONFIG_PMAC_PBOOK
1250 chip->suspend = tumbler_suspend;
1181 chip->resume = tumbler_resume; 1251 chip->resume = tumbler_resume;
1182#endif 1252#endif
1183 1253