Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8271601a authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

[media] omap3isp: queue: Merge the prepare and sglist functions



In preparation for the switch to the DMA API merge the two functions
that handle buffer preparation for the USERPTR cases (both page-backed
and non page-backed memory).

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: default avatarSakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent 73c1ea49
Loading
Loading
Loading
Loading
+69 −100
Original line number Diff line number Diff line
@@ -178,12 +178,12 @@ out:
}

/*
 * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
 * isp_video_buffer_prepare_kernel - Build 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)
static int isp_video_buffer_prepare_kernel(struct isp_video_buffer *buf)
{
	struct scatterlist *sg;
	unsigned int npages;
@@ -213,67 +213,6 @@ static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
	return 0;
}

/*
 * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer
 *
 * Walk the buffer pages list and create a 1:1 mapping to a scatter list.
 */
static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf)
{
	unsigned int offset = buf->offset;
	struct scatterlist *sg;
	unsigned int i;
	int ret;

	ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL);
	if (ret < 0)
		return ret;

	for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i) {
		if (PageHighMem(buf->pages[i])) {
			sg_free_table(&buf->sgt);
			return -EINVAL;
		}

		sg_set_page(sg, buf->pages[i], PAGE_SIZE - offset, offset);
		sg = sg_next(sg);
		offset = 0;
	}

	return 0;
}

/*
 * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer
 *
 * Create a scatter list of physically contiguous pages starting at the buffer
 * memory physical address.
 */
static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf)
{
	struct scatterlist *sg;
	unsigned int offset = buf->offset;
	unsigned long pfn = buf->paddr >> PAGE_SHIFT;
	unsigned int i;
	int ret;

	ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL);
	if (ret < 0)
		return ret;

	for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i, ++pfn) {
		sg_set_page(sg, pfn_to_page(pfn), PAGE_SIZE - offset, offset);
		/* PFNMAP buffers will not get DMA-mapped, set the DMA address
		 * manually.
		 */
		sg_dma_address(sg) = (pfn << PAGE_SHIFT) + offset;
		sg = sg_next(sg);
		offset = 0;
	}

	return 0;
}

/*
 * isp_video_buffer_cleanup - Release pages for a userspace VMA.
 *
@@ -316,11 +255,11 @@ static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
}

/*
 * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory.
 * isp_video_buffer_prepare_user - Prepare a userspace buffer.
 *
 * This function creates a list of pages for a userspace VMA. The number of
 * pages is first computed based on the buffer size, and pages are then
 * retrieved by a call to get_user_pages.
 * This function creates a scatter list with a 1:1 mapping for a userspace VMA.
 * The number of pages is first computed based on the buffer size, and pages are
 * then retrieved by a call to get_user_pages.
 *
 * Pages are pinned to memory by get_user_pages, making them available for DMA
 * transfers. However, due to memory management optimization, it seems the
@@ -340,16 +279,19 @@ static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
 */
static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
{
	struct scatterlist *sg;
	unsigned int offset;
	unsigned long data;
	unsigned int first;
	unsigned int last;
	unsigned int i;
	int ret;

	data = buf->vbuf.m.userptr;
	first = (data & PAGE_MASK) >> PAGE_SHIFT;
	last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT;
	offset = data & ~PAGE_MASK;

	buf->offset = data & ~PAGE_MASK;
	buf->npages = last - first + 1;
	buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0]));
	if (buf->pages == NULL)
@@ -364,68 +306,104 @@ static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)

	if (ret != buf->npages) {
		buf->npages = ret < 0 ? 0 : ret;
		isp_video_buffer_cleanup(buf);
		return -EFAULT;
	}

	ret = isp_video_buffer_lock_vma(buf, 1);
	if (ret < 0)
		isp_video_buffer_cleanup(buf);
		return ret;

	ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL);
	if (ret < 0)
		return ret;

	for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i) {
		if (PageHighMem(buf->pages[i])) {
			sg_free_table(&buf->sgt);
			return -EINVAL;
		}

		sg_set_page(sg, buf->pages[i], PAGE_SIZE - offset, offset);
		sg = sg_next(sg);
		offset = 0;
	}

	return 0;
}

