aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-07-30 09:13:33 -0400
committerJaroslav Kysela <perex@perex.cz>2008-08-25 03:57:38 -0400
commit51e9f2e665bf2b6a01be275d64c336d942c59a66 (patch)
tree75b79c23288a820eea6959e79060e3e4fb9d0eae
parent4e184f8fc06411f35fdcf4b9bc6187c857bf7214 (diff)
ALSA: Allocate larger pages in sgbuf
Most hardwares have limited buffer-descriptor table length. This also restricts the max buffer size of the sound driver. For example, snd-hda-intel has 1MB buffer size limit, and this is because it can have at most 256 BDL entries. For supporting larger buffers, we need to allocate larger pages even for sg-buffers. This patch changes the sgbuf allocation code to try to allocate larger pages first. At each head of the allocated pages, the number of allocated pages is stored in the lowest bits of the corresponding entry of the table addr field. This change isn't visible as long as the driver uses snd_sgbuf_get_addr() helper. Also, the patch adds a new function, snd_pcm_sgbuf_get_chunk_size(). This returns the size of the chunk on continuous pages starting at the given position offset. If the chunk reaches to a non-continuous page, it returns the size to the boundary. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--include/sound/memalloc.h4
-rw-r--r--include/sound/pcm.h3
-rw-r--r--sound/core/pcm_memory.c26
-rw-r--r--sound/core/sgbuf.c62
4 files changed, 77 insertions, 18 deletions
diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h
index 96d0dc171459..d787a6b4a101 100644
--- a/include/sound/memalloc.h
+++ b/include/sound/memalloc.h
@@ -97,7 +97,9 @@ static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
97 */ 97 */
98static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset) 98static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
99{ 99{
100 return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE; 100 dma_addr_t addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
101 addr &= PAGE_MASK;
102 return addr + offset % PAGE_SIZE;
101} 103}
102 104
103/* 105/*
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 8db89630c821..40c5a6fa6bcd 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -996,7 +996,8 @@ snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
996 996
997struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, 997struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
998 unsigned long offset); 998 unsigned long offset);
999 999unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
1000 unsigned int ofs, unsigned int size);
1000 1001
1001/* handle mmap counter - PCM mmap callback should handle this counter properly */ 1002/* handle mmap counter - PCM mmap callback should handle this counter properly */
1002static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) 1003static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
index 859b1185e69a..a6d42808828c 100644
--- a/sound/core/pcm_memory.c
+++ b/sound/core/pcm_memory.c
@@ -324,6 +324,32 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne
324 324
325EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); 325EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
326 326
327/*
328 * compute the max chunk size with continuous pages on sg-buffer
329 */
330unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
331 unsigned int ofs, unsigned int size)
332{
333 struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
334 unsigned int start, end, pg;
335
336 start = ofs >> PAGE_SHIFT;
337 end = (ofs + size - 1) >> PAGE_SHIFT;
338 /* check page continuity */
339 pg = sg->table[start].addr >> PAGE_SHIFT;
340 for (;;) {
341 start++;
342 if (start > end)
343 break;
344 pg++;
345 if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
346 return (start << PAGE_SHIFT) - ofs;
347 }
348 /* ok, all on continuous pages */
349 return size;
350}
351EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
352
327/** 353/**
328 * snd_pcm_lib_malloc_pages - allocate the DMA buffer 354 * snd_pcm_lib_malloc_pages - allocate the DMA buffer
329 * @substream: the substream to allocate the DMA buffer to 355 * @substream: the substream to allocate the DMA buffer to
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index cefd228cd2aa..d4564edd61d7 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)
41 tmpb.dev.type = SNDRV_DMA_TYPE_DEV; 41 tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
42 tmpb.dev.dev = sgbuf->dev; 42 tmpb.dev.dev = sgbuf->dev;
43 for (i = 0; i < sgbuf->pages; i++) { 43 for (i = 0; i < sgbuf->pages; i++) {
44 if (!(sgbuf->table[i].addr & ~PAGE_MASK))
45 continue; /* continuous pages */
44 tmpb.area = sgbuf->table[i].buf; 46 tmpb.area = sgbuf->table[i].buf;
45 tmpb.addr = sgbuf->table[i].addr; 47 tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
46 tmpb.bytes = PAGE_SIZE; 48 tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
47 snd_dma_free_pages(&tmpb); 49 snd_dma_free_pages(&tmpb);
48 } 50 }
49 if (dmab->area) 51 if (dmab->area)
@@ -58,13 +60,17 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
58 return 0; 60 return 0;
59} 61}
60 62
63#define MAX_ALLOC_PAGES 32
64
61void *snd_malloc_sgbuf_pages(struct device *device, 65void *snd_malloc_sgbuf_pages(struct device *device,
62 size_t size, struct snd_dma_buffer *dmab, 66 size_t size, struct snd_dma_buffer *dmab,
63 size_t *res_size) 67 size_t *res_size)
64{ 68{
65 struct snd_sg_buf *sgbuf; 69 struct snd_sg_buf *sgbuf;
66 unsigned int i, pages; 70 unsigned int i, pages, chunk, maxpages;
67 struct snd_dma_buffer tmpb; 71 struct snd_dma_buffer tmpb;
72 struct snd_sg_page *table;
73 struct page **pgtable;
68 74
69 dmab->area = NULL; 75 dmab->area = NULL;
70 dmab->addr = 0; 76 dmab->addr = 0;
@@ -74,31 +80,55 @@ void *snd_malloc_sgbuf_pages(struct device *device,
74 sgbuf->dev = device; 80 sgbuf->dev = device;
75 pages = snd_sgbuf_aligned_pages(size); 81 pages = snd_sgbuf_aligned_pages(size);
76 sgbuf->tblsize = sgbuf_align_table(pages); 82 sgbuf->tblsize = sgbuf_align_table(pages);
77 sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL); 83 table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
78 if (! sgbuf->table) 84 if (!table)
79 goto _failed; 85 goto _failed;
80 sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL); 86 sgbuf->table = table;
81 if (! sgbuf->page_table) 87 pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
88 if (!pgtable)
82 goto _failed; 89 goto _failed;
90 sgbuf->page_table = pgtable;
83 91
84 /* allocate each page */ 92 /* allocate pages */
85 for (i = 0; i < pages; i++) { 93 maxpages = MAX_ALLOC_PAGES;
86 if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) { 94 while (pages > 0) {
87 if (res_size == NULL) 95 chunk = pages;
96 /* don't be too eager to take a huge chunk */
97 if (chunk > maxpages)
98 chunk = maxpages;
99 chunk <<= PAGE_SHIFT;
100 if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
101 chunk, &tmpb) < 0) {
102 if (!sgbuf->pages)
103 return NULL;
104 if (!res_size)
88 goto _failed; 105 goto _failed;
89 *res_size = size = sgbuf->pages * PAGE_SIZE; 106 size = sgbuf->pages * PAGE_SIZE;
90 break; 107 break;
91 } 108 }
92 sgbuf->table[i].buf = tmpb.area; 109 chunk = tmpb.bytes >> PAGE_SHIFT;
93 sgbuf->table[i].addr = tmpb.addr; 110 for (i = 0; i < chunk; i++) {
94 sgbuf->page_table[i] = virt_to_page(tmpb.area); 111 table->buf = tmpb.area;
95 sgbuf->pages++; 112 table->addr = tmpb.addr;
113 if (!i)
114 table->addr |= chunk; /* mark head */
115 table++;
116 *pgtable++ = virt_to_page(tmpb.area);
117 tmpb.area += PAGE_SIZE;
118 tmpb.addr += PAGE_SIZE;
119 }
120 sgbuf->pages += chunk;
121 pages -= chunk;
122 if (chunk < maxpages)
123 maxpages = chunk;
96 } 124 }
97 125
98 sgbuf->size = size; 126 sgbuf->size = size;
99 dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL); 127 dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
100 if (! dmab->area) 128 if (! dmab->area)
101 goto _failed; 129 goto _failed;
130 if (res_size)
131 *res_size = sgbuf->size;
102 return dmab->area; 132 return dmab->area;
103 133
104 _failed: 134 _failed: