1/* 2 * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver 3 * 4 * FIMC-IS ISP video input and video output DMA interface driver 5 * 6 * Copyright (C) 2013 Samsung Electronics Co., Ltd. 7 * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> 8 * 9 * The hardware handling code derived from a driver written by 10 * Younghwan Joo <yhwan.joo@samsung.com>. 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License version 2 as 14 * published by the Free Software Foundation. 15 */ 16 17#include <linux/bitops.h> 18#include <linux/device.h> 19#include <linux/delay.h> 20#include <linux/errno.h> 21#include <linux/kernel.h> 22#include <linux/module.h> 23#include <linux/types.h> 24#include <linux/printk.h> 25#include <linux/pm_runtime.h> 26#include <linux/slab.h> 27#include <linux/videodev2.h> 28 29#include <media/v4l2-device.h> 30#include <media/v4l2-ioctl.h> 31#include <media/videobuf2-v4l2.h> 32#include <media/videobuf2-dma-contig.h> 33#include <media/exynos-fimc.h> 34 35#include "common.h" 36#include "media-dev.h" 37#include "fimc-is.h" 38#include "fimc-isp-video.h" 39#include "fimc-is-param.h" 40 41static int isp_video_capture_queue_setup(struct vb2_queue *vq, 42 const void *parg, 43 unsigned int *num_buffers, unsigned int *num_planes, 44 unsigned int sizes[], void *allocators[]) 45{ 46 const struct v4l2_format *pfmt = parg; 47 struct fimc_isp *isp = vb2_get_drv_priv(vq); 48 struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt; 49 const struct v4l2_pix_format_mplane *pixm = NULL; 50 const struct fimc_fmt *fmt; 51 unsigned int wh, i; 52 53 if (pfmt) { 54 pixm = &pfmt->fmt.pix_mp; 55 fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1); 56 wh = pixm->width * pixm->height; 57 } else { 58 fmt = isp->video_capture.format; 59 wh = vid_fmt->width * vid_fmt->height; 60 } 61 62 if (fmt == NULL) 63 return -EINVAL; 64 65 *num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN, 66 FIMC_ISP_REQ_BUFS_MAX); 67 *num_planes = fmt->memplanes; 68 69 for (i = 0; i < fmt->memplanes; i++) { 70 unsigned int size = (wh * fmt->depth[i]) / 8; 71 if (pixm) 72 sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); 73 else 74 sizes[i] = size; 75 allocators[i] = isp->alloc_ctx; 76 } 77 78 return 0; 79} 80 81static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is) 82{ 83 return &__get_curr_is_config(is)->isp.dma2_output; 84} 85 86static int isp_video_capture_start_streaming(struct vb2_queue *q, 87 unsigned int count) 88{ 89 struct fimc_isp *isp = vb2_get_drv_priv(q); 90 struct fimc_is *is = fimc_isp_to_is(isp); 91 struct param_dma_output *dma = __get_isp_dma2(is); 92 struct fimc_is_video *video = &isp->video_capture; 93 int ret; 94 95 if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) || 96 test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) 97 return 0; 98 99 100 dma->cmd = DMA_OUTPUT_COMMAND_ENABLE; 101 dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE; 102 dma->buffer_address = is->is_dma_p_region + 103 DMA2_OUTPUT_ADDR_ARRAY_OFFS; 104 dma->buffer_number = video->reqbufs_count; 105 dma->dma_out_mask = video->buf_mask; 106 107 isp_dbg(2, &video->ve.vdev, 108 "buf_count: %d, planes: %d, dma addr table: %#x\n", 109 video->buf_count, video->format->memplanes, 110 dma->buffer_address); 111 112 fimc_is_mem_barrier(); 113 114 fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); 115 __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); 116 117 ret = fimc_is_itf_s_param(is, false); 118 if (ret < 0) 119 return ret; 120 121 ret = fimc_pipeline_call(&video->ve, set_stream, 1); 122 if (ret < 0) 123 return ret; 124 125 set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); 126 return ret; 127} 128 129static void isp_video_capture_stop_streaming(struct vb2_queue *q) 130{ 131 struct fimc_isp *isp = vb2_get_drv_priv(q); 132 struct fimc_is *is = fimc_isp_to_is(isp); 133 struct param_dma_output *dma = __get_isp_dma2(is); 134 int ret; 135 136 ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0); 137 if (ret < 0) 138 return; 139 140 dma->cmd = DMA_OUTPUT_COMMAND_DISABLE; 141 dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE; 142 dma->buffer_number = 0; 143 dma->buffer_address = 0; 144 dma->dma_out_mask = 0; 145 146 fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); 147 __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); 148 149 ret = fimc_is_itf_s_param(is, false); 150 if (ret < 0) 151 dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__); 152 153 fimc_is_hw_set_isp_buf_mask(is, 0); 154 155 clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); 156 clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); 157 158 isp->video_capture.buf_count = 0; 159} 160 161static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb) 162{ 163 struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); 164 struct fimc_is_video *video = &isp->video_capture; 165 int i; 166 167 if (video->format == NULL) 168 return -EINVAL; 169 170 for (i = 0; i < video->format->memplanes; i++) { 171 unsigned long size = video->pixfmt.plane_fmt[i].sizeimage; 172 173 if (vb2_plane_size(vb, i) < size) { 174 v4l2_err(&video->ve.vdev, 175 "User buffer too small (%ld < %ld)\n", 176 vb2_plane_size(vb, i), size); 177 return -EINVAL; 178 } 179 vb2_set_plane_payload(vb, i, size); 180 } 181 182 /* Check if we get one of the already known buffers. */ 183 if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { 184 dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); 185 int i; 186 187 for (i = 0; i < video->buf_count; i++) 188 if (video->buffers[i]->dma_addr[0] == dma_addr) 189 return 0; 190 return -ENXIO; 191 } 192 193 return 0; 194} 195 196static void isp_video_capture_buffer_queue(struct vb2_buffer *vb) 197{ 198 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 199 struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); 200 struct fimc_is_video *video = &isp->video_capture; 201 struct fimc_is *is = fimc_isp_to_is(isp); 202 struct isp_video_buf *ivb = to_isp_video_buf(vbuf); 203 unsigned long flags; 204 unsigned int i; 205 206 if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { 207 spin_lock_irqsave(&is->slock, flags); 208 video->buf_mask |= BIT(ivb->index); 209 spin_unlock_irqrestore(&is->slock, flags); 210 } else { 211 unsigned int num_planes = video->format->memplanes; 212 213 ivb->index = video->buf_count; 214 video->buffers[ivb->index] = ivb; 215 216 for (i = 0; i < num_planes; i++) { 217 int buf_index = ivb->index * num_planes + i; 218 219 ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); 220 is->is_p_region->shared[32 + buf_index] = 221 ivb->dma_addr[i]; 222 223 isp_dbg(2, &video->ve.vdev, 224 "dma_buf %pad (%d/%d/%d) addr: %pad\n", 225 &buf_index, ivb->index, i, vb->index, 226 &ivb->dma_addr[i]); 227 } 228 229 if (++video->buf_count < video->reqbufs_count) 230 return; 231 232 video->buf_mask = (1UL << video->buf_count) - 1; 233 set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); 234 } 235 236 if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) 237 isp_video_capture_start_streaming(vb->vb2_queue, 0); 238} 239 240/* 241 * FIMC-IS ISP input and output DMA interface interrupt handler. 242 * Locking: called with is->slock spinlock held. 243 */ 244void fimc_isp_video_irq_handler(struct fimc_is *is) 245{ 246 struct fimc_is_video *video = &is->isp.video_capture; 247 struct vb2_v4l2_buffer *vbuf; 248 int buf_index; 249 250 /* TODO: Ensure the DMA is really stopped in stop_streaming callback */ 251 if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state)) 252 return; 253 254 buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count; 255 vbuf = &video->buffers[buf_index]->vb; 256 257 v4l2_get_timestamp(&vbuf->timestamp); 258 vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); 259 260 video->buf_mask &= ~BIT(buf_index); 261 fimc_is_hw_set_isp_buf_mask(is, video->buf_mask); 262} 263 264static const struct vb2_ops isp_video_capture_qops = { 265 .queue_setup = isp_video_capture_queue_setup, 266 .buf_prepare = isp_video_capture_buffer_prepare, 267 .buf_queue = isp_video_capture_buffer_queue, 268 .wait_prepare = vb2_ops_wait_prepare, 269 .wait_finish = vb2_ops_wait_finish, 270 .start_streaming = isp_video_capture_start_streaming, 271 .stop_streaming = isp_video_capture_stop_streaming, 272}; 273 274static int isp_video_open(struct file *file) 275{ 276 struct fimc_isp *isp = video_drvdata(file); 277 struct exynos_video_entity *ve = &isp->video_capture.ve; 278 struct media_entity *me = &ve->vdev.entity; 279 int ret; 280 281 if (mutex_lock_interruptible(&isp->video_lock)) 282 return -ERESTARTSYS; 283 284 ret = v4l2_fh_open(file); 285 if (ret < 0) 286 goto unlock; 287 288 ret = pm_runtime_get_sync(&isp->pdev->dev); 289 if (ret < 0) 290 goto rel_fh; 291 292 if (v4l2_fh_is_singular_file(file)) { 293 mutex_lock(&me->parent->graph_mutex); 294 295 ret = fimc_pipeline_call(ve, open, me, true); 296 297 /* Mark the video pipeline as in use. */ 298 if (ret == 0) 299 me->use_count++; 300 301 mutex_unlock(&me->parent->graph_mutex); 302 } 303 if (!ret) 304 goto unlock; 305rel_fh: 306 v4l2_fh_release(file); 307unlock: 308 mutex_unlock(&isp->video_lock); 309 return ret; 310} 311 312static int isp_video_release(struct file *file) 313{ 314 struct fimc_isp *isp = video_drvdata(file); 315 struct fimc_is_video *ivc = &isp->video_capture; 316 struct media_entity *entity = &ivc->ve.vdev.entity; 317 struct media_device *mdev = entity->parent; 318 319 mutex_lock(&isp->video_lock); 320 321 if (v4l2_fh_is_singular_file(file) && ivc->streaming) { 322 media_entity_pipeline_stop(entity); 323 ivc->streaming = 0; 324 } 325 326 vb2_fop_release(file); 327 328 if (v4l2_fh_is_singular_file(file)) { 329 fimc_pipeline_call(&ivc->ve, close); 330 331 mutex_lock(&mdev->graph_mutex); 332 entity->use_count--; 333 mutex_unlock(&mdev->graph_mutex); 334 } 335 336 pm_runtime_put(&isp->pdev->dev); 337 mutex_unlock(&isp->video_lock); 338 339 return 0; 340} 341 342static const struct v4l2_file_operations isp_video_fops = { 343 .owner = THIS_MODULE, 344 .open = isp_video_open, 345 .release = isp_video_release, 346 .poll = vb2_fop_poll, 347 .unlocked_ioctl = video_ioctl2, 348 .mmap = vb2_fop_mmap, 349}; 350 351/* 352 * Video node ioctl operations 353 */ 354static int isp_video_querycap(struct file *file, void *priv, 355 struct v4l2_capability *cap) 356{ 357 struct fimc_isp *isp = video_drvdata(file); 358 359 __fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING); 360 return 0; 361} 362 363static int isp_video_enum_fmt_mplane(struct file *file, void *priv, 364 struct v4l2_fmtdesc *f) 365{ 366 const struct fimc_fmt *fmt; 367 368 if (f->index >= FIMC_ISP_NUM_FORMATS) 369 return -EINVAL; 370 371 fmt = fimc_isp_find_format(NULL, NULL, f->index); 372 if (WARN_ON(fmt == NULL)) 373 return -EINVAL; 374 375 strlcpy(f->description, fmt->name, sizeof(f->description)); 376 f->pixelformat = fmt->fourcc; 377 378 return 0; 379} 380 381static int isp_video_g_fmt_mplane(struct file *file, void *fh, 382 struct v4l2_format *f) 383{ 384 struct fimc_isp *isp = video_drvdata(file); 385 386 f->fmt.pix_mp = isp->video_capture.pixfmt; 387 return 0; 388} 389 390static void __isp_video_try_fmt(struct fimc_isp *isp, 391 struct v4l2_pix_format_mplane *pixm, 392 const struct fimc_fmt **fmt) 393{ 394 *fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); 395 396 pixm->colorspace = V4L2_COLORSPACE_SRGB; 397 pixm->field = V4L2_FIELD_NONE; 398 pixm->num_planes = (*fmt)->memplanes; 399 pixm->pixelformat = (*fmt)->fourcc; 400 /* 401 * TODO: double check with the docmentation these width/height 402 * constraints are correct. 403 */ 404 v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN, 405 FIMC_ISP_SOURCE_WIDTH_MAX, 3, 406 &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN, 407 FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0); 408} 409 410static int isp_video_try_fmt_mplane(struct file *file, void *fh, 411 struct v4l2_format *f) 412{ 413 struct fimc_isp *isp = video_drvdata(file); 414 415 __isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL); 416 return 0; 417} 418 419static int isp_video_s_fmt_mplane(struct file *file, void *priv, 420 struct v4l2_format *f) 421{ 422 struct fimc_isp *isp = video_drvdata(file); 423 struct fimc_is *is = fimc_isp_to_is(isp); 424 struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; 425 const struct fimc_fmt *ifmt = NULL; 426 struct param_dma_output *dma = __get_isp_dma2(is); 427 428 __isp_video_try_fmt(isp, pixm, &ifmt); 429 430 if (WARN_ON(ifmt == NULL)) 431 return -EINVAL; 432 433 dma->format = DMA_OUTPUT_FORMAT_BAYER; 434 dma->order = DMA_OUTPUT_ORDER_GB_BG; 435 dma->plane = ifmt->memplanes; 436 dma->bitwidth = ifmt->depth[0]; 437 dma->width = pixm->width; 438 dma->height = pixm->height; 439 440 fimc_is_mem_barrier(); 441 442 isp->video_capture.format = ifmt; 443 isp->video_capture.pixfmt = *pixm; 444 445 return 0; 446} 447 448/* 449 * Check for source/sink format differences at each link. 450 * Return 0 if the formats match or -EPIPE otherwise. 451 */ 452static int isp_video_pipeline_validate(struct fimc_isp *isp) 453{ 454 struct v4l2_subdev *sd = &isp->subdev; 455 struct v4l2_subdev_format sink_fmt, src_fmt; 456 struct media_pad *pad; 457 int ret; 458 459 while (1) { 460 /* Retrieve format at the sink pad */ 461 pad = &sd->entity.pads[0]; 462 if (!(pad->flags & MEDIA_PAD_FL_SINK)) 463 break; 464 sink_fmt.pad = pad->index; 465 sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 466 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); 467 if (ret < 0 && ret != -ENOIOCTLCMD) 468 return -EPIPE; 469 470 /* Retrieve format at the source pad */ 471 pad = media_entity_remote_pad(pad); 472 if (pad == NULL || 473 media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) 474 break; 475 476 sd = media_entity_to_v4l2_subdev(pad->entity); 477 src_fmt.pad = pad->index; 478 src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 479 ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); 480 if (ret < 0 && ret != -ENOIOCTLCMD) 481 return -EPIPE; 482 483 if (src_fmt.format.width != sink_fmt.format.width || 484 src_fmt.format.height != sink_fmt.format.height || 485 src_fmt.format.code != sink_fmt.format.code) 486 return -EPIPE; 487 } 488 489 return 0; 490} 491 492static int isp_video_streamon(struct file *file, void *priv, 493 enum v4l2_buf_type type) 494{ 495 struct fimc_isp *isp = video_drvdata(file); 496 struct exynos_video_entity *ve = &isp->video_capture.ve; 497 struct media_entity *me = &ve->vdev.entity; 498 int ret; 499 500 ret = media_entity_pipeline_start(me, &ve->pipe->mp); 501 if (ret < 0) 502 return ret; 503 504 ret = isp_video_pipeline_validate(isp); 505 if (ret < 0) 506 goto p_stop; 507 508 ret = vb2_ioctl_streamon(file, priv, type); 509 if (ret < 0) 510 goto p_stop; 511 512 isp->video_capture.streaming = 1; 513 return 0; 514p_stop: 515 media_entity_pipeline_stop(me); 516 return ret; 517} 518 519static int isp_video_streamoff(struct file *file, void *priv, 520 enum v4l2_buf_type type) 521{ 522 struct fimc_isp *isp = video_drvdata(file); 523 struct fimc_is_video *video = &isp->video_capture; 524 int ret; 525 526 ret = vb2_ioctl_streamoff(file, priv, type); 527 if (ret < 0) 528 return ret; 529 530 media_entity_pipeline_stop(&video->ve.vdev.entity); 531 video->streaming = 0; 532 return 0; 533} 534 535static int isp_video_reqbufs(struct file *file, void *priv, 536 struct v4l2_requestbuffers *rb) 537{ 538 struct fimc_isp *isp = video_drvdata(file); 539 int ret; 540 541 ret = vb2_ioctl_reqbufs(file, priv, rb); 542 if (ret < 0) 543 return ret; 544 545 if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) { 546 rb->count = 0; 547 vb2_ioctl_reqbufs(file, priv, rb); 548 ret = -ENOMEM; 549 } 550 551 isp->video_capture.reqbufs_count = rb->count; 552 return ret; 553} 554 555static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { 556 .vidioc_querycap = isp_video_querycap, 557 .vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane, 558 .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane, 559 .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane, 560 .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane, 561 .vidioc_reqbufs = isp_video_reqbufs, 562 .vidioc_querybuf = vb2_ioctl_querybuf, 563 .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 564 .vidioc_create_bufs = vb2_ioctl_create_bufs, 565 .vidioc_qbuf = vb2_ioctl_qbuf, 566 .vidioc_dqbuf = vb2_ioctl_dqbuf, 567 .vidioc_streamon = isp_video_streamon, 568 .vidioc_streamoff = isp_video_streamoff, 569}; 570 571int fimc_isp_video_device_register(struct fimc_isp *isp, 572 struct v4l2_device *v4l2_dev, 573 enum v4l2_buf_type type) 574{ 575 struct vb2_queue *q = &isp->video_capture.vb_queue; 576 struct fimc_is_video *iv; 577 struct video_device *vdev; 578 int ret; 579 580 if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 581 iv = &isp->video_capture; 582 else 583 return -ENOSYS; 584 585 mutex_init(&isp->video_lock); 586 INIT_LIST_HEAD(&iv->pending_buf_q); 587 INIT_LIST_HEAD(&iv->active_buf_q); 588 iv->format = fimc_isp_find_format(NULL, NULL, 0); 589 iv->pixfmt.width = IS_DEFAULT_WIDTH; 590 iv->pixfmt.height = IS_DEFAULT_HEIGHT; 591 iv->pixfmt.pixelformat = iv->format->fourcc; 592 iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB; 593 iv->reqbufs_count = 0; 594 595 memset(q, 0, sizeof(*q)); 596 q->type = type; 597 q->io_modes = VB2_MMAP | VB2_USERPTR; 598 q->ops = &isp_video_capture_qops; 599 q->mem_ops = &vb2_dma_contig_memops; 600 q->buf_struct_size = sizeof(struct isp_video_buf); 601 q->drv_priv = isp; 602 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 603 q->lock = &isp->video_lock; 604 605 ret = vb2_queue_init(q); 606 if (ret < 0) 607 return ret; 608 609 vdev = &iv->ve.vdev; 610 memset(vdev, 0, sizeof(*vdev)); 611 snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s", 612 type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? 613 "capture" : "output"); 614 vdev->queue = q; 615 vdev->fops = &isp_video_fops; 616 vdev->ioctl_ops = &isp_video_ioctl_ops; 617 vdev->v4l2_dev = v4l2_dev; 618 vdev->minor = -1; 619 vdev->release = video_device_release_empty; 620 vdev->lock = &isp->video_lock; 621 622 iv->pad.flags = MEDIA_PAD_FL_SINK; 623 ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0); 624 if (ret < 0) 625 return ret; 626 627 video_set_drvdata(vdev, isp); 628 629 ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); 630 if (ret < 0) { 631 media_entity_cleanup(&vdev->entity); 632 return ret; 633 } 634 635 v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", 636 vdev->name, video_device_node_name(vdev)); 637 638 return 0; 639} 640 641void fimc_isp_video_device_unregister(struct fimc_isp *isp, 642 enum v4l2_buf_type type) 643{ 644 struct exynos_video_entity *ve; 645 646 if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 647 ve = &isp->video_capture.ve; 648 else 649 return; 650 651 mutex_lock(&isp->video_lock); 652 653 if (video_is_registered(&ve->vdev)) { 654 video_unregister_device(&ve->vdev); 655 media_entity_cleanup(&ve->vdev.entity); 656 ve->pipe = NULL; 657 } 658 659 mutex_unlock(&isp->video_lock); 660} 661