On 10/4/19 11:44 AM, Vandana BN wrote: > This patch adds meatadata capture support in vivid driver. > Adds new files for metadata capture. > Adds vivid controls to generate PTS and SCR for metadata stream. > > Signed-off-by: Vandana BN <bnvandana@xxxxxxxxx> > --- > V8 - squash commit:fixes for v4l2-compliance issues. > --- > drivers/media/platform/vivid/Makefile | 2 +- > drivers/media/platform/vivid/vivid-core.c | 107 +++++++++- > drivers/media/platform/vivid/vivid-core.h | 14 ++ > drivers/media/platform/vivid/vivid-ctrls.c | 65 ++++++ > .../media/platform/vivid/vivid-kthread-cap.c | 55 ++++- > drivers/media/platform/vivid/vivid-meta-cap.c | 201 ++++++++++++++++++ > drivers/media/platform/vivid/vivid-meta-cap.h | 29 +++ > drivers/media/platform/vivid/vivid-vid-cap.c | 5 +- > 8 files changed, 465 insertions(+), 13 deletions(-) > create mode 100644 drivers/media/platform/vivid/vivid-meta-cap.c > create mode 100644 drivers/media/platform/vivid/vivid-meta-cap.h > > diff --git a/drivers/media/platform/vivid/Makefile b/drivers/media/platform/vivid/Makefile > index 2f5762e3309a..af94abf9bce6 100644 > --- a/drivers/media/platform/vivid/Makefile > +++ b/drivers/media/platform/vivid/Makefile > @@ -3,7 +3,7 @@ vivid-objs := vivid-core.o vivid-ctrls.o vivid-vid-common.o vivid-vbi-gen.o \ > vivid-vid-cap.o vivid-vid-out.o vivid-kthread-cap.o vivid-kthread-out.o \ > vivid-radio-rx.o vivid-radio-tx.o vivid-radio-common.o \ > vivid-rds-gen.o vivid-sdr-cap.o vivid-vbi-cap.o vivid-vbi-out.o \ > - vivid-osd.o > + vivid-osd.o vivid-meta-cap.o > ifeq ($(CONFIG_VIDEO_VIVID_CEC),y) > vivid-objs += vivid-cec.o > endif > diff --git a/drivers/media/platform/vivid/vivid-core.c b/drivers/media/platform/vivid/vivid-core.c > index 53315c8dd2bb..97ab197bdec0 100644 > --- a/drivers/media/platform/vivid/vivid-core.c > +++ b/drivers/media/platform/vivid/vivid-core.c > @@ -37,6 +37,7 @@ > #include "vivid-osd.h" > #include "vivid-cec.h" > #include "vivid-ctrls.h" > +#include "vivid-meta-cap.h" > > #define VIVID_MODULE_NAME "vivid" > > @@ -79,6 +80,10 @@ static int radio_tx_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; > module_param_array(radio_tx_nr, int, NULL, 0444); > MODULE_PARM_DESC(radio_tx_nr, " radioX start number, -1 is autodetect"); > > +static int meta_cap_nr[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; > +module_param_array(meta_cap_nr, int, NULL, 0444); > +MODULE_PARM_DESC(meta_cap_nr, " videoX start number, -1 is autodetect"); > + > static int ccs_cap_mode[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = -1 }; > module_param_array(ccs_cap_mode, int, NULL, 0444); > MODULE_PARM_DESC(ccs_cap_mode, " capture crop/compose/scale mode:\n" > @@ -95,8 +100,13 @@ static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 > module_param_array(multiplanar, uint, NULL, 0444); > MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device."); > > -/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */ > -static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d }; > +/* > + * Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + > + * vbi-out + vid-out + meta-cap > + */ > +static unsigned int node_types[VIVID_MAX_DEVS] = { > + [0 ... (VIVID_MAX_DEVS - 1)] = 0x21d3d > +}; > module_param_array(node_types, uint, NULL, 0444); > MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the following meaning:\n" > "\t\t bit 0: Video Capture node\n" > @@ -106,7 +116,8 @@ MODULE_PARM_DESC(node_types, " node types, default is 0x1d3d. Bitmask with the f > "\t\t bit 8: Video Output node\n" > "\t\t bit 10-11: VBI Output node: 0 = none, 1 = raw vbi, 2 = sliced vbi, 3 = both\n" > "\t\t bit 12: Radio Transmitter node\n" > - "\t\t bit 16: Framebuffer for testing overlays"); > + "\t\t bit 16: Framebuffer for testing overlays\n" > + "\t\t bit 17: Metadata Capture node\n"); > > /* Default: 4 inputs */ > static unsigned num_inputs[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 4 }; > @@ -205,7 +216,7 @@ static int vidioc_querycap(struct file *file, void *priv, > cap->capabilities = dev->vid_cap_caps | dev->vid_out_caps | > dev->vbi_cap_caps | dev->vbi_out_caps | > dev->radio_rx_caps | dev->radio_tx_caps | > - dev->sdr_cap_caps | V4L2_CAP_DEVICE_CAPS; > + dev->sdr_cap_caps | dev->meta_cap_caps | V4L2_CAP_DEVICE_CAPS; > return 0; > } > > @@ -433,7 +444,8 @@ static bool vivid_is_last_user(struct vivid_dev *dev) > vivid_is_in_use(&dev->vbi_out_dev) + > vivid_is_in_use(&dev->sdr_cap_dev) + > vivid_is_in_use(&dev->radio_rx_dev) + > - vivid_is_in_use(&dev->radio_tx_dev); > + vivid_is_in_use(&dev->radio_tx_dev) + > + vivid_is_in_use(&dev->meta_cap_dev); > > return uses == 1; > } > @@ -459,6 +471,7 @@ static int vivid_fop_release(struct file *file) > set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); > set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); > set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); > + set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); > } > mutex_unlock(&dev->mutex); > if (file->private_data == dev->overlay_cap_owner) > @@ -604,6 +617,11 @@ static const struct v4l2_ioctl_ops vivid_ioctl_ops = { > .vidioc_log_status = vidioc_log_status, > .vidioc_subscribe_event = vidioc_subscribe_event, > .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > + > + .vidioc_enum_fmt_meta_cap = vidioc_enum_fmt_meta_cap, > + .vidioc_g_fmt_meta_cap = vidioc_g_fmt_meta_cap, > + .vidioc_s_fmt_meta_cap = vidioc_g_fmt_meta_cap, > + .vidioc_try_fmt_meta_cap = vidioc_g_fmt_meta_cap, > }; > > /* ----------------------------------------------------------------- > @@ -818,6 +836,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > dev->has_scaler_out ? 'Y' : 'N'); > } > > + /* do we create a meta capture device */ > + dev->has_meta_cap = node_type & 0x20000; > + > /* end detecting feature set */ > > if (dev->has_vid_cap) { > @@ -875,6 +896,16 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > dev->radio_tx_caps = V4L2_CAP_RDS_OUTPUT | V4L2_CAP_MODULATOR | > V4L2_CAP_READWRITE; > > + /* set up the capabilities of meta capture device */ > + if (dev->has_meta_cap) { > + dev->meta_cap_caps = V4L2_CAP_META_CAPTURE | > + V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; > + if (dev->has_audio_inputs) > + dev->meta_cap_caps |= V4L2_CAP_AUDIO; > + if (in_type_counter[TV]) > + dev->meta_cap_caps |= V4L2_CAP_TUNER; > + } > + > ret = -ENOMEM; > /* initialize the test pattern generator */ > tpg_init(&dev->tpg, 640, 360); > @@ -934,6 +965,9 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_AUDIO); > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_AUDIO); > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_ENUMAUDIO); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_AUDIO); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_AUDIO); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_ENUMAUDIO); > } > if (!dev->has_audio_outputs) { > v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_AUDOUT); > @@ -959,12 +993,16 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_FREQUENCY); > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_FREQUENCY); > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_FREQUENCY); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_FREQUENCY); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_FREQUENCY); > } > if (!has_tuner) { > v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_TUNER); > v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_G_TUNER); > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_TUNER); > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_G_TUNER); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_TUNER); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_G_TUNER); > } > if (in_type_counter[HDMI] == 0) { > v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_EDID); > @@ -990,6 +1028,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > v4l2_disable_ioctl(&dev->vid_cap_dev, VIDIOC_S_HW_FREQ_SEEK); > v4l2_disable_ioctl(&dev->vbi_cap_dev, VIDIOC_S_HW_FREQ_SEEK); > v4l2_disable_ioctl(&dev->sdr_cap_dev, VIDIOC_S_HW_FREQ_SEEK); > + v4l2_disable_ioctl(&dev->meta_cap_dev, VIDIOC_S_HW_FREQ_SEEK); > v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_S_FREQUENCY); > v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_G_FREQUENCY); > v4l2_disable_ioctl(&dev->vid_out_dev, VIDIOC_ENUM_FRAMESIZES); > @@ -1078,6 +1117,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > INIT_LIST_HEAD(&dev->vbi_cap_active); > INIT_LIST_HEAD(&dev->vbi_out_active); > INIT_LIST_HEAD(&dev->sdr_cap_active); > + INIT_LIST_HEAD(&dev->meta_cap_active); > > INIT_LIST_HEAD(&dev->cec_work_list); > spin_lock_init(&dev->cec_slock); > @@ -1225,6 +1265,27 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > dev->fb_info.node); > } > > + if (dev->has_meta_cap) { > + /* initialize meta_cap queue */ > + q = &dev->vb_meta_cap_q; > + q->type = V4L2_BUF_TYPE_META_CAPTURE; > + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; > + if (!allocator) > + q->io_modes |= VB2_USERPTR; > + q->drv_priv = dev; > + q->buf_struct_size = sizeof(struct vivid_buffer); > + q->ops = &vivid_meta_cap_qops; > + q->mem_ops = vivid_mem_ops[allocator]; > + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; > + q->min_buffers_needed = 2; > + q->lock = &dev->mutex; > + q->dev = dev->v4l2_dev.dev; > + q->supports_requests = true; > + ret = vb2_queue_init(q); > + if (ret) > + goto unreg_dev; > + } > + > #ifdef CONFIG_VIDEO_VIVID_CEC > if (dev->has_vid_cap && in_type_counter[HDMI]) { > struct cec_adapter *adap; > @@ -1265,6 +1326,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_rx); > v4l2_ctrl_handler_setup(&dev->ctrl_hdl_radio_tx); > v4l2_ctrl_handler_setup(&dev->ctrl_hdl_sdr_cap); > + v4l2_ctrl_handler_setup(&dev->ctrl_hdl_meta_cap); > > /* finally start creating the device nodes */ > if (dev->has_vid_cap) { > @@ -1492,6 +1554,35 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > video_device_node_name(vfd)); > } > > + if (dev->has_meta_cap) { > + vfd = &dev->meta_cap_dev; > + snprintf(vfd->name, sizeof(vfd->name), > + "vivid-%03d-meta-cap", inst); > + vfd->fops = &vivid_fops; > + vfd->ioctl_ops = &vivid_ioctl_ops; > + vfd->device_caps = dev->meta_cap_caps; > + vfd->release = video_device_release_empty; > + vfd->v4l2_dev = &dev->v4l2_dev; > + vfd->queue = &dev->vb_meta_cap_q; > + vfd->lock = &dev->mutex; > + vfd->tvnorms = tvnorms_cap; > + video_set_drvdata(vfd, dev); > +#ifdef CONFIG_MEDIA_CONTROLLER > + dev->meta_cap_pad.flags = MEDIA_PAD_FL_SINK; > + ret = media_entity_pads_init(&vfd->entity, 1, > + &dev->meta_cap_pad); > + if (ret) > + goto unreg_dev; > +#endif > + ret = video_register_device(vfd, VFL_TYPE_GRABBER, > + meta_cap_nr[inst]); > + if (ret < 0) > + goto unreg_dev; > + v4l2_info(&dev->v4l2_dev, > + "V4L2 metadata capture device registered as %s\n", > + video_device_node_name(vfd)); > + } > + > #ifdef CONFIG_MEDIA_CONTROLLER > /* Register the media device */ > ret = media_device_register(&dev->mdev); > @@ -1508,6 +1599,7 @@ static int vivid_create_instance(struct platform_device *pdev, int inst) > return 0; > > unreg_dev: > + video_unregister_device(&dev->meta_cap_dev); > video_unregister_device(&dev->radio_tx_dev); > video_unregister_device(&dev->radio_rx_dev); > video_unregister_device(&dev->sdr_cap_dev); > @@ -1624,6 +1716,11 @@ static int vivid_remove(struct platform_device *pdev) > unregister_framebuffer(&dev->fb_info); > vivid_fb_release_buffers(dev); > } > + if (dev->has_meta_cap) { > + v4l2_info(&dev->v4l2_dev, "unregistering %s\n", > + video_device_node_name(&dev->meta_cap_dev)); > + video_unregister_device(&dev->meta_cap_dev); > + } > cec_unregister_adapter(dev->cec_rx_adap); > for (j = 0; j < MAX_OUTPUTS; j++) > cec_unregister_adapter(dev->cec_tx_adap[j]); > diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h > index 7ebb14673c75..fd601345a17c 100644 > --- a/drivers/media/platform/vivid/vivid-core.h > +++ b/drivers/media/platform/vivid/vivid-core.h > @@ -131,6 +131,7 @@ struct vivid_dev { > struct media_pad vbi_cap_pad; > struct media_pad vbi_out_pad; > struct media_pad sdr_cap_pad; > + struct media_pad meta_cap_pad; > #endif > struct v4l2_ctrl_handler ctrl_hdl_user_gen; > struct v4l2_ctrl_handler ctrl_hdl_user_vid; > @@ -153,6 +154,9 @@ struct vivid_dev { > struct v4l2_ctrl_handler ctrl_hdl_radio_tx; > struct video_device sdr_cap_dev; > struct v4l2_ctrl_handler ctrl_hdl_sdr_cap; > + struct video_device meta_cap_dev; > + struct v4l2_ctrl_handler ctrl_hdl_meta_cap; > + > spinlock_t slock; > struct mutex mutex; > > @@ -164,6 +168,7 @@ struct vivid_dev { > u32 sdr_cap_caps; > u32 radio_rx_caps; > u32 radio_tx_caps; > + u32 meta_cap_caps; > > /* supported features */ > bool multiplanar; > @@ -189,6 +194,7 @@ struct vivid_dev { > bool has_radio_tx; > bool has_sdr_cap; > bool has_fb; > + bool has_meta_cap; > > bool can_loop_video; > > @@ -390,6 +396,8 @@ struct vivid_dev { > struct list_head vid_cap_active; > struct vb2_queue vb_vbi_cap_q; > struct list_head vbi_cap_active; > + struct vb2_queue vb_meta_cap_q; > + struct list_head meta_cap_active; > > /* thread for generating video capture stream */ > struct task_struct *kthread_vid_cap; > @@ -407,6 +415,9 @@ struct vivid_dev { > u32 vbi_cap_seq_count; > bool vbi_cap_streaming; > bool stream_sliced_vbi_cap; > + u32 meta_cap_seq_start; > + u32 meta_cap_seq_count; > + bool meta_cap_streaming; > > /* video output */ > const struct vivid_fmt *fmt_out; > @@ -527,6 +538,9 @@ struct vivid_dev { > /* CEC OSD String */ > char osd[14]; > unsigned long osd_jiffies; > + > + bool meta_pts; > + bool meta_scr; > }; > > static inline bool vivid_is_webcam(const struct vivid_dev *dev) > diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c > index cb19a9a73092..36e5944b51bb 100644 > --- a/drivers/media/platform/vivid/vivid-ctrls.c > +++ b/drivers/media/platform/vivid/vivid-ctrls.c > @@ -94,6 +94,9 @@ > > #define VIVID_CID_SDR_CAP_FM_DEVIATION (VIVID_CID_VIVID_BASE + 110) > > +#define VIVID_CID_META_CAP_GENERATE_PTS (VIVID_CID_VIVID_BASE + 111) > +#define VIVID_CID_META_CAP_GENERATE_SCR (VIVID_CID_VIVID_BASE + 112) > + > /* General User Controls */ > > static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) > @@ -110,6 +113,7 @@ static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl) > clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags); > clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags); > clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags); > + clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags); > break; > case VIVID_CID_BUTTON: > dev->button_pressed = 30; > @@ -1421,6 +1425,47 @@ static const struct v4l2_ctrl_config vivid_ctrl_sdr_cap_fm_deviation = { > .step = 1, > }; > > +/* Metadata Capture Control */ > + > +static int vivid_meta_cap_s_ctrl(struct v4l2_ctrl *ctrl) > +{ > + struct vivid_dev *dev = container_of(ctrl->handler, struct vivid_dev, > + ctrl_hdl_meta_cap); > + > + switch (ctrl->id) { > + case VIVID_CID_META_CAP_GENERATE_PTS: > + dev->meta_pts = ctrl->val; > + break; > + case VIVID_CID_META_CAP_GENERATE_SCR: > + dev->meta_scr = ctrl->val; > + break; > + } > + return 0; > +} > + > +static const struct v4l2_ctrl_ops vivid_meta_cap_ctrl_ops = { > + .s_ctrl = vivid_meta_cap_s_ctrl, > +}; > + > +static const struct v4l2_ctrl_config vivid_ctrl_meta_has_pts = { > + .ops = &vivid_meta_cap_ctrl_ops, > + .id = VIVID_CID_META_CAP_GENERATE_PTS, > + .name = "Generate PTS", > + .type = V4L2_CTRL_TYPE_BOOLEAN, > + .max = 1, > + .def = 1, > + .step = 1, > +}; > + > +static const struct v4l2_ctrl_config vivid_ctrl_meta_has_src_clk = { > + .ops = &vivid_meta_cap_ctrl_ops, > + .id = VIVID_CID_META_CAP_GENERATE_SCR, > + .name = "Generate SCR", > + .type = V4L2_CTRL_TYPE_BOOLEAN, > + .max = 1, > + .def = 1, > + .step = 1, > +}; > > static const struct v4l2_ctrl_config vivid_ctrl_class = { > .ops = &vivid_user_gen_ctrl_ops, > @@ -1448,6 +1493,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, > struct v4l2_ctrl_handler *hdl_radio_rx = &dev->ctrl_hdl_radio_rx; > struct v4l2_ctrl_handler *hdl_radio_tx = &dev->ctrl_hdl_radio_tx; > struct v4l2_ctrl_handler *hdl_sdr_cap = &dev->ctrl_hdl_sdr_cap; > + struct v4l2_ctrl_handler *hdl_meta_cap = &dev->ctrl_hdl_meta_cap; > + > struct v4l2_ctrl_config vivid_ctrl_dv_timings = { > .ops = &vivid_vid_cap_ctrl_ops, > .id = VIVID_CID_DV_TIMINGS, > @@ -1486,6 +1533,8 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, > v4l2_ctrl_new_custom(hdl_radio_tx, &vivid_ctrl_class, NULL); > v4l2_ctrl_handler_init(hdl_sdr_cap, 19); > v4l2_ctrl_new_custom(hdl_sdr_cap, &vivid_ctrl_class, NULL); > + v4l2_ctrl_handler_init(hdl_meta_cap, 2); > + v4l2_ctrl_new_custom(hdl_meta_cap, &vivid_ctrl_class, NULL); > > /* User Controls */ > dev->volume = v4l2_ctrl_new_std(hdl_user_aud, NULL, > @@ -1743,6 +1792,13 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, > v4l2_ctrl_new_custom(hdl_sdr_cap, > &vivid_ctrl_sdr_cap_fm_deviation, NULL); > } > + if (dev->has_meta_cap) { > + v4l2_ctrl_new_custom(hdl_meta_cap, > + &vivid_ctrl_meta_has_pts, NULL); > + v4l2_ctrl_new_custom(hdl_meta_cap, > + &vivid_ctrl_meta_has_src_clk, NULL); > + } > + > if (hdl_user_gen->error) > return hdl_user_gen->error; > if (hdl_user_vid->error) > @@ -1817,6 +1873,14 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap, > return hdl_sdr_cap->error; > dev->sdr_cap_dev.ctrl_handler = hdl_sdr_cap; > } > + if (dev->has_meta_cap) { > + v4l2_ctrl_add_handler(hdl_meta_cap, hdl_user_gen, NULL, false); > + v4l2_ctrl_add_handler(hdl_meta_cap, hdl_streaming, NULL, false); > + if (hdl_meta_cap->error) > + return hdl_meta_cap->error; > + dev->meta_cap_dev.ctrl_handler = hdl_meta_cap; > + } > + > return 0; > } > > @@ -1836,4 +1900,5 @@ void vivid_free_controls(struct vivid_dev *dev) > v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap); > v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_cap); > v4l2_ctrl_handler_free(&dev->ctrl_hdl_fb); > + v4l2_ctrl_handler_free(&dev->ctrl_hdl_meta_cap); > } > diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c > index 003319d7816d..95bb4551ee1d 100644 > --- a/drivers/media/platform/vivid/vivid-kthread-cap.c > +++ b/drivers/media/platform/vivid/vivid-kthread-cap.c > @@ -39,6 +39,7 @@ > #include "vivid-osd.h" > #include "vivid-ctrls.h" > #include "vivid-kthread-cap.h" > +#include "vivid-meta-cap.h" > > static inline v4l2_std_id vivid_get_std_cap(const struct vivid_dev *dev) > { > @@ -677,6 +678,7 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, > { > struct vivid_buffer *vid_cap_buf = NULL; > struct vivid_buffer *vbi_cap_buf = NULL; > + struct vivid_buffer *meta_cap_buf = NULL; > u64 f_time = 0; > > dprintk(dev, 1, "Video Capture Thread Tick\n"); > @@ -704,15 +706,20 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, > list_del(&vbi_cap_buf->list); > } > } > + if (!list_empty(&dev->meta_cap_active)) { > + meta_cap_buf = list_entry(dev->meta_cap_active.next, > + struct vivid_buffer, list); > + list_del(&meta_cap_buf->list); > + } > + > spin_unlock(&dev->slock); > > - if (!vid_cap_buf && !vbi_cap_buf) > + if (!vid_cap_buf && !vbi_cap_buf && !meta_cap_buf) > goto update_mv; > > f_time = dev->cap_frame_period * dev->vid_cap_seq_count + > - dev->cap_stream_start + dev->time_wrap_offset; > - if (!dev->tstamp_src_is_soe) > - f_time += dev->cap_frame_eof_offset; > + dev->cap_stream_start + dev->time_wrap_offset + > + dev->cap_frame_eof_offset; No, keep the code as in v7: f_time is the time at the beginning of the frame. Then only add dev->cap_frame_eof_offset where it is needed. Subtracting time values is harder to understand than adding time values. > > if (vid_cap_buf) { > v4l2_ctrl_request_setup(vid_cap_buf->vb.vb2_buf.req_obj.req, > @@ -735,6 +742,8 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, > vid_cap_buf->vb.vb2_buf.index); > > vid_cap_buf->vb.vb2_buf.timestamp = f_time; > + if (dev->tstamp_src_is_soe) > + vid_cap_buf->vb.vb2_buf.timestamp -= dev->cap_frame_eof_offset; This becomes: if (!dev->tstamp_src_is_soe) vid_cap_buf->vb.vb2_buf.timestamp += dev->cap_frame_eof_offset; > } > > if (vbi_cap_buf) { > @@ -758,6 +767,20 @@ static noinline_for_stack void vivid_thread_vid_cap_tick(struct vivid_dev *dev, > do_div(vbi_period, 100); > vbi_cap_buf->vb.vb2_buf.timestamp = f_time + vbi_period; This becomes f_time + dev->cap_frame_eof_offset + vbi_period; > } > + > + if (meta_cap_buf) { > + v4l2_ctrl_request_setup(meta_cap_buf->vb.vb2_buf.req_obj.req, > + &dev->ctrl_hdl_meta_cap); > + vivid_meta_cap_fillbuff(dev, meta_cap_buf, f_time); This is now wrong in v8, since vivid_meta_cap_fillbuff() expects the start-of-frame time. If f_time is once again the start-of-frame time, then this is correct again. > + v4l2_ctrl_request_complete(meta_cap_buf->vb.vb2_buf.req_obj.req, > + &dev->ctrl_hdl_meta_cap); > + vb2_buffer_done(&meta_cap_buf->vb.vb2_buf, dev->dqbuf_error ? > + VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); > + dprintk(dev, 2, "meta_cap %d done\n", > + meta_cap_buf->vb.vb2_buf.index); > + meta_cap_buf->vb.vb2_buf.timestamp = f_time; This becomes f_time + dev->cap_frame_eof_offset; Metadata always arrives at the end of the frame. Regards, Hans > + } > + > dev->dqbuf_error = false; > > update_mv: > @@ -835,6 +858,7 @@ static int vivid_thread_vid_cap(void *data) > dev->cap_seq_count = buffers_since_start + dev->cap_seq_offset; > dev->vid_cap_seq_count = dev->cap_seq_count - dev->vid_cap_seq_start; > dev->vbi_cap_seq_count = dev->cap_seq_count - dev->vbi_cap_seq_start; > + dev->meta_cap_seq_count = dev->cap_seq_count - dev->meta_cap_seq_start; > > vivid_thread_vid_cap_tick(dev, dropped_bufs); > > @@ -883,8 +907,10 @@ int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) > > if (pstreaming == &dev->vid_cap_streaming) > dev->vid_cap_seq_start = seq_count; > - else > + else if (pstreaming == &dev->vbi_cap_streaming) > dev->vbi_cap_seq_start = seq_count; > + else > + dev->meta_cap_seq_start = seq_count; > *pstreaming = true; > return 0; > } > @@ -894,6 +920,7 @@ int vivid_start_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) > > dev->vid_cap_seq_start = dev->seq_wrap * 128; > dev->vbi_cap_seq_start = dev->seq_wrap * 128; > + dev->meta_cap_seq_start = dev->seq_wrap * 128; > > dev->kthread_vid_cap = kthread_run(vivid_thread_vid_cap, dev, > "%s-vid-cap", dev->v4l2_dev.name); > @@ -951,7 +978,23 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) > } > } > > - if (dev->vid_cap_streaming || dev->vbi_cap_streaming) > + if (pstreaming == &dev->meta_cap_streaming) { > + while (!list_empty(&dev->meta_cap_active)) { > + struct vivid_buffer *buf; > + > + buf = list_entry(dev->meta_cap_active.next, > + struct vivid_buffer, list); > + list_del(&buf->list); > + v4l2_ctrl_request_complete(buf->vb.vb2_buf.req_obj.req, > + &dev->ctrl_hdl_meta_cap); > + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); > + dprintk(dev, 2, "meta_cap buffer %d done\n", > + buf->vb.vb2_buf.index); > + } > + } > + > + if (dev->vid_cap_streaming || dev->vbi_cap_streaming || > + dev->meta_cap_streaming) > return; > > /* shutdown control thread */ > diff --git a/drivers/media/platform/vivid/vivid-meta-cap.c b/drivers/media/platform/vivid/vivid-meta-cap.c > new file mode 100644 > index 000000000000..b2f42c09abe1 > --- /dev/null > +++ b/drivers/media/platform/vivid/vivid-meta-cap.c > @@ -0,0 +1,201 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * vivid-meta-cap.c - meta capture support functions. > + */ > + > +#include <linux/errno.h> > +#include <linux/kernel.h> > +#include <linux/videodev2.h> > +#include <media/v4l2-common.h> > +#include <linux/usb/video.h> > + > +#include "vivid-core.h" > +#include "vivid-kthread-cap.h" > +#include "vivid-meta-cap.h" > + > +static int meta_cap_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, > + unsigned int *nplanes, unsigned int sizes[], > + struct device *alloc_devs[]) > +{ > + struct vivid_dev *dev = vb2_get_drv_priv(vq); > + unsigned int size = sizeof(struct vivid_uvc_meta_buf); > + > + if (!vivid_is_webcam(dev)) > + return -EINVAL; > + > + if (*nplanes) { > + if (sizes[0] < size) > + return -EINVAL; > + } else { > + sizes[0] = size; > + } > + > + if (vq->num_buffers + *nbuffers < 2) > + *nbuffers = 2 - vq->num_buffers; > + > + *nplanes = 1; > + return 0; > +} > + > +static int meta_cap_buf_prepare(struct vb2_buffer *vb) > +{ > + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); > + unsigned int size = sizeof(struct vivid_uvc_meta_buf); > + > + dprintk(dev, 1, "%s\n", __func__); > + > + if (dev->buf_prepare_error) { > + /* > + * Error injection: test what happens if buf_prepare() returns > + * an error. > + */ > + dev->buf_prepare_error = false; > + return -EINVAL; > + } > + if (vb2_plane_size(vb, 0) < size) { > + dprintk(dev, 1, "%s data will not fit into plane (%lu < %u)\n", > + __func__, vb2_plane_size(vb, 0), size); > + return -EINVAL; > + } > + vb2_set_plane_payload(vb, 0, size); > + > + return 0; > +} > + > +static void meta_cap_buf_queue(struct vb2_buffer *vb) > +{ > + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); > + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); > + struct vivid_buffer *buf = container_of(vbuf, struct vivid_buffer, vb); > + > + dprintk(dev, 1, "%s\n", __func__); > + > + spin_lock(&dev->slock); > + list_add_tail(&buf->list, &dev->meta_cap_active); > + spin_unlock(&dev->slock); > +} > + > +static int meta_cap_start_streaming(struct vb2_queue *vq, unsigned int count) > +{ > + struct vivid_dev *dev = vb2_get_drv_priv(vq); > + int err; > + > + dprintk(dev, 1, "%s\n", __func__); > + dev->meta_cap_seq_count = 0; > + if (dev->start_streaming_error) { > + dev->start_streaming_error = false; > + err = -EINVAL; > + } else { > + err = vivid_start_generating_vid_cap(dev, > + &dev->meta_cap_streaming); > + } > + if (err) { > + struct vivid_buffer *buf, *tmp; > + > + list_for_each_entry_safe(buf, tmp, > + &dev->meta_cap_active, list) { > + list_del(&buf->list); > + vb2_buffer_done(&buf->vb.vb2_buf, > + VB2_BUF_STATE_QUEUED); > + } > + } > + return err; > +} > + > +/* abort streaming and wait for last buffer */ > +static void meta_cap_stop_streaming(struct vb2_queue *vq) > +{ > + struct vivid_dev *dev = vb2_get_drv_priv(vq); > + > + dprintk(dev, 1, "%s\n", __func__); > + vivid_stop_generating_vid_cap(dev, &dev->meta_cap_streaming); > +} > + > +static void meta_cap_buf_request_complete(struct vb2_buffer *vb) > +{ > + struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue); > + > + v4l2_ctrl_request_complete(vb->req_obj.req, &dev->ctrl_hdl_meta_cap); > +} > + > +const struct vb2_ops vivid_meta_cap_qops = { > + .queue_setup = meta_cap_queue_setup, > + .buf_prepare = meta_cap_buf_prepare, > + .buf_queue = meta_cap_buf_queue, > + .start_streaming = meta_cap_start_streaming, > + .stop_streaming = meta_cap_stop_streaming, > + .buf_request_complete = meta_cap_buf_request_complete, > + .wait_prepare = vb2_ops_wait_prepare, > + .wait_finish = vb2_ops_wait_finish, > +}; > + > +int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, > + struct v4l2_fmtdesc *f) > +{ > + struct vivid_dev *dev = video_drvdata(file); > + > + if (!vivid_is_webcam(dev)) > + return -EINVAL; > + > + if (f->index > 0) > + return -EINVAL; > + > + f->type = V4L2_BUF_TYPE_META_CAPTURE; > + f->pixelformat = V4L2_META_FMT_UVC; > + return 0; > +} > + > +int vidioc_g_fmt_meta_cap(struct file *file, void *priv, > + struct v4l2_format *f) > +{ > + struct vivid_dev *dev = video_drvdata(file); > + struct v4l2_meta_format *meta = &f->fmt.meta; > + > + if (!vivid_is_webcam(dev) || !dev->has_meta_cap) > + return -EINVAL; > + > + meta->dataformat = V4L2_META_FMT_UVC; > + meta->buffersize = sizeof(struct vivid_uvc_meta_buf); > + return 0; > +} > + > +void vivid_meta_cap_fillbuff(struct vivid_dev *dev, > + struct vivid_buffer *buf, u32 soe) > +{ > + struct vivid_uvc_meta_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); > + int buf_off = 0; > + > + buf->vb.sequence = dev->meta_cap_seq_count; > + if (dev->field_cap == V4L2_FIELD_ALTERNATE) > + buf->vb.sequence /= 2; > + memset(meta, 1, vb2_plane_size(&buf->vb.vb2_buf, 0)); > + > + meta->ns = ktime_get_ns(); > + meta->sof = buf->vb.sequence * 30; > + meta->length = sizeof(*meta) - offsetof(struct vivid_uvc_meta_buf, length); > + meta->flags = UVC_STREAM_EOH | UVC_STREAM_EOF; > + > + if ((buf->vb.sequence % 2) == 0) > + meta->flags |= UVC_STREAM_FID; > + > + dprintk(dev, 2, "%s ns:%llu sof:%4d len:%u flags: 0x%02x", > + __func__, meta->ns, meta->sof, meta->length, meta->flags); > + if (dev->meta_pts) { > + meta->flags |= UVC_STREAM_PTS; > + meta->buf[0] = soe / VIVID_META_CLOCK_UNIT; > + buf_off = 4; > + dprintk(dev, 2, " pts: %u\n", *(__u32 *)(meta->buf)); > + } > + > + if (dev->meta_scr) { > + meta->flags |= UVC_STREAM_SCR; > + meta->buf[buf_off] = (soe + dev->cap_frame_eof_offset) > + / VIVID_META_CLOCK_UNIT; > + > + meta->buf[buf_off + 4] = (buf->vb.sequence * 30) % 1000; > + dprintk(dev, 2, " stc: %u, sof counter: %u\n", > + *(__u32 *)(meta->buf + buf_off), > + *(__u16 *)(meta->buf + buf_off + 4)); > + } > + dprintk(dev, 2, "\n"); > +} > diff --git a/drivers/media/platform/vivid/vivid-meta-cap.h b/drivers/media/platform/vivid/vivid-meta-cap.h > new file mode 100644 > index 000000000000..58d23f2aa800 > --- /dev/null > +++ b/drivers/media/platform/vivid/vivid-meta-cap.h > @@ -0,0 +1,29 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * vivid-meta-cap.h - meta capture support functions. > + */ > +#ifndef _VIVID_META_CAP_H_ > +#define _VIVID_META_CAP_H_ > + > +#define VIVID_META_CLOCK_UNIT 10 /* 100 MHz */ > + > +struct vivid_uvc_meta_buf { > + __u64 ns; > + __u16 sof; > + __u8 length; > + __u8 flags; > + __u8 buf[10]; /* PTS(4)+STC(4)+SOF(2) */ > +} __packed; > + > +void vivid_meta_cap_fillbuff(struct vivid_dev *dev, > + struct vivid_buffer *buf, u32 soe); > + > +int vidioc_enum_fmt_meta_cap(struct file *file, void *priv, > + struct v4l2_fmtdesc *f); > + > +int vidioc_g_fmt_meta_cap(struct file *file, void *priv, > + struct v4l2_format *f); > + > +extern const struct vb2_ops vivid_meta_cap_qops; > + > +#endif > diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c > index 2d030732feac..e94beef008c8 100644 > --- a/drivers/media/platform/vivid/vivid-vid-cap.c > +++ b/drivers/media/platform/vivid/vivid-vid-cap.c > @@ -1356,7 +1356,9 @@ int vidioc_s_input(struct file *file, void *priv, unsigned i) > if (i == dev->input) > return 0; > > - if (vb2_is_busy(&dev->vb_vid_cap_q) || vb2_is_busy(&dev->vb_vbi_cap_q)) > + if (vb2_is_busy(&dev->vb_vid_cap_q) || > + vb2_is_busy(&dev->vb_vbi_cap_q) || > + vb2_is_busy(&dev->vb_meta_cap_q)) > return -EBUSY; > > dev->input = i; > @@ -1366,6 +1368,7 @@ int vidioc_s_input(struct file *file, void *priv, unsigned i) > dev->vid_cap_dev.tvnorms = V4L2_STD_ALL; > } > dev->vbi_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; > + dev->meta_cap_dev.tvnorms = dev->vid_cap_dev.tvnorms; > vivid_update_format_cap(dev, false); > > if (dev->colorspace) { >