From cd83f84e317134a234888a55a0b8680ad7975910 Mon Sep 17 00:00:00 2001 From: Dmitry Perchanov Date: Wed, 22 Mar 2023 13:35:25 +0200 Subject: [PATCH] ipu6: ipu-isys-video video node support for camera subdev - ipu-isys-video.c: - control for enhanced node support by pipeline subdevices enumerating - inheritance of sub-device controls within same vc - s/g_parm ioctl ops to set/get fps on subdev - vidioc_enum_framesizes - implement remote sensor polling - vidioc_enum_frameintervals - implement remote sensor polling - vidioc_enum_fmt - select only sub-device formats - vidioc_s_fmt_vid_cap_mplane - set remote link format - vidioc_try_fmt_vid_cap_mplane - try remote format - link validation for isys entities #134 - metadata support for D4XX_META format - ipu-isys-csi2-be-soc.c: link validation #134 - CSI2 BE SOC has multiple formats on capture pads that's the point where it match external pad0 format which will inherit format from CSI-2 external entity. - ipu-isys-csi2.c: link validation #134 - inherit format from CSI-2 external entity. - ipu-isys.h: V4L2_CID_IPU_ENUMERATE_LINK - ipu-isys-video.h: ipu_isys_video.enum_link_state state for link enumeration by vc - ipu-psys.c: fix compilation issue on kernel 5.15 - Resloves ipu-psys: MODULE_IMPORT_NS(DMA_BUF) for kernel 5.15 #77 - ipu-isys-queue.c: - Move firmware bring-up from video open to queue start streaming. This will increase firmware stability for start-stop toggling without closing video node for all streams. - Move firmware shutdown from video close to queue stop streaming Improves recovery process for multithread processes that not close video handle. Signed-off-by: Dmitry Perchanov --- .../media/pci/intel/ipu-isys-csi2-be-soc.c | 31 + drivers/media/pci/intel/ipu-isys-csi2.c | 13 + drivers/media/pci/intel/ipu-isys-queue.c | 149 +++- drivers/media/pci/intel/ipu-isys-video.c | 759 ++++++++++++++---- drivers/media/pci/intel/ipu-isys-video.h | 1 + drivers/media/pci/intel/ipu-psys.c | 2 +- include/uapi/linux/ipu-isys.h | 2 + 7 files changed, 809 insertions(+), 148 deletions(-) diff --git a/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c b/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c index caf3654d4e8d..367f06c5c613 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c +++ b/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c @@ -186,7 +186,38 @@ static struct v4l2_subdev_ops csi2_be_soc_sd_ops = { .pad = &csi2_be_soc_sd_pad_ops, }; +static int csi2_be_soc_link_validate(struct media_link *link) +{ + struct media_pipeline *media_pipe; + struct ipu_isys_pipeline *ip; + struct v4l2_subdev *source_sd; + struct v4l2_subdev *sink_sd; + struct v4l2_subdev_format fmt = { 0 }; + + int rval; + + if (!link->sink->entity || !link->source->entity) + return -EINVAL; + media_pipe = media_entity_pipeline(link->sink->entity); + if (!media_pipe) + return -EINVAL; + + ip = to_ipu_isys_pipeline(media_pipe); + source_sd = media_entity_to_v4l2_subdev(link->source->entity); + sink_sd = media_entity_to_v4l2_subdev(link->sink->entity); + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + fmt.pad = CSI2_PAD_SOURCE; + rval = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &fmt); + + fmt.pad = CSI2_BE_SOC_PAD_SINK; + rval = v4l2_subdev_call(sink_sd, pad, set_fmt, NULL, &fmt); + return v4l2_subdev_link_validate(link); +} + static struct media_entity_operations csi2_be_soc_entity_ops = { + .link_validate = csi2_be_soc_link_validate, }; static void csi2_be_soc_set_ffmt(struct v4l2_subdev *sd, diff --git a/drivers/media/pci/intel/ipu-isys-csi2.c b/drivers/media/pci/intel/ipu-isys-csi2.c index 67914fd60f7d..f00b8155b66c 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2.c +++ b/drivers/media/pci/intel/ipu-isys-csi2.c @@ -333,6 +333,7 @@ static int csi2_link_validate(struct media_link *link) struct ipu_isys_pipeline *ip; struct v4l2_subdev *source_sd; struct v4l2_subdev *sink_sd; + struct v4l2_subdev_format fmt = { 0 }; int rval; @@ -350,8 +351,20 @@ static int csi2_link_validate(struct media_link *link) ipu_isys_video_add_capture_done(ip, csi2_capture_done); source_sd = media_entity_to_v4l2_subdev(link->source->entity); sink_sd = media_entity_to_v4l2_subdev(link->sink->entity); + if (!source_sd) return -ENODEV; + /* source is external entity, get it's format */ + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = CSI2_PAD_SINK; + rval = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &fmt); + + /* set csi2 format for the same as external entity */ + rval = v4l2_subdev_call(sink_sd, pad, set_fmt, NULL, &fmt); + + rval = v4l2_subdev_link_validate(link); + if (rval) + return rval; if (strncmp(source_sd->name, IPU_ISYS_ENTITY_PREFIX, strlen(IPU_ISYS_ENTITY_PREFIX)) != 0) { diff --git a/drivers/media/pci/intel/ipu-isys-queue.c b/drivers/media/pci/intel/ipu-isys-queue.c index 6b0b74cc934f..a41b42c0d27b 100644 --- a/drivers/media/pci/intel/ipu-isys-queue.c +++ b/drivers/media/pci/intel/ipu-isys-queue.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -817,6 +818,15 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) mutex_unlock(&av->isys->stream_mutex); + if (av->pfmt->css_pixelformat){ + rval = aq->link_fmt_validate(aq); + if (rval) { + dev_err(&av->isys->adev->dev, + "%s: link format validation failed (%d)\n", + av->vdev.name, rval); + goto out_unprepare_streaming; + } + } ip = to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); pipe_av = container_of(ip, struct ipu_isys_video, ip); if (pipe_av != av) { @@ -872,6 +882,7 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&av->mutex); } +out_unprepare_streaming: mutex_lock(&av->isys->stream_mutex); if (first) ipu_isys_video_prepare_streaming(av, 0); @@ -883,12 +894,138 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) return rval; } +static int isys_fw_open(struct ipu_isys_video *av) +{ + struct ipu_isys *isys = av->isys; + struct ipu_bus_device *adev = to_ipu_bus_device(&isys->adev->dev); + struct ipu_device *isp = adev->isp; + int rval; + const struct ipu_isys_internal_pdata *ipdata; + + dev_warn(&isys->adev->dev, "%s:%d %s: enter\n", + __func__, __LINE__, av->vdev.name); + + mutex_lock(&isys->mutex); + + if (isys->reset_needed || isp->flr_done) { + mutex_unlock(&isys->mutex); + dev_warn(&isys->adev->dev, "%s:%d %s: isys power cycle required\n", + __func__, __LINE__, av->vdev.name); + return -EIO; + } + mutex_unlock(&isys->mutex); + + rval = pm_runtime_get_sync(&isys->adev->dev); + if (rval < 0) { + pm_runtime_put_noidle(&isys->adev->dev); + return rval; + } + + mutex_lock(&isys->mutex); + if (isys->video_opened++) { + /* Already open */ + mutex_unlock(&isys->mutex); + dev_warn(&isys->adev->dev, "%s:%d %s: Already open, exit %d\n", + __func__, __LINE__, av->vdev.name, isys->video_opened); + return 0; + } + + ipdata = isys->pdata->ipdata; + ipu_configure_spc(adev->isp, + &ipdata->hw_variant, + IPU_CPD_PKG_DIR_ISYS_SERVER_IDX, + isys->pdata->base, isys->pkg_dir, + isys->pkg_dir_dma_addr); + + /* + * Buffers could have been left to wrong queue at last closure. + * Move them now back to empty buffer queue. + */ + ipu_cleanup_fw_msg_bufs(isys); + + if (isys->fwcom) { + /* + * Something went wrong in previous shutdown. As we are now + * restarting isys we can safely delete old context. + */ + dev_err(&isys->adev->dev, "%s:%d %s Clearing old context\n", + __func__, __LINE__, av->vdev.name); + ipu_fw_isys_cleanup(isys); + } + + rval = ipu_fw_isys_init(av->isys, ipdata->num_parallel_streams); + if (rval < 0) + goto out_lib_init; + + mutex_unlock(&isys->mutex); + + dev_warn(&isys->adev->dev, "%s:%d %s: exit\n", + __func__, __LINE__, av->vdev.name); + return 0; + +out_lib_init: + isys->video_opened--; + mutex_unlock(&isys->mutex); + pm_runtime_put(&isys->adev->dev); + + return rval; +} + +static int isys_fw_release(struct ipu_isys_video *av) +{ + struct ipu_isys *isys = av->isys; + int ret = 0; + + dev_warn(&isys->adev->dev, "%s:%d %s: enter\n", + __func__, __LINE__, av->vdev.name); + mutex_lock(&isys->reset_mutex); + while (isys->in_reset) { + mutex_unlock(&isys->reset_mutex); + dev_warn(&isys->adev->dev, "%s:%d %s: wait for reset\n", + __func__, __LINE__, av->vdev.name); + usleep_range(10000, 11000); + mutex_lock(&isys->reset_mutex); + } + mutex_unlock(&isys->reset_mutex); + + mutex_lock(&isys->mutex); +dev_warn(&isys->adev->dev, "%s:%d %s: close fw video_opened: %d\n", + __func__, __LINE__, av->vdev.name, isys->video_opened); + if (isys->video_opened) + isys->video_opened--; + if (!isys->video_opened) { + dev_warn(&isys->adev->dev, "%s:%d %s: close fw\n", + __func__, __LINE__, av->vdev.name); + ipu_fw_isys_close(isys); + + if (isys->fwcom) { + isys->reset_needed = true; + ret = -EIO; + } + } + + mutex_unlock(&isys->mutex); + + if (isys->reset_needed) + pm_runtime_put_sync(&isys->adev->dev); + else + pm_runtime_put(&isys->adev->dev); + + dev_warn(&isys->adev->dev, "%s:%d %s: exit\n", + __func__, __LINE__, av->vdev.name); + return ret; +} + static int start_streaming(struct vb2_queue *q, unsigned int count) { struct ipu_isys_queue *aq = vb2_queue_to_ipu_isys_queue(q); struct ipu_isys_video *av = ipu_isys_queue_to_video(aq); int rval; + rval = isys_fw_open(av); + if (rval < 0) { + dev_err(&av->isys->adev->dev, "isys_fw_open failed: %d\n", rval); + } mutex_unlock(&av->mutex); mutex_lock(&av->isys->reset_mutex); while (av->isys->in_stop_streaming) { @@ -903,7 +1040,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) mutex_lock(&av->mutex); rval = __start_streaming(q, count); - + if (rval) + isys_fw_release(av); return rval; } @@ -912,7 +1050,8 @@ static void reset_stop_streaming(struct ipu_isys_video *av) struct ipu_isys_pipeline *ip = &av->ip; struct ipu_isys_queue *aq = &av->aq; - dev_dbg(&av->isys->adev->dev, "%s: stop streaming\n", av->vdev.name); + dev_warn(&av->isys->adev->dev, "%s():%d %s: stop streaming\n", + __func__, __LINE__, av->vdev.name); mutex_lock(&av->isys->stream_mutex); if (ip->nr_streaming == ip->nr_queues && ip->streaming) @@ -932,7 +1071,8 @@ static int reset_start_streaming(struct ipu_isys_video *av) unsigned long flags; int rval; - dev_dbg(&av->isys->adev->dev, "%s: start streaming\n", av->vdev.name); + dev_warn(&av->isys->adev->dev, "%s():%d %s: start streaming\n", + __func__, __LINE__, av->vdev.name); spin_lock_irqsave(&aq->lock, flags); while (!list_empty(&aq->active)) { @@ -1171,7 +1311,8 @@ static void stop_streaming(struct vb2_queue *q) mutex_lock(&av->isys->reset_mutex); av->isys->in_stop_streaming = false; mutex_unlock(&av->isys->reset_mutex); - + if (0 == ip->nr_streaming) + isys_fw_release(av); } static unsigned int diff --git a/drivers/media/pci/intel/ipu-isys-video.c b/drivers/media/pci/intel/ipu-isys-video.c index 553fa7513b8b..fc7e2082c827 100644 --- a/drivers/media/pci/intel/ipu-isys-video.c +++ b/drivers/media/pci/intel/ipu-isys-video.c @@ -39,10 +39,6 @@ const struct ipu_isys_pixelformat ipu_isys_pfmts_be_soc[] = { {V4L2_PIX_FMT_Y10, 16, 10, 0, MEDIA_BUS_FMT_Y10_1X10, IPU_FW_ISYS_FRAME_FORMAT_RAW16}, - {V4L2_PIX_FMT_Y8I, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, - IPU_FW_ISYS_FRAME_FORMAT_UYVY}, - {V4L2_PIX_FMT_Z16, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, - IPU_FW_ISYS_FRAME_FORMAT_UYVY}, {V4L2_PIX_FMT_UYVY, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, IPU_FW_ISYS_FRAME_FORMAT_UYVY}, {V4L2_PIX_FMT_YUYV, 16, 16, 0, MEDIA_BUS_FMT_YUYV8_1X16, @@ -51,8 +47,6 @@ const struct ipu_isys_pixelformat ipu_isys_pfmts_be_soc[] = { IPU_FW_ISYS_FRAME_FORMAT_NV16}, {V4L2_PIX_FMT_XRGB32, 32, 32, 0, MEDIA_BUS_FMT_RGB565_1X16, IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, - {V4L2_PIX_FMT_Y12I, 24, 24, 0, MEDIA_BUS_FMT_RGB888_1X24, - IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, {V4L2_PIX_FMT_XBGR32, 32, 32, 0, MEDIA_BUS_FMT_RGB888_1X24, IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, /* Raw bayer formats. */ @@ -82,6 +76,12 @@ const struct ipu_isys_pixelformat ipu_isys_pfmts_be_soc[] = { IPU_FW_ISYS_FRAME_FORMAT_RAW8}, {V4L2_PIX_FMT_GREY, 8, 8, 0, MEDIA_BUS_FMT_Y8_1X8, IPU_FW_ISYS_FRAME_FORMAT_RAW8}, + {V4L2_PIX_FMT_Z16, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, + IPU_FW_ISYS_FRAME_FORMAT_UYVY}, + {V4L2_PIX_FMT_Y8I, 16, 16, 0, MEDIA_BUS_FMT_VYUY8_1X16, + IPU_FW_ISYS_FRAME_FORMAT_YUYV}, + {V4L2_PIX_FMT_Y12I, 32, 24, 0, MEDIA_BUS_FMT_RGB888_1X24, + IPU_FW_ISYS_FRAME_FORMAT_RGBA888}, {V4L2_META_FMT_D4XX, 8, 8, 0, MEDIA_BUS_FMT_FIXED, 0}, {} }; @@ -93,10 +93,6 @@ const struct ipu_isys_pixelformat ipu_isys_pfmts_packed[] = { {V4L2_PIX_FMT_Y210, 20, 20, 0, MEDIA_BUS_FMT_YUYV10_1X20, IPU_FW_ISYS_FRAME_FORMAT_YUYV}, #endif - {V4L2_PIX_FMT_Y8I, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, - IPU_FW_ISYS_FRAME_FORMAT_UYVY}, - {V4L2_PIX_FMT_Z16, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, - IPU_FW_ISYS_FRAME_FORMAT_UYVY}, {V4L2_PIX_FMT_UYVY, 16, 16, 0, MEDIA_BUS_FMT_UYVY8_1X16, IPU_FW_ISYS_FRAME_FORMAT_UYVY}, {V4L2_PIX_FMT_YUYV, 16, 16, 0, MEDIA_BUS_FMT_YUYV8_1X16, @@ -145,30 +141,111 @@ const struct ipu_isys_pixelformat ipu_isys_pfmts_packed[] = { {} }; -static int video_open(struct file *file) +enum ipu_isys_enum_link_state { + IPU_ISYS_LINK_STATE_DISABLED = 0, + IPU_ISYS_LINK_STATE_ENABLED = 1, + IPU_ISYS_LINK_STATE_DONE = 2, + IPU_ISYS_LINK_STATE_MD = 3, + IPU_ISYS_LINK_STATE_MAX, +}; + +static int ipu_isys_query_sensor_info(struct media_pad *source_pad, + struct ipu_isys_pipeline *ip); + +static int ipu_isys_inherit_ctrls(struct ipu_isys_video *av, + struct v4l2_subdev *sd, void *data) { - struct ipu_isys_video *av = video_drvdata(file); - struct ipu_isys *isys = av->isys; - struct ipu_bus_device *adev = to_ipu_bus_device(&isys->adev->dev); - struct ipu_device *isp = adev->isp; - int rval; - const struct ipu_isys_internal_pdata *ipdata; + int ret = 0; + ret = v4l2_ctrl_add_handler(&av->ctrl_handler, + sd->ctrl_handler, NULL, true); + return ret; +} - mutex_lock(&isys->mutex); +static int media_pipeline_enumerate_by_vc_cb( + struct ipu_isys_video *av, + int (*cb_fn)(struct ipu_isys_video *av, + struct v4l2_subdev *sd, + void *data), + void *data) +{ + int ret = -ENOLINK; + struct ipu_isys_pipeline *ip = kmalloc( + sizeof(struct ipu_isys_pipeline), 128); + struct media_pipeline *pipe = &ip->pipe; + struct media_entity *entity = &av->vdev.entity; + struct media_device *mdev = entity->graph_obj.mdev; + struct media_graph *graph = &pipe->graph; + struct media_pad *source_pad = media_entity_remote_pad(&av->pad); + unsigned int pad_id; + struct v4l2_subdev *sd; + struct v4l2_control ct = { + .id = V4L2_CID_IPU_QUERY_SUB_STREAM, + }; - if (isys->reset_needed || isp->flr_done) { - mutex_unlock(&isys->mutex); - dev_warn(&isys->adev->dev, "isys power cycle required\n"); - return -EIO; + if (!source_pad) { + dev_err(entity->graph_obj.mdev->dev, "no remote pad found\n"); + kfree(ip); + return ret; } - mutex_unlock(&isys->mutex); - rval = pm_runtime_get_sync(&isys->adev->dev); - if (rval < 0) { - pm_runtime_put_noidle(&isys->adev->dev); - return rval; + pad_id = source_pad->index; + mutex_lock(&mdev->graph_mutex); + + ret = media_graph_walk_init(&pipe->graph, mdev); + if (ret) + goto error_graph_walk_start_enum; + + ret = ipu_isys_query_sensor_info(source_pad, ip); + if (ret) { + dev_err(entity->graph_obj.mdev->dev, + "query sensor info failed\n"); + goto error_graph_walk_start_enum; } + media_graph_walk_start(&pipe->graph, entity); + while ((entity = media_graph_walk_next(graph))) { + /* + * If entity's pipe is not null and it is video device, it has + * be enabled. + */ + if (entity->pipe && is_media_entity_v4l2_video_device(entity)) + continue; + + sd = media_entity_to_v4l2_subdev(entity); + /* pre-filter sub-devices */ + if (!sd) + continue; + if (!strlen(sd->name)) + continue; + if (!sd->ctrl_handler) + continue; + + ret = v4l2_g_ctrl(sd->ctrl_handler, &ct); + if (ret) + continue; + /* access only subdevices on same vc */ + if (ct.value >= 0 && ip->asv[ct.value].substream == + (pad_id - NR_OF_CSI2_BE_SOC_SINK_PADS)) + { + /* call function once */ + ret = cb_fn(av, sd, data); + break; + } + } + + av->enum_link_state = IPU_ISYS_LINK_STATE_DONE; +error_graph_walk_start_enum: + media_graph_walk_cleanup(graph); + mutex_unlock(&mdev->graph_mutex); + kfree(ip); + return ret; +} + +static int video_open(struct file *file) +{ + struct ipu_isys_video *av = video_drvdata(file); + int rval; + rval = v4l2_fh_open(file); if (rval) goto out_power_down; @@ -183,60 +260,17 @@ static int video_open(struct file *file) if (rval) goto out_v4l2_fh_release; - mutex_lock(&isys->mutex); - - if (isys->video_opened++) { - /* Already open */ - mutex_unlock(&isys->mutex); - return 0; - } - - ipdata = isys->pdata->ipdata; - ipu_configure_spc(adev->isp, - &ipdata->hw_variant, - IPU_CPD_PKG_DIR_ISYS_SERVER_IDX, - isys->pdata->base, isys->pkg_dir, - isys->pkg_dir_dma_addr); - - /* - * Buffers could have been left to wrong queue at last closure. - * Move them now back to empty buffer queue. - */ - ipu_cleanup_fw_msg_bufs(isys); - - if (isys->fwcom) { - /* - * Something went wrong in previous shutdown. As we are now - * restarting isys we can safely delete old context. - */ - dev_err(&isys->adev->dev, "Clearing old context\n"); - ipu_fw_isys_cleanup(isys); + if (av->enum_link_state == IPU_ISYS_LINK_STATE_ENABLED && + media_entity_remote_pad(&av->pad)) { + media_pipeline_enumerate_by_vc_cb(av, + ipu_isys_inherit_ctrls, NULL); } - rval = ipu_fw_isys_init(av->isys, ipdata->num_parallel_streams); - if (rval < 0) - goto out_lib_init; - - mutex_unlock(&isys->mutex); - return 0; -out_lib_init: - isys->video_opened--; - mutex_unlock(&isys->mutex); -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) - ipu_pipeline_pm_use(&av->vdev.entity, 0); -#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) - v4l2_pipeline_pm_use(&av->vdev.entity, 0); -#else - v4l2_pipeline_pm_put(&av->vdev.entity); -#endif - out_v4l2_fh_release: v4l2_fh_release(file); out_power_down: - pm_runtime_put(&isys->adev->dev); - return rval; } @@ -249,31 +283,6 @@ static int video_release(struct file *file) av->vdev.name); vb2_fop_release(file); - mutex_lock(&av->isys->reset_mutex); - while (av->isys->in_reset) { - mutex_unlock(&av->isys->reset_mutex); - dev_dbg(&av->isys->adev->dev, "release: %s: wait for reset\n", - av->vdev.name - ); - usleep_range(10000, 11000); - mutex_lock(&av->isys->reset_mutex); - } - mutex_unlock(&av->isys->reset_mutex); - - mutex_lock(&av->isys->mutex); - - if (!--av->isys->video_opened) { - dev_dbg(&av->isys->adev->dev, "release: %s: close fw\n", - av->vdev.name); - ipu_fw_isys_close(av->isys); - if (av->isys->fwcom) { - av->isys->reset_needed = true; - ret = -EIO; - } - } - - mutex_unlock(&av->isys->mutex); - #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) ipu_pipeline_pm_use(&av->vdev.entity, 0); #elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) @@ -281,12 +290,6 @@ static int video_release(struct file *file) #else v4l2_pipeline_pm_put(&av->vdev.entity); #endif - - if (av->isys->reset_needed) - pm_runtime_put_sync(&av->isys->adev->dev); - else - pm_runtime_put(&av->isys->adev->dev); - dev_dbg(&av->isys->adev->dev, "release: %s: exit\n", av->vdev.name); return ret; @@ -344,6 +347,11 @@ ipu_isys_get_pixelformat(struct ipu_isys_video *av, u32 pixelformat) } } + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE) + for (pfmt = av->pfmts; pfmt->bpp; pfmt++) + if (pfmt->pixelformat == pixelformat) + return pfmt; + /* Not found. Get the default, i.e. the first defined one. */ for (pfmt = av->pfmts; pfmt->bpp; pfmt++) { if (pfmt->code == *supported_codes) @@ -354,6 +362,225 @@ ipu_isys_get_pixelformat(struct ipu_isys_video *av, u32 pixelformat) return NULL; } +static int ipu_isys_get_parm_subdev(struct ipu_isys_video *av, + struct v4l2_subdev *sd, void *data) +{ + int ret = 0; + struct v4l2_subdev_frame_interval *fi = + (struct v4l2_subdev_frame_interval *)data; + ret = v4l2_subdev_call(sd, video, g_frame_interval, fi); + + return ret; +} + +static int ipu_isys_set_parm_subdev(struct ipu_isys_video *av, + struct v4l2_subdev *sd, void *data) +{ + int ret = 0; + struct v4l2_subdev_frame_interval *fi = + (struct v4l2_subdev_frame_interval *)data; + /* v4l2_subdev_call doesn't call, access directly */ + // ret = v4l2_subdev_call(sd, video, s_frame_interval, fi); + + if (sd && sd->ops && sd->ops->video && sd->ops->video->s_frame_interval) + ret = sd->ops->video->s_frame_interval(sd, fi); + + return ret; +} + +static int ipu_isys_get_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct ipu_isys_video *av = video_drvdata(file); + struct v4l2_subdev_frame_interval fi; + int ret = -ENOLINK; + + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DISABLED) + { + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 30; + + return 0; + } + + if (media_entity_remote_pad(&av->pad)) + ret = media_pipeline_enumerate_by_vc_cb(av, + ipu_isys_get_parm_subdev, &fi); + else + return -ENOLINK; + + a->parm.capture.timeperframe.numerator = fi.interval.numerator; + a->parm.capture.timeperframe.denominator = fi.interval.denominator; + + return ret; +} + +static int ipu_isys_set_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct ipu_isys_video *av = video_drvdata(file); + struct v4l2_subdev_frame_interval fi; + int ret = -ENOLINK; + + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DISABLED) + return 0; + + fi.interval.numerator = a->parm.capture.timeperframe.numerator; + fi.interval.denominator = a->parm.capture.timeperframe.denominator; + + if (media_entity_remote_pad(&av->pad)) + ret = media_pipeline_enumerate_by_vc_cb(av, + ipu_isys_set_parm_subdev, &fi); + + return ret; +} + +static int ipu_isys_enum_framesizes_subdev(struct ipu_isys_video *av, + struct v4l2_subdev *sd, void *data) +{ + int ret = 0; + struct v4l2_subdev_frame_size_enum *fse = + (struct v4l2_subdev_frame_size_enum *)data; + /* v4l2_subdev_call doesn't call, access directly */ + // ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, fse); + if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_frame_size) + ret = sd->ops->pad->enum_frame_size(sd, NULL, fse); + + return ret; +} + +static int ipu_isys_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *sizes) +{ + struct ipu_isys_video *av = video_drvdata(file); + struct media_pad *pad = other_pad(&av->vdev.entity.pads[0]); + const u32 *supported_codes; + const struct ipu_isys_pixelformat *pfmt; + u32 index; + struct v4l2_subdev_frame_size_enum fse; + struct v4l2_subdev *sd; + int ret = 0; + + if (!pad || !pad->entity) + return -EINVAL; + sd = media_entity_to_v4l2_subdev(pad->entity); + + supported_codes = to_ipu_isys_subdev(sd)->supported_codes[pad->index]; + + /* Walk the 0-terminated array for the f->index-th code. */ + for (index = sizes->index; *supported_codes && index; + index--, supported_codes++) + { + }; + + if (!*supported_codes) + return -EINVAL; + + /* Code found */ + for (pfmt = av->pfmts; pfmt->bpp; pfmt++) + if (pfmt->code == *supported_codes) + break; + + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE) { + for (pfmt = av->pfmts; pfmt->bpp; pfmt++) + if (pfmt->pixelformat == sizes->pixel_format) + break; + fse.code = pfmt->code; + fse.index = sizes->index; + ret = media_pipeline_enumerate_by_vc_cb(av, + ipu_isys_enum_framesizes_subdev, &fse); + } + else + ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); + + if (!ret && fse.max_width > 0 && fse.max_height > 0) + { + sizes->type = V4L2_FRMSIZE_TYPE_DISCRETE; + sizes->discrete.width = fse.max_width; + sizes->discrete.height = fse.max_height; + } + else + { + sizes->type = V4L2_FRMSIZE_TYPE_DISCRETE; + sizes->discrete.width = 0; + sizes->discrete.height = 0; + ret = -EINVAL; + } + return ret; +} + +static int ipu_isys_enum_frameintervals_subdev(struct ipu_isys_video *av, + struct v4l2_subdev *sd, void *data) +{ + int ret = 0; + struct v4l2_subdev_frame_interval_enum *fie = + (struct v4l2_subdev_frame_interval_enum *)data; + /* v4l2_subdev_call doesn't call, access directly */ + // ret = v4l2_subdev_call(sd, pad, enum_frame_interval, NULL, fie); + if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_frame_interval) + ret = sd->ops->pad->enum_frame_interval(sd, NULL, fie); + + return ret; +} + +static int ipu_isys_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *intervals) +{ + struct ipu_isys_video *av = video_drvdata(file); + struct media_pad *pad = other_pad(&av->vdev.entity.pads[0]); + const u32 *supported_codes; + const struct ipu_isys_pixelformat *pfmt; + int index; + struct v4l2_subdev_frame_interval_enum fie; + struct v4l2_subdev *sd; + int ret = 0; + + if (!pad || !pad->entity) + return -EINVAL; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + supported_codes = to_ipu_isys_subdev(sd)->supported_codes[pad->index]; + + /* Walk the 0-terminated array for the f->index-th code. */ + for (index = intervals->index; *supported_codes && index; + index--, supported_codes++) + { + }; + + if (!*supported_codes) + return -EINVAL; + + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE) { + for (pfmt = av->pfmts; pfmt->bpp; pfmt++) + if (pfmt->pixelformat == intervals->pixel_format) + break; + fie.code = pfmt->code; + fie.index = intervals->index; + fie.width = intervals->width; + fie.height = intervals->height; + ret = media_pipeline_enumerate_by_vc_cb(av, + ipu_isys_enum_frameintervals_subdev, &fie); + } + else + ret = v4l2_subdev_call(sd, pad, enum_frame_interval, NULL, &fie); + + if (!ret && fie.interval.numerator > 0 && fie.interval.denominator > 0) + { + intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; + intervals->discrete.numerator = fie.interval.numerator; + intervals->discrete.denominator = fie.interval.denominator; + } + else + { + intervals->type = V4L2_FRMIVAL_TYPE_DISCRETE; + intervals->discrete.numerator = 1; + intervals->discrete.denominator = 30; + ret = -EINVAL; + } + return ret; +} + int ipu_isys_vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { @@ -366,6 +593,68 @@ int ipu_isys_vidioc_querycap(struct file *file, void *fh, return 0; } +static int ipu_isys_enum_fmt_subdev(struct ipu_isys_video *av, + struct v4l2_subdev *sd, void *data) +{ + int ret = 0; + struct v4l2_subdev_mbus_code_enum *mce = + (struct v4l2_subdev_mbus_code_enum *)data; + /* v4l2_subdev_call doesn't call, access directly */ + // ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, mce); + if (sd && sd->ops && sd->ops->pad && sd->ops->pad->enum_mbus_code) + ret = sd->ops->pad->enum_mbus_code(sd, NULL, mce); + + return ret; +} + +int ipu_isys_vidioc_enum_fmt_meta(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct ipu_isys_video *av = video_drvdata(file); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) + struct media_pad *pad = + av->vdev.entity.pads[0].flags & MEDIA_PAD_FL_SOURCE ? + av->vdev.entity.links[0].sink : av->vdev.entity.links[0].source; +#else + struct media_pad *pad = other_pad(&av->vdev.entity.pads[0]); +#endif + struct v4l2_subdev *sd; + const u32 *supported_codes; + const struct ipu_isys_pixelformat *pfmt; + + if (!pad || !pad->entity) + return -EINVAL; + sd = media_entity_to_v4l2_subdev(pad->entity); + supported_codes = to_ipu_isys_subdev(sd)->supported_codes[pad->index]; + + /* Walk the 0-terminated array for the metadata code. */ + for (; *supported_codes && + *supported_codes != MEDIA_BUS_FMT_FIXED; + supported_codes++); + + supported_codes += f->index; + if (!*supported_codes) + return -EINVAL; + + f->flags = 0; + + /* Code found */ + for (pfmt = av->pfmts; pfmt->bpp; pfmt++){ + if (pfmt->code == MEDIA_BUS_FMT_FIXED) + break; + } + + if (!pfmt->bpp) { + dev_warn(&av->isys->adev->dev, + "Format not found in mapping table."); + return -EINVAL; + } + + f->pixelformat = pfmt->pixelformat; + + return 0; +} + int ipu_isys_vidioc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) { @@ -380,7 +669,9 @@ int ipu_isys_vidioc_enum_fmt(struct file *file, void *fh, struct v4l2_subdev *sd; const u32 *supported_codes; const struct ipu_isys_pixelformat *pfmt; - u32 index; + int index; + int ret; + struct v4l2_subdev_mbus_code_enum mce; if (!pad || !pad->entity) return -EINVAL; @@ -408,25 +699,51 @@ int ipu_isys_vidioc_enum_fmt(struct file *file, void *fh, return -EINVAL; } + /* poll remote sensors */ + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE) + { + mce.index = f->index; + mce.pad = 0; + ret = media_pipeline_enumerate_by_vc_cb(av, + ipu_isys_enum_fmt_subdev, &mce); + if (ret) + return -EINVAL; + for (index = 0, pfmt = av->pfmts; pfmt->bpp; pfmt++, index++) + ; + /* go from the end of format list */ + for (--index; index >= 0; index--) + { + pfmt = &av->pfmts[index]; + if (pfmt->code == mce.code) + break; + } + f->mbus_code = pfmt->code; + } + f->pixelformat = pfmt->pixelformat; return 0; } -static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh, +static int vidioc_g_fmt_meta_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct ipu_isys_video *av = video_drvdata(file); - if (fmt->type == V4L2_BUF_TYPE_META_CAPTURE) { - fmt->fmt.meta.buffersize = av->mpix.plane_fmt[0].sizeimage; - fmt->fmt.meta.bytesperline = av->mpix.plane_fmt[0].bytesperline; - fmt->fmt.meta.width = av->mpix.width; - fmt->fmt.meta.height = av->mpix.height; - fmt->fmt.meta.dataformat = av->mpix.pixelformat; + if (fmt->type != V4L2_BUF_TYPE_META_CAPTURE) + return -EINVAL; + fmt->fmt.meta.buffersize = av->mpix.plane_fmt[0].sizeimage; + fmt->fmt.meta.bytesperline = av->mpix.plane_fmt[0].bytesperline; + fmt->fmt.meta.width = av->mpix.width; + fmt->fmt.meta.height = av->mpix.height; + fmt->fmt.meta.dataformat = av->mpix.pixelformat; + return 0; +} - return 0; - } +static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct ipu_isys_video *av = video_drvdata(file); fmt->fmt.pix_mp = av->mpix; @@ -547,46 +864,139 @@ ipu_isys_video_try_fmt_vid_mplane(struct ipu_isys_video *av, return pfmt; } +static int ipu_isys_set_fmt_subdev(struct ipu_isys_video *av, + struct v4l2_subdev *sd, void *data) +{ + int ret = 0; + struct v4l2_subdev_format *fmt = + (struct v4l2_subdev_format *)data; + ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, fmt); + + return ret; +} + static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh, struct v4l2_format *f) { struct ipu_isys_video *av = video_drvdata(file); - struct v4l2_pix_format_mplane mpix; + struct media_pad *source_pad = media_entity_remote_pad(&av->pad); + struct media_pad *remote_pad = source_pad; + struct v4l2_subdev *sd = NULL; if (av->aq.vbq.streaming) return -EBUSY; - if (f->type == V4L2_BUF_TYPE_META_CAPTURE) { - memset(&av->mpix, 0, sizeof(av->mpix)); - memset(&mpix, 0, sizeof(mpix)); - mpix.width = f->fmt.meta.width; - mpix.height = f->fmt.meta.height; - mpix.pixelformat = f->fmt.meta.dataformat; - av->pfmt = av->try_fmt_vid_mplane(av, &mpix); - av->aq.vbq.type = V4L2_BUF_TYPE_META_CAPTURE; - av->aq.vbq.is_multiplanar = false; - av->aq.vbq.is_output = false; - av->mpix = mpix; - f->fmt.meta.width = mpix.width; - f->fmt.meta.height = mpix.height; - f->fmt.meta.dataformat = mpix.pixelformat; - f->fmt.meta.bytesperline = mpix.plane_fmt[0].bytesperline; - f->fmt.meta.buffersize = mpix.plane_fmt[0].sizeimage; - return 0; - } av->pfmt = av->try_fmt_vid_mplane(av, &f->fmt.pix_mp); av->mpix = f->fmt.pix_mp; + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE) + { + int ret = 0; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = 0, + }; + struct v4l2_mbus_framefmt format = { + .width = av->mpix.width, + .height = av->mpix.height, + .code = av->pfmt->code, + .field = av->mpix.field, + .colorspace = av->mpix.colorspace, + .ycbcr_enc = av->mpix.ycbcr_enc, + .quantization = av->mpix.quantization, + .xfer_func = av->mpix.xfer_func, + }; + fmt.format = format; + ret = media_pipeline_enumerate_by_vc_cb(av, ipu_isys_set_fmt_subdev, &fmt); + if (ret) + return -EINVAL; + + /* set format for CSI-2 and CSI2 BE SOC */ + do { + /* Non-subdev nodes can be safely ignored here. */ + if (!is_media_entity_v4l2_subdev(remote_pad->entity)) + continue; + /* Set only for IPU6 CSI entities */ + if ((strncmp(remote_pad->entity->name, + IPU_ISYS_ENTITY_PREFIX " CSI", + strlen(IPU_ISYS_ENTITY_PREFIX " CSI")) != 0)){ + dev_dbg(remote_pad->entity->graph_obj.mdev->dev, + "It finds: %s will set and break\n", remote_pad->entity->name); + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + ret = ipu_isys_set_fmt_subdev(av, sd, &fmt); + break; + } + dev_dbg(remote_pad->entity->graph_obj.mdev->dev, + "It finds: %s\n", remote_pad->entity->name); + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + ret = ipu_isys_set_fmt_subdev(av, sd, &fmt); + + if (ret) + return -EINVAL; + } while ((remote_pad = + media_entity_remote_pad(&remote_pad->entity->pads[0]))); + } return 0; } -static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh, - struct v4l2_format *f) +static int vidioc_s_fmt_meta_cap(struct file *file, void *fh, + struct v4l2_format *f) { struct ipu_isys_video *av = video_drvdata(file); + struct v4l2_pix_format_mplane mpix; + + if (av->aq.vbq.streaming) + return -EBUSY; - av->try_fmt_vid_mplane(av, &f->fmt.pix_mp); + if (f->type != V4L2_BUF_TYPE_META_CAPTURE) + return -EINVAL; + memset(&av->mpix, 0, sizeof(av->mpix)); + memset(&mpix, 0, sizeof(mpix)); + mpix.width = f->fmt.meta.width; + mpix.height = f->fmt.meta.height; + mpix.pixelformat = f->fmt.meta.dataformat; + av->pfmt = av->try_fmt_vid_mplane(av, &mpix); + av->aq.vbq.type = V4L2_BUF_TYPE_META_CAPTURE; + av->aq.vbq.is_multiplanar = false; + av->aq.vbq.is_output = false; + av->mpix = mpix; + f->fmt.meta.width = mpix.width; + f->fmt.meta.height = mpix.height; + f->fmt.meta.dataformat = mpix.pixelformat; + f->fmt.meta.bytesperline = mpix.plane_fmt[0].bytesperline; + f->fmt.meta.buffersize = mpix.plane_fmt[0].sizeimage; + return 0; +} +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct ipu_isys_video *av = video_drvdata(file); + const struct ipu_isys_pixelformat *pfmt = + av->try_fmt_vid_mplane(av, &f->fmt.pix_mp); + + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE) + { + int ret = 0; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = 0, + }; + struct v4l2_mbus_framefmt format = { + .width = f->fmt.pix_mp.width, + .height = f->fmt.pix_mp.height, + .code = pfmt->code, + .field = f->fmt.pix_mp.field, + .colorspace = f->fmt.pix_mp.colorspace, + .ycbcr_enc = f->fmt.pix_mp.ycbcr_enc, + .quantization = f->fmt.pix_mp.quantization, + .xfer_func = f->fmt.pix_mp.xfer_func, + }; + fmt.format = format; + ret = media_pipeline_enumerate_by_vc_cb(av, ipu_isys_set_fmt_subdev, &fmt); + if (ret) + return -EINVAL; + } return 0; } @@ -663,12 +1073,16 @@ static int link_validate(struct media_link *link) struct ipu_isys_pipeline *ip = to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); struct v4l2_subdev *sd; + struct media_pad *source_pad = media_entity_remote_pad(&av->pad); + struct v4l2_subdev_format fmt = { 0 }; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; WARN_ON(!ip); if (!link->source->entity) return -EINVAL; sd = media_entity_to_v4l2_subdev(link->source->entity); + if (is_external(av, link->source->entity)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 0, 0) ip->external = media_entity_remote_pad(av->vdev.entity.pads); @@ -680,6 +1094,20 @@ static int link_validate(struct media_link *link) ip->nr_queues++; + /* set format for "CSI2 BE SOC" specific pad + * to be "BE SOC capture" av node format. + */ + fmt.format.width = av->mpix.width; + fmt.format.height = av->mpix.height; + fmt.format.code = av->pfmt->code; + fmt.format.field = av->mpix.field; + fmt.format.colorspace = av->mpix.colorspace; + fmt.format.ycbcr_enc = av->mpix.ycbcr_enc; + fmt.format.quantization = av->mpix.quantization; + fmt.format.xfer_func = av->mpix.xfer_func; + fmt.pad = source_pad->index; + v4l2_subdev_call(sd, pad, set_fmt, NULL, &fmt); + return 0; } @@ -1079,7 +1507,7 @@ static int ipu_isys_query_sensor_info(struct media_pad *source_pad, (pad_id - NR_OF_CSI2_BE_SOC_SINK_PADS)) { ip->vc = ip->asv[qm.index].vc; flag = true; - pr_info("The current entity vc:id:%d\n", ip->vc); + // pr_info("The current entityvc:id:%d for: %s\n", ip->vc, sd->name); } dev_dbg(source_pad->entity->graph_obj.mdev->dev, "dentity vc:%d, dt:%x, substream:%d\n", @@ -1597,8 +2025,7 @@ ipu_isys_prepare_fw_cfg_default(struct ipu_isys_video *av, BITS_PER_BYTE), av->isys->line_align); - if (input_pin_info->dt == IPU_ISYS_MIPI_CSI2_TYPE_EMBEDDED8 || - input_pin_info->dt == IPU_ISYS_MIPI_CSI2_TYPE_RGB888) + if (input_pin_info->dt == IPU_ISYS_MIPI_CSI2_TYPE_EMBEDDED8) pin_info->pt = IPU_FW_ISYS_PIN_TYPE_MIPI; else pin_info->pt = aq->css_pin_type; @@ -1849,6 +2276,7 @@ static int start_stream_firmware(struct ipu_isys_video *av, if (rval < 0) { dev_err(dev, "can't open stream (%d)\n", rval); ipu_put_fw_mgs_buf(av->isys, (uintptr_t)stream_cfg); + rval = -EIO; goto out_put_stream_handle; } @@ -2214,6 +2642,9 @@ int ipu_isys_video_set_streaming(struct ipu_isys_video *av, } else { v4l2_subdev_call(esd, video, s_stream, state); } + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE || \ + av->enum_link_state == IPU_ISYS_LINK_STATE_MD) + rval = v4l2_subdev_call(esd, video, s_stream, state); } mutex_lock(&mdev->graph_mutex); @@ -2286,6 +2717,9 @@ int ipu_isys_video_set_streaming(struct ipu_isys_video *av, } if (rval) goto out_media_entity_stop_streaming_firmware; + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE || \ + av->enum_link_state == IPU_ISYS_LINK_STATE_MD) + rval = v4l2_subdev_call(esd, video, s_stream, state); } else { close_streaming_firmware(av); av->ip.vc = INVALIA_VC_ID; @@ -2363,9 +2797,9 @@ static const struct v4l2_ioctl_ops ioctl_ops_mplane = { .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane, .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane, .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, - .vidioc_enum_fmt_meta_cap = ipu_isys_vidioc_enum_fmt, - .vidioc_g_fmt_meta_cap = vidioc_g_fmt_vid_cap_mplane, - .vidioc_s_fmt_meta_cap = vidioc_s_fmt_vid_cap_mplane, + .vidioc_enum_fmt_meta_cap = ipu_isys_vidioc_enum_fmt_meta, + .vidioc_g_fmt_meta_cap = vidioc_g_fmt_meta_cap, + .vidioc_s_fmt_meta_cap = vidioc_s_fmt_meta_cap, .vidioc_try_fmt_meta_cap = vidioc_try_fmt_vid_cap_mplane, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -2380,6 +2814,10 @@ static const struct v4l2_ioctl_ops ioctl_ops_mplane = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, + .vidioc_g_parm = ipu_isys_get_parm, + .vidioc_s_parm = ipu_isys_set_parm, + .vidioc_enum_framesizes = ipu_isys_enum_framesizes, + .vidioc_enum_frameintervals = ipu_isys_enum_frameintervals, }; static const struct media_entity_operations entity_ops = { @@ -2398,6 +2836,37 @@ static const struct v4l2_file_operations isys_fops = { .release = video_release, }; +static int ipu_isys_video_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct ipu_isys_video *av = ctrl->priv; + struct ipu_isys *isys = av->isys; + mutex_lock(&isys->mutex); + + switch (ctrl->id) { + case V4L2_CID_IPU_ENUMERATE_LINK: + av->enum_link_state = ctrl->val; + break; + } + + mutex_unlock(&isys->mutex); + return 0; +} + +static const struct v4l2_ctrl_ops ipu_isys_video_ctrl_ops = { + .s_ctrl = ipu_isys_video_s_ctrl, +}; + +static const struct v4l2_ctrl_config ipu_isys_video_enum_link = { + .ops = &ipu_isys_video_ctrl_ops, + .id = V4L2_CID_IPU_ENUMERATE_LINK, + .name = "Enumerate graph link", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = IPU_ISYS_LINK_STATE_MAX, + .step = 1, + .def = 0, +}; + /* * Do everything that's needed to initialise things related to video * buffer queue, video node, and the related media entity. The caller @@ -2484,6 +2953,10 @@ int ipu_isys_video_init(struct ipu_isys_video *av, } av->pfmt = av->try_fmt_vid_mplane(av, &av->mpix); + /* create controls */ + if (av->vdev.ctrl_handler) { + v4l2_ctrl_new_custom(&av->ctrl_handler, &ipu_isys_video_enum_link, av); + } mutex_unlock(&av->mutex); diff --git a/drivers/media/pci/intel/ipu-isys-video.h b/drivers/media/pci/intel/ipu-isys-video.h index 013c3027317d..d5436cb1ed60 100644 --- a/drivers/media/pci/intel/ipu-isys-video.h +++ b/drivers/media/pci/intel/ipu-isys-video.h @@ -146,6 +146,7 @@ struct ipu_isys_video { unsigned int ts_offsets[VIDEO_MAX_PLANES]; unsigned int line_header_length; /* bits */ unsigned int line_footer_length; /* bits */ + unsigned int enum_link_state; /* state for link enumeration by vc */ const struct ipu_isys_pixelformat * (*try_fmt_vid_mplane)(struct ipu_isys_video *av, diff --git a/drivers/media/pci/intel/ipu-psys.c b/drivers/media/pci/intel/ipu-psys.c index 2e40849dfb72..2672996c025b 100644 --- a/drivers/media/pci/intel/ipu-psys.c +++ b/drivers/media/pci/intel/ipu-psys.c @@ -1842,6 +1842,6 @@ MODULE_AUTHOR("Zaikuo Wang "); MODULE_AUTHOR("Yunliang Ding "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Intel ipu processing system driver"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 71) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) || LINUX_VERSION_CODE == KERNEL_VERSION(5, 15, 71) MODULE_IMPORT_NS(DMA_BUF); #endif diff --git a/include/uapi/linux/ipu-isys.h b/include/uapi/linux/ipu-isys.h index f5bb12c16991..94bd27d8eb8d 100644 --- a/include/uapi/linux/ipu-isys.h +++ b/include/uapi/linux/ipu-isys.h @@ -12,6 +12,8 @@ #define V4L2_CID_IPU_QUERY_SUB_STREAM (V4L2_CID_IPU_BASE + 4) #define V4L2_CID_IPU_SET_SUB_STREAM (V4L2_CID_IPU_BASE + 5) +#define V4L2_CID_IPU_ENUMERATE_LINK (V4L2_CID_IPU_BASE + 6) + #define VIDIOC_IPU_GET_DRIVER_VERSION \ _IOWR('v', BASE_VIDIOC_PRIVATE + 3, uint32_t)