sun6i_video.c (17996B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing) 4 * All rights reserved. 5 * Author: Yong Deng <yong.deng@magewell.com> 6 */ 7 8#include <linux/of.h> 9 10#include <media/v4l2-device.h> 11#include <media/v4l2-event.h> 12#include <media/v4l2-ioctl.h> 13#include <media/v4l2-mc.h> 14#include <media/videobuf2-dma-contig.h> 15#include <media/videobuf2-v4l2.h> 16 17#include "sun6i_csi.h" 18#include "sun6i_video.h" 19 20/* This is got from BSP sources. */ 21#define MIN_WIDTH (32) 22#define MIN_HEIGHT (32) 23#define MAX_WIDTH (4800) 24#define MAX_HEIGHT (4800) 25 26struct sun6i_csi_buffer { 27 struct vb2_v4l2_buffer vb; 28 struct list_head list; 29 30 dma_addr_t dma_addr; 31 bool queued_to_csi; 32}; 33 34static const u32 supported_pixformats[] = { 35 V4L2_PIX_FMT_SBGGR8, 36 V4L2_PIX_FMT_SGBRG8, 37 V4L2_PIX_FMT_SGRBG8, 38 V4L2_PIX_FMT_SRGGB8, 39 V4L2_PIX_FMT_SBGGR10, 40 V4L2_PIX_FMT_SGBRG10, 41 V4L2_PIX_FMT_SGRBG10, 42 V4L2_PIX_FMT_SRGGB10, 43 V4L2_PIX_FMT_SBGGR12, 44 V4L2_PIX_FMT_SGBRG12, 45 V4L2_PIX_FMT_SGRBG12, 46 V4L2_PIX_FMT_SRGGB12, 47 V4L2_PIX_FMT_YUYV, 48 V4L2_PIX_FMT_YVYU, 49 V4L2_PIX_FMT_UYVY, 50 V4L2_PIX_FMT_VYUY, 51 V4L2_PIX_FMT_NV12_16L16, 52 V4L2_PIX_FMT_NV12, 53 V4L2_PIX_FMT_NV21, 54 V4L2_PIX_FMT_YUV420, 55 V4L2_PIX_FMT_YVU420, 56 V4L2_PIX_FMT_NV16, 57 V4L2_PIX_FMT_NV61, 58 V4L2_PIX_FMT_YUV422P, 59 V4L2_PIX_FMT_RGB565, 60 V4L2_PIX_FMT_RGB565X, 61 V4L2_PIX_FMT_JPEG, 62}; 63 64static bool is_pixformat_valid(unsigned int pixformat) 65{ 66 unsigned int i; 67 68 for (i = 0; i < ARRAY_SIZE(supported_pixformats); i++) 69 if (supported_pixformats[i] == pixformat) 70 return true; 71 72 return false; 73} 74 75static struct v4l2_subdev * 76sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad) 77{ 78 struct media_pad *remote; 79 80 remote = media_entity_remote_pad(&video->pad); 81 82 if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 83 return NULL; 84 85 if (pad) 86 *pad = remote->index; 87 88 return media_entity_to_v4l2_subdev(remote->entity); 89} 90 91static int sun6i_video_queue_setup(struct vb2_queue *vq, 92 unsigned int *nbuffers, 93 unsigned int *nplanes, 94 unsigned int sizes[], 95 struct device *alloc_devs[]) 96{ 97 struct sun6i_video *video = vb2_get_drv_priv(vq); 98 unsigned int size = video->fmt.fmt.pix.sizeimage; 99 100 if (*nplanes) 101 return sizes[0] < size ? -EINVAL : 0; 102 103 *nplanes = 1; 104 sizes[0] = size; 105 106 return 0; 107} 108 109static int sun6i_video_buffer_prepare(struct vb2_buffer *vb) 110{ 111 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 112 struct sun6i_csi_buffer *buf = 113 container_of(vbuf, struct sun6i_csi_buffer, vb); 114 struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue); 115 unsigned long size = video->fmt.fmt.pix.sizeimage; 116 117 if (vb2_plane_size(vb, 0) < size) { 118 v4l2_err(video->vdev.v4l2_dev, "buffer too small (%lu < %lu)\n", 119 vb2_plane_size(vb, 0), size); 120 return -EINVAL; 121 } 122 123 vb2_set_plane_payload(vb, 0, size); 124 125 buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); 126 127 vbuf->field = video->fmt.fmt.pix.field; 128 129 return 0; 130} 131 132static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count) 133{ 134 struct sun6i_video *video = vb2_get_drv_priv(vq); 135 struct sun6i_csi_buffer *buf; 136 struct sun6i_csi_buffer *next_buf; 137 struct sun6i_csi_config config; 138 struct v4l2_subdev *subdev; 139 unsigned long flags; 140 int ret; 141 142 video->sequence = 0; 143 144 ret = media_pipeline_start(&video->vdev.entity, &video->vdev.pipe); 145 if (ret < 0) 146 goto clear_dma_queue; 147 148 if (video->mbus_code == 0) { 149 ret = -EINVAL; 150 goto stop_media_pipeline; 151 } 152 153 subdev = sun6i_video_remote_subdev(video, NULL); 154 if (!subdev) { 155 ret = -EINVAL; 156 goto stop_media_pipeline; 157 } 158 159 config.pixelformat = video->fmt.fmt.pix.pixelformat; 160 config.code = video->mbus_code; 161 config.field = video->fmt.fmt.pix.field; 162 config.width = video->fmt.fmt.pix.width; 163 config.height = video->fmt.fmt.pix.height; 164 165 ret = sun6i_csi_update_config(video->csi, &config); 166 if (ret < 0) 167 goto stop_media_pipeline; 168 169 spin_lock_irqsave(&video->dma_queue_lock, flags); 170 171 buf = list_first_entry(&video->dma_queue, 172 struct sun6i_csi_buffer, list); 173 buf->queued_to_csi = true; 174 sun6i_csi_update_buf_addr(video->csi, buf->dma_addr); 175 176 sun6i_csi_set_stream(video->csi, true); 177 178 /* 179 * CSI will lookup the next dma buffer for next frame before the 180 * the current frame done IRQ triggered. This is not documented 181 * but reported by Ondřej Jirman. 182 * The BSP code has workaround for this too. It skip to mark the 183 * first buffer as frame done for VB2 and pass the second buffer 184 * to CSI in the first frame done ISR call. Then in second frame 185 * done ISR call, it mark the first buffer as frame done for VB2 186 * and pass the third buffer to CSI. And so on. The bad thing is 187 * that the first buffer will be written twice and the first frame 188 * is dropped even the queued buffer is sufficient. 189 * So, I make some improvement here. Pass the next buffer to CSI 190 * just follow starting the CSI. In this case, the first frame 191 * will be stored in first buffer, second frame in second buffer. 192 * This method is used to avoid dropping the first frame, it 193 * would also drop frame when lacking of queued buffer. 194 */ 195 next_buf = list_next_entry(buf, list); 196 next_buf->queued_to_csi = true; 197 sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr); 198 199 spin_unlock_irqrestore(&video->dma_queue_lock, flags); 200 201 ret = v4l2_subdev_call(subdev, video, s_stream, 1); 202 if (ret && ret != -ENOIOCTLCMD) 203 goto stop_csi_stream; 204 205 return 0; 206 207stop_csi_stream: 208 sun6i_csi_set_stream(video->csi, false); 209stop_media_pipeline: 210 media_pipeline_stop(&video->vdev.entity); 211clear_dma_queue: 212 spin_lock_irqsave(&video->dma_queue_lock, flags); 213 list_for_each_entry(buf, &video->dma_queue, list) 214 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 215 INIT_LIST_HEAD(&video->dma_queue); 216 spin_unlock_irqrestore(&video->dma_queue_lock, flags); 217 218 return ret; 219} 220 221static void sun6i_video_stop_streaming(struct vb2_queue *vq) 222{ 223 struct sun6i_video *video = vb2_get_drv_priv(vq); 224 struct v4l2_subdev *subdev; 225 unsigned long flags; 226 struct sun6i_csi_buffer *buf; 227 228 subdev = sun6i_video_remote_subdev(video, NULL); 229 if (subdev) 230 v4l2_subdev_call(subdev, video, s_stream, 0); 231 232 sun6i_csi_set_stream(video->csi, false); 233 234 media_pipeline_stop(&video->vdev.entity); 235 236 /* Release all active buffers */ 237 spin_lock_irqsave(&video->dma_queue_lock, flags); 238 list_for_each_entry(buf, &video->dma_queue, list) 239 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 240 INIT_LIST_HEAD(&video->dma_queue); 241 spin_unlock_irqrestore(&video->dma_queue_lock, flags); 242} 243 244static void sun6i_video_buffer_queue(struct vb2_buffer *vb) 245{ 246 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 247 struct sun6i_csi_buffer *buf = 248 container_of(vbuf, struct sun6i_csi_buffer, vb); 249 struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue); 250 unsigned long flags; 251 252 spin_lock_irqsave(&video->dma_queue_lock, flags); 253 buf->queued_to_csi = false; 254 list_add_tail(&buf->list, &video->dma_queue); 255 spin_unlock_irqrestore(&video->dma_queue_lock, flags); 256} 257 258void sun6i_video_frame_done(struct sun6i_video *video) 259{ 260 struct sun6i_csi_buffer *buf; 261 struct sun6i_csi_buffer *next_buf; 262 struct vb2_v4l2_buffer *vbuf; 263 264 spin_lock(&video->dma_queue_lock); 265 266 buf = list_first_entry(&video->dma_queue, 267 struct sun6i_csi_buffer, list); 268 if (list_is_last(&buf->list, &video->dma_queue)) { 269 dev_dbg(video->csi->dev, "Frame dropped!\n"); 270 goto unlock; 271 } 272 273 next_buf = list_next_entry(buf, list); 274 /* If a new buffer (#next_buf) had not been queued to CSI, the old 275 * buffer (#buf) is still holding by CSI for storing the next 276 * frame. So, we queue a new buffer (#next_buf) to CSI then wait 277 * for next ISR call. 278 */ 279 if (!next_buf->queued_to_csi) { 280 next_buf->queued_to_csi = true; 281 sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr); 282 dev_dbg(video->csi->dev, "Frame dropped!\n"); 283 goto unlock; 284 } 285 286 list_del(&buf->list); 287 vbuf = &buf->vb; 288 vbuf->vb2_buf.timestamp = ktime_get_ns(); 289 vbuf->sequence = video->sequence; 290 vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); 291 292 /* Prepare buffer for next frame but one. */ 293 if (!list_is_last(&next_buf->list, &video->dma_queue)) { 294 next_buf = list_next_entry(next_buf, list); 295 next_buf->queued_to_csi = true; 296 sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr); 297 } else { 298 dev_dbg(video->csi->dev, "Next frame will be dropped!\n"); 299 } 300 301unlock: 302 video->sequence++; 303 spin_unlock(&video->dma_queue_lock); 304} 305 306static const struct vb2_ops sun6i_csi_vb2_ops = { 307 .queue_setup = sun6i_video_queue_setup, 308 .wait_prepare = vb2_ops_wait_prepare, 309 .wait_finish = vb2_ops_wait_finish, 310 .buf_prepare = sun6i_video_buffer_prepare, 311 .start_streaming = sun6i_video_start_streaming, 312 .stop_streaming = sun6i_video_stop_streaming, 313 .buf_queue = sun6i_video_buffer_queue, 314}; 315 316static int vidioc_querycap(struct file *file, void *priv, 317 struct v4l2_capability *cap) 318{ 319 struct sun6i_video *video = video_drvdata(file); 320 321 strscpy(cap->driver, "sun6i-video", sizeof(cap->driver)); 322 strscpy(cap->card, video->vdev.name, sizeof(cap->card)); 323 snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", 324 video->csi->dev->of_node->name); 325 326 return 0; 327} 328 329static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, 330 struct v4l2_fmtdesc *f) 331{ 332 u32 index = f->index; 333 334 if (index >= ARRAY_SIZE(supported_pixformats)) 335 return -EINVAL; 336 337 f->pixelformat = supported_pixformats[index]; 338 339 return 0; 340} 341 342static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, 343 struct v4l2_format *fmt) 344{ 345 struct sun6i_video *video = video_drvdata(file); 346 347 *fmt = video->fmt; 348 349 return 0; 350} 351 352static int sun6i_video_try_fmt(struct sun6i_video *video, 353 struct v4l2_format *f) 354{ 355 struct v4l2_pix_format *pixfmt = &f->fmt.pix; 356 int bpp; 357 358 if (!is_pixformat_valid(pixfmt->pixelformat)) 359 pixfmt->pixelformat = supported_pixformats[0]; 360 361 v4l_bound_align_image(&pixfmt->width, MIN_WIDTH, MAX_WIDTH, 1, 362 &pixfmt->height, MIN_HEIGHT, MAX_WIDTH, 1, 1); 363 364 bpp = sun6i_csi_get_bpp(pixfmt->pixelformat); 365 pixfmt->bytesperline = (pixfmt->width * bpp) >> 3; 366 pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; 367 368 if (pixfmt->field == V4L2_FIELD_ANY) 369 pixfmt->field = V4L2_FIELD_NONE; 370 371 if (pixfmt->pixelformat == V4L2_PIX_FMT_JPEG) 372 pixfmt->colorspace = V4L2_COLORSPACE_JPEG; 373 else 374 pixfmt->colorspace = V4L2_COLORSPACE_SRGB; 375 376 pixfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; 377 pixfmt->quantization = V4L2_QUANTIZATION_DEFAULT; 378 pixfmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; 379 380 return 0; 381} 382 383static int sun6i_video_set_fmt(struct sun6i_video *video, struct v4l2_format *f) 384{ 385 int ret; 386 387 ret = sun6i_video_try_fmt(video, f); 388 if (ret) 389 return ret; 390 391 video->fmt = *f; 392 393 return 0; 394} 395 396static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, 397 struct v4l2_format *f) 398{ 399 struct sun6i_video *video = video_drvdata(file); 400 401 if (vb2_is_busy(&video->vb2_vidq)) 402 return -EBUSY; 403 404 return sun6i_video_set_fmt(video, f); 405} 406 407static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 408 struct v4l2_format *f) 409{ 410 struct sun6i_video *video = video_drvdata(file); 411 412 return sun6i_video_try_fmt(video, f); 413} 414 415static int vidioc_enum_input(struct file *file, void *fh, 416 struct v4l2_input *inp) 417{ 418 if (inp->index != 0) 419 return -EINVAL; 420 421 strscpy(inp->name, "camera", sizeof(inp->name)); 422 inp->type = V4L2_INPUT_TYPE_CAMERA; 423 424 return 0; 425} 426 427static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) 428{ 429 *i = 0; 430 431 return 0; 432} 433 434static int vidioc_s_input(struct file *file, void *fh, unsigned int i) 435{ 436 if (i != 0) 437 return -EINVAL; 438 439 return 0; 440} 441 442static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = { 443 .vidioc_querycap = vidioc_querycap, 444 .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, 445 .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, 446 .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, 447 .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, 448 449 .vidioc_enum_input = vidioc_enum_input, 450 .vidioc_s_input = vidioc_s_input, 451 .vidioc_g_input = vidioc_g_input, 452 453 .vidioc_reqbufs = vb2_ioctl_reqbufs, 454 .vidioc_querybuf = vb2_ioctl_querybuf, 455 .vidioc_qbuf = vb2_ioctl_qbuf, 456 .vidioc_expbuf = vb2_ioctl_expbuf, 457 .vidioc_dqbuf = vb2_ioctl_dqbuf, 458 .vidioc_create_bufs = vb2_ioctl_create_bufs, 459 .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 460 .vidioc_streamon = vb2_ioctl_streamon, 461 .vidioc_streamoff = vb2_ioctl_streamoff, 462 463 .vidioc_log_status = v4l2_ctrl_log_status, 464 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 465 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 466}; 467 468/* ----------------------------------------------------------------------------- 469 * V4L2 file operations 470 */ 471static int sun6i_video_open(struct file *file) 472{ 473 struct sun6i_video *video = video_drvdata(file); 474 int ret = 0; 475 476 if (mutex_lock_interruptible(&video->lock)) 477 return -ERESTARTSYS; 478 479 ret = v4l2_fh_open(file); 480 if (ret < 0) 481 goto unlock; 482 483 ret = v4l2_pipeline_pm_get(&video->vdev.entity); 484 if (ret < 0) 485 goto fh_release; 486 487 /* check if already powered */ 488 if (!v4l2_fh_is_singular_file(file)) 489 goto unlock; 490 491 ret = sun6i_csi_set_power(video->csi, true); 492 if (ret < 0) 493 goto fh_release; 494 495 mutex_unlock(&video->lock); 496 return 0; 497 498fh_release: 499 v4l2_fh_release(file); 500unlock: 501 mutex_unlock(&video->lock); 502 return ret; 503} 504 505static int sun6i_video_close(struct file *file) 506{ 507 struct sun6i_video *video = video_drvdata(file); 508 bool last_fh; 509 510 mutex_lock(&video->lock); 511 512 last_fh = v4l2_fh_is_singular_file(file); 513 514 _vb2_fop_release(file, NULL); 515 516 v4l2_pipeline_pm_put(&video->vdev.entity); 517 518 if (last_fh) 519 sun6i_csi_set_power(video->csi, false); 520 521 mutex_unlock(&video->lock); 522 523 return 0; 524} 525 526static const struct v4l2_file_operations sun6i_video_fops = { 527 .owner = THIS_MODULE, 528 .open = sun6i_video_open, 529 .release = sun6i_video_close, 530 .unlocked_ioctl = video_ioctl2, 531 .mmap = vb2_fop_mmap, 532 .poll = vb2_fop_poll 533}; 534 535/* ----------------------------------------------------------------------------- 536 * Media Operations 537 */ 538static int sun6i_video_link_validate_get_format(struct media_pad *pad, 539 struct v4l2_subdev_format *fmt) 540{ 541 if (is_media_entity_v4l2_subdev(pad->entity)) { 542 struct v4l2_subdev *sd = 543 media_entity_to_v4l2_subdev(pad->entity); 544 545 fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; 546 fmt->pad = pad->index; 547 return v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); 548 } 549 550 return -EINVAL; 551} 552 553static int sun6i_video_link_validate(struct media_link *link) 554{ 555 struct video_device *vdev = container_of(link->sink->entity, 556 struct video_device, entity); 557 struct sun6i_video *video = video_get_drvdata(vdev); 558 struct v4l2_subdev_format source_fmt; 559 int ret; 560 561 video->mbus_code = 0; 562 563 if (!media_entity_remote_pad(link->sink->entity->pads)) { 564 dev_info(video->csi->dev, 565 "video node %s pad not connected\n", vdev->name); 566 return -ENOLINK; 567 } 568 569 ret = sun6i_video_link_validate_get_format(link->source, &source_fmt); 570 if (ret < 0) 571 return ret; 572 573 if (!sun6i_csi_is_format_supported(video->csi, 574 video->fmt.fmt.pix.pixelformat, 575 source_fmt.format.code)) { 576 dev_err(video->csi->dev, 577 "Unsupported pixformat: 0x%x with mbus code: 0x%x!\n", 578 video->fmt.fmt.pix.pixelformat, 579 source_fmt.format.code); 580 return -EPIPE; 581 } 582 583 if (source_fmt.format.width != video->fmt.fmt.pix.width || 584 source_fmt.format.height != video->fmt.fmt.pix.height) { 585 dev_err(video->csi->dev, 586 "Wrong width or height %ux%u (%ux%u expected)\n", 587 video->fmt.fmt.pix.width, video->fmt.fmt.pix.height, 588 source_fmt.format.width, source_fmt.format.height); 589 return -EPIPE; 590 } 591 592 video->mbus_code = source_fmt.format.code; 593 594 return 0; 595} 596 597static const struct media_entity_operations sun6i_video_media_ops = { 598 .link_validate = sun6i_video_link_validate 599}; 600 601int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi, 602 const char *name) 603{ 604 struct video_device *vdev = &video->vdev; 605 struct vb2_queue *vidq = &video->vb2_vidq; 606 struct v4l2_format fmt = { 0 }; 607 int ret; 608 609 video->csi = csi; 610 611 /* Initialize the media entity... */ 612 video->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; 613 vdev->entity.ops = &sun6i_video_media_ops; 614 ret = media_entity_pads_init(&vdev->entity, 1, &video->pad); 615 if (ret < 0) 616 return ret; 617 618 mutex_init(&video->lock); 619 620 INIT_LIST_HEAD(&video->dma_queue); 621 spin_lock_init(&video->dma_queue_lock); 622 623 video->sequence = 0; 624 625 /* Setup default format */ 626 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 627 fmt.fmt.pix.pixelformat = supported_pixformats[0]; 628 fmt.fmt.pix.width = 1280; 629 fmt.fmt.pix.height = 720; 630 fmt.fmt.pix.field = V4L2_FIELD_NONE; 631 sun6i_video_set_fmt(video, &fmt); 632 633 /* Initialize videobuf2 queue */ 634 vidq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 635 vidq->io_modes = VB2_MMAP | VB2_DMABUF; 636 vidq->drv_priv = video; 637 vidq->buf_struct_size = sizeof(struct sun6i_csi_buffer); 638 vidq->ops = &sun6i_csi_vb2_ops; 639 vidq->mem_ops = &vb2_dma_contig_memops; 640 vidq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 641 vidq->lock = &video->lock; 642 /* Make sure non-dropped frame */ 643 vidq->min_buffers_needed = 3; 644 vidq->dev = csi->dev; 645 646 ret = vb2_queue_init(vidq); 647 if (ret) { 648 v4l2_err(&csi->v4l2_dev, "vb2_queue_init failed: %d\n", ret); 649 goto clean_entity; 650 } 651 652 /* Register video device */ 653 strscpy(vdev->name, name, sizeof(vdev->name)); 654 vdev->release = video_device_release_empty; 655 vdev->fops = &sun6i_video_fops; 656 vdev->ioctl_ops = &sun6i_video_ioctl_ops; 657 vdev->vfl_type = VFL_TYPE_VIDEO; 658 vdev->vfl_dir = VFL_DIR_RX; 659 vdev->v4l2_dev = &csi->v4l2_dev; 660 vdev->queue = vidq; 661 vdev->lock = &video->lock; 662 vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE; 663 video_set_drvdata(vdev, video); 664 665 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 666 if (ret < 0) { 667 v4l2_err(&csi->v4l2_dev, 668 "video_register_device failed: %d\n", ret); 669 goto clean_entity; 670 } 671 672 return 0; 673 674clean_entity: 675 media_entity_cleanup(&video->vdev.entity); 676 mutex_destroy(&video->lock); 677 return ret; 678} 679 680void sun6i_video_cleanup(struct sun6i_video *video) 681{ 682 vb2_video_unregister_device(&video->vdev); 683 media_entity_cleanup(&video->vdev.entity); 684 mutex_destroy(&video->lock); 685}