diff options
author | Takashi Iwai <tiwai@suse.de> | 2005-11-17 10:14:33 -0500 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2006-01-03 06:27:59 -0500 |
commit | b1e9ed26a9e472548a63a59014708fdae013b7a3 (patch) | |
tree | 69403fa8ec2c417af81098946888e2555ae5e4b9 /sound/pci/fm801.c | |
parent | 09668b441dacdf4640509b640ad73e24efd5204f (diff) |
[ALSA] fm801 - Add PM support
Modules: FM801 driver
Add PM support to fm801 driver.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/fm801.c')
-rw-r--r-- | sound/pci/fm801.c | 245 |
1 files changed, 158 insertions, 87 deletions
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index a57aca72e91a..6ab4aefbccf8 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c | |||
@@ -103,7 +103,11 @@ MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner."); | |||
103 | #define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ | 103 | #define FM801_OPL3_DATA1 0x6b /* OPL3 Bank 1 Write */ |
104 | #define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */ | 104 | #define FM801_POWERDOWN 0x70 /* Blocks Power Down Control */ |
105 | 105 | ||
106 | #define FM801_AC97_ADDR_SHIFT 10 | 106 | /* codec access */ |
107 | #define FM801_AC97_READ (1<<7) /* read=1, write=0 */ | ||
108 | #define FM801_AC97_VALID (1<<8) /* port valid=1 */ | ||
109 | #define FM801_AC97_BUSY (1<<9) /* busy=1 */ | ||
110 | #define FM801_AC97_ADDR_SHIFT 10 /* codec id (2bit) */ | ||
107 | 111 | ||
108 | /* playback and record control register bits */ | 112 | /* playback and record control register bits */ |
109 | #define FM801_BUF1_LAST (1<<1) | 113 | #define FM801_BUF1_LAST (1<<1) |
@@ -189,6 +193,10 @@ struct fm801 { | |||
189 | #ifdef TEA575X_RADIO | 193 | #ifdef TEA575X_RADIO |
190 | struct snd_tea575x tea; | 194 | struct snd_tea575x tea; |
191 | #endif | 195 | #endif |
196 | |||
197 | #ifdef CONFIG_PM | ||
198 | u16 saved_regs[0x20]; | ||
199 | #endif | ||
192 | }; | 200 | }; |
193 | 201 | ||
194 | static struct pci_device_id snd_fm801_ids[] = { | 202 | static struct pci_device_id snd_fm801_ids[] = { |
@@ -231,7 +239,7 @@ static void snd_fm801_codec_write(struct snd_ac97 *ac97, | |||
231 | * Wait until the codec interface is not ready.. | 239 | * Wait until the codec interface is not ready.. |
232 | */ | 240 | */ |
233 | for (idx = 0; idx < 100; idx++) { | 241 | for (idx = 0; idx < 100; idx++) { |
234 | if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) | 242 | if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) |
235 | goto ok1; | 243 | goto ok1; |
236 | udelay(10); | 244 | udelay(10); |
237 | } | 245 | } |
@@ -246,7 +254,7 @@ static void snd_fm801_codec_write(struct snd_ac97 *ac97, | |||
246 | * Wait until the write command is not completed.. | 254 | * Wait until the write command is not completed.. |
247 | */ | 255 | */ |
248 | for (idx = 0; idx < 1000; idx++) { | 256 | for (idx = 0; idx < 1000; idx++) { |
249 | if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) | 257 | if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) |
250 | return; | 258 | return; |
251 | udelay(10); | 259 | udelay(10); |
252 | } | 260 | } |
@@ -262,7 +270,7 @@ static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short | |||
262 | * Wait until the codec interface is not ready.. | 270 | * Wait until the codec interface is not ready.. |
263 | */ | 271 | */ |
264 | for (idx = 0; idx < 100; idx++) { | 272 | for (idx = 0; idx < 100; idx++) { |
265 | if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) | 273 | if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) |
266 | goto ok1; | 274 | goto ok1; |
267 | udelay(10); | 275 | udelay(10); |
268 | } | 276 | } |
@@ -271,9 +279,10 @@ static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short | |||
271 | 279 | ||
272 | ok1: | 280 | ok1: |
273 | /* read command */ | 281 | /* read command */ |
274 | outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | (1<<7), FM801_REG(chip, AC97_CMD)); | 282 | outw(reg | (ac97->addr << FM801_AC97_ADDR_SHIFT) | FM801_AC97_READ, |
283 | FM801_REG(chip, AC97_CMD)); | ||
275 | for (idx = 0; idx < 100; idx++) { | 284 | for (idx = 0; idx < 100; idx++) { |
276 | if (!(inw(FM801_REG(chip, AC97_CMD)) & (1<<9))) | 285 | if (!(inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_BUSY)) |
277 | goto ok2; | 286 | goto ok2; |
278 | udelay(10); | 287 | udelay(10); |
279 | } | 288 | } |
@@ -282,7 +291,7 @@ static unsigned short snd_fm801_codec_read(struct snd_ac97 *ac97, unsigned short | |||
282 | 291 | ||
283 | ok2: | 292 | ok2: |
284 | for (idx = 0; idx < 1000; idx++) { | 293 | for (idx = 0; idx < 1000; idx++) { |
285 | if (inw(FM801_REG(chip, AC97_CMD)) & (1<<8)) | 294 | if (inw(FM801_REG(chip, AC97_CMD)) & FM801_AC97_VALID) |
286 | goto ok3; | 295 | goto ok3; |
287 | udelay(10); | 296 | udelay(10); |
288 | } | 297 | } |
@@ -354,9 +363,11 @@ static int snd_fm801_playback_trigger(struct snd_pcm_substream *substream, | |||
354 | chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE); | 363 | chip->ply_ctrl &= ~(FM801_START | FM801_PAUSE); |
355 | break; | 364 | break; |
356 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 365 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
366 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
357 | chip->ply_ctrl |= FM801_PAUSE; | 367 | chip->ply_ctrl |= FM801_PAUSE; |
358 | break; | 368 | break; |
359 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 369 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
370 | case SNDRV_PCM_TRIGGER_RESUME: | ||
360 | chip->ply_ctrl &= ~FM801_PAUSE; | 371 | chip->ply_ctrl &= ~FM801_PAUSE; |
361 | break; | 372 | break; |
362 | default: | 373 | default: |
@@ -387,9 +398,11 @@ static int snd_fm801_capture_trigger(struct snd_pcm_substream *substream, | |||
387 | chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE); | 398 | chip->cap_ctrl &= ~(FM801_START | FM801_PAUSE); |
388 | break; | 399 | break; |
389 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 400 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
401 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
390 | chip->cap_ctrl |= FM801_PAUSE; | 402 | chip->cap_ctrl |= FM801_PAUSE; |
391 | break; | 403 | break; |
392 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 404 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
405 | case SNDRV_PCM_TRIGGER_RESUME: | ||
393 | chip->cap_ctrl &= ~FM801_PAUSE; | 406 | chip->cap_ctrl &= ~FM801_PAUSE; |
394 | break; | 407 | break; |
395 | default: | 408 | default: |
@@ -557,7 +570,7 @@ static struct snd_pcm_hardware snd_fm801_playback = | |||
557 | { | 570 | { |
558 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | 571 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | |
559 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | 572 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
560 | SNDRV_PCM_INFO_PAUSE | | 573 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | |
561 | SNDRV_PCM_INFO_MMAP_VALID), | 574 | SNDRV_PCM_INFO_MMAP_VALID), |
562 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | 575 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, |
563 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | 576 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, |
@@ -577,7 +590,7 @@ static struct snd_pcm_hardware snd_fm801_capture = | |||
577 | { | 590 | { |
578 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | 591 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | |
579 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | 592 | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
580 | SNDRV_PCM_INFO_PAUSE | | 593 | SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | |
581 | SNDRV_PCM_INFO_MMAP_VALID), | 594 | SNDRV_PCM_INFO_MMAP_VALID), |
582 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, | 595 | .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, |
583 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, | 596 | .rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, |
@@ -1218,6 +1231,85 @@ static int __devinit snd_fm801_mixer(struct fm801 *chip) | |||
1218 | * initialization routines | 1231 | * initialization routines |
1219 | */ | 1232 | */ |
1220 | 1233 | ||
1234 | static int wait_for_codec(struct fm801 *chip, unsigned int codec_id, | ||
1235 | unsigned short reg, unsigned long waits) | ||
1236 | { | ||
1237 | unsigned long timeout = jiffies + waits; | ||
1238 | |||
1239 | outw(FM801_AC97_READ | (codec_id << FM801_AC97_ADDR_SHIFT) | reg, | ||
1240 | FM801_REG(chip, AC97_CMD)); | ||
1241 | udelay(5); | ||
1242 | do { | ||
1243 | if ((inw(FM801_REG(chip, AC97_CMD)) & (FM801_AC97_VALID|FM801_AC97_BUSY)) | ||
1244 | == FM801_AC97_VALID) | ||
1245 | return 0; | ||
1246 | schedule_timeout_uninterruptible(1); | ||
1247 | } while (time_after(timeout, jiffies)); | ||
1248 | return -EIO; | ||
1249 | } | ||
1250 | |||
1251 | static int snd_fm801_chip_init(struct fm801 *chip, int resume) | ||
1252 | { | ||
1253 | int id; | ||
1254 | unsigned short cmdw; | ||
1255 | |||
1256 | /* codec cold reset + AC'97 warm reset */ | ||
1257 | outw((1<<5) | (1<<6), FM801_REG(chip, CODEC_CTRL)); | ||
1258 | inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */ | ||
1259 | udelay(100); | ||
1260 | outw(0, FM801_REG(chip, CODEC_CTRL)); | ||
1261 | |||
1262 | if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) { | ||
1263 | snd_printk(KERN_ERR "Primary AC'97 codec not found\n"); | ||
1264 | if (! resume) | ||
1265 | return -EIO; | ||
1266 | } | ||
1267 | |||
1268 | if (chip->multichannel) { | ||
1269 | if (chip->secondary_addr) { | ||
1270 | wait_for_codec(chip, chip->secondary_addr, | ||
1271 | AC97_VENDOR_ID1, msecs_to_jiffies(50)); | ||
1272 | } else { | ||
1273 | /* my card has the secondary codec */ | ||
1274 | /* at address #3, so the loop is inverted */ | ||
1275 | for (id = 3; id > 0; id--) { | ||
1276 | if (! wait_for_codec(chip, id, AC97_VENDOR_ID1, | ||
1277 | msecs_to_jiffies(50))) { | ||
1278 | cmdw = inw(FM801_REG(chip, AC97_DATA)); | ||
1279 | if (cmdw != 0xffff && cmdw != 0) { | ||
1280 | chip->secondary = 1; | ||
1281 | chip->secondary_addr = id; | ||
1282 | break; | ||
1283 | } | ||
1284 | } | ||
1285 | } | ||
1286 | } | ||
1287 | |||
1288 | /* the recovery phase, it seems that probing for non-existing codec might */ | ||
1289 | /* cause timeout problems */ | ||
1290 | wait_for_codec(chip, 0, AC97_VENDOR_ID1, msecs_to_jiffies(750)); | ||
1291 | } | ||
1292 | |||
1293 | /* init volume */ | ||
1294 | outw(0x0808, FM801_REG(chip, PCM_VOL)); | ||
1295 | outw(0x9f1f, FM801_REG(chip, FM_VOL)); | ||
1296 | outw(0x8808, FM801_REG(chip, I2S_VOL)); | ||
1297 | |||
1298 | /* I2S control - I2S mode */ | ||
1299 | outw(0x0003, FM801_REG(chip, I2S_MODE)); | ||
1300 | |||
1301 | /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */ | ||
1302 | cmdw = inw(FM801_REG(chip, IRQ_MASK)); | ||
1303 | cmdw &= ~0x0083; | ||
1304 | outw(cmdw, FM801_REG(chip, IRQ_MASK)); | ||
1305 | |||
1306 | /* interrupt clear */ | ||
1307 | outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); | ||
1308 | |||
1309 | return 0; | ||
1310 | } | ||
1311 | |||
1312 | |||
1221 | static int snd_fm801_free(struct fm801 *chip) | 1313 | static int snd_fm801_free(struct fm801 *chip) |
1222 | { | 1314 | { |
1223 | unsigned short cmdw; | 1315 | unsigned short cmdw; |
@@ -1255,9 +1347,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, | |||
1255 | struct fm801 ** rchip) | 1347 | struct fm801 ** rchip) |
1256 | { | 1348 | { |
1257 | struct fm801 *chip; | 1349 | struct fm801 *chip; |
1258 | unsigned char rev, id; | 1350 | unsigned char rev; |
1259 | unsigned short cmdw; | ||
1260 | unsigned long timeout; | ||
1261 | int err; | 1351 | int err; |
1262 | static struct snd_device_ops ops = { | 1352 | static struct snd_device_ops ops = { |
1263 | .dev_free = snd_fm801_dev_free, | 1353 | .dev_free = snd_fm801_dev_free, |
@@ -1294,81 +1384,7 @@ static int __devinit snd_fm801_create(struct snd_card *card, | |||
1294 | if (rev >= 0xb1) /* FM801-AU */ | 1384 | if (rev >= 0xb1) /* FM801-AU */ |
1295 | chip->multichannel = 1; | 1385 | chip->multichannel = 1; |
1296 | 1386 | ||
1297 | /* codec cold reset + AC'97 warm reset */ | 1387 | snd_fm801_chip_init(chip, 0); |
1298 | outw((1<<5)|(1<<6), FM801_REG(chip, CODEC_CTRL)); | ||
1299 | inw(FM801_REG(chip, CODEC_CTRL)); /* flush posting data */ | ||
1300 | udelay(100); | ||
1301 | outw(0, FM801_REG(chip, CODEC_CTRL)); | ||
1302 | |||
1303 | timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ | ||
1304 | |||
1305 | outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); | ||
1306 | udelay(5); | ||
1307 | do { | ||
1308 | if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) | ||
1309 | goto __ac97_secondary; | ||
1310 | schedule_timeout_uninterruptible(1); | ||
1311 | } while (time_after(timeout, jiffies)); | ||
1312 | snd_printk(KERN_ERR "Primary AC'97 codec not found\n"); | ||
1313 | snd_fm801_free(chip); | ||
1314 | return -EIO; | ||
1315 | |||
1316 | __ac97_secondary: | ||
1317 | if (!chip->multichannel) /* lookup is not required */ | ||
1318 | goto __ac97_ok; | ||
1319 | for (id = 3; id > 0; id--) { /* my card has the secondary codec */ | ||
1320 | /* at address #3, so the loop is inverted */ | ||
1321 | |||
1322 | timeout = jiffies + HZ / 20; | ||
1323 | |||
1324 | outw((1<<7) | (id << FM801_AC97_ADDR_SHIFT) | AC97_VENDOR_ID1, | ||
1325 | FM801_REG(chip, AC97_CMD)); | ||
1326 | udelay(5); | ||
1327 | do { | ||
1328 | if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) { | ||
1329 | cmdw = inw(FM801_REG(chip, AC97_DATA)); | ||
1330 | if (cmdw != 0xffff && cmdw != 0) { | ||
1331 | chip->secondary = 1; | ||
1332 | chip->secondary_addr = id; | ||
1333 | goto __ac97_ok; | ||
1334 | } | ||
1335 | } | ||
1336 | schedule_timeout_uninterruptible(1); | ||
1337 | } while (time_after(timeout, jiffies)); | ||
1338 | } | ||
1339 | |||
1340 | /* the recovery phase, it seems that probing for non-existing codec might */ | ||
1341 | /* cause timeout problems */ | ||
1342 | timeout = (jiffies + (3 * HZ) / 4) + 1; /* min 750ms */ | ||
1343 | |||
1344 | outw((1<<7) | (0 << FM801_AC97_ADDR_SHIFT), FM801_REG(chip, AC97_CMD)); | ||
1345 | udelay(5); | ||
1346 | do { | ||
1347 | if ((inw(FM801_REG(chip, AC97_CMD)) & (3<<8)) == (1<<8)) | ||
1348 | goto __ac97_ok; | ||
1349 | schedule_timeout_uninterruptible(1); | ||
1350 | } while (time_after(timeout, jiffies)); | ||
1351 | snd_printk(KERN_ERR "Primary AC'97 codec not responding\n"); | ||
1352 | snd_fm801_free(chip); | ||
1353 | return -EIO; | ||
1354 | |||
1355 | __ac97_ok: | ||
1356 | |||
1357 | /* init volume */ | ||
1358 | outw(0x0808, FM801_REG(chip, PCM_VOL)); | ||
1359 | outw(0x9f1f, FM801_REG(chip, FM_VOL)); | ||
1360 | outw(0x8808, FM801_REG(chip, I2S_VOL)); | ||
1361 | |||
1362 | /* I2S control - I2S mode */ | ||
1363 | outw(0x0003, FM801_REG(chip, I2S_MODE)); | ||
1364 | |||
1365 | /* interrupt setup - unmask MPU, PLAYBACK & CAPTURE */ | ||
1366 | cmdw = inw(FM801_REG(chip, IRQ_MASK)); | ||
1367 | cmdw &= ~0x0083; | ||
1368 | outw(cmdw, FM801_REG(chip, IRQ_MASK)); | ||
1369 | |||
1370 | /* interrupt clear */ | ||
1371 | outw(FM801_IRQ_PLAYBACK|FM801_IRQ_CAPTURE|FM801_IRQ_MPU, FM801_REG(chip, IRQ_STATUS)); | ||
1372 | 1388 | ||
1373 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { | 1389 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { |
1374 | snd_fm801_free(chip); | 1390 | snd_fm801_free(chip); |
@@ -1415,6 +1431,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci, | |||
1415 | snd_card_free(card); | 1431 | snd_card_free(card); |
1416 | return err; | 1432 | return err; |
1417 | } | 1433 | } |
1434 | card->private_data = chip; | ||
1418 | 1435 | ||
1419 | strcpy(card->driver, "FM801"); | 1436 | strcpy(card->driver, "FM801"); |
1420 | strcpy(card->shortname, "ForteMedia FM801-"); | 1437 | strcpy(card->shortname, "ForteMedia FM801-"); |
@@ -1462,11 +1479,65 @@ static void __devexit snd_card_fm801_remove(struct pci_dev *pci) | |||
1462 | pci_set_drvdata(pci, NULL); | 1479 | pci_set_drvdata(pci, NULL); |
1463 | } | 1480 | } |
1464 | 1481 | ||
1482 | #ifdef CONFIG_PM | ||
1483 | static unsigned char saved_regs[] = { | ||
1484 | FM801_PCM_VOL, FM801_I2S_VOL, FM801_FM_VOL, FM801_REC_SRC, | ||
1485 | FM801_PLY_CTRL, FM801_PLY_COUNT, FM801_PLY_BUF1, FM801_PLY_BUF2, | ||
1486 | FM801_CAP_CTRL, FM801_CAP_COUNT, FM801_CAP_BUF1, FM801_CAP_BUF2, | ||
1487 | FM801_CODEC_CTRL, FM801_I2S_MODE, FM801_VOLUME, FM801_GEN_CTRL, | ||
1488 | }; | ||
1489 | |||
1490 | static int snd_fm801_suspend(struct pci_dev *pci, pm_message_t state) | ||
1491 | { | ||
1492 | struct snd_card *card = pci_get_drvdata(pci); | ||
1493 | struct fm801 *chip = card->private_data; | ||
1494 | int i; | ||
1495 | |||
1496 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
1497 | snd_pcm_suspend_all(chip->pcm); | ||
1498 | snd_ac97_suspend(chip->ac97); | ||
1499 | snd_ac97_suspend(chip->ac97_sec); | ||
1500 | for (i = 0; i < ARRAY_SIZE(saved_regs); i++) | ||
1501 | chip->saved_regs[i] = inw(chip->port + saved_regs[i]); | ||
1502 | /* FIXME: tea575x suspend */ | ||
1503 | |||
1504 | pci_set_power_state(pci, PCI_D3hot); | ||
1505 | pci_disable_device(pci); | ||
1506 | pci_save_state(pci); | ||
1507 | return 0; | ||
1508 | } | ||
1509 | |||
1510 | static int snd_fm801_resume(struct pci_dev *pci) | ||
1511 | { | ||
1512 | struct snd_card *card = pci_get_drvdata(pci); | ||
1513 | struct fm801 *chip = card->private_data; | ||
1514 | int i; | ||
1515 | |||
1516 | pci_restore_state(pci); | ||
1517 | pci_enable_device(pci); | ||
1518 | pci_set_power_state(pci, PCI_D0); | ||
1519 | pci_set_master(pci); | ||
1520 | |||
1521 | snd_fm801_chip_init(chip, 1); | ||
1522 | snd_ac97_resume(chip->ac97); | ||
1523 | snd_ac97_resume(chip->ac97_sec); | ||
1524 | for (i = 0; i < ARRAY_SIZE(saved_regs); i++) | ||
1525 | outw(chip->saved_regs[i], chip->port + saved_regs[i]); | ||
1526 | |||
1527 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
1528 | return 0; | ||
1529 | } | ||
1530 | #endif | ||
1531 | |||
1465 | static struct pci_driver driver = { | 1532 | static struct pci_driver driver = { |
1466 | .name = "FM801", | 1533 | .name = "FM801", |
1467 | .id_table = snd_fm801_ids, | 1534 | .id_table = snd_fm801_ids, |
1468 | .probe = snd_card_fm801_probe, | 1535 | .probe = snd_card_fm801_probe, |
1469 | .remove = __devexit_p(snd_card_fm801_remove), | 1536 | .remove = __devexit_p(snd_card_fm801_remove), |
1537 | #ifdef CONFIG_PM | ||
1538 | .suspend = snd_fm801_suspend, | ||
1539 | .resume = snd_fm801_resume, | ||
1540 | #endif | ||
1470 | }; | 1541 | }; |
1471 | 1542 | ||
1472 | static int __init alsa_card_fm801_init(void) | 1543 | static int __init alsa_card_fm801_init(void) |