On Thu May 29 12:13:19 2025 +0800, Bingbu Cao wrote:
> IPU7 irmware is generated and released as signed Code Partition
> Directory (CPD) format file which is aligned with the SPI flash code
> partition definition. The driver parses the CPD based on the layout.
> 
> Syscom is an inter-process(or) communication mechanism between an IPU and
> host. Syscom uses message queues for message exchange between IPU and
> host. Each message queue has its consumer and producer, host queue
> messages to firmware as the producer and then firmware to dequeue the
> messages as consumer and vice versa. IPU and host use shared registers or
> memory to reside the read and write indices which are updated by consumer
> and producer.
> 
> IPU7 firmware defined its boot sequence, driver setup the boot
> configuration and program the boot parameters to start and run
> the firmware.
> 
> Signed-off-by: Bingbu Cao <bingbu....@intel.com>
> Signed-off-by: Sakari Ailus <sakari.ai...@linux.intel.com>
> Signed-off-by: Hans Verkuil <hverk...@xs4all.nl>

Patch committed.

Thanks,
Hans Verkuil

 drivers/staging/media/ipu7/ipu7-boot.c   | 430 +++++++++++++++++++++++++++++++
 drivers/staging/media/ipu7/ipu7-boot.h   |  25 ++
 drivers/staging/media/ipu7/ipu7-cpd.c    | 276 ++++++++++++++++++++
 drivers/staging/media/ipu7/ipu7-cpd.h    |  16 ++
 drivers/staging/media/ipu7/ipu7-syscom.c |  78 ++++++
 drivers/staging/media/ipu7/ipu7-syscom.h |  35 +++
 6 files changed, 860 insertions(+)

---

