diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/compress_offload.c | 54 | ||||
-rw-r--r-- | sound/core/init.c | 4 | ||||
-rw-r--r-- | sound/core/memalloc.c | 61 | ||||
-rw-r--r-- | sound/core/pcm.c | 4 | ||||
-rw-r--r-- | sound/core/pcm_dmaengine.c | 22 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 10 |
6 files changed, 134 insertions, 21 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index bea523a5d852..d9af6387f37c 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -680,14 +680,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream) return -EPERM; retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); 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) +{ + int ret; + + /* + * 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); + + /* we wait for drain to complete here, drain can return when + * interruption occurred, wait returned error or success. + * For the first two cases we don't do anything different here and + * return after waking up + */ + + ret = wait_event_interruptible(stream->runtime->sleep, + (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)); + if (ret == -ERESTARTSYS) + pr_debug("wait aborted by a signal"); + else if (ret) + pr_debug("wait for drain failed with %d\n", ret); + + + wake_up(&stream->runtime->sleep); + mutex_lock(&stream->device->lock); + + return ret; +} + static int snd_compr_drain(struct snd_compr_stream *stream) { int retval; @@ -695,12 +729,15 @@ 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; + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); - if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + if (retval) { + pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); + return retval; } - return retval; + + return snd_compress_wait_for_drain(stream); } static int snd_compr_next_track(struct snd_compr_stream *stream) @@ -736,9 +773,14 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) return -EPERM; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + if (retval) { + pr_debug("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.c b/sound/core/pcm.c index 17f45e8aa89c..e1e9e0c999fe 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -49,6 +49,8 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { + if (pcm->internal) + continue; if (pcm->card == card && pcm->device == device) return pcm; } @@ -60,6 +62,8 @@ static int snd_pcm_next(struct snd_card *card, int device) struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { + if (pcm->internal) + continue; if (pcm->card == card && pcm->device > device) return pcm->device; else if (pcm->card->number > card->number) 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) |