summaryrefslogtreecommitdiff
path: root/sound/core/sgbuf.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/core/sgbuf.c')
-rw-r--r--sound/core/sgbuf.c90
1 files changed, 59 insertions, 31 deletions
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index 29ddb76187e5..232cf3f1bcb3 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -10,20 +10,34 @@
#include <linux/vmalloc.h>
#include <linux/export.h>
#include <sound/memalloc.h>
-
+#include "memalloc_local.h"
+
+struct snd_sg_page {
+ void *buf;
+ dma_addr_t addr;
+};
+
+struct snd_sg_buf {
+ int size; /* allocated byte size */
+ int pages; /* allocated pages */
+ int tblsize; /* allocated table size */
+ struct snd_sg_page *table; /* address table */
+ struct page **page_table; /* page table (for vmap/vunmap) */
+ struct device *dev;
+};
/* table entries are align to 32 */
#define SGBUF_TBL_ALIGN 32
#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
-int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
+static void snd_dma_sg_free(struct snd_dma_buffer *dmab)
{
struct snd_sg_buf *sgbuf = dmab->private_data;
struct snd_dma_buffer tmpb;
int i;
- if (! sgbuf)
- return -EINVAL;
+ if (!sgbuf)
+ return;
vunmap(dmab->area);
dmab->area = NULL;
@@ -45,15 +59,11 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
kfree(sgbuf->page_table);
kfree(sgbuf);
dmab->private_data = NULL;
-
- return 0;
}
#define MAX_ALLOC_PAGES 32
-void *snd_malloc_sgbuf_pages(struct device *device,
- size_t size, struct snd_dma_buffer *dmab,
- size_t *res_size)
+static int snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size)
{
struct snd_sg_buf *sgbuf;
unsigned int i, pages, chunk, maxpages;
@@ -63,18 +73,16 @@ void *snd_malloc_sgbuf_pages(struct device *device,
int type = SNDRV_DMA_TYPE_DEV;
pgprot_t prot = PAGE_KERNEL;
- dmab->area = NULL;
- dmab->addr = 0;
dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
- if (! sgbuf)
- return NULL;
+ if (!sgbuf)
+ return -ENOMEM;
if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC_SG) {
type = SNDRV_DMA_TYPE_DEV_UC;
#ifdef pgprot_noncached
prot = pgprot_noncached(PAGE_KERNEL);
#endif
}
- sgbuf->dev = device;
+ sgbuf->dev = dmab->dev.dev;
pages = snd_sgbuf_aligned_pages(size);
sgbuf->tblsize = sgbuf_align_table(pages);
table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
@@ -94,12 +102,10 @@ void *snd_malloc_sgbuf_pages(struct device *device,
if (chunk > maxpages)
chunk = maxpages;
chunk <<= PAGE_SHIFT;
- if (snd_dma_alloc_pages_fallback(type, device,
+ if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev,
chunk, &tmpb) < 0) {
if (!sgbuf->pages)
goto _failed;
- if (!res_size)
- goto _failed;
size = sgbuf->pages * PAGE_SIZE;
break;
}
@@ -124,27 +130,42 @@ void *snd_malloc_sgbuf_pages(struct device *device,
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot);
if (! dmab->area)
goto _failed;
- if (res_size)
- *res_size = sgbuf->size;
- return dmab->area;
+ return 0;
_failed:
- snd_free_sgbuf_pages(dmab); /* free the table */
- return NULL;
+ snd_dma_sg_free(dmab); /* free the table */
+ return -ENOMEM;
}
-/*
- * compute the max chunk size with continuous pages on sg-buffer
- */
-unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
- unsigned int ofs, unsigned int size)
+static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct snd_sg_buf *sgbuf = dmab->private_data;
+ dma_addr_t addr;
+
+ addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
+ addr &= ~((dma_addr_t)PAGE_SIZE - 1);
+ return addr + offset % PAGE_SIZE;
+}
+
+static struct page *snd_dma_sg_get_page(struct snd_dma_buffer *dmab,
+ size_t offset)
+{
+ struct snd_sg_buf *sgbuf = dmab->private_data;
+ unsigned int idx = offset >> PAGE_SHIFT;
+
+ if (idx >= (unsigned int)sgbuf->pages)
+ return NULL;
+ return sgbuf->page_table[idx];
+}
+
+static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab,
+ unsigned int ofs,
+ unsigned int size)
{
struct snd_sg_buf *sg = dmab->private_data;
unsigned int start, end, pg;
- if (!sg)
- return size;
-
start = ofs >> PAGE_SHIFT;
end = (ofs + size - 1) >> PAGE_SHIFT;
/* check page continuity */
@@ -160,4 +181,11 @@ unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
/* ok, all on continuous pages */
return size;
}
-EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
+
+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,
+};