pcm_memory.c 13.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2
/*
 *  Digital Audio (PCM) abstract layer
3
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
Linus Torvalds's avatar
Linus Torvalds committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 *
 *   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
 *
 */

22
#include <linux/io.h>
Linus Torvalds's avatar
Linus Torvalds committed
23 24
#include <linux/time.h>
#include <linux/init.h>
25
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
26
#include <linux/moduleparam.h>
27
#include <linux/vmalloc.h>
28
#include <linux/export.h>
Linus Torvalds's avatar
Linus Torvalds committed
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#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.
 */
51
static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
Linus Torvalds's avatar
Linus Torvalds committed
52 53
{
	struct snd_dma_buffer *dmab = &substream->dma_buffer;
54
	size_t orig_size = size;
Linus Torvalds's avatar
Linus Torvalds committed
55 56 57 58 59 60 61 62 63 64 65 66
	int err;

	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 */
67 68 69 70
	pr_warn("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
		substream->pcm->card->number, substream->pcm->device,
		substream->stream ? 'c' : 'p', substream->number,
		substream->pcm->name, orig_size);
Linus Torvalds's avatar
Linus Torvalds committed
71 72 73 74 75 76
	return 0;
}

/*
 * release the preallocated buffer if not yet done.
 */
77
static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream)
Linus Torvalds's avatar
Linus Torvalds committed
78 79 80
{
	if (substream->dma_buffer.area == NULL)
		return;
81
	snd_dma_free_pages(&substream->dma_buffer);
Linus Torvalds's avatar
Linus Torvalds committed
82 83 84 85 86 87 88 89 90
	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.
 *
91
 * Return: Zero if successful, or a negative error code on failure.
Linus Torvalds's avatar
Linus Torvalds committed
92
 */
93
int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
Linus Torvalds's avatar
Linus Torvalds committed
94 95
{
	snd_pcm_lib_preallocate_dma_free(substream);
96
#ifdef CONFIG_SND_VERBOSE_PROCFS
97 98
	snd_info_free_entry(substream->proc_prealloc_max_entry);
	substream->proc_prealloc_max_entry = NULL;
99
	snd_info_free_entry(substream->proc_prealloc_entry);
100
	substream->proc_prealloc_entry = NULL;
101
#endif
Linus Torvalds's avatar
Linus Torvalds committed
102 103 104 105 106 107 108 109 110
	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.
 *
111
 * Return: Zero if successful, or a negative error code on failure.
Linus Torvalds's avatar
Linus Torvalds committed
112
 */
113
int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
Linus Torvalds's avatar
Linus Torvalds committed
114
{
115
	struct snd_pcm_substream *substream;
Linus Torvalds's avatar
Linus Torvalds committed
116 117 118 119 120 121 122
	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;
}
123 124
EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);

125
#ifdef CONFIG_SND_VERBOSE_PROCFS
Linus Torvalds's avatar
Linus Torvalds committed
126 127 128 129 130
/*
 * read callback for prealloc proc file
 *
 * prints the current allocated size in kB.
 */
131 132
static void snd_pcm_lib_preallocate_proc_read(struct snd_info_entry *entry,
					      struct snd_info_buffer *buffer)
Linus Torvalds's avatar
Linus Torvalds committed
133
{
134
	struct snd_pcm_substream *substream = entry->private_data;
Linus Torvalds's avatar
Linus Torvalds committed
135 136 137
	snd_iprintf(buffer, "%lu\n", (unsigned long) substream->dma_buffer.bytes / 1024);
}

138 139 140 141 142 143 144 145 146 147 148 149
/*
 * 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\n", (unsigned long) substream->dma_max / 1024);
}

Linus Torvalds's avatar
Linus Torvalds committed
150 151 152 153 154
/*
 * write callback for prealloc proc file
 *
 * accepts the preallocation size in kB.
 */
155 156
static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
					       struct snd_info_buffer *buffer)
Linus Torvalds's avatar
Linus Torvalds committed
157
{
158
	struct snd_pcm_substream *substream = entry->private_data;
Linus Torvalds's avatar
Linus Torvalds committed
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 196
	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;
	}
}

197
static inline void preallocate_info_init(struct snd_pcm_substream *substream)
Linus Torvalds's avatar
Linus Torvalds committed
198
{
199
	struct snd_info_entry *entry;
Linus Torvalds's avatar
Linus Torvalds committed
200 201 202 203

	if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
		entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
		entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
204
		entry->mode |= 0200;
Linus Torvalds's avatar
Linus Torvalds committed
205 206 207 208 209 210 211
		entry->private_data = substream;
		if (snd_info_register(entry) < 0) {
			snd_info_free_entry(entry);
			entry = NULL;
		}
	}
	substream->proc_prealloc_entry = entry;
212 213 214 215 216 217 218 219 220
	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;
221 222
}

223
#else /* !CONFIG_SND_VERBOSE_PROCFS */
224
#define preallocate_info_init(s)
225
#endif /* CONFIG_SND_VERBOSE_PROCFS */
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240

