Blame view

sound/core/pcm_memory.c 14.4 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
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
  #include <asm/io.h>
  #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
53
  {
  	struct snd_dma_buffer *dmab = &substream->dma_buffer;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
  	/* already reserved? */
  	if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
  		if (dmab->bytes >= size)
  			return 0; /* yes */
  		/* no, free the reserved block */
  		snd_dma_free_pages(dmab);
  		dmab->bytes = 0;
  	}
  
  	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 */
  	return 0;
  }
  
  /*
   * release the preallocated buffer if not yet done.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
79
  static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  {
  	if (substream->dma_buffer.area == NULL)
  		return;
  	if (substream->dma_buf_id)
  		snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id);
  	else
  		snd_dma_free_pages(&substream->dma_buffer);
  	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.
   *
   * Returns zero if successful, or a negative error code on failure.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
98
  int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
  {
  	snd_pcm_lib_preallocate_dma_free(substream);
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
101
  #ifdef CONFIG_SND_VERBOSE_PROCFS
c7132aeb7   Jaroslav Kysela   [ALSA] pcm core: ...
102
103
  	snd_info_free_entry(substream->proc_prealloc_max_entry);
  	substream->proc_prealloc_max_entry = NULL;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
104
  	snd_info_free_entry(substream->proc_prealloc_entry);
e28563cce   Takashi Iwai   [ALSA] Optimize f...
105
  	substream->proc_prealloc_entry = NULL;
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
106
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
114
115
116
117
  	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.
   *
   * Returns zero if successful, or a negative error code on failure.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
118
  int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
120
  	struct snd_pcm_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
124
125
126
127
  	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...
128
  EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
129
  #ifdef CONFIG_SND_VERBOSE_PROCFS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
134
  /*
   * read callback for prealloc proc file
   *
   * prints the current allocated size in kB.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
135
136
  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
137
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
138
  	struct snd_pcm_substream *substream = entry->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
141
142
143
  	snd_iprintf(buffer, "%lu
  ", (unsigned long) substream->dma_buffer.bytes / 1024);
  }
  
  /*
c7132aeb7   Jaroslav Kysela   [ALSA] pcm core: ...
144
145
146
147
148
149
150
151
152
153
154
155
156
   * 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
157
158
159
160
   * write callback for prealloc proc file
   *
   * accepts the preallocation size in kB.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
161
162
  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
163
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
164
  	struct snd_pcm_substream *substream = entry->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
196
197
198
199
200
201
  	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...
202
  static inline void preallocate_info_init(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
204
  	struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
  	if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  		entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
  		entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
bd7bf042e   Takashi Iwai   [ALSA] Fix permis...
209
  		entry->mode |= S_IWUSR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210
211
212
213
214
215
216
  		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: ...
217
218
219
220
221
222
223
224
225
  	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...
226
  }
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
227
  #else /* !CONFIG_SND_VERBOSE_PROCFS */
e28563cce   Takashi Iwai   [ALSA] Optimize f...
228
  #define preallocate_info_init(s)
b7d90a356   Takashi Iwai   [ALSA] Fix Oops a...
229
  #endif /* CONFIG_SND_VERBOSE_PROCFS */
e28563cce   Takashi Iwai   [ALSA] Optimize f...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  
  /*
   * 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
245
246
247
248
249
250
251
252
  	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...
253
   * @data: DMA type dependent data
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
257
258
259
260
261
262
263
264
265
   * @size: the requested pre-allocation size in bytes
   * @max: the max. allowed pre-allocation size
   *
   * Do pre-allocation for the given DMA buffer type.
   *
   * When substream->dma_buf_id is set, the function tries to look for
   * the reserved buffer, and the buffer is not freed but reserved at
   * destruction time.  The dma_buf_id must be unique for all systems
   * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id().
   *
   * Returns zero if successful, or a negative error code on failure.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
266
  int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
268
269
270
271
272
273
  				  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...
274
  EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  /**
25985edce   Lucas De Marchi   Fix common misspe...
276
   * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)
df8db936e   Takashi Iwai   [ALSA] Fix DocBoo...
277
   * @pcm: the pcm instance
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278
   * @type: DMA type (SNDRV_DMA_TYPE_*)
25985edce   Lucas De Marchi   Fix common misspe...
279
   * @data: DMA type dependent data
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
286
287
   * @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.
   *
   * Returns zero if successful, or a negative error code on failure.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
288
  int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
  					  int type, void *data,
  					  size_t size, size_t max)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
292
  	struct snd_pcm_substream *substream;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
294
295
296
297
298
299
300
  	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...
301
  EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
cc6a8acde   Takashi Iwai   ALSA: Fix SG-buff...
302
  #ifdef CONFIG_SND_DMA_SGBUF
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303
304
305
306
307
308
309
310
  /**
   * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
   * @substream: the pcm substream instance
   * @offset: the buffer offset
   *
   * Returns the page struct at the given buffer offset.
   * Used as the page callback of PCM ops.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
311
  struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
313
314
315
316
317
318
319
  {
  	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...
320
  EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
  /*
   * compute the max chunk size with continuous pages on sg-buffer
   */
  unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
  					  unsigned int ofs, unsigned int size)
  {
  	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream);
  	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_pcm_sgbuf_get_chunk_size);
cc6a8acde   Takashi Iwai   ALSA: Fix SG-buff...
346
  #endif /* CONFIG_SND_DMA_SGBUF */
51e9f2e66   Takashi Iwai   ALSA: Allocate la...
347

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
351
352
353
354
355
356
357
358
  /**
   * 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().
   *
   * Returns 1 if the buffer is changed, 0 if not changed, or a negative
   * code on failure.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
359
  int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
361
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
362
  	struct snd_dma_buffer *dmab = NULL;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
363
364
365
366
367
  	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
368
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
369
370
371
372
373
374
375
376
377
378
379
  
  	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...
380
381
  	if (substream->dma_buffer.area != NULL &&
  	    substream->dma_buffer.bytes >= size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
383
  		dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
  	} else {
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
384
  		dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
386
387
388
389
390
391
392
393
394
395
396
397
398
  		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...
399
  EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
402
403
404
405
406
407
  /**
   * 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().
   *
   * Returns zero if successful, or a negative error code on failure.
   */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
408
  int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
410
  	struct snd_pcm_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
412
413
  	if (PCM_RUNTIME_CHECK(substream))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
  	runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
418
419
420
421
422
423
424
  	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...
425
426
  
  EXPORT_SYMBOL(snd_pcm_lib_free_pages);
681b84e17   Clemens Ladisch   sound: pcm: add v...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  
  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()
   */
  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.
   */
  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);