Re: [PATCH] media: i.MX27 camera: Add resizing support.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Guennadi,
thank you for your review.

On 20 February 2012 15:13, Guennadi Liakhovetski <g.liakhovetski@xxxxxx> wrote:
>> @@ -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.

Yes, sorry.

>
>> +
>> +                     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.

I don't know about that, we can't avoid the fact that the code would
look more compact but I think it would be less clear.

>> +                     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 ">"

You are right.

>> +                     /* 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?

Yes.

>> +
>> +                     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?

Yes, they are. I can save these operations. Thank you.

>> +     }
>> +     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.

No problem.

>> +     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?

You are right. I did this for testing purposes and forgot to update it.

>> +     }
>> +
>>       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.

Fine, I'll fix it.

>> +     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().

Right.

>> +     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.

It's the same issue as with s_fmt, I will fix it.

I'll try to send a v2 tomorrow.

Regards.
-- 
Javier Martin
Vista Silicon S.L.
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux