From 8271601aca19c1d0ad29d0b65a5a6b9e6ef97bd3 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 6 Jan 2014 15:30:03 -0300 Subject: [PATCH] [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: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/omap3isp/ispqueue.c | 169 +++++++++------------ drivers/media/platform/omap3isp/ispqueue.h | 4 - 2 files changed, 69 insertions(+), 104 deletions(-) diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c index 51ec40d6ea42..a7be7d7d5db5 100644 --- a/drivers/media/platform/omap3isp/ispqueue.c +++ b/drivers/media/platform/omap3isp/ispqueue.c @@ -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; - 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(¤t->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(¤t->mm->mmap_sem); - return ret; + 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: diff --git a/drivers/media/platform/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h index 99c11e8c6e9d..f78325dbcf49 100644 --- a/drivers/media/platform/omap3isp/ispqueue.h +++ b/drivers/media/platform/omap3isp/ispqueue.h @@ -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; -- 2.34.1