FILE
imx-media-vdic.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/imx/imx-media-vdic.c
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * V4L2 Deinterlacer Subdev for Freescale i.MX5/6 SOC 4 * 5 * Copyright (c) 2017 Mentor Graphics Inc. 6 */ 7#include <media/v4l2-ctrls.h> 8#include <media/v4l2-device.h> 9#include <media/v4l2-ioctl.h> 10#include <media/v4l2-mc.h> 11#include <media/v4l2-subdev.h> 12#include <media/imx.h> 13#include "imx-media.h" 14 15/* 16 * This subdev implements two different video pipelines: 17 * 18 * CSI -> VDIC 19 * 20 * In this pipeline, the CSI sends a single interlaced field F(n-1) 21 * directly to the VDIC (and optionally the following field F(n) 22 * can be sent to memory via IDMAC channel 13). This pipeline only works 23 * in VDIC's high motion mode, which only requires a single field for 24 * processing. The other motion modes (low and medium) require three 25 * fields, so this pipeline does not work in those modes. Also, it is 26 * not clear how this pipeline can deal with the various field orders 27 * (sequential BT/TB, interlaced BT/TB). 28 * 29 * MEM -> CH8,9,10 -> VDIC 30 * 31 * In this pipeline, previous field F(n-1), current field F(n), and next 32 * field F(n+1) are transferred to the VDIC via IDMAC channels 8,9,10. 33 * These memory buffers can come from a video output or mem2mem device. 34 * All motion modes are supported by this pipeline. 35 * 36 * The "direct" CSI->VDIC pipeline requires no DMA, but it can only be 37 * used in high motion mode. 38 */ 39 40struct vdic_priv; 41 42struct vdic_pipeline_ops { 43 int (*setup)(struct vdic_priv *priv); 44 void (*start)(struct vdic_priv *priv); 45 void (*stop)(struct vdic_priv *priv); 46 void (*disable)(struct vdic_priv *priv); 47}; 48 49/* 50 * Min/Max supported width and heights. 51 */ 52#define MIN_W 32 53#define MIN_H 32 54#define MAX_W_VDIC 968 55#define MAX_H_VDIC 2048 56#define W_ALIGN 4 /* multiple of 16 pixels */ 57#define H_ALIGN 1 /* multiple of 2 lines */ 58#define S_ALIGN 1 /* multiple of 2 */ 59 60struct vdic_priv { 61 struct device *ipu_dev; 62 struct ipu_soc *ipu; 63 64 struct v4l2_subdev sd; 65 struct media_pad pad[VDIC_NUM_PADS]; 66 67 /* lock to protect all members below */ 68 struct mutex lock; 69 70 /* IPU units we require */ 71 struct ipu_vdi *vdi; 72 73 int active_input_pad; 74 75 struct ipuv3_channel *vdi_in_ch_p; /* F(n-1) transfer channel */ 76 struct ipuv3_channel *vdi_in_ch; /* F(n) transfer channel */ 77 struct ipuv3_channel *vdi_in_ch_n; /* F(n+1) transfer channel */ 78 79 /* pipeline operations */ 80 struct vdic_pipeline_ops *ops; 81 82 /* current and previous input buffers indirect path */ 83 struct imx_media_buffer *curr_in_buf; 84 struct imx_media_buffer *prev_in_buf; 85 86 /* 87 * translated field type, input line stride, and field size 88 * for indirect path 89 */ 90 u32 fieldtype; 91 u32 in_stride; 92 u32 field_size; 93 94 /* the source (a video device or subdev) */ 95 struct media_entity *src; 96 /* the sink that will receive the progressive out buffers */ 97 struct v4l2_subdev *sink_sd; 98 99 struct v4l2_mbus_framefmt format_mbus[VDIC_NUM_PADS]; 100 const struct imx_media_pixfmt *cc[VDIC_NUM_PADS]; 101 struct v4l2_fract frame_interval[VDIC_NUM_PADS]; 102 103 /* the video device at IDMAC input pad */ 104 struct imx_media_video_dev *vdev; 105 106 bool csi_direct; /* using direct CSI->VDIC->IC pipeline */ 107 108 /* motion select control */ 109 struct v4l2_ctrl_handler ctrl_hdlr; 110 enum ipu_motion_sel motion; 111 112 int stream_count; 113}; 114 115static void vdic_put_ipu_resources(struct vdic_priv *priv) 116{ 117 if (priv->vdi_in_ch_p) 118 ipu_idmac_put(priv->vdi_in_ch_p); 119 priv->vdi_in_ch_p = NULL; 120 121 if (priv->vdi_in_ch) 122 ipu_idmac_put(priv->vdi_in_ch); 123 priv->vdi_in_ch = NULL; 124 125 if (priv->vdi_in_ch_n) 126 ipu_idmac_put(priv->vdi_in_ch_n); 127 priv->vdi_in_ch_n = NULL; 128 129 if (!IS_ERR_OR_NULL(priv->vdi)) 130 ipu_vdi_put(priv->vdi); 131 priv->vdi = NULL; 132} 133 134static int vdic_get_ipu_resources(struct vdic_priv *priv) 135{ 136 int ret, err_chan; 137 struct ipuv3_channel *ch; 138 struct ipu_vdi *vdi; 139 140 vdi = ipu_vdi_get(priv->ipu); 141 if (IS_ERR(vdi)) { 142 v4l2_err(&priv->sd, "failed to get VDIC\n"); 143 ret = PTR_ERR(vdi); 144 goto out; 145 } 146 priv->vdi = vdi; 147 148 if (!priv->csi_direct) { 149 ch = ipu_idmac_get(priv->ipu, IPUV3_CHANNEL_MEM_VDI_PREV); 150 if (IS_ERR(ch)) { 151 err_chan = IPUV3_CHANNEL_MEM_VDI_PREV; 152 ret = PTR_ERR(ch); 153 goto out_err_chan; 154 } 155 priv->vdi_in_ch_p = ch; 156 157 ch = ipu_idmac_get(priv->ipu, IPUV3_CHANNEL_MEM_VDI_CUR); 158 if (IS_ERR(ch)) { 159 err_chan = IPUV3_CHANNEL_MEM_VDI_CUR; 160 ret = PTR_ERR(ch); 161 goto out_err_chan; 162 } 163 priv->vdi_in_ch = ch; 164 165 ch = ipu_idmac_get(priv->ipu, IPUV3_CHANNEL_MEM_VDI_NEXT); 166 if (IS_ERR(ch)) { 167 err_chan = IPUV3_CHANNEL_MEM_VDI_NEXT; 168 ret = PTR_ERR(ch); 169 goto out_err_chan; 170 } 171 priv->vdi_in_ch_n = ch; 172 } 173 174 return 0; 175 176out_err_chan: 177 v4l2_err(&priv->sd, "could not get IDMAC channel %u\n", err_chan); 178out: 179 vdic_put_ipu_resources(priv); 180 return ret; 181} 182 183static int setup_vdi_channel(struct vdic_priv *priv, 184 struct ipuv3_channel *channel, 185 dma_addr_t phys0, dma_addr_t phys1) 186{ 187 struct imx_media_video_dev *vdev = priv->vdev; 188 unsigned int burst_size; 189 struct ipu_image image; 190 int ret; 191 192 ipu_cpmem_zero(channel); 193 194 memset(&image, 0, sizeof(image)); 195 image.pix = vdev->fmt; 196 image.rect = vdev->compose; 197 /* one field to VDIC channels */ 198 image.pix.height /= 2; 199 image.rect.height /= 2; 200 image.phys0 = phys0; 201 image.phys1 = phys1; 202 203 ret = ipu_cpmem_set_image(channel, &image); 204 if (ret) 205 return ret; 206 207 burst_size = (image.pix.width & 0xf) ? 8 : 16; 208 ipu_cpmem_set_burstsize(channel, burst_size); 209 210 ipu_cpmem_set_axi_id(channel, 1); 211 212 ipu_idmac_set_double_buffer(channel, false); 213 214 return 0; 215} 216 217static int vdic_setup_direct(struct vdic_priv *priv) 218{ 219 /* set VDIC to receive from CSI for direct path */ 220 ipu_fsu_link(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT, 221 IPUV3_CHANNEL_CSI_VDI_PREV); 222 223 return 0; 224} 225 226static void vdic_start_direct(struct vdic_priv *priv) 227{ 228} 229 230static void vdic_stop_direct(struct vdic_priv *priv) 231{ 232} 233 234static void vdic_disable_direct(struct vdic_priv *priv) 235{ 236 ipu_fsu_unlink(priv->ipu, IPUV3_CHANNEL_CSI_DIRECT, 237 IPUV3_CHANNEL_CSI_VDI_PREV); 238} 239 240static int vdic_setup_indirect(struct vdic_priv *priv) 241{ 242 struct v4l2_mbus_framefmt *infmt; 243 const struct imx_media_pixfmt *incc; 244 int in_size, ret; 245 246 infmt = &priv->format_mbus[VDIC_SINK_PAD_IDMAC]; 247 incc = priv->cc[VDIC_SINK_PAD_IDMAC]; 248 249 in_size = (infmt->width * incc->bpp * infmt->height) >> 3; 250 251 /* 1/2 full image size */ 252 priv->field_size = in_size / 2; 253 priv->in_stride = incc->planar ? 254 infmt->width : (infmt->width * incc->bpp) >> 3; 255 256 priv->prev_in_buf = NULL; 257 priv->curr_in_buf = NULL; 258 259 priv->fieldtype = infmt->field; 260 261 /* init the vdi-in channels */ 262 ret = setup_vdi_channel(priv, priv->vdi_in_ch_p, 0, 0); 263 if (ret) 264 return ret; 265 ret = setup_vdi_channel(priv, priv->vdi_in_ch, 0, 0); 266 if (ret) 267 return ret; 268 return setup_vdi_channel(priv, priv->vdi_in_ch_n, 0, 0); 269} 270 271static void vdic_start_indirect(struct vdic_priv *priv) 272{ 273 /* enable the channels */ 274 ipu_idmac_enable_channel(priv->vdi_in_ch_p); 275 ipu_idmac_enable_channel(priv->vdi_in_ch); 276 ipu_idmac_enable_channel(priv->vdi_in_ch_n); 277} 278 279static void vdic_stop_indirect(struct vdic_priv *priv) 280{ 281 /* disable channels */ 282 ipu_idmac_disable_channel(priv->vdi_in_ch_p); 283 ipu_idmac_disable_channel(priv->vdi_in_ch); 284 ipu_idmac_disable_channel(priv->vdi_in_ch_n); 285} 286 287static void vdic_disable_indirect(struct vdic_priv *priv) 288{ 289} 290 291static struct vdic_pipeline_ops direct_ops = { 292 .setup = vdic_setup_direct, 293 .start = vdic_start_direct, 294 .stop = vdic_stop_direct, 295 .disable = vdic_disable_direct, 296}; 297 298static struct vdic_pipeline_ops indirect_ops = { 299 .setup = vdic_setup_indirect, 300 .start = vdic_start_indirect, 301 .stop = vdic_stop_indirect, 302 .disable = vdic_disable_indirect, 303}; 304 305static int vdic_start(struct vdic_priv *priv) 306{ 307 struct v4l2_mbus_framefmt *infmt; 308 int ret; 309 310 infmt = &priv->format_mbus[priv->active_input_pad]; 311 312 priv->ops = priv->csi_direct ? &direct_ops : &indirect_ops; 313 314 ret = vdic_get_ipu_resources(priv); 315 if (ret) 316 return ret; 317 318 /* 319 * init the VDIC. 320 * 321 * note we don't give infmt->code to ipu_vdi_setup(). The VDIC 322 * only supports 4:2:2 or 4:2:0, and this subdev will only 323 * negotiate 4:2:2 at its sink pads. 324 */ 325 ipu_vdi_setup(priv->vdi, MEDIA_BUS_FMT_UYVY8_2X8, 326 infmt->width, infmt->height); 327 ipu_vdi_set_field_order(priv->vdi, V4L2_STD_UNKNOWN, infmt->field); 328 ipu_vdi_set_motion(priv->vdi, priv->motion); 329 330 ret = priv->ops->setup(priv); 331 if (ret) 332 goto out_put_ipu; 333 334 ipu_vdi_enable(priv->vdi); 335 336 priv->ops->start(priv); 337 338 return 0; 339 340out_put_ipu: 341 vdic_put_ipu_resources(priv); 342 return ret; 343} 344 345static void vdic_stop(struct vdic_priv *priv) 346{ 347 priv->ops->stop(priv); 348 ipu_vdi_disable(priv->vdi); 349 priv->ops->disable(priv); 350 351 vdic_put_ipu_resources(priv); 352} 353 354/* 355 * V4L2 subdev operations. 356 */ 357 358static int vdic_s_ctrl(struct v4l2_ctrl *ctrl) 359{ 360 struct vdic_priv *priv = container_of(ctrl->handler, 361 struct vdic_priv, ctrl_hdlr); 362 enum ipu_motion_sel motion; 363 int ret = 0; 364 365 mutex_lock(&priv->lock); 366 367 switch (ctrl->id) { 368 case V4L2_CID_DEINTERLACING_MODE: 369 motion = ctrl->val; 370 if (motion != priv->motion) { 371 /* can't change motion control mid-streaming */ 372 if (priv->stream_count > 0) { 373 ret = -EBUSY; 374 goto out; 375 } 376 priv->motion = motion; 377 } 378 break; 379 default: 380 v4l2_err(&priv->sd, "Invalid control\n"); 381 ret = -EINVAL; 382 } 383 384out: 385 mutex_unlock(&priv->lock); 386 return ret; 387} 388 389static const struct v4l2_ctrl_ops vdic_ctrl_ops = { 390 .s_ctrl = vdic_s_ctrl, 391}; 392 393static const char * const vdic_ctrl_motion_menu[] = { 394 "No Motion Compensation", 395 "Low Motion", 396 "Medium Motion", 397 "High Motion", 398}; 399 400static int vdic_init_controls(struct vdic_priv *priv) 401{ 402 struct v4l2_ctrl_handler *hdlr = &priv->ctrl_hdlr; 403 int ret; 404 405 v4l2_ctrl_handler_init(hdlr, 1); 406 407 v4l2_ctrl_new_std_menu_items(hdlr, &vdic_ctrl_ops, 408 V4L2_CID_DEINTERLACING_MODE, 409 HIGH_MOTION, 0, HIGH_MOTION, 410 vdic_ctrl_motion_menu); 411 412 priv->sd.ctrl_handler = hdlr; 413 414 if (hdlr->error) { 415 ret = hdlr->error; 416 goto out_free; 417 } 418 419 v4l2_ctrl_handler_setup(hdlr); 420 return 0; 421 422out_free: 423 v4l2_ctrl_handler_free(hdlr); 424 return ret; 425} 426 427static int vdic_s_stream(struct v4l2_subdev *sd, int enable) 428{ 429 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 430 struct v4l2_subdev *src_sd = NULL; 431 int ret = 0; 432 433 mutex_lock(&priv->lock); 434 435 if (!priv->src || !priv->sink_sd) { 436 ret = -EPIPE; 437 goto out; 438 } 439 440 if (priv->csi_direct) 441 src_sd = media_entity_to_v4l2_subdev(priv->src); 442 443 /* 444 * enable/disable streaming only if stream_count is 445 * going from 0 to 1 / 1 to 0. 446 */ 447 if (priv->stream_count != !enable) 448 goto update_count; 449 450 dev_dbg(priv->ipu_dev, "%s: stream %s\n", sd->name, 451 enable ? "ON" : "OFF"); 452 453 if (enable) 454 ret = vdic_start(priv); 455 else 456 vdic_stop(priv); 457 if (ret) 458 goto out; 459 460 if (src_sd) { 461 /* start/stop upstream */ 462 ret = v4l2_subdev_call(src_sd, video, s_stream, enable); 463 ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; 464 if (ret) { 465 if (enable) 466 vdic_stop(priv); 467 goto out; 468 } 469 } 470 471update_count: 472 priv->stream_count += enable ? 1 : -1; 473 if (priv->stream_count < 0) 474 priv->stream_count = 0; 475out: 476 mutex_unlock(&priv->lock); 477 return ret; 478} 479 480static struct v4l2_mbus_framefmt * 481__vdic_get_fmt(struct vdic_priv *priv, struct v4l2_subdev_state *sd_state, 482 unsigned int pad, enum v4l2_subdev_format_whence which) 483{ 484 if (which == V4L2_SUBDEV_FORMAT_TRY) 485 return v4l2_subdev_state_get_format(sd_state, pad); 486 else 487 return &priv->format_mbus[pad]; 488} 489 490static int vdic_enum_mbus_code(struct v4l2_subdev *sd, 491 struct v4l2_subdev_state *sd_state, 492 struct v4l2_subdev_mbus_code_enum *code) 493{ 494 if (code->pad >= VDIC_NUM_PADS) 495 return -EINVAL; 496 497 return imx_media_enum_ipu_formats(&code->code, code->index, 498 PIXFMT_SEL_YUV); 499} 500 501static int vdic_get_fmt(struct v4l2_subdev *sd, 502 struct v4l2_subdev_state *sd_state, 503 struct v4l2_subdev_format *sdformat) 504{ 505 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 506 struct v4l2_mbus_framefmt *fmt; 507 int ret = 0; 508 509 if (sdformat->pad >= VDIC_NUM_PADS) 510 return -EINVAL; 511 512 mutex_lock(&priv->lock); 513 514 fmt = __vdic_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); 515 if (!fmt) { 516 ret = -EINVAL; 517 goto out; 518 } 519 520 sdformat->format = *fmt; 521out: 522 mutex_unlock(&priv->lock); 523 return ret; 524} 525 526static void vdic_try_fmt(struct vdic_priv *priv, 527 struct v4l2_subdev_state *sd_state, 528 struct v4l2_subdev_format *sdformat, 529 const struct imx_media_pixfmt **cc) 530{ 531 struct v4l2_mbus_framefmt *infmt; 532 533 *cc = imx_media_find_ipu_format(sdformat->format.code, 534 PIXFMT_SEL_YUV); 535 if (!*cc) { 536 u32 code; 537 538 imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV); 539 *cc = imx_media_find_ipu_format(code, PIXFMT_SEL_YUV); 540 sdformat->format.code = (*cc)->codes[0]; 541 } 542 543 infmt = __vdic_get_fmt(priv, sd_state, priv->active_input_pad, 544 sdformat->which); 545 546 switch (sdformat->pad) { 547 case VDIC_SRC_PAD_DIRECT: 548 sdformat->format = *infmt; 549 /* output is always progressive! */ 550 sdformat->format.field = V4L2_FIELD_NONE; 551 break; 552 case VDIC_SINK_PAD_DIRECT: 553 case VDIC_SINK_PAD_IDMAC: 554 v4l_bound_align_image(&sdformat->format.width, 555 MIN_W, MAX_W_VDIC, W_ALIGN, 556 &sdformat->format.height, 557 MIN_H, MAX_H_VDIC, H_ALIGN, S_ALIGN); 558 559 /* input must be interlaced! Choose SEQ_TB if not */ 560 if (!V4L2_FIELD_HAS_BOTH(sdformat->format.field)) 561 sdformat->format.field = V4L2_FIELD_SEQ_TB; 562 break; 563 } 564 565 imx_media_try_colorimetry(&sdformat->format, true); 566} 567 568static int vdic_set_fmt(struct v4l2_subdev *sd, 569 struct v4l2_subdev_state *sd_state, 570 struct v4l2_subdev_format *sdformat) 571{ 572 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 573 const struct imx_media_pixfmt *cc; 574 struct v4l2_mbus_framefmt *fmt; 575 int ret = 0; 576 577 if (sdformat->pad >= VDIC_NUM_PADS) 578 return -EINVAL; 579 580 mutex_lock(&priv->lock); 581 582 if (priv->stream_count > 0) { 583 ret = -EBUSY; 584 goto out; 585 } 586 587 vdic_try_fmt(priv, sd_state, sdformat, &cc); 588 589 fmt = __vdic_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); 590 *fmt = sdformat->format; 591 592 /* propagate format to source pad */ 593 if (sdformat->pad == VDIC_SINK_PAD_DIRECT || 594 sdformat->pad == VDIC_SINK_PAD_IDMAC) { 595 const struct imx_media_pixfmt *outcc; 596 struct v4l2_mbus_framefmt *outfmt; 597 struct v4l2_subdev_format format; 598 599 format.pad = VDIC_SRC_PAD_DIRECT; 600 format.which = sdformat->which; 601 format.format = sdformat->format; 602 vdic_try_fmt(priv, sd_state, &format, &outcc); 603 604 outfmt = __vdic_get_fmt(priv, sd_state, VDIC_SRC_PAD_DIRECT, 605 sdformat->which); 606 *outfmt = format.format; 607 if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) 608 priv->cc[VDIC_SRC_PAD_DIRECT] = outcc; 609 } 610 611 if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE) 612 priv->cc[sdformat->pad] = cc; 613out: 614 mutex_unlock(&priv->lock); 615 return ret; 616} 617 618static int vdic_link_setup(struct media_entity *entity, 619 const struct media_pad *local, 620 const struct media_pad *remote, u32 flags) 621{ 622 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 623 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 624 struct v4l2_subdev *remote_sd; 625 int ret = 0; 626 627 dev_dbg(priv->ipu_dev, "%s: link setup %s -> %s", 628 sd->name, remote->entity->name, local->entity->name); 629 630 mutex_lock(&priv->lock); 631 632 if (local->flags & MEDIA_PAD_FL_SOURCE) { 633 if (!is_media_entity_v4l2_subdev(remote->entity)) { 634 ret = -EINVAL; 635 goto out; 636 } 637 638 remote_sd = media_entity_to_v4l2_subdev(remote->entity); 639 640 if (flags & MEDIA_LNK_FL_ENABLED) { 641 if (priv->sink_sd) { 642 ret = -EBUSY; 643 goto out; 644 } 645 priv->sink_sd = remote_sd; 646 } else { 647 priv->sink_sd = NULL; 648 } 649 650 goto out; 651 } 652 653 /* this is a sink pad */ 654 655 if (flags & MEDIA_LNK_FL_ENABLED) { 656 if (priv->src) { 657 ret = -EBUSY; 658 goto out; 659 } 660 } else { 661 priv->src = NULL; 662 goto out; 663 } 664 665 if (local->index == VDIC_SINK_PAD_IDMAC) { 666 struct imx_media_video_dev *vdev = priv->vdev; 667 668 if (!is_media_entity_v4l2_video_device(remote->entity)) { 669 ret = -EINVAL; 670 goto out; 671 } 672 if (!vdev) { 673 ret = -ENODEV; 674 goto out; 675 } 676 677 priv->csi_direct = false; 678 } else { 679 if (!is_media_entity_v4l2_subdev(remote->entity)) { 680 ret = -EINVAL; 681 goto out; 682 } 683 684 remote_sd = media_entity_to_v4l2_subdev(remote->entity); 685 686 /* direct pad must connect to a CSI */ 687 if (!(remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_CSI) || 688 remote->index != CSI_SRC_PAD_DIRECT) { 689 ret = -EINVAL; 690 goto out; 691 } 692 693 priv->csi_direct = true; 694 } 695 696 priv->src = remote->entity; 697 /* record which input pad is now active */ 698 priv->active_input_pad = local->index; 699out: 700 mutex_unlock(&priv->lock); 701 return ret; 702} 703 704static int vdic_link_validate(struct v4l2_subdev *sd, 705 struct media_link *link, 706 struct v4l2_subdev_format *source_fmt, 707 struct v4l2_subdev_format *sink_fmt) 708{ 709 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 710 int ret; 711 712 ret = v4l2_subdev_link_validate_default(sd, link, 713 source_fmt, sink_fmt); 714 if (ret) 715 return ret; 716 717 mutex_lock(&priv->lock); 718 719 if (priv->csi_direct && priv->motion != HIGH_MOTION) { 720 v4l2_err(&priv->sd, 721 "direct CSI pipeline requires high motion\n"); 722 ret = -EINVAL; 723 } 724 725 mutex_unlock(&priv->lock); 726 return ret; 727} 728 729static int vdic_get_frame_interval(struct v4l2_subdev *sd, 730 struct v4l2_subdev_state *sd_state, 731 struct v4l2_subdev_frame_interval *fi) 732{ 733 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 734 735 /* 736 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 737 * subdev active state API. 738 */ 739 if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) 740 return -EINVAL; 741 742 if (fi->pad >= VDIC_NUM_PADS) 743 return -EINVAL; 744 745 mutex_lock(&priv->lock); 746 747 fi->interval = priv->frame_interval[fi->pad]; 748 749 mutex_unlock(&priv->lock); 750 751 return 0; 752} 753 754static int vdic_set_frame_interval(struct v4l2_subdev *sd, 755 struct v4l2_subdev_state *sd_state, 756 struct v4l2_subdev_frame_interval *fi) 757{ 758 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 759 struct v4l2_fract *input_fi, *output_fi; 760 int ret = 0; 761 762 /* 763 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 764 * subdev active state API. 765 */ 766 if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) 767 return -EINVAL; 768 769 mutex_lock(&priv->lock); 770 771 input_fi = &priv->frame_interval[priv->active_input_pad]; 772 output_fi = &priv->frame_interval[VDIC_SRC_PAD_DIRECT]; 773 774 switch (fi->pad) { 775 case VDIC_SINK_PAD_DIRECT: 776 case VDIC_SINK_PAD_IDMAC: 777 /* No limits on valid input frame intervals */ 778 if (fi->interval.numerator == 0 || 779 fi->interval.denominator == 0) 780 fi->interval = priv->frame_interval[fi->pad]; 781 /* Reset output interval */ 782 *output_fi = fi->interval; 783 if (priv->csi_direct) 784 output_fi->denominator *= 2; 785 break; 786 case VDIC_SRC_PAD_DIRECT: 787 /* 788 * frame rate at output pad is double input 789 * rate when using direct CSI->VDIC pipeline. 790 * 791 * TODO: implement VDIC frame skipping 792 */ 793 fi->interval = *input_fi; 794 if (priv->csi_direct) 795 fi->interval.denominator *= 2; 796 break; 797 default: 798 ret = -EINVAL; 799 goto out; 800 } 801 802 priv->frame_interval[fi->pad] = fi->interval; 803out: 804 mutex_unlock(&priv->lock); 805 return ret; 806} 807 808static int vdic_registered(struct v4l2_subdev *sd) 809{ 810 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 811 int i, ret; 812 u32 code; 813 814 for (i = 0; i < VDIC_NUM_PADS; i++) { 815 code = 0; 816 if (i != VDIC_SINK_PAD_IDMAC) 817 imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV); 818 819 /* set a default mbus format */ 820 ret = imx_media_init_mbus_fmt(&priv->format_mbus[i], 821 IMX_MEDIA_DEF_PIX_WIDTH, 822 IMX_MEDIA_DEF_PIX_HEIGHT, code, 823 V4L2_FIELD_NONE, &priv->cc[i]); 824 if (ret) 825 return ret; 826 827 /* init default frame interval */ 828 priv->frame_interval[i].numerator = 1; 829 priv->frame_interval[i].denominator = 30; 830 if (i == VDIC_SRC_PAD_DIRECT) 831 priv->frame_interval[i].denominator *= 2; 832 } 833 834 priv->active_input_pad = VDIC_SINK_PAD_DIRECT; 835 836 return vdic_init_controls(priv); 837} 838 839static void vdic_unregistered(struct v4l2_subdev *sd) 840{ 841 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 842 843 v4l2_ctrl_handler_free(&priv->ctrl_hdlr); 844} 845 846static const struct v4l2_subdev_pad_ops vdic_pad_ops = { 847 .enum_mbus_code = vdic_enum_mbus_code, 848 .get_fmt = vdic_get_fmt, 849 .set_fmt = vdic_set_fmt, 850 .get_frame_interval = vdic_get_frame_interval, 851 .set_frame_interval = vdic_set_frame_interval, 852 .link_validate = vdic_link_validate, 853}; 854 855static const struct v4l2_subdev_video_ops vdic_video_ops = { 856 .s_stream = vdic_s_stream, 857}; 858 859static const struct media_entity_operations vdic_entity_ops = { 860 .link_setup = vdic_link_setup, 861 .link_validate = v4l2_subdev_link_validate, 862}; 863 864static const struct v4l2_subdev_ops vdic_subdev_ops = { 865 .video = &vdic_video_ops, 866 .pad = &vdic_pad_ops, 867}; 868 869static const struct v4l2_subdev_internal_ops vdic_internal_ops = { 870 .init_state = imx_media_init_state, 871 .registered = vdic_registered, 872 .unregistered = vdic_unregistered, 873}; 874 875struct v4l2_subdev *imx_media_vdic_register(struct v4l2_device *v4l2_dev, 876 struct device *ipu_dev, 877 struct ipu_soc *ipu, 878 u32 grp_id) 879{ 880 struct vdic_priv *priv; 881 int i, ret; 882 883 priv = devm_kzalloc(ipu_dev, sizeof(*priv), GFP_KERNEL); 884 if (!priv) 885 return ERR_PTR(-ENOMEM); 886 887 priv->ipu_dev = ipu_dev; 888 priv->ipu = ipu; 889 890 v4l2_subdev_init(&priv->sd, &vdic_subdev_ops); 891 v4l2_set_subdevdata(&priv->sd, priv); 892 priv->sd.internal_ops = &vdic_internal_ops; 893 priv->sd.entity.ops = &vdic_entity_ops; 894 priv->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 895 priv->sd.owner = ipu_dev->driver->owner; 896 priv->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; 897 priv->sd.grp_id = grp_id; 898 imx_media_grp_id_to_sd_name(priv->sd.name, sizeof(priv->sd.name), 899 priv->sd.grp_id, ipu_get_num(ipu)); 900 901 mutex_init(&priv->lock); 902 903 for (i = 0; i < VDIC_NUM_PADS; i++) 904 priv->pad[i].flags = (i == VDIC_SRC_PAD_DIRECT) ? 905 MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; 906 907 ret = media_entity_pads_init(&priv->sd.entity, VDIC_NUM_PADS, 908 priv->pad); 909 if (ret) 910 goto free; 911 912 ret = v4l2_device_register_subdev(v4l2_dev, &priv->sd); 913 if (ret) 914 goto free; 915 916 return &priv->sd; 917free: 918 mutex_destroy(&priv->lock); 919 return ERR_PTR(ret); 920} 921 922int imx_media_vdic_unregister(struct v4l2_subdev *sd) 923{ 924 struct vdic_priv *priv = v4l2_get_subdevdata(sd); 925 926 v4l2_info(sd, "Removing\n"); 927 928 v4l2_device_unregister_subdev(sd); 929 mutex_destroy(&priv->lock); 930 media_entity_cleanup(&sd->entity); 931 932 return 0; 933}
~/
Read-only file preview.