powerpc: Implement dma_mmap_coherent()
PATCH 1/1 powerpc: Implement dma_mmap_coherent()
This is used by Alsa to mmap buffers allocated with dma_alloc_coherent() into userspace. We need a special variant to handle machines with non-coherent DMAs as those buffers have "special" virt addresses and require non-cachable mappings
---
Dunno if anybody with CONFIG_NOT_COHERENT_CACHE has some audio device that uses dma buffers (ie not usb-audio) and wants to try that out... should fix a long standing problem.
arch/powerpc/include/asm/dma-mapping.h | 7 +++++++ arch/powerpc/kernel/dma.c | 18 ++++++++++++++++++ arch/powerpc/mm/dma-noncoherent.c | 20 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 0 deletions(-)
arch/powerpc/include/asm/dma-mapping.h +7 -0
@@ -42,6 +42,7 @@ extern void __dma_free_coherent(size_t size, void *vaddr);extern void __dma_sync (void *vaddr,size_t size,int direction);extern void __dma_sync_page (struct page *page,unsigned long offset,size_t size,int direction); +extern unsigned long __dma_get_coherent_pfn (void *cpu_addr);#else /* ! CONFIG_NOT_COHERENT_CACHE */ /*
@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page, unsigned long offset,#define __dma_free_coherent (size, addr) ((void)0)#define __dma_sync (addr, size, rw) ((void)0)#define __dma_sync_page (pg, off, sz, rw) ((void)0) +#define __dma_get_coherent_pfn (cpu_addr) (0)#endif /* ! CONFIG_NOT_COHERENT_CACHE */
@@ -198,6 +200,11 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)#define dma_alloc_noncoherent (d, s, h, f) dma_alloc_coherent(d, s, h, f)#define dma_free_noncoherent (d, s, v, h) dma_free_coherent(d, s, v, h) +extern int dma_mmap_coherent (struct device *,struct vm_area_struct *, +void *,dma_addr_t ,size_t ); +#define ARCH_HAS_DMA_MMAP_COHERENT + +static inline void dma_cache_sync (struct device *dev,void *vaddr,size_t size,enum dma_data_direction direction) {
arch/powerpc/kernel/dma.c +18 -0
@@ -179,3 +179,21 @@ static int __init dma_init(void)return 0 ; }fs_initcall (dma_init); + +int dma_mmap_coherent (struct device *dev,struct vm_area_struct *vma, +void *cpu_addr,dma_addr_t handle,size_t size) +{ +unsigned long pfn; + +#ifdef CONFIG_NOT_COHERENT_CACHE + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot); + pfn =__dma_get_coherent_pfn (cpu_addr); +#else + pfn =page_to_pfn (virt_to_page (cpu_addr)); +#endif +return remap_pfn_range (vma, vma->vm_start, + pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} +
arch/powerpc/mm/dma-noncoherent.c +20 -0
@@ -399,3 +399,23 @@ void __dma_sync_page(struct page *page, unsigned long offset,#endif }EXPORT_SYMBOL (__dma_sync_page); + +/* +* Return the PFN for a given cpu virtual address returned by +* __dma_alloc_coherent. This is used by dma_mmap_coherent() +*/ +unsigned long __dma_get_coherent_pfn (void *cpu_addr) +{ +/* This should always be populated, so we don't test every +* level. If that fails, we'll have a nice crash which +* will be as good as a BUG_ON() +*/ +pgd_t *pgd =pgd_offset_k (cpu_addr); +pud_t *pud =pud_offset (pgd, cpu_addr); +pmd_t *pmd =pmd_offset (pud, cpu_addr); +pte_t *ptep =pte_offset_kernel (pmd, cpu_addr); + +if (pte_none (*ptep) || !pte_present (*ptep)) +return 0 ; +return pte_pfn (*ptep); +}
At Fri, 25 Mar 2011 17:50:06 +1100,
Quoted 31 lines · Benjamin Herrenschmidt
This is used by Alsa to mmap buffers allocated with
dma_alloc_coherent()into userspace. We need a special variant to handle machines with non-coherent DMAs as those buffers have "special" virt addresses and require non-cachable mappingsSigned-off-by Benjamin Herrenschmidt <[email protected]>---
Dunno if anybody with
CONFIG_NOT_COHERENT_CACHEhas some audio device that uses dma buffers (ie not usb-audio) and wants to try that out... should fix a long standing problem.arch/powerpc/include/asm/dma-mapping.h | 7 +++++++ arch/powerpc/kernel/dma.c | 18 ++++++++++++++++++ arch/powerpc/mm/dma-noncoherent.c | 20 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 0 deletions(-)arch/powerpc/include/asm/dma-mapping.h +1
-0@@ -42,6 +42,7 @@ extern void __dma_free_coherent(size_t size, void *vaddr);extern void __dma_sync (void *vaddr,size_t size,int direction);extern void __dma_sync_page (struct page *page,unsigned long offset,size_t size,int direction); +extern unsigned long __dma_get_coherent_pfn (void *cpu_addr);#else /* ! CONFIG_NOT_COHERENT_CACHE */ /*
arch/powerpc/include/asm/dma-mapping.h extern void __dma_sync_page(struct page *page, unsigned long offset, · line 56
Quoted 4 lines
arch/powerpc/include/asm/dma-mapping.h +1
-0@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page, unsigned long offset,#define __dma_free_coherent (size, addr) ((void)0)#define __dma_sync (addr, size, rw) ((void)0)#define __dma_sync_page (pg, off, sz, rw) ((void)0) +#define __dma_get_coherent_pfn (cpu_addr) (0)
Just nitpicking: shouldn't we define some real thing like
page_to_pfn(virt_to_page(cpu_addr))
or put something to trigger an error? Setting a bogus zero can be easily overlooked, I'm afraid.
thanks,
Takashi
Quoted 72 lines
arch/powerpc/include/asm/dma-mapping.h +5
-0#endif /* ! CONFIG_NOT_COHERENT_CACHE */ @@ -198,6 +200,11 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)#define dma_alloc_noncoherent (d, s, h, f) dma_alloc_coherent(d, s, h, f)#define dma_free_noncoherent (d, s, v, h) dma_free_coherent(d, s, v, h) +extern int dma_mmap_coherent (struct device *,struct vm_area_struct *, +void *,dma_addr_t ,size_t ); +#define ARCH_HAS_DMA_MMAP_COHERENT + +static inline void dma_cache_sync (struct device *dev,void *vaddr,size_t size,enum dma_data_direction direction) {arch/powerpc/kernel/dma.c +18
-0@@ -179,3 +179,21 @@ static int __init dma_init(void)return 0 ; }fs_initcall (dma_init); + +int dma_mmap_coherent (struct device *dev,struct vm_area_struct *vma, +void *cpu_addr,dma_addr_t handle,size_t size) +{ +unsigned long pfn; + +#ifdef CONFIG_NOT_COHERENT_CACHE + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot); + pfn =__dma_get_coherent_pfn (cpu_addr); +#else + pfn =page_to_pfn (virt_to_page (cpu_addr)); +#endif +return remap_pfn_range (vma, vma->vm_start, + pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} +arch/powerpc/mm/dma-noncoherent.c +20
-0@@ -399,3 +399,23 @@ void __dma_sync_page(struct page *page, unsigned long offset,#endif }EXPORT_SYMBOL (__dma_sync_page); + +/* +* Return the PFN for a given cpu virtual address returned by +* __dma_alloc_coherent. This is used by dma_mmap_coherent() +*/ +unsigned long __dma_get_coherent_pfn (void *cpu_addr) +{ +/* This should always be populated, so we don't test every +* level. If that fails, we'll have a nice crash which +* will be as good as a BUG_ON() +*/ +pgd_t *pgd =pgd_offset_k (cpu_addr); +pud_t *pud =pud_offset (pgd, cpu_addr); +pmd_t *pmd =pmd_offset (pud, cpu_addr); +pte_t *ptep =pte_offset_kernel (pmd, cpu_addr); + +if (pte_none (*ptep) || !pte_present (*ptep)) +return 0 ; +return pte_pfn (*ptep); +}
Quoted 11 lines · Takashi Iwai
Review extern void __dma_sync_page(struct page *page,
Quoted 1 line
(quoted diff) +0
-0@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page,unsigned long offset,
arch/powerpc/include/asm/dma-mapping.h extern void __dma_sync_page(struct page *page, unsigned long offset, · line 56
Quoted 4 lines
arch/powerpc/include/asm/dma-mapping.h +1
-0#define __dma_free_coherent (size, addr) ((void)0)#define __dma_sync (addr, size, rw) ((void)0)#define __dma_sync_page (pg, off, sz, rw) ((void)0) +#define __dma_get_coherent_pfn (cpu_addr) (0)Just nitpicking: shouldn't we define some real thing like
page_to_pfn(virt_to_page(cpu_addr))or put something to trigger an error? Setting a bogus zero can be easily overlooked, I'm afraid.
Yes, all those variants are bogus if you noticed :-) The trick is that they aren't actually called when CONFIG_NOT_COHERENT_CACHE is not set anyways, with the exception of __dma_sync(), so I want to just remove them all. But I'll do that in a separate patch.
Cheers,
Ben.
At Fri, 25 Mar 2011 20:11:57 +1100,
Quoted 14 lines · Benjamin Herrenschmidt
Quoted 11 lines · Takashi Iwai
Review extern void __dma_sync_page(struct page *page,
Quoted 1 line
(quoted diff) +0
-0@@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page,unsigned long offset,
arch/powerpc/include/asm/dma-mapping.h extern void __dma_sync_page(struct page *page, unsigned long offset, · line 56
Quoted 4 lines
arch/powerpc/include/asm/dma-mapping.h +1
-0#define __dma_free_coherent (size, addr) ((void)0)#define __dma_sync (addr, size, rw) ((void)0)#define __dma_sync_page (pg, off, sz, rw) ((void)0) +#define __dma_get_coherent_pfn (cpu_addr) (0)Just nitpicking: shouldn't we define some real thing like
page_to_pfn(virt_to_page(cpu_addr))or put something to trigger an error? Setting a bogus zero can be easily overlooked, I'm afraid.
Quoted 4 lines
Yes, all those variants are bogus if you noticed :-) The trick is that they aren't actually called when
CONFIG_NOT_COHERENT_CACHEis not set anyways, with the exception of__dma_sync(), so I want to just remove them all. But I'll do that in a separate patch.
OK, that makes sense.
thanks,
Takashi
Quoted 4 lines · Benjamin Herrenschmidt
This is used by Alsa to mmap buffers allocated with
dma_alloc_coherent()into userspace. We need a special variant to handle machines with non-coherent DMAs as those buffers have "special" virt addresses and require non-cachable mappings
Missing an EXPORT_SYMBOL_GPL... will fix that up before I merge.
Cheers,
Ben.
Quoted 105 lines
Signed-off-by Benjamin Herrenschmidt <[email protected]>---
Dunno if anybody with
CONFIG_NOT_COHERENT_CACHEhas some audio device that uses dma buffers (ie not usb-audio) and wants to try that out... should fix a long standing problem.arch/powerpc/include/asm/dma-mapping.h | 7 +++++++ arch/powerpc/kernel/dma.c | 18 ++++++++++++++++++ arch/powerpc/mm/dma-noncoherent.c | 20 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 0 deletions(-)arch/powerpc/include/asm/dma-mapping.h +7
-0@@ -42,6 +42,7 @@ extern void __dma_free_coherent(size_t size, void *vaddr);extern void __dma_sync (void *vaddr,size_t size,int direction);extern void __dma_sync_page (struct page *page,unsigned long offset,size_t size,int direction); +extern unsigned long __dma_get_coherent_pfn (void *cpu_addr);#else /* ! CONFIG_NOT_COHERENT_CACHE */ /* @@ -52,6 +53,7 @@ extern void __dma_sync_page(struct page *page, unsigned long offset,#define __dma_free_coherent (size, addr) ((void)0)#define __dma_sync (addr, size, rw) ((void)0)#define __dma_sync_page (pg, off, sz, rw) ((void)0) +#define __dma_get_coherent_pfn (cpu_addr) (0)#endif /* ! CONFIG_NOT_COHERENT_CACHE */ @@ -198,6 +200,11 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)#define dma_alloc_noncoherent (d, s, h, f) dma_alloc_coherent(d, s, h, f)#define dma_free_noncoherent (d, s, v, h) dma_free_coherent(d, s, v, h) +extern int dma_mmap_coherent (struct device *,struct vm_area_struct *, +void *,dma_addr_t ,size_t ); +#define ARCH_HAS_DMA_MMAP_COHERENT + +static inline void dma_cache_sync (struct device *dev,void *vaddr,size_t size,enum dma_data_direction direction) {arch/powerpc/kernel/dma.c +18
-0@@ -179,3 +179,21 @@ static int __init dma_init(void)return 0 ; }fs_initcall (dma_init); + +int dma_mmap_coherent (struct device *dev,struct vm_area_struct *vma, +void *cpu_addr,dma_addr_t handle,size_t size) +{ +unsigned long pfn; + +#ifdef CONFIG_NOT_COHERENT_CACHE + vma->vm_page_prot =pgprot_noncached (vma->vm_page_prot); + pfn =__dma_get_coherent_pfn (cpu_addr); +#else + pfn =page_to_pfn (virt_to_page (cpu_addr)); +#endif +return remap_pfn_range (vma, vma->vm_start, + pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} +arch/powerpc/mm/dma-noncoherent.c +20
-0@@ -399,3 +399,23 @@ void __dma_sync_page(struct page *page, unsigned long offset,#endif }EXPORT_SYMBOL (__dma_sync_page); + +/* +* Return the PFN for a given cpu virtual address returned by +* __dma_alloc_coherent. This is used by dma_mmap_coherent() +*/ +unsigned long __dma_get_coherent_pfn (void *cpu_addr) +{ +/* This should always be populated, so we don't test every +* level. If that fails, we'll have a nice crash which +* will be as good as a BUG_ON() +*/ +pgd_t *pgd =pgd_offset_k (cpu_addr); +pud_t *pud =pud_offset (pgd, cpu_addr); +pmd_t *pmd =pmd_offset (pud, cpu_addr); +pte_t *ptep =pte_offset_kernel (pmd, cpu_addr); + +if (pte_none (*ptep) || !pte_present (*ptep)) +return 0 ; +return pte_pfn (*ptep); +}_______________________________________________
Linuxppc-dev mailing list
[email protected]
https://lists.ozlabs.org/listinfo/linuxppc-dev