Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / sound / core / memalloc.c
diff --git a/kernel/sound/core/memalloc.c b/kernel/sound/core/memalloc.c
new file mode 100644 (file)
index 0000000..082509e
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ *  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_get_named_gen_pool(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);