summaryrefslogtreecommitdiff
path: root/sound/core
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/compress_offload.c69
-rw-r--r--sound/core/control_led.c2
-rw-r--r--sound/core/init.c105
-rw-r--r--sound/core/isadma.c38
-rw-r--r--sound/core/memalloc.c177
-rw-r--r--sound/core/memalloc_local.h2
-rw-r--r--sound/core/pcm_lib.c2
-rw-r--r--sound/core/pcm_memory.c67
-rw-r--r--sound/core/pcm_native.c23
-rw-r--r--sound/core/seq/seq_dummy.c8
-rw-r--r--sound/core/sgbuf.c30
11 files changed, 368 insertions, 155 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_lib.c b/sound/core/pcm_lib.c
index 7d5883432085..a144a3f68e9e 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1746,7 +1746,7 @@ static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
channels = params_channels(params);
frame_size = snd_pcm_format_size(format, channels);
if (frame_size > 0)
- params->fifo_size /= (unsigned)frame_size;
+ params->fifo_size /= frame_size;
}
return 0;
}
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 09c0e2a6489c..d233cb3b41d8 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -243,20 +243,28 @@ 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:
- return false;
+ /* we can't know the device, so just assume that the driver does
+ * everything right
+ */
+ return true;
case SNDRV_DMA_TYPE_CONTINUOUS:
case SNDRV_DMA_TYPE_VMALLOC:
return true;
default:
- return dma_can_mmap(substream->dma_buffer.dev.dev);
+ return dma_can_mmap(dmabuf->dev.dev);
}
}
@@ -3613,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.
@@ -3627,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,
};