I've gone through this one as well, and certainly seems like some much
better approaches in there.

I can't find anything to fault it.

Acked-by: Kieran Bingham <kie...@bingham.xyz>
Reviewed-by: Kieran Bingham <kie...@bingham.xyz>

Thanks again,

Kieran

On 07/09/16 23:25, Laurent Pinchart wrote:
> The handling of the TRY_FMT and S_FMT ioctls isn't correct. In
> particular, the sink format isn't propagated to the source format
> automatically, the strides are not computed when the device is opened,
> and the colorspace handling is wrong.
> 
> Rewrite the implementation.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+rene...@ideasonboard.com>
> ---
>  drivers/media/platform/rcar_fdp1.c | 324 
> +++++++++++++++++++++++--------------
>  1 file changed, 205 insertions(+), 119 deletions(-)
> 
> diff --git a/drivers/media/platform/rcar_fdp1.c 
> b/drivers/media/platform/rcar_fdp1.c
> index fdab41165f5a..480f89381f15 100644
> --- a/drivers/media/platform/rcar_fdp1.c
> +++ b/drivers/media/platform/rcar_fdp1.c
> @@ -40,7 +40,7 @@ static unsigned int debug;
>  module_param(debug, uint, 0644);
>  MODULE_PARM_DESC(debug, "activate debug info");
>  
> -/* Min Width/Height/Height-Field */
> +/* Minimum and maximum frame width/height */
>  #define FDP1_MIN_W           80U
>  #define FDP1_MIN_H           80U
>  
> @@ -48,6 +48,7 @@ MODULE_PARM_DESC(debug, "activate debug info");
>  #define FDP1_MAX_H           2160U
>  
>  #define FDP1_MAX_PLANES              3U
> +#define FDP1_MAX_STRIDE              8190U
>  
>  /* Flags that indicate a format can be used for capture/output */
>  #define FDP1_CAPTURE         BIT(0)
> @@ -1506,82 +1507,12 @@ static int fdp1_g_fmt(struct file *file, void *priv, 
> struct v4l2_format *f)
>       return 0;
>  }
>  
> -static int __fdp1_try_fmt(struct fdp1_ctx *ctx, const struct fdp1_fmt 
> **fmtinfo,
> -                       struct v4l2_pix_format_mplane *pix,
> -                       enum v4l2_buf_type type)
> +static void fdp1_compute_stride(struct v4l2_pix_format_mplane *pix,
> +                             const struct fdp1_fmt *fmt)
>  {
> -     const struct fdp1_fmt *fmt;
> -     unsigned int width = pix->width;
> -     unsigned int height = pix->height;
> -     unsigned int fmt_type;
>       unsigned int i;
>  
> -     fmt_type = V4L2_TYPE_IS_OUTPUT(type) ? FDP1_OUTPUT : FDP1_CAPTURE;
> -
> -     fmt = fdp1_find_format(pix->pixelformat);
> -     if (!fmt || !(fmt->types & fmt_type))
> -             fmt = fdp1_find_format(V4L2_PIX_FMT_YUYV);
> -
> -     pix->pixelformat = fmt->fourcc;
> -
> -     /* Manage colorspace on the two queues */
> -     if (V4L2_TYPE_IS_OUTPUT(type)) {
> -             if (pix->colorspace == V4L2_COLORSPACE_DEFAULT)
> -                     pix->colorspace = V4L2_COLORSPACE_REC709;
> -
> -             if (pix->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
> -                     pix->ycbcr_enc =
> -                             V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
> -
> -             if (pix->quantization == V4L2_QUANTIZATION_DEFAULT)
> -                     pix->quantization =
> -                             V4L2_MAP_QUANTIZATION_DEFAULT(false,
> -                                             pix->colorspace,
> -                                             pix->ycbcr_enc);
> -     } else {
> -             /* Manage the CAPTURE Queue */
> -             struct fdp1_q_data *src_data = &ctx->out_q;
> -
> -             if (fdp1_fmt_is_rgb(fmt)) {
> -                     pix->colorspace = V4L2_COLORSPACE_SRGB;
> -                     pix->ycbcr_enc = V4L2_YCBCR_ENC_SYCC;
> -                     pix->quantization = V4L2_QUANTIZATION_FULL_RANGE;
> -             } else {
> -                     /* Copy input queue colorspace across */
> -                     pix->colorspace = src_data->format.colorspace;
> -                     pix->ycbcr_enc = src_data->format.ycbcr_enc;
> -                     pix->quantization = src_data->format.quantization;
> -             }
> -     }
> -
> -     /* We should be allowing FIELDS through on the Output queue !*/
> -     if (V4L2_TYPE_IS_OUTPUT(type)) {
> -             /* Clamp to allowable field types */
> -             if (pix->field == V4L2_FIELD_ANY ||
> -                 pix->field == V4L2_FIELD_NONE)
> -                     pix->field = V4L2_FIELD_NONE;
> -             else if (!V4L2_FIELD_HAS_BOTH(pix->field))
> -                     pix->field = V4L2_FIELD_INTERLACED;
> -
> -             dprintk(ctx->fdp1, "Output Field Type set as %d\n", pix->field);
> -     } else {
> -             pix->field = V4L2_FIELD_NONE;
> -     }
> -
> -     pix->num_planes = fmt->num_planes;
> -
> -     /* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */
> -     width = round_down(width, fmt->hsub);
> -     height = round_down(height, fmt->vsub);
> -
> -     /* Clamp the width and height */
> -     pix->width = clamp(width, FDP1_MIN_W, FDP1_MAX_W);
> -     pix->height = clamp(height, FDP1_MIN_H, FDP1_MAX_H);
> -
> -     /* Compute and clamp the stride and image size. While not documented in
> -      * the datasheet, strides not aligned to a multiple of 128 bytes result
> -      * in image corruption.
> -      */
> +     /* Compute and clamp the stride and image size. */
>       for (i = 0; i < min_t(unsigned int, fmt->num_planes, 2U); ++i) {
>               unsigned int hsub = i > 0 ? fmt->hsub : 1;
>               unsigned int vsub = i > 0 ? fmt->vsub : 1;
> @@ -1591,91 +1522,247 @@ static int __fdp1_try_fmt(struct fdp1_ctx *ctx, 
> const struct fdp1_fmt **fmtinfo,
>  
>               bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline,
>                             pix->width / hsub * fmt->bpp[i] / 8,
> -                           round_down(65535U, align));
> +                           round_down(FDP1_MAX_STRIDE, align));
>  
>               pix->plane_fmt[i].bytesperline = round_up(bpl, align);
>               pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline
>                                           * pix->height / vsub;
>  
>               memset(pix->plane_fmt[i].reserved, 0,
> -                             sizeof(pix->plane_fmt[i].reserved));
> +                    sizeof(pix->plane_fmt[i].reserved));
>       }
>  
>       if (fmt->num_planes == 3) {
> -             /* The second and third planes must have the same stride. */
> +             /* The two chroma planes must have the same stride. */
>               pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline;
>               pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage;
>  
>               memset(pix->plane_fmt[2].reserved, 0,
> -                             sizeof(pix->plane_fmt[2].reserved));
> +                    sizeof(pix->plane_fmt[2].reserved));
>       }
> +}
> +
> +static void fdp1_try_fmt_output(struct fdp1_ctx *ctx,
> +                             const struct fdp1_fmt **fmtinfo,
> +                             struct v4l2_pix_format_mplane *pix)
> +{
> +     const struct fdp1_fmt *fmt;
> +     unsigned int width;
> +     unsigned int height;
> +
> +     /* Validate the pixel format to ensure the output queue supports it. */
> +     fmt = fdp1_find_format(pix->pixelformat);
> +     if (!fmt || !(fmt->types & FDP1_OUTPUT))
> +             fmt = fdp1_find_format(V4L2_PIX_FMT_YUYV);
> +
> +     if (fmtinfo)
> +             *fmtinfo = fmt;
>  
> +     pix->pixelformat = fmt->fourcc;
>       pix->num_planes = fmt->num_planes;
>  
> +     /*
> +      * Progressive video and all interlaced field orders are acceptable.
> +      * Default to V4L2_FIELD_INTERLACED.
> +      */
> +     if (pix->field != V4L2_FIELD_NONE &&
> +         pix->field != V4L2_FIELD_ALTERNATE &&
> +         !V4L2_FIELD_HAS_BOTH(pix->field))
> +             pix->field = V4L2_FIELD_INTERLACED;
> +
> +     /*
> +      * The deinterlacer doesn't care about the colorspace, accept all values
> +      * and default to V4L2_COLORSPACE_SMPTE170M. The YUV to RGB conversion
> +      * at the output of the deinterlacer supports a subset of encodings and
> +      * quantization methods and will only be available when the colorspace
> +      * allows it.
> +      */
> +     if (pix->colorspace == V4L2_COLORSPACE_DEFAULT)
> +             pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
> +
> +     /*
> +      * Align the width and height for YUV 4:2:2 and 4:2:0 formats and clamp
> +      * them to the supported frame size range. The height boundary are
> +      * related to the full frame, divide them by two when the format passes
> +      * fields in separate buffers.
> +      */
> +     width = round_down(pix->width, fmt->hsub);
> +     pix->width = clamp(width, FDP1_MIN_W, FDP1_MAX_W);
> +
> +     height = round_down(pix->height, fmt->vsub);
> +     if (pix->field == V4L2_FIELD_ALTERNATE)
> +             pix->height = clamp(height, FDP1_MIN_H / 2, FDP1_MAX_H / 2);
> +     else
> +             pix->height = clamp(height, FDP1_MIN_H, FDP1_MAX_H);
> +
> +     fdp1_compute_stride(pix, fmt);
> +}
> +
> +static void fdp1_try_fmt_capture(struct fdp1_ctx *ctx,
> +                              const struct fdp1_fmt **fmtinfo,
> +                              struct v4l2_pix_format_mplane *pix)
> +{
> +     struct fdp1_q_data *src_data = &ctx->out_q;
> +     enum v4l2_colorspace colorspace;
> +     enum v4l2_ycbcr_encoding ycbcr_enc;
> +     enum v4l2_quantization quantization;
> +     const struct fdp1_fmt *fmt;
> +     bool allow_rgb;
> +
> +     /*
> +      * Validate the pixel format. We can only accept RGB output formats if
> +      * the input encoding and quantization are compatible with the format
> +      * conversions supported by the hardware. The supported combinations are
> +      *
> +      * V4L2_YCBCR_ENC_601 + V4L2_QUANTIZATION_LIM_RANGE
> +      * V4L2_YCBCR_ENC_601 + V4L2_QUANTIZATION_FULL_RANGE
> +      * V4L2_YCBCR_ENC_709 + V4L2_QUANTIZATION_LIM_RANGE
> +      */
> +     colorspace = src_data->format.colorspace;
> +
> +     ycbcr_enc = src_data->format.ycbcr_enc;
> +     if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT)
> +             ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colorspace);
> +
> +     quantization = src_data->format.quantization;
> +     if (quantization == V4L2_QUANTIZATION_DEFAULT)
> +             quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false, colorspace,
> +                                                          ycbcr_enc);
> +
> +     allow_rgb = ycbcr_enc == V4L2_YCBCR_ENC_601 ||
> +                 (ycbcr_enc == V4L2_YCBCR_ENC_709 &&
> +                  quantization == V4L2_QUANTIZATION_LIM_RANGE);
> +
> +     fmt = fdp1_find_format(pix->pixelformat);
> +     if (!fmt || (!allow_rgb && fdp1_fmt_is_rgb(fmt)))
> +             fmt = fdp1_find_format(V4L2_PIX_FMT_YUYV);
> +
>       if (fmtinfo)
>               *fmtinfo = fmt;
>  
> -     return 0;
> +     pix->pixelformat = fmt->fourcc;
> +     pix->num_planes = fmt->num_planes;
> +     pix->field = V4L2_FIELD_NONE;
> +
> +     /*
> +      * The colorspace on the capture queue is copied from the output queue
> +      * as the hardware can't change the colorspace. It can convert YCbCr to
> +      * RGB though, in which case the encoding and quantization are set to
> +      * default values as anything else wouldn't make sense.
> +      */
> +     pix->colorspace = src_data->format.colorspace;
> +     pix->xfer_func = src_data->format.xfer_func;
> +
> +     if (fdp1_fmt_is_rgb(fmt)) {
> +             pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
> +             pix->quantization = V4L2_QUANTIZATION_DEFAULT;
> +     } else {
> +             pix->ycbcr_enc = src_data->format.ycbcr_enc;
> +             pix->quantization = src_data->format.quantization;
> +     }
> +
> +     /*
> +      * The frame width is identical to the output queue, and the height is
> +      * either doubled or identical depending on whether the output queue
> +      * field order contains one or two fields per frame.
> +      */
> +     pix->width = src_data->format.width;
> +     if (src_data->format.field == V4L2_FIELD_ALTERNATE)
> +             pix->height = 2 * src_data->format.height;
> +     else
> +             pix->height = src_data->format.height;
> +
> +     fdp1_compute_stride(pix, fmt);
>  }
>  
>  static int fdp1_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
>  {
>       struct fdp1_ctx *ctx = fh_to_ctx(priv);
> -     int ret;
>  
> -     ret = __fdp1_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type);
> +     if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +             fdp1_try_fmt_output(ctx, NULL, &f->fmt.pix_mp);
> +     else
> +             fdp1_try_fmt_capture(ctx, NULL, &f->fmt.pix_mp);
>  
> -     if (ret < 0)
> -             dprintk(ctx->fdp1, "try_fmt failed %d\n", ret);
> +     dprintk(ctx->fdp1, "Try %s format: %4s (0x%08x) %ux%u field %u\n",
> +             V4L2_TYPE_IS_OUTPUT(f->type) ? "output" : "capture",
> +             (char *)&f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.pixelformat,
> +             f->fmt.pix_mp.width, f->fmt.pix_mp.height, f->fmt.pix_mp.field);
>  
> -     return ret;
> +     return 0;
>  }
>  
> -static int fdp1_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +static void fdp1_set_format(struct fdp1_ctx *ctx,
> +                         struct v4l2_pix_format_mplane *pix,
> +                         enum v4l2_buf_type type)
>  {
> -     struct vb2_queue *vq;
> -     struct fdp1_ctx *ctx = fh_to_ctx(priv);
> -     struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> -     struct fdp1_q_data *q_data;
> +     struct fdp1_q_data *q_data = get_q_data(ctx, type);
>       const struct fdp1_fmt *fmtinfo;
> -     int ret;
> -
> -     vq = v4l2_m2m_get_vq(m2m_ctx, f->type);
> -
> -     if (vb2_is_busy(vq)) {
> -             v4l2_err(&ctx->fdp1->v4l2_dev, "%s queue busy\n", __func__);
> -             return -EBUSY;
> -     }
>  
> -     ret = __fdp1_try_fmt(ctx, &fmtinfo, &f->fmt.pix_mp, f->type);
> -     if (ret < 0) {
> -             v4l2_err(&ctx->fdp1->v4l2_dev, "set_fmt failed %d\n", ret);
> -             return ret;
> -     }
> +     if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
> +             fdp1_try_fmt_output(ctx, &fmtinfo, pix);
> +     else
> +             fdp1_try_fmt_capture(ctx, &fmtinfo, pix);
>  
> -     q_data = get_q_data(ctx, f->type);
> -     q_data->format = f->fmt.pix_mp;
>       q_data->fmt = fmtinfo;
> +     q_data->format = *pix;
>  
> -     q_data->vsize = f->fmt.pix_mp.height;
> -     if (q_data->format.field != V4L2_FIELD_NONE)
> +     q_data->vsize = pix->height;
> +     if (pix->field != V4L2_FIELD_NONE)
>               q_data->vsize /= 2;
>  
> -     q_data->stride_y = q_data->format.plane_fmt[0].bytesperline;
> -     q_data->stride_c = q_data->format.plane_fmt[1].bytesperline;
> +     q_data->stride_y = pix->plane_fmt[0].bytesperline;
> +     q_data->stride_c = pix->plane_fmt[1].bytesperline;
>  
>       /* Adjust strides for interleaved buffers */
> -     if (q_data->format.field == V4L2_FIELD_INTERLACED ||
> -         q_data->format.field == V4L2_FIELD_INTERLACED_TB ||
> -         q_data->format.field == V4L2_FIELD_INTERLACED_BT) {
> +     if (pix->field == V4L2_FIELD_INTERLACED ||
> +         pix->field == V4L2_FIELD_INTERLACED_TB ||
> +         pix->field == V4L2_FIELD_INTERLACED_BT) {
>               q_data->stride_y *= 2;
>               q_data->stride_c *= 2;
>       }
>  
> -     dprintk(ctx->fdp1,
> -             "Setting format for type %d, wxh: %dx%d, fmt: %4s (%d)\n",
> -                     f->type, q_data->format.width, q_data->format.height,
> -                     (char *)&q_data->fmt->fourcc, q_data->fmt->fourcc);
> +     /* Propagate the format from the output node to the capture node. */
> +     if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
> +             struct fdp1_q_data *dst_data = &ctx->cap_q;
> +
> +             /*
> +              * Copy the format, clear the per-plane bytes per line and image
> +              * size, override the field and double the height if needed.
> +              */
> +             dst_data->format = q_data->format;
> +             memset(dst_data->format.plane_fmt, 0,
> +                    sizeof(dst_data->format.plane_fmt));
> +
> +             dst_data->format.field = V4L2_FIELD_NONE;
> +             if (pix->field == V4L2_FIELD_ALTERNATE)
> +                     dst_data->format.height *= 2;
> +
> +             fdp1_try_fmt_capture(ctx, &dst_data->fmt, &dst_data->format);
> +
> +             dst_data->vsize = dst_data->format.height;
> +             dst_data->stride_y = dst_data->format.plane_fmt[0].bytesperline;
> +             dst_data->stride_c = dst_data->format.plane_fmt[1].bytesperline;
> +     }
> +}
> +
> +static int fdp1_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
> +{
> +     struct fdp1_ctx *ctx = fh_to_ctx(priv);
> +     struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +     struct vb2_queue *vq = v4l2_m2m_get_vq(m2m_ctx, f->type);
> +
> +     if (vb2_is_busy(vq)) {
> +             v4l2_err(&ctx->fdp1->v4l2_dev, "%s queue busy\n", __func__);
> +             return -EBUSY;
> +     }
> +
> +     fdp1_set_format(ctx, &f->fmt.pix_mp, f->type);
> +
> +     dprintk(ctx->fdp1, "Set %s format: %4s (0x%08x) %ux%u field %u\n",
> +             V4L2_TYPE_IS_OUTPUT(f->type) ? "output" : "capture",
> +             (char *)&f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.pixelformat,
> +             f->fmt.pix_mp.width, f->fmt.pix_mp.height, f->fmt.pix_mp.field);
>  
>       return 0;
>  }
> @@ -1989,6 +2076,7 @@ static int queue_init(void *priv, struct vb2_queue 
> *src_vq,
>  static int fdp1_open(struct file *file)
>  {
>       struct fdp1_dev *fdp1 = video_drvdata(file);
> +     struct v4l2_pix_format_mplane format;
>       struct fdp1_ctx *ctx = NULL;
>       struct v4l2_ctrl *ctrl;
>       unsigned int i;
> @@ -2044,10 +2132,8 @@ static int fdp1_open(struct file *file)
>       v4l2_ctrl_handler_setup(&ctx->hdl);
>  
>       /* Configure default parameters. */
> -     __fdp1_try_fmt(ctx, &ctx->out_q.fmt, &ctx->out_q.format,
> -                   V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
> -     __fdp1_try_fmt(ctx, &ctx->cap_q.fmt, &ctx->cap_q.format,
> -                   V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
> +     memset(&format, 0, sizeof(format));
> +     fdp1_set_format(ctx, &format, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
>  
>       ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(fdp1->m2m_dev, ctx, &queue_init);
>  
> 

-- 
Regards

Kieran Bingham
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to