imx-ic-prp.c (11579B)
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 struct imx_ic_priv *ic_priv = priv->ic_priv; 86 87 if (which == V4L2_SUBDEV_FORMAT_TRY) 88 return v4l2_subdev_get_try_format(&ic_priv->sd, sd_state, pad); 89 else 90 return &priv->format_mbus; 91} 92 93/* 94 * V4L2 subdev operations. 95 */ 96 97static int prp_enum_mbus_code(struct v4l2_subdev *sd, 98 struct v4l2_subdev_state *sd_state, 99 struct v4l2_subdev_mbus_code_enum *code) 100{ 101 struct prp_priv *priv = sd_to_priv(sd); 102 struct v4l2_mbus_framefmt *infmt; 103 int ret = 0; 104 105 mutex_lock(&priv->lock); 106 107 switch (code->pad) { 108 case PRP_SINK_PAD: 109 ret = imx_media_enum_ipu_formats(&code->code, code->index, 110 PIXFMT_SEL_YUV_RGB); 111 break; 112 case PRP_SRC_PAD_PRPENC: 113 case PRP_SRC_PAD_PRPVF: 114 if (code->index != 0) { 115 ret = -EINVAL; 116 goto out; 117 } 118 infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, 119 code->which); 120 code->code = infmt->code; 121 break; 122 default: 123 ret = -EINVAL; 124 } 125out: 126 mutex_unlock(&priv->lock); 127 return ret; 128} 129 130static int prp_get_fmt(struct v4l2_subdev *sd, 131 struct v4l2_subdev_state *sd_state, 132 struct v4l2_subdev_format *sdformat) 133{ 134 struct prp_priv *priv = sd_to_priv(sd); 135 struct v4l2_mbus_framefmt *fmt; 136 int ret = 0; 137 138 if (sdformat->pad >= PRP_NUM_PADS) 139 return -EINVAL; 140 141 mutex_lock(&priv->lock); 142 143 fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); 144 if (!fmt) { 145 ret = -EINVAL; 146 goto out; 147 } 148 149 sdformat->format = *fmt; 150out: 151 mutex_unlock(&priv->lock); 152 return ret; 153} 154 155static int prp_set_fmt(struct v4l2_subdev *sd, 156 struct v4l2_subdev_state *sd_state, 157 struct v4l2_subdev_format *sdformat) 158{ 159 struct prp_priv *priv = sd_to_priv(sd); 160 struct v4l2_mbus_framefmt *fmt, *infmt; 161 const struct imx_media_pixfmt *cc; 162 int ret = 0; 163 u32 code; 164 165 if (sdformat->pad >= PRP_NUM_PADS) 166 return -EINVAL; 167 168 mutex_lock(&priv->lock); 169 170 if (priv->stream_count > 0) { 171 ret = -EBUSY; 172 goto out; 173 } 174 175 infmt = __prp_get_fmt(priv, sd_state, PRP_SINK_PAD, sdformat->which); 176 177 switch (sdformat->pad) { 178 case PRP_SINK_PAD: 179 v4l_bound_align_image(&sdformat->format.width, MIN_W, MAX_W, 180 W_ALIGN, &sdformat->format.height, 181 MIN_H, MAX_H, H_ALIGN, S_ALIGN); 182 183 cc = imx_media_find_ipu_format(sdformat->format.code, 184 PIXFMT_SEL_YUV_RGB); 185 if (!cc) { 186 imx_media_enum_ipu_formats(&code, 0, 187 PIXFMT_SEL_YUV_RGB); 188 cc = imx_media_find_ipu_format(code, 189 PIXFMT_SEL_YUV_RGB); 190 sdformat->format.code = cc->codes[0]; 191 } 192 193 if (sdformat->format.field == V4L2_FIELD_ANY) 194 sdformat->format.field = V4L2_FIELD_NONE; 195 break; 196 case PRP_SRC_PAD_PRPENC: 197 case PRP_SRC_PAD_PRPVF: 198 /* Output pads mirror input pad */ 199 sdformat->format = *infmt; 200 break; 201 } 202 203 imx_media_try_colorimetry(&sdformat->format, true); 204 205 fmt = __prp_get_fmt(priv, sd_state, sdformat->pad, sdformat->which); 206 *fmt = sdformat->format; 207out: 208 mutex_unlock(&priv->lock); 209 return ret; 210} 211 212static int prp_link_setup(struct media_entity *entity, 213 const struct media_pad *local, 214 const struct media_pad *remote, u32 flags) 215{ 216 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 217 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); 218 struct prp_priv *priv = ic_priv->task_priv; 219 struct v4l2_subdev *remote_sd; 220 int ret = 0; 221 222 dev_dbg(ic_priv->ipu_dev, "%s: link setup %s -> %s", 223 ic_priv->sd.name, remote->entity->name, local->entity->name); 224 225 remote_sd = media_entity_to_v4l2_subdev(remote->entity); 226 227 mutex_lock(&priv->lock); 228 229 if (local->flags & MEDIA_PAD_FL_SINK) { 230 if (flags & MEDIA_LNK_FL_ENABLED) { 231 if (priv->src_sd) { 232 ret = -EBUSY; 233 goto out; 234 } 235 if (priv->sink_sd_prpenc && 236 (remote_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC)) { 237 ret = -EINVAL; 238 goto out; 239 } 240 priv->src_sd = remote_sd; 241 } else { 242 priv->src_sd = NULL; 243 } 244 245 goto out; 246 } 247 248 /* this is a source pad */ 249 if (flags & MEDIA_LNK_FL_ENABLED) { 250 switch (local->index) { 251 case PRP_SRC_PAD_PRPENC: 252 if (priv->sink_sd_prpenc) { 253 ret = -EBUSY; 254 goto out; 255 } 256 if (priv->src_sd && (priv->src_sd->grp_id & 257 IMX_MEDIA_GRP_ID_IPU_VDIC)) { 258 ret = -EINVAL; 259 goto out; 260 } 261 priv->sink_sd_prpenc = remote_sd; 262 break; 263 case PRP_SRC_PAD_PRPVF: 264 if (priv->sink_sd_prpvf) { 265 ret = -EBUSY; 266 goto out; 267 } 268 priv->sink_sd_prpvf = remote_sd; 269 break; 270 default: 271 ret = -EINVAL; 272 } 273 } else { 274 switch (local->index) { 275 case PRP_SRC_PAD_PRPENC: 276 priv->sink_sd_prpenc = NULL; 277 break; 278 case PRP_SRC_PAD_PRPVF: 279 priv->sink_sd_prpvf = NULL; 280 break; 281 default: 282 ret = -EINVAL; 283 } 284 } 285 286out: 287 mutex_unlock(&priv->lock); 288 return ret; 289} 290 291static int prp_link_validate(struct v4l2_subdev *sd, 292 struct media_link *link, 293 struct v4l2_subdev_format *source_fmt, 294 struct v4l2_subdev_format *sink_fmt) 295{ 296 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); 297 struct prp_priv *priv = ic_priv->task_priv; 298 struct v4l2_subdev *csi; 299 int ret; 300 301 ret = v4l2_subdev_link_validate_default(sd, link, 302 source_fmt, sink_fmt); 303 if (ret) 304 return ret; 305 306 csi = imx_media_pipeline_subdev(&ic_priv->sd.entity, 307 IMX_MEDIA_GRP_ID_IPU_CSI, true); 308 if (IS_ERR(csi)) 309 csi = NULL; 310 311 mutex_lock(&priv->lock); 312 313 if (priv->src_sd->grp_id & IMX_MEDIA_GRP_ID_IPU_VDIC) { 314 /* 315 * the ->PRPENC link cannot be enabled if the source 316 * is the VDIC 317 */ 318 if (priv->sink_sd_prpenc) { 319 ret = -EINVAL; 320 goto out; 321 } 322 } else { 323 /* the source is a CSI */ 324 if (!csi) { 325 ret = -EINVAL; 326 goto out; 327 } 328 } 329 330 if (csi) { 331 switch (csi->grp_id) { 332 case IMX_MEDIA_GRP_ID_IPU_CSI0: 333 priv->csi_id = 0; 334 break; 335 case IMX_MEDIA_GRP_ID_IPU_CSI1: 336 priv->csi_id = 1; 337 break; 338 default: 339 ret = -EINVAL; 340 } 341 } else { 342 priv->csi_id = 0; 343 } 344 345out: 346 mutex_unlock(&priv->lock); 347 return ret; 348} 349 350static int prp_s_stream(struct v4l2_subdev *sd, int enable) 351{ 352 struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); 353 struct prp_priv *priv = ic_priv->task_priv; 354 int ret = 0; 355 356 mutex_lock(&priv->lock); 357 358 if (!priv->src_sd || (!priv->sink_sd_prpenc && !priv->sink_sd_prpvf)) { 359 ret = -EPIPE; 360 goto out; 361 } 362 363 /* 364 * enable/disable streaming only if stream_count is 365 * going from 0 to 1 / 1 to 0. 366 */ 367 if (priv->stream_count != !enable) 368 goto update_count; 369 370 dev_dbg(ic_priv->ipu_dev, "%s: stream %s\n", sd->name, 371 enable ? "ON" : "OFF"); 372 373 if (enable) 374 ret = prp_start(priv); 375 else 376 prp_stop(priv); 377 if (ret) 378 goto out; 379 380 /* start/stop upstream */ 381 ret = v4l2_subdev_call(priv->src_sd, video, s_stream, enable); 382 ret = (ret && ret != -ENOIOCTLCMD) ? ret : 0; 383 if (ret) { 384 if (enable) 385 prp_stop(priv); 386 goto out; 387 } 388 389update_count: 390 priv->stream_count += enable ? 1 : -1; 391 if (priv->stream_count < 0) 392 priv->stream_count = 0; 393out: 394 mutex_unlock(&priv->lock); 395 return ret; 396} 397 398static int prp_g_frame_interval(struct v4l2_subdev *sd, 399 struct v4l2_subdev_frame_interval *fi) 400{ 401 struct prp_priv *priv = sd_to_priv(sd); 402 403 if (fi->pad >= PRP_NUM_PADS) 404 return -EINVAL; 405 406 mutex_lock(&priv->lock); 407 fi->interval = priv->frame_interval; 408 mutex_unlock(&priv->lock); 409 410 return 0; 411} 412 413static int prp_s_frame_interval(struct v4l2_subdev *sd, 414 struct v4l2_subdev_frame_interval *fi) 415{ 416 struct prp_priv *priv = sd_to_priv(sd); 417 418 if (fi->pad >= PRP_NUM_PADS) 419 return -EINVAL; 420 421 mutex_lock(&priv->lock); 422 423 /* No limits on valid frame intervals */ 424 if (fi->interval.numerator == 0 || fi->interval.denominator == 0) 425 fi->interval = priv->frame_interval; 426 else 427 priv->frame_interval = fi->interval; 428 429 mutex_unlock(&priv->lock); 430 431 return 0; 432} 433 434static int prp_registered(struct v4l2_subdev *sd) 435{ 436 struct prp_priv *priv = sd_to_priv(sd); 437 u32 code; 438 439 /* init default frame interval */ 440 priv->frame_interval.numerator = 1; 441 priv->frame_interval.denominator = 30; 442 443 /* set a default mbus format */ 444 imx_media_enum_ipu_formats(&code, 0, PIXFMT_SEL_YUV); 445 446 return imx_media_init_mbus_fmt(&priv->format_mbus, 447 IMX_MEDIA_DEF_PIX_WIDTH, 448 IMX_MEDIA_DEF_PIX_HEIGHT, code, 449 V4L2_FIELD_NONE, NULL); 450} 451 452static const struct v4l2_subdev_pad_ops prp_pad_ops = { 453 .init_cfg = imx_media_init_cfg, 454 .enum_mbus_code = prp_enum_mbus_code, 455 .get_fmt = prp_get_fmt, 456 .set_fmt = prp_set_fmt, 457 .link_validate = prp_link_validate, 458}; 459 460static const struct v4l2_subdev_video_ops prp_video_ops = { 461 .g_frame_interval = prp_g_frame_interval, 462 .s_frame_interval = prp_s_frame_interval, 463 .s_stream = prp_s_stream, 464}; 465 466static const struct media_entity_operations prp_entity_ops = { 467 .link_setup = prp_link_setup, 468 .link_validate = v4l2_subdev_link_validate, 469}; 470 471static const struct v4l2_subdev_ops prp_subdev_ops = { 472 .video = &prp_video_ops, 473 .pad = &prp_pad_ops, 474}; 475 476static const struct v4l2_subdev_internal_ops prp_internal_ops = { 477 .registered = prp_registered, 478}; 479 480static int prp_init(struct imx_ic_priv *ic_priv) 481{ 482 struct prp_priv *priv; 483 int i; 484 485 priv = devm_kzalloc(ic_priv->ipu_dev, sizeof(*priv), GFP_KERNEL); 486 if (!priv) 487 return -ENOMEM; 488 489 mutex_init(&priv->lock); 490 ic_priv->task_priv = priv; 491 priv->ic_priv = ic_priv; 492 493 for (i = 0; i < PRP_NUM_PADS; i++) 494 priv->pad[i].flags = (i == PRP_SINK_PAD) ? 495 MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; 496 497 return media_entity_pads_init(&ic_priv->sd.entity, PRP_NUM_PADS, 498 priv->pad); 499} 500 501static void prp_remove(struct imx_ic_priv *ic_priv) 502{ 503 struct prp_priv *priv = ic_priv->task_priv; 504 505 mutex_destroy(&priv->lock); 506} 507 508struct imx_ic_ops imx_ic_prp_ops = { 509 .subdev_ops = &prp_subdev_ops, 510 .internal_ops = &prp_internal_ops, 511 .entity_ops = &prp_entity_ops, 512 .init = prp_init, 513 .remove = prp_remove, 514};