cregit-Linux how code gets into the kernel

Release 4.16 drivers/staging/media/imx/imx-media-fim.c

/*
 * Frame Interval Monitor.
 *
 * Copyright (c) 2016 Mentor Graphics Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <media/imx.h>
#include "imx-media.h"


enum {
	
FIM_CL_ENABLE = 0,
	
FIM_CL_NUM,
	
FIM_CL_TOLERANCE_MIN,
	
FIM_CL_TOLERANCE_MAX,
	
FIM_CL_NUM_SKIP,
	
FIM_NUM_CONTROLS,
};


enum {
	
FIM_CL_ICAP_EDGE = 0,
	
FIM_CL_ICAP_CHANNEL,
	
FIM_NUM_ICAP_CONTROLS,
};


#define FIM_CL_ENABLE_DEF          0 
/* FIM disabled by default */

#define FIM_CL_NUM_DEF             8 
/* average 8 frames */

#define FIM_CL_NUM_SKIP_DEF        2 
/* skip 2 frames after restart */

#define FIM_CL_TOLERANCE_MIN_DEF  50 
/* usec */

#define FIM_CL_TOLERANCE_MAX_DEF   0 
/* no max tolerance (unbounded) */


struct imx_media_fim {
	
struct imx_media_dev *md;

	/* the owning subdev of this fim instance */
	
struct v4l2_subdev *sd;

	/* FIM's control handler */
	
struct v4l2_ctrl_handler ctrl_handler;

	/* control clusters */
	
struct v4l2_ctrl  *ctrl[FIM_NUM_CONTROLS];
	
struct v4l2_ctrl  *icap_ctrl[FIM_NUM_ICAP_CONTROLS];

	
spinlock_t        lock; /* protect control values */

	/* current control values */
	
bool              enabled;
	
int               num_avg;
	
int               num_skip;
	
unsigned long     tolerance_min; /* usec */
	
unsigned long     tolerance_max; /* usec */
	/* input capture method of measuring FI */
	
int               icap_channel;
	
int               icap_flags;

	
int               counter;
	
ktime_t		  last_ts;
	
unsigned long     sum;       /* usec */
	
unsigned long     nominal;   /* usec */

	
struct completion icap_first_event;
	
bool              stream_on;
};


#define icap_enabled(fim) ((fim)->icap_flags != IRQ_TYPE_NONE)


