Blame view

sound/core/pcm_memory.c 13.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  Digital Audio (PCM) abstract layer
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
3
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   *
   *
   *   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
   *
   */
6cbbfe1c8   Takashi Iwai   ALSA: Include lin...
21
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
  #include <linux/time.h>
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
24
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #include <linux/moduleparam.h>
ee7c343c0   Takashi Iwai   ALSA: pcm - Add m...
26
  #include <linux/vmalloc.h>
d81a6d717   Paul Gortmaker   sound: Add export...
27
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  #include <sound/core.h>
  #include <sound/pcm.h>
  #include <sound/info.h>
  #include <sound/initval.h>
  
  static int preallocate_dma = 1;
  module_param(preallocate_dma, int, 0444);
  MODULE_PARM_DESC(preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized.");
  
  static int maximum_substreams = 4;
  module_param(maximum_substreams, int, 0444);
  MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory.");
  
  static const size_t snd_minimum_buffer = 16384;
  
  
  /*
   * try to allocate as the large pages as possible.
   * stores the resultant memory size in *res_size.
   *
   * the minimum size is snd_minimum_buffer.  it should be power of 2.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
50
  static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
  {
  	struct snd_dma_buffer *dmab = &substream->dma_buffer;
6ab08ced6   Takashi Iwai   ALSA: PCM: Warn w...
53
  	size_t orig_size = size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
59
60
61
62
63
64
  	do {
  		if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
  					       size, dmab)) < 0) {
  			if (err != -ENOMEM)
  				return err; /* fatal error */
  		} else
  			return 0;
  		size >>= 1;
  	} while (size >= snd_minimum_buffer);
  	dmab->bytes = 0; /* tell error */
6ab08ced6   Takashi Iwai   ALSA: PCM: Warn w...
65
66
67
68
69
  	pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu
  ",
  		substream->pcm->card->number, substream->pcm->device,
  		substream->stream ? 'c' : 'p', substream->number,
  		substream->pcm->name, orig_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
  	return 0;
  }
  
  /*
   * release the preallocated buffer if not yet done.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
76
  static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
  {
  	if (substream->dma_buffer.area == NULL)
  		return;
47d98c026   Takashi Iwai   ALSA: Remove memo...
80
  	snd_dma_free_pages(&substream->dma_buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
  	substream->dma_buffer.area = NULL;
  }
  
  /**
   * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream.
   * @substream: the pcm substream instance
   *
   * Releases the pre-allocated buffer of the given substream.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
90
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
92
  int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93
94
  {
  	snd_pcm_lib_preallocate_dma_free(substream);
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
95
  #ifdef CONFIG_SND_VERBOSE_PROCFS
c7132aeb7   Jaroslav Kysela   [ALSA] pcm core: ...
96
97
  	snd_info_free_entry(substream->proc_prealloc_max_entry);
  	substream->proc_prealloc_max_entry = NULL;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
98
  	snd_info_free_entry(substream->proc_prealloc_entry);
e28563cce   Takashi Iwai   [ALSA] Optimize f...
99
  	substream->proc_prealloc_entry = NULL;
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
100
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
105
106
107
108
109
  	return 0;
  }
  
  /**
   * snd_pcm_lib_preallocate_free_for_all - release all pre-allocated buffers on the pcm
   * @pcm: the pcm instance
   *
   * Releases all the pre-allocated buffers on the given pcm.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
110
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
112
  int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
114
  	struct snd_pcm_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
  	int stream;
  
  	for (stream = 0; stream < 2; stream++)
  		for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
  			snd_pcm_lib_preallocate_free(substream);
  	return 0;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
122
  EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
123
  #ifdef CONFIG_SND_VERBOSE_PROCFS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
  /*
   * read callback for prealloc proc file
   *
   * prints the current allocated size in kB.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
129
130
  static void snd_pcm_lib_preallocate_proc_read(struct snd_info_entry *entry,
  					      struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
132
  	struct snd_pcm_substream *substream = entry->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
  	snd_iprintf(buffer, "%lu
  ", (unsigned long) substream->dma_buffer.bytes / 1024);
  }
  
  /*
c7132aeb7   Jaroslav Kysela   [ALSA] pcm core: ...
138
139
140
141
142
143
144
145
146
147
148
149
150
   * read callback for prealloc_max proc file
   *
   * prints the maximum allowed size in kB.
   */
  static void snd_pcm_lib_preallocate_max_proc_read(struct snd_info_entry *entry,
  						  struct snd_info_buffer *buffer)
  {
  	struct snd_pcm_substream *substream = entry->private_data;
  	snd_iprintf(buffer, "%lu
  ", (unsigned long) substream->dma_max / 1024);
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
153
154
   * write callback for prealloc proc file
   *
   * accepts the preallocation size in kB.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
155
156
  static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
  					       struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
158
  	struct snd_pcm_substream *substream = entry->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  	char line[64], str[64];
  	size_t size;
  	struct snd_dma_buffer new_dmab;
  
  	if (substream->runtime) {
  		buffer->error = -EBUSY;
  		return;
  	}
  	if (!snd_info_get_line(buffer, line, sizeof(line))) {
  		snd_info_get_str(str, line, sizeof(str));
  		size = simple_strtoul(str, NULL, 10) * 1024;
  		if ((size != 0 && size < 8192) || size > substream->dma_max) {
  			buffer->error = -EINVAL;
  			return;
  		}
  		if (substream->dma_buffer.bytes == size)
  			return;
  		memset(&new_dmab, 0, sizeof(new_dmab));
  		new_dmab.dev = substream->dma_buffer.dev;
  		if (size > 0) {
  			if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
  						substream->dma_buffer.dev.dev,
  						size, &new_dmab) < 0) {
  				buffer->error = -ENOMEM;
  				return;
  			}
  			substream->buffer_bytes_max = size;
  		} else {
  			substream->buffer_bytes_max = UINT_MAX;
  		}
  		if (substream->dma_buffer.area)
  			snd_dma_free_pages(&substream->dma_buffer);
  		substream->dma_buffer = new_dmab;
  	} else {
  		buffer->error = -EINVAL;
  	}
  }
