diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/compress_offload.c | 69 | ||||
-rw-r--r-- | sound/core/control_led.c | 2 | ||||
-rw-r--r-- | sound/core/init.c | 105 | ||||
-rw-r--r-- | sound/core/isadma.c | 38 | ||||
-rw-r--r-- | sound/core/memalloc.c | 177 | ||||
-rw-r--r-- | sound/core/memalloc_local.h | 2 | ||||
-rw-r--r-- | sound/core/pcm_memory.c | 67 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 18 | ||||
-rw-r--r-- | sound/core/seq/seq_dummy.c | 8 | ||||
-rw-r--r-- | sound/core/sgbuf.c | 30 |
10 files changed, 363 insertions, 153 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 21ce4c056a92..de514ec8c83d 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -47,8 +47,6 @@ * driver should be able to register multiple nodes */ -static DEFINE_MUTEX(device_mutex); - struct snd_compr_file { unsigned long caps; struct snd_compr_stream stream; @@ -1179,6 +1177,7 @@ int snd_compress_new(struct snd_card *card, int device, compr->card = card; compr->device = device; compr->direction = dirn; + mutex_init(&compr->lock); snd_compress_set_id(compr, id); @@ -1193,72 +1192,6 @@ int snd_compress_new(struct snd_card *card, int device, } EXPORT_SYMBOL_GPL(snd_compress_new); -static int snd_compress_add_device(struct snd_compr *device) -{ - int ret; - - if (!device->card) - return -EINVAL; - - /* register the card */ - ret = snd_card_register(device->card); - if (ret) - goto out; - return 0; - -out: - pr_err("failed with %d\n", ret); - return ret; - -} - -static int snd_compress_remove_device(struct snd_compr *device) -{ - return snd_card_free(device->card); -} - -/** - * snd_compress_register - register compressed device - * - * @device: compressed device to register - */ -int snd_compress_register(struct snd_compr *device) -{ - int retval; - - if (device->name == NULL || device->ops == NULL) - return -EINVAL; - - pr_debug("Registering compressed device %s\n", device->name); - if (snd_BUG_ON(!device->ops->open)) - return -EINVAL; - if (snd_BUG_ON(!device->ops->free)) - return -EINVAL; - if (snd_BUG_ON(!device->ops->set_params)) - return -EINVAL; - if (snd_BUG_ON(!device->ops->trigger)) - return -EINVAL; - - mutex_init(&device->lock); - - /* register a compressed card */ - mutex_lock(&device_mutex); - retval = snd_compress_add_device(device); - mutex_unlock(&device_mutex); - return retval; -} -EXPORT_SYMBOL_GPL(snd_compress_register); - -int snd_compress_deregister(struct snd_compr *device) -{ - pr_debug("Removing compressed device %s\n", device->name); - mutex_lock(&device_mutex); - snd_compress_remove_device(device); - mutex_unlock(&device_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_compress_deregister); - MODULE_DESCRIPTION("ALSA Compressed offload framework"); MODULE_AUTHOR("Vinod Koul <vinod.koul@linux.intel.com>"); MODULE_LICENSE("GPL v2"); diff --git a/sound/core/control_led.c b/sound/core/control_led.c index 764058cc345d..a95332b2b90b 100644 --- a/sound/core/control_led.c +++ b/sound/core/control_led.c @@ -564,7 +564,7 @@ static ssize_t set_led_id(struct snd_ctl_led_card *led_card, const char *buf, si else { for (; *s >= ' '; s++); *s = '\0'; - strlcpy(id.name, buf2, sizeof(id.name)); + strscpy(id.name, buf2, sizeof(id.name)); } break; } diff --git a/sound/core/init.c b/sound/core/init.c index 1490568efdb0..ac335f5906c6 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -134,6 +134,9 @@ void snd_device_initialize(struct device *dev, struct snd_card *card) } EXPORT_SYMBOL_GPL(snd_device_initialize); +static int snd_card_init(struct snd_card *card, struct device *parent, + int idx, const char *xid, struct module *module, + size_t extra_size); static int snd_card_do_free(struct snd_card *card); static const struct attribute_group card_dev_attr_group; @@ -163,9 +166,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid, { struct snd_card *card; int err; -#ifdef CONFIG_SND_DEBUG - char name[8]; -#endif if (snd_BUG_ON(!card_ret)) return -EINVAL; @@ -176,6 +176,74 @@ int snd_card_new(struct device *parent, int idx, const char *xid, card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); if (!card) return -ENOMEM; + + err = snd_card_init(card, parent, idx, xid, module, extra_size); + if (err < 0) { + kfree(card); + return err; + } + + *card_ret = card; + return 0; +} +EXPORT_SYMBOL(snd_card_new); + +static void __snd_card_release(struct device *dev, void *data) +{ + snd_card_free(data); +} + +/** + * snd_devm_card_new - managed snd_card object creation + * @parent: the parent device object + * @idx: card index (address) [0 ... (SNDRV_CARDS-1)] + * @xid: card identification (ASCII string) + * @module: top level module for locking + * @extra_size: allocate this extra size after the main soundcard structure + * @card_ret: the pointer to store the created card instance + * + * This function works like snd_card_new() but manages the allocated resource + * via devres, i.e. you don't need to free explicitly. + * + * When a snd_card object is created with this function and registered via + * snd_card_register(), the very first devres action to call snd_card_free() + * is added automatically. In that way, the resource disconnection is assured + * at first, then released in the expected order. + */ +int snd_devm_card_new(struct device *parent, int idx, const char *xid, + struct module *module, size_t extra_size, + struct snd_card **card_ret) +{ + struct snd_card *card; + int err; + + *card_ret = NULL; + card = devres_alloc(__snd_card_release, sizeof(*card) + extra_size, + GFP_KERNEL); + if (!card) + return -ENOMEM; + card->managed = true; + err = snd_card_init(card, parent, idx, xid, module, extra_size); + if (err < 0) { + devres_free(card); + return err; + } + + devres_add(parent, card); + *card_ret = card; + return 0; +} +EXPORT_SYMBOL_GPL(snd_devm_card_new); + +static int snd_card_init(struct snd_card *card, struct device *parent, + int idx, const char *xid, struct module *module, + size_t extra_size) +{ + int err; +#ifdef CONFIG_SND_DEBUG + char name[8]; +#endif + if (extra_size > 0) card->private_data = (char *)card + sizeof(struct snd_card); if (xid) @@ -197,7 +265,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid, mutex_unlock(&snd_card_mutex); dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n", idx, snd_ecards_limit - 1, err); - kfree(card); return err; } set_bit(idx, snd_cards_lock); /* lock it */ @@ -256,8 +323,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid, sprintf(name, "card%d", idx); card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root); #endif - - *card_ret = card; return 0; __error_ctl: @@ -266,7 +331,6 @@ int snd_card_new(struct device *parent, int idx, const char *xid, put_device(&card->card_dev); return err; } -EXPORT_SYMBOL(snd_card_new); /** * snd_card_ref - Get the card object from the index @@ -481,6 +545,7 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); static int snd_card_do_free(struct snd_card *card) { + card->releasing = true; #if IS_ENABLED(CONFIG_SND_MIXER_OSS) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); @@ -498,7 +563,8 @@ static int snd_card_do_free(struct snd_card *card) #endif if (card->release_completion) complete(card->release_completion); - kfree(card); + if (!card->managed) + kfree(card); return 0; } @@ -539,6 +605,15 @@ int snd_card_free(struct snd_card *card) DECLARE_COMPLETION_ONSTACK(released); int ret; + /* The call of snd_card_free() is allowed from various code paths; + * a manual call from the driver and the call via devres_free, and + * we need to avoid double-free. Moreover, the release via devres + * may call snd_card_free() twice due to its nature, we need to have + * the check here at the beginning. + */ + if (card->releasing) + return 0; + card->release_completion = &released; ret = snd_card_free_when_closed(card); if (ret) @@ -745,6 +820,11 @@ int snd_card_add_dev_attr(struct snd_card *card, } EXPORT_SYMBOL_GPL(snd_card_add_dev_attr); +static void trigger_card_free(void *data) +{ + snd_card_free(data); +} + /** * snd_card_register - register the soundcard * @card: soundcard structure @@ -768,6 +848,15 @@ int snd_card_register(struct snd_card *card) if (err < 0) return err; card->registered = true; + } else { + if (card->managed) + devm_remove_action(card->dev, trigger_card_free, card); + } + + if (card->managed) { + err = devm_add_action(card->dev, trigger_card_free, card); + if (err < 0) + return err; } err = snd_device_register_all(card); diff --git a/sound/core/isadma.c b/sound/core/isadma.c index c3d789ef6975..1f45ede023b4 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -97,3 +97,41 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) return size - result; } EXPORT_SYMBOL(snd_dma_pointer); + +struct snd_dma_data { + int dma; +}; + +static void __snd_release_dma(struct device *dev, void *data) +{ + struct snd_dma_data *p = data; + + snd_dma_disable(p->dma); + free_dma(p->dma); +} + +/** + * snd_devm_request_dma - the managed version of request_dma() + * @dev: the device pointer + * @dma: the dma number + * @name: the name string of the requester + * + * Returns zero on success, or a negative error code. + * The requested DMA will be automatically released at unbinding via devres. + */ +int snd_devm_request_dma(struct device *dev, int dma, const char *name) +{ + struct snd_dma_data *p; + + if (request_dma(dma, name)) + return -EBUSY; + p = devres_alloc(__snd_release_dma, sizeof(*p), GFP_KERNEL); + if (!p) { + free_dma(dma); + return -ENOMEM; + } + p->dma = dma; + devres_add(dev, p); + return 0; +} +EXPORT_SYMBOL_GPL(snd_devm_request_dma); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 439a358ecfe9..c7c943c661e6 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -29,12 +29,12 @@ static inline gfp_t snd_mem_get_gfp_flags(const struct snd_dma_buffer *dmab, return (__force gfp_t)(unsigned long)dmab->dev.dev; } -static int __snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) +static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) { const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab); if (WARN_ON_ONCE(!ops || !ops->alloc)) - return -EINVAL; + return NULL; return ops->alloc(dmab, size); } @@ -54,8 +54,6 @@ static int __snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) int snd_dma_alloc_pages(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) { - int err; - if (WARN_ON(!size)) return -ENXIO; if (WARN_ON(!dmab)) @@ -65,12 +63,9 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->dev.type = type; dmab->dev.dev = device; dmab->bytes = 0; - dmab->area = NULL; dmab->addr = 0; dmab->private_data = NULL; - err = __snd_dma_alloc_pages(dmab, size); - if (err < 0) - return err; + dmab->area = __snd_dma_alloc_pages(dmab, size); if (!dmab->area) return -ENOMEM; dmab->bytes = size; @@ -127,6 +122,52 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) } EXPORT_SYMBOL(snd_dma_free_pages); +/* called by devres */ +static void __snd_release_pages(struct device *dev, void *res) +{ + snd_dma_free_pages(res); +} + +/** + * snd_devm_alloc_pages - allocate the buffer and manage with devres + * @dev: the device pointer + * @type: the DMA buffer type + * @size: the buffer size to allocate + * + * Allocate buffer pages depending on the given type and manage using devres. + * The pages will be released automatically at the device removal. + * + * Unlike snd_dma_alloc_pages(), this function requires the real device pointer, + * hence it can't work with SNDRV_DMA_TYPE_CONTINUOUS or + * SNDRV_DMA_TYPE_VMALLOC type. + * + * The function returns the snd_dma_buffer object at success, or NULL if failed. + */ +struct snd_dma_buffer * +snd_devm_alloc_pages(struct device *dev, int type, size_t size) +{ + struct snd_dma_buffer *dmab; + int err; + + if (WARN_ON(type == SNDRV_DMA_TYPE_CONTINUOUS || + type == SNDRV_DMA_TYPE_VMALLOC)) + return NULL; + + dmab = devres_alloc(__snd_release_pages, sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return NULL; + + err = snd_dma_alloc_pages(type, dev, size, dmab); + if (err < 0) { + devres_free(dmab); + return NULL; + } + + devres_add(dev, dmab); + return dmab; +} +EXPORT_SYMBOL_GPL(snd_devm_alloc_pages); + /** * snd_dma_buffer_mmap - perform mmap of the given DMA buffer * @dmab: buffer allocation information @@ -198,12 +239,14 @@ EXPORT_SYMBOL(snd_sgbuf_get_chunk_size); /* * Continuous pages allocator */ -static int snd_dma_continuous_alloc(struct snd_dma_buffer *dmab, size_t size) +static void *snd_dma_continuous_alloc(struct snd_dma_buffer *dmab, size_t size) { gfp_t gfp = snd_mem_get_gfp_flags(dmab, GFP_KERNEL); + void *p = alloc_pages_exact(size, gfp); - dmab->area = alloc_pages_exact(size, gfp); - return 0; + if (p) + dmab->addr = page_to_phys(virt_to_page(p)); + return p; } static void snd_dma_continuous_free(struct snd_dma_buffer *dmab) @@ -215,7 +258,7 @@ static int snd_dma_continuous_mmap(struct snd_dma_buffer *dmab, struct vm_area_struct *area) { return remap_pfn_range(area, area->vm_start, - page_to_pfn(virt_to_page(dmab->area)), + dmab->addr >> PAGE_SHIFT, area->vm_end - area->vm_start, area->vm_page_prot); } @@ -229,12 +272,11 @@ static const struct snd_malloc_ops snd_dma_continuous_ops = { /* * VMALLOC allocator */ -static int snd_dma_vmalloc_alloc(struct snd_dma_buffer *dmab, size_t size) +static void *snd_dma_vmalloc_alloc(struct snd_dma_buffer *dmab, size_t size) { gfp_t gfp = snd_mem_get_gfp_flags(dmab, GFP_KERNEL | __GFP_HIGHMEM); - dmab->area = __vmalloc(size, gfp); - return 0; + return __vmalloc(size, gfp); } static void snd_dma_vmalloc_free(struct snd_dma_buffer *dmab) @@ -248,11 +290,13 @@ static int snd_dma_vmalloc_mmap(struct snd_dma_buffer *dmab, return remap_vmalloc_range(area, dmab->area, 0); } +#define get_vmalloc_page_addr(dmab, offset) \ + page_to_phys(vmalloc_to_page((dmab)->area + (offset))) + static dma_addr_t snd_dma_vmalloc_get_addr(struct snd_dma_buffer *dmab, size_t offset) { - return page_to_phys(vmalloc_to_page(dmab->area + offset)) + - offset % PAGE_SIZE; + return get_vmalloc_page_addr(dmab, offset) + offset % PAGE_SIZE; } static struct page *snd_dma_vmalloc_get_page(struct snd_dma_buffer *dmab, @@ -265,11 +309,23 @@ static unsigned int snd_dma_vmalloc_get_chunk_size(struct snd_dma_buffer *dmab, unsigned int ofs, unsigned int size) { - ofs %= PAGE_SIZE; - size += ofs; - if (size > PAGE_SIZE) - size = PAGE_SIZE; - return size - ofs; + unsigned int start, end; + unsigned long addr; + + start = ALIGN_DOWN(ofs, PAGE_SIZE); + end = ofs + size - 1; /* the last byte address */ + /* check page continuity */ + addr = get_vmalloc_page_addr(dmab, start); + for (;;) { + start += PAGE_SIZE; + if (start > end) + break; + addr += PAGE_SIZE; + if (get_vmalloc_page_addr(dmab, start) != addr) + return start - ofs; + } + /* ok, all on continuous pages */ + return size; } static const struct snd_malloc_ops snd_dma_vmalloc_ops = { @@ -286,20 +342,20 @@ static const struct snd_malloc_ops snd_dma_vmalloc_ops = { * IRAM allocator */ #ifdef CONFIG_GENERIC_ALLOCATOR -static int snd_dma_iram_alloc(struct snd_dma_buffer *dmab, size_t size) +static void *snd_dma_iram_alloc(struct snd_dma_buffer *dmab, size_t size) { struct device *dev = dmab->dev.dev; struct gen_pool *pool; + void *p; if (dev->of_node) { pool = of_gen_pool_get(dev->of_node, "iram", 0); /* Assign the pool into private_data field */ dmab->private_data = pool; - dmab->area = gen_pool_dma_alloc_align(pool, size, &dmab->addr, - PAGE_SIZE); - if (dmab->area) - return 0; + p = gen_pool_dma_alloc_align(pool, size, &dmab->addr, PAGE_SIZE); + if (p) + return p; } /* Internal memory might have limited size and no enough space, @@ -334,31 +390,31 @@ static const struct snd_malloc_ops snd_dma_iram_ops = { }; #endif /* CONFIG_GENERIC_ALLOCATOR */ +#define DEFAULT_GFP \ + (GFP_KERNEL | \ + __GFP_COMP | /* compound page lets parts be mapped */ \ + __GFP_NORETRY | /* don't trigger OOM-killer */ \ + __GFP_NOWARN) /* no stack trace print - this call is non-critical */ + /* * Coherent device pages allocator */ -static int snd_dma_dev_alloc(struct snd_dma_buffer *dmab, size_t size) +static void *snd_dma_dev_alloc(struct snd_dma_buffer *dmab, size_t size) { - gfp_t gfp_flags; - - gfp_flags = GFP_KERNEL - | __GFP_COMP /* compound page lets parts be mapped */ - | __GFP_NORETRY /* don't trigger OOM-killer */ - | __GFP_NOWARN; /* no stack trace print - this call is non-critical */ - dmab->area = dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr, - gfp_flags); + void *p; + + p = dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP); #ifdef CONFIG_X86 - if (dmab->area && dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC) - set_memory_wc((unsigned long)dmab->area, - PAGE_ALIGN(size) >> PAGE_SHIFT); + if (p && dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC) + set_memory_wc((unsigned long)p, PAGE_ALIGN(size) >> PAGE_SHIFT); #endif - return 0; + return p; } static void snd_dma_dev_free(struct snd_dma_buffer *dmab) { #ifdef CONFIG_X86 - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC) + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC) set_memory_wb((unsigned long)dmab->area, PAGE_ALIGN(dmab->bytes) >> PAGE_SHIFT); #endif @@ -368,6 +424,10 @@ static void snd_dma_dev_free(struct snd_dma_buffer *dmab) static int snd_dma_dev_mmap(struct snd_dma_buffer *dmab, struct vm_area_struct *area) { +#ifdef CONFIG_X86 + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC) + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); +#endif return dma_mmap_coherent(dmab->dev.dev, area, dmab->area, dmab->addr, dmab->bytes); } @@ -377,6 +437,37 @@ static const struct snd_malloc_ops snd_dma_dev_ops = { .free = snd_dma_dev_free, .mmap = snd_dma_dev_mmap, }; + +/* + * Write-combined pages + */ +#ifdef CONFIG_X86 +/* On x86, share the same ops as the standard dev ops */ +#define snd_dma_wc_ops snd_dma_dev_ops +#else /* CONFIG_X86 */ +static void *snd_dma_wc_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + return dma_alloc_wc(dmab->dev.dev, size, &dmab->addr, DEFAULT_GFP); +} + +static void snd_dma_wc_free(struct snd_dma_buffer *dmab) +{ + dma_free_wc(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); +} + +static int snd_dma_wc_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + return dma_mmap_wc(dmab->dev.dev, area, + dmab->area, dmab->addr, dmab->bytes); +} + +static const struct snd_malloc_ops snd_dma_wc_ops = { + .alloc = snd_dma_wc_alloc, + .free = snd_dma_wc_free, + .mmap = snd_dma_wc_mmap, +}; +#endif /* CONFIG_X86 */ #endif /* CONFIG_HAS_DMA */ /* @@ -387,14 +478,14 @@ static const struct snd_malloc_ops *dma_ops[] = { [SNDRV_DMA_TYPE_VMALLOC] = &snd_dma_vmalloc_ops, #ifdef CONFIG_HAS_DMA [SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops, - [SNDRV_DMA_TYPE_DEV_UC] = &snd_dma_dev_ops, + [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops, #ifdef CONFIG_GENERIC_ALLOCATOR [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops, #endif /* CONFIG_GENERIC_ALLOCATOR */ #endif /* CONFIG_HAS_DMA */ #ifdef CONFIG_SND_DMA_SGBUF [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops, - [SNDRV_DMA_TYPE_DEV_UC_SG] = &snd_dma_sg_ops, + [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops, #endif }; diff --git a/sound/core/memalloc_local.h b/sound/core/memalloc_local.h index dbea7f2aed07..9f2e0a608b49 100644 --- a/sound/core/memalloc_local.h +++ b/sound/core/memalloc_local.h @@ -3,7 +3,7 @@ #define __MEMALLOC_LOCAL_H struct snd_malloc_ops { - int (*alloc)(struct snd_dma_buffer *dmab, size_t size); + void *(*alloc)(struct snd_dma_buffer *dmab, size_t size); void (*free)(struct snd_dma_buffer *dmab); dma_addr_t (*get_addr)(struct snd_dma_buffer *dmab, size_t offset); struct page *(*get_page)(struct snd_dma_buffer *dmab, size_t offset); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index d7621ed105bd..7fbd1ccbb5b0 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -67,7 +67,8 @@ static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab) * * the minimum size is snd_minimum_buffer. it should be power of 2. */ -static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size) +static int preallocate_pcm_pages(struct snd_pcm_substream *substream, + size_t size, bool no_fallback) { struct snd_dma_buffer *dmab = &substream->dma_buffer; struct snd_card *card = substream->pcm->card; @@ -79,6 +80,8 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz size, dmab); if (err != -ENOMEM) return err; + if (no_fallback) + break; size >>= 1; } while (size >= snd_minimum_buffer); dmab->bytes = 0; /* tell error */ @@ -86,7 +89,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz substream->pcm->card->number, substream->pcm->device, substream->stream ? 'c' : 'p', substream->number, substream->pcm->name, orig_size); - return 0; + return -ENOMEM; } /** @@ -222,18 +225,31 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream) /* * pre-allocate the buffer and create a proc file for the substream */ -static void preallocate_pages(struct snd_pcm_substream *substream, +static int preallocate_pages(struct snd_pcm_substream *substream, int type, struct device *data, size_t size, size_t max, bool managed) { + int err; + if (snd_BUG_ON(substream->dma_buffer.dev.type)) - return; + return -EINVAL; substream->dma_buffer.dev.type = type; substream->dma_buffer.dev.dev = data; - if (size > 0 && preallocate_dma && substream->number < maximum_substreams) - preallocate_pcm_pages(substream, size); + if (size > 0) { + if (!max) { + /* no fallback, only also inform -ENOMEM */ + err = preallocate_pcm_pages(substream, size, true); + if (err < 0) + return err; + } else if (preallocate_dma && + substream->number < maximum_substreams) { + err = preallocate_pcm_pages(substream, size, false); + if (err < 0 && err != -ENOMEM) + return err; + } + } if (substream->dma_buffer.bytes > 0) substream->buffer_bytes_max = substream->dma_buffer.bytes; @@ -242,17 +258,22 @@ static void preallocate_pages(struct snd_pcm_substream *substream, preallocate_info_init(substream); if (managed) substream->managed_buffer_alloc = 1; + return 0; } -static void preallocate_pages_for_all(struct snd_pcm *pcm, int type, +static int preallocate_pages_for_all(struct snd_pcm *pcm, int type, void *data, size_t size, size_t max, bool managed) { struct snd_pcm_substream *substream; - int stream; + int stream, err; - for_each_pcm_substream(pcm, stream, substream) - preallocate_pages(substream, type, data, size, max, managed); + for_each_pcm_substream(pcm, stream, substream) { + err = preallocate_pages(substream, type, data, size, max, managed); + if (err < 0) + return err; + } + return 0; } /** @@ -309,11 +330,22 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); * When a buffer is actually allocated before the PCM hw_params call, it * turns on the runtime buffer_changed flag for drivers changing their h/w * parameters accordingly. + * + * When @size is non-zero and @max is zero, this tries to allocate for only + * the exact buffer size without fallback, and may return -ENOMEM. + * Otherwise, the function tries to allocate smaller chunks if the allocation + * fails. This is the behavior of snd_pcm_set_fixed_buffer(). + * + * When both @size and @max are zero, the function only sets up the buffer + * for later dynamic allocations. It's used typically for buffers with + * SNDRV_DMA_TYPE_VMALLOC type. + * + * Upon successful buffer allocation and setup, the function returns 0. */ -void snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type, +int snd_pcm_set_managed_buffer(struct snd_pcm_substream *substream, int type, struct device *data, size_t size, size_t max) { - preallocate_pages(substream, type, data, size, max, true); + return preallocate_pages(substream, type, data, size, max, true); } EXPORT_SYMBOL(snd_pcm_set_managed_buffer); @@ -329,11 +361,11 @@ EXPORT_SYMBOL(snd_pcm_set_managed_buffer); * Do pre-allocation to all substreams of the given pcm for the specified DMA * type and size, and set the managed_buffer_alloc flag to each substream. */ -void snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type, - struct device *data, - size_t size, size_t max) +int snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type, + struct device *data, + size_t size, size_t max) { - preallocate_pages_for_all(pcm, type, data, size, max, true); + return preallocate_pages_for_all(pcm, type, data, size, max, true); } EXPORT_SYMBOL(snd_pcm_set_managed_buffer_all); @@ -376,6 +408,9 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) substream->dma_buffer.bytes >= size) { dmab = &substream->dma_buffer; /* use the pre-allocated buffer */ } else { + /* dma_max=0 means the fixed size preallocation */ + if (substream->dma_buffer.area && !substream->dma_max) + return -ENOMEM; dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); if (! dmab) return -ENOMEM; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 71323d807dbf..d233cb3b41d8 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -243,13 +243,18 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, static bool hw_support_mmap(struct snd_pcm_substream *substream) { + struct snd_dma_buffer *dmabuf; + if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP)) return false; if (substream->ops->mmap || substream->ops->page) return true; - switch (substream->dma_buffer.dev.type) { + dmabuf = snd_pcm_get_dma_buf(substream); + if (!dmabuf) + dmabuf = &substream->dma_buffer; + switch (dmabuf->dev.type) { case SNDRV_DMA_TYPE_UNKNOWN: /* we can't know the device, so just assume that the driver does * everything right @@ -259,7 +264,7 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream) case SNDRV_DMA_TYPE_VMALLOC: return true; default: - return dma_can_mmap(substream->dma_buffer.dev.dev); + return dma_can_mmap(dmabuf->dev.dev); } } @@ -3616,6 +3621,12 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file) { + /* If drivers require the explicit sync (typically for non-coherent + * pages), we have to disable the mmap of status and control data + * to enforce the control via SYNC_PTR ioctl. + */ + if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC) + return false; /* See pcm_control_mmap_allowed() below. * Since older alsa-lib requires both status and control mmaps to be * coupled, we have to disable the status mmap for old alsa-lib, too. @@ -3630,6 +3641,9 @@ static bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file) { if (pcm_file->no_compat_mmap) return false; + /* see above */ + if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_EXPLICIT_SYNC) + return false; /* Disallow the control mmap when SYNC_APPLPTR flag is set; * it enforces the user-space to fall back to snd_pcm_sync_ptr(), * thus it effectively assures the manual update of appl_ptr. diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index ac760b1e3d12..8c18d8c4177e 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -20,15 +20,15 @@ are redirected to output port immediately. The routing can be done via aconnect program in alsa-utils. - Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY). + Each client has a static client number 14 (= SNDRV_SEQ_CLIENT_DUMMY). If you want to auto-load this module, you may add the following alias in your /etc/conf.modules file. - alias snd-seq-client-62 snd-seq-dummy + alias snd-seq-client-14 snd-seq-dummy - The module is loaded on demand for client 62, or /proc/asound/seq/ + The module is loaded on demand for client 14, or /proc/asound/seq/ is accessed. If you don't need this module to be loaded, alias - snd-seq-client-62 as "off". This will help modprobe. + snd-seq-client-14 as "off". This will help modprobe. The number of ports to be created can be specified via the module parameter "ports". For example, to create four ports, add the diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index 232cf3f1bcb3..8352a5cdb19f 100644 --- a/sound/core/sgbuf.c +++ b/sound/core/sgbuf.c @@ -43,8 +43,8 @@ static void snd_dma_sg_free(struct snd_dma_buffer *dmab) dmab->area = NULL; tmpb.dev.type = SNDRV_DMA_TYPE_DEV; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC_SG) - tmpb.dev.type = SNDRV_DMA_TYPE_DEV_UC; + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) + tmpb.dev.type = SNDRV_DMA_TYPE_DEV_WC; tmpb.dev.dev = sgbuf->dev; for (i = 0; i < sgbuf->pages; i++) { if (!(sgbuf->table[i].addr & ~PAGE_MASK)) @@ -63,7 +63,7 @@ static void snd_dma_sg_free(struct snd_dma_buffer *dmab) #define MAX_ALLOC_PAGES 32 -static int snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) +static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) { struct snd_sg_buf *sgbuf; unsigned int i, pages, chunk, maxpages; @@ -72,12 +72,13 @@ static int snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) struct page **pgtable; int type = SNDRV_DMA_TYPE_DEV; pgprot_t prot = PAGE_KERNEL; + void *area; dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL); if (!sgbuf) - return -ENOMEM; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC_SG) { - type = SNDRV_DMA_TYPE_DEV_UC; + return NULL; + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) { + type = SNDRV_DMA_TYPE_DEV_WC; #ifdef pgprot_noncached prot = pgprot_noncached(PAGE_KERNEL); #endif @@ -127,14 +128,14 @@ static int snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) } sgbuf->size = size; - dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot); - if (! dmab->area) + area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot); + if (!area) goto _failed; - return 0; + return area; _failed: snd_dma_sg_free(dmab); /* free the table */ - return -ENOMEM; + return NULL; } static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab, @@ -182,10 +183,19 @@ static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab, return size; } +static int snd_dma_sg_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return -ENOENT; /* continue with the default mmap handler */ +} + const struct snd_malloc_ops snd_dma_sg_ops = { .alloc = snd_dma_sg_alloc, .free = snd_dma_sg_free, .get_addr = snd_dma_sg_get_addr, .get_page = snd_dma_sg_get_page, .get_chunk_size = snd_dma_sg_get_chunk_size, + .mmap = snd_dma_sg_mmap, }; |