diff options
author | Takashi Iwai <tiwai@suse.de> | 2006-06-27 12:28:53 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-09-23 04:37:08 -0400 |
commit | 6dbe662874ba08585eaf732d126762c25ac8e3f7 (patch) | |
tree | 7460c36d4d848f223b682f7a700866bcf6dbc7d5 /sound | |
parent | 2b29b13c5794f648cd5e839796496704d787f5a6 (diff) |
[ALSA] Add experimental support of aggressive AC97 power-saving mode
Added CONFIG_SND_AC97_POWER_SAVE kernel config to enable the support
of aggressive AC97 power-saving mode. In this mode, the AC97
powerdown register bits are dynamically controlled at each open/close
of PCM streams.
The mode is activated via power_save option for snd-ac97-codec
driver. As default it's off. It can be turned on/off on the fly
via sysfs, too.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/drivers/Kconfig | 13 | ||||
-rw-r--r-- | sound/pci/ac97/ac97_codec.c | 264 | ||||
-rw-r--r-- | sound/pci/ac97/ac97_pcm.c | 18 | ||||
-rw-r--r-- | sound/pci/intel8x0.c | 14 | ||||
-rw-r--r-- | sound/pci/via82xx.c | 13 |
5 files changed, 283 insertions, 39 deletions
diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 395c4ef52ac9..897dc2dfd7dd 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig | |||
@@ -100,4 +100,17 @@ config SND_MPU401 | |||
100 | To compile this driver as a module, choose M here: the module | 100 | To compile this driver as a module, choose M here: the module |
101 | will be called snd-mpu401. | 101 | will be called snd-mpu401. |
102 | 102 | ||
103 | config SND_AC97_POWER_SAVE | ||
104 | bool "AC97 Power-Saving Mode" | ||
105 | depends on SND_AC97_CODEC && EXPERIMENTAL | ||
106 | default n | ||
107 | help | ||
108 | Say Y here to enable the aggressive power-saving support of | ||
109 | AC97 codecs. In this mode, the power-mode is dynamically | ||
110 | controlled at each open/close. | ||
111 | |||
112 | The mode is activated by passing power_save=1 option to | ||
113 | snd-ac97-codec driver. You can toggle it dynamically over | ||
114 | sysfs, too. | ||
115 | |||
103 | endmenu | 116 | endmenu |
diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index b35280ca2465..f82c636e99a9 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c | |||
@@ -47,6 +47,11 @@ static int enable_loopback; | |||
47 | module_param(enable_loopback, bool, 0444); | 47 | module_param(enable_loopback, bool, 0444); |
48 | MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); | 48 | MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); |
49 | 49 | ||
50 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
51 | static int power_save; | ||
52 | module_param(power_save, bool, 0644); | ||
53 | MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control"); | ||
54 | #endif | ||
50 | /* | 55 | /* |
51 | 56 | ||
52 | */ | 57 | */ |
@@ -187,6 +192,8 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = { | |||
187 | }; | 192 | }; |
188 | 193 | ||
189 | 194 | ||
195 | static void update_power_regs(struct snd_ac97 *ac97); | ||
196 | |||
190 | /* | 197 | /* |
191 | * I/O routines | 198 | * I/O routines |
192 | */ | 199 | */ |
@@ -554,6 +561,18 @@ int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value | |||
554 | } | 561 | } |
555 | err = snd_ac97_update_bits(ac97, reg, val_mask, val); | 562 | err = snd_ac97_update_bits(ac97, reg, val_mask, val); |
556 | snd_ac97_page_restore(ac97, page_save); | 563 | snd_ac97_page_restore(ac97, page_save); |
564 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
565 | /* check analog mixer power-down */ | ||
566 | if ((val_mask & 0x8000) && | ||
567 | (kcontrol->private_value & (1<<30))) { | ||
568 | if (val & 0x8000) | ||
569 | ac97->power_up &= ~(1 << (reg>>1)); | ||
570 | else | ||
571 | ac97->power_up |= 1 << (reg>>1); | ||
572 | if (power_save) | ||
573 | update_power_regs(ac97); | ||
574 | } | ||
575 | #endif | ||
557 | return err; | 576 | return err; |
558 | } | 577 | } |
559 | 578 | ||
@@ -962,6 +981,10 @@ static int snd_ac97_bus_dev_free(struct snd_device *device) | |||
962 | static int snd_ac97_free(struct snd_ac97 *ac97) | 981 | static int snd_ac97_free(struct snd_ac97 *ac97) |
963 | { | 982 | { |
964 | if (ac97) { | 983 | if (ac97) { |
984 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
985 | if (ac97->power_workq) | ||
986 | destroy_workqueue(ac97->power_workq); | ||
987 | #endif | ||
965 | snd_ac97_proc_done(ac97); | 988 | snd_ac97_proc_done(ac97); |
966 | if (ac97->bus) | 989 | if (ac97->bus) |
967 | ac97->bus->codec[ac97->num] = NULL; | 990 | ac97->bus->codec[ac97->num] = NULL; |
@@ -1117,7 +1140,9 @@ struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, str | |||
1117 | /* | 1140 | /* |
1118 | * create mute switch(es) for normal stereo controls | 1141 | * create mute switch(es) for normal stereo controls |
1119 | */ | 1142 | */ |
1120 | static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, int check_stereo, struct snd_ac97 *ac97) | 1143 | static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, |
1144 | int check_stereo, int check_amix, | ||
1145 | struct snd_ac97 *ac97) | ||
1121 | { | 1146 | { |
1122 | struct snd_kcontrol *kctl; | 1147 | struct snd_kcontrol *kctl; |
1123 | int err; | 1148 | int err; |
@@ -1137,10 +1162,14 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, | |||
1137 | } | 1162 | } |
1138 | if (mute_mask == 0x8080) { | 1163 | if (mute_mask == 0x8080) { |
1139 | struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); | 1164 | struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1); |
1165 | if (check_amix) | ||
1166 | tmp.private_value |= (1 << 30); | ||
1140 | tmp.index = ac97->num; | 1167 | tmp.index = ac97->num; |
1141 | kctl = snd_ctl_new1(&tmp, ac97); | 1168 | kctl = snd_ctl_new1(&tmp, ac97); |
1142 | } else { | 1169 | } else { |
1143 | struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1); | 1170 | struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1); |
1171 | if (check_amix) | ||
1172 | tmp.private_value |= (1 << 30); | ||
1144 | tmp.index = ac97->num; | 1173 | tmp.index = ac97->num; |
1145 | kctl = snd_ctl_new1(&tmp, ac97); | 1174 | kctl = snd_ctl_new1(&tmp, ac97); |
1146 | } | 1175 | } |
@@ -1186,7 +1215,9 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne | |||
1186 | /* | 1215 | /* |
1187 | * create a mute-switch and a volume for normal stereo/mono controls | 1216 | * create a mute-switch and a volume for normal stereo/mono controls |
1188 | */ | 1217 | */ |
1189 | static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int reg, int check_stereo, struct snd_ac97 *ac97) | 1218 | static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, |
1219 | int reg, int check_stereo, int check_amix, | ||
1220 | struct snd_ac97 *ac97) | ||
1190 | { | 1221 | { |
1191 | int err; | 1222 | int err; |
1192 | char name[44]; | 1223 | char name[44]; |
@@ -1197,7 +1228,9 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int | |||
1197 | 1228 | ||
1198 | if (snd_ac97_try_bit(ac97, reg, 15)) { | 1229 | if (snd_ac97_try_bit(ac97, reg, 15)) { |
1199 | sprintf(name, "%s Switch", pfx); | 1230 | sprintf(name, "%s Switch", pfx); |
1200 | if ((err = snd_ac97_cmute_new_stereo(card, name, reg, check_stereo, ac97)) < 0) | 1231 | if ((err = snd_ac97_cmute_new_stereo(card, name, reg, |
1232 | check_stereo, check_amix, | ||
1233 | ac97)) < 0) | ||
1201 | return err; | 1234 | return err; |
1202 | } | 1235 | } |
1203 | check_volume_resolution(ac97, reg, &lo_max, &hi_max); | 1236 | check_volume_resolution(ac97, reg, &lo_max, &hi_max); |
@@ -1209,8 +1242,10 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int | |||
1209 | return 0; | 1242 | return 0; |
1210 | } | 1243 | } |
1211 | 1244 | ||
1212 | #define snd_ac97_cmix_new(card, pfx, reg, ac97) snd_ac97_cmix_new_stereo(card, pfx, reg, 0, ac97) | 1245 | #define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \ |
1213 | #define snd_ac97_cmute_new(card, name, reg, ac97) snd_ac97_cmute_new_stereo(card, name, reg, 0, ac97) | 1246 | snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97) |
1247 | #define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \ | ||
1248 | snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97) | ||
1214 | 1249 | ||
1215 | static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97); | 1250 | static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97); |
1216 | 1251 | ||
@@ -1226,9 +1261,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1226 | /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */ | 1261 | /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */ |
1227 | if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { | 1262 | if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) { |
1228 | if (ac97->flags & AC97_HAS_NO_MASTER_VOL) | 1263 | if (ac97->flags & AC97_HAS_NO_MASTER_VOL) |
1229 | err = snd_ac97_cmute_new(card, "Master Playback Switch", AC97_MASTER, ac97); | 1264 | err = snd_ac97_cmute_new(card, "Master Playback Switch", |
1265 | AC97_MASTER, 0, ac97); | ||
1230 | else | 1266 | else |
1231 | err = snd_ac97_cmix_new(card, "Master Playback", AC97_MASTER, ac97); | 1267 | err = snd_ac97_cmix_new(card, "Master Playback", |
1268 | AC97_MASTER, 0, ac97); | ||
1232 | if (err < 0) | 1269 | if (err < 0) |
1233 | return err; | 1270 | return err; |
1234 | } | 1271 | } |
@@ -1265,19 +1302,23 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1265 | if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) | 1302 | if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) |
1266 | && !(ac97->flags & AC97_AD_MULTI)) { | 1303 | && !(ac97->flags & AC97_AD_MULTI)) { |
1267 | /* Surround Master (0x38) is with stereo mutes */ | 1304 | /* Surround Master (0x38) is with stereo mutes */ |
1268 | if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0) | 1305 | if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", |
1306 | AC97_SURROUND_MASTER, 1, 0, | ||
1307 | ac97)) < 0) | ||
1269 | return err; | 1308 | return err; |
1270 | } | 1309 | } |
1271 | 1310 | ||
1272 | /* build headphone controls */ | 1311 | /* build headphone controls */ |
1273 | if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { | 1312 | if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { |
1274 | if ((err = snd_ac97_cmix_new(card, "Headphone Playback", AC97_HEADPHONE, ac97)) < 0) | 1313 | if ((err = snd_ac97_cmix_new(card, "Headphone Playback", |
1314 | AC97_HEADPHONE, 0, ac97)) < 0) | ||
1275 | return err; | 1315 | return err; |
1276 | } | 1316 | } |
1277 | 1317 | ||
1278 | /* build master mono controls */ | 1318 | /* build master mono controls */ |
1279 | if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { | 1319 | if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) { |
1280 | if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", AC97_MASTER_MONO, ac97)) < 0) | 1320 | if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", |
1321 | AC97_MASTER_MONO, 0, ac97)) < 0) | ||
1281 | return err; | 1322 | return err; |
1282 | } | 1323 | } |
1283 | 1324 | ||
@@ -1310,7 +1351,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1310 | /* build Phone controls */ | 1351 | /* build Phone controls */ |
1311 | if (!(ac97->flags & AC97_HAS_NO_PHONE)) { | 1352 | if (!(ac97->flags & AC97_HAS_NO_PHONE)) { |
1312 | if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { | 1353 | if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) { |
1313 | if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0) | 1354 | if ((err = snd_ac97_cmix_new(card, "Phone Playback", |
1355 | AC97_PHONE, 1, ac97)) < 0) | ||
1314 | return err; | 1356 | return err; |
1315 | } | 1357 | } |
1316 | } | 1358 | } |
@@ -1318,7 +1360,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1318 | /* build MIC controls */ | 1360 | /* build MIC controls */ |
1319 | if (!(ac97->flags & AC97_HAS_NO_MIC)) { | 1361 | if (!(ac97->flags & AC97_HAS_NO_MIC)) { |
1320 | if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) { | 1362 | if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) { |
1321 | if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0) | 1363 | if ((err = snd_ac97_cmix_new(card, "Mic Playback", |
1364 | AC97_MIC, 1, ac97)) < 0) | ||
1322 | return err; | 1365 | return err; |
1323 | if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0) | 1366 | if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0) |
1324 | return err; | 1367 | return err; |
@@ -1327,14 +1370,16 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1327 | 1370 | ||
1328 | /* build Line controls */ | 1371 | /* build Line controls */ |
1329 | if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) { | 1372 | if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) { |
1330 | if ((err = snd_ac97_cmix_new(card, "Line Playback", AC97_LINE, ac97)) < 0) | 1373 | if ((err = snd_ac97_cmix_new(card, "Line Playback", |
1374 | AC97_LINE, 1, ac97)) < 0) | ||
1331 | return err; | 1375 | return err; |
1332 | } | 1376 | } |
1333 | 1377 | ||
1334 | /* build CD controls */ | 1378 | /* build CD controls */ |
1335 | if (!(ac97->flags & AC97_HAS_NO_CD)) { | 1379 | if (!(ac97->flags & AC97_HAS_NO_CD)) { |
1336 | if (snd_ac97_try_volume_mix(ac97, AC97_CD)) { | 1380 | if (snd_ac97_try_volume_mix(ac97, AC97_CD)) { |
1337 | if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0) | 1381 | if ((err = snd_ac97_cmix_new(card, "CD Playback", |
1382 | AC97_CD, 1, ac97)) < 0) | ||
1338 | return err; | 1383 | return err; |
1339 | } | 1384 | } |
1340 | } | 1385 | } |
@@ -1342,7 +1387,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1342 | /* build Video controls */ | 1387 | /* build Video controls */ |
1343 | if (!(ac97->flags & AC97_HAS_NO_VIDEO)) { | 1388 | if (!(ac97->flags & AC97_HAS_NO_VIDEO)) { |
1344 | if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { | 1389 | if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) { |
1345 | if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0) | 1390 | if ((err = snd_ac97_cmix_new(card, "Video Playback", |
1391 | AC97_VIDEO, 1, ac97)) < 0) | ||
1346 | return err; | 1392 | return err; |
1347 | } | 1393 | } |
1348 | } | 1394 | } |
@@ -1350,7 +1396,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1350 | /* build Aux controls */ | 1396 | /* build Aux controls */ |
1351 | if (!(ac97->flags & AC97_HAS_NO_AUX)) { | 1397 | if (!(ac97->flags & AC97_HAS_NO_AUX)) { |
1352 | if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { | 1398 | if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { |
1353 | if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0) | 1399 | if ((err = snd_ac97_cmix_new(card, "Aux Playback", |
1400 | AC97_AUX, 1, ac97)) < 0) | ||
1354 | return err; | 1401 | return err; |
1355 | } | 1402 | } |
1356 | } | 1403 | } |
@@ -1385,9 +1432,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1385 | } else { | 1432 | } else { |
1386 | if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) { | 1433 | if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) { |
1387 | if (ac97->flags & AC97_HAS_NO_PCM_VOL) | 1434 | if (ac97->flags & AC97_HAS_NO_PCM_VOL) |
1388 | err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97); | 1435 | err = snd_ac97_cmute_new(card, |
1436 | "PCM Playback Switch", | ||
1437 | AC97_PCM, 0, ac97); | ||
1389 | else | 1438 | else |
1390 | err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97); | 1439 | err = snd_ac97_cmix_new(card, "PCM Playback", |
1440 | AC97_PCM, 0, ac97); | ||
1391 | if (err < 0) | 1441 | if (err < 0) |
1392 | return err; | 1442 | return err; |
1393 | } | 1443 | } |
@@ -1398,7 +1448,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97) | |||
1398 | if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0) | 1448 | if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0) |
1399 | return err; | 1449 | return err; |
1400 | if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) { | 1450 | if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) { |
1401 | if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0) | 1451 | err = snd_ac97_cmute_new(card, "Capture Switch", |
1452 | AC97_REC_GAIN, 0, ac97); | ||
1453 | if (err < 0) | ||
1402 | return err; | 1454 | return err; |
1403 | } | 1455 | } |
1404 | if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0) | 1456 | if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0) |
@@ -1829,6 +1881,13 @@ static int snd_ac97_dev_disconnect(struct snd_device *device) | |||
1829 | /* build_ops to do nothing */ | 1881 | /* build_ops to do nothing */ |
1830 | static struct snd_ac97_build_ops null_build_ops; | 1882 | static struct snd_ac97_build_ops null_build_ops; |
1831 | 1883 | ||
1884 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
1885 | static void do_update_power(void *data) | ||
1886 | { | ||
1887 | update_power_regs(data); | ||
1888 | } | ||
1889 | #endif | ||
1890 | |||
1832 | /** | 1891 | /** |
1833 | * snd_ac97_mixer - create an Codec97 component | 1892 | * snd_ac97_mixer - create an Codec97 component |
1834 | * @bus: the AC97 bus which codec is attached to | 1893 | * @bus: the AC97 bus which codec is attached to |
@@ -1883,6 +1942,10 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, | |||
1883 | bus->codec[ac97->num] = ac97; | 1942 | bus->codec[ac97->num] = ac97; |
1884 | mutex_init(&ac97->reg_mutex); | 1943 | mutex_init(&ac97->reg_mutex); |
1885 | mutex_init(&ac97->page_mutex); | 1944 | mutex_init(&ac97->page_mutex); |
1945 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
1946 | ac97->power_workq = create_workqueue("ac97"); | ||
1947 | INIT_WORK(&ac97->power_work, do_update_power, ac97); | ||
1948 | #endif | ||
1886 | 1949 | ||
1887 | #ifdef CONFIG_PCI | 1950 | #ifdef CONFIG_PCI |
1888 | if (ac97->pci) { | 1951 | if (ac97->pci) { |
@@ -2117,15 +2180,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template, | |||
2117 | return -ENOMEM; | 2180 | return -ENOMEM; |
2118 | } | 2181 | } |
2119 | } | 2182 | } |
2120 | /* make sure the proper powerdown bits are cleared */ | 2183 | if (ac97_is_audio(ac97)) |
2121 | if (ac97->scaps && ac97_is_audio(ac97)) { | 2184 | update_power_regs(ac97); |
2122 | reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS); | ||
2123 | if (ac97->scaps & AC97_SCAP_SURROUND_DAC) | ||
2124 | reg &= ~AC97_EA_PRJ; | ||
2125 | if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) | ||
2126 | reg &= ~(AC97_EA_PRI | AC97_EA_PRK); | ||
2127 | snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg); | ||
2128 | } | ||
2129 | snd_ac97_proc_init(ac97); | 2185 | snd_ac97_proc_init(ac97); |
2130 | if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) { | 2186 | if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) { |
2131 | snd_ac97_free(ac97); | 2187 | snd_ac97_free(ac97); |
@@ -2153,22 +2209,155 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97) | |||
2153 | snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f); | 2209 | snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f); |
2154 | } | 2210 | } |
2155 | 2211 | ||
2156 | power = ac97->regs[AC97_POWERDOWN] | 0x8000; /* EAPD */ | 2212 | /* surround, CLFE, mic powerdown */ |
2157 | power |= 0x4000; /* Headphone amplifier powerdown */ | 2213 | power = ac97->regs[AC97_EXTENDED_STATUS]; |
2158 | power |= 0x0300; /* ADC & DAC powerdown */ | 2214 | if (ac97->scaps & AC97_SCAP_SURROUND_DAC) |
2215 | power |= AC97_EA_PRJ; | ||
2216 | if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) | ||
2217 | power |= AC97_EA_PRI | AC97_EA_PRK; | ||
2218 | power |= AC97_EA_PRL; | ||
2219 | snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power); | ||
2220 | |||
2221 | /* powerdown external amplifier */ | ||
2222 | if (ac97->scaps & AC97_SCAP_INV_EAPD) | ||
2223 | power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD; | ||
2224 | else if (! (ac97->scaps & AC97_SCAP_EAPD_LED)) | ||
2225 | power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD; | ||
2226 | power |= AC97_PD_PR6; /* Headphone amplifier powerdown */ | ||
2227 | power |= AC97_PD_PR0 | AC97_PD_PR1; /* ADC & DAC powerdown */ | ||
2159 | snd_ac97_write(ac97, AC97_POWERDOWN, power); | 2228 | snd_ac97_write(ac97, AC97_POWERDOWN, power); |
2160 | udelay(100); | 2229 | udelay(100); |
2161 | power |= 0x0400; /* Analog Mixer powerdown (Vref on) */ | 2230 | power |= AC97_PD_PR2 | AC97_PD_PR3; /* Analog Mixer powerdown */ |
2162 | snd_ac97_write(ac97, AC97_POWERDOWN, power); | ||
2163 | udelay(100); | ||
2164 | #if 0 | ||
2165 | /* FIXME: this causes click noises on some boards at resume */ | ||
2166 | power |= 0x3800; /* AC-link powerdown, internal Clk disable */ | ||
2167 | snd_ac97_write(ac97, AC97_POWERDOWN, power); | 2231 | snd_ac97_write(ac97, AC97_POWERDOWN, power); |
2232 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
2233 | if (power_save) { | ||
2234 | udelay(100); | ||
2235 | /* AC-link powerdown, internal Clk disable */ | ||
2236 | /* FIXME: this may cause click noises on some boards */ | ||
2237 | power |= AC97_PD_PR4 | AC97_PD_PR5; | ||
2238 | snd_ac97_write(ac97, AC97_POWERDOWN, power); | ||
2239 | } | ||
2168 | #endif | 2240 | #endif |
2169 | } | 2241 | } |
2170 | 2242 | ||
2171 | 2243 | ||
2244 | struct ac97_power_reg { | ||
2245 | unsigned short reg; | ||
2246 | unsigned short power_reg; | ||
2247 | unsigned short mask; | ||
2248 | }; | ||
2249 | |||
2250 | enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE }; | ||
2251 | |||
2252 | static struct ac97_power_reg power_regs[PWIDX_SIZE] = { | ||
2253 | [PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0}, | ||
2254 | [PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1}, | ||
2255 | [PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS, | ||
2256 | AC97_EA_PRI | AC97_EA_PRK}, | ||
2257 | [PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS, | ||
2258 | AC97_EA_PRJ}, | ||
2259 | [PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS, | ||
2260 | AC97_EA_PRL}, | ||
2261 | }; | ||
2262 | |||
2263 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
2264 | /** | ||
2265 | * snd_ac97_update_power - update the powerdown register | ||
2266 | * @ac97: the codec instance | ||
2267 | * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE | ||
2268 | * @powerup: non-zero when power up the part | ||
2269 | * | ||
2270 | * Update the AC97 powerdown register bits of the given part. | ||
2271 | */ | ||
2272 | int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup) | ||
2273 | { | ||
2274 | int i; | ||
2275 | |||
2276 | if (! ac97) | ||
2277 | return 0; | ||
2278 | |||
2279 | if (reg) { | ||
2280 | /* SPDIF requires DAC power, too */ | ||
2281 | if (reg == AC97_SPDIF) | ||
2282 | reg = AC97_PCM_FRONT_DAC_RATE; | ||
2283 | for (i = 0; i < PWIDX_SIZE; i++) { | ||
2284 | if (power_regs[i].reg == reg) { | ||
2285 | if (powerup) | ||
2286 | ac97->power_up |= (1 << i); | ||
2287 | else | ||
2288 | ac97->power_up &= ~(1 << i); | ||
2289 | break; | ||
2290 | } | ||
2291 | } | ||
2292 | } | ||
2293 | |||
2294 | if (! power_save) | ||
2295 | return 0; | ||
2296 | |||
2297 | if (! powerup && ac97->power_workq) | ||
2298 | /* adjust power-down bits after two seconds delay | ||
2299 | * (for avoiding loud click noises for many (OSS) apps | ||
2300 | * that open/close frequently) | ||
2301 | */ | ||
2302 | queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2); | ||
2303 | else | ||
2304 | update_power_regs(ac97); | ||
2305 | |||
2306 | return 0; | ||
2307 | } | ||
2308 | |||
2309 | EXPORT_SYMBOL(snd_ac97_update_power); | ||
2310 | #endif /* CONFIG_SND_AC97_POWER_SAVE */ | ||
2311 | |||
2312 | static void update_power_regs(struct snd_ac97 *ac97) | ||
2313 | { | ||
2314 | unsigned int power_up, bits; | ||
2315 | int i; | ||
2316 | |||
2317 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
2318 | if (power_save) | ||
2319 | power_up = ac97->power_up; | ||
2320 | else { | ||
2321 | #endif | ||
2322 | power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC); | ||
2323 | power_up |= (1 << PWIDX_MIC); | ||
2324 | if (ac97->scaps & AC97_SCAP_SURROUND_DAC) | ||
2325 | power_up |= (1 << PWIDX_SURR); | ||
2326 | if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) | ||
2327 | power_up |= (1 << PWIDX_CLFE); | ||
2328 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
2329 | } | ||
2330 | #endif | ||
2331 | if (power_up) { | ||
2332 | if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) { | ||
2333 | /* needs power-up analog mix and vref */ | ||
2334 | snd_ac97_update_bits(ac97, AC97_POWERDOWN, | ||
2335 | AC97_PD_PR3, 0); | ||
2336 | msleep(1); | ||
2337 | snd_ac97_update_bits(ac97, AC97_POWERDOWN, | ||
2338 | AC97_PD_PR2, 0); | ||
2339 | } | ||
2340 | } | ||
2341 | for (i = 0; i < PWIDX_SIZE; i++) { | ||
2342 | if (power_up & (1 << i)) | ||
2343 | bits = 0; | ||
2344 | else | ||
2345 | bits = power_regs[i].mask; | ||
2346 | snd_ac97_update_bits(ac97, power_regs[i].power_reg, | ||
2347 | power_regs[i].mask, bits); | ||
2348 | } | ||
2349 | if (! power_up) { | ||
2350 | if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) { | ||
2351 | /* power down analog mix and vref */ | ||
2352 | snd_ac97_update_bits(ac97, AC97_POWERDOWN, | ||
2353 | AC97_PD_PR2, AC97_PD_PR2); | ||
2354 | snd_ac97_update_bits(ac97, AC97_POWERDOWN, | ||
2355 | AC97_PD_PR3, AC97_PD_PR3); | ||
2356 | } | ||
2357 | } | ||
2358 | } | ||
2359 | |||
2360 | |||
2172 | #ifdef CONFIG_PM | 2361 | #ifdef CONFIG_PM |
2173 | /** | 2362 | /** |
2174 | * snd_ac97_suspend - General suspend function for AC97 codec | 2363 | * snd_ac97_suspend - General suspend function for AC97 codec |
@@ -2484,6 +2673,7 @@ static int tune_mute_led(struct snd_ac97 *ac97) | |||
2484 | msw->put = master_mute_sw_put; | 2673 | msw->put = master_mute_sw_put; |
2485 | snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); | 2674 | snd_ac97_remove_ctl(ac97, "External Amplifier", NULL); |
2486 | snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ | 2675 | snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */ |
2676 | ac97->scaps |= AC97_SCAP_EAPD_LED; | ||
2487 | return 0; | 2677 | return 0; |
2488 | } | 2678 | } |
2489 | 2679 | ||
diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index f684aa2c0067..3758d07182f8 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c | |||
@@ -269,6 +269,7 @@ int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate) | |||
269 | return -EINVAL; | 269 | return -EINVAL; |
270 | } | 270 | } |
271 | 271 | ||
272 | snd_ac97_update_power(ac97, reg, 1); | ||
272 | switch (reg) { | 273 | switch (reg) { |
273 | case AC97_PCM_MIC_ADC_RATE: | 274 | case AC97_PCM_MIC_ADC_RATE: |
274 | if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */ | 275 | if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0) /* MIC VRA */ |
@@ -606,6 +607,7 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate, | |||
606 | goto error; | 607 | goto error; |
607 | } | 608 | } |
608 | } | 609 | } |
610 | pcm->cur_dbl = r; | ||
609 | spin_unlock_irq(&pcm->bus->bus_lock); | 611 | spin_unlock_irq(&pcm->bus->bus_lock); |
610 | for (i = 3; i < 12; i++) { | 612 | for (i = 3; i < 12; i++) { |
611 | if (!(slots & (1 << i))) | 613 | if (!(slots & (1 << i))) |
@@ -651,6 +653,21 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm) | |||
651 | unsigned short slots = pcm->aslots; | 653 | unsigned short slots = pcm->aslots; |
652 | int i, cidx; | 654 | int i, cidx; |
653 | 655 | ||
656 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
657 | int r = pcm->cur_dbl; | ||
658 | for (i = 3; i < 12; i++) { | ||
659 | if (!(slots & (1 << i))) | ||
660 | continue; | ||
661 | for (cidx = 0; cidx < 4; cidx++) { | ||
662 | if (pcm->r[r].rslots[cidx] & (1 << i)) { | ||
663 | int reg = get_slot_reg(pcm, cidx, i, r); | ||
664 | snd_ac97_update_power(pcm->r[r].codec[cidx], | ||
665 | reg, 0); | ||
666 | } | ||
667 | } | ||
668 | } | ||
669 | #endif | ||
670 | |||
654 | bus = pcm->bus; | 671 | bus = pcm->bus; |
655 | spin_lock_irq(&pcm->bus->bus_lock); | 672 | spin_lock_irq(&pcm->bus->bus_lock); |
656 | for (i = 3; i < 12; i++) { | 673 | for (i = 3; i < 12; i++) { |
@@ -660,6 +677,7 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm) | |||
660 | bus->used_slots[pcm->stream][cidx] &= ~(1 << i); | 677 | bus->used_slots[pcm->stream][cidx] &= ~(1 << i); |
661 | } | 678 | } |
662 | pcm->aslots = 0; | 679 | pcm->aslots = 0; |
680 | pcm->cur_dbl = 0; | ||
663 | spin_unlock_irq(&pcm->bus->bus_lock); | 681 | spin_unlock_irq(&pcm->bus->bus_lock); |
664 | return 0; | 682 | return 0; |
665 | } | 683 | } |
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 6874263f1681..72dbaedcbdf5 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c | |||
@@ -2251,6 +2251,16 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing) | |||
2251 | /* ACLink on, 2 channels */ | 2251 | /* ACLink on, 2 channels */ |
2252 | cnt = igetdword(chip, ICHREG(GLOB_CNT)); | 2252 | cnt = igetdword(chip, ICHREG(GLOB_CNT)); |
2253 | cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); | 2253 | cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK); |
2254 | #ifdef CONFIG_SND_AC97_POWER_SAVE | ||
2255 | /* do cold reset - the full ac97 powerdown may leave the controller | ||
2256 | * in a warm state but actually it cannot communicate with the codec. | ||
2257 | */ | ||
2258 | iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_AC97COLD); | ||
2259 | cnt = igetdword(chip, ICHREG(GLOB_CNT)); | ||
2260 | udelay(10); | ||
2261 | iputdword(chip, ICHREG(GLOB_CNT), cnt | ICH_AC97COLD); | ||
2262 | msleep(1); | ||
2263 | #else | ||
2254 | /* finish cold or do warm reset */ | 2264 | /* finish cold or do warm reset */ |
2255 | cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; | 2265 | cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM; |
2256 | iputdword(chip, ICHREG(GLOB_CNT), cnt); | 2266 | iputdword(chip, ICHREG(GLOB_CNT), cnt); |
@@ -2265,6 +2275,7 @@ static int snd_intel8x0_ich_chip_init(struct intel8x0 *chip, int probing) | |||
2265 | return -EIO; | 2275 | return -EIO; |
2266 | 2276 | ||
2267 | __ok: | 2277 | __ok: |
2278 | #endif | ||
2268 | if (probing) { | 2279 | if (probing) { |
2269 | /* wait for any codec ready status. | 2280 | /* wait for any codec ready status. |
2270 | * Once it becomes ready it should remain ready | 2281 | * Once it becomes ready it should remain ready |
@@ -2485,7 +2496,7 @@ static int intel8x0_resume(struct pci_dev *pci) | |||
2485 | card->shortname, chip); | 2496 | card->shortname, chip); |
2486 | chip->irq = pci->irq; | 2497 | chip->irq = pci->irq; |
2487 | synchronize_irq(chip->irq); | 2498 | synchronize_irq(chip->irq); |
2488 | snd_intel8x0_chip_init(chip, 1); | 2499 | snd_intel8x0_chip_init(chip, 0); |
2489 | 2500 | ||
2490 | /* re-initialize mixer stuff */ | 2501 | /* re-initialize mixer stuff */ |
2491 | if (chip->device_type == DEVICE_INTEL_ICH4) { | 2502 | if (chip->device_type == DEVICE_INTEL_ICH4) { |
@@ -2615,6 +2626,7 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip) | |||
2615 | /* not 48000Hz, tuning the clock.. */ | 2626 | /* not 48000Hz, tuning the clock.. */ |
2616 | chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; | 2627 | chip->ac97_bus->clock = (chip->ac97_bus->clock * 48000) / pos; |
2617 | printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock); | 2628 | printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97_bus->clock); |
2629 | snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0); | ||
2618 | } | 2630 | } |
2619 | 2631 | ||
2620 | #ifdef CONFIG_PROC_FS | 2632 | #ifdef CONFIG_PROC_FS |
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 08da9234efb3..2c23a665c3e3 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c | |||
@@ -1277,7 +1277,18 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream) | |||
1277 | if (! ratep->used) | 1277 | if (! ratep->used) |
1278 | ratep->rate = 0; | 1278 | ratep->rate = 0; |
1279 | spin_unlock_irq(&ratep->lock); | 1279 | spin_unlock_irq(&ratep->lock); |
1280 | 1280 | if (! ratep->rate) { | |
1281 | if (! viadev->direction) { | ||
1282 | snd_ac97_update_power(chip->ac97, | ||
1283 | AC97_PCM_FRONT_DAC_RATE, 0); | ||
1284 | snd_ac97_update_power(chip->ac97, | ||
1285 | AC97_PCM_SURR_DAC_RATE, 0); | ||
1286 | snd_ac97_update_power(chip->ac97, | ||
1287 | AC97_PCM_LFE_DAC_RATE, 0); | ||
1288 | } else | ||
1289 | snd_ac97_update_power(chip->ac97, | ||
1290 | AC97_PCM_LR_ADC_RATE, 0); | ||
1291 | } | ||
1281 | viadev->substream = NULL; | 1292 | viadev->substream = NULL; |
1282 | return 0; | 1293 | return 0; |
1283 | } | 1294 | } |