[alsa-devel] To disable dma_area caching (twice)

Takashi Iwai tiwai at suse.de
Fri May 25 09:28:33 CEST 2012


At Fri, 18 May 2012 14:18:57 +0900,
MASAO TAKAHASHI wrote:
> 
> Hi everybody.
> Please advice me to solve my problem.
> 1.  My environment
>     Hardware	Alpha-project MS104-SH4AG with SH7764
>     kernel		sh-linux-2.6.29
>     ALSA driver sh7764-pcm.c developped 
>        by  Manuel Lauss <mano at roarinelk.homelinux.net>
> 
> 2. My problem
> 	I am using the above ALSA driver for SH7764 SSI.
> 	I have found the runtime->dma_area address refers DMA area
> 	via D-cache.
> 	So, playback start is too late after executing 
>           snd_pcm_writei().
> 	Until D-cache writeback is done, sound data stays in D-cache.
> 	I want to read and write from/to main memory 
> 	without D-cache.
> 
> 3. My idea to solve this problem
>    3-1 change allocating type of DMA area as follows
>        	snd_pcm_lib_preallocate_pages_for_all(
>             pcm, SNDRV_DMA_TYPE_CONTINUOUS,
>             snd_dma_continuous_data(GFP_KERNEL),
>             64*1024, 64*1024);
> 
>        is changed to
> 
>         snd_pcm_lib_preallocate_pages_for_all(
>             pcm, SNDRV_DMA_TYPE_DEV, <----------- here
>             snd_dma_continuous_data(GFP_KERNEL),
>             64*1024, 64*1024);
>         to let dma_addr pointing P2SEG area without caching
> 
>     3-2 program internal DMA controller start address 
>         with dma_addr
>    
>           ssidma_outl(sd, (virt_to_phys)(u32)runtime->dma_addr,
>                        SSIRDMADR);
> 
> 
>     3-3 add mmap_data_fault routine
>          static int sh7764_mmap_data_fault(    
>            structvm_area_struct*area,
>            struct vm_fault *vmf)
>         {
> 	  struct snd_pcm_substream *substream = area->vm_private_data;
> 	  struct snd_pcm_runtime *runtime;
> 	  unsigned long offset;
> 	  struct page * page;
> 	  void *vaddr;
> 	  size_t dma_bytes;
> 	
> 	  pr_debug("mmap_data_fault\n");
> 	  if (substream == NULL)
> 		return VM_FAULT_SIGBUS;
> 	  runtime = substream->runtime;
> 	  offset = vmf->pgoff << PAGE_SHIFT;
> 	  dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
> 	  if (offset > dma_bytes - PAGE_SIZE)
> 		return VM_FAULT_SIGBUS;
> 	  if (substream->ops->page) {
> 		page = substream->ops->page(substream, offset);
> 		if (!page)
> 			return VM_FAULT_SIGBUS;
> 	  } else {
> 		vaddr = runtime->dma_addr + offset;
> 		page = virt_to_page(vaddr);
> 	  }
> 	get_page(page);
> 	vmf->page = page;
> 	pr_debug("mmap_data_fault:page = %x\n", (unsigned int)page);
> 	return 0;
>        }
>        static struct vm_operations_struct sh7764_pcm_vm_ops_data =
>       {
> 	.open =		snd_pcm_mmap_data_open,
> 	.close =	snd_pcm_mmap_data_close,
> 	.fault =	sh7764_mmap_data_fault,
>       };
> 
>   3-3 add sh7764_pcm_mmap routine
>       static int sh7764_pcm_mmap(
>         struct snd_pcm_substream *substream,
> 	struct vm_area_struct *area)
>       {
> 	unsigned long	offset;
> 	unsigned long	mp;
> 	unsigned long	virt;
> 	int				map_size;
> 	struct page *map, *mapend, *ip;
> 	struct snd_pcm_runtime *runtime;
> 	int	ret;
>       #ifdef pgprot_noncached
>         area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
>       #endif
> 	offset = area->vm_pgoff << PAGE_SHIFT;
> 	runtime = substream->runtime;
> 	map_size = runtime->dma_bytes;
> 	mp = virt_to_phys((unsigned long)runtime->dma_addr));
> 	map = virt_to_page(mp);
> 	mapend = virt_to_page(mp+map_size - 1);
> 	for (ip = map; ip <= mapend; ip++){
> 		SetPageReserved(ip);
> 	area->vm_ops = &sh7764_pcm_vm_ops_data;
> 	area->vm_private_data = substream;
> 	area->vm_flags |= VM_IO;
> 	ret = io_remap_pfn_range(area, area->vm_start,
>                mp>>PAGE_SHIFT,
>                area->vm_end - area->vm_start, 
>                area->vm_page_prot);
> 		
> 	if (ret ){
> 	  pr_debug("sh7764-pcm.c:remap_pfn_range returned EAGAIN\n");
> 	  return -EAGAIN;
> 	}
> 	atomic_inc(&substream->mmap_count);
> 	pr_debug("mmap_count=%u\n", substream->mmap_count);
> 	return 0;
>     }
> 
>     static struct snd_pcm_ops sh7764_ssidma_ops = {
> 	.open		= sh7764_ssidma_open,
> 	.close		= sh7764_ssidma_close,
> 	.ioctl		= snd_pcm_lib_ioctl,
> 	.hw_params	= sh7764_hw_params,
> 	.hw_free	= sh7764_hw_free,
> 	.prepare	= sh7764_prepare,
> 	.trigger	= sh7764_trigger,
> 	.pointer	= sh7764_pos,
> 	.mmap		= sh7764_pcm_mmap,
>     };
>  ------------------------------------------------------------
> Sorry, so long contents.
> 
> My solution is right?
> or
> Is there any better solution ?

The "right" fix would be to implement a proper dma_mmap_coherent() for
SH architecture.  It's found for ARM and some PPC variants, but for
neither SH nor MIPS.


thanks,

Takashi


More information about the Alsa-devel mailing list