staging/bcm2835-codec: Add support for decoding interlaced streams
authorDave Stevenson <dave.stevenson@raspberrypi.com>
Fri, 18 Dec 2020 19:56:31 +0000 (19:56 +0000)
committerPhil Elwell <8911409+pelwell@users.noreply.github.com>
Tue, 14 Sep 2021 16:03:59 +0000 (17:03 +0100)
The video decoder can support decoding interlaced streams, so add
the required plumbing to signal this correctly.

The encoder and ISP do NOT support interlaced data, so trying to
configure an interlaced format on those nodes will be rejected.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c

index 4c01bc4..3f15c20 100644 (file)
@@ -619,6 +619,7 @@ struct bcm2835_codec_q_data {
        unsigned int            crop_height;
        bool                    selection_set;
        struct v4l2_fract       aspect_ratio;
+       enum v4l2_field         field;
 
        unsigned int            sizeimage;
        unsigned int            sequence;
@@ -986,6 +987,10 @@ static void handle_fmt_changed(struct bcm2835_codec_ctx *ctx,
        struct bcm2835_codec_q_data *q_data;
        struct mmal_msg_event_format_changed *format =
                (struct mmal_msg_event_format_changed *)mmal_buf->buffer;
+       struct mmal_parameter_video_interlace_type interlace;
+       int interlace_size = sizeof(interlace);
+       int ret;
+
        v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: Format changed: buff size min %u, rec %u, buff num min %u, rec %u\n",
                 __func__,
                 format->buffer_size_min,
@@ -1029,6 +1034,30 @@ static void handle_fmt_changed(struct bcm2835_codec_ctx *ctx,
        q_data->aspect_ratio.numerator = format->es.video.par.num;
        q_data->aspect_ratio.denominator = format->es.video.par.den;
 
+       ret = vchiq_mmal_port_parameter_get(ctx->dev->instance,
+                                           &ctx->component->output[0],
+                                           MMAL_PARAMETER_VIDEO_INTERLACE_TYPE,
+                                           &interlace,
+                                           &interlace_size);
+       if (!ret) {
+               switch (interlace.mode) {
+               case MMAL_INTERLACE_PROGRESSIVE:
+               default:
+                       q_data->field = V4L2_FIELD_NONE;
+                       break;
+               case MMAL_INTERLACE_FIELDS_INTERLEAVED_UPPER_FIRST:
+                       q_data->field = V4L2_FIELD_INTERLACED_TB;
+                       break;
+               case MMAL_INTERLACE_FIELDS_INTERLEAVED_LOWER_FIRST:
+                       q_data->field = V4L2_FIELD_INTERLACED_BT;
+                       break;
+               }
+               v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s: interlace mode %u, v4l2 field %u\n",
+                        __func__, interlace.mode, q_data->field);
+       } else {
+               q_data->field = V4L2_FIELD_NONE;
+       }
+
        queue_res_chg_event(ctx);
 }
 
@@ -1101,6 +1130,22 @@ static void op_buffer_cb(struct vchiq_mmal_instance *instance,
        vb2->vb2_buf.timestamp = mmal_buf->pts * 1000;
 
        vb2_set_plane_payload(&vb2->vb2_buf, 0, mmal_buf->length);
+       switch (mmal_buf->mmal_flags &
+                               (MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED |
+                                MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST)) {
+       case 0:
+       case MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST: /* Bogus */
+               vb2->field = V4L2_FIELD_NONE;
+               break;
+       case MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED:
+               vb2->field = V4L2_FIELD_INTERLACED_BT;
+               break;
+       case (MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED |
+             MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST):
+               vb2->field = V4L2_FIELD_INTERLACED_TB;
+               break;
+       }
+
        if (mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME)
                vb2->flags |= V4L2_BUF_FLAG_KEYFRAME;
 
@@ -1272,7 +1317,7 @@ static int vidioc_g_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f)
        f->fmt.pix_mp.width                     = q_data->crop_width;
        f->fmt.pix_mp.height                    = q_data->height;
        f->fmt.pix_mp.pixelformat               = q_data->fmt->fourcc;
-       f->fmt.pix_mp.field                     = V4L2_FIELD_NONE;
+       f->fmt.pix_mp.field                     = q_data->field;
        f->fmt.pix_mp.colorspace                = ctx->colorspace;
        f->fmt.pix_mp.plane_fmt[0].sizeimage    = q_data->sizeimage;
        f->fmt.pix_mp.plane_fmt[0].bytesperline = q_data->bytesperline;
@@ -1347,7 +1392,33 @@ static int vidioc_try_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f,
        memset(f->fmt.pix_mp.plane_fmt[0].reserved, 0,
               sizeof(f->fmt.pix_mp.plane_fmt[0].reserved));
 
-       f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+       if (ctx->dev->role == DECODE) {
+               switch (f->fmt.pix_mp.field) {
+               /*
+                * All of this is pretty much guesswork as we'll set the
+                * interlace format correctly come format changed, and signal
+                * it appropriately on each buffer.
+                */
+               default:
+               case V4L2_FIELD_NONE:
+               case V4L2_FIELD_ANY:
+                       f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+                       break;
+               case V4L2_FIELD_INTERLACED:
+                       f->fmt.pix_mp.field = V4L2_FIELD_INTERLACED;
+                       break;
+               case V4L2_FIELD_TOP:
+               case V4L2_FIELD_BOTTOM:
+               case V4L2_FIELD_INTERLACED_TB:
+                       f->fmt.pix_mp.field = V4L2_FIELD_INTERLACED_TB;
+                       break;
+               case V4L2_FIELD_INTERLACED_BT:
+                       f->fmt.pix_mp.field = V4L2_FIELD_INTERLACED_BT;
+                       break;
+               }
+       } else {
+               f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+       }
 
        return 0;
 }
