diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/Kconfig | 28 | ||||
-rw-r--r-- | sound/core/memalloc.c | 25 | ||||
-rw-r--r-- | sound/core/pcm_dmaengine.c | 83 | ||||
-rw-r--r-- | sound/core/pcm_memory.c | 5 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 17 | ||||
-rw-r--r-- | sound/core/timer.c | 10 |
6 files changed, 147 insertions, 21 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 4ee79ad6ae22..4044c42d8595 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -72,11 +72,11 @@ config SND_PCM_OSS config SND_PCM_OSS_PLUGINS bool "OSS PCM (digital audio) API - Include plugin system" depends on SND_PCM_OSS - default y + default y help - If you disable this option, the ALSA's OSS PCM API will not - support conversion of channels, formats and rates. It will - behave like most of new OSS/Free drivers in 2.4/2.6 kernels. + If you disable this option, the ALSA's OSS PCM API will not + support conversion of channels, formats and rates. It will + behave like most of new OSS/Free drivers in 2.4/2.6 kernels. config SND_PCM_TIMER bool "PCM timer interface" if EXPERT @@ -128,13 +128,13 @@ config SND_SUPPORT_OLD_API or older). config SND_PROC_FS - bool "Sound Proc FS Support" if EXPERT - depends on PROC_FS - default y - help - Say 'N' to disable Sound proc FS, which may reduce code size about - 9KB on x86_64 platform. - If unsure say Y. + bool "Sound Proc FS Support" if EXPERT + depends on PROC_FS + default y + help + Say 'N' to disable Sound proc FS, which may reduce code size about + 9KB on x86_64 platform. + If unsure say Y. config SND_VERBOSE_PROCFS bool "Verbose procfs contents" @@ -142,8 +142,8 @@ config SND_VERBOSE_PROCFS default y help Say Y here to include code for verbose procfs contents (provides - useful information to developers when a problem occurs). On the - other side, it makes the ALSA subsystem larger. + useful information to developers when a problem occurs). On the + other side, it makes the ALSA subsystem larger. config SND_VERBOSE_PRINTK bool "Verbose printk" @@ -164,7 +164,7 @@ config SND_DEBUG_VERBOSE depends on SND_DEBUG help Say Y here to enable extra-verbose debugging messages. - + Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages. So, say Y only if you are ready to be annoyed. diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 6850d13aa98c..a83553fbedf0 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/dma-mapping.h> #include <linux/genalloc.h> +#include <linux/vmalloc.h> #ifdef CONFIG_X86 #include <asm/set_memory.h> #endif @@ -99,6 +100,14 @@ static void snd_free_dev_iram(struct snd_dma_buffer *dmab) * */ +static inline gfp_t snd_mem_get_gfp_flags(const struct device *dev, + gfp_t default_gfp) +{ + if (!dev) + return default_gfp; + else + return (__force gfp_t)(unsigned long)dev; +} /** * snd_dma_alloc_pages - allocate the buffer area according to the given type @@ -116,20 +125,25 @@ static void snd_free_dev_iram(struct snd_dma_buffer *dmab) int snd_dma_alloc_pages(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) { + gfp_t gfp; + if (WARN_ON(!size)) return -ENXIO; if (WARN_ON(!dmab)) return -ENXIO; - if (WARN_ON(!device)) - return -EINVAL; dmab->dev.type = type; dmab->dev.dev = device; dmab->bytes = 0; switch (type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = alloc_pages_exact(size, - (__force gfp_t)(unsigned long)device); + gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL); + dmab->area = alloc_pages_exact(size, gfp); + dmab->addr = 0; + break; + case SNDRV_DMA_TYPE_VMALLOC: + gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL | __GFP_HIGHMEM); + dmab->area = __vmalloc(size, gfp, PAGE_KERNEL); dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA @@ -215,6 +229,9 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) case SNDRV_DMA_TYPE_CONTINUOUS: free_pages_exact(dmab->area, dmab->bytes); break; + case SNDRV_DMA_TYPE_VMALLOC: + vfree(dmab->area); + break; #ifdef CONFIG_HAS_DMA #ifdef CONFIG_GENERIC_ALLOCATOR case SNDRV_DMA_TYPE_DEV_IRAM: diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 89a05926ac73..5749a8a49784 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -369,4 +369,87 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); +/** + * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params + * @substream: PCM substream + * @dma_data: DAI DMA data + * @hw: PCM hw params + * @chan: DMA channel to use for data transfers + * + * Returns 0 on success, a negative error code otherwise. + * + * This function will query DMA capability, then refine the pcm hardware + * parameters. + */ +int snd_dmaengine_pcm_refine_runtime_hwparams( + struct snd_pcm_substream *substream, + struct snd_dmaengine_dai_dma_data *dma_data, + struct snd_pcm_hardware *hw, + struct dma_chan *chan) +{ + struct dma_slave_caps dma_caps; + u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + snd_pcm_format_t i; + int ret = 0; + + if (!hw || !chan || !dma_data) + return -EINVAL; + + ret = dma_get_slave_caps(chan, &dma_caps); + if (ret == 0) { + if (dma_caps.cmd_pause && dma_caps.cmd_resume) + hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; + if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) + hw->info |= SNDRV_PCM_INFO_BATCH; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + addr_widths = dma_caps.dst_addr_widths; + else + addr_widths = dma_caps.src_addr_widths; + } + + /* + * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep + * hw.formats set to 0, meaning no restrictions are in place. + * In this case it's the responsibility of the DAI driver to + * provide the supported format information. + */ + if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)) + /* + * Prepare formats mask for valid/allowed sample types. If the + * dma does not have support for the given physical word size, + * it needs to be masked out so user space can not use the + * format which produces corrupted audio. + * In case the dma driver does not implement the slave_caps the + * default assumption is that it supports 1, 2 and 4 bytes + * widths. + */ + for (i = SNDRV_PCM_FORMAT_FIRST; i <= SNDRV_PCM_FORMAT_LAST; i++) { + int bits = snd_pcm_format_physical_width(i); + + /* + * Enable only samples with DMA supported physical + * widths + */ + switch (bits) { + case 8: + case 16: + case 24: + case 32: + case 64: + if (addr_widths & (1 << (bits / 8))) + hw->formats |= pcm_format_to_bits(i); + break; + default: + /* Unsupported types */ + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams); + MODULE_LICENSE("GPL"); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 7600dcdf5fd4..17ee361ce791 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -203,7 +203,8 @@ static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream, if (substream->dma_buffer.bytes > 0) substream->buffer_bytes_max = substream->dma_buffer.bytes; substream->dma_max = max; - preallocate_info_init(substream); + if (max > 0) + preallocate_info_init(substream); } @@ -221,6 +222,8 @@ void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, int type, struct device *data, size_t size, size_t max) { + if (snd_BUG_ON(substream->dma_buffer.dev.type)) + return; substream->dma_buffer.dev.type = type; substream->dma_buffer.dev.dev = data; snd_pcm_lib_preallocate_pages1(substream, size, max); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 91c6ad58729f..998c63192ae4 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -13,6 +13,7 @@ #include <linux/pm_qos.h> #include <linux/io.h> #include <linux/dma-mapping.h> +#include <linux/vmalloc.h> #include <sound/core.h> #include <sound/control.h> #include <sound/info.h> @@ -222,7 +223,8 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream) return false; if (substream->ops->mmap || - substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV) + (substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV && + substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_DEV_UC)) return true; return dma_can_mmap(substream->dma_buffer.dev.dev); @@ -3334,7 +3336,18 @@ static inline struct page * snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs) { void *vaddr = substream->runtime->dma_area + ofs; - return virt_to_page(vaddr); + + switch (substream->dma_buffer.dev.type) { +#ifdef CONFIG_SND_DMA_SGBUF + case SNDRV_DMA_TYPE_DEV_SG: + case SNDRV_DMA_TYPE_DEV_UC_SG: + return snd_pcm_sgbuf_ops_page(substream, ofs); +#endif /* CONFIG_SND_DMA_SGBUF */ + case SNDRV_DMA_TYPE_VMALLOC: + return vmalloc_to_page(vaddr); + default: + return virt_to_page(vaddr); + } } /* diff --git a/sound/core/timer.c b/sound/core/timer.c index 59ae21b0bb93..013f0e69ff0f 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -74,6 +74,9 @@ static LIST_HEAD(snd_timer_slave_list); /* lock for slave active lists */ static DEFINE_SPINLOCK(slave_active_lock); +#define MAX_SLAVE_INSTANCES 1000 +static int num_slaves; + static DEFINE_MUTEX(register_mutex); static int snd_timer_free(struct snd_timer *timer); @@ -252,6 +255,10 @@ int snd_timer_open(struct snd_timer_instance **ti, err = -EINVAL; goto unlock; } + if (num_slaves >= MAX_SLAVE_INSTANCES) { + err = -EBUSY; + goto unlock; + } timeri = snd_timer_instance_new(owner, NULL); if (!timeri) { err = -ENOMEM; @@ -261,6 +268,7 @@ int snd_timer_open(struct snd_timer_instance **ti, timeri->slave_id = tid->device; timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; list_add_tail(&timeri->open_list, &snd_timer_slave_list); + num_slaves++; err = snd_timer_check_slave(timeri); if (err < 0) { snd_timer_close_locked(timeri, &card_dev_to_put); @@ -356,6 +364,8 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri, } list_del(&timeri->open_list); + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + num_slaves--; /* force to stop the timer */ snd_timer_stop(timeri); |