diff options
| author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2014-01-06 13:30:03 -0500 |
|---|---|---|
| committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-05-25 10:14:43 -0400 |
| commit | 8271601aca19c1d0ad29d0b65a5a6b9e6ef97bd3 (patch) | |
| tree | 7d426c119eb97ddd7a07faee90bd988487d825a5 | |
| parent | 73c1ea496cf79aec8a5681064719703b346301dc (diff) | |
[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 <laurent.pinchart@ideasonboard.com>
Acked-by: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
| -rw-r--r-- | drivers/media/platform/omap3isp/ispqueue.c | 169 | ||||
| -rw-r--r-- | 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: | |||
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | /* | 180 | /* |
| 181 | * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer | 181 | * isp_video_buffer_prepare_kernel - Build scatter list for a vmalloc'ed buffer |
| 182 | * | 182 | * |
| 183 | * Iterate over the vmalloc'ed area and create a scatter list entry for every | 183 | * Iterate over the vmalloc'ed area and create a scatter list entry for every |
| 184 | * page. | 184 | * page. |
| 185 | */ | 185 | */ |
| 186 | static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf) | 186 | static int isp_video_buffer_prepare_kernel(struct isp_video_buffer *buf) |
| 187 | { | 187 | { |
| 188 | struct scatterlist *sg; | 188 | struct scatterlist *sg; |
| 189 | unsigned int npages; | 189 | unsigned int npages; |
| @@ -214,67 +214,6 @@ static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf) | |||
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | /* | 216 | /* |
| 217 | * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer | ||
| 218 | * | ||
| 219 | * Walk the buffer pages list and create a 1:1 mapping to a scatter list. | ||
| 220 | */ | ||
| 221 | static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf) | ||
| 222 | { | ||
| 223 | unsigned int offset = buf->offset; | ||
| 224 | struct scatterlist *sg; | ||
| 225 | unsigned int i; | ||
| 226 | int ret; | ||
| 227 | |||
| 228 | ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL); | ||
| 229 | if (ret < 0) | ||
| 230 | return ret; | ||
| 231 | |||
| 232 | for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i) { | ||
| 233 | if (PageHighMem(buf->pages[i])) { | ||
| 234 | sg_free_table(&buf->sgt); | ||
| 235 | return -EINVAL; | ||
| 236 | } | ||
| 237 | |||
| 238 | sg_set_page(sg, buf->pages[i], PAGE_SIZE - offset, offset); | ||
| 239 | sg = sg_next(sg); | ||
| 240 | offset = 0; | ||
| 241 | } | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* | ||
| 247 | * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer | ||
| 248 | * | ||
| 249 | * Create a scatter list of physically contiguous pages starting at the buffer | ||
| 250 | * memory physical address. | ||
| 251 | */ | ||
| 252 | static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf) | ||
| 253 | { | ||
| 254 | struct scatterlist *sg; | ||
| 255 | unsigned int offset = buf->offset; | ||
| 256 | unsigned long pfn = buf->paddr >> PAGE_SHIFT; | ||
| 257 | unsigned int i; | ||
| 258 | int ret; | ||
| 259 | |||
| 260 | ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL); | ||
| 261 | if (ret < 0) | ||
| 262 | return ret; | ||
| 263 | |||
| 264 | for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i, ++pfn) { | ||
| 265 | sg_set_page(sg, pfn_to_page(pfn), PAGE_SIZE - offset, offset); | ||
| 266 | /* PFNMAP buffers will not get DMA-mapped, set the DMA address | ||
| 267 | * manually. | ||
| 268 | */ | ||
| 269 | sg_dma_address(sg) = (pfn << PAGE_SHIFT) + offset; | ||
| 270 | sg = sg_next(sg); | ||
| 271 | offset = 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | return 0; | ||
| 275 | } | ||
| 276 | |||
| 277 | /* | ||
| 278 | * isp_video_buffer_cleanup - Release pages for a userspace VMA. | 217 | * isp_video_buffer_cleanup - Release pages for a userspace VMA. |
| 279 | * | 218 | * |
| 280 | * Release pages locked by a call isp_video_buffer_prepare_user and free the | 219 | * Release pages locked by a call isp_video_buffer_prepare_user and free the |
| @@ -316,11 +255,11 @@ static void isp_video_buffer_cleanup(struct isp_video_buffer *buf) | |||
| 316 | } | 255 | } |
| 317 | 256 | ||
| 318 | /* | 257 | /* |
| 319 | * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory. | 258 | * isp_video_buffer_prepare_user - Prepare a userspace buffer. |
| 320 | * | 259 | * |
| 321 | * This function creates a list of pages for a userspace VMA. The number of | 260 | * This function creates a scatter list with a 1:1 mapping for a userspace VMA. |
| 322 | * pages is first computed based on the buffer size, and pages are then | 261 | * The number of pages is first computed based on the buffer size, and pages are |
| 323 | * retrieved by a call to get_user_pages. | 262 | * then retrieved by a call to get_user_pages. |
| 324 | * | 263 | * |
| 325 | * Pages are pinned to memory by get_user_pages, making them available for DMA | 264 | * Pages are pinned to memory by get_user_pages, making them available for DMA |
| 326 | * transfers. However, due to memory management optimization, it seems the | 265 | * 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) | |||
| 340 | */ | 279 | */ |
| 341 | static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) | 280 | static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) |
| 342 | { | 281 | { |
| 282 | struct scatterlist *sg; | ||
| 283 | unsigned int offset; | ||
| 343 | unsigned long data; | 284 | unsigned long data; |
| 344 | unsigned int first; | 285 | unsigned int first; |
| 345 | unsigned int last; | 286 | unsigned int last; |
| 287 | unsigned int i; | ||
| 346 | int ret; | 288 | int ret; |
| 347 | 289 | ||
| 348 | data = buf->vbuf.m.userptr; | 290 | data = buf->vbuf.m.userptr; |
| 349 | first = (data & PAGE_MASK) >> PAGE_SHIFT; | 291 | first = (data & PAGE_MASK) >> PAGE_SHIFT; |
| 350 | last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT; | 292 | last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT; |
| 293 | offset = data & ~PAGE_MASK; | ||
| 351 | 294 | ||
| 352 | buf->offset = data & ~PAGE_MASK; | ||
| 353 | buf->npages = last - first + 1; | 295 | buf->npages = last - first + 1; |
| 354 | buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0])); | 296 | buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0])); |
| 355 | if (buf->pages == NULL) | 297 | if (buf->pages == NULL) |
| @@ -364,68 +306,104 @@ static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf) | |||
| 364 | 306 | ||
| 365 | if (ret != buf->npages) { | 307 | if (ret != buf->npages) { |
| 366 | buf->npages = ret < 0 ? 0 : ret; | 308 | buf->npages = ret < 0 ? 0 : ret; |
| 367 | isp_video_buffer_cleanup(buf); | ||
| 368 | return -EFAULT; | 309 | return -EFAULT; |
| 369 | } | 310 | } |
| 370 | 311 | ||
| 371 | ret = isp_video_buffer_lock_vma(buf, 1); | 312 | ret = isp_video_buffer_lock_vma(buf, 1); |
| 372 | if (ret < 0) | 313 | if (ret < 0) |
| 373 | isp_video_buffer_cleanup(buf); | 314 | return ret; |
| 374 | 315 | ||
| 375 | return ret; | 316 | ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL); |
| 317 | if (ret < 0) | ||
| 318 | return ret; | ||
| 319 | |||
| 320 | for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i) { | ||
| 321 | if (PageHighMem(buf->pages[i])) { | ||
| 322 | sg_free_table(&buf->sgt); | ||
| 323 | return -EINVAL; | ||
| 324 | } | ||
| 325 | |||
| 326 | sg_set_page(sg, buf->pages[i], PAGE_SIZE - offset, offset); | ||
| 327 | sg = sg_next(sg); | ||
| 328 | offset = 0; | ||
| 329 | } | ||
| 330 | |||
| 331 | return 0; | ||
| 376 | } | 332 | } |
| 377 | 333 | ||
| 378 | /* | 334 | /* |
| 379 | * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer | 335 | * isp_video_buffer_prepare_pfnmap - Prepare a VM_PFNMAP userspace buffer |
| 380 | * | 336 | * |
| 381 | * Userspace VM_PFNMAP buffers are supported only if they are contiguous in | 337 | * Userspace VM_PFNMAP buffers are supported only if they are contiguous in |
| 382 | * memory and if they span a single VMA. | 338 | * memory and if they span a single VMA. Start by validating the user pointer to |
| 339 | * make sure it fulfils that condition, and then build a scatter list of | ||
| 340 | * physically contiguous pages starting at the buffer memory physical address. | ||
| 383 | * | 341 | * |
| 384 | * Return 0 if the buffer is valid, or -EFAULT otherwise. | 342 | * Return 0 on success, -EFAULT if the buffer isn't valid or -ENOMEM if memory |
| 343 | * can't be allocated. | ||
| 385 | */ | 344 | */ |
| 386 | static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) | 345 | static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf) |
| 387 | { | 346 | { |
| 388 | struct vm_area_struct *vma; | 347 | struct vm_area_struct *vma; |
| 348 | struct scatterlist *sg; | ||
| 389 | unsigned long prev_pfn; | 349 | unsigned long prev_pfn; |
| 390 | unsigned long this_pfn; | 350 | unsigned long this_pfn; |
| 391 | unsigned long start; | 351 | unsigned long start; |
| 352 | unsigned int offset; | ||
| 392 | unsigned long end; | 353 | unsigned long end; |
| 393 | dma_addr_t pa = 0; | 354 | unsigned long pfn; |
| 394 | int ret = -EFAULT; | 355 | unsigned int i; |
| 356 | int ret = 0; | ||
| 395 | 357 | ||
| 396 | start = buf->vbuf.m.userptr; | 358 | start = buf->vbuf.m.userptr; |
| 397 | end = buf->vbuf.m.userptr + buf->vbuf.length - 1; | 359 | end = buf->vbuf.m.userptr + buf->vbuf.length - 1; |
| 360 | offset = start & ~PAGE_MASK; | ||
| 398 | 361 | ||
| 399 | buf->offset = start & ~PAGE_MASK; | ||
| 400 | buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; | 362 | buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1; |
| 401 | buf->pages = NULL; | 363 | buf->pages = NULL; |
| 402 | 364 | ||
| 403 | down_read(¤t->mm->mmap_sem); | 365 | down_read(¤t->mm->mmap_sem); |
| 404 | vma = find_vma(current->mm, start); | 366 | vma = find_vma(current->mm, start); |
| 405 | if (vma == NULL || vma->vm_end < end) | 367 | if (vma == NULL || vma->vm_end < end) { |
| 406 | goto done; | 368 | ret = -EFAULT; |
| 369 | goto unlock; | ||
| 370 | } | ||
| 407 | 371 | ||
| 408 | for (prev_pfn = 0; start <= end; start += PAGE_SIZE) { | 372 | for (prev_pfn = 0; start <= end; start += PAGE_SIZE) { |
| 409 | ret = follow_pfn(vma, start, &this_pfn); | 373 | ret = follow_pfn(vma, start, &this_pfn); |
| 410 | if (ret) | 374 | if (ret < 0) |
| 411 | goto done; | 375 | goto unlock; |
| 412 | 376 | ||
| 413 | if (prev_pfn == 0) | 377 | if (prev_pfn == 0) |
| 414 | pa = this_pfn << PAGE_SHIFT; | 378 | pfn = this_pfn; |
| 415 | else if (this_pfn != prev_pfn + 1) { | 379 | else if (this_pfn != prev_pfn + 1) { |
| 416 | ret = -EFAULT; | 380 | ret = -EFAULT; |
| 417 | goto done; | 381 | goto unlock; |
| 418 | } | 382 | } |
| 419 | 383 | ||
| 420 | prev_pfn = this_pfn; | 384 | prev_pfn = this_pfn; |
| 421 | } | 385 | } |
| 422 | 386 | ||
| 423 | buf->paddr = pa + buf->offset; | 387 | unlock: |
| 424 | ret = 0; | ||
| 425 | |||
| 426 | done: | ||
| 427 | up_read(¤t->mm->mmap_sem); | 388 | up_read(¤t->mm->mmap_sem); |
| 428 | return ret; | 389 | if (ret < 0) |
| 390 | return ret; | ||
| 391 | |||
| 392 | ret = sg_alloc_table(&buf->sgt, buf->npages, GFP_KERNEL); | ||
| 393 | if (ret < 0) | ||
| 394 | return ret; | ||
| 395 | |||
| 396 | for (sg = buf->sgt.sgl, i = 0; i < buf->npages; ++i, ++pfn) { | ||
| 397 | sg_set_page(sg, pfn_to_page(pfn), PAGE_SIZE - offset, offset); | ||
| 398 | /* PFNMAP buffers will not get DMA-mapped, set the DMA address | ||
| 399 | * manually. | ||
| 400 | */ | ||
| 401 | sg_dma_address(sg) = (pfn << PAGE_SHIFT) + offset; | ||
| 402 | sg = sg_next(sg); | ||
| 403 | offset = 0; | ||
| 404 | } | ||
| 405 | |||
| 406 | return 0; | ||
| 429 | } | 407 | } |
| 430 | 408 | ||
| 431 | /* | 409 | /* |
| @@ -511,7 +489,7 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf) | |||
| 511 | 489 | ||
| 512 | switch (buf->vbuf.memory) { | 490 | switch (buf->vbuf.memory) { |
| 513 | case V4L2_MEMORY_MMAP: | 491 | case V4L2_MEMORY_MMAP: |
| 514 | ret = isp_video_buffer_sglist_kernel(buf); | 492 | ret = isp_video_buffer_prepare_kernel(buf); |
| 515 | break; | 493 | break; |
| 516 | 494 | ||
| 517 | case V4L2_MEMORY_USERPTR: | 495 | case V4L2_MEMORY_USERPTR: |
| @@ -519,19 +497,10 @@ static int isp_video_buffer_prepare(struct isp_video_buffer *buf) | |||
| 519 | if (ret < 0) | 497 | if (ret < 0) |
| 520 | return ret; | 498 | return ret; |
| 521 | 499 | ||
| 522 | if (buf->vm_flags & VM_PFNMAP) { | 500 | if (buf->vm_flags & VM_PFNMAP) |
| 523 | ret = isp_video_buffer_prepare_pfnmap(buf); | 501 | ret = isp_video_buffer_prepare_pfnmap(buf); |
| 524 | if (ret < 0) | 502 | else |
| 525 | return ret; | ||
| 526 | |||
| 527 | ret = isp_video_buffer_sglist_pfnmap(buf); | ||
| 528 | } else { | ||
| 529 | ret = isp_video_buffer_prepare_user(buf); | 503 | ret = isp_video_buffer_prepare_user(buf); |
| 530 | if (ret < 0) | ||
| 531 | return ret; | ||
| 532 | |||
| 533 | ret = isp_video_buffer_sglist_user(buf); | ||
| 534 | } | ||
| 535 | break; | 504 | break; |
| 536 | 505 | ||
| 537 | default: | 506 | 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 { | |||
| 69 | * @skip_cache: Whether to skip cache management operations for this buffer | 69 | * @skip_cache: Whether to skip cache management operations for this buffer |
| 70 | * @vaddr: Memory virtual address (for kernel buffers) | 70 | * @vaddr: Memory virtual address (for kernel buffers) |
| 71 | * @vm_flags: Buffer VMA flags (for userspace buffers) | 71 | * @vm_flags: Buffer VMA flags (for userspace buffers) |
| 72 | * @offset: Offset inside the first page (for userspace buffers) | ||
| 73 | * @npages: Number of pages (for userspace buffers) | 72 | * @npages: Number of pages (for userspace buffers) |
| 74 | * @pages: Pages table (for userspace non-VM_PFNMAP buffers) | 73 | * @pages: Pages table (for userspace non-VM_PFNMAP buffers) |
| 75 | * @paddr: Memory physical address (for userspace VM_PFNMAP buffers) | ||
| 76 | * @sgt: Scatter gather table (for non-VM_PFNMAP buffers) | 74 | * @sgt: Scatter gather table (for non-VM_PFNMAP buffers) |
| 77 | * @vbuf: V4L2 buffer | 75 | * @vbuf: V4L2 buffer |
| 78 | * @irqlist: List head for insertion into IRQ queue | 76 | * @irqlist: List head for insertion into IRQ queue |
| @@ -91,10 +89,8 @@ struct isp_video_buffer { | |||
| 91 | 89 | ||
| 92 | /* For userspace buffers. */ | 90 | /* For userspace buffers. */ |
| 93 | vm_flags_t vm_flags; | 91 | vm_flags_t vm_flags; |
| 94 | unsigned long offset; | ||
| 95 | unsigned int npages; | 92 | unsigned int npages; |
| 96 | struct page **pages; | 93 | struct page **pages; |
| 97 | dma_addr_t paddr; | ||
| 98 | 94 | ||
| 99 | /* For all buffers except VM_PFNMAP. */ | 95 | /* For all buffers except VM_PFNMAP. */ |
| 100 | struct sg_table sgt; | 96 | struct sg_table sgt; |
