|
@@ -41,9 +41,11 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
|
|
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
|
|
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
|
|
tmpb.dev.dev = sgbuf->dev;
|
|
tmpb.dev.dev = sgbuf->dev;
|
|
for (i = 0; i < sgbuf->pages; i++) {
|
|
for (i = 0; i < sgbuf->pages; i++) {
|
|
|
|
+ if (!(sgbuf->table[i].addr & ~PAGE_MASK))
|
|
|
|
+ continue; /* continuous pages */
|
|
tmpb.area = sgbuf->table[i].buf;
|
|
tmpb.area = sgbuf->table[i].buf;
|
|
- tmpb.addr = sgbuf->table[i].addr;
|
|
|
|
- tmpb.bytes = PAGE_SIZE;
|
|
|
|
|
|
+ tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
|
|
|
|
+ tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
|
|
snd_dma_free_pages(&tmpb);
|
|
snd_dma_free_pages(&tmpb);
|
|
}
|
|
}
|
|
if (dmab->area)
|
|
if (dmab->area)
|
|
@@ -58,13 +60,17 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#define MAX_ALLOC_PAGES 32
|
|
|
|
+
|
|
void *snd_malloc_sgbuf_pages(struct device *device,
|
|
void *snd_malloc_sgbuf_pages(struct device *device,
|
|
size_t size, struct snd_dma_buffer *dmab,
|
|
size_t size, struct snd_dma_buffer *dmab,
|
|
size_t *res_size)
|
|
size_t *res_size)
|
|
{
|
|
{
|
|
struct snd_sg_buf *sgbuf;
|
|
struct snd_sg_buf *sgbuf;
|
|
- unsigned int i, pages;
|
|
|
|
|
|
+ unsigned int i, pages, chunk, maxpages;
|
|
struct snd_dma_buffer tmpb;
|
|
struct snd_dma_buffer tmpb;
|
|
|
|
+ struct snd_sg_page *table;
|
|
|
|
+ struct page **pgtable;
|
|
|
|
|
|
dmab->area = NULL;
|
|
dmab->area = NULL;
|
|
dmab->addr = 0;
|
|
dmab->addr = 0;
|
|
@@ -74,31 +80,55 @@ void *snd_malloc_sgbuf_pages(struct device *device,
|
|
sgbuf->dev = device;
|
|
sgbuf->dev = device;
|
|
pages = snd_sgbuf_aligned_pages(size);
|
|
pages = snd_sgbuf_aligned_pages(size);
|
|
sgbuf->tblsize = sgbuf_align_table(pages);
|
|
sgbuf->tblsize = sgbuf_align_table(pages);
|
|
- sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL);
|
|
|
|
- if (! sgbuf->table)
|
|
|
|
|
|
+ table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
|
|
|
|
+ if (!table)
|
|
goto _failed;
|
|
goto _failed;
|
|
- sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL);
|
|
|
|
- if (! sgbuf->page_table)
|
|
|
|
|
|
+ sgbuf->table = table;
|
|
|
|
+ pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
|
|
|
|
+ if (!pgtable)
|
|
goto _failed;
|
|
goto _failed;
|
|
|
|
+ sgbuf->page_table = pgtable;
|
|
|
|
|
|
- /* allocate each page */
|
|
|
|
- for (i = 0; i < pages; i++) {
|
|
|
|
- if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) {
|
|
|
|
- if (res_size == NULL)
|
|
|
|
|
|
+ /* allocate pages */
|
|
|
|
+ maxpages = MAX_ALLOC_PAGES;
|
|
|
|
+ while (pages > 0) {
|
|
|
|
+ chunk = pages;
|
|
|
|
+ /* don't be too eager to take a huge chunk */
|
|
|
|
+ if (chunk > maxpages)
|
|
|
|
+ chunk = maxpages;
|
|
|
|
+ chunk <<= PAGE_SHIFT;
|
|
|
|
+ if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
|
|
|
|
+ chunk, &tmpb) < 0) {
|
|
|
|
+ if (!sgbuf->pages)
|
|
|
|
+ return NULL;
|
|
|
|
+ if (!res_size)
|
|
goto _failed;
|
|
goto _failed;
|
|
- *res_size = size = sgbuf->pages * PAGE_SIZE;
|
|
|
|
|
|
+ size = sgbuf->pages * PAGE_SIZE;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- sgbuf->table[i].buf = tmpb.area;
|
|
|
|
- sgbuf->table[i].addr = tmpb.addr;
|
|
|
|
- sgbuf->page_table[i] = virt_to_page(tmpb.area);
|
|
|
|
- sgbuf->pages++;
|
|
|
|
|
|
+ chunk = tmpb.bytes >> PAGE_SHIFT;
|
|
|
|
+ for (i = 0; i < chunk; i++) {
|
|
|
|
+ table->buf = tmpb.area;
|
|
|
|
+ table->addr = tmpb.addr;
|
|
|
|
+ if (!i)
|
|
|
|
+ table->addr |= chunk; /* mark head */
|
|
|
|
+ table++;
|
|
|
|
+ *pgtable++ = virt_to_page(tmpb.area);
|
|
|
|
+ tmpb.area += PAGE_SIZE;
|
|
|
|
+ tmpb.addr += PAGE_SIZE;
|
|
|
|
+ }
|
|
|
|
+ sgbuf->pages += chunk;
|
|
|
|
+ pages -= chunk;
|
|
|
|
+ if (chunk < maxpages)
|
|
|
|
+ maxpages = chunk;
|
|
}
|
|
}
|
|
|
|
|
|
sgbuf->size = size;
|
|
sgbuf->size = size;
|
|
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
|
|
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
|
|
if (! dmab->area)
|
|
if (! dmab->area)
|
|
goto _failed;
|
|
goto _failed;
|
|
|
|
+ if (res_size)
|
|
|
|
+ *res_size = sgbuf->size;
|
|
return dmab->area;
|
|
return dmab->area;
|
|
|
|
|
|
_failed:
|
|
_failed:
|