/*
* ispqueue.c
*
* TI OMAP3 ISP - Video buffers queue handling
*
* Copyright (C) 2010 Nokia Corporation
*
* Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Sakari Ailus <sakari.ailus@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <asm/cacheflush.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/poll.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ispqueue.h"
/* -----------------------------------------------------------------------------
* Video buffers management
*/
/*
* isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP
*
* The typical operation required here is Cache Invalidation across
* the (user space) buffer address range. And this _must_ be done
* at QBUF stage (and *only* at QBUF).
*
* We try to use optimal cache invalidation function:
* - dmac_map_area:
* - used when the number of pages are _low_.
* - it becomes quite slow as the number of pages increase.
* - for 648x492 viewfinder (150 pages) it takes 1.3 ms.
* - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms.
*
* - flush_cache_all:
* - used when the number of pages are _high_.
* - time taken in the range of 500-900 us.
* - has a higher penalty but, as whole dcache + icache is invalidated
*/
/*
* FIXME: dmac_inv_range crashes randomly on the user space buffer
* address. Fall back to flush_cache_all for now.
*/
#define ISP_CACHE_FLUSH_PAGES_MAX 0
static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf)
{
if (buf->skip_cache)
return;
if (buf->vbuf.m.userptr == 0 || buf->npages == 0 ||
buf->npages > ISP_CACHE_FLUSH_PAGES_MAX)
flush_cache_all();
else {
dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length,
DMA_FROM_DEVICE);
outer_inv_range(buf->vbuf.m.userptr,
buf->vbuf.m.userptr + buf->vbuf.length);
}
}
/*
* isp_video_buffer_lock_vma - Prevent VMAs from being unmapped
*
* Lock the VMAs underlying the given buffer into memory. This avoids the
* userspace buffer mapping from being swapped out, making VIPT cache handling
* easier.
*
* Note that the pages will not be freed as the buffers have been locked to
* memory using by a call to get_user_pages(), but the userspace mapping could
* still disappear if the VMAs are not locked. This is caused by the memory
* management code trying to be as lock-less as possible, which results in the
* userspace mapping manager not finding out that the pages are locked under
* some conditions.
*/
static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock)
{
struct vm_area_struct *vma;
unsigned long start;
unsigned long end;
int ret = 0;
if (buf->vbuf.memory == V4L2_MEMORY_MMAP)
return 0;
/* We can be called from workqueue context if the current task dies to
* unlock the VMAs. In that case there's no current memory management
* context so unlocking can't be performed, but the VMAs have been or
* are getting destroyed anyway so it doesn't really matter.
*/
if (!current || !current->mm)
return lock ? -EINVAL : 0;
start = buf->vbuf.m.userptr;
end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
down_write(¤t->mm->mmap_sem);
spin_lock(¤t->mm->page_table_lock);
do {
vma = find_vma(current->mm, start);
if (vma == NULL) {
ret = -EFAULT;
goto out;
}
if (lock)
vma->vm_flags |= VM_LOCKED;
else
vma->vm_flags &= ~VM_LOCKED;
start = vma->vm_end + 1;
} while (vma->vm_end < end);
if (lock)
buf->vm_flags |= VM_LOCKED;
else
buf->vm_flags &= ~VM_LOCKED;
out:
spin_unlock(¤t->mm->page_table_lock);
up_write(¤t->mm->mmap_sem);
return ret;
}
/*
* isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
*
* Iterate over the vmalloc'ed area and create a scatter list entry for every
* page.
*/
static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
{