aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-09-04 06:19:36 -0400
committerTakashi Iwai <tiwai@suse.de>2009-09-07 03:01:10 -0400
commita68c4d11336610dc348620766119db09675707c2 (patch)
tree5d12014b8547f6dd13281cd1b6be2bd3d38adfa9
parentb5d10781731ece07bb2049e7743907194a5cc3f1 (diff)
ALSA: dummy - Fake buffer allocations
Instead of allocating the real buffers, use a fake buffer and ignore read/write in the dummy driver so that we can save the resources. For mmap, a single page (unique to the direction, though) is reused to all buffers. When the app requires to read/write the real buffers, pass fake_buffer=0 module option at loading time. This will get back to the old behavior. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt7
-rw-r--r--sound/drivers/dummy.c106
2 files changed, 105 insertions, 8 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index bb488b575de..ea16e7d184a 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -518,6 +518,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
518 pcm_substreams - Number of PCM substreams assigned to each PCM 518 pcm_substreams - Number of PCM substreams assigned to each PCM
519 (default = 8, up to 16) 519 (default = 8, up to 16)
520 hrtimer - Use hrtimer (=1, default) or system timer (=0) 520 hrtimer - Use hrtimer (=1, default) or system timer (=0)
521 fake_buffer - Fake buffer allocations (default = 1)
521 522
522 When multiple PCM devices are created, snd-dummy gives different 523 When multiple PCM devices are created, snd-dummy gives different
523 behavior to each PCM device: 524 behavior to each PCM device:
@@ -526,6 +527,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
526 2 = interleaved without mmap 527 2 = interleaved without mmap
527 3 = non-interleaved without mmap 528 3 = non-interleaved without mmap
528 529
530 As default, snd-dummy drivers doesn't allocate the real buffers
531 but either ignores read/write or mmap a single dummy page to all
532 buffer pages, in order to save the resouces. If your apps need
533 the read/ written buffer data to be consistent, pass fake_buffer=0
534 option.
535
529 The power-management is supported. 536 The power-management is supported.
530 537
531 Module snd-echo3g 538 Module snd-echo3g
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index e8e29bfb85e..2ee6c8ebe25 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -153,6 +153,7 @@ static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
153#ifdef CONFIG_HIGH_RES_TIMERS 153#ifdef CONFIG_HIGH_RES_TIMERS
154static int hrtimer = 1; 154static int hrtimer = 1;
155#endif 155#endif
156static int fake_buffer = 1;
156 157
157module_param_array(index, int, NULL, 0444); 158module_param_array(index, int, NULL, 0444);
158MODULE_PARM_DESC(index, "Index value for dummy soundcard."); 159MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
@@ -166,6 +167,8 @@ module_param_array(pcm_substreams, int, NULL, 0444);
166MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); 167MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
167//module_param_array(midi_devs, int, NULL, 0444); 168//module_param_array(midi_devs, int, NULL, 0444);
168//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); 169//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
170module_param(fake_buffer, bool, 0444);
171MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
169#ifdef CONFIG_HIGH_RES_TIMERS 172#ifdef CONFIG_HIGH_RES_TIMERS
170module_param(hrtimer, bool, 0644); 173module_param(hrtimer, bool, 0644);
171MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source."); 174MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source.");
@@ -481,11 +484,8 @@ static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
481 484
482static int dummy_pcm_prepare(struct snd_pcm_substream *substream) 485static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
483{ 486{
484 struct snd_pcm_runtime *runtime = substream->runtime;
485 struct snd_dummy *dummy = snd_pcm_substream_chip(substream); 487 struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
486 488
487 snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
488 bytes_to_samples(runtime, runtime->dma_bytes));
489 return dummy->timer_ops->prepare(substream); 489 return dummy->timer_ops->prepare(substream);
490} 490}
491 491
@@ -518,12 +518,19 @@ static struct snd_pcm_hardware dummy_pcm_hardware = {
518static int dummy_pcm_hw_params(struct snd_pcm_substream *substream, 518static int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
519 struct snd_pcm_hw_params *hw_params) 519 struct snd_pcm_hw_params *hw_params)
520{ 520{
521 if (fake_buffer) {
522 /* runtime->dma_bytes has to be set manually to allow mmap */
523 substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
524 return 0;
525 }
521 return snd_pcm_lib_malloc_pages(substream, 526 return snd_pcm_lib_malloc_pages(substream,
522 params_buffer_bytes(hw_params)); 527 params_buffer_bytes(hw_params));
523} 528}
524 529
525static int dummy_pcm_hw_free(struct snd_pcm_substream *substream) 530static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
526{ 531{
532 if (fake_buffer)
533 return 0;
527 return snd_pcm_lib_free_pages(substream); 534 return snd_pcm_lib_free_pages(substream);
528} 535}
529 536
@@ -570,6 +577,60 @@ static int dummy_pcm_close(struct snd_pcm_substream *substream)
570 return 0; 577 return 0;
571} 578}
572 579
580/*
581 * dummy buffer handling
582 */
583
584static void *dummy_page[2];
585
586static void free_fake_buffer(void)
587{
588 if (fake_buffer) {
589 int i;
590 for (i = 0; i < 2; i++)
591 if (dummy_page[i]) {
592 free_page((unsigned long)dummy_page[i]);
593 dummy_page[i] = NULL;
594 }
595 }
596}
597
598static int alloc_fake_buffer(void)
599{
600 int i;
601
602 if (!fake_buffer)
603 return 0;
604 for (i = 0; i < 2; i++) {
605 dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL);
606 if (!dummy_page[i]) {
607 free_fake_buffer();
608 return -ENOMEM;
609 }
610 }
611 return 0;
612}
613
614static int dummy_pcm_copy(struct snd_pcm_substream *substream,
615 int channel, snd_pcm_uframes_t pos,
616 void __user *dst, snd_pcm_uframes_t count)
617{
618 return 0; /* do nothing */
619}
620
621static int dummy_pcm_silence(struct snd_pcm_substream *substream,
622 int channel, snd_pcm_uframes_t pos,
623 snd_pcm_uframes_t count)
624{
625 return 0; /* do nothing */
626}
627
628static struct page *dummy_pcm_page(struct snd_pcm_substream *substream,
629 unsigned long offset)
630{
631 return virt_to_page(dummy_page[substream->stream]); /* the same page */
632}
633
573static struct snd_pcm_ops dummy_pcm_ops = { 634static struct snd_pcm_ops dummy_pcm_ops = {
574 .open = dummy_pcm_open, 635 .open = dummy_pcm_open,
575 .close = dummy_pcm_close, 636 .close = dummy_pcm_close,
@@ -581,10 +642,25 @@ static struct snd_pcm_ops dummy_pcm_ops = {
581 .pointer = dummy_pcm_pointer, 642 .pointer = dummy_pcm_pointer,
582}; 643};
583 644
645static struct snd_pcm_ops dummy_pcm_ops_no_buf = {
646 .open = dummy_pcm_open,
647 .close = dummy_pcm_close,
648 .ioctl = snd_pcm_lib_ioctl,
649 .hw_params = dummy_pcm_hw_params,
650 .hw_free = dummy_pcm_hw_free,
651 .prepare = dummy_pcm_prepare,
652 .trigger = dummy_pcm_trigger,
653 .pointer = dummy_pcm_pointer,
654 .copy = dummy_pcm_copy,
655 .silence = dummy_pcm_silence,
656 .page = dummy_pcm_page,
657};
658
584static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, 659static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
585 int substreams) 660 int substreams)
586{ 661{
587 struct snd_pcm *pcm; 662 struct snd_pcm *pcm;
663 struct snd_pcm_ops *ops;
588 int err; 664 int err;
589 665
590 err = snd_pcm_new(dummy->card, "Dummy PCM", device, 666 err = snd_pcm_new(dummy->card, "Dummy PCM", device,
@@ -592,14 +668,21 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
592 if (err < 0) 668 if (err < 0)
593 return err; 669 return err;
594 dummy->pcm = pcm; 670 dummy->pcm = pcm;
595 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &dummy_pcm_ops); 671 if (fake_buffer)
596 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &dummy_pcm_ops); 672 ops = &dummy_pcm_ops_no_buf;
673 else
674 ops = &dummy_pcm_ops;
675 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops);
676 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
597 pcm->private_data = dummy; 677 pcm->private_data = dummy;
598 pcm->info_flags = 0; 678 pcm->info_flags = 0;
599 strcpy(pcm->name, "Dummy PCM"); 679 strcpy(pcm->name, "Dummy PCM");
600 snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, 680 if (!fake_buffer) {
601 snd_dma_continuous_data(GFP_KERNEL), 681 snd_pcm_lib_preallocate_pages_for_all(pcm,
602 0, 64*1024); 682 SNDRV_DMA_TYPE_CONTINUOUS,
683 snd_dma_continuous_data(GFP_KERNEL),
684 0, 64*1024);
685 }
603 return 0; 686 return 0;
604} 687}
605 688
@@ -822,6 +905,7 @@ static void snd_dummy_unregister_all(void)
822 for (i = 0; i < ARRAY_SIZE(devices); ++i) 905 for (i = 0; i < ARRAY_SIZE(devices); ++i)
823 platform_device_unregister(devices[i]); 906 platform_device_unregister(devices[i]);
824 platform_driver_unregister(&snd_dummy_driver); 907 platform_driver_unregister(&snd_dummy_driver);
908 free_fake_buffer();
825} 909}
826 910
827static int __init alsa_card_dummy_init(void) 911static int __init alsa_card_dummy_init(void)
@@ -832,6 +916,12 @@ static int __init alsa_card_dummy_init(void)
832 if (err < 0) 916 if (err < 0)
833 return err; 917 return err;
834 918
919 err = alloc_fake_buffer();
920 if (err < 0) {
921 platform_driver_unregister(&snd_dummy_driver);
922 return err;
923 }
924
835 cards = 0; 925 cards = 0;
836 for (i = 0; i < SNDRV_CARDS; i++) { 926 for (i = 0; i < SNDRV_CARDS; i++) {
837 struct platform_device *device; 927 struct platform_device *device;