On Tue, 14 Feb 2012, Javier Martin wrote:

> If the attached video sensor cannot provide the
> requested image size, try to use resizing engine
> included in the eMMa-PrP IP.
> 
> This patch supports both averaging and bilinear
> algorithms.
> 
> Signed-off-by: Javier Martin <[email protected]>
> ---
>  drivers/media/video/mx2_camera.c |  251 
> +++++++++++++++++++++++++++++++++++++-
>  1 files changed, 249 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/media/video/mx2_camera.c 
> b/drivers/media/video/mx2_camera.c
> index 9e84368..6516ee4 100644
> --- a/drivers/media/video/mx2_camera.c
> +++ b/drivers/media/video/mx2_camera.c
> @@ -19,6 +19,7 @@
>  #include <linux/dma-mapping.h>
>  #include <linux/errno.h>
>  #include <linux/fs.h>
> +#include <linux/gcd.h>
>  #include <linux/interrupt.h>
>  #include <linux/kernel.h>
>  #include <linux/mm.h>
> @@ -204,10 +205,25 @@
>  #define PRP_INTR_LBOVF               (1 << 7)
>  #define PRP_INTR_CH2OVF              (1 << 8)
>  
> +/* Resizing registers */
> +#define PRP_RZ_VALID_TBL_LEN(x)      ((x) << 24)
> +#define PRP_RZ_VALID_BILINEAR        (1 << 31)
> +
>  #define mx27_camera_emma(pcdev)      (cpu_is_mx27() && pcdev->use_emma)
>  
>  #define MAX_VIDEO_MEM        16
>  
> +#define RESIZE_NUM_MIN       1
> +#define RESIZE_NUM_MAX       20
> +#define BC_COEF              3
> +#define SZ_COEF              (1 << BC_COEF)
> +
> +#define RESIZE_DIR_H 0
> +#define RESIZE_DIR_V 1
> +
> +#define RESIZE_ALGO_BILINEAR 0
> +#define RESIZE_ALGO_AVERAGING 1
> +
>  struct mx2_prp_cfg {
>       int channel;
>       u32 in_fmt;
> @@ -217,6 +233,13 @@ struct mx2_prp_cfg {
>       u32 irq_flags;
>  };
>  
> +/* prp resizing parameters */
> +struct emma_prp_resize {
> +     int             algo; /* type of algorithm used */
> +     int             len; /* number of coefficients */
> +     unsigned char   s[RESIZE_NUM_MAX]; /* table of coefficients */
> +};
> +
>  /* prp configuration for a client-host fmt pair */
>  struct mx2_fmt_cfg {
>       enum v4l2_mbus_pixelcode        in_fmt;
> @@ -278,6 +301,8 @@ struct mx2_camera_dev {
>       dma_addr_t              discard_buffer_dma;
>       size_t                  discard_size;
>       struct mx2_fmt_cfg      *emma_prp;
> +     struct emma_prp_resize  resizing[2];
> +     unsigned int            s_width, s_height;
>       u32                     frame_count;
>       struct vb2_alloc_ctx    *alloc_ctx;
>  };
> @@ -687,7 +712,7 @@ static void mx27_camera_emma_buf_init(struct 
> soc_camera_device *icd,
>       struct mx2_camera_dev *pcdev = ici->priv;
>       struct mx2_fmt_cfg *prp = pcdev->emma_prp;
>  
> -     writel((icd->user_width << 16) | icd->user_height,
> +     writel((pcdev->s_width << 16) | pcdev->s_height,
>              pcdev->base_emma + PRP_SRC_FRAME_SIZE);
>       writel(prp->cfg.src_pixel,
>              pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
> @@ -707,6 +732,74 @@ static void mx27_camera_emma_buf_init(struct 
> soc_camera_device *icd,
>       writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
>  }
>  
> +static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
> +{
> +     int dir;
> +
> +     for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
> +             unsigned char *s = pcdev->resizing[dir].s;
> +             int len = pcdev->resizing[dir].len;
> +             unsigned int coeff[2] = {0, 0};
> +             unsigned int valid  = 0;
> +             int i;
> +
> +             if (len == 0)
> +                     continue;
> +
> +             for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
> +                     int j;
> +
> +                     j = i > 9 ? 1 : 0;
> +                     coeff[j] = (coeff[j] << BC_COEF) |
> +                     (s[i] & (SZ_COEF - 1));

Please, remember to indent line continuations.

> +
> +                     if (i == 5 || i == 15)
> +                             coeff[j] <<= 1;
> +
> +                     valid = (valid << 1) | (s[i] >> BC_COEF);
> +             }
> +
> +             valid |= PRP_RZ_VALID_TBL_LEN(len);
> +
> +             if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
> +                     valid |= PRP_RZ_VALID_BILINEAR;
> +
> +             if (pcdev->emma_prp->cfg.channel == 1) {

Maybe put horizontal and vertical register addresses in an array too to 
avoid this "if?" Just an idea - if you don't think, the code would look 
nicer, just leave as is.

> +                     if (dir == RESIZE_DIR_H) {
> +                             writel(coeff[0], pcdev->base_emma +
> +                                                     PRP_CH1_RZ_HORI_COEF1);
> +                             writel(coeff[1], pcdev->base_emma +
> +                                                     PRP_CH1_RZ_HORI_COEF2);
> +                             writel(valid, pcdev->base_emma +
> +                                                     PRP_CH1_RZ_HORI_VALID);
> +                     } else {
> +                             writel(coeff[0], pcdev->base_emma +
> +                                                     PRP_CH1_RZ_VERT_COEF1);
> +                             writel(coeff[1], pcdev->base_emma +
> +                                                     PRP_CH1_RZ_VERT_COEF2);
> +                             writel(valid, pcdev->base_emma +
> +                                                     PRP_CH1_RZ_VERT_VALID);
> +                     }
> +             } else {
> +                     if (dir == RESIZE_DIR_H) {
> +                             writel(coeff[0], pcdev->base_emma +
> +                                                     PRP_CH2_RZ_HORI_COEF1);
> +                             writel(coeff[1], pcdev->base_emma +
> +                                                     PRP_CH2_RZ_HORI_COEF2);
> +                             writel(valid, pcdev->base_emma +
> +                                                     PRP_CH2_RZ_HORI_VALID);
> +                     } else {
> +                             writel(coeff[0], pcdev->base_emma +
> +                                                     PRP_CH2_RZ_VERT_COEF1);
> +                             writel(coeff[1], pcdev->base_emma +
> +                                                     PRP_CH2_RZ_VERT_COEF2);
> +                             writel(valid, pcdev->base_emma +
> +                                                     PRP_CH2_RZ_VERT_VALID);
> +                     }
> +             }
> +     }
> +}
> +
>  static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
>  {
>       struct soc_camera_device *icd = soc_camera_from_vb2q(q);
> @@ -773,6 +866,8 @@ static int mx2_start_streaming(struct vb2_queue *q, 
> unsigned int count)
>               list_add_tail(&pcdev->buf_discard[1].queue,
>                                     &pcdev->discard);
>  
> +             mx2_prp_resize_commit(pcdev);
> +
>               mx27_camera_emma_buf_init(icd, bytesperline);
>  
>               if (prp->cfg.channel == 1) {
> @@ -1059,6 +1154,119 @@ static int mx2_camera_get_formats(struct 
> soc_camera_device *icd,
>       return formats;
>  }
>  
> +static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
> +                           struct v4l2_mbus_framefmt *mf_in,
> +                           struct v4l2_pix_format *pix_out)
> +{
> +     int num, den;
> +     unsigned long m;
> +     int i, dir;
> +
> +     for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
> +             unsigned char *s = pcdev->resizing[dir].s;
> +             int len = 0;
> +             int in, out;
> +
> +             if (dir == RESIZE_DIR_H) {
> +                     in = mf_in->width;
> +                     out = pix_out->width;
> +             } else {
> +                     in = mf_in->height;
> +                     out = pix_out->height;
> +             }
> +
> +             if (in < out)
> +                     return -EINVAL;
> +             else if (in == out)
> +                     continue;
> +
> +             /* Calculate ratio */
> +             m = gcd(in, out);
> +             num = in / m;
> +             den = out / m;
> +             if (num > RESIZE_NUM_MAX)
> +                     return -EINVAL;
> +
> +             if ((num >= 2 * den) && (den == 1) &&
> +                 (num < 9) && (!(num & 0x01))) {
> +                     int sum = 0;
> +                     int j;
> +
> +                     /* Average scaling for > 2:1 ratios */

">=" rather than ">"

> +                     /* Support can be added for num >=9 and odd values */

So, this is only used for downscaling by 1/2, 1/4, 1/6, and 1/8?

> +
> +                     pcdev->resizing[dir].algo = RESIZE_ALGO_AVERAGING;
> +                     len = num;
> +
> +                     for (i = 0; i < (len / 2); i++)
> +                             s[i] = 8;
> +
> +                     do {
> +                             for (i = 0; i < (len / 2); i++) {
> +                                     s[i] = s[i] >> 1;
> +                                     sum = 0;
> +                                     for (j = 0; j < (len / 2); j++)
> +                                             sum += s[j];
> +                                     if (sum == 4)
> +                                             break;
> +                             }
> +                     } while (sum != 4);
> +
> +                     for (i = (len / 2); i < len; i++)
> +                             s[i] = s[len - i - 1];
> +
> +                     s[len - 1] |= SZ_COEF;
> +             } else {
> +                     /* bilinear scaling for < 2:1 ratios */
> +                     int v; /* overflow counter */
> +                     int coeff, nxt; /* table output */
> +                     int in_pos_inc = 2 * den;
> +                     int out_pos = num;
> +                     int out_pos_inc = 2 * num;
> +                     int init_carry = num - den;
> +                     int carry = init_carry;
> +
> +                     pcdev->resizing[dir].algo = RESIZE_ALGO_BILINEAR;
> +                     v = den + in_pos_inc;
> +                     do {
> +                             coeff = v - out_pos;
> +                             out_pos += out_pos_inc;
> +                             carry += out_pos_inc;
> +                             for (nxt = 0; v < out_pos; nxt++) {
> +                                     v += in_pos_inc;
> +                                     carry -= in_pos_inc;
> +                             }
> +
> +                             if (len > RESIZE_NUM_MAX)
> +                                     return -EINVAL;
> +
> +                             coeff = ((coeff << BC_COEF) +
> +                                     (in_pos_inc >> 1)) / in_pos_inc;
> +
> +                             if (coeff >= (SZ_COEF - 1))
> +                                     coeff--;
> +
> +                             coeff |= SZ_COEF;
> +                             s[len] = (unsigned char)coeff;
> +                             len++;
> +
> +                             for (i = 1; i < nxt; i++) {
> +                                     if (len >= RESIZE_NUM_MAX)
> +                                             return -EINVAL;
> +                                     s[len] = 0;
> +                                     len++;
> +                             }
> +                     } while (carry != init_carry);
> +             }
> +             pcdev->resizing[dir].len = len;
> +             if (dir == RESIZE_DIR_H)
> +                     mf_in->width = mf_in->width * den / num;
> +             else
> +                     mf_in->height = mf_in->height * den / num;

Aren't your calculations exact, so that here you can just use 
pix_out->width and pix_out->height?

> +     }
> +     return 0;
> +}
> +
>  static int mx2_camera_set_fmt(struct soc_camera_device *icd,
>                              struct v4l2_format *f)
>  {
> @@ -1070,6 +1278,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device 
> *icd,
>       struct v4l2_mbus_framefmt mf;
>       int ret;
>  
> +     dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
> +             __func__, pix->width, pix->height);
> +
>       xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
>       if (!xlate) {
>               dev_warn(icd->parent, "Format %x not found\n",
> @@ -1087,6 +1298,18 @@ static int mx2_camera_set_fmt(struct soc_camera_device 
> *icd,
>       if (ret < 0 && ret != -ENOIOCTLCMD)
>               return ret;
>  
> +     /* Store width and height returned by the sensor for resizing */
> +     pcdev->s_width = mf.width;
> +     pcdev->s_height = mf.height;
> +     dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
> +             __func__, pcdev->s_width, pcdev->s_height);
> +
> +     memset(pcdev->resizing, 0, sizeof(struct emma_prp_resize) << 1);

I think, just sizeof(pcdev->resizing) will do the trick.

> +     if (mf.width != pix->width || mf.height != pix->height) {
> +             if (mx2_emmaprp_resize(pcdev, &mf, pix) < 0)
> +                     return -EINVAL;

Hmmm... This looks like a regression, not an improvement - you return more 
errors now, than without this resizing support. Wouldn't it be possible to 
fall back to the current behaviour if prp resizing is impossible?

> +     }
> +
>       if (mf.code != xlate->code)
>               return -EINVAL;
>  
> @@ -1100,6 +1323,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device 
> *icd,
>               pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
>                                               xlate->host_fmt->fourcc);
>  
> +     dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
> +             __func__, pix->width, pix->height);
> +
>       return 0;
>  }
>  
> @@ -1109,11 +1335,16 @@ static int mx2_camera_try_fmt(struct 
> soc_camera_device *icd,
>       struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
>       const struct soc_camera_format_xlate *xlate;
>       struct v4l2_pix_format *pix = &f->fmt.pix;
> -     struct v4l2_mbus_framefmt mf;
>       __u32 pixfmt = pix->pixelformat;
> +     struct v4l2_mbus_framefmt mf;

This swap doesn't seem to be needed.

> +     struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
> +     struct mx2_camera_dev *pcdev = ici->priv;
>       unsigned int width_limit;
>       int ret;
>  
> +     dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
> +             __func__, pix->width, pix->height);
> +
>       xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
>       if (pixfmt && !xlate) {
>               dev_warn(icd->parent, "Format %x not found\n", pixfmt);
> @@ -1163,6 +1394,19 @@ static int mx2_camera_try_fmt(struct soc_camera_device 
> *icd,
>       if (ret < 0)
>               return ret;
>  
> +     /* Store width and height returned by the sensor for resizing */
> +     pcdev->s_width = mf.width;
> +     pcdev->s_height = mf.height;

You don't need these in .try_fmt().

> +     dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
> +             __func__, pcdev->s_width, pcdev->s_height);
> +
> +     /* If the sensor does not support image size try PrP resizing */
> +     memset(pcdev->resizing, 0, sizeof(struct emma_prp_resize) << 1);

Same for sizeof()

> +     if (mf.width != pix->width || mf.height != pix->height) {
> +             if (mx2_emmaprp_resize(pcdev, &mf, pix) < 0)
> +                     return -EINVAL;

.try_fmt() really shouldn't fail.

> +     }
> +
>       if (mf.field == V4L2_FIELD_ANY)
>               mf.field = V4L2_FIELD_NONE;
>       /*
> @@ -1181,6 +1425,9 @@ static int mx2_camera_try_fmt(struct soc_camera_device 
> *icd,
>       pix->field      = mf.field;
>       pix->colorspace = mf.colorspace;
>  
> +     dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
> +             __func__, pix->width, pix->height);
> +
>       return 0;
>  }
>  
> -- 
> 1.7.0.4

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to