From 5e203930106f2b1c397e7a74053b4c525d38a175 Mon Sep 17 00:00:00 2001 From: Hao Yao Date: Wed, 23 Sep 2026 15:30:59 +0800 Subject: [PATCH] IPU7 release for iot on 2026-09-23 Signed-off-by: Hao Yao --- .../media/pci/intel/ipu7/ipu7-isys-csi-phy.c | 61 +- drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c | 7 +- .../media/pci/intel/ipu7/ipu7-isys-video.c | 1 - drivers/media/pci/intel/ipu7/ipu7-isys.c | 73 +- drivers/media/pci/intel/ipu7/ipu7-isys.h | 1 + drivers/media/pci/intel/ipu7/ipu7.h | 6 +- drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 41 +- .../media/pci/intel/ipu7/psys/ipu7-fw-psys.c | 25 +- ...1-ipu7-Wait-ipu-fw-requests-to-clear.patch | 291 ++++ ...laim-pending-fw-msg-buffers-by-strea.patch | 123 ++ ...nc-at-buffer_prepare-callback-as-DMA.patch | 40 + ...-staging-ipu7-add-isys-reset-feature.patch | 820 ++++++++++++ ...h-staging-add-enable-CONFIG_DEBUG_FS.patch | 267 ++++ ...-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch | 109 ++ ...d-patch-for-use-DPHY-as-the-default-.patch | 30 + ...-add-pacth-for-ipu7-Kconfig-Makefile.patch | 75 ++ ...ing-add-ipu7-isys-tpg-and-MGC-config.patch | 811 +++++++++++ .../0011-INT3472-Support-LT6911GXD.patch | 23 + ...-media-i2c-add-support-for-lt6911gxd.patch | 42 + ...a-pci-enable-lt6911gxd-in-ipu-bridge.patch | 23 + .../0014-ipu-bridge-add-CPHY-support.patch | 110 ++ .../0015-media-i2c-add-isx031-config.patch | 45 + ...t-v4l2_subdev_enable_streams_api-tru.patch | 24 + ...ate-IPU7-firmware-ABI-version-to-1.2.patch | 83 ++ ...atch-staging-add-IPU8_PCI_ID-support.patch | 23 + ...ing-ipu7-Add-IPU8-ABI-version-1.0.14.patch | 466 +++++++ ...ine-gpreg_stride-for-different-IPU-v.patch | 77 ++ ...u7-Fix-potential-NULL-pointer-derefe.patch | 58 + ...u7-set-skipframe-flag-when-frame-err.patch | 39 + ...ing-ipu7-Add-more-insys-frame-format.patch | 30 + ...synchronous-RPM-suspend-in-probe-fai.patch | 33 + ...e-interrupts-when-device-is-suspende.patch | 57 + ...-ipu7-update-CDPHY-register-settings.patch | 77 ++ ...isys-let-v4l2-set-default-colorspace.patch | 36 + ...ource-pad-according-to-csi2-ep-fwnod.patch | 105 ++ ...-csi2-fwnode-ep-when-destroying-v4l2.patch | 38 + ...rt-for-Maxim-MAX96717-GMSL2-Serializ.patch | 1180 +++++++++++++++++ .../0031-media-mc-Add-INTERNAL-pad-flag.patch | 76 ++ ...-CSI-2-endpoint-during-device-regist.patch | 51 + ...ndling-in-C-PHY-and-CSI-2-configurat.patch | 220 +++ 40 files changed, 5639 insertions(+), 58 deletions(-) create mode 100644 patch/v6.18.3_iot/0001-ipu7-Wait-ipu-fw-requests-to-clear.patch create mode 100644 patch/v6.18.3_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch create mode 100644 patch/v6.18.3_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch create mode 100644 patch/v6.18.3_iot/0004-staging-ipu7-add-isys-reset-feature.patch create mode 100644 patch/v6.18.3_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch create mode 100644 patch/v6.18.3_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch create mode 100644 patch/v6.18.3_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch create mode 100644 patch/v6.18.3_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch create mode 100644 patch/v6.18.3_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch create mode 100644 patch/v6.18.3_iot/0011-INT3472-Support-LT6911GXD.patch create mode 100644 patch/v6.18.3_iot/0012-media-i2c-add-support-for-lt6911gxd.patch create mode 100644 patch/v6.18.3_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch create mode 100644 patch/v6.18.3_iot/0014-ipu-bridge-add-CPHY-support.patch create mode 100644 patch/v6.18.3_iot/0015-media-i2c-add-isx031-config.patch create mode 100644 patch/v6.18.3_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch create mode 100644 patch/v6.18.3_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch create mode 100644 patch/v6.18.3_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch create mode 100644 patch/v6.18.3_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.14.patch create mode 100644 patch/v6.18.3_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch create mode 100644 patch/v6.18.3_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch create mode 100644 patch/v6.18.3_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch create mode 100644 patch/v6.18.3_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch create mode 100644 patch/v6.18.3_iot/0024-media-ipu7-call-synchronous-RPM-suspend-in-probe-fai.patch create mode 100644 patch/v6.18.3_iot/0025-media-ipu7-ignore-interrupts-when-device-is-suspende.patch create mode 100644 patch/v6.18.3_iot/0026-media-ipu7-update-CDPHY-register-settings.patch create mode 100644 patch/v6.18.3_iot/0027-media-ipu7-isys-let-v4l2-set-default-colorspace.patch create mode 100644 patch/v6.18.3_iot/0028-media-ipu7-get-source-pad-according-to-csi2-ep-fwnod.patch create mode 100644 patch/v6.18.3_iot/0029-media-ipu7-Clean-csi2-fwnode-ep-when-destroying-v4l2.patch create mode 100644 patch/v6.18.3_iot/0030-kernel-add-support-for-Maxim-MAX96717-GMSL2-Serializ.patch create mode 100644 patch/v6.18.3_iot/0031-media-mc-Add-INTERNAL-pad-flag.patch create mode 100644 patch/v6.18.3_iot/0032-media-ipu7-Parse-CSI-2-endpoint-during-device-regist.patch create mode 100644 patch/v6.18.3_iot/0033-ipu7-Fix-lane-handling-in-C-PHY-and-CSI-2-configurat.patch diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c index a56aeec..b6f0863 100644 --- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c +++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c @@ -728,7 +728,6 @@ static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, bool aggregation, u64 mbps) { - u8 trios = 2; u16 coarse_target; u16 deass_thresh; u16 delay_thresh; @@ -746,18 +745,18 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, val = 0x155; if (is_ipu7(isys->adev->isp->hw_ver)) - trios = 3; + lanes = 3; dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9); dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7); dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7); reg = CORE_DIG_CLANE_0_RW_LP_0; - for (i = 0; i < trios; i++) + for (i = 0; i < lanes; i++) dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); val = (mbps > 900U) ? 1U : 0U; - for (i = 0; i < trios; i++) { + for (i = 0; i < lanes; i++) { reg = CORE_DIG_CLANE_0_RW_HS_RX_0; dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); @@ -782,7 +781,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, else coarse_target = 56; - for (i = 0; i < trios; i++) { + for (i = 0; i < lanes; i++) { reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); } @@ -794,7 +793,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); - if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) { + if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes > 2) { dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, 1, 0, 0); @@ -803,7 +802,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, 0, 0, 0); } - for (i = 0; i < trios; i++) { + for (i = 0; i < lanes; i++) { reg = CORE_DIG_RW_TRIO0_0 + i * 0x400; dwc_phy_write_mask(isys, id, reg, 1, 6, 8); dwc_phy_write_mask(isys, id, reg, 1, 3, 5); @@ -815,7 +814,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, deass_thresh++; reg = CORE_DIG_RW_TRIO0_2; - for (i = 0; i < trios; i++) + for (i = 0; i < lanes; i++) dwc_phy_write_mask(isys, id, reg + 0x400 * i, deass_thresh, 0, 7); @@ -825,7 +824,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, delay_thresh = 1; reg = CORE_DIG_RW_TRIO0_1; - for (i = 0; i < trios; i++) + for (i = 0; i < lanes; i++) dwc_phy_write_mask(isys, id, reg + 0x400 * i, delay_thresh, 0, 15); @@ -837,21 +836,21 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, reset_thresh = 1; reg = CORE_DIG_RW_TRIO0_0; - for (i = 0; i < trios; i++) + for (i = 0; i < lanes; i++) dwc_phy_write_mask(isys, id, reg + 0x400 * i, reset_thresh, 9, 11); /* Tuning ITMINRX to 2 for CPHY */ reg = CORE_DIG_CLANE_0_RW_LP_0; - for (i = 0; i < trios; i++) + for (i = 0; i < lanes; i++) dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15); reg = CORE_DIG_CLANE_0_RW_LP_2; - for (i = 0; i < trios; i++) + for (i = 0; i < lanes; i++) dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); reg = CORE_DIG_CLANE_0_RW_HS_RX_0; - for (i = 0; i < trios; i++) + for (i = 0; i < lanes; i++) dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); for (i = 0; i < ARRAY_SIZE(table7); i++) { @@ -873,6 +872,27 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); } + + if (aggregation) { + dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, 1, 1); + + /* + * C-PHY has no clock lane, so unlike D-PHY no AFE lane is the + * shared clock that has to follow port A. + */ + for (i = 0; i < (lanes + 1); i++) { + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, 3, 3, 4); + } + } + + /* Only port A runs rext calibration; other ports reuse its result. */ + if (isys->phy_rext_cal && id) { + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, + isys->phy_rext_cal, 0, 3); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, + 1, 11, 11); + } } static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, @@ -962,21 +982,23 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) { struct ipu7_isys *isys = csi2->isys; - u32 lanes = csi2->nlanes; + u32 total_lanes = csi2->nlanes; + u32 active_lanes = total_lanes; bool aggregation = false; u32 id = csi2->port; int ret; /* lanes remapping for aggregation (port AB) mode */ - if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) { + if (!is_ipu7(isys->adev->isp->hw_ver) && active_lanes > 2 && + id == PORT_A) { aggregation = true; - lanes = 2; + active_lanes = 2; } ipu7_isys_csi_phy_reset(isys, id); gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1); gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2); - gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); + gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << active_lanes) - 1U); gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf); gpreg_write(isys, id, PHY_MODE, csi2->phy_mode); @@ -993,7 +1015,7 @@ int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) ipu7_isys_csi_ctrl_cfg(csi2); ipu7_isys_csi_ctrl_dids_config(csi2, id); - ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); + ret = ipu7_isys_phy_config(isys, id, active_lanes, aggregation); if (ret < 0) return ret; @@ -1012,7 +1034,8 @@ int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) /* config PORT_B if aggregation mode */ if (aggregation) { - ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); + ret = ipu7_isys_phy_config(isys, PORT_B, + total_lanes - active_lanes, aggregation); if (ret < 0) return ret; diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c index 6ba21c3..caec6c4 100644 --- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c +++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c @@ -175,6 +175,11 @@ static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) ipu7_isys_csi_phy_powerdown(csi2); + if (csi2->port == 0U && csi2->nlanes > 2U && + !is_ipu7(isys->adev->isp->hw_ver)) + writel(0x0, isys_base + IS_IO_GPREGS_BASE + + CSI_PORTAB_AGGREGATION); + writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK); csi2_irq_disable(csi2); } @@ -195,7 +200,7 @@ static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n", port, readl(isys_base + offset + CSI_PORT_CLK_GATE), readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK)); - if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) { + if (port == 0U && nlanes > 2U && !is_ipu7(isys->adev->isp->hw_ver)) { dev_dbg(dev, "CSI port %u in aggregation mode\n", port); writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION); } diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c index 94d6a3f..093486f 100644 --- a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c +++ b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c @@ -238,7 +238,6 @@ static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.c b/drivers/media/pci/intel/ipu7/ipu7-isys.c index 8b0f0c3..7f3c047 100644 --- a/drivers/media/pci/intel/ipu7/ipu7-isys.c +++ b/drivers/media/pci/intel/ipu7/ipu7-isys.c @@ -59,23 +59,64 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, struct ipu7_isys_csi2_config *csi2) { struct device *dev = &isys->adev->auxdev.dev; - unsigned int i; + int source_pad; int ret; v4l2_set_subdev_hostdata(sd, csi2); - for (i = 0; i < sd->entity.num_pads; i++) { - if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) - break; - } + if (csi2->ep) { + struct v4l2_fwnode_endpoint vep_source = { + .bus_type = V4L2_MBUS_UNKNOWN + }; + struct fwnode_handle *ep_source; - if (i == sd->entity.num_pads) { - dev_warn(dev, "no source pad in external entity\n"); - ret = -ENOENT; - goto skip_unregister_subdev; + ep_source = fwnode_graph_get_remote_endpoint(csi2->ep); + if (!ep_source) { + dev_warn(dev, "no remote endpoint for subdev\n"); + ret = -ENOENT; + goto skip_unregister_subdev; + } + + source_pad = media_entity_get_fwnode_pad(&sd->entity, ep_source, + MEDIA_PAD_FL_SOURCE); + + ret = v4l2_fwnode_endpoint_parse(ep_source, &vep_source); + fwnode_handle_put(ep_source); + + if (source_pad < 0) { + dev_warn( + dev, + "error in no acquire source pad in external entity\n"); + ret = -ENOENT; + goto skip_unregister_subdev; + } + + dev_dbg(&isys->adev->auxdev.dev, "%s: CSI2 ep %pfw\n", __func__, + csi2->ep); + dev_dbg(&isys->adev->auxdev.dev, + "%s: source pad %d for subdev %s\n", __func__, + source_pad, sd->name); + + if (ret) + goto skip_unregister_subdev; + + csi2->nlanes = vep_source.bus.mipi_csi2.num_data_lanes; + csi2->bus_type = vep_source.bus_type; + } else { + for (source_pad = 0; source_pad < sd->entity.num_pads; + source_pad++) { + if (sd->entity.pads[source_pad].flags & + MEDIA_PAD_FL_SOURCE) + break; + } + if (source_pad == sd->entity.num_pads) { + dev_warn(dev, "no source pad in external entity\n"); + ret = -ENOENT; + goto skip_unregister_subdev; + } } - ret = media_create_pad_link(&sd->entity, i, + ret = media_create_pad_link(&sd->entity, source_pad, &isys->csi2[csi2->port].asd.sd.entity, 0, MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); @@ -300,9 +341,18 @@ static int isys_notifier_complete(struct v4l2_async_notifier *notifier) return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); } +static void isys_notifier_destroy(struct v4l2_async_connection *asc) +{ + struct sensor_async_sd *s_asd = + container_of(asc, struct sensor_async_sd, asc); + + fwnode_handle_put(s_asd->csi2.ep); +} + static const struct v4l2_async_notifier_operations isys_async_ops = { .bound = isys_notifier_bound, .complete = isys_notifier_complete, + .destroy = isys_notifier_destroy, }; static int isys_notifier_init(struct ipu7_isys *isys) @@ -351,8 +401,7 @@ static int isys_notifier_init(struct ipu7_isys *isys) s_asd->csi2.port = vep.base.port; s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; s_asd->csi2.bus_type = vep.bus_type; - - fwnode_handle_put(ep); + s_asd->csi2.ep = ep; continue; diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.h b/drivers/media/pci/intel/ipu7/ipu7-isys.h index 0343b2e..69fb564 100644 --- a/drivers/media/pci/intel/ipu7/ipu7-isys.h +++ b/drivers/media/pci/intel/ipu7/ipu7-isys.h @@ -158,6 +158,7 @@ struct ipu7_isys_csi2_config { unsigned int nlanes; unsigned int port; enum v4l2_mbus_type bus_type; + struct fwnode_handle *ep; }; struct ipu7_isys_subdev_i2c_info { diff --git a/drivers/media/pci/intel/ipu7/ipu7.h b/drivers/media/pci/intel/ipu7/ipu7.h index b48bc1d..41f3efd 100644 --- a/drivers/media/pci/intel/ipu7/ipu7.h +++ b/drivers/media/pci/intel/ipu7/ipu7.h @@ -81,13 +81,13 @@ struct ipu7_device { void __iomem *base; void __iomem *pb_base; +#ifdef CONFIG_DEBUG_FS + struct dentry *ipu7_dir; +#endif u8 hw_ver; bool ipc_reinit; bool secure_mode; bool ipu7_bus_ready_to_probe; -#ifdef CONFIG_DEBUG_FS - struct dentry *ipu7_dir; -#endif }; #define IPU_DMA_MASK 39 diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c index ce16e4c..3c6bcbb 100644 --- a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c +++ b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c @@ -836,7 +836,7 @@ static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, struct ipu7_psys_fh *fh) { struct ipu7_psys *psys = fh->psys; - int ret = 0; + int ret; if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { dev_err(&psys->dev, "Wrong state %d to open graph %d\n", @@ -855,13 +855,16 @@ static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, return -EINVAL; } + /* Publish the node count once the input is valid; clear it on failure. */ + fh->ip->num_nodes = graph->num_nodes; + reinit_completion(&fh->ip->graph_open); ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); if (ret) { dev_err(&psys->dev, "Failed to open graph %d\n", fh->ip->graph_id); - return ret; + goto err_clear_nodes; } fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; @@ -871,19 +874,26 @@ static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, if (!ret) { dev_err(&psys->dev, "Open graph %d timeout\n", fh->ip->graph_id); - fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto err_set_closed; } if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { dev_err(&psys->dev, "Failed to set graph\n"); - fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; - return -EINVAL; + ret = -EINVAL; + goto err_set_closed; } graph->graph_id = fh->ip->graph_id; return 0; + +err_set_closed: + fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; +err_clear_nodes: + fh->ip->num_nodes = 0; + + return ret; } static void ipu_psys_cleanup_running_task_queue(struct ipu7_psys_fh *fh) @@ -921,6 +931,7 @@ static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) } fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; + fh->ip->num_nodes = 0; ret = wait_for_completion_timeout(&fh->ip->graph_close, IPU_FW_CALL_TIMEOUT_JIFFIES); @@ -1350,11 +1361,8 @@ static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) { struct dentry *file; struct dentry *dir; -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0) + dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); -#else - dir = debugfs_create_dir("ipu7-psys", NULL); -#endif if (IS_ERR(dir)) return -ENOMEM; @@ -1505,7 +1513,9 @@ static void ipu7_psys_remove(struct auxiliary_device *auxdev) psys->adev->get_running_fw_task_count = NULL; #ifdef CONFIG_DEBUG_FS - if (psys->debugfsdir) + struct ipu7_device *isp = psys->adev->isp; + + if (isp->ipu7_dir) debugfs_remove_recursive(psys->debugfsdir); #endif @@ -1585,7 +1595,7 @@ static struct auxiliary_driver ipu7_psys_driver = { }, }; -static int __init ipu7_psys_init(void) +static int __init ipu7_psys_driver_init(void) { int ret; @@ -1599,14 +1609,15 @@ static int __init ipu7_psys_init(void) return ret; } -module_init(ipu7_psys_init); -static void __exit ipu7_psys_exit(void) +static void __exit ipu7_psys_driver_exit(void) { auxiliary_driver_unregister(&ipu7_psys_driver); bus_unregister(&ipu7_psys_bus); } -module_exit(ipu7_psys_exit); + +module_init(ipu7_psys_driver_init); +module_exit(ipu7_psys_driver_exit); MODULE_AUTHOR("Bingbu Cao "); MODULE_AUTHOR("Qingwu Zhang "); diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c index 15ba548..7d93b12 100644 --- a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c +++ b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c @@ -474,11 +474,20 @@ int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, struct ipu7_syscom_context *ctx = psys->adev->syscom; struct ipu7_msg_task *msg = tq->msg_task; struct ia_gofo_msg_indirect *ind; - u32 node_q_id = ip->q_id[task->node_ctx_id]; + u8 node_ctx_id = task->node_ctx_id; + u32 node_q_id; u32 teb_hi, teb_lo; u64 teb; u8 i, term_id; u8 num_terms; + u8 max_terms; + + if (node_ctx_id >= ip->num_nodes || + node_ctx_id >= ARRAY_SIZE(ip->nodes) || + node_ctx_id >= ARRAY_SIZE(ip->q_id)) + return -EINVAL; + + node_q_id = ip->q_id[node_ctx_id]; ind = ipu7_syscom_get_token(ctx, node_q_id); if (!ind) @@ -486,7 +495,7 @@ int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, memset(msg, 0, sizeof(*msg)); msg->graph_id = task->graph_id; - msg->node_ctx_id = task->node_ctx_id; + msg->node_ctx_id = node_ctx_id; msg->profile_idx = 0U; /* Only one profile on HKR */ msg->frame_id = task->frame_id; msg->frag_id = 0U; /* No frag, set to 0 */ @@ -500,14 +509,16 @@ int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, sizeof(task->payload_reuse_bm)); - teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; - teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; + teb_hi = ip->nodes[node_ctx_id].profiles[0].teb[1]; + teb_lo = ip->nodes[node_ctx_id].profiles[0].teb[0]; teb = (teb_lo | (((u64)teb_hi) << 32)); - num_terms = ip->nodes[msg->node_ctx_id].num_terms; - for (i = 0U; i < num_terms; i++) { + num_terms = ip->nodes[node_ctx_id].num_terms; + max_terms = min_t(u8, num_terms, (u8)ARRAY_SIZE(tq->task_buffers)); + for (i = 0U; i < max_terms; i++) { term_id = tq->task_buffers[i].term_id; - if ((1U << term_id) & teb) + if (term_id < ARRAY_SIZE(msg->term_buffers) && + (teb & BIT_ULL(term_id))) msg->term_buffers[term_id] = tq->ipu7_addr[i]; } diff --git a/patch/v6.18.3_iot/0001-ipu7-Wait-ipu-fw-requests-to-clear.patch b/patch/v6.18.3_iot/0001-ipu7-Wait-ipu-fw-requests-to-clear.patch new file mode 100644 index 0000000..45b3131 --- /dev/null +++ b/patch/v6.18.3_iot/0001-ipu7-Wait-ipu-fw-requests-to-clear.patch @@ -0,0 +1,291 @@ +From 7c226ce24f27e00ab45f31881b6e48a9e2bc3d00 Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Wed, 17 Jun 2026 10:13:25 +0800 +Subject: [PATCH 01/26] ipu7: Wait ipu fw requests to clear + +IPU ISYS MMUs run in parallel, and runtime TLB invalidation may +overlap with ongoing data movement. For fw msg buf expansion, +invalidating ISYS MMU0 is sufficient. + +The goal of acquire_fw_task_buffer_lock is to block subsequent +getting fw message buffer or task queue while waiting for +framebuflist_fw to drain. + +With "glimagesink sync=false", gstreamer will launch each stream one +by one. So, when isys message buffer is not enough, just allocating +max buffer count(10) is enough. + +Signed-off-by: hepengpx +--- + drivers/staging/media/ipu7/ipu7-bus.c | 2 + + drivers/staging/media/ipu7/ipu7-bus.h | 6 +++ + drivers/staging/media/ipu7/ipu7-dma.c | 8 +++- + drivers/staging/media/ipu7/ipu7-isys.c | 30 ++++++++++++++- + drivers/staging/media/ipu7/ipu7-mmu.c | 52 +++++++++++++++++++++++++- + drivers/staging/media/ipu7/ipu7-mmu.h | 8 +++- + 6 files changed, 99 insertions(+), 7 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-bus.c b/drivers/staging/media/ipu7/ipu7-bus.c +index 7da44fde00..cd0641781c 100644 +--- a/drivers/staging/media/ipu7/ipu7-bus.c ++++ b/drivers/staging/media/ipu7/ipu7-bus.c +@@ -75,6 +75,7 @@ static void ipu7_bus_release(struct device *dev) + { + struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); + ++ mutex_destroy(&adev->acquire_fw_task_buffer_lock); + kfree(adev->pdata); + kfree(adev); + } +@@ -96,6 +97,7 @@ ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, + adev->isp = isp; + adev->ctrl = ctrl; + adev->pdata = pdata; ++ mutex_init(&adev->acquire_fw_task_buffer_lock); + auxdev = &adev->auxdev; + auxdev->name = name; + auxdev->id = (pci_domain_nr(pdev->bus) << 16) | +diff --git a/drivers/staging/media/ipu7/ipu7-bus.h b/drivers/staging/media/ipu7/ipu7-bus.h +index 45157df16e..eb9d0c907d 100644 +--- a/drivers/staging/media/ipu7/ipu7-bus.h ++++ b/drivers/staging/media/ipu7/ipu7-bus.h +@@ -46,6 +46,12 @@ struct ipu7_bus_device { + struct ia_gofo_boot_config *boot_config; + dma_addr_t boot_config_dma_addr; + u32 boot_config_size; ++ ++ /* Serialize FW message buffer or task queue acquisition against ++ * TLB invalidation. ++ */ ++ struct mutex acquire_fw_task_buffer_lock; ++ unsigned int (*get_running_fw_task_count)(struct ipu7_bus_device *adev); + }; + + struct ipu7_auxdrv_data { +diff --git a/drivers/staging/media/ipu7/ipu7-dma.c b/drivers/staging/media/ipu7/ipu7-dma.c +index a118b41b2f..05478ab0f0 100644 +--- a/drivers/staging/media/ipu7/ipu7-dma.c ++++ b/drivers/staging/media/ipu7/ipu7-dma.c +@@ -206,6 +206,8 @@ void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, + } + } + ++ if (mmu->mmid == ISYS_MMID) ++ mmu->tlb_invalidate(mmu, IPU_IS_MMU_FW_RD); + info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); + if (!info->vaddr) + goto out_unmap; +@@ -286,7 +288,7 @@ void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, + + __free_buffer(pages, size, attrs); + +- mmu->tlb_invalidate(mmu); ++ mmu->tlb_invalidate(mmu, -1); + + __free_iova(&mmu->dmap->iovad, iova); + +@@ -366,7 +368,9 @@ void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), + PFN_PHYS(iova_size(iova))); + +- mmu->tlb_invalidate(mmu); ++ mutex_lock(&sys->acquire_fw_task_buffer_lock); ++ mmu->tlb_invalidate(mmu, -1); ++ mutex_unlock(&sys->acquire_fw_task_buffer_lock); + __free_iova(&mmu->dmap->iovad, iova); + } + EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sg, "INTEL_IPU7"); +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index cb2f49f3e0..1d1d6f5158 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -576,6 +576,8 @@ static void isys_remove(struct auxiliary_device *auxdev) + struct isys_fw_msgs *fwmsg, *safe; + struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); + ++ adev->get_running_fw_task_count = NULL; ++ + for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) + mutex_destroy(&isys->streams[i].mutex); + +@@ -634,6 +636,23 @@ static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) + return -ENOMEM; + } + ++static unsigned int ipu7_isys_get_running_fw_task_count( ++ struct ipu7_bus_device *adev) ++{ ++ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); ++ unsigned long flags; ++ unsigned int count; ++ ++ if (!isys) ++ return 0; ++ ++ spin_lock_irqsave(&isys->listlock, flags); ++ count = list_count_nodes(&isys->framebuflist_fw); ++ spin_unlock_irqrestore(&isys->listlock, flags); ++ ++ return count; ++} ++ + struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) + { + struct device *dev = &stream->isys->adev->auxdev.dev; +@@ -642,18 +661,22 @@ struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) + unsigned long flags; + int ret; + ++ mutex_lock(&isys->adev->acquire_fw_task_buffer_lock); + spin_lock_irqsave(&isys->listlock, flags); + if (list_empty(&isys->framebuflist)) { + spin_unlock_irqrestore(&isys->listlock, flags); + dev_dbg(dev, "Frame buffer list empty\n"); + +- ret = alloc_fw_msg_bufs(isys, 5); +- if (ret < 0) ++ ret = alloc_fw_msg_bufs(isys, 10); ++ if (ret < 0) { ++ mutex_unlock(&isys->adev->acquire_fw_task_buffer_lock); + return NULL; ++ } + + spin_lock_irqsave(&isys->listlock, flags); + if (list_empty(&isys->framebuflist)) { + spin_unlock_irqrestore(&isys->listlock, flags); ++ mutex_unlock(&isys->adev->acquire_fw_task_buffer_lock); + dev_err(dev, "Frame list empty\n"); + return NULL; + } +@@ -661,6 +684,7 @@ struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) + msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); + list_move(&msg->head, &isys->framebuflist_fw); + spin_unlock_irqrestore(&isys->listlock, flags); ++ mutex_unlock(&isys->adev->acquire_fw_task_buffer_lock); + memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); + + return msg; +@@ -744,6 +768,8 @@ static int isys_probe(struct auxiliary_device *auxdev, + INIT_LIST_HEAD(&isys->framebuflist_fw); + + dev_set_drvdata(&auxdev->dev, isys); ++ adev->get_running_fw_task_count = ++ ipu7_isys_get_running_fw_task_count; + + isys->icache_prefetch = 0; + isys->phy_rext_cal = 0; +diff --git a/drivers/staging/media/ipu7/ipu7-mmu.c b/drivers/staging/media/ipu7/ipu7-mmu.c +index ded1986eb8..2119c1ecb1 100644 +--- a/drivers/staging/media/ipu7/ipu7-mmu.c ++++ b/drivers/staging/media/ipu7/ipu7-mmu.c +@@ -27,6 +27,7 @@ + #include + + #include "ipu7.h" ++#include "ipu7-bus.h" + #include "ipu7-dma.h" + #include "ipu7-mmu.h" + #include "ipu7-platform-regs.h" +@@ -52,6 +53,8 @@ + #define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) + + #define MMU_TLB_INVALIDATE_TIMEOUT 2000 ++#define WAIT_FW_MSG_BUFS_CLEAR_TIME_MS 17 ++#define WAIT_FW_MSG_BUFS_CLEAR_TIMES 5 + + static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu) + { +@@ -66,12 +69,41 @@ static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu) + } + } + +-static void tlb_invalidate(struct ipu7_mmu *mmu) ++static void tlb_invalidate(struct ipu7_mmu *mmu, int mmu_id) + { + unsigned long flags; ++ unsigned int start, end; + unsigned int i; + int ret; + u32 val; ++ unsigned int prev_task_cnt = UINT_MAX; ++ unsigned int curr_task_cnt; ++ unsigned int not_decreasing_count = 0; ++ struct ipu7_bus_device *adev = to_ipu7_bus_device(mmu->dev); ++ ++ if (adev->get_running_fw_task_count) { ++ while (1) { ++ curr_task_cnt = ++ adev->get_running_fw_task_count(adev); ++ if (curr_task_cnt == 0) ++ break; ++ ++ if (curr_task_cnt >= prev_task_cnt) ++ not_decreasing_count++; ++ else ++ not_decreasing_count = 0; ++ prev_task_cnt = curr_task_cnt; ++ ++ if (not_decreasing_count > ++ WAIT_FW_MSG_BUFS_CLEAR_TIMES) { ++ dev_warn(mmu->dev, ++ "wait running fw tasks clear timeout\n"); ++ break; ++ } ++ ++ msleep(WAIT_FW_MSG_BUFS_CLEAR_TIME_MS); ++ } ++ } + + spin_lock_irqsave(&mmu->ready_lock, flags); + if (!mmu->ready) { +@@ -79,7 +111,23 @@ static void tlb_invalidate(struct ipu7_mmu *mmu) + return; + } + +- for (i = 0; i < mmu->nr_mmus; i++) { ++ /* mmu_id < 0: all MMUs, otherwise one MMU. */ ++ if (mmu_id < 0) { ++ start = 0; ++ end = mmu->nr_mmus; ++ } else if (mmu_id >= mmu->nr_mmus) { ++ dev_warn(mmu->dev, "invalid mmu_id %d, nr_mmus %u\n", ++ mmu_id, mmu->nr_mmus); ++ spin_unlock_irqrestore(&mmu->ready_lock, flags); ++ return; ++ } ++ ++ if (mmu_id >= 0) { ++ start = mmu_id; ++ end = mmu_id + 1; ++ } ++ ++ for (i = start; i < end; i++) { + writel(0xffffffffU, mmu->mmu_hw[i].base + + MMU_REG_INVALIDATE_0); + +diff --git a/drivers/staging/media/ipu7/ipu7-mmu.h b/drivers/staging/media/ipu7/ipu7-mmu.h +index d85bb8ffc7..e4bc485496 100644 +--- a/drivers/staging/media/ipu7/ipu7-mmu.h ++++ b/drivers/staging/media/ipu7/ipu7-mmu.h +@@ -20,6 +20,12 @@ struct ipu7_mmu_info; + #define ISYS_MMID 0x1 + #define PSYS_MMID 0x0 + ++#define IPU_IS_MMU_FW_RD 0 ++#define IPU_IS_MMU_FW_WR 1 ++#define IPU_IS_MMU_M0 2 ++#define IPU_IS_MMU_M1 3 ++#define IPU_IS_MMU_UPIPE 4 ++ + /* IPU7 for LNL */ + /* IS MMU Cmd RD */ + #define IPU7_IS_MMU_FW_RD_OFFSET 0x274000 +@@ -396,7 +402,7 @@ struct ipu7_mmu { + bool ready; + spinlock_t ready_lock; /* Serialize access to bool ready */ + +- void (*tlb_invalidate)(struct ipu7_mmu *mmu); ++ void (*tlb_invalidate)(struct ipu7_mmu *mmu, int mmu_id); + }; + + struct ipu7_mmu *ipu7_mmu_init(struct device *dev, diff --git a/patch/v6.18.3_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch b/patch/v6.18.3_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch new file mode 100644 index 0000000..560d375 --- /dev/null +++ b/patch/v6.18.3_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch @@ -0,0 +1,123 @@ +From 23c617526871af701f2069f1900b3f66969a139a Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Fri, 5 Jun 2026 10:50:07 +0800 +Subject: [PATCH 02/26] staging: ipu7: reclaim pending fw msg buffers by + stream_id + +When one stream stops, firmware stops that stream immediately. +Some fw msg bufs may still remain in framebuflist_fw and should be +reclaimed. + +A stream_id is added to isys_fw_msgs to indicate which stream each +message belongs to. + +Signed-off-by: hepengpx +--- + drivers/staging/media/ipu7/ipu7-isys-queue.c | 2 ++ + drivers/staging/media/ipu7/ipu7-isys-video.c | 4 ++++ + drivers/staging/media/ipu7/ipu7-isys.c | 14 ++++++++++++++ + drivers/staging/media/ipu7/ipu7-isys.h | 3 +++ + 4 files changed, 23 insertions(+) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c +index 434d9d9c71..02466f8836 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-queue.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c +@@ -314,6 +314,7 @@ static int ipu7_isys_stream_start(struct ipu7_isys_video *av, + if (!msg) + return -ENOMEM; + ++ msg->stream_id = stream->stream_handle; + buf = &msg->fw_msg.frame; + + ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); +@@ -403,6 +404,7 @@ static void buf_queue(struct vb2_buffer *vb) + goto out; + } + ++ msg->stream_id = stream->stream_handle; + buf = &msg->fw_msg.frame; + + ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl); +diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c +index 1a7c8a91ff..7e89441f5a 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-video.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-video.c +@@ -457,6 +457,7 @@ static int start_stream_firmware(struct ipu7_isys_video *av, + if (!msg) + return -ENOMEM; + ++ msg->stream_id = stream->stream_handle; + stream_cfg = &msg->fw_msg.stream; + stream_cfg->port_id = stream->stream_source; + stream_cfg->vc = stream->vc; +@@ -513,6 +514,7 @@ static int start_stream_firmware(struct ipu7_isys_video *av, + ret = -ENOMEM; + goto out_put_stream_opened; + } ++ msg->stream_id = stream->stream_handle; + buf = &msg->fw_msg.frame; + + ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); +@@ -787,6 +789,7 @@ int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, + struct media_pad *r_pad; + struct v4l2_subdev *sd; + u32 r_stream = 0; ++ u16 stream_id = stream->stream_handle; + int ret = 0; + + dev_dbg(dev, "set stream: %d\n", state); +@@ -811,6 +814,7 @@ int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, + } + + close_streaming_firmware(av); ++ ipu7_cleanup_fw_msg_bufs_by_stream_id(av->isys, stream_id); + } else { + ret = start_stream_firmware(av, bl); + if (ret) { +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index 1d1d6f5158..8a4b153a03 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -701,6 +701,20 @@ void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys) + spin_unlock_irqrestore(&isys->listlock, flags); + } + ++void ipu7_cleanup_fw_msg_bufs_by_stream_id(struct ipu7_isys *isys, ++ u16 stream_id) ++{ ++ struct isys_fw_msgs *fwmsg, *fwmsg0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&isys->listlock, flags); ++ list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) { ++ if (fwmsg->stream_id == stream_id) ++ list_move(&fwmsg->head, &isys->framebuflist); ++ } ++ spin_unlock_irqrestore(&isys->listlock, flags); ++} ++ + void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data) + { + struct isys_fw_msgs *msg; +diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h +index ef1ab1b42f..d7f6b7bc54 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -119,6 +119,7 @@ struct isys_fw_msgs { + } fw_msg; + struct list_head head; + dma_addr_t dma_addr; ++ u16 stream_id; + }; + + struct ipu7_isys_csi2_config { +@@ -135,6 +136,8 @@ struct sensor_async_sd { + struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream); + void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data); + void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys); ++void ipu7_cleanup_fw_msg_bufs_by_stream_id(struct ipu7_isys *isys, ++ u16 stream_id); + int isys_isr_one(struct ipu7_bus_device *adev); + void ipu7_isys_setup_hw(struct ipu7_isys *isys); + #endif /* IPU7_ISYS_H */ diff --git a/patch/v6.18.3_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch b/patch/v6.18.3_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch new file mode 100644 index 0000000..e8d540f --- /dev/null +++ b/patch/v6.18.3_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch @@ -0,0 +1,40 @@ +From e68905275d7525f56166502a256195fb0992226e Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 25 Oct 2025 16:42:45 +0800 +Subject: [PATCH 03/26] media: ipu: Dma sync at buffer_prepare callback as DMA + is non-coherent + +Test Platform: +PTLRVP +LNLRVP + +Signed-off-by: Bingbu Cao +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/ipu7-isys-queue.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c +index 02466f8836..537542ff5a 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-queue.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c +@@ -85,7 +85,9 @@ static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) + { + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); ++ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); ++ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); + struct device *dev = &av->isys->adev->auxdev.dev; + u32 bytesperline = av->pix_fmt.bytesperline; + u32 height = av->pix_fmt.height; +@@ -100,6 +102,9 @@ static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) + av->vdev.name, bytesperline, height); + vb2_set_plane_payload(vb, 0, bytesperline * height); + ++ /* assume IPU is not DMA coherent */ ++ ipu7_dma_sync_sgtable(isys->adev, sg); ++ + return 0; + } + diff --git a/patch/v6.18.3_iot/0004-staging-ipu7-add-isys-reset-feature.patch b/patch/v6.18.3_iot/0004-staging-ipu7-add-isys-reset-feature.patch new file mode 100644 index 0000000..3e25881 --- /dev/null +++ b/patch/v6.18.3_iot/0004-staging-ipu7-add-isys-reset-feature.patch @@ -0,0 +1,820 @@ +From 3708234ce2ad1aae05adb4d412ba9df1b647450d Mon Sep 17 00:00:00 2001 +From: Lin Yang +Date: Tue, 9 Jun 2026 17:47:39 +0800 +Subject: [PATCH 04/26] staging: ipu7: add isys reset feature + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/Kconfig | 10 + + drivers/staging/media/ipu7/ipu7-isys-queue.c | 353 ++++++++++++++++++- + drivers/staging/media/ipu7/ipu7-isys-queue.h | 3 + + drivers/staging/media/ipu7/ipu7-isys-video.c | 81 +++++ + drivers/staging/media/ipu7/ipu7-isys-video.h | 8 + + drivers/staging/media/ipu7/ipu7-isys.c | 75 ++++ + drivers/staging/media/ipu7/ipu7-isys.h | 16 + + 7 files changed, 545 insertions(+), 1 deletion(-) + +diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig +index 7d831ba750..c4eee7c3e6 100644 +--- a/drivers/staging/media/ipu7/Kconfig ++++ b/drivers/staging/media/ipu7/Kconfig +@@ -17,3 +17,13 @@ config VIDEO_INTEL_IPU7 + + To compile this driver, say Y here! It contains 2 modules - + intel_ipu7 and intel_ipu7_isys. ++ ++config VIDEO_INTEL_IPU7_ISYS_RESET ++ bool "IPU7 ISYS RESET" ++ depends on VIDEO_INTEL_IPU7 ++ default n ++ help ++ This option enables IPU7 ISYS reset feature to support ++ HDMI-MIPI converter hot-plugging. ++ ++ If doubt, say N here. +diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c +index 537542ff5a..27d8b1b331 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-queue.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c +@@ -11,6 +11,9 @@ + #include + #include + #include ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++#include ++#endif + + #include + #include +@@ -26,6 +29,9 @@ + #include "ipu7-isys-csi2-regs.h" + #include "ipu7-isys-video.h" + #include "ipu7-platform-regs.h" ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++#include "ipu7-cpd.h" ++#endif + + #define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) + +@@ -230,6 +236,16 @@ static int buffer_list_get(struct ipu7_isys_stream *stream, + ib = list_last_entry(&aq->incoming, + struct ipu7_isys_buffer, head); + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); ++ ++ if (av->skipframe) { ++ atomic_set(&ib->skipframe_flag, 1); ++ av->skipframe--; ++ } else { ++ atomic_set(&ib->skipframe_flag, 0); ++ } ++#endif + dev_dbg(dev, "buffer: %s: buffer %u\n", + ipu7_isys_queue_to_video(aq)->vdev.name, + ipu7_isys_buffer_to_vb2_buffer(ib)->index); +@@ -385,6 +401,18 @@ static void buf_queue(struct vb2_buffer *vb) + return; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ mutex_lock(&av->isys->reset_mutex); ++ if (av->isys->state & RESET_STATE_IN_RESET) { ++ dev_dbg(dev, "in reset, adding to incoming\n"); ++ mutex_unlock(&av->isys->reset_mutex); ++ return; ++ } ++ mutex_unlock(&av->isys->reset_mutex); ++ ++ /* ip may be cleared in ipu reset */ ++ stream = av->stream; ++#endif + mutex_lock(&stream->mutex); + + if (stream->nr_streaming != stream->nr_queues) { +@@ -600,6 +628,9 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) + + out: + mutex_unlock(&stream->mutex); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ av->start_streaming = 1; ++#endif + + return 0; + +@@ -620,17 +651,331 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) + return ret; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++static void reset_stop_streaming(struct ipu7_isys_video *av) ++{ ++ struct ipu7_isys_queue *aq = &av->aq; ++ struct device *dev = &av->isys->adev->auxdev.dev; ++ struct ipu7_isys_stream *stream = av->stream; ++ struct ipu7_isys_buffer *ib; ++ struct vb2_buffer *vb; ++ unsigned long flags; ++ ++ dev_dbg(dev, "reset stop streams: %s\n", av->vdev.name); ++ mutex_lock(&av->isys->stream_mutex); ++ if (stream->nr_streaming == stream->nr_queues && stream->streaming) ++ ipu7_isys_video_set_streaming(av, 0, NULL); ++ mutex_unlock(&av->isys->stream_mutex); ++ ++ mutex_lock(&stream->mutex); ++ stream->nr_streaming--; ++ list_del(&aq->node); ++ stream->streaming = 0; ++ mutex_unlock(&stream->mutex); ++ ++ ipu7_isys_stream_cleanup(av); ++ ++ spin_lock_irqsave(&aq->lock, flags); ++ while (!list_empty(&aq->active)) { ++ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, ++ head); ++ vb = ipu7_isys_buffer_to_vb2_buffer(ib); ++ ++ list_del(&ib->head); ++ spin_unlock_irqrestore(&aq->lock, flags); ++ ++ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); ++ ++ spin_lock_irqsave(&aq->lock, flags); ++ } ++ spin_unlock_irqrestore(&aq->lock, flags); ++ ++ ipu7_isys_fw_close(av->isys); ++} ++ ++static int reset_start_streaming(struct ipu7_isys_video *av) ++{ ++ struct ipu7_isys_queue *aq = &av->aq; ++ struct device *dev = &av->isys->adev->auxdev.dev; ++ struct ipu7_isys_buffer_list __bl, *bl = NULL; ++ struct ipu7_isys_stream *stream; ++ struct media_entity *source_entity = NULL; ++ int nr_queues; ++ int ret; ++ ++ dev_dbg(dev, "%s: reset start streaming\n", av->vdev.name); ++ ++ av->skipframe = 1; ++ ++ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); ++ if (ret < 0) { ++ dev_dbg(dev, "failed to setup video\n"); ++ goto out_return_buffers; ++ } ++ ++ ret = ipu7_isys_link_fmt_validate(aq); ++ if (ret) { ++ dev_dbg(dev, ++ "%s: link format validation failed (%d)\n", ++ av->vdev.name, ret); ++ goto out_pipeline_stop; ++ } ++ ++ stream = av->stream; ++ mutex_lock(&stream->mutex); ++ if (!stream->nr_streaming) { ++ ret = ipu7_isys_video_prepare_stream(av, source_entity, ++ nr_queues); ++ if (ret) { ++ mutex_unlock(&stream->mutex); ++ goto out_pipeline_stop; ++ } ++ } ++ ++ stream->nr_streaming++; ++ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, ++ stream->nr_queues); ++ ++ list_add(&aq->node, &stream->queues); ++ ++ if (stream->nr_streaming != stream->nr_queues) ++ goto out; ++ ++ bl = &__bl; ++ int retry = 5; ++ while (retry--) { ++ ret = buffer_list_get(stream, bl); ++ if (ret < 0) { ++ dev_dbg(dev, "wait for incoming buffer, retry %d\n", retry); ++ usleep_range(100000, 110000); ++ continue; ++ } ++ break; ++ } ++ ++ /* ++ * In reset start streaming and no buffer available, ++ * it is considered that gstreamer has been closed, ++ * and reset start is no needed, not driver bug. ++ */ ++ if (ret) { ++ dev_dbg(dev, "reset start: no buffer available, gstreamer colsed\n"); ++ mutex_lock(&av->isys->stream_mutex); ++ if (stream->nr_streaming == stream->nr_queues && stream->streaming) ++ ipu7_isys_video_set_streaming(av, 0, NULL); ++ mutex_unlock(&av->isys->stream_mutex); ++ ++ goto out_stream_start; ++ } ++ ++ ret = ipu7_isys_fw_open(av->isys); ++ if (ret) ++ goto out_stream_start; ++ ++ ipu7_isys_setup_hw(av->isys); ++ ++ ret = ipu7_isys_stream_start(av, bl, false); ++ if (ret) ++ goto out_isys_fw_close; ++ ++out: ++ mutex_unlock(&stream->mutex); ++ av->start_streaming = 1; ++ return 0; ++ ++out_isys_fw_close: ++ ipu7_isys_fw_close(av->isys); ++ ++out_stream_start: ++ list_del(&aq->node); ++ stream->nr_streaming--; ++ mutex_unlock(&stream->mutex); ++ ++out_pipeline_stop: ++ ipu7_isys_stream_cleanup(av); ++ ++out_return_buffers: ++ return_buffers(aq, VB2_BUF_STATE_QUEUED); ++ av->start_streaming = 0; ++ dev_dbg(dev, "%s: reset start streaming failed!\n", av->vdev.name); ++ return ret; ++} ++ ++static int ipu_isys_reset(struct ipu7_isys_video *self_av, ++ struct ipu7_isys_stream *self_stream) ++{ ++ struct ipu7_isys *isys = self_av->isys; ++ struct ipu7_bus_device *adev = isys->adev; ++ struct ipu7_isys_video *av = NULL; ++ struct ipu7_isys_stream *stream = NULL; ++ struct device *dev = &adev->auxdev.dev; ++ int i, j; ++ int has_streaming = 0; ++ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = ++ &isys->pdata->ipdata->csi2; ++ ++ mutex_lock(&isys->reset_mutex); ++ if (isys->state & RESET_STATE_IN_RESET) { ++ mutex_unlock(&isys->reset_mutex); ++ return 0; ++ } ++ isys->state |= RESET_STATE_IN_RESET; ++ dev_dbg(dev, "%s: %s\n", __func__, self_av->vdev.name); ++ ++ while (isys->state & RESET_STATE_IN_STOP_STREAMING) { ++ dev_dbg(dev, "isys reset: %s: wait for stop\n", ++ self_av->vdev.name); ++ mutex_unlock(&isys->reset_mutex); ++ usleep_range(10000, 11000); ++ mutex_lock(&isys->reset_mutex); ++ } ++ ++ mutex_unlock(&isys->reset_mutex); ++ ++ for (i = 0; i < csi2_pdata->nports; i++) { ++ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { ++ av = &isys->csi2[i].av[j]; ++ if (av == self_av) ++ continue; ++ ++ stream = av->stream; ++ if (!stream || stream == self_stream) ++ continue; ++ ++ if (!stream->streaming && !stream->nr_streaming) ++ continue; ++ ++ av->reset = true; ++ has_streaming = true; ++ reset_stop_streaming(av); ++ } ++ } ++ ++ if (!has_streaming) ++ goto end_of_reset; ++ ++ ipu7_cleanup_fw_msg_bufs(isys); ++ ++ for (j = 0; j < csi2_pdata->nports; j++) { ++ for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { ++ av = &isys->csi2[j].av[i]; ++ if (!av->reset) ++ continue; ++ ++ av->reset = false; ++ reset_start_streaming(av); ++ } ++ } ++ ++end_of_reset: ++ mutex_lock(&isys->reset_mutex); ++ isys->state &= ~RESET_STATE_IN_RESET; ++ mutex_unlock(&isys->reset_mutex); ++ dev_dbg(dev, "reset done\n"); ++ ++ return 0; ++} ++ + static void stop_streaming(struct vb2_queue *q) + { + struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); + struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); + struct ipu7_isys_stream *stream = av->stream; ++ int ret = 0; ++ ++ struct device *dev = &av->isys->adev->auxdev.dev; ++ bool need_reset; ++ ++ dev_dbg(dev, "stop: %s: enter\n", av->vdev.name); ++ ++ mutex_lock(&av->isys->reset_mutex); ++ while (av->isys->state) { ++ mutex_unlock(&av->isys->reset_mutex); ++ dev_dbg(dev, "stop: %s: wait for reset or stop, isys->state = %d\n", ++ av->vdev.name, av->isys->state); ++ usleep_range(10000, 11000); ++ mutex_lock(&av->isys->reset_mutex); ++ } ++ ++ if (!av->start_streaming) { ++ mutex_unlock(&av->isys->reset_mutex); ++ return_buffers(aq, VB2_BUF_STATE_ERROR); ++ return; ++ } ++ ++ av->isys->state |= RESET_STATE_IN_STOP_STREAMING; ++ mutex_unlock(&av->isys->reset_mutex); ++ ++ stream = av->stream; ++ if (!stream) { ++ dev_err(dev, "stop: %s: ip cleard!\n", av->vdev.name); ++ return_buffers(aq, VB2_BUF_STATE_ERROR); ++ mutex_lock(&av->isys->reset_mutex); ++ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; ++ mutex_unlock(&av->isys->reset_mutex); ++ return; ++ } + + mutex_lock(&stream->mutex); + mutex_lock(&av->isys->stream_mutex); + if (stream->nr_streaming == stream->nr_queues && stream->streaming) +- ipu7_isys_video_set_streaming(av, 0, NULL); ++ ret = ipu7_isys_video_set_streaming(av, 0, NULL); + mutex_unlock(&av->isys->stream_mutex); ++ if (ret) { ++ dev_err(dev, "stop: video set streaming failed\n"); ++ mutex_unlock(&stream->mutex); ++ return; ++ } ++ ++ stream->nr_streaming--; ++ list_del(&aq->node); ++ stream->streaming = 0; ++ ++ mutex_unlock(&stream->mutex); ++ ++ ipu7_isys_stream_cleanup(av); ++ ++ return_buffers(aq, VB2_BUF_STATE_ERROR); ++ ++ ipu7_isys_fw_close(av->isys); ++ ++ av->start_streaming = 0; ++ mutex_lock(&av->isys->reset_mutex); ++ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; ++ need_reset = av->isys->need_reset; ++ mutex_unlock(&av->isys->reset_mutex); ++ ++ if (need_reset) { ++ if (av->isys->stream_opened > 0) { ++ ipu_isys_reset(av, stream); ++ } else { ++ mutex_lock(&av->isys->reset_mutex); ++ av->isys->need_reset = false; ++ mutex_unlock(&av->isys->reset_mutex); ++ } ++ } ++ ++ dev_dbg(dev, "stop: %s: exit\n", av->vdev.name); ++} ++#else ++static void stop_streaming(struct vb2_queue *q) ++{ ++ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); ++ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); ++ struct ipu7_isys_stream *stream = av->stream; ++ int ret = 0; ++ ++ mutex_lock(&stream->mutex); ++ mutex_lock(&av->isys->stream_mutex); ++ if (stream->nr_streaming == stream->nr_queues && stream->streaming) ++ ret = ipu7_isys_video_set_streaming(av, 0, NULL); ++ mutex_unlock(&av->isys->stream_mutex); ++ if (ret) { ++ dev_err(&av->isys->adev->auxdev.dev, ++ "stop: video set streaming failed\n"); ++ mutex_unlock(&stream->mutex); ++ return; ++ } + + stream->nr_streaming--; + list_del(&aq->node); +@@ -644,6 +989,7 @@ static void stop_streaming(struct vb2_queue *q) + + ipu7_isys_fw_close(av->isys); + } ++#endif + + static unsigned int + get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) +@@ -725,6 +1071,11 @@ static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) + * to the userspace when it is de-queued + */ + atomic_set(&ib->str2mmio_flag, 0); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ } else if (atomic_read(&ib->skipframe_flag)) { ++ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); ++ atomic_set(&ib->skipframe_flag, 0); ++#endif + } else { + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } +diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.h b/drivers/staging/media/ipu7/ipu7-isys-queue.h +index 0cb08a38f7..5a909c3a78 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-queue.h ++++ b/drivers/staging/media/ipu7/ipu7-isys-queue.h +@@ -31,6 +31,9 @@ struct ipu7_isys_queue { + struct ipu7_isys_buffer { + struct list_head head; + atomic_t str2mmio_flag; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ atomic_t skipframe_flag; ++#endif + }; + + struct ipu7_isys_video_buffer { +diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c +index 7e89441f5a..cc0bbbc0f2 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-video.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-video.c +@@ -18,6 +18,9 @@ + #include + #include + #include ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++#include ++#endif + + #include + #include +@@ -93,6 +96,26 @@ static int video_open(struct file *file) + return v4l2_fh_open(file); + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++static int video_release(struct file *file) ++{ ++ struct ipu7_isys_video *av = video_drvdata(file); ++ ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "release: %s: enter\n", av->vdev.name); ++ mutex_lock(&av->isys->reset_mutex); ++ while (av->isys->state & RESET_STATE_IN_RESET) { ++ mutex_unlock(&av->isys->reset_mutex); ++ dev_dbg(&av->isys->adev->auxdev.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); ++ return vb2_fop_release(file); ++} ++#endif ++ + const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) + { + unsigned int i; +@@ -589,7 +612,11 @@ static void stop_streaming_firmware(struct ipu7_isys_video *av) + } + + tout = wait_for_completion_timeout(&stream->stream_stop_completion, ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ FW_CALL_TIMEOUT_JIFFIES_RESET); ++#else + FW_CALL_TIMEOUT_JIFFIES); ++#endif + if (!tout) + dev_warn(dev, "stream stop time out\n"); + else if (stream->error) +@@ -614,7 +641,11 @@ static void close_streaming_firmware(struct ipu7_isys_video *av) + } + + tout = wait_for_completion_timeout(&stream->stream_close_completion, ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ FW_CALL_TIMEOUT_JIFFIES_RESET); ++#else + FW_CALL_TIMEOUT_JIFFIES); ++#endif + if (!tout) + dev_warn(dev, "stream close time out\n"); + else if (stream->error) +@@ -622,6 +653,12 @@ static void close_streaming_firmware(struct ipu7_isys_video *av) + else + dev_dbg(dev, "close stream: complete\n"); + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ stream->last_sequence = atomic_read(&stream->sequence); ++ dev_dbg(dev, "ip->last_sequence = %d\n", ++ stream->last_sequence); ++ ++#endif + put_stream_opened(av); + } + +@@ -636,7 +673,18 @@ int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, + return -EINVAL; + + stream->nr_queues = nr_queues; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ if (av->isys->state & RESET_STATE_IN_RESET) { ++ atomic_set(&stream->sequence, stream->last_sequence); ++ dev_dbg(&av->isys->adev->auxdev.dev, ++ "atomic_set : stream->last_sequence = %d\n", ++ stream->last_sequence); ++ } else { ++ atomic_set(&stream->sequence, 0); ++ } ++#else + atomic_set(&stream->sequence, 0); ++#endif + atomic_set(&stream->buf_id, 0); + + stream->seq_index = 0; +@@ -872,7 +920,11 @@ static const struct v4l2_file_operations isys_fops = { + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .open = video_open, ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ .release = video_release, ++#else + .release = vb2_fop_release, ++#endif + }; + + int ipu7_isys_fw_open(struct ipu7_isys *isys) +@@ -911,6 +963,28 @@ int ipu7_isys_fw_open(struct ipu7_isys *isys) + return ret; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++void ipu7_isys_fw_close(struct ipu7_isys *isys) ++{ ++ mutex_lock(&isys->mutex); ++ ++ isys->ref_count--; ++ ++ if (!isys->ref_count) ++ ipu7_fw_isys_close(isys); ++ ++ mutex_unlock(&isys->mutex); ++ ++ mutex_lock(&isys->reset_mutex); ++ if (isys->need_reset) { ++ mutex_unlock(&isys->reset_mutex); ++ pm_runtime_put_sync(&isys->adev->auxdev.dev); ++ } else { ++ mutex_unlock(&isys->reset_mutex); ++ pm_runtime_put(&isys->adev->auxdev.dev); ++ } ++} ++#else + void ipu7_isys_fw_close(struct ipu7_isys *isys) + { + mutex_lock(&isys->mutex); +@@ -923,6 +997,7 @@ void ipu7_isys_fw_close(struct ipu7_isys *isys) + mutex_unlock(&isys->mutex); + pm_runtime_put(&isys->adev->auxdev.dev); + } ++#endif + + int ipu7_isys_setup_video(struct ipu7_isys_video *av, + struct media_entity **source_entity, int *nr_queues) +@@ -1058,6 +1133,12 @@ int ipu7_isys_video_init(struct ipu7_isys_video *av) + __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); + av->pix_fmt = format.fmt.pix; + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ av->reset = false; ++ av->skipframe = 0; ++ av->start_streaming = 0; ++#endif ++ + video_set_drvdata(&av->vdev, av); + + ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); +diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.h b/drivers/staging/media/ipu7/ipu7-isys-video.h +index 1ac1787fab..e6d1da2b7b 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-video.h ++++ b/drivers/staging/media/ipu7/ipu7-isys-video.h +@@ -53,6 +53,9 @@ struct ipu7_isys_stream { + struct mutex mutex; + struct media_entity *source_entity; + atomic_t sequence; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ int last_sequence; ++#endif + atomic_t buf_id; + unsigned int seq_index; + struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; +@@ -89,6 +92,11 @@ struct ipu7_isys_video { + unsigned int streaming; + u8 vc; + u8 dt; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ unsigned int reset; ++ unsigned int skipframe; ++ unsigned int start_streaming; ++#endif + }; + + #define ipu7_isys_queue_to_video(__aq) \ +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index 8a4b153a03..bbe78b26e4 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -371,6 +371,62 @@ static int isys_csi2_create_media_links(struct ipu7_isys *isys) + return 0; + } + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++static void isys_v4l2_notify(struct v4l2_subdev *sd, unsigned int notification, ++ void *arg) ++{ ++ struct ipu7_isys *isys = ++ container_of(sd->v4l2_dev, struct ipu7_isys, v4l2_dev); ++ struct device *dev = &isys->adev->auxdev.dev; ++ struct v4l2_event *ev = arg; ++ struct ipu7_isys_csi2_config *csi2_cfg; ++ unsigned int i; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&isys->power_lock, flags); ++ if (!isys->power) { ++ spin_unlock_irqrestore(&isys->power_lock, flags); ++ dev_dbg(dev, "%s: isys powered off, ignore notify %u\n", ++ sd->name, notification); ++ return; ++ } ++ spin_unlock_irqrestore(&isys->power_lock, flags); ++ ++ if (notification == V4L2_DEVICE_NOTIFY_EVENT) { ++ if (ev->type == V4L2_EVENT_SOURCE_CHANGE || ++ ev->type == V4L2_EVENT_EOS) { ++ csi2_cfg = v4l2_get_subdev_hostdata(sd); ++ if (!csi2_cfg) { ++ dev_warn(dev, "%s: missing csi2 cfg for notify %u\n", ++ sd->name, ev->type); ++ return; ++ } ++ ++ for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { ++ struct ipu7_isys_video *av = ++ &isys->csi2[csi2_cfg->port].av[i]; ++ ++ if (READ_ONCE(av->start_streaming)) { ++ dev_info(dev, ++ "%s: isys need reset due to notify %u on port %u\n", ++ sd->name, ev->type, csi2_cfg->port); ++ mutex_lock(&isys->reset_mutex); ++ isys->need_reset = true; ++ mutex_unlock(&isys->reset_mutex); ++ return; ++ } ++ } ++ dev_dbg(dev, ++ "%s: notify %u ignored, no AV starting on this port\n", ++ sd->name, notification); ++ } ++ } else { ++ dev_warn(dev, "%s: unknown notification %u\n", ++ sd->name, notification); ++ } ++} ++#endif ++ + static int isys_register_devices(struct ipu7_isys *isys) + { + struct device *dev = &isys->adev->auxdev.dev; +@@ -390,6 +446,9 @@ static int isys_register_devices(struct ipu7_isys *isys) + isys->v4l2_dev.mdev = &isys->media_dev; + isys->v4l2_dev.ctrl_handler = NULL; + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ isys->v4l2_dev.notify = isys_v4l2_notify; ++#endif + ret = v4l2_device_register(dev, &isys->v4l2_dev); + if (ret < 0) + goto out_media_device_unregister; +@@ -540,6 +599,12 @@ static int isys_runtime_pm_suspend(struct device *dev) + isys->power = 0; + spin_unlock_irqrestore(&isys->power_lock, flags); + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ mutex_lock(&isys->reset_mutex); ++ isys->need_reset = false; ++ mutex_unlock(&isys->reset_mutex); ++ ++#endif + cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); + + ipu7_mmu_hw_cleanup(adev->mmu); +@@ -596,6 +661,9 @@ static void isys_remove(struct auxiliary_device *auxdev) + + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->mutex); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ mutex_destroy(&isys->reset_mutex); ++#endif + } + + static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) +@@ -776,6 +844,10 @@ static int isys_probe(struct auxiliary_device *auxdev, + + mutex_init(&isys->mutex); + mutex_init(&isys->stream_mutex); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ mutex_init(&isys->reset_mutex); ++ isys->state = 0; ++#endif + + spin_lock_init(&isys->listlock); + INIT_LIST_HEAD(&isys->framebuflist); +@@ -817,6 +889,9 @@ static int isys_probe(struct auxiliary_device *auxdev, + out_cleanup_fw: + ipu7_fw_isys_release(isys); + out_cleanup_isys: ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ mutex_destroy(&isys->reset_mutex); ++#endif + cpu_latency_qos_remove_request(&isys->pm_qos); + + for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) +diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h +index d7f6b7bc54..48d207f095 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -44,8 +44,16 @@ + #define IPU_ISYS_MAX_WIDTH 8160U + #define IPU_ISYS_MAX_HEIGHT 8190U + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++#define RESET_STATE_IN_RESET 1U ++#define RESET_STATE_IN_STOP_STREAMING 2U ++ ++#endif + #define FW_CALL_TIMEOUT_JIFFIES \ + msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++#define FW_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) ++#endif + + struct isys_fw_log { + struct mutex mutex; /* protect whole struct */ +@@ -68,6 +76,9 @@ struct isys_fw_log { + * @streams_lock: serialise access to streams + * @streams: streams per firmware stream ID + * @syscom: fw communication layer context ++ #ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ * @need_reset: Isys requires d0i0->i3 transition ++ #endif + * @ref_count: total number of callers fw open + * @mutex: serialise access isys video open/release related operations + * @stream_mutex: serialise stream start and stop, queueing requests +@@ -109,6 +120,11 @@ struct ipu7_isys { + + struct ipu7_insys_config *subsys_config; + dma_addr_t subsys_config_dma_addr; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ struct mutex reset_mutex; ++ bool need_reset; ++ int state; ++#endif + }; + + struct isys_fw_msgs { diff --git a/patch/v6.18.3_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch b/patch/v6.18.3_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch new file mode 100644 index 0000000..7d5328b --- /dev/null +++ b/patch/v6.18.3_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch @@ -0,0 +1,267 @@ +From 4b774de376db579047de27c0a1cd0bc6699f70bd Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Mon, 20 Oct 2025 12:36:12 +0800 +Subject: [PATCH 05/26] patch: staging add enable CONFIG_DEBUG_FS + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/ipu7-isys.c | 84 ++++++++++++++++++++++++++ + drivers/staging/media/ipu7/ipu7-isys.h | 7 +++ + drivers/staging/media/ipu7/ipu7.c | 60 ++++++++++++++++++ + drivers/staging/media/ipu7/ipu7.h | 3 + + 4 files changed, 154 insertions(+) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index bbe78b26e4..e2ea828af7 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -9,6 +9,9 @@ + #include + #include + #include ++#ifdef CONFIG_DEBUG_FS ++#include ++#endif + #include + #include + #include +@@ -643,6 +646,10 @@ static void isys_remove(struct auxiliary_device *auxdev) + + adev->get_running_fw_task_count = NULL; + ++#ifdef CONFIG_DEBUG_FS ++ if (adev->isp->ipu7_dir) ++ debugfs_remove_recursive(isys->debugfsdir); ++#endif + for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) + mutex_destroy(&isys->streams[i].mutex); + +@@ -666,6 +673,78 @@ static void isys_remove(struct auxiliary_device *auxdev) + #endif + } + ++#ifdef CONFIG_DEBUG_FS ++static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, ++ loff_t *pos) ++{ ++ struct ipu7_isys *isys = file->private_data; ++ struct isys_fw_log *fw_log = isys->fw_log; ++ struct device *dev = &isys->adev->auxdev.dev; ++ u32 log_size; ++ int ret = 0; ++ void *buf; ++ ++ if (!fw_log) ++ return 0; ++ ++ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ mutex_lock(&fw_log->mutex); ++ if (!fw_log->size) { ++ dev_warn(dev, "no available fw log\n"); ++ mutex_unlock(&fw_log->mutex); ++ goto free_and_return; ++ } ++ ++ if (fw_log->size > FW_LOG_BUF_SIZE) ++ log_size = FW_LOG_BUF_SIZE; ++ else ++ log_size = fw_log->size; ++ ++ memcpy(buf, fw_log->addr, log_size); ++ dev_info(dev, "copy %d bytes fw log to user...\n", log_size); ++ mutex_unlock(&fw_log->mutex); ++ ++ ret = simple_read_from_buffer(userbuf, size, pos, buf, ++ log_size); ++free_and_return: ++ kvfree(buf); ++ ++ return ret; ++} ++ ++static const struct file_operations isys_fw_log_fops = { ++ .open = simple_open, ++ .owner = THIS_MODULE, ++ .read = fwlog_read, ++ .llseek = default_llseek, ++}; ++ ++static int ipu7_isys_init_debugfs(struct ipu7_isys *isys) ++{ ++ struct dentry *file; ++ struct dentry *dir; ++ ++ dir = debugfs_create_dir("isys", isys->adev->isp->ipu7_dir); ++ if (IS_ERR(dir)) ++ return -ENOMEM; ++ ++ file = debugfs_create_file("fwlog", 0400, ++ dir, isys, &isys_fw_log_fops); ++ if (IS_ERR(file)) ++ goto err; ++ ++ isys->debugfsdir = dir; ++ ++ return 0; ++err: ++ debugfs_remove_recursive(dir); ++ return -ENOMEM; ++} ++#endif ++ + static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) + { + struct ipu7_bus_device *adev = isys->adev; +@@ -862,6 +941,11 @@ static int isys_probe(struct auxiliary_device *auxdev, + + isys_stream_init(isys); + ++#ifdef CONFIG_DEBUG_FS ++ /* Debug fs failure is not fatal. */ ++ ipu7_isys_init_debugfs(isys); ++#endif ++ + cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); + ret = alloc_fw_msg_bufs(isys, 20); + if (ret < 0) +diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h +index 48d207f095..60520834fd 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -25,6 +25,10 @@ + #include "ipu7-isys-csi2.h" + #include "ipu7-isys-video.h" + ++#ifdef CONFIG_DEBUG_FS ++struct dentry; ++ ++#endif + #define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" + + /* FW support max 16 streams */ +@@ -103,6 +107,9 @@ struct ipu7_isys { + unsigned int ref_count; + unsigned int stream_opened; + ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *debugfsdir; ++#endif + struct mutex mutex; /* Serialise isys video open/release related */ + struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ + +diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c +index 5cddc09c72..a4b0264cc5 100644 +--- a/drivers/staging/media/ipu7/ipu7.c ++++ b/drivers/staging/media/ipu7/ipu7.c +@@ -7,6 +7,9 @@ + #include + #include + #include ++#ifdef CONFIG_DEBUG_FS ++#include ++#endif + #include + #include + #include +@@ -2247,6 +2250,49 @@ void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) + } + EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); + ++#ifdef CONFIG_DEBUG_FS ++static struct debugfs_blob_wrapper isys_fw_error; ++static struct debugfs_blob_wrapper psys_fw_error; ++ ++static int ipu7_init_debugfs(struct ipu7_device *isp) ++{ ++ struct dentry *file; ++ struct dentry *dir; ++ ++ dir = debugfs_create_dir(pci_name(isp->pdev), NULL); ++ if (!dir) ++ return -ENOMEM; ++ ++ isys_fw_error.data = &fw_error_log[IPU_IS]; ++ isys_fw_error.size = sizeof(fw_error_log[IPU_IS]); ++ file = debugfs_create_blob("is_fw_error", 0400, dir, &isys_fw_error); ++ if (!file) ++ goto err; ++ psys_fw_error.data = &fw_error_log[IPU_PS]; ++ psys_fw_error.size = sizeof(fw_error_log[IPU_PS]); ++ file = debugfs_create_blob("ps_fw_error", 0400, dir, &psys_fw_error); ++ if (!file) ++ goto err; ++ ++ isp->ipu7_dir = dir; ++ ++ return 0; ++err: ++ debugfs_remove_recursive(dir); ++ return -ENOMEM; ++} ++ ++static void ipu7_remove_debugfs(struct ipu7_device *isp) ++{ ++ /* ++ * Since isys and psys debugfs dir will be created under ipu root dir, ++ * mark its dentry to NULL to avoid duplicate removal. ++ */ ++ debugfs_remove_recursive(isp->ipu7_dir); ++ isp->ipu7_dir = NULL; ++} ++#endif /* CONFIG_DEBUG_FS */ ++ + static void ipu7_pci_config_setup(struct pci_dev *dev) + { + u16 pci_command; +@@ -2603,6 +2649,13 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) + pm_runtime_put(&isp->psys->auxdev.dev); + } + ++#ifdef CONFIG_DEBUG_FS ++ ret = ipu7_init_debugfs(isp); ++ if (ret) { ++ dev_err_probe(dev, ret, "Failed to initialize debugfs\n"); ++ goto out_ipu_bus_del_devices; ++ } ++#endif + pm_runtime_put_noidle(dev); + pm_runtime_allow(dev); + +@@ -2615,6 +2668,10 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) + ipu7_unmap_fw_code_region(isp->isys); + if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) + ipu7_unmap_fw_code_region(isp->psys); ++#ifdef CONFIG_DEBUG_FS ++ if (!IS_ERR_OR_NULL(isp->fw_code_region)) ++ vfree(isp->fw_code_region); ++#endif + if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) + ipu7_mmu_cleanup(isp->psys->mmu); + if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) +@@ -2635,6 +2692,9 @@ static void ipu7_pci_remove(struct pci_dev *pdev) + { + struct ipu7_device *isp = pci_get_drvdata(pdev); + ++#ifdef CONFIG_DEBUG_FS ++ ipu7_remove_debugfs(isp); ++#endif + if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) + ipu7_unmap_fw_code_region(isp->isys); + if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) +diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h +index ac8ac06894..3b3ea5fb61 100644 +--- a/drivers/staging/media/ipu7/ipu7.h ++++ b/drivers/staging/media/ipu7/ipu7.h +@@ -81,6 +81,9 @@ struct ipu7_device { + + void __iomem *base; + void __iomem *pb_base; ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *ipu7_dir; ++#endif + u8 hw_ver; + bool ipc_reinit; + bool secure_mode; diff --git a/patch/v6.18.3_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch b/patch/v6.18.3_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch new file mode 100644 index 0000000..3d4e000 --- /dev/null +++ b/patch/v6.18.3_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch @@ -0,0 +1,109 @@ +From fddac4ea199f257ea3cbc67d02871c90cfe4742e Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 12:38:44 +0800 +Subject: [PATCH 07/26] patch: staging add enable ENABLE_FW_OFFLINE_LOGGER + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/ipu7-fw-isys.c | 59 +++++++++++++++++++++++ + drivers/staging/media/ipu7/ipu7-fw-isys.h | 3 ++ + drivers/staging/media/ipu7/ipu7-isys.c | 4 ++ + 3 files changed, 66 insertions(+) + +diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c +index e4b9c36457..1ece286c29 100644 +--- a/drivers/staging/media/ipu7/ipu7-fw-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c +@@ -205,6 +205,65 @@ void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) + ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); + } + ++#ifdef ENABLE_FW_OFFLINE_LOGGER ++int ipu7_fw_isys_get_log(struct ipu7_isys *isys) ++{ ++ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); ++ struct device *dev = &isys->adev->auxdev.dev; ++ struct isys_fw_log *fw_log = isys->fw_log; ++ struct ia_gofo_msg_log *log_msg; ++ u8 msg_type, msg_len; ++ u32 count, fmt_id; ++ void *token; ++ ++ token = ipu7_syscom_get_token(isys->adev->syscom, ++ IPU_INSYS_OUTPUT_LOG_QUEUE); ++ if (!token) ++ return -ENODATA; ++ ++ while (token) { ++ log_msg = (struct ia_gofo_msg_log *)token; ++ ++ msg_type = log_msg->header.tlv_header.tlv_type; ++ msg_len = log_msg->header.tlv_header.tlv_len32; ++ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) ++ dev_warn(dev, "Invalid msg data from Log queue!\n"); ++ ++ count = log_msg->log_info_ts.log_info.log_counter; ++ fmt_id = log_msg->log_info_ts.log_info.fmt_id; ++ if (count > fw_log->count + 1) ++ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", ++ count, fw_log->count); ++ ++ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { ++ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); ++ ipu7_syscom_put_token(isys->adev->syscom, ++ IPU_INSYS_OUTPUT_LOG_QUEUE); ++ return -EIO; ++ } ++ ++ if (log_size + fw_log->head - fw_log->addr > ++ FW_LOG_BUF_SIZE) ++ fw_log->head = fw_log->addr; ++ ++ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, ++ sizeof(struct ia_gofo_msg_log_info_ts)); ++ ++ fw_log->count = count; ++ fw_log->head += log_size; ++ fw_log->size += log_size; ++ ++ ipu7_syscom_put_token(isys->adev->syscom, ++ IPU_INSYS_OUTPUT_LOG_QUEUE); ++ ++ token = ipu7_syscom_get_token(isys->adev->syscom, ++ IPU_INSYS_OUTPUT_LOG_QUEUE); ++ }; ++ ++ return 0; ++} ++ ++#endif + void ipu7_fw_isys_dump_stream_cfg(struct device *dev, + struct ipu7_insys_stream_cfg *cfg) + { +diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.h b/drivers/staging/media/ipu7/ipu7-fw-isys.h +index b556feda6b..1235adc969 100644 +--- a/drivers/staging/media/ipu7/ipu7-fw-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-fw-isys.h +@@ -36,4 +36,7 @@ int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, + size_t size, u16 send_type); + struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); + void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); ++#ifdef ENABLE_FW_OFFLINE_LOGGER ++int ipu7_fw_isys_get_log(struct ipu7_isys *isys); ++#endif + #endif +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index fffd29a09f..389e75ac76 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -1323,6 +1323,10 @@ int isys_isr_one(struct ipu7_bus_device *adev) + if (!isys->adev->syscom) + return 1; + ++#ifdef ENABLE_FW_OFFLINE_LOGGER ++ ipu7_fw_isys_get_log(isys); ++#endif ++ + resp = ipu7_fw_isys_get_resp(isys); + if (!resp) + return 1; diff --git a/patch/v6.18.3_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch b/patch/v6.18.3_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch new file mode 100644 index 0000000..722536b --- /dev/null +++ b/patch/v6.18.3_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch @@ -0,0 +1,30 @@ +From 7007f57d64eb215395b347d93a693f6e17d85754 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 12:44:25 +0800 +Subject: [PATCH 08/26] patch: staging add patch for use DPHY as the default + phy mode + +Signed-off-by: Bingbu Cao +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/ipu7-isys.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index 389e75ac76..1936444722 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -84,10 +84,10 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, + } + + isys->csi2[csi2->port].nlanes = csi2->nlanes; +- if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) +- isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; +- else ++ if (csi2->bus_type == V4L2_MBUS_CSI2_CPHY) + isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; ++ else ++ isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; + + return 0; + diff --git a/patch/v6.18.3_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch b/patch/v6.18.3_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch new file mode 100644 index 0000000..0009fcf --- /dev/null +++ b/patch/v6.18.3_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch @@ -0,0 +1,75 @@ +From 4b7db21b71607dee0f3ae7e2d1d9a017223dc612 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 15:39:28 +0800 +Subject: [PATCH 09/26] patch: staging add pacth for ipu7 Kconfig Makefile + +Support kernel v6.10 + +Due to change "kbuild: use $(src) instead of $(srctree)/$(src) for +source directory" at https://lore.kernel.org/lkml/20240416121838. +95427-5-masahiroy@kernel.org/, the old include paths in Makefile +can't work on kernel v6.10. To keep compatible with < v6.10 kernel, +add another check in Makefile. + +Signed-off-by: linya14x +Signed-off-by: Hao Yao +--- + drivers/staging/media/ipu7/Kconfig | 11 ++++++++--- + drivers/staging/media/ipu7/Makefile | 11 +++++++++++ + 2 files changed, 19 insertions(+), 3 deletions(-) + +diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig +index c4eee7c3e6..0a6e68c763 100644 +--- a/drivers/staging/media/ipu7/Kconfig ++++ b/drivers/staging/media/ipu7/Kconfig +@@ -4,7 +4,12 @@ config VIDEO_INTEL_IPU7 + depends on VIDEO_DEV + depends on X86 && HAS_DMA + depends on IPU_BRIDGE || !IPU_BRIDGE +- depends on PCI ++ # ++ # This driver incorrectly tries to override the dma_ops. It should ++ # never have done that, but for now keep it working on architectures ++ # that use dma ops ++ # ++ depends on ARCH_HAS_DMA_OPS + select AUXILIARY_BUS + select IOMMU_IOVA + select VIDEO_V4L2_SUBDEV_API +@@ -15,8 +20,8 @@ config VIDEO_INTEL_IPU7 + This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs + and used for capturing images and video from camera sensors. + +- To compile this driver, say Y here! It contains 2 modules - +- intel_ipu7 and intel_ipu7_isys. ++ To compile this driver, say Y here! It contains 3 modules - ++ intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. + + config VIDEO_INTEL_IPU7_ISYS_RESET + bool "IPU7 ISYS RESET" +diff --git a/drivers/staging/media/ipu7/Makefile b/drivers/staging/media/ipu7/Makefile +index 6d2aec219e..3c35ca5664 100644 +--- a/drivers/staging/media/ipu7/Makefile ++++ b/drivers/staging/media/ipu7/Makefile +@@ -1,6 +1,13 @@ + # SPDX-License-Identifier: GPL-2.0 + # Copyright (c) 2017 - 2025 Intel Corporation. + ++is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) ++ifeq ($(is_kernel_lt_6_10), 1) ++ifneq ($(EXTERNAL_BUILD), 1) ++src := $(srctree)/$(src) ++endif ++endif ++ + intel-ipu7-objs += ipu7.o \ + ipu7-bus.o \ + ipu7-dma.o \ +@@ -21,3 +28,7 @@ intel-ipu7-isys-objs += ipu7-isys.o \ + ipu7-isys-subdev.o + + obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o ++ ++obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ ++ ++ccflags-y += -I$(src)/ diff --git a/patch/v6.18.3_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch b/patch/v6.18.3_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch new file mode 100644 index 0000000..fa3c3df --- /dev/null +++ b/patch/v6.18.3_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch @@ -0,0 +1,811 @@ +From 936b4fdabd9efb082dd45403343d82e874dfda00 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 16:47:40 +0800 +Subject: [PATCH 10/26] patch: staging add ipu7 isys tpg and MGC config + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/Kconfig | 11 + + drivers/staging/media/ipu7/ipu7-isys-tpg.c | 693 +++++++++++++++++++++ + drivers/staging/media/ipu7/ipu7-isys-tpg.h | 70 +++ + 3 files changed, 774 insertions(+) + create mode 100644 drivers/staging/media/ipu7/ipu7-isys-tpg.c + create mode 100644 drivers/staging/media/ipu7/ipu7-isys-tpg.h + +diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig +index 0a6e68c763..91954fdadc 100644 +--- a/drivers/staging/media/ipu7/Kconfig ++++ b/drivers/staging/media/ipu7/Kconfig +@@ -23,6 +23,17 @@ config VIDEO_INTEL_IPU7 + To compile this driver, say Y here! It contains 3 modules - + intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. + ++config VIDEO_INTEL_IPU7_MGC ++ bool "Compile for IPU7 MGC driver" ++ depends on VIDEO_INTEL_IPU7 ++ help ++ If selected, MGC device nodes would be created. ++ ++ Recommended for driver developers only. ++ ++ If you want to the MGC devices exposed to user as media entity, ++ you must select this option, otherwise no. ++ + config VIDEO_INTEL_IPU7_ISYS_RESET + bool "IPU7 ISYS RESET" + depends on VIDEO_INTEL_IPU7 +diff --git a/drivers/staging/media/ipu7/ipu7-isys-tpg.c b/drivers/staging/media/ipu7/ipu7-isys-tpg.c +new file mode 100644 +index 0000000000..35b6298e4f +--- /dev/null ++++ b/drivers/staging/media/ipu7/ipu7-isys-tpg.c +@@ -0,0 +1,693 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * Copyright (C) 2013 - 2025 Intel Corporation ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "ipu7.h" ++#include "ipu7-bus.h" ++#include "ipu7-buttress-regs.h" ++#include "ipu7-isys.h" ++#include "ipu7-isys-subdev.h" ++#include "ipu7-isys-tpg.h" ++#include "ipu7-isys-video.h" ++#include "ipu7-isys-csi2-regs.h" ++#include "ipu7-platform-regs.h" ++ ++static const u32 tpg_supported_codes[] = { ++ MEDIA_BUS_FMT_SBGGR8_1X8, ++ MEDIA_BUS_FMT_SGBRG8_1X8, ++ MEDIA_BUS_FMT_SGRBG8_1X8, ++ MEDIA_BUS_FMT_SRGGB8_1X8, ++ MEDIA_BUS_FMT_SBGGR10_1X10, ++ MEDIA_BUS_FMT_SGBRG10_1X10, ++ MEDIA_BUS_FMT_SGRBG10_1X10, ++ MEDIA_BUS_FMT_SRGGB10_1X10, ++ MEDIA_BUS_FMT_SBGGR12_1X12, ++ MEDIA_BUS_FMT_SGBRG12_1X12, ++ MEDIA_BUS_FMT_SGRBG12_1X12, ++ MEDIA_BUS_FMT_SRGGB12_1X12, ++ 0, ++}; ++ ++#define IPU_ISYS_FREQ 533000000UL ++ ++static u32 isys_mbus_code_to_bpp(u32 code) ++{ ++ switch (code) { ++ case MEDIA_BUS_FMT_RGB888_1X24: ++ return 24; ++ case MEDIA_BUS_FMT_YUYV10_1X20: ++ return 20; ++ case MEDIA_BUS_FMT_Y10_1X10: ++ case MEDIA_BUS_FMT_RGB565_1X16: ++ case MEDIA_BUS_FMT_UYVY8_1X16: ++ case MEDIA_BUS_FMT_YUYV8_1X16: ++ return 16; ++ case MEDIA_BUS_FMT_SBGGR12_1X12: ++ case MEDIA_BUS_FMT_SGBRG12_1X12: ++ case MEDIA_BUS_FMT_SGRBG12_1X12: ++ case MEDIA_BUS_FMT_SRGGB12_1X12: ++ return 12; ++ case MEDIA_BUS_FMT_SBGGR10_1X10: ++ case MEDIA_BUS_FMT_SGBRG10_1X10: ++ case MEDIA_BUS_FMT_SGRBG10_1X10: ++ case MEDIA_BUS_FMT_SRGGB10_1X10: ++ return 10; ++ case MEDIA_BUS_FMT_SBGGR8_1X8: ++ case MEDIA_BUS_FMT_SGBRG8_1X8: ++ case MEDIA_BUS_FMT_SGRBG8_1X8: ++ case MEDIA_BUS_FMT_SRGGB8_1X8: ++ return 8; ++ default: ++ WARN_ON(1); ++ return 0; ++ } ++} ++ ++static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { ++ .s_stream = tpg_set_stream, ++}; ++ ++static int ipu7_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ipu7_isys_tpg *tpg = container_of(container_of(ctrl->handler, ++ struct ++ ipu7_isys_subdev, ++ ctrl_handler), ++ struct ipu7_isys_tpg, asd); ++ switch (ctrl->id) { ++ case V4L2_CID_HBLANK: ++ writel(ctrl->val, tpg->base + MGC_MG_HBLANK); ++ break; ++ case V4L2_CID_VBLANK: ++ writel(ctrl->val, tpg->base + MGC_MG_VBLANK); ++ break; ++ case V4L2_CID_TEST_PATTERN: ++ writel(ctrl->val, tpg->base + MGC_MG_TPG_MODE); ++ break; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops ipu7_isys_tpg_ctrl_ops = { ++ .s_ctrl = ipu7_isys_tpg_s_ctrl, ++}; ++ ++static u64 ipu7_isys_tpg_rate(struct ipu7_isys_tpg *tpg, unsigned int bpp) ++{ ++ return MGC_PPC * IPU_ISYS_FREQ / bpp; ++} ++ ++static const char *const tpg_mode_items[] = { ++ "Ramp", ++ "Checkerboard", ++ "Monochrome per frame", ++ "Color palette", ++}; ++ ++static struct v4l2_ctrl_config tpg_mode = { ++ .ops = &ipu7_isys_tpg_ctrl_ops, ++ .id = V4L2_CID_TEST_PATTERN, ++ .name = "Test Pattern", ++ .type = V4L2_CTRL_TYPE_MENU, ++ .min = TPG_MODE_RAMP, ++ .max = ARRAY_SIZE(tpg_mode_items) - 1, ++ .def = TPG_MODE_COLOR_PALETTE, ++ .menu_skip_mask = 0x2, ++ .qmenu = tpg_mode_items, ++}; ++ ++static void ipu7_isys_tpg_init_controls(struct v4l2_subdev *sd) ++{ ++ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); ++ int hblank; ++ u64 default_pixel_rate; ++ ++ hblank = 1024; ++ ++ tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, ++ &ipu7_isys_tpg_ctrl_ops, ++ V4L2_CID_HBLANK, 8, 65535, 1, hblank); ++ ++ tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, ++ &ipu7_isys_tpg_ctrl_ops, ++ V4L2_CID_VBLANK, 8, 65535, 1, 1024); ++ ++ default_pixel_rate = ipu7_isys_tpg_rate(tpg, 8); ++ tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, ++ &ipu7_isys_tpg_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, ++ default_pixel_rate, ++ default_pixel_rate, ++ 1, default_pixel_rate); ++ if (tpg->pixel_rate) { ++ tpg->pixel_rate->cur.val = default_pixel_rate; ++ tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; ++ } ++ ++ v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); ++} ++ ++static int tpg_sd_init_cfg(struct v4l2_subdev *sd, ++ struct v4l2_subdev_state *state) ++{ ++ struct v4l2_subdev_route routes[] = { ++ { ++ .source_pad = 0, ++ .source_stream = 0, ++ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, ++ } ++ }; ++ ++ struct v4l2_subdev_krouting routing = { ++ .num_routes = 1, ++ .routes = routes, ++ }; ++ ++ static const struct v4l2_mbus_framefmt format = { ++ .width = 1920, ++ .height = 1080, ++ .code = MEDIA_BUS_FMT_SBGGR10_1X10, ++ .field = V4L2_FIELD_NONE, ++ }; ++ ++ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &format); ++} ++ ++static const struct v4l2_subdev_internal_ops ipu7_isys_tpg_internal_ops = { ++ .init_state = tpg_sd_init_cfg, ++}; ++ ++static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { ++ .get_fmt = v4l2_subdev_get_fmt, ++ .set_fmt = ipu7_isys_subdev_set_fmt, ++ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, ++}; ++ ++static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, ++ struct v4l2_event_subscription *sub) ++{ ++ switch (sub->type) { ++ case V4L2_EVENT_FRAME_SYNC: ++ return v4l2_event_subscribe(fh, sub, 10, NULL); ++ case V4L2_EVENT_CTRL: ++ return v4l2_ctrl_subscribe_event(fh, sub); ++ default: ++ return -EINVAL; ++ } ++}; ++ ++/* V4L2 subdev core operations */ ++static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { ++ .subscribe_event = subscribe_event, ++ .unsubscribe_event = v4l2_event_subdev_unsubscribe, ++}; ++ ++static const struct v4l2_subdev_ops tpg_sd_ops = { ++ .core = &tpg_sd_core_ops, ++ .video = &tpg_sd_video_ops, ++ .pad = &tpg_sd_pad_ops, ++}; ++ ++static struct media_entity_operations tpg_entity_ops = { ++ .link_validate = v4l2_subdev_link_validate, ++}; ++ ++void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream) ++{ ++ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); ++ struct video_device *vdev = tpg->asd.sd.devnode; ++ struct v4l2_event ev = { ++ .type = V4L2_EVENT_FRAME_SYNC, ++ }; ++ ++ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); ++ ++ v4l2_event_queue(vdev, &ev); ++ ++ dev_dbg(&stream->isys->adev->auxdev.dev, ++ "sof_event::tpg-%i sequence: %i\n", tpg->index, ++ ev.u.frame_sync.frame_sequence); ++} ++ ++void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream) ++{ ++ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); ++ u32 frame_sequence = atomic_read(&stream->sequence); ++ ++ dev_dbg(&stream->isys->adev->auxdev.dev, ++ "eof_event::tpg-%i sequence: %i\n", ++ tpg->index, frame_sequence); ++} ++ ++#define DEFAULT_VC_ID 0 ++static bool is_metadata_enabled(const struct ipu7_isys_tpg *tpg) ++{ ++ return false; ++} ++ ++static void ipu7_mipigen_regdump(const struct ipu7_isys_tpg *tpg, ++ void __iomem *mg_base) ++{ ++ struct device *dev = &tpg->isys->adev->auxdev.dev; ++ ++ dev_dbg(dev, "---------MGC REG DUMP START----------"); ++ ++ dev_dbg(dev, "MGC RX_TYPE_REG 0x%x = 0x%x", ++ MGC_MG_CSI_ADAPT_LAYER_TYPE, ++ readl(mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE)); ++ dev_dbg(dev, "MGC MG_MODE_REG 0x%x = 0x%x", ++ MGC_MG_MODE, readl(mg_base + MGC_MG_MODE)); ++ dev_dbg(dev, "MGC MIPI_VC_REG 0x%x = 0x%x", ++ MGC_MG_MIPI_VC, readl(mg_base + MGC_MG_MIPI_VC)); ++ dev_dbg(dev, "MGC MIPI_DTYPES_REG 0x%x = 0x%x", ++ MGC_MG_MIPI_DTYPES, readl(mg_base + MGC_MG_MIPI_DTYPES)); ++ dev_dbg(dev, "MGC MULTI_DTYPES_REG 0x%x = 0x%x", ++ MGC_MG_MULTI_DTYPES_MODE, ++ readl(mg_base + MGC_MG_MULTI_DTYPES_MODE)); ++ dev_dbg(dev, "MGC NOF_FRAMES_REG 0x%x = 0x%x", ++ MGC_MG_NOF_FRAMES, readl(mg_base + MGC_MG_NOF_FRAMES)); ++ dev_dbg(dev, "MGC FRAME_DIM_REG 0x%x = 0x%x", ++ MGC_MG_FRAME_DIM, readl(mg_base + MGC_MG_FRAME_DIM)); ++ dev_dbg(dev, "MGC HBLANK_REG 0x%x = 0x%x", ++ MGC_MG_HBLANK, readl(mg_base + MGC_MG_HBLANK)); ++ dev_dbg(dev, "MGC VBLANK_REG 0x%x = 0x%x", ++ MGC_MG_VBLANK, readl(mg_base + MGC_MG_VBLANK)); ++ dev_dbg(dev, "MGC TPG_MODE_REG 0x%x = 0x%x", ++ MGC_MG_TPG_MODE, readl(mg_base + MGC_MG_TPG_MODE)); ++ dev_dbg(dev, "MGC R0=0x%x G0=0x%x B0=0x%x", ++ readl(mg_base + MGC_MG_TPG_R0), ++ readl(mg_base + MGC_MG_TPG_G0), ++ readl(mg_base + MGC_MG_TPG_B0)); ++ dev_dbg(dev, "MGC R1=0x%x G1=0x%x B1=0x%x", ++ readl(mg_base + MGC_MG_TPG_R1), ++ readl(mg_base + MGC_MG_TPG_G1), ++ readl(mg_base + MGC_MG_TPG_B1)); ++ dev_dbg(dev, "MGC TPG_MASKS_REG 0x%x = 0x%x", ++ MGC_MG_TPG_MASKS, readl(mg_base + MGC_MG_TPG_MASKS)); ++ dev_dbg(dev, "MGC TPG_XY_MASK_REG 0x%x = 0x%x", ++ MGC_MG_TPG_XY_MASK, readl(mg_base + MGC_MG_TPG_XY_MASK)); ++ dev_dbg(dev, "MGC TPG_TILE_DIM_REG 0x%x = 0x%x", ++ MGC_MG_TPG_TILE_DIM, readl(mg_base + MGC_MG_TPG_TILE_DIM)); ++ dev_dbg(dev, "MGC DTO_SPEED_CTRL_EN_REG 0x%x = 0x%x", ++ MGC_MG_DTO_SPEED_CTRL_EN, ++ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_EN)); ++ dev_dbg(dev, "MGC DTO_SPEED_CTRL_INCR_VAL_REG 0x%x = 0x%x", ++ MGC_MG_DTO_SPEED_CTRL_INCR_VAL, ++ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL)); ++ dev_dbg(dev, "MGC MG_FRAME_NUM_STTS 0x%x = 0x%x", ++ MGC_MG_FRAME_NUM_STTS, ++ readl(mg_base + MGC_MG_FRAME_NUM_STTS)); ++ ++ dev_dbg(dev, "---------MGC REG DUMP END----------"); ++} ++ ++#define TPG_STOP_TIMEOUT 500000 ++static int tpg_stop_stream(const struct ipu7_isys_tpg *tpg) ++{ ++ struct device *dev = &tpg->isys->adev->auxdev.dev; ++ int ret; ++ unsigned int port; ++ u32 status; ++ void __iomem *reg; ++ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; ++ void __iomem *mg_base = tpg->base; ++ ++ port = 1 << tpg->index; ++ ++ dev_dbg(dev, "MG%d generated %u frames", tpg->index, ++ readl(mgc_base + MGC_MG_FRAME_NUM_STTS)); ++ writel(port, mgc_base + MGC_ASYNC_STOP); ++ ++ dev_dbg(dev, "wait for MG%d stop", tpg->index); ++ ++ reg = mg_base + MGC_MG_STOPPED_STTS; ++ ret = readl_poll_timeout(reg, status, status & 0x1, 200, ++ TPG_STOP_TIMEOUT); ++ if (ret < 0) { ++ dev_err(dev, "mgc stop timeout"); ++ return ret; ++ } ++ ++ dev_dbg(dev, "MG%d STOPPED", tpg->index); ++ ++ return 0; ++} ++ ++#define IS_IO_CLK (IPU7_IS_FREQ_CTL_DEFAULT_RATIO * 100 / 6) ++#define TPG_FRAME_RATE 30 ++#define TPG_BLANK_RATIO (4 / 3) ++static void tpg_get_timing(const struct ipu7_isys_tpg *tpg, u32 *dto, ++ u32 *hblank_cycles, u32 *vblank_cycles) ++{ ++ struct v4l2_mbus_framefmt format; ++ u32 width, height; ++ u32 code; ++ u32 bpp; ++ u32 bits_per_line; ++ u64 line_time_ns, frame_time_us, cycles, ns_per_cycle, rate; ++ u64 vblank_us, hblank_us; ++ u32 ref_clk; ++ struct device *dev = &tpg->isys->adev->auxdev.dev; ++ u32 dto_incr_val = 0x100; ++ int ret; ++ ++ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, ++ 0, 0, &format); ++ if (ret) ++ return; ++ ++ width = format.width; ++ height = format.height; ++ code = format.code; ++ ++ bpp = isys_mbus_code_to_bpp(code); ++ if (!bpp) ++ return; ++ ++ dev_dbg(dev, "MG%d code = 0x%x bpp = %u\n", tpg->index, code, bpp); ++ bits_per_line = width * bpp * TPG_BLANK_RATIO; ++ ++ cycles = div_u64(bits_per_line, 64); ++ dev_dbg(dev, "MG%d bits_per_line = %u cycles = %llu\n", tpg->index, ++ bits_per_line, cycles); ++ ++ do { ++ dev_dbg(dev, "MG%d try dto_incr_val 0x%x\n", tpg->index, ++ dto_incr_val); ++ rate = div_u64(1 << 16, dto_incr_val); ++ ns_per_cycle = div_u64(rate * 1000, IS_IO_CLK); ++ dev_dbg(dev, "MG%d ns_per_cycles = %llu\n", tpg->index, ++ ns_per_cycle); ++ ++ line_time_ns = cycles * ns_per_cycle; ++ frame_time_us = line_time_ns * height / 1000; ++ dev_dbg(dev, "MG%d line_time_ns = %llu frame_time_us = %llu\n", ++ tpg->index, line_time_ns, frame_time_us); ++ ++ if (frame_time_us * TPG_FRAME_RATE < USEC_PER_SEC) ++ break; ++ ++ /* dto incr val step 0x100 */ ++ dto_incr_val += 0x100; ++ } while (dto_incr_val < (1 << 16)); ++ ++ if (dto_incr_val >= (1 << 16)) { ++ dev_warn(dev, "No DTO_INCR_VAL found\n"); ++ hblank_us = 10; /* 10us */ ++ vblank_us = 10000; /* 10ms */ ++ dto_incr_val = 0x1000; ++ } else { ++ hblank_us = line_time_ns * (TPG_BLANK_RATIO - 1) / 1000; ++ vblank_us = div_u64(1000000, TPG_FRAME_RATE) - frame_time_us; ++ } ++ ++ dev_dbg(dev, "hblank_us = %llu, vblank_us = %llu dto_incr_val = %u\n", ++ hblank_us, vblank_us, dto_incr_val); ++ ++ ref_clk = tpg->isys->adev->isp->buttress.ref_clk; ++ ++ *dto = dto_incr_val; ++ *hblank_cycles = hblank_us * ref_clk / 10; ++ *vblank_cycles = vblank_us * ref_clk / 10; ++ dev_dbg(dev, "hblank_cycles = %u, vblank_cycles = %u\n", ++ *hblank_cycles, *vblank_cycles); ++} ++ ++static int tpg_start_stream(const struct ipu7_isys_tpg *tpg) ++{ ++ struct v4l2_mbus_framefmt format; ++ u32 port_map; ++ u32 csi_port; ++ u32 code, bpp; ++ u32 width, height; ++ u32 dto, hblank, vblank; ++ struct device *dev = &tpg->isys->adev->auxdev.dev; ++ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; ++ void __iomem *mg_base = tpg->base; ++ int ret; ++ ++ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, ++ 0, 0, &format); ++ if (ret) ++ return ret; ++ ++ width = format.width; ++ height = format.height; ++ code = format.code; ++ dev_dbg(dev, "MG%d code: 0x%x resolution: %ux%u\n", ++ tpg->index, code, width, height); ++ bpp = isys_mbus_code_to_bpp(code); ++ if (!bpp) ++ return -EINVAL; ++ ++ csi_port = tpg->index; ++ if (csi_port >= 4) ++ dev_err(dev, "invalid tpg index %u\n", tpg->index); ++ ++ dev_dbg(dev, "INSYS MG%d was mapped to CSI%d\n", ++ DEFAULT_VC_ID, csi_port); ++ ++ /* config port map ++ * TODO: add VC support and TPG with multiple ++ * source pads. Currently, for simplicity, only map 1 mg to 1 csi port ++ */ ++ port_map = 1 << tpg->index; ++ writel(port_map, mgc_base + MGC_CSI_PORT_MAP(csi_port)); ++ ++ /* configure adapt layer type */ ++ writel(1, mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE); ++ ++ /* configure MGC mode ++ * 0 - Disable MGC ++ * 1 - Enable PRBS ++ * 2 - Enable TPG ++ * 3 - Reserved [Write phase: SW/FW debug] ++ */ ++ writel(2, mg_base + MGC_MG_MODE); ++ ++ /* config mg init counter */ ++ writel(0, mg_base + MGC_MG_INIT_COUNTER); ++ ++ /* ++ * configure virtual channel ++ * TODO: VC support if need ++ * currently each MGC just uses 1 virtual channel ++ */ ++ writel(DEFAULT_VC_ID, mg_base + MGC_MG_MIPI_VC); ++ ++ /* ++ * configure data type and multi dtypes mode ++ * TODO: it needs to add the metedata flow. ++ */ ++ if (is_metadata_enabled(tpg)) { ++ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), ++ mg_base + MGC_MG_MIPI_DTYPES); ++ writel(2, mg_base + MGC_MG_MULTI_DTYPES_MODE); ++ } else { ++ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), ++ mg_base + MGC_MG_MIPI_DTYPES); ++ writel(0, mg_base + MGC_MG_MULTI_DTYPES_MODE); ++ } ++ ++ /* ++ * configure frame information ++ */ ++ writel(0, mg_base + MGC_MG_NOF_FRAMES); ++ writel(width | height << 16, mg_base + MGC_MG_FRAME_DIM); ++ ++ tpg_get_timing(tpg, &dto, &hblank, &vblank); ++ writel(hblank, mg_base + MGC_MG_HBLANK); ++ writel(vblank, mg_base + MGC_MG_VBLANK); ++ ++ /* ++ * configure tpg mode, colors, mask, tile dimension ++ * Mode was set by user configuration ++ * 0 - Ramp mode ++ * 1 - Checkerboard ++ * 2 - Monochrome per frame ++ * 3 - Color palette ++ */ ++ writel(TPG_MODE_COLOR_PALETTE, mg_base + MGC_MG_TPG_MODE); ++ ++ /* red and green for checkerboard, n/a for other modes */ ++ writel(58, mg_base + MGC_MG_TPG_R0); ++ writel(122, mg_base + MGC_MG_TPG_G0); ++ writel(46, mg_base + MGC_MG_TPG_B0); ++ writel(123, mg_base + MGC_MG_TPG_R1); ++ writel(85, mg_base + MGC_MG_TPG_G1); ++ writel(67, mg_base + MGC_MG_TPG_B1); ++ ++ writel(0x0, mg_base + MGC_MG_TPG_FACTORS); ++ ++ /* hor_mask [15:0] ver_mask [31:16] */ ++ writel(0xffffffff, mg_base + MGC_MG_TPG_MASKS); ++ /* xy_mask [11:0] */ ++ writel(0xfff, mg_base + MGC_MG_TPG_XY_MASK); ++ writel(((MGC_TPG_TILE_WIDTH << 16) | MGC_TPG_TILE_HEIGHT), ++ mg_base + MGC_MG_TPG_TILE_DIM); ++ ++ writel(dto, mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL); ++ writel(1, mg_base + MGC_MG_DTO_SPEED_CTRL_EN); ++ ++ /* disable err_injection */ ++ writel(0, mg_base + MGC_MG_ERR_INJECT); ++ writel(0, mg_base + MGC_MG_ERR_LOCATION); ++ ++ ipu7_mipigen_regdump(tpg, mg_base); ++ ++ dev_dbg(dev, "starting MG%d streaming...\n", csi_port); ++ ++ /* kick and start */ ++ writel(port_map, mgc_base + MGC_KICK); ++ ++ return 0; ++} ++ ++static void ipu7_isys_ungate_mgc(struct ipu7_isys_tpg *tpg, int enable) ++{ ++ struct ipu7_isys_csi2 *csi2; ++ u32 offset; ++ struct ipu7_isys *isys = tpg->isys; ++ ++ csi2 = &isys->csi2[tpg->index]; ++ offset = IS_IO_GPREGS_BASE; ++ ++ /* MGC is in use by SW or not */ ++ if (enable) ++ writel(1, csi2->base + offset + MGC_CLK_GATE); ++ else ++ writel(0, csi2->base + offset + MGC_CLK_GATE); ++} ++ ++static void ipu7_isys_mgc_csi2_s_stream(struct ipu7_isys_tpg *tpg, int enable) ++{ ++ struct device *dev = &tpg->isys->adev->auxdev.dev; ++ struct ipu7_isys *isys = tpg->isys; ++ struct ipu7_isys_csi2 *csi2; ++ u32 port, offset, val; ++ void __iomem *isys_base = isys->pdata->base; ++ ++ port = tpg->index; ++ csi2 = &isys->csi2[port]; ++ ++ offset = IS_IO_GPREGS_BASE; ++ val = readl(isys_base + offset + CSI_PORT_CLK_GATE); ++ dev_dbg(dev, "current CSI port %u clk gate 0x%x\n", port, val); ++ ++ if (!enable) { ++ writel(~(1 << port) & val, ++ isys_base + offset + CSI_PORT_CLK_GATE); ++ return; ++ } ++ ++ /* set csi port is using by SW */ ++ writel(1 << port | val, isys_base + offset + CSI_PORT_CLK_GATE); ++ /* input is coming from MGC */ ++ offset = IS_IO_CSI2_ADPL_PORT_BASE(port); ++ writel(CSI_MIPIGEN_INPUT, ++ csi2->base + offset + CSI2_ADPL_INPUT_MODE); ++} ++ ++/* TODO: add the processing of vc */ ++int tpg_set_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); ++ struct ipu7_isys_stream *stream = tpg->av->stream; ++ struct device *dev = &tpg->isys->adev->auxdev.dev; ++ int ret; ++ ++ if (tpg->index >= IPU7_ISYS_CSI_PORT_NUM) { ++ dev_err(dev, "invalid MGC index %d\n", tpg->index); ++ return -EINVAL; ++ } ++ ++ if (!enable) { ++ /* Stop MGC */ ++ stream->asd->is_tpg = false; ++ stream->asd = NULL; ++ ipu7_isys_mgc_csi2_s_stream(tpg, enable); ++ ret = tpg_stop_stream(tpg); ++ ipu7_isys_ungate_mgc(tpg, enable); ++ ++ return ret; ++ } ++ ++ stream->asd = &tpg->asd; ++ /* ungate the MGC clock to program */ ++ ipu7_isys_ungate_mgc(tpg, enable); ++ /* Start MGC */ ++ ret = tpg_start_stream(tpg); ++ v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); ++ ipu7_isys_mgc_csi2_s_stream(tpg, enable); ++ ++ return ret; ++} ++ ++void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg) ++{ ++ v4l2_device_unregister_subdev(&tpg->asd.sd); ++ ipu7_isys_subdev_cleanup(&tpg->asd); ++} ++ ++int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, struct ipu7_isys *isys, ++ void __iomem *base, void __iomem *sel, ++ unsigned int index) ++{ ++ struct device *dev = &isys->adev->auxdev.dev; ++ int ret; ++ ++ tpg->isys = isys; ++ tpg->base = base; ++ tpg->sel = sel; ++ tpg->index = index; ++ ++ tpg->asd.sd.entity.ops = &tpg_entity_ops; ++ tpg->asd.ctrl_init = ipu7_isys_tpg_init_controls; ++ tpg->asd.isys = isys; ++ ++ ret = ipu7_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, ++ NR_OF_TPG_SINK_PADS, NR_OF_TPG_SOURCE_PADS); ++ if (ret) ++ return ret; ++ ++ tpg->asd.sd.flags &= ~V4L2_SUBDEV_FL_STREAMS; ++ tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ++ tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; ++ ++ tpg->asd.source = IPU_INSYS_MIPI_PORT_0 + index; ++ tpg->asd.supported_codes = tpg_supported_codes; ++ tpg->asd.sd.internal_ops = &ipu7_isys_tpg_internal_ops; ++ ++ snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), ++ IPU_ISYS_ENTITY_PREFIX " TPG %u", index); ++ v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); ++ ++ ret = v4l2_subdev_init_finalize(&tpg->asd.sd); ++ if (ret) { ++ dev_err(dev, "failed to finalize subdev (%d)\n", ret); ++ goto fail; ++ } ++ ++ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); ++ if (ret) { ++ dev_info(dev, "can't register v4l2 subdev\n"); ++ goto fail; ++ } ++ ++ return 0; ++ ++fail: ++ ipu7_isys_tpg_cleanup(tpg); ++ ++ return ret; ++} +diff --git a/drivers/staging/media/ipu7/ipu7-isys-tpg.h b/drivers/staging/media/ipu7/ipu7-isys-tpg.h +new file mode 100644 +index 0000000000..e2542a6472 +--- /dev/null ++++ b/drivers/staging/media/ipu7/ipu7-isys-tpg.h +@@ -0,0 +1,70 @@ ++/* SPDX-License-Identifier: GPL-2.0-only */ ++/* ++ * Copyright (C) 2013 - 2025 Intel Corporation ++ */ ++ ++#ifndef IPU7_ISYS_TPG_H ++#define IPU7_ISYS_TPG_H ++ ++#include ++#include ++#include ++ ++#include "ipu7-isys-subdev.h" ++#include "ipu7-isys-video.h" ++#include "ipu7-isys-queue.h" ++ ++struct ipu7_isys_tpg_pdata; ++struct ipu7_isys; ++ ++#define TPG_PAD_SOURCE 0 ++#define NR_OF_TPG_PADS 1 ++#define NR_OF_TPG_SOURCE_PADS 1 ++#define NR_OF_TPG_SINK_PADS 0 ++#define NR_OF_TPG_STREAMS 1 ++ ++enum isys_tpg_mode { ++ TPG_MODE_RAMP = 0, ++ TPG_MODE_CHECKERBOARD = 1, ++ TPG_MODE_MONO = 2, ++ TPG_MODE_COLOR_PALETTE = 3, ++}; ++ ++/* ++ * struct ipu7_isys_tpg ++ * ++ * @nlanes: number of lanes in the receiver ++ */ ++struct ipu7_isys_tpg { ++ struct ipu7_isys_subdev asd; ++ struct ipu7_isys_tpg_pdata *pdata; ++ struct ipu7_isys *isys; ++ struct ipu7_isys_video *av; ++ ++ /* MG base not MGC */ ++ void __iomem *base; ++ void __iomem *sel; ++ unsigned int index; ++ ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *pixel_rate; ++}; ++ ++#define ipu7_isys_subdev_to_tpg(__sd) \ ++ container_of(__sd, struct ipu7_isys_tpg, asd) ++ ++#define to_ipu7_isys_tpg(sd) \ ++ container_of(to_ipu7_isys_subdev(sd), \ ++ struct ipu7_isys_tpg, asd) ++ ++void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream); ++void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream); ++int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, ++ struct ipu7_isys *isys, ++ void __iomem *base, void __iomem *sel, ++ unsigned int index); ++void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg); ++int tpg_set_stream(struct v4l2_subdev *sd, int enable); ++ ++#endif /* IPU7_ISYS_TPG_H */ diff --git a/patch/v6.18.3_iot/0011-INT3472-Support-LT6911GXD.patch b/patch/v6.18.3_iot/0011-INT3472-Support-LT6911GXD.patch new file mode 100644 index 0000000..b75d7ec --- /dev/null +++ b/patch/v6.18.3_iot/0011-INT3472-Support-LT6911GXD.patch @@ -0,0 +1,23 @@ +From 396d480876a7d9af3bd2dbb469ac39d489ac0b6b Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Mon, 8 Sep 2025 15:41:24 +0800 +Subject: [PATCH 11/26] INT3472: Support LT6911GXD + +Signed-off-by: linya14x +--- + drivers/platform/x86/intel/int3472/discrete.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c +index 1505fc3ef7..bad41d4d5b 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -454,7 +454,7 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) + return ret; + } + +- if (cldb.control_logic_type != 1) { ++ if (cldb.control_logic_type != 1 && cldb.control_logic_type != 5) { + dev_err(&pdev->dev, "Unsupported control logic type %u\n", + cldb.control_logic_type); + return -EINVAL; diff --git a/patch/v6.18.3_iot/0012-media-i2c-add-support-for-lt6911gxd.patch b/patch/v6.18.3_iot/0012-media-i2c-add-support-for-lt6911gxd.patch new file mode 100644 index 0000000..4606af8 --- /dev/null +++ b/patch/v6.18.3_iot/0012-media-i2c-add-support-for-lt6911gxd.patch @@ -0,0 +1,42 @@ +From 413a176db607ce83e402a8d3a5bded837354e622 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 25 Oct 2025 16:30:08 +0800 +Subject: [PATCH 12/26] media: i2c: add support for lt6911gxd + +Signed-off-by: linya14x +--- + drivers/media/i2c/Kconfig | 11 +++++++++++ + drivers/media/i2c/Makefile | 1 + + 2 files changed, 12 insertions(+) + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index cdd7ba5da0..33866b6fe0 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -277,6 +277,17 @@ config VIDEO_IMX415 + To compile this driver as a module, choose M here: the + module will be called imx415. + ++config VIDEO_LT6911GXD ++ tristate "Lontium LT6911GXD decoder" ++ depends on ACPI || COMPILE_TEST ++ select V4L2_CCI_I2C ++ help ++ This is a Video4Linux2 sensor-level driver for the Lontium ++ LT6911GXD HDMI to MIPI CSI-2 bridge. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called lt6911gxd. ++ + config VIDEO_MAX9271_LIB + tristate + +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index 57cdd8dc96..f03528f27c 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -165,3 +165,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o + obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o + obj-$(CONFIG_VIDEO_WM8739) += wm8739.o + obj-$(CONFIG_VIDEO_WM8775) += wm8775.o ++obj-$(CONFIG_VIDEO_LT6911GXD) += lt6911gxd.o diff --git a/patch/v6.18.3_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch b/patch/v6.18.3_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch new file mode 100644 index 0000000..760674a --- /dev/null +++ b/patch/v6.18.3_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch @@ -0,0 +1,23 @@ +From 701b778801e5472819392d4b815aa3ab6ffca0ee Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Tue, 21 Apr 2026 14:33:42 +0800 +Subject: [PATCH 13/26] media: pci: enable lt6911gxd in ipu-bridge + +Signed-off-by: linya14x +--- + drivers/media/pci/intel/ipu-bridge.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +index 4e579352ab..827662f19d 100644 +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -72,6 +72,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { + IPU_SENSOR_CONFIG("INT3537", 1, 437000000), + /* Lontium lt6911uxe */ + IPU_SENSOR_CONFIG("INTC10C5", 0), ++ /* Lontium lt6911gxd */ ++ IPU_SENSOR_CONFIG("INTC1124", 0), + /* Omnivision OV01A10 / OV01A1S */ + IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000), + IPU_SENSOR_CONFIG("OVTI01AS", 1, 400000000), diff --git a/patch/v6.18.3_iot/0014-ipu-bridge-add-CPHY-support.patch b/patch/v6.18.3_iot/0014-ipu-bridge-add-CPHY-support.patch new file mode 100644 index 0000000..aee181b --- /dev/null +++ b/patch/v6.18.3_iot/0014-ipu-bridge-add-CPHY-support.patch @@ -0,0 +1,110 @@ +From f438bf5c942e613272a621e8096c70175dc34002 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 17 Apr 2026 15:30:52 +0800 +Subject: [PATCH 14/26] ipu-bridge: add CPHY support + +get DPHY or CPHY mode when parse ssdb + +Signed-off-by: linya14x +--- + drivers/media/pci/intel/ipu-bridge.c | 23 ++++++++++++++++++++++- + include/media/ipu-bridge.h | 11 +++++++++-- + 2 files changed, 31 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c +index 827662f19d..cd7835fb2f 100644 +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -35,6 +35,9 @@ + */ + #define IVSC_DEV_NAME "intel_vsc" + ++#define PHY_MODE_DPHY 0 ++#define PHY_MODE_CPHY 1 ++ + /* + * Extend this array with ACPI Hardware IDs of devices known to be working + * plus the number of link-frequencies expected by their drivers, along with +@@ -314,6 +317,7 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) + + sensor->link = ssdb.link; + sensor->lanes = ssdb.lanes; ++ sensor->phyconfig = ssdb.phyconfig; + sensor->mclkspeed = ssdb.mclkspeed; + sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); + sensor->orientation = ipu_bridge_parse_orientation(adev); +@@ -332,6 +336,7 @@ static void ipu_bridge_create_fwnode_properties( + { + struct ipu_property_names *names = &sensor->prop_names; + struct software_node *nodes = sensor->swnodes; ++ u8 bus_type; + + sensor->prop_names = prop_names; + +@@ -389,9 +394,16 @@ static void ipu_bridge_create_fwnode_properties( + PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); + } + ++ if (sensor->phyconfig == PHY_MODE_DPHY) ++ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; ++ else if (sensor->phyconfig == PHY_MODE_CPHY) ++ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; ++ else ++ bus_type = V4L2_FWNODE_BUS_TYPE_GUESS; ++ + sensor->ep_properties[0] = PROPERTY_ENTRY_U32( + sensor->prop_names.bus_type, +- V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); ++ bus_type); + sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( + sensor->prop_names.data_lanes, + bridge->data_lanes, sensor->lanes); +@@ -411,6 +423,15 @@ static void ipu_bridge_create_fwnode_properties( + sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( + sensor->prop_names.remote_endpoint, + sensor->remote_ref); ++ ++ /* ++ * TODO: Remove the bus_type property for IPU ++ * 1. keep fwnode property list no change. ++ * 2. IPU driver needs to get bus_type from remote sensor ep. ++ */ ++ sensor->ipu_properties[2] = PROPERTY_ENTRY_U32 ++ (sensor->prop_names.bus_type, ++ bus_type); + } + + static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) +diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h +index 16fac76545..5f837612b2 100644 +--- a/include/media/ipu-bridge.h ++++ b/include/media/ipu-bridge.h +@@ -91,7 +91,13 @@ struct ipu_sensor_ssdb { + u8 controllogicid; + u8 reserved1[3]; + u8 mclkport; +- u8 reserved2[13]; ++ u8 pmicpos; ++ u8 voltagerail; ++ u8 pprval; ++ u8 pprunit; ++ u8 flashid; ++ u8 phyconfig; ++ u8 reserved2[7]; + } __packed; + + struct ipu_property_names { +@@ -139,11 +145,12 @@ struct ipu_sensor { + u32 rotation; + enum v4l2_fwnode_orientation orientation; + const char *vcm_type; ++ u8 phyconfig; + + struct ipu_property_names prop_names; + struct property_entry ep_properties[5]; + struct property_entry dev_properties[5]; +- struct property_entry ipu_properties[3]; ++ struct property_entry ipu_properties[4]; + struct property_entry ivsc_properties[1]; + struct property_entry ivsc_sensor_ep_properties[4]; + struct property_entry ivsc_ipu_ep_properties[4]; diff --git a/patch/v6.18.3_iot/0015-media-i2c-add-isx031-config.patch b/patch/v6.18.3_iot/0015-media-i2c-add-isx031-config.patch new file mode 100644 index 0000000..0457a4b --- /dev/null +++ b/patch/v6.18.3_iot/0015-media-i2c-add-isx031-config.patch @@ -0,0 +1,45 @@ +From d4e0902d2f60f5b19a14af841a77cb1f4f7e6058 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 25 Oct 2025 16:46:35 +0800 +Subject: [PATCH 15/26] media: i2c: add ISX031 config + +Signed-off-by: hepengpx +Signed-off-by: linya14x +--- + drivers/media/i2c/Kconfig | 8 ++++++++ + drivers/media/i2c/Makefile | 1 + + 2 files changed, 9 insertions(+) + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index 33866b6fe0..62fcbcca1d 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -288,6 +288,14 @@ config VIDEO_LT6911GXD + To compile this driver as a module, choose M here: the + module will be called lt6911gxd. + ++config VIDEO_ISX031 ++ tristate "ISX031 sensor support" ++ depends on VIDEO_DEV && I2C ++ select VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ help ++ This is a Video4Linux2 sensor-level driver for ISX031 camera. ++ + config VIDEO_MAX9271_LIB + tristate + +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index f03528f27c..39913fc45a 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -62,6 +62,7 @@ obj-$(CONFIG_VIDEO_IMX412) += imx412.o + obj-$(CONFIG_VIDEO_IMX415) += imx415.o + obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o + obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o ++obj-$(CONFIG_VIDEO_ISX031) += isx031.o + obj-$(CONFIG_VIDEO_KS0127) += ks0127.o + obj-$(CONFIG_VIDEO_LM3560) += lm3560.o + obj-$(CONFIG_VIDEO_LM3646) += lm3646.o +-- +2.43.0 \ No newline at end of file diff --git a/patch/v6.18.3_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch b/patch/v6.18.3_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch new file mode 100644 index 0000000..c18aa90 --- /dev/null +++ b/patch/v6.18.3_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch @@ -0,0 +1,24 @@ +From e45df13d66146f112017a3875f8d8a1776364ee2 Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Wed, 27 Aug 2025 11:10:06 +0800 +Subject: [PATCH 16/26] drivers: media: set v4l2_subdev_enable_streams_api=true + for WA + +Signed-off-by: hepengpx +--- + drivers/media/v4l2-core/v4l2-subdev.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c +index 25e66bf18f..cf6ac8acb9 100644 +--- a/drivers/media/v4l2-core/v4l2-subdev.c ++++ b/drivers/media/v4l2-core/v4l2-subdev.c +@@ -56,7 +56,7 @@ struct v4l2_subdev_stream_config { + * 'v4l2_subdev_enable_streams_api' to 1 below. + */ + +-static bool v4l2_subdev_enable_streams_api; ++static bool v4l2_subdev_enable_streams_api = true; + #endif + + /* diff --git a/patch/v6.18.3_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch b/patch/v6.18.3_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch new file mode 100644 index 0000000..253b8c2 --- /dev/null +++ b/patch/v6.18.3_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch @@ -0,0 +1,83 @@ +From af3c1ff2501c0054cb78262156e1d175d4e68c7e Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 14 Nov 2025 18:41:08 +0800 +Subject: [PATCH 17/26] staging: ipu7: Update IPU7 firmware ABI version to + 1.2.1.20251215_224531 + +Signed-off-by: Hao Yao +--- + drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h | 1 + + drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 11 +++++++---- + drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h | 2 +- + 3 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h +index a1519c4fe6..4ce304f54e 100644 +--- a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h ++++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h +@@ -153,6 +153,7 @@ enum ia_gofo_boot_state { + IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, + IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, + IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, ++ IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_FAILURE = 0xDEAD1016U, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, + IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, + IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, +diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h +index c42d0b7a26..7f622bfe9a 100644 +--- a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h ++++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h +@@ -47,7 +47,6 @@ enum ipu7_insys_resp_type { + IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, + IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, + IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, +- IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, + N_IPU_INSYS_RESP_TYPE + }; + +@@ -201,7 +200,8 @@ enum ipu7_insys_mipi_dt_rename_mode { + enum ipu7_insys_output_link_dest { + IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, + IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, +- IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 ++ IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2, ++ N_IPU_INSYS_OUTPUT_LINK_DEST + }; + + enum ipu7_insys_dpcm_type { +@@ -220,9 +220,12 @@ enum ipu7_insys_dpcm_predictor { + + enum ipu7_insys_send_queue_token_flag { + IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, +- IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 ++ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1, ++ N_IPU_INSYS_SEND_QUEUE_TOKEN_FLAG + }; + ++#define IPU_INSYS_MIPI_FRAME_NUMBER_DONT_CARE UINT16_MAX ++ + #pragma pack(push, 1) + struct ipu7_insys_resolution { + u32 width; +@@ -312,7 +315,7 @@ struct ipu7_insys_resp { + u8 pin_id; + u8 frame_id; + u8 skip_frame; +- u8 pad[2]; ++ u16 mipi_fn; + }; + + struct ipu7_insys_resp_queue_token { +diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h +index 8a78dd0936..1319f0eb63 100644 +--- a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h ++++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h +@@ -217,7 +217,7 @@ struct ipu7_msg_task { + u8 frag_id; + u8 req_done_msg; + u8 req_done_irq; +- u8 reserved[1]; ++ u8 disable_save; + ipu7_msg_teb_t payload_reuse_bm; + ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; + }; diff --git a/patch/v6.18.3_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch b/patch/v6.18.3_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch new file mode 100644 index 0000000..fbb7c87 --- /dev/null +++ b/patch/v6.18.3_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch @@ -0,0 +1,23 @@ +From 0eb59ef2f6d960ab7a3e748aa704c487bce4d56a Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 15:01:40 +0800 +Subject: [PATCH 18/26] patch: staging add IPU8_PCI_ID support + +Signed-off-by: Bingbu Cao +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/ipu7.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c +index 9b2cfc9f9e..86c2bdd956 100644 +--- a/drivers/staging/media/ipu7/ipu7.c ++++ b/drivers/staging/media/ipu7/ipu7.c +@@ -2817,6 +2817,7 @@ static const struct dev_pm_ops ipu7_pm_ops = { + static const struct pci_device_id ipu7_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, ++ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, + {0,} + }; + MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); diff --git a/patch/v6.18.3_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.14.patch b/patch/v6.18.3_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.14.patch new file mode 100644 index 0000000..56913ec --- /dev/null +++ b/patch/v6.18.3_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.14.patch @@ -0,0 +1,466 @@ +From a98a3c8d0753ca8dc6480f7d497b897cf7a3aec5 Mon Sep 17 00:00:00 2001 +From: Shunyong Yang +Date: Tue, 16 Jun 2026 10:35:28 +0800 +Subject: [PATCH 19/26] staging: ipu7: Add IPU8 ABI version 1.0.14 + +Signed-off-by: Shunyong Yang +--- + .../staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 93 +++++++++- + drivers/staging/media/ipu7/ipu7-fw-isys.c | 167 +++++++++++++++++- + drivers/staging/media/ipu7/ipu7-isys-queue.c | 5 +- + drivers/staging/media/ipu7/ipu7-isys-video.c | 13 ++ + drivers/staging/media/ipu7/ipu7-isys.h | 9 + + 5 files changed, 273 insertions(+), 14 deletions(-) + +diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h +index 7f622bfe9a..ca9e35872e 100644 +--- a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h ++++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h +@@ -251,9 +251,16 @@ struct ipu7_insys_output_link { + u8 pad[2]; + }; + ++struct ipu7_insys_output_cropping_v1 { ++ u16 line_top; ++ u16 line_bottom; ++}; ++ + struct ipu7_insys_output_cropping { + u16 line_top; + u16 line_bottom; ++ u16 column_left; ++ u16 column_right; + }; + + struct ipu7_insys_output_dpcm { +@@ -263,9 +270,35 @@ struct ipu7_insys_output_dpcm { + u8 pad; + }; + +-struct ipu7_insys_output_pin { ++enum ipu_insys_cfa_dim { ++ IPU_INSYS_CFA_DIM_2x2 = 0, ++ IPU_INSYS_CFA_DIM_4x4 = 1, ++ N_IPU_INSYS_CFA_DIM ++}; ++ ++#define IPU_INSYS_MAX_BINNING_FACTOR (4U) ++#define IPU_INSYS_UPIPE_MAX_OUTPUTS (2U) ++#define IPU_INSYS_UPIPE_MAX_UOB_FIFO_ALLOC (4U) ++#define IPU_INSYS_UPIPE_STREAM_CFG_BUF_SIZE (32U) ++#define IPU_INSYS_UPIPE_FRAME_CFG_BUF_SIZE (36U) ++ ++struct ipu7_insys_upipe_output_pin { ++ ia_gofo_addr_t opaque_pin_cfg; ++ u16 plane_offset_1; ++ u16 plane_offset_2; ++ u8 single_uob_fifo; ++ u8 shared_uob_fifo; ++ u8 pad[2]; ++}; ++ ++struct ipu7_insys_capture_output_pin_cfg { ++ struct ipu7_insys_capture_output_pin_payload pin_payload; ++ ia_gofo_addr_t upipe_capture_cfg; ++}; ++ ++struct ipu7_insys_output_pin_v1 { + struct ipu7_insys_output_link link; +- struct ipu7_insys_output_cropping crop; ++ struct ipu7_insys_output_cropping_v1 crop; + struct ipu7_insys_output_dpcm dpcm; + u32 stride; + u16 ft; +@@ -275,6 +308,21 @@ struct ipu7_insys_output_pin { + u8 pad[3]; + }; + ++struct ipu7_insys_output_pin { ++ struct ipu7_insys_output_link link; ++ struct ipu7_insys_output_cropping crop; ++ struct ipu7_insys_output_dpcm dpcm; ++ struct ipu7_insys_upipe_output_pin upipe_pin_cfg; ++ u32 stride; ++ u16 ft; ++ u8 upipe_enable; ++ u8 send_irq; ++ u8 input_pin_id; ++ u8 early_ack_en; ++ u8 cfa_dim; ++ u8 binning_factor; ++}; ++ + struct ipu7_insys_input_pin { + struct ipu7_insys_resolution input_res; + u16 sync_msg_map; +@@ -285,6 +333,17 @@ struct ipu7_insys_input_pin { + u8 pad[2]; + }; + ++struct ipu7_insys_stream_cfg_v1 { ++ struct ipu7_insys_input_pin input_pins[4]; ++ struct ipu7_insys_output_pin_v1 output_pins[4]; ++ u16 stream_msg_map; ++ u8 port_id; ++ u8 vc; ++ u8 nof_input_pins; ++ u8 nof_output_pins; ++ u8 pad[2]; ++}; ++ + struct ipu7_insys_stream_cfg { + struct ipu7_insys_input_pin input_pins[4]; + struct ipu7_insys_output_pin output_pins[4]; +@@ -296,7 +355,7 @@ struct ipu7_insys_stream_cfg { + u8 pad[2]; + }; + +-struct ipu7_insys_buffset { ++struct ipu7_insys_buffset_v1 { + struct ipu7_insys_capture_output_pin_payload output_pins[4]; + u8 capture_msg_map; + u8 frame_id; +@@ -304,6 +363,28 @@ struct ipu7_insys_buffset { + u8 pad[5]; + }; + ++struct ipu7_insys_buffset { ++ struct ipu7_insys_capture_output_pin_cfg output_pins[4]; ++ u8 capture_msg_map; ++ u8 frame_id; ++ u8 skip_frame; ++ u8 pad[5]; ++}; ++ ++struct ipu7_insys_resp_v1 { ++ u64 buf_id; ++ struct ipu7_insys_capture_output_pin_payload pin; ++ struct ia_gofo_msg_err error_info; ++ u32 timestamp[2]; ++ u8 type; ++ u8 msg_link_streaming_mode; ++ u8 stream_id; ++ u8 pin_id; ++ u8 frame_id; ++ u8 skip_frame; ++ u16 mipi_fn; ++}; ++ + struct ipu7_insys_resp { + u64 buf_id; + struct ipu7_insys_capture_output_pin_payload pin; +@@ -372,6 +453,12 @@ enum insys_msg_err_stream { + INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_OUTPUT = 36, + INSYS_MSG_ERR_STREAM_WIDTH_OUTPUT_SIZE = 37, + INSYS_MSG_ERR_STREAM_CLOSED = 38, ++ INSYS_MSG_ERR_STREAM_BINNING_FACTOR_NOT_SUPPORTED = 39, ++ INSYS_MSG_ERR_STREAM_CFA_DIM_NOT_SUPPORTED = 40, ++ INSYS_MSG_ERR_STREAM_INVALID_UPIPE_ENABLE = 41, ++ INSYS_MSG_ERR_STREAM_INVALID_UPIPE_UOB_SINGLE = 42, ++ INSYS_MSG_ERR_STREAM_INVALID_UPIPE_UOB_SHARED = 43, ++ INSYS_MSG_ERR_STREAM_INVALID_UPIPE_OPAQUE_PIN_CFG = 44, + INSYS_MSG_ERR_STREAM_N + }; + +diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c +index 1ece286c29..ca2dbf0d32 100644 +--- a/drivers/staging/media/ipu7/ipu7-fw-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c +@@ -31,6 +31,118 @@ static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = { + "STREAM_CLOSE" + }; + ++static void isys_stream_cfg_to_v1(struct ipu7_insys_stream_cfg_v1 *dst, ++ const struct ipu7_insys_stream_cfg *src) ++{ ++ unsigned int i; ++ ++ memset(dst, 0, sizeof(*dst)); ++ memcpy(dst->input_pins, src->input_pins, sizeof(dst->input_pins)); ++ dst->stream_msg_map = src->stream_msg_map; ++ dst->port_id = src->port_id; ++ dst->vc = src->vc; ++ dst->nof_input_pins = src->nof_input_pins; ++ dst->nof_output_pins = src->nof_output_pins; ++ ++ for (i = 0; i < ARRAY_SIZE(dst->output_pins); i++) { ++ dst->output_pins[i].link = src->output_pins[i].link; ++ dst->output_pins[i].crop.line_top = ++ src->output_pins[i].crop.line_top; ++ dst->output_pins[i].crop.line_bottom = ++ src->output_pins[i].crop.line_bottom; ++ dst->output_pins[i].dpcm = src->output_pins[i].dpcm; ++ dst->output_pins[i].stride = src->output_pins[i].stride; ++ dst->output_pins[i].ft = src->output_pins[i].ft; ++ dst->output_pins[i].send_irq = src->output_pins[i].send_irq; ++ dst->output_pins[i].input_pin_id = ++ src->output_pins[i].input_pin_id; ++ dst->output_pins[i].early_ack_en = ++ src->output_pins[i].early_ack_en; ++ } ++} ++ ++static void isys_buffset_to_v1(struct ipu7_insys_buffset_v1 *dst, ++ const struct ipu7_insys_buffset *src) ++{ ++ unsigned int i; ++ ++ memset(dst, 0, sizeof(*dst)); ++ for (i = 0; i < ARRAY_SIZE(dst->output_pins); i++) ++ dst->output_pins[i] = src->output_pins[i].pin_payload; ++ ++ dst->capture_msg_map = src->capture_msg_map; ++ dst->frame_id = src->frame_id; ++ dst->skip_frame = src->skip_frame; ++} ++ ++static size_t isys_prepare_fw_payload_v1(void *cpu_mapped_buf, ++ u16 send_type, size_t size) ++{ ++ if (!cpu_mapped_buf) ++ return 0; ++ ++ switch (send_type) { ++ case IPU_INSYS_SEND_TYPE_STREAM_OPEN: { ++ struct ipu7_insys_stream_cfg cfg; ++ ++ memcpy(&cfg, cpu_mapped_buf, sizeof(cfg)); ++ isys_stream_cfg_to_v1(cpu_mapped_buf, &cfg); ++ return sizeof(struct ipu7_insys_stream_cfg_v1); ++ } ++ case IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE: ++ case IPU_INSYS_SEND_TYPE_STREAM_CAPTURE: { ++ struct ipu7_insys_buffset set; ++ ++ memcpy(&set, cpu_mapped_buf, sizeof(set)); ++ isys_buffset_to_v1(cpu_mapped_buf, &set); ++ return sizeof(struct ipu7_insys_buffset_v1); ++ } ++ default: ++ return size; ++ } ++} ++ ++static size_t isys_prepare_fw_payload(void *cpu_mapped_buf, ++ u16 send_type, size_t size) ++{ ++ if (!cpu_mapped_buf) ++ return 0; ++ ++ switch (send_type) { ++ case IPU_INSYS_SEND_TYPE_STREAM_OPEN: ++ return sizeof(struct ipu7_insys_stream_cfg); ++ case IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE: ++ case IPU_INSYS_SEND_TYPE_STREAM_CAPTURE: ++ return sizeof(struct ipu7_insys_buffset); ++ default: ++ return size; ++ } ++} ++ ++static __maybe_unused void isys_decode_resp_v1(struct ipu7_insys_resp *dst, const void *token) ++{ ++ const struct ipu7_insys_resp_v1 *src = token; ++ ++ memset(dst, 0, sizeof(*dst)); ++ dst->buf_id = src->buf_id; ++ dst->pin = src->pin; ++ dst->error_info = src->error_info; ++ dst->timestamp[0] = src->timestamp[0]; ++ dst->timestamp[1] = src->timestamp[1]; ++ dst->type = src->type; ++ dst->msg_link_streaming_mode = src->msg_link_streaming_mode; ++ dst->stream_id = src->stream_id; ++ dst->pin_id = src->pin_id; ++ dst->frame_id = src->frame_id; ++ dst->skip_frame = src->skip_frame; ++ dst->mipi_fn = src->mipi_fn; ++} ++ ++static void isys_decode_resp(struct ipu7_insys_resp *dst, const void *token) ++{ ++ memcpy(dst, token, sizeof(*dst)); ++} ++ + int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, + const unsigned int stream_handle, + void *cpu_mapped_buf, +@@ -50,8 +162,11 @@ int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, + * Time to flush cache in case we have some payload. Not all messages + * have that + */ +- if (cpu_mapped_buf) ++ if (cpu_mapped_buf) { ++ size = isys->abi_ops.prepare_payload(cpu_mapped_buf, ++ send_type, size); + clflush_cache_range(cpu_mapped_buf, size); ++ } + + token = ipu7_syscom_get_token(ctx, stream_handle + + IPU_INSYS_INPUT_MSG_QUEUE); +@@ -97,6 +212,15 @@ int ipu7_fw_isys_init(struct ipu7_isys *isys) + if (!syscom) + return -ENOMEM; + ++ if (is_ipu8(adev->isp->hw_ver)) ++ isys->abi_ops.prepare_payload = isys_prepare_fw_payload; ++ else ++ isys->abi_ops.prepare_payload = isys_prepare_fw_payload_v1; ++ ++ isys->abi_ops.decode_resp = isys_decode_resp; ++ isys->abi_ops.resp_queue_token_size = ++ sizeof(struct ipu7_insys_resp); ++ + adev->syscom = syscom; + syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES; + syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES; +@@ -111,11 +235,11 @@ int ipu7_fw_isys_init(struct ipu7_isys *isys) + queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].max_capacity = + IPU_ISYS_SIZE_RECV_QUEUE; + queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes = +- sizeof(struct ipu7_insys_resp); ++ isys->abi_ops.resp_queue_token_size; + queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].max_capacity = + IPU_ISYS_SIZE_LOG_QUEUE; + queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes = +- sizeof(struct ipu7_insys_resp); ++ isys->abi_ops.resp_queue_token_size; + queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0; + queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0; + +@@ -195,9 +319,15 @@ int ipu7_fw_isys_close(struct ipu7_isys *isys) + + struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys) + { +- return (struct ipu7_insys_resp *) +- ipu7_syscom_get_token(isys->adev->syscom, +- IPU_INSYS_OUTPUT_MSG_QUEUE); ++ void *token = ipu7_syscom_get_token(isys->adev->syscom, ++ IPU_INSYS_OUTPUT_MSG_QUEUE); ++ ++ if (!token) ++ return NULL; ++ ++ isys->abi_ops.decode_resp(&isys->resp, token); ++ ++ return &isys->resp; + } + + void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) +@@ -327,6 +457,10 @@ void ipu7_fw_isys_dump_stream_cfg(struct device *dev, + cfg->output_pins[i].crop.line_top); + dev_dbg(dev, "\t.crop.line_bottom = %d\n", + cfg->output_pins[i].crop.line_bottom); ++ dev_dbg(dev, "\t.crop.column_left = %d\n", ++ cfg->output_pins[i].crop.column_left); ++ dev_dbg(dev, "\t.crop.column_right = %d\n", ++ cfg->output_pins[i].crop.column_right); + + dev_dbg(dev, "\t.dpcm_enable = %d\n", + cfg->output_pins[i].dpcm.enable); +@@ -334,6 +468,18 @@ void ipu7_fw_isys_dump_stream_cfg(struct device *dev, + cfg->output_pins[i].dpcm.type); + dev_dbg(dev, "\t.dpcm.predictor = %d\n", + cfg->output_pins[i].dpcm.predictor); ++ dev_dbg(dev, "\t.upipe_enable = %d\n", ++ cfg->output_pins[i].upipe_enable); ++ dev_dbg(dev, "\t.upipe_pin_cfg.opaque_pin_cfg = %d\n", ++ cfg->output_pins[i].upipe_pin_cfg.opaque_pin_cfg); ++ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_1 = %d\n", ++ cfg->output_pins[i].upipe_pin_cfg.plane_offset_1); ++ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_2 = %d\n", ++ cfg->output_pins[i].upipe_pin_cfg.plane_offset_2); ++ dev_dbg(dev, "\t.upipe_pin_cfg.singel_uob_fifo = %d\n", ++ cfg->output_pins[i].upipe_pin_cfg.single_uob_fifo); ++ dev_dbg(dev, "\t.upipe_pin_cfg.shared_uob_fifo = %d\n", ++ cfg->output_pins[i].upipe_pin_cfg.shared_uob_fifo); + } + dev_dbg(dev, "---------------------------\n"); + } +@@ -352,9 +498,12 @@ void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, + + for (i = 0; i < outputs; i++) { + dev_dbg(dev, ".output_pin[%d]:\n", i); +- dev_dbg(dev, "\t.user_token = %llx\n", +- buf->output_pins[i].user_token); +- dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); ++ dev_dbg(dev, "\t.pin_payload.user_token = %llx\n", ++ buf->output_pins[i].pin_payload.user_token); ++ dev_dbg(dev, "\t.pin_payload.addr = 0x%x\n", ++ buf->output_pins[i].pin_payload.addr); ++ dev_dbg(dev, "\t.pin_payload.upipe_capture_cfg = 0x%x\n", ++ buf->output_pins[i].upipe_capture_cfg); + } + dev_dbg(dev, "---------------------------\n"); + } +diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c +index 27d8b1b331..97ed241bc1 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-queue.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c +@@ -269,8 +269,9 @@ static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, + struct ipu7_isys_video_buffer *ivb = + vb2_buffer_to_ipu7_isys_video_buffer(vvb); + +- set->output_pins[aq->fw_output].addr = ivb->dma_addr; +- set->output_pins[aq->fw_output].user_token = (uintptr_t)set; ++ set->output_pins[aq->fw_output].pin_payload.addr = ivb->dma_addr; ++ set->output_pins[aq->fw_output].pin_payload.user_token = (uintptr_t)set; ++ set->output_pins[aq->fw_output].upipe_capture_cfg = 0; + } + + /* +diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c +index cc0bbbc0f2..96713f9883 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-video.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-video.c +@@ -441,10 +441,23 @@ static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, + /* output pin crop */ + output_pin->crop.line_top = 0; + output_pin->crop.line_bottom = 0; ++ output_pin->crop.column_left = 0; ++ output_pin->crop.column_right = 0; + + /* output de-compression */ + output_pin->dpcm.enable = 0; + ++ /* upipe_cfg */ ++ output_pin->upipe_pin_cfg.opaque_pin_cfg = 0; ++ output_pin->upipe_pin_cfg.plane_offset_1 = 0; ++ output_pin->upipe_pin_cfg.plane_offset_2 = 0; ++ output_pin->upipe_pin_cfg.single_uob_fifo = 0; ++ output_pin->upipe_pin_cfg.shared_uob_fifo = 0; ++ output_pin->upipe_enable = 0; ++ output_pin->binning_factor = 0; ++ /* stupid setting, even unused, SW still need to set a valid value */ ++ output_pin->cfa_dim = IPU_INSYS_CFA_DIM_2x2; ++ + /* frame format type */ + pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); + output_pin->ft = (u16)pfmt->css_pixelformat; +diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h +index af18182229..0343b2ef00 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -67,6 +67,13 @@ struct isys_fw_log { + u32 size; /* actual size of log content, in bits */ + }; + ++struct ipu7_isys_abi_ops { ++ size_t (*prepare_payload)(void *cpu_mapped_buf, u16 send_type, ++ size_t size); ++ void (*decode_resp)(struct ipu7_insys_resp *dst, const void *token); ++ size_t resp_queue_token_size; ++}; ++ + /* + * struct ipu7_isys + * +@@ -124,6 +131,8 @@ struct ipu7_isys { + struct list_head framebuflist; + struct list_head framebuflist_fw; + struct v4l2_async_notifier notifier; ++ struct ipu7_isys_abi_ops abi_ops; ++ struct ipu7_insys_resp resp; + + struct ipu7_insys_config *subsys_config; + dma_addr_t subsys_config_dma_addr; diff --git a/patch/v6.18.3_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch b/patch/v6.18.3_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch new file mode 100644 index 0000000..fd8c493 --- /dev/null +++ b/patch/v6.18.3_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch @@ -0,0 +1,77 @@ +From 963cbc362dc6a23bb0cb3ec85dfe13cbfdd12d92 Mon Sep 17 00:00:00 2001 +From: Hao Yao +Date: Mon, 26 Jan 2026 18:10:20 +0800 +Subject: [PATCH 20/26] staging: ipu7: Define gpreg_stride for different IPU + versions + +Signed-off-by: Hao Yao +--- + drivers/staging/media/ipu7/ipu7-isys-csi-phy.c | 6 ++++-- + drivers/staging/media/ipu7/ipu7.c | 3 +++ + drivers/staging/media/ipu7/ipu7.h | 1 + + 3 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +index b8c5db7ae3..bd83aef706 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +@@ -167,7 +167,8 @@ static void gpreg_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) + { + void __iomem *isys_base = isys->pdata->base; + u32 gpreg = isys->pdata->ipdata->csi2.gpreg; +- void __iomem *base = isys_base + gpreg + 0x1000 * id; ++ void __iomem *base = isys_base + gpreg + ++ isys->pdata->ipdata->csi2.gpreg_stride * id; + struct device *dev = &isys->adev->auxdev.dev; + + dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x", +@@ -344,7 +345,8 @@ static int ipu7_isys_phy_ready(struct ipu7_isys *isys, u32 id) + { + void __iomem *isys_base = isys->pdata->base; + u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg; +- void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id; ++ void __iomem *gpreg = isys_base + gpreg_offset + ++ isys->pdata->ipdata->csi2.gpreg_stride * id; + struct device *dev = &isys->adev->auxdev.dev; + unsigned int i; + u32 phy_ready; +diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c +index 86c2bdd956..9307fd6e52 100644 +--- a/drivers/staging/media/ipu7/ipu7.c ++++ b/drivers/staging/media/ipu7/ipu7.c +@@ -56,6 +56,7 @@ static const unsigned int ipu7_csi_offsets[] = { + static struct ipu_isys_internal_pdata ipu7p5_isys_ipdata = { + .csi2 = { + .gpreg = IS_IO_CSI2_GPREGS_BASE, ++ .gpreg_stride = 0x1000, + }, + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, +@@ -796,6 +797,7 @@ static struct ipu_psys_internal_pdata ipu7p5_psys_ipdata = { + static struct ipu_isys_internal_pdata ipu7_isys_ipdata = { + .csi2 = { + .gpreg = IS_IO_CSI2_GPREGS_BASE, ++ .gpreg_stride = 0x1000, + }, + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, +@@ -1313,6 +1315,7 @@ static struct ipu_psys_internal_pdata ipu7_psys_ipdata = { + static struct ipu_isys_internal_pdata ipu8_isys_ipdata = { + .csi2 = { + .gpreg = IPU8_IS_IO_CSI2_GPREGS_BASE, ++ .gpreg_stride = 0x2000, + }, + .hw_variant = { + .offset = IPU_UNIFIED_OFFSET, +diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h +index 21988ce41a..41f3efded9 100644 +--- a/drivers/staging/media/ipu7/ipu7.h ++++ b/drivers/staging/media/ipu7/ipu7.h +@@ -206,6 +206,7 @@ struct ipu7_isys_internal_csi2_pdata { + u32 nports; + u32 const *offsets; + u32 gpreg; ++ u32 gpreg_stride; + }; + + struct ipu7_hw_variants { diff --git a/patch/v6.18.3_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch b/patch/v6.18.3_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch new file mode 100644 index 0000000..0b280ae --- /dev/null +++ b/patch/v6.18.3_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch @@ -0,0 +1,58 @@ +From c22999564a7cc6841c1c9e52e7387f676953d58b Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 28 Feb 2026 17:21:52 +0800 +Subject: [PATCH 21/26] staging: media: ipu7: Fix potential NULL pointer + dereference in DMA free/unmap + +Check if MMU or dmap is valid before accessing them in ipu7_dma_free() +and ipu7_dma_unmap_sg(). This prevents potential crashes if these +functions are called after the MMU has been cleaned up." + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/ipu7-dma.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-dma.c b/drivers/staging/media/ipu7/ipu7-dma.c +index 05478ab0f0..f090a3235d 100644 +--- a/drivers/staging/media/ipu7/ipu7-dma.c ++++ b/drivers/staging/media/ipu7/ipu7-dma.c +@@ -248,12 +248,16 @@ void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, + { + struct ipu7_mmu *mmu = sys->mmu; + struct pci_dev *pdev = sys->isp->pdev; +- struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); ++ struct iova *iova; + dma_addr_t pci_dma_addr, ipu7_iova; + struct vm_info *info; + struct page **pages; + unsigned int i; + ++ if (!mmu || !mmu->dmap) ++ return; ++ ++ iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); + if (WARN_ON(!iova)) + return; + +@@ -335,8 +339,7 @@ void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + { + struct device *dev = &sys->auxdev.dev; + struct ipu7_mmu *mmu = sys->mmu; +- struct iova *iova = find_iova(&mmu->dmap->iovad, +- PHYS_PFN(sg_dma_address(sglist))); ++ struct iova *iova; + struct scatterlist *sg; + dma_addr_t pci_dma_addr; + unsigned int i; +@@ -344,6 +347,10 @@ void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, + if (!nents) + return; + ++ if (!mmu || !mmu->dmap) ++ return; ++ ++ iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(sg_dma_address(sglist))); + if (WARN_ON(!iova)) + return; + diff --git a/patch/v6.18.3_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch b/patch/v6.18.3_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch new file mode 100644 index 0000000..f39764a --- /dev/null +++ b/patch/v6.18.3_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch @@ -0,0 +1,39 @@ +From e131b8f56f8950cb38a4541f2d52faeab8b642e7 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Thu, 5 Mar 2026 15:53:38 +0800 +Subject: [PATCH 22/26] staging: media: ipu7:set skipframe flag when frame + error + +--- + drivers/staging/media/ipu7/ipu7-isys-queue.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c +index 97ed241bc1..4c4bd60e06 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-queue.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c +@@ -1091,6 +1091,9 @@ void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, + struct device *dev = &isys->adev->auxdev.dev; + struct ipu7_isys_buffer *ib; + struct vb2_buffer *vb; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ struct vb2_v4l2_buffer *vbuf; ++#endif + unsigned long flags; + bool first = true; + struct vb2_v4l2_buffer *buf; +@@ -1135,6 +1138,14 @@ void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, + + ipu7_isys_buf_calc_sequence_time(ib, time); + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ if (!IA_GOFO_MSG_ERR_IS_OK(info->error_info)){ ++ vbuf = to_vb2_v4l2_buffer(vb); ++ dev_dbg(dev, "buffer:%s sequence %u frame error, skip frame\n", ++ ipu7_isys_queue_to_video(aq)->vdev.name, vbuf->sequence); ++ atomic_set(&ib->skipframe_flag, 1); ++ } ++#endif + ipu7_isys_queue_buf_done(ib); + + return; diff --git a/patch/v6.18.3_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch b/patch/v6.18.3_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch new file mode 100644 index 0000000..c8baf76 --- /dev/null +++ b/patch/v6.18.3_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch @@ -0,0 +1,30 @@ +From 45efcd259dec9651369c7021cac89fea671a8d68 Mon Sep 17 00:00:00 2001 +From: Hao Yao +Date: Fri, 27 Mar 2026 18:04:38 +0800 +Subject: [PATCH 23/26] staging: ipu7: Add more insys frame format + +Signed-off-by: Hao Yao +--- + drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h +index ca9e35872e..5d1e23a940 100644 +--- a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h ++++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h +@@ -125,6 +125,15 @@ enum ipu7_insys_frame_format_type { + IPU_INSYS_FRAME_FORMAT_ARGB888 = 31, + IPU_INSYS_FRAME_FORMAT_BGRA888 = 32, + IPU_INSYS_FRAME_FORMAT_ABGR888 = 33, ++ IPU_INSYS_FRAME_FORMAT_RGB888 = 34, ++ IPU_INSYS_FRAME_FORMAT_YUV420_LEGACY = 35, ++ IPU_INSYS_FRAME_FORMAT_RAW6 = 36, ++ IPU_INSYS_FRAME_FORMAT_RAW7 = 37, ++ IPU_INSYS_FRAME_FORMAT_RGB444 = 38, ++ IPU_INSYS_FRAME_FORMAT_RGB666 = 39, ++ IPU_INSYS_FRAME_FORMAT_RAW20 = 40, ++ IPU_INSYS_FRAME_FORMAT_P010 = 41, ++ IPU_INSYS_FRAME_FORMAT_RGB555 = 42, + N_IPU_INSYS_FRAME_FORMAT + }; + diff --git a/patch/v6.18.3_iot/0024-media-ipu7-call-synchronous-RPM-suspend-in-probe-fai.patch b/patch/v6.18.3_iot/0024-media-ipu7-call-synchronous-RPM-suspend-in-probe-fai.patch new file mode 100644 index 0000000..6a76b51 --- /dev/null +++ b/patch/v6.18.3_iot/0024-media-ipu7-call-synchronous-RPM-suspend-in-probe-fai.patch @@ -0,0 +1,33 @@ +From 3b95cec7003ffcf9b7ba7a7e64314bde2f986b2f Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Mon, 5 Jan 2026 16:15:49 +0800 +Subject: [PATCH 24/26] media: ipu7: call synchronous RPM suspend in probe + failure + +If firmware authentication failed during driver probe, driver call +an asynchronous API to suspend the psys device but the bus device +will be removed soon, thus runtime PM of bus device will be disabled +soon, that will cancel the suspend request, so use synchronous +suspend to make sure the runtime suspend before disabling its RPM. + +IPU7 hardware has constraints that the PSYS device must be powered +off before ISYS, otherwise it will cause machine check error. + +Signed-off-by: Bingbu Cao +--- + drivers/staging/media/ipu7/ipu7.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c +index 9307fd6e52..f47a78e7ae 100644 +--- a/drivers/staging/media/ipu7/ipu7.c ++++ b/drivers/staging/media/ipu7/ipu7.c +@@ -2690,7 +2690,7 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) + if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) + ipu7_mmu_cleanup(isp->isys->mmu); + if (!IS_ERR_OR_NULL(isp->psys)) +- pm_runtime_put(&isp->psys->auxdev.dev); ++ pm_runtime_put_sync(&isp->psys->auxdev.dev); + ipu7_bus_del_devices(pdev); + release_firmware(isp->cpd_fw); + buttress_exit: diff --git a/patch/v6.18.3_iot/0025-media-ipu7-ignore-interrupts-when-device-is-suspende.patch b/patch/v6.18.3_iot/0025-media-ipu7-ignore-interrupts-when-device-is-suspende.patch new file mode 100644 index 0000000..1ffa2f9 --- /dev/null +++ b/patch/v6.18.3_iot/0025-media-ipu7-ignore-interrupts-when-device-is-suspende.patch @@ -0,0 +1,57 @@ +From 25ffc40c96dd9872cb8b36c00e91fb0ea0e9ad3d Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Mon, 5 Jan 2026 16:37:22 +0800 +Subject: [PATCH 25/26] media: ipu7: ignore interrupts when device is suspended + +IPU7 devices have shared interrupts with others. In some case when +IPU7 device is suspended, driver get unexpected interrupt and invalid +irq status 0xffffffff from ISR_STATUS and PB LOCAL_STATUS +registers as interrupt is triggered from other device on shared +irq line. + +In order to avoid this issue use pm_runtime_get_if_active() to check +if IPU7 device is resumed, ignore the invalid irq status and use +synchronize_irq() in suspend. + +Signed-off-by: Bingbu Cao +--- + drivers/staging/media/ipu7/ipu7-buttress.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-buttress.c b/drivers/staging/media/ipu7/ipu7-buttress.c +index e5707f5e30..e4328cafe9 100644 +--- a/drivers/staging/media/ipu7/ipu7-buttress.c ++++ b/drivers/staging/media/ipu7/ipu7-buttress.c +@@ -342,14 +342,22 @@ irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) + u32 disable_irqs = 0; + u32 irq_status; + unsigned int i; ++ int active; + +- pm_runtime_get_noresume(dev); ++ active = pm_runtime_get_if_active(dev); ++ if (active <= 0) ++ return IRQ_NONE; + + pb_irq = readl(isp->pb_base + INTERRUPT_STATUS); + writel(pb_irq, isp->pb_base + INTERRUPT_STATUS); + + /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */ + pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK); ++ if (WARN_ON_ONCE(pb_local_irq == 0xffffffff)) { ++ pm_runtime_put_noidle(dev); ++ return IRQ_NONE; ++ } ++ + if (pb_local_irq & ~BIT(0)) { + dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq, + pb_local_irq); +@@ -365,7 +373,7 @@ irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) + } + + irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); +- if (!irq_status) { ++ if (!irq_status || WARN_ON_ONCE(irq_status == 0xffffffff)) { + pm_runtime_put_noidle(dev); + return IRQ_NONE; + } diff --git a/patch/v6.18.3_iot/0026-media-ipu7-update-CDPHY-register-settings.patch b/patch/v6.18.3_iot/0026-media-ipu7-update-CDPHY-register-settings.patch new file mode 100644 index 0000000..fe9573b --- /dev/null +++ b/patch/v6.18.3_iot/0026-media-ipu7-update-CDPHY-register-settings.patch @@ -0,0 +1,77 @@ +From f8d99cd60a1a2b25ad7145ae1ac6ab473fa1529e Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Fri, 19 Dec 2025 15:29:00 +0800 +Subject: [PATCH 26/26] media: ipu7: update CDPHY register settings + +Some CPHY settings are copied from Wins code, but some of +them aren't correct and need to be fixed. + +Program 45ohm for tuning resistance to fix CPHY problem and +update the ITMINRX and GMODE for CPHY high data rate. + +Test Platform: +PTLRVP +LNLRVP + +Signed-off-by: Bingbu Cao +--- + drivers/staging/media/ipu7/ipu7-isys-csi-phy.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +index bd83aef706..a56aeecde1 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +@@ -124,6 +124,7 @@ static const struct cdr_fbk_cap_prog_params table7[] = { + { 1350, 1589, 4 }, + { 1590, 1949, 5 }, + { 1950, 2499, 6 }, ++ { 2500, 3500, 7 }, + { } + }; + +@@ -732,7 +733,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + u16 deass_thresh; + u16 delay_thresh; + u16 reset_thresh; +- u16 cap_prog = 6U; ++ u16 cap_prog; + u16 reg; + u16 val; + u32 i; +@@ -840,9 +841,10 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + reset_thresh, 9, 11); + ++ /* Tuning ITMINRX to 2 for CPHY */ + reg = CORE_DIG_CLANE_0_RW_LP_0; + for (i = 0; i < trios; i++) +- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); ++ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15); + + reg = CORE_DIG_CLANE_0_RW_LP_2; + for (i = 0; i < trios; i++) +@@ -862,7 +864,11 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + for (i = 0; i < (lanes + 1); i++) { + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); +- dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); ++ /* Set GMODE to 2 when CPHY >= 1.5Gsps */ ++ if (mbps >= 1500) ++ dwc_phy_write_mask(isys, id, reg, 2U, 3, 4); ++ else ++ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); + + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); +@@ -932,8 +938,9 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + 7, 12, 14); + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, + 0, 8, 10); ++ /* resistance tuning: 1 for 45ohm, 0 for 50ohm */ + dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, +- 0, 8, 8); ++ 1, 8, 8); + + if (aggregation) + phy_mode = isys->csi2[0].phy_mode; diff --git a/patch/v6.18.3_iot/0027-media-ipu7-isys-let-v4l2-set-default-colorspace.patch b/patch/v6.18.3_iot/0027-media-ipu7-isys-let-v4l2-set-default-colorspace.patch new file mode 100644 index 0000000..164b455 --- /dev/null +++ b/patch/v6.18.3_iot/0027-media-ipu7-isys-let-v4l2-set-default-colorspace.patch @@ -0,0 +1,36 @@ +From 4bafde77170e2a95874959676384b2e5e479e0d5 Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Mon, 6 Jul 2026 13:46:15 +0800 +Subject: [PATCH 1/3] media: ipu7-isys: let v4l2 set default colorspace + +Forcing f->fmt.pix.colorspace to V4L2_COLORSPACE_RAW in +__ipu_isys_vidioc_try_fmt_vid_cap() overrides the colorspace propagated +from the connected sensor sub-device. For non-Bayer sensors (e.g. UYVY +output from the D4XX RGB stream) this causes v4l2src and other +userspace clients to reject the negotiated format because the +round-tripped colorimetry does not match what they requested. + +Drop the hardcoded assignment so the colorspace value supplied by +userspace (or already present on the buffer) is preserved. + +Signed-off-by: Florent Pirou +Signed-off-by: Ng, Khai Wen +--- + drivers/staging/media/ipu7/ipu7-isys-video.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c +index 76b6b6884647..bbda18c95b0e 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-video.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-video.c +@@ -238,7 +238,6 @@ static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, + &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); + + f->fmt.pix.field = V4L2_FIELD_NONE; +- f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; + f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; +-- +2.43.0 + diff --git a/patch/v6.18.3_iot/0028-media-ipu7-get-source-pad-according-to-csi2-ep-fwnod.patch b/patch/v6.18.3_iot/0028-media-ipu7-get-source-pad-according-to-csi2-ep-fwnod.patch new file mode 100644 index 0000000..3ee60f9 --- /dev/null +++ b/patch/v6.18.3_iot/0028-media-ipu7-get-source-pad-according-to-csi2-ep-fwnod.patch @@ -0,0 +1,105 @@ +From 7a0d1a4ba098bbfd997d151651abe789ad53cd39 Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Mon, 6 Jul 2026 14:05:44 +0800 +Subject: [PATCH 2/3] media: ipu7: get source pad according to csi2 ep fwnode + +Signed-off-by: He, Pengpeng +Signed-off-by: Yew, Chang Ching +--- + drivers/staging/media/ipu7/ipu7-isys.c | 53 ++++++++++++++++++++------ + drivers/staging/media/ipu7/ipu7-isys.h | 1 + + 2 files changed, 42 insertions(+), 12 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index 870f60cc8f2f..8f3eff0e3ea1 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -58,23 +58,53 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, + struct ipu7_isys_csi2_config *csi2) + { + struct device *dev = &isys->adev->auxdev.dev; +- unsigned int i; ++ int source_pad; + int ret; + + v4l2_set_subdev_hostdata(sd, csi2); + +- for (i = 0; i < sd->entity.num_pads; i++) { +- if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) +- break; +- } ++ if (csi2->ep) { ++ struct fwnode_handle *ep_source; + +- if (i == sd->entity.num_pads) { +- dev_warn(dev, "no source pad in external entity\n"); +- ret = -ENOENT; +- goto skip_unregister_subdev; ++ ep_source = fwnode_graph_get_remote_endpoint(csi2->ep); ++ if (!ep_source) { ++ dev_warn(dev, "no remote endpoint for subdev\n"); ++ ret = -ENOENT; ++ goto skip_unregister_subdev; ++ } ++ ++ source_pad = media_entity_get_fwnode_pad(&sd->entity, ep_source, ++ MEDIA_PAD_FL_SOURCE); ++ fwnode_handle_put(ep_source); ++ ++ if (source_pad < 0) { ++ dev_warn( ++ dev, ++ "error in no acquire source pad in external entity\n"); ++ ret = -ENOENT; ++ goto skip_unregister_subdev; ++ } ++ ++ dev_dbg(&isys->adev->auxdev.dev, "%s: CSI2 ep %pfw\n", __func__, ++ csi2->ep); ++ dev_dbg(&isys->adev->auxdev.dev, ++ "%s: source pad %d for subdev %s\n", __func__, ++ source_pad, sd->name); ++ } else { ++ for (source_pad = 0; source_pad < sd->entity.num_pads; ++ source_pad++) { ++ if (sd->entity.pads[source_pad].flags & ++ MEDIA_PAD_FL_SOURCE) ++ break; ++ } ++ if (source_pad == sd->entity.num_pads) { ++ dev_warn(dev, "no source pad in external entity\n"); ++ ret = -ENOENT; ++ goto skip_unregister_subdev; ++ } + } + +- ret = media_create_pad_link(&sd->entity, i, ++ ret = media_create_pad_link(&sd->entity, source_pad, + &isys->csi2[csi2->port].asd.sd.entity, + 0, MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); +@@ -350,8 +380,7 @@ static int isys_notifier_init(struct ipu7_isys *isys) + s_asd->csi2.port = vep.base.port; + s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; + s_asd->csi2.bus_type = vep.bus_type; +- +- fwnode_handle_put(ep); ++ s_asd->csi2.ep = ep; + + continue; + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h +index 0343b2ef00b3..69fb564e64c2 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -158,6 +158,7 @@ struct ipu7_isys_csi2_config { + unsigned int nlanes; + unsigned int port; + enum v4l2_mbus_type bus_type; ++ struct fwnode_handle *ep; + }; + + struct sensor_async_sd { +-- +2.43.0 + diff --git a/patch/v6.18.3_iot/0029-media-ipu7-Clean-csi2-fwnode-ep-when-destroying-v4l2.patch b/patch/v6.18.3_iot/0029-media-ipu7-Clean-csi2-fwnode-ep-when-destroying-v4l2.patch new file mode 100644 index 0000000..297814a --- /dev/null +++ b/patch/v6.18.3_iot/0029-media-ipu7-Clean-csi2-fwnode-ep-when-destroying-v4l2.patch @@ -0,0 +1,38 @@ +From c04551210bc6432dd38f750d7091b714382904fd Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Mon, 6 Jul 2026 14:07:41 +0800 +Subject: [PATCH 3/3] media: ipu7: Clean csi2 fwnode ep when destroying + v4l2_async_connection + +Signed-off-by: He, Pengpeng +Signed-off-by: Yew, Chang Ching +--- + drivers/staging/media/ipu7/ipu7-isys.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index 8f3eff0e3ea1..8a7002d9d8bc 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -329,9 +329,18 @@ static int isys_notifier_complete(struct v4l2_async_notifier *notifier) + return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); + } + ++static void isys_notifier_destroy(struct v4l2_async_connection *asc) ++{ ++ struct sensor_async_sd *s_asd = ++ container_of(asc, struct sensor_async_sd, asc); ++ ++ fwnode_handle_put(s_asd->csi2.ep); ++} ++ + static const struct v4l2_async_notifier_operations isys_async_ops = { + .bound = isys_notifier_bound, + .complete = isys_notifier_complete, ++ .destroy = isys_notifier_destroy, + }; + + static int isys_notifier_init(struct ipu7_isys *isys) +-- +2.43.0 + diff --git a/patch/v6.18.3_iot/0030-kernel-add-support-for-Maxim-MAX96717-GMSL2-Serializ.patch b/patch/v6.18.3_iot/0030-kernel-add-support-for-Maxim-MAX96717-GMSL2-Serializ.patch new file mode 100644 index 0000000..8da4fef --- /dev/null +++ b/patch/v6.18.3_iot/0030-kernel-add-support-for-Maxim-MAX96717-GMSL2-Serializ.patch @@ -0,0 +1,1180 @@ +From 198d561353101bc2ee37187f35daaaff221212f7 Mon Sep 17 00:00:00 2001 +From: "He, Pengpeng" +Date: Wed, 2 Sep 2026 13:00:44 +0800 +Subject: [PATCH] kernel: add support for Maxim MAX96717 GMSL2 Serializer and + related configurations + +--- + drivers/media/i2c/Kconfig | 19 +- + drivers/media/i2c/Makefile | 2 +- + drivers/media/i2c/max96717.c | 1102 ---------------------------------- + 3 files changed, 3 insertions(+), 1120 deletions(-) + delete mode 100644 drivers/media/i2c/max96717.c + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index 2f19e784c112..9e9fb64e699e 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -825,6 +825,8 @@ config VIDEO_THP7312 + + endmenu + ++source "drivers/media/i2c/maxim-serdes/Kconfig" ++ + menuconfig VIDEO_CAMERA_LENS + bool "Lens drivers" + depends on MEDIA_CAMERA_SUPPORT && I2C +@@ -1739,23 +1741,6 @@ config VIDEO_MAX96714 + To compile this driver as a module, choose M here: the + module will be called max96714. + +-config VIDEO_MAX96717 +- tristate "Maxim MAX96717 GMSL2 Serializer support" +- depends on I2C && VIDEO_DEV && COMMON_CLK +- select I2C_MUX +- select MEDIA_CONTROLLER +- select GPIOLIB +- select V4L2_CCI_I2C +- select V4L2_FWNODE +- select VIDEO_V4L2_SUBDEV_API +- help +- Device driver for the Maxim MAX96717 GMSL2 Serializer. +- MAX96717 serializers convert video on a MIPI CSI-2 +- input to a GMSL2 output. +- +- To compile this driver as a module, choose M here: the +- module will be called max96717. +- + endmenu + + endif # VIDEO_DEV +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index b9b8559eb9eb..3d5dfd90e0d5 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -72,7 +72,6 @@ obj-$(CONFIG_VIDEO_M52790) += m52790.o + obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o + obj-$(CONFIG_VIDEO_MAX9286) += max9286.o + obj-$(CONFIG_VIDEO_MAX96714) += max96714.o +-obj-$(CONFIG_VIDEO_MAX96717) += max96717.o + obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o + obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o + obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o +@@ -166,5 +165,6 @@ obj-$(CONFIG_VIDEO_TW9910) += tw9910.o + obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o + obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o + obj-$(CONFIG_VIDEO_WM8739) += wm8739.o + obj-$(CONFIG_VIDEO_WM8775) += wm8775.o ++obj-$(CONFIG_VIDEO_MAXIM_SERDES) += maxim-serdes/ + obj-$(CONFIG_VIDEO_LT6911GXD) += lt6911gxd.o +diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c +deleted file mode 100644 +index c8ae7890d9fa..000000000000 +--- a/drivers/media/i2c/max96717.c ++++ /dev/null +@@ -1,1102 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-/* +- * Maxim GMSL2 Serializer Driver +- * +- * Copyright (C) 2024 Collabora Ltd. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#define MAX96717_DEVICE_ID 0xbf +-#define MAX96717F_DEVICE_ID 0xc8 +-#define MAX96717_PORTS 2 +-#define MAX96717_PAD_SINK 0 +-#define MAX96717_PAD_SOURCE 1 +-#define MAX96717_CSI_NLANES 4 +- +-#define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL +- +-/* DEV */ +-#define MAX96717_REG3 CCI_REG8(0x3) +-#define MAX96717_RCLKSEL GENMASK(1, 0) +-#define RCLKSEL_REF_PLL CCI_REG8(0x3) +-#define MAX96717_REG6 CCI_REG8(0x6) +-#define RCLKEN BIT(5) +-#define MAX96717_DEV_ID CCI_REG8(0xd) +-#define MAX96717_DEV_REV CCI_REG8(0xe) +-#define MAX96717_DEV_REV_MASK GENMASK(3, 0) +- +-/* VID_TX Z */ +-#define MAX96717_VIDEO_TX0 CCI_REG8(0x110) +-#define MAX96717_VIDEO_AUTO_BPP BIT(3) +-#define MAX96717_VIDEO_TX2 CCI_REG8(0x112) +-#define MAX96717_VIDEO_PCLKDET BIT(7) +- +-/* VTX_Z */ +-#define MAX96717_VTX0 CCI_REG8(0x24e) +-#define MAX96717_VTX1 CCI_REG8(0x24f) +-#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1) +-#define MAX96717_VTX_VS_DLY CCI_REG24(0x250) +-#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253) +-#define MAX96717_VTX_VS_LOW CCI_REG24(0x256) +-#define MAX96717_VTX_V2H CCI_REG24(0x259) +-#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c) +-#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e) +-#define MAX96717_VTX_HS_CNT CCI_REG16(0x260) +-#define MAX96717_VTX_V2D CCI_REG24(0x262) +-#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265) +-#define MAX96717_VTX_DE_LOW CCI_REG16(0x267) +-#define MAX96717_VTX_DE_CNT CCI_REG16(0x269) +-#define MAX96717_VTX29 CCI_REG8(0x26b) +-#define MAX96717_VTX_MODE GENMASK(1, 0) +-#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c) +-#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d) +-#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270) +-#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273) +-#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274) +-#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275) +- +-/* GPIO */ +-#define MAX96717_NUM_GPIO 11 +-#define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3) +-#define MAX96717_GPIO_OUT BIT(4) +-#define MAX96717_GPIO_IN BIT(3) +-#define MAX96717_GPIO_RX_EN BIT(2) +-#define MAX96717_GPIO_TX_EN BIT(1) +-#define MAX96717_GPIO_OUT_DIS BIT(0) +- +-/* FRONTTOP */ +-/* MAX96717 only have CSI port 'B' */ +-#define MAX96717_FRONTOP0 CCI_REG8(0x308) +-#define MAX96717_START_PORT_B BIT(5) +- +-/* MIPI_RX */ +-#define MAX96717_MIPI_RX1 CCI_REG8(0x331) +-#define MAX96717_MIPI_LANES_CNT GENMASK(5, 4) +-#define MAX96717_MIPI_RX2 CCI_REG8(0x332) /* phy1 Lanes map */ +-#define MAX96717_PHY2_LANES_MAP GENMASK(7, 4) +-#define MAX96717_MIPI_RX3 CCI_REG8(0x333) /* phy2 Lanes map */ +-#define MAX96717_PHY1_LANES_MAP GENMASK(3, 0) +-#define MAX96717_MIPI_RX4 CCI_REG8(0x334) /* phy1 lane polarities */ +-#define MAX96717_PHY1_LANES_POL GENMASK(6, 4) +-#define MAX96717_MIPI_RX5 CCI_REG8(0x335) /* phy2 lane polarities */ +-#define MAX96717_PHY2_LANES_POL GENMASK(2, 0) +- +-/* MIPI_RX_EXT */ +-#define MAX96717_MIPI_RX_EXT11 CCI_REG8(0x383) +-#define MAX96717_TUN_MODE BIT(7) +- +-/* REF_VTG */ +-#define REF_VTG0 CCI_REG8(0x3f0) +-#define REFGEN_PREDEF_EN BIT(6) +-#define REFGEN_PREDEF_FREQ_MASK GENMASK(5, 4) +-#define REFGEN_PREDEF_FREQ_ALT BIT(3) +-#define REFGEN_RST BIT(1) +-#define REFGEN_EN BIT(0) +- +-/* MISC */ +-#define PIO_SLEW_1 CCI_REG8(0x570) +- +-enum max96717_vpg_mode { +- MAX96717_VPG_DISABLED = 0, +- MAX96717_VPG_CHECKERBOARD = 1, +- MAX96717_VPG_GRADIENT = 2, +-}; +- +-struct max96717_priv { +- struct i2c_client *client; +- struct regmap *regmap; +- struct i2c_mux_core *mux; +- struct v4l2_mbus_config_mipi_csi2 mipi_csi2; +- struct v4l2_subdev sd; +- struct media_pad pads[MAX96717_PORTS]; +- struct v4l2_ctrl_handler ctrl_handler; +- struct v4l2_async_notifier notifier; +- struct v4l2_subdev *source_sd; +- u16 source_sd_pad; +- u64 enabled_source_streams; +- u8 pll_predef_index; +- struct clk_hw clk_hw; +- struct gpio_chip gpio_chip; +- enum max96717_vpg_mode pattern; +-}; +- +-static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd) +-{ +- return container_of(sd, struct max96717_priv, sd); +-} +- +-static inline struct max96717_priv *clk_hw_to_max96717(struct clk_hw *hw) +-{ +- return container_of(hw, struct max96717_priv, clk_hw); +-} +- +-static int max96717_i2c_mux_select(struct i2c_mux_core *mux, u32 chan) +-{ +- return 0; +-} +- +-static int max96717_i2c_mux_init(struct max96717_priv *priv) +-{ +- priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, +- 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, +- max96717_i2c_mux_select, NULL); +- if (!priv->mux) +- return -ENOMEM; +- +- return i2c_mux_add_adapter(priv->mux, 0, 0); +-} +- +-static inline int max96717_start_csi(struct max96717_priv *priv, bool start) +-{ +- return cci_update_bits(priv->regmap, MAX96717_FRONTOP0, +- MAX96717_START_PORT_B, +- start ? MAX96717_START_PORT_B : 0, NULL); +-} +- +-static int max96717_apply_patgen_timing(struct max96717_priv *priv, +- struct v4l2_subdev_state *state) +-{ +- struct v4l2_mbus_framefmt *fmt = +- v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE); +- const u32 h_active = fmt->width; +- const u32 h_fp = 88; +- const u32 h_sw = 44; +- const u32 h_bp = 148; +- u32 h_tot; +- const u32 v_active = fmt->height; +- const u32 v_fp = 4; +- const u32 v_sw = 5; +- const u32 v_bp = 36; +- u32 v_tot; +- int ret = 0; +- +- h_tot = h_active + h_fp + h_sw + h_bp; +- v_tot = v_active + v_fp + v_sw + v_bp; +- +- /* 75 Mhz pixel clock */ +- cci_update_bits(priv->regmap, MAX96717_VTX1, +- MAX96717_PATTERN_CLK_FREQ, 0xa, &ret); +- +- dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height, +- fmt->width); +- +- cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret); +- cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret); +- cci_write(priv->regmap, MAX96717_VTX_VS_LOW, +- (v_active + v_fp + v_bp) * h_tot, &ret); +- cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret); +- cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp, +- &ret); +- cci_write(priv->regmap, MAX96717_VTX_V2D, +- h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret); +- cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret); +- cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret); +- cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp, +- &ret); +- cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret); +- /* B G R */ +- cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret); +- /* B G R */ +- cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret); +- cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret); +- cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret); +- cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret); +- cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret); +- +- return ret; +-} +- +-static int max96717_apply_patgen(struct max96717_priv *priv, +- struct v4l2_subdev_state *state) +-{ +- unsigned int val; +- int ret = 0; +- +- if (priv->pattern) +- ret = max96717_apply_patgen_timing(priv, state); +- +- cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0, +- &ret); +- +- val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern); +- cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE, +- val, &ret); +- return ret; +-} +- +-static int max96717_s_ctrl(struct v4l2_ctrl *ctrl) +-{ +- struct max96717_priv *priv = +- container_of(ctrl->handler, struct max96717_priv, ctrl_handler); +- int ret; +- +- switch (ctrl->id) { +- case V4L2_CID_TEST_PATTERN: +- if (priv->enabled_source_streams) +- return -EBUSY; +- priv->pattern = ctrl->val; +- break; +- default: +- return -EINVAL; +- } +- +- /* Use bpp from bpp register */ +- ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0, +- MAX96717_VIDEO_AUTO_BPP, +- priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP, +- NULL); +- +- /* +- * Pattern generator doesn't work with tunnel mode. +- * Needs RGB color format and deserializer tunnel mode must be disabled. +- */ +- return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11, +- MAX96717_TUN_MODE, +- priv->pattern ? 0 : MAX96717_TUN_MODE, &ret); +-} +- +-static const char * const max96717_test_pattern[] = { +- "Disabled", +- "Checkerboard", +- "Gradient" +-}; +- +-static const struct v4l2_ctrl_ops max96717_ctrl_ops = { +- .s_ctrl = max96717_s_ctrl, +-}; +- +-static int max96717_gpiochip_get(struct gpio_chip *gpiochip, +- unsigned int offset) +-{ +- struct max96717_priv *priv = gpiochip_get_data(gpiochip); +- u64 val; +- int ret; +- +- ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), +- &val, NULL); +- if (ret) +- return ret; +- +- if (val & MAX96717_GPIO_OUT_DIS) +- return !!(val & MAX96717_GPIO_IN); +- else +- return !!(val & MAX96717_GPIO_OUT); +-} +- +-static int max96717_gpiochip_set(struct gpio_chip *gpiochip, +- unsigned int offset, int value) +-{ +- struct max96717_priv *priv = gpiochip_get_data(gpiochip); +- +- return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), +- MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL); +-} +- +-static int max96717_gpio_get_direction(struct gpio_chip *gpiochip, +- unsigned int offset) +-{ +- struct max96717_priv *priv = gpiochip_get_data(gpiochip); +- u64 val; +- int ret; +- +- ret = cci_read(priv->regmap, MAX96717_GPIO_REG_A(offset), &val, NULL); +- if (ret < 0) +- return ret; +- +- return !!(val & MAX96717_GPIO_OUT_DIS); +-} +- +-static int max96717_gpio_direction_out(struct gpio_chip *gpiochip, +- unsigned int offset, int value) +-{ +- struct max96717_priv *priv = gpiochip_get_data(gpiochip); +- +- return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), +- MAX96717_GPIO_OUT_DIS | MAX96717_GPIO_OUT, +- value ? MAX96717_GPIO_OUT : 0, NULL); +-} +- +-static int max96717_gpio_direction_in(struct gpio_chip *gpiochip, +- unsigned int offset) +-{ +- struct max96717_priv *priv = gpiochip_get_data(gpiochip); +- +- return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset), +- MAX96717_GPIO_OUT_DIS, MAX96717_GPIO_OUT_DIS, +- NULL); +-} +- +-static int max96717_gpiochip_probe(struct max96717_priv *priv) +-{ +- struct device *dev = &priv->client->dev; +- struct gpio_chip *gc = &priv->gpio_chip; +- int i, ret = 0; +- +- gc->label = dev_name(dev); +- gc->parent = dev; +- gc->owner = THIS_MODULE; +- gc->ngpio = MAX96717_NUM_GPIO; +- gc->base = -1; +- gc->can_sleep = true; +- gc->get_direction = max96717_gpio_get_direction; +- gc->direction_input = max96717_gpio_direction_in; +- gc->direction_output = max96717_gpio_direction_out; +- gc->set = max96717_gpiochip_set; +- gc->get = max96717_gpiochip_get; +- +- /* Disable GPIO forwarding */ +- for (i = 0; i < gc->ngpio; i++) +- cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(i), +- MAX96717_GPIO_RX_EN | MAX96717_GPIO_TX_EN, +- 0, &ret); +- +- if (ret) +- return ret; +- +- ret = devm_gpiochip_add_data(dev, gc, priv); +- if (ret) { +- dev_err(dev, "Unable to create gpio_chip\n"); +- return ret; +- } +- +- return 0; +-} +- +-static int _max96717_set_routing(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *state, +- struct v4l2_subdev_krouting *routing) +-{ +- static const struct v4l2_mbus_framefmt format = { +- .width = 1280, +- .height = 1080, +- .code = MEDIA_BUS_FMT_Y8_1X8, +- .field = V4L2_FIELD_NONE, +- }; +- int ret; +- +- ret = v4l2_subdev_routing_validate(sd, routing, +- V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); +- if (ret) +- return ret; +- +- ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int max96717_set_routing(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *state, +- enum v4l2_subdev_format_whence which, +- struct v4l2_subdev_krouting *routing) +-{ +- struct max96717_priv *priv = sd_to_max96717(sd); +- +- if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) +- return -EBUSY; +- +- return _max96717_set_routing(sd, state, routing); +-} +- +-static int max96717_set_fmt(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *state, +- struct v4l2_subdev_format *format) +-{ +- struct max96717_priv *priv = sd_to_max96717(sd); +- struct v4l2_mbus_framefmt *fmt; +- u64 stream_source_mask; +- +- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && +- priv->enabled_source_streams) +- return -EBUSY; +- +- /* No transcoding, source and sink formats must match. */ +- if (format->pad == MAX96717_PAD_SOURCE) +- return v4l2_subdev_get_fmt(sd, state, format); +- +- /* Set sink format */ +- fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); +- if (!fmt) +- return -EINVAL; +- +- *fmt = format->format; +- +- /* Propagate to source format */ +- fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, +- format->stream); +- if (!fmt) +- return -EINVAL; +- *fmt = format->format; +- +- stream_source_mask = BIT(format->stream); +- +- return v4l2_subdev_state_xlate_streams(state, MAX96717_PAD_SOURCE, +- MAX96717_PAD_SINK, +- &stream_source_mask); +-} +- +-static int max96717_init_state(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *state) +-{ +- struct v4l2_subdev_route routes[] = { +- { +- .sink_pad = MAX96717_PAD_SINK, +- .sink_stream = 0, +- .source_pad = MAX96717_PAD_SOURCE, +- .source_stream = 0, +- .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, +- }, +- }; +- struct v4l2_subdev_krouting routing = { +- .num_routes = ARRAY_SIZE(routes), +- .routes = routes, +- }; +- +- return _max96717_set_routing(sd, state, &routing); +-} +- +-static bool max96717_pipe_pclkdet(struct max96717_priv *priv) +-{ +- u64 val = 0; +- +- cci_read(priv->regmap, MAX96717_VIDEO_TX2, &val, NULL); +- +- return val & MAX96717_VIDEO_PCLKDET; +-} +- +-static int max96717_log_status(struct v4l2_subdev *sd) +-{ +- struct max96717_priv *priv = sd_to_max96717(sd); +- struct device *dev = &priv->client->dev; +- +- dev_info(dev, "Serializer: max96717\n"); +- dev_info(dev, "Pipe: pclkdet:%d\n", max96717_pipe_pclkdet(priv)); +- +- return 0; +-} +- +-static int max96717_enable_streams(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *state, u32 pad, +- u64 streams_mask) +-{ +- struct max96717_priv *priv = sd_to_max96717(sd); +- u64 sink_streams; +- int ret; +- +- if (!priv->enabled_source_streams) +- max96717_start_csi(priv, true); +- +- ret = max96717_apply_patgen(priv, state); +- if (ret) +- goto stop_csi; +- +- if (!priv->pattern) { +- sink_streams = +- v4l2_subdev_state_xlate_streams(state, +- MAX96717_PAD_SOURCE, +- MAX96717_PAD_SINK, +- &streams_mask); +- +- ret = v4l2_subdev_enable_streams(priv->source_sd, +- priv->source_sd_pad, +- sink_streams); +- if (ret) +- goto stop_csi; +- } +- +- priv->enabled_source_streams |= streams_mask; +- +- return 0; +- +-stop_csi: +- if (!priv->enabled_source_streams) +- max96717_start_csi(priv, false); +- +- return ret; +-} +- +-static int max96717_disable_streams(struct v4l2_subdev *sd, +- struct v4l2_subdev_state *state, u32 pad, +- u64 streams_mask) +-{ +- struct max96717_priv *priv = sd_to_max96717(sd); +- u64 sink_streams; +- +- /* +- * Stop the CSI receiver first then the source, +- * otherwise the device may become unresponsive +- * while holding the I2C bus low. +- */ +- priv->enabled_source_streams &= ~streams_mask; +- if (!priv->enabled_source_streams) +- max96717_start_csi(priv, false); +- +- if (!priv->pattern) { +- int ret; +- +- sink_streams = +- v4l2_subdev_state_xlate_streams(state, +- MAX96717_PAD_SOURCE, +- MAX96717_PAD_SINK, +- &streams_mask); +- +- ret = v4l2_subdev_disable_streams(priv->source_sd, +- priv->source_sd_pad, +- sink_streams); +- if (ret) +- return ret; +- } +- +- return 0; +-} +- +-static const struct v4l2_subdev_pad_ops max96717_pad_ops = { +- .enable_streams = max96717_enable_streams, +- .disable_streams = max96717_disable_streams, +- .set_routing = max96717_set_routing, +- .get_fmt = v4l2_subdev_get_fmt, +- .set_fmt = max96717_set_fmt, +-}; +- +-static const struct v4l2_subdev_core_ops max96717_subdev_core_ops = { +- .log_status = max96717_log_status, +-}; +- +-static const struct v4l2_subdev_internal_ops max96717_internal_ops = { +- .init_state = max96717_init_state, +-}; +- +-static const struct v4l2_subdev_ops max96717_subdev_ops = { +- .core = &max96717_subdev_core_ops, +- .pad = &max96717_pad_ops, +-}; +- +-static const struct media_entity_operations max96717_entity_ops = { +- .link_validate = v4l2_subdev_link_validate, +-}; +- +-static int max96717_notify_bound(struct v4l2_async_notifier *notifier, +- struct v4l2_subdev *source_subdev, +- struct v4l2_async_connection *asd) +-{ +- struct max96717_priv *priv = sd_to_max96717(notifier->sd); +- struct device *dev = &priv->client->dev; +- int ret; +- +- ret = media_entity_get_fwnode_pad(&source_subdev->entity, +- source_subdev->fwnode, +- MEDIA_PAD_FL_SOURCE); +- if (ret < 0) { +- dev_err(dev, "Failed to find pad for %s\n", +- source_subdev->name); +- return ret; +- } +- +- priv->source_sd = source_subdev; +- priv->source_sd_pad = ret; +- +- ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, +- &priv->sd.entity, 0, +- MEDIA_LNK_FL_ENABLED | +- MEDIA_LNK_FL_IMMUTABLE); +- if (ret) { +- dev_err(dev, "Unable to link %s:%u -> %s:0\n", +- source_subdev->name, priv->source_sd_pad, +- priv->sd.name); +- return ret; +- } +- +- return 0; +-} +- +-static const struct v4l2_async_notifier_operations max96717_notify_ops = { +- .bound = max96717_notify_bound, +-}; +- +-static int max96717_v4l2_notifier_register(struct max96717_priv *priv) +-{ +- struct device *dev = &priv->client->dev; +- struct v4l2_async_connection *asd; +- struct fwnode_handle *ep_fwnode; +- int ret; +- +- ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), +- MAX96717_PAD_SINK, 0, 0); +- if (!ep_fwnode) { +- dev_err(dev, "No graph endpoint\n"); +- return -ENODEV; +- } +- +- v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd); +- +- asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, +- struct v4l2_async_connection); +- +- fwnode_handle_put(ep_fwnode); +- +- if (IS_ERR(asd)) { +- dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); +- v4l2_async_nf_cleanup(&priv->notifier); +- return PTR_ERR(asd); +- } +- +- priv->notifier.ops = &max96717_notify_ops; +- +- ret = v4l2_async_nf_register(&priv->notifier); +- if (ret) { +- dev_err(dev, "Failed to register subdev_notifier"); +- v4l2_async_nf_cleanup(&priv->notifier); +- return ret; +- } +- +- return 0; +-} +- +-static int max96717_subdev_init(struct max96717_priv *priv) +-{ +- struct device *dev = &priv->client->dev; +- int ret; +- +- v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops); +- priv->sd.internal_ops = &max96717_internal_ops; +- +- v4l2_ctrl_handler_init(&priv->ctrl_handler, 1); +- priv->sd.ctrl_handler = &priv->ctrl_handler; +- +- v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler, +- &max96717_ctrl_ops, +- V4L2_CID_TEST_PATTERN, +- ARRAY_SIZE(max96717_test_pattern) - 1, +- 0, 0, max96717_test_pattern); +- if (priv->ctrl_handler.error) { +- ret = priv->ctrl_handler.error; +- goto err_free_ctrl; +- } +- +- priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; +- priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; +- priv->sd.entity.ops = &max96717_entity_ops; +- +- priv->pads[MAX96717_PAD_SINK].flags = MEDIA_PAD_FL_SINK; +- priv->pads[MAX96717_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; +- +- ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); +- if (ret) { +- dev_err_probe(dev, ret, "Failed to init pads\n"); +- goto err_free_ctrl; +- } +- +- ret = v4l2_subdev_init_finalize(&priv->sd); +- if (ret) { +- dev_err_probe(dev, ret, +- "v4l2 subdev init finalized failed\n"); +- goto err_entity_cleanup; +- } +- ret = max96717_v4l2_notifier_register(priv); +- if (ret) { +- dev_err_probe(dev, ret, +- "v4l2 subdev notifier register failed\n"); +- goto err_free_state; +- } +- +- ret = v4l2_async_register_subdev(&priv->sd); +- if (ret) { +- dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); +- goto err_unreg_notif; +- } +- +- return 0; +- +-err_unreg_notif: +- v4l2_async_nf_unregister(&priv->notifier); +- v4l2_async_nf_cleanup(&priv->notifier); +-err_free_state: +- v4l2_subdev_cleanup(&priv->sd); +-err_entity_cleanup: +- media_entity_cleanup(&priv->sd.entity); +-err_free_ctrl: +- v4l2_ctrl_handler_free(&priv->ctrl_handler); +- +- return ret; +-} +- +-static void max96717_subdev_uninit(struct max96717_priv *priv) +-{ +- v4l2_async_unregister_subdev(&priv->sd); +- v4l2_async_nf_unregister(&priv->notifier); +- v4l2_async_nf_cleanup(&priv->notifier); +- v4l2_subdev_cleanup(&priv->sd); +- media_entity_cleanup(&priv->sd.entity); +- v4l2_ctrl_handler_free(&priv->ctrl_handler); +-} +- +-struct max96717_pll_predef_freq { +- unsigned long freq; +- bool is_alt; +- u8 val; +-}; +- +-static const struct max96717_pll_predef_freq max96717_predef_freqs[] = { +- { 13500000, true, 0 }, { 19200000, false, 0 }, +- { 24000000, true, 1 }, { 27000000, false, 1 }, +- { 37125000, false, 2 }, { 74250000, false, 3 }, +-}; +- +-static unsigned long +-max96717_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +-{ +- struct max96717_priv *priv = clk_hw_to_max96717(hw); +- +- return max96717_predef_freqs[priv->pll_predef_index].freq; +-} +- +-static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv, +- unsigned long rate) +-{ +- unsigned int i, idx = 0; +- unsigned long diff_new, diff_old = U32_MAX; +- +- for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) { +- diff_new = abs(rate - max96717_predef_freqs[i].freq); +- if (diff_new < diff_old) { +- diff_old = diff_new; +- idx = i; +- } +- } +- +- return idx; +-} +- +-static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long *parent_rate) +-{ +- struct max96717_priv *priv = clk_hw_to_max96717(hw); +- struct device *dev = &priv->client->dev; +- unsigned int idx; +- +- idx = max96717_clk_find_best_index(priv, rate); +- +- if (rate != max96717_predef_freqs[idx].freq) { +- dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n", +- rate, max96717_predef_freqs[idx].freq); +- } +- +- return max96717_predef_freqs[idx].freq; +-} +- +-static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate, +- unsigned long parent_rate) +-{ +- struct max96717_priv *priv = clk_hw_to_max96717(hw); +- unsigned int val, idx; +- int ret = 0; +- +- idx = max96717_clk_find_best_index(priv, rate); +- +- val = FIELD_PREP(REFGEN_PREDEF_FREQ_MASK, +- max96717_predef_freqs[idx].val); +- +- if (max96717_predef_freqs[idx].is_alt) +- val |= REFGEN_PREDEF_FREQ_ALT; +- +- val |= REFGEN_RST | REFGEN_PREDEF_EN; +- +- cci_write(priv->regmap, REF_VTG0, val, &ret); +- cci_update_bits(priv->regmap, REF_VTG0, REFGEN_RST | REFGEN_EN, +- REFGEN_EN, &ret); +- if (ret) +- return ret; +- +- priv->pll_predef_index = idx; +- +- return 0; +-} +- +-static int max96717_clk_prepare(struct clk_hw *hw) +-{ +- struct max96717_priv *priv = clk_hw_to_max96717(hw); +- +- return cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, +- RCLKEN, NULL); +-} +- +-static void max96717_clk_unprepare(struct clk_hw *hw) +-{ +- struct max96717_priv *priv = clk_hw_to_max96717(hw); +- +- cci_update_bits(priv->regmap, MAX96717_REG6, RCLKEN, 0, NULL); +-} +- +-static const struct clk_ops max96717_clk_ops = { +- .prepare = max96717_clk_prepare, +- .unprepare = max96717_clk_unprepare, +- .set_rate = max96717_clk_set_rate, +- .recalc_rate = max96717_clk_recalc_rate, +- .round_rate = max96717_clk_round_rate, +-}; +- +-static int max96717_register_clkout(struct max96717_priv *priv) +-{ +- struct device *dev = &priv->client->dev; +- struct clk_init_data init = { .ops = &max96717_clk_ops }; +- int ret; +- +- init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", dev_name(dev)); +- if (!init.name) +- return -ENOMEM; +- +- /* RCLKSEL Reference PLL output */ +- ret = cci_update_bits(priv->regmap, MAX96717_REG3, MAX96717_RCLKSEL, +- MAX96717_RCLKSEL, NULL); +- /* MFP4 fastest slew rate */ +- cci_update_bits(priv->regmap, PIO_SLEW_1, BIT(5) | BIT(4), 0, &ret); +- if (ret) +- goto free_init_name; +- +- priv->clk_hw.init = &init; +- +- /* Initialize to 24 MHz */ +- ret = max96717_clk_set_rate(&priv->clk_hw, +- MAX96717_DEFAULT_CLKOUT_RATE, 0); +- if (ret < 0) +- goto free_init_name; +- +- ret = devm_clk_hw_register(dev, &priv->clk_hw); +- kfree(init.name); +- if (ret) +- return dev_err_probe(dev, ret, "Cannot register clock HW\n"); +- +- ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, +- &priv->clk_hw); +- if (ret) +- return dev_err_probe(dev, ret, +- "Cannot add OF clock provider\n"); +- +- return 0; +- +-free_init_name: +- kfree(init.name); +- return ret; +-} +- +-static int max96717_init_csi_lanes(struct max96717_priv *priv) +-{ +- struct v4l2_mbus_config_mipi_csi2 *mipi = &priv->mipi_csi2; +- unsigned long lanes_used = 0; +- unsigned int nlanes, lane, val = 0; +- int ret; +- +- nlanes = mipi->num_data_lanes; +- +- ret = cci_update_bits(priv->regmap, MAX96717_MIPI_RX1, +- MAX96717_MIPI_LANES_CNT, +- FIELD_PREP(MAX96717_MIPI_LANES_CNT, +- nlanes - 1), NULL); +- +- /* lanes polarity */ +- for (lane = 0; lane < nlanes + 1; lane++) { +- if (!mipi->lane_polarities[lane]) +- continue; +- /* Clock lane */ +- if (lane == 0) +- val |= BIT(2); +- else if (lane < 3) +- val |= BIT(lane - 1); +- else +- val |= BIT(lane); +- } +- +- cci_update_bits(priv->regmap, MAX96717_MIPI_RX5, +- MAX96717_PHY2_LANES_POL, +- FIELD_PREP(MAX96717_PHY2_LANES_POL, val), &ret); +- +- cci_update_bits(priv->regmap, MAX96717_MIPI_RX4, +- MAX96717_PHY1_LANES_POL, +- FIELD_PREP(MAX96717_PHY1_LANES_POL, +- val >> 3), &ret); +- /* lanes mapping */ +- for (lane = 0, val = 0; lane < nlanes; lane++) { +- val |= (mipi->data_lanes[lane] - 1) << (lane * 2); +- lanes_used |= BIT(mipi->data_lanes[lane] - 1); +- } +- +- /* +- * Unused lanes need to be mapped as well to not have +- * the same lanes mapped twice. +- */ +- for (; lane < MAX96717_CSI_NLANES; lane++) { +- unsigned int idx = find_first_zero_bit(&lanes_used, +- MAX96717_CSI_NLANES); +- +- val |= idx << (lane * 2); +- lanes_used |= BIT(idx); +- } +- +- cci_update_bits(priv->regmap, MAX96717_MIPI_RX3, +- MAX96717_PHY1_LANES_MAP, +- FIELD_PREP(MAX96717_PHY1_LANES_MAP, val), &ret); +- +- return cci_update_bits(priv->regmap, MAX96717_MIPI_RX2, +- MAX96717_PHY2_LANES_MAP, +- FIELD_PREP(MAX96717_PHY2_LANES_MAP, val >> 4), +- &ret); +-} +- +-static int max96717_hw_init(struct max96717_priv *priv) +-{ +- struct device *dev = &priv->client->dev; +- u64 dev_id, val; +- int ret; +- +- ret = cci_read(priv->regmap, MAX96717_DEV_ID, &dev_id, NULL); +- if (ret) +- return dev_err_probe(dev, ret, +- "Fail to read the device id\n"); +- +- if (dev_id != MAX96717_DEVICE_ID && dev_id != MAX96717F_DEVICE_ID) +- return dev_err_probe(dev, -EOPNOTSUPP, +- "Unsupported device id got %x\n", (u8)dev_id); +- +- ret = cci_read(priv->regmap, MAX96717_DEV_REV, &val, NULL); +- if (ret) +- return dev_err_probe(dev, ret, +- "Fail to read device revision"); +- +- dev_dbg(dev, "Found %x (rev %lx)\n", (u8)dev_id, +- (u8)val & MAX96717_DEV_REV_MASK); +- +- ret = cci_read(priv->regmap, MAX96717_MIPI_RX_EXT11, &val, NULL); +- if (ret) +- return dev_err_probe(dev, ret, +- "Fail to read mipi rx extension"); +- +- if (!(val & MAX96717_TUN_MODE)) +- return dev_err_probe(dev, -EOPNOTSUPP, +- "Only supporting tunnel mode"); +- +- return max96717_init_csi_lanes(priv); +-} +- +-static int max96717_parse_dt(struct max96717_priv *priv) +-{ +- struct device *dev = &priv->client->dev; +- struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; +- struct fwnode_handle *ep_fwnode; +- unsigned char num_data_lanes; +- int ret; +- +- ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), +- MAX96717_PAD_SINK, 0, 0); +- if (!ep_fwnode) +- return dev_err_probe(dev, -ENOENT, "no endpoint found\n"); +- +- ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep); +- +- fwnode_handle_put(ep_fwnode); +- +- if (ret < 0) +- return dev_err_probe(dev, ret, "Failed to parse sink endpoint"); +- +- num_data_lanes = vep.bus.mipi_csi2.num_data_lanes; +- if (num_data_lanes < 1 || num_data_lanes > MAX96717_CSI_NLANES) +- return dev_err_probe(dev, -EINVAL, +- "Invalid data lanes must be 1 to 4\n"); +- +- priv->mipi_csi2 = vep.bus.mipi_csi2; +- +- return 0; +-} +- +-static int max96717_probe(struct i2c_client *client) +-{ +- struct device *dev = &client->dev; +- struct max96717_priv *priv; +- int ret; +- +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; +- +- priv->client = client; +- priv->regmap = devm_cci_regmap_init_i2c(client, 16); +- if (IS_ERR(priv->regmap)) { +- ret = PTR_ERR(priv->regmap); +- return dev_err_probe(dev, ret, "Failed to init regmap\n"); +- } +- +- ret = max96717_parse_dt(priv); +- if (ret) +- return dev_err_probe(dev, ret, "Failed to parse the dt\n"); +- +- ret = max96717_hw_init(priv); +- if (ret) +- return dev_err_probe(dev, ret, +- "Failed to initialize the hardware\n"); +- +- ret = max96717_gpiochip_probe(priv); +- if (ret) +- return dev_err_probe(&client->dev, ret, +- "Failed to init gpiochip\n"); +- +- ret = max96717_register_clkout(priv); +- if (ret) +- return dev_err_probe(dev, ret, "Failed to register clkout\n"); +- +- ret = max96717_subdev_init(priv); +- if (ret) +- return dev_err_probe(dev, ret, +- "Failed to initialize v4l2 subdev\n"); +- +- ret = max96717_i2c_mux_init(priv); +- if (ret) { +- dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); +- max96717_subdev_uninit(priv); +- } +- +- return ret; +-} +- +-static void max96717_remove(struct i2c_client *client) +-{ +- struct v4l2_subdev *sd = i2c_get_clientdata(client); +- struct max96717_priv *priv = sd_to_max96717(sd); +- +- max96717_subdev_uninit(priv); +- i2c_mux_del_adapters(priv->mux); +-} +- +-static const struct of_device_id max96717_of_ids[] = { +- { .compatible = "maxim,max96717f" }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, max96717_of_ids); +- +-static struct i2c_driver max96717_i2c_driver = { +- .driver = { +- .name = "max96717", +- .of_match_table = max96717_of_ids, +- }, +- .probe = max96717_probe, +- .remove = max96717_remove, +-}; +- +-module_i2c_driver(max96717_i2c_driver); +- +-MODULE_DESCRIPTION("Maxim GMSL2 MAX96717 Serializer Driver"); +-MODULE_AUTHOR("Julien Massot "); +-MODULE_LICENSE("GPL"); +-- +2.43.0 + diff --git a/patch/v6.18.3_iot/0031-media-mc-Add-INTERNAL-pad-flag.patch b/patch/v6.18.3_iot/0031-media-mc-Add-INTERNAL-pad-flag.patch new file mode 100644 index 0000000..1b6d0c9 --- /dev/null +++ b/patch/v6.18.3_iot/0031-media-mc-Add-INTERNAL-pad-flag.patch @@ -0,0 +1,76 @@ +From 8ca39c83ce030a6bf6826b230b91408d5a74c9d1 Mon Sep 17 00:00:00 2001 +From: Sakari Ailus +Date: Fri, 14 Nov 2025 16:51:41 +0200 +Subject: [PATCH 5/5] media: mc: Add INTERNAL pad flag + +Internal sink pads will be used as routing endpoints in V4L2 [GS]_ROUTING +IOCTLs, to indicate that the stream begins in the entity. Internal sink +pads are pads that have both SINK and INTERNAL flags set. + +Also prevent creating links to pads that have been flagged as internal and +initialising SOURCE pads with INTERNAL flag set. + +Signed-off-by: Sakari Ailus +Reviewed-by: Tomi Valkeinen +Reviewed-by: Laurent Pinchart +Reviewed-by: Mirela Rabulea +--- + drivers/media/mc/mc-entity.c | 15 ++++++++++++--- + include/uapi/linux/media.h | 1 + + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c +--- a/drivers/media/mc/mc-entity.c ++++ b/drivers/media/mc/mc-entity.c +@@ -209,11 +209,16 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, + mutex_lock(&mdev->graph_mutex); + + media_entity_for_each_pad(entity, iter) { ++ const u32 pad_flags = iter->flags & (MEDIA_PAD_FL_SINK | ++ MEDIA_PAD_FL_SOURCE | ++ MEDIA_PAD_FL_INTERNAL); ++ + iter->entity = entity; + iter->index = i++; + +- if (hweight32(iter->flags & (MEDIA_PAD_FL_SINK | +- MEDIA_PAD_FL_SOURCE)) != 1) { ++ if (pad_flags != MEDIA_PAD_FL_SINK && ++ pad_flags != (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL) && ++ pad_flags != MEDIA_PAD_FL_SOURCE) { + ret = -EINVAL; + break; + } +@@ -1118,7 +1123,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type, + + for (i = 0; i < entity->num_pads; i++) { + if ((entity->pads[i].flags & +- (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type) ++ (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE | ++ MEDIA_PAD_FL_INTERNAL)) != pad_type) + continue; + + if (entity->pads[i].sig_type == sig_type) +@@ -1148,6 +1154,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, + return -EINVAL; + if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) + return -EINVAL; ++ if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL) || ++ WARN_ON(sink->pads[sink_pad].flags & MEDIA_PAD_FL_INTERNAL)) ++ return -EINVAL; + + link = media_add_link(&source->links); + if (link == NULL) +diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h +--- a/include/uapi/linux/media.h ++++ b/include/uapi/linux/media.h +@@ -208,6 +208,7 @@ struct media_entity_desc { + #define MEDIA_PAD_FL_SINK (1U << 0) + #define MEDIA_PAD_FL_SOURCE (1U << 1) + #define MEDIA_PAD_FL_MUST_CONNECT (1U << 2) ++#define MEDIA_PAD_FL_INTERNAL (1U << 3) + + struct media_pad_desc { + __u32 entity; /* entity ID */ +-- +2.51.1 diff --git a/patch/v6.18.3_iot/0032-media-ipu7-Parse-CSI-2-endpoint-during-device-regist.patch b/patch/v6.18.3_iot/0032-media-ipu7-Parse-CSI-2-endpoint-during-device-regist.patch new file mode 100644 index 0000000..e604fa6 --- /dev/null +++ b/patch/v6.18.3_iot/0032-media-ipu7-Parse-CSI-2-endpoint-during-device-regist.patch @@ -0,0 +1,51 @@ +From 794136e6f5a6aebd3107407ea597015d59909c42 Mon Sep 17 00:00:00 2001 +From: Pengpeng He +Date: Fri, 18 Sep 2026 11:41:20 +0800 +Subject: [PATCH 1/3] media: ipu7: Parse CSI-2 endpoint during device + registration + +Parse the remote endpoint to obtain the lane count and bus type. +Use the parsed information to configure the CSI-2 receiver. +--- + drivers/staging/media/ipu7/ipu7-isys.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index 7434e4e82baf..fdbb969e5a4c 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -64,6 +64,9 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, + v4l2_set_subdev_hostdata(sd, csi2); + + if (csi2->ep) { ++ struct v4l2_fwnode_endpoint vep_source = { ++ .bus_type = V4L2_MBUS_UNKNOWN ++ }; + struct fwnode_handle *ep_source; + + ep_source = fwnode_graph_get_remote_endpoint(csi2->ep); +@@ -75,6 +78,8 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, + + source_pad = media_entity_get_fwnode_pad(&sd->entity, ep_source, + MEDIA_PAD_FL_SOURCE); ++ ++ ret = v4l2_fwnode_endpoint_parse(ep_source, &vep_source); + fwnode_handle_put(ep_source); + + if (source_pad < 0) { +@@ -90,6 +95,12 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, + dev_dbg(&isys->adev->auxdev.dev, + "%s: source pad %d for subdev %s\n", __func__, + source_pad, sd->name); ++ ++ if (ret) ++ goto skip_unregister_subdev; ++ ++ csi2->nlanes = vep_source.bus.mipi_csi2.num_data_lanes; ++ csi2->bus_type = vep_source.bus_type; + } else { + for (source_pad = 0; source_pad < sd->entity.num_pads; + source_pad++) { +-- +2.43.0 + diff --git a/patch/v6.18.3_iot/0033-ipu7-Fix-lane-handling-in-C-PHY-and-CSI-2-configurat.patch b/patch/v6.18.3_iot/0033-ipu7-Fix-lane-handling-in-C-PHY-and-CSI-2-configurat.patch new file mode 100644 index 0000000..7ecef00 --- /dev/null +++ b/patch/v6.18.3_iot/0033-ipu7-Fix-lane-handling-in-C-PHY-and-CSI-2-configurat.patch @@ -0,0 +1,220 @@ +From 05c16dda4197ea51de1a7b3a61d51c27f5e24f26 Mon Sep 17 00:00:00 2001 +From: Pengpeng He +Date: Tue, 15 Sep 2026 09:49:30 +0800 +Subject: [PATCH 2/3] ipu7: Fix lane handling in C-PHY and CSI-2 configuration + +Track total and active lanes separately when configuring the PHY. +Handle CSI port aggregation with the correct lane counts. +--- + .../staging/media/ipu7/ipu7-isys-csi-phy.c | 61 +++++++++++++------ + drivers/staging/media/ipu7/ipu7-isys-csi2.c | 7 ++- + 2 files changed, 48 insertions(+), 20 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +index a56aeecde191..b6f0863478c5 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +@@ -728,7 +728,6 @@ static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + bool aggregation, u64 mbps) + { +- u8 trios = 2; + u16 coarse_target; + u16 deass_thresh; + u16 delay_thresh; +@@ -746,18 +745,18 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + val = 0x155; + + if (is_ipu7(isys->adev->isp->hw_ver)) +- trios = 3; ++ lanes = 3; + + dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9); + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7); + dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7); + + reg = CORE_DIG_CLANE_0_RW_LP_0; +- for (i = 0; i < trios; i++) ++ for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); + + val = (mbps > 900U) ? 1U : 0U; +- for (i = 0; i < trios; i++) { ++ for (i = 0; i < lanes; i++) { + reg = CORE_DIG_CLANE_0_RW_HS_RX_0; + dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); + dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); +@@ -782,7 +781,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + else + coarse_target = 56; + +- for (i = 0; i < trios; i++) { ++ for (i = 0; i < lanes; i++) { + reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; + dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); + } +@@ -794,7 +793,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); + +- if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) { ++ if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes > 2) { + dwc_phy_write_mask(isys, id, + CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, + 1, 0, 0); +@@ -803,7 +802,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + 0, 0, 0); + } + +- for (i = 0; i < trios; i++) { ++ for (i = 0; i < lanes; i++) { + reg = CORE_DIG_RW_TRIO0_0 + i * 0x400; + dwc_phy_write_mask(isys, id, reg, 1, 6, 8); + dwc_phy_write_mask(isys, id, reg, 1, 3, 5); +@@ -815,7 +814,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + deass_thresh++; + + reg = CORE_DIG_RW_TRIO0_2; +- for (i = 0; i < trios; i++) ++ for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + deass_thresh, 0, 7); + +@@ -825,7 +824,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + delay_thresh = 1; + + reg = CORE_DIG_RW_TRIO0_1; +- for (i = 0; i < trios; i++) ++ for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + delay_thresh, 0, 15); + +@@ -837,21 +836,21 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + reset_thresh = 1; + + reg = CORE_DIG_RW_TRIO0_0; +- for (i = 0; i < trios; i++) ++ for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, + reset_thresh, 9, 11); + + /* Tuning ITMINRX to 2 for CPHY */ + reg = CORE_DIG_CLANE_0_RW_LP_0; +- for (i = 0; i < trios; i++) ++ for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15); + + reg = CORE_DIG_CLANE_0_RW_LP_2; +- for (i = 0; i < trios; i++) ++ for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); + + reg = CORE_DIG_CLANE_0_RW_HS_RX_0; +- for (i = 0; i < trios; i++) ++ for (i = 0; i < lanes; i++) + dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); + + for (i = 0; i < ARRAY_SIZE(table7); i++) { +@@ -873,6 +872,27 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; + dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); + } ++ ++ if (aggregation) { ++ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, 1, 1); ++ ++ /* ++ * C-PHY has no clock lane, so unlike D-PHY no AFE lane is the ++ * shared clock that has to follow port A. ++ */ ++ for (i = 0; i < (lanes + 1); i++) { ++ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 + 0x400 * i; ++ dwc_phy_write_mask(isys, id, reg, 3, 3, 4); ++ } ++ } ++ ++ /* Only port A runs rext calibration; other ports reuse its result. */ ++ if (isys->phy_rext_cal && id) { ++ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, ++ isys->phy_rext_cal, 0, 3); ++ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, ++ 1, 11, 11); ++ } + } + + static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, +@@ -962,21 +982,23 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, + int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) + { + struct ipu7_isys *isys = csi2->isys; +- u32 lanes = csi2->nlanes; ++ u32 total_lanes = csi2->nlanes; ++ u32 active_lanes = total_lanes; + bool aggregation = false; + u32 id = csi2->port; + int ret; + + /* lanes remapping for aggregation (port AB) mode */ +- if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) { ++ if (!is_ipu7(isys->adev->isp->hw_ver) && active_lanes > 2 && ++ id == PORT_A) { + aggregation = true; +- lanes = 2; ++ active_lanes = 2; + } + + ipu7_isys_csi_phy_reset(isys, id); + gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1); + gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2); +- gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); ++ gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << active_lanes) - 1U); + gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf); + gpreg_write(isys, id, PHY_MODE, csi2->phy_mode); + +@@ -993,7 +1015,7 @@ int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) + ipu7_isys_csi_ctrl_cfg(csi2); + ipu7_isys_csi_ctrl_dids_config(csi2, id); + +- ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); ++ ret = ipu7_isys_phy_config(isys, id, active_lanes, aggregation); + if (ret < 0) + return ret; + +@@ -1012,7 +1034,8 @@ int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) + + /* config PORT_B if aggregation mode */ + if (aggregation) { +- ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); ++ ret = ipu7_isys_phy_config(isys, PORT_B, ++ total_lanes - active_lanes, aggregation); + if (ret < 0) + return ret; + +diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.c b/drivers/staging/media/ipu7/ipu7-isys-csi2.c +index 4023db4a6466..c8730353989d 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-csi2.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-csi2.c +@@ -148,6 +148,11 @@ static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) + + ipu7_isys_csi_phy_powerdown(csi2); + ++ if (csi2->port == 0U && csi2->nlanes > 2U && ++ !is_ipu7(isys->adev->isp->hw_ver)) ++ writel(0x0, isys_base + IS_IO_GPREGS_BASE + ++ CSI_PORTAB_AGGREGATION); ++ + writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK); + csi2_irq_disable(csi2); + } +@@ -168,7 +173,7 @@ static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) + dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n", + port, readl(isys_base + offset + CSI_PORT_CLK_GATE), + readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK)); +- if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) { ++ if (port == 0U && nlanes > 2U && !is_ipu7(isys->adev->isp->hw_ver)) { + dev_dbg(dev, "CSI port %u in aggregation mode\n", port); + writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION); + } +-- +2.43.0 +