]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
media: staging/ipu7: add IPU7 input system device driver
authorBingbu Cao <bingbu.cao@intel.com>
Thu, 29 May 2025 04:13:21 +0000 (12:13 +0800)
committerHans Verkuil <hverkuil@xs4all.nl>
Thu, 3 Jul 2025 13:44:21 +0000 (15:44 +0200)
The main input system driver mainly cover the basic hardware setup, v4l2
devices registration, firmware stream interfaces and interrupt handling.

Input system CSI2 receiver is exposed as a v4l2 sub-device. Each CSI2
sub-device represent one single CSI2 hardware port which be linked with
external sub-device such camera sensor by linked with ISYS CSI2's sink
pad. The CSI2 source pad is linked to the sink pad of video capture device.

Register V4L2 video device and setup the VB2 queues to support video
capture. Video streaming callback will trigger the input system driver to
construct a input system stream configuration for firmware based on data
type and stream ID and then queue buffers to firmware to do capture.

IPU7 CSI-2 D-PHY hardware is a Synopsys DWC MIPI CSI2 Rx IP, the driver
program the DPHY to receive MIPI data from camera sensors.

Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
16 files changed:
drivers/staging/media/ipu7/ipu7-fw-isys.c [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-fw-isys.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-csi-phy.c [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-csi-phy.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-csi2.c [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-csi2.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-queue.c [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-queue.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-subdev.c [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-subdev.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-video.c [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys-video.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys.c [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7-isys.h [new file with mode: 0644]
drivers/staging/media/ipu7/ipu7.c

diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c
new file mode 100644 (file)
index 0000000..e4b9c36
--- /dev/null
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/cacheflush.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "abi/ipu7_fw_insys_config_abi.h"
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-boot.h"
+#include "ipu7-bus.h"
+#include "ipu7-dma.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-isys.h"
+#include "ipu7-platform-regs.h"
+#include "ipu7-syscom.h"
+
+static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = {
+       "STREAM_OPEN",
+       "STREAM_START_AND_CAPTURE",
+       "STREAM_CAPTURE",
+       "STREAM_ABORT",
+       "STREAM_FLUSH",
+       "STREAM_CLOSE"
+};
+
+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys,
+                            const unsigned int stream_handle,
+                            void *cpu_mapped_buf,
+                            dma_addr_t dma_mapped_buf,
+                            size_t size, u16 send_type)
+{
+       struct ipu7_syscom_context *ctx = isys->adev->syscom;
+       struct device *dev = &isys->adev->auxdev.dev;
+       struct ipu7_insys_send_queue_token *token;
+
+       if (send_type >= N_IPU_INSYS_SEND_TYPE)
+               return -EINVAL;
+
+       dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]);
+
+       /*
+        * Time to flush cache in case we have some payload. Not all messages
+        * have that
+        */
+       if (cpu_mapped_buf)
+               clflush_cache_range(cpu_mapped_buf, size);
+
+       token = ipu7_syscom_get_token(ctx, stream_handle +
+                                     IPU_INSYS_INPUT_MSG_QUEUE);
+       if (!token)
+               return -EBUSY;
+
+       token->addr = dma_mapped_buf;
+       token->buf_handle = (unsigned long)cpu_mapped_buf;
+       token->send_type = send_type;
+       token->stream_id = stream_handle;
+       token->flag = IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE;
+
+       ipu7_syscom_put_token(ctx, stream_handle + IPU_INSYS_INPUT_MSG_QUEUE);
+       /* now wakeup FW */
+       ipu_buttress_wakeup_is_uc(isys->adev->isp);
+
+       return 0;
+}
+
+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys,
+                           const unsigned int stream_handle, u16 send_type)
+{
+       return ipu7_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
+                                       send_type);
+}
+
+int ipu7_fw_isys_init(struct ipu7_isys *isys)
+{
+       struct syscom_queue_config *queue_configs;
+       struct ipu7_bus_device *adev = isys->adev;
+       struct device *dev = &adev->auxdev.dev;
+       struct ipu7_insys_config *isys_config;
+       struct ipu7_syscom_context *syscom;
+       dma_addr_t isys_config_dma_addr;
+       unsigned int i, num_queues;
+       u32 freq;
+       u8 major;
+       int ret;
+
+       /* Allocate and init syscom context. */
+       syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context),
+                             GFP_KERNEL);
+       if (!syscom)
+               return -ENOMEM;
+
+       adev->syscom = syscom;
+       syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES;
+       syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES;
+       num_queues = syscom->num_input_queues + syscom->num_output_queues;
+       queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues),
+                                    GFP_KERNEL);
+       if (!queue_configs) {
+               ipu7_fw_isys_release(isys);
+               return -ENOMEM;
+       }
+       syscom->queue_configs = queue_configs;
+       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);
+       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);
+       queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0;
+       queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0;
+
+       queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].max_capacity =
+               IPU_ISYS_MAX_STREAMS;
+       queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes =
+               sizeof(struct ipu7_insys_send_queue_token);
+
+       for (i = IPU_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) {
+               queue_configs[i].max_capacity = IPU_ISYS_SIZE_SEND_QUEUE;
+               queue_configs[i].token_size_in_bytes =
+                       sizeof(struct ipu7_insys_send_queue_token);
+       }
+
+       /* Allocate ISYS subsys config. */
+       isys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_insys_config),
+                                    &isys_config_dma_addr, GFP_KERNEL, 0);
+       if (!isys_config) {
+               dev_err(dev, "Failed to allocate isys subsys config.\n");
+               ipu7_fw_isys_release(isys);
+               return -ENOMEM;
+       }
+       isys->subsys_config = isys_config;
+       isys->subsys_config_dma_addr = isys_config_dma_addr;
+       memset(isys_config, 0, sizeof(struct ipu7_insys_config));
+       isys_config->logger_config.use_source_severity = 0;
+       isys_config->logger_config.use_channels_enable_bitmask = 1;
+       isys_config->logger_config.channels_enable_bitmask =
+               LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK;
+       isys_config->logger_config.hw_printf_buffer_base_addr = 0U;
+       isys_config->logger_config.hw_printf_buffer_size_bytes = 0U;
+       isys_config->wdt_config.wdt_timer1_us = 0;
+       isys_config->wdt_config.wdt_timer2_us = 0;
+       ret = ipu_buttress_get_isys_freq(adev->isp, &freq);
+       if (ret) {
+               dev_err(dev, "Failed to get ISYS frequency.\n");
+               ipu7_fw_isys_release(isys);
+               return ret;
+       }
+
+       ipu7_dma_sync_single(adev, isys_config_dma_addr,
+                            sizeof(struct ipu7_insys_config));
+
+       major = is_ipu8(adev->isp->hw_ver) ? 2U : 1U;
+       ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues,
+                                        freq, isys_config_dma_addr, major);
+       if (ret)
+               ipu7_fw_isys_release(isys);
+
+       return ret;
+}
+
+void ipu7_fw_isys_release(struct ipu7_isys *isys)
+{
+       struct ipu7_bus_device *adev = isys->adev;
+
+       ipu7_boot_release_boot_config(adev);
+       if (isys->subsys_config) {
+               ipu7_dma_free(adev,
+                             sizeof(struct ipu7_insys_config),
+                             isys->subsys_config,
+                             isys->subsys_config_dma_addr, 0);
+               isys->subsys_config = NULL;
+               isys->subsys_config_dma_addr = 0;
+       }
+}
+
+int ipu7_fw_isys_open(struct ipu7_isys *isys)
+{
+       return ipu7_boot_start_fw(isys->adev);
+}
+
+int ipu7_fw_isys_close(struct ipu7_isys *isys)
+{
+       return ipu7_boot_stop_fw(isys->adev);
+}
+
+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 ipu7_fw_isys_put_resp(struct ipu7_isys *isys)
+{
+       ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE);
+}
+
+void ipu7_fw_isys_dump_stream_cfg(struct device *dev,
+                                 struct ipu7_insys_stream_cfg *cfg)
+{
+       unsigned int i;
+
+       dev_dbg(dev, "---------------------------\n");
+       dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n");
+
+       dev_dbg(dev, ".port id %d\n", cfg->port_id);
+       dev_dbg(dev, ".vc %d\n", cfg->vc);
+       dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins);
+       dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins);
+       dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map);
+
+       for (i = 0; i < cfg->nof_input_pins; i++) {
+               dev_dbg(dev, ".input_pin[%d]:\n", i);
+               dev_dbg(dev, "\t.dt = 0x%0x\n",
+                       cfg->input_pins[i].dt);
+               dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n",
+                       cfg->input_pins[i].disable_mipi_unpacking);
+               dev_dbg(dev, "\t.dt_rename_mode = %d\n",
+                       cfg->input_pins[i].dt_rename_mode);
+               dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",
+                       cfg->input_pins[i].mapped_dt);
+               dev_dbg(dev, "\t.input_res = %d x %d\n",
+                       cfg->input_pins[i].input_res.width,
+                       cfg->input_pins[i].input_res.height);
+               dev_dbg(dev, "\t.sync_msg_map = 0x%x\n",
+                       cfg->input_pins[i].sync_msg_map);
+       }
+
+       for (i = 0; i < cfg->nof_output_pins; i++) {
+               dev_dbg(dev, ".output_pin[%d]:\n", i);
+               dev_dbg(dev, "\t.input_pin_id = %d\n",
+                       cfg->output_pins[i].input_pin_id);
+               dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);
+               dev_dbg(dev, "\t.send_irq = %d\n",
+                       cfg->output_pins[i].send_irq);
+               dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);
+
+               dev_dbg(dev, "\t.link.buffer_lines = %d\n",
+                       cfg->output_pins[i].link.buffer_lines);
+               dev_dbg(dev, "\t.link.foreign_key = %d\n",
+                       cfg->output_pins[i].link.foreign_key);
+               dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n",
+                       cfg->output_pins[i].link.granularity_pointer_update);
+               dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n",
+                       cfg->output_pins[i].link.msg_link_streaming_mode);
+               dev_dbg(dev, "\t.link.pbk_id = %d\n",
+                       cfg->output_pins[i].link.pbk_id);
+               dev_dbg(dev, "\t.link.pbk_slot_id = %d\n",
+                       cfg->output_pins[i].link.pbk_slot_id);
+               dev_dbg(dev, "\t.link.dest = %d\n",
+                       cfg->output_pins[i].link.dest);
+               dev_dbg(dev, "\t.link.use_sw_managed = %d\n",
+                       cfg->output_pins[i].link.use_sw_managed);
+               dev_dbg(dev, "\t.link.is_snoop = %d\n",
+                       cfg->output_pins[i].link.is_snoop);
+
+               dev_dbg(dev, "\t.crop.line_top = %d\n",
+                       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.dpcm_enable = %d\n",
+                       cfg->output_pins[i].dpcm.enable);
+               dev_dbg(dev, "\t.dpcm.type = %d\n",
+                       cfg->output_pins[i].dpcm.type);
+               dev_dbg(dev, "\t.dpcm.predictor = %d\n",
+                       cfg->output_pins[i].dpcm.predictor);
+       }
+       dev_dbg(dev, "---------------------------\n");
+}
+
+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev,
+                                     struct ipu7_insys_buffset *buf,
+                                     unsigned int outputs)
+{
+       unsigned int i;
+
+       dev_dbg(dev, "--------------------------\n");
+       dev_dbg(dev, "IPU_ISYS_BUFF_SET\n");
+       dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map);
+       dev_dbg(dev, ".frame_id = %d\n", buf->frame_id);
+       dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame);
+
+       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, "---------------------------\n");
+}
diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.h b/drivers/staging/media/ipu7/ipu7-fw-isys.h
new file mode 100644 (file)
index 0000000..b556fed
--- /dev/null
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_ISYS_H
+#define IPU7_FW_ISYS_H
+
+#include <linux/types.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+struct device;
+struct ipu7_insys_buffset;
+struct ipu7_insys_stream_cfg;
+struct ipu7_isys;
+
+/* From here on type defines not coming from the ISYSAPI interface */
+
+int ipu7_fw_isys_init(struct ipu7_isys *isys);
+void ipu7_fw_isys_release(struct ipu7_isys *isys);
+int ipu7_fw_isys_open(struct ipu7_isys *isys);
+int ipu7_fw_isys_close(struct ipu7_isys *isys);
+
+void ipu7_fw_isys_dump_stream_cfg(struct device *dev,
+                                 struct ipu7_insys_stream_cfg *cfg);
+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev,
+                                     struct ipu7_insys_buffset *buf,
+                                     unsigned int outputs);
+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys,
+                           const unsigned int stream_handle, u16 send_type);
+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys,
+                            const unsigned int stream_handle,
+                            void *cpu_mapped_buf,
+                            dma_addr_t dma_mapped_buf,
+                            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);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c
new file mode 100644 (file)
index 0000000..4407750
--- /dev/null
@@ -0,0 +1,1034 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-device.h>
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-platform-regs.h"
+#include "ipu7-isys-csi-phy.h"
+
+#define PORT_A         0U
+#define PORT_B         1U
+#define PORT_C         2U
+#define PORT_D         3U
+
+#define N_DATA_IDS     8U
+static DECLARE_BITMAP(data_ids, N_DATA_IDS);
+
+struct ddlcal_counter_ref_s {
+       u16 min_mbps;
+       u16 max_mbps;
+
+       u16 ddlcal_counter_ref;
+};
+
+struct ddlcal_params {
+       u16 min_mbps;
+       u16 max_mbps;
+       u16 oa_lanex_hsrx_cdphy_sel_fast;
+       u16 ddlcal_max_phase;
+       u16 phase_bound;
+       u16 ddlcal_dll_fbk;
+       u16 ddlcal_ddl_coarse_bank;
+       u16 fjump_deskew;
+       u16 min_eye_opening_deskew;
+};
+
+struct i_thssettle_params {
+       u16 min_mbps;
+       u16 max_mbps;
+       u16 i_thssettle;
+};
+
+ /* lane2 for 4l3t, lane1 for 2l2t */
+struct oa_lane_clk_div_params {
+       u16 min_mbps;
+       u16 max_mbps;
+       u16 oa_lane_hsrx_hs_clk_div;
+};
+
+struct cdr_fbk_cap_prog_params {
+       u16 min_mbps;
+       u16 max_mbps;
+       u16 val;
+};
+
+static const struct ddlcal_counter_ref_s table0[] = {
+       { 1500, 1999, 118 },
+       { 2000, 2499, 157 },
+       { 2500, 3499, 196 },
+       { 3500, 4499, 274 },
+       { 4500, 4500, 352 },
+       { }
+};
+
+static const struct ddlcal_params table1[] = {
+       { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 },
+       { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 },
+       { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 },
+       { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 },
+       { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 },
+       { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 },
+       { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 },
+       { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 },
+       { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 },
+       { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 },
+       { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 },
+       { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 },
+       { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 },
+       { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 },
+       { }
+};
+
+static const struct i_thssettle_params table2[] = {
+       { 80, 124, 24 },
+       { 125, 249, 20 },
+       { 250, 499, 16 },
+       { 500, 749, 14 },
+       { 750, 1499, 13 },
+       { 1500, 4500, 12 },
+       { }
+};
+
+static const struct oa_lane_clk_div_params table6[] = {
+       { 80, 159, 0x1 },
+       { 160, 319, 0x2 },
+       { 320, 639, 0x3 },
+       { 640, 1279, 0x4 },
+       { 1280, 2560, 0x5 },
+       { 2561, 4500, 0x6 },
+       { }
+};
+
+static const struct cdr_fbk_cap_prog_params table7[] = {
+       { 80, 919, 0 },
+       { 920, 1029, 1 },
+       { 1030, 1169, 2 },
+       { 1170, 1349, 3 },
+       { 1350, 1589, 4 },
+       { 1590, 1949, 5 },
+       { 1950, 2499, 6 },
+       { }
+};
+
+static void dwc_phy_write(struct ipu7_isys *isys, u32 id, u32 addr, u16 data)
+{
+       void __iomem *isys_base = isys->pdata->base;
+       void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id);
+
+       dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%zx = data 0x%04x",
+               base + addr - isys_base, data);
+       writew(data, base + addr);
+}
+
+static u16 dwc_phy_read(struct ipu7_isys *isys, u32 id, u32 addr)
+{
+       void __iomem *isys_base = isys->pdata->base;
+       void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id);
+       u16 data;
+
+       data = readw(base + addr);
+       dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%zx = data 0x%04x",
+               base + addr - isys_base, data);
+
+       return data;
+}
+
+static void dwc_csi_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data)
+{
+       void __iomem *isys_base = isys->pdata->base;
+       void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id);
+       struct device *dev = &isys->adev->auxdev.dev;
+
+       dev_dbg(dev, "csi write: reg 0x%zx = data 0x%08x",
+               base + addr - isys_base, data);
+       writel(data, base + addr);
+       dev_dbg(dev, "csi read: reg 0x%zx = data 0x%08x",
+               base + addr - isys_base, readl(base + addr));
+}
+
+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;
+       struct device *dev = &isys->adev->auxdev.dev;
+
+       dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x",
+               base + addr - isys_base, data);
+       writel(data, base + addr);
+       dev_dbg(dev, "gpreg read: reg 0x%zx = data 0x%08x",
+               base + addr - isys_base, readl(base + addr));
+}
+
+static u32 dwc_csi_read(struct ipu7_isys *isys, u32 id, u32 addr)
+{
+       void __iomem *isys_base = isys->pdata->base;
+       void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id);
+       u32 data;
+
+       data = readl(base + addr);
+       dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%zx = data 0x%x",
+               base + addr - isys_base, data);
+
+       return data;
+}
+
+static void dwc_phy_write_mask(struct ipu7_isys *isys, u32 id, u32 addr,
+                              u16 val, u8 lo, u8 hi)
+{
+       u32 temp, mask;
+
+       WARN_ON(lo > hi);
+       WARN_ON(hi > 15);
+
+       mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi));
+       temp = dwc_phy_read(isys, id, addr);
+       temp &= ~mask;
+       temp |= (val << lo) & mask;
+       dwc_phy_write(isys, id, addr, temp);
+}
+
+static void dwc_csi_write_mask(struct ipu7_isys *isys, u32 id, u32 addr,
+                              u32 val, u8 hi, u8 lo)
+{
+       u32 temp, mask;
+
+       WARN_ON(lo > hi);
+
+       mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi));
+       temp = dwc_csi_read(isys, id, addr);
+       temp &= ~mask;
+       temp |= (val << lo) & mask;
+       dwc_csi_write(isys, id, addr, temp);
+}
+
+static void ipu7_isys_csi_ctrl_cfg(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_isys *isys = csi2->isys;
+       struct device *dev = &isys->adev->auxdev.dev;
+       u32 id, lanes, phy_mode;
+       u32 val;
+
+       id = csi2->port;
+       lanes = csi2->nlanes;
+       phy_mode = csi2->phy_mode;
+       dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u",
+               id, lanes, phy_mode);
+
+       val = dwc_csi_read(isys, id, VERSION);
+       dev_dbg(dev, "csi-%d controller version = 0x%x", id, val);
+
+       /* num of active data lanes */
+       dwc_csi_write(isys, id, N_LANES, lanes - 1);
+       dwc_csi_write(isys, id, CDPHY_MODE, phy_mode);
+       dwc_csi_write(isys, id, VC_EXTENSION, 0);
+
+       /* only mask PHY_FATAL and PKT_FATAL interrupts */
+       dwc_csi_write(isys, id, INT_MSK_PHY_FATAL, 0xff);
+       dwc_csi_write(isys, id, INT_MSK_PKT_FATAL, 0x3);
+       dwc_csi_write(isys, id, INT_MSK_PHY, 0x0);
+       dwc_csi_write(isys, id, INT_MSK_LINE, 0x0);
+       dwc_csi_write(isys, id, INT_MSK_BNDRY_FRAME_FATAL, 0x0);
+       dwc_csi_write(isys, id, INT_MSK_SEQ_FRAME_FATAL, 0x0);
+       dwc_csi_write(isys, id, INT_MSK_CRC_FRAME_FATAL, 0x0);
+       dwc_csi_write(isys, id, INT_MSK_PLD_CRC_FATAL, 0x0);
+       dwc_csi_write(isys, id, INT_MSK_DATA_ID, 0x0);
+       dwc_csi_write(isys, id, INT_MSK_ECC_CORRECTED, 0x0);
+}
+
+static void ipu7_isys_csi_phy_reset(struct ipu7_isys *isys, u32 id)
+{
+       dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 0);
+       dwc_csi_write(isys, id, DPHY_RSTZ, 0);
+       dwc_csi_write(isys, id, CSI2_RESETN, 0);
+       gpreg_write(isys, id, PHY_RESET, 0);
+       gpreg_write(isys, id, PHY_SHUTDOWN, 0);
+}
+
+/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */
+static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt)
+{
+       struct ipu7_isys *isys = csi2->isys;
+       u32 reg, n;
+       u8 lo, hi;
+       int ret;
+
+       dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n",
+               id, vc, dt);
+
+       dwc_csi_write(isys, id, VC_EXTENSION, 0x0);
+       n = find_first_zero_bit(data_ids, N_DATA_IDS);
+       if (n == N_DATA_IDS)
+               return -ENOSPC;
+
+       ret = test_and_set_bit(n, data_ids);
+       if (ret)
+               return -EBUSY;
+
+       reg = n < 4 ? DATA_IDS_VC_1 : DATA_IDS_VC_2;
+       lo = (n % 4) * 8;
+       hi = lo + 4;
+       dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo);
+
+       reg = n < 4 ? DATA_IDS_1 : DATA_IDS_2;
+       lo = (n % 4) * 8;
+       hi = lo + 5;
+       dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo);
+
+       return 0;
+}
+
+static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id)
+{
+       struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
+       struct device *dev = &csi2->isys->adev->auxdev.dev;
+       struct v4l2_mbus_frame_desc desc;
+       struct v4l2_subdev *ext_sd;
+       struct media_pad *pad;
+       unsigned int i;
+       int ret;
+
+       pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+       if (IS_ERR(pad)) {
+               dev_warn(dev, "can't get remote source pad of %s (%ld)\n",
+                        csi2->asd.sd.name, PTR_ERR(pad));
+               return PTR_ERR(pad);
+       }
+
+       ext_sd = media_entity_to_v4l2_subdev(pad->entity);
+       if (WARN(!ext_sd, "Failed to get subdev for entity %s\n",
+                pad->entity->name))
+               return -ENODEV;
+
+       ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc);
+       if (ret)
+               return ret;
+
+       if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+               dev_warn(dev, "Unsupported frame descriptor type\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < desc.num_entries; i++) {
+               desc_entry = &desc.entry[i];
+               if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) {
+                       ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc,
+                                           desc_entry->bus.csi2.dt);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+#define CDPHY_TIMEOUT 5000000U
+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;
+       struct device *dev = &isys->adev->auxdev.dev;
+       unsigned int i;
+       u32 phy_ready;
+       u32 reg, rext;
+       int ret;
+
+       dev_dbg(dev, "waiting phy ready...\n");
+       ret = readl_poll_timeout(gpreg + PHY_READY, phy_ready,
+                                phy_ready & BIT(0) && phy_ready != ~0U,
+                                100, CDPHY_TIMEOUT);
+       dev_dbg(dev, "phy %u ready = 0x%08x\n", id, readl(gpreg + PHY_READY));
+       dev_dbg(dev, "csi %u PHY_RX = 0x%08x\n", id,
+               dwc_csi_read(isys, id, PHY_RX));
+       dev_dbg(dev, "csi %u PHY_STOPSTATE = 0x%08x\n", id,
+               dwc_csi_read(isys, id, PHY_STOPSTATE));
+       dev_dbg(dev, "csi %u PHY_CAL = 0x%08x\n", id,
+               dwc_csi_read(isys, id, PHY_CAL));
+       for (i = 0; i < 4U; i++) {
+               reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U);
+               dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n",
+                       id, i, dwc_phy_read(isys, id, reg));
+       }
+       dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id,
+               dwc_phy_read(isys, id, PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5));
+       dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id,
+               dwc_phy_read(isys, id, PPI_R_TERMCAL_DEBUG_0));
+       dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id,
+               dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_RB));
+       dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id,
+               dwc_phy_read(isys, id, PPI_R_HSDCOCAL_DEBUG_RB));
+       dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id,
+               dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_VT));
+
+       if (!ret) {
+               if (id) {
+                       dev_dbg(dev, "ignore phy %u rext\n", id);
+                       return 0;
+               }
+
+               rext = dwc_phy_read(isys, id,
+                                   CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & 0xfU;
+               dev_dbg(dev, "phy %u rext value = %u\n", id, rext);
+               isys->phy_rext_cal = (rext ? rext : 5);
+
+               return 0;
+       }
+
+       dev_err(dev, "wait phy ready timeout!\n");
+
+       return ret;
+}
+
+static int lookup_table1(u64 mbps)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(table1); i++) {
+               if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps)
+                       return i;
+       }
+
+       return -ENXIO;
+}
+
+static const u16 deskew_fine_mem[] = {
+       0x0404, 0x040c, 0x0414, 0x041c,
+       0x0423, 0x0429, 0x0430, 0x043a,
+       0x0445, 0x044a, 0x0450, 0x045a,
+       0x0465, 0x0469, 0x0472, 0x047a,
+       0x0485, 0x0489, 0x0490, 0x049a,
+       0x04a4, 0x04ac, 0x04b4, 0x04bc,
+       0x04c4, 0x04cc, 0x04d4, 0x04dc,
+       0x04e4, 0x04ec, 0x04f4, 0x04fc,
+       0x0504, 0x050c, 0x0514, 0x051c,
+       0x0523, 0x0529, 0x0530, 0x053a,
+       0x0545, 0x054a, 0x0550, 0x055a,
+       0x0565, 0x0569, 0x0572, 0x057a,
+       0x0585, 0x0589, 0x0590, 0x059a,
+       0x05a4, 0x05ac, 0x05b4, 0x05bc,
+       0x05c4, 0x05cc, 0x05d4, 0x05dc,
+       0x05e4, 0x05ec, 0x05f4, 0x05fc,
+       0x0604, 0x060c, 0x0614, 0x061c,
+       0x0623, 0x0629, 0x0632, 0x063a,
+       0x0645, 0x064a, 0x0650, 0x065a,
+       0x0665, 0x0669, 0x0672, 0x067a,
+       0x0685, 0x0689, 0x0690, 0x069a,
+       0x06a4, 0x06ac, 0x06b4, 0x06bc,
+       0x06c4, 0x06cc, 0x06d4, 0x06dc,
+       0x06e4, 0x06ec, 0x06f4, 0x06fc,
+       0x0704, 0x070c, 0x0714, 0x071c,
+       0x0723, 0x072a, 0x0730, 0x073a,
+       0x0745, 0x074a, 0x0750, 0x075a,
+       0x0765, 0x0769, 0x0772, 0x077a,
+       0x0785, 0x0789, 0x0790, 0x079a,
+       0x07a4, 0x07ac, 0x07b4, 0x07bc,
+       0x07c4, 0x07cc, 0x07d4, 0x07dc,
+       0x07e4, 0x07ec, 0x07f4, 0x07fc,
+};
+
+static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
+                                 bool aggregation, u64 mbps)
+{
+       u16 hsrxval0 = 0;
+       u16 hsrxval1 = 0;
+       u16 hsrxval2 = 0;
+       int index;
+       u16 reg;
+       u16 val;
+       u32 i;
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, 0, 0, 9);
+       if (mbps > 1500)
+               dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7,
+                                  40, 0, 7);
+       else
+               dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7,
+                                  104, 0, 7);
+
+       dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 80, 0, 7);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_0, 191, 0, 9);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 34, 7, 12);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, 38, 8, 15);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 4, 12, 15);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 2, 10, 11);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 8, 8);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 38, 0, 7);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 9, 9);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_4, 10, 0, 9);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_6, 20, 0, 9);
+       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 19, 0, 6);
+
+       for (i = 0; i < ARRAY_SIZE(table0); i++) {
+               if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) {
+                       dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3,
+                                          table0[i].ddlcal_counter_ref,
+                                          0, 9);
+                       break;
+               }
+       }
+
+       index = lookup_table1(mbps);
+       if (index >= 0) {
+               dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1,
+                                  table1[index].phase_bound, 0, 7);
+               dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5,
+                                  table1[index].ddlcal_dll_fbk, 4, 9);
+               dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5,
+                                  table1[index].ddlcal_ddl_coarse_bank, 0, 3);
+
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8;
+               val = table1[index].oa_lanex_hsrx_cdphy_sel_fast;
+               for (i = 0; i < lanes + 1; i++)
+                       dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+                                          12, 12);
+       }
+
+       reg = CORE_DIG_DLANE_0_RW_LP_0;
+       for (i = 0; i < lanes; i++)
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2,
+                          0, 0, 0);
+       if (!is_ipu7(isys->adev->isp->hw_ver) ||
+           id == PORT_B || id == PORT_C) {
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2,
+                                  1, 0, 0);
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2,
+                                  0, 0, 0);
+       } else {
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2,
+                                  0, 0, 0);
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2,
+                                  1, 0, 0);
+       }
+
+       if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2,
+                                  0, 0, 0);
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2,
+                                  0, 0, 0);
+       }
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 0, 2);
+       dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 3, 5);
+
+       reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12;
+       val = (mbps > 1500) ? 0 : 1;
+       for (i = 0; i < lanes + 1; i++) {
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3);
+       }
+
+       reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13;
+       val = (mbps > 1500) ? 0 : 1;
+       for (i = 0; i < lanes + 1; i++) {
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3);
+       }
+
+       if (!is_ipu7(isys->adev->isp->hw_ver) || id == PORT_B || id == PORT_C)
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9;
+       else
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9;
+
+       for (i = 0; i < ARRAY_SIZE(table6); i++) {
+               if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) {
+                       dwc_phy_write_mask(isys, id, reg,
+                                          table6[i].oa_lane_hsrx_hs_clk_div,
+                                          5, 7);
+                       break;
+               }
+       }
+
+       if (aggregation) {
+               dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1,
+                                  1, 1);
+
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15;
+               dwc_phy_write_mask(isys, id, reg, 3, 3, 4);
+
+               val = (id == PORT_A) ? 3 : 0;
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15;
+               dwc_phy_write_mask(isys, id, reg, val, 3, 4);
+
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15;
+               dwc_phy_write_mask(isys, id, reg, 3, 3, 4);
+       }
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_0, 28, 0, 7);
+       dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_7, 6, 0, 7);
+
+       reg = CORE_DIG_DLANE_0_RW_HS_RX_0;
+       for (i = 0; i < ARRAY_SIZE(table2); i++) {
+               if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) {
+                       u8 j;
+
+                       for (j = 0; j < lanes; j++)
+                               dwc_phy_write_mask(isys, id, reg + (j * 0x400),
+                                                  table2[i].i_thssettle,
+                                                  8, 15);
+                       break;
+               }
+       }
+
+       /* deskew */
+       for (i = 0; i < lanes; i++) {
+               reg = CORE_DIG_DLANE_0_RW_CFG_1;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+                                  ((mbps > 1500) ? 0x1 : 0x2), 2, 3);
+
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_2;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+                                  ((mbps > 2500) ? 0 : 1), 15, 15);
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13);
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12);
+
+               reg = CORE_DIG_DLANE_0_RW_LP_0;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15);
+
+               reg = CORE_DIG_DLANE_0_RW_LP_2;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0);
+
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_1;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7);
+
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_3;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2);
+               index = lookup_table1(mbps);
+               if (index >= 0) {
+                       val = table1[index].fjump_deskew;
+                       dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+                                          3, 8);
+               }
+
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_4;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15);
+
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_5;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7);
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15);
+
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_6;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7);
+               index = lookup_table1(mbps);
+               if (index >= 0) {
+                       val = table1[index].min_eye_opening_deskew;
+                       dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+                                          8, 15);
+               }
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_7;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13);
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15);
+
+               reg = CORE_DIG_DLANE_0_RW_HS_RX_9;
+               index = lookup_table1(mbps);
+               if (index >= 0) {
+                       val = table1[index].ddlcal_max_phase;
+                       dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+                                          val, 0, 7);
+               }
+       }
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_0, 1, 12, 15);
+       dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0);
+
+       for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++)
+               dwc_phy_write_mask(isys, id, CORE_DIG_COMMON_RW_DESKEW_FINE_MEM,
+                                  deskew_fine_mem[i], 0, 15);
+
+       if (mbps > 1500) {
+               hsrxval0 = 4;
+               hsrxval2 = 3;
+       }
+
+       if (mbps > 2500)
+               hsrxval1 = 2;
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9,
+                          hsrxval0, 0, 2);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9,
+                          hsrxval0, 0, 2);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9,
+                          hsrxval0, 0, 2);
+       if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9,
+                                  hsrxval0, 0, 2);
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9,
+                                  hsrxval0, 0, 2);
+       }
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9,
+                          hsrxval1, 3, 4);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9,
+                          hsrxval1, 3, 4);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9,
+                          hsrxval1, 3, 4);
+       if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9,
+                                  hsrxval1, 3, 4);
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9,
+                                  hsrxval1, 3, 4);
+       }
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15,
+                          hsrxval2, 0, 2);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15,
+                          hsrxval2, 0, 2);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15,
+                          hsrxval2, 0, 2);
+       if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15,
+                                  hsrxval2, 0, 2);
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15,
+                                  hsrxval2, 0, 2);
+       }
+
+       /* force and override rext */
+       if (isys->phy_rext_cal && id) {
+               dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8,
+                                  isys->phy_rext_cal, 0, 3);
+               dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
+                                  1, 11, 11);
+       }
+}
+
+static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
+                                 bool aggregation, u64 mbps)
+{
+       u8 trios = 2;
+       u16 coarse_target;
+       u16 deass_thresh;
+       u16 delay_thresh;
+       u16 reset_thresh;
+       u16 cap_prog = 6U;
+       u16 reg;
+       u16 val;
+       u32 i;
+       u32 r;
+
+       if (is_ipu7p5(isys->adev->isp->hw_ver))
+               val = 0x15;
+       else
+               val = 0x155;
+
+       if (is_ipu7(isys->adev->isp->hw_ver))
+               trios = 3;
+
+       dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9);
+       dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7);
+       dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7);
+
+       reg = CORE_DIG_CLANE_0_RW_LP_0;
+       for (i = 0; i < trios; i++)
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);
+
+       val = (mbps > 900U) ? 1U : 0U;
+       for (i = 0; i < trios; i++) {
+               reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0);
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+
+               reg = CORE_DIG_CLANE_0_RW_HS_RX_1;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15);
+
+               reg = CORE_DIG_CLANE_0_RW_HS_RX_5;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15);
+
+               reg = CORE_DIG_CLANE_0_RW_HS_RX_6;
+               dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15);
+       }
+
+       /*
+        * Below 900Msps, always use the same value.
+        * The formula is suitable for data rate 80-3500Msps.
+        * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5
+        */
+       if (mbps >= 80U)
+               coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1;
+       else
+               coarse_target = 56;
+
+       for (i = 0; i < trios; i++) {
+               reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400;
+               dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15);
+       }
+
+       dwc_phy_write_mask(isys, id,
+                          CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0);
+       dwc_phy_write_mask(isys, id,
+                          CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0);
+       dwc_phy_write_mask(isys, id,
+                          CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0);
+
+       if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) {
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2,
+                                  1, 0, 0);
+               dwc_phy_write_mask(isys, id,
+                                  CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2,
+                                  0, 0, 0);
+       }
+
+       for (i = 0; i < trios; i++) {
+               reg = CORE_DIG_RW_TRIO0_0 + i * 0x400;
+               dwc_phy_write_mask(isys, id, reg, 1, 6, 8);
+               dwc_phy_write_mask(isys, id, reg, 1, 3, 5);
+               dwc_phy_write_mask(isys, id, reg, 2, 0, 2);
+       }
+
+       deass_thresh = (u16)div_u64_rem(7 * 1000 * 6, mbps * 5U, &r) + 1;
+       if (r != 0)
+               deass_thresh++;
+
+       reg = CORE_DIG_RW_TRIO0_2;
+       for (i = 0; i < trios; i++)
+               dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+                                  deass_thresh, 0, 7);
+
+       delay_thresh =
+               ((224U - (9U * 7U)) * 1000U) / (5U * mbps) - 7U;
+
+       if (delay_thresh < 1)
+               delay_thresh = 1;
+
+       reg = CORE_DIG_RW_TRIO0_1;
+       for (i = 0; i < trios; i++)
+               dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+                                  delay_thresh, 0, 15);
+
+       reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r);
+       if (!r)
+               reset_thresh--;
+
+       if (reset_thresh < 1)
+               reset_thresh = 1;
+
+       reg = CORE_DIG_RW_TRIO0_0;
+       for (i = 0; i < trios; i++)
+               dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+                                  reset_thresh, 9, 11);
+
+       reg = CORE_DIG_CLANE_0_RW_LP_0;
+       for (i = 0; i < trios; i++)
+               dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15);
+
+       reg = CORE_DIG_CLANE_0_RW_LP_2;
+       for (i = 0; i < trios; i++)
+               dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0);
+
+       reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
+       for (i = 0; i < trios; i++)
+               dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6);
+
+       for (i = 0; i < ARRAY_SIZE(table7); i++) {
+               if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) {
+                       cap_prog = table7[i].val;
+                       break;
+               }
+       }
+
+       for (i = 0; i < (lanes + 1); i++) {
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i;
+               dwc_phy_write_mask(isys, id, reg, 4U, 0, 2);
+               dwc_phy_write_mask(isys, id, reg, 0U, 3, 4);
+
+               reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i;
+               dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12);
+       }
+}
+
+static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
+                               bool aggregation)
+{
+       struct device *dev = &isys->adev->auxdev.dev;
+       u32 phy_mode;
+       s64 link_freq;
+       u64 mbps;
+
+       if (aggregation)
+               link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[0]);
+       else
+               link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[id]);
+
+       if (link_freq < 0) {
+               dev_err(dev, "get link freq failed (%lld)\n", link_freq);
+               return link_freq;
+       }
+
+       mbps = div_u64(link_freq, 500000);
+       dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n",
+               id, lanes, aggregation, mbps);
+
+       dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_10, 48, 0, 7);
+       dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2,
+                          1, 12, 13);
+       dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0,
+                          63, 2, 7);
+       dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_STARTUP_1_1,
+                          563, 0, 11);
+       dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 5, 0, 7);
+       /* bypass the RCAL state (bit6) */
+       if (aggregation && id != PORT_A)
+               dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 0x45,
+                                  0, 7);
+
+       dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_6, 39, 0, 7);
+       dwc_phy_write_mask(isys, id, PPI_CALIBCTRL_RW_COMMON_BG_0, 500, 0, 8);
+       dwc_phy_write_mask(isys, id, PPI_RW_TERMCAL_CFG_0, 38, 0, 6);
+       dwc_phy_write_mask(isys, id, PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF, 800, 0, 10);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF_RANGE, 27, 0, 4);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 47, 0, 8);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 127, 9, 15);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1);
+       dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1);
+       dwc_phy_write_mask(isys, id, PPI_RW_COMMON_CFG, 3, 0, 1);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0,
+                          0, 10, 10);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1,
+                          1, 10, 10);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1,
+                          0, 15, 15);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3,
+                          3, 8, 9);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0,
+                          0, 15, 15);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6,
+                          7, 12, 14);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
+                          0, 8, 10);
+       dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5,
+                          0, 8, 8);
+
+       if (aggregation)
+               phy_mode = isys->csi2[0].phy_mode;
+       else
+               phy_mode = isys->csi2[id].phy_mode;
+
+       if (phy_mode == PHY_MODE_DPHY) {
+               ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps);
+       } else if (phy_mode == PHY_MODE_CPHY) {
+               ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps);
+       } else {
+               dev_err(dev, "unsupported phy mode %d!\n",
+                       isys->csi2[id].phy_mode);
+       }
+
+       return 0;
+}
+
+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_isys *isys = csi2->isys;
+       u32 lanes = csi2->nlanes;
+       bool aggregation = false;
+       u32 id = csi2->port;
+       int ret;
+
+       /* lanes remapping for aggregation (port AB) mode */
+       if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) {
+               aggregation = true;
+               lanes = 2;
+       }
+
+       ipu7_isys_csi_phy_reset(isys, id);
+       gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1);
+       gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2);
+       gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U);
+       gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf);
+       gpreg_write(isys, id, PHY_MODE, csi2->phy_mode);
+
+       /* config PORT_B if aggregation mode */
+       if (aggregation) {
+               ipu7_isys_csi_phy_reset(isys, PORT_B);
+               gpreg_write(isys, PORT_B, PHY_CLK_LANE_CONTROL, 0x0);
+               gpreg_write(isys, PORT_B, PHY_LANE_CONTROL_EN, 0x3);
+               gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0x2);
+               gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0xf);
+               gpreg_write(isys, PORT_B, PHY_MODE, csi2->phy_mode);
+       }
+
+       ipu7_isys_csi_ctrl_cfg(csi2);
+       ipu7_isys_csi_ctrl_dids_config(csi2, id);
+
+       ret = ipu7_isys_phy_config(isys, id, lanes, aggregation);
+       if (ret < 0)
+               return ret;
+
+       gpreg_write(isys, id, PHY_RESET, 1);
+       gpreg_write(isys, id, PHY_SHUTDOWN, 1);
+       dwc_csi_write(isys, id, DPHY_RSTZ, 1);
+       dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 1);
+       dwc_csi_write(isys, id, CSI2_RESETN, 1);
+
+       ret = ipu7_isys_phy_ready(isys, id);
+       if (ret < 0)
+               return ret;
+
+       gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0);
+       gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0);
+
+       /* config PORT_B if aggregation mode */
+       if (aggregation) {
+               ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation);
+               if (ret < 0)
+                       return ret;
+
+               gpreg_write(isys, PORT_B, PHY_RESET, 1);
+               gpreg_write(isys, PORT_B, PHY_SHUTDOWN, 1);
+               dwc_csi_write(isys, PORT_B, DPHY_RSTZ, 1);
+               dwc_csi_write(isys, PORT_B, PHY_SHUTDOWNZ, 1);
+               dwc_csi_write(isys, PORT_B, CSI2_RESETN, 1);
+               ret = ipu7_isys_phy_ready(isys, PORT_B);
+               if (ret < 0)
+                       return ret;
+
+               gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0);
+               gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0);
+       }
+
+       return 0;
+}
+
+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_isys *isys = csi2->isys;
+
+       ipu7_isys_csi_phy_reset(isys, csi2->port);
+       if (!is_ipu7(isys->adev->isp->hw_ver) &&
+           csi2->nlanes > 2U && csi2->port == PORT_A)
+               ipu7_isys_csi_phy_reset(isys, PORT_B);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h
new file mode 100644 (file)
index 0000000..dfdcb61
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_CSI_PHY_H
+#define IPU7_ISYS_CSI_PHY_H
+
+struct ipu7_isys;
+
+#define PHY_MODE_DPHY          0U
+#define PHY_MODE_CPHY          1U
+
+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2);
+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h b/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h
new file mode 100644 (file)
index 0000000..aad52c4
--- /dev/null
@@ -0,0 +1,1197 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_CSI2_REG_H
+#define IPU7_ISYS_CSI2_REG_H
+
+/* IS main regs base */
+#define IS_MAIN_BASE                           0x240000
+#define IS_MAIN_S2B_BASE                       (IS_MAIN_BASE + 0x22000)
+#define IS_MAIN_B2O_BASE                       (IS_MAIN_BASE + 0x26000)
+#define IS_MAIN_ISD_M0_BASE                    (IS_MAIN_BASE + 0x2b000)
+#define IS_MAIN_ISD_M1_BASE                    (IS_MAIN_BASE + 0x2b100)
+#define IS_MAIN_ISD_INT_BASE                   (IS_MAIN_BASE + 0x2b200)
+#define IS_MAIN_GDA_BASE                       (IS_MAIN_BASE + 0x32000)
+#define IS_MAIN_GPREGS_MAIN_BASE               (IS_MAIN_BASE + 0x32500)
+#define IS_MAIN_IRQ_CTRL_BASE                  (IS_MAIN_BASE + 0x32700)
+#define IS_MAIN_PWM_CTRL_BASE                  (IS_MAIN_BASE + 0x32b00)
+
+#define S2B_IRQ_COMMON_0_CTL_STATUS            (IS_MAIN_S2B_BASE + 0x1c)
+#define S2B_IRQ_COMMON_0_CTL_CLEAR             (IS_MAIN_S2B_BASE + 0x20)
+#define S2B_IRQ_COMMON_0_CTL_ENABLE            (IS_MAIN_S2B_BASE + 0x24)
+#define S2B_IID_IRQ_CTL_STATUS(iid)            (IS_MAIN_S2B_BASE + 0x94 + \
+                                                0x100 * (iid))
+
+#define B2O_IRQ_COMMON_0_CTL_STATUS            (IS_MAIN_B2O_BASE + 0x30)
+#define B2O_IRQ_COMMON_0_CTL_CLEAR             (IS_MAIN_B2O_BASE + 0x34)
+#define B2O_IRQ_COMMON_0_CTL_ENABLE            (IS_MAIN_B2O_BASE + 0x38)
+#define B2O_IID_IRQ_CTL_STATUS(oid)            (IS_MAIN_B2O_BASE + 0x3dc + \
+                                                0x200 * (oid))
+
+#define ISD_M0_IRQ_CTL_STATUS                  (IS_MAIN_ISD_M0_BASE + 0x1c)
+#define ISD_M0_IRQ_CTL_CLEAR                   (IS_MAIN_ISD_M0_BASE + 0x20)
+#define ISD_M0_IRQ_CTL_ENABLE                  (IS_MAIN_ISD_M0_BASE + 0x24)
+
+#define ISD_M1_IRQ_CTL_STATUS                  (IS_MAIN_ISD_M1_BASE + 0x1c)
+#define ISD_M1_IRQ_CTL_CLEAR                   (IS_MAIN_ISD_M1_BASE + 0x20)
+#define ISD_M1_IRQ_CTL_ENABLE                  (IS_MAIN_ISD_M1_BASE + 0x24)
+
+#define ISD_INT_IRQ_CTL_STATUS                 (IS_MAIN_ISD_INT_BASE + 0x1c)
+#define ISD_INT_IRQ_CTL_CLEAR                  (IS_MAIN_ISD_INT_BASE + 0x20)
+#define ISD_INT_IRQ_CTL_ENABLE                 (IS_MAIN_ISD_INT_BASE + 0x24)
+
+#define GDA_IRQ_CTL_STATUS                     (IS_MAIN_GDA_BASE + 0x1c)
+#define GDA_IRQ_CTL_CLEAR                      (IS_MAIN_GDA_BASE + 0x20)
+#define GDA_IRQ_CTL_ENABLE                     (IS_MAIN_GDA_BASE + 0x24)
+
+#define IS_MAIN_IRQ_CTL_EDGE                   IS_MAIN_IRQ_CTRL_BASE
+#define IS_MAIN_IRQ_CTL_MASK                   (IS_MAIN_IRQ_CTRL_BASE + 0x4)
+#define IS_MAIN_IRQ_CTL_STATUS                 (IS_MAIN_IRQ_CTRL_BASE + 0x8)
+#define IS_MAIN_IRQ_CTL_CLEAR                  (IS_MAIN_IRQ_CTRL_BASE + 0xc)
+#define IS_MAIN_IRQ_CTL_ENABLE                 (IS_MAIN_IRQ_CTRL_BASE + 0x10)
+#define IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE                (IS_MAIN_IRQ_CTRL_BASE + 0x14)
+
+/* IS IO regs base */
+#define IS_PHY_NUM                             4U
+#define IS_IO_BASE                             0x280000
+
+/* dwc csi cdphy registers */
+#define IS_IO_CDPHY_BASE(i)                    (IS_IO_BASE + 0x10000 * (i))
+#define PPI_STARTUP_RW_COMMON_DPHY_0                   0x1800
+#define PPI_STARTUP_RW_COMMON_DPHY_1                   0x1802
+#define PPI_STARTUP_RW_COMMON_DPHY_2                   0x1804
+#define PPI_STARTUP_RW_COMMON_DPHY_3                   0x1806
+#define PPI_STARTUP_RW_COMMON_DPHY_4                   0x1808
+#define PPI_STARTUP_RW_COMMON_DPHY_5                   0x180a
+#define PPI_STARTUP_RW_COMMON_DPHY_6                   0x180c
+#define PPI_STARTUP_RW_COMMON_DPHY_7                   0x180e
+#define PPI_STARTUP_RW_COMMON_DPHY_8                   0x1810
+#define PPI_STARTUP_RW_COMMON_DPHY_9                   0x1812
+#define PPI_STARTUP_RW_COMMON_DPHY_A                   0x1814
+#define PPI_STARTUP_RW_COMMON_DPHY_10                  0x1820
+#define PPI_STARTUP_RW_COMMON_STARTUP_1_1              0x1822
+#define PPI_STARTUP_RW_COMMON_STARTUP_1_2              0x1824
+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0          0x1840
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1           0x1842
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2           0x1844
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3           0x1846
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4           0x1848
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5           0x184a
+#define PPI_CALIBCTRL_RW_COMMON_BG_0                   0x184c
+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7          0x184e
+#define PPI_CALIBCTRL_RW_ADC_CFG_0                     0x1850
+#define PPI_CALIBCTRL_RW_ADC_CFG_1                     0x1852
+#define PPI_CALIBCTRL_R_ADC_DEBUG                      0x1854
+#define PPI_RW_LPDCOCAL_TOP_OVERRIDE                   0x1c00
+#define PPI_RW_LPDCOCAL_TIMEBASE                       0x1c02
+#define PPI_RW_LPDCOCAL_NREF                           0x1c04
+#define PPI_RW_LPDCOCAL_NREF_RANGE                     0x1c06
+#define PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN               0x1c08
+#define PPI_RW_LPDCOCAL_TWAIT_CONFIG                   0x1c0a
+#define PPI_RW_LPDCOCAL_VT_CONFIG                      0x1c0c
+#define PPI_R_LPDCOCAL_DEBUG_RB                                0x1c0e
+#define PPI_RW_LPDCOCAL_COARSE_CFG                     0x1c10
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_RB                 0x1c12
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB          0x1c14
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB          0x1c16
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB           0x1c18
+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR                0x1c1a
+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR                0x1c1c
+#define PPI_R_LPDCOCAL_DEBUG_VT                                0x1c1e
+#define PPI_RW_LB_TIMEBASE_CONFIG                      0x1c20
+#define PPI_RW_LB_STARTCMU_CONFIG                      0x1c22
+#define PPI_R_LBPULSE_COUNTER_RB                       0x1c24
+#define PPI_R_LB_START_CMU_RB                          0x1c26
+#define PPI_RW_LB_DPHY_BURST_START                     0x1c28
+#define PPI_RW_LB_CPHY_BURST_START                     0x1c2a
+#define PPI_RW_DDLCAL_CFG_0                            0x1c40
+#define PPI_RW_DDLCAL_CFG_1                            0x1c42
+#define PPI_RW_DDLCAL_CFG_2                            0x1c44
+#define PPI_RW_DDLCAL_CFG_3                            0x1c46
+#define PPI_RW_DDLCAL_CFG_4                            0x1c48
+#define PPI_RW_DDLCAL_CFG_5                            0x1c4a
+#define PPI_RW_DDLCAL_CFG_6                            0x1c4c
+#define PPI_RW_DDLCAL_CFG_7                            0x1c4e
+#define PPI_R_DDLCAL_DEBUG_0                           0x1c50
+#define PPI_R_DDLCAL_DEBUG_1                           0x1c52
+#define PPI_RW_PARITY_TEST                             0x1c60
+#define PPI_RW_STARTUP_OVR_0                           0x1c62
+#define PPI_RW_STARTUP_STATE_OVR_1                     0x1c64
+#define PPI_RW_DTB_SELECTOR                            0x1c66
+#define PPI_RW_DPHY_CLK_SPARE                          0x1c6a
+#define PPI_RW_COMMON_CFG                              0x1c6c
+#define PPI_RW_TERMCAL_CFG_0                           0x1c80
+#define PPI_R_TERMCAL_DEBUG_0                          0x1c82
+#define PPI_RW_TERMCAL_CTRL_0                          0x1c84
+#define PPI_RW_OFFSETCAL_CFG_0                         0x1ca0
+#define PPI_R_OFFSETCAL_DEBUG_LANE0                    0x1ca2
+#define PPI_R_OFFSETCAL_DEBUG_LANE1                    0x1ca4
+#define PPI_R_OFFSETCAL_DEBUG_LANE2                    0x1ca6
+#define PPI_R_OFFSETCAL_DEBUG_LANE3                    0x1ca8
+#define PPI_R_OFFSETCAL_DEBUG_LANE4                    0x1caa
+#define PPI_RW_HSDCOCAL_CFG_O                          0x1d00
+#define PPI_RW_HSDCOCAL_CFG_1                          0x1d02
+#define PPI_RW_HSDCOCAL_CFG_2                          0x1d04
+#define PPI_RW_HSDCOCAL_CFG_3                          0x1d06
+#define PPI_RW_HSDCOCAL_CFG_4                          0x1d08
+#define PPI_RW_HSDCOCAL_CFG_5                          0x1d0a
+#define PPI_RW_HSDCOCAL_CFG_6                          0x1d0c
+#define PPI_RW_HSDCOCAL_CFG_7                          0x1d0e
+#define PPI_RW_HSDCOCAL_CFG_8                          0x1d10
+#define PPI_R_HSDCOCAL_DEBUG_RB                                0x1d12
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0      0x2000
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1      0x2002
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2      0x2004
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3      0x2006
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4      0x2008
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5      0x200a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6      0x200c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7      0x200e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8      0x2010
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9       0x2012
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10      0x2014
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11      0x2016
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12      0x2018
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13      0x201a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14      0x201c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15      0x201e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0      0x2020
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1      0x2022
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2      0x2024
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3      0x2026
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4      0x2028
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5      0x202a
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6      0x202c
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7      0x202e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8      0x2030
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9      0x2032
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10      0x2034
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11      0x2036
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12      0x2038
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13      0x203a
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14      0x203c
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15      0x203e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0          0x2040
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1          0x2042
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2          0x2044
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3          0x2046
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4          0x2048
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5          0x204a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6          0x204c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7          0x204e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8          0x2050
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9          0x2052
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10         0x2054
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11         0x2056
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12         0x2058
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13         0x205a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14         0x205c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15         0x205e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0          0x2060
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1          0x2062
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2          0x2064
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3          0x2066
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4          0x2068
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5          0x206a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6          0x206c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7          0x206e
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8           0x2070
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9           0x2072
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10          0x2074
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11          0x2076
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12          0x2078
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13          0x207a
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14          0x207c
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15          0x207e
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0           0x2080
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1           0x2082
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2           0x2084
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3           0x2086
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4           0x2088
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0       0x20a0
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1       0x20a2
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2      0x20a4
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3      0x20a6
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4       0x20a8
+#define CORE_DIG_RW_TRIO0_0                            0x2100
+#define CORE_DIG_RW_TRIO0_1                            0x2102
+#define CORE_DIG_RW_TRIO0_2                            0x2104
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0      0x2400
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1      0x2402
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2      0x2404
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3      0x2406
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4      0x2408
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5      0x240a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6      0x240c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7      0x240e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8      0x2410
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9       0x2412
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10      0x2414
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11      0x2416
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12      0x2418
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13      0x241a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14      0x241c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15      0x241e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0      0x2420
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1      0x2422
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2      0x2424
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3      0x2426
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4      0x2428
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5      0x242a
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6      0x242c
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7      0x242e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8      0x2430
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9      0x2432
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10      0x2434
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11      0x2436
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12      0x2438
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13      0x243a
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14      0x243c
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15      0x243e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0          0x2440
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1          0x2442
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2          0x2444
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3          0x2446
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4          0x2448
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5          0x244a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6          0x244c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7          0x244e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8          0x2450
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9          0x2452
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10         0x2454
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11         0x2456
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12         0x2458
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13         0x245a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14         0x245c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15         0x245e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0          0x2460
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1          0x2462
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2          0x2464
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3          0x2466
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4          0x2468
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5          0x246a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6          0x246c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7          0x246e
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8           0x2470
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9           0x2472
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10          0x2474
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11          0x2476
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12          0x2478
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13          0x247a
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14          0x247c
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15          0x247e
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0           0x2480
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1           0x2482
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2           0x2484
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3           0x2486
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4           0x2488
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0       0x24a0
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1       0x24a2
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2      0x24a4
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3      0x24a6
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4       0x24a8
+#define CORE_DIG_RW_TRIO1_0                            0x2500
+#define CORE_DIG_RW_TRIO1_1                            0x2502
+#define CORE_DIG_RW_TRIO1_2                            0x2504
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0      0x2800
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1      0x2802
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2      0x2804
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3      0x2806
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4      0x2808
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5      0x280a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6      0x280c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7      0x280e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8      0x2810
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9       0x2812
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10      0x2814
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11      0x2816
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12      0x2818
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13      0x281a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14      0x281c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15      0x281e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0      0x2820
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1      0x2822
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2      0x2824
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3      0x2826
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4      0x2828
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5      0x282a
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6      0x282c
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7      0x282e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8      0x2830
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9      0x2832
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10      0x2834
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11      0x2836
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12      0x2838
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13      0x283a
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14      0x283c
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15      0x283e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0          0x2840
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1          0x2842
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2          0x2844
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3          0x2846
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4          0x2848
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5          0x284a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6          0x284c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7          0x284e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8          0x2850
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9          0x2852
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10         0x2854
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11         0x2856
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12         0x2858
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13         0x285a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14         0x285c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15         0x285e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0          0x2860
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1          0x2862
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2          0x2864
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3          0x2866
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4          0x2868
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5          0x286a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6          0x286c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7          0x286e
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8           0x2870
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9           0x2872
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10          0x2874
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11          0x2876
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12          0x2878
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13          0x287a
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14          0x287c
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15          0x287e
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0           0x2880
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1           0x2882
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2           0x2884
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3           0x2886
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4           0x2888
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0       0x28a0
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1       0x28a2
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2      0x28a4
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3      0x28a6
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4       0x28a8
+#define CORE_DIG_RW_TRIO2_0                            0x2900
+#define CORE_DIG_RW_TRIO2_1                            0x2902
+#define CORE_DIG_RW_TRIO2_2                            0x2904
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0      0x2c00
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1      0x2c02
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2      0x2c04
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3      0x2c06
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4      0x2c08
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5      0x2c0a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6      0x2c0c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7      0x2c0e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8      0x2c10
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9       0x2c12
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10      0x2c14
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11      0x2c16
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12      0x2c18
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13      0x2c1a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14      0x2c1c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15      0x2c1e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0          0x2c40
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1          0x2c42
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2          0x2c44
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3          0x2c46
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4          0x2c48
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5          0x2c4a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6          0x2c4c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7          0x2c4e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8          0x2c50
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9          0x2c52
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10         0x2c54
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11         0x2c56
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12         0x2c58
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13         0x2c5a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14         0x2c5c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15         0x2c5e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0          0x2c60
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1          0x2c62
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2          0x2c64
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3          0x2c66
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4          0x2c68
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5          0x2c6a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6          0x2c6c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7          0x2c6e
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8           0x2c70
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9           0x2c72
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10          0x2c74
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11          0x2c76
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12          0x2c78
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13          0x2c7a
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14          0x2c7c
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15          0x2c7e
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0           0x2c80
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1           0x2c82
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2           0x2c84
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3           0x2c86
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4           0x2c88
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0          0x3040
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1          0x3042
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2          0x3044
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3          0x3046
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4          0x3048
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5          0x304a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6          0x304c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7          0x304e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8          0x3050
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9          0x3052
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10         0x3054
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11         0x3056
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12         0x3058
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13         0x305a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14         0x305c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15         0x305e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0          0x3060
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1          0x3062
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2          0x3064
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3          0x3066
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4          0x3068
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5          0x306a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6          0x306c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7          0x306e
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8           0x3070
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9           0x3072
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10          0x3074
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11          0x3076
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12          0x3078
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13          0x307a
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14          0x307c
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15          0x307e
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0           0x3080
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1           0x3082
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2           0x3084
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3           0x3086
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4           0x3088
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0                0x3400
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1                0x3402
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2         0x3404
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0          0x3800
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1          0x3802
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2          0x3804
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3          0x3806
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4          0x3808
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5          0x380a
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6          0x380c
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7          0x380e
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8          0x3810
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9          0x3812
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10         0x3814
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11          0x3816
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12          0x3818
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13          0x381a
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14          0x381c
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15          0x381e
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0           0x3820
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1           0x3822
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2           0x3824
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3           0x3826
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4           0x3828
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0             0x3840
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1             0x3842
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2             0x3844
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3             0x3846
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4             0x3848
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5             0x384a
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6             0x384c
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7             0x384e
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8             0x3850
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9             0x3852
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10            0x3854
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11            0x3856
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12             0x3858
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13             0x385a
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14             0x385c
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15             0x385e
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0              0x3860
+#define CORE_DIG_RW_COMMON_0                           0x3880
+#define CORE_DIG_RW_COMMON_1                           0x3882
+#define CORE_DIG_RW_COMMON_2                           0x3884
+#define CORE_DIG_RW_COMMON_3                           0x3886
+#define CORE_DIG_RW_COMMON_4                           0x3888
+#define CORE_DIG_RW_COMMON_5                           0x388a
+#define CORE_DIG_RW_COMMON_6                           0x388c
+#define CORE_DIG_RW_COMMON_7                           0x388e
+#define CORE_DIG_RW_COMMON_8                           0x3890
+#define CORE_DIG_RW_COMMON_9                           0x3892
+#define CORE_DIG_RW_COMMON_10                          0x3894
+#define CORE_DIG_RW_COMMON_11                          0x3896
+#define CORE_DIG_RW_COMMON_12                          0x3898
+#define CORE_DIG_RW_COMMON_13                          0x389a
+#define CORE_DIG_RW_COMMON_14                          0x389c
+#define CORE_DIG_RW_COMMON_15                          0x389e
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0           0x39e0
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1           0x39e2
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2           0x39e4
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3           0x39e6
+#define CORE_DIG_COMMON_RW_DESKEW_FINE_MEM             0x3fe0
+#define CORE_DIG_COMMON_R_DESKEW_FINE_MEM              0x3fe2
+#define PPI_RW_DPHY_LANE0_LBERT_0                      0x4000
+#define PPI_RW_DPHY_LANE0_LBERT_1                      0x4002
+#define PPI_R_DPHY_LANE0_LBERT_0                       0x4004
+#define PPI_R_DPHY_LANE0_LBERT_1                       0x4006
+#define PPI_RW_DPHY_LANE0_SPARE                                0x4008
+#define PPI_RW_DPHY_LANE1_LBERT_0                      0x4400
+#define PPI_RW_DPHY_LANE1_LBERT_1                      0x4402
+#define PPI_R_DPHY_LANE1_LBERT_0                       0x4404
+#define PPI_R_DPHY_LANE1_LBERT_1                       0x4406
+#define PPI_RW_DPHY_LANE1_SPARE                                0x4408
+#define PPI_RW_DPHY_LANE2_LBERT_0                      0x4800
+#define PPI_RW_DPHY_LANE2_LBERT_1                      0x4802
+#define PPI_R_DPHY_LANE2_LBERT_0                       0x4804
+#define PPI_R_DPHY_LANE2_LBERT_1                       0x4806
+#define PPI_RW_DPHY_LANE2_SPARE                                0x4808
+#define PPI_RW_DPHY_LANE3_LBERT_0                      0x4c00
+#define PPI_RW_DPHY_LANE3_LBERT_1                      0x4c02
+#define PPI_R_DPHY_LANE3_LBERT_0                       0x4c04
+#define PPI_R_DPHY_LANE3_LBERT_1                       0x4c06
+#define PPI_RW_DPHY_LANE3_SPARE                                0x4c08
+#define CORE_DIG_DLANE_0_RW_CFG_0                      0x6000
+#define CORE_DIG_DLANE_0_RW_CFG_1                      0x6002
+#define CORE_DIG_DLANE_0_RW_CFG_2                      0x6004
+#define CORE_DIG_DLANE_0_RW_LP_0                       0x6080
+#define CORE_DIG_DLANE_0_RW_LP_1                       0x6082
+#define CORE_DIG_DLANE_0_RW_LP_2                       0x6084
+#define CORE_DIG_DLANE_0_R_LP_0                                0x60a0
+#define CORE_DIG_DLANE_0_R_LP_1                                0x60a2
+#define CORE_DIG_DLANE_0_R_HS_TX_0                     0x60e0
+#define CORE_DIG_DLANE_0_RW_HS_RX_0                    0x6100
+#define CORE_DIG_DLANE_0_RW_HS_RX_1                    0x6102
+#define CORE_DIG_DLANE_0_RW_HS_RX_2                    0x6104
+#define CORE_DIG_DLANE_0_RW_HS_RX_3                    0x6106
+#define CORE_DIG_DLANE_0_RW_HS_RX_4                    0x6108
+#define CORE_DIG_DLANE_0_RW_HS_RX_5                    0x610a
+#define CORE_DIG_DLANE_0_RW_HS_RX_6                    0x610c
+#define CORE_DIG_DLANE_0_RW_HS_RX_7                    0x610e
+#define CORE_DIG_DLANE_0_RW_HS_RX_8                    0x6110
+#define CORE_DIG_DLANE_0_RW_HS_RX_9                    0x6112
+#define CORE_DIG_DLANE_0_R_HS_RX_0                     0x6120
+#define CORE_DIG_DLANE_0_R_HS_RX_1                     0x6122
+#define CORE_DIG_DLANE_0_R_HS_RX_2                     0x6124
+#define CORE_DIG_DLANE_0_R_HS_RX_3                     0x6126
+#define CORE_DIG_DLANE_0_R_HS_RX_4                     0x6128
+#define CORE_DIG_DLANE_0_RW_HS_TX_0                    0x6200
+#define CORE_DIG_DLANE_0_RW_HS_TX_1                    0x6202
+#define CORE_DIG_DLANE_0_RW_HS_TX_2                    0x6204
+#define CORE_DIG_DLANE_0_RW_HS_TX_3                    0x6206
+#define CORE_DIG_DLANE_0_RW_HS_TX_4                    0x6208
+#define CORE_DIG_DLANE_0_RW_HS_TX_5                    0x620a
+#define CORE_DIG_DLANE_0_RW_HS_TX_6                    0x620c
+#define CORE_DIG_DLANE_0_RW_HS_TX_7                    0x620e
+#define CORE_DIG_DLANE_0_RW_HS_TX_8                    0x6210
+#define CORE_DIG_DLANE_0_RW_HS_TX_9                    0x6212
+#define CORE_DIG_DLANE_0_RW_HS_TX_10                   0x6214
+#define CORE_DIG_DLANE_0_RW_HS_TX_11                   0x6216
+#define CORE_DIG_DLANE_0_RW_HS_TX_12                   0x6218
+#define CORE_DIG_DLANE_1_RW_CFG_0                      0x6400
+#define CORE_DIG_DLANE_1_RW_CFG_1                      0x6402
+#define CORE_DIG_DLANE_1_RW_CFG_2                      0x6404
+#define CORE_DIG_DLANE_1_RW_LP_0                       0x6480
+#define CORE_DIG_DLANE_1_RW_LP_1                       0x6482
+#define CORE_DIG_DLANE_1_RW_LP_2                       0x6484
+#define CORE_DIG_DLANE_1_R_LP_0                                0x64a0
+#define CORE_DIG_DLANE_1_R_LP_1                                0x64a2
+#define CORE_DIG_DLANE_1_R_HS_TX_0                     0x64e0
+#define CORE_DIG_DLANE_1_RW_HS_RX_0                    0x6500
+#define CORE_DIG_DLANE_1_RW_HS_RX_1                    0x6502
+#define CORE_DIG_DLANE_1_RW_HS_RX_2                    0x6504
+#define CORE_DIG_DLANE_1_RW_HS_RX_3                    0x6506
+#define CORE_DIG_DLANE_1_RW_HS_RX_4                    0x6508
+#define CORE_DIG_DLANE_1_RW_HS_RX_5                    0x650a
+#define CORE_DIG_DLANE_1_RW_HS_RX_6                    0x650c
+#define CORE_DIG_DLANE_1_RW_HS_RX_7                    0x650e
+#define CORE_DIG_DLANE_1_RW_HS_RX_8                    0x6510
+#define CORE_DIG_DLANE_1_RW_HS_RX_9                    0x6512
+#define CORE_DIG_DLANE_1_R_HS_RX_0                     0x6520
+#define CORE_DIG_DLANE_1_R_HS_RX_1                     0x6522
+#define CORE_DIG_DLANE_1_R_HS_RX_2                     0x6524
+#define CORE_DIG_DLANE_1_R_HS_RX_3                     0x6526
+#define CORE_DIG_DLANE_1_R_HS_RX_4                     0x6528
+#define CORE_DIG_DLANE_1_RW_HS_TX_0                    0x6600
+#define CORE_DIG_DLANE_1_RW_HS_TX_1                    0x6602
+#define CORE_DIG_DLANE_1_RW_HS_TX_2                    0x6604
+#define CORE_DIG_DLANE_1_RW_HS_TX_3                    0x6606
+#define CORE_DIG_DLANE_1_RW_HS_TX_4                    0x6608
+#define CORE_DIG_DLANE_1_RW_HS_TX_5                    0x660a
+#define CORE_DIG_DLANE_1_RW_HS_TX_6                    0x660c
+#define CORE_DIG_DLANE_1_RW_HS_TX_7                    0x660e
+#define CORE_DIG_DLANE_1_RW_HS_TX_8                    0x6610
+#define CORE_DIG_DLANE_1_RW_HS_TX_9                    0x6612
+#define CORE_DIG_DLANE_1_RW_HS_TX_10                   0x6614
+#define CORE_DIG_DLANE_1_RW_HS_TX_11                   0x6616
+#define CORE_DIG_DLANE_1_RW_HS_TX_12                   0x6618
+#define CORE_DIG_DLANE_2_RW_CFG_0                      0x6800
+#define CORE_DIG_DLANE_2_RW_CFG_1                      0x6802
+#define CORE_DIG_DLANE_2_RW_CFG_2                      0x6804
+#define CORE_DIG_DLANE_2_RW_LP_0                       0x6880
+#define CORE_DIG_DLANE_2_RW_LP_1                       0x6882
+#define CORE_DIG_DLANE_2_RW_LP_2                       0x6884
+#define CORE_DIG_DLANE_2_R_LP_0                                0x68a0
+#define CORE_DIG_DLANE_2_R_LP_1                                0x68a2
+#define CORE_DIG_DLANE_2_R_HS_TX_0                     0x68e0
+#define CORE_DIG_DLANE_2_RW_HS_RX_0                    0x6900
+#define CORE_DIG_DLANE_2_RW_HS_RX_1                    0x6902
+#define CORE_DIG_DLANE_2_RW_HS_RX_2                    0x6904
+#define CORE_DIG_DLANE_2_RW_HS_RX_3                    0x6906
+#define CORE_DIG_DLANE_2_RW_HS_RX_4                    0x6908
+#define CORE_DIG_DLANE_2_RW_HS_RX_5                    0x690a
+#define CORE_DIG_DLANE_2_RW_HS_RX_6                    0x690c
+#define CORE_DIG_DLANE_2_RW_HS_RX_7                    0x690e
+#define CORE_DIG_DLANE_2_RW_HS_RX_8                    0x6910
+#define CORE_DIG_DLANE_2_RW_HS_RX_9                    0x6912
+#define CORE_DIG_DLANE_2_R_HS_RX_0                     0x6920
+#define CORE_DIG_DLANE_2_R_HS_RX_1                     0x6922
+#define CORE_DIG_DLANE_2_R_HS_RX_2                     0x6924
+#define CORE_DIG_DLANE_2_R_HS_RX_3                     0x6926
+#define CORE_DIG_DLANE_2_R_HS_RX_4                     0x6928
+#define CORE_DIG_DLANE_2_RW_HS_TX_0                    0x6a00
+#define CORE_DIG_DLANE_2_RW_HS_TX_1                    0x6a02
+#define CORE_DIG_DLANE_2_RW_HS_TX_2                    0x6a04
+#define CORE_DIG_DLANE_2_RW_HS_TX_3                    0x6a06
+#define CORE_DIG_DLANE_2_RW_HS_TX_4                    0x6a08
+#define CORE_DIG_DLANE_2_RW_HS_TX_5                    0x6a0a
+#define CORE_DIG_DLANE_2_RW_HS_TX_6                    0x6a0c
+#define CORE_DIG_DLANE_2_RW_HS_TX_7                    0x6a0e
+#define CORE_DIG_DLANE_2_RW_HS_TX_8                    0x6a10
+#define CORE_DIG_DLANE_2_RW_HS_TX_9                    0x6a12
+#define CORE_DIG_DLANE_2_RW_HS_TX_10                   0x6a14
+#define CORE_DIG_DLANE_2_RW_HS_TX_11                   0x6a16
+#define CORE_DIG_DLANE_2_RW_HS_TX_12                   0x6a18
+#define CORE_DIG_DLANE_3_RW_CFG_0                      0x6c00
+#define CORE_DIG_DLANE_3_RW_CFG_1                      0x6c02
+#define CORE_DIG_DLANE_3_RW_CFG_2                      0x6c04
+#define CORE_DIG_DLANE_3_RW_LP_0                       0x6c80
+#define CORE_DIG_DLANE_3_RW_LP_1                       0x6c82
+#define CORE_DIG_DLANE_3_RW_LP_2                       0x6c84
+#define CORE_DIG_DLANE_3_R_LP_0                                0x6ca0
+#define CORE_DIG_DLANE_3_R_LP_1                                0x6ca2
+#define CORE_DIG_DLANE_3_R_HS_TX_0                     0x6ce0
+#define CORE_DIG_DLANE_3_RW_HS_RX_0                    0x6d00
+#define CORE_DIG_DLANE_3_RW_HS_RX_1                    0x6d02
+#define CORE_DIG_DLANE_3_RW_HS_RX_2                    0x6d04
+#define CORE_DIG_DLANE_3_RW_HS_RX_3                    0x6d06
+#define CORE_DIG_DLANE_3_RW_HS_RX_4                    0x6d08
+#define CORE_DIG_DLANE_3_RW_HS_RX_5                    0x6d0a
+#define CORE_DIG_DLANE_3_RW_HS_RX_6                    0x6d0c
+#define CORE_DIG_DLANE_3_RW_HS_RX_7                    0x6d0e
+#define CORE_DIG_DLANE_3_RW_HS_RX_8                    0x6d10
+#define CORE_DIG_DLANE_3_RW_HS_RX_9                    0x6d12
+#define CORE_DIG_DLANE_3_R_HS_RX_0                     0x6d20
+#define CORE_DIG_DLANE_3_R_HS_RX_1                     0x6d22
+#define CORE_DIG_DLANE_3_R_HS_RX_2                     0x6d24
+#define CORE_DIG_DLANE_3_R_HS_RX_3                     0x6d26
+#define CORE_DIG_DLANE_3_R_HS_RX_4                     0x6d28
+#define CORE_DIG_DLANE_3_RW_HS_TX_0                    0x6e00
+#define CORE_DIG_DLANE_3_RW_HS_TX_1                    0x6e02
+#define CORE_DIG_DLANE_3_RW_HS_TX_2                    0x6e04
+#define CORE_DIG_DLANE_3_RW_HS_TX_3                    0x6e06
+#define CORE_DIG_DLANE_3_RW_HS_TX_4                    0x6e08
+#define CORE_DIG_DLANE_3_RW_HS_TX_5                    0x6e0a
+#define CORE_DIG_DLANE_3_RW_HS_TX_6                    0x6e0c
+#define CORE_DIG_DLANE_3_RW_HS_TX_7                    0x6e0e
+#define CORE_DIG_DLANE_3_RW_HS_TX_8                    0x6e10
+#define CORE_DIG_DLANE_3_RW_HS_TX_9                    0x6e12
+#define CORE_DIG_DLANE_3_RW_HS_TX_10                   0x6e14
+#define CORE_DIG_DLANE_3_RW_HS_TX_11                   0x6e16
+#define CORE_DIG_DLANE_3_RW_HS_TX_12                   0x6e18
+#define CORE_DIG_DLANE_CLK_RW_CFG_0                    0x7000
+#define CORE_DIG_DLANE_CLK_RW_CFG_1                    0x7002
+#define CORE_DIG_DLANE_CLK_RW_CFG_2                    0x7004
+#define CORE_DIG_DLANE_CLK_RW_LP_0                     0x7080
+#define CORE_DIG_DLANE_CLK_RW_LP_1                     0x7082
+#define CORE_DIG_DLANE_CLK_RW_LP_2                     0x7084
+#define CORE_DIG_DLANE_CLK_R_LP_0                      0x70a0
+#define CORE_DIG_DLANE_CLK_R_LP_1                      0x70a2
+#define CORE_DIG_DLANE_CLK_R_HS_TX_0                   0x70e0
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_0                  0x7100
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_1                  0x7102
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_2                  0x7104
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_3                  0x7106
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_4                  0x7108
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_5                  0x710a
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_6                  0x710c
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_7                  0x710e
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_8                  0x7110
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_9                  0x7112
+#define CORE_DIG_DLANE_CLK_R_HS_RX_0                   0x7120
+#define CORE_DIG_DLANE_CLK_R_HS_RX_1                   0x7122
+#define CORE_DIG_DLANE_CLK_R_HS_RX_2                   0x7124
+#define CORE_DIG_DLANE_CLK_R_HS_RX_3                   0x7126
+#define CORE_DIG_DLANE_CLK_R_HS_RX_4                   0x7128
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_0                  0x7200
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_1                  0x7202
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_2                  0x7204
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_3                  0x7206
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_4                  0x7208
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_5                  0x720a
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_6                  0x720c
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_7                  0x720e
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_8                  0x7210
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_9                  0x7212
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_10                 0x7214
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_11                 0x7216
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_12                 0x7218
+#define PPI_RW_CPHY_TRIO0_LBERT_0                      0x8000
+#define PPI_RW_CPHY_TRIO0_LBERT_1                      0x8002
+#define PPI_R_CPHY_TRIO0_LBERT_0                       0x8004
+#define PPI_R_CPHY_TRIO0_LBERT_1                       0x8006
+#define PPI_RW_CPHY_TRIO0_SPARE                                0x8008
+#define PPI_RW_CPHY_TRIO1_LBERT_0                      0x8400
+#define PPI_RW_CPHY_TRIO1_LBERT_1                      0x8402
+#define PPI_R_CPHY_TRIO1_LBERT_0                       0x8404
+#define PPI_R_CPHY_TRIO1_LBERT_1                       0x8406
+#define PPI_RW_CPHY_TRIO1_SPARE                                0x8408
+#define PPI_RW_CPHY_TRIO2_LBERT_0                      0x8800
+#define PPI_RW_CPHY_TRIO2_LBERT_1                      0x8802
+#define PPI_R_CPHY_TRIO2_LBERT_0                       0x8804
+#define PPI_R_CPHY_TRIO2_LBERT_1                       0x8806
+#define PPI_RW_CPHY_TRIO2_SPARE                                0x8808
+#define CORE_DIG_CLANE_0_RW_CFG_0                      0xa000
+#define CORE_DIG_CLANE_0_RW_CFG_2                      0xa004
+#define CORE_DIG_CLANE_0_RW_LP_0                       0xa080
+#define CORE_DIG_CLANE_0_RW_LP_1                       0xa082
+#define CORE_DIG_CLANE_0_RW_LP_2                       0xa084
+#define CORE_DIG_CLANE_0_R_LP_0                                0xa0a0
+#define CORE_DIG_CLANE_0_R_LP_1                                0xa0a2
+#define CORE_DIG_CLANE_0_RW_HS_RX_0                    0xa100
+#define CORE_DIG_CLANE_0_RW_HS_RX_1                    0xa102
+#define CORE_DIG_CLANE_0_RW_HS_RX_2                    0xa104
+#define CORE_DIG_CLANE_0_RW_HS_RX_3                    0xa106
+#define CORE_DIG_CLANE_0_RW_HS_RX_4                    0xa108
+#define CORE_DIG_CLANE_0_RW_HS_RX_5                    0xa10a
+#define CORE_DIG_CLANE_0_RW_HS_RX_6                    0xa10c
+#define CORE_DIG_CLANE_0_R_RX_0                                0xa120
+#define CORE_DIG_CLANE_0_R_RX_1                                0xa122
+#define CORE_DIG_CLANE_0_R_TX_0                                0xa124
+#define CORE_DIG_CLANE_0_R_RX_2                                0xa126
+#define CORE_DIG_CLANE_0_R_RX_3                                0xa128
+#define CORE_DIG_CLANE_0_RW_HS_TX_0                    0xa200
+#define CORE_DIG_CLANE_0_RW_HS_TX_1                    0xa202
+#define CORE_DIG_CLANE_0_RW_HS_TX_2                    0xa204
+#define CORE_DIG_CLANE_0_RW_HS_TX_3                    0xa206
+#define CORE_DIG_CLANE_0_RW_HS_TX_4                    0xa208
+#define CORE_DIG_CLANE_0_RW_HS_TX_5                    0xa20a
+#define CORE_DIG_CLANE_0_RW_HS_TX_6                    0xa20c
+#define CORE_DIG_CLANE_0_RW_HS_TX_7                    0xa20e
+#define CORE_DIG_CLANE_0_RW_HS_TX_8                    0xa210
+#define CORE_DIG_CLANE_0_RW_HS_TX_9                    0xa212
+#define CORE_DIG_CLANE_0_RW_HS_TX_10                   0xa214
+#define CORE_DIG_CLANE_0_RW_HS_TX_11                   0xa216
+#define CORE_DIG_CLANE_0_RW_HS_TX_12                   0xa218
+#define CORE_DIG_CLANE_0_RW_HS_TX_13                   0xa21a
+#define CORE_DIG_CLANE_1_RW_CFG_0                      0xa400
+#define CORE_DIG_CLANE_1_RW_CFG_2                      0xa404
+#define CORE_DIG_CLANE_1_RW_LP_0                       0xa480
+#define CORE_DIG_CLANE_1_RW_LP_1                       0xa482
+#define CORE_DIG_CLANE_1_RW_LP_2                       0xa484
+#define CORE_DIG_CLANE_1_R_LP_0                                0xa4a0
+#define CORE_DIG_CLANE_1_R_LP_1                                0xa4a2
+#define CORE_DIG_CLANE_1_RW_HS_RX_0                    0xa500
+#define CORE_DIG_CLANE_1_RW_HS_RX_1                    0xa502
+#define CORE_DIG_CLANE_1_RW_HS_RX_2                    0xa504
+#define CORE_DIG_CLANE_1_RW_HS_RX_3                    0xa506
+#define CORE_DIG_CLANE_1_RW_HS_RX_4                    0xa508
+#define CORE_DIG_CLANE_1_RW_HS_RX_5                    0xa50a
+#define CORE_DIG_CLANE_1_RW_HS_RX_6                    0xa50c
+#define CORE_DIG_CLANE_1_R_RX_0                                0xa520
+#define CORE_DIG_CLANE_1_R_RX_1                                0xa522
+#define CORE_DIG_CLANE_1_R_TX_0                                0xa524
+#define CORE_DIG_CLANE_1_R_RX_2                                0xa526
+#define CORE_DIG_CLANE_1_R_RX_3                                0xa528
+#define CORE_DIG_CLANE_1_RW_HS_TX_0                    0xa600
+#define CORE_DIG_CLANE_1_RW_HS_TX_1                    0xa602
+#define CORE_DIG_CLANE_1_RW_HS_TX_2                    0xa604
+#define CORE_DIG_CLANE_1_RW_HS_TX_3                    0xa606
+#define CORE_DIG_CLANE_1_RW_HS_TX_4                    0xa608
+#define CORE_DIG_CLANE_1_RW_HS_TX_5                    0xa60a
+#define CORE_DIG_CLANE_1_RW_HS_TX_6                    0xa60c
+#define CORE_DIG_CLANE_1_RW_HS_TX_7                    0xa60e
+#define CORE_DIG_CLANE_1_RW_HS_TX_8                    0xa610
+#define CORE_DIG_CLANE_1_RW_HS_TX_9                    0xa612
+#define CORE_DIG_CLANE_1_RW_HS_TX_10                   0xa614
+#define CORE_DIG_CLANE_1_RW_HS_TX_11                   0xa616
+#define CORE_DIG_CLANE_1_RW_HS_TX_12                   0xa618
+#define CORE_DIG_CLANE_1_RW_HS_TX_13                   0xa61a
+#define CORE_DIG_CLANE_2_RW_CFG_0                      0xa800
+#define CORE_DIG_CLANE_2_RW_CFG_2                      0xa804
+#define CORE_DIG_CLANE_2_RW_LP_0                       0xa880
+#define CORE_DIG_CLANE_2_RW_LP_1                       0xa882
+#define CORE_DIG_CLANE_2_RW_LP_2                       0xa884
+#define CORE_DIG_CLANE_2_R_LP_0                                0xa8a0
+#define CORE_DIG_CLANE_2_R_LP_1                                0xa8a2
+#define CORE_DIG_CLANE_2_RW_HS_RX_0                    0xa900
+#define CORE_DIG_CLANE_2_RW_HS_RX_1                    0xa902
+#define CORE_DIG_CLANE_2_RW_HS_RX_2                    0xa904
+#define CORE_DIG_CLANE_2_RW_HS_RX_3                    0xa906
+#define CORE_DIG_CLANE_2_RW_HS_RX_4                    0xa908
+#define CORE_DIG_CLANE_2_RW_HS_RX_5                    0xa90a
+#define CORE_DIG_CLANE_2_RW_HS_RX_6                    0xa90c
+#define CORE_DIG_CLANE_2_R_RX_0                                0xa920
+#define CORE_DIG_CLANE_2_R_RX_1                                0xa922
+#define CORE_DIG_CLANE_2_R_TX_0                                0xa924
+#define CORE_DIG_CLANE_2_R_RX_2                                0xa926
+#define CORE_DIG_CLANE_2_R_RX_3                                0xa928
+#define CORE_DIG_CLANE_2_RW_HS_TX_0                    0xaa00
+#define CORE_DIG_CLANE_2_RW_HS_TX_1                    0xaa02
+#define CORE_DIG_CLANE_2_RW_HS_TX_2                    0xaa04
+#define CORE_DIG_CLANE_2_RW_HS_TX_3                    0xaa06
+#define CORE_DIG_CLANE_2_RW_HS_TX_4                    0xaa08
+#define CORE_DIG_CLANE_2_RW_HS_TX_5                    0xaa0a
+#define CORE_DIG_CLANE_2_RW_HS_TX_6                    0xaa0c
+#define CORE_DIG_CLANE_2_RW_HS_TX_7                    0xaa0e
+#define CORE_DIG_CLANE_2_RW_HS_TX_8                    0xaa10
+#define CORE_DIG_CLANE_2_RW_HS_TX_9                    0xaa12
+#define CORE_DIG_CLANE_2_RW_HS_TX_10                   0xaa14
+#define CORE_DIG_CLANE_2_RW_HS_TX_11                   0xaa16
+#define CORE_DIG_CLANE_2_RW_HS_TX_12                   0xaa18
+#define CORE_DIG_CLANE_2_RW_HS_TX_13                   0xaa1a
+
+/* dwc csi host controller registers */
+#define IS_IO_CSI2_HOST_BASE(i)                        (IS_IO_BASE + 0x40000 + \
+                                                0x2000 * (i))
+#define VERSION                                        0x0
+#define N_LANES                                        0x4
+#define CSI2_RESETN                            0x8
+#define INT_ST_MAIN                            0xc
+#define DATA_IDS_1                             0x10
+#define DATA_IDS_2                             0x14
+#define CDPHY_MODE                             0x1c
+#define DATA_IDS_VC_1                          0x30
+#define DATA_IDS_VC_2                          0x34
+#define PHY_SHUTDOWNZ                          0x40
+#define DPHY_RSTZ                              0x44
+#define PHY_RX                                 0x48
+#define PHY_STOPSTATE                          0x4c
+#define PHY_TEST_CTRL0                         0x50
+#define PHY_TEST_CTRL1                         0x54
+#define PPI_PG_PATTERN_VRES                    0x60
+#define PPI_PG_PATTERN_HRES                    0x64
+#define PPI_PG_CONFIG                          0x68
+#define PPI_PG_ENABLE                          0x6c
+#define PPI_PG_STATUS                          0x70
+#define VC_EXTENSION                           0xc8
+#define PHY_CAL                                        0xcc
+#define INT_ST_PHY_FATAL                       0xe0
+#define INT_MSK_PHY_FATAL                      0xe4
+#define INT_FORCE_PHY_FATAL                    0xe8
+#define INT_ST_PKT_FATAL                       0xf0
+#define INT_MSK_PKT_FATAL                      0xf4
+#define INT_FORCE_PKT_FATAL                    0xf8
+#define INT_ST_PHY                             0x110
+#define INT_MSK_PHY                            0x114
+#define INT_FORCE_PHY                          0x118
+#define INT_ST_LINE                            0x130
+#define INT_MSK_LINE                           0x134
+#define INT_FORCE_LINE                         0x138
+#define INT_ST_BNDRY_FRAME_FATAL               0x280
+#define INT_MSK_BNDRY_FRAME_FATAL              0x284
+#define INT_FORCE_BNDRY_FRAME_FATAL            0x288
+#define INT_ST_SEQ_FRAME_FATAL                 0x290
+#define INT_MSK_SEQ_FRAME_FATAL                        0x294
+#define INT_FORCE_SEQ_FRAME_FATAL              0x298
+#define INT_ST_CRC_FRAME_FATAL                 0x2a0
+#define INT_MSK_CRC_FRAME_FATAL                        0x2a4
+#define INT_FORCE_CRC_FRAME_FATAL              0x2a8
+#define INT_ST_PLD_CRC_FATAL                   0x2b0
+#define INT_MSK_PLD_CRC_FATAL                  0x2b4
+#define INT_FORCE_PLD_CRC_FATAL                        0x2b8
+#define INT_ST_DATA_ID                         0x2c0
+#define INT_MSK_DATA_ID                                0x2c4
+#define INT_FORCE_DATA_ID                      0x2c8
+#define INT_ST_ECC_CORRECTED                   0x2d0
+#define INT_MSK_ECC_CORRECTED                  0x2d4
+#define INT_FORCE_ECC_CORRECTED                        0x2d8
+#define SCRAMBLING                             0x300
+#define SCRAMBLING_SEED1                       0x304
+#define SCRAMBLING_SEED2                       0x308
+#define SCRAMBLING_SEED3                       0x30c
+#define SCRAMBLING_SEED4                       0x310
+#define SCRAMBLING                             0x300
+
+#define IS_IO_CSI2_ADPL_PORT_BASE(i)           (IS_IO_BASE + 0x40800 + \
+                                                0x2000 * (i))
+#define CSI2_ADPL_INPUT_MODE                   0x0
+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN      0x4
+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR    0x8
+#define CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS                0xc
+#define CSI2_ADPL_IRQ_CTL_COMMON_STATUS                0xa4
+#define CSI2_ADPL_IRQ_CTL_COMMON_CLEAR         0xa8
+#define CSI2_ADPL_IRQ_CTL_COMMON_ENABLE                0xac
+#define CSI2_ADPL_IRQ_CTL_FS_STATUS            0xbc
+#define CSI2_ADPL_IRQ_CTL_FS_CLEAR             0xc0
+#define CSI2_ADPL_IRQ_CTL_FS_ENABLE            0xc4
+#define CSI2_ADPL_IRQ_CTL_FE_STATUS            0xc8
+#define CSI2_ADPL_IRQ_CTL_FE_CLEAR             0xcc
+#define CSI2_ADPL_IRQ_CTL_FE_ENABLE            0xd0
+
+/* software control the legacy csi irq */
+#define IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i)  (IS_IO_BASE + 0x40c00 + \
+                                                0x2000 * (i))
+#define IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40d00 + \
+                                                0x2000 * (i))
+#define IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE                (IS_IO_BASE + 0x49000)
+#define IS_IO_CSI2_IRQ_CTRL_BASE               (IS_IO_BASE + 0x4e100)
+
+#define IRQ_CTL_EDGE                           0x0
+#define IRQ_CTL_MASK                           0x4
+#define IRQ_CTL_STATUS                         0x8
+#define IRQ_CTL_CLEAR                          0xc
+#define IRQ_CTL_ENABLE                         0x10
+/* FE irq for PTL */
+#define IRQ1_CTL_MASK                          0x14
+#define IRQ1_CTL_STATUS                                0x18
+#define IRQ1_CTL_CLEAR                         0x1c
+#define IRQ1_CTL_ENABLE                                0x20
+
+/* software to set the clock gate to use the port or mgc */
+#define IS_IO_GPREGS_BASE                      (IS_IO_BASE + 0x49200)
+#define SRST_PORT_ARB                          0x0
+#define SRST_MGC                               0x4
+#define SRST_WIDTH_CONV                                0x8
+#define SRST_CSI_IRQ                           0xc
+#define SRST_CSI_LEGACY_IRQ                    0x10
+#define CLK_EN_TXCLKESC                                0x14
+#define CLK_DIV_FACTOR_IS_CLK                  0x18
+#define CLK_DIV_FACTOR_APB_CLK                 0x1c
+#define CSI_PORT_CLK_GATE                      0x20
+#define CSI_PORTAB_AGGREGATION                 0x24
+#define MGC_CLK_GATE                           0x2c
+#define CG_CTRL_BITS                           0x3c
+#define SPARE_RW                               0xf8
+#define SPARE_RO                               0xfc
+
+#define IS_IO_CSI2_MPF_PORT_BASE(i)            (IS_IO_BASE + 0x53000 + \
+                                                0x1000 * (i))
+#define MPF_16_IRQ_CNTRL_STATUS                        0x238
+#define MPF_16_IRQ_CNTRL_CLEAR                 0x23c
+#define MPF_16_IRQ_CNTRL_ENABLE                        0x240
+
+/* software config the phy */
+#define IS_IO_CSI2_GPREGS_BASE         (IS_IO_BASE + 0x53400)
+#define IPU8_IS_IO_CSI2_GPREGS_BASE    (IS_IO_BASE + 0x40e00)
+#define CSI_ADAPT_LAYER_SRST           0x0
+#define MPF_SRST_RST                   0x4
+#define CSI_ERR_IRQ_CTRL_SRST          0x8
+#define CSI_SYNC_RC_SRST               0xc
+#define CSI_CG_CTRL_BITS               0x10
+#define SOC_CSI2HOST_SELECT            0x14
+#define PHY_RESET                      0x18
+#define PHY_SHUTDOWN                   0x1c
+#define PHY_MODE                       0x20
+#define PHY_READY                      0x24
+#define PHY_CLK_LANE_FORCE_CONTROL     0x28
+#define PHY_CLK_LANE_CONTROL           0x2c
+#define PHY_CLK_LANE_STATUS            0x30
+#define PHY_LANE_RX_ESC_REQ            0x34
+#define PHY_LANE_RX_ESC_DATA           0x38
+#define PHY_LANE_TURNDISABLE           0x3c
+#define PHY_LANE_DIRECTION             0x40
+#define PHY_LANE_FORCE_CONTROL         0x44
+#define PHY_LANE_CONTROL_EN            0x48
+#define PHY_LANE_CONTROL_DATAWIDTH     0x4c
+#define PHY_LANE_STATUS                        0x50
+#define PHY_LANE_ERR                   0x54
+#define PHY_LANE_RXALP                 0x58
+#define PHY_LANE_RXALP_NIBBLE          0x5c
+#define PHY_PARITY_ERROR               0x60
+#define PHY_DEBUG_REGS_CLK_GATE_EN     0x64
+#define SPARE_RW                       0xf8
+#define SPARE_RO                       0xfc
+
+/* software not touch */
+#define PORT_ARB_BASE                          (IS_IO_BASE + 0x4e000)
+#define PORT_ARB_IRQ_CTL_STATUS                        0x4
+#define PORT_ARB_IRQ_CTL_CLEAR                 0x8
+#define PORT_ARB_IRQ_CTL_ENABLE                        0xc
+
+#define MGC_PPC                                        4U
+#define MGC_DTYPE_RAW(i)                       (((i) - 8) / 2)
+#define IS_IO_MGC_BASE                         (IS_IO_BASE + 0x48000)
+#define MGC_KICK                               0x0
+#define MGC_ASYNC_STOP                         0x4
+#define MGC_PORT_OFFSET                                0x100
+#define MGC_CSI_PORT_MAP(i)                    (0x8 + (i) * 0x4)
+#define MGC_MG_PORT(i)                         (IS_IO_MGC_BASE + \
+                                                (i) * MGC_PORT_OFFSET)
+/* per mgc instance */
+#define MGC_MG_CSI_ADAPT_LAYER_TYPE            0x28
+#define MGC_MG_MODE                            0x2c
+#define MGC_MG_INIT_COUNTER                    0x30
+#define MGC_MG_MIPI_VC                         0x34
+#define MGC_MG_MIPI_DTYPES                     0x38
+#define MGC_MG_MULTI_DTYPES_MODE               0x3c
+#define MGC_MG_NOF_FRAMES                      0x40
+#define MGC_MG_FRAME_DIM                       0x44
+#define MGC_MG_HBLANK                          0x48
+#define MGC_MG_VBLANK                          0x4c
+#define MGC_MG_TPG_MODE                                0x50
+#define MGC_MG_TPG_R0                          0x54
+#define MGC_MG_TPG_G0                          0x58
+#define MGC_MG_TPG_B0                          0x5c
+#define MGC_MG_TPG_R1                          0x60
+#define MGC_MG_TPG_G1                          0x64
+#define MGC_MG_TPG_B1                          0x68
+#define MGC_MG_TPG_FACTORS                     0x6c
+#define MGC_MG_TPG_MASKS                       0x70
+#define MGC_MG_TPG_XY_MASK                     0x74
+#define MGC_MG_TPG_TILE_DIM                    0x78
+#define MGC_MG_PRBS_LFSR_INIT_0                        0x7c
+#define MGC_MG_PRBS_LFSR_INIT_1                        0x80
+#define MGC_MG_SYNC_STOP_POINT                 0x84
+#define MGC_MG_SYNC_STOP_POINT_LOC             0x88
+#define MGC_MG_ERR_INJECT                      0x8c
+#define MGC_MG_ERR_LOCATION                    0x90
+#define MGC_MG_DTO_SPEED_CTRL_EN               0x94
+#define MGC_MG_DTO_SPEED_CTRL_INCR_VAL         0x98
+#define MGC_MG_HOR_LOC_STTS                    0x9c
+#define MGC_MG_VER_LOC_STTS                    0xa0
+#define MGC_MG_FRAME_NUM_STTS                  0xa4
+#define MGC_MG_BUSY_STTS                       0xa8
+#define MGC_MG_STOPPED_STTS                    0xac
+/* tile width and height in pixels for Chess board and Color palette */
+#define MGC_TPG_TILE_WIDTH                     64U
+#define MGC_TPG_TILE_HEIGHT                    64U
+
+#define IPU_CSI_PORT_A_ADDR_OFFSET             0x0
+#define IPU_CSI_PORT_B_ADDR_OFFSET             0x0
+#define IPU_CSI_PORT_C_ADDR_OFFSET             0x0
+#define IPU_CSI_PORT_D_ADDR_OFFSET             0x0
+
+/*
+ * 0 - CSI RX Port 0 interrupt;
+ * 1 - MPF Port 0 interrupt;
+ * 2 - CSI RX Port 1 interrupt;
+ * 3 - MPF Port 1 interrupt;
+ * 4 - CSI RX Port 2 interrupt;
+ * 5 - MPF Port 2 interrupt;
+ * 6 - CSI RX Port 3 interrupt;
+ * 7 - MPF Port 3 interrupt;
+ * 8 - Port ARB FIFO 0 overflow;
+ * 9 - Port ARB FIFO 1 overflow;
+ * 10 - Port ARB FIFO 2 overflow;
+ * 11 - Port ARB FIFO 3 overflow;
+ * 12 - isys_cfgnoc_err_probe_intl;
+ * 13-15 - reserved
+ */
+#define IPU7_CSI_IS_IO_IRQ_MASK                0xffff
+
+/* Adapter layer irq */
+#define IPU7_CSI_ADPL_IRQ_MASK         0xffff
+
+/* sw irq from legacy irq control
+ * legacy irq status
+ * IPU7
+ * 0 - CSI Port 0 error interrupt
+ * 1 - CSI Port 0 sync interrupt
+ * 2 - CSI Port 1 error interrupt
+ * 3 - CSI Port 1 sync interrupt
+ * 4 - CSI Port 2 error interrupt
+ * 5 - CSI Port 2 sync interrupt
+ * 6 - CSI Port 3 error interrupt
+ * 7 - CSI Port 3 sync interrupt
+ * IPU7P5
+ * 0 - CSI Port 0 error interrupt
+ * 1 - CSI Port 0 fs interrupt
+ * 2 - CSI Port 0 fe interrupt
+ * 3 - CSI Port 1 error interrupt
+ * 4 - CSI Port 1 fs interrupt
+ * 5 - CSI Port 1 fe interrupt
+ * 6 - CSI Port 2 error interrupt
+ * 7 - CSI Port 2 fs interrupt
+ * 8 - CSI Port 2 fe interrupt
+ */
+#define IPU7_CSI_RX_LEGACY_IRQ_MASK            0x1ff
+
+/* legacy error status per port
+ * 0 - Error handler FIFO full;
+ * 1 - Reserved Short Packet encoding detected;
+ * 2 - Reserved Long Packet encoding detected;
+ * 3 - Received packet is too short (fewer data words than specified in header);
+ * 4 - Received packet is too long (more data words than specified in header);
+ * 5 - Short packet discarded due to errors;
+ * 6 - Long packet discarded due to errors;
+ * 7 - CSI Combo Rx interrupt;
+ * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0;
+ */
+#define IPU7_CSI_RX_ERROR_IRQ_MASK             0xfff
+
+/*
+ * 0 - VC0 frame start received
+ * 1 - VC0 frame end received
+ * 2 - VC1 frame start received
+ * 3 - VC1 frame end received
+ * 4 - VC2 frame start received
+ * 5 - VC2 frame end received
+ * 6 - VC3 frame start received
+ * 7 - VC3 frame end received
+ * 8 - VC4 frame start received
+ * 9 - VC4 frame end received
+ * 10 - VC5 frame start received
+ * 11 - VC5 frame end received
+ * 12 - VC6 frame start received
+ * 13 - VC6 frame end received
+ * 14 - VC7 frame start received
+ * 15 - VC7 frame end received
+ * 16 - VC8 frame start received
+ * 17 - VC8 frame end received
+ * 18 - VC9 frame start received
+ * 19 - VC9 frame end received
+ * 20 - VC10 frame start received
+ * 21 - VC10 frame end received
+ * 22 - VC11 frame start received
+ * 23 - VC11 frame end received
+ * 24 - VC12 frame start received
+ * 25 - VC12 frame end received
+ * 26 - VC13 frame start received
+ * 27 - VC13 frame end received
+ * 28 - VC14 frame start received
+ * 29 - VC14 frame end received
+ * 30 - VC15 frame start received
+ * 31 - VC15 frame end received
+ */
+
+#define IPU7_CSI_RX_SYNC_IRQ_MASK              0x0
+#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK         0x0
+
+#define CSI_RX_NUM_ERRORS_IN_IRQ               12U
+#define CSI_RX_NUM_SYNC_IN_IRQ                 32U
+
+enum CSI_FE_MODE_TYPE {
+       CSI_FE_DPHY_MODE = 0,
+       CSI_FE_CPHY_MODE = 1,
+};
+
+enum CSI_FE_INPUT_MODE {
+       CSI_SENSOR_INPUT = 0,
+       CSI_MIPIGEN_INPUT = 1,
+};
+
+enum MGC_CSI_ADPL_TYPE {
+       MGC_MAPPED_2_LANES = 0,
+       MGC_MAPPED_4_LANES = 1,
+};
+
+enum CSI2HOST_SELECTION {
+       CSI2HOST_SEL_SOC = 0,
+       CSI2HOST_SEL_CSI2HOST = 1,
+};
+
+#define IPU7_ISYS_LEGACY_IRQ_CSI2(port)                (0x3 << (port))
+#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port)      (0x7 << (port))
+
+/* ---------------------------------------------------------------- */
+#define CSI_REG_BASE                           0x220000
+#define CSI_REG_BASE_PORT(id)                  ((id) * 0x1000)
+
+/* CSI Port General Purpose Registers */
+#define CSI_REG_PORT_GPREG_SRST                        0x0
+#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST   0x4
+#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL   0x8
+
+#define CSI_RX_NUM_IRQ                         32U
+
+#define IPU7_CSI_RX_SYNC_FS_VC                 0x55555555
+#define IPU7_CSI_RX_SYNC_FE_VC                 0xaaaaaaaa
+#define IPU7P5_CSI_RX_SYNC_FS_VC               0xffff
+#define IPU7P5_CSI_RX_SYNC_FE_VC               0xffff
+
+#endif /* IPU7_ISYS_CSI2_REG_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.c b/drivers/staging/media/ipu7/ipu7-isys-csi2.c
new file mode 100644 (file)
index 0000000..9c16ae9
--- /dev/null
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/atomic.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-isys-csi-phy.h"
+
+static const u32 csi2_supported_codes[] = {
+       MEDIA_BUS_FMT_Y10_1X10,
+       MEDIA_BUS_FMT_RGB565_1X16,
+       MEDIA_BUS_FMT_RGB888_1X24,
+       MEDIA_BUS_FMT_UYVY8_1X16,
+       MEDIA_BUS_FMT_YUYV8_1X16,
+       MEDIA_BUS_FMT_YUYV10_1X20,
+       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,
+       MEDIA_BUS_FMT_SBGGR8_1X8,
+       MEDIA_BUS_FMT_SGBRG8_1X8,
+       MEDIA_BUS_FMT_SGRBG8_1X8,
+       MEDIA_BUS_FMT_SRGGB8_1X8,
+       0,
+};
+
+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2)
+{
+       struct media_pad *src_pad;
+
+       src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+       if (IS_ERR(src_pad)) {
+               dev_err(&csi2->isys->adev->auxdev.dev,
+                       "can't get source pad of %s (%ld)\n",
+                       csi2->asd.sd.name, PTR_ERR(src_pad));
+               return PTR_ERR(src_pad);
+       }
+
+       return v4l2_get_link_freq(src_pad, 0, 0);
+}
+
+static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+                               struct v4l2_event_subscription *sub)
+{
+       struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+       struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
+       struct device *dev = &csi2->isys->adev->auxdev.dev;
+
+       dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
+               sub->type, sub->id);
+
+       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;
+       }
+}
+
+static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
+       .subscribe_event = csi2_subscribe_event,
+       .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_device *isp = csi2->isys->adev->isp;
+       unsigned int offset, mask;
+
+       /* enable CSI2 legacy error irq */
+       offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+       mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+       writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+       writel(mask, csi2->base + offset + IRQ_CTL_MASK);
+       writel(mask, csi2->base + offset + IRQ_CTL_ENABLE);
+
+       /* enable CSI2 legacy sync irq */
+       offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+       mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
+       writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+       writel(mask, csi2->base + offset + IRQ_CTL_MASK);
+       writel(mask, csi2->base + offset + IRQ_CTL_ENABLE);
+
+       mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK;
+       if (!is_ipu7(isp->hw_ver)) {
+               writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR);
+               writel(mask, csi2->base + offset + IRQ1_CTL_MASK);
+               writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE);
+       }
+}
+
+static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_device *isp = csi2->isys->adev->isp;
+       unsigned int offset, mask;
+
+       /* disable CSI2 legacy error irq */
+       offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+       mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+       writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+       writel(0, csi2->base + offset + IRQ_CTL_MASK);
+       writel(0, csi2->base + offset + IRQ_CTL_ENABLE);
+
+       /* disable CSI2 legacy sync irq */
+       offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+       mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
+       writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+       writel(0, csi2->base + offset + IRQ_CTL_MASK);
+       writel(0, csi2->base + offset + IRQ_CTL_ENABLE);
+
+       if (!is_ipu7(isp->hw_ver)) {
+               writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR);
+               writel(0, csi2->base + offset + IRQ1_CTL_MASK);
+               writel(0, csi2->base + offset + IRQ1_CTL_ENABLE);
+       }
+}
+
+static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_isys *isys = csi2->isys;
+       void __iomem *isys_base = isys->pdata->base;
+
+       ipu7_isys_csi_phy_powerdown(csi2);
+
+       writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK);
+       csi2_irq_disable(csi2);
+}
+
+static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_isys *isys = csi2->isys;
+       struct device *dev = &isys->adev->auxdev.dev;
+       void __iomem *isys_base = isys->pdata->base;
+       unsigned int port, nlanes, offset;
+       int ret;
+
+       port = csi2->port;
+       nlanes = csi2->nlanes;
+
+       offset = IS_IO_GPREGS_BASE;
+       writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK);
+       dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n",
+               port, readl(isys_base + offset + CSI_PORT_CLK_GATE),
+               readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK));
+       if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) {
+               dev_dbg(dev, "CSI port %u in aggregation mode\n", port);
+               writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION);
+       }
+
+       /* input is coming from CSI receiver (sensor) */
+       offset = IS_IO_CSI2_ADPL_PORT_BASE(port);
+       writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE);
+       writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN);
+
+       ret = ipu7_isys_csi_phy_powerup(csi2);
+       if (ret) {
+               dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret);
+               return ret;
+       }
+
+       csi2_irq_enable(csi2);
+
+       return 0;
+}
+
+static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd,
+                                 struct v4l2_subdev_state *state,
+                                 struct v4l2_subdev_selection *sel)
+{
+       struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+       struct device *dev = &asd->isys->adev->auxdev.dev;
+       struct v4l2_mbus_framefmt *sink_ffmt;
+       struct v4l2_mbus_framefmt *src_ffmt;
+       struct v4l2_rect *crop;
+
+       if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
+               return -EINVAL;
+
+       sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
+                                                                sel->pad,
+                                                                sel->stream);
+       if (!sink_ffmt)
+               return -EINVAL;
+
+       src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
+       if (!src_ffmt)
+               return -EINVAL;
+
+       crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+       if (!crop)
+               return -EINVAL;
+
+       /* Only vertical cropping is supported */
+       sel->r.left = 0;
+       sel->r.width = sink_ffmt->width;
+       /* Non-bayer formats can't be single line cropped */
+       if (!ipu7_isys_is_bayer_format(sink_ffmt->code))
+               sel->r.top &= ~1U;
+       sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT,
+                             sink_ffmt->height - sel->r.top);
+       *crop = sel->r;
+
+       /* update source pad format */
+       src_ffmt->width = sel->r.width;
+       src_ffmt->height = sel->r.height;
+       if (ipu7_isys_is_bayer_format(sink_ffmt->code))
+               src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code,
+                                                              sel->r.left,
+                                                              sel->r.top);
+       dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
+               sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+               src_ffmt->code);
+
+       return 0;
+}
+
+static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd,
+                                 struct v4l2_subdev_state *state,
+                                 struct v4l2_subdev_selection *sel)
+{
+       struct v4l2_mbus_framefmt *sink_ffmt;
+       struct v4l2_rect *crop;
+       int ret = 0;
+
+       if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
+               return -EINVAL;
+
+       sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
+                                                                sel->pad,
+                                                                sel->stream);
+       if (!sink_ffmt)
+               return -EINVAL;
+
+       crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+       if (!crop)
+               return -EINVAL;
+
+       switch (sel->target) {
+       case V4L2_SEL_TGT_CROP_DEFAULT:
+       case V4L2_SEL_TGT_CROP_BOUNDS:
+               sel->r.left = 0;
+               sel->r.top = 0;
+               sel->r.width = sink_ffmt->width;
+               sel->r.height = sink_ffmt->height;
+               break;
+       case V4L2_SEL_TGT_CROP:
+               sel->r = *crop;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*
+ * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set
+ * of streams.
+ */
+#define CSI2_SUBDEV_MAX_STREAM_ID 63
+
+static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd,
+                                        struct v4l2_subdev_state *state,
+                                        u32 pad, u64 streams_mask)
+{
+       struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+       struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
+       struct v4l2_subdev *r_sd;
+       struct media_pad *rp;
+       u32 sink_pad, sink_stream;
+       int ret, i;
+
+       if (!csi2->stream_count) {
+               dev_dbg(&csi2->isys->adev->auxdev.dev,
+                       "stream on CSI2-%u with %u lanes\n", csi2->port,
+                       csi2->nlanes);
+               ret = ipu7_isys_csi2_enable_stream(csi2);
+               if (ret)
+                       return ret;
+       }
+
+       for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) {
+               if (streams_mask & BIT_ULL(i))
+                       break;
+       }
+
+       ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i,
+                                                   &sink_pad, &sink_stream);
+       if (ret)
+               return ret;
+
+       rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]);
+       r_sd = media_entity_to_v4l2_subdev(rp->entity);
+
+       ret = v4l2_subdev_enable_streams(r_sd, rp->index,
+                                        BIT_ULL(sink_stream));
+       if (!ret) {
+               csi2->stream_count++;
+               return 0;
+       }
+
+       if (!csi2->stream_count)
+               ipu7_isys_csi2_disable_stream(csi2);
+
+       return ret;
+}
+
+static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd,
+                                         struct v4l2_subdev_state *state,
+                                         u32 pad, u64 streams_mask)
+{
+       struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+       struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
+       struct v4l2_subdev *r_sd;
+       struct media_pad *rp;
+       u32 sink_pad, sink_stream;
+       int ret, i;
+
+       for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) {
+               if (streams_mask & BIT_ULL(i))
+                       break;
+       }
+
+       ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i,
+                                                   &sink_pad, &sink_stream);
+       if (ret)
+               return ret;
+
+       rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]);
+       r_sd = media_entity_to_v4l2_subdev(rp->entity);
+
+       v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream));
+
+       if (--csi2->stream_count)
+               return 0;
+
+       dev_dbg(&csi2->isys->adev->auxdev.dev,
+               "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes);
+
+       ipu7_isys_csi2_disable_stream(csi2);
+
+       return 0;
+}
+
+static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
+       .get_fmt = v4l2_subdev_get_fmt,
+       .set_fmt = ipu7_isys_subdev_set_fmt,
+       .get_selection = ipu7_isys_csi2_get_sel,
+       .set_selection = ipu7_isys_csi2_set_sel,
+       .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code,
+       .enable_streams = ipu7_isys_csi2_enable_streams,
+       .disable_streams = ipu7_isys_csi2_disable_streams,
+       .set_routing = ipu7_isys_subdev_set_routing,
+};
+
+static const struct v4l2_subdev_ops csi2_sd_ops = {
+       .core = &csi2_sd_core_ops,
+       .pad = &csi2_sd_pad_ops,
+};
+
+static const struct media_entity_operations csi2_entity_ops = {
+       .link_validate = v4l2_subdev_link_validate,
+       .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2)
+{
+       if (!csi2->isys)
+               return;
+
+       v4l2_device_unregister_subdev(&csi2->asd.sd);
+       v4l2_subdev_cleanup(&csi2->asd.sd);
+       ipu7_isys_subdev_cleanup(&csi2->asd);
+       csi2->isys = NULL;
+}
+
+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2,
+                       struct ipu7_isys *isys,
+                       void __iomem *base, unsigned int index)
+{
+       struct device *dev = &isys->adev->auxdev.dev;
+       int ret;
+
+       csi2->isys = isys;
+       csi2->base = base;
+       csi2->port = index;
+
+       if (!is_ipu7(isys->adev->isp->hw_ver))
+               csi2->legacy_irq_mask = 0x7U << (index * 3U);
+       else
+               csi2->legacy_irq_mask = 0x3U << (index * 2U);
+
+       dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index,
+               csi2->legacy_irq_mask);
+
+       csi2->asd.sd.entity.ops = &csi2_entity_ops;
+       csi2->asd.isys = isys;
+
+       ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
+                                   IPU7_NR_OF_CSI2_SINK_PADS,
+                                   IPU7_NR_OF_CSI2_SRC_PADS);
+       if (ret)
+               return ret;
+
+       csi2->asd.source = (int)index;
+       csi2->asd.supported_codes = csi2_supported_codes;
+       snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
+                IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index);
+       v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
+
+       ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
+       if (ret) {
+               dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret);
+               goto isys_subdev_cleanup;
+       }
+
+       ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
+       if (ret) {
+               dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret);
+               goto v4l2_subdev_cleanup;
+       }
+
+       return 0;
+
+v4l2_subdev_cleanup:
+       v4l2_subdev_cleanup(&csi2->asd.sd);
+isys_subdev_cleanup:
+       ipu7_isys_subdev_cleanup(&csi2->asd);
+
+       return ret;
+}
+
+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream)
+{
+       struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+       struct device *dev = &stream->isys->adev->auxdev.dev;
+       struct video_device *vdev = csi2->asd.sd.devnode;
+       struct v4l2_event ev = {
+               .type = V4L2_EVENT_FRAME_SYNC,
+       };
+
+       ev.id = stream->vc;
+       ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
+       v4l2_event_queue(vdev, &ev);
+
+       dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
+               csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
+}
+
+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream)
+{
+       struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+       struct device *dev = &stream->isys->adev->auxdev.dev;
+       u32 frame_sequence = atomic_read(&stream->sequence);
+
+       dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
+               csi2->port, frame_sequence);
+}
+
+int ipu7_isys_csi2_get_remote_desc(u32 source_stream,
+                                  struct ipu7_isys_csi2 *csi2,
+                                  struct media_entity *source_entity,
+                                  struct v4l2_mbus_frame_desc_entry *entry,
+                                  int *nr_queues)
+{
+       struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
+       struct device *dev = &csi2->isys->adev->auxdev.dev;
+       struct v4l2_mbus_frame_desc desc;
+       struct v4l2_subdev *source;
+       struct media_pad *pad;
+       unsigned int i;
+       int ret;
+
+       source = media_entity_to_v4l2_subdev(source_entity);
+       if (!source)
+               return -EPIPE;
+
+       pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]);
+       if (!pad)
+               return -EPIPE;
+
+       ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
+       if (ret)
+               return ret;
+
+       if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+               dev_err(dev, "Unsupported frame descriptor type\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < desc.num_entries; i++) {
+               if (source_stream == desc.entry[i].stream) {
+                       desc_entry = &desc.entry[i];
+                       break;
+               }
+       }
+
+       if (!desc_entry) {
+               dev_err(dev, "Failed to find stream %u from remote subdev\n",
+                       source_stream);
+               return -EINVAL;
+       }
+
+       if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) {
+               dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
+               return -EINVAL;
+       }
+
+       *entry = *desc_entry;
+
+       for (i = 0; i < desc.num_entries; i++) {
+               if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc)
+                       (*nr_queues)++;
+       }
+
+       return 0;
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.h b/drivers/staging/media/ipu7/ipu7-isys-csi2.h
new file mode 100644 (file)
index 0000000..6c23b80
--- /dev/null
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_CSI2_H
+#define IPU7_ISYS_CSI2_H
+
+#include <linux/container_of.h>
+#include <linux/types.h>
+
+#include "ipu7-isys-subdev.h"
+#include "ipu7-isys-video.h"
+
+struct ipu7_isys;
+struct ipu7_isys_csi2_pdata;
+struct ipu7_isys_stream;
+
+#define IPU7_NR_OF_CSI2_VC             16U
+#define INVALID_VC_ID                  -1
+#define IPU7_NR_OF_CSI2_SINK_PADS      1U
+#define IPU7_CSI2_PAD_SINK             0U
+#define IPU7_NR_OF_CSI2_SRC_PADS       8U
+#define IPU7_CSI2_PAD_SRC              1U
+#define IPU7_NR_OF_CSI2_PADS           (IPU7_NR_OF_CSI2_SINK_PADS + \
+                                        IPU7_NR_OF_CSI2_SRC_PADS)
+
+/*
+ * struct ipu7_isys_csi2
+ *
+ * @nlanes: number of lanes in the receiver
+ */
+struct ipu7_isys_csi2 {
+       struct ipu7_isys_subdev asd;
+       struct ipu7_isys_csi2_pdata *pdata;
+       struct ipu7_isys *isys;
+       struct ipu7_isys_video av[IPU7_NR_OF_CSI2_SRC_PADS];
+
+       void __iomem *base;
+       u32 receiver_errors;
+       u32 legacy_irq_mask;
+       unsigned int nlanes;
+       unsigned int port;
+       unsigned int phy_mode;
+       unsigned int stream_count;
+};
+
+#define ipu7_isys_subdev_to_csi2(__sd)                 \
+       container_of(__sd, struct ipu7_isys_csi2, asd)
+
+#define to_ipu7_isys_csi2(__asd) container_of(__asd, struct ipu7_isys_csi2, asd)
+
+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2);
+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, struct ipu7_isys *isys,
+                       void __iomem *base, unsigned int index);
+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2);
+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream);
+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream);
+int ipu7_isys_csi2_get_remote_desc(u32 source_stream,
+                                  struct ipu7_isys_csi2 *csi2,
+                                  struct media_entity *source_entity,
+                                  struct v4l2_mbus_frame_desc_entry *entry,
+                                  int *nr_queues);
+#endif /* IPU7_ISYS_CSI2_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c
new file mode 100644 (file)
index 0000000..7046c29
--- /dev/null
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7-bus.h"
+#include "ipu7-dma.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-isys-video.h"
+#include "ipu7-platform-regs.h"
+
+#define IPU_MAX_FRAME_COUNTER  (U8_MAX + 1)
+
+static int ipu7_isys_buf_init(struct vb2_buffer *vb)
+{
+       struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue);
+       struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0);
+       struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+       struct ipu7_isys_video_buffer *ivb =
+               vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+       int ret;
+
+       ret = ipu7_dma_map_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0);
+       if (ret)
+               return ret;
+
+       ivb->dma_addr = sg_dma_address(sg->sgl);
+
+       return 0;
+}
+
+static void ipu7_isys_buf_cleanup(struct vb2_buffer *vb)
+{
+       struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue);
+       struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0);
+       struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+       struct ipu7_isys_video_buffer *ivb =
+               vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+
+       ivb->dma_addr = 0;
+       ipu7_dma_unmap_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0);
+}
+
+static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+                                unsigned int *num_planes, unsigned int sizes[],
+                                struct device *alloc_devs[])
+{
+       struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q);
+       struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       u32 size = av->pix_fmt.sizeimage;
+
+       /* num_planes == 0: we're being called through VIDIOC_REQBUFS */
+       if (!*num_planes) {
+               sizes[0] = size;
+       } else if (sizes[0] < size) {
+               dev_dbg(dev, "%s: queue setup: size %u < %u\n",
+                       av->vdev.name, sizes[0], size);
+               return -EINVAL;
+       }
+
+       *num_planes = 1;
+
+       return 0;
+}
+
+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_video *av = ipu7_isys_queue_to_video(aq);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       u32 bytesperline = av->pix_fmt.bytesperline;
+       u32 height = av->pix_fmt.height;
+
+       dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
+               av->vdev.name, av->pix_fmt.sizeimage, vb2_plane_size(vb, 0));
+
+       if (av->pix_fmt.sizeimage > vb2_plane_size(vb, 0))
+               return -EINVAL;
+
+       dev_dbg(dev, "buffer: %s: bytesperline %u, height %u\n",
+               av->vdev.name, bytesperline, height);
+       vb2_set_plane_payload(vb, 0, bytesperline * height);
+
+       return 0;
+}
+
+/*
+ * Queue a buffer list back to incoming or active queues. The buffers
+ * are removed from the buffer list.
+ */
+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl,
+                                unsigned long op_flags,
+                                enum vb2_buffer_state state)
+{
+       struct ipu7_isys_buffer *ib, *ib_safe;
+       unsigned long flags;
+       bool first = true;
+
+       if (!bl)
+               return;
+
+       WARN_ON_ONCE(!bl->nbufs);
+       WARN_ON_ONCE(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE &&
+                    op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING);
+
+       list_for_each_entry_safe(ib, ib_safe, &bl->head, head) {
+               struct ipu7_isys_video *av;
+
+               struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+               struct ipu7_isys_queue *aq =
+                       vb2_queue_to_isys_queue(vb->vb2_queue);
+
+               av = ipu7_isys_queue_to_video(aq);
+               spin_lock_irqsave(&aq->lock, flags);
+               list_del(&ib->head);
+               if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE)
+                       list_add(&ib->head, &aq->active);
+               else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING)
+                       list_add_tail(&ib->head, &aq->incoming);
+               spin_unlock_irqrestore(&aq->lock, flags);
+
+               if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE)
+                       vb2_buffer_done(vb, state);
+
+               if (first) {
+                       dev_dbg(&av->isys->adev->auxdev.dev,
+                               "queue buf list %p flags %lx, s %d, %d bufs\n",
+                               bl, op_flags, state, bl->nbufs);
+                       first = false;
+               }
+
+               bl->nbufs--;
+       }
+
+       WARN_ON(bl->nbufs);
+}
+
+/*
+ * flush_firmware_streamon_fail() - Flush in cases where requests may
+ * have been queued to firmware and the *firmware streamon fails for a
+ * reason or another.
+ */
+static void flush_firmware_streamon_fail(struct ipu7_isys_stream *stream)
+{
+       struct ipu7_isys_queue *aq;
+       unsigned long flags;
+
+       lockdep_assert_held(&stream->mutex);
+
+       list_for_each_entry(aq, &stream->queues, node) {
+               struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+               struct device *dev = &av->isys->adev->auxdev.dev;
+               struct ipu7_isys_buffer *ib, *ib_safe;
+
+               spin_lock_irqsave(&aq->lock, flags);
+               list_for_each_entry_safe(ib, ib_safe, &aq->active, head) {
+                       struct vb2_buffer *vb =
+                               ipu7_isys_buffer_to_vb2_buffer(ib);
+
+                       list_del(&ib->head);
+                       if (av->streaming) {
+                               dev_dbg(dev,
+                                       "%s: queue buffer %u back to incoming\n",
+                                       av->vdev.name, vb->index);
+                               /* Queue already streaming, return to driver. */
+                               list_add(&ib->head, &aq->incoming);
+                               continue;
+                       }
+                       /* Queue not yet streaming, return to user. */
+                       dev_dbg(dev, "%s: return %u back to videobuf2\n",
+                               av->vdev.name, vb->index);
+                       vb2_buffer_done(ipu7_isys_buffer_to_vb2_buffer(ib),
+                                       VB2_BUF_STATE_QUEUED);
+               }
+               spin_unlock_irqrestore(&aq->lock, flags);
+       }
+}
+
+/*
+ * Attempt obtaining a buffer list from the incoming queues, a list of buffers
+ * that contains one entry from each video buffer queue. If a buffer can't be
+ * obtained from every queue, the buffers are returned back to the queue.
+ */
+static int buffer_list_get(struct ipu7_isys_stream *stream,
+                          struct ipu7_isys_buffer_list *bl)
+{
+       unsigned long buf_flag = IPU_ISYS_BUFFER_LIST_FL_INCOMING;
+       struct device *dev = &stream->isys->adev->auxdev.dev;
+       struct ipu7_isys_queue *aq;
+       unsigned long flags;
+
+       bl->nbufs = 0;
+       INIT_LIST_HEAD(&bl->head);
+
+       list_for_each_entry(aq, &stream->queues, node) {
+               struct ipu7_isys_buffer *ib;
+
+               spin_lock_irqsave(&aq->lock, flags);
+               if (list_empty(&aq->incoming)) {
+                       spin_unlock_irqrestore(&aq->lock, flags);
+                       if (!list_empty(&bl->head))
+                               ipu7_isys_buffer_list_queue(bl, buf_flag, 0);
+                       return -ENODATA;
+               }
+
+               ib = list_last_entry(&aq->incoming,
+                                    struct ipu7_isys_buffer, head);
+
+               dev_dbg(dev, "buffer: %s: buffer %u\n",
+                       ipu7_isys_queue_to_video(aq)->vdev.name,
+                       ipu7_isys_buffer_to_vb2_buffer(ib)->index);
+               list_del(&ib->head);
+               list_add(&ib->head, &bl->head);
+               spin_unlock_irqrestore(&aq->lock, flags);
+
+               bl->nbufs++;
+       }
+
+       dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs);
+
+       return 0;
+}
+
+static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
+                                             struct ipu7_insys_buffset *set)
+{
+       struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+       struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_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;
+}
+
+/*
+ * Convert a buffer list to a isys fw ABI framebuffer set. The
+ * buffer list is not modified.
+ */
+#define IPU_ISYS_FRAME_NUM_THRESHOLD   (30)
+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set,
+                                      struct ipu7_isys_stream *stream,
+                                      struct ipu7_isys_buffer_list *bl)
+{
+       struct ipu7_isys_buffer *ib;
+       u32 buf_id;
+
+       WARN_ON(!bl->nbufs);
+
+       set->skip_frame = 0;
+       set->capture_msg_map = IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP |
+                              IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ;
+
+       buf_id = atomic_fetch_inc(&stream->buf_id);
+       set->frame_id = buf_id % IPU_MAX_FRAME_COUNTER;
+
+       list_for_each_entry(ib, &bl->head, head) {
+               struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+
+               ipu7_isys_buf_to_fw_frame_buf_pin(vb, set);
+       }
+}
+
+/* Start streaming for real. The buffer list must be available. */
+static int ipu7_isys_stream_start(struct ipu7_isys_video *av,
+                                 struct ipu7_isys_buffer_list *bl, bool error)
+{
+       struct ipu7_isys_stream *stream = av->stream;
+       struct device *dev = &stream->isys->adev->auxdev.dev;
+       struct ipu7_isys_buffer_list __bl;
+       int ret;
+
+       mutex_lock(&stream->isys->stream_mutex);
+
+       ret = ipu7_isys_video_set_streaming(av, 1, bl);
+       mutex_unlock(&stream->isys->stream_mutex);
+       if (ret)
+               goto out_requeue;
+
+       stream->streaming = 1;
+
+       bl = &__bl;
+
+       do {
+               struct ipu7_insys_buffset *buf = NULL;
+               struct isys_fw_msgs *msg;
+               enum ipu7_insys_send_type send_type =
+                       IPU_INSYS_SEND_TYPE_STREAM_CAPTURE;
+
+               ret = buffer_list_get(stream, bl);
+               if (ret < 0)
+                       break;
+
+               msg = ipu7_get_fw_msg_buf(stream);
+               if (!msg)
+                       return -ENOMEM;
+
+               buf = &msg->fw_msg.frame;
+
+               ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl);
+
+               ipu7_fw_isys_dump_frame_buff_set(dev, buf,
+                                                stream->nr_output_pins);
+
+               ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE,
+                                           0);
+
+               ret = ipu7_fw_isys_complex_cmd(stream->isys,
+                                              stream->stream_handle, buf,
+                                              msg->dma_addr, sizeof(*buf),
+                                              send_type);
+       } while (!WARN_ON(ret));
+
+       return 0;
+
+out_requeue:
+       if (bl && bl->nbufs)
+               ipu7_isys_buffer_list_queue(bl,
+                                           IPU_ISYS_BUFFER_LIST_FL_INCOMING |
+                                           (error ?
+                                            IPU_ISYS_BUFFER_LIST_FL_SET_STATE :
+                                            0), error ? VB2_BUF_STATE_ERROR :
+                                           VB2_BUF_STATE_QUEUED);
+       flush_firmware_streamon_fail(stream);
+
+       return ret;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+       struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+       struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+       struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+       struct ipu7_isys_video_buffer *ivb =
+               vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+       struct media_pipeline *media_pipe =
+               media_entity_pipeline(&av->vdev.entity);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct ipu7_isys_stream *stream = av->stream;
+       struct ipu7_isys_buffer *ib = &ivb->ib;
+       struct ipu7_insys_buffset *buf = NULL;
+       struct ipu7_isys_buffer_list bl;
+       struct isys_fw_msgs *msg;
+       unsigned long flags;
+       dma_addr_t dma;
+       int ret;
+
+       dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name);
+
+       dma = ivb->dma_addr;
+       dev_dbg(dev, "iova: iova %pad\n", &dma);
+
+       spin_lock_irqsave(&aq->lock, flags);
+       list_add(&ib->head, &aq->incoming);
+       spin_unlock_irqrestore(&aq->lock, flags);
+
+       if (!media_pipe || !vb->vb2_queue->start_streaming_called) {
+               dev_dbg(dev, "media pipeline is not ready for %s\n",
+                       av->vdev.name);
+               return;
+       }
+
+       mutex_lock(&stream->mutex);
+
+       if (stream->nr_streaming != stream->nr_queues) {
+               dev_dbg(dev, "not streaming yet, adding to incoming\n");
+               goto out;
+       }
+
+       /*
+        * We just put one buffer to the incoming list of this queue
+        * (above). Let's see whether all queues in the pipeline would
+        * have a buffer.
+        */
+       ret = buffer_list_get(stream, &bl);
+       if (ret < 0) {
+               dev_dbg(dev, "No buffers available\n");
+               goto out;
+       }
+
+       msg = ipu7_get_fw_msg_buf(stream);
+       if (!msg) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       buf = &msg->fw_msg.frame;
+
+       ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl);
+
+       ipu7_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins);
+
+       if (!stream->streaming) {
+               ret = ipu7_isys_stream_start(av, &bl, true);
+               if (ret)
+                       dev_err(dev, "stream start failed.\n");
+               goto out;
+       }
+
+       /*
+        * We must queue the buffers in the buffer list to the
+        * appropriate video buffer queues BEFORE passing them to the
+        * firmware since we could get a buffer event back before we
+        * have queued them ourselves to the active queue.
+        */
+       ipu7_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
+
+       ret = ipu7_fw_isys_complex_cmd(stream->isys, stream->stream_handle,
+                                      buf, msg->dma_addr, sizeof(*buf),
+                                      IPU_INSYS_SEND_TYPE_STREAM_CAPTURE);
+       if (ret < 0)
+               dev_err(dev, "send stream capture failed\n");
+
+out:
+       mutex_unlock(&stream->mutex);
+}
+
+static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq)
+{
+       struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct media_pad *remote_pad =
+               media_pad_remote_pad_first(av->vdev.entity.pads);
+       struct v4l2_mbus_framefmt format;
+       struct v4l2_subdev *sd;
+       u32 r_stream, code;
+       int ret;
+
+       if (!remote_pad)
+               return -ENOTCONN;
+
+       sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+       r_stream = ipu7_isys_get_src_stream_by_src_pad(sd, remote_pad->index);
+
+       ret = ipu7_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream,
+                                          &format);
+       if (ret) {
+               dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n",
+                       sd->entity.name, remote_pad->index, r_stream);
+               return ret;
+       }
+
+       if (format.width != av->pix_fmt.width ||
+           format.height != av->pix_fmt.height) {
+               dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
+                       av->pix_fmt.width, av->pix_fmt.height, format.width,
+                       format.height);
+               return -EINVAL;
+       }
+
+       code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code;
+       if (format.code != code) {
+               dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n",
+                       code, format.code);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void return_buffers(struct ipu7_isys_queue *aq,
+                          enum vb2_buffer_state state)
+{
+       struct ipu7_isys_buffer *ib;
+       struct vb2_buffer *vb;
+       unsigned long flags;
+
+       spin_lock_irqsave(&aq->lock, flags);
+       /*
+        * Something went wrong (FW crash / HW hang / not all buffers
+        * returned from isys) if there are still buffers queued in active
+        * queue. We have to clean up places a bit.
+        */
+       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, state);
+
+               spin_lock_irqsave(&aq->lock, flags);
+       }
+
+       while (!list_empty(&aq->incoming)) {
+               ib = list_last_entry(&aq->incoming, 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, state);
+
+               spin_lock_irqsave(&aq->lock, flags);
+       }
+
+       spin_unlock_irqrestore(&aq->lock, flags);
+}
+
+static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av)
+{
+       video_device_pipeline_stop(&av->vdev);
+       ipu7_isys_put_stream(av->stream);
+       av->stream = NULL;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+       struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q);
+       struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       const struct ipu7_isys_pixelformat *pfmt =
+               ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+       struct ipu7_isys_buffer_list __bl, *bl = NULL;
+       struct ipu7_isys_stream *stream;
+       struct media_entity *source_entity = NULL;
+       int nr_queues, ret;
+
+       dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
+               av->vdev.name, av->pix_fmt.width, av->pix_fmt.height,
+               pfmt->css_pixelformat);
+
+       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;
+       ret = buffer_list_get(stream, bl);
+       if (ret < 0) {
+               dev_warn(dev, "no buffer available, DRIVER BUG?\n");
+               goto out;
+       }
+
+       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);
+
+       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);
+
+       return ret;
+}
+
+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;
+
+       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);
+       mutex_unlock(&av->isys->stream_mutex);
+
+       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);
+}
+
+static unsigned int
+get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time)
+{
+       struct ipu7_isys *isys = stream->isys;
+       struct device *dev = &isys->adev->auxdev.dev;
+       unsigned int i;
+
+       /*
+        * The timestamp is invalid as no TSC in some FPGA platform,
+        * so get the sequence from pipeline directly in this case.
+        */
+       if (time == 0)
+               return atomic_read(&stream->sequence) - 1;
+
+       for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++)
+               if (time == stream->seq[i].timestamp) {
+                       dev_dbg(dev, "SOF: using seq nr %u for ts %llu\n",
+                               stream->seq[i].sequence, time);
+                       return stream->seq[i].sequence;
+               }
+
+       dev_dbg(dev, "SOF: looking for %llu\n", time);
+       for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++)
+               dev_dbg(dev, "SOF: sequence %u, timestamp value %llu\n",
+                       stream->seq[i].sequence, stream->seq[i].timestamp);
+       dev_dbg(dev, "SOF sequence number not found\n");
+
+       return atomic_read(&stream->sequence) - 1;
+}
+
+static u64 get_sof_ns_delta(struct ipu7_isys_video *av, u64 time)
+{
+       struct ipu7_bus_device *adev = av->isys->adev;
+       struct ipu7_device *isp = adev->isp;
+       u64 delta, tsc_now;
+
+       ipu_buttress_tsc_read(isp, &tsc_now);
+       if (!tsc_now)
+               return 0;
+
+       delta = tsc_now - time;
+
+       return ipu_buttress_tsc_ticks_to_ns(delta, isp);
+}
+
+static void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib,
+                                            u64 time)
+{
+       struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+       struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+       struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+       struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct ipu7_isys_stream *stream = av->stream;
+       u64 ns;
+       u32 sequence;
+
+       ns = ktime_get_ns() - get_sof_ns_delta(av, time);
+       sequence = get_sof_sequence_by_timestamp(stream, time);
+
+       vbuf->vb2_buf.timestamp = ns;
+       vbuf->sequence = sequence;
+
+       dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n",
+               av->vdev.name, ktime_get_ns(), sequence);
+       dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index,
+               vbuf->vb2_buf.timestamp);
+}
+
+static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib)
+{
+       struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+
+       if (atomic_read(&ib->str2mmio_flag)) {
+               vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+               /*
+                * Operation on buffer is ended with error and will be reported
+                * to the userspace when it is de-queued
+                */
+               atomic_set(&ib->str2mmio_flag, 0);
+       } else {
+               vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+       }
+}
+
+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream,
+                              struct ipu7_insys_resp *info)
+{
+       struct ipu7_isys_queue *aq = stream->output_pins[info->pin_id].aq;
+       u64 time = ((u64)info->timestamp[1] << 32 | info->timestamp[0]);
+       struct ipu7_isys *isys = stream->isys;
+       struct device *dev = &isys->adev->auxdev.dev;
+       struct ipu7_isys_buffer *ib;
+       struct vb2_buffer *vb;
+       unsigned long flags;
+       bool first = true;
+       struct vb2_v4l2_buffer *buf;
+
+       dev_dbg(dev, "buffer: %s: received buffer %8.8x %d\n",
+               ipu7_isys_queue_to_video(aq)->vdev.name, info->pin.addr,
+               info->frame_id);
+
+       spin_lock_irqsave(&aq->lock, flags);
+       if (list_empty(&aq->active)) {
+               spin_unlock_irqrestore(&aq->lock, flags);
+               dev_err(dev, "active queue empty\n");
+               return;
+       }
+
+       list_for_each_entry_reverse(ib, &aq->active, head) {
+               struct ipu7_isys_video_buffer *ivb;
+               struct vb2_v4l2_buffer *vvb;
+               dma_addr_t addr;
+
+               vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+               vvb = to_vb2_v4l2_buffer(vb);
+               ivb = vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+               addr = ivb->dma_addr;
+
+               if (info->pin.addr != addr) {
+                       if (first)
+                               dev_err(dev, "Unexpected buffer address %pad\n",
+                                       &addr);
+
+                       first = false;
+                       continue;
+               }
+
+               dev_dbg(dev, "buffer: found buffer %pad\n", &addr);
+
+               buf = to_vb2_v4l2_buffer(vb);
+               buf->field = V4L2_FIELD_NONE;
+
+               list_del(&ib->head);
+               spin_unlock_irqrestore(&aq->lock, flags);
+
+               ipu7_isys_buf_calc_sequence_time(ib, time);
+
+               ipu7_isys_queue_buf_done(ib);
+
+               return;
+       }
+
+       dev_err(dev, "Failed to find a matching video buffer\n");
+
+       spin_unlock_irqrestore(&aq->lock, flags);
+}
+
+static const struct vb2_ops ipu7_isys_queue_ops = {
+       .queue_setup = ipu7_isys_queue_setup,
+       .buf_init = ipu7_isys_buf_init,
+       .buf_prepare = ipu7_isys_buf_prepare,
+       .buf_cleanup = ipu7_isys_buf_cleanup,
+       .start_streaming = start_streaming,
+       .stop_streaming = stop_streaming,
+       .buf_queue = buf_queue,
+};
+
+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq)
+{
+       struct ipu7_isys *isys = ipu7_isys_queue_to_video(aq)->isys;
+       struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+       struct ipu7_bus_device *adev = isys->adev;
+       int ret;
+
+       if (!aq->vbq.io_modes)
+               aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF;
+
+       aq->vbq.drv_priv = isys;
+       aq->vbq.ops = &ipu7_isys_queue_ops;
+       aq->vbq.lock = &av->mutex;
+       aq->vbq.mem_ops = &vb2_dma_sg_memops;
+       aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       aq->vbq.min_queued_buffers = 1;
+       aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+       ret = vb2_queue_init(&aq->vbq);
+       if (ret)
+               return ret;
+
+       aq->dev = &adev->auxdev.dev;
+       aq->vbq.dev = &adev->isp->pdev->dev;
+       spin_lock_init(&aq->lock);
+       INIT_LIST_HEAD(&aq->active);
+       INIT_LIST_HEAD(&aq->incoming);
+
+       return 0;
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.h b/drivers/staging/media/ipu7/ipu7-isys-queue.h
new file mode 100644 (file)
index 0000000..0cb08a3
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_QUEUE_H
+#define IPU7_ISYS_QUEUE_H
+
+#include <linux/atomic.h>
+#include <linux/container_of.h>
+#include <linux/list.h>
+#include <linux/spinlock_types.h>
+
+#include <media/videobuf2-v4l2.h>
+
+struct device;
+struct ipu7_isys_stream;
+struct ipu7_insys_resp;
+struct ipu7_insys_buffset;
+
+struct ipu7_isys_queue {
+       struct vb2_queue vbq;
+       struct list_head node;
+       struct device *dev;
+       spinlock_t lock;
+       struct list_head active;
+       struct list_head incoming;
+       unsigned int fw_output;
+};
+
+struct ipu7_isys_buffer {
+       struct list_head head;
+       atomic_t str2mmio_flag;
+};
+
+struct ipu7_isys_video_buffer {
+       struct vb2_v4l2_buffer vb_v4l2;
+       struct ipu7_isys_buffer ib;
+       dma_addr_t dma_addr;
+};
+
+#define IPU_ISYS_BUFFER_LIST_FL_INCOMING       BIT(0)
+#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1)
+#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE      BIT(2)
+
+struct ipu7_isys_buffer_list {
+       struct list_head head;
+       unsigned int nbufs;
+};
+
+#define vb2_queue_to_isys_queue(__vb2)                         \
+       container_of(__vb2, struct ipu7_isys_queue, vbq)
+
+#define ipu7_isys_to_isys_video_buffer(__ib)                   \
+       container_of(__ib, struct ipu7_isys_video_buffer, ib)
+
+#define vb2_buffer_to_ipu7_isys_video_buffer(__vvb)                    \
+       container_of(__vvb, struct ipu7_isys_video_buffer, vb_v4l2)
+
+#define ipu7_isys_buffer_to_vb2_buffer(__ib)                           \
+       (&ipu7_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf)
+
+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl,
+                                unsigned long op_flags,
+                                enum vb2_buffer_state state);
+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set,
+                                      struct ipu7_isys_stream *stream,
+                                      struct ipu7_isys_buffer_list *bl);
+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream,
+                              struct ipu7_insys_resp *info);
+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq);
+#endif /* IPU7_ISYS_QUEUE_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.c b/drivers/staging/media/ipu7/ipu7-isys-subdev.c
new file mode 100644 (file)
index 0000000..98b6ef6
--- /dev/null
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/minmax.h>
+#include <linux/types.h>
+
+#include <media/media-entity.h>
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include <uapi/linux/media-bus-format.h>
+
+#include "ipu7-bus.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-subdev.h"
+
+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code)
+{
+       switch (code) {
+       case MEDIA_BUS_FMT_RGB565_1X16:
+               return MIPI_CSI2_DT_RGB565;
+       case MEDIA_BUS_FMT_RGB888_1X24:
+               return MIPI_CSI2_DT_RGB888;
+       case MEDIA_BUS_FMT_YUYV10_1X20:
+               return MIPI_CSI2_DT_YUV422_10B;
+       case MEDIA_BUS_FMT_UYVY8_1X16:
+       case MEDIA_BUS_FMT_YUYV8_1X16:
+               return MIPI_CSI2_DT_YUV422_8B;
+       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 MIPI_CSI2_DT_RAW12;
+       case MEDIA_BUS_FMT_Y10_1X10:
+       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 MIPI_CSI2_DT_RAW10;
+       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 MIPI_CSI2_DT_RAW8;
+       default:
+               WARN_ON(1);
+               return 0xff;
+       }
+}
+
+bool ipu7_isys_is_bayer_format(u32 code)
+{
+       switch (ipu7_isys_mbus_code_to_mipi(code)) {
+       case MIPI_CSI2_DT_RAW8:
+       case MIPI_CSI2_DT_RAW10:
+       case MIPI_CSI2_DT_RAW12:
+       case MIPI_CSI2_DT_RAW14:
+       case MIPI_CSI2_DT_RAW16:
+       case MIPI_CSI2_DT_RAW20:
+       case MIPI_CSI2_DT_RAW24:
+       case MIPI_CSI2_DT_RAW28:
+               return true;
+       default:
+               return false;
+       }
+}
+
+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y)
+{
+       static const u32 code_map[] = {
+               MEDIA_BUS_FMT_SRGGB8_1X8,
+               MEDIA_BUS_FMT_SGRBG8_1X8,
+               MEDIA_BUS_FMT_SGBRG8_1X8,
+               MEDIA_BUS_FMT_SBGGR8_1X8,
+               MEDIA_BUS_FMT_SRGGB10_1X10,
+               MEDIA_BUS_FMT_SGRBG10_1X10,
+               MEDIA_BUS_FMT_SGBRG10_1X10,
+               MEDIA_BUS_FMT_SBGGR10_1X10,
+               MEDIA_BUS_FMT_SRGGB12_1X12,
+               MEDIA_BUS_FMT_SGRBG12_1X12,
+               MEDIA_BUS_FMT_SGBRG12_1X12,
+               MEDIA_BUS_FMT_SBGGR12_1X12,
+       };
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(code_map); i++)
+               if (code_map[i] == code)
+                       break;
+
+       if (WARN_ON(i == ARRAY_SIZE(code_map)))
+               return code;
+
+       return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))];
+}
+
+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+                            struct v4l2_subdev_state *state,
+                            struct v4l2_subdev_format *format)
+{
+       struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+       u32 code = asd->supported_codes[0];
+       struct v4l2_mbus_framefmt *fmt;
+       u32 other_pad, other_stream;
+       struct v4l2_rect *crop;
+       unsigned int i;
+       int ret;
+
+       /* No transcoding, source and sink formats must match. */
+       if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
+           sd->entity.num_pads > 1)
+               return v4l2_subdev_get_fmt(sd, state, format);
+
+       format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH,
+                                    IPU_ISYS_MAX_WIDTH);
+       format->format.height = clamp(format->format.height,
+                                     IPU_ISYS_MIN_HEIGHT,
+                                     IPU_ISYS_MAX_HEIGHT);
+
+       for (i = 0; asd->supported_codes[i]; i++) {
+               if (asd->supported_codes[i] == format->format.code) {
+                       code = asd->supported_codes[i];
+                       break;
+               }
+       }
+       format->format.code = code;
+       format->format.field = V4L2_FIELD_NONE;
+
+       /* Store the format and propagate it to the source pad. */
+       fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+       if (!fmt)
+               return -EINVAL;
+
+       *fmt = format->format;
+
+       if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
+               return 0;
+
+       /* propagate format to following source pad */
+       fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+                                                          format->stream);
+       if (!fmt)
+               return -EINVAL;
+
+       *fmt = format->format;
+
+       ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+                                                   format->pad,
+                                                   format->stream,
+                                                   &other_pad,
+                                                   &other_stream);
+       if (ret)
+               return -EINVAL;
+
+       crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
+       /* reset crop */
+       crop->left = 0;
+       crop->top = 0;
+       crop->width = fmt->width;
+       crop->height = fmt->height;
+
+       return 0;
+}
+
+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+                                   struct v4l2_subdev_state *state,
+                                   struct v4l2_subdev_mbus_code_enum *code)
+{
+       struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+       const u32 *supported_codes = asd->supported_codes;
+       u32 index;
+
+       for (index = 0; supported_codes[index]; index++) {
+               if (index == code->index) {
+                       code->code = supported_codes[index];
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int subdev_set_routing(struct v4l2_subdev *sd,
+                             struct v4l2_subdev_state *state,
+                             struct v4l2_subdev_krouting *routing)
+{
+       static const struct v4l2_mbus_framefmt fmt = {
+               .width = 4096,
+               .height = 3072,
+               .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+               .field = V4L2_FIELD_NONE,
+       };
+       int ret;
+
+       ret = v4l2_subdev_routing_validate(sd, routing,
+                                          V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+       if (ret)
+               return ret;
+
+       return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt);
+}
+
+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+                                struct v4l2_mbus_framefmt *format)
+{
+       struct v4l2_subdev_state *state;
+       struct v4l2_mbus_framefmt *fmt;
+
+       if (!sd || !format)
+               return -EINVAL;
+
+       state = v4l2_subdev_lock_and_get_active_state(sd);
+       fmt = v4l2_subdev_state_get_format(state, pad, stream);
+       if (fmt)
+               *format = *fmt;
+       v4l2_subdev_unlock_state(state);
+
+       return fmt ? 0 : -EINVAL;
+}
+
+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
+{
+       struct v4l2_subdev_state *state;
+       struct v4l2_subdev_route *routes;
+       u32 source_stream = 0;
+       unsigned int i;
+
+       state = v4l2_subdev_lock_and_get_active_state(sd);
+       if (!state)
+               return 0;
+
+       routes = state->routing.routes;
+       for (i = 0; i < state->routing.num_routes; i++) {
+               if (routes[i].source_pad == pad) {
+                       source_stream = routes[i].source_stream;
+                       break;
+               }
+       }
+
+       v4l2_subdev_unlock_state(state);
+
+       return source_stream;
+}
+
+static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd,
+                                      struct v4l2_subdev_state *state)
+{
+       struct v4l2_subdev_route route = {
+               .sink_pad = 0,
+               .sink_stream = 0,
+               .source_pad = 1,
+               .source_stream = 0,
+               .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+       };
+       struct v4l2_subdev_krouting routing = {
+               .num_routes = 1,
+               .routes = &route,
+       };
+
+       return subdev_set_routing(sd, state, &routing);
+}
+
+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd,
+                                struct v4l2_subdev_state *state,
+                                enum v4l2_subdev_format_whence which,
+                                struct v4l2_subdev_krouting *routing)
+{
+       return subdev_set_routing(sd, state, routing);
+}
+
+static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = {
+       .init_state = ipu7_isys_subdev_init_state,
+};
+
+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd,
+                         const struct v4l2_subdev_ops *ops,
+                         unsigned int nr_ctrls,
+                         unsigned int num_sink_pads,
+                         unsigned int num_source_pads)
+{
+       unsigned int num_pads = num_sink_pads + num_source_pads;
+       unsigned int i;
+       int ret;
+
+       v4l2_subdev_init(&asd->sd, ops);
+
+       asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+                        V4L2_SUBDEV_FL_HAS_EVENTS |
+                        V4L2_SUBDEV_FL_STREAMS;
+       asd->sd.owner = THIS_MODULE;
+       asd->sd.dev = &asd->isys->adev->auxdev.dev;
+       asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+       asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops;
+
+       asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
+                               sizeof(*asd->pad), GFP_KERNEL);
+       if (!asd->pad)
+               return -ENOMEM;
+
+       for (i = 0; i < num_sink_pads; i++)
+               asd->pad[i].flags = MEDIA_PAD_FL_SINK |
+                       MEDIA_PAD_FL_MUST_CONNECT;
+
+       for (i = num_sink_pads; i < num_pads; i++)
+               asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+       ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
+       if (ret) {
+               pr_err("isys subdev init failed %d.\n", ret);
+               return ret;
+       }
+
+       if (asd->ctrl_init) {
+               ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
+               if (ret)
+                       goto out_media_entity_cleanup;
+
+               asd->ctrl_init(&asd->sd);
+               if (asd->ctrl_handler.error) {
+                       ret = asd->ctrl_handler.error;
+                       goto out_v4l2_ctrl_handler_free;
+               }
+
+               asd->sd.ctrl_handler = &asd->ctrl_handler;
+       }
+
+       asd->source = -1;
+
+       return 0;
+
+out_v4l2_ctrl_handler_free:
+       v4l2_ctrl_handler_free(&asd->ctrl_handler);
+
+out_media_entity_cleanup:
+       media_entity_cleanup(&asd->sd.entity);
+
+       return ret;
+}
+
+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd)
+{
+       media_entity_cleanup(&asd->sd.entity);
+       v4l2_ctrl_handler_free(&asd->ctrl_handler);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.h b/drivers/staging/media/ipu7/ipu7-isys-subdev.h
new file mode 100644 (file)
index 0000000..1057ec3
--- /dev/null
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_SUBDEV_H
+#define IPU7_ISYS_SUBDEV_H
+
+#include <linux/container_of.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+struct ipu7_isys;
+
+struct ipu7_isys_subdev {
+       struct v4l2_subdev sd;
+       struct ipu7_isys *isys;
+       u32 const *supported_codes;
+       struct media_pad *pad;
+       struct v4l2_ctrl_handler ctrl_handler;
+       void (*ctrl_init)(struct v4l2_subdev *sd);
+       int source;     /* SSI stream source; -1 if unset */
+};
+
+#define to_ipu7_isys_subdev(__sd)                      \
+       container_of(__sd, struct ipu7_isys_subdev, sd)
+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code);
+bool ipu7_isys_is_bayer_format(u32 code);
+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y);
+
+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+                            struct v4l2_subdev_state *state,
+                            struct v4l2_subdev_format *format);
+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+                                   struct v4l2_subdev_state *state,
+                                   struct v4l2_subdev_mbus_code_enum
+                                   *code);
+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+                                struct v4l2_mbus_framefmt *format);
+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd,
+                                struct v4l2_subdev_state *state,
+                                enum v4l2_subdev_format_whence which,
+                                struct v4l2_subdev_krouting *routing);
+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd,
+                         const struct v4l2_subdev_ops *ops,
+                         unsigned int nr_ctrls,
+                         unsigned int num_sink_pads,
+                         unsigned int num_source_pads);
+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd);
+#endif /* IPU7_ISYS_SUBDEV_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c
new file mode 100644 (file)
index 0000000..8756da3
--- /dev/null
@@ -0,0 +1,1112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/align.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress-regs.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-video.h"
+#include "ipu7-platform-regs.h"
+
+const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = {
+       {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW16},
+       {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,
+        IPU_INSYS_FRAME_FORMAT_RAW8},
+       {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,
+        IPU_INSYS_FRAME_FORMAT_RAW8},
+       {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,
+        IPU_INSYS_FRAME_FORMAT_RAW8},
+       {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
+        IPU_INSYS_FRAME_FORMAT_RAW8},
+       {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW12},
+       {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW12},
+       {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW12},
+       {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
+        IPU_INSYS_FRAME_FORMAT_RAW12},
+       {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW10},
+       {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW10},
+       {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW10},
+       {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+        IPU_INSYS_FRAME_FORMAT_RAW10},
+       {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
+        IPU_INSYS_FRAME_FORMAT_UYVY},
+       {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
+        IPU_INSYS_FRAME_FORMAT_YUYV},
+       {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
+        IPU_INSYS_FRAME_FORMAT_RGB565},
+       {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
+        IPU_INSYS_FRAME_FORMAT_RGBA888},
+};
+
+static int video_open(struct file *file)
+{
+       return v4l2_fh_open(file);
+}
+
+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) {
+               const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i];
+
+               if (pfmt->pixelformat == pixelformat)
+                       return pfmt;
+       }
+
+       return &ipu7_isys_pfmts[0];
+}
+
+static int ipu7_isys_vidioc_querycap(struct file *file, void *fh,
+                                    struct v4l2_capability *cap)
+{
+       struct ipu7_isys_video *av = video_drvdata(file);
+
+       strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver));
+       strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));
+
+       return 0;
+}
+
+static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh,
+                                    struct v4l2_fmtdesc *f)
+{
+       unsigned int i, num_found;
+
+       for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) {
+               if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       continue;
+
+               if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code)
+                       continue;
+
+               if (num_found < f->index) {
+                       num_found++;
+                       continue;
+               }
+
+               f->flags = 0;
+               f->pixelformat = ipu7_isys_pfmts[i].pixelformat;
+
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh,
+                                           struct v4l2_frmsizeenum *fsize)
+{
+       unsigned int i;
+
+       if (fsize->index > 0)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) {
+               if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat)
+                       continue;
+
+               fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+               fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH;
+               fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH;
+               fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT;
+               fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT;
+               fsize->stepwise.step_width = 2;
+               fsize->stepwise.step_height = 2;
+
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+                                         struct v4l2_format *f)
+{
+       struct ipu7_isys_video *av = video_drvdata(file);
+
+       f->fmt.pix = av->pix_fmt;
+
+       return 0;
+}
+
+static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type,
+                                 u32 *format, u32 *width, u32 *height,
+                                 u32 *bytesperline, u32 *sizeimage)
+{
+       const struct ipu7_isys_pixelformat *pfmt =
+               ipu7_isys_get_isys_format(*format);
+
+       *format = pfmt->pixelformat;
+       *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH);
+       *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT);
+
+       if (pfmt->bpp != pfmt->bpp_packed)
+               *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
+       else
+               *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE);
+
+       *bytesperline = ALIGN(*bytesperline, 64U);
+
+       /*
+        * (height + 1) * bytesperline due to a hardware issue: the DMA unit
+        * is a power of two, and a line should be transferred as few units
+        * as possible. The result is that up to line length more data than
+        * the image size may be transferred to memory after the image.
+        * Another limitation is the GDA allocation unit size. For low
+        * resolution it gives a bigger number. Use larger one to avoid
+        * memory corruption.
+        */
+       *sizeimage = *bytesperline * *height +
+               max(*bytesperline,
+                   av->isys->pdata->ipdata->isys_dma_overshoot);
+}
+
+static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av,
+                                             struct v4l2_format *f)
+{
+       ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat,
+                             &f->fmt.pix.width, &f->fmt.pix.height,
+                             &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage);
+
+       f->fmt.pix.field = V4L2_FIELD_NONE;
+       f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;
+       f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+       f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
+       f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+                                           struct v4l2_format *f)
+{
+       struct ipu7_isys_video *av = video_drvdata(file);
+
+       if (vb2_is_busy(&av->aq.vbq))
+               return -EBUSY;
+
+       __ipu_isys_vidioc_try_fmt_vid_cap(av, f);
+
+       return 0;
+}
+
+static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+                                         struct v4l2_format *f)
+{
+       struct ipu7_isys_video *av = video_drvdata(file);
+
+       ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f);
+       av->pix_fmt = f->fmt.pix;
+
+       return 0;
+}
+
+static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv,
+                                   struct v4l2_requestbuffers *p)
+{
+       struct ipu7_isys_video *av = video_drvdata(file);
+       int ret;
+
+       av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);
+       av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);
+
+       ret = vb2_queue_change_type(&av->aq.vbq, p->type);
+       if (ret)
+               return ret;
+
+       return vb2_ioctl_reqbufs(file, priv, p);
+}
+
+static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv,
+                                       struct v4l2_create_buffers *p)
+{
+       struct ipu7_isys_video *av = video_drvdata(file);
+       int ret;
+
+       av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);
+       av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);
+
+       ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);
+       if (ret)
+               return ret;
+
+       return vb2_ioctl_create_bufs(file, priv, p);
+}
+
+static int link_validate(struct media_link *link)
+{
+       struct ipu7_isys_video *av =
+               container_of(link->sink, struct ipu7_isys_video, pad);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct v4l2_subdev_state *s_state;
+       struct v4l2_mbus_framefmt *s_fmt;
+       struct v4l2_subdev *s_sd;
+       struct media_pad *s_pad;
+       u32 s_stream, code;
+       int ret = -EPIPE;
+
+       if (!link->source->entity)
+               return ret;
+
+       s_sd = media_entity_to_v4l2_subdev(link->source->entity);
+       s_state = v4l2_subdev_get_unlocked_active_state(s_sd);
+       if (!s_state)
+               return ret;
+
+       dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",
+               link->source->entity->name, link->source->index,
+               link->sink->entity->name);
+
+       s_pad = media_pad_remote_pad_first(&av->pad);
+       s_stream = ipu7_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);
+
+       v4l2_subdev_lock_state(s_state);
+
+       s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);
+       if (!s_fmt) {
+               dev_err(dev, "failed to get source pad format\n");
+               goto unlock;
+       }
+
+       code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code;
+
+       if (s_fmt->width != av->pix_fmt.width ||
+           s_fmt->height != av->pix_fmt.height || s_fmt->code != code) {
+               dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
+                       s_fmt->width, s_fmt->height, s_fmt->code,
+                       av->pix_fmt.width, av->pix_fmt.height, code);
+               goto unlock;
+       }
+
+       v4l2_subdev_unlock_state(s_state);
+
+       return 0;
+unlock:
+       v4l2_subdev_unlock_state(s_state);
+
+       return ret;
+}
+
+static void get_stream_opened(struct ipu7_isys_video *av)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&av->isys->streams_lock, flags);
+       av->isys->stream_opened++;
+       spin_unlock_irqrestore(&av->isys->streams_lock, flags);
+}
+
+static void put_stream_opened(struct ipu7_isys_video *av)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&av->isys->streams_lock, flags);
+       av->isys->stream_opened--;
+       spin_unlock_irqrestore(&av->isys->streams_lock, flags);
+}
+
+static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av,
+                               struct ipu7_insys_stream_cfg *cfg)
+{
+       struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
+       struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
+       struct ipu7_isys_stream *stream = av->stream;
+       const struct ipu7_isys_pixelformat *pfmt =
+               ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+       struct ipu7_insys_output_pin *output_pin;
+       struct ipu7_insys_input_pin *input_pin;
+       int input_pins = cfg->nof_input_pins++;
+       struct ipu7_isys_queue *aq = &av->aq;
+       struct ipu7_isys *isys = av->isys;
+       struct device *dev = &isys->adev->auxdev.dev;
+       struct v4l2_mbus_framefmt fmt;
+       int output_pins;
+       u32 src_stream;
+       int ret;
+
+       src_stream = ipu7_isys_get_src_stream_by_src_pad(sd, src_pad->index);
+       ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
+                                          &fmt);
+       if (ret < 0) {
+               dev_err(dev, "can't get stream format (%d)\n", ret);
+               return ret;
+       }
+
+       input_pin = &cfg->input_pins[input_pins];
+       input_pin->input_res.width = fmt.width;
+       input_pin->input_res.height = fmt.height;
+       input_pin->dt = av->dt;
+       input_pin->disable_mipi_unpacking = 0;
+       pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+       if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE)
+               input_pin->disable_mipi_unpacking = 1;
+       input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE;
+       input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME;
+       /* if enable polling isys interrupt, the follow values maybe set */
+       input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF |
+               IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED |
+               IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF |
+               IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED;
+
+       output_pins = cfg->nof_output_pins++;
+       aq->fw_output = output_pins;
+       stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready;
+       stream->output_pins[output_pins].aq = aq;
+
+       output_pin = &cfg->output_pins[output_pins];
+       /* output pin msg link */
+       output_pin->link.buffer_lines = 0;
+       output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE;
+       output_pin->link.granularity_pointer_update = 0;
+       output_pin->link.msg_link_streaming_mode =
+               IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF;
+
+       output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE;
+       output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE;
+       output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM;
+       output_pin->link.use_sw_managed = 1;
+       /* TODO: set the snoop bit for metadata capture */
+       output_pin->link.is_snoop = 0;
+
+       /* output pin crop */
+       output_pin->crop.line_top = 0;
+       output_pin->crop.line_bottom = 0;
+
+       /* output de-compression */
+       output_pin->dpcm.enable = 0;
+
+       /* frame format type */
+       pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+       output_pin->ft = (u16)pfmt->css_pixelformat;
+
+       /* stride in bytes */
+       output_pin->stride = av->pix_fmt.bytesperline;
+       output_pin->send_irq = 1;
+       output_pin->early_ack_en = 0;
+
+       /* input pin id */
+       output_pin->input_pin_id = input_pins;
+
+       return 0;
+}
+
+/* Create stream and start it using the CSS FW ABI. */
+static int start_stream_firmware(struct ipu7_isys_video *av,
+                                struct ipu7_isys_buffer_list *bl)
+{
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct ipu7_isys_stream *stream = av->stream;
+       struct ipu7_insys_stream_cfg *stream_cfg;
+       struct ipu7_insys_buffset *buf = NULL;
+       struct isys_fw_msgs *msg = NULL;
+       struct ipu7_isys_queue *aq;
+       int ret, retout, tout;
+       u16 send_type;
+
+       if (WARN_ON(!bl))
+               return -EIO;
+
+       msg = ipu7_get_fw_msg_buf(stream);
+       if (!msg)
+               return -ENOMEM;
+
+       stream_cfg = &msg->fw_msg.stream;
+       stream_cfg->port_id = stream->stream_source;
+       stream_cfg->vc = stream->vc;
+       stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP |
+                                    IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ;
+
+       list_for_each_entry(aq, &stream->queues, node) {
+               struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq);
+
+               ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg);
+               if (ret < 0) {
+                       ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+                       return ret;
+               }
+       }
+
+       ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg);
+
+       stream->nr_output_pins = stream_cfg->nof_output_pins;
+
+       reinit_completion(&stream->stream_open_completion);
+
+       ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle,
+                                      stream_cfg, msg->dma_addr,
+                                      sizeof(*stream_cfg),
+                                      IPU_INSYS_SEND_TYPE_STREAM_OPEN);
+       if (ret < 0) {
+               dev_err(dev, "can't open stream (%d)\n", ret);
+               ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+               return ret;
+       }
+
+       get_stream_opened(av);
+
+       tout = wait_for_completion_timeout(&stream->stream_open_completion,
+                                          FW_CALL_TIMEOUT_JIFFIES);
+
+       ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+
+       if (!tout) {
+               dev_err(dev, "stream open time out\n");
+               ret = -ETIMEDOUT;
+               goto out_put_stream_opened;
+       }
+       if (stream->error) {
+               dev_err(dev, "stream open error: %d\n", stream->error);
+               ret = -EIO;
+               goto out_put_stream_opened;
+       }
+       dev_dbg(dev, "start stream: open complete\n");
+
+       msg = ipu7_get_fw_msg_buf(stream);
+       if (!msg) {
+               ret = -ENOMEM;
+               goto out_put_stream_opened;
+       }
+       buf = &msg->fw_msg.frame;
+
+       ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl);
+       ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
+
+       reinit_completion(&stream->stream_start_completion);
+
+       send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
+       ipu7_fw_isys_dump_frame_buff_set(dev, buf,
+                                        stream_cfg->nof_output_pins);
+       ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf,
+                                      msg->dma_addr, sizeof(*buf),
+                                      send_type);
+       if (ret < 0) {
+               dev_err(dev, "can't start streaming (%d)\n", ret);
+               goto out_stream_close;
+       }
+
+       tout = wait_for_completion_timeout(&stream->stream_start_completion,
+                                          FW_CALL_TIMEOUT_JIFFIES);
+       if (!tout) {
+               dev_err(dev, "stream start time out\n");
+               ret = -ETIMEDOUT;
+               goto out_stream_close;
+       }
+       if (stream->error) {
+               dev_err(dev, "stream start error: %d\n", stream->error);
+               ret = -EIO;
+               goto out_stream_close;
+       }
+       dev_dbg(dev, "start stream: complete\n");
+
+       return 0;
+
+out_stream_close:
+       reinit_completion(&stream->stream_close_completion);
+
+       retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle,
+                                        IPU_INSYS_SEND_TYPE_STREAM_CLOSE);
+       if (retout < 0) {
+               dev_dbg(dev, "can't close stream (%d)\n", retout);
+               goto out_put_stream_opened;
+       }
+
+       tout = wait_for_completion_timeout(&stream->stream_close_completion,
+                                          FW_CALL_TIMEOUT_JIFFIES);
+       if (!tout)
+               dev_err(dev, "stream close time out with error %d\n",
+                       stream->error);
+       else
+               dev_dbg(dev, "stream close complete\n");
+
+out_put_stream_opened:
+       put_stream_opened(av);
+
+       return ret;
+}
+
+static void stop_streaming_firmware(struct ipu7_isys_video *av)
+{
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct ipu7_isys_stream *stream = av->stream;
+       int ret, tout;
+
+       reinit_completion(&stream->stream_stop_completion);
+
+       ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle,
+                                     IPU_INSYS_SEND_TYPE_STREAM_FLUSH);
+       if (ret < 0) {
+               dev_err(dev, "can't stop stream (%d)\n", ret);
+               return;
+       }
+
+       tout = wait_for_completion_timeout(&stream->stream_stop_completion,
+                                          FW_CALL_TIMEOUT_JIFFIES);
+       if (!tout)
+               dev_warn(dev, "stream stop time out\n");
+       else if (stream->error)
+               dev_warn(dev, "stream stop error: %d\n", stream->error);
+       else
+               dev_dbg(dev, "stop stream: complete\n");
+}
+
+static void close_streaming_firmware(struct ipu7_isys_video *av)
+{
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct ipu7_isys_stream *stream =  av->stream;
+       int ret, tout;
+
+       reinit_completion(&stream->stream_close_completion);
+
+       ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle,
+                                     IPU_INSYS_SEND_TYPE_STREAM_CLOSE);
+       if (ret < 0) {
+               dev_err(dev, "can't close stream (%d)\n", ret);
+               return;
+       }
+
+       tout = wait_for_completion_timeout(&stream->stream_close_completion,
+                                          FW_CALL_TIMEOUT_JIFFIES);
+       if (!tout)
+               dev_warn(dev, "stream close time out\n");
+       else if (stream->error)
+               dev_warn(dev, "stream close error: %d\n", stream->error);
+       else
+               dev_dbg(dev, "close stream: complete\n");
+
+       put_stream_opened(av);
+}
+
+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av,
+                                  struct media_entity *source_entity,
+                                  int nr_queues)
+{
+       struct ipu7_isys_stream *stream = av->stream;
+       struct ipu7_isys_csi2 *csi2;
+
+       if (WARN_ON(stream->nr_streaming))
+               return -EINVAL;
+
+       stream->nr_queues = nr_queues;
+       atomic_set(&stream->sequence, 0);
+       atomic_set(&stream->buf_id, 0);
+
+       stream->seq_index = 0;
+       memset(stream->seq, 0, sizeof(stream->seq));
+
+       if (WARN_ON(!list_empty(&stream->queues)))
+               return -EINVAL;
+
+       stream->stream_source = stream->asd->source;
+
+       csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+       csi2->receiver_errors = 0;
+       stream->source_entity = source_entity;
+
+       dev_dbg(&av->isys->adev->auxdev.dev,
+               "prepare stream: external entity %s\n",
+               stream->source_entity->name);
+
+       return 0;
+}
+
+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream)
+{
+       unsigned long flags;
+       struct device *dev;
+       unsigned int i;
+
+       if (!stream) {
+               pr_err("ipu7-isys: no available stream\n");
+               return;
+       }
+
+       dev = &stream->isys->adev->auxdev.dev;
+
+       spin_lock_irqsave(&stream->isys->streams_lock, flags);
+       for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+               if (&stream->isys->streams[i] == stream) {
+                       if (stream->isys->streams_ref_count[i] > 0)
+                               stream->isys->streams_ref_count[i]--;
+                       else
+                               dev_warn(dev, "invalid stream %d\n", i);
+
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&stream->isys->streams_lock, flags);
+}
+
+static struct ipu7_isys_stream *
+ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd)
+{
+       struct ipu7_isys_stream *stream = NULL;
+       struct ipu7_isys *isys = av->isys;
+       unsigned long flags;
+       unsigned int i;
+       u8 vc = av->vc;
+
+       if (!isys)
+               return NULL;
+
+       spin_lock_irqsave(&isys->streams_lock, flags);
+       for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+               if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&
+                   isys->streams[i].asd == asd) {
+                       isys->streams_ref_count[i]++;
+                       stream = &isys->streams[i];
+                       break;
+               }
+       }
+
+       if (!stream) {
+               for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+                       if (!isys->streams_ref_count[i]) {
+                               isys->streams_ref_count[i]++;
+                               stream = &isys->streams[i];
+                               stream->vc = vc;
+                               stream->asd = asd;
+                               break;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+       return stream;
+}
+
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle)
+{
+       unsigned long flags;
+       struct ipu7_isys_stream *stream = NULL;
+
+       if (!isys)
+               return NULL;
+
+       if (stream_handle >= IPU_ISYS_MAX_STREAMS) {
+               dev_err(&isys->adev->auxdev.dev,
+                       "stream_handle %d is invalid\n", stream_handle);
+               return NULL;
+       }
+
+       spin_lock_irqsave(&isys->streams_lock, flags);
+       if (isys->streams_ref_count[stream_handle] > 0) {
+               isys->streams_ref_count[stream_handle]++;
+               stream = &isys->streams[stream_handle];
+       }
+       spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+       return stream;
+}
+
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc)
+{
+       struct ipu7_isys_stream *stream = NULL;
+       unsigned long flags;
+       unsigned int i;
+
+       if (!isys)
+               return NULL;
+
+       if (source < 0) {
+               dev_err(&isys->adev->auxdev.dev,
+                       "query stream with invalid port number\n");
+               return NULL;
+       }
+
+       spin_lock_irqsave(&isys->streams_lock, flags);
+       for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+               if (!isys->streams_ref_count[i])
+                       continue;
+
+               if (isys->streams[i].stream_source == source &&
+                   isys->streams[i].vc == vc) {
+                       stream = &isys->streams[i];
+                       isys->streams_ref_count[i]++;
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+       return stream;
+}
+
+static u32 get_remote_pad_stream(struct media_pad *r_pad)
+{
+       struct v4l2_subdev_state *state;
+       struct v4l2_subdev *sd;
+       u32 stream_id = 0;
+       unsigned int i;
+
+       sd = media_entity_to_v4l2_subdev(r_pad->entity);
+       state = v4l2_subdev_lock_and_get_active_state(sd);
+       if (!state)
+               return 0;
+
+       for (i = 0; i < state->stream_configs.num_configs; i++) {
+               struct v4l2_subdev_stream_config *cfg =
+                       &state->stream_configs.configs[i];
+               if (cfg->pad == r_pad->index) {
+                       stream_id = cfg->stream;
+                       break;
+               }
+       }
+
+       v4l2_subdev_unlock_state(state);
+
+       return stream_id;
+}
+
+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state,
+                                 struct ipu7_isys_buffer_list *bl)
+{
+       struct ipu7_isys_stream *stream = av->stream;
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct media_pad *r_pad;
+       struct v4l2_subdev *sd;
+       u32 r_stream;
+       int ret = 0;
+
+       dev_dbg(dev, "set stream: %d\n", state);
+
+       if (WARN(!stream->source_entity, "No source entity for stream\n"))
+               return -ENODEV;
+
+       sd = &stream->asd->sd;
+       r_pad = media_pad_remote_pad_first(&av->pad);
+       r_stream = get_remote_pad_stream(r_pad);
+       if (!state) {
+               stop_streaming_firmware(av);
+
+               /* stop sub-device which connects with video */
+               dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n",
+                       sd->name, r_pad->index, BIT_ULL(r_stream));
+               ret = v4l2_subdev_disable_streams(sd, r_pad->index,
+                                                 BIT_ULL(r_stream));
+               if (ret) {
+                       dev_err(dev, "disable streams %s failed with %d\n",
+                               sd->name, ret);
+                       return ret;
+               }
+
+               close_streaming_firmware(av);
+       } else {
+               ret = start_stream_firmware(av, bl);
+               if (ret) {
+                       dev_err(dev, "start stream of firmware failed\n");
+                       return ret;
+               }
+
+               /* start sub-device which connects with video */
+               dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n",
+                       sd->name, r_pad->index, BIT_ULL(r_stream));
+               ret = v4l2_subdev_enable_streams(sd, r_pad->index,
+                                                BIT_ULL(r_stream));
+               if (ret) {
+                       dev_err(dev, "enable streams %s failed with %d\n",
+                               sd->name, ret);
+                       goto out_media_entity_stop_streaming_firmware;
+               }
+       }
+
+       av->streaming = state;
+
+       return 0;
+
+out_media_entity_stop_streaming_firmware:
+       stop_streaming_firmware(av);
+
+       return ret;
+}
+
+static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = {
+       .vidioc_querycap = ipu7_isys_vidioc_querycap,
+       .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt,
+       .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes,
+       .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap,
+       .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap,
+       .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap,
+       .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs,
+       .vidioc_create_bufs = ipu7_isys_vidioc_create_bufs,
+       .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+       .vidioc_querybuf = vb2_ioctl_querybuf,
+       .vidioc_qbuf = vb2_ioctl_qbuf,
+       .vidioc_dqbuf = vb2_ioctl_dqbuf,
+       .vidioc_streamon = vb2_ioctl_streamon,
+       .vidioc_streamoff = vb2_ioctl_streamoff,
+       .vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct media_entity_operations entity_ops = {
+       .link_validate = link_validate,
+};
+
+static const struct v4l2_file_operations isys_fops = {
+       .owner = THIS_MODULE,
+       .poll = vb2_fop_poll,
+       .unlocked_ioctl = video_ioctl2,
+       .mmap = vb2_fop_mmap,
+       .open = video_open,
+       .release = vb2_fop_release,
+};
+
+int ipu7_isys_fw_open(struct ipu7_isys *isys)
+{
+       struct ipu7_bus_device *adev = isys->adev;
+       int ret;
+
+       ret = pm_runtime_resume_and_get(&adev->auxdev.dev);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&isys->mutex);
+
+       if (isys->ref_count++)
+               goto unlock;
+
+       /*
+        * Buffers could have been left to wrong queue at last closure.
+        * Move them now back to empty buffer queue.
+        */
+       ipu7_cleanup_fw_msg_bufs(isys);
+
+       ret = ipu7_fw_isys_open(isys);
+       if (ret < 0)
+               goto out;
+
+unlock:
+       mutex_unlock(&isys->mutex);
+
+       return 0;
+out:
+       isys->ref_count--;
+       mutex_unlock(&isys->mutex);
+       pm_runtime_put(&adev->auxdev.dev);
+
+       return ret;
+}
+
+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);
+}
+
+int ipu7_isys_setup_video(struct ipu7_isys_video *av,
+                         struct media_entity **source_entity, int *nr_queues)
+{
+       const struct ipu7_isys_pixelformat *pfmt =
+               ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+       struct device *dev = &av->isys->adev->auxdev.dev;
+       struct media_pad *source_pad, *remote_pad;
+       struct v4l2_mbus_frame_desc_entry entry;
+       struct v4l2_subdev_route *route = NULL;
+       struct v4l2_subdev_route *r;
+       struct v4l2_subdev_state *state;
+       struct ipu7_isys_subdev *asd;
+       struct v4l2_subdev *remote_sd;
+       struct media_pipeline *pipeline;
+       int ret = -EINVAL;
+
+       *nr_queues = 0;
+
+       remote_pad = media_pad_remote_pad_unique(&av->pad);
+       if (IS_ERR(remote_pad)) {
+               dev_dbg(dev, "failed to get remote pad\n");
+               return PTR_ERR(remote_pad);
+       }
+
+       remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+       asd = to_ipu7_isys_subdev(remote_sd);
+
+       source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
+       if (!source_pad) {
+               dev_dbg(dev, "No external source entity\n");
+               return -ENODEV;
+       }
+
+       *source_entity = source_pad->entity;
+
+       state = v4l2_subdev_lock_and_get_active_state(remote_sd);
+       for_each_active_route(&state->routing, r) {
+               if (r->source_pad == remote_pad->index)
+                       route = r;
+       }
+
+       if (!route) {
+               v4l2_subdev_unlock_state(state);
+               dev_dbg(dev, "Failed to find route\n");
+               return -ENODEV;
+       }
+
+       v4l2_subdev_unlock_state(state);
+
+       ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream,
+                                            to_ipu7_isys_csi2(asd),
+                                            *source_entity, &entry,
+                                            nr_queues);
+       if (ret == -ENOIOCTLCMD) {
+               av->vc = 0;
+               av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code);
+               if (av->dt == 0xff)
+                       return -EINVAL;
+               *nr_queues = 1;
+       } else if (*nr_queues && !ret) {
+               dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
+                       entry.stream, entry.length, entry.bus.csi2.vc,
+                       entry.bus.csi2.dt);
+
+               av->vc = entry.bus.csi2.vc;
+               av->dt = entry.bus.csi2.dt;
+       } else {
+               dev_err(dev, "failed to get remote frame desc\n");
+               return ret;
+       }
+
+       pipeline = media_entity_pipeline(&av->vdev.entity);
+       if (!pipeline)
+               ret = video_device_pipeline_alloc_start(&av->vdev);
+       else
+               ret = video_device_pipeline_start(&av->vdev, pipeline);
+       if (ret < 0) {
+               dev_dbg(dev, "media pipeline start failed\n");
+               return ret;
+       }
+
+       av->stream = ipu7_isys_get_stream(av, asd);
+       if (!av->stream) {
+               video_device_pipeline_stop(&av->vdev);
+               dev_err(dev, "no available stream for firmware\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * Do everything that's needed to initialise things related to video
+ * buffer queue, video node, and the related media entity. The caller
+ * is expected to assign isys field and set the name of the video
+ * device.
+ */
+int ipu7_isys_video_init(struct ipu7_isys_video *av)
+{
+       struct v4l2_format format = {
+               .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+               .fmt.pix = {
+                       .width = 1920,
+                       .height = 1080,
+               },
+       };
+       int ret;
+
+       mutex_init(&av->mutex);
+       av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
+               V4L2_CAP_VIDEO_CAPTURE;
+       av->vdev.vfl_dir = VFL_DIR_RX;
+
+       ret = ipu7_isys_queue_init(&av->aq);
+       if (ret)
+               goto out_mutex_destroy;
+
+       av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+       ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
+       if (ret)
+               goto out_vb2_queue_cleanup;
+
+       av->vdev.entity.ops = &entity_ops;
+       av->vdev.release = video_device_release_empty;
+       av->vdev.fops = &isys_fops;
+       av->vdev.v4l2_dev = &av->isys->v4l2_dev;
+       av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev;
+       av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops;
+       av->vdev.queue = &av->aq.vbq;
+       av->vdev.lock = &av->mutex;
+
+       __ipu_isys_vidioc_try_fmt_vid_cap(av, &format);
+       av->pix_fmt = format.fmt.pix;
+
+       set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
+       video_set_drvdata(&av->vdev, av);
+
+       ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);
+       if (ret)
+               goto out_media_entity_cleanup;
+
+       return ret;
+
+out_media_entity_cleanup:
+       vb2_video_unregister_device(&av->vdev);
+       media_entity_cleanup(&av->vdev.entity);
+
+out_vb2_queue_cleanup:
+       vb2_queue_release(&av->aq.vbq);
+
+out_mutex_destroy:
+       mutex_destroy(&av->mutex);
+
+       return ret;
+}
+
+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av)
+{
+       vb2_video_unregister_device(&av->vdev);
+       media_entity_cleanup(&av->vdev.entity);
+       mutex_destroy(&av->mutex);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.h b/drivers/staging/media/ipu7/ipu7-isys-video.h
new file mode 100644 (file)
index 0000000..1ac1787
--- /dev/null
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_VIDEO_H
+#define IPU7_ISYS_VIDEO_H
+
+#include <linux/atomic.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+
+#include "ipu7-isys-queue.h"
+
+#define IPU_INSYS_OUTPUT_PINS          11U
+#define IPU_ISYS_MAX_PARALLEL_SOF      2U
+
+struct file;
+struct ipu7_isys;
+struct ipu7_isys_csi2;
+struct ipu7_insys_stream_cfg;
+struct ipu7_isys_subdev;
+
+struct ipu7_isys_pixelformat {
+       u32 pixelformat;
+       u32 bpp;
+       u32 bpp_packed;
+       u32 code;
+       u32 css_pixelformat;
+};
+
+struct sequence_info {
+       unsigned int sequence;
+       u64 timestamp;
+};
+
+struct output_pin_data {
+       void (*pin_ready)(struct ipu7_isys_stream *stream,
+                         struct ipu7_insys_resp *info);
+       struct ipu7_isys_queue *aq;
+};
+
+/*
+ * Align with firmware stream. Each stream represents a CSI virtual channel.
+ * May map to multiple video devices
+ */
+struct ipu7_isys_stream {
+       struct mutex mutex;
+       struct media_entity *source_entity;
+       atomic_t sequence;
+       atomic_t buf_id;
+       unsigned int seq_index;
+       struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF];
+       int stream_source;
+       int stream_handle;
+       unsigned int nr_output_pins;
+       struct ipu7_isys_subdev *asd;
+
+       int nr_queues;  /* Number of capture queues */
+       int nr_streaming;
+       int streaming;
+       struct list_head queues;
+       struct completion stream_open_completion;
+       struct completion stream_close_completion;
+       struct completion stream_start_completion;
+       struct completion stream_stop_completion;
+       struct ipu7_isys *isys;
+
+       struct output_pin_data output_pins[IPU_INSYS_OUTPUT_PINS];
+       int error;
+       u8 vc;
+};
+
+struct ipu7_isys_video {
+       struct ipu7_isys_queue aq;
+       /* Serialise access to other fields in the struct. */
+       struct mutex mutex;
+       struct media_pad pad;
+       struct video_device vdev;
+       struct v4l2_pix_format pix_fmt;
+       struct ipu7_isys *isys;
+       struct ipu7_isys_csi2 *csi2;
+       struct ipu7_isys_stream *stream;
+       unsigned int streaming;
+       u8 vc;
+       u8 dt;
+};
+
+#define ipu7_isys_queue_to_video(__aq)                 \
+       container_of(__aq, struct ipu7_isys_video, aq)
+
+extern const struct ipu7_isys_pixelformat ipu7_isys_pfmts[];
+
+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat);
+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av,
+                                  struct media_entity *source_entity,
+                                  int nr_queues);
+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state,
+                                 struct ipu7_isys_buffer_list *bl);
+int ipu7_isys_fw_open(struct ipu7_isys *isys);
+void ipu7_isys_fw_close(struct ipu7_isys *isys);
+int ipu7_isys_setup_video(struct ipu7_isys_video *av,
+                         struct media_entity **source_entity, int *nr_queues);
+int ipu7_isys_video_init(struct ipu7_isys_video *av);
+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av);
+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream);
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys,
+                                u8 stream_handle);
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc);
+#endif /* IPU7_ISYS_VIDEO_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c
new file mode 100644 (file)
index 0000000..cb2f49f
--- /dev/null
@@ -0,0 +1,1166 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <media/ipu-bridge.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7-bus.h"
+#include "ipu7-buttress-regs.h"
+#include "ipu7-cpd.h"
+#include "ipu7-dma.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-mmu.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-csi-phy.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-isys-video.h"
+#include "ipu7-platform-regs.h"
+
+#define ISYS_PM_QOS_VALUE      300
+
+static int
+isys_complete_ext_device_registration(struct ipu7_isys *isys,
+                                     struct v4l2_subdev *sd,
+                                     struct ipu7_isys_csi2_config *csi2)
+{
+       struct device *dev = &isys->adev->auxdev.dev;
+       unsigned int i;
+       int ret;
+
+       v4l2_set_subdev_hostdata(sd, csi2);
+
+       for (i = 0; i < sd->entity.num_pads; i++) {
+               if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+                       break;
+       }
+
+       if (i == sd->entity.num_pads) {
+               dev_warn(dev, "no source pad in external entity\n");
+               ret = -ENOENT;
+               goto skip_unregister_subdev;
+       }
+
+       ret = media_create_pad_link(&sd->entity, i,
+                                   &isys->csi2[csi2->port].asd.sd.entity,
+                                   0, MEDIA_LNK_FL_ENABLED |
+                                   MEDIA_LNK_FL_IMMUTABLE);
+       if (ret) {
+               dev_warn(dev, "can't create link\n");
+               goto skip_unregister_subdev;
+       }
+
+       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
+               isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY;
+
+       return 0;
+
+skip_unregister_subdev:
+       v4l2_device_unregister_subdev(sd);
+       return ret;
+}
+
+static void isys_stream_init(struct ipu7_isys *isys)
+{
+       unsigned int i;
+
+       for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+               mutex_init(&isys->streams[i].mutex);
+               init_completion(&isys->streams[i].stream_open_completion);
+               init_completion(&isys->streams[i].stream_close_completion);
+               init_completion(&isys->streams[i].stream_start_completion);
+               init_completion(&isys->streams[i].stream_stop_completion);
+               INIT_LIST_HEAD(&isys->streams[i].queues);
+               isys->streams[i].isys = isys;
+               isys->streams[i].stream_handle = i;
+               isys->streams[i].vc = INVALID_VC_ID;
+       }
+}
+
+static int isys_fw_log_init(struct ipu7_isys *isys)
+{
+       struct device *dev = &isys->adev->auxdev.dev;
+       struct isys_fw_log *fw_log;
+       void *log_buf;
+
+       if (isys->fw_log)
+               return 0;
+
+       fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL);
+       if (!fw_log)
+               return -ENOMEM;
+
+       mutex_init(&fw_log->mutex);
+
+       log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL);
+       if (!log_buf)
+               return -ENOMEM;
+
+       fw_log->head = log_buf;
+       fw_log->addr = log_buf;
+       fw_log->count = 0;
+       fw_log->size = 0;
+
+       isys->fw_log = fw_log;
+
+       return 0;
+}
+
+/* The .bound() notifier callback when a match is found */
+static int isys_notifier_bound(struct v4l2_async_notifier *notifier,
+                              struct v4l2_subdev *sd,
+                              struct v4l2_async_connection *asc)
+{
+       struct ipu7_isys *isys = container_of(notifier,
+                                             struct ipu7_isys, notifier);
+       struct sensor_async_sd *s_asd =
+               container_of(asc, struct sensor_async_sd, asc);
+       struct device *dev = &isys->adev->auxdev.dev;
+       int ret;
+
+       ret = ipu_bridge_instantiate_vcm(sd->dev);
+       if (ret) {
+               dev_err(dev, "instantiate vcm failed\n");
+               return ret;
+       }
+
+       dev_info(dev, "bind %s nlanes is %d port is %d\n",
+                sd->name, s_asd->csi2.nlanes, s_asd->csi2.port);
+       isys_complete_ext_device_registration(isys, sd, &s_asd->csi2);
+
+       return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
+}
+
+static int isys_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+       struct ipu7_isys *isys = container_of(notifier,
+                                             struct ipu7_isys, notifier);
+
+       dev_info(&isys->adev->auxdev.dev,
+                "All sensor registration completed.\n");
+
+       return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations isys_async_ops = {
+       .bound = isys_notifier_bound,
+       .complete = isys_notifier_complete,
+};
+
+static int isys_notifier_init(struct ipu7_isys *isys)
+{
+       const struct ipu7_isys_internal_csi2_pdata *csi2 =
+               &isys->pdata->ipdata->csi2;
+       struct ipu7_device *isp = isys->adev->isp;
+       struct device *dev = &isp->pdev->dev;
+       unsigned int i;
+       int ret;
+
+       v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev);
+
+       for (i = 0; i < csi2->nports; i++) {
+               struct v4l2_fwnode_endpoint vep = {
+                       .bus_type = V4L2_MBUS_UNKNOWN
+               };
+               struct sensor_async_sd *s_asd;
+               struct fwnode_handle *ep;
+
+               ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0,
+                                                    FWNODE_GRAPH_ENDPOINT_NEXT);
+               if (!ep)
+                       continue;
+
+               ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+               if (ret)
+                       goto err_parse;
+
+               if (vep.bus_type != V4L2_MBUS_CSI2_DPHY &&
+                   vep.bus_type != V4L2_MBUS_CSI2_CPHY) {
+                       ret = -EINVAL;
+                       dev_err(dev, "unsupported bus type %d!\n",
+                               vep.bus_type);
+                       goto err_parse;
+               }
+
+               s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep,
+                                                       struct
+                                                       sensor_async_sd);
+               if (IS_ERR(s_asd)) {
+                       ret = PTR_ERR(s_asd);
+                       goto err_parse;
+               }
+
+               s_asd->csi2.port = vep.base.port;
+               s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
+               s_asd->csi2.bus_type = vep.bus_type;
+
+               fwnode_handle_put(ep);
+
+               continue;
+
+err_parse:
+               fwnode_handle_put(ep);
+               return ret;
+       }
+
+       if (list_empty(&isys->notifier.waiting_list)) {
+               /* isys probe could continue with async subdevs missing */
+               dev_warn(dev, "no subdev found in graph\n");
+               return 0;
+       }
+
+       isys->notifier.ops = &isys_async_ops;
+       ret = v4l2_async_nf_register(&isys->notifier);
+       if (ret) {
+               dev_err(dev, "failed to register async notifier(%d)\n", ret);
+               v4l2_async_nf_cleanup(&isys->notifier);
+       }
+
+       return ret;
+}
+
+static void isys_notifier_cleanup(struct ipu7_isys *isys)
+{
+       v4l2_async_nf_unregister(&isys->notifier);
+       v4l2_async_nf_cleanup(&isys->notifier);
+}
+
+static void isys_unregister_video_devices(struct ipu7_isys *isys)
+{
+       const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+               &isys->pdata->ipdata->csi2;
+       unsigned int i, j;
+
+       for (i = 0; i < csi2_pdata->nports; i++)
+               for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++)
+                       ipu7_isys_video_cleanup(&isys->csi2[i].av[j]);
+}
+
+static int isys_register_video_devices(struct ipu7_isys *isys)
+{
+       const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+               &isys->pdata->ipdata->csi2;
+       unsigned int i, j;
+       int ret;
+
+       for (i = 0; i < csi2_pdata->nports; i++) {
+               for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) {
+                       struct ipu7_isys_video *av = &isys->csi2[i].av[j];
+
+                       snprintf(av->vdev.name, sizeof(av->vdev.name),
+                                IPU_ISYS_ENTITY_PREFIX " ISYS Capture %u",
+                                i * IPU7_NR_OF_CSI2_SRC_PADS + j);
+                       av->isys = isys;
+                       av->aq.vbq.buf_struct_size =
+                               sizeof(struct ipu7_isys_video_buffer);
+
+                       ret = ipu7_isys_video_init(av);
+                       if (ret)
+                               goto fail;
+               }
+       }
+
+       return 0;
+
+fail:
+       i = i + 1U;
+       while (i--) {
+               while (j--)
+                       ipu7_isys_video_cleanup(&isys->csi2[i].av[j]);
+               j = IPU7_NR_OF_CSI2_SRC_PADS;
+       }
+
+       return ret;
+}
+
+static void isys_csi2_unregister_subdevices(struct ipu7_isys *isys)
+{
+       const struct ipu7_isys_internal_csi2_pdata *csi2 =
+               &isys->pdata->ipdata->csi2;
+       unsigned int i;
+
+       for (i = 0; i < csi2->nports; i++)
+               ipu7_isys_csi2_cleanup(&isys->csi2[i]);
+}
+
+static int isys_csi2_register_subdevices(struct ipu7_isys *isys)
+{
+       const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+               &isys->pdata->ipdata->csi2;
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < csi2_pdata->nports; i++) {
+               ret = ipu7_isys_csi2_init(&isys->csi2[i], isys,
+                                         isys->pdata->base +
+                                         csi2_pdata->offsets[i], i);
+               if (ret)
+                       goto fail;
+       }
+
+       isys->isr_csi2_mask = IPU7_CSI_RX_LEGACY_IRQ_MASK;
+
+       return 0;
+
+fail:
+       while (i--)
+               ipu7_isys_csi2_cleanup(&isys->csi2[i]);
+
+       return ret;
+}
+
+static int isys_csi2_create_media_links(struct ipu7_isys *isys)
+{
+       const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+               &isys->pdata->ipdata->csi2;
+       struct device *dev = &isys->adev->auxdev.dev;
+       struct media_entity *sd;
+       unsigned int i, j;
+       int ret;
+
+       for (i = 0; i < csi2_pdata->nports; i++) {
+               sd = &isys->csi2[i].asd.sd.entity;
+
+               for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) {
+                       struct ipu7_isys_video *av = &isys->csi2[i].av[j];
+
+                       ret = media_create_pad_link(sd, IPU7_CSI2_PAD_SRC + j,
+                                                   &av->vdev.entity, 0, 0);
+                       if (ret) {
+                               dev_err(dev, "CSI2 can't create link\n");
+                               return ret;
+                       }
+
+                       av->csi2 = &isys->csi2[i];
+               }
+       }
+
+       return 0;
+}
+
+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;
+
+       ret = isys_notifier_init(isys);
+       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;
+}
+
+static void isys_unregister_devices(struct ipu7_isys *isys)
+{
+       isys_unregister_video_devices(isys);
+       isys_csi2_unregister_subdevices(isys);
+       v4l2_device_unregister(&isys->v4l2_dev);
+       media_device_unregister(&isys->media_dev);
+       media_device_cleanup(&isys->media_dev);
+}
+
+static void enable_csi2_legacy_irq(struct ipu7_isys *isys, bool enable)
+{
+       u32 offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE;
+       void __iomem *base = isys->pdata->base;
+       u32 mask = isys->isr_csi2_mask;
+
+       if (!enable) {
+               writel(mask, base + offset + IRQ_CTL_CLEAR);
+               writel(0, base + offset + IRQ_CTL_ENABLE);
+               return;
+       }
+
+       writel(mask, base + offset + IRQ_CTL_EDGE);
+       writel(mask, base + offset + IRQ_CTL_CLEAR);
+       writel(mask, base + offset + IRQ_CTL_MASK);
+       writel(mask, base + offset + IRQ_CTL_ENABLE);
+}
+
+static void enable_to_sw_irq(struct ipu7_isys *isys, bool enable)
+{
+       void __iomem *base = isys->pdata->base;
+       u32 mask = IS_UC_TO_SW_IRQ_MASK;
+       u32 offset = IS_UC_CTRL_BASE;
+
+       if (!enable) {
+               writel(0, base + offset + TO_SW_IRQ_CNTL_ENABLE);
+               return;
+       }
+
+       writel(mask, base + offset + TO_SW_IRQ_CNTL_CLEAR);
+       writel(mask, base + offset + TO_SW_IRQ_CNTL_MASK_N);
+       writel(mask, base + offset + TO_SW_IRQ_CNTL_ENABLE);
+}
+
+void ipu7_isys_setup_hw(struct ipu7_isys *isys)
+{
+       u32 offset;
+       void __iomem *base = isys->pdata->base;
+
+       /* soft reset */
+       offset = IS_IO_GPREGS_BASE;
+
+       writel(0x0, base + offset + CLK_EN_TXCLKESC);
+       /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */
+       writel(0x0, base + offset + CLK_DIV_FACTOR_IS_CLK);
+       /* correct the initial printf configuration */
+       writel(0x200, base + IS_UC_CTRL_BASE + PRINTF_AXI_CNTL);
+
+       enable_to_sw_irq(isys, 1);
+       enable_csi2_legacy_irq(isys, 1);
+}
+
+static void isys_cleanup_hw(struct ipu7_isys *isys)
+{
+       enable_csi2_legacy_irq(isys, 0);
+       enable_to_sw_irq(isys, 0);
+}
+
+static int isys_runtime_pm_resume(struct device *dev)
+{
+       struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+       struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+       struct ipu7_device *isp = adev->isp;
+       unsigned long flags;
+       int ret;
+
+       if (!isys)
+               return 0;
+
+       ret = ipu7_mmu_hw_init(adev->mmu);
+       if (ret)
+               return ret;
+
+       cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE);
+
+       ret = ipu_buttress_start_tsc_sync(isp);
+       if (ret)
+               return ret;
+
+       spin_lock_irqsave(&isys->power_lock, flags);
+       isys->power = 1;
+       spin_unlock_irqrestore(&isys->power_lock, flags);
+
+       return 0;
+}
+
+static int isys_runtime_pm_suspend(struct device *dev)
+{
+       struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+       struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+       unsigned long flags;
+
+       if (!isys)
+               return 0;
+
+       isys_cleanup_hw(isys);
+
+       spin_lock_irqsave(&isys->power_lock, flags);
+       isys->power = 0;
+       spin_unlock_irqrestore(&isys->power_lock, flags);
+
+       cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
+
+       ipu7_mmu_hw_cleanup(adev->mmu);
+
+       return 0;
+}
+
+static int isys_suspend(struct device *dev)
+{
+       struct ipu7_isys *isys = dev_get_drvdata(dev);
+
+       /* If stream is open, refuse to suspend */
+       if (isys->stream_opened)
+               return -EBUSY;
+
+       return 0;
+}
+
+static int isys_resume(struct device *dev)
+{
+       return 0;
+}
+
+static const struct dev_pm_ops isys_pm_ops = {
+       .runtime_suspend = isys_runtime_pm_suspend,
+       .runtime_resume = isys_runtime_pm_resume,
+       .suspend = isys_suspend,
+       .resume = isys_resume,
+};
+
+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);
+
+       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_notifier_cleanup(isys);
+       isys_unregister_devices(isys);
+
+       cpu_latency_qos_remove_request(&isys->pm_qos);
+
+       mutex_destroy(&isys->stream_mutex);
+       mutex_destroy(&isys->mutex);
+}
+
+static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount)
+{
+       struct ipu7_bus_device *adev = isys->adev;
+       struct isys_fw_msgs *addr;
+       dma_addr_t dma_addr;
+       unsigned long flags;
+       unsigned int i;
+
+       for (i = 0; i < amount; i++) {
+               addr = ipu7_dma_alloc(adev, sizeof(struct isys_fw_msgs),
+                                     &dma_addr, GFP_KERNEL, 0);
+               if (!addr)
+                       break;
+               addr->dma_addr = dma_addr;
+
+               spin_lock_irqsave(&isys->listlock, flags);
+               list_add(&addr->head, &isys->framebuflist);
+               spin_unlock_irqrestore(&isys->listlock, flags);
+       }
+
+       if (i == amount)
+               return 0;
+
+       spin_lock_irqsave(&isys->listlock, flags);
+       while (!list_empty(&isys->framebuflist)) {
+               addr = list_first_entry(&isys->framebuflist,
+                                       struct isys_fw_msgs, head);
+               list_del(&addr->head);
+               spin_unlock_irqrestore(&isys->listlock, flags);
+               ipu7_dma_free(adev, sizeof(struct isys_fw_msgs),
+                             addr, addr->dma_addr, 0);
+               spin_lock_irqsave(&isys->listlock, flags);
+       }
+       spin_unlock_irqrestore(&isys->listlock, flags);
+
+       return -ENOMEM;
+}
+
+struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream)
+{
+       struct device *dev = &stream->isys->adev->auxdev.dev;
+       struct ipu7_isys *isys = stream->isys;
+       struct isys_fw_msgs *msg;
+       unsigned long flags;
+       int ret;
+
+       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)
+                       return NULL;
+
+               spin_lock_irqsave(&isys->listlock, flags);
+               if (list_empty(&isys->framebuflist)) {
+                       spin_unlock_irqrestore(&isys->listlock, flags);
+                       dev_err(dev, "Frame list empty\n");
+                       return NULL;
+               }
+       }
+       msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head);
+       list_move(&msg->head, &isys->framebuflist_fw);
+       spin_unlock_irqrestore(&isys->listlock, flags);
+       memset(&msg->fw_msg, 0, sizeof(msg->fw_msg));
+
+       return msg;
+}
+
+void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys)
+{
+       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)
+               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;
+       void *ptr = (void *)data;
+       unsigned long flags;
+
+       if (WARN_ON_ONCE(!ptr))
+               return;
+
+       spin_lock_irqsave(&isys->listlock, flags);
+       msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy);
+       list_move(&msg->head, &isys->framebuflist);
+       spin_unlock_irqrestore(&isys->listlock, flags);
+}
+
+static int isys_probe(struct auxiliary_device *auxdev,
+                     const struct auxiliary_device_id *auxdev_id)
+{
+       const struct ipu7_isys_internal_csi2_pdata *csi2_pdata;
+       struct ipu7_bus_device *adev = auxdev_to_adev(auxdev);
+       struct ipu7_device *isp = adev->isp;
+       struct ipu7_isys *isys;
+       int ret = 0;
+
+       if (!isp->ipu7_bus_ready_to_probe)
+               return -EPROBE_DEFER;
+
+       isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL);
+       if (!isys)
+               return -ENOMEM;
+
+       ret = pm_runtime_resume_and_get(&auxdev->dev);
+       if (ret < 0)
+               return ret;
+
+       adev->auxdrv_data =
+               (const struct ipu7_auxdrv_data *)auxdev_id->driver_data;
+       adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver);
+       isys->adev = adev;
+       isys->pdata = adev->pdata;
+
+       INIT_LIST_HEAD(&isys->requests);
+       csi2_pdata = &isys->pdata->ipdata->csi2;
+
+       isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports,
+                                 sizeof(*isys->csi2), GFP_KERNEL);
+       if (!isys->csi2) {
+               ret = -ENOMEM;
+               goto out_runtime_put;
+       }
+
+       ret = ipu7_mmu_hw_init(adev->mmu);
+       if (ret)
+               goto out_runtime_put;
+
+       spin_lock_init(&isys->streams_lock);
+       spin_lock_init(&isys->power_lock);
+       isys->power = 0;
+
+       mutex_init(&isys->mutex);
+       mutex_init(&isys->stream_mutex);
+
+       spin_lock_init(&isys->listlock);
+       INIT_LIST_HEAD(&isys->framebuflist);
+       INIT_LIST_HEAD(&isys->framebuflist_fw);
+
+       dev_set_drvdata(&auxdev->dev, isys);
+
+       isys->icache_prefetch = 0;
+       isys->phy_rext_cal = 0;
+
+       isys_stream_init(isys);
+
+       cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
+       ret = alloc_fw_msg_bufs(isys, 20);
+       if (ret < 0)
+               goto out_cleanup_isys;
+
+       ret = ipu7_fw_isys_init(isys);
+       if (ret)
+               goto out_cleanup_isys;
+
+       ret = isys_register_devices(isys);
+       if (ret)
+               goto out_cleanup_fw;
+
+       ret = isys_fw_log_init(isys);
+       if (ret)
+               goto out_cleanup;
+
+       ipu7_mmu_hw_cleanup(adev->mmu);
+       pm_runtime_put(&auxdev->dev);
+
+       return 0;
+
+out_cleanup:
+       isys_unregister_devices(isys);
+out_cleanup_fw:
+       ipu7_fw_isys_release(isys);
+out_cleanup_isys:
+       cpu_latency_qos_remove_request(&isys->pm_qos);
+
+       for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++)
+               mutex_destroy(&isys->streams[i].mutex);
+
+       mutex_destroy(&isys->mutex);
+       mutex_destroy(&isys->stream_mutex);
+
+       ipu7_mmu_hw_cleanup(adev->mmu);
+
+out_runtime_put:
+       pm_runtime_put(&auxdev->dev);
+
+       return ret;
+}
+
+struct ipu7_csi2_error {
+       const char *error_string;
+       bool is_info_only;
+};
+
+/*
+ * Strings corresponding to CSI-2 receiver errors are here.
+ * Corresponding macros are defined in the header file.
+ */
+static const struct ipu7_csi2_error dphy_rx_errors[] = {
+       { "Error handler FIFO full", false },
+       { "Reserved Short Packet encoding detected", true },
+       { "Reserved Long Packet encoding detected", true },
+       { "Received packet is too short", false},
+       { "Received packet is too long", false},
+       { "Short packet discarded due to errors", false },
+       { "Long packet discarded due to errors", false },
+       { "CSI Combo Rx interrupt", false },
+       { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false },
+       { "Received NULL packet", true },
+       { "Received blanking packet", true },
+       { "Tie to 0", true },
+       { }
+};
+
+static void ipu7_isys_register_errors(struct ipu7_isys_csi2 *csi2)
+{
+       u32 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+       u32 status = readl(csi2->base + offset + IRQ_CTL_STATUS);
+       u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+
+       if (!status)
+               return;
+
+       dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n",
+               csi2->port, status);
+
+       writel(status & mask, csi2->base + offset + IRQ_CTL_CLEAR);
+       csi2->receiver_errors |= status & mask;
+}
+
+static void ipu7_isys_csi2_error(struct ipu7_isys_csi2 *csi2)
+{
+       struct ipu7_csi2_error const *errors;
+       unsigned int i;
+       u32 status;
+
+       /* Register errors once more in case of error interrupts are disabled */
+       ipu7_isys_register_errors(csi2);
+       status = csi2->receiver_errors;
+       csi2->receiver_errors = 0;
+       errors = dphy_rx_errors;
+
+       for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {
+               if (status & BIT(i))
+                       dev_err_ratelimited(&csi2->isys->adev->auxdev.dev,
+                                           "csi2-%i error: %s\n",
+                                           csi2->port,
+                                           errors[i].error_string);
+       }
+}
+
+struct resp_to_msg {
+       enum ipu7_insys_resp_type type;
+       const char *msg;
+};
+
+static const struct resp_to_msg is_fw_msg[] = {
+       {IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE,
+        "IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE"},
+       {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
+        "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK"},
+       {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK,
+        "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK"},
+       {IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK,
+        "IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK"},
+       {IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK,
+        "IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK"},
+       {IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK,
+        "IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK"},
+       {IPU_INSYS_RESP_TYPE_PIN_DATA_READY,
+        "IPU_INSYS_RESP_TYPE_PIN_DATA_READY"},
+       {IPU_INSYS_RESP_TYPE_FRAME_SOF, "IPU_INSYS_RESP_TYPE_FRAME_SOF"},
+       {IPU_INSYS_RESP_TYPE_FRAME_EOF, "IPU_INSYS_RESP_TYPE_FRAME_EOF"},
+       {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
+        "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE"},
+       {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE,
+        "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE"},
+       {N_IPU_INSYS_RESP_TYPE, "N_IPU_INSYS_RESP_TYPE"},
+};
+
+int isys_isr_one(struct ipu7_bus_device *adev)
+{
+       struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+       struct ipu7_isys_stream *stream = NULL;
+       struct device *dev = &adev->auxdev.dev;
+       struct ipu7_isys_csi2 *csi2 = NULL;
+       struct ia_gofo_msg_err err_info;
+       struct ipu7_insys_resp *resp;
+       u64 ts;
+
+       if (!isys->adev->syscom)
+               return 1;
+
+       resp = ipu7_fw_isys_get_resp(isys);
+       if (!resp)
+               return 1;
+       if (resp->type >= N_IPU_INSYS_RESP_TYPE) {
+               dev_err(dev, "Unknown response type %u stream %u\n",
+                       resp->type, resp->stream_id);
+               ipu7_fw_isys_put_resp(isys);
+               return 1;
+       }
+
+       err_info = resp->error_info;
+       ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0];
+       if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE &&
+           err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) {
+               /* receive a sp w/o command, firmware drop it */
+               dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n",
+                       resp->type, is_fw_msg[resp->type].msg,
+                       resp->stream_id);
+               dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n",
+                       resp->pin_id, resp->buf_id, resp->frame_id);
+               dev_dbg(dev, "\terror group %u code %u details [%u %u]\n",
+                       err_info.err_group, err_info.err_code,
+                       err_info.err_detail[0], err_info.err_detail[1]);
+       } else if (!IA_GOFO_MSG_ERR_IS_OK(err_info)) {
+               dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n",
+                       resp->type, is_fw_msg[resp->type].msg, resp->stream_id,
+                       resp->pin_id, resp->buf_id, resp->frame_id);
+               dev_err(dev, "\terror group %u code %u details [%u %u]\n",
+                       err_info.err_group, err_info.err_code,
+                       err_info.err_detail[0], err_info.err_detail[1]);
+       } else {
+               dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n",
+                       resp->type, is_fw_msg[resp->type].msg, resp->stream_id,
+                       resp->pin_id, resp->buf_id, resp->frame_id);
+               dev_dbg(dev, "\tts %llu\n", ts);
+       }
+
+       if (resp->stream_id >= IPU_ISYS_MAX_STREAMS) {
+               dev_err(dev, "bad stream handle %u\n",
+                       resp->stream_id);
+               goto leave;
+       }
+
+       stream = ipu7_isys_query_stream_by_handle(isys, resp->stream_id);
+       if (!stream) {
+               dev_err(dev, "stream of stream_handle %u is unused\n",
+                       resp->stream_id);
+               goto leave;
+       }
+
+       stream->error = err_info.err_code;
+
+       if (stream->asd)
+               csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+
+       switch (resp->type) {
+       case IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE:
+               complete(&stream->stream_open_completion);
+               break;
+       case IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK:
+               complete(&stream->stream_close_completion);
+               break;
+       case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
+               complete(&stream->stream_start_completion);
+               break;
+       case IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK:
+               complete(&stream->stream_stop_completion);
+               break;
+       case IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK:
+               complete(&stream->stream_stop_completion);
+               break;
+       case IPU_INSYS_RESP_TYPE_PIN_DATA_READY:
+               /*
+                * firmware only release the capture msg until software
+                * get pin_data_ready event
+                */
+               ipu7_put_fw_msg_buf(ipu7_bus_get_drvdata(adev), resp->buf_id);
+               if (resp->pin_id < IPU_INSYS_OUTPUT_PINS &&
+                   stream->output_pins[resp->pin_id].pin_ready)
+                       stream->output_pins[resp->pin_id].pin_ready(stream,
+                                                                   resp);
+               else
+                       dev_err(dev, "No handler for pin %u ready\n",
+                               resp->pin_id);
+               if (csi2)
+                       ipu7_isys_csi2_error(csi2);
+
+               break;
+       case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK:
+               break;
+       case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
+       case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE:
+               break;
+       case IPU_INSYS_RESP_TYPE_FRAME_SOF:
+               if (csi2)
+                       ipu7_isys_csi2_sof_event_by_stream(stream);
+
+               stream->seq[stream->seq_index].sequence =
+                       atomic_read(&stream->sequence) - 1U;
+               stream->seq[stream->seq_index].timestamp = ts;
+               dev_dbg(dev,
+                       "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n",
+                       resp->stream_id, resp->frame_id,
+                       stream->seq[stream->seq_index].sequence, ts);
+               stream->seq_index = (stream->seq_index + 1U)
+                       % IPU_ISYS_MAX_PARALLEL_SOF;
+               break;
+       case IPU_INSYS_RESP_TYPE_FRAME_EOF:
+               if (csi2)
+                       ipu7_isys_csi2_eof_event_by_stream(stream);
+
+               dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n",
+                       resp->stream_id,
+                       stream->seq[stream->seq_index].sequence, ts);
+               break;
+       default:
+               dev_err(dev, "Unknown response type %u stream %u\n",
+                       resp->type, resp->stream_id);
+               break;
+       }
+
+       ipu7_isys_put_stream(stream);
+leave:
+       ipu7_fw_isys_put_resp(isys);
+
+       return 0;
+}
+
+static void ipu7_isys_csi2_isr(struct ipu7_isys_csi2 *csi2)
+{
+       struct device *dev = &csi2->isys->adev->auxdev.dev;
+       struct ipu7_device *isp = csi2->isys->adev->isp;
+       struct ipu7_isys_stream *s;
+       u32 sync, offset;
+       u32 fe = 0;
+       u8 vc;
+
+       ipu7_isys_register_errors(csi2);
+
+       offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+       sync = readl(csi2->base + offset + IRQ_CTL_STATUS);
+       writel(sync, csi2->base + offset + IRQ_CTL_CLEAR);
+       dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync);
+
+       if (!is_ipu7(isp->hw_ver)) {
+               fe = readl(csi2->base + offset + IRQ1_CTL_STATUS);
+               writel(fe, csi2->base + offset + IRQ1_CTL_CLEAR);
+               dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe);
+       }
+
+       for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) {
+               s = ipu7_isys_query_stream_by_source(csi2->isys,
+                                                    csi2->asd.source, vc);
+               if (!s)
+                       continue;
+
+               if (!is_ipu7(isp->hw_ver)) {
+                       if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc))
+                               ipu7_isys_csi2_sof_event_by_stream(s);
+
+                       if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc))
+                               ipu7_isys_csi2_eof_event_by_stream(s);
+               } else {
+                       if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2)))
+                               ipu7_isys_csi2_sof_event_by_stream(s);
+
+                       if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2)))
+                               ipu7_isys_csi2_eof_event_by_stream(s);
+               }
+       }
+}
+
+static irqreturn_t isys_isr(struct ipu7_bus_device *adev)
+{
+       struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+       u32 status_csi, status_sw, csi_offset, sw_offset;
+       struct device *dev = &isys->adev->auxdev.dev;
+       void __iomem *base = isys->pdata->base;
+
+       spin_lock(&isys->power_lock);
+       if (!isys->power) {
+               spin_unlock(&isys->power_lock);
+               return IRQ_NONE;
+       }
+
+       csi_offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE;
+       sw_offset = IS_BASE;
+
+       status_csi = readl(base + csi_offset + IRQ_CTL_STATUS);
+       status_sw = readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS);
+       if (!status_csi && !status_sw) {
+               spin_unlock(&isys->power_lock);
+               return IRQ_NONE;
+       }
+
+       if (status_csi)
+               dev_dbg(dev, "status csi 0x%08x\n", status_csi);
+       if (status_sw)
+               dev_dbg(dev, "status to_sw 0x%08x\n", status_sw);
+
+       do {
+               writel(status_sw, base + sw_offset + TO_SW_IRQ_CNTL_CLEAR);
+               writel(status_csi, base + csi_offset + IRQ_CTL_CLEAR);
+
+               if (isys->isr_csi2_mask & status_csi) {
+                       unsigned int i;
+
+                       for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
+                               /* irq from not enabled port */
+                               if (!isys->csi2[i].base)
+                                       continue;
+                               if (status_csi & isys->csi2[i].legacy_irq_mask)
+                                       ipu7_isys_csi2_isr(&isys->csi2[i]);
+                       }
+               }
+
+               if (!isys_isr_one(adev))
+                       status_sw = TO_SW_IRQ_FW;
+               else
+                       status_sw = 0;
+
+               status_csi = readl(base + csi_offset + IRQ_CTL_STATUS);
+               status_sw |= readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS);
+       } while ((status_csi & isys->isr_csi2_mask) ||
+                (status_sw & TO_SW_IRQ_FW));
+
+       writel(TO_SW_IRQ_MASK, base + sw_offset + TO_SW_IRQ_CNTL_MASK_N);
+
+       spin_unlock(&isys->power_lock);
+
+       return IRQ_HANDLED;
+}
+
+static const struct ipu7_auxdrv_data ipu7_isys_auxdrv_data = {
+       .isr = isys_isr,
+       .isr_threaded = NULL,
+       .wake_isr_thread = false,
+};
+
+static const struct auxiliary_device_id ipu7_isys_id_table[] = {
+       {
+               .name = "intel_ipu7.isys",
+               .driver_data = (kernel_ulong_t)&ipu7_isys_auxdrv_data,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(auxiliary, ipu7_isys_id_table);
+
+static struct auxiliary_driver isys_driver = {
+       .name = IPU_ISYS_NAME,
+       .probe = isys_probe,
+       .remove = isys_remove,
+       .id_table = ipu7_isys_id_table,
+       .driver = {
+               .pm = &isys_pm_ops,
+       },
+};
+
+module_auxiliary_driver(isys_driver);
+
+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
+MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel ipu7 input system driver");
+MODULE_IMPORT_NS("INTEL_IPU7");
+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE");
diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h
new file mode 100644 (file)
index 0000000..ef1ab1b
--- /dev/null
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_H
+#define IPU7_ISYS_H
+
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm_qos.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+
+#include "abi/ipu7_fw_msg_abi.h"
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-video.h"
+
+#define IPU_ISYS_ENTITY_PREFIX         "Intel IPU7"
+
+/* FW support max 16 streams */
+#define IPU_ISYS_MAX_STREAMS           16U
+
+/*
+ * Current message queue configuration. These must be big enough
+ * so that they never gets full. Queues are located in system memory
+ */
+#define IPU_ISYS_SIZE_RECV_QUEUE       40U
+#define IPU_ISYS_SIZE_LOG_QUEUE                256U
+#define IPU_ISYS_SIZE_SEND_QUEUE       40U
+#define IPU_ISYS_NUM_RECV_QUEUE                1U
+
+#define IPU_ISYS_MIN_WIDTH             2U
+#define IPU_ISYS_MIN_HEIGHT            2U
+#define IPU_ISYS_MAX_WIDTH             8160U
+#define IPU_ISYS_MAX_HEIGHT            8190U
+
+#define FW_CALL_TIMEOUT_JIFFIES                \
+       msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS)
+
+struct isys_fw_log {
+       struct mutex mutex; /* protect whole struct */
+       void *head;
+       void *addr;
+       u32 count; /* running counter of log */
+       u32 size; /* actual size of log content, in bits */
+};
+
+/*
+ * struct ipu7_isys
+ *
+ * @media_dev: Media device
+ * @v4l2_dev: V4L2 device
+ * @adev: ISYS bus device
+ * @power: Is ISYS powered on or not?
+ * @isr_bits: Which bits does the ISR handle?
+ * @power_lock: Serialise access to power (power state in general)
+ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers
+ * @streams_lock: serialise access to streams
+ * @streams: streams per firmware stream ID
+ * @syscom: fw communication layer context
+ * @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
+ * @pdata: platform data pointer
+ * @csi2: CSI-2 receivers
+ */
+struct ipu7_isys {
+       struct media_device media_dev;
+       struct v4l2_device v4l2_dev;
+       struct ipu7_bus_device *adev;
+
+       int power;
+       spinlock_t power_lock;  /* Serialise access to power */
+       u32 isr_csi2_mask;
+       u32 csi2_rx_ctrl_cached;
+       spinlock_t streams_lock;
+       struct ipu7_isys_stream streams[IPU_ISYS_MAX_STREAMS];
+       int streams_ref_count[IPU_ISYS_MAX_STREAMS];
+       u32 phy_rext_cal;
+       bool icache_prefetch;
+       bool csi2_cse_ipc_not_supported;
+       unsigned int ref_count;
+       unsigned int stream_opened;
+
+       struct mutex mutex;     /* Serialise isys video open/release related */
+       struct mutex stream_mutex;      /* Stream start, stop, queueing reqs */
+
+       struct ipu7_isys_pdata *pdata;
+
+       struct ipu7_isys_csi2 *csi2;
+       struct isys_fw_log *fw_log;
+
+       struct list_head requests;
+       struct pm_qos_request pm_qos;
+       spinlock_t listlock;    /* Protect framebuflist */
+       struct list_head framebuflist;
+       struct list_head framebuflist_fw;
+       struct v4l2_async_notifier notifier;
+
+       struct ipu7_insys_config *subsys_config;
+       dma_addr_t subsys_config_dma_addr;
+};
+
+struct isys_fw_msgs {
+       union {
+               u64 dummy;
+               struct ipu7_insys_buffset frame;
+               struct ipu7_insys_stream_cfg stream;
+       } fw_msg;
+       struct list_head head;
+       dma_addr_t dma_addr;
+};
+
+struct ipu7_isys_csi2_config {
+       unsigned int nlanes;
+       unsigned int port;
+       enum v4l2_mbus_type bus_type;
+};
+
+struct sensor_async_sd {
+       struct v4l2_async_connection asc;
+       struct ipu7_isys_csi2_config csi2;
+};
+
+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);
+int isys_isr_one(struct ipu7_bus_device *adev);
+void ipu7_isys_setup_hw(struct ipu7_isys *isys);
+#endif /* IPU7_ISYS_H */
index b3c4789fa5c805dd75d647e15d18bf4626d4df4a..4a70d3527cb77f99d9e9a79077e94519b8c0caa2 100644 (file)
@@ -2745,10 +2745,8 @@ static int ipu7_runtime_resume(struct device *dev)
 }
 
 static const struct dev_pm_ops ipu7_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume)
-       SET_RUNTIME_PM_OPS(&ipu7_suspend,       /* Same as in suspend flow */
-                          &ipu7_runtime_resume,
-                          NULL)
+       SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume)
+       RUNTIME_PM_OPS(&ipu7_suspend, &ipu7_runtime_resume, NULL)
 };
 
 static const struct pci_device_id ipu7_pci_tbl[] = {