From df954ef07a36781a107776a32964635bf2e4fecd Mon Sep 17 00:00:00 2001 From: Shunyong Yang Date: Tue, 9 Jun 2026 10:28:15 +0800 Subject: [PATCH] IPU7/IPU8 release for iot for kernel version 7.0.0 on 2026-06-09 Signed-off-by: Shunyong Yang --- drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 6 +- ...t-back-firmware-message-buffers-when.patch | 284 ++++++ ...laim-pending-fw-msg-buffers-by-strea.patch | 126 +++ ...nc-at-buffer_prepare-callback-as-DMA.patch | 43 + ...ng-add-ipu7-isys-reset-code-for-v7.0.patch | 823 ++++++++++++++++++ ...h-staging-add-enable-CONFIG_DEBUG_FS.patch | 270 ++++++ ...ing-add-enable-CONFIG_INTEL_IPU_ACPI.patch | 405 +++++++++ ...-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch | 112 +++ ...d-patch-for-use-DPHY-as-the-default-.patch | 33 + ...-add-pacth-for-ipu7-Kconfig-Makefile.patch | 79 ++ ...ing-add-ipu7-isys-tpg-and-MGC-config.patch | 814 +++++++++++++++++ .../0011-INT3472-Support-LT6911GXD.patch | 26 + ...-media-i2c-add-support-for-lt6911gxd.patch | 45 + ...a-pci-enable-lt6911gxd-in-ipu-bridge.patch | 26 + .../0014-ipu-bridge-add-CPHY-support.patch | 113 +++ ...max9x-add-config-in-makefile-kconfig.patch | 55 ++ ...t-v4l2_subdev_enable_streams_api-tru.patch | 27 + ...ate-IPU7-firmware-ABI-version-to-1.2.patch | 86 ++ ...atch-staging-add-IPU8_PCI_ID-support.patch | 26 + ...ing-ipu7-Add-IPU8-ABI-version-1.0.12.patch | 480 ++++++++++ ...ine-gpreg_stride-for-different-IPU-v.patch | 80 ++ ...u7-Fix-potential-NULL-pointer-derefe.patch | 61 ++ ...u7-set-skipframe-flag-when-frame-err.patch | 42 + ...ing-ipu7-Add-more-insys-frame-format.patch | 33 + 24 files changed, 4093 insertions(+), 2 deletions(-) create mode 100644 patch/v7.0.0_iot/0001-staging-ipu7-Wait-back-firmware-message-buffers-when.patch create mode 100644 patch/v7.0.0_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch create mode 100644 patch/v7.0.0_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch create mode 100644 patch/v7.0.0_iot/0004-patch-staging-add-ipu7-isys-reset-code-for-v7.0.patch create mode 100644 patch/v7.0.0_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch create mode 100644 patch/v7.0.0_iot/0006-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.patch create mode 100644 patch/v7.0.0_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch create mode 100644 patch/v7.0.0_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch create mode 100644 patch/v7.0.0_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch create mode 100644 patch/v7.0.0_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch create mode 100644 patch/v7.0.0_iot/0011-INT3472-Support-LT6911GXD.patch create mode 100644 patch/v7.0.0_iot/0012-media-i2c-add-support-for-lt6911gxd.patch create mode 100644 patch/v7.0.0_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch create mode 100644 patch/v7.0.0_iot/0014-ipu-bridge-add-CPHY-support.patch create mode 100644 patch/v7.0.0_iot/0015-max9x-add-config-in-makefile-kconfig.patch create mode 100644 patch/v7.0.0_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch create mode 100644 patch/v7.0.0_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch create mode 100644 patch/v7.0.0_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch create mode 100644 patch/v7.0.0_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.12.patch create mode 100644 patch/v7.0.0_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch create mode 100644 patch/v7.0.0_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch create mode 100644 patch/v7.0.0_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch create mode 100644 patch/v7.0.0_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c index 838f0b2..d89e8b6 100644 --- a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c +++ b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c @@ -84,8 +84,10 @@ static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; nr = pin_user_pages_fast(start & PAGE_MASK, npages, flags, pages); - if (nr < npages) + if (nr < npages) { + ret = nr < 0 ? nr : -EFAULT; goto error; + } attach->pages = pages; attach->npages = npages; @@ -103,7 +105,7 @@ static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) error_up_read: mmap_read_unlock(current->mm); error: - if (nr) + if (nr > 0) unpin_user_pages(pages, nr); kvfree(pages); diff --git a/patch/v7.0.0_iot/0001-staging-ipu7-Wait-back-firmware-message-buffers-when.patch b/patch/v7.0.0_iot/0001-staging-ipu7-Wait-back-firmware-message-buffers-when.patch new file mode 100644 index 0000000..3d65e39 --- /dev/null +++ b/patch/v7.0.0_iot/0001-staging-ipu7-Wait-back-firmware-message-buffers-when.patch @@ -0,0 +1,284 @@ +From 582759ec17edaa8fd0cf8d32616033a6fd39c3de Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Fri, 5 Jun 2026 10:50:21 +0800 +Subject: [PATCH 01/23] staging: ipu7: Wait back firmware message buffers when + tlb_invalidate + +IPU MMUs run in parallel, and runtime TLB invalidation may overlap with +ongoing data movement. For fw msg buf expansion, invalidating MMU0 is +sufficient. + +The goal of acquire_fw_msgbuf_lock is to block subsequent fw msg buf +allocations while waiting for framebuflist_fw to drain. It is not meant +to serialize all TLB invalidation callers. + +If TLB invalidation is triggered outside the fw msg buf allocation path, +the first caller takes acquire_fw_msgbuf_lock (when unlocked), establishes +the protection window. Later callers may proceed without taking the lock +again, which is acceptable for this design goal. + +Test results show fw msg buf count may stay unchanged for at most one poll. +To reduce latency, use half-frame wait time for each polling interval. + +Signed-off-by: hepengpx +--- + drivers/staging/media/ipu7/ipu7-dma.c | 7 ++- + drivers/staging/media/ipu7/ipu7-isys.c | 10 +++- + drivers/staging/media/ipu7/ipu7-isys.h | 2 + + drivers/staging/media/ipu7/ipu7-mmu.c | 75 +++++++++++++++++++++++++- + drivers/staging/media/ipu7/ipu7-mmu.h | 8 ++- + 5 files changed, 96 insertions(+), 6 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-dma.c b/drivers/staging/media/ipu7/ipu7-dma.c +index 9a356ae3b95a..39b9d12f2c68 100644 +--- a/drivers/staging/media/ipu7/ipu7-dma.c ++++ b/drivers/staging/media/ipu7/ipu7-dma.c +@@ -206,6 +206,9 @@ 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 +289,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 +369,7 @@ 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); ++ mmu->tlb_invalidate(mmu, -1); + __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 cb2f49f3e0fa..6c89eab60023 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -594,6 +594,7 @@ static void isys_remove(struct auxiliary_device *auxdev) + + mutex_destroy(&isys->stream_mutex); + mutex_destroy(&isys->mutex); ++ mutex_destroy(&isys->acquire_fw_msgbuf_lock); + } + + static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) +@@ -642,18 +643,22 @@ struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) + unsigned long flags; + int ret; + ++ mutex_lock(&isys->acquire_fw_msgbuf_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) ++ if (ret < 0) { ++ mutex_unlock(&isys->acquire_fw_msgbuf_lock); + return NULL; ++ } + + spin_lock_irqsave(&isys->listlock, flags); + if (list_empty(&isys->framebuflist)) { + spin_unlock_irqrestore(&isys->listlock, flags); ++ mutex_unlock(&isys->acquire_fw_msgbuf_lock); + dev_err(dev, "Frame list empty\n"); + return NULL; + } +@@ -661,6 +666,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->acquire_fw_msgbuf_lock); + memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); + + return msg; +@@ -738,6 +744,7 @@ static int isys_probe(struct auxiliary_device *auxdev, + + mutex_init(&isys->mutex); + mutex_init(&isys->stream_mutex); ++ mutex_init(&isys->acquire_fw_msgbuf_lock); + + spin_lock_init(&isys->listlock); + INIT_LIST_HEAD(&isys->framebuflist); +@@ -777,6 +784,7 @@ static int isys_probe(struct auxiliary_device *auxdev, + out_cleanup_fw: + ipu7_fw_isys_release(isys); + out_cleanup_isys: ++ mutex_destroy(&isys->acquire_fw_msgbuf_lock); + 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 ef1ab1b42f6c..29abb9a2d375 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -109,6 +109,8 @@ struct ipu7_isys { + + struct ipu7_insys_config *subsys_config; + dma_addr_t subsys_config_dma_addr; ++ /* Protect framebuflist when getting fw msg buf. */ ++ struct mutex acquire_fw_msgbuf_lock; + }; + + struct isys_fw_msgs { +diff --git a/drivers/staging/media/ipu7/ipu7-mmu.c b/drivers/staging/media/ipu7/ipu7-mmu.c +index 6ded07ccd780..58bd0db39103 100644 +--- a/drivers/staging/media/ipu7/ipu7-mmu.c ++++ b/drivers/staging/media/ipu7/ipu7-mmu.c +@@ -30,6 +30,7 @@ + #include "ipu7-dma.h" + #include "ipu7-mmu.h" + #include "ipu7-platform-regs.h" ++#include "ipu7-isys.h" + + #define ISP_PAGE_SHIFT 12 + #define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) +@@ -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,20 +69,86 @@ 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; ++ bool locked_get_fw = false; ++ struct ipu7_isys *isys = NULL; ++ unsigned int prev_fw_cnt = UINT_MAX; ++ unsigned int curr_fw_cnt = UINT_MAX; ++ unsigned int not_decreasing_count = 0; ++ ++ if (mmu->mmid == ISYS_MMID) { ++ isys = dev_get_drvdata(mmu->dev); ++ if (!isys) { ++ dev_warn(mmu->dev, "isys drvdata is NULL, skip tlb invalidate wait\n"); ++ return; ++ } ++ ++ if (!mutex_is_locked(&isys->acquire_fw_msgbuf_lock)) { ++ mutex_lock(&isys->acquire_fw_msgbuf_lock); ++ locked_get_fw = true; ++ } ++ ++ while (1) { ++ spin_lock_irqsave(&isys->listlock, flags); ++ curr_fw_cnt = list_count_nodes(&isys->framebuflist_fw); ++ spin_unlock_irqrestore(&isys->listlock, flags); ++ if (curr_fw_cnt == 0) ++ break; ++ ++ if (curr_fw_cnt >= prev_fw_cnt) ++ not_decreasing_count++; ++ else ++ not_decreasing_count = 0; ++ prev_fw_cnt = curr_fw_cnt; ++ ++ /* ++ * Timeout after consecutive non-decreasing counts. ++ * Continue invalidate to avoid indefinite stall. ++ */ ++ if (not_decreasing_count > ++ WAIT_FW_MSG_BUFS_CLEAR_TIMES) { ++ dev_warn(&isys->adev->auxdev.dev, ++ "wait framebuflist_fw empty timeout"); ++ break; ++ } ++ ++ msleep(WAIT_FW_MSG_BUFS_CLEAR_TIME_MS); ++ } ++ } + + spin_lock_irqsave(&mmu->ready_lock, flags); + if (!mmu->ready) { + spin_unlock_irqrestore(&mmu->ready_lock, flags); ++ if (locked_get_fw) ++ mutex_unlock(&isys->acquire_fw_msgbuf_lock); + 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); ++ if (locked_get_fw) ++ mutex_unlock(&isys->acquire_fw_msgbuf_lock); ++ 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); + +@@ -106,6 +175,8 @@ static void tlb_invalidate(struct ipu7_mmu *mmu) + } + + spin_unlock_irqrestore(&mmu->ready_lock, flags); ++ if (locked_get_fw) ++ mutex_unlock(&isys->acquire_fw_msgbuf_lock); + } + + static dma_addr_t map_single(struct ipu7_mmu_info *mmu_info, void *ptr) +diff --git a/drivers/staging/media/ipu7/ipu7-mmu.h b/drivers/staging/media/ipu7/ipu7-mmu.h +index d85bb8ffc711..e4bc485496f4 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, +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch b/patch/v7.0.0_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch new file mode 100644 index 0000000..a7a92a8 --- /dev/null +++ b/patch/v7.0.0_iot/0002-staging-ipu7-reclaim-pending-fw-msg-buffers-by-strea.patch @@ -0,0 +1,126 @@ +From c1d0d37c7284e5b10d2b116ac5eb71a80e8b0556 Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Fri, 5 Jun 2026 10:50:07 +0800 +Subject: [PATCH 02/23] 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 434d9d9c7158..02466f883675 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 8c6730833f24..9953d4aa3a38 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-video.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-video.c +@@ -452,6 +452,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; +@@ -508,6 +509,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); +@@ -782,6 +784,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); +@@ -806,6 +809,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 6c89eab60023..e2e6e6f6b092 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -683,6 +683,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 29abb9a2d375..c6a3d4f22a25 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -121,6 +121,7 @@ struct isys_fw_msgs { + } fw_msg; + struct list_head head; + dma_addr_t dma_addr; ++ u16 stream_id; + }; + + struct ipu7_isys_csi2_config { +@@ -137,6 +138,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 */ +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch b/patch/v7.0.0_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch new file mode 100644 index 0000000..af2b1cc --- /dev/null +++ b/patch/v7.0.0_iot/0003-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA.patch @@ -0,0 +1,43 @@ +From d80b23251ebdcf578a701447bf4a0296468dc842 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 25 Oct 2025 16:42:45 +0800 +Subject: [PATCH 03/23] 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 02466f883675..537542ff5ad4 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; + } + +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0004-patch-staging-add-ipu7-isys-reset-code-for-v7.0.patch b/patch/v7.0.0_iot/0004-patch-staging-add-ipu7-isys-reset-code-for-v7.0.patch new file mode 100644 index 0000000..ed93f66 --- /dev/null +++ b/patch/v7.0.0_iot/0004-patch-staging-add-ipu7-isys-reset-code-for-v7.0.patch @@ -0,0 +1,823 @@ +From 63d5147bceab8c35d14f867f234f5e5dcd6f2dbe Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Tue, 21 Apr 2026 14:30:26 +0800 +Subject: [PATCH 04/23] patch: staging add ipu7 isys reset code for v7.0 + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/Kconfig | 10 + + drivers/staging/media/ipu7/ipu7-isys-queue.c | 354 ++++++++++++++++++- + drivers/staging/media/ipu7/ipu7-isys-queue.h | 3 + + drivers/staging/media/ipu7/ipu7-isys-video.c | 98 +++++ + drivers/staging/media/ipu7/ipu7-isys-video.h | 8 + + drivers/staging/media/ipu7/ipu7-isys.c | 55 +++ + drivers/staging/media/ipu7/ipu7-isys.h | 16 + + 7 files changed, 543 insertions(+), 1 deletion(-) + +diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig +index 7d831ba7501d..c4eee7c3e6d7 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 537542ff5ad4..c7cd9b3e66f0 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,282 @@ 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 ipu7_isys_stream *stream = av->stream; ++ struct ipu7_isys_buffer *ib; ++ struct vb2_buffer *vb; ++ unsigned long flags; ++ ++ 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); ++ ++ dev_dbg(dev, "reset stop streams\n"); ++ 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); ++ ++ dev_dbg(dev, "reset start streams\n"); ++ ++ 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); +@@ -643,7 +939,58 @@ static void stop_streaming(struct vb2_queue *q) + 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 (!stream->nr_streaming) { ++ 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); ++ 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); ++} ++#endif + + static unsigned int + get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) +@@ -725,6 +1072,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 0cb08a38f756..5a909c3a78d2 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 9953d4aa3a38..6e6c627b957e 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 +@@ -88,6 +91,42 @@ const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { + IPU_INSYS_FRAME_FORMAT_RGBA888}, + }; + ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++static int video_open(struct file *file) ++ { ++ struct ipu7_isys_video *av = video_drvdata(file); ++ struct ipu7_isys *isys = av->isys; ++ struct ipu7_bus_device *adev = isys->adev; ++ ++ mutex_lock(&isys->reset_mutex); ++ if (isys->need_reset) { ++ mutex_unlock(&isys->reset_mutex); ++ dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); ++ return -EIO; ++ } ++ mutex_unlock(&isys->reset_mutex); ++ return v4l2_fh_open(file); ++ } ++ ++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; +@@ -584,7 +623,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) +@@ -609,7 +652,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) +@@ -617,6 +664,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); + } + +@@ -631,7 +684,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; +@@ -866,8 +930,13 @@ static const struct v4l2_file_operations isys_fops = { + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ .open = video_open, ++ .release = video_release, ++#else + .open = v4l2_fh_open, + .release = vb2_fop_release, ++#endif + }; + + int ipu7_isys_fw_open(struct ipu7_isys *isys) +@@ -906,6 +975,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); +@@ -918,6 +1009,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) +@@ -1053,6 +1145,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 1ac1787fabef..e6d1da2b7b47 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 e2e6e6f6b092..b59cf28a9f17 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -371,6 +371,42 @@ 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; ++ 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) && ++ isys->stream_opened > 1) { ++ dev_dbg(dev, "%s: isys need reset due to notify %u, stream opened %d\n", ++ sd->name, ev->type, isys->stream_opened); ++ mutex_lock(&isys->reset_mutex); ++ isys->need_reset = true; ++ mutex_unlock(&isys->reset_mutex); ++ } ++ } 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 +426,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 +579,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); +@@ -594,6 +639,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 + mutex_destroy(&isys->acquire_fw_msgbuf_lock); + } + +@@ -759,6 +807,10 @@ static int isys_probe(struct auxiliary_device *auxdev, + mutex_init(&isys->mutex); + mutex_init(&isys->stream_mutex); + mutex_init(&isys->acquire_fw_msgbuf_lock); ++#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); +@@ -798,6 +850,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 + mutex_destroy(&isys->acquire_fw_msgbuf_lock); + cpu_latency_qos_remove_request(&isys->pm_qos); + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h +index c6a3d4f22a25..18f54c17d4b7 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 +@@ -111,6 +122,11 @@ struct ipu7_isys { + dma_addr_t subsys_config_dma_addr; + /* Protect framebuflist when getting fw msg buf. */ + struct mutex acquire_fw_msgbuf_lock; ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ struct mutex reset_mutex; ++ bool need_reset; ++ int state; ++#endif + }; + + struct isys_fw_msgs { +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch b/patch/v7.0.0_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch new file mode 100644 index 0000000..8c62c77 --- /dev/null +++ b/patch/v7.0.0_iot/0005-patch-staging-add-enable-CONFIG_DEBUG_FS.patch @@ -0,0 +1,270 @@ +From a32ce6c2c845555cac99a923f4fac515f8d88805 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Mon, 20 Oct 2025 12:36:12 +0800 +Subject: [PATCH 05/23] 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 b59cf28a9f17..1da071fa5241 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 +@@ -621,6 +624,10 @@ static void isys_remove(struct auxiliary_device *auxdev) + struct isys_fw_msgs *fwmsg, *safe; + struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); + ++#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); + +@@ -645,6 +652,78 @@ static void isys_remove(struct auxiliary_device *auxdev) + mutex_destroy(&isys->acquire_fw_msgbuf_lock); + } + ++#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; +@@ -823,6 +902,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 18f54c17d4b7..a2a7d75696c5 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 c771e763f8c5..9c6bfac3edbc 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 ac8ac0689468..3b3ea5fb613f 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; +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0006-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.patch b/patch/v7.0.0_iot/0006-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.patch new file mode 100644 index 0000000..90be4f1 --- /dev/null +++ b/patch/v7.0.0_iot/0006-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.patch @@ -0,0 +1,405 @@ +From 3dbefe49f48fddc9d8945d87a5f89d87170be810 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 30 Jan 2026 12:16:30 +0800 +Subject: [PATCH 06/23] patch: staging add enable CONFIG_INTEL_IPU_ACPI + +Signed-off-by: linya14x +--- + drivers/staging/media/ipu7/ipu7-isys.c | 232 ++++++++++++++++++++++++- + drivers/staging/media/ipu7/ipu7-isys.h | 15 ++ + drivers/staging/media/ipu7/ipu7.c | 10 ++ + drivers/staging/media/ipu7/ipu7.h | 3 + + 4 files changed, 258 insertions(+), 2 deletions(-) + +diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c +index 1da071fa5241..71cd9a779e83 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -96,6 +96,126 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, + return ret; + } + ++struct isys_i2c_test { ++ u8 bus_nr; ++ u16 addr; ++ struct i2c_client *client; ++}; ++ ++static int isys_i2c_test(struct device *dev, void *priv) ++{ ++ struct i2c_client *client = i2c_verify_client(dev); ++ struct isys_i2c_test *test = priv; ++ ++ if (!client) ++ return 0; ++ ++ if (i2c_adapter_id(client->adapter) != test->bus_nr || ++ client->addr != test->addr) ++ return 0; ++ ++ test->client = client; ++ ++ return 0; ++} ++ ++static ++struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, ++ struct ipu7_isys_subdev_info *sd_info) ++{ ++ struct i2c_board_info *info = &sd_info->i2c.board_info; ++ struct isys_i2c_test test = { ++ .bus_nr = i2c_adapter_id(adapter), ++ .addr = info->addr, ++ }; ++ int ret; ++ ++ ret = i2c_for_each_dev(&test, isys_i2c_test); ++ if (ret || !test.client) ++ return NULL; ++ return test.client; ++} ++ ++static int isys_register_ext_subdev(struct ipu7_isys *isys, ++ struct ipu7_isys_subdev_info *sd_info) ++{ ++ struct device *dev = &isys->adev->auxdev.dev; ++ struct i2c_adapter *adapter; ++ struct v4l2_subdev *sd; ++ struct i2c_client *client; ++ int ret; ++ int bus; ++ ++ bus = sd_info->i2c.i2c_adapter_id; ++ adapter = i2c_get_adapter(bus); ++ if (!adapter) { ++ dev_warn(dev, "can't find adapter\n"); ++ return -ENOENT; ++ } ++ ++ dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", ++ sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, ++ bus); ++ ++ if (sd_info->csi2) { ++ dev_info(dev, "sensor device on CSI port: %d\n", ++ sd_info->csi2->port); ++ if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || ++ !isys->csi2[sd_info->csi2->port].isys) { ++ dev_warn(dev, "invalid csi2 port %u\n", ++ sd_info->csi2->port); ++ ret = -EINVAL; ++ goto skip_put_adapter; ++ } ++ } else { ++ dev_info(dev, "No camera subdevice\n"); ++ } ++ ++ client = isys_find_i2c_subdev(adapter, sd_info); ++ if (client) { ++ dev_warn(dev, "Device exists\n"); ++#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) ++ /* TODO: remove i2c_unregister_device() */ ++ i2c_unregister_device(client); ++#else ++ ret = 0; ++ goto skip_put_adapter; ++#endif ++ } ++ ++ sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, ++ &sd_info->i2c.board_info, NULL); ++ if (!sd) { ++ dev_warn(dev, "can't create new i2c subdev\n"); ++ ret = -EINVAL; ++ goto skip_put_adapter; ++ } ++ ++ if (!sd_info->csi2) ++ return 0; ++ ++ return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); ++ ++skip_put_adapter: ++ i2c_put_adapter(adapter); ++ ++ return ret; ++} ++ ++static void isys_register_ext_subdevs(struct ipu7_isys *isys) ++{ ++ struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; ++ struct ipu7_isys_subdev_info **sd_info; ++ ++ if (!spdata) { ++ dev_info(&isys->adev->auxdev.dev, ++ "no subdevice info provided\n"); ++ return; ++ } ++ for (sd_info = spdata->subdevs; *sd_info; sd_info++) ++ isys_register_ext_subdev(isys, *sd_info); ++} ++ + static void isys_stream_init(struct ipu7_isys *isys) + { + unsigned int i; +@@ -142,6 +262,7 @@ static int isys_fw_log_init(struct ipu7_isys *isys) + return 0; + } + ++#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) + /* The .bound() notifier callback when a match is found */ + static int isys_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, +@@ -260,6 +381,7 @@ static void isys_notifier_cleanup(struct ipu7_isys *isys) + v4l2_async_nf_unregister(&isys->notifier); + v4l2_async_nf_cleanup(&isys->notifier); + } ++#endif + + static void isys_unregister_video_devices(struct ipu7_isys *isys) + { +@@ -410,6 +532,7 @@ static void isys_v4l2_notify(struct v4l2_subdev *sd, unsigned int notification, + } + #endif + ++#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) + static int isys_register_devices(struct ipu7_isys *isys) + { + struct device *dev = &isys->adev->auxdev.dev; +@@ -448,7 +571,74 @@ static int isys_register_devices(struct ipu7_isys *isys) + if (ret) + goto out_csi2_unregister_subdevices; + +- ret = isys_notifier_init(isys); ++ if (!isys->pdata->spdata) { ++ ret = isys_notifier_init(isys); ++ if (ret) ++ goto out_csi2_unregister_subdevices; ++ } else { ++ isys_register_ext_subdevs(isys); ++ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); ++ if (ret) ++ goto out_csi2_unregister_subdevices; ++ } ++ ++ return 0; ++ ++out_csi2_unregister_subdevices: ++ isys_csi2_unregister_subdevices(isys); ++ ++out_video_unregister_device: ++ isys_unregister_video_devices(isys); ++ ++out_v4l2_device_unregister: ++ v4l2_device_unregister(&isys->v4l2_dev); ++ ++out_media_device_unregister: ++ media_device_unregister(&isys->media_dev); ++ media_device_cleanup(&isys->media_dev); ++ ++ dev_err(dev, "failed to register isys devices\n"); ++ ++ return ret; ++} ++#else ++static int isys_register_devices(struct ipu7_isys *isys) ++{ ++ struct device *dev = &isys->adev->auxdev.dev; ++ struct pci_dev *pdev = isys->adev->isp->pdev; ++ int ret; ++ ++ media_device_pci_init(&isys->media_dev, ++ pdev, IPU_MEDIA_DEV_MODEL_NAME); ++ ++ strscpy(isys->v4l2_dev.name, isys->media_dev.model, ++ sizeof(isys->v4l2_dev.name)); ++ ++ ret = media_device_register(&isys->media_dev); ++ if (ret < 0) ++ goto out_media_device_unregister; ++ ++ isys->v4l2_dev.mdev = &isys->media_dev; ++ isys->v4l2_dev.ctrl_handler = NULL; ++ ++ ret = v4l2_device_register(dev, &isys->v4l2_dev); ++ if (ret < 0) ++ goto out_media_device_unregister; ++ ++ ret = isys_register_video_devices(isys); ++ if (ret) ++ goto out_v4l2_device_unregister; ++ ++ ret = isys_csi2_register_subdevices(isys); ++ if (ret) ++ goto out_video_unregister_device; ++ ++ ret = isys_csi2_create_media_links(isys); ++ if (ret) ++ goto out_csi2_unregister_subdevices; ++ ++ isys_register_ext_subdevs(isys); ++ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); + if (ret) + goto out_csi2_unregister_subdevices; + +@@ -471,6 +661,7 @@ static int isys_register_devices(struct ipu7_isys *isys) + + return ret; + } ++#endif + + static void isys_unregister_devices(struct ipu7_isys *isys) + { +@@ -618,6 +809,7 @@ static const struct dev_pm_ops isys_pm_ops = { + .resume = isys_resume, + }; + ++#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) + static void isys_remove(struct auxiliary_device *auxdev) + { + struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); +@@ -639,7 +831,9 @@ static void isys_remove(struct auxiliary_device *auxdev) + ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), + fwmsg, fwmsg->dma_addr, 0); + +- isys_notifier_cleanup(isys); ++ if (!isys->pdata->spdata) ++ isys_notifier_cleanup(isys); ++ + isys_unregister_devices(isys); + + cpu_latency_qos_remove_request(&isys->pm_qos); +@@ -651,6 +845,40 @@ static void isys_remove(struct auxiliary_device *auxdev) + #endif + mutex_destroy(&isys->acquire_fw_msgbuf_lock); + } ++#else ++static void isys_remove(struct auxiliary_device *auxdev) ++{ ++ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); ++ struct isys_fw_msgs *fwmsg, *safe; ++ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); ++ ++#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); ++ ++ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) ++ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), ++ fwmsg, fwmsg->dma_addr, 0); ++ ++ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) ++ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), ++ fwmsg, fwmsg->dma_addr, 0); ++ ++ isys_unregister_devices(isys); ++ ++ cpu_latency_qos_remove_request(&isys->pm_qos); ++ ++ mutex_destroy(&isys->stream_mutex); ++ mutex_destroy(&isys->mutex); ++#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET ++ mutex_destroy(&isys->reset_mutex); ++#endif ++ mutex_destroy(&isys->acquire_fw_msgbuf_lock); ++} ++#endif + + #ifdef CONFIG_DEBUG_FS + static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, +diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h +index a2a7d75696c5..7351fa82888f 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.h ++++ b/drivers/staging/media/ipu7/ipu7-isys.h +@@ -153,6 +153,21 @@ struct ipu7_isys_csi2_config { + enum v4l2_mbus_type bus_type; + }; + ++struct ipu7_isys_subdev_i2c_info { ++ struct i2c_board_info board_info; ++ int i2c_adapter_id; ++ char i2c_adapter_bdf[32]; ++}; ++ ++struct ipu7_isys_subdev_info { ++ struct ipu7_isys_csi2_config *csi2; ++ struct ipu7_isys_subdev_i2c_info i2c; ++}; ++ ++struct ipu7_isys_subdev_pdata { ++ struct ipu7_isys_subdev_info **subdevs; ++}; ++ + struct sensor_async_sd { + struct v4l2_async_connection asc; + struct ipu7_isys_csi2_config csi2; +diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c +index 9c6bfac3edbc..e0b00b3a8e84 100644 +--- a/drivers/staging/media/ipu7/ipu7.c ++++ b/drivers/staging/media/ipu7/ipu7.c +@@ -39,6 +39,10 @@ + #include "ipu7-mmu.h" + #include "ipu7-platform-regs.h" + ++#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) ++#include ++ ++#endif + #define IPU_PCI_BAR 0 + #define IPU_PCI_PBBAR 4 + +@@ -2129,6 +2133,7 @@ static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) + static struct ipu7_bus_device * + ipu7_isys_init(struct pci_dev *pdev, struct device *parent, + const struct ipu_buttress_ctrl *ctrl, void __iomem *base, ++ struct ipu7_isys_subdev_pdata *spdata, + const struct ipu_isys_internal_pdata *ipdata, + unsigned int nr) + { +@@ -2159,6 +2164,7 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, + + pdata->base = base; + pdata->ipdata = ipdata; ++ pdata->spdata = spdata; + + isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, + IPU_ISYS_NAME); +@@ -2581,7 +2587,11 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) + goto out_ipu_bus_del_devices; + } + ++#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) ++ ipu_get_acpi_devices_new(&dev->platform_data); ++#endif + isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, ++ dev->platform_data, + isys_ipdata, 0); + if (IS_ERR(isp->isys)) { + ret = PTR_ERR(isp->isys); +diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h +index 3b3ea5fb613f..21988ce41acf 100644 +--- a/drivers/staging/media/ipu7/ipu7.h ++++ b/drivers/staging/media/ipu7/ipu7.h +@@ -144,6 +144,8 @@ struct ipu7_device { + /* Currently chosen arbitration mechanism for VC1 */ + #define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB + ++struct ipu7_isys_subdev_pdata; ++ + /* One L2 entry maps 1024 L1 entries and one L1 entry per page */ + #define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) + /* Max L2 blocks per stream */ +@@ -226,6 +228,7 @@ struct ipu_isys_internal_pdata { + struct ipu7_isys_pdata { + void __iomem *base; + const struct ipu_isys_internal_pdata *ipdata; ++ struct ipu7_isys_subdev_pdata *spdata; + }; + + struct ipu_psys_internal_pdata { +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch b/patch/v7.0.0_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch new file mode 100644 index 0000000..dfdb526 --- /dev/null +++ b/patch/v7.0.0_iot/0007-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.patch @@ -0,0 +1,112 @@ +From 12efa4a4b95d42b156f9d44bba7060920d91546d Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 12:38:44 +0800 +Subject: [PATCH 07/23] 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 e4b9c364572b..1ece286c2934 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 b556feda6b08..1235adc9694e 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 71cd9a779e83..459747c82955 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys.c ++++ b/drivers/staging/media/ipu7/ipu7-isys.c +@@ -1286,6 +1286,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; +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch b/patch/v7.0.0_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch new file mode 100644 index 0000000..cc3e4d6 --- /dev/null +++ b/patch/v7.0.0_iot/0008-patch-staging-add-patch-for-use-DPHY-as-the-default-.patch @@ -0,0 +1,33 @@ +From 05d9de1a9498f4711708cac85da0bbb4e24c7288 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 12:44:25 +0800 +Subject: [PATCH 08/23] 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 459747c82955..7c9b024adb78 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; + +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch b/patch/v7.0.0_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch new file mode 100644 index 0000000..fe30595 --- /dev/null +++ b/patch/v7.0.0_iot/0009-patch-staging-add-pacth-for-ipu7-Kconfig-Makefile.patch @@ -0,0 +1,79 @@ +From 9da10ad7b3645bcaf405061b6697818e944374c7 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 15:39:28 +0800 +Subject: [PATCH 09/23] 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 + +Change-Id: I5975ee086d25cdf62ed1ee66f892ec9805695a25 +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 c4eee7c3e6d7..0a6e68c7632d 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 6d2aec219e65..3c35ca566473 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)/ +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch b/patch/v7.0.0_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch new file mode 100644 index 0000000..b11f6f5 --- /dev/null +++ b/patch/v7.0.0_iot/0010-patch-staging-add-ipu7-isys-tpg-and-MGC-config.patch @@ -0,0 +1,814 @@ +From b10b738bf4f02f6759c6405d9e356472dcb397de Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 16:47:40 +0800 +Subject: [PATCH 10/23] 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 0a6e68c7632d..91954fdadc8b 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 000000000000..35b6298e4f8c +--- /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 000000000000..e2542a6472e3 +--- /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 */ +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0011-INT3472-Support-LT6911GXD.patch b/patch/v7.0.0_iot/0011-INT3472-Support-LT6911GXD.patch new file mode 100644 index 0000000..1c7ec93 --- /dev/null +++ b/patch/v7.0.0_iot/0011-INT3472-Support-LT6911GXD.patch @@ -0,0 +1,26 @@ +From e0f87e2f32d7a1a0c4a3513cffe57b5464a7e212 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Mon, 8 Sep 2025 15:41:24 +0800 +Subject: [PATCH 11/23] 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 1c65ce87cde0..980a7ff33966 100644 +--- a/drivers/platform/x86/intel/int3472/discrete.c ++++ b/drivers/platform/x86/intel/int3472/discrete.c +@@ -461,7 +461,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; +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0012-media-i2c-add-support-for-lt6911gxd.patch b/patch/v7.0.0_iot/0012-media-i2c-add-support-for-lt6911gxd.patch new file mode 100644 index 0000000..e9fad99 --- /dev/null +++ b/patch/v7.0.0_iot/0012-media-i2c-add-support-for-lt6911gxd.patch @@ -0,0 +1,45 @@ +From 34440824265feea08803e5fa60f5be4f3a324f98 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 25 Oct 2025 16:30:08 +0800 +Subject: [PATCH 12/23] 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 5eb1e0e0a87a..ae709197f375 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -287,6 +287,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 a3a6396df3c4..59bf02aca8d5 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -169,3 +169,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 +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch b/patch/v7.0.0_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch new file mode 100644 index 0000000..1d82128 --- /dev/null +++ b/patch/v7.0.0_iot/0013-media-pci-enable-lt6911gxd-in-ipu-bridge.patch @@ -0,0 +1,26 @@ +From 5c26caf8019ef53f1009596bdfb0a0a4ecc2b21f Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Tue, 21 Apr 2026 14:33:42 +0800 +Subject: [PATCH 13/23] 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 32cc95a766b7..960c2a7f1fae 100644 +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -97,6 +97,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { + IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), + /* Toshiba T4KA3 */ + IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), ++ /* Lontium lt6911gxd */ ++ IPU_SENSOR_CONFIG("INTC1124", 0), + }; + + /* +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0014-ipu-bridge-add-CPHY-support.patch b/patch/v7.0.0_iot/0014-ipu-bridge-add-CPHY-support.patch new file mode 100644 index 0000000..3749e9e --- /dev/null +++ b/patch/v7.0.0_iot/0014-ipu-bridge-add-CPHY-support.patch @@ -0,0 +1,113 @@ +From 1f5bc51f035647f58da48781465c8120e8830b8d Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 17 Apr 2026 15:30:52 +0800 +Subject: [PATCH 14/23] 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 960c2a7f1fae..d049db382e01 100644 +--- a/drivers/media/pci/intel/ipu-bridge.c ++++ b/drivers/media/pci/intel/ipu-bridge.c +@@ -36,6 +36,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 +@@ -347,6 +350,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); +@@ -365,6 +369,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; + +@@ -422,9 +427,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); +@@ -444,6 +456,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 16fac765456e..5f837612b2e8 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]; +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0015-max9x-add-config-in-makefile-kconfig.patch b/patch/v7.0.0_iot/0015-max9x-add-config-in-makefile-kconfig.patch new file mode 100644 index 0000000..df21d96 --- /dev/null +++ b/patch/v7.0.0_iot/0015-max9x-add-config-in-makefile-kconfig.patch @@ -0,0 +1,55 @@ +From c1af44f38daf27642c72a606141634ffe7e731d0 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 25 Oct 2025 16:46:35 +0800 +Subject: [PATCH 15/23] max9x: add config in makefile & kconfig + +Signed-off-by: hepengpx +Signed-off-by: linya14x +--- + drivers/media/i2c/Kconfig | 16 ++++++++++++++++ + drivers/media/i2c/Makefile | 2 ++ + 2 files changed, 18 insertions(+) + +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +index ae709197f375..fb238c900207 100644 +--- a/drivers/media/i2c/Kconfig ++++ b/drivers/media/i2c/Kconfig +@@ -298,6 +298,22 @@ 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_MAX9X ++ tristate "MAX9X serdes 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 MAX9X serdes. ++ + config VIDEO_MAX9271_LIB + tristate + +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +index 59bf02aca8d5..61228063d739 100644 +--- a/drivers/media/i2c/Makefile ++++ b/drivers/media/i2c/Makefile +@@ -63,6 +63,8 @@ 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_MAX9X) += max9x/ ++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 + diff --git a/patch/v7.0.0_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch b/patch/v7.0.0_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch new file mode 100644 index 0000000..0151ba2 --- /dev/null +++ b/patch/v7.0.0_iot/0016-drivers-media-set-v4l2_subdev_enable_streams_api-tru.patch @@ -0,0 +1,27 @@ +From a82c214baa7b4177a70fd32f2676df7eebd034e3 Mon Sep 17 00:00:00 2001 +From: hepengpx +Date: Wed, 27 Aug 2025 11:10:06 +0800 +Subject: [PATCH 16/23] 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 32e6f60e26c7..9cf007577e66 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 + + /* +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch b/patch/v7.0.0_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch new file mode 100644 index 0000000..27c44ad --- /dev/null +++ b/patch/v7.0.0_iot/0017-staging-ipu7-Update-IPU7-firmware-ABI-version-to-1.2.patch @@ -0,0 +1,86 @@ +From b3e93606b3dda504c57a4e0799e444fa76fd4279 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 14 Nov 2025 18:41:08 +0800 +Subject: [PATCH 17/23] 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 a1519c4fe661..4ce304f54e4b 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 c42d0b7a2627..7f622bfe9af6 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 8a78dd0936df..1319f0eb6319 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]; + }; +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch b/patch/v7.0.0_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch new file mode 100644 index 0000000..50dd5cf --- /dev/null +++ b/patch/v7.0.0_iot/0018-patch-staging-add-IPU8_PCI_ID-support.patch @@ -0,0 +1,26 @@ +From b23235fabe24bf9f5367dde79a03a7b2c213eb3a Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Fri, 24 Oct 2025 15:01:40 +0800 +Subject: [PATCH 18/23] 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 e0b00b3a8e84..adaf9a9324d1 100644 +--- a/drivers/staging/media/ipu7/ipu7.c ++++ b/drivers/staging/media/ipu7/ipu7.c +@@ -2821,6 +2821,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); +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.12.patch b/patch/v7.0.0_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.12.patch new file mode 100644 index 0000000..47fd55c --- /dev/null +++ b/patch/v7.0.0_iot/0019-staging-ipu7-Add-IPU8-ABI-version-1.0.12.patch @@ -0,0 +1,480 @@ +From fab7263816856190f3859cd839a5fe48d7ad6af0 Mon Sep 17 00:00:00 2001 +From: Hao Yao +Date: Fri, 24 Oct 2025 12:09:58 +0800 +Subject: [PATCH 19/23] staging: ipu7: Add IPU8 ABI version 1.0.12 + +Signed-off-by: Hao Yao +--- + .../staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 93 +++++++++- + drivers/staging/media/ipu7/ipu7-fw-isys.c | 170 +++++++++++++++++- + 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, 276 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 7f622bfe9af6..9a4c63b68044 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,16 +270,57 @@ struct ipu7_insys_output_dpcm { + u8 pad; + }; + ++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_v1 crop; ++ struct ipu7_insys_output_dpcm dpcm; ++ u32 stride; ++ u16 ft; ++ u8 send_irq; ++ u8 input_pin_id; ++ u8 early_ack_en; ++ 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 pad[3]; ++ u8 cfa_dim; ++ u8 binning_factor; + }; + + struct ipu7_insys_input_pin { +@@ -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,7 +363,15 @@ struct ipu7_insys_buffset { + u8 pad[5]; + }; + +-struct ipu7_insys_resp { ++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; +@@ -318,6 +385,20 @@ struct ipu7_insys_resp { + u16 mipi_fn; + }; + ++struct ipu7_insys_resp { ++ u64 buf_id; ++ struct ipu7_insys_capture_output_pin_payload pin; ++ struct ia_gofo_msg_err error_info; ++ u32 timestamp[2]; ++ u16 mipi_fn; ++ u8 type; ++ u8 msg_link_streaming_mode; ++ u8 stream_id; ++ u8 pin_id; ++ u8 frame_id; ++ u8 skip_frame; ++}; ++ + struct ipu7_insys_resp_queue_token { + struct ipu7_insys_resp resp_info; + }; +@@ -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 1ece286c2934..c594dbdbf3cc 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 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,18 @@ 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; ++ isys->abi_ops.decode_resp = isys_decode_resp; ++ isys->abi_ops.resp_queue_token_size = ++ sizeof(struct ipu7_insys_resp); ++ } else { ++ isys->abi_ops.prepare_payload = isys_prepare_fw_payload_v1; ++ isys->abi_ops.decode_resp = isys_decode_resp_v1; ++ isys->abi_ops.resp_queue_token_size = ++ sizeof(struct ipu7_insys_resp_v1); ++ } ++ + adev->syscom = syscom; + syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES; + syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES; +@@ -111,11 +238,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 +322,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 +460,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 +471,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 +501,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 c7cd9b3e66f0..9df4f8853180 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 6e6c627b957e..9e35aae5befe 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-video.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-video.c +@@ -452,10 +452,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 7351fa82888f..87d30afbd872 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; +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch b/patch/v7.0.0_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch new file mode 100644 index 0000000..4207498 --- /dev/null +++ b/patch/v7.0.0_iot/0020-staging-ipu7-Define-gpreg_stride-for-different-IPU-v.patch @@ -0,0 +1,80 @@ +From 6054a5264cfacc7b087da3ad5999bb386592ae1c Mon Sep 17 00:00:00 2001 +From: Hao Yao +Date: Mon, 26 Jan 2026 18:10:20 +0800 +Subject: [PATCH 20/23] 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 3f15af3b4c79..0886a78def6e 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c +@@ -168,7 +168,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", +@@ -345,7 +346,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 adaf9a9324d1..df53a6523b39 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 21988ce41acf..41f3efded9cb 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 { +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch b/patch/v7.0.0_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch new file mode 100644 index 0000000..c8e4a3a --- /dev/null +++ b/patch/v7.0.0_iot/0021-staging-media-ipu7-Fix-potential-NULL-pointer-derefe.patch @@ -0,0 +1,61 @@ +From ab16a7e7ae0b361ae50cddcec7522b61af15c008 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Sat, 28 Feb 2026 17:21:52 +0800 +Subject: [PATCH 21/23] 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 39b9d12f2c68..57734a552aa7 100644 +--- a/drivers/staging/media/ipu7/ipu7-dma.c ++++ b/drivers/staging/media/ipu7/ipu7-dma.c +@@ -249,12 +249,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; + +@@ -336,8 +340,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; +@@ -345,6 +348,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; + +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch b/patch/v7.0.0_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch new file mode 100644 index 0000000..4798843 --- /dev/null +++ b/patch/v7.0.0_iot/0022-staging-media-ipu7-set-skipframe-flag-when-frame-err.patch @@ -0,0 +1,42 @@ +From fddccb3103edf4fb02f5fbe52a7d0098e4237c98 Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Thu, 5 Mar 2026 15:53:38 +0800 +Subject: [PATCH 22/23] 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 9df4f8853180..22239f11f7f7 100644 +--- a/drivers/staging/media/ipu7/ipu7-isys-queue.c ++++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c +@@ -1092,6 +1092,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; +@@ -1136,6 +1139,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; +-- +2.43.0 + diff --git a/patch/v7.0.0_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch b/patch/v7.0.0_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch new file mode 100644 index 0000000..ba7cb43 --- /dev/null +++ b/patch/v7.0.0_iot/0023-staging-ipu7-Add-more-insys-frame-format.patch @@ -0,0 +1,33 @@ +From 1f5e476980ab639f5cd97c761648e9881d1d5649 Mon Sep 17 00:00:00 2001 +From: Hao Yao +Date: Fri, 27 Mar 2026 18:04:38 +0800 +Subject: [PATCH 23/23] 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 9a4c63b68044..db1bcfd639b2 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 + }; + +-- +2.43.0 +