123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- /*
- * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
- * Takashi Iwai <tiwai@suse.de>
- *
- * Generic memory allocators
- *
- *
- * 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
- *
- */
- #include <linux/slab.h>
- #include <linux/mm.h>
- #include <linux/dma-mapping.h>
- #include <linux/genalloc.h>
- #include <sound/memalloc.h>
- /*
- *
- * Generic memory allocators
- *
- */
- /**
- * snd_malloc_pages - allocate pages with the given size
- * @size: the size to allocate in bytes
- * @gfp_flags: the allocation conditions, GFP_XXX
- *
- * Allocates the physically contiguous pages with the given size.
- *
- * Return: The pointer of the buffer, or %NULL if no enough memory.
- */
- void *snd_malloc_pages(size_t size, gfp_t gfp_flags)
- {
- int pg;
- if (WARN_ON(!size))
- return NULL;
- if (WARN_ON(!gfp_flags))
- return NULL;
- gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */
- pg = get_order(size);
- return (void *) __get_free_pages(gfp_flags, pg);
- }
- /**
- * snd_free_pages - release the pages
- * @ptr: the buffer pointer to release
- * @size: the allocated buffer size
- *
- * Releases the buffer allocated via snd_malloc_pages().
- */
- void snd_free_pages(void *ptr, size_t size)
- {
- int pg;
- if (ptr == NULL)
- return;
- pg = get_order(size);
- free_pages((unsigned long) ptr, pg);
- }
- /*
- *
- * Bus-specific memory allocators
- *
- */
- #ifdef CONFIG_HAS_DMA
- /* allocate the coherent DMA pages */
- static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
- {
- int pg;
- gfp_t gfp_flags;
- if (WARN_ON(!dma))
- return NULL;
- pg = get_order(size);
- gfp_flags = GFP_KERNEL
- | __GFP_COMP /* compound page lets parts be mapped */
- | __GFP_NORETRY /* don't trigger OOM-killer */
- | __GFP_NOWARN; /* no stack trace print - this call is non-critical */
- return dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags);
- }
- /* free the coherent DMA pages */
- static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,
- dma_addr_t dma)
- {
- int pg;
- if (ptr == NULL)
- return;
- pg = get_order(size);
- dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
- }
- #ifdef CONFIG_GENERIC_ALLOCATOR
- /**
- * snd_malloc_dev_iram - allocate memory from on-chip internal ram
- * @dmab: buffer allocation record to store the allocated data
- * @size: number of bytes to allocate from the iram
- *
- * This function requires iram phandle provided via of_node
- */
- static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
- {
- struct device *dev = dmab->dev.dev;
- struct gen_pool *pool = NULL;
- dmab->area = NULL;
- dmab->addr = 0;
- if (dev->of_node)
- pool = of_gen_pool_get(dev->of_node, "iram", 0);
- if (!pool)
- return;
- /* Assign the pool into private_data field */
- dmab->private_data = pool;
- dmab->area = gen_pool_dma_alloc(pool, size, &dmab->addr);
- }
- /**
- * snd_free_dev_iram - free allocated specific memory from on-chip internal ram
- * @dmab: buffer allocation record to store the allocated data
- */
- static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
- {
- struct gen_pool *pool = dmab->private_data;
- if (pool && dmab->area)
- gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
- }
- #endif /* CONFIG_GENERIC_ALLOCATOR */
- #endif /* CONFIG_HAS_DMA */
- /*
- *
- * ALSA generic memory management
- *
- */
- /**
- * snd_dma_alloc_pages - allocate the buffer area according to the given type
- * @type: the DMA buffer type
- * @device: the device pointer
- * @size: the buffer size to allocate
- * @dmab: buffer allocation record to store the allocated data
- *
- * Calls the memory-allocator function for the corresponding
- * buffer type.
- *
- * Return: Zero if the buffer with the given size is allocated successfully,
- * otherwise a negative value on error.
- */
- int snd_dma_alloc_pages(int type, struct device *device, size_t size,
- struct snd_dma_buffer *dmab)
- {
- if (WARN_ON(!size))
- return -ENXIO;
- if (WARN_ON(!dmab))
- return -ENXIO;
- dmab->dev.type = type;
- dmab->dev.dev = device;
- dmab->bytes = 0;
- switch (type) {
- case SNDRV_DMA_TYPE_CONTINUOUS:
- dmab->area = snd_malloc_pages(size,
- (__force gfp_t)(unsigned long)device);
- dmab->addr = 0;
- break;
- #ifdef CONFIG_HAS_DMA
- #ifdef CONFIG_GENERIC_ALLOCATOR
- case SNDRV_DMA_TYPE_DEV_IRAM:
- snd_malloc_dev_iram(dmab, size);
- if (dmab->area)
- break;
- /* Internal memory might have limited size and no enough space,
- * so if we fail to malloc, try to fetch memory traditionally.
- */
- dmab->dev.type = SNDRV_DMA_TYPE_DEV;
- #endif /* CONFIG_GENERIC_ALLOCATOR */
- case SNDRV_DMA_TYPE_DEV:
- dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
- break;
- #endif
- #ifdef CONFIG_SND_DMA_SGBUF
- case SNDRV_DMA_TYPE_DEV_SG:
- snd_malloc_sgbuf_pages(device, size, dmab, NULL);
- break;
- #endif
- default:
- pr_err("snd-malloc: invalid device type %d\n", type);
- dmab->area = NULL;
- dmab->addr = 0;
- return -ENXIO;
- }
- if (! dmab->area)
- return -ENOMEM;
- dmab->bytes = size;
- return 0;
- }
- /**
- * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
- * @type: the DMA buffer type
- * @device: the device pointer
- * @size: the buffer size to allocate
- * @dmab: buffer allocation record to store the allocated data
- *
- * Calls the memory-allocator function for the corresponding
- * buffer type. When no space is left, this function reduces the size and
- * tries to allocate again. The size actually allocated is stored in
- * res_size argument.
- *
- * Return: Zero if the buffer with the given size is allocated successfully,
- * otherwise a negative value on error.
- */
- int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
- struct snd_dma_buffer *dmab)
- {
- int err;
- while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) {
- size_t aligned_size;
- if (err != -ENOMEM)
- return err;
- if (size <= PAGE_SIZE)
- return -ENOMEM;
- aligned_size = PAGE_SIZE << get_order(size);
- if (size != aligned_size)
- size = aligned_size;
- else
- size >>= 1;
- }
- if (! dmab->area)
- return -ENOMEM;
- return 0;
- }
- /**
- * snd_dma_free_pages - release the allocated buffer
- * @dmab: the buffer allocation record to release
- *
- * Releases the allocated buffer via snd_dma_alloc_pages().
- */
- void snd_dma_free_pages(struct snd_dma_buffer *dmab)
- {
- switch (dmab->dev.type) {
- case SNDRV_DMA_TYPE_CONTINUOUS:
- snd_free_pages(dmab->area, dmab->bytes);
- break;
- #ifdef CONFIG_HAS_DMA
- #ifdef CONFIG_GENERIC_ALLOCATOR
- case SNDRV_DMA_TYPE_DEV_IRAM:
- snd_free_dev_iram(dmab);
- break;
- #endif /* CONFIG_GENERIC_ALLOCATOR */
- case SNDRV_DMA_TYPE_DEV:
- snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
- break;
- #endif
- #ifdef CONFIG_SND_DMA_SGBUF
- case SNDRV_DMA_TYPE_DEV_SG:
- snd_free_sgbuf_pages(dmab);
- break;
- #endif
- default:
- pr_err("snd-malloc: invalid device type %d\n", dmab->dev.type);
- }
- }
- /*
- * exports
- */
- EXPORT_SYMBOL(snd_dma_alloc_pages);
- EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
- EXPORT_SYMBOL(snd_dma_free_pages);
- EXPORT_SYMBOL(snd_malloc_pages);
- EXPORT_SYMBOL(snd_free_pages);
|