lkml.org 
[lkml]   [2017]   [Mar]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v6 32/39] media: imx: csi: add frame skipping support
    Date
    From: Philipp Zabel <p.zabel@pengutronix.de>

    The CSI can skip any out of up to 6 input frames, allowing to reduce the
    frame rate at the output pads by small fractions.

    Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
    Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
    Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
    ---
    drivers/staging/media/imx/imx-media-csi.c | 127 ++++++++++++++++++++++++++++--
    1 file changed, 122 insertions(+), 5 deletions(-)

    diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
    index 5a09fa8..d9c3a3b 100644
    --- a/drivers/staging/media/imx/imx-media-csi.c
    +++ b/drivers/staging/media/imx/imx-media-csi.c
    @@ -1,13 +1,15 @@
    /*
    * V4L2 Capture CSI Subdev for Freescale i.MX5/6 SOC
    *
    - * Copyright (c) 2014-2016 Mentor Graphics Inc.
    + * Copyright (c) 2014-2017 Mentor Graphics Inc.
    + * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de>
    *
    * 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/gcd.h>
    #include <linux/interrupt.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    @@ -40,6 +42,18 @@
    #define H_ALIGN 1 /* multiple of 2 lines */
    #define S_ALIGN 1 /* multiple of 2 */

    +/*
    + * struct csi_skip_desc - CSI frame skipping descriptor
    + * @keep - number of frames kept per max_ratio frames
    + * @max_ratio - width of skip_smfc, written to MAX_RATIO bitfield
    + * @skip_smfc - skip pattern written to the SKIP_SMFC bitfield
    + */
    +struct csi_skip_desc {
    + u8 keep;
    + u8 max_ratio;
    + u8 skip_smfc;
    +};
    +
    struct csi_priv {
    struct device *dev;
    struct ipu_soc *ipu;
    @@ -65,6 +79,7 @@ struct csi_priv {
    const struct imx_media_pixfmt *cc[CSI_NUM_PADS];
    struct v4l2_fract frame_interval;
    struct v4l2_rect crop;
    + const struct csi_skip_desc *skip[CSI_NUM_PADS - 1];

    /* active vb2 buffers to send to video dev sink */
    struct imx_media_buffer *active_vb2_buf[2];
    @@ -536,10 +551,12 @@ static int csi_setup(struct csi_priv *priv)
    struct v4l2_mbus_config sensor_mbus_cfg;
    struct v4l2_of_endpoint *sensor_ep;
    struct v4l2_mbus_framefmt if_fmt;
    + const struct csi_skip_desc *skip;

    infmt = &priv->format_mbus[CSI_SINK_PAD];
    outfmt = &priv->format_mbus[priv->active_output_pad];
    sensor_ep = &priv->sensor->sensor_ep;
    + skip = priv->skip[priv->active_output_pad - 1];

    /* compose mbus_config from sensor endpoint */
    sensor_mbus_cfg.type = sensor_ep->bus_type;
    @@ -564,6 +581,9 @@ static int csi_setup(struct csi_priv *priv)

    ipu_csi_set_dest(priv->csi, priv->dest);

    + ipu_csi_set_skip_smfc(priv->csi, skip->skip_smfc, skip->max_ratio - 1,
    + 0);
    +
    ipu_csi_dump(priv->csi);

    return 0;
    @@ -627,6 +647,79 @@ static void csi_stop(struct csi_priv *priv)
    ipu_csi_disable(priv->csi);
    }

    +static const struct csi_skip_desc csi_skip[12] = {
    + { 1, 1, 0x00 }, /* Keep all frames */
    + { 5, 6, 0x10 }, /* Skip every sixth frame */
    + { 4, 5, 0x08 }, /* Skip every fifth frame */
    + { 3, 4, 0x04 }, /* Skip every fourth frame */
    + { 2, 3, 0x02 }, /* Skip every third frame */
    + { 3, 5, 0x0a }, /* Skip frames 1 and 3 of every 5 */
    + { 1, 2, 0x01 }, /* Skip every second frame */
    + { 2, 5, 0x0b }, /* Keep frames 1 and 4 of every 5 */
    + { 1, 3, 0x03 }, /* Keep one in three frames */
    + { 1, 4, 0x07 }, /* Keep one in four frames */
    + { 1, 5, 0x0f }, /* Keep one in five frames */
    + { 1, 6, 0x1f }, /* Keep one in six frames */
    +};
    +
    +static void csi_apply_skip_interval(const struct csi_skip_desc *skip,
    + struct v4l2_fract *interval)
    +{
    + unsigned int div;
    +
    + interval->numerator *= skip->max_ratio;
    + interval->denominator *= skip->keep;
    +
    + /* Reduce fraction to lowest terms */
    + div = gcd(interval->numerator, interval->denominator);
    + if (div > 1) {
    + interval->numerator /= div;
    + interval->denominator /= div;
    + }
    +}
    +
    +/*
    + * Find the skip pattern to produce the output frame interval closest to the
    + * requested one, for the given input frame interval. Updates the output frame
    + * interval to the exact value.
    + */
    +static const struct csi_skip_desc *csi_find_best_skip(struct v4l2_fract *in,
    + struct v4l2_fract *out)
    +{
    + const struct csi_skip_desc *skip = &csi_skip[0], *best_skip = skip;
    + u32 min_err = UINT_MAX;
    + u64 want_us;
    + int i;
    +
    + /* Default to 1:1 ratio */
    + if (out->numerator == 0 || out->denominator == 0 ||
    + in->numerator == 0 || in->denominator == 0) {
    + *out = *in;
    + return best_skip;
    + }
    +
    + want_us = div_u64((u64)USEC_PER_SEC * out->numerator, out->denominator);
    +
    + /* Find the reduction closest to the requested time per frame */
    + for (i = 0; i < ARRAY_SIZE(csi_skip); i++, skip++) {
    + u64 tmp, err;
    +
    + tmp = div_u64((u64)USEC_PER_SEC * in->numerator *
    + skip->max_ratio, in->denominator * skip->keep);
    +
    + err = abs((s64)tmp - want_us);
    + if (err < min_err) {
    + min_err = err;
    + best_skip = skip;
    + }
    + }
    +
    + *out = *in;
    + csi_apply_skip_interval(best_skip, out);
    +
    + return best_skip;
    +}
    +
    /*
    * V4L2 subdev operations.
    */
    @@ -636,8 +729,16 @@ static int csi_g_frame_interval(struct v4l2_subdev *sd,
    {
    struct csi_priv *priv = v4l2_get_subdevdata(sd);

    + if (fi->pad >= CSI_NUM_PADS)
    + return -EINVAL;
    +
    mutex_lock(&priv->lock);
    +
    fi->interval = priv->frame_interval;
    +
    + if (fi->pad != CSI_SINK_PAD)
    + csi_apply_skip_interval(priv->skip[fi->pad - 1], &fi->interval);
    +
    mutex_unlock(&priv->lock);

    return 0;
    @@ -648,14 +749,27 @@ static int csi_s_frame_interval(struct v4l2_subdev *sd,
    {
    struct csi_priv *priv = v4l2_get_subdevdata(sd);

    + if (fi->pad >= CSI_NUM_PADS)
    + return -EINVAL;
    +
    mutex_lock(&priv->lock);

    - /* Output pads mirror active input pad, no limits on input pads */
    - if (fi->pad == CSI_SRC_PAD_IDMAC || fi->pad == CSI_SRC_PAD_DIRECT)
    - fi->interval = priv->frame_interval;
    + /* No limits on input pad */
    + if (fi->pad == CSI_SINK_PAD) {
    + priv->frame_interval = fi->interval;
    +
    + /* Reset frame skipping ratio to 1:1 */
    + priv->skip[0] = &csi_skip[0];
    + priv->skip[1] = &csi_skip[0];

    - priv->frame_interval = fi->interval;
    + goto out;
    + }
    +
    + /* Output pads depend on input interval, modified by frame skipping */
    + priv->skip[fi->pad - 1] = csi_find_best_skip(&priv->frame_interval,
    + &fi->interval);

    +out:
    mutex_unlock(&priv->lock);

    return 0;
    @@ -1268,6 +1382,9 @@ static int csi_registered(struct v4l2_subdev *sd)
    &priv->cc[i]);
    if (ret)
    goto put_csi;
    + /* disable frame skipping */
    + if (i != CSI_SINK_PAD)
    + priv->skip[i - 1] = &csi_skip[0];
    }

    /* init default frame interval */
    --
    2.7.4
    \
     
     \ /
      Last update: 2017-03-28 02:46    [W:5.123 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site