diff options
author | Knut Petersen <Knut_Petersen@t-online.de> | 2013-08-21 03:18:54 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-08-22 05:54:57 -0400 |
commit | 528ba522e18b95d25adc62367f04290776c390e5 (patch) | |
tree | 65f4fff4b10d155aa6fc5a21be2b52f4f4d2ccac | |
parent | b43dd416be21bc8ad60984e13def032f01aaaa18 (diff) |
ALSA: rme96: Add PM support v3
Without proper power management handling, the first use
of a Digi96/8 anytime after a suspend / resume cycle will
start playback with distortions.
v3: Abort if vmalloc() of suspend buffers fail, but do not
leak memory in that case.
[fixed wrong memory leak fix again -- tiwai]
Signed-off-by: Knut Petersen <Knut_Petersen@t-online.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/pci/rme96.c | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 4e9a5563eeca..0506530de5a5 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c | |||
@@ -239,6 +239,13 @@ struct rme96 { | |||
239 | 239 | ||
240 | u8 rev; /* card revision number */ | 240 | u8 rev; /* card revision number */ |
241 | 241 | ||
242 | #ifdef CONFIG_PM | ||
243 | u32 playback_pointer; | ||
244 | u32 capture_pointer; | ||
245 | void *playback_suspend_buffer; | ||
246 | void *capture_suspend_buffer; | ||
247 | #endif | ||
248 | |||
242 | struct snd_pcm_substream *playback_substream; | 249 | struct snd_pcm_substream *playback_substream; |
243 | struct snd_pcm_substream *capture_substream; | 250 | struct snd_pcm_substream *capture_substream; |
244 | 251 | ||
@@ -370,6 +377,7 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info = | |||
370 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | | 377 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | |
371 | SNDRV_PCM_INFO_MMAP_VALID | | 378 | SNDRV_PCM_INFO_MMAP_VALID | |
372 | SNDRV_PCM_INFO_SYNC_START | | 379 | SNDRV_PCM_INFO_SYNC_START | |
380 | SNDRV_PCM_INFO_RESUME | | ||
373 | SNDRV_PCM_INFO_INTERLEAVED | | 381 | SNDRV_PCM_INFO_INTERLEAVED | |
374 | SNDRV_PCM_INFO_PAUSE), | 382 | SNDRV_PCM_INFO_PAUSE), |
375 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | | 383 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | |
@@ -400,6 +408,7 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info = | |||
400 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | | 408 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | |
401 | SNDRV_PCM_INFO_MMAP_VALID | | 409 | SNDRV_PCM_INFO_MMAP_VALID | |
402 | SNDRV_PCM_INFO_SYNC_START | | 410 | SNDRV_PCM_INFO_SYNC_START | |
411 | SNDRV_PCM_INFO_RESUME | | ||
403 | SNDRV_PCM_INFO_INTERLEAVED | | 412 | SNDRV_PCM_INFO_INTERLEAVED | |
404 | SNDRV_PCM_INFO_PAUSE), | 413 | SNDRV_PCM_INFO_PAUSE), |
405 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | | 414 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | |
@@ -430,6 +439,7 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info = | |||
430 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | | 439 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | |
431 | SNDRV_PCM_INFO_MMAP_VALID | | 440 | SNDRV_PCM_INFO_MMAP_VALID | |
432 | SNDRV_PCM_INFO_SYNC_START | | 441 | SNDRV_PCM_INFO_SYNC_START | |
442 | SNDRV_PCM_INFO_RESUME | | ||
433 | SNDRV_PCM_INFO_INTERLEAVED | | 443 | SNDRV_PCM_INFO_INTERLEAVED | |
434 | SNDRV_PCM_INFO_PAUSE), | 444 | SNDRV_PCM_INFO_PAUSE), |
435 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | | 445 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | |
@@ -456,6 +466,7 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info = | |||
456 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | | 466 | .info = (SNDRV_PCM_INFO_MMAP_IOMEM | |
457 | SNDRV_PCM_INFO_MMAP_VALID | | 467 | SNDRV_PCM_INFO_MMAP_VALID | |
458 | SNDRV_PCM_INFO_SYNC_START | | 468 | SNDRV_PCM_INFO_SYNC_START | |
469 | SNDRV_PCM_INFO_RESUME | | ||
459 | SNDRV_PCM_INFO_INTERLEAVED | | 470 | SNDRV_PCM_INFO_INTERLEAVED | |
460 | SNDRV_PCM_INFO_PAUSE), | 471 | SNDRV_PCM_INFO_PAUSE), |
461 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | | 472 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | |
@@ -1386,6 +1397,7 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream, | |||
1386 | } | 1397 | } |
1387 | break; | 1398 | break; |
1388 | 1399 | ||
1400 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
1389 | case SNDRV_PCM_TRIGGER_STOP: | 1401 | case SNDRV_PCM_TRIGGER_STOP: |
1390 | if (RME96_ISPLAYING(rme96)) { | 1402 | if (RME96_ISPLAYING(rme96)) { |
1391 | if (substream != rme96->playback_substream) | 1403 | if (substream != rme96->playback_substream) |
@@ -1401,6 +1413,7 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream, | |||
1401 | : RME96_STOP_PLAYBACK); | 1413 | : RME96_STOP_PLAYBACK); |
1402 | break; | 1414 | break; |
1403 | 1415 | ||
1416 | case SNDRV_PCM_TRIGGER_RESUME: | ||
1404 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 1417 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
1405 | if (!RME96_ISPLAYING(rme96)) | 1418 | if (!RME96_ISPLAYING(rme96)) |
1406 | snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH | 1419 | snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH |
@@ -1441,6 +1454,7 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream, | |||
1441 | } | 1454 | } |
1442 | break; | 1455 | break; |
1443 | 1456 | ||
1457 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
1444 | case SNDRV_PCM_TRIGGER_STOP: | 1458 | case SNDRV_PCM_TRIGGER_STOP: |
1445 | if (RME96_ISRECORDING(rme96)) { | 1459 | if (RME96_ISRECORDING(rme96)) { |
1446 | if (substream != rme96->capture_substream) | 1460 | if (substream != rme96->capture_substream) |
@@ -1456,6 +1470,7 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream, | |||
1456 | : RME96_STOP_CAPTURE); | 1470 | : RME96_STOP_CAPTURE); |
1457 | break; | 1471 | break; |
1458 | 1472 | ||
1473 | case SNDRV_PCM_TRIGGER_RESUME: | ||
1459 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 1474 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
1460 | if (!RME96_ISRECORDING(rme96)) | 1475 | if (!RME96_ISRECORDING(rme96)) |
1461 | snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH | 1476 | snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH |
@@ -1556,6 +1571,10 @@ snd_rme96_free(void *private_data) | |||
1556 | pci_release_regions(rme96->pci); | 1571 | pci_release_regions(rme96->pci); |
1557 | rme96->port = 0; | 1572 | rme96->port = 0; |
1558 | } | 1573 | } |
1574 | #ifdef CONFIG_PM | ||
1575 | vfree(rme96->playback_suspend_buffer); | ||
1576 | vfree(rme96->capture_suspend_buffer); | ||
1577 | #endif | ||
1559 | pci_disable_device(rme96->pci); | 1578 | pci_disable_device(rme96->pci); |
1560 | } | 1579 | } |
1561 | 1580 | ||
@@ -2354,6 +2373,83 @@ snd_rme96_create_switches(struct snd_card *card, | |||
2354 | * Card initialisation | 2373 | * Card initialisation |
2355 | */ | 2374 | */ |
2356 | 2375 | ||
2376 | #ifdef CONFIG_PM | ||
2377 | |||
2378 | static int | ||
2379 | snd_rme96_suspend(struct pci_dev *pci, | ||
2380 | pm_message_t state) | ||
2381 | { | ||
2382 | struct snd_card *card = pci_get_drvdata(pci); | ||
2383 | struct rme96 *rme96 = card->private_data; | ||
2384 | |||
2385 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | ||
2386 | snd_pcm_suspend(rme96->playback_substream); | ||
2387 | snd_pcm_suspend(rme96->capture_substream); | ||
2388 | |||
2389 | /* save capture & playback pointers */ | ||
2390 | rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS) | ||
2391 | & RME96_RCR_AUDIO_ADDR_MASK; | ||
2392 | rme96->capture_pointer = readl(rme96->iobase + RME96_IO_GET_REC_POS) | ||
2393 | & RME96_RCR_AUDIO_ADDR_MASK; | ||
2394 | |||
2395 | /* save playback and capture buffers */ | ||
2396 | memcpy_fromio(rme96->playback_suspend_buffer, | ||
2397 | rme96->iobase + RME96_IO_PLAY_BUFFER, RME96_BUFFER_SIZE); | ||
2398 | memcpy_fromio(rme96->capture_suspend_buffer, | ||
2399 | rme96->iobase + RME96_IO_REC_BUFFER, RME96_BUFFER_SIZE); | ||
2400 | |||
2401 | /* disable the DAC */ | ||
2402 | rme96->areg &= ~RME96_AR_DAC_EN; | ||
2403 | writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); | ||
2404 | |||
2405 | pci_disable_device(pci); | ||
2406 | pci_save_state(pci); | ||
2407 | |||
2408 | return 0; | ||
2409 | } | ||
2410 | |||
2411 | static int | ||
2412 | snd_rme96_resume(struct pci_dev *pci) | ||
2413 | { | ||
2414 | struct snd_card *card = pci_get_drvdata(pci); | ||
2415 | struct rme96 *rme96 = card->private_data; | ||
2416 | |||
2417 | pci_restore_state(pci); | ||
2418 | pci_enable_device(pci); | ||
2419 | |||
2420 | /* reset playback and record buffer pointers */ | ||
2421 | writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS | ||
2422 | + rme96->playback_pointer); | ||
2423 | writel(0, rme96->iobase + RME96_IO_SET_REC_POS | ||
2424 | + rme96->capture_pointer); | ||
2425 | |||
2426 | /* restore playback and capture buffers */ | ||
2427 | memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER, | ||
2428 | rme96->playback_suspend_buffer, RME96_BUFFER_SIZE); | ||
2429 | memcpy_toio(rme96->iobase + RME96_IO_REC_BUFFER, | ||
2430 | rme96->capture_suspend_buffer, RME96_BUFFER_SIZE); | ||
2431 | |||
2432 | /* reset the ADC */ | ||
2433 | writel(rme96->areg | RME96_AR_PD2, | ||
2434 | rme96->iobase + RME96_IO_ADDITIONAL_REG); | ||
2435 | writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); | ||
2436 | |||
2437 | /* reset and enable DAC, restore analog volume */ | ||
2438 | snd_rme96_reset_dac(rme96); | ||
2439 | rme96->areg |= RME96_AR_DAC_EN; | ||
2440 | writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); | ||
2441 | if (RME96_HAS_ANALOG_OUT(rme96)) { | ||
2442 | usleep_range(3000, 10000); | ||
2443 | snd_rme96_apply_dac_volume(rme96); | ||
2444 | } | ||
2445 | |||
2446 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | ||
2447 | |||
2448 | return 0; | ||
2449 | } | ||
2450 | |||
2451 | #endif | ||
2452 | |||
2357 | static void snd_rme96_card_free(struct snd_card *card) | 2453 | static void snd_rme96_card_free(struct snd_card *card) |
2358 | { | 2454 | { |
2359 | snd_rme96_free(card->private_data); | 2455 | snd_rme96_free(card->private_data); |
@@ -2390,6 +2486,23 @@ snd_rme96_probe(struct pci_dev *pci, | |||
2390 | return err; | 2486 | return err; |
2391 | } | 2487 | } |
2392 | 2488 | ||
2489 | #ifdef CONFIG_PM | ||
2490 | rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); | ||
2491 | if (!rme96->playback_suspend_buffer) { | ||
2492 | snd_printk(KERN_ERR | ||
2493 | "Failed to allocate playback suspend buffer!\n"); | ||
2494 | snd_card_free(card); | ||
2495 | return -ENOMEM; | ||
2496 | } | ||
2497 | rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE); | ||
2498 | if (!rme96->capture_suspend_buffer) { | ||
2499 | snd_printk(KERN_ERR | ||
2500 | "Failed to allocate capture suspend buffer!\n"); | ||
2501 | snd_card_free(card); | ||
2502 | return -ENOMEM; | ||
2503 | } | ||
2504 | #endif | ||
2505 | |||
2393 | strcpy(card->driver, "Digi96"); | 2506 | strcpy(card->driver, "Digi96"); |
2394 | switch (rme96->pci->device) { | 2507 | switch (rme96->pci->device) { |
2395 | case PCI_DEVICE_ID_RME_DIGI96: | 2508 | case PCI_DEVICE_ID_RME_DIGI96: |
@@ -2432,6 +2545,10 @@ static struct pci_driver rme96_driver = { | |||
2432 | .id_table = snd_rme96_ids, | 2545 | .id_table = snd_rme96_ids, |
2433 | .probe = snd_rme96_probe, | 2546 | .probe = snd_rme96_probe, |
2434 | .remove = snd_rme96_remove, | 2547 | .remove = snd_rme96_remove, |
2548 | #ifdef CONFIG_PM | ||
2549 | .suspend = snd_rme96_suspend, | ||
2550 | .resume = snd_rme96_resume, | ||
2551 | #endif | ||
2435 | }; | 2552 | }; |
2436 | 2553 | ||
2437 | module_pci_driver(rme96_driver); | 2554 | module_pci_driver(rme96_driver); |