diff --git a/drivers/staging/media/ipu7/ipu7-boot.c 
b/drivers/staging/media/ipu7/ipu7-boot.c
new file mode 100644
index 000000000000..d7901ff78b38
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-boot.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 - 2025 Intel Corporation
+ */
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "abi/ipu7_fw_boot_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-boot.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress-regs.h"
+#include "ipu7-dma.h"
+#include "ipu7-platform-regs.h"
+#include "ipu7-syscom.h"
+
+#define IPU_FW_START_STOP_TIMEOUT              2000
+#define IPU_BOOT_CELL_RESET_TIMEOUT            (2 * USEC_PER_SEC)
+#define BOOT_STATE_IS_CRITICAL(s)      IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s)
+#define BOOT_STATE_IS_READY(s)         ((s) == IA_GOFO_FW_BOOT_STATE_READY)
+#define BOOT_STATE_IS_INACTIVE(s)      ((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE)
+
+struct ipu7_boot_context {
+       u32 base;
+       u32 dmem_address;
+       u32 status_ctrl_reg;
+       u32 fw_start_address_reg;
+       u32 fw_code_base_reg;
+};
+
+static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = {
+       {
+               /* ISYS */
+               .dmem_address = IPU_ISYS_DMEM_OFFSET,
+               .status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS,
+               .fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR,
+               .fw_code_base_reg = IS_UC_CTRL_BASE
+       },
+       {
+               /* PSYS */
+               .dmem_address = IPU_PSYS_DMEM_OFFSET,
+               .status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS,
+               .fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR,
+               .fw_code_base_reg = PS_UC_CTRL_BASE
+       }
+};
+
+static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev,
+                               enum ia_gofo_buttress_reg_id reg)
+{
+       u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX;
+
+       return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg);
+}
+
+static void write_fw_boot_param(const struct ipu7_bus_device *adev,
+                               enum ia_gofo_buttress_reg_id reg,
+                               u32 val)
+{
+       void __iomem *base = adev->isp->base;
+
+       dev_dbg(&adev->auxdev.dev,
+               "write boot param reg: %d addr: %x val: 0x%x\n",
+               reg, get_fw_boot_reg_addr(adev, reg), val);
+       writel(val, base + get_fw_boot_reg_addr(adev, reg));
+}
+
+static u32 read_fw_boot_param(const struct ipu7_bus_device *adev,
+                             enum ia_gofo_buttress_reg_id reg)
+{
+       void __iomem *base = adev->isp->base;
+
+       return readl(base + get_fw_boot_reg_addr(adev, reg));
+}
+
+static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev)
+{
+       const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+       const struct device *dev = &adev->auxdev.dev;
+       u32 ucx_ctrl_status = ctx->status_ctrl_reg;
+       u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT;
+       void __iomem *base = adev->isp->base;
+       u32 val, val2;
+       int ret;
+
+       dev_dbg(dev, "cell enter reset...\n");
+       val = readl(base + ucx_ctrl_status);
+       dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n",
+               ucx_ctrl_status, val);
+
+       dev_dbg(dev, "force cell reset...\n");
+       val |= UCX_CTL_RESET;
+       val &= ~UCX_CTL_RUN;
+
+       dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+               ucx_ctrl_status, val);
+       writel(val, base + ucx_ctrl_status);
+
+       ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
+                                (val2 & 0x3U) == (val & 0x3U), 100, timeout);
+       if (ret) {
+               dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2);
+               return -ETIMEDOUT;
+       }
+
+       dev_dbg(dev, "cell exit reset...\n");
+       val = readl(base + ucx_ctrl_status);
+       WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN),
+            "cell status 0x%x", val);
+
+       val &= ~(UCX_CTL_RESET | UCX_CTL_RUN);
+       dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+               ucx_ctrl_status, val);
+       writel(val, base + ucx_ctrl_status);
+
+       ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
+                                (val2 & 0x3U) == (val & 0x3U), 100, timeout);
+       if (ret) {
+               dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev)
+{
+       const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+       void __iomem *base = adev->isp->base;
+       const struct device *dev = &adev->auxdev.dev;
+       u32 val;
+
+       dev_dbg(dev, "starting cell...\n");
+       val = readl(base + ctx->status_ctrl_reg);
+       WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN));
+
+       val &= ~UCX_CTL_RESET;
+       val |= UCX_CTL_RUN;
+       dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+               ctx->status_ctrl_reg, val);
+       writel(val, base + ctx->status_ctrl_reg);
+}
+
+static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev)
+{
+       const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+       void __iomem *base = adev->isp->base;
+       const struct device *dev = &adev->auxdev.dev;
+       u32 val;
+
+       dev_dbg(dev, "stopping cell...\n");
+
+       val = readl(base + ctx->status_ctrl_reg);
+       val &= ~UCX_CTL_RUN;
+       dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+               ctx->status_ctrl_reg, val);
+       writel(val, base + ctx->status_ctrl_reg);
+
+       /* Wait for uC transactions complete */
+       usleep_range(10, 20);
+
+       val = readl(base + ctx->status_ctrl_reg);
+       val |= UCX_CTL_RESET;
+       dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+               ctx->status_ctrl_reg, val);
+       writel(val, base + ctx->status_ctrl_reg);
+}
+
+static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev)
+{
+       const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+       void __iomem *base = adev->isp->base;
+
+       dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n",
+               ctx->fw_start_address_reg, adev->fw_entry);
+       writel(adev->fw_entry, base + ctx->fw_start_address_reg);
+
+       return ipu7_boot_cell_reset(adev);
+}
+
+static void init_boot_config(struct ia_gofo_boot_config *boot_config,
+                            u32 length, u8 major)
+{
+       /* syscom version, new syscom2 version */
+       boot_config->length = length;
+       boot_config->config_version.major = 1U;
+       boot_config->config_version.minor = 0U;
+       boot_config->config_version.subminor = 0U;
+       boot_config->config_version.patch = 0U;
+
+       /* msg version for task interface */
+       boot_config->client_version_support.num_versions = 1U;
+       boot_config->client_version_support.versions[0].major = major;
+       boot_config->client_version_support.versions[0].minor = 0U;
+       boot_config->client_version_support.versions[0].subminor = 0U;
+       boot_config->client_version_support.versions[0].patch = 0U;
+}
+
+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev,
+                              struct syscom_queue_config *qconfigs,
+                              int num_queues, u32 uc_freq,
+                              dma_addr_t subsys_config, u8 major)
+{
+       u32 total_queue_size_aligned = 0;
+       struct ipu7_syscom_context *syscom = adev->syscom;
+       struct ia_gofo_boot_config *boot_config;
+       struct syscom_queue_params_config *cfgs;
+       struct device *dev = &adev->auxdev.dev;
+       struct syscom_config_s *syscfg;
+       dma_addr_t queue_mem_dma_ptr;
+       void *queue_mem_ptr;
+       unsigned int i;
+
+       dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n",
+               num_queues, uc_freq, &subsys_config);
+       /* Allocate boot config. */
+       adev->boot_config_size =
+               sizeof(*cfgs) * num_queues + sizeof(*boot_config);
+       adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size,
+                                          &adev->boot_config_dma_addr,
+                                          GFP_KERNEL, 0);
+       if (!adev->boot_config) {
+               dev_err(dev, "Failed to allocate boot config.\n");
+               return -ENOMEM;
+       }
+
+       boot_config = adev->boot_config;
+       memset(boot_config, 0, sizeof(struct ia_gofo_boot_config));
+       init_boot_config(boot_config, adev->boot_config_size, major);
+       boot_config->subsys_config = subsys_config;
+
+       boot_config->uc_tile_frequency = uc_freq;
+       boot_config->uc_tile_frequency_units =
+               IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ;
+       boot_config->syscom_context_config.max_output_queues =
+               syscom->num_output_queues;
+       boot_config->syscom_context_config.max_input_queues =
+               syscom->num_input_queues;
+
+       ipu7_dma_sync_single(adev, adev->boot_config_dma_addr,
+                            adev->boot_config_size);
+
+       for (i = 0; i < num_queues; i++) {
+               u32 queue_size = qconfigs[i].max_capacity *
+                       qconfigs[i].token_size_in_bytes;
+
+               queue_size = ALIGN(queue_size, 64U);
+               total_queue_size_aligned += queue_size;
+               qconfigs[i].queue_size = queue_size;
+       }
+
+       /* Allocate queue memory */
+       syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned,
+                                          &syscom->queue_mem_dma_addr,
+                                          GFP_KERNEL, 0);
+       if (!syscom->queue_mem) {
+               dev_err(dev, "Failed to allocate queue memory.\n");
+               return -ENOMEM;
+       }
+       syscom->queue_mem_size = total_queue_size_aligned;
+
+       syscfg = &boot_config->syscom_context_config;
+       cfgs = ipu7_syscom_get_queue_config(syscfg);
+       queue_mem_ptr = syscom->queue_mem;
+       queue_mem_dma_ptr = syscom->queue_mem_dma_addr;
+       for (i = 0; i < num_queues; i++) {
+               cfgs[i].token_array_mem = queue_mem_dma_ptr;
+               cfgs[i].max_capacity = qconfigs[i].max_capacity;
+               cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes;
+               qconfigs[i].token_array_mem = queue_mem_ptr;
+               queue_mem_dma_ptr += qconfigs[i].queue_size;
+               queue_mem_ptr += qconfigs[i].queue_size;
+       }
+
+       ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr,
+                            total_queue_size_aligned);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7");
+
+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev)
+{
+       struct ipu7_syscom_context *syscom = adev->syscom;
+
+       if (syscom->queue_mem) {
+               ipu7_dma_free(adev, syscom->queue_mem_size,
+                             syscom->queue_mem,
+                             syscom->queue_mem_dma_addr, 0);
+               syscom->queue_mem = NULL;
+               syscom->queue_mem_dma_addr = 0;
+       }
+
+       if (adev->boot_config) {
+               ipu7_dma_free(adev, adev->boot_config_size,
+                             adev->boot_config,
+                             adev->boot_config_dma_addr, 0);
+               adev->boot_config = NULL;
+               adev->boot_config_dma_addr = 0;
+       }
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7");
+
+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev)
+{
+       const struct device *dev = &adev->auxdev.dev;
+       u32 timeout = IPU_FW_START_STOP_TIMEOUT;
+       void __iomem *base = adev->isp->base;
+       u32 boot_state, last_boot_state;
+       u32 indices_addr, msg_ver, id;
+       int ret;
+
+       ret = ipu7_boot_cell_init(adev);
+       if (ret)
+               return ret;
+
+       dev_dbg(dev, "start booting fw...\n");
+       /* store "uninit" state to syscom/boot state reg */
+       write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID,
+                           IA_GOFO_FW_BOOT_STATE_UNINIT);
+       /*
+        * Set registers to zero
+        * (not strictly required, but recommended for diagnostics)
+        */
+       write_fw_boot_param(adev,
+                           IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0);
+       write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0);
+       /* store firmware configuration address */
+       write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID,
+                           adev->boot_config_dma_addr);
+
+       /* Kick uC, then wait for boot complete */
+       ipu7_boot_cell_start(adev);
+
+       last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT;
+       while (timeout--) {
+               boot_state = read_fw_boot_param(adev,
+                                               IA_GOFO_FW_BOOT_STATE_ID);
+               if (boot_state != last_boot_state) {
+                       dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n",
+                               last_boot_state, boot_state);
+                       last_boot_state = boot_state;
+               }
+               if (BOOT_STATE_IS_CRITICAL(boot_state) ||
+                   BOOT_STATE_IS_READY(boot_state))
+                       break;
+               usleep_range(1000, 1200);
+       }
+
+       if (BOOT_STATE_IS_CRITICAL(boot_state)) {
+               ipu7_dump_fw_error_log(adev);
+               dev_err(dev, "critical boot state error 0x%x\n", boot_state);
+               return -EINVAL;
+       } else if (!BOOT_STATE_IS_READY(boot_state)) {
+               dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state);
+               return -ETIMEDOUT;
+       }
+       dev_dbg(dev, "fw boot done.\n");
+
+       /* Get FW syscom queue indices addr */
+       id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID;
+       indices_addr = read_fw_boot_param(adev, id);
+       adev->syscom->queue_indices = base + indices_addr;
+       dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr);
+
+       /* Get message version. */
+       msg_ver = read_fw_boot_param(adev,
+                                    IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID);
+       dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7");
+
+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev)
+{
+       const struct device *dev = &adev->auxdev.dev;
+       u32 timeout = IPU_FW_START_STOP_TIMEOUT;
+       u32 boot_state;
+
+       boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID);
+       if (BOOT_STATE_IS_CRITICAL(boot_state) ||
+           !BOOT_STATE_IS_READY(boot_state)) {
+               dev_err(dev, "fw not ready for shutdown, state 0x%x\n",
+                       boot_state);
+               return -EBUSY;
+       }
+
+       /* Issue shutdown to start shutdown process */
+       dev_dbg(dev, "stopping fw...\n");
+       write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID,
+                           IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD);
+       while (timeout--) {
+               boot_state = read_fw_boot_param(adev,
+                                               IA_GOFO_FW_BOOT_STATE_ID);
+               if (BOOT_STATE_IS_CRITICAL(boot_state) ||
+                   BOOT_STATE_IS_INACTIVE(boot_state))
+                       break;
+               usleep_range(1000, 1200);
+       }
+
+       if (BOOT_STATE_IS_CRITICAL(boot_state)) {
+               ipu7_dump_fw_error_log(adev);
+               dev_err(dev, "critical boot state error 0x%x\n", boot_state);
+               return -EINVAL;
+       } else if (!BOOT_STATE_IS_INACTIVE(boot_state)) {
+               dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state);
+               return -ETIMEDOUT;
+       }
+
+       ipu7_boot_cell_stop(adev);
+       dev_dbg(dev, "stop fw done.\n");
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7");
+
+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev)
+{
+       return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7");
diff --git a/drivers/staging/media/ipu7/ipu7-boot.h 
b/drivers/staging/media/ipu7/ipu7-boot.h
new file mode 100644
index 000000000000..5600be849931
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-boot.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2022 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_BOOT_H
+#define IPU7_BOOT_H
+
+#include <linux/types.h>
+
+struct ipu7_bus_device;
+struct syscom_queue_config;
+
+#define FW_QUEUE_CONFIG_SIZE(num_queues)       \
+       (sizeof(struct syscom_queue_config) * (num_queues))
+
+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev,
+                              struct syscom_queue_config *qconfigs,
+                              int num_queues, u32 uc_freq,
+                              dma_addr_t subsys_config, u8 major);
+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev);
+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev);
+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev);
+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-cpd.c 
b/drivers/staging/media/ipu7/ipu7-cpd.c
new file mode 100644
index 000000000000..4f49fb57eae4
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-cpd.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015 - 2025 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/gfp_types.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "ipu7.h"
+#include "ipu7-cpd.h"
+
+/* $CPD */
+#define CPD_HDR_MARK           0x44504324
+
+/* Maximum size is 4K DWORDs or 16KB */
+#define MAX_MANIFEST_SIZE      (SZ_4K * sizeof(u32))
+
+#define CPD_MANIFEST_IDX       0
+#define CPD_BINARY_START_IDX   1U
+#define CPD_METADATA_START_IDX 2U
+#define CPD_BINARY_NUM         2U /* ISYS + PSYS */
+/*
+ * Entries include:
+ * 1 manifest entry.
+ * 1 metadata entry for each sub system(ISYS and PSYS).
+ * 1 binary entry for each sub system(ISYS and PSYS).
+ */
+#define CPD_ENTRY_NUM          (CPD_BINARY_NUM * 2U + 1U)
+
+#define CPD_METADATA_ATTR      0xa
+#define CPD_METADATA_IPL       0x1c
+#define ONLINE_METADATA_SIZE   128U
+#define ONLINE_METADATA_LINES  6U
+
+struct ipu7_cpd_hdr {
+       u32 hdr_mark;
+       u32 ent_cnt;
+       u8 hdr_ver;
+       u8 ent_ver;
+       u8 hdr_len;
+       u8 rsvd;
+       u8 partition_name[4];
+       u32 crc32;
+} __packed;
+
+struct ipu7_cpd_ent {
+       u8 name[12];
+       u32 offset;
+       u32 len;
+       u8 rsvd[4];
+} __packed;
+
+struct ipu7_cpd_metadata_hdr {
+       u32 type;
+       u32 len;
+} __packed;
+
+struct ipu7_cpd_metadata_attr {
+       struct ipu7_cpd_metadata_hdr hdr;
+       u8 compression_type;
+       u8 encryption_type;
+       u8 rsvd[2];
+       u32 uncompressed_size;
+       u32 compressed_size;
+       u32 module_id;
+       u8 hash[48];
+} __packed;
+
+struct ipu7_cpd_metadata_ipl {
+       struct ipu7_cpd_metadata_hdr hdr;
+       u32 param[4];
+       u8 rsvd[8];
+} __packed;
+
+struct ipu7_cpd_metadata {
+       struct ipu7_cpd_metadata_attr attr;
+       struct ipu7_cpd_metadata_ipl ipl;
+} __packed;
+
+static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx)
+{
+       const struct ipu7_cpd_hdr *cpd_hdr = cpd;
+
+       return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx;
+}
+
+#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0)
+
+static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int 
idx)
+{
+       struct ipu7_cpd_ent *cpd_ent =
+               ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2);
+
+       return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset);
+}
+
+static int ipu7_cpd_validate_cpd(struct ipu7_device *isp,
+                                const void *cpd, unsigned long data_size)
+{
+       const struct ipu7_cpd_hdr *cpd_hdr = cpd;
+       struct device *dev = &isp->pdev->dev;
+       struct ipu7_cpd_ent *ent;
+       unsigned int i;
+       u8 len;
+
+       len = cpd_hdr->hdr_len;
+
+       /* Ensure cpd hdr is within moduledata */
+       if (data_size < len) {
+               dev_err(dev, "Invalid CPD moduledata size\n");
+               return -EINVAL;
+       }
+
+       /* Check for CPD file marker */
+       if (cpd_hdr->hdr_mark != CPD_HDR_MARK) {
+               dev_err(dev, "Invalid CPD header marker\n");
+               return -EINVAL;
+       }
+
+       /* Sanity check for CPD entry header */
+       if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) {
+               dev_err(dev, "Invalid CPD entry number %d\n",
+                       cpd_hdr->ent_cnt);
+               return -EINVAL;
+       }
+       if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
+               dev_err(dev, "Invalid CPD entry headers\n");
+               return -EINVAL;
+       }
+
+       /* Ensure that all entries are within moduledata */
+       ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len);
+       for (i = 0; i < cpd_hdr->ent_cnt; i++) {
+               if (data_size < ent->offset ||
+                   data_size - ent->offset < ent->len) {
+                       dev_err(dev, "Invalid CPD entry %d\n", i);
+                       return -EINVAL;
+               }
+               ent++;
+       }
+
+       return 0;
+}
+
+static int ipu7_cpd_validate_metadata(struct ipu7_device *isp,
+                                     const void *cpd, int idx)
+{
+       const struct ipu7_cpd_ent *cpd_ent =
+               ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2);
+       const struct ipu7_cpd_metadata *metadata =
+               ipu7_cpd_get_metadata(cpd, idx);
+       struct device *dev = &isp->pdev->dev;
+
+       /* Sanity check for metadata size */
+       if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) {
+               dev_err(dev, "Invalid metadata size\n");
+               return -EINVAL;
+       }
+
+       /* Validate type and length of metadata sections */
+       if (metadata->attr.hdr.type != CPD_METADATA_ATTR) {
+               dev_err(dev, "Invalid metadata attr type (%d)\n",
+                       metadata->attr.hdr.type);
+               return -EINVAL;
+       }
+       if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) {
+               dev_err(dev, "Invalid metadata attr size (%d)\n",
+                       metadata->attr.hdr.len);
+               return -EINVAL;
+       }
+       if (metadata->ipl.hdr.type != CPD_METADATA_IPL) {
+               dev_err(dev, "Invalid metadata ipl type (%d)\n",
+                       metadata->ipl.hdr.type);
+               return -EINVAL;
+       }
+       if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) {
+               dev_err(dev, "Invalid metadata ipl size (%d)\n",
+                       metadata->ipl.hdr.len);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file,
+                              unsigned long cpd_file_size)
+{
+       struct device *dev = &isp->pdev->dev;
+       struct ipu7_cpd_ent *ent;
+       unsigned int i;
+       int ret;
+       char *buf;
+
+       ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size);
+       if (ret) {
+               dev_err(dev, "Invalid CPD in file\n");
+               return -EINVAL;
+       }
+
+       /* Sanity check for manifest size */
+       ent = ipu7_cpd_get_manifest(cpd_file);
+       if (ent->len > MAX_MANIFEST_SIZE) {
+               dev_err(dev, "Invalid manifest size\n");
+               return -EINVAL;
+       }
+
+       /* Validate metadata */
+       for (i = 0; i < CPD_BINARY_NUM; i++) {
+               ret = ipu7_cpd_validate_metadata(isp, cpd_file, i);
+               if (ret) {
+                       dev_err(dev, "Invalid metadata%d\n", i);
+                       return ret;
+               }
+       }
+
+       /* Get fw binary version. */
+       buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       for (i = 0; i < CPD_BINARY_NUM; i++) {
+               char *lines[ONLINE_METADATA_LINES];
+               char *info = buf;
+               unsigned int l;
+
+               ent = ipu7_cpd_get_entry(cpd_file,
+                                        CPD_BINARY_START_IDX + i * 2U);
+               memcpy(info, (u8 *)cpd_file + ent->offset + ent->len -
+                      ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE);
+               for (l = 0; l < ONLINE_METADATA_LINES; l++) {
+                       lines[l] = strsep((char **)&info, "\n");
+                       if (!lines[l])
+                               break;
+               }
+               if (l < ONLINE_METADATA_LINES) {
+                       dev_err(dev, "Failed to parse fw binary%d info.\n", i);
+                       continue;
+               }
+               dev_info(dev, "FW binary%d info:\n", i);
+               dev_info(dev, "Name: %s\n", lines[1]);
+               dev_info(dev, "Version: %s\n", lines[2]);
+               dev_info(dev, "Timestamp: %s\n", lines[3]);
+               dev_info(dev, "Commit: %s\n", lines[4]);
+       }
+       kfree(buf);
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7");
+
+int ipu7_cpd_copy_binary(const void *cpd, const char *name,
+                        void *code_region, u32 *entry)
+{
+       unsigned int i;
+
+       for (i = 0; i < CPD_BINARY_NUM; i++) {
+               const struct ipu7_cpd_ent *binary =
+                       ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U);
+               const struct ipu7_cpd_metadata *metadata =
+                       ipu7_cpd_get_metadata(cpd, i);
+
+               if (!strncmp(binary->name, name, sizeof(binary->name))) {
+                       memcpy(code_region + metadata->ipl.param[0],
+                              cpd + binary->offset, binary->len);
+                       *entry = metadata->ipl.param[2];
+                       return 0;
+               }
+       }
+
+       return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7");
diff --git a/drivers/staging/media/ipu7/ipu7-cpd.h 
b/drivers/staging/media/ipu7/ipu7-cpd.h
new file mode 100644
index 000000000000..b4178848c6b9
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-cpd.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2015 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_CPD_H
+#define IPU7_CPD_H
+
+struct ipu7_device;
+
+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp,
+                              const void *cpd_file,
+                              unsigned long cpd_file_size);
+int ipu7_cpd_copy_binary(const void *cpd, const char *name,
+                        void *code_region, u32 *entry);
+#endif /* IPU7_CPD_H */
diff --git a/drivers/staging/media/ipu7/ipu7-syscom.c 
b/drivers/staging/media/ipu7/ipu7-syscom.c
new file mode 100644
index 000000000000..3f9f9c5c3cce
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-syscom.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/export.h>
+#include <linux/io.h>
+
+#include "abi/ipu7_fw_syscom_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-syscom.h"
+
+static void __iomem *ipu7_syscom_get_indices(struct ipu7_syscom_context *ctx,
+                                            u32 q)
+{
+       return ctx->queue_indices + (q * sizeof(struct syscom_queue_indices_s));
+}
+
+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q)
+{
+       struct syscom_queue_config *queue_params = &ctx->queue_configs[q];
+       void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q);
+       u32 write_index = readl(queue_indices +
+                               offsetof(struct syscom_queue_indices_s,
+                                        write_index));
+       u32 read_index = readl(queue_indices +
+                              offsetof(struct syscom_queue_indices_s,
+                                       read_index));
+       void *token = NULL;
+
+       if (q < ctx->num_output_queues) {
+               /* Output queue */
+               bool empty = (write_index == read_index);
+
+               if (!empty)
+                       token = queue_params->token_array_mem +
+                               read_index *
+                               queue_params->token_size_in_bytes;
+       } else {
+               /* Input queue */
+               bool full = (read_index == ((write_index + 1U) %
+                                           (u32)queue_params->max_capacity));
+
+               if (!full)
+                       token = queue_params->token_array_mem +
+                               write_index * queue_params->token_size_in_bytes;
+       }
+       return token;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_token, "INTEL_IPU7");
+
+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q)
+{
+       struct syscom_queue_config *queue_params = &ctx->queue_configs[q];
+       void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q);
+       u32 offset, index;
+
+       if (q < ctx->num_output_queues)
+               /* Output queue */
+               offset = offsetof(struct syscom_queue_indices_s, read_index);
+
+       else
+               /* Input queue */
+               offset = offsetof(struct syscom_queue_indices_s, write_index);
+
+       index = readl(queue_indices + offset);
+       writel((index + 1U) % queue_params->max_capacity,
+              queue_indices + offset);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_put_token, "INTEL_IPU7");
+
+struct syscom_queue_params_config *
+ipu7_syscom_get_queue_config(struct syscom_config_s *config)
+{
+       return (struct syscom_queue_params_config *)(&config[1]);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_queue_config, "INTEL_IPU7");
diff --git a/drivers/staging/media/ipu7/ipu7-syscom.h 
b/drivers/staging/media/ipu7/ipu7-syscom.h
new file mode 100644
index 000000000000..e1fbe3b7914e
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-syscom.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_SYSCOM_H
+#define IPU7_SYSCOM_H
+
+#include <linux/types.h>
+
+struct syscom_config_s;
+struct syscom_queue_params_config;
+
+struct syscom_queue_config {
+       void *token_array_mem;
+       u32 queue_size;
+       u16 token_size_in_bytes;
+       u16 max_capacity;
+};
+
+struct ipu7_syscom_context {
+       u16 num_input_queues;
+       u16 num_output_queues;
+       struct syscom_queue_config *queue_configs;
+       void __iomem *queue_indices;
+       dma_addr_t queue_mem_dma_addr;
+       void *queue_mem;
+       u32 queue_mem_size;
+};
+
+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q);
+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q);
+struct syscom_queue_params_config *
+ipu7_syscom_get_queue_config(struct syscom_config_s *config);
+#endif

Reply via email to