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 92fb5ecce2fb..26fad0682b89 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c +++ b/drivers/media/pci/intel/ipu-isys-csi2-be-soc.c @@ -25,6 +25,7 @@ static const u32 csi2_be_soc_supported_codes_pad[] = { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_VYUY8_1X16, MEDIA_BUS_FMT_SBGGR12_1X12, MEDIA_BUS_FMT_SGBRG12_1X12, MEDIA_BUS_FMT_SGRBG12_1X12, diff --git a/drivers/media/pci/intel/ipu-isys-csi2.c b/drivers/media/pci/intel/ipu-isys-csi2.c index 0582388ca04d..fc00fa25babc 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2.c +++ b/drivers/media/pci/intel/ipu-isys-csi2.c @@ -25,6 +25,7 @@ static const u32 csi2_supported_codes_pad_sink[] = { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_VYUY8_1X16, MEDIA_BUS_FMT_YUYV10_1X20, MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, @@ -52,6 +53,7 @@ static const u32 csi2_supported_codes_pad_source[] = { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_YUYV8_1X16, + MEDIA_BUS_FMT_VYUY8_1X16, MEDIA_BUS_FMT_YUYV10_1X20, MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, diff --git a/drivers/media/pci/intel/ipu-isys-queue.c b/drivers/media/pci/intel/ipu-isys-queue.c index 9b5a043fe8f0..6cfdd2851aa0 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 @@ -611,7 +612,7 @@ static void buf_queue(struct vb2_buffer *vb) "error: buffer list get failed\n"); WARN_ON(1); } else { - dev_info(&av->isys->adev->dev, + dev_dbg(&av->isys->adev->dev, "not enough buffers available\n"); } goto out; @@ -818,7 +819,7 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) } mutex_unlock(&av->isys->stream_mutex); - + if (!count) { // link validation fail if we restart stream triggered by fw rval = aq->link_fmt_validate(aq); if (rval) { dev_err(&av->isys->adev->dev, @@ -826,6 +827,7 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) 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); @@ -894,12 +896,136 @@ 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) { + 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) { @@ -914,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; } @@ -923,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) @@ -943,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)) { @@ -1182,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-subdev.c b/drivers/media/pci/intel/ipu-isys-subdev.c index 43c6ee0706c1..f9e40533f652 100644 --- a/drivers/media/pci/intel/ipu-isys-subdev.c +++ b/drivers/media/pci/intel/ipu-isys-subdev.c @@ -23,6 +23,7 @@ unsigned int ipu_isys_mbus_code_to_bpp(u32 code) case MEDIA_BUS_FMT_RGB565_1X16: case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: return 16; case MEDIA_BUS_FMT_SBGGR12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: @@ -34,6 +35,7 @@ unsigned int ipu_isys_mbus_code_to_bpp(u32 code) case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: return 10; + case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: @@ -60,6 +62,7 @@ unsigned int ipu_isys_mbus_code_to_mipi(u32 code) return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_10; case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: return IPU_ISYS_MIPI_CSI2_TYPE_YUV422_8; case MEDIA_BUS_FMT_SBGGR12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: @@ -72,6 +75,7 @@ unsigned int ipu_isys_mbus_code_to_mipi(u32 code) case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: return IPU_ISYS_MIPI_CSI2_TYPE_RAW10; + case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_SBGGR8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: diff --git a/drivers/media/pci/intel/ipu-isys-video.c b/drivers/media/pci/intel/ipu-isys-video.c index faf6e47985f7..1047e505a460 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, @@ -80,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}, {} }; @@ -90,10 +92,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, @@ -142,30 +140,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; @@ -180,60 +259,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; + 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); } - 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); - } - - 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; } @@ -246,31 +282,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) @@ -278,12 +289,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; @@ -341,6 +346,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) @@ -351,6 +361,224 @@ 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) { @@ -363,6 +591,20 @@ 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(struct file *file, void *fh, struct v4l2_fmtdesc *f) { @@ -377,7 +619,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; @@ -405,6 +649,27 @@ 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; @@ -534,10 +799,26 @@ 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); + // if (sd && sd->ops && sd->ops->pad && sd->ops->pad->set_fmt) + // ret = sd->ops->pad->set_fmt(sd, 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 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; @@ -545,6 +826,53 @@ static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh, 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; } @@ -552,9 +880,31 @@ 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); - - av->try_fmt_vid_mplane(av, &f->fmt.pix_mp); - + 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; } @@ -1496,7 +1846,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 entityvc:id:%d\n", ip->vc); + // pr_info("The current entityvc:id:%d for: %s\n", ip->vc, sd->name); } } @@ -1506,6 +1856,81 @@ static int ipu_isys_query_sensor_info(struct media_pad *source_pad, return ret; } +static int media_pipeline_update_fmt(struct ipu_isys_video *av) +{ + struct media_pad *source_pad = media_entity_remote_pad(&av->pad); + struct media_pad *remote_pad = source_pad; + struct v4l2_subdev *sd = NULL; + 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, + }; + + if (av->aq.vbq.streaming) + return -EBUSY; + + fmt.format = format; + + /* 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)) { + struct v4l2_ext_control c = { + .id = V4L2_CID_IPU_SET_SUB_STREAM, + }; + struct v4l2_ext_controls cs = {.count = 1, + .controls = &c, + }; + dev_dbg(remote_pad->entity->graph_obj.mdev->dev, + "%s():%d %s, switch sensor and complete\n", + __func__, __LINE__, + remote_pad->entity->name); + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + c.value64 = SUB_STREAM_SET_VALUE(av->ip.vc, 0xff); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) + v4l2_s_ext_ctrls(NULL, sd->ctrl_handler, + sd->devnode, + sd->v4l2_dev->mdev, + &cs); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + v4l2_s_ext_ctrls(NULL, sd->ctrl_handler, + sd->v4l2_dev->mdev, + &cs); +#endif + break; + } + + dev_dbg(remote_pad->entity->graph_obj.mdev->dev, + "%s():%d WE finds: %s\n", + __func__, __LINE__, + 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 media_pipeline_walk_by_vc(struct ipu_isys_video *av, struct media_pipeline *pipe) { @@ -1548,6 +1973,15 @@ static int media_pipeline_walk_by_vc(struct ipu_isys_video *av, if (ret) goto error_graph_walk_start; } + /* + * Update media link format according to capture format + * This needed as for entities CSI2 BE SOC source pad and CSI-2 sink + * and source pads have single link point while BE-SOC sink + * and external entities has multiple source pads. + */ + if (av->enum_link_state == IPU_ISYS_LINK_STATE_DONE || \ + av->enum_link_state == IPU_ISYS_LINK_STATE_MD) + media_pipeline_update_fmt(av); #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) media_graph_walk_start(&pipe->graph, entity_enum); @@ -1894,6 +2328,9 @@ int ipu_isys_video_set_streaming(struct ipu_isys_video *av, esd->v4l2_dev->mdev, &cs); #endif + 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); @@ -1962,6 +2399,9 @@ int ipu_isys_video_set_streaming(struct ipu_isys_video *av, #endif 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; @@ -2052,6 +2492,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 = { @@ -2070,6 +2514,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 @@ -2155,6 +2630,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-isys.c b/drivers/media/pci/intel/ipu-isys.c index 23ee60bd20d6..217c58e5da47 100644 --- a/drivers/media/pci/intel/ipu-isys.c +++ b/drivers/media/pci/intel/ipu-isys.c @@ -422,6 +422,57 @@ static int isys_register_ext_subdev(struct ipu_isys *isys, return rval; } +static int isys_unregister_ext_subdev(struct ipu_isys *isys, + struct ipu_isys_subdev_info *sd_info) +{ + struct i2c_adapter *adapter; + struct i2c_client *client; + int rval; + int bus; + + bus = ipu_get_i2c_bus_id(sd_info->i2c.i2c_adapter_id, + sd_info->i2c.i2c_adapter_bdf, + sizeof(sd_info->i2c.i2c_adapter_bdf)); + if (bus < 0) { + dev_err(&isys->adev->dev, + "getting i2c bus id for adapter %d (bdf %s) failed", + sd_info->i2c.i2c_adapter_id, + sd_info->i2c.i2c_adapter_bdf); + return -ENOENT; + } + dev_info(&isys->adev->dev, + "got i2c bus id %d for adapter %d (bdf %s)", bus, + sd_info->i2c.i2c_adapter_id, + sd_info->i2c.i2c_adapter_bdf); + adapter = i2c_get_adapter(bus); + if (!adapter) { + dev_warn(&isys->adev->dev, "can't find adapter\n"); + return -ENOENT; + } + + dev_info(&isys->adev->dev, + "unregister i2c subdev for %s (address %2.2x, bus %d)", + sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, + bus); + + client = isys_find_i2c_subdev(adapter, sd_info); + if (!client) { + dev_dbg(&isys->adev->dev, "Device not exists\n"); + rval = 0; + goto skip_put_adapter; + } + + i2c_unregister_device(client); + i2c_put_adapter(adapter); + + return 0; + +skip_put_adapter: + i2c_put_adapter(adapter); + + return rval; +} + static void isys_register_ext_subdevs(struct ipu_isys *isys) { struct ipu_isys_subdev_pdata *spdata = isys->pdata->spdata; @@ -434,6 +485,19 @@ static void isys_register_ext_subdevs(struct ipu_isys *isys) for (sd_info = spdata->subdevs; *sd_info; sd_info++) isys_register_ext_subdev(isys, *sd_info); } + +static void isys_unregister_ext_subdevs(struct ipu_isys *isys) +{ + struct ipu_isys_subdev_pdata *spdata = isys->pdata->spdata; + struct ipu_isys_subdev_info **sd_info; + + if (!spdata) { + dev_info(&isys->adev->dev, "no subdevice info provided\n"); + return; + } + for (sd_info = spdata->subdevs; *sd_info; sd_info++) + isys_unregister_ext_subdev(isys, *sd_info); +} #endif static void isys_unregister_subdevices(struct ipu_isys *isys) @@ -793,6 +857,9 @@ static int isys_register_devices(struct ipu_isys *isys) static void isys_unregister_devices(struct ipu_isys *isys) { isys_unregister_subdevices(isys); +#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU_USE_PLATFORMDATA) + isys_unregister_ext_subdevs(isys); +#endif v4l2_device_unregister(&isys->v4l2_dev); media_device_unregister(&isys->media_dev); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) diff --git a/drivers/media/pci/intel/ipu-psys.c b/drivers/media/pci/intel/ipu-psys.c index ca2830e5dc7e..f804b3bc7a4a 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)