1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * TI OMAP4 ISS V4L2 Driver - Generic video node 4 * 5 * Copyright (C) 2012 Texas Instruments, Inc. 6 * 7 * Author: Sergio Aguirre <sergio.a.aguirre@gmail.com> 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/mm.h> 12 #include <linux/pagemap.h> 13 #include <linux/sched.h> 14 #include <linux/slab.h> 15 #include <linux/vmalloc.h> 16 #include <linux/module.h> 17 18 #include <media/v4l2-dev.h> 19 #include <media/v4l2-ioctl.h> 20 #include <media/v4l2-mc.h> 21 22 #include "iss_video.h" 23 #include "iss.h" 24 25 /* ----------------------------------------------------------------------------- 26 * Helper functions 27 */ 28 29 static struct iss_format_info formats[] = { 30 { MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, 31 MEDIA_BUS_FMT_Y8_1X8, MEDIA_BUS_FMT_Y8_1X8, 32 V4L2_PIX_FMT_GREY, 8, }, 33 { MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y10_1X10, 34 MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y8_1X8, 35 V4L2_PIX_FMT_Y10, 10, }, 36 { MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y10_1X10, 37 MEDIA_BUS_FMT_Y12_1X12, MEDIA_BUS_FMT_Y8_1X8, 38 V4L2_PIX_FMT_Y12, 12, }, 39 { MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, 40 MEDIA_BUS_FMT_SBGGR8_1X8, MEDIA_BUS_FMT_SBGGR8_1X8, 41 V4L2_PIX_FMT_SBGGR8, 8, }, 42 { MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, 43 MEDIA_BUS_FMT_SGBRG8_1X8, MEDIA_BUS_FMT_SGBRG8_1X8, 44 V4L2_PIX_FMT_SGBRG8, 8, }, 45 { MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, 46 MEDIA_BUS_FMT_SGRBG8_1X8, MEDIA_BUS_FMT_SGRBG8_1X8, 47 V4L2_PIX_FMT_SGRBG8, 8, }, 48 { MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, 49 MEDIA_BUS_FMT_SRGGB8_1X8, MEDIA_BUS_FMT_SRGGB8_1X8, 50 V4L2_PIX_FMT_SRGGB8, 8, }, 51 { MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 52 MEDIA_BUS_FMT_SGRBG10_1X10, 0, 53 V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, 54 { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, 55 MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR8_1X8, 56 V4L2_PIX_FMT_SBGGR10, 10, }, 57 { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, 58 MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SGBRG8_1X8, 59 V4L2_PIX_FMT_SGBRG10, 10, }, 60 { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, 61 MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SGRBG8_1X8, 62 V4L2_PIX_FMT_SGRBG10, 10, }, 63 { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, 64 MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SRGGB8_1X8, 65 V4L2_PIX_FMT_SRGGB10, 10, }, 66 { MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR10_1X10, 67 MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SBGGR8_1X8, 68 V4L2_PIX_FMT_SBGGR12, 12, }, 69 { MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG10_1X10, 70 MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGBRG8_1X8, 71 V4L2_PIX_FMT_SGBRG12, 12, }, 72 { MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG10_1X10, 73 MEDIA_BUS_FMT_SGRBG12_1X12, MEDIA_BUS_FMT_SGRBG8_1X8, 74 V4L2_PIX_FMT_SGRBG12, 12, }, 75 { MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB10_1X10, 76 MEDIA_BUS_FMT_SRGGB12_1X12, MEDIA_BUS_FMT_SRGGB8_1X8, 77 V4L2_PIX_FMT_SRGGB12, 12, }, 78 { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16, 79 MEDIA_BUS_FMT_UYVY8_1X16, 0, 80 V4L2_PIX_FMT_UYVY, 16, }, 81 { MEDIA_BUS_FMT_YUYV8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, 82 MEDIA_BUS_FMT_YUYV8_1X16, 0, 83 V4L2_PIX_FMT_YUYV, 16, }, 84 { MEDIA_BUS_FMT_YUYV8_1_5X8, MEDIA_BUS_FMT_YUYV8_1_5X8, 85 MEDIA_BUS_FMT_YUYV8_1_5X8, 0, 86 V4L2_PIX_FMT_NV12, 8, }, 87 }; 88 89 const struct iss_format_info * 90 omap4iss_video_format_info(u32 code) 91 { 92 unsigned int i; 93 94 for (i = 0; i < ARRAY_SIZE(formats); ++i) { 95 if (formats[i].code == code) 96 return &formats[i]; 97 } 98 99 return NULL; 100 } 101 102 /* 103 * iss_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format 104 * @video: ISS video instance 105 * @mbus: v4l2_mbus_framefmt format (input) 106 * @pix: v4l2_pix_format format (output) 107 * 108 * Fill the output pix structure with information from the input mbus format. 109 * The bytesperline and sizeimage fields are computed from the requested bytes 110 * per line value in the pix format and information from the video instance. 111 * 112 * Return the number of padding bytes at end of line. 113 */ 114 static unsigned int iss_video_mbus_to_pix(const struct iss_video *video, 115 const struct v4l2_mbus_framefmt *mbus, 116 struct v4l2_pix_format *pix) 117 { 118 unsigned int bpl = pix->bytesperline; 119 unsigned int min_bpl; 120 unsigned int i; 121 122 memset(pix, 0, sizeof(*pix)); 123 pix->width = mbus->width; 124 pix->height = mbus->height; 125 126 /* 127 * Skip the last format in the loop so that it will be selected if no 128 * match is found. 129 */ 130 for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) { 131 if (formats[i].code == mbus->code) 132 break; 133 } 134 135 min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8; 136 137 /* 138 * Clamp the requested bytes per line value. If the maximum bytes per 139 * line value is zero, the module doesn't support user configurable line 140 * sizes. Override the requested value with the minimum in that case. 141 */ 142 if (video->bpl_max) 143 bpl = clamp(bpl, min_bpl, video->bpl_max); 144 else 145 bpl = min_bpl; 146 147 if (!video->bpl_zero_padding || bpl != min_bpl) 148 bpl = ALIGN(bpl, video->bpl_alignment); 149 150 pix->pixelformat = formats[i].pixelformat; 151 pix->bytesperline = bpl; 152 pix->sizeimage = pix->bytesperline * pix->height; 153 pix->colorspace = mbus->colorspace; 154 pix->field = mbus->field; 155 156 /* FIXME: Special case for NV12! We should make this nicer... */ 157 if (pix->pixelformat == V4L2_PIX_FMT_NV12) 158 pix->sizeimage += (pix->bytesperline * pix->height) / 2; 159 160 return bpl - min_bpl; 161 } 162 163 static void iss_video_pix_to_mbus(const struct v4l2_pix_format *pix, 164 struct v4l2_mbus_framefmt *mbus) 165 { 166 unsigned int i; 167 168 memset(mbus, 0, sizeof(*mbus)); 169 mbus->width = pix->width; 170 mbus->height = pix->height; 171 172 /* 173 * Skip the last format in the loop so that it will be selected if no 174 * match is found. 175 */ 176 for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) { 177 if (formats[i].pixelformat == pix->pixelformat) 178 break; 179 } 180 181 mbus->code = formats[i].code; 182 mbus->colorspace = pix->colorspace; 183 mbus->field = pix->field; 184 } 185 186 static struct v4l2_subdev * 187 iss_video_remote_subdev(struct iss_video *video, u32 *pad) 188 { 189 struct media_pad *remote; 190 191 remote = media_pad_remote_pad_first(&video->pad); 192 193 if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) 194 return NULL; 195 196 if (pad) 197 *pad = remote->index; 198 199 return media_entity_to_v4l2_subdev(remote->entity); 200 } 201 202 /* Return a pointer to the ISS video instance at the far end of the pipeline. */ 203 static struct iss_video * 204 iss_video_far_end(struct iss_video *video, struct iss_pipeline *pipe) 205 { 206 struct media_pipeline_entity_iter iter; 207 struct media_entity *entity; 208 struct iss_video *far_end = NULL; 209 int ret; 210 211 ret = media_pipeline_entity_iter_init(&pipe->pipe, &iter); 212 if (ret) 213 return ERR_PTR(-ENOMEM); 214 215 media_pipeline_for_each_entity(&pipe->pipe, &iter, entity) { 216 struct iss_video *other; 217 218 if (entity == &video->video.entity) 219 continue; 220 221 if (!is_media_entity_v4l2_video_device(entity)) 222 continue; 223 224 other = to_iss_video(media_entity_to_video_device(entity)); 225 if (other->type != video->type) { 226 far_end = other; 227 break; 228 } 229 } 230 231 media_pipeline_entity_iter_cleanup(&iter); 232 233 return far_end; 234 } 235 236 static int 237 __iss_video_get_format(struct iss_video *video, 238 struct v4l2_mbus_framefmt *format) 239 { 240 struct v4l2_subdev_format fmt = { 241 .which = V4L2_SUBDEV_FORMAT_ACTIVE, 242 }; 243 struct v4l2_subdev *subdev; 244 u32 pad; 245 int ret; 246 247 subdev = iss_video_remote_subdev(video, &pad); 248 if (!subdev) 249 return -EINVAL; 250 251 fmt.pad = pad; 252 253 mutex_lock(&video->mutex); 254 ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 255 mutex_unlock(&video->mutex); 256 257 if (ret) 258 return ret; 259 260 *format = fmt.format; 261 return 0; 262 } 263 264 static int 265 iss_video_check_format(struct iss_video *video, struct iss_video_fh *vfh) 266 { 267 struct v4l2_mbus_framefmt format; 268 struct v4l2_pix_format pixfmt; 269 int ret; 270 271 ret = __iss_video_get_format(video, &format); 272 if (ret < 0) 273 return ret; 274 275 pixfmt.bytesperline = 0; 276 ret = iss_video_mbus_to_pix(video, &format, &pixfmt); 277 278 if (vfh->format.fmt.pix.pixelformat != pixfmt.pixelformat || 279 vfh->format.fmt.pix.height != pixfmt.height || 280 vfh->format.fmt.pix.width != pixfmt.width || 281 vfh->format.fmt.pix.bytesperline != pixfmt.bytesperline || 282 vfh->format.fmt.pix.sizeimage != pixfmt.sizeimage) 283 return -EINVAL; 284 285 return ret; 286 } 287 288 /* ----------------------------------------------------------------------------- 289 * Video queue operations 290 */ 291 292 static int iss_video_queue_setup(struct vb2_queue *vq, 293 unsigned int *count, unsigned int *num_planes, 294 unsigned int sizes[], 295 struct device *alloc_devs[]) 296 { 297 struct iss_video_fh *vfh = vb2_get_drv_priv(vq); 298 struct iss_video *video = vfh->video; 299 300 /* Revisit multi-planar support for NV12 */ 301 *num_planes = 1; 302 303 sizes[0] = vfh->format.fmt.pix.sizeimage; 304 if (sizes[0] == 0) 305 return -EINVAL; 306 307 *count = min(*count, video->capture_mem / PAGE_ALIGN(sizes[0])); 308 309 return 0; 310 } 311 312 static void iss_video_buf_cleanup(struct vb2_buffer *vb) 313 { 314 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 315 struct iss_buffer *buffer = container_of(vbuf, struct iss_buffer, vb); 316 317 if (buffer->iss_addr) 318 buffer->iss_addr = 0; 319 } 320 321 static int iss_video_buf_prepare(struct vb2_buffer *vb) 322 { 323 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 324 struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue); 325 struct iss_buffer *buffer = container_of(vbuf, struct iss_buffer, vb); 326 struct iss_video *video = vfh->video; 327 unsigned long size = vfh->format.fmt.pix.sizeimage; 328 dma_addr_t addr; 329 330 if (vb2_plane_size(vb, 0) < size) 331 return -ENOBUFS; 332 333 addr = vb2_dma_contig_plane_dma_addr(vb, 0); 334 if (!IS_ALIGNED(addr, 32)) { 335 dev_dbg(video->iss->dev, 336 "Buffer address must be aligned to 32 bytes boundary.\n"); 337 return -EINVAL; 338 } 339 340 vb2_set_plane_payload(vb, 0, size); 341 buffer->iss_addr = addr; 342 return 0; 343 } 344 345 static void iss_video_buf_queue(struct vb2_buffer *vb) 346 { 347 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 348 struct iss_video_fh *vfh = vb2_get_drv_priv(vb->vb2_queue); 349 struct iss_video *video = vfh->video; 350 struct iss_buffer *buffer = container_of(vbuf, struct iss_buffer, vb); 351 struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); 352 unsigned long flags; 353 bool empty; 354 355 spin_lock_irqsave(&video->qlock, flags); 356 357 /* 358 * Mark the buffer is faulty and give it back to the queue immediately 359 * if the video node has registered an error. vb2 will perform the same 360 * check when preparing the buffer, but that is inherently racy, so we 361 * need to handle the race condition with an authoritative check here. 362 */ 363 if (unlikely(video->error)) { 364 vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); 365 spin_unlock_irqrestore(&video->qlock, flags); 366 return; 367 } 368 369 empty = list_empty(&video->dmaqueue); 370 list_add_tail(&buffer->list, &video->dmaqueue); 371 372 spin_unlock_irqrestore(&video->qlock, flags); 373 374 if (empty) { 375 enum iss_pipeline_state state; 376 unsigned int start; 377 378 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 379 state = ISS_PIPELINE_QUEUE_OUTPUT; 380 else 381 state = ISS_PIPELINE_QUEUE_INPUT; 382 383 spin_lock_irqsave(&pipe->lock, flags); 384 pipe->state |= state; 385 video->ops->queue(video, buffer); 386 video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_QUEUED; 387 388 start = iss_pipeline_ready(pipe); 389 if (start) 390 pipe->state |= ISS_PIPELINE_STREAM; 391 spin_unlock_irqrestore(&pipe->lock, flags); 392 393 if (start) 394 omap4iss_pipeline_set_stream(pipe, 395 ISS_PIPELINE_STREAM_SINGLESHOT); 396 } 397 } 398 399 static const struct vb2_ops iss_video_vb2ops = { 400 .queue_setup = iss_video_queue_setup, 401 .buf_prepare = iss_video_buf_prepare, 402 .buf_queue = iss_video_buf_queue, 403 .buf_cleanup = iss_video_buf_cleanup, 404 }; 405 406 /* 407 * omap4iss_video_buffer_next - Complete the current buffer and return the next 408 * @video: ISS video object 409 * 410 * Remove the current video buffer from the DMA queue and fill its timestamp, 411 * field count and state fields before waking up its completion handler. 412 * 413 * For capture video nodes, the buffer state is set to VB2_BUF_STATE_DONE if no 414 * error has been flagged in the pipeline, or to VB2_BUF_STATE_ERROR otherwise. 415 * 416 * The DMA queue is expected to contain at least one buffer. 417 * 418 * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is 419 * empty. 420 */ 421 struct iss_buffer *omap4iss_video_buffer_next(struct iss_video *video) 422 { 423 struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); 424 enum iss_pipeline_state state; 425 struct iss_buffer *buf; 426 unsigned long flags; 427 428 spin_lock_irqsave(&video->qlock, flags); 429 if (WARN_ON(list_empty(&video->dmaqueue))) { 430 spin_unlock_irqrestore(&video->qlock, flags); 431 return NULL; 432 } 433 434 buf = list_first_entry(&video->dmaqueue, struct iss_buffer, 435 list); 436 list_del(&buf->list); 437 spin_unlock_irqrestore(&video->qlock, flags); 438 439 buf->vb.vb2_buf.timestamp = ktime_get_ns(); 440 441 /* 442 * Do frame number propagation only if this is the output video node. 443 * Frame number either comes from the CSI receivers or it gets 444 * incremented here if H3A is not active. 445 * Note: There is no guarantee that the output buffer will finish 446 * first, so the input number might lag behind by 1 in some cases. 447 */ 448 if (video == pipe->output && !pipe->do_propagation) 449 buf->vb.sequence = 450 atomic_inc_return(&pipe->frame_number); 451 else 452 buf->vb.sequence = atomic_read(&pipe->frame_number); 453 454 vb2_buffer_done(&buf->vb.vb2_buf, pipe->error ? 455 VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); 456 pipe->error = false; 457 458 spin_lock_irqsave(&video->qlock, flags); 459 if (list_empty(&video->dmaqueue)) { 460 spin_unlock_irqrestore(&video->qlock, flags); 461 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 462 state = ISS_PIPELINE_QUEUE_OUTPUT 463 | ISS_PIPELINE_STREAM; 464 else 465 state = ISS_PIPELINE_QUEUE_INPUT 466 | ISS_PIPELINE_STREAM; 467 468 spin_lock_irqsave(&pipe->lock, flags); 469 pipe->state &= ~state; 470 if (video->pipe.stream_state == ISS_PIPELINE_STREAM_CONTINUOUS) 471 video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN; 472 spin_unlock_irqrestore(&pipe->lock, flags); 473 return NULL; 474 } 475 476 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input) { 477 spin_lock(&pipe->lock); 478 pipe->state &= ~ISS_PIPELINE_STREAM; 479 spin_unlock(&pipe->lock); 480 } 481 482 buf = list_first_entry(&video->dmaqueue, struct iss_buffer, 483 list); 484 spin_unlock_irqrestore(&video->qlock, flags); 485 buf->vb.vb2_buf.state = VB2_BUF_STATE_ACTIVE; 486 return buf; 487 } 488 489 /* 490 * omap4iss_video_cancel_stream - Cancel stream on a video node 491 * @video: ISS video object 492 * 493 * Cancelling a stream mark all buffers on the video node as erroneous and makes 494 * sure no new buffer can be queued. 495 */ 496 void omap4iss_video_cancel_stream(struct iss_video *video) 497 { 498 unsigned long flags; 499 500 spin_lock_irqsave(&video->qlock, flags); 501 502 while (!list_empty(&video->dmaqueue)) { 503 struct iss_buffer *buf; 504 505 buf = list_first_entry(&video->dmaqueue, struct iss_buffer, 506 list); 507 list_del(&buf->list); 508 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 509 } 510 511 vb2_queue_error(video->queue); 512 video->error = true; 513 514 spin_unlock_irqrestore(&video->qlock, flags); 515 } 516 517 /* ----------------------------------------------------------------------------- 518 * V4L2 ioctls 519 */ 520 521 static int 522 iss_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 523 { 524 struct iss_video *video = video_drvdata(file); 525 526 strscpy(cap->driver, ISS_VIDEO_DRIVER_NAME, sizeof(cap->driver)); 527 strscpy(cap->card, video->video.name, sizeof(cap->card)); 528 strscpy(cap->bus_info, "media", sizeof(cap->bus_info)); 529 cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING 530 | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT; 531 532 return 0; 533 } 534 535 static int 536 iss_video_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) 537 { 538 struct iss_video *video = video_drvdata(file); 539 struct v4l2_mbus_framefmt format; 540 unsigned int index = f->index; 541 unsigned int i; 542 int ret; 543 544 if (f->type != video->type) 545 return -EINVAL; 546 547 ret = __iss_video_get_format(video, &format); 548 if (ret < 0) 549 return ret; 550 551 for (i = 0; i < ARRAY_SIZE(formats); ++i) { 552 const struct iss_format_info *info = &formats[i]; 553 554 if (format.code != info->code) 555 continue; 556 557 if (index == 0) { 558 f->pixelformat = info->pixelformat; 559 return 0; 560 } 561 562 index--; 563 } 564 565 return -EINVAL; 566 } 567 568 static int 569 iss_video_get_format(struct file *file, void *fh, struct v4l2_format *format) 570 { 571 struct iss_video_fh *vfh = to_iss_video_fh(fh); 572 struct iss_video *video = video_drvdata(file); 573 574 if (format->type != video->type) 575 return -EINVAL; 576 577 mutex_lock(&video->mutex); 578 *format = vfh->format; 579 mutex_unlock(&video->mutex); 580 581 return 0; 582 } 583 584 static int 585 iss_video_set_format(struct file *file, void *fh, struct v4l2_format *format) 586 { 587 struct iss_video_fh *vfh = to_iss_video_fh(fh); 588 struct iss_video *video = video_drvdata(file); 589 struct v4l2_mbus_framefmt fmt; 590 591 if (format->type != video->type) 592 return -EINVAL; 593 594 mutex_lock(&video->mutex); 595 596 /* 597 * Fill the bytesperline and sizeimage fields by converting to media bus 598 * format and back to pixel format. 599 */ 600 iss_video_pix_to_mbus(&format->fmt.pix, &fmt); 601 iss_video_mbus_to_pix(video, &fmt, &format->fmt.pix); 602 603 vfh->format = *format; 604 605 mutex_unlock(&video->mutex); 606 return 0; 607 } 608 609 static int 610 iss_video_try_format(struct file *file, void *fh, struct v4l2_format *format) 611 { 612 struct iss_video *video = video_drvdata(file); 613 struct v4l2_subdev_format fmt = { 614 .which = V4L2_SUBDEV_FORMAT_ACTIVE, 615 }; 616 struct v4l2_subdev *subdev; 617 u32 pad; 618 int ret; 619 620 if (format->type != video->type) 621 return -EINVAL; 622 623 subdev = iss_video_remote_subdev(video, &pad); 624 if (!subdev) 625 return -EINVAL; 626 627 iss_video_pix_to_mbus(&format->fmt.pix, &fmt.format); 628 629 fmt.pad = pad; 630 ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); 631 if (ret) 632 return ret; 633 634 iss_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix); 635 return 0; 636 } 637 638 static int 639 iss_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel) 640 { 641 struct iss_video *video = video_drvdata(file); 642 struct v4l2_subdev_format format = { 643 .which = V4L2_SUBDEV_FORMAT_ACTIVE, 644 }; 645 struct v4l2_subdev *subdev; 646 struct v4l2_subdev_selection sdsel = { 647 .which = V4L2_SUBDEV_FORMAT_ACTIVE, 648 .target = sel->target, 649 }; 650 u32 pad; 651 int ret; 652 653 switch (sel->target) { 654 case V4L2_SEL_TGT_CROP: 655 case V4L2_SEL_TGT_CROP_BOUNDS: 656 case V4L2_SEL_TGT_CROP_DEFAULT: 657 if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 658 return -EINVAL; 659 break; 660 case V4L2_SEL_TGT_COMPOSE: 661 case V4L2_SEL_TGT_COMPOSE_BOUNDS: 662 case V4L2_SEL_TGT_COMPOSE_DEFAULT: 663 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 664 return -EINVAL; 665 break; 666 default: 667 return -EINVAL; 668 } 669 subdev = iss_video_remote_subdev(video, &pad); 670 if (!subdev) 671 return -EINVAL; 672 673 /* 674 * Try the get selection operation first and fallback to get format if 675 * not implemented. 676 */ 677 sdsel.pad = pad; 678 ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel); 679 if (!ret) 680 sel->r = sdsel.r; 681 if (ret != -ENOIOCTLCMD) 682 return ret; 683 684 format.pad = pad; 685 ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format); 686 if (ret < 0) 687 return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 688 689 sel->r.left = 0; 690 sel->r.top = 0; 691 sel->r.width = format.format.width; 692 sel->r.height = format.format.height; 693 694 return 0; 695 } 696 697 static int 698 iss_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel) 699 { 700 struct iss_video *video = video_drvdata(file); 701 struct v4l2_subdev *subdev; 702 struct v4l2_subdev_selection sdsel = { 703 .which = V4L2_SUBDEV_FORMAT_ACTIVE, 704 .target = sel->target, 705 .flags = sel->flags, 706 .r = sel->r, 707 }; 708 u32 pad; 709 int ret; 710 711 switch (sel->target) { 712 case V4L2_SEL_TGT_CROP: 713 if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 714 return -EINVAL; 715 break; 716 case V4L2_SEL_TGT_COMPOSE: 717 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 718 return -EINVAL; 719 break; 720 default: 721 return -EINVAL; 722 } 723 subdev = iss_video_remote_subdev(video, &pad); 724 if (!subdev) 725 return -EINVAL; 726 727 sdsel.pad = pad; 728 mutex_lock(&video->mutex); 729 ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel); 730 mutex_unlock(&video->mutex); 731 if (!ret) 732 sel->r = sdsel.r; 733 734 return ret == -ENOIOCTLCMD ? -ENOTTY : ret; 735 } 736 737 static int 738 iss_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a) 739 { 740 struct iss_video_fh *vfh = to_iss_video_fh(fh); 741 struct iss_video *video = video_drvdata(file); 742 743 if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 744 video->type != a->type) 745 return -EINVAL; 746 747 memset(a, 0, sizeof(*a)); 748 a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 749 a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 750 a->parm.output.timeperframe = vfh->timeperframe; 751 752 return 0; 753 } 754 755 static int 756 iss_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a) 757 { 758 struct iss_video_fh *vfh = to_iss_video_fh(fh); 759 struct iss_video *video = video_drvdata(file); 760 761 if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT || 762 video->type != a->type) 763 return -EINVAL; 764 765 if (a->parm.output.timeperframe.denominator == 0) 766 a->parm.output.timeperframe.denominator = 1; 767 768 vfh->timeperframe = a->parm.output.timeperframe; 769 770 return 0; 771 } 772 773 static int 774 iss_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) 775 { 776 struct iss_video_fh *vfh = to_iss_video_fh(fh); 777 778 return vb2_reqbufs(&vfh->queue, rb); 779 } 780 781 static int 782 iss_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) 783 { 784 struct iss_video_fh *vfh = to_iss_video_fh(fh); 785 786 return vb2_querybuf(&vfh->queue, b); 787 } 788 789 static int 790 iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) 791 { 792 struct iss_video *video = video_drvdata(file); 793 struct iss_video_fh *vfh = to_iss_video_fh(fh); 794 795 return vb2_qbuf(&vfh->queue, video->video.v4l2_dev->mdev, b); 796 } 797 798 static int 799 iss_video_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *e) 800 { 801 struct iss_video_fh *vfh = to_iss_video_fh(fh); 802 803 return vb2_expbuf(&vfh->queue, e); 804 } 805 806 static int 807 iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) 808 { 809 struct iss_video_fh *vfh = to_iss_video_fh(fh); 810 811 return vb2_dqbuf(&vfh->queue, b, file->f_flags & O_NONBLOCK); 812 } 813 814 /* 815 * Stream management 816 * 817 * Every ISS pipeline has a single input and a single output. The input can be 818 * either a sensor or a video node. The output is always a video node. 819 * 820 * As every pipeline has an output video node, the ISS video objects at the 821 * pipeline output stores the pipeline state. It tracks the streaming state of 822 * both the input and output, as well as the availability of buffers. 823 * 824 * In sensor-to-memory mode, frames are always available at the pipeline input. 825 * Starting the sensor usually requires I2C transfers and must be done in 826 * interruptible context. The pipeline is started and stopped synchronously 827 * to the stream on/off commands. All modules in the pipeline will get their 828 * subdev set stream handler called. The module at the end of the pipeline must 829 * delay starting the hardware until buffers are available at its output. 830 * 831 * In memory-to-memory mode, starting/stopping the stream requires 832 * synchronization between the input and output. ISS modules can't be stopped 833 * in the middle of a frame, and at least some of the modules seem to become 834 * busy as soon as they're started, even if they don't receive a frame start 835 * event. For that reason frames need to be processed in single-shot mode. The 836 * driver needs to wait until a frame is completely processed and written to 837 * memory before restarting the pipeline for the next frame. Pipelined 838 * processing might be possible but requires more testing. 839 * 840 * Stream start must be delayed until buffers are available at both the input 841 * and output. The pipeline must be started in the vb2 queue callback with 842 * the buffers queue spinlock held. The modules subdev set stream operation must 843 * not sleep. 844 */ 845 static int 846 iss_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) 847 { 848 struct iss_video_fh *vfh = to_iss_video_fh(fh); 849 struct iss_video *video = video_drvdata(file); 850 struct media_device *mdev = video->video.entity.graph_obj.mdev; 851 struct media_pipeline_pad_iter iter; 852 enum iss_pipeline_state state; 853 struct iss_pipeline *pipe; 854 struct iss_video *far_end; 855 struct media_pad *pad; 856 unsigned long flags; 857 int ret; 858 859 if (type != video->type) 860 return -EINVAL; 861 862 mutex_lock(&video->stream_lock); 863 864 /* 865 * Start streaming on the pipeline. No link touching an entity in the 866 * pipeline can be activated or deactivated once streaming is started. 867 */ 868 pipe = to_iss_pipeline(&video->video.entity) ? : &video->pipe; 869 pipe->external = NULL; 870 pipe->external_rate = 0; 871 pipe->external_bpp = 0; 872 873 ret = media_entity_enum_init(&pipe->ent_enum, mdev); 874 if (ret) 875 goto err_entity_enum_init; 876 877 if (video->iss->pdata->set_constraints) 878 video->iss->pdata->set_constraints(video->iss, true); 879 880 ret = video_device_pipeline_start(&video->video, &pipe->pipe); 881 if (ret < 0) 882 goto err_media_pipeline_start; 883 884 media_pipeline_for_each_pad(&pipe->pipe, &iter, pad) 885 media_entity_enum_set(&pipe->ent_enum, pad->entity); 886 887 /* 888 * Verify that the currently configured format matches the output of 889 * the connected subdev. 890 */ 891 ret = iss_video_check_format(video, vfh); 892 if (ret < 0) 893 goto err_iss_video_check_format; 894 895 video->bpl_padding = ret; 896 video->bpl_value = vfh->format.fmt.pix.bytesperline; 897 898 /* 899 * Find the ISS video node connected at the far end of the pipeline and 900 * update the pipeline. 901 */ 902 far_end = iss_video_far_end(video, pipe); 903 if (IS_ERR(far_end)) { 904 ret = PTR_ERR(far_end); 905 goto err_iss_video_check_format; 906 } 907 908 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 909 state = ISS_PIPELINE_STREAM_OUTPUT | ISS_PIPELINE_IDLE_OUTPUT; 910 pipe->input = far_end; 911 pipe->output = video; 912 } else { 913 if (!far_end) { 914 ret = -EPIPE; 915 goto err_iss_video_check_format; 916 } 917 918 state = ISS_PIPELINE_STREAM_INPUT | ISS_PIPELINE_IDLE_INPUT; 919 pipe->input = video; 920 pipe->output = far_end; 921 } 922 923 spin_lock_irqsave(&pipe->lock, flags); 924 pipe->state &= ~ISS_PIPELINE_STREAM; 925 pipe->state |= state; 926 spin_unlock_irqrestore(&pipe->lock, flags); 927 928 /* 929 * Set the maximum time per frame as the value requested by userspace. 930 * This is a soft limit that can be overridden if the hardware doesn't 931 * support the request limit. 932 */ 933 if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 934 pipe->max_timeperframe = vfh->timeperframe; 935 936 video->queue = &vfh->queue; 937 INIT_LIST_HEAD(&video->dmaqueue); 938 video->error = false; 939 atomic_set(&pipe->frame_number, -1); 940 941 ret = vb2_streamon(&vfh->queue, type); 942 if (ret < 0) 943 goto err_iss_video_check_format; 944 945 /* 946 * In sensor-to-memory mode, the stream can be started synchronously 947 * to the stream on command. In memory-to-memory mode, it will be 948 * started when buffers are queued on both the input and output. 949 */ 950 if (!pipe->input) { 951 unsigned long flags; 952 953 ret = omap4iss_pipeline_set_stream(pipe, 954 ISS_PIPELINE_STREAM_CONTINUOUS); 955 if (ret < 0) 956 goto err_omap4iss_set_stream; 957 spin_lock_irqsave(&video->qlock, flags); 958 if (list_empty(&video->dmaqueue)) 959 video->dmaqueue_flags |= ISS_VIDEO_DMAQUEUE_UNDERRUN; 960 spin_unlock_irqrestore(&video->qlock, flags); 961 } 962 963 mutex_unlock(&video->stream_lock); 964 965 return 0; 966 967 err_omap4iss_set_stream: 968 vb2_streamoff(&vfh->queue, type); 969 err_iss_video_check_format: 970 video_device_pipeline_stop(&video->video); 971 err_media_pipeline_start: 972 if (video->iss->pdata->set_constraints) 973 video->iss->pdata->set_constraints(video->iss, false); 974 video->queue = NULL; 975 976 err_entity_enum_init: 977 media_entity_enum_cleanup(&pipe->ent_enum); 978 979 mutex_unlock(&video->stream_lock); 980 981 return ret; 982 } 983 984 static int 985 iss_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) 986 { 987 struct iss_video_fh *vfh = to_iss_video_fh(fh); 988 struct iss_video *video = video_drvdata(file); 989 struct iss_pipeline *pipe = to_iss_pipeline(&video->video.entity); 990 enum iss_pipeline_state state; 991 unsigned long flags; 992 993 if (type != video->type) 994 return -EINVAL; 995 996 mutex_lock(&video->stream_lock); 997 998 if (!vb2_is_streaming(&vfh->queue)) 999 goto done; 1000 1001 /* Update the pipeline state. */ 1002 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 1003 state = ISS_PIPELINE_STREAM_OUTPUT 1004 | ISS_PIPELINE_QUEUE_OUTPUT; 1005 else 1006 state = ISS_PIPELINE_STREAM_INPUT 1007 | ISS_PIPELINE_QUEUE_INPUT; 1008 1009 spin_lock_irqsave(&pipe->lock, flags); 1010 pipe->state &= ~state; 1011 spin_unlock_irqrestore(&pipe->lock, flags); 1012 1013 /* Stop the stream. */ 1014 omap4iss_pipeline_set_stream(pipe, ISS_PIPELINE_STREAM_STOPPED); 1015 vb2_streamoff(&vfh->queue, type); 1016 video->queue = NULL; 1017 1018 media_entity_enum_cleanup(&pipe->ent_enum); 1019 1020 if (video->iss->pdata->set_constraints) 1021 video->iss->pdata->set_constraints(video->iss, false); 1022 video_device_pipeline_stop(&video->video); 1023 1024 done: 1025 mutex_unlock(&video->stream_lock); 1026 return 0; 1027 } 1028 1029 static int 1030 iss_video_enum_input(struct file *file, void *fh, struct v4l2_input *input) 1031 { 1032 if (input->index > 0) 1033 return -EINVAL; 1034 1035 strscpy(input->name, "camera", sizeof(input->name)); 1036 input->type = V4L2_INPUT_TYPE_CAMERA; 1037 1038 return 0; 1039 } 1040 1041 static int 1042 iss_video_g_input(struct file *file, void *fh, unsigned int *input) 1043 { 1044 *input = 0; 1045 1046 return 0; 1047 } 1048 1049 static int 1050 iss_video_s_input(struct file *file, void *fh, unsigned int input) 1051 { 1052 return input == 0 ? 0 : -EINVAL; 1053 } 1054 1055 static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { 1056 .vidioc_querycap = iss_video_querycap, 1057 .vidioc_enum_fmt_vid_cap = iss_video_enum_format, 1058 .vidioc_g_fmt_vid_cap = iss_video_get_format, 1059 .vidioc_s_fmt_vid_cap = iss_video_set_format, 1060 .vidioc_try_fmt_vid_cap = iss_video_try_format, 1061 .vidioc_g_fmt_vid_out = iss_video_get_format, 1062 .vidioc_s_fmt_vid_out = iss_video_set_format, 1063 .vidioc_try_fmt_vid_out = iss_video_try_format, 1064 .vidioc_g_selection = iss_video_get_selection, 1065 .vidioc_s_selection = iss_video_set_selection, 1066 .vidioc_g_parm = iss_video_get_param, 1067 .vidioc_s_parm = iss_video_set_param, 1068 .vidioc_reqbufs = iss_video_reqbufs, 1069 .vidioc_querybuf = iss_video_querybuf, 1070 .vidioc_qbuf = iss_video_qbuf, 1071 .vidioc_expbuf = iss_video_expbuf, 1072 .vidioc_dqbuf = iss_video_dqbuf, 1073 .vidioc_streamon = iss_video_streamon, 1074 .vidioc_streamoff = iss_video_streamoff, 1075 .vidioc_enum_input = iss_video_enum_input, 1076 .vidioc_g_input = iss_video_g_input, 1077 .vidioc_s_input = iss_video_s_input, 1078 }; 1079 1080 /* ----------------------------------------------------------------------------- 1081 * V4L2 file operations 1082 */ 1083 1084 static int iss_video_open(struct file *file) 1085 { 1086 struct iss_video *video = video_drvdata(file); 1087 struct iss_video_fh *handle; 1088 struct vb2_queue *q; 1089 int ret = 0; 1090 1091 handle = kzalloc(sizeof(*handle), GFP_KERNEL); 1092 if (!handle) 1093 return -ENOMEM; 1094 1095 v4l2_fh_init(&handle->vfh, &video->video); 1096 v4l2_fh_add(&handle->vfh); 1097 1098 /* If this is the first user, initialise the pipeline. */ 1099 if (!omap4iss_get(video->iss)) { 1100 ret = -EBUSY; 1101 goto done; 1102 } 1103 1104 ret = v4l2_pipeline_pm_get(&video->video.entity); 1105 if (ret < 0) { 1106 omap4iss_put(video->iss); 1107 goto done; 1108 } 1109 1110 q = &handle->queue; 1111 1112 q->type = video->type; 1113 q->io_modes = VB2_MMAP | VB2_DMABUF; 1114 q->drv_priv = handle; 1115 q->ops = &iss_video_vb2ops; 1116 q->mem_ops = &vb2_dma_contig_memops; 1117 q->buf_struct_size = sizeof(struct iss_buffer); 1118 q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 1119 q->dev = video->iss->dev; 1120 1121 ret = vb2_queue_init(q); 1122 if (ret) { 1123 omap4iss_put(video->iss); 1124 goto done; 1125 } 1126 1127 memset(&handle->format, 0, sizeof(handle->format)); 1128 handle->format.type = video->type; 1129 handle->timeperframe.denominator = 1; 1130 1131 handle->video = video; 1132 file->private_data = &handle->vfh; 1133 1134 done: 1135 if (ret < 0) { 1136 v4l2_fh_del(&handle->vfh); 1137 v4l2_fh_exit(&handle->vfh); 1138 kfree(handle); 1139 } 1140 1141 return ret; 1142 } 1143 1144 static int iss_video_release(struct file *file) 1145 { 1146 struct iss_video *video = video_drvdata(file); 1147 struct v4l2_fh *vfh = file->private_data; 1148 struct iss_video_fh *handle = to_iss_video_fh(vfh); 1149 1150 /* Disable streaming and free the buffers queue resources. */ 1151 iss_video_streamoff(file, vfh, video->type); 1152 1153 v4l2_pipeline_pm_put(&video->video.entity); 1154 1155 /* Release the videobuf2 queue */ 1156 vb2_queue_release(&handle->queue); 1157 1158 v4l2_fh_del(vfh); 1159 v4l2_fh_exit(vfh); 1160 kfree(handle); 1161 file->private_data = NULL; 1162 1163 omap4iss_put(video->iss); 1164 1165 return 0; 1166 } 1167 1168 static __poll_t iss_video_poll(struct file *file, poll_table *wait) 1169 { 1170 struct iss_video_fh *vfh = to_iss_video_fh(file->private_data); 1171 1172 return vb2_poll(&vfh->queue, file, wait); 1173 } 1174 1175 static int iss_video_mmap(struct file *file, struct vm_area_struct *vma) 1176 { 1177 struct iss_video_fh *vfh = to_iss_video_fh(file->private_data); 1178 1179 return vb2_mmap(&vfh->queue, vma); 1180 } 1181 1182 static const struct v4l2_file_operations iss_video_fops = { 1183 .owner = THIS_MODULE, 1184 .unlocked_ioctl = video_ioctl2, 1185 .open = iss_video_open, 1186 .release = iss_video_release, 1187 .poll = iss_video_poll, 1188 .mmap = iss_video_mmap, 1189 }; 1190 1191 /* ----------------------------------------------------------------------------- 1192 * ISS video core 1193 */ 1194 1195 static const struct iss_video_operations iss_video_dummy_ops = { 1196 }; 1197 1198 int omap4iss_video_init(struct iss_video *video, const char *name) 1199 { 1200 const char *direction; 1201 int ret; 1202 1203 switch (video->type) { 1204 case V4L2_BUF_TYPE_VIDEO_CAPTURE: 1205 direction = "output"; 1206 video->pad.flags = MEDIA_PAD_FL_SINK; 1207 break; 1208 case V4L2_BUF_TYPE_VIDEO_OUTPUT: 1209 direction = "input"; 1210 video->pad.flags = MEDIA_PAD_FL_SOURCE; 1211 break; 1212 1213 default: 1214 return -EINVAL; 1215 } 1216 1217 ret = media_entity_pads_init(&video->video.entity, 1, &video->pad); 1218 if (ret < 0) 1219 return ret; 1220 1221 spin_lock_init(&video->qlock); 1222 mutex_init(&video->mutex); 1223 atomic_set(&video->active, 0); 1224 1225 spin_lock_init(&video->pipe.lock); 1226 mutex_init(&video->stream_lock); 1227 1228 /* Initialize the video device. */ 1229 if (!video->ops) 1230 video->ops = &iss_video_dummy_ops; 1231 1232 video->video.fops = &iss_video_fops; 1233 snprintf(video->video.name, sizeof(video->video.name), 1234 "OMAP4 ISS %s %s", name, direction); 1235 video->video.vfl_type = VFL_TYPE_VIDEO; 1236 video->video.release = video_device_release_empty; 1237 video->video.ioctl_ops = &iss_video_ioctl_ops; 1238 video->pipe.stream_state = ISS_PIPELINE_STREAM_STOPPED; 1239 1240 video_set_drvdata(&video->video, video); 1241 1242 return 0; 1243 } 1244 1245 void omap4iss_video_cleanup(struct iss_video *video) 1246 { 1247 media_entity_cleanup(&video->video.entity); 1248 mutex_destroy(&video->stream_lock); 1249 mutex_destroy(&video->mutex); 1250 } 1251 1252 int omap4iss_video_register(struct iss_video *video, struct v4l2_device *vdev) 1253 { 1254 int ret; 1255 1256 video->video.v4l2_dev = vdev; 1257 if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 1258 video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE; 1259 else 1260 video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT; 1261 video->video.device_caps |= V4L2_CAP_STREAMING; 1262 1263 ret = video_register_device(&video->video, VFL_TYPE_VIDEO, -1); 1264 if (ret < 0) 1265 dev_err(video->iss->dev, 1266 "could not register video device (%d)\n", ret); 1267 1268 return ret; 1269 } 1270 1271 void omap4iss_video_unregister(struct iss_video *video) 1272 { 1273 video_unregister_device(&video->video); 1274 } 1275