staging/bcm2835_codec: Add support for image_fx to deinterlace
authorDom Cobley <popcornmix@gmail.com>
Fri, 6 Aug 2021 14:37:16 +0000 (15:37 +0100)
committerDom Cobley <popcornmix@gmail.com>
Mon, 21 Mar 2022 16:04:23 +0000 (16:04 +0000)
Adds another /dev/video node wrapping image_fx doing deinterlace.

Co-developed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Dom Cobley <popcornmix@gmail.com>
drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c

index 8e6a3ab..38b59aa 100644 (file)
@@ -58,6 +58,10 @@ static int isp_video_nr = 12;
 module_param(isp_video_nr, int, 0644);
 MODULE_PARM_DESC(isp_video_nr, "isp video device number");
 
+static int deinterlace_video_nr = 18;
+module_param(deinterlace_video_nr, int, 0644);
+MODULE_PARM_DESC(deinterlace_video_nr, "deinterlace video device number");
+
 /*
  * Workaround for GStreamer v4l2convert component not considering Bayer formats
  * as raw, and therefore not considering a V4L2 device that supports them as
@@ -71,22 +75,33 @@ static unsigned int debug;
 module_param(debug, uint, 0644);
 MODULE_PARM_DESC(debug, "activates debug info (0-3)");
 
+static bool advanced_deinterlace = true;
+module_param(advanced_deinterlace, bool, 0644);
+MODULE_PARM_DESC(advanced_deinterlace, "Use advanced deinterlace");
+
+static int field_override;
+module_param(field_override, int, 0644);
+MODULE_PARM_DESC(field_override, "force TB(8)/BT(9) field");
+
 enum bcm2835_codec_role {
        DECODE,
        ENCODE,
        ISP,
+       DEINTERLACE,
 };
 
 static const char * const roles[] = {
        "decode",
        "encode",
-       "isp"
+       "isp",
+       "image_fx",
 };
 
 static const char * const components[] = {
        "ril.video_decode",
        "ril.video_encode",
        "ril.isp",
+       "ril.image_fx",
 };
 
 /* Timeout for stop_streaming to allow all buffers to return */
@@ -683,6 +698,7 @@ struct bcm2835_codec_driver {
        struct bcm2835_codec_dev *encode;
        struct bcm2835_codec_dev *decode;
        struct bcm2835_codec_dev *isp;
+       struct bcm2835_codec_dev *deinterlace;
 };
 
 enum {
@@ -1196,6 +1212,19 @@ static void vb2_to_mmal_buffer(struct m2m_mmal_buffer *buf,
        do_div(pts, 1000);
        buf->mmal.pts = pts;
        buf->mmal.dts = MMAL_TIME_UNKNOWN;
+
+       switch (field_override ? field_override : vb2->field) {
+       default:
+       case V4L2_FIELD_NONE:
+               break;
+       case V4L2_FIELD_INTERLACED_BT:
+               buf->mmal.mmal_flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
+               break;
+       case V4L2_FIELD_INTERLACED_TB:
+               buf->mmal.mmal_flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED |
+                                       MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
+               break;
+       }
 }
 
 /* device_run() - prepares and starts the device
@@ -1396,7 +1425,7 @@ 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));
 
-       if (ctx->dev->role == DECODE) {
+       if (ctx->dev->role == DECODE || ctx->dev->role == DEINTERLACE) {
                switch (f->fmt.pix_mp.field) {
                /*
                 * All of this is pretty much guesswork as we'll set the
@@ -1686,6 +1715,46 @@ static int vidioc_g_selection(struct file *file, void *priv,
                break;
        case ISP:
                break;
+       case DEINTERLACE:
+               if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       switch (s->target) {
+                       case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+                       case V4L2_SEL_TGT_COMPOSE:
+                               s->r.left = 0;
+                               s->r.top = 0;
+                               s->r.width = q_data->crop_width;
+                               s->r.height = q_data->crop_height;
+                               break;
+                       case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+                               s->r.left = 0;
+                               s->r.top = 0;
+                               s->r.width = q_data->crop_width;
+                               s->r.height = q_data->crop_height;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+               } else {
+                       /* must be V4L2_BUF_TYPE_VIDEO_OUTPUT */
+                       switch (s->target) {
+                       case V4L2_SEL_TGT_CROP_DEFAULT:
+                       case V4L2_SEL_TGT_CROP_BOUNDS:
+                               s->r.top = 0;
+                               s->r.left = 0;
+                               s->r.width = q_data->bytesperline;
+                               s->r.height = q_data->height;
+                               break;
+                       case V4L2_SEL_TGT_CROP:
+                               s->r.top = 0;
+                               s->r.left = 0;
+                               s->r.width = q_data->crop_width;
+                               s->r.height = q_data->crop_height;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+               }
+               break;
        }
 
        return 0;
