This is the first shot at this - I've tested it on ARM, covering both
ISA ALSA devices on a PCI machine, and driver model devices on a non-
PCI, non-ISA machine. However, it needs more testing. Can people
on alsa-devel please test these patches.
This patch adds support for the generic device/driver model to ALSA
for the sole purpose of supporting their DMA mapping functionality.
This patch changes snd_malloc_sgbuf_pages() to use this dma mapping
functionality.
diff -urpN orig/sound/core/Makefile linux/sound/core/Makefile
--- orig/sound/core/Makefile Wed Feb 18 22:35:45 2004
+++ linux/sound/core/Makefile Sun Feb 29 16:37:15 2004
@@ -15,10 +15,7 @@ endif
snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
pcm_memory.o
-snd-page-alloc-objs := memalloc.o
-ifeq ($(CONFIG_PCI),y)
-snd-page-alloc-objs += sgbuf.o
-endif
+snd-page-alloc-objs := memalloc.o sgbuf.o
snd-rawmidi-objs := rawmidi.o
snd-timer-objs := timer.o
diff -urpN orig/sound/core/memalloc.c linux/sound/core/memalloc.c
--- orig/sound/core/memalloc.c Wed Feb 18 22:35:45 2004
+++ linux/sound/core/memalloc.c Sun Feb 29 17:32:54 2004
@@ -157,6 +157,9 @@ static int compare_device(const struct s
case SNDRV_DMA_TYPE_SBUS:
return a->dev.sbus == b->dev.sbus;
#endif
+ case SNDRV_DMA_TYPE_DEV:
+ case SNDRV_DMA_TYPE_DEV_SG:
+ return a->dev.dev == b->dev.dev;
}
return 0;
}
@@ -196,7 +199,7 @@ int snd_dma_alloc_pages(const struct snd
dmab->area = snd_malloc_pci_pages(dev->dev.pci, size, &dmab->addr);
break;
case SNDRV_DMA_TYPE_PCI_SG:
- snd_malloc_sgbuf_pages(dev->dev.pci, size, dmab);
+ snd_malloc_sgbuf_pages(&dev->dev.pci->dev, size, dmab);
break;
#endif
#ifdef CONFIG_SBUS
@@ -204,6 +207,12 @@ int snd_dma_alloc_pages(const struct snd
dmab->area = snd_malloc_sbus_pages(dev->dev.sbus, size, &dmab->addr);
break;
#endif
+ case SNDRV_DMA_TYPE_DEV:
+ dmab->area = snd_malloc_dev_pages(dev->dev.dev, size, &dmab->addr);
+ break;
+ case SNDRV_DMA_TYPE_DEV_SG:
+ snd_malloc_sgbuf_pages(dev->dev.dev, size, dmab);
+ break;
default:
printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type);
dmab->area = NULL;
@@ -248,6 +257,12 @@ void snd_dma_free_pages(const struct snd
snd_free_sbus_pages(dev->dev.sbus, dmab->bytes, dmab->area,
dmab->addr);
break;
#endif
+ case SNDRV_DMA_TYPE_DEV:
+ snd_free_dev_pages(dev->dev.dev, dmab->bytes, dmab->area, dmab->addr);
+ break;
+ case SNDRV_DMA_TYPE_DEV_SG:
+ snd_free_sgbuf_pages(dmab);
+ break;
default:
printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type);
}
@@ -492,6 +507,104 @@ void snd_free_pages(void *ptr, size_t si
free_pages((unsigned long) ptr, pg);
}
+/**
+ * snd_malloc_dev_pages - allocate pages for a device with the given size
+ * @dev: the device pointer
+ * @size: the size to allocate in bytes
+ * @dma: the pointer to store the DMA address of the buffer
+ *
+ * Allocates the physically contiguous pages with the given size for
+ * the device.
+ *
+ * Returns the pointer of the buffer, or NULL if not enough memory.
+ */
+void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma)
+{
+ int pg;
+ void *res;
+
+ snd_assert(size > 0, return NULL);
+ snd_assert(dma != NULL, return NULL);
+ pg = get_order(size);
+ res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, GFP_KERNEL);
+ if (res != NULL)
+ mark_pages(res, pg);
+
+ return res;
+}
+EXPORT_SYMBOL(snd_malloc_dev_pages);
+
+/**
+ * snd_malloc_dev_page - allocate a page in the valid device dma mask
+ * @dev: device pointer
+ * @addrp: the pointer to store the DMA address of the buffer
+ *
+ * Allocates a single page for the given device and returns
+ * the virtual address and stores the physical address on addrp.
+ *
+ * This function cannot be called from interrupt handlers or
+ * within spinlocks.
+ */
+void *snd_malloc_dev_page(struct device *dev, dma_addr_t *addrp)
+{
+ return snd_malloc_dev_pages(dev, PAGE_SIZE, addrp);
+}
+EXPORT_SYMBOL(snd_malloc_dev_page);
+
+/**
+ * snd_malloc_dev_pages_fallback - allocate pages with the given size with fallback
for a device
+ * @dev: device pointer
+ * @size: the requested size to allocate in bytes
+ * @dma: the pointer to store the DMA address of the buffer
+ * @res_size: the pointer to store the size of buffer actually allocated
+ *
+ * Allocates the physically contiguous pages with the given request
+ * size for a device. 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.
+ *
+ * Returns the pointer of the buffer, or NULL if no enoguh memory.
+ */
+void *snd_malloc_dev_pages_fallback(struct device *dev, size_t size,
+ dma_addr_t *dma, size_t *res_size)
+{
+ void *res;
+
+ snd_assert(res_size != NULL, return NULL);
+ do {
+ if ((res = snd_malloc_dev_pages(dev, size, dma)) != NULL) {
+ *res_size = size;
+ return res;
+ }
+ size >>= 1;
+ } while (size >= PAGE_SIZE);
+ return NULL;
+}
+EXPORT_SYMBOL(snd_malloc_dev_pages_fallback);
+
+/**
+ * snd_free_dev_pages - release the pages
+ * @dev: device pointer
+ * @size: the allocated buffer size
+ * @ptr: the CPU address of buffer to release
+ * @dma: the DMA address of the buffer
+ *
+ * Releases the buffer allocated via snd_malloc_dev_pages().
+ */
+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);
+ unmark_pages(ptr, pg);
+ dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma);
+}
+EXPORT_SYMBOL(snd_free_dev_pages);
+
+
#if defined(CONFIG_ISA) && ! defined(CONFIG_PCI)
/**
@@ -925,6 +1038,14 @@ static int snd_mem_proc_read(char *page,
len += sprintf(page + len, "SBUS [%x]",
mem->dev.dev.sbus->slot);
break;
#endif
+ case SNDRV_DMA_TYPE_DEV:
+ case SNDRV_DMA_TYPE_DEV_SG:
+ if (mem->dev.dev.dev) {
+ len += sprintf(page + len, "%s [%s]",
+ mem->dev.type == SNDRV_DMA_TYPE_DEV_SG
? "DEV" : "DEV-SG",
+ mem->dev.dev.dev->bus_id);
+ }
+ break;
default:
len += sprintf(page + len, "UNKNOWN");
break;
diff -urpN orig/sound/core/pcm_lib.c linux/sound/core/pcm_lib.c
--- orig/sound/core/pcm_lib.c Wed Feb 18 22:35:45 2004
+++ linux/sound/core/pcm_lib.c Sun Feb 29 15:48:13 2004
@@ -2670,7 +2670,6 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pc
EXPORT_SYMBOL(snd_pcm_lib_preallocate_pci_pages_for_all);
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages);
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sg_pages_for_all);
-EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
#endif
#ifdef CONFIG_SBUS
EXPORT_SYMBOL(snd_pcm_lib_preallocate_sbus_pages);
diff -urpN orig/sound/core/pcm_memory.c linux/sound/core/pcm_memory.c
--- orig/sound/core/pcm_memory.c Sun Feb 29 19:27:59 2004
+++ linux/sound/core/pcm_memory.c Sun Feb 29 19:27:13 2004
@@ -275,6 +275,129 @@ int snd_pcm_lib_preallocate_pages_for_al
return 0;
}
+/**
+ * snd_pcm_lib_preallocate_dev_pages - pre-allocation for a device
+ *
+ * @dev: device
+ * @substream: substream to assign the buffer
+ * @size: the requested pre-allocation size in bytes
+ * @max: max. buffer size acceptable for the changes via proc file
+ *
+ * Do pre-allocation for the device.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_dev_pages(struct device *dev,
+ snd_pcm_substream_t *substream,
+ size_t size, size_t max)
+{
+ substream->dma_device.type = SNDRV_DMA_TYPE_DEV;
+ substream->dma_device.dev.dev = dev;
+ setup_pcm_id(substream);
+ return snd_pcm_lib_preallocate_pages1(substream, size, max);
+}
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_dev_pages);
+
+/*
+ * FIXME: the function name is too long for docbook!
+ *
+ * snd_pcm_lib_preallocate_dev_pages_for_all - pre-allocation for a device (all
substreams)
+ * @dev: device
+ * @pcm: pcm to assign the buffer
+ * @size: the requested pre-allocation size in bytes
+ * @max: max. buffer size acceptable for the changes via proc file
+ *
+ * Do pre-allocation to all substreams of the given pcm for the
+ * device.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_dev_pages_for_all(struct device *dev,
+ snd_pcm_t *pcm,
+ size_t size, size_t max)
+{
+ snd_pcm_substream_t *substream;
+ 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_dev_pages(dev, substream,
size, max)) < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_dev_pages_for_all);
+
+/**
+ * snd_pcm_lib_preallocate_dev_sg_pages - initialize SG-buffer for a device
+ *
+ * @dev: device
+ * @substream: substream to assign the buffer
+ * @size: the requested pre-allocation size in bytes
+ * @max: max. buffer size acceptable for the changes via proc file
+ *
+ * Initializes SG-buffer for a device.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_dev_sg_pages(struct device *dev,
+ snd_pcm_substream_t *substream,
+ size_t size, size_t max)
+{
+ substream->dma_device.type = SNDRV_DMA_TYPE_DEV_SG;
+ substream->dma_device.dev.dev = dev;
+ setup_pcm_id(substream);
+ return snd_pcm_lib_preallocate_pages1(substream, size, max);
+}
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_dev_sg_pages);
+
+/*
+ * FIXME: the function name is too long for docbook!
+ *
+ * snd_pcm_lib_preallocate_dev_sg_pages_for_all - initialize SG-buffer for a device
(all substreams)
+ * @dev: device
+ * @pcm: pcm to assign the buffer
+ * @size: the requested pre-allocation size in bytes
+ * @max: max. buffer size acceptable for the changes via proc file
+ *
+ * Initialize the SG-buffer to all substreams of the given pcm for the
+ * device.
+ *
+ * Returns zero if successful, or a negative error code on failure.
+ */
+int snd_pcm_lib_preallocate_dev_sg_pages_for_all(struct device *dev,
+ snd_pcm_t *pcm,
+ size_t size, size_t max)
+{
+ snd_pcm_substream_t *substream;
+ 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_dev_sg_pages(dev,
substream, size, max)) < 0)
+ return err;
+ return 0;
+}
+EXPORT_SYMBOL(snd_pcm_lib_preallocate_dev_sg_pages_for_all);
+
+/**
+ * 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.
+ */
+struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long
offset)
+{
+ 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];
+}
+EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page);
+
#ifdef CONFIG_ISA
/**
* snd_pcm_lib_preallocate_isa_pages - pre-allocation for the ISA bus
@@ -555,24 +678,6 @@ int snd_pcm_lib_preallocate_sg_pages_for
return 0;
}
-/**
- * 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.
- */
-struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long
offset)
-{
- 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];
-}
-
#endif /* CONFIG_PCI */
#ifndef MODULE
diff -urpN orig/sound/core/sgbuf.c linux/sound/core/sgbuf.c
--- orig/sound/core/sgbuf.c Sat Jun 14 22:35:05 2003
+++ linux/sound/core/sgbuf.c Sun Feb 29 16:38:53 2004
@@ -33,8 +33,8 @@
/**
- * snd_malloc_sgbuf_pages - allocate the pages for the PCI SG buffer
- * @pci: the pci device pointer
+ * snd_malloc_sgbuf_pages - allocate the pages for a device SG buffer
+ * @dev: the device pointer
* @size: the requested buffer size in bytes
* @dmab: the buffer record to store
*
@@ -48,7 +48,7 @@
* Returns the mapped virtual address of the buffer if allocation was
* successful, or NULL at error.
*/
-void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer
*dmab)
+void *snd_malloc_sgbuf_pages(struct device *dev, size_t size, struct snd_dma_buffer
*dmab)
{
struct snd_sg_buf *sgbuf;
unsigned int i, pages;
@@ -59,7 +59,7 @@ void *snd_malloc_sgbuf_pages(struct pci_
if (! sgbuf)
return NULL;
memset(sgbuf, 0, sizeof(*sgbuf));
- sgbuf->pci = pci;
+ sgbuf->dev = dev;
pages = snd_sgbuf_aligned_pages(size);
sgbuf->tblsize = sgbuf_align_table(pages);
sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL);
@@ -75,7 +75,7 @@ void *snd_malloc_sgbuf_pages(struct pci_
for (i = 0; i < pages; i++) {
void *ptr;
dma_addr_t addr;
- ptr = snd_malloc_pci_page(sgbuf->pci, &addr);
+ ptr = snd_malloc_dev_page(sgbuf->dev, &addr);
if (! ptr)
goto _failed;
sgbuf->table[i].buf = ptr;
@@ -115,7 +115,7 @@ int snd_free_sgbuf_pages(struct snd_dma_
return -EINVAL;
for (i = 0; i < sgbuf->pages; i++)
- snd_free_pci_page(sgbuf->pci, sgbuf->table[i].buf,
sgbuf->table[i].addr);
+ snd_free_dev_page(sgbuf->dev, sgbuf->table[i].buf,
sgbuf->table[i].addr);
if (dmab->area)
vunmap(dmab->area);
dmab->area = NULL;
diff -urpN orig/include/sound/memalloc.h linux/include/sound/memalloc.h
--- orig/include/sound/memalloc.h Sun Feb 29 20:33:35 2004
+++ linux/include/sound/memalloc.h Sun Feb 29 22:23:20 2004
@@ -29,12 +29,15 @@
#include <asm/sbus.h>
#endif
+struct device;
+
/*
* buffer device info
*/
struct snd_dma_device {
int type; /* SNDRV_MEM_TYPE_XXX */
union {
+ struct device *dev; /* generic device */
struct pci_dev *pci; /* for PCI and PCI-SG types */
unsigned int flags; /* GFP_XXX for continous and ISA types */
#ifdef CONFIG_SBUS
@@ -53,6 +56,8 @@ struct snd_dma_device {
#define SNDRV_DMA_TYPE_PCI 3 /* PCI continuous */
#define SNDRV_DMA_TYPE_SBUS 4 /* SBUS continuous */
#define SNDRV_DMA_TYPE_PCI_SG 5 /* PCI SG-buffer */
+#define SNDRV_DMA_TYPE_DEV 6 /* generic device continuous */
+#define SNDRV_DMA_TYPE_DEV_SG 7 /* generic device SG-buffer */
#ifdef CONFIG_PCI
/*
@@ -99,6 +104,16 @@ void *snd_malloc_pages(size_t size, unsi
void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t
*res_size);
void snd_free_pages(void *ptr, size_t size);
+/*
+ * Generic device continuous pages
+ */
+void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma_addr);
+void *snd_malloc_dev_pages_fallback(struct device *dev, size_t size, dma_addr_t
*dma_addr, size_t *res_size);
+void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, dma_addr_t
dma_addr);
+/* one page allocation */
+void *snd_malloc_dev_page(struct device *dev, dma_addr_t *dma_addr);
+#define snd_free_dev_page(dev,ptr,addr) snd_free_dev_pages(dev,PAGE_SIZE,ptr,addr)
+
#ifdef CONFIG_PCI
/*
* PCI continuous pages
@@ -136,9 +151,8 @@ void snd_free_isa_pages(size_t size, voi
#endif /* CONFIG_PCI */
#endif /* CONFIG_ISA */
-#ifdef CONFIG_PCI
/*
- * Scatter-Gather PCI pages
+ * Scatter-Gather generic device pages
*/
struct snd_sg_page {
void *buf;
@@ -151,10 +165,10 @@ struct snd_sg_buf {
int tblsize; /* allocated table size */
struct snd_sg_page *table; /* address table */
struct page **page_table; /* page table (for vmap/vunmap) */
- struct pci_dev *pci;
+ struct device *dev;
};
-void *snd_malloc_sgbuf_pages(struct pci_dev *pci, size_t size, struct snd_dma_buffer
*dmab);
+void *snd_malloc_sgbuf_pages(struct device *dev, size_t size, struct snd_dma_buffer
*dmab);
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
/*
@@ -172,6 +186,5 @@ static inline dma_addr_t snd_sgbuf_get_a
{
return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE;
}
-#endif /* CONFIG_PCI */
#endif /* __SOUND_MEMALLOC_H */
diff -urpN orig/include/sound/pcm.h linux/include/sound/pcm.h
--- orig/include/sound/pcm.h Wed Feb 18 22:35:25 2004
+++ linux/include/sound/pcm.h Sun Feb 29 15:33:19 2004
@@ -932,10 +932,6 @@ int snd_pcm_lib_preallocate_sg_pages(str
int snd_pcm_lib_preallocate_sg_pages_for_all(struct pci_dev *pci,
snd_pcm_t *pcm,
size_t size, size_t max);
-#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private)
-#define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size)
-#define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs)
-struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long
offset);
#endif
#ifdef CONFIG_SBUS
@@ -948,6 +944,24 @@ int snd_pcm_lib_preallocate_sbus_pages_f
size_t max);
#endif
+int snd_pcm_lib_preallocate_dev_pages(struct device *dev,
+ snd_pcm_substream_t *substream,
+ size_t size, size_t max);
+int snd_pcm_lib_preallocate_dev_pages_for_all(struct device *dev,
+ snd_pcm_t *pcm,
+ size_t size,
+ size_t max);
+int snd_pcm_lib_preallocate_dev_sg_pages(struct device *dev,
+ snd_pcm_substream_t *substream,
+ size_t size, size_t max);
+int snd_pcm_lib_preallocate_dev_sg_pages_for_all(struct device *dev,
+ snd_pcm_t *pcm,
+ size_t size, size_t max);
+#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private)
+#define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size)
+#define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs)
+struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long
offset);
+
static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
{
*max = dma < 4 ? 64 * 1024 : 128 * 1024;
--
Russell King
Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/
maintainer of: 2.6 PCMCIA - http://pcmcia.arm.linux.org.uk/
2.6 Serial core
-------------------------------------------------------
SF.Net is sponsored by: Speed Start Your Linux Apps Now.
Build and deploy apps & Web services for Linux with
a free DVD software kit from IBM. Click Now!
http://ads.osdn.com/?ad_id=1356&alloc_id=3438&op=click
_______________________________________________
Alsa-devel mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/alsa-devel