imx-media-fim.c (11993B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Frame Interval Monitor. 4 * 5 * Copyright (c) 2016 Mentor Graphics Inc. 6 */ 7#include <linux/delay.h> 8#include <linux/irq.h> 9#include <linux/module.h> 10#include <linux/platform_device.h> 11#include <linux/slab.h> 12#include <linux/spinlock.h> 13#include <media/v4l2-ctrls.h> 14#include <media/v4l2-subdev.h> 15#include <media/imx.h> 16#include "imx-media.h" 17 18enum { 19 FIM_CL_ENABLE = 0, 20 FIM_CL_NUM, 21 FIM_CL_TOLERANCE_MIN, 22 FIM_CL_TOLERANCE_MAX, 23 FIM_CL_NUM_SKIP, 24 FIM_NUM_CONTROLS, 25}; 26 27enum { 28 FIM_CL_ICAP_EDGE = 0, 29 FIM_CL_ICAP_CHANNEL, 30 FIM_NUM_ICAP_CONTROLS, 31}; 32 33#define FIM_CL_ENABLE_DEF 0 /* FIM disabled by default */ 34#define FIM_CL_NUM_DEF 8 /* average 8 frames */ 35#define FIM_CL_NUM_SKIP_DEF 2 /* skip 2 frames after restart */ 36#define FIM_CL_TOLERANCE_MIN_DEF 50 /* usec */ 37#define FIM_CL_TOLERANCE_MAX_DEF 0 /* no max tolerance (unbounded) */ 38 39struct imx_media_fim { 40 /* the owning subdev of this fim instance */ 41 struct v4l2_subdev *sd; 42 43 /* FIM's control handler */ 44 struct v4l2_ctrl_handler ctrl_handler; 45 46 /* control clusters */ 47 struct v4l2_ctrl *ctrl[FIM_NUM_CONTROLS]; 48 struct v4l2_ctrl *icap_ctrl[FIM_NUM_ICAP_CONTROLS]; 49 50 spinlock_t lock; /* protect control values */ 51 52 /* current control values */ 53 bool enabled; 54 int num_avg; 55 int num_skip; 56 unsigned long tolerance_min; /* usec */ 57 unsigned long tolerance_max; /* usec */ 58 /* input capture method of measuring FI */ 59 int icap_channel; 60 int icap_flags; 61 62 int counter; 63 ktime_t last_ts; 64 unsigned long sum; /* usec */ 65 unsigned long nominal; /* usec */ 66 67 struct completion icap_first_event; 68 bool stream_on; 69}; 70 71#define icap_enabled(fim) ((fim)->icap_flags != IRQ_TYPE_NONE) 72 73static void update_fim_nominal(struct imx_media_fim *fim, 74 const struct v4l2_fract *fi) 75{ 76 if (fi->denominator == 0) { 77 dev_dbg(fim->sd->dev, "no frame interval, FIM disabled\n"); 78 fim->enabled = false; 79 return; 80 } 81 82 fim->nominal = DIV_ROUND_CLOSEST_ULL(1000000ULL * (u64)fi->numerator, 83 fi->denominator); 84 85 dev_dbg(fim->sd->dev, "FI=%lu usec\n", fim->nominal); 86} 87 88static void reset_fim(struct imx_media_fim *fim, bool curval) 89{ 90 struct v4l2_ctrl *icap_chan = fim->icap_ctrl[FIM_CL_ICAP_CHANNEL]; 91 struct v4l2_ctrl *icap_edge = fim->icap_ctrl[FIM_CL_ICAP_EDGE]; 92 struct v4l2_ctrl *en = fim->ctrl[FIM_CL_ENABLE]; 93 struct v4l2_ctrl *num = fim->ctrl[FIM_CL_NUM]; 94 struct v4l2_ctrl *skip = fim->ctrl[FIM_CL_NUM_SKIP]; 95 struct v4l2_ctrl *tol_min = fim->ctrl[FIM_CL_TOLERANCE_MIN]; 96 struct v4l2_ctrl *tol_max = fim->ctrl[FIM_CL_TOLERANCE_MAX]; 97 98 if (curval) { 99 fim->enabled = en->cur.val; 100 fim->icap_flags = icap_edge->cur.val; 101 fim->icap_channel = icap_chan->cur.val; 102 fim->num_avg = num->cur.val; 103 fim->num_skip = skip->cur.val; 104 fim->tolerance_min = tol_min->cur.val; 105 fim->tolerance_max = tol_max->cur.val; 106 } else { 107 fim->enabled = en->val; 108 fim->icap_flags = icap_edge->val; 109 fim->icap_channel = icap_chan->val; 110 fim->num_avg = num->val; 111 fim->num_skip = skip->val; 112 fim->tolerance_min = tol_min->val; 113 fim->tolerance_max = tol_max->val; 114 } 115 116 /* disable tolerance range if max <= min */ 117 if (fim->tolerance_max <= fim->tolerance_min) 118 fim->tolerance_max = 0; 119 120 /* num_skip must be >= 1 if input capture not used */ 121 if (!icap_enabled(fim)) 122 fim->num_skip = max_t(int, fim->num_skip, 1); 123 124 fim->counter = -fim->num_skip; 125 fim->sum = 0; 126} 127 128static void send_fim_event(struct imx_media_fim *fim, unsigned long error) 129{ 130 static const struct v4l2_event ev = { 131 .type = V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR, 132 }; 133 134 v4l2_subdev_notify_event(fim->sd, &ev); 135} 136 137/* 138 * Monitor an averaged frame interval. If the average deviates too much 139 * from the nominal frame rate, send the frame interval error event. The 140 * frame intervals are averaged in order to quiet noise from 141 * (presumably random) interrupt latency. 142 */ 143static void frame_interval_monitor(struct imx_media_fim *fim, 144 ktime_t timestamp) 145{ 146 long long interval, error; 147 unsigned long error_avg; 148 bool send_event = false; 149 150 if (!fim->enabled || ++fim->counter <= 0) 151 goto out_update_ts; 152 153 /* max error is less than l00µs, so use 32-bit division or fail */ 154 interval = ktime_to_ns(ktime_sub(timestamp, fim->last_ts)); 155 error = abs(interval - NSEC_PER_USEC * (u64)fim->nominal); 156 if (error > U32_MAX) 157 error = U32_MAX; 158 else 159 error = abs((u32)error / NSEC_PER_USEC); 160 161 if (fim->tolerance_max && error >= fim->tolerance_max) { 162 dev_dbg(fim->sd->dev, 163 "FIM: %llu ignored, out of tolerance bounds\n", 164 error); 165 fim->counter--; 166 goto out_update_ts; 167 } 168 169 fim->sum += error; 170 171 if (fim->counter == fim->num_avg) { 172 error_avg = DIV_ROUND_CLOSEST(fim->sum, fim->num_avg); 173 174 if (error_avg > fim->tolerance_min) 175 send_event = true; 176 177 dev_dbg(fim->sd->dev, "FIM: error: %lu usec%s\n", 178 error_avg, send_event ? " (!!!)" : ""); 179 180 fim->counter = 0; 181 fim->sum = 0; 182 } 183 184out_update_ts: 185 fim->last_ts = timestamp; 186 if (send_event) 187 send_fim_event(fim, error_avg); 188} 189 190#ifdef CONFIG_IMX_GPT_ICAP 191/* 192 * Input Capture method of measuring frame intervals. Not subject 193 * to interrupt latency. 194 */ 195static void fim_input_capture_handler(int channel, void *dev_id, 196 ktime_t timestamp) 197{ 198 struct imx_media_fim *fim = dev_id; 199 unsigned long flags; 200 201 spin_lock_irqsave(&fim->lock, flags); 202 203 frame_interval_monitor(fim, timestamp); 204 205 if (!completion_done(&fim->icap_first_event)) 206 complete(&fim->icap_first_event); 207 208 spin_unlock_irqrestore(&fim->lock, flags); 209} 210 211static int fim_request_input_capture(struct imx_media_fim *fim) 212{ 213 init_completion(&fim->icap_first_event); 214 215 return mxc_request_input_capture(fim->icap_channel, 216 fim_input_capture_handler, 217 fim->icap_flags, fim); 218} 219 220static void fim_free_input_capture(struct imx_media_fim *fim) 221{ 222 mxc_free_input_capture(fim->icap_channel, fim); 223} 224 225#else /* CONFIG_IMX_GPT_ICAP */ 226 227static int fim_request_input_capture(struct imx_media_fim *fim) 228{ 229 return 0; 230} 231 232static void fim_free_input_capture(struct imx_media_fim *fim) 233{ 234} 235 236#endif /* CONFIG_IMX_GPT_ICAP */ 237 238/* 239 * In case we are monitoring the first frame interval after streamon 240 * (when fim->num_skip = 0), we need a valid fim->last_ts before we 241 * can begin. This only applies to the input capture method. It is not 242 * possible to accurately measure the first FI after streamon using the 243 * EOF method, so fim->num_skip minimum is set to 1 in that case, so this 244 * function is a noop when the EOF method is used. 245 */ 246static void fim_acquire_first_ts(struct imx_media_fim *fim) 247{ 248 unsigned long ret; 249 250 if (!fim->enabled || fim->num_skip > 0) 251 return; 252 253 ret = wait_for_completion_timeout( 254 &fim->icap_first_event, 255 msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); 256 if (ret == 0) 257 v4l2_warn(fim->sd, "wait first icap event timeout\n"); 258} 259 260/* FIM Controls */ 261static int fim_s_ctrl(struct v4l2_ctrl *ctrl) 262{ 263 struct imx_media_fim *fim = container_of(ctrl->handler, 264 struct imx_media_fim, 265 ctrl_handler); 266 unsigned long flags; 267 int ret = 0; 268 269 spin_lock_irqsave(&fim->lock, flags); 270 271 switch (ctrl->id) { 272 case V4L2_CID_IMX_FIM_ENABLE: 273 break; 274 case V4L2_CID_IMX_FIM_ICAP_EDGE: 275 if (fim->stream_on) 276 ret = -EBUSY; 277 break; 278 default: 279 ret = -EINVAL; 280 } 281 282 if (!ret) 283 reset_fim(fim, false); 284 285 spin_unlock_irqrestore(&fim->lock, flags); 286 return ret; 287} 288 289static const struct v4l2_ctrl_ops fim_ctrl_ops = { 290 .s_ctrl = fim_s_ctrl, 291}; 292 293static const struct v4l2_ctrl_config fim_ctrl[] = { 294 [FIM_CL_ENABLE] = { 295 .ops = &fim_ctrl_ops, 296 .id = V4L2_CID_IMX_FIM_ENABLE, 297 .name = "FIM Enable", 298 .type = V4L2_CTRL_TYPE_BOOLEAN, 299 .def = FIM_CL_ENABLE_DEF, 300 .min = 0, 301 .max = 1, 302 .step = 1, 303 }, 304 [FIM_CL_NUM] = { 305 .ops = &fim_ctrl_ops, 306 .id = V4L2_CID_IMX_FIM_NUM, 307 .name = "FIM Num Average", 308 .type = V4L2_CTRL_TYPE_INTEGER, 309 .def = FIM_CL_NUM_DEF, 310 .min = 1, /* no averaging */ 311 .max = 64, /* average 64 frames */ 312 .step = 1, 313 }, 314 [FIM_CL_TOLERANCE_MIN] = { 315 .ops = &fim_ctrl_ops, 316 .id = V4L2_CID_IMX_FIM_TOLERANCE_MIN, 317 .name = "FIM Tolerance Min", 318 .type = V4L2_CTRL_TYPE_INTEGER, 319 .def = FIM_CL_TOLERANCE_MIN_DEF, 320 .min = 2, 321 .max = 200, 322 .step = 1, 323 }, 324 [FIM_CL_TOLERANCE_MAX] = { 325 .ops = &fim_ctrl_ops, 326 .id = V4L2_CID_IMX_FIM_TOLERANCE_MAX, 327 .name = "FIM Tolerance Max", 328 .type = V4L2_CTRL_TYPE_INTEGER, 329 .def = FIM_CL_TOLERANCE_MAX_DEF, 330 .min = 0, 331 .max = 500, 332 .step = 1, 333 }, 334 [FIM_CL_NUM_SKIP] = { 335 .ops = &fim_ctrl_ops, 336 .id = V4L2_CID_IMX_FIM_NUM_SKIP, 337 .name = "FIM Num Skip", 338 .type = V4L2_CTRL_TYPE_INTEGER, 339 .def = FIM_CL_NUM_SKIP_DEF, 340 .min = 0, /* skip no frames */ 341 .max = 256, /* skip 256 frames */ 342 .step = 1, 343 }, 344}; 345 346static const struct v4l2_ctrl_config fim_icap_ctrl[] = { 347 [FIM_CL_ICAP_EDGE] = { 348 .ops = &fim_ctrl_ops, 349 .id = V4L2_CID_IMX_FIM_ICAP_EDGE, 350 .name = "FIM Input Capture Edge", 351 .type = V4L2_CTRL_TYPE_INTEGER, 352 .def = IRQ_TYPE_NONE, /* input capture disabled by default */ 353 .min = IRQ_TYPE_NONE, 354 .max = IRQ_TYPE_EDGE_BOTH, 355 .step = 1, 356 }, 357 [FIM_CL_ICAP_CHANNEL] = { 358 .ops = &fim_ctrl_ops, 359 .id = V4L2_CID_IMX_FIM_ICAP_CHANNEL, 360 .name = "FIM Input Capture Channel", 361 .type = V4L2_CTRL_TYPE_INTEGER, 362 .def = 0, 363 .min = 0, 364 .max = 1, 365 .step = 1, 366 }, 367}; 368 369static int init_fim_controls(struct imx_media_fim *fim) 370{ 371 struct v4l2_ctrl_handler *hdlr = &fim->ctrl_handler; 372 int i, ret; 373 374 v4l2_ctrl_handler_init(hdlr, FIM_NUM_CONTROLS + FIM_NUM_ICAP_CONTROLS); 375 376 for (i = 0; i < FIM_NUM_CONTROLS; i++) 377 fim->ctrl[i] = v4l2_ctrl_new_custom(hdlr, 378 &fim_ctrl[i], 379 NULL); 380 for (i = 0; i < FIM_NUM_ICAP_CONTROLS; i++) 381 fim->icap_ctrl[i] = v4l2_ctrl_new_custom(hdlr, 382 &fim_icap_ctrl[i], 383 NULL); 384 if (hdlr->error) { 385 ret = hdlr->error; 386 goto err_free; 387 } 388 389 v4l2_ctrl_cluster(FIM_NUM_CONTROLS, fim->ctrl); 390 v4l2_ctrl_cluster(FIM_NUM_ICAP_CONTROLS, fim->icap_ctrl); 391 392 return 0; 393err_free: 394 v4l2_ctrl_handler_free(hdlr); 395 return ret; 396} 397 398/* 399 * Monitor frame intervals via EOF interrupt. This method is 400 * subject to uncertainty errors introduced by interrupt latency. 401 * 402 * This is a noop if the Input Capture method is being used, since 403 * the frame_interval_monitor() is called by the input capture event 404 * callback handler in that case. 405 */ 406void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp) 407{ 408 unsigned long flags; 409 410 spin_lock_irqsave(&fim->lock, flags); 411 412 if (!icap_enabled(fim)) 413 frame_interval_monitor(fim, timestamp); 414 415 spin_unlock_irqrestore(&fim->lock, flags); 416} 417 418/* Called by the subdev in its s_stream callback */ 419int imx_media_fim_set_stream(struct imx_media_fim *fim, 420 const struct v4l2_fract *fi, 421 bool on) 422{ 423 unsigned long flags; 424 int ret = 0; 425 426 v4l2_ctrl_lock(fim->ctrl[FIM_CL_ENABLE]); 427 428 if (fim->stream_on == on) 429 goto out; 430 431 if (on) { 432 spin_lock_irqsave(&fim->lock, flags); 433 reset_fim(fim, true); 434 update_fim_nominal(fim, fi); 435 spin_unlock_irqrestore(&fim->lock, flags); 436 437 if (icap_enabled(fim)) { 438 ret = fim_request_input_capture(fim); 439 if (ret) 440 goto out; 441 fim_acquire_first_ts(fim); 442 } 443 } else { 444 if (icap_enabled(fim)) 445 fim_free_input_capture(fim); 446 } 447 448 fim->stream_on = on; 449out: 450 v4l2_ctrl_unlock(fim->ctrl[FIM_CL_ENABLE]); 451 return ret; 452} 453 454int imx_media_fim_add_controls(struct imx_media_fim *fim) 455{ 456 /* add the FIM controls to the calling subdev ctrl handler */ 457 return v4l2_ctrl_add_handler(fim->sd->ctrl_handler, 458 &fim->ctrl_handler, NULL, false); 459} 460 461/* Called by the subdev in its subdev registered callback */ 462struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd) 463{ 464 struct imx_media_fim *fim; 465 int ret; 466 467 fim = devm_kzalloc(sd->dev, sizeof(*fim), GFP_KERNEL); 468 if (!fim) 469 return ERR_PTR(-ENOMEM); 470 471 fim->sd = sd; 472 473 spin_lock_init(&fim->lock); 474 475 ret = init_fim_controls(fim); 476 if (ret) 477 return ERR_PTR(ret); 478 479 return fim; 480} 481 482void imx_media_fim_free(struct imx_media_fim *fim) 483{ 484 v4l2_ctrl_handler_free(&fim->ctrl_handler); 485}