diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-09-04 06:19:36 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-09-07 03:01:10 -0400 |
commit | a68c4d11336610dc348620766119db09675707c2 (patch) | |
tree | 5d12014b8547f6dd13281cd1b6be2bd3d38adfa9 | |
parent | b5d10781731ece07bb2049e7743907194a5cc3f1 (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.txt | 7 | ||||
-rw-r--r-- | sound/drivers/dummy.c | 106 |
2 files changed, 105 insertions, 8 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index bb488b575def..ea16e7d184a4 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 e8e29bfb85ec..2ee6c8ebe25a 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 |
154 | static int hrtimer = 1; | 154 | static int hrtimer = 1; |
155 | #endif | 155 | #endif |
156 | static int fake_buffer = 1; | ||
156 | 157 | ||
157 | module_param_array(index, int, NULL, 0444); | 158 | module_param_array(index, int, NULL, 0444); |
158 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); | 159 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); |
@@ -166,6 +167,8 @@ module_param_array(pcm_substreams, int, NULL, 0444); | |||
166 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); | 167 | MODULE_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."); |
170 | module_param(fake_buffer, bool, 0444); | ||
171 | MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations."); | ||
169 | #ifdef CONFIG_HIGH_RES_TIMERS | 172 | #ifdef CONFIG_HIGH_RES_TIMERS |
170 | module_param(hrtimer, bool, 0644); | 173 | module_param(hrtimer, bool, 0644); |
171 | MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source."); | 174 | MODULE_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 | ||
482 | static int dummy_pcm_prepare(struct snd_pcm_substream *substream) | 485 | static 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 = { | |||
518 | static int dummy_pcm_hw_params(struct snd_pcm_substream *substream, | 518 | static 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 | ||
525 | static int dummy_pcm_hw_free(struct snd_pcm_substream *substream) | 530 | static 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 | |||
584 | static void *dummy_page[2]; | ||
585 | |||
586 | static 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 | |||
598 | static 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 | |||
614 | static 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 | |||
621 | static 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 | |||
628 | static 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 | |||
573 | static struct snd_pcm_ops dummy_pcm_ops = { | 634 | static 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 | ||
645 | static 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 | |||
584 | static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | 659 | static 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 | ||
827 | static int __init alsa_card_dummy_init(void) | 911 | static 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; |