diff options
Diffstat (limited to 'sound/core/pcm_native.c')
| -rw-r--r-- | sound/core/pcm_native.c | 83 | 
1 files changed, 59 insertions, 24 deletions
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index ab73edf2c89..29ab46a12e1 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -26,6 +26,7 @@  #include <linux/time.h>  #include <linux/pm_qos_params.h>  #include <linux/uio.h> +#include <linux/dma-mapping.h>  #include <sound/core.h>  #include <sound/control.h>  #include <sound/info.h> @@ -3061,6 +3062,27 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file  }  #endif /* coherent mmap */ +static inline struct page * +snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs) +{ +	void *vaddr = substream->runtime->dma_area + ofs; +#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) +	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) +		return virt_to_page(CAC_ADDR(vaddr)); +#endif +#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE) +	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) { +		dma_addr_t addr = substream->runtime->dma_addr + ofs; +		addr -= get_dma_offset(substream->dma_buffer.dev.dev); +		/* assume dma_handle set via pfn_to_phys() in +		 * mm/dma-noncoherent.c +		 */ +		return pfn_to_page(addr >> PAGE_SHIFT); +	} +#endif +	return virt_to_page(vaddr); +} +  /*   * fault callback for mmapping a RAM page   */ @@ -3071,7 +3093,6 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,  	struct snd_pcm_runtime *runtime;  	unsigned long offset;  	struct page * page; -	void *vaddr;  	size_t dma_bytes;  	if (substream == NULL) @@ -3081,36 +3102,53 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area,  	dma_bytes = PAGE_ALIGN(runtime->dma_bytes);  	if (offset > dma_bytes - PAGE_SIZE)  		return VM_FAULT_SIGBUS; -	if (substream->ops->page) { +	if (substream->ops->page)  		page = substream->ops->page(substream, offset); -		if (!page) -			return VM_FAULT_SIGBUS; -	} else { -		vaddr = runtime->dma_area + offset; -		page = virt_to_page(vaddr); -	} +	else +		page = snd_pcm_default_page_ops(substream, offset); +	if (!page) +		return VM_FAULT_SIGBUS;  	get_page(page);  	vmf->page = page;  	return 0;  } -static const struct vm_operations_struct snd_pcm_vm_ops_data = -{ +static const struct vm_operations_struct snd_pcm_vm_ops_data = { +	.open =		snd_pcm_mmap_data_open, +	.close =	snd_pcm_mmap_data_close, +}; + +static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {  	.open =		snd_pcm_mmap_data_open,  	.close =	snd_pcm_mmap_data_close,  	.fault =	snd_pcm_mmap_data_fault,  }; +#ifndef ARCH_HAS_DMA_MMAP_COHERENT +/* This should be defined / handled globally! */ +#ifdef CONFIG_ARM +#define ARCH_HAS_DMA_MMAP_COHERENT +#endif +#endif +  /*   * mmap the DMA buffer on RAM   */  static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,  				struct vm_area_struct *area)  { -	area->vm_ops = &snd_pcm_vm_ops_data; -	area->vm_private_data = substream;  	area->vm_flags |= VM_RESERVED; -	atomic_inc(&substream->mmap_count); +#ifdef ARCH_HAS_DMA_MMAP_COHERENT +	if (!substream->ops->page && +	    substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) +		return dma_mmap_coherent(substream->dma_buffer.dev.dev, +					 area, +					 substream->runtime->dma_area, +					 substream->runtime->dma_addr, +					 area->vm_end - area->vm_start); +#endif /* ARCH_HAS_DMA_MMAP_COHERENT */ +	/* mmap with fault handler */ +	area->vm_ops = &snd_pcm_vm_ops_data_fault;  	return 0;  } @@ -3118,12 +3156,6 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream,   * mmap the DMA buffer on I/O memory area   */  #if SNDRV_PCM_INFO_MMAP_IOMEM -static const struct vm_operations_struct snd_pcm_vm_ops_data_mmio = -{ -	.open =		snd_pcm_mmap_data_open, -	.close =	snd_pcm_mmap_data_close, -}; -  int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,  			   struct vm_area_struct *area)  { @@ -3133,8 +3165,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,  #ifdef pgprot_noncached  	area->vm_page_prot = pgprot_noncached(area->vm_page_prot);  #endif -	area->vm_ops = &snd_pcm_vm_ops_data_mmio; -	area->vm_private_data = substream;  	area->vm_flags |= VM_IO;  	size = area->vm_end - area->vm_start;  	offset = area->vm_pgoff << PAGE_SHIFT; @@ -3142,7 +3172,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,  				(substream->runtime->dma_addr + offset) >> PAGE_SHIFT,  				size, area->vm_page_prot))  		return -EAGAIN; -	atomic_inc(&substream->mmap_count);  	return 0;  } @@ -3159,6 +3188,7 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,  	long size;  	unsigned long offset;  	size_t dma_bytes; +	int err;  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {  		if (!(area->vm_flags & (VM_WRITE|VM_READ))) @@ -3183,10 +3213,15 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,  	if (offset > dma_bytes - size)  		return -EINVAL; +	area->vm_ops = &snd_pcm_vm_ops_data; +	area->vm_private_data = substream;  	if (substream->ops->mmap) -		return substream->ops->mmap(substream, area); +		err = substream->ops->mmap(substream, area);  	else -		return snd_pcm_default_mmap(substream, area); +		err = snd_pcm_default_mmap(substream, area); +	if (!err) +		atomic_inc(&substream->mmap_count); +	return err;  }  EXPORT_SYMBOL(snd_pcm_mmap_data);  |