At Fri, 18 May 2012 14:18:57 +0900, MASAO TAKAHASHI wrote:
Hi everybody. Please advice me to solve my problem.
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@roarinelk.homelinux.net
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.
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