2 * Frame Interval Monitor.
4 * Copyright (c) 2016 Mentor Graphics Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 #include <linux/delay.h>
12 #include <linux/irq.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/slab.h>
16 #include <linux/spinlock.h>
17 #include <media/v4l2-ctrls.h>
18 #include <media/v4l2-subdev.h>
19 #include <media/imx.h>
20 #include "imx-media.h"
34 FIM_NUM_ICAP_CONTROLS,
37 #define FIM_CL_ENABLE_DEF 0 /* FIM disabled by default */
38 #define FIM_CL_NUM_DEF 8 /* average 8 frames */
39 #define FIM_CL_NUM_SKIP_DEF 2 /* skip 2 frames after restart */
40 #define FIM_CL_TOLERANCE_MIN_DEF 50 /* usec */
41 #define FIM_CL_TOLERANCE_MAX_DEF 0 /* no max tolerance (unbounded) */
43 struct imx_media_fim {
44 struct imx_media_dev *md;
46 /* the owning subdev of this fim instance */
47 struct v4l2_subdev *sd;
49 /* FIM's control handler */
50 struct v4l2_ctrl_handler ctrl_handler;
52 /* control clusters */
53 struct v4l2_ctrl *ctrl[FIM_NUM_CONTROLS];
54 struct v4l2_ctrl *icap_ctrl[FIM_NUM_ICAP_CONTROLS];
56 spinlock_t lock; /* protect control values */
58 /* current control values */
62 unsigned long tolerance_min; /* usec */
63 unsigned long tolerance_max; /* usec */
64 /* input capture method of measuring FI */
69 struct timespec last_ts;
70 unsigned long sum; /* usec */
71 unsigned long nominal; /* usec */
73 struct completion icap_first_event;
77 #define icap_enabled(fim) ((fim)->icap_flags != IRQ_TYPE_NONE)
79 static void update_fim_nominal(struct imx_media_fim *fim,
80 const struct v4l2_fract *fi)
82 if (fi->denominator == 0) {
83 dev_dbg(fim->sd->dev, "no frame interval, FIM disabled\n");
88 fim->nominal = DIV_ROUND_CLOSEST_ULL(1000000ULL * (u64)fi->numerator,
91 dev_dbg(fim->sd->dev, "FI=%lu usec\n", fim->nominal);
94 static void reset_fim(struct imx_media_fim *fim, bool curval)
96 struct v4l2_ctrl *icap_chan = fim->icap_ctrl[FIM_CL_ICAP_CHANNEL];
97 struct v4l2_ctrl *icap_edge = fim->icap_ctrl[FIM_CL_ICAP_EDGE];
98 struct v4l2_ctrl *en = fim->ctrl[FIM_CL_ENABLE];
99 struct v4l2_ctrl *num = fim->ctrl[FIM_CL_NUM];
100 struct v4l2_ctrl *skip = fim->ctrl[FIM_CL_NUM_SKIP];
101 struct v4l2_ctrl *tol_min = fim->ctrl[FIM_CL_TOLERANCE_MIN];
102 struct v4l2_ctrl *tol_max = fim->ctrl[FIM_CL_TOLERANCE_MAX];
105 fim->enabled = en->cur.val;
106 fim->icap_flags = icap_edge->cur.val;
107 fim->icap_channel = icap_chan->cur.val;
108 fim->num_avg = num->cur.val;
109 fim->num_skip = skip->cur.val;
110 fim->tolerance_min = tol_min->cur.val;
111 fim->tolerance_max = tol_max->cur.val;
113 fim->enabled = en->val;
114 fim->icap_flags = icap_edge->val;
115 fim->icap_channel = icap_chan->val;
116 fim->num_avg = num->val;
117 fim->num_skip = skip->val;
118 fim->tolerance_min = tol_min->val;
119 fim->tolerance_max = tol_max->val;
122 /* disable tolerance range if max <= min */
123 if (fim->tolerance_max <= fim->tolerance_min)
124 fim->tolerance_max = 0;
126 /* num_skip must be >= 1 if input capture not used */
127 if (!icap_enabled(fim))
128 fim->num_skip = max_t(int, fim->num_skip, 1);
130 fim->counter = -fim->num_skip;
134 static void send_fim_event(struct imx_media_fim *fim, unsigned long error)
136 static const struct v4l2_event ev = {
137 .type = V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR,
140 v4l2_subdev_notify_event(fim->sd, &ev);
144 * Monitor an averaged frame interval. If the average deviates too much
145 * from the nominal frame rate, send the frame interval error event. The
146 * frame intervals are averaged in order to quiet noise from
147 * (presumably random) interrupt latency.
149 static void frame_interval_monitor(struct imx_media_fim *fim,
152 unsigned long interval, error, error_avg;
153 bool send_event = false;
154 struct timespec diff;
156 if (!fim->enabled || ++fim->counter <= 0)
159 diff = timespec_sub(*ts, fim->last_ts);
160 interval = diff.tv_sec * 1000 * 1000 + diff.tv_nsec / 1000;
161 error = abs(interval - fim->nominal);
163 if (fim->tolerance_max && error >= fim->tolerance_max) {
164 dev_dbg(fim->sd->dev,
165 "FIM: %lu ignored, out of tolerance bounds\n",
173 if (fim->counter == fim->num_avg) {
174 error_avg = DIV_ROUND_CLOSEST(fim->sum, fim->num_avg);
176 if (error_avg > fim->tolerance_min)
179 dev_dbg(fim->sd->dev, "FIM: error: %lu usec%s\n",
180 error_avg, send_event ? " (!!!)" : "");
189 send_fim_event(fim, error_avg);
192 #ifdef CONFIG_IMX_GPT_ICAP
194 * Input Capture method of measuring frame intervals. Not subject
195 * to interrupt latency.
197 static void fim_input_capture_handler(int channel, void *dev_id,
200 struct imx_media_fim *fim = dev_id;
203 spin_lock_irqsave(&fim->lock, flags);
205 frame_interval_monitor(fim, ts);
207 if (!completion_done(&fim->icap_first_event))
208 complete(&fim->icap_first_event);
210 spin_unlock_irqrestore(&fim->lock, flags);
213 static int fim_request_input_capture(struct imx_media_fim *fim)
215 init_completion(&fim->icap_first_event);
217 return mxc_request_input_capture(fim->icap_channel,
218 fim_input_capture_handler,
219 fim->icap_flags, fim);
222 static void fim_free_input_capture(struct imx_media_fim *fim)
224 mxc_free_input_capture(fim->icap_channel, fim);
227 #else /* CONFIG_IMX_GPT_ICAP */
229 static int fim_request_input_capture(struct imx_media_fim *fim)
234 static void fim_free_input_capture(struct imx_media_fim *fim)
238 #endif /* CONFIG_IMX_GPT_ICAP */
241 * In case we are monitoring the first frame interval after streamon
242 * (when fim->num_skip = 0), we need a valid fim->last_ts before we
243 * can begin. This only applies to the input capture method. It is not
244 * possible to accurately measure the first FI after streamon using the
245 * EOF method, so fim->num_skip minimum is set to 1 in that case, so this
246 * function is a noop when the EOF method is used.
248 static void fim_acquire_first_ts(struct imx_media_fim *fim)
252 if (!fim->enabled || fim->num_skip > 0)
255 ret = wait_for_completion_timeout(
256 &fim->icap_first_event,
257 msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT));
259 v4l2_warn(fim->sd, "wait first icap event timeout\n");
263 static int fim_s_ctrl(struct v4l2_ctrl *ctrl)
265 struct imx_media_fim *fim = container_of(ctrl->handler,
266 struct imx_media_fim,
271 spin_lock_irqsave(&fim->lock, flags);
274 case V4L2_CID_IMX_FIM_ENABLE:
276 case V4L2_CID_IMX_FIM_ICAP_EDGE:
285 reset_fim(fim, false);
287 spin_unlock_irqrestore(&fim->lock, flags);
291 static const struct v4l2_ctrl_ops fim_ctrl_ops = {
292 .s_ctrl = fim_s_ctrl,
295 static const struct v4l2_ctrl_config fim_ctrl[] = {
297 .ops = &fim_ctrl_ops,
298 .id = V4L2_CID_IMX_FIM_ENABLE,
299 .name = "FIM Enable",
300 .type = V4L2_CTRL_TYPE_BOOLEAN,
301 .def = FIM_CL_ENABLE_DEF,
307 .ops = &fim_ctrl_ops,
308 .id = V4L2_CID_IMX_FIM_NUM,
309 .name = "FIM Num Average",
310 .type = V4L2_CTRL_TYPE_INTEGER,
311 .def = FIM_CL_NUM_DEF,
312 .min = 1, /* no averaging */
313 .max = 64, /* average 64 frames */
316 [FIM_CL_TOLERANCE_MIN] = {
317 .ops = &fim_ctrl_ops,
318 .id = V4L2_CID_IMX_FIM_TOLERANCE_MIN,
319 .name = "FIM Tolerance Min",
320 .type = V4L2_CTRL_TYPE_INTEGER,
321 .def = FIM_CL_TOLERANCE_MIN_DEF,
326 [FIM_CL_TOLERANCE_MAX] = {
327 .ops = &fim_ctrl_ops,
328 .id = V4L2_CID_IMX_FIM_TOLERANCE_MAX,
329 .name = "FIM Tolerance Max",
330 .type = V4L2_CTRL_TYPE_INTEGER,
331 .def = FIM_CL_TOLERANCE_MAX_DEF,
336 [FIM_CL_NUM_SKIP] = {
337 .ops = &fim_ctrl_ops,
338 .id = V4L2_CID_IMX_FIM_NUM_SKIP,
339 .name = "FIM Num Skip",
340 .type = V4L2_CTRL_TYPE_INTEGER,
341 .def = FIM_CL_NUM_SKIP_DEF,
342 .min = 0, /* skip no frames */
343 .max = 256, /* skip 256 frames */
348 static const struct v4l2_ctrl_config fim_icap_ctrl[] = {
349 [FIM_CL_ICAP_EDGE] = {
350 .ops = &fim_ctrl_ops,
351 .id = V4L2_CID_IMX_FIM_ICAP_EDGE,
352 .name = "FIM Input Capture Edge",
353 .type = V4L2_CTRL_TYPE_INTEGER,
354 .def = IRQ_TYPE_NONE, /* input capture disabled by default */
355 .min = IRQ_TYPE_NONE,
356 .max = IRQ_TYPE_EDGE_BOTH,
359 [FIM_CL_ICAP_CHANNEL] = {
360 .ops = &fim_ctrl_ops,
361 .id = V4L2_CID_IMX_FIM_ICAP_CHANNEL,
362 .name = "FIM Input Capture Channel",
363 .type = V4L2_CTRL_TYPE_INTEGER,
371 static int init_fim_controls(struct imx_media_fim *fim)
373 struct v4l2_ctrl_handler *hdlr = &fim->ctrl_handler;
376 v4l2_ctrl_handler_init(hdlr, FIM_NUM_CONTROLS + FIM_NUM_ICAP_CONTROLS);
378 for (i = 0; i < FIM_NUM_CONTROLS; i++)
379 fim->ctrl[i] = v4l2_ctrl_new_custom(hdlr,
382 for (i = 0; i < FIM_NUM_ICAP_CONTROLS; i++)
383 fim->icap_ctrl[i] = v4l2_ctrl_new_custom(hdlr,
391 v4l2_ctrl_cluster(FIM_NUM_CONTROLS, fim->ctrl);
392 v4l2_ctrl_cluster(FIM_NUM_ICAP_CONTROLS, fim->icap_ctrl);
396 v4l2_ctrl_handler_free(hdlr);
401 * Monitor frame intervals via EOF interrupt. This method is
402 * subject to uncertainty errors introduced by interrupt latency.
404 * This is a noop if the Input Capture method is being used, since
405 * the frame_interval_monitor() is called by the input capture event
406 * callback handler in that case.
408 void imx_media_fim_eof_monitor(struct imx_media_fim *fim, struct timespec *ts)
412 spin_lock_irqsave(&fim->lock, flags);
414 if (!icap_enabled(fim))
415 frame_interval_monitor(fim, ts);
417 spin_unlock_irqrestore(&fim->lock, flags);
419 EXPORT_SYMBOL_GPL(imx_media_fim_eof_monitor);
421 /* Called by the subdev in its s_stream callback */
422 int imx_media_fim_set_stream(struct imx_media_fim *fim,
423 const struct v4l2_fract *fi,
429 v4l2_ctrl_lock(fim->ctrl[FIM_CL_ENABLE]);
431 if (fim->stream_on == on)
435 spin_lock_irqsave(&fim->lock, flags);
436 reset_fim(fim, true);
437 update_fim_nominal(fim, fi);
438 spin_unlock_irqrestore(&fim->lock, flags);
440 if (icap_enabled(fim)) {
441 ret = fim_request_input_capture(fim);
444 fim_acquire_first_ts(fim);
447 if (icap_enabled(fim))
448 fim_free_input_capture(fim);
453 v4l2_ctrl_unlock(fim->ctrl[FIM_CL_ENABLE]);
456 EXPORT_SYMBOL_GPL(imx_media_fim_set_stream);
458 int imx_media_fim_add_controls(struct imx_media_fim *fim)
460 /* add the FIM controls to the calling subdev ctrl handler */
461 return v4l2_ctrl_add_handler(fim->sd->ctrl_handler,
462 &fim->ctrl_handler, NULL);
464 EXPORT_SYMBOL_GPL(imx_media_fim_add_controls);
466 /* Called by the subdev in its subdev registered callback */
467 struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd)
469 struct imx_media_fim *fim;
472 fim = devm_kzalloc(sd->dev, sizeof(*fim), GFP_KERNEL);
474 return ERR_PTR(-ENOMEM);
476 /* get media device */
477 fim->md = dev_get_drvdata(sd->v4l2_dev->dev);
480 spin_lock_init(&fim->lock);
482 ret = init_fim_controls(fim);
488 EXPORT_SYMBOL_GPL(imx_media_fim_init);
490 void imx_media_fim_free(struct imx_media_fim *fim)
492 v4l2_ctrl_handler_free(&fim->ctrl_handler);
494 EXPORT_SYMBOL_GPL(imx_media_fim_free);