Re: ALSA on MIPS platform

From: Takashi Iwai
Date: Wed Jan 25 2006 - 13:54:56 EST


At Wed, 25 Jan 2006 23:50:07 +0900 (JST),
Atsushi Nemoto wrote:
>
> Hi. I'm using PCI Sound cards on MIPS platform which has noncoherent
> DMA. There are some issues in ALSA for these platform.
>
> (This topic comes from linux-mips ML.
> http://www.linux-mips.org/cgi-bin/mesg.cgi?a=linux-mips&i=20060124030725.GA14063%40deprecation.cyrius.com)
>
>
> 1. virt_to_page vs. dma_alloc_coherent problem.
>
> ALSA uses virt_to_page() to get 'struct page' for DMA area which was
> allocated using dma_alloc_coherent(). On MIPS with
> CONFIG_DMA_NONCOHERENT, typically physical address range
> 0x0-0x1fffffff are mapped to 0x8000000-0x9fffffff with cached and
> mapped to 0xa0000000-0xbfffffff with uncached. If we got physical
> address 0x01000000 for DMA, the virtual address is 0xa1000000. On the
> other hand, virt_to_page() expects normal(cached) virtual address. So
> we can use virt_to_page() with kmalloc() or dma_alloc_noncoherent(),
> but not with dma_alloc_coherent().
>
> Here is some fragments from kernel code. You can see what I mean exactly.
>
> /* from include/asm-mips/mach-generic/spaces.h */
> #define CAC_BASE 0x80000000
> #define UNCAC_BASE 0xa0000000
> #define PAGE_OFFSET 0x80000000UL
> /* from include/asm-mips/page.h */
> #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET)
> #define __va(x) ((void *)((unsigned long) (x) + PAGE_OFFSET))
> #define pfn_to_page(pfn) (mem_map + (pfn))
> #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
> #define UNCAC_ADDR(addr) ((addr) - PAGE_OFFSET + UNCAC_BASE)
> #define CAC_ADDR(addr) ((addr) - UNCAC_BASE + PAGE_OFFSET)
> /* from arch/mips/mm/dma-noncoherent.c */
> void *dma_alloc_coherent(struct device *dev, size_t size,
> dma_addr_t * dma_handle, gfp_t gfp)
> {
> void *ret;
>
> ret = dma_alloc_noncoherent(dev, size, dma_handle, gfp);
> if (ret) {
> dma_cache_wback_inv((unsigned long) ret, size);
> ret = UNCAC_ADDR(ret);
> }
>
> return ret;
> }
>
> How do we fix this?
>
> A. hack sound/core/memalloc.c, pcm_native.c, sgbuf.c
>
> something like:
> #if defined(__mips__) && defined(CONFIG_DMA_NONCOHERENT)
> mark_pages(virt_to_page(CAC_ADDR(res)), pg); /* should be dma_to_page() */
> #else
> mark_pages(virt_to_page(res), pg); /* should be dma_to_page() */
> #endif
>
> It's ugly.
>
> B. fix mips virt_to_page()
>
> #define TO_PHYS_MASK 0x1fffffff
> #define virt_to_page(kaddr) pfn_to_page(((kaddr) & TO_PHYS_MASK) >> PAGE_SHIFT)
>
> a bit slower. MIPS need one instruction to create 0x80000000
> constant, two instruction for 0x1fffffff. There are many usage of
> virt_to_page() in mm/slab.c so its performance might be important.
>
> C. introduce dma_to_page() which returns struct page * for dma_addr_t.
>
> The comment in ALSA code (/* should be dma_to_page() */) mean this?
>
> For MIPS, it would be:
> #define dma_to_page(addr) pfn_to_page((addr) >> PAGE_SHIFT)
> But I do not know for other platform.
>
>
> Which is preferred, or is there other good way?
>
>
> 2. mmapping DMA area.
>
> As described above, DMA area is uncached on those platform. So mmap
> should ensure user programs do not cache these area.
> snd_pcm_default_mmap() is used for mmapping the DMA area but its
> vm_page_prot is not configured as uncached. On the other hand,
> snd_pcm_lib_mmap_iomem() do it using pgprot_noncached().
>
> snd_pcm_default_mmap() should do same thing for those MIPS platform.
>
> static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,
> struct vm_area_struct *area)
> {
> #if defined(__mips__) && defined(CONFIG_DMA_NONCOHERENT)
> area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
> #endif
> area->vm_ops = &snd_pcm_vm_ops_data;
>
> Is this a right fix? Are there any platform which have same problem?

The right fix is, IMO, to port dma_mmap_coherent() to all archs.
Currently, it's only on ARM.


Takashi
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/