diff options
-rw-r--r-- | include/sound/memalloc.h | 4 | ||||
-rw-r--r-- | include/sound/pcm.h | 3 | ||||
-rw-r--r-- | sound/core/pcm_memory.c | 26 | ||||
-rw-r--r-- | sound/core/sgbuf.c | 62 |
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 | */ |
98 | static inline dma_addr_t snd_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset) | 98 | static 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 | ||
997 | struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, | 997 | struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, |
998 | unsigned long offset); | 998 | unsigned long offset); |
999 | 999 | unsigned 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 */ |
1002 | static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) | 1003 | static 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 | ||
325 | EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); | 325 | EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); |
326 | 326 | ||
327 | /* | ||
328 | * compute the max chunk size with continuous pages on sg-buffer | ||
329 | */ | ||
330 | unsigned 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 | } | ||
351 | EXPORT_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 | |||
61 | void *snd_malloc_sgbuf_pages(struct device *device, | 65 | void *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: |