e28563cce   Takashi Iwai   [ALSA] Optimize f...
196
  static inline void preallocate_info_init(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
198
  	struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  	if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  		entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  		entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
bd7bf042e   Takashi Iwai   [ALSA] Fix permis...
203
  		entry->mode |= S_IWUSR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
206
207
208
209
210
  		entry->private_data = substream;
  		if (snd_info_register(entry) < 0) {
  			snd_info_free_entry(entry);
  			entry = NULL;
  		}
  	}
  	substream->proc_prealloc_entry = entry;
c7132aeb7   Jaroslav Kysela   [ALSA] pcm core: ...
211
212
213
214
215
216
217
218
219
  	if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) {
  		entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read;
  		entry->private_data = substream;
  		if (snd_info_register(entry) < 0) {
  			snd_info_free_entry(entry);
  			entry = NULL;
  		}
  	}
  	substream->proc_prealloc_max_entry = entry;
e28563cce   Takashi Iwai   [ALSA] Optimize f...
220
  }
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
221
  #else /* !CONFIG_SND_VERBOSE_PROCFS */
e28563cce   Takashi Iwai   [ALSA] Optimize f...
222
  #define preallocate_info_init(s)
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
223
  #endif /* CONFIG_SND_VERBOSE_PROCFS */
e28563cce   Takashi Iwai   [ALSA] Optimize f...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  
  /*
   * pre-allocate the buffer and create a proc file for the substream
   */
  static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
  					  size_t size, size_t max)
  {
  
  	if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
  		preallocate_pcm_pages(substream, size);
  
  	if (substream->dma_buffer.bytes > 0)
  		substream->buffer_bytes_max = substream->dma_buffer.bytes;
  	substream->dma_max = max;
  	preallocate_info_init(substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
243
244
245
246
  	return 0;
  }
  
  
  /**
   * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
   * @substream: the pcm substream instance
   * @type: DMA type (SNDRV_DMA_TYPE_*)
25985edce   Lucas De Marchi   Fix common misspe...
247
   * @data: DMA type dependent data
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
251
252
   * @size: the requested pre-allocation size in bytes
   * @max: the max. allowed pre-allocation size
   *
   * Do pre-allocation for the given DMA buffer type.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
253
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
255
  int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
260
261
262
  				  int type, struct device *data,
  				  size_t size, size_t max)
  {
  	substream->dma_buffer.dev.type = type;
  	substream->dma_buffer.dev.dev = data;
  	return snd_pcm_lib_preallocate_pages1(substream, size, max);
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
263
  EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  /**
25985edce   Lucas De Marchi   Fix common misspe...
265
   * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
266
   * @pcm: the pcm instance
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
   * @type: DMA type (SNDRV_DMA_TYPE_*)
25985edce   Lucas De Marchi   Fix common misspe...
268
   * @data: DMA type dependent data
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
271
272
273
274
   * @size: the requested pre-allocation size in bytes
   * @max: the max. allowed pre-allocation size
   *
   * Do pre-allocation to all substreams of the given pcm for the
   * specified DMA type.
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
275
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
277
  int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
279
280
  					  int type, void *data,
  					  size_t size, size_t max)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
281
  	struct snd_pcm_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
  	int stream, err;
  
  	for (stream = 0; stream < 2; stream++)
  		for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
  			if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0)
  				return err;
  	return 0;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
290
  EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
cc6a8acde   Takashi Iwai   ALSA: Fix SG-buff...
291
  #ifdef CONFIG_SND_DMA_SGBUF
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
296
  /**
   * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
   * @substream: the pcm substream instance
   * @offset: the buffer offset
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
   * Used as the page callback of PCM ops.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
298
299
   *
   * Return: The page struct at the given buffer offset. %NULL on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
301
  struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
  {
  	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
  
  	unsigned int idx = offset >> PAGE_SHIFT;
  	if (idx >= (unsigned int)sgbuf->pages)
  		return NULL;
  	return sgbuf->page_table[idx];
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
310
  EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
cc6a8acde   Takashi Iwai   ALSA: Fix SG-buff...
311
  #endif /* CONFIG_SND_DMA_SGBUF */
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
312

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
319
320
  /**
   * snd_pcm_lib_malloc_pages - allocate the DMA buffer
   * @substream: the substream to allocate the DMA buffer to
   * @size: the requested buffer size in bytes
   *
   * Allocates the DMA buffer on the BUS type given earlier to
   * snd_pcm_lib_preallocate_xxx_pages().
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
321
   * Return: 1 if the buffer is changed, 0 if not changed, or a negative
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
   * code on failure.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
324
  int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
326
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
  	struct snd_dma_buffer *dmab = NULL;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
328
329
330
331
332
  	if (PCM_RUNTIME_CHECK(substream))
  		return -EINVAL;
  	if (snd_BUG_ON(substream->dma_buffer.dev.type ==
  		       SNDRV_DMA_TYPE_UNKNOWN))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
340
341
342
343
344
  
  	if (runtime->dma_buffer_p) {
  		/* perphaps, we might free the large DMA memory region
  		   to save some space here, but the actual solution
  		   costs us less time */
  		if (runtime->dma_buffer_p->bytes >= size) {
  			runtime->dma_bytes = size;
  			return 0;	/* ok, do not change */
  		}
  		snd_pcm_lib_free_pages(substream);
  	}
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
345
346
  	if (substream->dma_buffer.area != NULL &&
  	    substream->dma_buffer.bytes >= size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
  		dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
  	} else {
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
349
  		dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  		if (! dmab)
  			return -ENOMEM;
  		dmab->dev = substream->dma_buffer.dev;
  		if (snd_dma_alloc_pages(substream->dma_buffer.dev.type,
  					substream->dma_buffer.dev.dev,
  					size, dmab) < 0) {
  			kfree(dmab);
  			return -ENOMEM;
  		}
  	}
  	snd_pcm_set_runtime_buffer(substream, dmab);
  	runtime->dma_bytes = size;
  	return 1;			/* area was changed */
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
364
  EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
