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