1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * V4L2 Capture IC Preprocess Subdev for Freescale i.MX5/6 SOC 4 * 5 * This subdevice handles capture of video frames from the CSI or VDIC, 6 * which are routed directly to the Image Converter preprocess tasks, 7 * for resizing, colorspace conversion, and rotation. 8 * 9 * Copyright (c) 2012-2017 Mentor Graphics Inc. 10 */ 11#include <linux/delay.h> 12#include <linux/interrupt.h> 13#include <linux/module.h> 14#include <linux/sched.h> 15#include <linux/slab.h> 16#include <linux/spinlock.h> 17#include <linux/timer.h> 18#include <media/v4l2-ctrls.h> 19#include <media/v4l2-device.h> 20#include <media/v4l2-ioctl.h> 21#include <media/v4l2-subdev.h> 22#include <media/imx.h> 23#include "imx-media.h" 24#include "imx-ic.h" 25 26/* 27 * Min/Max supported width and heights. 28 */ 29#define MIN_W 32 30#define MIN_H 32 31#define MAX_W 4096 32#define MAX_H 4096 33#define W_ALIGN 4 /* multiple of 16 pixels */ 34#define H_ALIGN 1 /* multiple of 2 lines */ 35#define S_ALIGN 1 /* multiple of 2 */ 36 37struct prp_priv { 38 struct imx_ic_priv *ic_priv; 39 struct media_pad pad[PRP_NUM_PADS]; 40 41 /* lock to protect all members below */ 42 struct mutex lock; 43 44 struct v4l2_subdev *src_sd; 45 struct v4l2_subdev *sink_sd_prpenc; 46 struct v4l2_subdev *sink_sd_prpvf; 47 48 /* the CSI id at link validate */ 49 int csi_id; 50 51 struct v4l2_mbus_framefmt format_mbus; 52 struct v4l2_fract frame_interval; 53 54 int stream_count; 55}; 56 57static inline struct prp_priv *sd_to_priv(struct v4l2_subdev *sd) 58{ 59 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); 60 61 return ic_priv->task_priv; 62} 63 64static int prp_start(struct prp_priv *priv) 65{ 66 struct imx_ic_priv *ic_priv = priv->ic_priv; 67 bool src_is_vdic; 68 69 /* set IC to receive from CSI or VDI depending on source */ 70 src_is_vdic = !!(priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC); 71 72 ipu_set_ic_src_mux(ic_priv->ipu, priv->csi_id, src_is_vdic); 73 74 return 0; 75} 76 77static void prp_stop(struct prp_priv *priv) 78{ 79} 80 81static struct v4l2_mbus_framefmt * 82__prp_get_fmt(struct prp_priv *priv, struct v4l2_subdev_state *sd_state, 83 unsigned int pad, enum v4l2_subdev_format_whence which) 84{ 85 if (which == V4L2_SUBDEV_FORMAT_TRY) 86 return v4l2_subdev_state_get_format(sd_state, pad); 87 else 88 return &priv->format_mbus; 89} 90 91/* 92 * V4L2 subdev operations. 93 */ 94 95static int prp_enum_mbus_code(struct v4l2_subdev *sd, 96 struct v4l2_subdev_state *sd_state, 97 struct v4l2_subdev_mbus_code_enum *code) 98{ 99 struct prp_priv *priv = sd_to_priv(sd); 100 struct v4l2_mbus_framefmt *infmt; 101 int ret = 0; 102 103 mutex_lock(&priv->lock); 104 105 switch (code->pad) { 106 case PRP_SINK_PAD: 107 ret = imx_media_enum_ipu_formats(&code->code, code->index, 108 PIXFMT_SEL_YUV_RGB); 109 break; 110 case PRP_SRC_PAD_PRPENC: 111 case PRP_SRC_PAD_PRPVF: 112 if (code->index != 0) { 113 ret = -EINVAL; 114 goto out; 115 } 116 infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, 117 code->which); 118 code->code = infmt->code; 119 break; 120 default: 121 ret = -EINVAL; 122 } 123out: 124 mutex_unlock(&priv->lock); 125 return ret; 126} 127 128static int prp_get_fmt(struct v4l2_subdev *sd, 129 struct v4l2_subdev_state *sd_state, 130 struct v4l2_subdev_format *sdformat) 131{ 132 struct prp_priv *priv = sd_to_priv(sd); 133 struct v4l2_mbus_framefmt *fmt; 134 int ret = 0; 135 136 if (sdformat->pad >= PRP_NUM_PADS) 137 return -EINVAL; 138 139 mutex_lock(&priv->lock); 140 141 fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); 142 if (!fmt) { 143 ret = -EINVAL; 144 goto out; 145 } 146 147 sdformat->format = *fmt; 148out: 149 mutex_unlock(&priv->lock); 150 return ret; 151} 152 153static int prp_set_fmt(struct v4l2_subdev *sd, 154 struct v4l2_subdev_state *sd_state, 155 struct v4l2_subdev_format *sdformat) 156{ 157 struct prp_priv *priv = sd_to_priv(sd); 158 struct v4l2_mbus_framefmt *fmt, *infmt; 159 const struct imx_media_pixfmt *cc; 160 int ret = 0; 161 u32 code; 162 163 if (sdformat->pad >= PRP_NUM_PADS) 164 return -EINVAL; 165 166 mutex_lock(&priv->lock); 167 168 if (priv->stream_count > 0) { 169 ret = -EBUSY; 170 goto out; 171 } 172 173 infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, sdformat->which); 174 175 switch (sdformat->pad) { 176 case PRP_SINK_PAD: 177 v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W, 178 W_ALIGN, &sdformat->format.height, 179 MIN_H, MAX_H, H_ALIGN, S_ALIGN); 180 181 cc = imx_media_find_ipu_format(sdformat->format.code, 182 PIXFMT_SEL_YUV_RGB); 183 if (!cc) { 184 imx_media_enum_ipu_formats(&code, 0, 185 PIXFMT_SEL_YUV_RGB); 186 cc = imx_media_find_ipu_format(code, 187 PIXFMT_SEL_YUV_RGB); 188 sdformat->format.code = cc->codes[0]; 189 } 190 191 if (sdformat->format.field == V4L2_FIELD_ANY) 192 sdformat->format.field = V4L2_FIELD_NONE; 193 break; 194 case PRP_SRC_PAD_PRPENC: 195 case PRP_SRC_PAD_PRPVF: 196 /* Output pads mirror input pad */ 197 sdformat->format = *infmt; 198 break; 199 } 200 201 imx_media_try_colorimetry(&sdformat->format, true); 202 203 fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); 204 *fmt = sdformat->format; 205out: 206 mutex_unlock(&priv->lock); 207 return ret; 208} 209 210static int prp_link_setup(struct media_entity *entity, 211 const struct media_pad *local, 212 const struct media_pad *remote, u32 flags) 213{ 214 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 215 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); 216 struct prp_priv *priv = ic_priv->task_priv; 217 struct v4l2_subdev *remote_sd; 218 int ret = 0; 219 220 dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s", 221 ic_priv->sd.name, remote->entity->name, local->entity->name); 222 223 remote_sd = media_entity_to_v4l2_subdev(remote->entity); 224 225 mutex_lock(&priv->lock); 226 227 if (local->flags & MEDIA_PAD_FL_SINK) { 228 if (flags & MEDIA_LNK_FL_ENABLED) { 229 if (priv->src_sd) { 230 ret = -EBUSY; 231 goto out; 232 } 233 if (priv->sink_sd_prpenc && 234 (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) { 235 ret = -EINVAL; 236 goto out; 237 } 238 priv->src_sd = remote_sd; 239 } else { 240 priv->src_sd = NULL; 241 } 242 243 goto out; 244 } 245 246 /* this is a source pad */ 247 if (flags & MEDIA_LNK_FL_ENABLED) { 248 switch (local->index) { 249 case PRP_SRC_PAD_PRPENC: 250 if (priv->sink_sd_prpenc) { 251 ret = -EBUSY; 252 goto out; 253 } 254 if (priv->src_sd && (priv->src_sd->grp_id & 255 IMX_MEDIA_GRP_ID_IPU_VDIC)) { 256 ret = -EINVAL; 257 goto out; 258 } 259 priv->sink_sd_prpenc = remote_sd; 260 break; 261 case PRP_SRC_PAD_PRPVF: 262 if (priv->sink_sd_prpvf) { 263 ret = -EBUSY; 264 goto out; 265 } 266 priv->sink_sd_prpvf = remote_sd; 267 break; 268 default: 269 ret = -EINVAL; 270 } 271 } else { 272 switch (local->index) { 273 case PRP_SRC_PAD_PRPENC: 274 priv->sink_sd_prpenc = NULL; 275 break; 276 case PRP_SRC_PAD_PRPVF: 277 priv->sink_sd_prpvf = NULL; 278 break; 279 default: 280 ret = -EINVAL; 281 } 282 } 283 284out: 285 mutex_unlock(&priv->lock); 286 return ret; 287} 288 289static int prp_link_validate(struct v4l2_subdev *sd, 290 struct media_link *link, 291 struct v4l2_subdev_format *source_fmt, 292 struct v4l2_subdev_format *sink_fmt) 293{ 294 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); 295 struct prp_priv *priv = ic_priv->task_priv; 296 struct v4l2_subdev *csi; 297 int ret; 298 299 ret = v4l2_subdev_link_validate_default(sd, link, 300 source_fmt, sink_fmt); 301 if (ret) 302 return ret; 303 304 csi = imx_media_pipeline_subdev(&ic_priv->sd.entity, 305 IMX_MEDIA_GRP_ID_IPU_CSI, true); 306 if (IS_ERR(csi)) 307 csi = NULL; 308 309 mutex_lock(&priv->lock); 310 311 if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) { 312 /* 313 * the ->PRPENC link cannot be enabled if the source 314 * is the VDIC 315 */ 316 if (priv->sink_sd_prpenc) { 317 ret = -EINVAL; 318 goto out; 319 } 320 } else { 321 /* the source is a CSI */ 322 if (!csi) { 323 ret = -EINVAL; 324 goto out; 325 } 326 } 327 328 if (csi) { 329 switch (csi->grp_id) { 330 case IMX_MEDIA_GRP_ID_IPU_CSI0: 331 priv->csi_id = 0; 332 break; 333 case IMX_MEDIA_GRP_ID_IPU_CSI1: 334 priv->csi_id = 1; 335 break; 336 default: 337 ret = -EINVAL; 338 } 339 } else { 340 priv->csi_id = 0; 341 } 342 343out: 344 mutex_unlock(&priv->lock); 345 return ret; 346} 347 348static int prp_s_stream(struct v4l2_subdev *sd, int enable) 349{ 350 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); 351 struct prp_priv *priv = ic_priv->task_priv; 352 int ret = 0; 353 354 mutex_lock(&priv->lock); 355 356 if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) { 357 ret = -EPIPE; 358 goto out; 359 } 360 361 /* 362 * enable/disable streaming only if stream_count is 363 * going from 0 to 1 / 1 to 0. 364 */ 365 if (priv->stream_count != !enable) 366 goto update_count; 367 368 dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name, 369 enable ? "ON" : "OFF"); 370 371 if (enable) 372 ret = prp_start(priv); 373 else 374 prp_stop(priv); 375 if (ret) 376 goto out; 377 378 /* start/stop upstream */ 379 ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); 380 ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; 381 if (ret) { 382 if (enable) 383 prp_stop(priv); 384 goto out; 385 } 386 387update_count: 388 priv->stream_count += enable ? 1 : -1; 389 if (priv->stream_count < 0) 390 priv->stream_count = 0; 391out: 392 mutex_unlock(&priv->lock); 393 return ret; 394} 395 396static int prp_get_frame_interval(struct v4l2_subdev *sd, 397 struct v4l2_subdev_state *sd_state, 398 struct v4l2_subdev_frame_interval *fi) 399{ 400 struct prp_priv *priv = sd_to_priv(sd); 401 402 /* 403 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 404 * subdev active state API. 405 */ 406 if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) 407 return -EINVAL; 408 409 if (fi->pad >= PRP_NUM_PADS) 410 return -EINVAL; 411 412 mutex_lock(&priv->lock); 413 fi->interval = priv->frame_interval; 414 mutex_unlock(&priv->lock); 415 416 return 0; 417} 418 419static int prp_set_frame_interval(struct v4l2_subdev *sd, 420 struct v4l2_subdev_state *sd_state, 421 struct v4l2_subdev_frame_interval *fi) 422{ 423 struct prp_priv *priv = sd_to_priv(sd); 424 425 /* 426 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2 427 * subdev active state API. 428 */ 429 if (fi->which != V4L2_SUBDEV_FORMAT_ACTIVE) 430 return -EINVAL; 431 432 if (fi->pad >= PRP_NUM_PADS) 433 return -EINVAL; 434 435 mutex_lock(&priv->lock); 436 437 /* No limits on valid frame intervals */ 438 if (fi->interval.numerator == 0 || fi->interval.denominator == 0) 439 fi->interval = priv->frame_interval; 440 else 441 priv->frame_interval = fi->interval; 442 443 mutex_unlock(&priv->lock); 444 445 return 0; 446} 447 448static int prp_registered(struct v4l2_subdev *sd) 449{ 450 struct prp_priv *priv = sd_to_priv(sd); 451 u32 code; 452 453 /* init default frame interval */ 454 priv->frame_interval.numerator = 1; 455 priv->frame_interval.denominator = 30; 456 457 /* set a default mbus format */ 458 imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV); 459 460 return imx_media_init_mbus_fmt(&priv->format_mbus, 461 IMX_MEDIA_DEF_PIX_WIDTH, 462 IMX_MEDIA_DEF_PIX_HEIGHT, code, 463 V4L2_FIELD_NONE, NULL); 464} 465 466static const struct v4l2_subdev_pad_ops prp_pad_ops = { 467 .enum_mbus_code = prp_enum_mbus_code, 468 .get_fmt = prp_get_fmt, 469 .set_fmt = prp_set_fmt, 470 .get_frame_interval = prp_get_frame_interval, 471 .set_frame_interval = prp_set_frame_interval, 472 .link_validate = prp_link_validate, 473}; 474 475static const struct v4l2_subdev_video_ops prp_video_ops = { 476 .s_stream = prp_s_stream, 477}; 478 479static const struct media_entity_operations prp_entity_ops = { 480 .link_setup = prp_link_setup, 481 .link_validate = v4l2_subdev_link_validate, 482}; 483 484static const struct v4l2_subdev_ops prp_subdev_ops = { 485 .video = &prp_video_ops, 486 .pad = &prp_pad_ops, 487}; 488 489static const struct v4l2_subdev_internal_ops prp_internal_ops = { 490 .init_state = imx_media_init_state, 491 .registered = prp_registered, 492}; 493 494static int prp_init(struct imx_ic_priv *ic_priv) 495{ 496 struct prp_priv *priv; 497 int i; 498 499 priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL); 500 if (!priv) 501 return -ENOMEM; 502 503 mutex_init(&priv->lock); 504 ic_priv->task_priv = priv; 505 priv->ic_priv = ic_priv; 506 507 for (i = 0; i < PRP_NUM_PADS; i++) 508 priv->pad[i].flags = (i == PRP_SINK_PAD) ? 509 MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; 510 511 return media_entity_pads_init(&ic_priv->sd.entity, PRP_NUM_PADS, 512 priv->pad); 513} 514 515static void prp_remove(struct imx_ic_priv *ic_priv) 516{ 517 struct prp_priv *priv = ic_priv->task_priv; 518 519 mutex_destroy(&priv->lock); 520} 521 522struct imx_ic_ops imx_ic_prp_ops = { 523 .subdev_ops = &prp_subdev_ops, 524 .internal_ops = &prp_internal_ops, 525 .entity_ops = &prp_entity_ops, 526 .init = prp_init, 527 .remove = prp_remove, 528}; 529