@@ -1761,6 +1830,41 @@ static int vidioc_s_selection(struct file *file, void *priv,
                break;
        case ISP:
                break;
+       case DEINTERLACE:
+               if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+                       switch (s->target) {
+                       case V4L2_SEL_TGT_COMPOSE:
+                               /* Accept cropped image */
+                               s->r.left = 0;
+                               s->r.top = 0;
+                               s->r.width = min(s->r.width, q_data->crop_width);
+                               s->r.height = min(s->r.height, q_data->height);
+                               q_data->crop_width = s->r.width;
+                               q_data->crop_height = s->r.height;
+                               q_data->selection_set = true;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               } else {
+                       /* must be V4L2_BUF_TYPE_VIDEO_OUTPUT */
+                       switch (s->target) {
+                       case V4L2_SEL_TGT_CROP:
+                               /* Only support crop from (0,0) */
+                               s->r.top = 0;
+                               s->r.left = 0;
+                               s->r.width = min(s->r.width, q_data->crop_width);
+                               s->r.height = min(s->r.height, q_data->height);
+                               q_data->crop_width = s->r.width;
+                               q_data->crop_height = s->r.height;
+                               q_data->selection_set = true;
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       break;
+               }
        }
 
        return 0;
@@ -2335,6 +2439,30 @@ static int bcm2835_codec_create_component(struct bcm2835_codec_ctx *ctx)
                                              MMAL_PARAMETER_VIDEO_VALIDATE_TIMESTAMPS,
                                              &enable,
                                              sizeof(enable));
+       } else if (dev->role == DEINTERLACE) {
+               /* Select the default deinterlace algorithm. */
+               int half_framerate = 0;
+               int default_frame_interval = -1; /* don't interpolate */
+               int frame_type = 5; /* 0=progressive, 3=TFF, 4=BFF, 5=see frame */
+               int use_qpus = 0;
+               enum mmal_parameter_imagefx effect =
+                       advanced_deinterlace && ctx->q_data[V4L2_M2M_SRC].crop_width <= 800 ?
+                       MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV :
+                       MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST;
+               struct mmal_parameter_imagefx_parameters params = {
+                       .effect = effect,
+                       .num_effect_params = 4,
+                       .effect_parameter = { frame_type,
+                                             default_frame_interval,
+                                             half_framerate,
+                                             use_qpus },
+               };
+
+               vchiq_mmal_port_parameter_set(dev->instance,
+                                             &ctx->component->output[0],
+                                             MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,
+                                             &params,
+                                             sizeof(params));
        }
 
        setup_mmal_port_format(ctx, &ctx->q_data[V4L2_M2M_SRC],
@@ -3173,6 +3301,16 @@ static int bcm2835_codec_create(struct bcm2835_codec_driver *drv,
                function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
                video_nr = isp_video_nr;
                break;
+       case DEINTERLACE:
+               v4l2_disable_ioctl(vfd, VIDIOC_ENCODER_CMD);
+               v4l2_disable_ioctl(vfd, VIDIOC_TRY_ENCODER_CMD);
+               v4l2_disable_ioctl(vfd, VIDIOC_DECODER_CMD);
+               v4l2_disable_ioctl(vfd, VIDIOC_TRY_DECODER_CMD);
+               v4l2_disable_ioctl(vfd, VIDIOC_S_PARM);
+               v4l2_disable_ioctl(vfd, VIDIOC_G_PARM);
+               function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+               video_nr = deinterlace_video_nr;
+               break;
        default:
                ret = -EINVAL;
                goto unreg_dev;
@@ -3268,6 +3406,10 @@ static int bcm2835_codec_probe(struct platform_device *pdev)
        if (ret)
                goto out;
 
+       ret = bcm2835_codec_create(drv, &drv->deinterlace, DEINTERLACE);
+       if (ret)
+               goto out;
+
        /* Register the media device node */
        if (media_device_register(mdev) < 0)
                goto out;
@@ -3277,6 +3419,10 @@ static int bcm2835_codec_probe(struct platform_device *pdev)
        return 0;
 
 out:
+       if (drv->deinterlace) {
+               bcm2835_codec_destroy(drv->deinterlace);
+               drv->deinterlace = NULL;
+       }
        if (drv->isp) {
                bcm2835_codec_destroy(drv->isp);
                drv->isp = NULL;
@@ -3298,6 +3444,8 @@ static int bcm2835_codec_remove(struct platform_device *pdev)
 
        media_device_unregister(&drv->mdev);
 
+       bcm2835_codec_destroy(drv->deinterlace);
+
        bcm2835_codec_destroy(drv->isp);
 
        bcm2835_codec_destroy(drv->encode);