From 0816ac6fc28a030d52b839ec864287f54aac0f1b 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 - dual link external entity support for aggregated streaming - 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. - dual link support - 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. - ipu-isys.c: debugfs create subdevices dynamically - ipu6-acpi-pdata.c: fix suffix to match port number Signed-off-by: Dmitry Perchanov --- .../media/pci/intel/ipu-isys-csi2-be-soc.c | 31 + drivers/media/pci/intel/ipu-isys-csi2.c | 32 + drivers/media/pci/intel/ipu-isys-queue.c | 202 +++- drivers/media/pci/intel/ipu-isys-video.c | 883 ++++++++++++++---- drivers/media/pci/intel/ipu-isys-video.h | 1 + drivers/media/pci/intel/ipu-isys.c | 136 ++- drivers/media/pci/intel/ipu-psys.c | 2 +- .../media/platform/intel/ipu6-acpi-pdata.c | 2 +- include/uapi/linux/ipu-isys.h | 2 + 9 files changed, 1103 insertions(+), 188 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 4a3d15cc52dd..3bee7917aab1 100644 --- a/drivers/media/pci/intel/ipu-isys-csi2.c +++ b/drivers/media/pci/intel/ipu-isys-csi2.c @@ -333,6 +333,8 @@ 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 = 0; if (!link->sink->entity || !link->source->entity) return -EINVAL; @@ -343,13 +345,43 @@ static int csi2_link_validate(struct media_link *link) to_ipu_isys_csi2(media_entity_to_v4l2_subdev(link->sink->entity)); ip = to_ipu_isys_pipeline(media_pipe); + if (!ip) + return -EINVAL; + if (ip->external && ip->external->entity) { + if (ip->external == link->source) { + dev_dbg(&csi2->isys->adev->dev, + "%s:%d: ip external entity: %s link source: %s ip->vc: %d proceed with validation\n", + __func__, __LINE__, ip->external->entity->name, link->source->entity->name, ip->vc); + } else { + dev_dbg(&csi2->isys->adev->dev, + "%s:%d: ip external entity: %s link source: %s ip->vc: %d skip validation\n", + __func__, __LINE__, ip->external->entity->name, link->source->entity->name, ip->vc); + return 0; + } + } csi2->receiver_errors = 0; ip->csi2 = csi2; 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); + if (rval) + return rval; + + /* set csi2 format for the same as external entity */ + rval = v4l2_subdev_call(sink_sd, pad, set_fmt, NULL, &fmt); + if (rval) + return rval; + + 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 1dc00a73cfa2..cb9b9f748b5b 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 @@ -322,6 +323,10 @@ static int buffer_list_get(struct ipu_isys_pipeline *ip, if (list_empty(&aq->incoming)) { spin_unlock_irqrestore(&aq->lock, flags); ret = -ENODATA; + dev_dbg(&ip->isys->adev->dev, + "%s:%d %s: list_empty(&aq->incoming) ENODATA\n", + __func__, __LINE__, + container_of(ip, struct ipu_isys_video, ip)->vdev.name); goto error; } @@ -548,8 +553,8 @@ static void buf_queue(struct vb2_buffer *vb) unsigned int i; int rval; - dev_dbg(&av->isys->adev->dev, "buffer: %s: buf_queue %u\n", - av->vdev.name, + dev_dbg(&av->isys->adev->dev, "buffer: %s: %8.8llx, buf_queue %u\n", + av->vdev.name, vb2_dma_contig_plane_dma_addr(vb, 0), #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) vb->v4l2_buf.index #else @@ -558,8 +563,8 @@ static void buf_queue(struct vb2_buffer *vb) ); for (i = 0; i < vb->num_planes; i++) - dev_dbg(&av->isys->adev->dev, "iova: plane %u iova 0x%x\n", i, - (u32)vb2_dma_contig_plane_dma_addr(vb, i)); + dev_dbg(&av->isys->adev->dev, "iova: plane %u iova 0x%x vaddr:0x%p\n", i, + (u32)vb2_dma_contig_plane_dma_addr(vb, i), vb2_plane_vaddr(vb,i)); spin_lock_irqsave(&aq->lock, flags); list_add(&ib->head, &aq->incoming); @@ -612,7 +617,7 @@ static void buf_queue(struct vb2_buffer *vb) WARN_ON(1); } else { dev_dbg(&av->isys->adev->dev, - "not enough buffers available\n"); + "not enough buffers available rval: %d\n", rval); } goto out; } @@ -683,6 +688,9 @@ int ipu_isys_link_fmt_validate(struct ipu_isys_queue *aq) fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; fmt.pad = pad->index; + dev_dbg(&av->isys->adev->dev, + "%s:%d: sd->name: %s pad->index: %d \n", + __func__, __LINE__, sd->name, pad->index); rval = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); if (rval) return rval; @@ -797,7 +805,7 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) int rval; dev_dbg(&av->isys->adev->dev, - "stream: %s: width %u, height %u, css pixelformat %u\n", + "stream: %s: width %u, height %u, css pixelformat 0x%x\n", av->vdev.name, av->mpix.width, av->mpix.height, av->pfmt->css_pixelformat); @@ -817,12 +825,28 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) mutex_unlock(&av->isys->stream_mutex); + dev_dbg(&av->isys->adev->dev, + "%s: first:%d, css pixelformat 0x%x\n", + av->vdev.name, first, av->pfmt->css_pixelformat); + + if (first && 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) { mutex_unlock(&av->mutex); mutex_lock(&pipe_av->mutex); } + dev_dbg(&av->isys->adev->dev, + "%s: nr_streaming:%d->%d, nr_queues:%d, streaming:%d!\n", + av->vdev.name,ip->nr_streaming,ip->nr_streaming+1,ip->nr_queues,ip->streaming); ip->nr_streaming++; dev_dbg(&av->isys->adev->dev, "queue %u of %u\n", ip->nr_streaming, @@ -830,7 +854,7 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) list_add(&aq->node, &ip->queues); if (ip->nr_streaming != ip->nr_queues) { dev_dbg(&av->isys->adev->dev, - "%s: streaming queue not match (%d)(%d)\n", + "%s: streaming queue not match nr_streaming(%d) nr_queues(%d) out..\n", av->vdev.name, ip->nr_streaming, ip->nr_queues); goto out; } @@ -844,7 +868,7 @@ static int __start_streaming(struct vb2_queue *q, unsigned int count) goto out_stream_start; } else if (rval < 0) { dev_dbg(&av->isys->adev->dev, - "no request available, postponing streamon\n"); + "no request available, postponing streamon %d\n", rval); goto out; } } @@ -872,6 +896,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 +908,146 @@ 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; + + if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE) { + return 0; + } + + 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; + + if (av->aq.vbq.type == V4L2_BUF_TYPE_META_CAPTURE) { + return 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_dbg(&isys->adev->dev, "%s:%d %s: close fw video_opened: %d->%d\n", + __func__, __LINE__, av->vdev.name, isys->video_opened, isys->video_opened-1); + 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 +1062,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; } @@ -913,7 +1073,8 @@ static void reset_stop_streaming(struct ipu_isys_video *av) to_ipu_isys_pipeline(media_entity_pipeline(&av->vdev.entity)); 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) @@ -939,7 +1100,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)) { @@ -947,6 +1109,10 @@ static int reset_start_streaming(struct ipu_isys_video *av) struct ipu_isys_buffer, head); + struct vb2_buffer *vb = ipu_isys_buffer_to_vb2_buffer(ib); + dev_dbg(&av->isys->adev->dev, "buffer: %s: %8.8llx, vaddr: %p moved from active to incoming\n", + av->vdev.name, vb2_dma_contig_plane_dma_addr(vb, 0), vb2_plane_vaddr(vb,0)); + list_del(&ib->head); list_add_tail(&ib->head, &aq->incoming); @@ -1180,19 +1346,20 @@ static void stop_streaming(struct vb2_queue *q) } return_buffers(aq, VB2_BUF_STATE_ERROR); - if (av->isys->reset_needed) + if (av->isys->reset_needed) { if (!ip->nr_streaming) ipu_isys_reset(av, ip); else av->isys->reset_needed = 0; - + } dev_dbg(&av->isys->adev->dev, "stop: %s: exit\n", av->vdev.name); 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 @@ -1337,8 +1504,9 @@ void ipu_isys_queue_buf_ready(struct ipu_isys_pipeline *ip, struct vb2_v4l2_buffer *buf; #endif - dev_dbg(&isys->adev->dev, "buffer: %s: received buffer %8.8x\n", - ipu_isys_queue_to_video(aq)->vdev.name, info->pin.addr); + dev_dbg(&isys->adev->dev, + "ipu_isys_queue_buf_ready: buffer: %s: received buffer %8.8x, pin_id %d\n", + ipu_isys_queue_to_video(aq)->vdev.name, info->pin.addr, info->pin_id); spin_lock_irqsave(&aq->lock, flags); if (list_empty(&aq->active)) { @@ -1355,7 +1523,7 @@ void ipu_isys_queue_buf_ready(struct ipu_isys_pipeline *ip, if (info->pin.addr != addr) { if (first) - dev_err(&isys->adev->dev, + dev_dbg(&isys->adev->dev, "WARN: buffer address %pad expected!\n", &addr); first = false; diff --git a/drivers/media/pci/intel/ipu-isys-video.c b/drivers/media/pci/intel/ipu-isys-video.c index 553fa7513b8b..18b1d823bd3a 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,113 @@ 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, + "%s:%d no remote pad found\n", + __func__, __LINE__); + 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 +262,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 +285,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 +292,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 +349,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 +364,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 +595,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 +671,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 +701,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 +866,151 @@ 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; + struct ipu_isys_pipeline *ip; 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; + ip = &av->ip; + if (!(ip && ip->external && ip->external->entity)) { + ret = ipu_isys_query_sensor_info(source_pad, ip); + 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 for %d will set and break\n", + remote_pad->entity->name,source_pad->index); + if(ip->external && ip->external->entity) { + sd = media_entity_to_v4l2_subdev(ip->external->entity); + } else { + sd = media_entity_to_v4l2_subdev(remote_pad->entity); + } + ret = ipu_isys_set_fmt_subdev(av, sd, &fmt); + if (ret) + return -EINVAL; + } + dev_dbg(remote_pad->entity->graph_obj.mdev->dev, + "It finds: %s for %d\n", remote_pad->entity->name, source_pad->index); + 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 +1087,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); @@ -677,9 +1105,28 @@ static int link_validate(struct media_link *link) #endif ip->source = to_ipu_isys_subdev(sd)->source; } + if (ip->external && ip->external->entity) + dev_dbg(&av->isys->adev->dev, + "%s:%d: sd:%s ip->external: %s ip->vc: %d ip->nr_queues:%d->%d\n", + __func__, __LINE__, sd->name, ip->external->entity->name, + ip->vc, ip->nr_queues, ip->nr_queues+1); 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; } @@ -1015,7 +1462,7 @@ static int ipu_isys_query_sensor_info(struct media_pad *source_pad, struct media_pad *extern_pad = NULL; struct v4l2_subdev *sd = NULL; struct v4l2_querymenu qm = {.id = V4L2_CID_IPU_QUERY_SUB_STREAM, }; - + struct media_link *link; while ((remote_pad = #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 0, 0) media_entity_remote_pad(&remote_pad->entity->pads[0]) @@ -1056,6 +1503,22 @@ static int ipu_isys_query_sensor_info(struct media_pad *source_pad, "It doesn't find extern entity\n"); return -ENOLINK; } + /* scan enabled link external entities */ + list_for_each_entry(link, &remote_pad->entity->links, list) { + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + if(!link->source) + continue; + if (strncmp(link->source->entity->name, + IPU_ISYS_CSI2_ENTITY_PREFIX, + strlen(IPU_ISYS_CSI2_ENTITY_PREFIX)) == 0) + continue; + sd = media_entity_to_v4l2_subdev(link->source->entity); + if (!sd) { + dev_dbg(source_pad->entity->graph_obj.mdev->dev, + "It doesn't find extern entity\n"); + return -ENOLINK; + } /* Get the sub stream info and set the current pipe's vc id */ for (i = 0; i < CSI2_BE_SOC_SOURCE_PADS_NUM; i++) { @@ -1079,7 +1542,9 @@ 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); + ip->external = link->source; + pr_debug("The current entityvc:id:%d pad_id: %d, substream %d, for: %s, set ip->external\n", + ip->vc, pad_id, ip->asv[qm.index].substream, sd->name); } dev_dbg(source_pad->entity->graph_obj.mdev->dev, "dentity vc:%d, dt:%x, substream:%d\n", @@ -1089,6 +1554,7 @@ static int ipu_isys_query_sensor_info(struct media_pad *source_pad, if (flag) return 0; + } return ret; } @@ -1118,12 +1584,15 @@ static int media_pipeline_walk_by_vc(struct ipu_isys_video *av, int entity_vc = INVALIA_VC_ID; u32 n; bool is_vc = false; + unsigned int av_pad_id; if (!source_pad) { - dev_err(entity->graph_obj.mdev->dev, "no remote pad found\n"); + dev_err(entity->graph_obj.mdev->dev, + "%s:%d no remote pad found\n", + __func__, __LINE__); return ret; } - + av_pad_id = source_pad->index; is_vc = is_support_vc(source_pad, ip); if (is_vc) { ret = ipu_isys_query_sensor_info(source_pad, ip); @@ -1186,7 +1655,7 @@ static int media_pipeline_walk_by_vc(struct ipu_isys_video *av, #endif if (!source_pad) { dev_warn(entity->graph_obj.mdev->dev, - "no remote pad found\n"); + "%s:%d no remote pad found\n", __func__, __LINE__); continue; } pad_id = source_pad->index; @@ -1200,6 +1669,18 @@ static int media_pipeline_walk_by_vc(struct ipu_isys_video *av, if (entity_vc != ip->vc) continue; + if (ip->asv[i].substream == av_pad_id) + dev_dbg(entity->graph_obj.mdev->dev, + "METADATA VC :%d substream:%d, srcpad:%d\n", + entity_vc, ip->asv[i].substream, av_pad_id); + + if ((ip->asv[i].substream != (av_pad_id - 1)) && + (ip->asv[i].substream != av_pad_id)) { + dev_dbg(entity->graph_obj.mdev->dev, + "SKIP VC:%d substream:%d, srcpad:%d\n", + entity_vc, ip->asv[i].substream, av_pad_id); + continue; + } } entity->pipe = pipe; @@ -1233,6 +1714,9 @@ static int media_pipeline_walk_by_vc(struct ipu_isys_video *av, if (link->sink != pad || !(link->flags & MEDIA_LNK_FL_ENABLED)) continue; + dev_dbg(entity->graph_obj.mdev->dev, + "%s:%d: calling link_validate entity->name: %s \n", + __func__, __LINE__, entity->name); ret = entity->ops->link_validate(link); if (ret < 0 && ret != -ENOIOCTLCMD) { @@ -1345,7 +1829,9 @@ static int media_pipeline_walk_by_vc(struct ipu_isys_video *av, bool is_vc = false; if (!source_pad) { - dev_err(entity->graph_obj.mdev->dev, "no remote pad found\n"); + dev_err(entity->graph_obj.mdev->dev, + "%s:%d no remote pad found\n", + __func__, __LINE__); return ret; } @@ -1399,7 +1885,7 @@ static int media_pipeline_walk_by_vc(struct ipu_isys_video *av, if (!source_pad) { dev_warn(entity->graph_obj.mdev->dev, - "no remote pad found\n"); + "%s:%d no remote pad found\n", __func__, __LINE__); continue; } pad_id = source_pad->index; @@ -1532,7 +2018,8 @@ ipu_isys_prepare_fw_cfg_default(struct ipu_isys_video *av, unsigned int sub_stream_id; if (!source_pad) { - dev_err(&av->isys->adev->dev, "no remote pad found\n"); + dev_err(&av->isys->adev->dev, + "%s:%d no remote pad found\n", __func__, __LINE__); return; } @@ -1597,8 +2084,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 +2335,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; } @@ -1888,21 +2375,10 @@ static int start_stream_firmware(struct ipu_isys_video *av, reinit_completion(&ip->stream_start_completion); - if (bl) { - send_type = IPU_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE; - ipu_fw_isys_dump_frame_buff_set(dev, buf, - stream_cfg->nof_output_pins); - rval = ipu_fw_isys_complex_cmd(av->isys, - ip->stream_handle, - buf, to_dma_addr(msg), - sizeof(*buf), - send_type); - } else { - send_type = IPU_FW_ISYS_SEND_TYPE_STREAM_START; - rval = ipu_fw_isys_simple_cmd(av->isys, - ip->stream_handle, - send_type); - } + send_type = IPU_FW_ISYS_SEND_TYPE_STREAM_START; + rval = ipu_fw_isys_simple_cmd(av->isys, + ip->stream_handle, + send_type); if (rval < 0) { dev_err(dev, "can't start streaming (%d)\n", rval); @@ -1921,6 +2397,20 @@ static int start_stream_firmware(struct ipu_isys_video *av, rval = -EIO; goto out_stream_close; } + + if (bl) { + send_type = IPU_FW_ISYS_SEND_TYPE_STREAM_CAPTURE; + ipu_fw_isys_dump_frame_buff_set(dev, buf, + stream_cfg->nof_output_pins); + rval = ipu_fw_isys_complex_cmd(av->isys, + ip->stream_handle, + buf, to_dma_addr(msg), + sizeof(*buf), + send_type); + if (!WARN_ON(rval < 0)) + dev_dbg(dev, "can't queued buffer (%d)\n", rval); + } + dev_dbg(dev, "start stream: complete\n"); return 0; @@ -2076,6 +2566,7 @@ int ipu_isys_video_prepare_streaming(struct ipu_isys_video *av, ip = &av->ip; + dev_dbg(dev, "prepare stream: state:%d nr_queues:%d->0\n", state, ip->nr_queues); WARN_ON(ip->nr_streaming); ip->has_sof = false; ip->nr_queues = 0; @@ -2103,6 +2594,9 @@ int ipu_isys_video_prepare_streaming(struct ipu_isys_video *av, dev_dbg(dev, "pipeline start failed\n"); goto out_enum_cleanup; } + if(ip->external && ip->external->entity) + dev_dbg(dev, "ip->external: %s\n", + ip->external->entity->name); if (!ip->external) { dev_err(dev, "no external entity set! Driver bug?\n"); @@ -2202,18 +2696,23 @@ int ipu_isys_video_set_streaming(struct ipu_isys_video *av, if (!v4l2_query_ext_ctrl(esd->ctrl_handler, &qm_ctrl)) { c.value64 = SUB_STREAM_SET_VALUE(ip->vc, state); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) - v4l2_s_ext_ctrls(NULL, esd->ctrl_handler, + rval = v4l2_s_ext_ctrls(NULL, esd->ctrl_handler, esd->devnode, esd->v4l2_dev->mdev, &cs); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) - v4l2_s_ext_ctrls(NULL, esd->ctrl_handler, + rval = v4l2_s_ext_ctrls(NULL, esd->ctrl_handler, esd->v4l2_dev->mdev, &cs); #endif } else { - v4l2_subdev_call(esd, video, s_stream, state); + rval = v4l2_subdev_call(esd, video, s_stream, state); } + if (rval) + goto out_media_entity_graph_init; + 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 +2785,12 @@ 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); + if (rval) + goto out_media_entity_stop_streaming_firmware; + } } else { close_streaming_firmware(av); av->ip.vc = INVALIA_VC_ID; @@ -2332,6 +2837,7 @@ int ipu_isys_video_set_streaming(struct ipu_isys_video *av, out_media_entity_graph_init: media_graph_walk_cleanup(&ip->graph); + dev_warn(dev, "stream on for %s failed: %d\n", ip->external->entity->name, rval); return rval; } @@ -2363,9 +2869,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 +2886,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 +2908,39 @@ 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; + if(av->enum_link_state == IPU_ISYS_LINK_STATE_MD) + av->vdev.device_caps &= ~(V4L2_CAP_VIDEO_CAPTURE_MPLANE); + 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 +3027,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 5e23af2969e9..b72c72dd5709 100644 --- a/drivers/media/pci/intel/ipu-isys.c +++ b/drivers/media/pci/intel/ipu-isys.c @@ -1049,6 +1049,135 @@ static void isys_remove(struct ipu_bus_device *adev) } #ifdef CONFIG_DEBUG_FS +#if defined(CONFIG_VIDEO_INTEL_IPU_USE_PLATFORMDATA) +#include +/* use as such: +echo "0 2 d4xx 1 0x16 0x60 0x48" | sudo tee /sys/kernel/debug/intel-ipu6/isys/new_device + +*/ +static ssize_t ipu_isys_new_device_set(struct file *flip, + const char __user *buffer, size_t len, loff_t *offset) +{ + struct ipu_isys *isys = flip->f_inode->i_private; + + int res = 0; + int port = 0, lanes = 0, adapter = 0; + int sens = 0, ser = 0, des = 0; + char name[I2C_NAME_SIZE], end = 0; + char buf[128]; + struct serdes_subdev_info *serdes_sdinfo = NULL; + struct serdes_platform_data *pdata = NULL; + struct ipu_isys_subdev_info *sd_info = NULL; + struct ipu_isys_csi2_config *csi2_config = NULL; + + (void)offset; + + if (!(isys && isys->adev && &isys->adev->dev)) + return -EINVAL; + if (copy_from_user(buf, buffer, len)) { + pr_err("copy_from_user failed\n"); + return 0; + } + + buf[len] = 0; + dev_info(&isys->adev->dev, "isys_new_device_set function running val:%s\n", buf); + res = sscanf(buf, "%d %d %s %d 0x%02x 0x%02x 0x%02x%c", &port, &lanes, name, &adapter, &sens, &ser, &des, &end); + if (res != 8 && end != '\n') { + dev_err(NULL, "%s: Incorrect parameters\n", "new_device"); + return -EINVAL; + } + dev_info(&isys->adev->dev, "res:%d, port:%d, lanes:%d, name:%s, adapter:%d, sens:0x%02x, ser:0x%02x, des:0x%02x\n", + res,port, lanes, name, adapter, sens, ser, des); + + csi2_config = kzalloc(sizeof(*csi2_config), GFP_KERNEL); + if (!csi2_config) { + res = -ENOMEM; + goto error; + } + sd_info = kzalloc(sizeof(*sd_info), GFP_KERNEL); + if (!sd_info) { + res = -ENOMEM; + goto error; + } + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + res = -ENOMEM; + goto error; + } + serdes_sdinfo = kzalloc(sizeof(*serdes_sdinfo), GFP_KERNEL); + if (!serdes_sdinfo) { + res = -ENOMEM; + goto error; + } + pdata->suffix = port + 'a'; + + serdes_sdinfo->ser_alias = ser; + serdes_sdinfo->board_info.addr = des; + strlcpy(serdes_sdinfo->board_info.type, name, I2C_NAME_SIZE); + + + pdata->subdev_num = 1; + pdata->subdev_info = serdes_sdinfo; + + sd_info->i2c.i2c_adapter_id = adapter; + csi2_config->nlanes = lanes; + csi2_config->port = port; + sd_info->csi2 = csi2_config; + strlcpy(sd_info->i2c.board_info.type, name, I2C_NAME_SIZE); + + sd_info->i2c.board_info.addr = sens; + sd_info->i2c.board_info.platform_data = pdata; + + res = isys_register_ext_subdev(isys, sd_info); + if (res) { + goto error; + } + res = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); + if (res) { + isys_unregister_ext_subdev(isys, sd_info); + goto error; + } + return len; + +error: + if (csi2_config) + kfree(csi2_config); + if (sd_info) + kfree(sd_info); + if (pdata) + kfree(pdata); + if (serdes_sdinfo) + kfree(serdes_sdinfo); + return res; +} + +static ssize_t ipu_isys_new_device_get(struct file *flip, + char __user *buffer, size_t len, loff_t *offset) +{ + struct ipu_isys *isys = flip->f_inode->i_private; + char msg[256] = {0}; + static int once = 0; + int ret; + + ret = snprintf(msg, sizeof(msg), "IPU CSI2 new device binding\n" + " \n"); + if (copy_to_user(buffer, msg, strlen(msg))) { + dev_err(&isys->adev->dev, "copy_to_user failed\n"); + ret = -EFAULT; + } else { + dev_info(&isys->adev->dev, "\n%s\n", msg); + once = ~once; + ret = strlen(msg) & once; + } + return ret; +} + +static const struct file_operations isys_new_device_fops = { + .read = &ipu_isys_new_device_get, + .write = &ipu_isys_new_device_set, +}; +#endif + static int ipu_isys_icache_prefetch_get(void *data, u64 *val) { struct ipu_isys *isys = data; @@ -1089,7 +1218,12 @@ static int ipu_isys_init_debugfs(struct ipu_isys *isys) dir, isys, &isys_icache_prefetch_fops); if (IS_ERR(file)) goto err; - +#if defined(CONFIG_VIDEO_INTEL_IPU_USE_PLATFORMDATA) + file = debugfs_create_file("new_device", 0600, + dir, isys, &isys_new_device_fops); + if (IS_ERR(file)) + goto err; +#endif isys->debugfsdir = dir; #ifdef IPU_ISYS_GPC 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/drivers/media/platform/intel/ipu6-acpi-pdata.c b/drivers/media/platform/intel/ipu6-acpi-pdata.c index 7cdc6f99ab66..fda9acb533fa 100644 --- a/drivers/media/platform/intel/ipu6-acpi-pdata.c +++ b/drivers/media/platform/intel/ipu6-acpi-pdata.c @@ -669,7 +669,7 @@ int set_pdata(struct ipu_isys_subdev_info **sensor_sd, /* use ascii */ if (!strcmp(sensor_name, D457_NAME) && port >= 0) - pdata->suffix = serdes_info.deser_num + SUFFIX_BASE + 1; + pdata->suffix = port + SUFFIX_BASE + 1; else if (port > 0) pdata->suffix = port + SUFFIX_BASE; else 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)