366
367
368
369
370
  /**
   * snd_pcm_lib_free_pages - release the allocated DMA buffer.
   * @substream: the substream to release the DMA buffer
   *
   * Releases the DMA buffer allocated via snd_pcm_lib_malloc_pages().
   *
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
371
   * Return: Zero if successful, or a negative error code on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
373
  int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
375
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
377
378
  	if (PCM_RUNTIME_CHECK(substream))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
386
387
388
389
  	if (runtime->dma_area == NULL)
  		return 0;
  	if (runtime->dma_buffer_p != &substream->dma_buffer) {
  		/* it's a newly allocated buffer.  release it now. */
  		snd_dma_free_pages(runtime->dma_buffer_p);
  		kfree(runtime->dma_buffer_p);
  	}
  	snd_pcm_set_runtime_buffer(substream, NULL);
  	return 0;
  }
e88e8ae63   Takashi Iwai   [ALSA] Move OSS-s...
390
  EXPORT_SYMBOL(snd_pcm_lib_free_pages);
681b84e17   Clemens Ladisch   sound: pcm: add v...
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
  
  int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
  				      size_t size, gfp_t gfp_flags)
  {
  	struct snd_pcm_runtime *runtime;
  
  	if (PCM_RUNTIME_CHECK(substream))
  		return -EINVAL;
  	runtime = substream->runtime;
  	if (runtime->dma_area) {
  		if (runtime->dma_bytes >= size)
  			return 0; /* already large enough */
  		vfree(runtime->dma_area);
  	}
  	runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
  	if (!runtime->dma_area)
  		return -ENOMEM;
  	runtime->dma_bytes = size;
  	return 1;
  }
  EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer);
  
  /**
   * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer
   * @substream: the substream with a buffer allocated by
   *	snd_pcm_lib_alloc_vmalloc_buffer()
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
417
418
   *
   * Return: Zero if successful, or a negative error code on failure.
681b84e17   Clemens Ladisch   sound: pcm: add v...
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
   */
  int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_runtime *runtime;
  
  	if (PCM_RUNTIME_CHECK(substream))
  		return -EINVAL;
  	runtime = substream->runtime;
  	vfree(runtime->dma_area);
  	runtime->dma_area = NULL;
  	return 0;
  }
  EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer);
  
  /**
   * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct
   * @substream: the substream with a buffer allocated by
   *	snd_pcm_lib_alloc_vmalloc_buffer()
   * @offset: offset in the buffer
   *
   * This function is to be used as the page callback in the PCM ops.
eb7c06e8e   Yacine Belkadi   ALSA: add/change ...
440
441
   *
   * Return: The page struct, or %NULL on failure.
681b84e17   Clemens Ladisch   sound: pcm: add v...
442
443
444
445
446
447
448
   */
  struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
  					  unsigned long offset)
  {
  	return vmalloc_to_page(substream->runtime->dma_area + offset);
  }
  EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page);