[alsa-devel] To disable dma_area caching (twice)
MASAO TAKAHASHI
masao-takahashi at kanno.co.jp
Fri May 18 07:18:57 CEST 2012
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 ?
Best regards
----------
MASAO TAKAHASHI <masao-takahashi at kanno.co.jp>
KANNO WORKS CO., LTD. JAPAN
TEL 093-436-2330
More information about the Alsa-devel
mailing list