static void update_fim_nominal(struct imx_media_fim *fim, const struct v4l2_fract *fi) { if (fi->denominator == 0) { dev_dbg(fim->sd->dev, "no frame interval, FIM disabled\n"); fim->enabled = false; return; } fim->nominal = DIV_ROUND_CLOSEST_ULL(1000000ULL * (u64)fi->numerator, fi->denominator); dev_dbg(fim->sd->dev, "FI=%lu usec\n", fim->nominal); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam80100.00%1100.00%
Total80100.00%1100.00%


static void reset_fim(struct imx_media_fim *fim, bool curval) { struct v4l2_ctrl *icap_chan = fim->icap_ctrl[FIM_CL_ICAP_CHANNEL]; struct v4l2_ctrl *icap_edge = fim->icap_ctrl[FIM_CL_ICAP_EDGE]; struct v4l2_ctrl *en = fim->ctrl[FIM_CL_ENABLE]; struct v4l2_ctrl *num = fim->ctrl[FIM_CL_NUM]; struct v4l2_ctrl *skip = fim->ctrl[FIM_CL_NUM_SKIP]; struct v4l2_ctrl *tol_min = fim->ctrl[FIM_CL_TOLERANCE_MIN]; struct v4l2_ctrl *tol_max = fim->ctrl[FIM_CL_TOLERANCE_MAX]; if (curval) { fim->enabled = en->cur.val; fim->icap_flags = icap_edge->cur.val; fim->icap_channel = icap_chan->cur.val; fim->num_avg = num->cur.val; fim->num_skip = skip->cur.val; fim->tolerance_min = tol_min->cur.val; fim->tolerance_max = tol_max->cur.val; } else { fim->enabled = en->val; fim->icap_flags = icap_edge->val; fim->icap_channel = icap_chan->val; fim->num_avg = num->val; fim->num_skip = skip->val; fim->tolerance_min = tol_min->val; fim->tolerance_max = tol_max->val; } /* disable tolerance range if max <= min */ if (fim->tolerance_max <= fim->tolerance_min) fim->tolerance_max = 0; /* num_skip must be >= 1 if input capture not used */ if (!icap_enabled(fim)) fim->num_skip = max_t(int, fim->num_skip, 1); fim->counter = -fim->num_skip; fim->sum = 0; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam289100.00%1100.00%
Total289100.00%1100.00%


static void send_fim_event(struct imx_media_fim *fim, unsigned long error) { static const struct v4l2_event ev = { .type = V4L2_EVENT_IMX_FRAME_INTERVAL_ERROR, }; v4l2_subdev_notify_event(fim->sd, &ev); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam38100.00%1100.00%
Total38100.00%1100.00%

/* * Monitor an averaged frame interval. If the average deviates too much * from the nominal frame rate, send the frame interval error event. The * frame intervals are averaged in order to quiet noise from * (presumably random) interrupt latency. */
static void frame_interval_monitor(struct imx_media_fim *fim, ktime_t timestamp) { long long interval, error; unsigned long error_avg; bool send_event = false; if (!fim->enabled || ++fim->counter <= 0) goto out_update_ts; /* max error is less than l00µs, so use 32-bit division or fail */ interval = ktime_to_ns(ktime_sub(timestamp, fim->last_ts)); error = abs(interval - NSEC_PER_USEC * (u64)fim->nominal); if (error > U32_MAX) error = U32_MAX; else error = abs((u32)error / NSEC_PER_USEC); if (fim->tolerance_max && error >= fim->tolerance_max) { dev_dbg(fim->sd->dev, "FIM: %llu ignored, out of tolerance bounds\n", error); fim->counter--; goto out_update_ts; } fim->sum += error; if (fim->counter == fim->num_avg) { error_avg = DIV_ROUND_CLOSEST(fim->sum, fim->num_avg); if (error_avg > fim->tolerance_min) send_event = true; dev_dbg(fim->sd->dev, "FIM: error: %lu usec%s\n", error_avg, send_event ? " (!!!)" : ""); fim->counter = 0; fim->sum = 0; } out_update_ts: fim->last_ts = timestamp; if (send_event) send_fim_event(fim, error_avg); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam18581.14%150.00%
Arnd Bergmann4318.86%150.00%
Total228100.00%2100.00%

#ifdef CONFIG_IMX_GPT_ICAP /* * Input Capture method of measuring frame intervals. Not subject * to interrupt latency. */
static void fim_input_capture_handler(int channel, void *dev_id, ktime_t timestamp) { struct imx_media_fim *fim = dev_id; unsigned long flags; spin_lock_irqsave(&fim->lock, flags); frame_interval_monitor(fim, timestamp); if (!completion_done(&fim->icap_first_event)) complete(&fim->icap_first_event); spin_unlock_irqrestore(&fim->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam7095.89%150.00%
Arnd Bergmann34.11%150.00%
Total73100.00%2100.00%


static int fim_request_input_capture(struct imx_media_fim *fim) { init_completion(&fim->icap_first_event); return mxc_request_input_capture(fim->icap_channel, fim_input_capture_handler, fim->icap_flags, fim); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam35100.00%1100.00%
Total35100.00%1100.00%


static void fim_free_input_capture(struct imx_media_fim *fim) { mxc_free_input_capture(fim->icap_channel, fim); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam20100.00%1100.00%
Total20100.00%1100.00%

#else /* CONFIG_IMX_GPT_ICAP */
static int fim_request_input_capture(struct imx_media_fim *fim) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam14100.00%1100.00%
Total14100.00%1100.00%


static void fim_free_input_capture(struct imx_media_fim *fim) { }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam10100.00%1100.00%
Total10100.00%1100.00%

#endif /* CONFIG_IMX_GPT_ICAP */ /* * In case we are monitoring the first frame interval after streamon * (when fim->num_skip = 0), we need a valid fim->last_ts before we * can begin. This only applies to the input capture method. It is not * possible to accurately measure the first FI after streamon using the * EOF method, so fim->num_skip minimum is set to 1 in that case, so this * function is a noop when the EOF method is used. */
static void fim_acquire_first_ts(struct imx_media_fim *fim) { unsigned long ret; if (!fim->enabled || fim->num_skip > 0) return; ret = wait_for_completion_timeout( &fim->icap_first_event, msecs_to_jiffies(IMX_MEDIA_EOF_TIMEOUT)); if (ret == 0) v4l2_warn(fim->sd, "wait first icap event timeout\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam59100.00%1100.00%
Total59100.00%1100.00%

/* FIM Controls */
static int fim_s_ctrl(struct v4l2_ctrl *ctrl) { struct imx_media_fim *fim = container_of(ctrl->handler, struct imx_media_fim, ctrl_handler); unsigned long flags; int ret = 0; spin_lock_irqsave(&fim->lock, flags); switch (ctrl->id) { case V4L2_CID_IMX_FIM_ENABLE: break; case V4L2_CID_IMX_FIM_ICAP_EDGE: if (fim->stream_on) ret = -EBUSY; break; default: ret = -EINVAL; } if (!ret) reset_fim(fim, false); spin_unlock_irqrestore(&fim->lock, flags); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam105100.00%1100.00%
Total105100.00%1100.00%

static const struct v4l2_ctrl_ops fim_ctrl_ops = { .s_ctrl = fim_s_ctrl, }; static const struct v4l2_ctrl_config fim_ctrl[] = { [FIM_CL_ENABLE] = { .ops = &fim_ctrl_ops, .id = V4L2_CID_IMX_FIM_ENABLE, .name = "FIM Enable", .type = V4L2_CTRL_TYPE_BOOLEAN, .def = FIM_CL_ENABLE_DEF, .min = 0, .max = 1, .step = 1, }, [FIM_CL_NUM] = { .ops = &fim_ctrl_ops, .id = V4L2_CID_IMX_FIM_NUM, .name = "FIM Num Average", .type = V4L2_CTRL_TYPE_INTEGER, .def = FIM_CL_NUM_DEF, .min = 1, /* no averaging */ .max = 64, /* average 64 frames */ .step = 1, }, [FIM_CL_TOLERANCE_MIN] = { .ops = &fim_ctrl_ops, .id = V4L2_CID_IMX_FIM_TOLERANCE_MIN, .name = "FIM Tolerance Min", .type = V4L2_CTRL_TYPE_INTEGER, .def = FIM_CL_TOLERANCE_MIN_DEF, .min = 2, .max = 200, .step = 1, }, [FIM_CL_TOLERANCE_MAX] = { .ops = &fim_ctrl_ops, .id = V4L2_CID_IMX_FIM_TOLERANCE_MAX, .name = "FIM Tolerance Max", .type = V4L2_CTRL_TYPE_INTEGER, .def = FIM_CL_TOLERANCE_MAX_DEF, .min = 0, .max = 500, .step = 1, }, [FIM_CL_NUM_SKIP] = { .ops = &fim_ctrl_ops, .id = V4L2_CID_IMX_FIM_NUM_SKIP, .name = "FIM Num Skip", .type = V4L2_CTRL_TYPE_INTEGER, .def = FIM_CL_NUM_SKIP_DEF, .min = 0, /* skip no frames */ .max = 256, /* skip 256 frames */ .step = 1, }, }; static const struct v4l2_ctrl_config fim_icap_ctrl[] = { [FIM_CL_ICAP_EDGE] = { .ops = &fim_ctrl_ops, .id = V4L2_CID_IMX_FIM_ICAP_EDGE, .name = "FIM Input Capture Edge", .type = V4L2_CTRL_TYPE_INTEGER, .def = IRQ_TYPE_NONE, /* input capture disabled by default */ .min = IRQ_TYPE_NONE, .max = IRQ_TYPE_EDGE_BOTH, .step = 1, }, [FIM_CL_ICAP_CHANNEL] = { .ops = &fim_ctrl_ops, .id = V4L2_CID_IMX_FIM_ICAP_CHANNEL, .name = "FIM Input Capture Channel", .type = V4L2_CTRL_TYPE_INTEGER, .def = 0, .min = 0, .max = 1, .step = 1, }, };
static int init_fim_controls(struct imx_media_fim *fim) { struct v4l2_ctrl_handler *hdlr = &fim->ctrl_handler; int i, ret; v4l2_ctrl_handler_init(hdlr, FIM_NUM_CONTROLS + FIM_NUM_ICAP_CONTROLS); for (i = 0; i < FIM_NUM_CONTROLS; i++) fim->ctrl[i] = v4l2_ctrl_new_custom(hdlr, &fim_ctrl[i], NULL); for (i = 0; i < FIM_NUM_ICAP_CONTROLS; i++) fim->icap_ctrl[i] = v4l2_ctrl_new_custom(hdlr, &fim_icap_ctrl[i], NULL); if (hdlr->error) { ret = hdlr->error; goto err_free; } v4l2_ctrl_cluster(FIM_NUM_CONTROLS, fim->ctrl); v4l2_ctrl_cluster(FIM_NUM_ICAP_CONTROLS, fim->icap_ctrl); return 0; err_free: v4l2_ctrl_handler_free(hdlr); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam149100.00%1100.00%
Total149100.00%1100.00%

/* * Monitor frame intervals via EOF interrupt. This method is * subject to uncertainty errors introduced by interrupt latency. * * This is a noop if the Input Capture method is being used, since * the frame_interval_monitor() is called by the input capture event * callback handler in that case. */
void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp) { unsigned long flags; spin_lock_irqsave(&fim->lock, flags); if (!icap_enabled(fim)) frame_interval_monitor(fim, timestamp); spin_unlock_irqrestore(&fim->lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam4994.23%150.00%
Arnd Bergmann35.77%150.00%
Total52100.00%2100.00%

EXPORT_SYMBOL_GPL(imx_media_fim_eof_monitor); /* Called by the subdev in its s_stream callback */
int imx_media_fim_set_stream(struct imx_media_fim *fim, const struct v4l2_fract *fi, bool on) { unsigned long flags; int ret = 0; v4l2_ctrl_lock(fim->ctrl[FIM_CL_ENABLE]); if (fim->stream_on == on) goto out; if (on) { spin_lock_irqsave(&fim->lock, flags); reset_fim(fim, true); update_fim_nominal(fim, fi); spin_unlock_irqrestore(&fim->lock, flags); if (icap_enabled(fim)) { ret = fim_request_input_capture(fim); if (ret) goto out; fim_acquire_first_ts(fim); } } else { if (icap_enabled(fim)) fim_free_input_capture(fim); } fim->stream_on = on; out: v4l2_ctrl_unlock(fim->ctrl[FIM_CL_ENABLE]); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam153100.00%1100.00%
Total153100.00%1100.00%

EXPORT_SYMBOL_GPL(imx_media_fim_set_stream);
int imx_media_fim_add_controls(struct imx_media_fim *fim) { /* add the FIM controls to the calling subdev ctrl handler */ return v4l2_ctrl_add_handler(fim->sd->ctrl_handler, &fim->ctrl_handler, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam28100.00%1100.00%
Total28100.00%1100.00%

EXPORT_SYMBOL_GPL(imx_media_fim_add_controls); /* Called by the subdev in its subdev registered callback */
struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd) { struct imx_media_fim *fim; int ret; fim = devm_kzalloc(sd->dev, sizeof(*fim), GFP_KERNEL); if (!fim) return ERR_PTR(-ENOMEM); /* get media device */ fim->md = dev_get_drvdata(sd->v4l2_dev->dev); fim->sd = sd; spin_lock_init(&fim->lock); ret = init_fim_controls(fim); if (ret) return ERR_PTR(ret); return fim; }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam97100.00%1100.00%
Total97100.00%1100.00%

EXPORT_SYMBOL_GPL(imx_media_fim_init);
void imx_media_fim_free(struct imx_media_fim *fim) { v4l2_ctrl_handler_free(&fim->ctrl_handler); }

Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam18100.00%1100.00%
Total18100.00%1100.00%

EXPORT_SYMBOL_GPL(imx_media_fim_free);

Overall Contributors

PersonTokensPropCommitsCommitProp
Steve Longerbeam198797.55%150.00%
Arnd Bergmann502.45%150.00%
Total2037100.00%2100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.