aboutsummaryrefslogtreecommitdiffstats
path: root/sound/sh/aica.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-07-09 04:36:22 -0400
committerJaroslav Kysela <perex@suse.cz>2007-07-20 05:11:47 -0400
commitd20b9b20e5f3bb82a6f38583154cb2f163d465d7 (patch)
tree18c6d93adde1c4185f31f997441977a04d4176f8 /sound/sh/aica.c
parent43bbb6ccc67775b432c31d5fd2e1050329ffdcbf (diff)
[ALSA] aica - fix behaviour in poor resource conditions
Fix behaviour in poor resource conditions. With this new patch, tidied with indent and with SNDRV_PCM_TRIGGER_STOP being used. Signed-off by: Adrian McMenamin <adrian@mcmen.demon.co.uk> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/sh/aica.c')
-rw-r--r--sound/sh/aica.c82
1 files changed, 36 insertions, 46 deletions
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index 97bb86a58622..739786529ca5 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -64,12 +64,6 @@ module_param(enable, bool, 0644);
64MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); 64MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
65 65
66/* Use workqueue */ 66/* Use workqueue */
67
68static struct spu_work_holder {
69 struct work_struct spu_dma_work;
70 void *sspointer;
71} spu_working;
72
73static struct workqueue_struct *aica_queue; 67static struct workqueue_struct *aica_queue;
74 68
75/* Simple platform device */ 69/* Simple platform device */
@@ -100,9 +94,9 @@ static void spu_write_wait(void)
100 break; 94 break;
101 /* To ensure hardware failure doesn't wedge kernel */ 95 /* To ensure hardware failure doesn't wedge kernel */
102 time_count++; 96 time_count++;
103 if (time_count > 0x10000) 97 if (time_count > 0x10000) {
104 { 98 snd_printk
105 snd_printk("WARNING: G2 FIFO appears to be blocked.\n"); 99 ("WARNING: G2 FIFO appears to be blocked.\n");
106 break; 100 break;
107 } 101 }
108 } 102 }
@@ -226,11 +220,11 @@ static int aica_dma_transfer(int channels, int buffer_size,
226 runtime = substream->runtime; 220 runtime = substream->runtime;
227 for (q = 0; q < channels; q++) { 221 for (q = 0; q < channels; q++) {
228 err = dma_xfer(AICA_DMA_CHANNEL, 222 err = dma_xfer(AICA_DMA_CHANNEL,
229 (unsigned long)(runtime->dma_area + 223 (unsigned long) (runtime->dma_area +
230 (AICA_BUFFER_SIZE * q) / 224 (AICA_BUFFER_SIZE * q) /
231 channels + 225 channels +
232 AICA_PERIOD_SIZE * 226 AICA_PERIOD_SIZE *
233 period_offset), 227 period_offset),
234 AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET + 228 AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
235 AICA_PERIOD_SIZE * period_offset, 229 AICA_PERIOD_SIZE * period_offset,
236 buffer_size / channels, AICA_DMA_MODE); 230 buffer_size / channels, AICA_DMA_MODE);
@@ -244,26 +238,25 @@ static int aica_dma_transfer(int channels, int buffer_size,
244static void startup_aica(struct snd_card_aica *dreamcastcard) 238static void startup_aica(struct snd_card_aica *dreamcastcard)
245{ 239{
246 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET, 240 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
247 dreamcastcard->channel, 241 dreamcastcard->channel, sizeof(struct aica_channel));
248 sizeof(struct aica_channel));
249 aica_chn_start(); 242 aica_chn_start();
250} 243}
251 244
252static void run_spu_dma(struct work_struct *work) 245static void run_spu_dma(struct work_struct *work)
253{ 246{
254 int buffer_size; 247 int buffer_size;
255 struct snd_pcm_substream *substream;
256 struct snd_pcm_runtime *runtime; 248 struct snd_pcm_runtime *runtime;
257 struct snd_card_aica *dreamcastcard; 249 struct snd_card_aica *dreamcastcard;
258 struct spu_work_holder *holder = container_of(work, struct spu_work_holder, spu_dma_work); 250 dreamcastcard =
259 substream = holder-> sspointer; 251 container_of(work, struct snd_card_aica, spu_dma_work);
260 dreamcastcard = substream->pcm->private_data; 252 runtime = dreamcastcard->substream->runtime;
261 runtime = substream->runtime;
262 if (unlikely(dreamcastcard->dma_check == 0)) { 253 if (unlikely(dreamcastcard->dma_check == 0)) {
263 buffer_size = frames_to_bytes(runtime, runtime->buffer_size); 254 buffer_size =
255 frames_to_bytes(runtime, runtime->buffer_size);
264 if (runtime->channels > 1) 256 if (runtime->channels > 1)
265 dreamcastcard->channel->flags |= 0x01; 257 dreamcastcard->channel->flags |= 0x01;
266 aica_dma_transfer(runtime->channels, buffer_size, substream); 258 aica_dma_transfer(runtime->channels, buffer_size,
259 dreamcastcard->substream);
267 startup_aica(dreamcastcard); 260 startup_aica(dreamcastcard);
268 dreamcastcard->clicks = 261 dreamcastcard->clicks =
269 buffer_size / (AICA_PERIOD_SIZE * runtime->channels); 262 buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
@@ -271,13 +264,11 @@ static void run_spu_dma(struct work_struct *work)
271 } else { 264 } else {
272 aica_dma_transfer(runtime->channels, 265 aica_dma_transfer(runtime->channels,
273 AICA_PERIOD_SIZE * runtime->channels, 266 AICA_PERIOD_SIZE * runtime->channels,
274 substream); 267 dreamcastcard->substream);
275 snd_pcm_period_elapsed(dreamcastcard->substream); 268 snd_pcm_period_elapsed(dreamcastcard->substream);
276 dreamcastcard->clicks++; 269 dreamcastcard->clicks++;
277 if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER)) 270 if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
278 {
279 dreamcastcard->clicks %= AICA_PERIOD_NUMBER; 271 dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
280 }
281 mod_timer(&dreamcastcard->timer, jiffies + 1); 272 mod_timer(&dreamcastcard->timer, jiffies + 1);
282 } 273 }
283} 274}
@@ -289,7 +280,7 @@ static void aica_period_elapsed(unsigned long timer_var)
289 struct snd_pcm_runtime *runtime; 280 struct snd_pcm_runtime *runtime;
290 struct snd_pcm_substream *substream; 281 struct snd_pcm_substream *substream;
291 struct snd_card_aica *dreamcastcard; 282 struct snd_card_aica *dreamcastcard;
292 substream = (struct snd_pcm_substream *)timer_var; 283 substream = (struct snd_pcm_substream *) timer_var;
293 runtime = substream->runtime; 284 runtime = substream->runtime;
294 dreamcastcard = substream->pcm->private_data; 285 dreamcastcard = substream->pcm->private_data;
295 /* Have we played out an additional period? */ 286 /* Have we played out an additional period? */
@@ -307,27 +298,24 @@ static void aica_period_elapsed(unsigned long timer_var)
307 dreamcastcard->current_period = play_period; 298 dreamcastcard->current_period = play_period;
308 if (unlikely(dreamcastcard->dma_check == 0)) 299 if (unlikely(dreamcastcard->dma_check == 0))
309 dreamcastcard->dma_check = 1; 300 dreamcastcard->dma_check = 1;
310 queue_work(aica_queue, &(spu_working.spu_dma_work)); 301 queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
311} 302}
312 303
313static void spu_begin_dma(struct snd_pcm_substream *substream) 304static void spu_begin_dma(struct snd_pcm_substream *substream)
314{ 305{
315 /* Must be atomic */
316 struct snd_card_aica *dreamcastcard; 306 struct snd_card_aica *dreamcastcard;
317 struct snd_pcm_runtime *runtime; 307 struct snd_pcm_runtime *runtime;
318 runtime = substream->runtime; 308 runtime = substream->runtime;
319 dreamcastcard = substream->pcm->private_data; 309 dreamcastcard = substream->pcm->private_data;
320 /* Use queue to do the heavy lifting */ 310 /*get the queue to do the work */
321 spu_working.sspointer = substream; 311 queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
322 INIT_WORK(&(spu_working.spu_dma_work), run_spu_dma);
323 queue_work(aica_queue, &(spu_working.spu_dma_work));
324 /* Timer may already be running */ 312 /* Timer may already be running */
325 if (unlikely(dreamcastcard->timer.data)) { 313 if (unlikely(dreamcastcard->timer.data)) {
326 mod_timer(&dreamcastcard->timer, jiffies + 4); 314 mod_timer(&dreamcastcard->timer, jiffies + 4);
327 return; 315 return;
328 } 316 }
329 init_timer(&(dreamcastcard->timer)); 317 init_timer(&(dreamcastcard->timer));
330 dreamcastcard->timer.data = (unsigned long)substream; 318 dreamcastcard->timer.data = (unsigned long) substream;
331 dreamcastcard->timer.function = aica_period_elapsed; 319 dreamcastcard->timer.function = aica_period_elapsed;
332 dreamcastcard->timer.expires = jiffies + 4; 320 dreamcastcard->timer.expires = jiffies + 4;
333 add_timer(&(dreamcastcard->timer)); 321 add_timer(&(dreamcastcard->timer));
@@ -366,7 +354,9 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream
366 *substream) 354 *substream)
367{ 355{
368 struct snd_card_aica *dreamcastcard = substream->pcm->private_data; 356 struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
369 del_timer(&dreamcastcard->timer); 357 flush_workqueue(aica_queue);
358 if (dreamcastcard->timer.data)
359 del_timer(&dreamcastcard->timer);
370 kfree(dreamcastcard->channel); 360 kfree(dreamcastcard->channel);
371 spu_disable(); 361 spu_disable();
372 return 0; 362 return 0;
@@ -385,7 +375,8 @@ static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
385{ 375{
386 /* Allocate a DMA buffer using ALSA built-ins */ 376 /* Allocate a DMA buffer using ALSA built-ins */
387 return 377 return
388 snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); 378 snd_pcm_lib_malloc_pages(substream,
379 params_buffer_bytes(hw_params));
389} 380}
390 381
391static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream 382static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
@@ -402,15 +393,11 @@ static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
402static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream 393static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
403 *substream, int cmd) 394 *substream, int cmd)
404{ 395{
405 struct snd_card_aica *dreamcastcard;
406 switch (cmd) { 396 switch (cmd) {
407 case SNDRV_PCM_TRIGGER_START: 397 case SNDRV_PCM_TRIGGER_START:
408 spu_begin_dma(substream); 398 spu_begin_dma(substream);
409 break; 399 break;
410 case SNDRV_PCM_TRIGGER_STOP: 400 case SNDRV_PCM_TRIGGER_STOP:
411 dreamcastcard = substream->pcm->private_data;
412 if (dreamcastcard->timer.data)
413 del_timer(&dreamcastcard->timer);
414 aica_chn_halt(); 401 aica_chn_halt();
415 break; 402 break;
416 default: 403 default:
@@ -444,7 +431,8 @@ static int __init snd_aicapcmchip(struct snd_card_aica
444 int err; 431 int err;
445 /* AICA has no capture ability */ 432 /* AICA has no capture ability */
446 err = 433 err =
447 snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0, &pcm); 434 snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
435 &pcm);
448 if (unlikely(err < 0)) 436 if (unlikely(err < 0))
449 return err; 437 return err;
450 pcm->private_data = dreamcastcard; 438 pcm->private_data = dreamcastcard;
@@ -524,9 +512,7 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
524 dreamcastcard->channel->vol = ucontrol->value.integer.value[0]; 512 dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
525 dreamcastcard->master_volume = ucontrol->value.integer.value[0]; 513 dreamcastcard->master_volume = ucontrol->value.integer.value[0];
526 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET, 514 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
527 dreamcastcard->channel, 515 dreamcastcard->channel, sizeof(struct aica_channel));
528 sizeof(struct aica_channel));
529
530 return 1; 516 return 1;
531} 517}
532 518
@@ -610,6 +596,8 @@ static int __init snd_aica_probe(struct platform_device *devptr)
610 strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); 596 strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
611 strcpy(dreamcastcard->card->longname, 597 strcpy(dreamcastcard->card->longname,
612 "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast"); 598 "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
599 /* Prepare to use the queue */
600 INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
613 /* Load the PCM 'chip' */ 601 /* Load the PCM 'chip' */
614 err = snd_aicapcmchip(dreamcastcard, 0); 602 err = snd_aicapcmchip(dreamcastcard, 0);
615 if (unlikely(err < 0)) 603 if (unlikely(err < 0))
@@ -663,8 +651,10 @@ static int __init aica_init(void)
663 651
664static void __exit aica_exit(void) 652static void __exit aica_exit(void)
665{ 653{
666 /* Destroy the aica kernel thread */ 654 /* Destroy the aica kernel thread *
667 destroy_workqueue(aica_queue); 655 * being extra cautious to check if it exists*/
656 if (likely(aica_queue))
657 destroy_workqueue(aica_queue);
668 platform_device_unregister(pd); 658 platform_device_unregister(pd);
669 platform_driver_unregister(&snd_aica_driver); 659 platform_driver_unregister(&snd_aica_driver);
670 /* Kill any sound still playing and reset ARM7 to safe state */ 660 /* Kill any sound still playing and reset ARM7 to safe state */