aboutsummaryrefslogtreecommitdiff
path: root/sound/core/sgbuf.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-10-11 09:16:54 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2008-10-11 09:16:54 -0700
commitd3570a5a7b8d0604fa012129f92637dc1534f62c (patch)
tree15d290595c363a2bdd4f4d6b3a2f7433e6e48f6d /sound/core/sgbuf.c
parent37d9869ed928268409b48f52c57449918c0fd307 (diff)
parent72474be62d6ec2e0337ff01ecbd737f9c5c242c7 (diff)
Merge branch 'for-linus' of git://git.alsa-project.org/alsa-kernel
* 'for-linus' of git://git.alsa-project.org/alsa-kernel: (258 commits) ALSA: hda: VREF powerdown for headphones ALSA: hda: STAC_HP_M4 ALSA: ASoC: Check for machine type in GTA01 machine driver ALSA: mtpav - Fix race in probe ALSA: usb-audio: dynamic detection of MIDI interfaces in uaxx-quirk ALSA: Add a note on dependency of RTC stuff ALSA: ASoC: add new param mux to dapm_mux_update_power ALSA: Increase components array size ALSA: ASoC: Correct inverted Mic PGA Switch control in wm8510 driver ALSA: hda: comment typo fix ALSA: hda: comment typo fix ALSA: hda - Fix PCI SSID for ASROCK K18N78FullHD-hSLI ALSA: snd-usb-audio: support for Edirol UA-4FX device ALSA: usb - Fix possible Oops at USB-MIDI disconnection ALSA: hda - Fix another ALC889A (rev 0x100101) ALSA: hda: add more board-specific information for Realtek ALC662 rev1 ALSA: Correct Vladimir Barinov's e-mail address ALSA: cs46xx: Add PCI IDs for TerraTec and Hercules cards ALSA: hda: SPDIF stream muting support ALSA: hda: appletv support ...
Diffstat (limited to 'sound/core/sgbuf.c')
-rw-r--r--sound/core/sgbuf.c62
1 files changed, 46 insertions, 16 deletions
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index cefd228cd2a..d4564edd61d 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -41,9 +41,11 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
tmpb.dev.dev = sgbuf->dev;
for (i = 0; i < sgbuf->pages; i++) {
+ if (!(sgbuf->table[i].addr & ~PAGE_MASK))
+ continue; /* continuous pages */
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);
}
if (dmab->area)
@@ -58,13 +60,17 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
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)
{
struct snd_sg_buf *sgbuf;
- unsigned int i, pages;
+ unsigned int i, pages, chunk, maxpages;
struct snd_dma_buffer tmpb;
+ struct snd_sg_page *table;
+ struct page **pgtable;
dmab->area = NULL;
dmab->addr = 0;
@@ -74,31 +80,55 @@ void *snd_malloc_sgbuf_pages(struct device *device,
sgbuf->dev = device;
pages = snd_sgbuf_aligned_pages(size);
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;
- 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;
+ 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;
- *res_size = size = sgbuf->pages * PAGE_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++;
+ 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;
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
if (! dmab->area)
goto _failed;
+ if (res_size)
+ *res_size = sgbuf->size;
return dmab->area;
_failed: