Blame view

sound/core/sgbuf.c 4.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * 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
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
  #include <linux/slab.h>
  #include <linux/mm.h>
  #include <linux/vmalloc.h>
9d069dc00   Takashi Iwai   ALSA: Make snd_sg...
24
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
29
  #include <sound/memalloc.h>
  
  
  /* table entries are align to 32 */
  #define SGBUF_TBL_ALIGN		32
7ab399262   Clemens Ladisch   [ALSA] use the AL...
30
  #define sgbuf_align_table(tbl)	ALIGN((tbl), SGBUF_TBL_ALIGN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
32
33
34
35
36
37
38
39
  
  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;
6af845e4e   Takashi Iwai   ALSA: Fix vunmap ...
40
41
42
  	if (dmab->area)
  		vunmap(dmab->area);
  	dmab->area = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  	tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
  	tmpb.dev.dev = sgbuf->dev;
  	for (i = 0; i < sgbuf->pages; i++) {
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
46
47
  		if (!(sgbuf->table[i].addr & ~PAGE_MASK))
  			continue; /* continuous pages */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  		tmpb.area = sgbuf->table[i].buf;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
49
50
  		tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
  		tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
  		snd_dma_free_pages(&tmpb);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
55
56
57
58
59
60
  
  	kfree(sgbuf->table);
  	kfree(sgbuf->page_table);
  	kfree(sgbuf);
  	dmab->private_data = NULL;
  	
  	return 0;
  }
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
61
  #define MAX_ALLOC_PAGES		32
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
65
66
  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;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
67
  	unsigned int i, pages, chunk, maxpages;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  	struct snd_dma_buffer tmpb;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
69
70
  	struct snd_sg_page *table;
  	struct page **pgtable;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
  
  	dmab->area = NULL;
  	dmab->addr = 0;
59feddb25   Panagiotis Issaris   [ALSA] Conversion...
74
  	dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
  	if (! sgbuf)
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
  	sgbuf->dev = device;
  	pages = snd_sgbuf_aligned_pages(size);
  	sgbuf->tblsize = sgbuf_align_table(pages);
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
80
81
  	table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
  	if (!table)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  		goto _failed;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
83
84
85
  	sgbuf->table = table;
  	pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
  	if (!pgtable)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  		goto _failed;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
87
  	sgbuf->page_table = pgtable;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88

51e9f2e66   Takashi Iwai   ALSA: Allocate la...
89
90
91
92
93
94
95
96
97
98
99
  	/* 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)
c810f9039   Takashi Iwai   ALSA: PCM: Fix po...
100
  				goto _failed;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
101
  			if (!res_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  				goto _failed;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
103
  			size = sgbuf->pages * PAGE_SIZE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
  			break;
  		}
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
  		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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
124
125
126
  	}
  
  	sgbuf->size = size;
  	dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);
  	if (! dmab->area)
  		goto _failed;
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
127
128
  	if (res_size)
  		*res_size = sgbuf->size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
132
133
134
  	return dmab->area;
  
   _failed:
  	snd_free_sgbuf_pages(dmab); /* free the table */
  	return NULL;
  }
9d069dc00   Takashi Iwai   ALSA: Make snd_sg...
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  
  /*
   * compute the max chunk size with continuous pages on sg-buffer
   */
  unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
  				      unsigned int ofs, unsigned int size)
  {
  	struct snd_sg_buf *sg = dmab->private_data;
  	unsigned int start, end, pg;
  
  	start = ofs >> PAGE_SHIFT;
  	end = (ofs + size - 1) >> PAGE_SHIFT;
  	/* check page continuity */
  	pg = sg->table[start].addr >> PAGE_SHIFT;
  	for (;;) {
  		start++;
  		if (start > end)
  			break;
  		pg++;
  		if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
  			return (start << PAGE_SHIFT) - ofs;
  	}
  	/* ok, all on continuous pages */
  	return size;
  }
  EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);