diff options
Diffstat (limited to 'sound/core/sgbuf.c')
-rw-r--r-- | sound/core/sgbuf.c | 90 |
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, +}; |