aboutsummaryrefslogtreecommitdiffstats
path: root/sound/sh/aica.c
diff options
context:
space:
mode:
authorAdrian McMenamin <adrian@mcmen.demon.co.uk>2007-10-18 04:46:59 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-23 02:04:33 -0400
commit44e0b6821d7eacb4f93d2c131d436f96e500aa08 (patch)
tree8155a80b42af1ebf9ed0ac558bf3ec164076baac /sound/sh/aica.c
parente5ab3a7c00e682e0e24677203856769df1b9b0cb (diff)
[ALSA] protect Dreamcast PCM driver (AICA) from G2 bus effects
The G2 bus on the SEGA Dreamcast connects both the maple peripheral bus and the AICA sound memory. DMA requests on one can cause the other to timeout on memory operations. This patch prevents maple interrupts from causing hiccoughs in the AICA sound (maple bus code will land in 2.6.24). There are other cleanups for this (AICA) code - but this is in effect a regression fix rather than a cleanup. Signed-off-by: Adrian McMenamin <adrian@mcmen.demon.co.uk> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Diffstat (limited to 'sound/sh/aica.c')
-rw-r--r--sound/sh/aica.c31
1 files changed, 29 insertions, 2 deletions
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 131ec4812288..88dc840152ce 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -106,11 +106,14 @@ static void spu_write_wait(void)
106static void spu_memset(u32 toi, u32 what, int length) 106static void spu_memset(u32 toi, u32 what, int length)
107{ 107{
108 int i; 108 int i;
109 unsigned long flags;
109 snd_assert(length % 4 == 0, return); 110 snd_assert(length % 4 == 0, return);
110 for (i = 0; i < length; i++) { 111 for (i = 0; i < length; i++) {
111 if (!(i % 8)) 112 if (!(i % 8))
112 spu_write_wait(); 113 spu_write_wait();
114 local_irq_save(flags);
113 writel(what, toi + SPU_MEMORY_BASE); 115 writel(what, toi + SPU_MEMORY_BASE);
116 local_irq_restore(flags);
114 toi++; 117 toi++;
115 } 118 }
116} 119}
@@ -118,6 +121,7 @@ static void spu_memset(u32 toi, u32 what, int length)
118/* spu_memload - write to SPU address space */ 121/* spu_memload - write to SPU address space */
119static void spu_memload(u32 toi, void *from, int length) 122static void spu_memload(u32 toi, void *from, int length)
120{ 123{
124 unsigned long flags;
121 u32 *froml = from; 125 u32 *froml = from;
122 u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi); 126 u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
123 int i; 127 int i;
@@ -128,7 +132,9 @@ static void spu_memload(u32 toi, void *from, int length)
128 if (!(i % 8)) 132 if (!(i % 8))
129 spu_write_wait(); 133 spu_write_wait();
130 val = *froml; 134 val = *froml;
135 local_irq_save(flags);
131 writel(val, to); 136 writel(val, to);
137 local_irq_restore(flags);
132 froml++; 138 froml++;
133 to++; 139 to++;
134 } 140 }
@@ -138,28 +144,36 @@ static void spu_memload(u32 toi, void *from, int length)
138static void spu_disable(void) 144static void spu_disable(void)
139{ 145{
140 int i; 146 int i;
147 unsigned long flags;
141 u32 regval; 148 u32 regval;
142 spu_write_wait(); 149 spu_write_wait();
143 regval = readl(ARM_RESET_REGISTER); 150 regval = readl(ARM_RESET_REGISTER);
144 regval |= 1; 151 regval |= 1;
145 spu_write_wait(); 152 spu_write_wait();
153 local_irq_save(flags);
146 writel(regval, ARM_RESET_REGISTER); 154 writel(regval, ARM_RESET_REGISTER);
155 local_irq_restore(flags);
147 for (i = 0; i < 64; i++) { 156 for (i = 0; i < 64; i++) {
148 spu_write_wait(); 157 spu_write_wait();
149 regval = readl(SPU_REGISTER_BASE + (i * 0x80)); 158 regval = readl(SPU_REGISTER_BASE + (i * 0x80));
150 regval = (regval & ~0x4000) | 0x8000; 159 regval = (regval & ~0x4000) | 0x8000;
151 spu_write_wait(); 160 spu_write_wait();
161 local_irq_save(flags);
152 writel(regval, SPU_REGISTER_BASE + (i * 0x80)); 162 writel(regval, SPU_REGISTER_BASE + (i * 0x80));
163 local_irq_restore(flags);
153 } 164 }
154} 165}
155 166
156/* spu_enable - set spu registers to enable sound output */ 167/* spu_enable - set spu registers to enable sound output */
157static void spu_enable(void) 168static void spu_enable(void)
158{ 169{
170 unsigned long flags;
159 u32 regval = readl(ARM_RESET_REGISTER); 171 u32 regval = readl(ARM_RESET_REGISTER);
160 regval &= ~1; 172 regval &= ~1;
161 spu_write_wait(); 173 spu_write_wait();
174 local_irq_save(flags);
162 writel(regval, ARM_RESET_REGISTER); 175 writel(regval, ARM_RESET_REGISTER);
176 local_irq_restore(flags);
163} 177}
164 178
165/* 179/*
@@ -168,25 +182,34 @@ static void spu_enable(void)
168*/ 182*/
169static void spu_reset(void) 183static void spu_reset(void)
170{ 184{
185 unsigned long flags;
171 spu_disable(); 186 spu_disable();
172 spu_memset(0, 0, 0x200000 / 4); 187 spu_memset(0, 0, 0x200000 / 4);
173 /* Put ARM7 in endless loop */ 188 /* Put ARM7 in endless loop */
189 local_irq_save(flags);
174 ctrl_outl(0xea000002, SPU_MEMORY_BASE); 190 ctrl_outl(0xea000002, SPU_MEMORY_BASE);
191 local_irq_restore(flags);
175 spu_enable(); 192 spu_enable();
176} 193}
177 194
178/* aica_chn_start - write to spu to start playback */ 195/* aica_chn_start - write to spu to start playback */
179static void aica_chn_start(void) 196static void aica_chn_start(void)
180{ 197{
198 unsigned long flags;
181 spu_write_wait(); 199 spu_write_wait();
200 local_irq_save(flags);
182 writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT); 201 writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
202 local_irq_restore(flags);
183} 203}
184 204
185/* aica_chn_halt - write to spu to halt playback */ 205/* aica_chn_halt - write to spu to halt playback */
186static void aica_chn_halt(void) 206static void aica_chn_halt(void)
187{ 207{
208 unsigned long flags;
188 spu_write_wait(); 209 spu_write_wait();
210 local_irq_save(flags);
189 writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT); 211 writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
212 local_irq_restore(flags);
190} 213}
191 214
192/* ALSA code below */ 215/* ALSA code below */
@@ -213,12 +236,13 @@ static int aica_dma_transfer(int channels, int buffer_size,
213 int q, err, period_offset; 236 int q, err, period_offset;
214 struct snd_card_aica *dreamcastcard; 237 struct snd_card_aica *dreamcastcard;
215 struct snd_pcm_runtime *runtime; 238 struct snd_pcm_runtime *runtime;
216 err = 0; 239 unsigned long flags;
217 dreamcastcard = substream->pcm->private_data; 240 dreamcastcard = substream->pcm->private_data;
218 period_offset = dreamcastcard->clicks; 241 period_offset = dreamcastcard->clicks;
219 period_offset %= (AICA_PERIOD_NUMBER / channels); 242 period_offset %= (AICA_PERIOD_NUMBER / channels);
220 runtime = substream->runtime; 243 runtime = substream->runtime;
221 for (q = 0; q < channels; q++) { 244 for (q = 0; q < channels; q++) {
245 local_irq_save(flags);
222 err = dma_xfer(AICA_DMA_CHANNEL, 246 err = dma_xfer(AICA_DMA_CHANNEL,
223 (unsigned long) (runtime->dma_area + 247 (unsigned long) (runtime->dma_area +
224 (AICA_BUFFER_SIZE * q) / 248 (AICA_BUFFER_SIZE * q) /
@@ -228,9 +252,12 @@ static int aica_dma_transfer(int channels, int buffer_size,
228 AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET + 252 AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
229 AICA_PERIOD_SIZE * period_offset, 253 AICA_PERIOD_SIZE * period_offset,
230 buffer_size / channels, AICA_DMA_MODE); 254 buffer_size / channels, AICA_DMA_MODE);
231 if (unlikely(err < 0)) 255 if (unlikely(err < 0)) {
256 local_irq_restore(flags);
232 break; 257 break;
258 }
233 dma_wait_for_completion(AICA_DMA_CHANNEL); 259 dma_wait_for_completion(AICA_DMA_CHANNEL);
260 local_irq_restore(flags);
234 } 261 }
235 return err; 262 return err;
236} 263}