/*
 * 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);
Linus Torvalds's avatar
Linus Torvalds committed
241 242 243 244 245 246 247 248
	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_*)
Lucas De Marchi's avatar
Lucas De Marchi committed
249
 * @data: DMA type dependent data
Linus Torvalds's avatar
Linus Torvalds committed
250 251 252 253 254
 * @size: the requested pre-allocation size in bytes
 * @max: the max. allowed pre-allocation size
 *
 * Do pre-allocation for the given DMA buffer type.
 *
255
 * Return: Zero if successful, or a negative error code on failure.
Linus Torvalds's avatar
Linus Torvalds committed
256
 */
257
int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
Linus Torvalds's avatar
Linus Torvalds committed
258 259 260 261 262 263 264
				  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);
}
265 266
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);

Linus Torvalds's avatar
Linus Torvalds committed
267
/**
Lucas De Marchi's avatar
Lucas De Marchi committed
268
 * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continuous memory type (all substreams)
Takashi Iwai's avatar
Takashi Iwai committed
269
 * @pcm: the pcm instance
Linus Torvalds's avatar
Linus Torvalds committed
270
 * @type: DMA type (SNDRV_DMA_TYPE_*)
Lucas De Marchi's avatar
Lucas De Marchi committed
271
 * @data: DMA type dependent data
Linus Torvalds's avatar
Linus Torvalds committed
272 273 274 275 276 277
 * @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.
 *
278
 * Return: Zero if successful, or a negative error code on failure.
Linus Torvalds's avatar
Linus Torvalds committed
279
 */
280
int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
Linus Torvalds's avatar
Linus Torvalds committed
281 282 283
					  int type, void *data,
					  size_t size, size_t max)
{
284
	struct snd_pcm_substream *substream;
Linus Torvalds's avatar
Linus Torvalds committed
285 286 287 288 289 290 291 292
	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;
}
293 294
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);

295
#ifdef CONFIG_SND_DMA_SGBUF
Linus Torvalds's avatar
Linus Torvalds committed
296 297 298 299 300 301
/**
 * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
 * @substream: the pcm substream instance
 * @offset: the buffer offset
 *
 * Used as the page callback of PCM ops.
302 303
 *
 * Return: The page struct at the given buffer offset. %NULL on failure.
Linus Torvalds's avatar
Linus Torvalds committed
304
 */
305
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
Linus Torvalds's avatar
Linus Torvalds committed
306 307 308 309 310 311 312 313
{
	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];
}
314
EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
315
#endif /* CONFIG_SND_DMA_SGBUF */
316

Linus Torvalds's avatar
Linus Torvalds committed
317 318 319 320 321 322 323 324
/**
 * 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().
 *
325
 * Return: 1 if the buffer is changed, 0 if not changed, or a negative
Linus Torvalds's avatar
Linus Torvalds committed
326 327
 * code on failure.
 */
328
int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
Linus Torvalds's avatar
Linus Torvalds committed
329
{
330
	struct snd_pcm_runtime *runtime;
Linus Torvalds's avatar
Linus Torvalds committed
331 332
	struct snd_dma_buffer *dmab = NULL;

333 334 335 336 337
	if (PCM_RUNTIME_CHECK(substream))
		return -EINVAL;
	if (snd_BUG_ON(substream->dma_buffer.dev.type ==
		       SNDRV_DMA_TYPE_UNKNOWN))
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
338 339 340 341 342 343 344 345 346 347 348 349
	runtime = substream->runtime;

	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);
	}
350 351
	if (substream->dma_buffer.area != NULL &&
	    substream->dma_buffer.bytes >= size) {
Linus Torvalds's avatar
Linus Torvalds committed
352 353
		dmab = &substream->dma_buffer; /* use the pre-allocated buffer */
	} else {
354
		dmab = kzalloc(sizeof(*dmab), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
355 356 357 358 359 360 361 362 363 364 365 366 367 368
		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 */
}
369 370
EXPORT_SYMBOL(snd_pcm_lib_malloc_pages);

Linus Torvalds's avatar
Linus Torvalds committed
371 372 373 374 375 376
/**
 * 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().
 *
377
 * Return: Zero if successful, or a negative error code on failure.
Linus Torvalds's avatar
Linus Torvalds committed
378
 */
379
int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
Linus Torvalds's avatar
Linus Torvalds committed
380
{
381
	struct snd_pcm_runtime *runtime;
Linus Torvalds's avatar
Linus Torvalds committed
382

383 384
	if (PCM_RUNTIME_CHECK(substream))
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387 388 389 390 391 392 393 394 395
	runtime = substream->runtime;
	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;
}
396
EXPORT_SYMBOL(snd_pcm_lib_free_pages);
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422

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()
423 424
 *
 * Return: Zero if successful, or a negative error code on failure.
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
 */
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.
446 447
 *
 * Return: The page struct, or %NULL on failure.
448 449 450 451 452 453 454
 */
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);