new file mode 100644
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 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 send_msg_types[N_IPU_INSYS_SEND_TYPE][32] = {
+ "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 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");
+}
new file mode 100644
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2024 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
new file mode 100644
@@ -0,0 +1,1051 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 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 0
+#define PORT_B 1
+#define PORT_C 2
+#define PORT_D 3
+
+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;
+};
+
+struct i_coarse_target_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 i_coarse_target;
+};
+
+struct post_thres_params {
+ u16 min_mbps;
+ u16 max_mbps;
+
+ u16 post_received_reset_thresh;
+ u16 post_det_delay_thresh;
+};
+
+ /* 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 des_delay_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 deserializer_en_delay_deass_thresh;
+};
+
+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},
+ {}
+};
+
+enum ppi_datawidth {
+ PPI_DATAWIDTH_8BIT = 0,
+ PPI_DATAWIDTH_16BIT = 1,
+};
+
+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 - (1 << lo) + 1)) & (~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 - (1 << lo) + 1)) & (~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);
+}
+
+#define N_DATA_IDS 8
+static DECLARE_BITMAP(data_ids, N_DATA_IDS);
+/* 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 < 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 (5000000)
+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 < 4; i++) {
+ reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400);
+ 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) & 0xf;
+ dev_dbg(dev, "phy %u rext value = %u\n", id, rext);
+ isys->phy_rext_cal = rext;
+
+ if (!isys->phy_rext_cal)
+ isys->phy_rext_cal = 5;
+
+ return 0;
+ }
+
+ dev_err(dev, "wait phy ready timeout!\n");
+
+ return ret;
+}
+
+#define lookup(table, mbps) \
+({ \
+ unsigned int i_; \
+ u16 mbps_ = (u16)(mbps); \
+ for (i_ = 0; i_ < ARRAY_SIZE((table)); i_++) { \
+ if (mbps_ >= (table)[i_].min_mbps && \
+ mbps_ <= (table)[i_].max_mbps) \
+ break; \
+ } \
+ (i_ == ARRAY_SIZE((table))) ? -ENXIO : i_; \
+})
+
+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, hsrxval1, hsrxval2;
+ 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);
+
+ index = lookup(table0, mbps);
+ if (index >= 0)
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3,
+ table0[index].ddlcal_counter_ref, 0, 9);
+
+ 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);
+ }
+
+ index = lookup(table6, mbps);
+ if (index >= 0) {
+ val = table6[index].oa_lane_hsrx_hs_clk_div;
+ 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;
+
+ dwc_phy_write_mask(isys, id, reg, val, 5, 7);
+ }
+
+ 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);
+
+ index = lookup(table2, mbps);
+ if (index >= 0) {
+ val = table2[index].i_thssettle;
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_0;
+ for (i = 0; i < lanes; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+ 8, 15);
+ }
+
+ /* 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 = 0;
+ hsrxval1 = 0;
+ hsrxval2 = 0;
+ }
+
+ if (mbps > 1500) {
+ hsrxval0 = 4;
+ hsrxval1 = 0;
+ 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)
+{
+ u16 coarse_target;
+ u16 deass_thresh;
+ u16 delay_thresh;
+ u16 reset_thresh;
+ u16 equalizer;
+ u16 cap_prog;
+ u16 gmode;
+ u16 reg;
+ u16 val;
+ u32 i;
+ u32 r;
+ int index;
+
+ if (is_ipu7p5(isys->adev->isp->hw_ver))
+ val = 0x15;
+ else
+ val = 0x155;
+
+ 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 < lanes; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);
+
+ val = (mbps > 900U) ? 1U : 0U;
+ for (i = 0; i < lanes; i++) {
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+
+ 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 = (u16)div_u64_rem(mbps, 16, &r);
+ if (!r)
+ coarse_target--;
+ } else {
+ coarse_target = 56;
+ }
+
+ for (i = 0; i < lanes; i++) {
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400;
+ dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15);
+ }
+
+ 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 < lanes; i++) {
+ reg = CORE_DIG_RW_TRIO0_0 + i * 0x400;
+ dwc_phy_write_mask(isys, id, reg, 1, 6, 8);
+ dwc_phy_write_mask(isys, id, reg, 1, 3, 5);
+ 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 < lanes; 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 < lanes; 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 < lanes; 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 < lanes; 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 < lanes; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0);
+
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
+ for (i = 0; i < lanes; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6);
+
+ equalizer = 4U;
+ gmode = 0U;
+ cap_prog = 6U;
+
+ index = lookup(table7, mbps);
+ if (index >= 0)
+ cap_prog = table7[index].val;
+
+ for (i = 0; i < lanes; i++) {
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i;
+ dwc_phy_write_mask(isys, id, reg, equalizer, 0, 2);
+ dwc_phy_write_mask(isys, id, reg, gmode, 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 void 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_warn(dev, "get link freq failed, use default mbps\n");
+ link_freq = 560000000;
+ }
+
+ 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);
+ }
+}
+
+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, (1 << lanes) - 1);
+ 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);
+
+ ipu7_isys_phy_config(isys, id, lanes, aggregation);
+ 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) {
+ ipu7_isys_phy_config(isys, PORT_B, 2, aggregation);
+ 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 > 2 && csi2->port == PORT_A)
+ ipu7_isys_csi_phy_reset(isys, PORT_B);
+}
new file mode 100644
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2024 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
new file mode 100644
@@ -0,0 +1,1197 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2024 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 + 0x0)
+#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 (4)
+#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 4
+#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 64
+#define MGC_TPG_TILE_HEIGHT 64
+
+#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 0 /* 0xffffffff */
+#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0 /* 0xffffffff */
+
+#define CSI_RX_NUM_ERRORS_IN_IRQ 12
+#define CSI_RX_NUM_SYNC_IN_IRQ 32
+
+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 32
+
+#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 */
new file mode 100644
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 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;
+ struct v4l2_subdev *ext_sd;
+ struct device *dev;
+
+ if (!csi2)
+ return -EINVAL;
+
+ dev = &csi2->isys->adev->auxdev.dev;
+ src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+ if (IS_ERR(src_pad)) {
+ dev_err(dev, "can't get source pad of %s (%ld)\n",
+ csi2->asd.sd.name, PTR_ERR(src_pad));
+ return PTR_ERR(src_pad);
+ }
+
+ ext_sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name))
+ return -ENODEV;
+
+ return v4l2_get_link_freq(ext_sd->ctrl_handler, 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_en(struct ipu7_isys_csi2 *csi2, bool enable)
+{
+ struct ipu7_device *isp = csi2->isys->adev->isp;
+ unsigned int offset, mask;
+
+ if (!enable) {
+ /* 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);
+ }
+
+ return;
+ }
+
+ /* 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 ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_isys *isys = csi2->isys;
+ void __iomem *isys_base = isys->pdata->base;
+ unsigned int port, nlanes, offset, val;
+
+ port = csi2->port;
+ nlanes = csi2->nlanes;
+
+ csi2_irq_en(csi2, 0);
+
+ offset = IS_IO_GPREGS_BASE;
+ val = readl(isys_base + offset + CSI_PORT_CLK_GATE);
+ val &= ~(1 << port);
+ if (port == 0 && nlanes == 4 && !is_ipu7(isys->adev->isp->hw_ver))
+ val &= ~BIT(1);
+ writel(val, isys_base + offset + CSI_PORT_CLK_GATE);
+
+ /* power down */
+ ipu7_isys_csi_phy_powerdown(csi2);
+ writel(0x4, isys_base + offset + CLK_DIV_FACTOR_APB_CLK);
+}
+
+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, val;
+ int ret;
+
+ port = csi2->port;
+ nlanes = csi2->nlanes;
+
+ offset = IS_IO_GPREGS_BASE;
+ /* port AB support aggregation, configure 2 ports */
+ val = readl(isys_base + offset + CSI_PORT_CLK_GATE);
+ writel(val | (0x3 << (port & 0x2)),
+ isys_base + offset + CSI_PORT_CLK_GATE);
+ 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 == 0 && nlanes == 4 && !is_ipu7(isys->adev->isp->hw_ver)) {
+ dev_info(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);
+
+ /* Enable DPHY power */
+ 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_en(csi2, 1);
+
+ 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 == 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 &= ~1;
+ sel->r.height = clamp(sel->r.height & ~1, 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 *r_pad;
+ 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;
+
+ r_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
+ r_sd = media_entity_to_v4l2_subdev(r_pad->entity);
+
+ ret = v4l2_subdev_enable_streams(r_sd, r_pad->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 *r_pad;
+ 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;
+
+ r_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
+ r_sd = media_entity_to_v4l2_subdev(r_pad->entity);
+
+ v4l2_subdev_disable_streams(r_sd, r_pad->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 = 0x7 << (index * 3);
+ else
+ csi2->legacy_irq_mask = 0x3 << (index * 2);
+
+ 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,
+ NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS);
+ if (ret)
+ return ret;
+
+ csi2->asd.source = IPU_INSYS_MIPI_PORT_0 + 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[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 >= 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;
+}
new file mode 100644
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2024 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 NR_OF_CSI2_VC 16
+#define INVALID_VC_ID -1
+#define NR_OF_CSI2_SINK_PADS 1
+#define CSI2_PAD_SINK 0
+#define NR_OF_CSI2_SRC_PADS 8
+#define CSI2_PAD_SRC 1
+#define NR_OF_CSI2_PADS (NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS)
+
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A 0
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B 0
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A 95
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B -8
+
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A 0
+#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B 0
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A 85
+#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B -2
+
+/*
+ * 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[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 */
new file mode 100644
@@ -0,0 +1,836 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 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-video.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_video *av = ipu7_isys_queue_to_video(aq);
+ struct ipu7_isys_buffer *ib;
+ bool need_reset = false;
+ struct vb2_buffer *vb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ while (!list_empty(&aq->incoming)) {
+ ib = list_first_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);
+ }
+
+ /*
+ * 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_first_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);
+ need_reset = true;
+ }
+
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ if (need_reset) {
+ mutex_lock(&av->isys->mutex);
+ av->isys->need_reset = true;
+ mutex_unlock(&av->isys->mutex);
+ }
+}
+
+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;
+
+ 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,
+ struct ipu7_insys_resp *info)
+{
+ u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0];
+ 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 0;
+}
+
+static u64 get_sof_ns_delta(struct ipu7_isys_video *av,
+ struct ipu7_insys_resp *info)
+{
+ 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 - ((u64)info->timestamp[1] << 32 | info->timestamp[0]);
+
+ return ipu_buttress_tsc_ticks_to_ns(delta, isp);
+}
+
+void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib,
+ struct ipu7_insys_resp *info)
+{
+ 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, info);
+ sequence = get_sof_sequence_by_timestamp(stream, info);
+
+ 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);
+}
+
+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;
+ 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, info);
+
+ 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;
+}
new file mode 100644
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2024 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;
+ /*
+ * @lock: serialise access to queued and pre_streamon_queued
+ */
+ 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_buf_calc_sequence_time(struct ipu7_isys_buffer *ib,
+ struct ipu7_insys_resp *info);
+void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib);
+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 */
new file mode 100644
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 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_bpp(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ return 24;
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ return 20;
+ case MEDIA_BUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ return 16;
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ return 12;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ return 10;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ return 8;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+}
+
+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 -EINVAL;
+ }
+}
+
+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 ^ (((y & 1) << 1) | (x & 1))];
+}
+
+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 format = {
+ .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, &format);
+}
+
+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);
+}
new file mode 100644
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2024 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_bpp(u32 code);
+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 *fmt);
+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 */
new file mode 100644
@@ -0,0 +1,1130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 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)
+{
+ struct ipu7_isys_video *av = video_drvdata(file);
+ struct ipu7_isys *isys = av->isys;
+ struct ipu7_bus_device *adev = isys->adev;
+
+ mutex_lock(&isys->mutex);
+ if (isys->need_reset) {
+ mutex_unlock(&isys->mutex);
+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n");
+ return -EIO;
+ }
+ mutex_unlock(&isys->mutex);
+
+ 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));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "%s",
+ av->isys->media_dev.bus_info);
+
+ 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, av->isys->line_align);
+
+ /*
+ * (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\n");
+ else if (stream->error)
+ dev_err(dev, "stream close 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);
+
+ if (isys->need_reset)
+ pm_runtime_put_sync(&isys->adev->auxdev.dev);
+ else
+ pm_runtime_put(&isys->adev->auxdev.dev);
+}
+
+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);
+ *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);
+}
new file mode 100644
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2024 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 11
+#define IPU_ISYS_MAX_PARALLEL_SOF 2
+
+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 */
new file mode 100644
@@ -0,0 +1,1170 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2024 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 < 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 < 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 * 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 + 1;
+ while (i--) {
+ while (j--)
+ ipu7_isys_video_cleanup(&isys->csi2[i].av[j]);
+ j = 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 < NR_OF_CSI2_SRC_PADS; j++) {
+ struct ipu7_isys_video *av = &isys->csi2[i].av[j];
+
+ ret = media_create_pad_link(sd, 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);
+}
+
+static void 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);
+
+ isys_setup_hw(isys);
+
+ 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);
+
+ mutex_lock(&isys->mutex);
+ isys->need_reset = false;
+ mutex_unlock(&isys->mutex);
+
+ 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->line_align = IPU_ISYS_2600_MEM_LINE_ALIGN;
+ 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);
+ alloc_fw_msg_bufs(isys, 20);
+
+ ret = ipu7_fw_isys_init(isys);
+ if (ret)
+ goto out_cleanup_mmu;
+
+ 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_mmu:
+ 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);
+ int 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) - 1;
+ 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 + 1)
+ % 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 < 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 & (1 << vc))
+ ipu7_isys_csi2_sof_event_by_stream(s);
+
+ if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1 << vc))
+ ipu7_isys_csi2_eof_event_by_stream(s);
+ } else {
+ if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1 << (vc * 2)))
+ ipu7_isys_csi2_sof_event_by_stream(s);
+
+ if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2 << (vc * 2)))
+ ipu7_isys_csi2_eof_event_by_stream(s);
+ }
+ }
+}
+
+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");
new file mode 100644
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2024 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 16
+
+#define IPU_ISYS_2600_MEM_LINE_ALIGN 64
+
+/*
+ * 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 40
+#define IPU_ISYS_SIZE_LOG_QUEUE 256
+#define IPU_ISYS_SIZE_SEND_QUEUE 40
+#define IPU_ISYS_NUM_RECV_QUEUE 1
+
+#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
+ * @line_align: line alignment in memory
+ * @need_reset: Isys requires d0i0->i3 transition
+ * @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];
+ unsigned int line_align;
+ u32 phy_rext_cal;
+ bool need_reset;
+ 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);
+
+extern const struct v4l2_ioctl_ops ipu7_isys_ioctl_ops;
+
+int isys_isr_one(struct ipu7_bus_device *adev);
+irqreturn_t isys_isr(struct ipu7_bus_device *adev);
+
+#endif /* IPU7_ISYS_H */