diff options
Diffstat (limited to 'sound/ppc/tumbler.c')
-rw-r--r-- | sound/ppc/tumbler.c | 142 |
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 | ||
660 | enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP }; | 666 | enum { 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 | ||
703 | static int snapper_info_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | 712 | static 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 | ||
856 | static struct work_struct device_change; | 865 | static struct work_struct device_change; |
857 | 866 | ||
858 | static void | 867 | static void device_change_handler(void *self) |
859 | device_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) | |||
879 | static void tumbler_update_automute(pmac_t *chip, int do_notify) | 914 | static 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 */ | ||
1042 | static 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 */ |
1015 | static void tumbler_resume(pmac_t *chip) | 1068 | static 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 | ||