@@ -1430,6 +1501,8 @@ static int vidioc_s_fmt(struct bcm2835_codec_ctx *ctx, struct v4l2_format *f,
        ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
        ctx->quant = f->fmt.pix_mp.quantization;
 
+       q_data->field = f->fmt.pix_mp.field;
+
        /* All parameters should have been set correctly by try_fmt */
        q_data->bytesperline = f->fmt.pix_mp.plane_fmt[0].bytesperline;
        q_data->sizeimage = f->fmt.pix_mp.plane_fmt[0].sizeimage;
@@ -2429,11 +2502,6 @@ static int bcm2835_codec_buf_prepare(struct vb2_buffer *vb)
        if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
                if (vbuf->field == V4L2_FIELD_ANY)
                        vbuf->field = V4L2_FIELD_NONE;
-               if (vbuf->field != V4L2_FIELD_NONE) {
-                       v4l2_err(&ctx->dev->v4l2_dev, "%s field isn't supported\n",
-                                __func__);
-                       return -EINVAL;
-               }
        }
 
        if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
@@ -2735,6 +2803,7 @@ static int bcm2835_codec_open(struct file *file)
                              ctx->q_data[V4L2_M2M_SRC].crop_width,
                              ctx->q_data[V4L2_M2M_SRC].height,
                              ctx->q_data[V4L2_M2M_SRC].fmt);
+       ctx->q_data[V4L2_M2M_SRC].field = V4L2_FIELD_NONE;
 
        ctx->q_data[V4L2_M2M_DST].crop_width = DEFAULT_WIDTH;
        ctx->q_data[V4L2_M2M_DST].crop_height = DEFAULT_HEIGHT;
@@ -2749,6 +2818,7 @@ static int bcm2835_codec_open(struct file *file)
                              ctx->q_data[V4L2_M2M_DST].fmt);
        ctx->q_data[V4L2_M2M_DST].aspect_ratio.numerator = 1;
        ctx->q_data[V4L2_M2M_DST].aspect_ratio.denominator = 1;
+       ctx->q_data[V4L2_M2M_DST].field = V4L2_FIELD_NONE;
 
        ctx->colorspace = V4L2_COLORSPACE_REC709;
        ctx->bitrate = 10 * 1000 * 1000;