diff options
-rw-r--r-- | sound/sh/aica.c | 82 | ||||
-rw-r--r-- | sound/sh/aica.h | 1 |
2 files changed, 37 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); | |||
64 | MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); | 64 | MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); |
65 | 65 | ||
66 | /* Use workqueue */ | 66 | /* Use workqueue */ |
67 | |||
68 | static struct spu_work_holder { | ||
69 | struct work_struct spu_dma_work; | ||
70 | void *sspointer; | ||
71 | } spu_working; | ||
72 | |||
73 | static struct workqueue_struct *aica_queue; | 67 | static 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, | |||
244 | static void startup_aica(struct snd_card_aica *dreamcastcard) | 238 | static 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 | ||
252 | static void run_spu_dma(struct work_struct *work) | 245 | static 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 | ||
313 | static void spu_begin_dma(struct snd_pcm_substream *substream) | 304 | static 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 | ||
391 | static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream | 382 | static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream |
@@ -402,15 +393,11 @@ static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream | |||
402 | static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream | 393 | static 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 | ||
664 | static void __exit aica_exit(void) | 652 | static 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 */ |
diff --git a/sound/sh/aica.h b/sound/sh/aica.h index 0603b5b0376b..8c11e3d10a50 100644 --- a/sound/sh/aica.h +++ b/sound/sh/aica.h | |||
@@ -69,6 +69,7 @@ struct aica_channel { | |||
69 | }; | 69 | }; |
70 | 70 | ||
71 | struct snd_card_aica { | 71 | struct snd_card_aica { |
72 | struct work_struct spu_dma_work; | ||
72 | struct snd_card *card; | 73 | struct snd_card *card; |
73 | struct aica_channel *channel; | 74 | struct aica_channel *channel; |
74 | struct snd_pcm_substream *substream; | 75 | struct snd_pcm_substream *substream; |