123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- /*
- * Scatter-Gather buffer
- *
- * Copyright (c) by Takashi Iwai <tiwai@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
- #include <linux/config.h>
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/vmalloc.h>
- #include <sound/memalloc.h>
- /* table entries are align to 32 */
- #define SGBUF_TBL_ALIGN 32
- #define sgbuf_align_table(tbl) ((((tbl) + SGBUF_TBL_ALIGN - 1) / SGBUF_TBL_ALIGN) * SGBUF_TBL_ALIGN)
- int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
- {
- struct snd_sg_buf *sgbuf = dmab->private_data;
- struct snd_dma_buffer tmpb;
- int i;
- if (! sgbuf)
- return -EINVAL;
- tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
- tmpb.dev.dev = sgbuf->dev;
- for (i = 0; i < sgbuf->pages; i++) {
- tmpb.area = sgbuf->table[i].buf;
- tmpb.addr = sgbuf->table[i].addr;
- tmpb.bytes = PAGE_SIZE;
- snd_dma_free_pages(&tmpb);
- }
- if (dmab->area)
- vunmap(dmab->area);
- dmab->area = NULL;
- kfree(sgbuf->table);
- kfree(sgbuf->page_table);
- kfree(sgbuf);
- dmab->private_data = NULL;
-
- return 0;
- }
- void *snd_malloc_sgbuf_pages(struct device *device,
- size_t size, struct snd_dma_buffer *dmab,
- size_t *res_size)
- {
- struct snd_sg_buf *sgbuf;
- unsigned int i, pages;
- struct snd_dma_buffer tmpb;
- dmab->area = NULL;
- dmab->addr = 0;
- dmab->private_data = sgbuf = kmalloc(sizeof(*sgbuf), GFP_KERNEL);
- if (! sgbuf)
- return NULL;
- memset(sgbuf, 0, sizeof(*sgbuf));
- sgbuf->dev = device;
- pages = snd_sgbuf_aligned_pages(size);
- sgbuf->tblsize = sgbuf_align_table(pages);
- sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL);
- if (! sgbuf->table)
- goto _failed;
- memset(sgbuf->table, 0, sizeof(*sgbuf->table) * sgbuf->tblsize);
- sgbuf->page_table = kmalloc(sizeof(*sgbuf->page_table) * sgbuf->tblsize, GFP_KERNEL);
- if (! sgbuf->page_table)
- goto _failed;
- memset(sgbuf->page_table, 0, sizeof(*sgbuf->page_table) * sgbuf->tblsize);
- /* 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)
- goto _failed;
- *res_size = size = sgbuf->pages * PAGE_SIZE;
- break;
- }
- sgbuf->table[i].buf = tmpb.area;
- sgbuf->table[i].addr = tmpb.addr;
- sgbuf->page_table[i] = virt_to_page(tmpb.area);
- sgbuf->pages++;
- }
- sgbuf->size = size;
- dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
- if (! dmab->area)
- goto _failed;
- return dmab->area;
- _failed:
- snd_free_sgbuf_pages(dmab); /* free the table */
- return NULL;
- }
|