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 | |||
