diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/compress_offload.c | 41 | ||||
-rw-r--r-- | sound/core/init.c | 4 | ||||
-rw-r--r-- | sound/core/memalloc.c | 61 | ||||
-rw-r--r-- | sound/core/pcm_dmaengine.c | 22 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 10 |
5 files changed, 120 insertions, 18 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index bea523a5d852..3eb47d0006a7 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->wait); data->stream.runtime = runtime; f->private_data = (void *)data; mutex_lock(&compr->lock); @@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream) if (!retval) { stream->runtime->state = SNDRV_PCM_STATE_SETUP; wake_up(&stream->runtime->sleep); + snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; } return retval; } +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) +{ + /* + * We are called with lock held. So drop the lock while we wait for + * drain complete notfication from the driver + * + * It is expected that driver will notify the drain completion and then + * stream will be moved to SETUP state, even if draining resulted in an + * error. We can trigger next track after this. + */ + stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + mutex_unlock(&stream->device->lock); + + wait_event(stream->runtime->wait, stream->runtime->drain_wake); + + wake_up(&stream->runtime->sleep); + mutex_lock(&stream->device->lock); + + return 0; +} + static int snd_compr_drain(struct snd_compr_stream *stream) { int retval; @@ -695,11 +718,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream) if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; + + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); - if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + if (retval) { + pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); + return retval; } + + retval = snd_compress_wait_for_drain(stream); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; return retval; } @@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) if (stream->next_track == false) return -EPERM; + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + if (retval) { + pr_err("Partial drain returned failure\n"); + wake_up(&stream->runtime->sleep); + return retval; + } stream->next_track = false; - return retval; + return snd_compress_wait_for_drain(stream); } static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) diff --git a/sound/core/init.c b/sound/core/init.c index 6b9087115da2..1351f22f651c 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -66,7 +66,7 @@ static int module_slot_match(struct module *module, int idx) #ifdef MODULE const char *s1, *s2; - if (!module || !module->name || !slots[idx]) + if (!module || !*module->name || !slots[idx]) return 0; s1 = module->name; @@ -597,7 +597,7 @@ static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, /* last resort... */ snd_printk(KERN_ERR "unable to set card id (%s)\n", id); if (card->proc_root->name) - strcpy(card->id, card->proc_root->name); + strlcpy(card->id, card->proc_root->name, sizeof(card->id)); } /** diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index bdf826f4fe0c..9d93f02c6285 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -30,6 +30,7 @@ #include <linux/seq_file.h> #include <asm/uaccess.h> #include <linux/dma-mapping.h> +#include <linux/genalloc.h> #include <linux/moduleparam.h> #include <linux/mutex.h> #include <sound/memalloc.h> @@ -157,6 +158,51 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, dec_snd_pages(pg); dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } + +#ifdef CONFIG_GENERIC_ALLOCATOR +/** + * snd_malloc_dev_iram - allocate memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + * @size: number of bytes to allocate from the iram + * + * This function requires iram phandle provided via of_node + */ +static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) +{ + struct device *dev = dmab->dev.dev; + struct gen_pool *pool = NULL; + + dmab->area = NULL; + dmab->addr = 0; + + if (dev->of_node) + pool = of_get_named_gen_pool(dev->of_node, "iram", 0); + + if (!pool) + return; + + /* Assign the pool into private_data field */ + dmab->private_data = pool; + + dmab->area = (void *)gen_pool_alloc(pool, size); + if (!dmab->area) + return; + + dmab->addr = gen_pool_virt_to_phys(pool, (unsigned long)dmab->area); +} + +/** + * snd_free_dev_iram - free allocated specific memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + */ +static void snd_free_dev_iram(struct snd_dma_buffer *dmab) +{ + struct gen_pool *pool = dmab->private_data; + + if (pool && dmab->area) + gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes); +} +#endif /* CONFIG_GENERIC_ALLOCATOR */ #endif /* CONFIG_HAS_DMA */ /* @@ -197,6 +243,16 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_malloc_dev_iram(dmab, size); + if (dmab->area) + break; + /* Internal memory might have limited size and no enough space, + * so if we fail to malloc, try to fetch memory traditionally. + */ + dmab->dev.type = SNDRV_DMA_TYPE_DEV; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; @@ -269,6 +325,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) snd_free_pages(dmab->area, dmab->bytes); break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_free_dev_iram(dmab); + break; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index aa924d9b7986..94d08733cb38 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -63,23 +63,19 @@ int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, struct dma_slave_config *slave_config) { enum dma_slave_buswidth buswidth; + int bits; - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: + bits = snd_pcm_format_physical_width(params_format(params)); + if (bits < 8 || bits > 64) + return -EINVAL; + else if (bits == 8) buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; - break; - case SNDRV_PCM_FORMAT_S16_LE: + else if (bits == 16) buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; - break; - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S32_LE: + else if (bits <= 32) buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; - break; - default: - return -EINVAL; - } + else + buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { slave_config->direction = DMA_MEM_TO_DEV; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a68d4c6d702c..01a5e05ede95 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2428,6 +2428,7 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream) case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; @@ -2460,6 +2461,7 @@ static int snd_pcm_delay(struct snd_pcm_substream *substream, case SNDRV_PCM_STATE_DRAINING: if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) goto __badfd; + /* Fall through */ case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; @@ -3199,6 +3201,14 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; +#ifdef CONFIG_GENERIC_ALLOCATOR + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return remap_pfn_range(area, area->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + area->vm_end - area->vm_start, area->vm_page_prot); + } +#endif /* CONFIG_GENERIC_ALLOCATOR */ #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) |