/*
 * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer
 * isp_video_buffer_prepare_pfnmap - Prepare a VM_PFNMAP userspace buffer
 *
 * Userspace VM_PFNMAP buffers are supported only if they are contiguous in
 * memory and if they span a single VMA.
 * memory and if they span a single VMA. Start by validating the user pointer to
 * make sure it fulfils that condition, and then build a scatter list of
 * physically contiguous pages starting at the buffer memory physical address.
 *
 * Return 0 if the buffer is valid, or -EFAULT otherwise.
 * Return 0 on success, -EFAULT if the buffer isn't valid or -ENOMEM if memory
 * can't be allocated.
 */
static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf)
{
	struct vm_area_struct *vma;
	struct scatterlist *sg;
	unsigned long prev_pfn;
	unsigned long this_pfn;
	unsigned long start;
	unsigned int offset;
	unsigned long end;
	dma_addr_t pa = 0;
	int ret = -EFAULT;
	unsigned long pfn;
	unsigned int i;
	int ret = 0;

	start = buf->vbuf.m.userptr;
	end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
	offset = start & ~PAGE_MASK;

	buf->offset = start & ~PAGE_MASK;
	buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
	buf->pages = NULL;

	down_read(&current->mm->mmap_sem);
	vma = find_vma(current->mm, start);
	if (vma == NULL || vma->vm_end < end)
		goto done;
	if (vma == NULL || vma->vm_end < end) {
		ret = -EFAULT;
		goto unlock;
	}

	for (prev_pfn = 0; start <= end; start += PAGE_SIZE) {
		ret = follow_pfn(vma, start, &this_pfn);
		if (ret)
			goto done;
		if (ret < 0)
			goto unlock;

		if (prev_pfn == 0)
			pa = this_pfn << PAGE_SHIFT;
			pfn = this_pfn;
		else if (this_pfn != prev_pfn + 1) {
			ret = -EFAULT;
			goto done;
			goto unlock;
		}

		prev_pfn = this_pfn;
	}

	buf->paddr = pa + buf->offset;
	ret = 0;

done:
unlock:
	up_read(&current->mm->mmap_sem);
	if (ret < 0)
		return ret;

	ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL);
	if (ret < 0)
		return ret;

	for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i, ++pfn) {
		sg_set_page(sg, pfn_to_page(pfn), PAGE_SIZE - offset, offset);
		/* PFNMAP buffers will not get DMA-mapped, set the DMA address
		 * manually.
		 */
		sg_dma_address(sg) = (pfn << PAGE_SHIFT) + offset;
		sg = sg_next(sg);
		offset = 0;
	}

	return 0;
}

/*
@@ -511,7 +489,7 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)

	switch (buf->vbuf.memory) {
	case V4L2_MEMORY_MMAP:
		ret = isp_video_buffer_sglist_kernel(buf);
		ret = isp_video_buffer_prepare_kernel(buf);
		break;

	case V4L2_MEMORY_USERPTR:
@@ -519,19 +497,10 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
		if (ret < 0)
			return ret;

		if (buf->vm_flags & VM_PFNMAP) {
		if (buf->vm_flags & VM_PFNMAP)
			ret = isp_video_buffer_prepare_pfnmap(buf);
			if (ret < 0)
				return ret;

			ret = isp_video_buffer_sglist_pfnmap(buf);
		} else {
		else
			ret = isp_video_buffer_prepare_user(buf);
			if (ret < 0)
				return ret;

			ret = isp_video_buffer_sglist_user(buf);
		}
		break;

	default:
+0 −4
Original line number Diff line number Diff line
@@ -69,10 +69,8 @@ enum isp_video_buffer_state {
 * @skip_cache: Whether to skip cache management operations for this buffer
 * @vaddr: Memory virtual address (for kernel buffers)
 * @vm_flags: Buffer VMA flags (for userspace buffers)
 * @offset: Offset inside the first page (for userspace buffers)
 * @npages: Number of pages (for userspace buffers)
 * @pages: Pages table (for userspace non-VM_PFNMAP buffers)
 * @paddr: Memory physical address (for userspace VM_PFNMAP buffers)
 * @sgt: Scatter gather table (for non-VM_PFNMAP buffers)
 * @vbuf: V4L2 buffer
 * @irqlist: List head for insertion into IRQ queue
@@ -91,10 +89,8 @@ struct isp_video_buffer {

	/* For userspace buffers. */
	vm_flags_t vm_flags;
	unsigned long offset;
	unsigned int npages;
	struct page **pages;
	dma_addr_t paddr;

	/* For all buffers except VM_PFNMAP. */
	struct sg_table sgt;