aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@debian.org>2007-09-03 09:42:16 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-16 09:59:52 -0400
commit222fa0b0d2fdb2373a71d532c2cabd2ec920b3b3 (patch)
treefff88e0f8be8540b1fd74cc7f7d7b9fec783a8f4
parent7abcacb09ac0f9c6848f1e7d86b284427fa83cee (diff)
[ALSA] cs5535audio: fix PRD register save/restore power management race
In the suspend path, we currently save the PRD registers and then disable DMA. This is racy; the sound hardware might update the PRD register as it finishes processing some DMA pages between when we've saved the PRD registers and when DMA actually gets disabled. Furthermore, we actively check whether or not DMA is enabled before saving PRD registers; there's no reason to do that, as the PRD registers should not update when we twiddle the ACC_BM[x]_CMD register(s). Worst case, we save the PRD registers twice; even powering down the ACC shouldn't mess with the PRD registers (according to the 5536 data sheet, section 5.3.7.4, power-down procedure). This patch reworks all that to first disable DMA, and then save PRD registers. Signed-off-by: Andres Salomon <dilinger@debian.org> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
-rw-r--r--sound/pci/cs5535audio/cs5535audio.h1
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c2
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pm.c14
3 files changed, 7 insertions, 10 deletions
diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h
index 4fd1f31a6cf9..c7a204467037 100644
--- a/sound/pci/cs5535audio/cs5535audio.h
+++ b/sound/pci/cs5535audio/cs5535audio.h
@@ -106,7 +106,6 @@ struct cs5535audio_dma {
106 struct snd_pcm_substream *substream; 106 struct snd_pcm_substream *substream;
107 unsigned int buf_addr, buf_bytes; 107 unsigned int buf_addr, buf_bytes;
108 unsigned int period_bytes, periods; 108 unsigned int period_bytes, periods;
109 int suspended;
110 u32 saved_prd; 109 u32 saved_prd;
111}; 110};
112 111
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index 9a1e87fd4815..21df0634af32 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -297,14 +297,12 @@ static int snd_cs5535audio_trigger(struct snd_pcm_substream *substream, int cmd)
297 break; 297 break;
298 case SNDRV_PCM_TRIGGER_RESUME: 298 case SNDRV_PCM_TRIGGER_RESUME:
299 dma->ops->enable_dma(cs5535au); 299 dma->ops->enable_dma(cs5535au);
300 dma->suspended = 0;
301 break; 300 break;
302 case SNDRV_PCM_TRIGGER_STOP: 301 case SNDRV_PCM_TRIGGER_STOP:
303 dma->ops->disable_dma(cs5535au); 302 dma->ops->disable_dma(cs5535au);
304 break; 303 break;
305 case SNDRV_PCM_TRIGGER_SUSPEND: 304 case SNDRV_PCM_TRIGGER_SUSPEND:
306 dma->ops->disable_dma(cs5535au); 305 dma->ops->disable_dma(cs5535au);
307 dma->suspended = 1;
308 break; 306 break;
309 default: 307 default:
310 snd_printk(KERN_ERR "unhandled trigger\n"); 308 snd_printk(KERN_ERR "unhandled trigger\n");
diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c
index 3e4d198a4502..9a4e84aa3e04 100644
--- a/sound/pci/cs5535audio/cs5535audio_pm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pm.c
@@ -64,13 +64,13 @@ int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state)
64 int i; 64 int i;
65 65
66 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 66 snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
67 snd_pcm_suspend_all(cs5535au->pcm);
68 snd_ac97_suspend(cs5535au->ac97);
67 for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { 69 for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
68 struct cs5535audio_dma *dma = &cs5535au->dmas[i]; 70 struct cs5535audio_dma *dma = &cs5535au->dmas[i];
69 if (dma && dma->substream && !dma->suspended) 71 if (dma && dma->substream)
70 dma->saved_prd = dma->ops->read_prd(cs5535au); 72 dma->saved_prd = dma->ops->read_prd(cs5535au);
71 } 73 }
72 snd_pcm_suspend_all(cs5535au->pcm);
73 snd_ac97_suspend(cs5535au->ac97);
74 /* save important regs, then disable aclink in hw */ 74 /* save important regs, then disable aclink in hw */
75 snd_cs5535audio_stop_hardware(cs5535au); 75 snd_cs5535audio_stop_hardware(cs5535au);
76 76
@@ -112,17 +112,17 @@ int snd_cs5535audio_resume(struct pci_dev *pci)
112 if (!timeout) 112 if (!timeout)
113 snd_printk(KERN_ERR "Failure getting AC Link ready\n"); 113 snd_printk(KERN_ERR "Failure getting AC Link ready\n");
114 114
115 /* we depend on ac97 to perform the codec power up */
116 snd_ac97_resume(cs5535au->ac97);
117 /* set up rate regs, dma. actual initiation is done in trig */ 115 /* set up rate regs, dma. actual initiation is done in trig */
118 for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { 116 for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) {
119 struct cs5535audio_dma *dma = &cs5535au->dmas[i]; 117 struct cs5535audio_dma *dma = &cs5535au->dmas[i];
120 if (dma && dma->substream && dma->suspended) { 118 if (dma && dma->substream) {
121 dma->substream->ops->prepare(dma->substream); 119 dma->substream->ops->prepare(dma->substream);
122 dma->ops->setup_prd(cs5535au, dma->saved_prd); 120 dma->ops->setup_prd(cs5535au, dma->saved_prd);
123 } 121 }
124 } 122 }
125 123
124 /* we depend on ac97 to perform the codec power up */
125 snd_ac97_resume(cs5535au->ac97);
126 snd_power_change_state(card, SNDRV_CTL_POWER_D0); 126 snd_power_change_state(card, SNDRV_CTL_POWER_D0);
127 127
128 return 0; 128 return 0;