diff --git a/dkms.conf b/dkms.conf index 78920d473931..b742ec371bb5 100644 --- a/dkms.conf +++ b/dkms.conf @@ -65,27 +65,19 @@ if ! version_lt ${KERNEL_VERSION} ${KV_I2C_MAX9X_ISX031}; then BUILT_MODULE_LOCATION[6]="drivers/media/i2c" DEST_MODULE_LOCATION[6]="/updates" - BUILT_MODULE_NAME[7]="max9295" + BUILT_MODULE_NAME[7]="max9x" BUILT_MODULE_LOCATION[7]="drivers/media/i2c/max9x" DEST_MODULE_LOCATION[7]="/updates" - BUILT_MODULE_NAME[8]="max9296" - BUILT_MODULE_LOCATION[8]="drivers/media/i2c/max9x" + BUILT_MODULE_NAME[8]="ipu6-acpi" + BUILT_MODULE_LOCATION[8]="drivers/media/platform/intel/" DEST_MODULE_LOCATION[8]="/updates" - BUILT_MODULE_NAME[9]="serdes" - BUILT_MODULE_LOCATION[9]="drivers/media/i2c/max9x" + BUILT_MODULE_NAME[9]="ipu6-acpi-pdata" + BUILT_MODULE_LOCATION[9]="drivers/media/platform/intel/" DEST_MODULE_LOCATION[9]="/updates" - BUILT_MODULE_NAME[10]="ipu6-acpi" + BUILT_MODULE_NAME[10]="ipu6-acpi-common" BUILT_MODULE_LOCATION[10]="drivers/media/platform/intel/" DEST_MODULE_LOCATION[10]="/updates" - - BUILT_MODULE_NAME[11]="ipu6-acpi-pdata" - BUILT_MODULE_LOCATION[11]="drivers/media/platform/intel/" - DEST_MODULE_LOCATION[11]="/updates" - - BUILT_MODULE_NAME[12]="ipu6-acpi-common" - BUILT_MODULE_LOCATION[12]="drivers/media/platform/intel/" - DEST_MODULE_LOCATION[12]="/updates" fi diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile index 6bddef53d022..0917fbb155e4 100644 --- a/drivers/media/i2c/max9x/Makefile +++ b/drivers/media/i2c/max9x/Makefile @@ -6,5 +6,6 @@ ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU6)),) ccflags-y += -I$(src)/../../pci/intel/ipu6/ endif +ccflags-y += -I$(src)/../../../../include/media obj-m += max9x.o max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o \ No newline at end of file diff --git a/drivers/media/i2c/max9x/serdes.h b/drivers/media/i2c/max9x/serdes.h index 227f61e03229..0d3ae69ff86f 100644 --- a/drivers/media/i2c/max9x/serdes.h +++ b/drivers/media/i2c/max9x/serdes.h @@ -44,7 +44,7 @@ #if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) #include "ipu6-isys.h" #endif -#include +#include "ipu-acpi-pdata.h" #include "max9x_pdata.h" #define MAX9X_VDD_REGULATOR_NAME "vdd" diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h index 0889e01abdf3..90b406936e85 100644 --- a/include/media/ipu-acpi-pdata.h +++ b/include/media/ipu-acpi-pdata.h @@ -46,8 +46,11 @@ struct serdes_platform_data { unsigned int FPD_gpio; char suffix; unsigned int link_freq_mbps; + enum v4l2_mbus_type bus_type; unsigned int deser_nlanes; unsigned int ser_nlanes; + unsigned int des_port; + char ser_name[I2C_NAME_SIZE]; struct i2c_board_info *deser_board_info; }; @@ -58,6 +61,8 @@ struct serdes_subdev_info { unsigned short phy_i2c_addr; unsigned short ser_alias; char suffix[5]; /* suffix for subdevs */ + unsigned short ser_phys_addr; + unsigned int sensor_dt; }; struct serdes_module_pdata { diff --git a/kernel_patches/patch_6.12_mainline/0033-Support-IPU6-PSYS-FW-trace-dump-for-upstream-driver.patch b/kernel_patches/patch_6.12_mainline/0033-Support-IPU6-PSYS-FW-trace-dump-for-upstream-driver.patch index 3e13dc1276b6..8d4c6709e184 100644 --- a/kernel_patches/patch_6.12_mainline/0033-Support-IPU6-PSYS-FW-trace-dump-for-upstream-driver.patch +++ b/kernel_patches/patch_6.12_mainline/0033-Support-IPU6-PSYS-FW-trace-dump-for-upstream-driver.patch @@ -1,9 +1,7 @@ -From da41fbb54f5d8141ef93e2eaefb286c59fdca7db Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Fri, 25 Apr 2025 09:44:01 +0800 +From 9be3f6b342f17152516e3e0f226e669df83bd7bd Mon Sep 17 00:00:00 2001 +From: linya14x +Date: Mon, 13 Oct 2025 15:11:55 +0800 Subject: [PATCH] Support IPU6 PSYS FW trace dump for upstream driver - -Signed-off-by: hepengpx --- drivers/media/pci/intel/ipu6/ipu6-trace.c | 6 +++ drivers/media/pci/intel/ipu6/ipu6-trace.h | 1 + @@ -38,7 +36,7 @@ index f66d8898b1db..fe89d1b203b6 100644 +bool is_ipu_trace_enable(void); #endif diff --git a/drivers/media/pci/intel/ipu6/psys/ipu-psys.c b/drivers/media/pci/intel/ipu6/psys/ipu-psys.c -index 9f367496cbd0..e8638a918215 100644 +index d3422ea20e51..be71ba4518ad 100644 --- a/drivers/media/pci/intel/ipu6/psys/ipu-psys.c +++ b/drivers/media/pci/intel/ipu6/psys/ipu-psys.c @@ -31,6 +31,7 @@ @@ -49,7 +47,7 @@ index 9f367496cbd0..e8638a918215 100644 static bool async_fw_init; module_param(async_fw_init, bool, 0664); -@@ -1201,6 +1202,8 @@ static int ipu_psys_sched_cmd(void *ptr) +@@ -1179,6 +1180,8 @@ static int ipu_psys_sched_cmd(void *ptr) return 0; } @@ -58,7 +56,7 @@ index 9f367496cbd0..e8638a918215 100644 static void start_sp(struct ipu6_bus_device *adev) { struct ipu_psys *psys = ipu6_bus_get_drvdata(adev); -@@ -1211,7 +1214,7 @@ static void start_sp(struct ipu6_bus_device *adev) +@@ -1189,7 +1192,7 @@ static void start_sp(struct ipu6_bus_device *adev) val |= IPU6_PSYS_SPC_STATUS_START | IPU6_PSYS_SPC_STATUS_RUN | IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE; @@ -67,7 +65,7 @@ index 9f367496cbd0..e8638a918215 100644 IPU6_PSYS_SPC_STATUS_ICACHE_PREFETCH : 0; writel(val, spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL); } -@@ -1304,6 +1307,40 @@ static void run_fw_init_work(struct work_struct *work) +@@ -1282,6 +1285,40 @@ static void run_fw_init_work(struct work_struct *work) } } @@ -108,7 +106,7 @@ index 9f367496cbd0..e8638a918215 100644 static int ipu6_psys_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *auxdev_id) { -@@ -1442,6 +1479,9 @@ static int ipu6_psys_probe(struct auxiliary_device *auxdev, +@@ -1432,6 +1469,9 @@ static int ipu6_psys_probe(struct auxiliary_device *auxdev, strscpy(psys->caps.dev_model, IPU6_MEDIA_DEV_MODEL_NAME, sizeof(psys->caps.dev_model)); @@ -118,9 +116,9 @@ index 9f367496cbd0..e8638a918215 100644 mutex_unlock(&ipu_psys_mutex); dev_info(dev, "psys probe minor: %d\n", minor); -@@ -1503,6 +1543,8 @@ static void ipu6_psys_remove(struct auxiliary_device *auxdev) - +@@ -1500,6 +1540,8 @@ static void ipu6_psys_remove(struct auxiliary_device *auxdev) clear_bit(MINOR(psys->cdev.dev), ipu_psys_devices); + cdev_del(&psys->cdev); + ipu_trace_uninit(&auxdev->dev); + @@ -128,5 +126,5 @@ index 9f367496cbd0..e8638a918215 100644 mutex_destroy(&psys->mutex); -- -2.34.1 +2.43.0 diff --git a/kernel_patches/patch_6.12_mainline/0056-media-i2c-add-support-for-isx031-max9296.patch b/kernel_patches/patch_6.12_mainline/0056-media-i2c-add-support-for-isx031-max9296.patch deleted file mode 100644 index c0f61a39122b..000000000000 --- a/kernel_patches/patch_6.12_mainline/0056-media-i2c-add-support-for-isx031-max9296.patch +++ /dev/null @@ -1,7888 +0,0 @@ -From 2b9d455c0537e6c6491ad9787772ba049d3fa2e3 Mon Sep 17 00:00:00 2001 -From: zouxiaoh -Date: Mon, 30 Jun 2025 17:04:11 +0800 -Subject: [PATCH] media: i2c: add support for isx031 + max9296 - -Signed-off-by: zouxiaoh ---- - drivers/media/i2c/isx031.c | 885 ++++++ - drivers/media/i2c/max9x/Makefile | 5 + - drivers/media/i2c/max9x/max9295.c | 851 ++++++ - drivers/media/i2c/max9x/max9295.h | 143 + - drivers/media/i2c/max9x/max9296.c | 969 +++++++ - drivers/media/i2c/max9x/max9296.h | 148 + - drivers/media/i2c/max9x/max9x_pdata.h | 102 + - drivers/media/i2c/max9x/serdes.c | 2466 +++++++++++++++++ - drivers/media/i2c/max9x/serdes.h | 404 +++ - .../media/platform/intel/ipu6-acpi-common.c | 451 +++ - .../media/platform/intel/ipu6-acpi-pdata.c | 832 ++++++ - drivers/media/platform/intel/ipu6-acpi.c | 250 ++ - include/media/i2c/isx031.h | 24 + - include/media/ipu-acpi-pdata.h | 111 + - include/media/ipu-acpi.h | 222 ++ - 15 files changed, 7863 insertions(+) - create mode 100644 drivers/media/i2c/isx031.c - create mode 100644 drivers/media/i2c/max9x/Makefile - create mode 100644 drivers/media/i2c/max9x/max9295.c - create mode 100644 drivers/media/i2c/max9x/max9295.h - create mode 100644 drivers/media/i2c/max9x/max9296.c - create mode 100644 drivers/media/i2c/max9x/max9296.h - create mode 100644 drivers/media/i2c/max9x/max9x_pdata.h - create mode 100644 drivers/media/i2c/max9x/serdes.c - create mode 100644 drivers/media/i2c/max9x/serdes.h - create mode 100644 drivers/media/platform/intel/ipu6-acpi-common.c - create mode 100644 drivers/media/platform/intel/ipu6-acpi-pdata.c - create mode 100644 drivers/media/platform/intel/ipu6-acpi.c - create mode 100644 include/media/i2c/isx031.h - create mode 100644 include/media/ipu-acpi-pdata.h - create mode 100644 include/media/ipu-acpi.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -new file mode 100644 -index 000000000000..acaf41a82fbe ---- /dev/null -+++ b/drivers/media/i2c/isx031.c -@@ -0,0 +1,885 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2022-2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#define to_isx031(_sd) container_of(_sd, struct isx031, sd) -+ -+#define ISX031_REG_MODE_SELECT 0x8A01 -+#define ISX031_MODE_STANDBY 0x00 -+#define ISX031_MODE_STREAMING 0x80 -+ -+#define ISX031_REG_SENSOR_STATE 0x6005 -+#define ISX031_STATE_STREAMING 0x05 -+#define ISX031_STATE_STARTUP 0x02 -+ -+#define ISX031_REG_MODE_SET_F_LOCK 0xBEF0 -+#define ISX031_MODE_UNLOCK 0x53 -+ -+struct isx031_reg { -+ enum { -+ ISX031_REG_LEN_DELAY = 0, -+ ISX031_REG_LEN_08BIT = 1, -+ ISX031_REG_LEN_16BIT = 2, -+ } mode; -+ u16 address; -+ u16 val; -+}; -+ -+struct isx031_reg_list { -+ u32 num_of_regs; -+ const struct isx031_reg *regs; -+}; -+ -+struct isx031_link_freq_config { -+ const struct isx031_reg_list reg_list; -+}; -+ -+struct isx031_mode { -+ /* Frame width in pixels */ -+ u32 width; -+ -+ /* Frame height in pixels */ -+ u32 height; -+ -+ /* MEDIA_BUS_FMT */ -+ u32 code; -+ -+ /* MODE_FPS*/ -+ u32 fps; -+ -+ /* Sensor register settings for this resolution */ -+ const struct isx031_reg_list reg_list; -+}; -+ -+struct isx031 { -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ -+ /* Current mode */ -+ const struct isx031_mode *cur_mode; -+ /* Previous mode */ -+ const struct isx031_mode *pre_mode; -+ -+ /* To serialize asynchronus callbacks */ -+ struct mutex mutex; -+ -+ /* i2c client */ -+ struct i2c_client *client; -+ -+ struct isx031_platform_data *platform_data; -+ struct gpio_desc *reset_gpio; -+ -+ /* Streaming on/off */ -+ bool streaming; -+}; -+ -+static const struct isx031_reg isx031_init_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0xFFFF, 0x00}, // select mode -+ {ISX031_REG_LEN_08BIT, 0x0171, 0x00}, // close F_EBD -+ {ISX031_REG_LEN_08BIT, 0x0172, 0x00}, // close R_EBD -+}; -+ -+static const struct isx031_reg isx031_framesync_reg[] = { -+ /* External sync */ -+ {ISX031_REG_LEN_08BIT, 0xBF14, 0x01}, /* SG_MODE_APL */ -+ {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ -+ {ISX031_REG_LEN_08BIT, 0x0153, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* external pulse-based sync */ -+ {ISX031_REG_LEN_08BIT, 0x0144, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AF1, 0x00}, -+}; -+ -+static const struct isx031_reg isx031_1920_1536_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, // H size = 1920 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, // H croped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0x00}, // V size 1536 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x06}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0x00}, // V cropped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x80}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x06}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, -+}; -+ -+static const struct isx031_reg isx031_1920_1080_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, // H size = 1920 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, // H croped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0x38}, // V size 1080 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x04}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0xE4}, // V cropped 228*2 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x80}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0x38}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x04}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0xE4}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, -+ -+}; -+ -+static const struct isx031_reg isx031_1280_720_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x00}, // H size = 1280 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x05}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x40}, // H croped 320*2 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0xD0}, // V size 720 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x02}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0x98}, // V cropped 408*2 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x05}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x40}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0xD0}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x02}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0x98}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x01}, -+}; -+ -+static const struct isx031_reg_list isx031_init_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_init_reg), -+ .regs = isx031_init_reg, -+}; -+ -+static const struct isx031_reg_list isx031_framesync_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_framesync_reg), -+ .regs = isx031_framesync_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1920_1536_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1920_1536_30fps_reg), -+ .regs = isx031_1920_1536_30fps_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1920_1080_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1920_1080_30fps_reg), -+ .regs = isx031_1920_1080_30fps_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1280_720_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1280_720_30fps_reg), -+ .regs = isx031_1280_720_30fps_reg, -+}; -+ -+static const struct isx031_mode supported_modes[] = { -+ { -+ .width = 1920, -+ .height = 1080, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .fps = 30, -+ .reg_list = isx031_1920_1080_30fps_reg_list, -+ }, -+ { -+ .width = 1280, -+ .height = 720, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .fps = 30, -+ .reg_list = isx031_1280_720_30fps_reg_list, -+ }, -+ { -+ .width = 1920, -+ .height = 1536, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .fps = 30, -+ .reg_list = isx031_1920_1536_30fps_reg_list, -+ }, -+}; -+ -+static int isx031_reset(struct gpio_desc *reset_gpio) -+{ -+ if (!IS_ERR_OR_NULL(reset_gpio)) { -+ gpiod_set_value_cansleep(reset_gpio, 0); -+ usleep_range(500, 1000); -+ gpiod_set_value_cansleep(reset_gpio, 1); -+ /*Needs to sleep for quite a while before register writes*/ -+ usleep_range(200 * 1000, 200 * 1000 + 500); -+ -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int isx031_read_reg(struct isx031 *isx031, u16 reg, u16 len, u32 *val) -+{ -+ struct i2c_client *client = isx031->client; -+ struct i2c_msg msgs[2]; -+ u8 addr_buf[2]; -+ u8 data_buf[4] = {0}; -+ int ret; -+ -+ if (len > 4) -+ return -EINVAL; -+ -+ put_unaligned_be16(reg, addr_buf); -+ msgs[0].addr = client->addr; -+ msgs[0].flags = 0; -+ msgs[0].len = sizeof(addr_buf); -+ msgs[0].buf = addr_buf; -+ msgs[1].addr = client->addr; -+ msgs[1].flags = I2C_M_RD; -+ msgs[1].len = len; -+ msgs[1].buf = &data_buf[4 - len]; -+ -+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); -+ if (ret != ARRAY_SIZE(msgs)) -+ return -EIO; -+ -+ *val = get_unaligned_be32(data_buf); -+ -+ return 0; -+} -+ -+static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) -+{ -+ struct i2c_client *client = isx031->client; -+ u8 buf[6]; -+ -+ if (len > 4) -+ return -EINVAL; -+ -+ dev_dbg(&client->dev, "%s, reg %x len %x, val %x\n", __func__, reg, len, val); -+ put_unaligned_be16(reg, buf); -+ put_unaligned_be32(val << 8 * (4 - len), buf + 2); -+ if (i2c_master_send(client, buf, len + 2) != len + 2) { -+ dev_err(&client->dev, "%s:failed: reg=%2x\n", __func__, reg); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+static int isx031_write_reg_list(struct isx031 *isx031, -+ const struct isx031_reg_list *r_list) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(&isx031->sd); -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < r_list->num_of_regs; i++) { -+ if (r_list->regs[i].mode == ISX031_REG_LEN_DELAY) { -+ msleep(r_list->regs[i].val); -+ continue; -+ } -+ ret = isx031_write_reg(isx031, r_list->regs[i].address, -+ ISX031_REG_LEN_08BIT, -+ r_list->regs[i].val); -+ if (ret) { -+ dev_err_ratelimited(&client->dev, -+ "failed to write reg 0x%4.4x. error = %d", -+ r_list->regs[i].address, ret); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static int isx031_mode_transit(struct isx031 *isx031, int state) -+{ -+ struct i2c_client *client = isx031->client; -+ int ret; -+ int cur_mode, mode; -+ u32 val; -+ int retry = 50; -+ -+ if (state == ISX031_STATE_STARTUP) -+ mode = ISX031_MODE_STANDBY; -+ else if (state == ISX031_STATE_STREAMING) -+ mode = ISX031_MODE_STREAMING; -+ -+ retry = 50; -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0) -+ break; -+ usleep_range(10000, 10500); -+ } -+ cur_mode = val; -+ -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F_LOCK, 1, -+ ISX031_MODE_UNLOCK); -+ if (ret) { -+ dev_err(&client->dev, "failed to unlock mode"); -+ return ret; -+ } -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, -+ mode); -+ if (ret) { -+ dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", -+ cur_mode, mode); -+ return ret; -+ } -+ -+ /*streaming transit to standby need 1 frame+5ms*/ -+ retry = 50; -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0 && val == state) -+ break; -+ usleep_range(10000, 10500); -+ } -+ -+ return 0; -+} -+ -+static int isx031_identify_module(struct isx031 *isx031) -+{ -+ struct i2c_client *client = isx031->client; -+ int ret; -+ int retry = 50; -+ u32 val; -+ -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0) -+ break; -+ usleep_range(100000, 100500); -+ } -+ -+ if (ret) -+ return ret; -+ -+ dev_dbg(&client->dev, "sensor in mode 0x%x", val); -+ -+ /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly*/ -+ if (val == ISX031_STATE_STREAMING) { -+ if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) -+ return ret; -+ } -+ -+ ret = isx031_write_reg_list(isx031, &isx031_init_reg_list); -+ if (ret) -+ return ret; -+ if (isx031->platform_data != NULL) { -+ ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed in set framesync."); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static void isx031_update_pad_format(const struct isx031_mode *mode, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ fmt->width = mode->width; -+ fmt->height = mode->height; -+ fmt->code = mode->code; -+ fmt->field = V4L2_FIELD_NONE; -+} -+ -+static int isx031_start_streaming(struct isx031 *isx031) -+{ -+ int ret; -+ struct i2c_client *client = isx031->client; -+ const struct isx031_reg_list *reg_list; -+ -+ if (isx031->cur_mode != isx031->pre_mode) { -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed to set stream mode"); -+ return ret; -+ } -+ isx031->pre_mode = isx031->cur_mode; -+ } else { -+ dev_dbg(&client->dev, "same mode, skip write reg list"); -+ } -+ -+ ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); -+ if (ret) { -+ dev_err(&client->dev, "failed to start streaming"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void isx031_stop_streaming(struct isx031 *isx031) -+{ -+ struct i2c_client *client = isx031->client; -+ if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) -+ dev_err(&client->dev, "failed to stop streaming"); -+} -+ -+static int isx031_set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ struct i2c_client *client = isx031->client; -+ int ret = 0; -+ -+ if (isx031->streaming == enable) -+ return 0; -+ -+ mutex_lock(&isx031->mutex); -+ if (enable) { -+ ret = pm_runtime_get_sync(&client->dev); -+ if (ret < 0) { -+ pm_runtime_put_noidle(&client->dev); -+ mutex_unlock(&isx031->mutex); -+ return ret; -+ } -+ -+ ret = isx031_start_streaming(isx031); -+ if (ret) { -+ enable = 0; -+ isx031_stop_streaming(isx031); -+ pm_runtime_put(&client->dev); -+ } -+ } else { -+ isx031_stop_streaming(isx031); -+ pm_runtime_put(&client->dev); -+ } -+ -+ isx031->streaming = enable; -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return ret; -+} -+ -+static int isx031_enable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return isx031_set_stream(subdev, true); -+} -+ -+static int isx031_disable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return isx031_set_stream(subdev, false); -+} -+ -+static int __maybe_unused isx031_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ if (isx031->streaming) -+ isx031_stop_streaming(isx031); -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int __maybe_unused isx031_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ const struct isx031_reg_list *reg_list; -+ int ret; -+ -+ if (isx031->reset_gpio != NULL) -+ isx031_reset(isx031->reset_gpio); -+ -+ ret = isx031_identify_module(isx031); -+ if (ret == 0) { -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "resume: failed to apply cur mode"); -+ return ret; -+ } -+ } else { -+ dev_err(&client->dev, "isx031 resume failed"); -+ return ret; -+ } -+ -+ mutex_lock(&isx031->mutex); -+ if (isx031->streaming) { -+ ret = isx031_start_streaming(isx031); -+ if (ret) { -+ isx031->streaming = false; -+ isx031_stop_streaming(isx031); -+ mutex_unlock(&isx031->mutex); -+ return ret; -+ } -+ } -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static unsigned int isx031_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_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return MIPI_CSI2_DT_YUV422_8B; -+ case MEDIA_BUS_FMT_SBGGR16_1X16: -+ case MEDIA_BUS_FMT_SGBRG16_1X16: -+ case MEDIA_BUS_FMT_SGRBG16_1X16: -+ case MEDIA_BUS_FMT_SRGGB16_1X16: -+ return MIPI_CSI2_DT_RAW16; -+ 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_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: -+ /* return unavailable MIPI data type - 0x3f */ -+ WARN_ON(1); -+ return 0x3f; -+ } -+} -+ -+static int isx031_get_frame_desc(struct v4l2_subdev *sd, -+ unsigned int pad, struct v4l2_mbus_frame_desc *desc) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ unsigned int i; -+ -+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; -+ desc->num_entries = V4L2_FRAME_DESC_ENTRY_MAX; -+ -+ for (i = 0; i < desc->num_entries; i++) { -+ desc->entry[i].flags = 0; -+ desc->entry[i].pixelcode = MEDIA_BUS_FMT_FIXED; -+ desc->entry[i].length = 0; -+ desc->entry[i].stream = i; -+ desc->entry[i].bus.csi2.vc = i; -+ desc->entry[i].bus.csi2.dt = isx031_mbus_code_to_mipi(isx031->cur_mode->code); -+ } -+ -+ return 0; -+} -+ -+static int isx031_set_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ const struct isx031_mode *mode; -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) -+ if (supported_modes[i].code == fmt->format.code && -+ supported_modes[i].width == fmt->format.width && -+ supported_modes[i].height == fmt->format.height) { -+ mode = &supported_modes[i]; -+ break; -+ } -+ -+ if (i >= ARRAY_SIZE(supported_modes)) -+ mode = &supported_modes[0]; -+ -+ mutex_lock(&isx031->mutex); -+ -+ isx031_update_pad_format(mode, &fmt->format); -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; -+ } else { -+ isx031->cur_mode = mode; -+ } -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_get_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ fmt->format = *v4l2_subdev_state_get_format(sd_state, -+ fmt->pad); -+ else -+ isx031_update_pad_format(isx031->cur_mode, &fmt->format); -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ isx031_update_pad_format(&supported_modes[0], -+ v4l2_subdev_state_get_format(fh->state, 0)); -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static const struct v4l2_subdev_video_ops isx031_video_ops = { -+ .s_stream = isx031_set_stream, -+}; -+ -+static const struct v4l2_subdev_pad_ops isx031_pad_ops = { -+ .set_fmt = isx031_set_format, -+ .get_fmt = isx031_get_format, -+ .get_frame_desc = isx031_get_frame_desc, -+ .enable_streams = isx031_enable_streams, -+ .disable_streams = isx031_disable_streams, -+}; -+ -+static const struct v4l2_subdev_ops isx031_subdev_ops = { -+ .video = &isx031_video_ops, -+ .pad = &isx031_pad_ops, -+}; -+ -+static const struct media_entity_operations isx031_subdev_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+static const struct v4l2_subdev_internal_ops isx031_internal_ops = { -+ .open = isx031_open, -+}; -+ -+static void isx031_remove(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ v4l2_async_unregister_subdev(sd); -+ media_entity_cleanup(&sd->entity); -+ pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); -+ -+} -+ -+static int isx031_probe(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd; -+ struct isx031 *isx031; -+ const struct isx031_reg_list *reg_list; -+ int ret; -+ -+ isx031 = devm_kzalloc(&client->dev, sizeof(*isx031), GFP_KERNEL); -+ if (!isx031) -+ return -ENOMEM; -+ -+ isx031->client = client; -+ isx031->platform_data = client->dev.platform_data; -+ if (isx031->platform_data == NULL) -+ dev_warn(&client->dev, "no platform data provided\n"); -+ -+ isx031->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", -+ GPIOD_OUT_HIGH); -+ if (IS_ERR(isx031->reset_gpio)) -+ return -EPROBE_DEFER; -+ else if (isx031->reset_gpio == NULL) -+ dev_warn(&client->dev, "Reset GPIO not found"); -+ else { -+ dev_dbg(&client->dev, "Found reset GPIO"); -+ isx031_reset(isx031->reset_gpio); -+ } -+ -+ /* initialize subdevice */ -+ sd = &isx031->sd; -+ v4l2_i2c_subdev_init(sd, client, &isx031_subdev_ops); -+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -+ sd->internal_ops = &isx031_internal_ops; -+ sd->entity.ops = &isx031_subdev_entity_ops; -+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ -+ /* initialize subdev media pad */ -+ isx031->pad.flags = MEDIA_PAD_FL_SOURCE; -+ ret = media_entity_pads_init(&sd->entity, 1, &isx031->pad); -+ if (ret < 0) { -+ dev_err(&client->dev, -+ "%s : media entity init Failed %d\n", __func__, ret); -+ return ret; -+ } -+ -+ ret = isx031_identify_module(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to find sensor: %d", ret); -+ return ret; -+ } -+ -+ if (isx031->platform_data && isx031->platform_data->suffix) -+ snprintf(isx031->sd.name, sizeof(isx031->sd.name), "isx031 %c", -+ isx031->platform_data->suffix); -+ -+ mutex_init(&isx031->mutex); -+ -+ /* 1920x1536 default */ -+ isx031->cur_mode = NULL; -+ isx031->pre_mode = &supported_modes[0]; -+ reg_list = &isx031->pre_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed to apply preset mode"); -+ goto probe_error_media_entity_cleanup; -+ } -+ isx031->cur_mode = isx031->pre_mode; -+ ret = v4l2_async_register_subdev_sensor(&isx031->sd); -+ if (ret < 0) { -+ dev_err(&client->dev, "failed to register V4L2 subdev: %d", -+ ret); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ /* -+ * Device is already turned on by i2c-core with ACPI domain PM. -+ * Enable runtime PM and turn off the device. -+ */ -+ pm_runtime_set_active(&client->dev); -+ pm_runtime_enable(&client->dev); -+ pm_runtime_idle(&client->dev); -+ -+ return 0; -+ -+probe_error_media_entity_cleanup: -+ media_entity_cleanup(&isx031->sd.entity); -+ pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops isx031_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(isx031_suspend, isx031_resume) -+}; -+ -+static const struct i2c_device_id isx031_id_table[] = { -+ { "isx031", 0 }, -+ { /* sentinel */ }, -+}; -+MODULE_DEVICE_TABLE(i2c, isx031_id_table); -+ -+static struct i2c_driver isx031_i2c_driver = { -+ .driver = { -+ .name = "isx031", -+ .pm = &isx031_pm_ops, -+ }, -+ .probe = isx031_probe, -+ .remove = isx031_remove, -+ .id_table = isx031_id_table, -+}; -+ -+module_i2c_driver(isx031_i2c_driver); -+ -+MODULE_AUTHOR("Hao Yao "); -+MODULE_DESCRIPTION("isx031 sensor driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -new file mode 100644 -index 000000000000..aa6dcb0cd20d ---- /dev/null -+++ b/drivers/media/i2c/max9x/Makefile -@@ -0,0 +1,5 @@ -+ccflags-y += -I$(src)/../../pci/intel/ipu6/ -+ -+obj-m += serdes.o -+obj-m += max9295.o -+obj-m += max9296.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -new file mode 100644 -index 000000000000..052c02a847fd ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -0,0 +1,851 @@ -+/* -+ * max9295_main.c - Maxim MAX9295 CSI-2 to GMSL2/GMSL1 Serializer -+ * -+ * Copyright (c) 2020, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max9295.h" -+ -+static const struct regmap_config max9295_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+}; -+ -+static const char *const max9295_gpio_chip_names[] = { -+ "MFP1", -+ "MFP2", -+ "MFP3", -+ "MFP4", -+ "MFP5", -+ "MFP6", -+ "MFP7", -+ "MFP8", -+ "MFP9", -+ "MFP10", -+ "MFP11", -+}; -+ -+/* Declarations */ -+static int max9295_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int csi_id, bool enable); -+static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max9295_set_video_pipe_double_loading(struct max9x_common *common, unsigned int pipe_id, unsigned int bpp); -+static int max9295_set_video_pipe_pixel_padding(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int min_bpp, unsigned int max_bpp); -+static int max9295_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max9295_enable_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_disable_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_set_local_control_channel_enabled(struct max9x_common *common, bool enabled); -+static int max9295_select_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_deselect_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_verify_devid(struct max9x_common *common); -+static int max9295_enable(struct max9x_common *common); -+static int max9295_disable(struct max9x_common *common); -+static int max9295_remap_addr(struct max9x_common *common); -+static int max9295_add_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr); -+static int max9295_remove_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr); -+static int max9295_reset(struct max9x_common *common); -+ -+/* max9295 gpio */ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip); -+static int max9295_gpio_get_direction(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_direction_input(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value); -+static int max9295_gpio_get(struct gpio_chip *chip, unsigned int offset); -+static void max9295_gpio_set(struct gpio_chip *chip, unsigned int offset, int value); -+static int max9295_setup_gpio(struct max9x_common *common); -+/* max9295 gpio */ -+ -+static struct max9x_common_ops max9295_common_ops = { -+ .max_elements = max9295_max_elements, -+ .soft_reset = max9295_reset, -+ .enable = max9295_enable, -+ .disable = max9295_disable, -+ .verify_devid = max9295_verify_devid, -+ .remap_addr = max9295_remap_addr, -+ .setup_gpio = max9295_setup_gpio, -+}; -+ -+static struct max9x_serial_link_ops max9295_serial_link_ops = { -+ .enable = max9295_enable_serial_link, -+ .disable = max9295_disable_serial_link, -+ .select = max9295_select_serial_link, -+ .deselect = max9295_deselect_serial_link, -+}; -+ -+static struct max9x_translation_ops max9295_translation_ops = { -+ .add = max9295_add_translate_addr, -+ .remove = max9295_remove_translate_addr, -+}; -+ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip) -+{ -+ return container_of(chip, struct max9x_common, gpio_chip); -+} -+ -+static int max9295_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ TRY(ret, regmap_read(map, MAX9295_GPIO_A(offset), &val)); -+ -+ return (FIELD_GET(MAX9295_GPIO_A_OUT_DIS_FIELD, val) == 0U ? -+ GPIOD_OUT_LOW : GPIOD_IN); -+} -+ -+static int max9295_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), -+ MAX9295_GPIO_A_OUT_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_DIS_FIELD, 1U)); -+} -+ -+static int max9295_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int mask = 0; -+ unsigned int val; -+ -+ mask = MAX9295_GPIO_A_OUT_DIS_FIELD | MAX9295_GPIO_A_OUT_FIELD | -+ MAX9295_GPIO_A_RX_EN_FIELD; -+ -+ // Enable the GPIO as an output -+ val = MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_DIS_FIELD, 0U); -+ // Write out the initial value to the GPIO -+ val |= MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U)); -+ // Disable remote control over SerDes link -+ val |= MAX9X_FIELD_PREP(MAX9295_GPIO_A_RX_EN_FIELD, 0U); -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), mask, val); -+} -+ -+static int max9295_gpio_get(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ TRY(ret, regmap_read(map, MAX9295_GPIO_A(offset), &val)); -+ -+ return FIELD_GET(MAX9295_GPIO_A_IN_FIELD, val); -+} -+ -+static void max9295_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ regmap_update_bits(map, MAX9295_GPIO_A(offset), -+ MAX9295_GPIO_A_OUT_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U))); -+} -+ -+static int max9295_setup_gpio(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ struct max9x_gpio_pdata *gpio_pdata; -+ -+ if (common->dev->platform_data) { -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ gpio_pdata = &pdata->gpio; -+ } -+ -+ // Functions -+ if (gpio_pdata && gpio_pdata->label) -+ common->gpio_chip.label = gpio_pdata->label; -+ else -+ common->gpio_chip.label = dev_name(dev); -+ -+ dev_dbg(dev, "gpio_chip label is %s, dev_name is %s", -+ common->gpio_chip.label, dev_name(dev)); -+ -+ common->gpio_chip.parent = dev; -+ common->gpio_chip.get_direction = max9295_gpio_get_direction; -+ common->gpio_chip.direction_input = max9295_gpio_direction_input; -+ common->gpio_chip.direction_output = max9295_gpio_direction_output; -+ common->gpio_chip.get = max9295_gpio_get; -+ common->gpio_chip.set = max9295_gpio_set; -+ common->gpio_chip.ngpio = MAX9295_NUM_GPIO; -+ common->gpio_chip.can_sleep = 1; -+ common->gpio_chip.base = -1; -+ if (gpio_pdata && gpio_pdata->names) -+ common->gpio_chip.names = gpio_pdata->names; -+ else -+ common->gpio_chip.names = max9295_gpio_chip_names; -+ -+ ret = devm_gpiochip_add_data(dev, &common->gpio_chip, common); -+ if (ret < 0) { -+ dev_err(dev, "gpio_init: Failed to add max9295_gpio\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max9295_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, csi %d: %s, %d lanes", pipe_id, csi_id, -+ (enable ? "enable" : "disable"), common->csi_link[csi_id].config.num_lanes); -+ -+ // Select number of lanes for CSI port csi_id -+ TRY(ret, regmap_update_bits(map, MAX9295_MIPI_RX_1, -+ MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ common->csi_link[csi_id].config.num_lanes - 1)) -+ ); -+ -+ // Select CSI port csi_id for video pipe pipe_id -+ // Enable CSI port csi_id (9295A only has port 1, 9295B has both ports) -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_0, -+ MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) | MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id), csi_id) | -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id), enable ? 1U : 0U)) -+ ); -+ -+ // Start video pipe pipe_id from CSI port csi_id -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_9, -+ MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), enable ? 1U : 0U)) -+ ); -+ -+ // Enable video transmit for pipe -+ TRY(ret, regmap_update_bits(map, MAX9295_REG2, -+ MAX9295_REG2_VID_TX_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX9295_REG2_VID_TX_EN_FIELD(pipe_id), enable ? 1U : 0U)) -+ ); -+ -+ return 0; -+} -+ -+static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, -+ unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int data_type_slot, dt; -+ int ret; -+ -+ for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { -+ dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; -+ dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", -+ pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | -+ MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -+ ); -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9295_set_video_pipe_double_loading() - Configure Double Loading Mode on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.1 "Double Mode (Serializer)" -+ */ -+static int max9295_set_video_pipe_double_loading(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_pipe_config *config = &common->video_pipe[pipe_id].config; -+ unsigned int reg; -+ unsigned int fields; -+ int ret; -+ -+ if (pipe_id >= MAX9295_NUM_VIDEO_PIPES) -+ return -EINVAL; -+ -+ // Reset double loading registers to defaults -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_10, -+ MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id), -+ 0x0)); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_11, -+ MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id) | -+ MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id), -+ 0x0)); -+ -+ // Enable double pixel mode for pipe -+ switch (bpp) { -+ case 0: -+ //bpp not used on this pipe, but still valid input -+ break; -+ case 8: -+ reg = MAX9295_FRONTTOP_10; -+ fields = MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id); -+ break; -+ case 10: -+ reg = MAX9295_FRONTTOP_11; -+ fields = MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id); -+ break; -+ case 12: -+ reg = MAX9295_FRONTTOP_11; -+ fields = MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ // Enable pixel doubling for specified pipe -+ if (bpp != 0) { -+ dev_info(dev, "Configuring double loading mode for pipe %u (%ubpp -> %ubpp)", -+ pipe_id, bpp, (bpp * 2)); -+ TRY(ret, regmap_update_bits(map, reg, fields, 0xFF)); -+ } -+ -+ // We share MAX9295_SOFT_BPP_FIELD/MAX9295_SOFT_BPP_EN_FIELD with -+ // max9295_set_video_pipe_pixel_padding(), only write to it if zero -+ // padding is disabled and double loading mode is enabled. Zero padding -+ // takes precedence and handles the 'both are disabled' case. -+ if (config->soft_min_pixel_bpp == 0 && config->soft_max_pixel_bpp == 0) { -+ // Override output bpp -+ TRY(ret, regmap_update_bits(map, MAX9295_SOFT_BPP(pipe_id), -+ MAX9295_SOFT_BPP_EN_FIELD | MAX9295_SOFT_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_EN_FIELD, bpp == 0 ? 0U : 1U) | -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_FIELD, bpp * 2)) -+ ); -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9295_set_video_pipe_pixel_padding() - Configure zero padding on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @min_bpp: Smallest BPP value of data in the pipe. Must be >= 8. -+ * @max_bpp: Largest BPP value of data in the pipe. Must be <= 16. -+ * -+ * Normally, video pipes can only transmit data with the same BPP value. Zero -+ * padding is a method to allow data with multiple BPP values to be transmitted -+ * over the same video pipe by padding the smaller BPP data to be the same BPP -+ * as the largest BPP data. The deserializer will automatically recover the -+ * data's original BPP based on datatype information transmitted alongside the -+ * padded data. -+ * -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.2 "Zero Padding" -+ */ -+static int max9295_set_video_pipe_pixel_padding(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int min_bpp, unsigned int max_bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_pipe_config *config = &common->video_pipe[pipe_id].config; -+ int ret; -+ bool enable = (min_bpp != 0) && (max_bpp != 0); -+ -+ if (enable) -+ dev_dbg(dev, "Configuring zero padding for pipe %u (%u <= bpp <= %u)", pipe_id, min_bpp, max_bpp); -+ else -+ dev_dbg(dev, "%s: pipe %u, min_bpp: %u, max_bpp: %u not enabled", __func__, pipe_id, min_bpp, max_bpp); -+ -+ if (pipe_id >= MAX9295_NUM_VIDEO_PIPES) -+ return -EINVAL; -+ -+ /* Auto bpp should be disabled to override bpp */ -+ TRY(ret, regmap_update_bits(map, MAX9295_VIDEO_TX0(pipe_id), -+ MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD, enable ? 0U : 1U)) -+ ); -+ -+ if (enable) -+ TRY(ret, regmap_update_bits(map, MAX9295_VIDEO_TX1(pipe_id), -+ MAX9295_VIDEO_TX1_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_VIDEO_TX1_BPP_FIELD, max_bpp)) -+ ); -+ -+ // We share MAX9295_SOFT_BPP_FIELD/MAX9295_SOFT_BPP_EN_FIELD with -+ // max9295_set_video_pipe_double_loading(), only write to it if zero -+ // padding is enabled (this function takes precedence) or if both zero -+ // pading is disabled and double loading is disabled. -+ if (enable || (!enable && config->dbl_pixel_bpp == 0)) { -+ TRY(ret, regmap_update_bits(map, MAX9295_SOFT_BPP(pipe_id), -+ MAX9295_SOFT_BPP_EN_FIELD | MAX9295_SOFT_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_EN_FIELD, enable ? 1U : 0U) | -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_FIELD, min_bpp)) -+ ); -+ } -+ -+ return 0; -+} -+ -+static int max9295_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX9295_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX9295_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX9295_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX9295_NUM_CSI_LINKS; -+ case MAX9X_DATA_TYPES: -+ return MAX9295_NUM_DATA_TYPES; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static int max9295_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_dbg(dev, "Link %d: Enable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ TRY(ret, max9295_set_pipe_data_types_enabled(common, pipe_id, true)); -+ -+ TRY(ret, max9295_set_video_pipe_double_loading(common, pipe_id, config->dbl_pixel_bpp)); -+ -+ TRY(ret, max9295_set_video_pipe_pixel_padding(common, pipe_id, -+ config->soft_min_pixel_bpp, config->soft_max_pixel_bpp)); -+ -+ TRY(ret, max9295_set_pipe_csi_enabled(common, pipe_id, config->src_csi, true)); -+ } -+ -+ return 0; -+} -+ -+static int max9295_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_dbg(dev, "Link %d: Disable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ TRY(ret, max9295_set_pipe_data_types_enabled(common, pipe_id, false)); -+ -+ TRY(ret, max9295_set_pipe_csi_enabled(common, pipe_id, config->src_csi, false)); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Enable RCLK (27MHz) output on MFP4 pin. This pin is routed on some imager boards -+ * to the imager instead of an on-board oscillator. -+ */ -+static int max9295_enable_rclk(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ if (!common->external_refclk_enable) { -+ dev_dbg(dev, "Enable RCLK: external-refclk-enable not present, not enabling external refclk"); -+ return 0; -+ } -+ -+ dev_info(dev, "Enable RCLK: 27MHz"); -+ -+ // Configure pre-defined 27MHz frequency (0b01 = 27MHz) -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ MAX9295_REFGEN_PREDEF_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_REFGEN_PREDEF_FREQ_FIELD, 1U)) -+ ); -+ -+ // Enable reference generation -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ MAX9295_REFGEN_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_REFGEN_EN_FIELD, 1U)) -+ ); -+ -+ // Configure reference generation output on MFP4 -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ MAX9295_PCLK_GPIO_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) -+ ); -+ -+ // Enable output -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ MAX9295_PCLK_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_REG3, -+ MAX9295_RCLK_SEL_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_RCLK_SEL_FIELD, 3U)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_REG6, -+ MAX9295_RCLK_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_RCLK_EN_FIELD, 1U)) -+ ); -+ -+ return 0; -+} -+ -+static int max9295_set_local_control_channel_enabled(struct max9x_common *common, bool enabled) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); -+} -+ -+static int max9295_select_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max9295_set_local_control_channel_enabled(common, true); -+} -+ -+static int max9295_deselect_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max9295_set_local_control_channel_enabled(common, false); -+} -+ -+static int max9295_verify_devid(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int dev_id, dev_rev; -+ int ret; -+ -+ // Fetch and output chip name + revision -+ TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); -+ -+ switch (dev_id) { -+ case MAX9295A: -+ dev_info(dev, "Detected MAX9295A revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ case MAX9295B: -+ dev_info(dev, "Detected MAX9295B revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ case MAX9295E: -+ dev_info(dev, "Detected MAX9295E revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ default: -+ dev_err(dev, "Unknown device ID %d revision %ld", dev_id, -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int max9295_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ TRY(ret, max9295_verify_devid(common)); -+ -+ // Turn on RCLK/PCLK -+ ret = max9295_enable_rclk(common); -+ if (ret) -+ dev_warn(dev, "Failed to enable RCLK output"); -+ -+ // Initialize local CC to off -+ TRY(ret, max9295_set_local_control_channel_enabled(common, false)); -+ -+ /* Clear the pipe maps */ -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); -+ -+ /* Clear the csi port selections */ -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ -+ return 0; -+} -+ -+static int max9295_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "Disable"); -+ -+ return 0; -+} -+ -+//TODO: more efforts to remap -+static int max9295_remap_addr(struct max9x_common *common) -+{ -+ int ret; -+ struct device *dev = common->dev; -+ -+ if (!common->phys_client) -+ return 0; -+ -+ if (!common->phys_map) -+ return 0; -+ -+ dev_info(dev, "Remap address from 0x%02x to 0x%02x", -+ common->phys_client->addr, common->client->addr); -+ -+ TRY(ret, regmap_update_bits(common->phys_map, MAX9295_REG0, -+ MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, common->client->addr)) -+ ); -+ -+ /* -+ * Use lower bits of I2C address as unique TX_SRC_ID to prevent -+ * conflicts for info frames, SPI, etc. (Leave video pipes alone) -+ */ -+ ret = regmap_update_bits(common->map, MAX9295_CFGI_INFOFR_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ ret |= regmap_update_bits(common->map, MAX9295_CFGL_SPI_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ ret |= regmap_update_bits(common->map, MAX9295_CFGC_CC_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ ret |= regmap_update_bits(common->map, MAX9295_CFGL_GPIO_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ ret |= regmap_update_bits(common->map, MAX9295_CFGL_IIC_X, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ ret |= regmap_update_bits(common->map, MAX9295_CFGL_IIC_Y, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int max9295_add_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int alias; -+ unsigned int src; -+ int ret; -+ -+ for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ -+ src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); -+ if (src == virt_addr || src == 0) { -+ dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", -+ MAX9295_I2C_SRC(i2c_id, alias), virt_addr, -+ MAX9295_I2C_DST(i2c_id, alias), phys_addr); -+ TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) -+ ); -+ } -+ } -+ -+ return -ENOMEM; -+} -+ -+static int max9295_remove_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr) -+{ -+ struct regmap *map = common->map; -+ unsigned int alias; -+ unsigned int src; -+ int ret; -+ -+ for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); -+ if (src == virt_addr) { -+ return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); -+ } -+ } -+ -+ return 0; -+} -+ -+static int max9295_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Reset"); -+ -+ /* Reset entire chip by CTRL0_RST_ALL: 0x10[7]*/ -+ TRY(ret, regmap_write(map, MAX9295_CTRL0, MAX9295_CTRL0_RST_ALL)); -+ usleep_range(45000, 45050); -+ -+ return 0; -+} -+ -+static int max9295_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ while (max9295_verify_devid(common) != 0) { -+ dev_dbg(dev, "resume not ready"); -+ usleep_range(100000, 100050); -+ } -+ return max9x_common_resume(common); -+} -+ -+static int max9295_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ return max9x_common_suspend(common); -+} -+ -+static int max9295_freeze(struct device *dev) -+{ -+ return max9295_suspend(dev); -+} -+ -+static int max9295_restore(struct device *dev) -+{ -+ return max9295_resume(dev); -+} -+ -+static int max9295_probe(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *ser = NULL; -+ int ret; -+ -+ dev_dbg(dev, "Probing"); -+ -+ ser = devm_kzalloc(dev, sizeof(*ser), GFP_KERNEL); -+ if (!ser) { -+ dev_err(dev, "Failed to allocate memory."); -+ return -ENOMEM; -+ } -+ -+ ser->type = MAX9X_SERIALIZER; -+ ser->translation_ops = &max9295_translation_ops; -+ -+ ret = max9x_common_init_i2c_client(ser, client, &max9295_regmap_config, -+ &max9295_common_ops, -+ &max9295_serial_link_ops, -+ NULL, NULL); -+ if (ret) -+ return ret; -+ -+ dev_info(dev, "probe successful"); -+ return 0; -+} -+ -+static void max9295_remove(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *ser = NULL; -+ -+ dev_dbg(dev, "Removing"); -+ -+ ser = max9x_client_to_common(client); -+ max9x_destroy(ser); -+} -+ -+static const struct dev_pm_ops max9295_pm_ops = { -+ .suspend = max9295_suspend, -+ .resume = max9295_resume, -+ .freeze = max9295_freeze, -+ .restore = max9295_restore, -+}; -+ -+static struct i2c_device_id max9295_idtable[] = { -+ {"max9295", 0}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(i2c, max9295_idtable); -+ -+static struct i2c_driver max9295_driver = { -+ .driver = { -+ .name = "max9295", -+ .owner = THIS_MODULE, -+ /* -+ * TODO: -+ * Since max9295 is powered externally, -+ * there is no need to handle suspend/resume now, but will add later: -+ * .pm = &max9295_pm_ops, -+ */ -+ }, -+ .probe = max9295_probe, -+ .remove = max9295_remove, -+ .id_table = max9295_idtable, -+}; -+ -+module_i2c_driver(max9295_driver); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Yan, Dongcheng "); -+MODULE_DESCRIPTION("Maxim MAX9295 CSI-2/parallel to GMSL2 Serializer driver"); -diff --git a/drivers/media/i2c/max9x/max9295.h b/drivers/media/i2c/max9x/max9295.h -new file mode 100644 -index 000000000000..de4505639dec ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9295.h -@@ -0,0 +1,143 @@ -+/* -+ * max9295.h - Maxim MAX9295 registers and constants. -+ * -+ * Copyright (c) 2020, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9295_H_ -+#define _MAX9295_H_ -+ -+#include -+#include "serdes.h" -+ -+enum max9295_dev_id { -+ MAX9295A = 0x91, -+ MAX9295B = 0x93, -+ MAX9295E = 0x9B -+}; -+ -+enum max9295_gpio_out_type { -+ MAX9295_GPIO_OUT_TYPE_OPEN_DRAIN = 0, -+ MAX9295_GPIO_OUT_TYPE_PUSH_PULL = 1, -+}; -+ -+enum max9295_gpio_pull_updn_sel { -+ MAX9295_GPIO_PULL_UPDN_SEL_NONE = 0, -+ MAX9295_GPIO_PULL_UPDN_SEL_UP = 1, -+ MAX9295_GPIO_PULL_UPDN_SEL_DOWN = 2, -+}; -+ -+#define MAX9295_NUM_ALIASES 2 /* 2 per i2c bus */ -+#define MAX9295_NUM_SERIAL_LINKS 1 -+#define MAX9295_NUM_VIDEO_PIPES 4 -+#define MAX9295_NUM_MIPI_MAPS 16 -+#define MAX9295_NUM_CSI_LINKS 2 // Only 1 port, but it is technically Port B -+#define MAX9295_NUM_GPIO 11 -+#define MAX9295_NUM_DATA_TYPES 4 -+ -+#define MAX9295_REG0 (0x0) -+#define MAX9295_REG0_DEV_ADDR_FIELD GENMASK(7, 1) -+ -+#define MAX9295_PHY_REM_CTRL (0x1) -+#define MAX9295_PHY_REM_CTRL_DIS_FIELD BIT(4) -+#define MAX9295_PHY_LOCAL_CTRL_DIS_FIELD BIT(5) -+ -+#define MAX9295_REG2 (0x2) -+#define MAX9295_REG2_VID_TX_EN_FIELD(pipe_id) BIT((pipe_id) + 4) -+ -+#define MAX9295_DEV_ID (0xD) -+#define MAX9295_DEV_REV (0xE) -+#define MAX9295_DEV_REV_FIELD GENMASK(3, 0) -+#define MAX9295_CTRL0 (0x10) -+#define MAX9295_CTRL0_RST_ALL BIT(7) -+ -+#define MAX9295_CFGI_INFOFR_TR3 (0x7B) -+#define MAX9295_CFGL_SPI_TR3 (0x83) -+#define MAX9295_CFGC_CC_TR3 (0x8B) -+#define MAX9295_CFGL_GPIO_TR3 (0x93) -+#define MAX9295_CFGL_IIC_X (0xA3) -+#define MAX9295_CFGL_IIC_Y (0xAB) -+#define MAX9295_TR3_TX_SRC_ID GENMASK(2, 0) -+ -+#define MAX9295_VIDEO_TX0(pipe_id) (0x100 + (pipe_id) * 8) -+#define MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD BIT(3) -+#define MAX9295_VIDEO_TX1(pipe_id) (0x101 + (pipe_id) * 8) -+#define MAX9295_VIDEO_TX1_BPP_FIELD GENMASK(5, 0) -+ -+#define MAX9295_GPIO(gpio) (0x2BE + ((gpio) * 3)) -+#define MAX9295_GPIO_A(gpio) (MAX9295_GPIO(gpio) + 0) -+#define MAX9295_GPIO_A_OUT_DIS_FIELD BIT(0) -+#define MAX9295_GPIO_A_TX_EN_FIELD BIT(1) -+#define MAX9295_GPIO_A_RX_EN_FIELD BIT(2) -+#define MAX9295_GPIO_A_IN_FIELD BIT(3) -+#define MAX9295_GPIO_A_OUT_FIELD BIT(4) -+#define MAX9295_GPIO_A_RES_CFG_FIELD BIT(7) -+#define MAX9295_GPIO_B(gpio) (MAX9295_GPIO(gpio) + 1) -+#define MAX9295_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX9295_GPIO_B_OUT_TYPE_FIELD BIT(5) -+#define MAX9295_GPIO_B_PULL_UPDN_SEL_FIELD GENMASK(7, 6) -+#define MAX9295_GPIO_C(gpio) (MAX9295_GPIO(gpio) + 2) -+#define MAX9295_GPIO_C_RX_ID GENMASK(4, 0) -+ -+#define MAX9295_FRONTTOP_0 (0x308) -+#define MAX9295_FRONTTOP_0_LINE_INFO BIT(6) -+#define MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id) BIT((csi_id) + 4) -+#define MAX9295_FRONTTOP_9 (0x311) -+#define MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id) BIT((pipe_id) + 4 * (csi_id)) -+ -+// Double loading mode -+#define MAX9295_FRONTTOP_10 (0x312) -+#define MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_11 (0x313) -+#define MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id) BIT((pipe_id) + 4) -+ -+#define MAX9295_MEM_DT_SEL(pipe_id, dt_slot) (0x314 + (dt_slot) / 2 * 0xC2 + 2 * (pipe_id) + (dt_slot)) -+#define MAX9295_MEM_DT_SEL_DT_FIELD GENMASK(5, 0) -+#define MAX9295_MEM_DT_SEL_EN_FIELD BIT(6) -+ -+// Software BPP override (used for double loading mode and zero padding) -+#define MAX9295_SOFT_BPP(pipe_id) (0x31C + (pipe_id)) -+#define MAX9295_SOFT_BPP_EN_FIELD BIT(5) -+#define MAX9295_SOFT_BPP_FIELD GENMASK(4, 0) -+ -+#define MAX9295_MIPI_RX (0x330) -+#define MAX9295_MIPI_RX_1 (MAX9295_MIPI_RX + 1) -+#define MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id) (GENMASK(1, 0) << (csi_id * 4)) -+ -+// I2C SRC/DST -+#define MAX9295_I2C_SRC(i2c_id, n) ((i2c_id == 0 ? 0x42 : (0x550 + (4 * ((i2c_id) - 1)))) + (2 * (n)) + 0) -+#define MAX9295_I2C_SRC_FIELD GENMASK(7, 1) -+#define MAX9295_I2C_DST(i2c_id, n) ((i2c_id == 0 ? 0x42 : (0x550 + (4 * ((i2c_id) - 1)))) + (2 * (n)) + 1) -+#define MAX9295_I2C_DST_FIELD GENMASK(7, 1) -+ -+// RCLK registers -+#define MAX9295_REG6 (0x6) -+#define MAX9295_RCLK_EN_FIELD BIT(5) -+#define MAX9295_REG3 (0x3) -+#define MAX9295_RCLK_SEL_FIELD GENMASK(1, 0) -+#define MAX9295_REF_VTG0 (0x3F0) -+#define MAX9295_REFGEN_PREDEF_FREQ_FIELD GENMASK(5, 4) -+#define MAX9295_REFGEN_PREDEF_ALT_FIELD BIT(3) -+#define MAX9295_REFGEN_EN_FIELD BIT(0) -+#define MAX9295_REF_VTG1 (0x3F1) -+#define MAX9295_PCLK_GPIO_FIELD GENMASK(5, 1) -+#define MAX9295_PCLK_EN_FIELD BIT(0) -+ -+#endif /* _MAX9295_H_ */ -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -new file mode 100644 -index 000000000000..e3c41ff94e68 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -0,0 +1,969 @@ -+/* -+ * max9296.c - Maxim MAX9296 GMSL2/GMSL1 to CSI-2 Deserializer -+ * -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max9296.h" -+ -+// Params -+int max9296_serial_link_timeout_ms = MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -+module_param(max9296_serial_link_timeout_ms, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max9296_serial_link_timeout_ms, "Timeout for serial link in milliseconds"); -+ -+static const struct regmap_config max9296_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+}; -+ -+// Declarations -+static int max9296_set_phy_mode(struct max9x_common *common, unsigned int phy_mode); -+static int max9296_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map); -+static int max9296_set_phy_dpll_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz); -+static int max9296_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes); -+static int max9296_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, -+ bool enable, unsigned int width); -+static int max9296_configure_csi_dphy(struct max9x_common *common); -+static int max9296_verify_devid(struct max9x_common *common); -+static int max9296_enable(struct max9x_common *common); -+static int max9296_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max9296_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked); -+static int max9296_serial_link_reset(struct max9x_common *common, unsigned int link_id); -+static int max9296_set_serial_link_rate(struct max9x_common *common, unsigned int link_id); -+static int max9296_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe); -+static int max9296_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps); -+static int max9296_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int map_id, struct max9x_serdes_mipi_map *mipi_map); -+static int max9296_set_csi_double_loading_mode(struct max9x_common *common, unsigned int csi_id, unsigned int bpp); -+static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned int link_id); -+static int max9296_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9296_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9296_isolate_serial_link(struct max9x_common *common, unsigned int link); -+static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned int link); -+static int max9296_wait_link_lock(struct max9x_common *common, int link); -+static int max9296_enable_csi_link(struct max9x_common *common, unsigned int csi_link_id); -+static int max9296_disable_csi_link(struct max9x_common *common, unsigned int csi_link_id); -+ -+/* Currently unused */ -+static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_id, unsigned int *phy_lane_map); -+ -+// Functions -+static int max9296_set_phy_mode(struct max9x_common *common, unsigned int phy_mode) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI: phy_mode=%d", phy_mode); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY0, -+ MAX9296_MIPI_PHY0_MODE_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_MIPI_PHY0_MODE_FIELD, phy_mode)); -+} -+ -+static int max9296_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: %s", csi_id, (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY_ENABLE, -+ MAX9296_MIPI_PHY_ENABLE_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9296_MIPI_PHY_ENABLE_FIELD(csi_id), enable ? 1U : 0U)); -+} -+ -+static int max9296_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: phy_lane_map=0x%04x", csi_id, phy_lane_map); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY_LANE_MAP(csi_id), -+ MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi_id, 0) -+ | MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi_id, 1), -+ phy_lane_map); -+} -+ -+static int max9296_set_phy_dpll_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: DPLL %s", csi_id, (enable ? "on" : "off")); -+ -+ return regmap_update_bits(map, MAX9296_DPLL_RESET(csi_id), -+ MAX9296_DPLL_RESET_SOFT_RST_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_DPLL_RESET_SOFT_RST_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max9296_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ if (WARN_ONCE(freq_mhz > 0 && freq_mhz < MAX9296_DPLL_FREQ_MHZ_MULTIPLE, -+ "CSI frequency must be greater than %d MHz", MAX9296_DPLL_FREQ_MHZ_MULTIPLE)) -+ return -EINVAL; -+ -+ dev_dbg(dev, "CSI link %d: freq %u MHz, %u mult", csi_id, freq_mhz, freq_mhz / MAX9296_DPLL_FREQ_MHZ_MULTIPLE); -+ -+ return regmap_update_bits(map, MAX9296_DPLL_FREQ(csi_id), -+ MAX9296_DPLL_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_DPLL_FREQ_FIELD, freq_mhz / MAX9296_DPLL_FREQ_MHZ_MULTIPLE)); -+} -+ -+ -+static int max9296_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: %d lanes", csi_id, num_lanes); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_TX_LANE_CNT(csi_id), -+ MAX9296_MIPI_TX_LANE_CNT_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_LANE_CNT_FIELD, -+ (common->csi_link[csi_id].config.num_lanes - 1))); -+} -+ -+static int max9296_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, -+ bool enable, unsigned int width) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: Initial deskew %s", csi_id, enable ? "enabled" : "disabled"); -+ -+ /* clamp initial deskew width to 7 which is 8*32k UI*/ -+ if (width > 7) -+ width = 7; -+ -+ return regmap_write(map, MAX9296_MIPI_TX_DESKEW_INIT(csi_id), -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_DESKEW_INIT_AUTO_EN, enable) | -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_DESKEW_INIT_WIDTH, width)); -+} -+ -+static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_id, -+ unsigned int *phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ int i; -+ const unsigned int phy_count = 2; -+ const unsigned int controller = csi_id / phy_count; -+ -+ if (common->csi_link[csi_id].config.num_lanes != common->csi_link[csi_id].config.num_maps) { -+ dev_err(dev, "CSI%u number of maps %u must match number of lanes %u.", csi_id, -+ common->csi_link[csi_id].config.num_maps, -+ common->csi_link[csi_id].config.num_lanes); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < common->csi_link[csi_id].config.num_maps; i++) { -+ const struct max9x_serdes_phy_map *map = &common->csi_link[csi_id].config.map[i]; -+ -+ if (map->int_csi >= 4) { -+ dev_err(dev, "CSI%u does not have %u Lanes can not map.", csi_id, -+ map->int_csi + 1); -+ return -EINVAL; -+ } -+ if (map->phy_lane >= 2) { -+ dev_err(dev, "Each PHY has 2 lanes can not map %u.", map->phy_ind); -+ return -EINVAL; -+ } -+ if (map->phy_ind < (controller * phy_count) || -+ map->phy_ind >= ((controller + 1) * phy_count)) { -+ dev_err(dev, "CSI%u does not have PHYs %u can not map.", csi_id, -+ map->phy_ind); -+ return -EINVAL; -+ } -+ phy_lane_map[map->phy_ind] |= MAX9X_FIELD_PREP( -+ MAX9296_MIPI_PHY_LANE_MAP_FIELD(map->phy_ind, map->phy_lane), map->int_csi); -+ } -+ return 0; -+} -+ -+static int max9296_configure_csi_dphy(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phy_mode; -+ unsigned int phy_lane_map[MAX9296_NUM_CSI_LINKS] = {0}; -+ unsigned int csi_id; -+ unsigned int PHY1, PHY2; -+ int ret; -+ -+ for (csi_id = 0; csi_id < MAX9296_NUM_CSI_LINKS; csi_id++) { -+ dev_dbg(dev, "CSI link %d: enabled=%d, num_lanes=%d, freq_mhz=%d init_deskew=%d", -+ csi_id, common->csi_link[csi_id].enabled, -+ common->csi_link[csi_id].config.num_lanes, -+ common->csi_link[csi_id].config.freq_mhz, -+ common->csi_link[csi_id].config.auto_init_deskew_enabled); -+ } -+ -+ PHY1 = common->csi_link[0].enabled ? common->csi_link[0].config.num_lanes : 0; -+ PHY2 = common->csi_link[1].enabled ? common->csi_link[1].config.num_lanes : 0; -+ /* Each csi controller has 4 lanes. each phy has 2 lanes*/ -+ if ((PHY1 + PHY2) > 4) { -+ dev_err(dev, "CSI controller 1 has more than %u lanes. PHY0: %u, PHY1: %u", 4, PHY1, -+ PHY1); -+ return -EINVAL; -+ } -+ -+ PHY1 = common->csi_link[2].enabled ? common->csi_link[2].config.num_lanes : 0; -+ PHY2 = common->csi_link[3].enabled ? common->csi_link[3].config.num_lanes : 0; -+ /* Each csi controller has 4 lanes. each phy has 2 lanes */ -+ if ((PHY1 + PHY2) > 4) { -+ dev_err(dev, "CSI controller 2 has more than %u lanes. PHY2: %u, PHY3: %u", 4, PHY1, -+ PHY1); -+ return -EINVAL; -+ } -+ -+ if (common->csi_link[1].enabled && common->csi_link[1].config.num_lanes == 4) { -+ if (common->csi_link[0].enabled) -+ dev_warn(dev, "CSI link 0 enabled, but CSI link 1 is 4 lanes."); -+ -+ /* lane 1 is master for CSI controller 1*/ -+ max9296_conf_phy_maps(common, 1, phy_lane_map); -+ if (common->csi_link[2].enabled && common->csi_link[2].config.num_lanes == 4) { -+ if (common->csi_link[3].enabled) -+ dev_warn(dev, "CSI link 3 enabled, but CSI link 2 is 4 lanes."); -+ dev_dbg(dev, "CSI phy mode is set to 2X4Lanes"); -+ /* lane 2 is master for CSI controller 2*/ -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_2X4; // 2x 4lane -+ } else { -+ dev_dbg(dev, "CSI phy mode is set to A: 4Lanes B: 2X2Lanes"); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ max9296_conf_phy_maps(common, 3, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_1X4A_22; // A: 1x 4lane B: 2x 2lane -+ } -+ } else { -+ max9296_conf_phy_maps(common, 1, phy_lane_map); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ if (common->csi_link[2].enabled && common->csi_link[2].config.num_lanes == 4) { -+ if (common->csi_link[3].enabled) -+ dev_warn(dev, "CSI link 3 enabled, but CSI link 2 is 4 lanes."); -+ dev_dbg(dev, "CSI phy mode is set to A: 2X2Lanes B: 4Lanes"); -+ /* lane 2 is master for CSI controller 2*/ -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_1X4B_22; // B: 1x 4lane A: 2x 2lane -+ } else { -+ dev_dbg(dev, "CSI phy mode is set to 4X2Lanes"); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ max9296_conf_phy_maps(common, 3, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_4X2; // 4x 2lane -+ } -+ } -+ -+ TRY(ret, max9296_set_phy_mode(common, phy_mode)); -+ -+ for (csi_id = 0; csi_id < MAX9296_NUM_CSI_LINKS; csi_id++) { -+ struct max9x_serdes_csi_config *config = -+ &common->csi_link[csi_id].config; -+ -+ TRY(ret, max9296_set_phy_enabled(common, csi_id, false)); -+ -+ TRY(ret, max9296_set_phy_dpll_enabled(common, csi_id, false)); -+ -+ TRY(ret, max9296_set_phy_lane_map(common, csi_id, -+ phy_lane_map[csi_id]) -+ ); -+ -+ TRY(ret, max9296_set_mipi_lane_cnt(common, csi_id, -+ config->num_lanes) -+ ); -+ } -+ -+ return 0; -+} -+ -+static int max9296_verify_devid(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int dev_id, dev_rev; -+ int ret; -+ -+ // Fetch and output chip name + revision -+ TRY(ret, regmap_read(map, MAX9296_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read(map, MAX9296_DEV_REV, &dev_rev)); -+ -+ dev_rev = FIELD_GET(MAX9296_DEV_REV_FIELD, dev_rev); -+ switch (dev_id) { -+ case MAX9296A: -+ dev_info(dev, "Detected MAX9296A revision 0x%x", dev_rev); -+ break; -+ default: -+ dev_warn(dev, "Unknown chip ID 0x%x revision 0x%x", -+ dev_id, dev_rev); -+ break; -+ } -+ -+ return 0; -+} -+ -+static int max9296_set_all_reset(struct max9x_common *common, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Reset ALL %s", (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_CTRL0, -+ MAX9296_CTRL0_RESET_ALL_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_CTRL0_RESET_ALL_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max9296_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int link_id; -+ int ret; -+ -+ dev_dbg(dev, "Enable"); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ ret = max9296_disable_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ } -+ -+ ret = max9296_verify_devid(common); -+ if (ret) -+ return ret; -+ -+ ret = max9296_configure_csi_dphy(common); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int max9296_soft_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ dev_dbg(dev, "Soft reset"); -+ -+ TRY(ret, max9296_set_all_reset(common, 1)); -+ /* lock time for reset_all / PWDNB in spec, I2C Wake time is 2.25ms */ -+ usleep_range(45000, 45050); -+ TRY(ret, max9296_set_all_reset(common, 0)); -+ -+ return 0; -+} -+ -+static int max9296_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX9296_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX9296_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX9296_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX9296_NUM_CSI_LINKS; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static struct max9x_common_ops max9296_common_ops = { -+ .enable = max9296_enable, -+ .soft_reset = max9296_soft_reset, -+ .max_elements = max9296_max_elements, -+ .verify_devid = max9296_verify_devid, -+}; -+ -+static int max9296_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ /* -+ * link_id ignored for max9296, pipe routing done through stream-ids, -+ * which must be unique across links -+ */ -+ dev_dbg(dev, "Video-pipe %d: src_pipe=%u", pipe_id, src_pipe); -+ -+ return regmap_update_bits(map, MAX9296_VIDEO_PIPE_SEL(pipe_id), -+ MAX9296_VIDEO_PIPE_STR_SEL_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_VIDEO_PIPE_STR_SEL_FIELD, src_pipe)); -+} -+ -+static int max9296_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val = 0; -+ int ret; -+ -+ if (num_maps > 0) -+ val = GENMASK(num_maps - 1, 0); -+ -+ dev_dbg(dev, "Video-pipe %d: num_maps=%u", pipe_id, num_maps); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_EN_L(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_EN_FIELD, val)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_EN_H(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_EN_FIELD, val >> 8)) -+ ); -+ -+ return 0; -+} -+ -+static int max9296_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int map_id, struct max9x_serdes_mipi_map *mipi_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, map %d: VC%d:DT%02x->VC%d:DT%02x, dst_csi=%d ", -+ pipe_id, map_id, mipi_map->src_vc, mipi_map->src_dt, -+ mipi_map->dst_vc, mipi_map->dst_dt, mipi_map->dst_csi); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_SRC_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRC_L_VC_FIELD, mipi_map->src_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRC_L_DT_FIELD, mipi_map->src_dt)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_DST_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_DST_L_VC_FIELD, mipi_map->dst_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_DST_L_DT_FIELD, mipi_map->dst_dt)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_SRCDST_H(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRCDST_H_SRC_VC_FIELD, mipi_map->src_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRCDST_H_DST_VC_FIELD, mipi_map->dst_vc)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9296_MAP_DPHY_DEST(pipe_id, map_id), -+ MAX9296_MAP_DPHY_DEST_FIELD(map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_DPHY_DEST_FIELD(map_id), mipi_map->dst_csi)) -+ ); -+ -+ return 0; -+} -+ -+/** -+ * max9296_set_csi_double_loading_mode() - Configure Double Loading Mode on a CSI controller -+ * @common: max9x_common -+ * @csi_id: Target CSI controller's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.4.2.3 "Double Mode (Deserializer)" -+ */ -+static int max9296_set_csi_double_loading_mode(struct max9x_common *common, unsigned int csi_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int value; -+ -+ switch (bpp) { -+ case 0: -+ value = 0; -+ break; -+ case 8: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_8BPP, 1U); -+ // To fully support 8bpp, additional register writes are -+ // needed for 'bpp8dbl' and 'bpp8dbl_mode' fields on each pipe. -+ dev_err(dev, "8 BPP currently unsupported for pixel doubling"); -+ return -EINVAL; -+ case 10: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_10BPP, 1U); -+ break; -+ case 12: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_12BPP, 1U); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ if (bpp > 0) -+ dev_info(dev, "Configuring double loading mode on CSI %d: %u bpp -> %u bpp", -+ csi_id, bpp, (bpp * 2)); -+ -+ // Enable alt mem mapping -+ return regmap_update_bits(map, MAX9296_MIPI_TX_ALT_MEM(csi_id), -+ MAX9296_MIPI_TX_ALT_MEM_FIELD, value); -+} -+ -+static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct max9x_serdes_csi_link *csi_link; -+ int ret; -+ -+ if (csi_id > common->num_csi_links) -+ return -EINVAL; -+ -+ csi_link = &common->csi_link[csi_id]; -+ -+ if (WARN_ONCE(enable && csi_link->enabled == false, "Tried to enable a disabled CSI port???")) -+ return -EINVAL; -+ -+ if (WARN_ONCE(enable && csi_link->config.num_lanes == 0, "Tried to enable CSI port with no lanes???")) -+ return -EINVAL; -+ -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+ dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, (enable ? "enable" : "disable"), csi_link->usecount); -+ -+ if (enable && csi_link->usecount == 1) { -+ // Enable && first user -+ ret = max9296_set_initial_deskew(common, csi_id, csi_link->config.auto_init_deskew_enabled, -+ csi_link->config.initial_deskew_width); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_freq(common, csi_id, csi_link->config.freq_mhz); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ } else if (!enable && csi_link->usecount == 0) { -+ // Disable && no more users -+ ret = max9296_set_phy_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ } -+ -+ return 0; -+} -+ -+static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Video-pipe %d: %s", pipe_id, (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_VIDEO_PIPE_EN(pipe_id), -+ MAX9296_VIDEO_PIPE_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_VIDEO_PIPE_EN_FIELD(pipe_id), enable ? 1U : 0U)); -+} -+ -+/***** max9296_serial_link_ops auxiliary functions *****/ -+static int max9296_set_serial_link_rate(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_serial_config *config = &common->serial_link[link_id].config; -+ unsigned int tx_rate, rx_rate; -+ -+ tx_rate = max9x_serdes_mhz_to_rate(max9296_tx_rates, ARRAY_SIZE(max9296_tx_rates), config->tx_freq_mhz); -+ if (tx_rate < 0) -+ return tx_rate; -+ -+ rx_rate = max9x_serdes_mhz_to_rate(max9296_rx_rates, ARRAY_SIZE(max9296_rx_rates), config->rx_freq_mhz); -+ if (rx_rate < 0) -+ return rx_rate; -+ -+ dev_dbg(dev, "Serial-link %d: TX=%d MHz RX=%d MHz", link_id, config->tx_freq_mhz, config->rx_freq_mhz); -+ -+ return regmap_update_bits(map, MAX9296_PHY_REM_CTRL, -+ MAX9296_PHY_REM_CTRL_TX_FIELD | MAX9296_PHY_REM_CTRL_RX_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_PHY_REM_CTRL_TX_FIELD, tx_rate) | -+ MAX9X_FIELD_PREP(MAX9296_PHY_REM_CTRL_RX_FIELD, rx_rate)); -+} -+ -+static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max9296_set_video_pipe_src(common, pipe_id, config->src_link, config->src_pipe); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_video_pipe_maps_enabled(common, pipe_id, config->num_maps); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max9296_set_video_pipe_map(common, pipe_id, map_id, &config->map[map_id]); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_csi_double_loading_mode(common, -+ config->map[map_id].dst_csi, config->dbl_pixel_bpp); -+ if (ret) -+ return ret; -+ -+ if (common->csi_link[config->map[map_id].dst_csi].config.auto_start) { -+ ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, true); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ ret = max9296_set_video_pipe_enabled(common, pipe_id, true); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max9296_serial_link_reset(struct max9x_common *common, unsigned int link_id) -+{ -+ struct regmap *map = common->map; -+ -+ /* GMSL RX rate must be the same as the SER. This is set in REG1(0x1)[1:0] */ -+ return regmap_update_bits(map, MAX9296_CTRL0, MAX9296_CTRL0_RESET_ONESHOT_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_CTRL0_RESET_ONESHOT_FIELD, 1U)); -+} -+ -+static int max9296_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked) -+{ -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ /* Only looks at link A refer to header file */ -+ ret = regmap_read(map, MAX9296_PHY_LOCKED(link_id), &val); -+ if (ret) -+ return ret; -+ -+ if (FIELD_GET(MAX9296_PHY_LOCKED_FIELD, val) != 0) -+ *locked = true; -+ else -+ *locked = false; -+ -+ return 0; -+} -+ -+static int max9296_wait_link_lock(struct max9x_common *common, int link) -+{ -+ bool locked; -+ int ret; -+ ulong timeout = jiffies + msecs_to_jiffies(max9296_serial_link_timeout_ms); -+ -+ do { -+ ret = max9296_get_serial_link_lock(common, link, &locked); -+ if (ret == 0 && locked) -+ return 0; -+ -+ usleep_range(1000, 2000); -+ } while (time_is_after_jiffies(timeout)); -+ -+ return -ETIMEDOUT; -+} -+/***** max9296_serial_link_ops auxiliary functions *****/ -+ -+/***** max9296_serial_link_ops *****/ -+static int max9296_isolate_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int link_cfg; -+ unsigned int auto_link; -+ int ret; -+ -+ dev_dbg(dev, "Isolate link %d", link); -+ -+ auto_link = 0; -+ link_cfg = (link == 0) ? MAX9296_LINK_A : MAX9296_LINK_B; -+ -+ TRY_DEV_HERE(ret, regmap_update_bits(map, MAX9296_CTRL0, -+ MAX9296_CTRL0_AUTO_CFG_FIELD | MAX9296_CTRL0_LINK_CFG_FIELD, -+ FIELD_PREP(MAX9296_CTRL0_AUTO_CFG_FIELD, auto_link) -+ | FIELD_PREP(MAX9296_CTRL0_LINK_CFG_FIELD, link_cfg)), -+ dev); -+ -+ TRY_DEV_HERE(ret, max9296_serial_link_reset(common, link), dev); -+ -+ TRY_DEV_HERE(ret, max9296_wait_link_lock(common, link), dev); -+ -+ return 0; -+} -+ -+static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int link_cfg; -+ unsigned int auto_link = 0; -+ int ret; -+ bool link_a = common->serial_link[0].detected; -+ bool link_b = common->serial_link[1].detected; -+ -+ if (link_a && link_b) -+ link_cfg = MAX9296_LINK_SPLIT; -+ else if (link_a) -+ link_cfg = MAX9296_LINK_A; -+ else if (link_b) -+ link_cfg = MAX9296_LINK_B; -+ -+ dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -+ -+ TRY_DEV_HERE(ret, regmap_update_bits( -+ map, -+ MAX9296_CTRL0, -+ MAX9296_CTRL0_AUTO_CFG_FIELD -+ |MAX9296_CTRL0_LINK_CFG_FIELD, -+ FIELD_PREP(MAX9296_CTRL0_AUTO_CFG_FIELD, auto_link) -+ |FIELD_PREP(MAX9296_CTRL0_LINK_CFG_FIELD, link_cfg)), -+ dev); -+ -+ TRY_DEV_HERE(ret, max9296_serial_link_reset(common, link), dev); -+ -+ TRY_DEV_HERE(ret, max9296_wait_link_lock(common, link), dev); -+ -+ return 0; -+} -+ -+static int max9296_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ -+ if (WARN_ON_ONCE(link_id >= common->num_serial_links)) -+ return -EINVAL; -+ -+ if (WARN_ONCE(common->serial_link[link_id].config.link_type -+ != MAX9X_LINK_TYPE_GMSL2, "Only GMSL2 is supported!")) -+ return -EINVAL; -+ -+ // GMSL2 -+ ret = max9296_set_serial_link_rate(common, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max9296_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ common->serial_link[link_id].detected = true; -+ -+ ret = max9296_set_serial_link_routing(common, link_id); -+ if (ret) -+ return ret; -+ -+ max9296_deisolate_serial_link(common, link_id); -+ -+ return 0; -+} -+ -+static int max9296_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max9296_set_video_pipe_enabled(common, pipe_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_video_pipe_maps_enabled(common, pipe_id, 0); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static struct max9x_serial_link_ops max9296_serial_link_ops = { -+ .enable = max9296_enable_serial_link, -+ .disable = max9296_disable_serial_link, -+ .isolate = max9296_isolate_serial_link, -+ .deisolate = max9296_deisolate_serial_link, -+}; -+/***** max9296_serial_link_ops *****/ -+ -+static int max9296_enable_csi_link(struct max9x_common *common, unsigned int csi_link_id) -+{ -+ return max9296_set_csi_link_enabled(common, csi_link_id, true); -+} -+ -+static int max9296_disable_csi_link(struct max9x_common *common, unsigned int csi_link_id) -+{ -+ return max9296_set_csi_link_enabled(common, csi_link_id, false); -+} -+ -+static struct max9x_csi_link_ops max9296_csi_link_ops = { -+ .enable = max9296_enable_csi_link, -+ .disable = max9296_disable_csi_link, -+}; -+ -+static int max9296_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ return max9x_common_resume(common); -+} -+ -+static int max9296_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ return max9x_common_suspend(common); -+} -+ -+static int max9296_freeze(struct device *dev) -+{ -+ return max9296_suspend(dev); -+} -+ -+static int max9296_restore(struct device *dev) -+{ -+ return max9296_resume(dev); -+} -+ -+static int max9296_probe(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *des = NULL; -+ int ret = 0; -+ -+ dev_dbg(dev, "Probing"); -+ -+ des = devm_kzalloc(dev, sizeof(*des), GFP_KERNEL); -+ if (!des) { -+ dev_err(dev, "Failed to allocate memory."); -+ return -ENOMEM; -+ } -+ -+ des->type = MAX9X_DESERIALIZER; -+ -+ ret = max9x_common_init_i2c_client(des, client, &max9296_regmap_config, -+ &max9296_common_ops, -+ &max9296_serial_link_ops, -+ &max9296_csi_link_ops, -+ NULL); -+ if (ret) -+ return ret; -+ -+ dev_info(dev, "probe successful"); -+ return 0; -+} -+ -+static void max9296_remove(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *des = NULL; -+ -+ dev_dbg(dev, "%s Removing", client->name); -+ -+ des = max9x_client_to_common(client); -+ max9x_destroy(des); -+} -+ -+static const struct dev_pm_ops max9296_pm_ops = { -+ .suspend = max9296_suspend, -+ .resume = max9296_resume, -+ .freeze = max9296_freeze, -+ .restore = max9296_restore, -+}; -+ -+static struct i2c_device_id max9296_idtable[] = { -+ {"max9296", 0}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(i2c, max9296_idtable); -+ -+static struct i2c_driver max9296_driver = { -+ .driver = { -+ .name = "max9296", -+ .owner = THIS_MODULE, -+ .pm = &max9296_pm_ops, -+ }, -+ .probe = max9296_probe, -+ .remove = max9296_remove, -+ .id_table = max9296_idtable, -+}; -+ -+module_i2c_driver(max9296_driver); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Jacob Kiggins "); -+MODULE_AUTHOR("Yan Dongcheng "); -+MODULE_DESCRIPTION("Maxim MAX9296 Dual GMSL2/GMSL1 to CSI-2 Deserializer driver"); -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -new file mode 100644 -index 000000000000..ee602d35a983 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -0,0 +1,148 @@ -+/* -+ * max9296.h - Maxim 9296 registers and constants. -+ * -+ * Copyright (c) 2023-2024 Define Design Deploy Corp. All Rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9296_H_ -+#define _MAX9296_H_ -+ -+#include -+#include "serdes.h" -+ -+enum max9296_dev_id { -+ MAX9296A = 0x94 -+}; -+ -+enum max9296_phy_mode { -+ MAX9296_MIPI_PHY_4X2 = BIT(0), // four 2-lane ports -+ MAX9296_MIPI_PHY_1X4 = BIT(1), // one 4-lane (CSI2 is master (TODO: Which port???)) -+ MAX9296_MIPI_PHY_2X4 = BIT(2), // two 4-lane ports (CSI1 is master for port A, CSI2 for port B) -+ MAX9296_MIPI_PHY_1X4A_22 = BIT(3), // one 4-lane (PHY0+PHY1, CSI1 for port A) port and two 2-lane ports -+ MAX9296_MIPI_PHY_1X4B_22 = BIT(4), // one 4-lane (PHY2+PHY3, CSI2 for port B) port and two 2-lane ports -+}; -+ -+enum max9296_link_mode { -+ MAX9296_LINK_AB, -+ MAX9296_LINK_A, -+ MAX9296_LINK_B, -+ MAX9296_LINK_SPLIT -+}; -+ -+#define MAX9296_NUM_SERIAL_LINKS 2 -+#define MAX9296_NUM_VIDEO_PIPES 4 -+#define MAX9296_NUM_MIPI_MAPS 16 -+#define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ -+/* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ -+ -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 -+ -+#define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 -+ -+#define MAX9296_FLD_OFS(n, bits_per_field, count) (((n) % (count)) * (bits_per_field)) -+#define MAX9296_OFFSET_GENMASK(offset, h, l) GENMASK(offset + h, offset + l) -+ -+#define MAX9296_CTRL0 0x10 -+#define MAX9296_CTRL0_AUTO_CFG_FIELD BIT(4) -+#define MAX9296_CTRL0_LINK_CFG_FIELD GENMASK(1, 0) -+#define MAX9296_CTRL0_RESET_LINK_FIELD BIT(6) // One bit to reset whole link -+#define MAX9296_CTRL0_RESET_ALL_FIELD BIT(7) -+#define MAX9296_CTRL0_RESET_ONESHOT_FIELD BIT(5) -+ -+#define MAX9296_PHY_REM_CTRL (0x1) -+#define MAX9296_PHY_REM_CTRL_TX_FIELD (GENMASK(1, 0) << 2) -+#define MAX9296_PHY_REM_CTRL_RX_FIELD GENMASK(1, 0) -+#define MAX9296_PHY_REM_CTRL_DIS_FIELD BIT(4) -+ -+/* -+ *CTRL3(0x13) LINK_MODE is set to link A. -+ */ -+#define MAX9296_PHY_LOCKED(link) (0x13) /* Based on link mode */ -+#define MAX9296_PHY_LOCKED_FIELD BIT(3) -+ -+#define MAX9296_DEV_ID 0xD -+#define MAX9296_DEV_REV 0xE -+#define MAX9296_DEV_REV_FIELD GENMASK(3, 0) -+ -+#define MAX9296_VIDEO_PIPE_SEL(pipe) (0x50 + pipe) -+#define MAX9296_VIDEO_PIPE_STR_SEL_FIELD GENMASK(1, 0) -+ -+#define MAX9296_VIDEO_PIPE_EN(pipe) (0x2) -+#define MAX9296_VIDEO_PIPE_EN_FIELD(pipe) (BIT(pipe) << 4) -+ -+#define MAX9296_DPLL_FREQ(phy) (0x31D + ((phy) * 3)) -+#define MAX9296_DPLL_FREQ_FIELD GENMASK(4, 0) -+ -+#define MAX9296_MIPI_TX_EXT(pipe) (0x500 + ((pipe) * 0x10)) -+ -+#define MAX9296_MIPI_PHY0 0x330 -+#define MAX9296_MIPI_PHY0_MODE_FIELD GENMASK(4, 0) -+#define MAX9296_MIPI_PHY_ENABLE 0x332 -+#define MAX9296_MIPI_PHY_ENABLE_FIELD(csi) BIT((csi) + 4) -+#define MAX9296_MIPI_PHY_LANE_MAP(csi) (0x333 + (csi) / 2) -+#define MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi, lane) \ -+ (GENMASK(1, 0) << (MAX9296_FLD_OFS(csi, 4, 2) + MAX9296_FLD_OFS(lane, 2, 2))) -+ -+// Note that CSIs and pipes overlap: -+#define MAX9296_MIPI_TX(pipe) (0x400 + ((pipe) * 0x40)) -+ -+/* Offsets for MAX9296_MIPI_TX under 0x0B are only for lanes 1 and 3 */ -+#define MAX9296_MIPI_TX_LANE_CNT(csi) (MAX9296_MIPI_TX(csi) + 0x0A) -+#define MAX9296_MIPI_TX_LANE_CNT_FIELD GENMASK(7, 6) -+#define MAX9296_MIPI_TX_DESKEW_INIT(csi) (MAX9296_MIPI_TX(csi) + 0x03) -+#define MAX9296_MIPI_TX_DESKEW_INIT_AUTO_EN BIT(7) -+#define MAX9296_MIPI_TX_DESKEW_INIT_WIDTH GENMASK(2, 0) -+#define MAX9296_MAP_EN_L(pipe) (MAX9296_MIPI_TX(pipe) + 0x0B) -+#define MAX9296_MAP_EN_H(pipe) (MAX9296_MIPI_TX(pipe) + 0x0C) -+#define MAX9296_MAP_EN_FIELD GENMASK(7, 0) -+#define MAX9296_MAP_SRC_L(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x0D + ((map) * 2)) -+#define MAX9296_MAP_SRC_L_VC_FIELD GENMASK(7, 6) -+#define MAX9296_MAP_SRC_L_DT_FIELD GENMASK(5, 0) -+#define MAX9296_MAP_DST_L(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x0D + ((map) * 2) + 1) -+#define MAX9296_MAP_DST_L_VC_FIELD GENMASK(7, 6) -+#define MAX9296_MAP_DST_L_DT_FIELD GENMASK(5, 0) -+#define MAX9296_MAP_SRCDST_H(pipe, map) (MAX9296_MIPI_TX_EXT(pipe) + (map)) -+#define MAX9296_MAP_SRCDST_H_SRC_VC_FIELD GENMASK(7, 5) -+#define MAX9296_MAP_SRCDST_H_DST_VC_FIELD GENMASK(4, 2) -+#define MAX9296_MAP_DPHY_DEST(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x2D + ((map) / 4)) -+#define MAX9296_MAP_DPHY_DEST_FIELD(map) (GENMASK(1, 0) << MAX9296_FLD_OFS(map, 2, 4)) -+ -+#define MAX9296_MIPI_TX_ALT_MEM(csi) (MAX9296_MIPI_TX(csi) + 0x33) -+#define MAX9296_MIPI_TX_ALT_MEM_FIELD GENMASK(2, 0) -+#define MAX9296_MIPI_TX_ALT_MEM_8BPP BIT(1) -+#define MAX9296_MIPI_TX_ALT_MEM_10BPP BIT(2) -+#define MAX9296_MIPI_TX_ALT_MEM_12BPP BIT(0) -+ -+#define MAX9296_GPIO_A(gpio_num) (0x2B0 + 3 * gpio_num) -+#define MAX9296_GPIO_B(gpio_num) (MAX9296_GPIO_A(gpio_num) + 1) -+#define MAX9296_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX9296_GPIO_C(gpio_num) (MAX9296_GPIO_A(gpio_num) + 2) -+#define MAX9296_GPIO_C_RX_ID GENMASK(4, 0) -+ -+#define MAX9296_DPLL_RESET(phy) (0x1C00 + ((phy) * 0x100)) -+#define MAX9296_DPLL_RESET_SOFT_RST_FIELD BIT(0) -+ -+static struct max9x_serdes_rate_table max9296_rx_rates[] = { -+ { .val = 1, .freq_mhz = 3000}, // 3 GHz -+ { .val = 2, .freq_mhz = 6000}, // 6 GHz -+}; -+ -+static struct max9x_serdes_rate_table max9296_tx_rates[] = { -+ { .val = 0, .freq_mhz = 187}, // 187.5 MHz -+}; -+ -+#endif /* _MAX9296_H_ */ -diff --git a/drivers/media/i2c/max9x/max9x_pdata.h b/drivers/media/i2c/max9x/max9x_pdata.h -new file mode 100644 -index 000000000000..b048c693e77e ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9x_pdata.h -@@ -0,0 +1,102 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9X_PDATA_H_ -+#define _MAX9X_PDATA_H_ -+ -+#include -+#include -+ -+struct max9x_common; -+ -+enum max9x_serdes_link_type { -+ MAX9X_LINK_TYPE_GMSL1 = 0, -+ MAX9X_LINK_TYPE_GMSL2, -+ MAX9X_LINK_TYPE_FPDLINK, -+}; -+ -+struct max9x_subdev_pdata { -+ unsigned int serial_link_id; // DES -+ struct i2c_board_info board_info; -+ unsigned short phys_addr; // Remap or translate subdev -+}; -+ -+struct max9x_serial_link_pdata { -+ unsigned int link_id; -+ enum max9x_serdes_link_type link_type; -+ unsigned int rx_freq_mhz; -+ unsigned int tx_freq_mhz; -+ char poc_regulator[32]; -+ -+ // SER -+ struct i2c_client *des_client; -+ unsigned int des_link_id; -+}; -+ -+struct max9x_serdes_mipi_map { -+ u16 src_vc; -+ u16 src_dt; -+ u16 dst_vc; -+ u16 dst_dt; -+ u16 dst_csi; -+}; -+ -+struct max9x_video_pipe_pdata { -+ unsigned int serial_link_id; // DES: src-link, SER: dst-link -+ unsigned int pipe_id; // X, Y, Z, U -+ -+ // DES -+ struct max9x_serdes_mipi_map *maps; -+ unsigned int num_maps; -+ unsigned int src_pipe_id; -+ -+ // SER -+ unsigned int src_csi_id; -+ unsigned int *data_types; -+ unsigned int num_data_types; -+}; -+ -+struct max9x_serdes_phy_map { -+ u8 int_csi; // Internal CSI lane -+ u8 phy_ind; // PHY index -+ u8 phy_lane; // PHY lane index -+}; -+ -+struct max9x_csi_link_pdata { -+ unsigned int link_id; -+ unsigned int num_lanes; -+ struct max9x_serdes_phy_map *maps; -+ unsigned int num_maps; -+ unsigned int tx_rate_mbps; -+ bool auto_initial_deskew; -+ unsigned int initial_deskew_width; -+ bool auto_start; -+}; -+ -+struct max9x_gpio_pdata { -+ const char *label; -+ const char *const *names; -+}; -+ -+struct max9x_pdata { -+ unsigned short phys_addr; // Remap self -+ char suffix[5]; -+ -+ struct max9x_subdev_pdata *subdevs; // DES: the serializers, 1 per link; SER: sensor, eeprom, etc -+ unsigned int num_subdevs; -+ -+ struct max9x_serial_link_pdata *serial_links; // DES only. SER is currently presumed to have only 1 active link -+ unsigned int num_serial_links; -+ -+ struct max9x_video_pipe_pdata *video_pipes; -+ unsigned int num_video_pipes; -+ -+ struct max9x_csi_link_pdata *csi_links; -+ unsigned int num_csi_links; -+ -+ struct max9x_gpio_pdata gpio; -+ -+ bool external_refclk_enable; -+}; -+ -+#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -new file mode 100644 -index 000000000000..f29949d96aa7 ---- /dev/null -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -0,0 +1,2466 @@ -+/* -+ * serdes.c -+ * -+ * Copyright (c) 2018-2020 D3 Engineering. All rights reserved. -+ * Copyright (c) 2023, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2025 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "serdes.h" -+ -+static const s64 max9x_op_sys_clock[] = { -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2000), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1900), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1800), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1700), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1600), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1000), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(900), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(800), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(700), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(600), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(80), -+}; -+ -+typedef int (*max9x_serdes_parse_child_func)(struct max9x_common *common, struct device_node *node); -+ -+static int max9x_soft_reset(struct max9x_common *common); -+static int max9x_remap_addr(struct max9x_common *common); -+static int max9x_setup_gpio(struct max9x_common *common); -+static int max9x_enable(struct max9x_common *common); -+static int max9x_disable(struct max9x_common *common); -+//static int max9x_verify_devid(struct max9x_common *common); -+ -+static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned int link_id); -+static int max9x_create_adapters_resume(struct max9x_common *common); -+ -+static int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id); -+static int max9x_create_adapters(struct max9x_common *common); -+static int max9x_csi_link_to_pad(struct max9x_common *common, int csi_id); -+static int max9x_serial_link_to_pad(struct max9x_common *common, int link_id); -+static int max9x_register_v4l_subdev(struct max9x_common *common); -+static int max9x_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_sysfs_create_get_link(struct max9x_common *common, unsigned int link_id); -+static void max9x_sysfs_destroy_get_link(struct max9x_common *common, unsigned int link_id); -+ -+static int max9x_enable_line_faults(struct max9x_common *common); -+static int max9x_disable_line_faults(struct max9x_common *common); -+static void max9x_sysfs_destroy_line_fault_status(struct max9x_common *common, unsigned int line); -+ -+static int max9x_parse_pdata(struct max9x_common *common, struct max9x_pdata *pdata); -+static int max9x_parse_serial_link_pdata(struct max9x_common *common, -+ struct max9x_serial_link_pdata *max9x_serial_link_pdata); -+static int max9x_parse_video_pipe_pdata(struct max9x_common *common, -+ struct max9x_video_pipe_pdata *video_pipe_pdata); -+static int max9x_parse_csi_link_pdata(struct max9x_common *common, struct max9x_csi_link_pdata *csi_link_pdata); -+static int max9x_parse_subdev_pdata(struct max9x_common *common, struct max9x_subdev_pdata *subdev_pdata); -+ -+static int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id); -+static int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id); -+ -+static int max9x_des_isolate_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_des_deisolate_serial_link(struct max9x_common *common, unsigned int link_id); -+ -+static ssize_t max9x_link_status_show(struct device *dev, struct device_attribute *attr, char *buf); -+ -+static int max9x_setup_translations(struct max9x_common *common); -+static int max9x_disable_translations(struct max9x_common *common); -+ -+static int max9x_s_stream(struct v4l2_subdev *sd, int enable); -+ -+#define MAX9X_ALLOCATE_ELEMENTS(common, element_type, elements, num_elements) ({ \ -+ struct device *dev = common->dev; \ -+ int allocate_ret = 0; \ -+ (common)->num_elements = 0; \ -+ (common)->elements = NULL; \ -+ if ((common)->common_ops && (common)->common_ops->max_elements) { \ -+ (common)->num_elements = (common)->common_ops->max_elements((common), element_type); \ -+ if ((common)->num_elements > 0) { \ -+ (common)->elements = devm_kzalloc((common)->dev, \ -+ (common)->num_elements * sizeof(typeof(*((common)->elements))), GFP_KERNEL); \ -+ if (!(common)->elements) { \ -+ dev_err(dev, "Failed to allocated memory for " # element_type); \ -+ allocate_ret = -ENOMEM; \ -+ } \ -+ } \ -+ } \ -+ allocate_ret; \ -+}) -+ -+static struct max9x_pdata *pdata_ser(struct device *dev, struct max9x_subdev_pdata *sdinfo, const char *name, -+ unsigned int phys_addr, unsigned int virt_addr) -+{ -+ struct max9x_pdata *pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -+ -+ dev_dbg(dev, "ser %s phys %02x virt %02x\n", name, phys_addr, virt_addr); -+ -+ sdinfo->board_info.platform_data = pdata; -+ strscpy(sdinfo->board_info.type, name, I2C_NAME_SIZE); -+ sdinfo->board_info.addr = virt_addr; -+ sdinfo->phys_addr = pdata->phys_addr = phys_addr; -+ -+ return pdata; -+} -+ -+static struct max9x_pdata *pdata_sensor(struct device *dev, struct max9x_subdev_pdata *sdinfo, const char *name, -+ unsigned int phys_addr, unsigned int virt_addr) -+{ -+ dev_dbg(dev, "sen %s phys %02x virt %02x\n", name, phys_addr, virt_addr); -+ -+ strscpy(sdinfo->board_info.type, name, I2C_NAME_SIZE); -+ sdinfo->board_info.addr = virt_addr; -+ sdinfo->phys_addr = phys_addr; -+ -+ return NULL; -+} -+ -+static struct max9x_pdata *PCA_00C003084(struct device *dev, char *suffix, -+ unsigned int virt_addr, struct max9x_subdev_pdata *ser_sdinfo) -+{ -+ struct max9x_pdata *ser_pdata = pdata_ser(dev, ser_sdinfo, "max9295", 0x40, virt_addr); -+ struct max9x_serial_link_pdata *ser_serial_link; -+ struct max9x_video_pipe_pdata *ser_video_pipe; -+ -+ //NOTE: i2c_dev_set_name() will prepend "i2c-" to this name -+ char *dev_name = devm_kzalloc(dev, I2C_NAME_SIZE, GFP_KERNEL); -+ -+ snprintf(dev_name, I2C_NAME_SIZE, "max9295 %s", suffix); -+ ser_sdinfo->board_info.dev_name = dev_name; -+ -+ snprintf(ser_pdata->suffix, sizeof(ser_pdata->suffix), "%s", suffix); -+ -+ ser_pdata->num_serial_links = 1; -+ ser_pdata->serial_links = devm_kzalloc(dev, ser_pdata->num_serial_links * sizeof(*ser_pdata->serial_links), -+ GFP_KERNEL); -+ -+ ser_serial_link = &ser_pdata->serial_links[0]; -+ ser_serial_link->link_id = 0; -+ ser_serial_link->link_type = MAX9X_LINK_TYPE_GMSL2; -+ ser_serial_link->rx_freq_mhz = 6000; -+ ser_serial_link->tx_freq_mhz = 187; -+ -+ ser_pdata->num_video_pipes = 1; -+ ser_pdata->video_pipes = devm_kzalloc(dev, -+ ser_pdata->num_video_pipes * sizeof(*ser_pdata->video_pipes), GFP_KERNEL); -+ -+ ser_video_pipe = &ser_pdata->video_pipes[0]; -+ ser_video_pipe->serial_link_id = 0; -+ ser_video_pipe->pipe_id = ser_sdinfo->serial_link_id; -+ ser_video_pipe->src_csi_id = 1; // PHY B typically -+ -+ return ser_pdata; -+} -+ -+static void PCA_00C003115(struct device *dev, char *suffix, unsigned int virt_addr, -+ struct max9x_subdev_pdata *ser_sdinfo, struct max9x_pdata *ser_pdata) -+{ -+ ser_pdata->num_subdevs = 1; -+ ser_pdata->subdevs = devm_kzalloc(dev, ser_pdata->num_subdevs * sizeof(*ser_pdata->subdevs), GFP_KERNEL); -+ pdata_sensor(dev, &ser_pdata->subdevs[0], "isx031", 0x1A, virt_addr); -+ -+ //NOTE: i2c_dev_set_name() will prepend "i2c-" to this name -+ char *dev_name = devm_kzalloc(dev, I2C_NAME_SIZE, GFP_KERNEL); -+ -+ snprintf(dev_name, I2C_NAME_SIZE, "isx031 %s", suffix); -+ ser_pdata->subdevs[0].board_info.dev_name = dev_name; -+ -+ struct max9x_video_pipe_pdata *ser_video_pipe = &ser_pdata->video_pipes[0]; -+ -+ ser_video_pipe->num_data_types = 1; -+ ser_video_pipe->data_types = devm_kzalloc(dev, -+ ser_video_pipe->num_data_types * sizeof(*ser_video_pipe->data_types), GFP_KERNEL); -+ ser_video_pipe->data_types[0] = 0x1E; -+ -+ ser_pdata->num_csi_links = 1; -+ ser_pdata->csi_links = devm_kzalloc(dev, ser_pdata->num_csi_links * sizeof(*ser_pdata->csi_links), GFP_KERNEL); -+ -+ struct max9x_csi_link_pdata *csi_link = &ser_pdata->csi_links[0]; -+ -+ csi_link->link_id = 1; -+ csi_link->num_lanes = 4; -+} -+ -+static void *ipu6_pdata(struct device *dev) -+{ -+ /* -+ * Assumptions: -+ * - All ports have same model of sensor -+ * - Single-port usage will always be port 0, never port 1 -+ * - Serializer always uses serial link 0 -+ */ -+ struct serdes_platform_data *ipu_pdata = dev->platform_data; -+ unsigned int num_ports = ipu_pdata->subdev_num; -+ struct max9x_pdata *des_pdata = devm_kzalloc(dev, sizeof(*des_pdata), GFP_KERNEL); -+ -+ snprintf(des_pdata->suffix, sizeof(des_pdata->suffix), "%c", ipu_pdata->suffix); -+ des_pdata->num_serial_links = num_ports; -+ des_pdata->serial_links = devm_kzalloc(dev, -+ des_pdata->num_serial_links * sizeof(*des_pdata->serial_links), GFP_KERNEL); -+ -+ des_pdata->num_subdevs = num_ports; -+ des_pdata->subdevs = devm_kzalloc(dev, des_pdata->num_subdevs * sizeof(*des_pdata->subdevs), GFP_KERNEL); -+ -+ des_pdata->num_video_pipes = num_ports; -+ des_pdata->video_pipes = devm_kzalloc(dev, -+ des_pdata->num_video_pipes * sizeof(*des_pdata->video_pipes), GFP_KERNEL); -+ -+ for (unsigned int serial_link_id = 0; serial_link_id < des_pdata->num_serial_links; serial_link_id++) { -+ struct max9x_serial_link_pdata *serial_link = &des_pdata->serial_links[serial_link_id]; -+ unsigned int video_pipe_id = serial_link_id; -+ struct serdes_subdev_info *ipu_sdinfo = &ipu_pdata->subdev_info[serial_link_id]; -+ struct max9x_video_pipe_pdata *des_video_pipe = &des_pdata->video_pipes[video_pipe_id]; -+ struct max9x_subdev_pdata *ser_sdinfo = &des_pdata->subdevs[serial_link_id]; -+ const char *sensor_name = ipu_sdinfo->board_info.type; -+ unsigned int ser_alias = ipu_sdinfo->ser_alias; -+ unsigned int sensor_alias = ipu_sdinfo->board_info.addr; -+ -+ serial_link->link_id = serial_link_id; -+ serial_link->link_type = MAX9X_LINK_TYPE_GMSL2; -+ serial_link->rx_freq_mhz = 6000; -+ serial_link->tx_freq_mhz = 187; -+ -+ des_video_pipe->serial_link_id = serial_link_id; -+ des_video_pipe->pipe_id = video_pipe_id; -+ des_video_pipe->num_maps = 3; -+ des_video_pipe->maps = devm_kzalloc(dev, -+ des_video_pipe->num_maps * sizeof(*des_video_pipe->maps), GFP_KERNEL); -+ -+ ser_sdinfo->serial_link_id = serial_link_id; -+ -+ SET_CSI_MAP(des_video_pipe->maps, 0, 0, 0x00, video_pipe_id, 0x00, 1); -+ SET_CSI_MAP(des_video_pipe->maps, 1, 0, 0x01, video_pipe_id, 0x01, 1); -+ -+ if (strcmp(sensor_name, "isx031") == 0) { -+ struct max9x_pdata *ser_pdata = PCA_00C003084(dev, ipu_sdinfo->suffix, ser_alias, ser_sdinfo); -+ -+ PCA_00C003115(dev, ipu_sdinfo->suffix, sensor_alias, ser_sdinfo, ser_pdata); -+ SET_CSI_MAP(des_video_pipe->maps, 2, 0, 0x1E, video_pipe_id, 0x1E, 1); /* YUV422 8-bit */ -+ } -+ -+ des_video_pipe->src_pipe_id = video_pipe_id; -+ } -+ -+ des_pdata->num_csi_links = 1; -+ des_pdata->csi_links = devm_kzalloc(dev, des_pdata->num_csi_links * sizeof(*des_pdata->csi_links), GFP_KERNEL); -+ -+ do { -+ struct max9x_csi_link_pdata *csi_link = &des_pdata->csi_links[0]; -+ -+ csi_link->link_id = 1; -+ csi_link->num_lanes = 2; -+ csi_link->tx_rate_mbps = 2000; -+ csi_link->auto_initial_deskew = true; -+ csi_link->initial_deskew_width = 7; -+ csi_link->auto_start = false; -+ csi_link->num_maps = 2; -+ csi_link->maps = devm_kzalloc(dev, csi_link->num_maps * sizeof(*csi_link->maps), GFP_KERNEL); -+ SET_PHY_MAP(csi_link->maps, 0, 0, 1, 0); /* 0 (DA0) -> PHY1.0 */ -+ SET_PHY_MAP(csi_link->maps, 1, 1, 1, 1); /* 1 (DA1) -> PHY1.1 */ -+ } while (0); -+ -+ return des_pdata; -+} -+ -+// //TODO: remap not hardcode according to pdata -+static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[link_id]; -+ unsigned int phys_addr, virt_addr; -+ struct i2c_client *virt_client; -+ struct regmap *virt_map; -+ unsigned int val; -+ const struct regmap_config regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ }; -+ -+ if (!serial_link->remote.pdata) -+ return 0; // No device to remap -+ -+ ret = max9x_select_i2c_chan(common->muxc, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ phys_addr = serial_link->remote.pdata->phys_addr; -+ virt_addr = serial_link->remote.pdata->board_info.addr; -+ if (phys_addr == virt_addr) -+ return 0; // Remap is not necessary -+ -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ -+ virt_client = i2c_new_dummy_device(common->client->adapter, virt_addr); -+ if (IS_ERR_OR_NULL(virt_client)) -+ goto err_regmap; -+ -+ virt_map = regmap_init_i2c(virt_client, ®map_config); -+ if (IS_ERR_OR_NULL(virt_map)) -+ goto err_virt_client; -+ -+ usleep_range(1000, 1050); -+ -+ ret = regmap_read(virt_map, 0xD, &val); -+ if (ret) -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ else -+ dev_dbg(common->dev, "DEV_ID after: 0x%02x", val); -+ -+ regmap_exit(virt_map); -+ -+err_virt_client: -+ i2c_unregister_device(virt_client); -+ -+err_regmap: -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ max9x_des_deisolate_serial_link(common, link_id); -+ -+ return ret; -+} -+ -+static int max9x_create_adapters_resume(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int link_id; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ int err = 0; -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ dev_info(dev, "Serial-link %d: %senabled", -+ link_id, (common->serial_link[link_id].enabled ? "" : "not ")); -+ -+ if (!common->serial_link[link_id].enabled) -+ continue; -+ -+ // This exponential retry works around a current problem in the locking code. -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ err = max9x_enable_serial_link(common, link_id); -+ if (!err) -+ break; -+ -+ dev_warn(dev, -+ "enable link %d failed, trying again (waiting %d ms)", -+ link_id, ms); -+ msleep(ms); -+ } -+ if (ms > RETRY_MS_MAX) { -+ dev_err(dev, "failed to enable link %d after multiple retries", -+ link_id); -+ max9x_disable_serial_link(common, link_id); -+ common->serial_link[link_id].enabled = false; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ err = max9x_remap_serializers_resume(common, link_id); -+ if (err) { -+ dev_err(dev, "failed to remap serializers on link %d", link_id); -+ max9x_disable_serial_link(common, link_id); -+ common->serial_link[link_id].enabled = false; -+ } -+ } -+ } -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) -+ max9x_setup_translations(common); -+ -+ return 0; -+} -+ -+// Functions -+int max9x_common_resume(struct max9x_common *common) -+{ -+ struct max9x_common *des_common = NULL; -+ struct device *dev = common->dev; -+ u32 des_link; -+ u32 phys_addr, virt_addr; -+ int ret; -+ -+ const struct regmap_config regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ }; -+ -+ if (dev->platform_data) { -+ struct max9x_pdata *pdata = dev->platform_data; -+ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -+ if (des_common) -+ des_link = pdata->serial_links[0].des_link_id; -+ } -+ } -+ -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ // Isolate this link until after reset and potential address remapping, -+ // avoiding a race condition with two serializers resetting at the same time -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto enable_err; -+ } -+ -+ dev_dbg(dev, "create phys dummy device"); -+ -+ if (phys_addr != virt_addr) { -+ common->phys_client = i2c_new_dummy_device(common->client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(common->phys_client)) { -+ dev_err(dev, "Failed to create dummy device for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ -+ common->phys_map = regmap_init_i2c(common->phys_client, ®map_config); -+ if (IS_ERR_OR_NULL(common->phys_map)) { -+ dev_err(dev, "Failed to create dummy device regmap for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ } -+ -+ dev_dbg(dev, "Enable"); -+ -+ ret = max9x_enable(common); -+ if (ret) -+ dev_err(dev, "Failed to enable"); -+ -+enable_err: -+ if (common->phys_map) { -+ regmap_exit(common->phys_map); -+ common->phys_map = NULL; -+ } -+ -+ if (common->phys_client) { -+ i2c_unregister_device(common->phys_client); -+ common->phys_client = NULL; -+ } -+ -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ // Allow other serializers to continue -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ -+ ret = max9x_create_adapters_resume(common); -+ if (ret) -+ goto err_enable; -+ -+ return 0; -+ -+err_enable: -+ max9x_disable(common); -+ -+ return ret; -+} -+EXPORT_SYMBOL(max9x_common_resume); -+ -+int max9x_common_suspend(struct max9x_common *common) -+{ -+ unsigned int link_id; -+ -+ dev_dbg(common->dev, "try to suspend"); -+ -+ max9x_disable_translations(common); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) -+ max9x_disable_serial_link(common, link_id); -+ -+ max9x_disable(common); -+ -+ return 0; -+} -+EXPORT_SYMBOL(max9x_common_suspend); -+ -+int max9x_common_init_i2c_client(struct max9x_common *common, -+ struct i2c_client *client, -+ const struct regmap_config *regmap_config, -+ struct max9x_common_ops *common_ops, -+ struct max9x_serial_link_ops *serial_link_ops, -+ struct max9x_csi_link_ops *csi_link_ops, -+ struct max9x_line_fault_ops *lf_ops) -+{ -+ struct device *dev = &client->dev; -+ struct i2c_adapter *adap = to_i2c_adapter(dev->parent); -+ struct max9x_common *des_common = NULL; -+ u32 des_link; -+ u32 phys_addr, virt_addr; -+ int ret; -+ -+ common->dev = dev; -+ common->client = client; -+ common->common_ops = common_ops; -+ common->serial_link_ops = serial_link_ops; -+ common->csi_link_ops = csi_link_ops; -+ common->line_fault_ops = lf_ops; -+ -+ /* If no GPIO is found this will return NULL, and will not error */ -+ common->reset_gpio = devm_gpiod_get_optional(dev, MAX9X_RESET_GPIO_NAME, GPIOD_OUT_HIGH); -+ if (IS_ERR(common->reset_gpio)) { -+ dev_err(dev, "gpiod_get failed with error: %ld", PTR_ERR(common->reset_gpio)); -+ return PTR_ERR(common->reset_gpio); -+ } -+ -+ common->vdd_regulator = devm_regulator_get_optional(dev, MAX9X_VDD_REGULATOR_NAME); -+ if (IS_ERR_OR_NULL(common->vdd_regulator)) -+ dev_dbg(dev, "Missing VDD regulator"); -+ -+ common->map = devm_regmap_init_i2c(client, regmap_config); -+ if (IS_ERR_OR_NULL(common->map)) { -+ dev_err(dev, "Failed to create regmap."); -+ return PTR_ERR(common->map); -+ } -+ -+ mutex_init(&common->link_mutex); -+ mutex_init(&common->isolate_mutex); -+ common->isolated_link = -1; -+ common->selected_link = -1; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_CSI_LINK, csi_link, num_csi_links); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_VIDEO_PIPE, video_pipe, num_video_pipes); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_SERIAL_LINK, serial_link, num_serial_links); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_LINE_FAULT, line_fault, num_line_faults); -+ if (ret) -+ return ret; -+ -+ common->muxc = i2c_mux_alloc( -+ adap, dev, -+ common->num_serial_links, -+ 0, // no priv space needed -+ I2C_MUX_LOCKED, -+ max9x_select_i2c_chan, -+ max9x_deselect_i2c_chan); -+ if (IS_ERR_OR_NULL(common->muxc)) { -+ dev_err(dev, "Failed to allocate mux core"); -+ return -ENOMEM; -+ } -+ common->muxc->priv = common; -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ dev->platform_data = ipu6_pdata(dev); -+ -+ if (dev->platform_data) { -+ struct max9x_pdata *pdata = dev->platform_data; -+ -+ dev_dbg(dev, "Parse pdata"); -+ -+ ret = max9x_parse_pdata(common, pdata); -+ if (ret) -+ return ret; -+ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -+ if (des_common) -+ des_link = pdata->serial_links[0].des_link_id; -+ } -+ } -+ -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ // Isolate this link until after reset and potential address remapping, -+ // avoiding a race condition with two serializers resetting at the same time -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto enable_err; -+ } -+ -+ if (phys_addr != virt_addr) { -+ common->phys_client = i2c_new_dummy_device(common->client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(common->phys_client)) { -+ dev_err(dev, "Failed to create dummy device for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ -+ common->phys_map = regmap_init_i2c(common->phys_client, regmap_config); -+ if (IS_ERR_OR_NULL(common->phys_map)) { -+ dev_err(dev, "Failed to create dummy device regmap for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ } -+ -+ dev_dbg(dev, "Enable"); -+ -+ ret = max9x_enable(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable"); -+ // Delay return until after the temporary regamp & phys_client are resolved -+ goto enable_err; -+ } -+ -+enable_err: -+ if (common->phys_map) { -+ regmap_exit(common->phys_map); -+ common->phys_map = NULL; -+ } -+ -+ if (common->phys_client) { -+ i2c_unregister_device(common->phys_client); -+ common->phys_client = NULL; -+ } -+ -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ // Allow other serializers to continue -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ -+ // _Now_ bail if enable() failed -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "Enable gpio"); -+ ret = max9x_setup_gpio(common); -+ if (ret) -+ goto err_enable; -+ -+ dev_dbg(dev, "Enable line faults"); -+ -+ ret = max9x_enable_line_faults(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable line faults"); -+ goto err_enable; -+ } -+ -+ dev_dbg(dev, "Enable links"); -+ -+ ret = max9x_create_adapters(common); -+ if (ret) -+ goto err_enable; -+ -+ ret = max9x_register_v4l_subdev(common); -+ if (ret) -+ goto err_adapters; -+ -+ return 0; -+ -+err_adapters: -+ i2c_mux_del_adapters(common->muxc); -+ -+err_enable: -+ max9x_disable(common); -+ -+ return ret; -+} -+EXPORT_SYMBOL(max9x_common_init_i2c_client); -+ -+void max9x_destroy(struct max9x_common *common) -+{ -+ unsigned int link_id; -+ unsigned int line; -+ -+ dev_dbg(common->dev, "Destroy"); -+ -+ max9x_disable_translations(common); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ max9x_disable_serial_link(common, link_id); -+ max9x_sysfs_destroy_get_link(common, link_id); -+ } -+ -+ for (line = 0; line < common->num_line_faults; line++) -+ max9x_sysfs_destroy_line_fault_status(common, line); -+ -+ max9x_disable(common); -+ -+ // unregister devices? -+ -+ v4l2_async_unregister_subdev(&common->v4l.sd); -+ media_entity_cleanup(&common->v4l.sd.entity); -+ -+ i2c_mux_del_adapters(common->muxc); -+ mutex_destroy(&common->link_mutex); -+ mutex_destroy(&common->isolate_mutex); -+} -+EXPORT_SYMBOL(max9x_destroy); -+ -+/** -+ * max9x_serdes_mhz_to_rate()- Lookup register value for given frequency -+ * -+ * Return: Positive value on success, negative error on failure -+ */ -+int max9x_serdes_mhz_to_rate(struct max9x_serdes_rate_table *table, -+ int len, unsigned int freq_mhz) -+{ -+ int i; -+ -+ for (i = 0; i < len; i++) -+ if (table[i].freq_mhz == freq_mhz) -+ return table[i].val; -+ -+ WARN_ONCE(true, "Invalid GMSL frequency: %d MHz", freq_mhz); -+ -+ return -EINVAL; -+} -+EXPORT_SYMBOL(max9x_serdes_mhz_to_rate); -+ -+struct max9x_common *max9x_phandle_to_common(struct device_node *node, const char *name) -+{ -+ struct device_node *des_node = of_parse_phandle(node, name, 0); -+ struct i2c_client *c = of_find_i2c_device_by_node(des_node); -+ -+ if (!c) -+ return NULL; -+ -+ return max9x_client_to_common(c); -+} -+EXPORT_SYMBOL(max9x_phandle_to_common); -+ -+struct max9x_common *max9x_sd_to_common(struct v4l2_subdev *sd) -+{ -+ if (!sd) -+ return NULL; -+ -+ struct max9x_serdes_v4l *v4l = container_of(sd, struct max9x_serdes_v4l, sd); -+ -+ return container_of(v4l, struct max9x_common, v4l); -+} -+EXPORT_SYMBOL(max9x_sd_to_common); -+ -+struct max9x_common *max9x_client_to_common(struct i2c_client *client) -+{ -+ if (!client) -+ return NULL; -+ -+ return max9x_sd_to_common(i2c_get_clientdata(client)); -+} -+EXPORT_SYMBOL(max9x_client_to_common); -+ -+// Static functions -+ -+int max9x_soft_reset(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->soft_reset) -+ return common->common_ops->soft_reset(common); -+ -+ return 0; -+} -+ -+int max9x_remap_addr(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->remap_addr) -+ return common->common_ops->remap_addr(common); -+ -+ return 0; -+} -+ -+int max9x_setup_gpio(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->remap_addr) -+ return common->common_ops->setup_gpio(common); -+ -+ return 0; -+} -+ -+/** -+ * max9x_enable() - Enable reset and power to des. -+ */ -+int max9x_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phys_addr, virt_addr; -+ int ret; -+ -+ if (common->regulator_enabled) -+ return 0; -+ -+ virt_addr = common->client->addr; -+ phys_addr = (common->phys_client ? common->phys_client->addr : virt_addr); -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_enable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ common->regulator_enabled = true; -+ } -+ -+ // Ensure device is reset -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) { -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ usleep_range(1000, 1050); -+ gpiod_set_value_cansleep(common->reset_gpio, 0); -+ } else { -+ // No reset_gpio, device requires soft-reset -+ if (phys_addr == virt_addr) { -+ // No remapping necessary -+ ret = max9x_soft_reset(common); -+ if (ret) -+ goto err; -+ } else if (phys_addr != virt_addr) { -+ // Try virt address first -+ ret = max9x_soft_reset(common); -+ if (ret) { -+ // Nope? Try phys address next -+ struct regmap *virt_map = common->map; -+ -+ common->map = common->phys_map; -+ ret = max9x_soft_reset(common); -+ common->map = virt_map; -+ if (ret) -+ goto err; // Not found at either address, fail -+ } -+ } -+ } -+ -+ dev_dbg(dev, "sleep for startup after soft reset"); -+ usleep_range(100000, 100050); -+ -+ if (phys_addr != virt_addr) { -+ // Device is now reset, but requires remap -+ ret = max9x_remap_addr(common); -+ if (ret) -+ goto err; -+ } -+ -+ if (common->common_ops && common->common_ops->enable) { -+ ret = common->common_ops->enable(common); -+ if (ret) -+ goto err; -+ } -+ -+ return 0; -+ -+err: -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (common->regulator_enabled && !IS_ERR_OR_NULL(common->vdd_regulator)) -+ regulator_disable(common->vdd_regulator); -+ -+ common->regulator_enabled = false; -+ -+ return ret; -+} -+ -+/** -+ * max9x_disable() - Disable reset and power to des. -+ */ -+int max9x_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ ret = max9x_disable_line_faults(common); -+ if (ret) { -+ dev_err(dev, "Failed to disable line faults"); -+ return ret; -+ } -+ -+ if (common->regulator_enabled) { -+ if (common->common_ops && common->common_ops->disable) -+ return common->common_ops->disable(common); -+ -+ common->regulator_enabled = false; -+ -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_disable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to disable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ } -+ return 0; -+} -+ -+//int max9x_verify_devid(struct max9x_common *common) -+//{ -+// if (common->common_ops && common->common_ops->verify_devid) -+// return common->common_ops->verify_devid(common); -+// return 0; -+//} -+ -+//TODO: remap not hardcode according to pdata -+int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[link_id]; -+ unsigned int phys_addr, virt_addr; -+ struct i2c_client *phys_client, *virt_client; -+ struct regmap *phys_map, *virt_map; -+ unsigned int val; -+ const struct regmap_config regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ }; -+ -+ if (!serial_link->remote.pdata) -+ return 0; // No device to remap -+ -+ ret = max9x_select_i2c_chan(common->muxc, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ phys_addr = serial_link->remote.pdata->phys_addr; -+ virt_addr = serial_link->remote.pdata->board_info.addr; -+ if (phys_addr == virt_addr) -+ return 0; // Remap is not necessary -+ -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ -+ phys_client = i2c_new_dummy_device(common->client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(phys_client)) { -+ dev_err(common->dev, "Failed to create dummy client for phys_addr"); -+ return PTR_ERR(phys_client); -+ } -+ -+ phys_map = regmap_init_i2c(phys_client, ®map_config); -+ if (IS_ERR_OR_NULL(phys_map)) -+ goto err_client; -+ -+ virt_client = i2c_new_dummy_device(common->client->adapter, virt_addr); -+ if (IS_ERR_OR_NULL(virt_client)) -+ goto err_regmap; -+ -+ virt_map = regmap_init_i2c(virt_client, ®map_config); -+ if (IS_ERR_OR_NULL(virt_map)) -+ goto err_virt_client; -+ -+ ret = regmap_read(virt_map, 0xD, &val); -+ if (!ret) { -+ dev_info(common->dev, "Remap not necessary"); -+ ret = 0; -+ goto err_virt_regmap; -+ } -+ -+ ret = regmap_read(phys_map, 0xD, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ goto err_virt_regmap; -+ } else { -+ dev_dbg(common->dev, "DEV_ID before: 0x%02x", val); -+ } -+ -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ if (ret) { -+ dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ phys_addr, virt_addr, ret); -+ goto err_virt_regmap; -+ } -+ -+ usleep_range(1000, 1050); -+ -+ ret = regmap_read(virt_map, 0xD, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ goto err_virt_regmap; -+ } else { -+ dev_dbg(common->dev, "DEV_ID after: 0x%02x", val); -+ } -+ -+err_virt_regmap: -+ regmap_exit(virt_map); -+err_virt_client: -+ i2c_unregister_device(virt_client); -+ -+err_regmap: -+ regmap_exit(phys_map); -+err_client: -+ i2c_unregister_device(phys_client); -+ -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ -+ max9x_des_deisolate_serial_link(common, link_id); -+ -+ return ret; -+} -+ -+int max9x_create_adapters(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int link_id; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ int err = 0; -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ err = max9x_sysfs_create_get_link(common, link_id); -+ if (err) { -+ dev_err(dev, "failed to create sysfs lock status file for link %d", -+ link_id); -+ continue; -+ } -+ -+ dev_info(dev, "Serial-link %d: %senabled", -+ link_id, (common->serial_link[link_id].enabled ? "" : "not ")); -+ -+ if (!common->serial_link[link_id].enabled) -+ continue; -+ -+ // This exponential retry works around a current problem in the locking code. -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ err = max9x_enable_serial_link(common, link_id); -+ if (!err) -+ break; -+ -+ dev_warn(dev, -+ "enable link %d failed, trying again (waiting %d ms)", -+ link_id, ms); -+ msleep(ms); -+ } -+ if (ms > RETRY_MS_MAX) { -+ dev_err(dev, "failed to enable link %d after multiple retries", -+ link_id); -+ goto err_disable; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ err = max9x_remap_serializers(common, link_id); -+ if (err) { -+ dev_err(dev, "failed to remap serializers on link %d", link_id); -+ goto err_disable; -+ } -+ } -+ -+ continue; -+err_disable: -+ max9x_disable_serial_link(common, link_id); -+ common->serial_link[link_id].enabled = false; -+ } -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ max9x_setup_translations(common); -+ -+ err = i2c_mux_add_adapter(common->muxc, 0, link_id); -+ if (err) { -+ dev_err(dev, "failed to add adapter for link %d", -+ link_id); -+ max9x_disable_serial_link(common, link_id); -+ continue; -+ } -+ } -+ -+ return 0; -+} -+ -+//TODO: refactor -+static int max9x_subdev_s_stream(struct max9x_common *common, unsigned int serial_link_id, int enable) -+{ -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ int n; -+ struct media_pad *local_pad; -+ struct media_pad *remote_pad; -+ struct v4l2_subdev *remote_sd; -+ unsigned int video_pipe_id; -+ int err; -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ n = max9x_serial_link_to_pad(common, serial_link_id); -+ if (n < 0) { -+ dev_err(common->dev, "Invalid link %d", serial_link_id); -+ return n; -+ } -+ -+ dev_dbg(common->dev, "subdev_s_stream serial %d -> pad %d, %s", -+ serial_link_id, n, (enable ? "on" : "off")); -+ -+ if (enable) { -+ for (video_pipe_id = 0; video_pipe_id < common->num_video_pipes; video_pipe_id++) { -+ struct max9x_serdes_video_pipe *video_pipe = &common->video_pipe[video_pipe_id]; -+ unsigned int map_id; -+ -+ if (!video_pipe->enabled) -+ continue; -+ -+ if (video_pipe->config.src_link != serial_link_id) -+ continue; -+ -+ for (map_id = 0; map_id < video_pipe->config.num_maps; map_id++) { -+ unsigned int csi_link_id = video_pipe->config.map[map_id].dst_csi; -+ -+ if (common->csi_link[csi_link_id].config.auto_start) -+ continue; // Already started at probe -+ -+ if (common->csi_link_ops && common->csi_link_ops->enable) { -+ err = common->csi_link_ops->enable(common, csi_link_id); -+ if (err) -+ dev_warn(common->dev, "csi_link_ops->enable CSI %d failed: %d", -+ csi_link_id, err); -+ } -+ } -+ } -+ } -+ -+ local_pad = &v4l->pads[n]; -+ -+ remote_pad = media_pad_remote_pad_first(local_pad); -+ if (IS_ERR_OR_NULL(remote_pad)) { -+ dev_err(common->dev, "Failed to find remote pad for link %d", serial_link_id); -+ return IS_ERR(remote_pad) ? PTR_ERR(remote_pad) : -ENODEV; -+ } -+ -+ if (!remote_pad->entity) { -+ dev_err(common->dev, "Remote pad has no entity??"); -+ return -ENODEV; -+ } -+ -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ if (!remote_sd) { -+ dev_err(common->dev, "Failed to resolve entity to subdev"); -+ return -ENODEV; -+ } -+ -+ err = max9x_s_stream(remote_sd, enable); -+ // err = v4l2_subdev_call(remote_sd, video, s_stream, enable); -+ if (err) -+ return err; -+ -+ if (!enable) { -+ for (video_pipe_id = 0; video_pipe_id < common->num_video_pipes; video_pipe_id++) { -+ struct max9x_serdes_video_pipe *video_pipe = &common->video_pipe[video_pipe_id]; -+ unsigned int map_id; -+ -+ if (!video_pipe->enabled) -+ continue; -+ -+ if (video_pipe->config.src_link != serial_link_id) -+ continue; -+ -+ for (map_id = 0; map_id < video_pipe->config.num_maps; map_id++) { -+ unsigned int csi_link_id = video_pipe->config.map[map_id].dst_csi; -+ -+ if (common->csi_link_ops && common->csi_link_ops->disable) { -+ err = common->csi_link_ops->disable(common, csi_link_id); -+ if (err) -+ dev_warn(common->dev, "csi_link_ops->disable CSI %d failed: %d", -+ csi_link_id, err); -+ } -+ } -+ } -+ } -+ -+ } -+ -+ return 0; -+} -+ -+static int max9x_s_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ -+ // NOTE: This is only called if V4L2_CID_IPU_SET_SUB_STREAM is not -+ // a valid control. (Typically only invoked by DES) -+ -+ /* Stream on/off sensor */ -+ dev_dbg(sd->dev, "s_stream %s", (enable ? "on" : "off")); -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ int n; -+ struct media_pad *local_pad; -+ struct media_pad *remote_pad; -+ struct v4l2_subdev *remote_sd; -+ -+ n = max9x_csi_link_to_pad(common, 0); -+ if (n < 0) { -+ dev_err(common->dev, "No CSI links?"); -+ return n; -+ } -+ -+ local_pad = &v4l->pads[n]; -+ -+ remote_pad = media_pad_remote_pad_first(local_pad); -+ if (IS_ERR_OR_NULL(remote_pad)) { -+ dev_err(common->dev, "Failed to find remote pad for CSI link %d", 0); -+ return IS_ERR(remote_pad) ? PTR_ERR(remote_pad) : -ENODEV; -+ } -+ -+ if (!remote_pad->entity) { -+ dev_err(common->dev, "Remote pad has no entity??"); -+ return -ENODEV; -+ } -+ -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ if (!remote_sd) { -+ dev_err(common->dev, "Failed to resolve entity to subdev"); -+ return -ENODEV; -+ } -+ -+ dev_dbg(common->dev, "s_stream %s:%d %s", remote_sd->name, remote_pad->index, (enable ? "on" : "off")); -+ -+ return v4l2_subdev_call(remote_sd, video, s_stream, enable); -+ } -+ -+ -+ return 0; -+} -+ -+/* not used now -+static u32 max9x_get_sink_pad_by_pad(u32 source_pad, u32 source_stream, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route *routes; -+ u32 sink_pad = 0; -+ unsigned int i; -+ -+ if (!state) -+ return 0; -+ -+ routes = state->routing.routes; -+ for (i = 0; i < state->routing.num_routes; i++) { -+ if (routes[i].source_pad == source_pad && -+ routes[i].source_stream == source_stream) { -+ sink_pad = routes[i].sink_pad; -+ break; -+ } -+ } -+ -+ return sink_pad; -+} -+*/ -+ -+static int _max9x_set_stream(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask, int enable) -+{ -+ struct max9x_serdes_v4l *v4l = container_of(subdev, struct max9x_serdes_v4l, sd); -+ struct max9x_common *common = container_of(v4l, struct max9x_common, v4l); -+ struct device *dev = common->dev; -+ int i; -+ -+ dev_dbg(dev, "%s max9x\n", enable != 0 ? "enable" : "disable"); -+ -+ for (i = 0; i < 64; i++) { -+ if (streams_mask & ((u64)1 << i)) -+ break; -+ } -+ -+ return max9x_subdev_s_stream(common, i, enable); -+} -+ -+static int max9x_enable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return _max9x_set_stream(subdev, state, pad, streams_mask, true); -+} -+ -+static int max9x_disable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return _max9x_set_stream(subdev, state, pad, streams_mask, false); -+} -+ -+static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ -+ if (IS_ERR_OR_NULL(fmt)) { -+ dev_err(common->dev, "Invalid fmt %p", fmt); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (fmt->pad < 0 || fmt->pad >= common->v4l.num_pads) { -+ dev_err(sd->dev, "%s invalid pad %d", __func__, fmt->pad); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ return v4l2_subdev_state_get_format(v4l2_state, fmt->pad); -+ -+ //TODO: refactor __max9x_get_ffmt, v4l.ffmts is not used -+ if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -+ return &common->v4l.ffmts[fmt->pad]; -+ -+ return ERR_PTR(-EINVAL); -+} -+ -+ -+static int max9x_get_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_mbus_framefmt *ffmt; -+ -+ mutex_lock(&v4l->lock); -+ -+ ffmt = __max9x_get_ffmt(sd, v4l2_state, fmt); -+ if (IS_ERR_OR_NULL(ffmt)) { -+ mutex_unlock(&v4l->lock); -+ return -EINVAL; -+ } -+ -+ fmt->format = *ffmt; -+ mutex_unlock(&v4l->lock); -+ -+ dev_dbg(sd->dev, "subdev_format: which: %s, pad: %d.", -+ fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE ? -+ "V4L2_SUBDEV_FORMAT_ACTIVE" : "V4L2_SUBDEV_FORMAT_TRY", -+ fmt->pad); -+ -+ dev_dbg(sd->dev, "framefmt: width: %d, height: %d, code: 0x%x.", -+ fmt->format.width, fmt->format.height, fmt->format.code); -+ -+ return 0; -+} -+ -+static int max9x_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_mbus_framefmt *ffmt; -+ -+ mutex_lock(&v4l->lock); -+ -+ ffmt = __max9x_get_ffmt(sd, v4l2_state, fmt); -+ if (IS_ERR_OR_NULL(ffmt)) { -+ mutex_unlock(&v4l->lock); -+ return -EINVAL; -+ } -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { -+ ffmt->width = fmt->format.width; -+ ffmt->height = fmt->format.height; -+ ffmt->code = fmt->format.code; -+ } -+ fmt->format = *ffmt; -+ -+ mutex_unlock(&v4l->lock); -+ -+ return 0; -+} -+ -+static int max9x_get_frame_desc(struct v4l2_subdev *sd, -+ unsigned int pad, struct v4l2_mbus_frame_desc *desc) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ -+ if (pad < 0 || pad >= common->v4l.num_pads) -+ return -EINVAL; -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ int n; -+ struct media_pad *local_pad; -+ struct media_pad *remote_pad; -+ struct v4l2_subdev *remote_sd; -+ -+ n = max9x_serial_link_to_pad(common, pad); -+ if (n < 0) { -+ dev_err(common->dev, "Invalid link %d", pad); -+ return n; -+ } -+ local_pad = &v4l->pads[n]; -+ -+ remote_pad = media_pad_remote_pad_first(local_pad); -+ if (IS_ERR_OR_NULL(remote_pad)) { -+ dev_err(common->dev, -+ "Failed to find remote pad for link %d", pad); -+ return IS_ERR(remote_pad) ? PTR_ERR(remote_pad) : -+ -ENODEV; -+ } -+ -+ if (!remote_pad->entity) { -+ dev_err(common->dev, "Remote pad has no entity??"); -+ return -ENODEV; -+ } -+ -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ if (!remote_sd) { -+ dev_err(common->dev, -+ "Failed to resolve entity to subdev"); -+ return -ENODEV; -+ } -+ -+ common = max9x_sd_to_common(remote_sd); -+ v4l = &common->v4l; -+ n = max9x_csi_link_to_pad(common, 0); -+ if (n < 0) { -+ dev_err(common->dev, "Invalid link %d", pad); -+ return n; -+ } -+ -+ local_pad = &v4l->pads[n]; -+ -+ remote_pad = media_pad_remote_pad_first(local_pad); -+ if (IS_ERR_OR_NULL(remote_pad)) { -+ dev_err(common->dev, -+ "Failed to find remote pad for CSI link %d", 0); -+ return IS_ERR(remote_pad) ? PTR_ERR(remote_pad) : -+ -ENODEV; -+ } -+ -+ if (!remote_pad->entity) { -+ dev_err(common->dev, "Remote pad has no entity??"); -+ return -ENODEV; -+ } -+ -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ if (!remote_sd) { -+ dev_err(common->dev, -+ "Failed to resolve entity to subdev"); -+ return -ENODEV; -+ } -+ return v4l2_subdev_call(remote_sd, pad, get_frame_desc, 0, -+ desc); -+ } -+ -+ return 0; -+} -+ -+static int max9x_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ if (mbus_code_to_csi_dt(code->code) < 0) -+ return -EINVAL; -+ return 0; -+} -+ -+static int _max9x_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_krouting *routing) -+{ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 1920, -+ .height = 1536, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ }; -+ int ret; -+ -+ /* -+ * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until -+ * frame desc is made dynamically allocated. -+ */ -+ -+ if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) -+ return -E2BIG; -+ -+ ret = v4l2_subdev_routing_validate(sd, routing, -+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | -+ V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); -+ if (ret) -+ return ret; -+ -+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+static int max9x_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing) -+{ -+ _max9x_set_routing(sd, state, routing); -+ -+ return 0; -+} -+ -+static int max9x_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ -+ struct v4l2_subdev_route des_routes[] = { -+ { -+ .sink_pad = 5, -+ .sink_stream = 0, -+ .source_pad = 0, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }, -+ }; -+ struct v4l2_subdev_route ser_routes[] = { -+ { -+ .sink_pad = 0, -+ .sink_stream = 0, -+ .source_pad = 2, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }, -+ }; -+ struct v4l2_subdev_krouting des_routing = { -+ .num_routes = ARRAY_SIZE(des_routes), -+ .routes = des_routes, -+ }; -+ struct v4l2_subdev_krouting ser_routing = { -+ .num_routes = ARRAY_SIZE(ser_routes), -+ .routes = ser_routes, -+ }; -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ return _max9x_set_routing(sd, state, &des_routing); -+ else -+ return _max9x_set_routing(sd, state, &ser_routing); -+} -+ -+static const struct v4l2_subdev_pad_ops max9x_sd_pad_ops = { -+ .get_fmt = max9x_get_fmt, -+ .set_fmt = max9x_set_fmt, -+ .get_frame_desc = max9x_get_frame_desc, -+ .enum_mbus_code = max9x_enum_mbus_code, -+ .set_routing = max9x_set_routing, -+ .enable_streams = max9x_enable_streams, -+ .disable_streams = max9x_disable_streams, -+}; -+ -+static struct v4l2_subdev_ops max9x_sd_ops = { -+ .pad = &max9x_sd_pad_ops, -+}; -+ -+static int max9x_registered(struct v4l2_subdev *sd) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct device *dev = common->dev; -+ int ret; -+ -+ for (unsigned int link_id = 0; link_id < common->num_serial_links; link_id++) { -+ if (!common->serial_link[link_id].enabled) { -+ dev_dbg(dev, "Serial-link %d not enabled, skipping subdevs", link_id); -+ continue; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ struct max9x_subdev_pdata *subdev_pdata = -+ common->serial_link[link_id].remote.pdata; -+ -+ if (subdev_pdata) { -+ struct max9x_pdata *ser_pdata = -+ subdev_pdata->board_info.platform_data; -+ -+ WARN_ON(ser_pdata->num_serial_links < 1); -+ -+ ser_pdata->serial_links[0].des_client = common->client; -+ ser_pdata->serial_links[0].des_link_id = link_id; -+ -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); -+ -+ if (IS_ERR_OR_NULL(subdev)) { -+ dev_err(dev, "Failure registering serializer %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ return PTR_ERR(subdev); -+ } -+ -+ dev_dbg(dev, "Registered serializer %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ -+ struct max9x_common *ser_common = max9x_sd_to_common(subdev); -+ -+ int remote_pad = max9x_serial_link_to_pad(ser_common, 0); -+ int local_pad = max9x_serial_link_to_pad(common, link_id); -+ -+ dev_dbg(dev, "Create link from ser link 0 (pad %d) -> des link %d (pad %d)", -+ remote_pad, link_id, local_pad); -+ -+ ret = media_create_pad_link(&subdev->entity, remote_pad, -+ &sd->entity, local_pad, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (ret) { -+ dev_err(dev, "Failed creating pad link to serializer"); -+ return ret; -+ } -+ //for suspend/resume sequential -+ // ser_common->serial_link[0].near.client = common->client; -+ // common->serial_link[link_id].remote.client = ser_common->client; -+ } -+ } else { -+ struct max9x_pdata *pdata = dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ -+ if (subdev_pdata->serial_link_id == link_id) { -+ char dev_id[I2C_NAME_SIZE]; -+ -+ snprintf(dev_id, sizeof(dev_id), "i2c-%s", -+ subdev_pdata->board_info.dev_name); -+ -+ dev_dbg(dev, "Registering sensor %s (%s)...", -+ subdev_pdata->board_info.type, dev_id); -+ -+ static struct gpiod_lookup_table sensor_gpios = { -+ .dev_id = "", -+ .table = { -+ GPIO_LOOKUP("", 0, "reset", -+ GPIO_ACTIVE_HIGH), -+ {} -+ }, -+ }; -+ -+ sensor_gpios.dev_id = dev_id; -+ sensor_gpios.table[0].key = common->gpio_chip.label; -+ -+ gpiod_add_lookup_table(&sensor_gpios); -+ -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); -+ -+ gpiod_remove_lookup_table(&sensor_gpios); -+ -+ if (IS_ERR_OR_NULL(subdev)) { -+ dev_err(dev, -+ "Failure registering sensor %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ return PTR_ERR(subdev); -+ } -+ -+ snprintf(subdev->name, sizeof(subdev->name), -+ "isx031 %s", pdata->suffix); -+ dev_dbg(dev, "Registered sensor %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ -+ int remote_pad = media_get_pad_index(&subdev->entity, -+ MEDIA_PAD_FL_SOURCE, -+ PAD_SIGNAL_DEFAULT); -+ int local_pad = max9x_csi_link_to_pad(common, 0); -+ -+ dev_dbg(dev, "Create link from sen pad %d -> ser link %d (pad %d)", -+ remote_pad, link_id, -+ local_pad); -+ -+ ret = media_create_pad_link(&subdev->entity, remote_pad, -+ &sd->entity, local_pad, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (ret) { -+ dev_err(dev, "Failed creating pad link to serializer"); -+ return ret; -+ } -+ -+ } -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static struct v4l2_subdev_internal_ops max9x_sd_internal_ops = { -+ .registered = max9x_registered, -+ .init_state = max9x_init_state, -+}; -+ -+static int max9x_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct max9x_serdes_v4l *v4l = container_of(ctrl->handler, struct max9x_serdes_v4l, ctrl_handler); -+ struct max9x_common *common = container_of(v4l, struct max9x_common, v4l); -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "s_ctrl"); -+ -+ switch (ctrl->id) { -+ case V4L2_CID_LINK_FREQ: { -+ if (ctrl->p_new.p_u8) { -+ if (*ctrl->p_new.p_u8 <= (ARRAY_SIZE(max9x_op_sys_clock) - 1)) { -+ unsigned int csi_link_id; -+ struct v4l2_ctrl *link_freq = v4l->link_freq; -+ -+ dev_info(dev, "user-modified %s index val=%d to user-val=%d", -+ ctrl->name, (unsigned int)link_freq->val, -+ (unsigned int)*ctrl->p_new.p_u8); -+ -+ link_freq->val = (s32) *ctrl->p_new.p_u8; -+ -+ for (csi_link_id = 0; csi_link_id < common->num_csi_links; csi_link_id++) { -+ if (!common->csi_link[csi_link_id].enabled) -+ continue; -+ -+ common->csi_link[csi_link_id].config.freq_mhz = -+ MAX9X_LINK_FREQ_HZ_TO_MBPS(max9x_op_sys_clock[link_freq->val]); -+ -+ if (common->csi_link[csi_link_id].config.freq_mhz > 1500) { -+ common->csi_link[csi_link_id].config.auto_init_deskew_enabled = true; -+ common->csi_link[csi_link_id].config.initial_deskew_width = 7; -+ } -+ } -+ } -+ } -+ } -+ break; -+ -+ default: -+ dev_info(dev, "unknown control id: 0x%X", ctrl->id); -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops max9x_ctrl_ops = { -+ .s_ctrl = max9x_s_ctrl, -+}; -+ -+static struct v4l2_ctrl_config max9x_v4l2_controls[] = { -+ { -+ .ops = &max9x_ctrl_ops, -+ .id = V4L2_CID_LINK_FREQ, -+ .name = "V4L2_CID_LINK_FREQ", -+ .type = V4L2_CTRL_TYPE_INTEGER_MENU, -+ .min = 0, -+ .max = ARRAY_SIZE(max9x_op_sys_clock) - 1, -+ .def = 2, -+ .menu_skip_mask = 0, -+ .qmenu_int = max9x_op_sys_clock, -+ }, -+}; -+ -+static int max9x_csi_link_to_pad(struct max9x_common *common, int csi_id) -+{ -+ return (csi_id < common->num_csi_links) ? csi_id : -EINVAL; -+} -+ -+static int max9x_serial_link_to_pad(struct max9x_common *common, int link_id) -+{ -+ dev_dbg(common->dev, "link_id %d < num_serial_links %d ? num_csi_links %d + link_id %d : -EINVAL", -+ link_id, common->num_serial_links, common->num_csi_links, link_id); -+ return (link_id < common->num_serial_links) ? common->num_csi_links + link_id : -EINVAL; -+} -+ -+static int max9x_register_v4l_subdev(struct max9x_common *common) -+{ -+ struct i2c_client *client = common->client; -+ struct device *dev = common->dev; -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_subdev *sd = &v4l->sd; -+ struct v4l2_ctrl_handler *ctrl_handler = &v4l->ctrl_handler; -+ struct max9x_pdata *pdata = dev->platform_data; -+ int ret; -+ -+ mutex_init(&v4l->lock); -+ -+ v4l2_i2c_subdev_init(sd, client, &max9x_sd_ops); -+ snprintf(sd->name, sizeof(sd->name), "%s %s", client->name, pdata->suffix); -+ -+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; -+ sd->internal_ops = &max9x_sd_internal_ops; -+ sd->entity.function = MEDIA_ENT_F_VID_MUX; -+ -+ v4l->num_pads = common->num_csi_links + common->num_serial_links; -+ v4l->pads = devm_kzalloc(dev, v4l->num_pads * sizeof(*v4l->pads), GFP_KERNEL); -+ v4l->ffmts = devm_kzalloc(dev, v4l->num_pads * sizeof(*v4l->ffmts), GFP_KERNEL); -+ -+ //change sink/source turn -+ for (unsigned int p = 0; p < v4l->num_pads; p++) { -+ struct media_pad *pad = &v4l->pads[p]; -+ -+ if (p < common->num_csi_links) { -+ unsigned int c = p; -+ -+ dev_dbg(dev, "pad %d/%d -> csi %d", p, v4l->num_pads, c); -+ -+ pad->flags = (common->type == MAX9X_SERIALIZER) ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; -+ } else if (p >= common->num_csi_links) { -+ unsigned int s = p - common->num_csi_links; -+ -+ dev_dbg(dev, "pad %d/%d -> serial %d", p, v4l->num_pads, s); -+ -+ pad->flags = (common->type == MAX9X_SERIALIZER) ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; -+ } -+ } -+ -+ ret = v4l2_ctrl_handler_init(ctrl_handler, ARRAY_SIZE(max9x_v4l2_controls)); -+ if (ret) { -+ dev_err(dev, "Failed to init V4L2 controls: %d", ret); -+ return ret; -+ } -+ -+ for (int i = 0; i < ARRAY_SIZE(max9x_v4l2_controls); i++) { -+ struct v4l2_ctrl_config *ctrl_config = &max9x_v4l2_controls[i]; -+ struct v4l2_ctrl *ctrl; -+ -+ if (ctrl_config->id == V4L2_CID_LINK_FREQ) { -+ unsigned int link_freq_n; -+ unsigned int csi_link_id; -+ -+ for (link_freq_n = 0; link_freq_n < ARRAY_SIZE(max9x_op_sys_clock); link_freq_n++) { -+ unsigned int link_freq = MAX9X_LINK_FREQ_HZ_TO_MBPS(max9x_op_sys_clock[link_freq_n]); -+ -+ for (csi_link_id = 0; csi_link_id < common->num_csi_links; csi_link_id++) { -+ if (!common->csi_link[csi_link_id].enabled) -+ continue; -+ if (common->csi_link[csi_link_id].config.freq_mhz == link_freq) { -+ ctrl_config->def = link_freq_n; -+ break; -+ } -+ } -+ } -+ } -+ -+ ctrl = v4l2_ctrl_new_custom(ctrl_handler, ctrl_config, common); -+ if (!ctrl) { -+ ret = ctrl_handler->error; -+ dev_err(dev, "Failed to create V4L2 control %s: %d", ctrl_config->name, ret); -+ goto probe_error_v4l2_ctrl_handler_free; -+ } -+ } -+ -+ v4l->link_freq = v4l2_ctrl_find(ctrl_handler, V4L2_CID_LINK_FREQ); -+ v4l->sd.ctrl_handler = ctrl_handler; -+ -+ ret = media_entity_pads_init(&v4l->sd.entity, v4l->num_pads, v4l->pads); -+ if (ret) { -+ dev_err(dev, "Failed to init media entity: %d", ret); -+ goto probe_error_v4l2_ctrl_handler_free; -+ } -+ -+ ret = v4l2_subdev_init_finalize(&v4l->sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev: %d\n", ret); -+ media_entity_cleanup(&v4l->sd.entity); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ ret = v4l2_async_register_subdev(&v4l->sd); -+ if (ret) { -+ dev_err(dev, "v4l register failed: %d", ret); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ return 0; -+ -+probe_error_media_entity_cleanup: -+ media_entity_cleanup(&v4l->sd.entity); -+probe_error_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(ctrl_handler); -+ return ret; -+} -+ -+/** -+ * max9x_enable_serial_link() - Enable power and logic to link. -+ */ -+int max9x_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct max9x_serdes_serial_link *serial_link; -+ struct device *dev = common->dev; -+ int ret; -+ -+ serial_link = &common->serial_link[link_id]; -+ -+ if (!serial_link->regulator_enabled) { -+ if (!IS_ERR_OR_NULL(serial_link->poc_regulator)) { -+ ret = regulator_enable(serial_link->poc_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_POC_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ -+ serial_link->regulator_enabled = true; -+ } -+ -+ if (common->serial_link_ops && common->serial_link_ops->enable) -+ return common->serial_link_ops->enable(common, link_id); -+ -+ return 0; -+} -+ -+/** -+ * max9x_disable_serial_link() - Disable power and logic to link. -+ */ -+int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct max9x_serdes_serial_link *serial_link; -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (link_id >= common->num_serial_links) -+ return 0; -+ -+ serial_link = &common->serial_link[link_id]; -+ -+ if (serial_link->regulator_enabled) { -+ if (common->serial_link_ops && common->serial_link_ops->disable) -+ common->serial_link_ops->disable(common, link_id); -+ -+ serial_link->regulator_enabled = false; -+ -+ if (!IS_ERR_OR_NULL(serial_link->poc_regulator)) { -+ ret = regulator_disable(serial_link->poc_regulator); -+ -+ if (ret) { -+ dev_err(dev, "Failed to disable %s", MAX9X_POC_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9x_sysfs_create_get_link() - Creates a sysfs virtual file to check link lock status -+ */ -+int max9x_sysfs_create_get_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ char *attr_name; -+ -+ if (common->serial_link_ops && common->serial_link_ops->get_locked) { -+ struct device_attribute *link_lock_status = -+ devm_kzalloc(dev, sizeof(struct device_attribute), GFP_KERNEL); -+ -+ if (!link_lock_status) { -+ dev_err(dev, "Failed to allocate memory for link lock status"); -+ return -ENOMEM; -+ } -+ -+ attr_name = (char *)devm_kzalloc(dev, sizeof(char) * ATTR_NAME_LEN, GFP_KERNEL); -+ if (!attr_name) { -+ dev_err(dev, "Failed to allocate memory link lock attribute name"); -+ return -ENOMEM; -+ } -+ -+ ret = snprintf(attr_name, ATTR_NAME_LEN, "link-lock_%d", link_id); -+ if (ret < 0) -+ return ret; -+ -+ link_lock_status->attr.name = attr_name; -+ link_lock_status->attr.mode = ATTR_READ_ONLY; -+ link_lock_status->show = max9x_link_status_show; -+ -+ ret = device_create_file(dev, link_lock_status); -+ if (ret < 0) -+ return ret; -+ -+ common->serial_link[link_id].link_lock_status = link_lock_status; -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9x_sysfs_destroy_get_link() - Destroys the sysfs device attribute for link lock status -+ */ -+static void max9x_sysfs_destroy_get_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ -+ if (common->serial_link[link_id].link_lock_status) -+ device_remove_file(dev, common->serial_link[link_id].link_lock_status); -+} -+ -+/** -+ * max9x_enable_line_faults() - Enables all the line fault monitors using the device tree -+ */ -+int max9x_enable_line_faults(struct max9x_common *common) -+{ -+ return 0; -+} -+ -+/** -+ * max9x_disable_line_faults() - Disables all currently stored line fault monitors -+ */ -+int max9x_disable_line_faults(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ int line; -+ int final_ret = 0; -+ -+ if (common->line_fault && -+ common->line_fault_ops && -+ common->line_fault_ops->disable) { -+ -+ for (line = 0; line < common->num_line_faults; line++) { -+ ret = common->line_fault_ops->disable(common, line); -+ -+ if (ret) { -+ dev_err(dev, "Failed to disable line fault %d", line); -+ final_ret = ret; -+ } -+ -+ common->line_fault[line].enabled = false; -+ } -+ } -+ -+ return final_ret; -+} -+ -+static void max9x_sysfs_destroy_line_fault_status(struct max9x_common *common, unsigned int line) -+{ -+ struct device *dev = common->dev; -+ -+ if (common->line_fault[line].line_fault_status) -+ device_remove_file(dev, common->line_fault[line].line_fault_status); -+} -+ -+static int max9x_parse_pdata(struct max9x_common *common, struct max9x_pdata *pdata) -+{ -+ int ret; -+ -+ for (int i = 0; i < pdata->num_serial_links; i++) { -+ ret = max9x_parse_serial_link_pdata(common, &pdata->serial_links[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_video_pipes; i++) { -+ ret = max9x_parse_video_pipe_pdata(common, &pdata->video_pipes[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_csi_links; i++) { -+ ret = max9x_parse_csi_link_pdata(common, &pdata->csi_links[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_subdevs; i++) { -+ ret = max9x_parse_subdev_pdata(common, &pdata->subdevs[i]); -+ if (ret) -+ goto err; -+ } -+ -+ common->external_refclk_enable = pdata->external_refclk_enable; -+ -+err: -+ return ret; -+} -+ -+static int max9x_parse_serial_link_pdata(struct max9x_common *common, -+ struct max9x_serial_link_pdata *serial_link_pdata) -+{ -+ struct device *dev = common->dev; -+ unsigned int serial_link_id = serial_link_pdata->link_id; -+ -+ if (serial_link_id > common->num_serial_links) { -+ dev_err(dev, "Serial link pdata: Invalid link id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[serial_link_id]; -+ -+ serial_link->enabled = true; -+ -+ serial_link->config.link_type = serial_link_pdata->link_type; -+ serial_link->config.rx_freq_mhz = serial_link_pdata->rx_freq_mhz; -+ serial_link->config.tx_freq_mhz = serial_link_pdata->tx_freq_mhz; -+ -+ if (serial_link_pdata->poc_regulator[0] != 0) { -+ serial_link->poc_regulator = devm_regulator_get_optional(dev, serial_link_pdata->poc_regulator); -+ -+ if (PTR_ERR(serial_link->poc_regulator) == -EPROBE_DEFER) { -+ dev_dbg(dev, "POC regulator not ready deferring..."); -+ return -EPROBE_DEFER; -+ } -+ if (IS_ERR_OR_NULL(serial_link->poc_regulator)) -+ dev_dbg(dev, "Missing POC regulator"); -+ } -+ -+ return 0; -+} -+ -+static int max9x_parse_video_pipe_pdata(struct max9x_common *common, -+ struct max9x_video_pipe_pdata *video_pipe_pdata) -+{ -+ struct device *dev = common->dev; -+ unsigned int serial_link_id = video_pipe_pdata->serial_link_id; -+ unsigned int pipe_id = video_pipe_pdata->pipe_id; -+ unsigned int max_maps; -+ unsigned int max_data_types; -+ -+ if (serial_link_id > common->num_serial_links) { -+ dev_err(dev, "Video pdata: Invalid serial link id"); -+ return -EINVAL; -+ } -+ if (pipe_id > common->num_video_pipes) { -+ dev_err(dev, "Video pdata: Invalid video pipe id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_video_pipe *pipe = &common->video_pipe[pipe_id]; -+ -+ pipe->enabled = true; -+ -+ max_maps = 0; -+ max_data_types = 0; -+ if (common->common_ops && common->common_ops->max_elements) { -+ max_maps = common->common_ops->max_elements(common, MAX9X_MIPI_MAP); -+ max_data_types = common->common_ops->max_elements(common, MAX9X_DATA_TYPES); -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ if (video_pipe_pdata->num_maps > max_maps) { -+ dev_err(dev, "Video pdata: Too many maps"); -+ return -EINVAL; -+ } -+ -+ pipe->config.map = devm_kzalloc(dev, -+ video_pipe_pdata->num_maps * sizeof(*pipe->config.map), -+ GFP_KERNEL); -+ if (!pipe->config.map) { -+ dev_err(dev, "Video pdata: Failed t oallocate mmeory for maps"); -+ return -ENOMEM; -+ } -+ -+ pipe->config.src_link = video_pipe_pdata->serial_link_id; -+ pipe->config.src_pipe = video_pipe_pdata->src_pipe_id; -+ -+ for (unsigned int i = 0; i < video_pipe_pdata->num_maps; i++) { -+ struct max9x_serdes_mipi_map *map = &pipe->config.map[i]; -+ struct max9x_serdes_mipi_map *map_pdata = &video_pipe_pdata->maps[i]; -+ -+ map->src_vc = map_pdata->src_vc; -+ map->src_dt = map_pdata->src_dt; -+ map->dst_vc = map_pdata->dst_vc; -+ map->dst_dt = map_pdata->dst_dt; -+ map->dst_csi = map_pdata->dst_csi; -+ } -+ pipe->config.num_maps = video_pipe_pdata->num_maps; -+ } else if (common->type == MAX9X_SERIALIZER) { -+ if (video_pipe_pdata->num_data_types > max_data_types) { -+ dev_err(dev, "Video pdata: Too many maps"); -+ return -EINVAL; -+ } -+ -+ pipe->config.data_type = devm_kzalloc(dev, -+ video_pipe_pdata->num_data_types * sizeof(*pipe->config.map), -+ GFP_KERNEL); -+ if (!pipe->config.data_type) { -+ dev_err(dev, "Video pdata: Failed t oallocate mmeory for data types"); -+ return -ENOMEM; -+ } -+ -+ pipe->config.src_csi = video_pipe_pdata->src_csi_id; -+ -+ for (unsigned int i = 0; i < video_pipe_pdata->num_data_types; i++) { -+ pipe->config.data_type[i] = video_pipe_pdata->data_types[i]; -+ } -+ pipe->config.num_data_types = video_pipe_pdata->num_data_types; -+ } -+ -+ return 0; -+} -+ -+static int max9x_parse_csi_link_pdata(struct max9x_common *common, -+ struct max9x_csi_link_pdata *csi_link_pdata) -+{ -+ unsigned int csi_link_id = csi_link_pdata->link_id; -+ -+ if (csi_link_id > common->num_csi_links) { -+ dev_err(common->dev, "CSI link pdata: Invalid link id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_csi_link *csi_link = &common->csi_link[csi_link_id]; -+ -+ csi_link->enabled = true; -+ -+ csi_link->config.num_maps = csi_link_pdata->num_maps; -+ csi_link->config.map = -+ devm_kzalloc(common->dev, -+ csi_link->config.num_maps * sizeof(*csi_link->config.map), -+ GFP_KERNEL); -+ memcpy(csi_link->config.map, csi_link_pdata->maps, -+ csi_link->config.num_maps * sizeof(*csi_link->config.map)); -+ csi_link->config.num_lanes = csi_link_pdata->num_lanes; -+ csi_link->config.freq_mhz = csi_link_pdata->tx_rate_mbps; -+ csi_link->config.auto_init_deskew_enabled = csi_link_pdata->auto_initial_deskew; -+ -+ if (csi_link->config.auto_init_deskew_enabled) -+ csi_link->config.initial_deskew_width = csi_link_pdata->initial_deskew_width; -+ csi_link->config.auto_start = csi_link_pdata->auto_start; -+ -+ return 0; -+} -+ -+static int max9x_parse_subdev_pdata(struct max9x_common *common, -+ struct max9x_subdev_pdata *subdev_pdata) -+{ -+ unsigned int serial_link_id = subdev_pdata->serial_link_id; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[serial_link_id]; -+ -+ if (!serial_link->enabled) -+ return 0; -+ -+ serial_link->remote.pdata = subdev_pdata; -+ -+ return 0; -+} -+ -+ -+int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) -+{ -+ struct max9x_common *common = i2c_mux_priv(muxc); -+ int ret = 0; -+ unsigned long timeout = jiffies + msecs_to_jiffies(10000); -+ -+ if (unlikely(chan_id > common->num_serial_links)) -+ return -EINVAL; -+ -+ // Wait until link is free -+ do { -+ mutex_lock(&common->isolate_mutex); -+ if (common->selected_link < 0 || chan_id == common->selected_link) -+ break; -+ -+ mutex_unlock(&common->isolate_mutex); -+ -+ usleep_range(1000, 1050); -+ -+ if (time_is_before_jiffies(timeout)) { -+ dev_dbg(common->dev, "select %d TIMEOUT", chan_id); -+ return -ETIMEDOUT; -+ } -+ } while (1); -+ -+ common->selected_link = chan_id; -+ -+ if (common->serial_link_ops && common->serial_link_ops->select) -+ ret = common->serial_link_ops->select(common, chan_id); -+ -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) -+{ -+ struct max9x_common *common = i2c_mux_priv(muxc); -+ int ret = 0; -+ -+ if (unlikely(chan_id > common->num_serial_links)) -+ return -EINVAL; -+ -+ if (common->serial_link_ops && common->serial_link_ops->deselect) -+ ret = common->serial_link_ops->deselect(common, chan_id); -+ -+ mutex_lock(&common->isolate_mutex); -+ common->selected_link = -1; -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+int max9x_des_isolate_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret = 0; -+ unsigned long timeout = jiffies + msecs_to_jiffies(10000); -+ -+ if (link_id >= common->num_serial_links) { -+ dev_err(common->dev, "link_id %d outside of num_serial_links %d", link_id, common->num_serial_links); -+ return -EINVAL; -+ } -+ -+ dev_info(common->dev, "Isolate %d", link_id); -+ -+ do { -+ mutex_lock(&common->isolate_mutex); -+ if ((common->isolated_link < 0) && (common->selected_link < 0 || link_id == common->selected_link)) -+ break; -+ -+ if (common->isolated_link == link_id) { -+ dev_warn(common->dev, "Link %d is already isolated", link_id); -+ mutex_unlock(&common->isolate_mutex); -+ return -EINVAL; -+ } -+ mutex_unlock(&common->isolate_mutex); -+ -+ usleep_range(1000, 1050); -+ -+ if (time_is_before_jiffies(timeout)) -+ return -ETIMEDOUT; -+ } while (1); -+ -+ common->isolated_link = link_id; -+ if (common->serial_link_ops && common->serial_link_ops->isolate) -+ ret = common->serial_link_ops->isolate(common, link_id); -+ -+ mutex_unlock(&common->isolate_mutex); -+ dev_info(common->dev, "Isolate %d complete", link_id); -+ -+ return ret; -+} -+ -+int max9x_des_deisolate_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret = 0; -+ -+ if (link_id >= common->num_serial_links) -+ return -EINVAL; -+ -+ dev_info(common->dev, "Deisolate %d", link_id); -+ mutex_lock(&common->isolate_mutex); -+ -+ if (common->serial_link_ops && common->serial_link_ops->deisolate) -+ ret = common->serial_link_ops->deisolate(common, link_id); -+ -+ common->isolated_link = -1; -+ mutex_unlock(&common->isolate_mutex); -+ dev_info(common->dev, "Deisolate %d complete", link_id); -+ -+ return ret; -+} -+ -+ssize_t max9x_link_status_show(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct max9x_common *common = dev_get_drvdata(dev); -+ int link; -+ int ret; -+ bool locked; -+ -+ ret = sscanf(attr->attr.name, "link-lock_%d", &link); -+ if (ret < 0) -+ return ret; -+ -+ if (common->serial_link_ops && common->serial_link_ops->get_locked) { -+ ret = common->serial_link_ops->get_locked(common, link, &locked); -+ if (ret < 0) -+ return ret; -+ -+ return sysfs_emit(buf, "%d", !!locked); -+ } -+ -+ dev_err(dev, "get_locked not defined"); -+ return -EINVAL; -+} -+ -+int max9x_setup_translations(struct max9x_common *common) -+{ -+ int err; -+ -+ if (!common->translation_ops) -+ return 0; -+ -+ // Translation is currently only supported on serializer side -+ // (translating requests from SOC to remote sensor module) -+ if (common->type != MAX9X_SERIALIZER) -+ return 0; -+ -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ unsigned int virt_addr = subdev_pdata->board_info.addr; -+ unsigned int phys_addr = subdev_pdata->phys_addr ? subdev_pdata->phys_addr : virt_addr; -+ -+ if (virt_addr == phys_addr || common->translation_ops->add == NULL) -+ continue; -+ -+ // Only I2C 1 is supported at this time -+ err = common->translation_ops->add(common, 0, virt_addr, phys_addr); -+ if (err) -+ dev_warn(common->dev, "Failed to add translation for i2c address 0x%02x -> 0x%02x: %d", -+ virt_addr, phys_addr, err); -+ return err; -+ } -+ -+ return 0; -+} -+ -+int max9x_disable_translations(struct max9x_common *common) -+{ -+ int err; -+ -+ if (!common->translation_ops) -+ return 0; -+ -+ // Translation is currently only supported on serializer side -+ // (translating requests from SOC to remote sensor module) -+ if (common->type != MAX9X_SERIALIZER) -+ return 0; -+ -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ unsigned int virt_addr = subdev_pdata->board_info.addr; -+ unsigned int phys_addr = subdev_pdata->phys_addr ? subdev_pdata->phys_addr : virt_addr; -+ -+ if (virt_addr != phys_addr) { -+ if (common->translation_ops->add) { -+ // Only I2C 1 is supported at this time -+ err = common->translation_ops->remove(common, 0, virt_addr, phys_addr); -+ if (err) -+ dev_err(common->dev, "Failed to remove translation for i2c address 0x%02x -> 0x%02x: %d", -+ virt_addr, phys_addr, err); -+ return err; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Yan, Dongcheng "); -+MODULE_DESCRIPTION("Common logic for Maxim GMSL serializers & deserializers"); -diff --git a/drivers/media/i2c/max9x/serdes.h b/drivers/media/i2c/max9x/serdes.h -new file mode 100644 -index 000000000000..0d812c403f41 ---- /dev/null -+++ b/drivers/media/i2c/max9x/serdes.h -@@ -0,0 +1,404 @@ -+/* -+ * serdes.h -+ * -+ * Copyright (c) 2018-2020 D3 Engineering. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _SERDES_H_ -+#define _SERDES_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu6-isys.h" -+#include -+#include "max9x_pdata.h" -+ -+#define MAX9X_VDD_REGULATOR_NAME "vdd" -+#define MAX9X_POC_REGULATOR_NAME "poc" -+#define MAX9X_RESET_GPIO_NAME "reset" -+ -+/*Used for device attributes*/ -+#define ATTR_NAME_LEN (30) /* arbitrary number used to allocate an attribute */ -+#define ATTR_READ_ONLY (0444) -+ -+#define MAX9X_LINK_FREQ_MBPS_TO_HZ(mbps) (((unsigned long long)(mbps)*1000000ULL)/2ULL) -+#define MAX9X_LINK_FREQ_HZ_TO_MBPS(hz) (((unsigned long long)(hz)*2ULL)/1000000ULL) -+#define MAX9X_LINK_FREQ_MBPS_TO_REG(mbps) ((mbps)/100U) -+ -+#define MAX9X_FIELD_PREP(_mask, _val) \ -+ ({ \ -+ ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ -+ }) -+ -+#define TRY(err, expr) \ -+ do {\ -+ err = expr; \ -+ if (err) { \ -+ return err; \ -+ } \ -+ } while (0) -+ -+#define TRY_DEV_HERE(err, expr, dev) \ -+ do { \ -+ err = expr; \ -+ if (err) { \ -+ dev_err((dev), \ -+ "%s failed (%d) in %s() at %s:%d", #expr, \ -+ (err), __func__, __FILE__, __LINE__); \ -+ return err; \ -+ } \ -+ } while (0) -+ -+/* dt is defined in soft_dt_x, such as BACKTOP15(0x316).[5:0] */ -+#define MIPI_CSI2_TYPE_YUV422_8 0x1e -+#define MIPI_CSI2_TYPE_YUV422_10 0x1f -+#define MIPI_CSI2_TYPE_RGB565 0x22 -+#define MIPI_CSI2_TYPE_RGB888 0x24 -+#define MIPI_CSI2_TYPE_RAW8 0x2a -+#define MIPI_CSI2_TYPE_RAW10 0x2b -+#define MIPI_CSI2_TYPE_RAW12 0x2c -+ -+static inline int mbus_code_to_csi_dt(int code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return MIPI_CSI2_TYPE_RGB565; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return MIPI_CSI2_TYPE_RGB888; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return MIPI_CSI2_TYPE_YUV422_10; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ case MEDIA_BUS_FMT_VYUY8_1X16: -+ return MIPI_CSI2_TYPE_YUV422_8; -+ 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_TYPE_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_TYPE_RAW10; -+ case MEDIA_BUS_FMT_Y8_1X8: -+ 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_TYPE_RAW8; -+ default: -+ return -EINVAL; -+ } -+} -+ -+enum max9x_serdes_type { -+ MAX9X_DESERIALIZER = 0, -+ MAX9X_SERIALIZER = 1, -+}; -+ -+struct max9x_serdes_rate_table { -+ unsigned int val; -+ unsigned int freq_mhz; -+}; -+ -+struct max9x_serdes_csi_config { -+ struct max9x_serdes_phy_map *map; -+ unsigned int num_maps; -+ unsigned int num_lanes; -+ unsigned int freq_mhz; -+ unsigned int initial_deskew_width; -+ bool auto_init_deskew_enabled; -+ bool auto_start; -+}; -+ -+struct max9x_serdes_pipe_config { -+ // Deserializer -+ unsigned int src_link; -+ unsigned int src_pipe; -+ struct max9x_serdes_mipi_map *map; -+ unsigned int num_maps; -+ -+ // Serializer -+ //TODO: dst_link? -+ unsigned int src_csi; -+ unsigned int *data_type; -+ unsigned int num_data_types; -+ //TODO: MIPI VC filter mask -+ -+ /* -+ * Which bpp value to double i.e. dbl_pixel_bpp = 10 will -+ * cause 10 bit data to be transmitted together. -+ */ -+ //TODO: Multiple dbl bpp values -+ unsigned int dbl_pixel_bpp; -+ -+ /* -+ * Software override for bits per pixel. This is compatible with -+ * double bpp. These are the min/max values before padding -+ * and after doubling. Leave either 0 to disable. -+ */ -+ unsigned int soft_min_pixel_bpp; -+ unsigned int soft_max_pixel_bpp; -+ -+ //TODO: MIPI DT override -+}; -+ -+struct max9x_serdes_serial_config { -+ enum max9x_serdes_link_type link_type; -+ unsigned int rx_freq_mhz; // Previously forward_freq_mhz -+ unsigned int tx_freq_mhz; // Previously back_freq_mhz -+}; -+ -+struct max9x_serdes_v4l { -+ struct mutex lock; -+ struct max9x_common *common; -+ struct v4l2_subdev sd; -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct media_pad *pads; -+ int num_pads; -+ -+ struct v4l2_ctrl *link_freq; // CSI link frequency, used to determine ISP clock -+ struct v4l2_mbus_framefmt *ffmts; -+ int ref_count; -+}; -+ -+struct max9x_serdes_csi_link { -+ bool enabled; -+ unsigned int usecount; -+ struct max9x_serdes_csi_config config; -+}; -+ -+struct max9x_serdes_video_pipe { -+ bool enabled; -+ //TODO: Anything else need to be tracked? -+ struct max9x_serdes_pipe_config config; -+}; -+ -+struct max9x_serdes_serial_link { -+ bool enabled; -+ bool detected; -+ struct regulator *poc_regulator; /* feeds the serializer, imager */ -+ bool regulator_enabled; -+ struct { -+ struct i2c_client *client; -+ struct max9x_subdev_pdata *pdata; -+ } remote; -+ struct regmap *map; -+ struct max9x_serdes_serial_config config; -+ struct device_attribute *link_lock_status; -+}; -+ -+struct max9x_serdes_line_fault { -+ bool enabled; -+ struct device_attribute *line_fault_status; -+}; -+ -+struct max9x_common { -+ struct device *dev; -+ struct i2c_client *client; -+ struct regmap *map; -+ struct i2c_client *phys_client; -+ struct regmap *phys_map; -+ struct i2c_mux_core *muxc; -+ struct gpio_chip gpio_chip; -+ enum max9x_serdes_type type; -+ -+ struct gpio_desc *reset_gpio; -+ struct regulator *vdd_regulator; -+ bool regulator_enabled; -+ -+ struct max9x_common_ops *common_ops; -+ struct max9x_serial_link_ops *serial_link_ops; -+ struct max9x_csi_link_ops *csi_link_ops; -+ struct max9x_line_fault_ops *line_fault_ops; -+ struct max9x_translation_ops *translation_ops; -+ -+ struct max9x_serdes_csi_link *csi_link; -+ int num_csi_links; -+ -+ struct max9x_serdes_video_pipe *video_pipe; -+ int num_video_pipes; -+ -+ struct max9x_serdes_serial_link *serial_link; -+ int num_serial_links; -+ -+ struct max9x_serdes_line_fault *line_fault; -+ int num_line_faults; -+ -+ struct max9x_serdes_v4l v4l; -+ -+ struct mutex link_mutex; -+ struct mutex isolate_mutex; -+ int isolated_link; -+ int selected_link; -+ bool external_refclk_enable; -+}; -+ -+int max9x_serdes_mhz_to_rate(struct max9x_serdes_rate_table *table, int len, unsigned int freq_mhz); -+ -+struct max9x_common *max9x_phandle_to_common(struct device_node *node, const char *name); -+struct max9x_common *max9x_sd_to_common(struct v4l2_subdev *sd); -+struct max9x_common *max9x_client_to_common(struct i2c_client *client); -+ -+enum max9x_element_type { -+ MAX9X_SERIAL_LINK = 0, -+ MAX9X_VIDEO_PIPE, -+ MAX9X_MIPI_MAP, -+ MAX9X_CSI_LINK, -+ MAX9X_LINE_FAULT, -+ MAX9X_DATA_TYPES -+}; -+ -+struct max9x_common_ops { -+ int (*soft_reset)(struct max9x_common *common); -+ int (*enable)(struct max9x_common *common); -+ int (*disable)(struct max9x_common *common); -+ int (*max_elements)(struct max9x_common *common, enum max9x_element_type element); -+ int (*verify_devid)(struct max9x_common *common); -+ int (*remap_addr)(struct max9x_common *common); -+ int (*setup_gpio)(struct max9x_common *common); -+}; -+ -+struct max9x_serial_link_ops { -+ int (*enable)(struct max9x_common *common, unsigned int link); -+ int (*disable)(struct max9x_common *common, unsigned int link); -+ int (*select)(struct max9x_common *common, unsigned int link); -+ int (*deselect)(struct max9x_common *common, unsigned int link); -+ int (*get_locked)(struct max9x_common *common, unsigned int link, bool *locked); -+ int (*isolate)(struct max9x_common *common, unsigned int link); -+ int (*deisolate)(struct max9x_common *common, unsigned int link); -+}; -+ -+struct max9x_csi_link_ops { -+ int (*enable)(struct max9x_common *common, unsigned int link); -+ int (*disable)(struct max9x_common *common, unsigned int link); -+}; -+ -+struct max9x_translation_ops { -+ int (*add)(struct max9x_common *common, unsigned int i2c_id, unsigned int src, unsigned int dst); -+ int (*remove)(struct max9x_common *common, unsigned int i2c_id, unsigned int src, unsigned int dst); -+}; -+ -+struct max9x_line_fault_ops { -+ int (*enable)(struct max9x_common *common, unsigned int line); -+ int (*disable)(struct max9x_common *common, unsigned int line); -+ int (*get_status)(struct max9x_common *common, unsigned int line); -+}; -+ -+int max9x_common_init_i2c_client(struct max9x_common *common, -+ struct i2c_client *client, -+ const struct regmap_config *regmap_config, -+ struct max9x_common_ops *common_ops, -+ struct max9x_serial_link_ops *serial_link_ops, -+ struct max9x_csi_link_ops *csi_link_ops, -+ struct max9x_line_fault_ops *lf_ops); -+void max9x_destroy(struct max9x_common *common); -+int max9x_common_resume(struct max9x_common *common); -+int max9x_common_suspend(struct max9x_common *common); -+ -+/* -+ * Both DES and SER have their own i2c_mux_core: -+ * 1. SER's muxc does not provide select/deselect and has at most one link -+ * (which itself has all children of the SER/DES link) -+ * 2. DER's muxc has driver specific select/deselect, one or more links, -+ * each of which must have exactly one SER -+ * 3. SER's probe() performs re-numbering and address translation (if needed: -+ * i2c-mux means this is no longer strictly required) -+ * -+ * des -> i2c-mux -> link@0 -> ser -> sensor, eeprom, etc -+ * link@n -> ser -> sensor, eeprom, etc -+ * -+ * +-----+ -+ * | des | -+ * +--+--+ -+ * | -+ * | +--------+ -+ * +--+ link@0 | -+ * | +------+-+ -+ * | | +-----+ -+ * | +----+ ser | -+ * | +--+--+ -+ * | | -+ * | | +--------+ -+ * | +--+ link@0 | -+ * | +-----+--+ -+ * | | -+ * | | +--------+ -+ * | +----+ sensor | -+ * | | +--------+ -+ * | | -+ * | | +--------+ -+ * | +----+ eeprom | -+ * | +--------+ -+ * | -+ * | +--------+ -+ * +--+ link@n | -+ * +------+-+ -+ * | +-----+ -+ * +----+ ser | -+ * +--+--+ -+ * | -+ * | +--------+ -+ * +--+ link@0 | -+ * +-----+--+ -+ * | -+ * | +--------+ -+ * +----+ sensor | -+ * | +--------+ -+ * | -+ * | +--------+ -+ * +----+ eeprom | -+ * +--------+ -+ */ -+ -+// for pdata parse -+ -+#define SET_CSI_MAP(maps, i, _src_vc, _src_dt, _dst_vc, _dst_dt, _dst_csi) do { \ -+ (maps)[i].src_vc = _src_vc; \ -+ (maps)[i].src_dt = _src_dt; \ -+ (maps)[i].dst_vc = _dst_vc; \ -+ (maps)[i].dst_dt = _dst_dt; \ -+ (maps)[i].dst_csi = _dst_csi; \ -+} while (0) -+ -+#define SET_PHY_MAP(maps, i, _int_csi, _phy_ind, _phy_lane) do { \ -+ (maps)[i].int_csi = _int_csi; \ -+ (maps)[i].phy_ind = _phy_ind; \ -+ (maps)[i].phy_lane = _phy_lane; \ -+} while (0) -+ -+#endif // _SERDES_H_ -diff --git a/drivers/media/platform/intel/ipu6-acpi-common.c b/drivers/media/platform/intel/ipu6-acpi-common.c -new file mode 100644 -index 000000000000..07581ecb42b4 ---- /dev/null -+++ b/drivers/media/platform/intel/ipu6-acpi-common.c -@@ -0,0 +1,451 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016--2024 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#include -+#include -+#include "ipu6-isys.h" -+#include -+#include -+ -+static int get_integer_dsdt_data(struct device *dev, const u8 *dsdt, -+ int func, u64 *out) -+{ -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ union acpi_object *obj; -+ -+ obj = acpi_evaluate_dsm(dev_handle, (void *)dsdt, 0, func, NULL); -+ if (!obj) { -+ pr_err("IPU6 ACPI: Could not find corresponding DSM\n"); -+ return -ENODEV; -+ } -+ -+ if (obj->type != ACPI_TYPE_INTEGER) { -+ ACPI_FREE(obj); -+ return -ENODEV; -+ } -+ *out = obj->integer.value; -+ ACPI_FREE(obj); -+ return 0; -+} -+ -+static int read_acpi_block(struct device *dev, char *id, void *data, u32 size) -+{ -+ union acpi_object *obj; -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ int status; -+ u32 buffer_length; -+ -+ status = acpi_evaluate_object(dev_handle, id, NULL, &buffer); -+ if (!ACPI_SUCCESS(status)) { -+ pr_err("IPU6 ACPI: Could not find ACPI block with err %d", status); -+ return -ENODEV; -+ } -+ -+ obj = (union acpi_object *)buffer.pointer; -+ if (!obj || obj->type != ACPI_TYPE_BUFFER) { -+ pr_err("IPU6 ACPI: Could not read ACPI buffer\n"); -+ status = -ENODEV; -+ goto err; -+ } -+ -+ if (obj->buffer.length > size) { -+ pr_err("IPU6 ACPI: Given buffer is too small\n"); -+ status = -ENODEV; -+ goto err; -+ } -+ -+ memcpy(data, obj->buffer.pointer, min(size, obj->buffer.length)); -+ buffer_length = obj->buffer.length; -+ kfree(buffer.pointer); -+ -+ return buffer_length; -+err: -+ kfree(buffer.pointer); -+ return status; -+} -+ -+static int ipu_acpi_get_gpio_data(struct device *dev, struct ipu_gpio_info *gpio, int size, -+ u64 *gpio_num) -+{ -+ const u8 dsdt_cam_gpio[] = { -+ 0x40, 0x46, 0x23, 0x79, 0x10, 0x9e, 0xea, 0x4f, -+ 0xa5, 0xc1, 0xB5, 0xaa, 0x8b, 0x19, 0x75, 0x6f }; -+ -+ int i = 0, j = 0, retries = 0, loop = 0; -+ u64 num_gpio; -+ int rval; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_gpio, 1, &num_gpio); -+ -+ if (rval < 0) { -+ pr_err("IPU6 ACPI: Failed to get number of GPIO pins\n"); -+ return rval; -+ } -+ -+ pr_info("IPU6 APCI: Num of gpio found = %lld", num_gpio); -+ -+ if (num_gpio == 0) { -+ *gpio_num = num_gpio; -+ return rval; -+ } -+ -+ if (num_gpio > size) { -+ pr_err("IPU6 ACPI: Incorrect number of GPIO pins\n"); -+ return rval; -+ } -+ -+ /* repeat until all gpio pin is saved */ -+ while (i < num_gpio && loop <= LOOP_SIZE) { -+ u64 data; -+ struct gpio_desc *desc = NULL; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_gpio, i + 2, &data); -+ -+ if (rval < 0) { -+ pr_err("IPU6 ACPI: Failed to get GPIO data\n"); -+ return -ENODEV; -+ } -+ -+ gpio[i].func = (data & 0xff); -+ gpio[i].valid = FALSE; -+ -+ desc = gpiod_get_index(dev, NULL, i + retries, GPIOD_ASIS); -+ -+ if (!IS_ERR(desc)) { -+ unsigned short pin = desc_to_gpio(desc); -+ bool save = TRUE; -+ -+ /* always save first GPIO pin */ -+ if (i == 0) -+ save = TRUE; -+ -+ /* check subsequent GPIO pin for replicate */ -+ else { -+ for (j = 0; j <= i; j++) { -+ /* retry if same as previous pin */ -+ if (gpio[j].pin == pin) { -+ retries++; -+ save = FALSE; -+ gpiod_put(desc); -+ break; -+ } -+ } -+ } -+ -+ /* save into array */ -+ if (save == TRUE) { -+ pr_info("IPU6 ACPI: DSM: Pin number = %d. Func = %x.", pin, gpio[i].func); -+ gpio[i].pin = pin; -+ gpio[i].valid = TRUE; -+ gpiod_put(desc); -+ i++; -+ retries = 0; -+ } -+ } -+ loop++; -+ } -+ *gpio_num = num_gpio; -+ -+ return rval; -+} -+ -+// Callback to parse I2C resources from _CRS -+static acpi_status parse_i2c_resource(struct acpi_resource *res, void *data) -+{ -+ char **controller_path = data; -+ struct acpi_resource_i2c_serialbus *i2c; -+ -+ if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) -+ return AE_OK; -+ -+ i2c = &res->data.i2c_serial_bus; -+ if (i2c->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) -+ return AE_OK; -+ -+ *controller_path = kstrdup(i2c->resource_source.string_ptr, GFP_KERNEL); -+ return AE_CTRL_TERMINATE; -+} -+ -+// Get I2C bdf via _CRS -+static int ipu_get_i2c_bdf_crs(struct device *dev, struct ipu_i2c_info *info) -+{ -+ acpi_handle handle = ACPI_HANDLE(dev); -+ acpi_status status; -+ struct acpi_device *controller_adev; -+ struct pci_dev *pci_dev; -+ char *controller_path = NULL; -+ -+ if (!handle) { -+ dev_err(dev, "No ACPI handle\n"); -+ return -EINVAL; -+ } -+ -+ // Parse _CRS for I2C resource -+ status = acpi_walk_resources(handle, "_CRS", parse_i2c_resource, &controller_path); -+ if (ACPI_FAILURE(status) || !controller_path) { -+ dev_err(dev, "Failed to parse _CRS: %s\n", acpi_format_exception(status)); -+ return -EIO; -+ } -+ -+ // Get the I2C controller's ACPI device -+ status = acpi_get_handle(NULL, controller_path, &handle); -+ if (ACPI_FAILURE(status)) { -+ dev_err(dev, "Invalid controller path: %s\n", controller_path); -+ kfree(controller_path); -+ controller_path = NULL; -+ return -ENODEV; -+ } -+ -+ controller_adev = acpi_fetch_acpi_dev(handle); -+ if (!controller_adev) { -+ dev_err(dev, "No ACPI device for controller: %s\n", controller_path); -+ kfree(controller_path); -+ return -ENODEV; -+ } -+ -+ // Map to PCI device -+ pci_dev = acpi_get_pci_dev(handle); -+ if (!pci_dev) { -+ dev_err(dev, "No PCI device for controller: %s\n", controller_path); -+ kfree(controller_path); -+ return -ENODEV; -+ } -+ -+ snprintf(info->bdf, sizeof(info->bdf), "%04x:%02x:%02x.%x", -+ pci_domain_nr(pci_dev->bus), pci_dev->bus->number, -+ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); -+ pci_dev_put(pci_dev); -+ -+ return 0; -+} -+ -+static int ipu_acpi_get_i2c_info(struct device *dev, struct ipu_i2c_info *i2c, int size, u64 *num) -+{ -+ const u8 dsdt_cam_i2c[] = { -+ 0x49, 0x75, 0x25, 0x26, 0x71, 0x92, 0xA4, 0x4C, -+ 0xBB, 0x43, 0xC4, 0x89, 0x9D, 0x5A, 0x48, 0x81}; -+ -+ u64 num_i2c; -+ int i; -+ int rval; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, 1, &num_i2c); -+ -+ if (rval < 0) { -+ pr_err("IPU6 ACPI: Failed to get number of I2C\n"); -+ return -ENODEV; -+ } -+ for (i = 0; i < num_i2c && i < size; i++) { -+ u64 data; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, i + 2, -+ &data); -+ -+ if (rval < 0) { -+ pr_err("IPU6 ACPI: Failed to get I2C data\n"); -+ return -ENODEV; -+ } -+ -+ i2c[i].bus = ((data >> 24) & 0xff); -+ i2c[i].addr = (data >> 8) & 0xff; -+ rval = ipu_get_i2c_bdf_crs(dev, i2c + i); -+ if (rval < 0) { -+ pr_err("IPU6 ACPI: Failed to get i2c bdf\n"); -+ return -ENODEV; -+ } -+ pr_info("IPU6 ACPI: DSM: i2c bus %d addr %x bdf: %s\n", -+ i2c[i].bus, i2c[i].addr, i2c[i].bdf); -+ } -+ -+ *num = num_i2c; -+ -+ return 0; -+} -+ -+static int match_depend(struct device *dev, const void *data) -+{ -+ return (dev && dev->fwnode == data) ? 1 : 0; -+} -+ -+int ipu_acpi_get_control_logic_data(struct device *dev, -+ struct control_logic_data **ctl_data) -+{ -+ /* CLDB data */ -+ struct control_logic_data_packed ctl_logic_data; -+ int ret; -+ -+ ret = read_acpi_block(dev, "CLDB", &ctl_logic_data, -+ sizeof(ctl_logic_data)); -+ -+ if (ret < 0) { -+ pr_err("IPU6 ACPI: Fail to read from CLDB"); -+ return ret; -+ } -+ -+ (*ctl_data)->type = ctl_logic_data.controllogictype; -+ (*ctl_data)->id = ctl_logic_data.controllogicid; -+ (*ctl_data)->sku = ctl_logic_data.sensorcardsku; -+ -+ pr_info("IPU6 ACPI: CLDB: version %d clid %d cltype %d sku %d", -+ ctl_logic_data.version, -+ ctl_logic_data.controllogicid, -+ ctl_logic_data.controllogictype, -+ ctl_logic_data.sensorcardsku); -+ -+ /* GPIO data */ -+ ret = ipu_acpi_get_gpio_data(dev, (*ctl_data)->gpio, ARRAY_SIZE((*ctl_data)->gpio), -+ &((*ctl_data)->gpio_num)); -+ -+ if (ret < 0) { -+ dev_err(dev, "Failed to get GPIO data"); -+ return ret; -+ } -+ return 0; -+} -+ -+int ipu_acpi_get_dep_data(struct device *dev, -+ struct control_logic_data *ctl_data) -+{ -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ struct acpi_handle_list dep_devices; -+ acpi_status status; -+ int i; -+ int rval; -+ -+ ctl_data->completed = false; -+ -+ if (!acpi_has_method(dev_handle, "_DEP")) { -+ pr_err("IPU6 ACPI: %s does not have _DEP method", dev_name(dev)); -+ return 0; -+ } -+ -+ if (!acpi_evaluate_reference(dev_handle, "_DEP", NULL, &dep_devices)) { -+ pr_err("IPU6 ACPI: %s failed to evaluate _DEP", dev_name(dev)); -+ return -ENODEV; -+ } -+ -+ for (i = 0; i < dep_devices.count; i++) { -+ struct acpi_device *device; -+ struct acpi_device_info *info; -+ struct device *p_dev; -+ int match; -+ -+ status = acpi_get_object_info(dep_devices.handles[i], &info); -+ if (ACPI_FAILURE(status)) { -+ pr_err("IPU6 ACPI: %s error reading _DEP device info", dev_name(dev)); -+ continue; -+ } -+ -+ match = info->valid & ACPI_VALID_HID && -+ !strcmp(info->hardware_id.string, "INT3472"); -+ -+ kfree(info); -+ -+ if (!match) -+ continue; -+ -+ /* Process device IN3472 created by acpi */ -+ device = acpi_fetch_acpi_dev(dep_devices.handles[i]); -+ if (!device) { -+ pr_err("IPU6 ACPI: Failed to get ACPI device"); -+ return -ENODEV; -+ } -+ -+ pr_info("IPU6 ACPI: Dependent ACPI device found: %s\n", -+ dev_name(&device->dev)); -+ -+ p_dev = bus_find_device(&platform_bus_type, NULL, -+ &device->fwnode, match_depend); -+ -+ if (p_dev) { -+ pr_info("IPU6 ACPI: Dependent platform device found %s\n", -+ dev_name(p_dev)); -+ -+ /* obtain Control Logic Data from BIOS */ -+ rval = ipu_acpi_get_control_logic_data(p_dev, &ctl_data); -+ -+ if (rval) { -+ pr_err("IPU6 ACPI: Error getting Control Logic Data"); -+ return rval; -+ } -+ -+ ctl_data->completed = true; -+ } else -+ pr_err("IPU6 ACPI: Dependent platform device not found for %s\n", dev_name(dev)); -+ } -+ -+ if (!ctl_data->completed) { -+ ctl_data->type = CL_EMPTY; -+ pr_err("IPU6 APCI: No control logic data available"); -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_acpi_get_dep_data); -+ -+int ipu_acpi_get_cam_data(struct device *dev, -+ struct sensor_bios_data *sensor) -+{ -+ /* SSDB */ -+ struct sensor_bios_data_packed sensor_data; -+ int ret; -+ -+ ret = read_acpi_block(dev, "SSDB", &sensor_data, -+ sizeof(sensor_data)); -+ -+ if (ret < 0) { -+ pr_err("IPU6 ACPI: Fail to read from SSDB with err %d", ret); -+ return ret; -+ } -+ -+ /* Xshutdown is not part of the ssdb data */ -+ sensor->link = sensor_data.link; -+ sensor->lanes = sensor_data.lanes; -+ sensor->pprval = sensor_data.pprval; -+ sensor->pprunit = sensor_data.pprunit; -+ -+ pr_info("IPU6 ACPI: SSDB: name %s. link %d. lanes %d. pprval %d. pprunit %x", -+ dev_name(dev), sensor->link, sensor->lanes, sensor->pprval, sensor->pprunit); -+ -+ /* I2C */ -+ ret = ipu_acpi_get_i2c_info(dev, sensor->i2c, ARRAY_SIZE(sensor->i2c), &sensor->i2c_num); -+ -+ if (ret < 0) { -+ pr_err("IPU6 ACPI: Failed to get I2C info"); -+ return ret; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_acpi_get_cam_data); -+ -+MODULE_AUTHOR("Samu Onkalo "); -+MODULE_AUTHOR("Khai Wen Ng "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU6 ACPI support"); -+ -diff --git a/drivers/media/platform/intel/ipu6-acpi-pdata.c b/drivers/media/platform/intel/ipu6-acpi-pdata.c -new file mode 100644 -index 000000000000..f1f84ec445e5 ---- /dev/null -+++ b/drivers/media/platform/intel/ipu6-acpi-pdata.c -@@ -0,0 +1,832 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016--2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#include -+#include "ipu6-isys.h" -+#include -+#include -+ -+#define MIN_SENSOR_I2C 1 -+#define MIN_SERDES_I2C 3 -+#define SENSOR_2X_I2C 5 -+#define SUFFIX_BASE 96 -+#define MSG_LEN 128 -+ -+static struct ipu_isys_subdev_pdata *ptr_built_in_pdata; -+ -+void set_built_in_pdata(struct ipu_isys_subdev_pdata *pdata) -+{ -+ ptr_built_in_pdata = pdata; -+}; -+EXPORT_SYMBOL(set_built_in_pdata); -+ -+static struct ipu_isys_clk_mapping clk_mapping[] = { -+ { CLKDEV_INIT(NULL, NULL, NULL), NULL } -+}; -+ -+struct ipu_isys_subdev_pdata acpi_subdev_pdata = { -+ .subdevs = (struct ipu_isys_subdev_info *[]) { -+ NULL, NULL, NULL, NULL, NULL, -+ }, -+ .clk_map = clk_mapping, -+}; -+ -+struct serdes_local serdes_info; -+ -+struct ipu_isys_subdev_pdata *get_acpi_subdev_pdata(void) -+{ -+ struct ipu_isys_subdev_pdata *ptr; -+ ptr = &acpi_subdev_pdata; -+ return ptr; -+} -+EXPORT_SYMBOL(get_acpi_subdev_pdata); -+ -+static void print_serdes_sdinfo(struct serdes_subdev_info *sdinfo) -+{ -+ int j; -+ struct serdes_module_pdata *sd_mpdata = sdinfo->board_info.platform_data; -+ -+ if (!sd_mpdata) { -+ pr_err("Empty serdes module pdata"); -+ return; -+ } -+ -+ pr_debug("\t\trx_port \t\t= %d", sdinfo->rx_port); -+ pr_debug("\t\tphy_i2c_addr \t\t= 0x%x", sdinfo->phy_i2c_addr); -+ pr_debug("\t\tser_alias \t\t= 0x%x", sdinfo->ser_alias); -+ pr_debug("\t\tsuffix \t\t\t= %s", sdinfo->suffix); -+ pr_debug("\t\tboard_info.type \t= %s", sdinfo->board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sdinfo->board_info.addr); -+ -+ pr_debug("serdes board_info.platform_data"); -+ pr_debug("\t\tlanes \t\t\t= %d", sd_mpdata->lanes); -+ pr_debug("\t\tmodule_name \t\t= %s", sd_mpdata->module_name); -+ pr_debug("\t\tfsin \t\t\t= %d", sd_mpdata->fsin); -+ -+ if (serdes_info.gpio_powerup_seq > 0) -+ for (j = 0; j < serdes_info.gpio_powerup_seq; j++) -+ pr_debug("\t\t gpio_powerup_seq[%d] \t= %d", j, -+ (int)sd_mpdata->gpio_powerup_seq[j]); -+} -+ -+static void print_serdes_subdev(struct ipu_isys_subdev_info *sd) -+{ -+ struct serdes_platform_data *sd_pdata = sd->i2c.board_info.platform_data; -+ int i; -+ struct serdes_subdev_info *sd_sdinfo; -+ struct serdes_module_pdata *sd_mpdata; -+ -+ if (!sd_pdata) { -+ pr_err("Empty serdes subdev pdata"); -+ return; -+ } -+ -+ pr_debug("IPU6 ACPI: %s", __func__); -+ pr_debug("sd_csi2"); -+ pr_debug("\t\tnlanes \t\t\t= %d", sd->csi2->nlanes); -+ pr_debug("\t\tport \t\t\t= %d", sd->csi2->port); -+ -+ pr_debug("sd->i2c"); -+ pr_debug("\t\ti2c_adapter_bdf \t= %s", sd->i2c.i2c_adapter_bdf); -+ pr_debug("\t\tboard_info.type \t= %s", sd->i2c.board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sd->i2c.board_info.addr); -+ -+ pr_debug("sd->i2c.board_info.platform_data"); -+ pr_debug("\t\treset_gpio \t\t= %d", sd_pdata->reset_gpio); -+ pr_debug("\t\tFPD_gpio \t\t= %d", sd_pdata->FPD_gpio); -+ pr_debug("\t\tsuffix \t\t\t= %c", sd_pdata->suffix); -+ -+ for (i = 0; i < serdes_info.rx_port; i++) { -+ sd_sdinfo = &sd_pdata->subdev_info[i]; -+ sd_mpdata = sd_sdinfo->board_info.platform_data; -+ -+ if (!sd_mpdata) -+ continue; -+ -+ pr_debug("serdes subdev_info[%d]", i); -+ print_serdes_sdinfo(sd_sdinfo); -+ } -+ -+} -+ -+static void print_subdev(struct ipu_isys_subdev_info *sd) -+{ -+ struct sensor_platform_data *spdata = sd->i2c.board_info.platform_data; -+ int i; -+ -+ if (!spdata) { -+ pr_err("IPU6 ACPI: Empty sensor subdev"); -+ return; -+ } -+ -+ pr_debug("IPU6 ACPI: %s", __func__); -+ pr_debug("sd->csi2"); -+ pr_debug("\t\tnlanes \t\t\t= %d", sd->csi2->nlanes); -+ pr_debug("\t\tport \t\t\t= %d", sd->csi2->port); -+ -+ pr_debug("sd->i2c"); -+ pr_debug("\t\ti2c_adapter_bdf \t= %s", sd->i2c.i2c_adapter_bdf); -+ pr_debug("\t\tboard_info.type \t= %s", sd->i2c.board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sd->i2c.board_info.addr); -+ -+ pr_debug("sd->i2c.platform_data"); -+ pr_debug("\t\tport \t\t\t= %d", spdata->port); -+ pr_debug("\t\tlanes \t\t\t= %d", spdata->lanes); -+ pr_debug("\t\ti2c_slave_address \t= 0x%x", spdata->i2c_slave_address); -+ pr_debug("\t\tirq_pin \t\t= %d", spdata->irq_pin); -+ pr_debug("\t\tirq_pin_name \t\t= %s", spdata->irq_pin_name); -+ pr_debug("\t\tsuffix \t\t\t= %c", spdata->suffix); -+ pr_debug("\t\treset_pin \t\t= %d", spdata->reset_pin); -+ pr_debug("\t\tdetect_pin \t\t= %d", spdata->detect_pin); -+ -+ for (i = 0; i < IPU_SPDATA_GPIO_NUM; i++) -+ pr_debug("\t\tgpios[%d] \t\t= %d", i, spdata->gpios[i]); -+} -+ -+static void add_local_subdevs(struct ipu_isys_subdev_info *new_subdev_info) -+{ -+ struct ipu_isys_subdev_pdata *ptr_acpi_subdev_pdata = &acpi_subdev_pdata; -+ int i = 0; -+ -+ while (i < MAX_ACPI_SENSOR_NUM) { -+ if (!ptr_acpi_subdev_pdata->subdevs[i]) { -+ ptr_acpi_subdev_pdata->subdevs[i] = new_subdev_info; -+ ptr_acpi_subdev_pdata->subdevs[i+1] = NULL; -+ break; -+ } -+ i++; -+ } -+} -+ -+static void update_short(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned short *old_short, -+ unsigned int new_short) -+{ -+ if (*old_short != new_short) { -+ dev_info(dev, "%s 0x%x -> 0x%x", msg, *old_short, new_short); -+ *old_short = new_short; -+ } -+} -+ -+static void update_hex(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned int *old_hex, -+ unsigned int new_hex) -+{ -+ if (*old_hex != new_hex) { -+ dev_info(dev, "%s 0x%x -> 0x%x", msg, *old_hex, new_hex); -+ *old_hex = new_hex; -+ } -+} -+ -+static void update_int(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned int *old_int, -+ unsigned int new_int) -+{ -+ if (*old_int != new_int) { -+ dev_info(dev, "%s %d -> %d", msg, *old_int, new_int); -+ *old_int = new_int; -+ } -+} -+ -+static void update_inta(struct device *dev, -+ char msg[MSG_LEN], -+ int old_int[MSG_LEN], -+ int new_int[MSG_LEN], -+ size_t size) -+{ -+ int i; -+ -+ for (i = 0; i < size; i++) { -+ if (old_int[i] != new_int[i]) { -+ dev_info(dev, "%s %d -> %d", msg, old_int[i], new_int[i]); -+ old_int[i] = new_int[i]; -+ } -+ } -+} -+ -+static void update_str(struct device *dev, -+ char msg[MSG_LEN], -+ char old_str[MSG_LEN], -+ char new_str[MSG_LEN]) -+{ -+ if (strcmp(old_str, new_str) != 0) { -+ dev_info(dev, "%s %s -> %s", msg, old_str, new_str); -+ strscpy(old_str, new_str, strlen(new_str)+1); -+ } -+} -+ -+static void update_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_sd, -+ struct ipu_isys_subdev_info **old_sd) -+{ -+ struct sensor_platform_data *old_pdata = -+ (*old_sd)->i2c.board_info.platform_data; -+ -+ struct sensor_platform_data *new_pdata = -+ new_sd->i2c.board_info.platform_data; -+ -+ /* csi2 */ -+ update_int(dev, "CSI2 port", &(*old_sd)->csi2->port, new_sd->csi2->port); -+ update_int(dev, "CSI2 nlanes", &(*old_sd)->csi2->nlanes, new_sd->csi2->nlanes); -+ -+ /* i2c */ -+ update_short(dev, "I2C board_info addr", &(*old_sd)->i2c.board_info.addr, -+ new_sd->i2c.board_info.addr); -+ update_str(dev, "I2C i2c_adapter_bdf", (*old_sd)->i2c.i2c_adapter_bdf, -+ new_sd->i2c.i2c_adapter_bdf); -+ -+ /* platform data */ -+ update_int(dev, "pdata port", &(old_pdata)->port, new_pdata->port); -+ update_int(dev, "pdata lanes", &(old_pdata)->lanes, new_pdata->lanes); -+ update_hex(dev, "pdata I2C slave addr", &(old_pdata)->i2c_slave_address, -+ new_pdata->i2c_slave_address); -+ update_int(dev, "pdata irq_pin", &(old_pdata)->irq_pin, new_pdata->irq_pin); -+ update_str(dev, "pdata irq_pin_name", old_pdata->irq_pin_name, new_pdata->irq_pin_name); -+ update_int(dev, "pdata reset_pin", &(old_pdata)->reset_pin, new_pdata->reset_pin); -+ update_int(dev, "pdata detect_pin", &(old_pdata)->detect_pin, new_pdata->detect_pin); -+ update_inta(dev, "pdata gpios", old_pdata->gpios, new_pdata->gpios, IPU_SPDATA_GPIO_NUM); -+} -+ -+static void update_serdes_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_sd, -+ struct ipu_isys_subdev_info **old_sd) -+{ -+ struct serdes_platform_data *old_pdata = -+ (*old_sd)->i2c.board_info.platform_data; -+ -+ struct serdes_platform_data *new_pdata = -+ new_sd->i2c.board_info.platform_data; -+ -+ int i; -+ struct serdes_subdev_info *old_sdinfo, *new_sdinfo; -+ struct serdes_module_pdata *old_mpdata, *new_mpdata; -+ -+ /* csi2 */ -+ update_int(dev, "CSI2 port", &(*old_sd)->csi2->port, new_sd->csi2->port); -+ update_int(dev, "CSI2 nlanes", &(*old_sd)->csi2->nlanes, new_sd->csi2->nlanes); -+ -+ /* i2c */ -+ update_short(dev, "I2C board_info addr", &(*old_sd)->i2c.board_info.addr, -+ new_sd->i2c.board_info.addr); -+ update_str(dev, "I2C i2c_adapter_bdf", (*old_sd)->i2c.i2c_adapter_bdf, -+ new_sd->i2c.i2c_adapter_bdf); -+ -+ update_int(dev, "I2C Pdata reset_gpio", &old_pdata->reset_gpio, -+ new_pdata->reset_gpio); -+ update_int(dev, "I2C Pdata FPD_gpio", &old_pdata->FPD_gpio, new_pdata->FPD_gpio); -+ -+ /* platform data */ -+ for (i = 0; i < SERDES_MAX_PORT; i++) { -+ old_sdinfo = &old_pdata->subdev_info[i]; -+ old_mpdata = old_sdinfo->board_info.platform_data; -+ -+ new_sdinfo = &new_pdata->subdev_info[i]; -+ new_mpdata = new_sdinfo->board_info.platform_data; -+ -+ if (!strcmp(old_sdinfo->board_info.type, new_sdinfo->board_info.type) && -+ old_sdinfo->suffix == new_sdinfo->suffix) { -+ update_short(dev, "SdInfo port", &old_sdinfo->rx_port, -+ new_sdinfo->rx_port); -+ update_short(dev, "SdInfo ser_alias", &old_sdinfo->ser_alias, -+ new_sdinfo->ser_alias); -+ update_short(dev, "SdInfo board_info.addr", &old_sdinfo->board_info.addr, -+ new_sdinfo->board_info.addr); -+ -+ if (!strcmp(old_mpdata->module_name, new_mpdata->module_name)) { -+ update_int(dev, "mPdata lanes", &old_mpdata->lanes, -+ new_mpdata->lanes); -+ update_int(dev, "mPdata fsin", &old_mpdata->fsin, -+ new_mpdata->fsin); -+ update_inta(dev, "mPdata gpio_powerup_seq", -+ (int *)old_mpdata->gpio_powerup_seq, -+ (int *)new_mpdata->gpio_powerup_seq, -+ SERDES_MAX_GPIO_POWERUP_SEQ); -+ } -+ } -+ } -+} -+ -+static int compare_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_subdev, -+ struct ipu_isys_subdev_info *old_subdev, -+ enum connection_type connect) -+{ -+ /* check for ACPI HID in existing pdata */ -+ if (old_subdev->acpi_hid) { -+ /* compare with HID for User Custom */ -+ if (!strcmp(old_subdev->acpi_hid, dev_name(dev))) { -+ dev_info(dev, "Found matching sensor : %s", dev_name(dev)); -+ return 0; -+ } -+ } -+ /* compare sensor type */ -+ if (!strcmp(old_subdev->i2c.board_info.type, -+ new_subdev->i2c.board_info.type)) { -+ -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *old_pdata, *new_pdata; -+ -+ old_pdata = (struct sensor_platform_data *) -+ old_subdev->i2c.board_info.platform_data; -+ -+ new_pdata = (struct sensor_platform_data *) -+ new_subdev->i2c.board_info.platform_data; -+ -+ if (old_pdata->suffix == new_pdata->suffix) { -+ dev_info(dev, "Found matching sensor : %s %c", -+ old_subdev->i2c.board_info.type, -+ old_pdata->suffix); -+ return 0; -+ } -+ } else if (connect == TYPE_SERDES) { -+ struct serdes_platform_data *old_pdata, *new_pdata; -+ -+ old_pdata = (struct serdes_platform_data *) -+ old_subdev->i2c.board_info.platform_data; -+ -+ new_pdata = (struct serdes_platform_data *) -+ new_subdev->i2c.board_info.platform_data; -+ -+ if (old_pdata->suffix == new_pdata->suffix) { -+ dev_info(dev, "Found matching sensor : %s %c", -+ old_subdev->i2c.board_info.type, -+ old_pdata->suffix); -+ return 0; -+ } -+ } -+ } -+ return -1; -+} -+ -+static void update_pdata(struct device *dev, -+ struct ipu_isys_subdev_info *new_subdev, -+ enum connection_type connect) -+{ -+ struct ipu_isys_subdev_info *acpi_subdev; -+ bool found = false; -+ -+ acpi_subdev = new_subdev; -+ -+ /* update local ipu_isys_subdev_pdata */ -+ add_local_subdevs(acpi_subdev); -+ -+ /* found existing pdata */ -+ if (ptr_built_in_pdata) { -+ struct ipu_isys_subdev_info **subdevs, *sd_info; -+ -+ for (subdevs = ptr_built_in_pdata->subdevs; *subdevs; subdevs++) { -+ sd_info = *subdevs; -+ -+ /* found similar subdev in existing pdata*/ -+ if (!compare_subdev(dev, acpi_subdev, sd_info, connect)) { -+ /* print and update old subdev */ -+ if (connect == TYPE_DIRECT) { -+ dev_dbg(dev, "Old sensor subdev\n"); -+ print_subdev(sd_info); -+ update_subdev(dev, acpi_subdev, &sd_info); -+ dev_dbg(dev, "Updated subdev\n"); -+ print_subdev(sd_info); -+ } else if (connect == TYPE_SERDES) { -+ dev_dbg(dev, "Old serdes subdev\n"); -+ print_serdes_subdev(sd_info); -+ update_serdes_subdev(dev, acpi_subdev, &sd_info); -+ dev_dbg(dev, "Updated subdev\n"); -+ print_serdes_subdev(sd_info); -+ } -+ -+ /* stop once similar subdev updated */ -+ found = true; -+ break; -+ } -+ } -+ -+ /* no similar subdev found */ -+ if (!found) { -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *acpi_pdata; -+ -+ acpi_pdata = (struct sensor_platform_data *) -+ acpi_subdev->i2c.board_info.platform_data; -+ -+ dev_err(dev, "Pdata does not contain %s %c\n", -+ acpi_subdev->i2c.board_info.type, -+ acpi_pdata->suffix); -+ -+ /* print new subdev */ -+ print_subdev(acpi_subdev); -+ -+ } else { -+ struct serdes_platform_data *acpi_pdata; -+ -+ acpi_pdata = (struct serdes_platform_data *) -+ acpi_subdev->i2c.board_info.platform_data; -+ -+ dev_err(dev, "Pdata does not contain %s %c\n", -+ acpi_subdev->i2c.board_info.type, -+ acpi_pdata->suffix); -+ -+ print_serdes_subdev(acpi_subdev); -+ } -+ } -+ } -+ /* does not have existing pdata */ -+ else { -+ /* print new subdev */ -+ if (connect == TYPE_DIRECT) { -+ pr_debug("New sensor subdev\n"); -+ print_subdev(acpi_subdev); -+ } else { -+ pr_debug("New serdes subdev\n"); -+ print_serdes_subdev(acpi_subdev); -+ } -+ } -+ -+ /* update total num of sensor connected */ -+ if (connect == TYPE_SERDES) { -+ serdes_info.deser_num++; -+ } -+} -+ -+static void set_common_gpio(struct control_logic_data *ctl_data, -+ struct sensor_platform_data **pdata) -+{ -+ int i; -+ -+ /* TODO: consider remove specific naming such as irq_pin, and use gpios[] */ -+ (*pdata)->irq_pin = -1; -+ (*pdata)->reset_pin = -1; -+ (*pdata)->detect_pin = -1; -+ -+ (*pdata)->gpios[0] = -1; -+ (*pdata)->gpios[1] = 0; -+ (*pdata)->gpios[2] = 0; -+ (*pdata)->gpios[3] = 0; -+ -+ /* all sensors should have RESET GPIO */ -+ if (ctl_data->completed && ctl_data->gpio_num > 0) -+ for (i = 0; i < ctl_data->gpio_num; i++) -+ if (ctl_data->gpio[i].func != GPIO_RESET) -+ dev_err(ctl_data->dev, -+ "IPU6 ACPI: Invalid GPIO func: %d\n", -+ ctl_data->gpio[i].func); -+} -+ -+static int set_csi2(struct ipu_isys_subdev_info **sensor_sd, -+ unsigned int lanes, -+ unsigned int port) -+{ -+ struct ipu6_isys_csi2_config *csi2_config; -+ -+ csi2_config = kzalloc(sizeof(*csi2_config), GFP_KERNEL); -+ if (!csi2_config) -+ return -ENOMEM; -+ -+ csi2_config->nlanes = lanes; -+ csi2_config->port = port; -+ (*sensor_sd)->csi2 = csi2_config; -+ -+ return 0; -+} -+ -+static void set_i2c(struct ipu_isys_subdev_info **sensor_sd, -+ struct device *dev, -+ const char *sensor_name, -+ unsigned int addr, -+ const char *i2c_adapter_bdf) -+{ -+ dev_info(dev, "IPU6 ACPI: kernel I2C BDF: %s", i2c_adapter_bdf); -+ (*sensor_sd)->i2c.board_info.addr = addr; -+ strscpy((*sensor_sd)->i2c.board_info.type, sensor_name, I2C_NAME_SIZE); -+ strscpy((*sensor_sd)->i2c.i2c_adapter_bdf, i2c_adapter_bdf, -+ sizeof((*sensor_sd)->i2c.i2c_adapter_bdf)); -+} -+ -+static void set_serdes_sd_pdata(struct serdes_module_pdata **module_pdata, -+ const char *sensor_name, const char *hid_name, -+ unsigned int lanes) -+{ -+ /* general */ -+ (*module_pdata)->lanes = lanes; -+ strscpy((*module_pdata)->module_name, sensor_name, I2C_NAME_SIZE); -+} -+ -+#define PORT_NR 8 -+ -+static int set_serdes_subdev(struct ipu_isys_subdev_info **serdes_sd, -+ struct device *dev, -+ struct serdes_platform_data **pdata, -+ const char *sensor_name, -+ const char *hid_name, -+ unsigned int lanes, -+ unsigned int addr, -+ unsigned int subdev_num) -+{ -+ int i; -+ struct serdes_module_pdata *module_pdata[PORT_NR]; -+ struct serdes_subdev_info *serdes_sdinfo; -+ size_t subdev_size = subdev_num * sizeof(*serdes_sdinfo); -+ unsigned int port = (*pdata)->suffix - SUFFIX_BASE - 1; -+ -+ serdes_sdinfo = kzalloc(subdev_size, GFP_KERNEL); -+ if (!serdes_sdinfo) -+ return -ENOMEM; -+ -+ for (i = 0; i < subdev_num; i++) { -+ module_pdata[i] = kzalloc(sizeof(*module_pdata[i]), GFP_KERNEL); -+ if (!module_pdata[i]) { -+ kfree(serdes_sdinfo); -+ return -ENOMEM; -+ } -+ -+ set_serdes_sd_pdata(&module_pdata[i], sensor_name, hid_name, lanes); -+ -+ /* board info */ -+ strscpy(serdes_sdinfo[i].board_info.type, sensor_name, I2C_NAME_SIZE); -+ serdes_sdinfo[i].board_info.addr = serdes_info.sensor_map_addr + i; -+ -+ serdes_sdinfo[i].board_info.platform_data = module_pdata[i]; -+ -+ /* serdes_subdev_info */ -+ serdes_sdinfo[i].rx_port = i; -+ serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; -+ -+ serdes_sdinfo[i].phy_i2c_addr = serdes_info.phy_i2c_addr; -+ snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", -+ SUFFIX_BASE + i + 1, port); -+ } -+ -+ (*pdata)->subdev_info = serdes_sdinfo; -+ (*pdata)->subdev_num = subdev_num; -+ -+ return 0; -+} -+ -+static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, -+ struct device *dev, -+ const char *sensor_name, -+ const char *hid_name, -+ struct control_logic_data *ctl_data, -+ unsigned int port, -+ unsigned int lanes, -+ unsigned int addr, -+ unsigned int subdev_num, -+ unsigned int deser_lanes, -+ bool is_dummy, -+ enum connection_type connect) -+{ -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *pdata; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return -ENOMEM; -+ -+ pr_debug("IPU6 ACPI: %s - Direct connection", __func__); -+ /* use ascii */ -+ /* port for start from 0 */ -+ if (port >= 0) { -+ pdata->suffix = port + SUFFIX_BASE + 1; -+ pr_info("IPU6 ACPI: create %s on port %d", -+ sensor_name, port); -+ } else -+ dev_err(dev, "INVALID MIPI PORT"); -+ -+ pdata->port = port; -+ pdata->lanes = lanes; -+ pdata->i2c_slave_address = addr; -+ -+ /* gpio */ -+ set_common_gpio(ctl_data, &pdata); -+ -+ (*sensor_sd)->i2c.board_info.platform_data = pdata; -+ } else if (connect == TYPE_SERDES) { -+ struct serdes_platform_data *pdata; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return -ENOMEM; -+ -+ pr_debug("IPU6 ACPI: %s - Serdes connection", __func__); -+ -+ /* use ascii */ -+ if (port >= 0) { -+ pdata->suffix = port + SUFFIX_BASE + 1; -+ pr_info("IPU6 ACPI: create %s on mipi port %d", -+ sensor_name, port); -+ } else -+ pr_err("IPU6 ACPI: Invalid MIPI Port : %d", port); -+ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ if (!strcmp(sensor_name, ISX031_NAME)) -+ pdata->link_freq_mbps = 1600; -+#endif -+ -+ pdata->deser_nlanes = deser_lanes; -+ pdata->ser_nlanes = lanes; -+ set_serdes_subdev(sensor_sd, dev, &pdata, sensor_name, hid_name, lanes, addr, subdev_num); -+ -+ (*sensor_sd)->i2c.board_info.platform_data = pdata; -+ pdata->deser_board_info = &(*sensor_sd)->i2c.board_info; -+ } -+ -+ return 0; -+} -+ -+static void set_serdes_info(struct device *dev, const char *sensor_name, -+ const char *serdes_name, -+ struct sensor_bios_data *cam_data) -+{ -+ int i; -+ -+ serdes_info.deser_num = 0; -+ /* pprunit as num of sensor connected to deserializer */ -+ serdes_info.rx_port = cam_data->pprunit; -+ -+ /* i2c devices */ -+ serdes_info.i2c_num = cam_data->i2c_num; -+ -+ i = 1; -+ /* serializer mapped addr */ -+ serdes_info.ser_map_addr = cam_data->i2c[i++].addr; -+ /* sensor mapped addr */ -+ serdes_info.sensor_map_addr = cam_data->i2c[i++].addr; -+ -+ serdes_info.gpio_powerup_seq = 0; -+ -+ serdes_info.phy_i2c_addr = 0; -+ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ if (!strcmp(sensor_name, ISX031_NAME)) -+ serdes_info.phy_i2c_addr = ISX031_I2C_ADDRESS; -+#endif -+ -+} -+ -+static int populate_sensor_pdata(struct device *dev, -+ struct ipu_isys_subdev_info **sensor_sd, -+ struct sensor_bios_data *cam_data, -+ struct control_logic_data *ctl_data, -+ enum connection_type connect, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name) -+{ -+ int ret; -+ -+ if (connect == TYPE_DIRECT) { -+ /* sensor csi2 info */ -+ ret = set_csi2(sensor_sd, cam_data->lanes, cam_data->link); -+ if (ret) -+ return ret; -+ -+ /* sensor i2c info */ -+ if (cam_data->i2c_num == MIN_SENSOR_I2C) { -+ pr_debug("IPU6 ACPI: num of I2C device for Direct connection: %lld is Correct.", -+ cam_data->i2c_num); -+ set_i2c(sensor_sd, dev, sensor_name, cam_data->i2c[0].addr, cam_data->i2c[0].bdf); -+ } else { -+ pr_err("IPU6 ACPI: num of I2C device for Direct connection : %lld is Incorrect", -+ cam_data->i2c_num); -+ return -1; -+ } -+ -+ if (ctl_data->type != CL_DISCRETE) { -+ dev_err(dev, "IPU6 ACPI: Control Logic Type\n"); -+ dev_err(dev, "for %s: %d is Incorrect\n", -+ sensor_name, ctl_data->type); -+ return -EINVAL; -+ } -+ } else if (connect == TYPE_SERDES) { -+ /* serdes csi2 info. pprval as deserializer lane */ -+ ret = set_csi2(sensor_sd, cam_data->pprval, cam_data->link); -+ if (ret) -+ return ret; -+ -+ /* Use DISCRETE Control Logic or No Control Logic for serdes */ -+ if (ctl_data->type != CL_DISCRETE && ctl_data->type != CL_EMPTY) { -+ pr_err("IPU6 ACPI: Control Logic Type for serdes: %d is Incorrect", -+ ctl_data->type); -+ return -1; -+ } -+ -+ /* serdes i2c info */ -+ if (cam_data->i2c_num >= MIN_SERDES_I2C) { -+ pr_debug("IPU6 ACPI: num of I2C device for Serdes connection: %lld is Correct", -+ cam_data->i2c_num); -+ set_i2c(sensor_sd, dev, serdes_name, cam_data->i2c[0].addr, cam_data->i2c[0].bdf); -+ } else { -+ pr_err("IPU6 ACPI: num of I2C device for Serdes connection: %lld is Incorrect", -+ cam_data->i2c_num); -+ return -1; -+ } -+ -+ /* local serdes info */ -+ set_serdes_info(dev, sensor_name, serdes_name, cam_data); -+ } -+ -+ /* Use last I2C device */ -+ ret = set_pdata(sensor_sd, dev, sensor_name, hid_name, ctl_data, cam_data->link, -+ cam_data->lanes, cam_data->i2c[cam_data->i2c_num - 1].addr, -+ cam_data->pprunit, cam_data->pprval, false, connect); -+ -+ if (ret) -+ return ret; -+ -+ update_pdata(dev, *sensor_sd, connect); -+ -+ /* Lontium specific */ -+ -+ return 0; -+} -+ -+int get_sensor_pdata(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, size_t size, -+ enum connection_type connect, const char *sensor_name, -+ const char *serdes_name, const char *hid_name) -+{ -+ struct sensor_bios_data *cam_data; -+ struct control_logic_data *ctl_data; -+ struct ipu_isys_subdev_info *sensor_sd; -+ int rval; -+ -+ cam_data = kzalloc(sizeof(*cam_data), GFP_KERNEL); -+ if (!cam_data) -+ return -ENOMEM; -+ -+ cam_data->dev = dev; -+ -+ ctl_data = kzalloc(sizeof(*ctl_data), GFP_KERNEL); -+ if (!ctl_data) { -+ kfree(cam_data); -+ return -ENOMEM; -+ } -+ -+ ctl_data->dev = dev; -+ -+ sensor_sd = kzalloc(sizeof(*sensor_sd), GFP_KERNEL); -+ if (!sensor_sd) { -+ kfree(cam_data); -+ kfree(ctl_data); -+ return -ENOMEM; -+ } -+ -+ /* camera info */ -+ rval = ipu_acpi_get_cam_data(dev, cam_data); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ /* control logic info */ -+ rval = ipu_acpi_get_dep_data(dev, ctl_data); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ /* populate pdata */ -+ rval = populate_sensor_pdata(dev, &sensor_sd, cam_data, ctl_data, -+ connect, sensor_name, serdes_name, hid_name); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ dev->platform_data = sensor_sd; -+ -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+} -+EXPORT_SYMBOL(get_sensor_pdata); -+ -+MODULE_AUTHOR("Khai Wen, Ng "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU6 ACPI support"); -diff --git a/drivers/media/platform/intel/ipu6-acpi.c b/drivers/media/platform/intel/ipu6-acpi.c -new file mode 100644 -index 000000000000..beed2597d864 ---- /dev/null -+++ b/drivers/media/platform/intel/ipu6-acpi.c -@@ -0,0 +1,250 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016--2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu6-isys.h" -+#include "ipu6.h" -+ -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+static LIST_HEAD(devices); -+ -+static struct ipu_camera_module_data *add_device_to_list( -+ struct list_head *devices) -+{ -+ struct ipu_camera_module_data *cam_device; -+ -+ cam_device = kzalloc(sizeof(*cam_device), GFP_KERNEL); -+ if (!cam_device) -+ return NULL; -+ -+ list_add(&cam_device->list, devices); -+ return cam_device; -+} -+ -+static const struct ipu_acpi_devices supported_devices[] = { -+/* -+ * { "ACPI ID", sensor_name, get_sensor_pdata, NULL, 0, TYPE, serdes_name }, // Custom HID -+ */ -+#if IS_ENABLED(CONFIG_VIDEO_MAX9X) -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ { "INTC031M", ISX031_NAME, get_sensor_pdata, NULL, 0, TYPE_SERDES, "max9296" },// D3 ISX031 HID -+#endif -+#endif -+}; -+ -+static int get_table_index(const char *acpi_name) -+{ -+ unsigned int i; -+ for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { -+ if (!strncmp(supported_devices[i].hid_name, acpi_name, -+ strlen(supported_devices[i].hid_name))) -+ return i; -+ } -+ -+ return -ENODEV; -+} -+ -+/* List of ACPI devices what we can handle */ -+/* Must match with HID in BIOS option. Add new sensor if required */ -+static const struct acpi_device_id ipu_acpi_match[] = { -+/* -+ * { "AR0234A", 0 }, // Custom HID -+ */ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ { "INTC1031", 0 }, // ISX031 HID -+ { "INTC031M", 0 }, // D3CMC68N-115-084 ISX031 HID -+#endif -+ {}, -+}; -+ -+static int ipu_acpi_get_pdata(struct device *dev, int index) -+{ -+ struct ipu_camera_module_data *camdata; -+ int rval; -+ -+ if (index < 0) { -+ pr_err("Device is not in supported devices list\n"); -+ return -ENODEV; -+ } -+ -+ camdata = add_device_to_list(&devices); -+ if (!camdata) -+ return -ENOMEM; -+ -+ pr_info("IPU6 ACPI: Getting BIOS data for %s (%s)", -+ supported_devices[index].real_driver, dev_name(dev)); -+ -+ rval = supported_devices[index].get_platform_data( -+ dev, camdata, -+ supported_devices[index].priv_data, -+ supported_devices[index].priv_size, -+ supported_devices[index].connect, -+ supported_devices[index].real_driver, -+ supported_devices[index].serdes_name, -+ supported_devices[index].hid_name); -+ -+ if (rval) -+ return -EPROBE_DEFER; -+ -+ return 0; -+} -+ -+/* -+ * different acpi devices may have same HID, so acpi_dev_get_first_match_dev -+ * will always match device to simple fwnode. -+ */ -+static int ipu_acpi_test(struct device *dev, void *priv) -+{ -+ struct acpi_device *adev = NULL; -+ int rval; -+ int acpi_idx = get_table_index(dev_name(dev)); -+ -+ if (acpi_idx < 0) -+ return 0; -+ else -+ dev_info(dev, "IPU6 ACPI: ACPI device %s\n", dev_name(dev)); -+ -+ const char *target_hid = supported_devices[acpi_idx].hid_name; -+ -+ if (!ACPI_COMPANION(dev)) { -+ while ((adev = acpi_dev_get_next_match_dev(adev, target_hid, -+ NULL, -1))) { -+ if (adev->flags.reserved == 0) { -+ adev->flags.reserved = 1; -+ break; -+ } -+ acpi_dev_put(adev); -+ } -+ -+ if (!adev) { -+ dev_dbg(dev, "No ACPI device found for %s\n", target_hid); -+ return 0; -+ } else { -+ set_primary_fwnode(dev, &adev->fwnode); -+ dev_dbg(dev, "Assigned fwnode to %s\n", dev_name(dev)); -+ } -+ } -+ -+ if (ACPI_COMPANION(dev) != adev) { -+ dev_err(dev, "Failed to set ACPI companion for %s\n", -+ dev_name(dev)); -+ acpi_dev_put(adev); -+ return 0; -+ } -+ -+ acpi_dev_put(adev); -+ -+ rval = ipu_acpi_get_pdata(dev, acpi_idx); -+ if (rval) { -+ pr_err("IPU6 ACPI: Failed to process ACPI data"); -+ return rval; -+ } -+ -+ return 0; /* Continue iteration */ -+} -+ -+/* Scan all i2c devices and pick ones which we can handle */ -+ -+/* Try to get all IPU related devices mentioned in BIOS and all related information -+ * If there is existing ipu_isys_subdev_pdata, update the existing pdata -+ * If not, return a new generated existing pdata -+ */ -+ -+int ipu_get_acpi_devices(void *driver_data, -+ struct device *dev, -+ struct ipu_isys_subdev_pdata **spdata, -+ struct ipu_isys_subdev_pdata **built_in_pdata, -+ int (*fn) -+ (struct device *, void *, -+ struct ipu6_isys_csi2_config *csi2, -+ bool reprobe)) -+{ -+ int rval; -+ -+ if (!built_in_pdata) -+ dev_dbg(dev, "Built-in pdata not found"); -+ else { -+ dev_dbg(dev, "Built-in pdata found"); -+ set_built_in_pdata(*built_in_pdata); -+ } -+ -+ if ((!fn) || (!driver_data)) -+ return -ENODEV; -+ -+ rval = acpi_bus_for_each_dev(ipu_acpi_test, NULL); -+ if (rval < 0) -+ return rval; -+ -+ if (!built_in_pdata) { -+ dev_dbg(dev, "Return ACPI generated pdata"); -+ *spdata = get_acpi_subdev_pdata(); -+ } else -+ dev_dbg(dev, "Return updated built-in pdata"); -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_get_acpi_devices); -+ -+static int __init ipu_acpi_init(void) -+{ -+ set_built_in_pdata(NULL); -+ return 0; -+} -+ -+static void __exit ipu_acpi_exit(void) -+{ -+} -+ -+module_init(ipu_acpi_init); -+module_exit(ipu_acpi_exit); -+ -+MODULE_AUTHOR("Samu Onkalo "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU6 ACPI support"); -diff --git a/include/media/i2c/isx031.h b/include/media/i2c/isx031.h -new file mode 100644 -index 000000000000..e703e001db6b ---- /dev/null -+++ b/include/media/i2c/isx031.h -@@ -0,0 +1,24 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2014 - 2022 Intel Corporation */ -+ -+#ifndef __ISX031_H -+#define __ISX031_H -+ -+#include -+ -+#define ISX031_NAME "isx031" -+ -+#define ISX031_I2C_ADDRESS 0x1a -+ -+struct isx031_platform_data { -+ unsigned int port; -+ unsigned int lanes; -+ uint32_t i2c_slave_address; -+ int irq_pin; -+ unsigned int irq_pin_flags; -+ char irq_pin_name[16]; -+ char suffix; -+ int gpios[4]; -+}; -+ -+#endif /* __ISX031_H */ -diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h -new file mode 100644 -index 000000000000..3b3d5088f943 ---- /dev/null -+++ b/include/media/ipu-acpi-pdata.h -@@ -0,0 +1,111 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2023-2025 Intel Corporation */ -+ -+#include -+#include -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+#define CL_EMPTY 0 -+#define CL_DISCRETE 1 -+#define CL_LT 5 -+#define SERDES_MAX_PORT 4 -+#define SERDES_MAX_GPIO_POWERUP_SEQ 4 -+#define LOOP_SIZE 10 -+ -+int get_sensor_pdata(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, size_t size, -+ enum connection_type connect, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name); -+ -+struct ipu_isys_subdev_pdata *get_acpi_subdev_pdata(void); -+ -+struct sensor_platform_data { -+ unsigned int port; -+ unsigned int lanes; -+ uint32_t i2c_slave_address; -+ int irq_pin; -+ unsigned int irq_pin_flags; -+ char irq_pin_name[IPU_SPDATA_IRQ_PIN_NAME_LEN]; -+ int reset_pin; -+ int detect_pin; -+ char suffix; -+ int gpios[IPU_SPDATA_GPIO_NUM]; -+}; -+ -+struct serdes_platform_data { -+ unsigned int subdev_num; -+ struct serdes_subdev_info *subdev_info; -+ unsigned int reset_gpio; -+ unsigned int FPD_gpio; -+ char suffix; -+ unsigned int link_freq_mbps; -+ unsigned int deser_nlanes; -+ unsigned int ser_nlanes; -+ struct i2c_board_info *deser_board_info; -+}; -+ -+struct serdes_subdev_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ unsigned short rx_port; -+ unsigned short phy_i2c_addr; -+ unsigned short ser_alias; -+ char suffix[5]; /* suffix for subdevs */ -+}; -+ -+struct serdes_module_pdata { -+ unsigned short i2c_addr; -+ unsigned short i2c_adapter; -+ unsigned int lanes; -+ int xshutdown; -+ int fsin; -+ int reset; -+ char gpio_powerup_seq[SERDES_MAX_GPIO_POWERUP_SEQ]; -+ unsigned int module_flags; -+ char module_name[I2C_NAME_SIZE]; -+ char suffix; -+}; -+ -+struct serdes_local { -+ /* num of camera sensor connected to current mipi port */ -+ unsigned int rx_port; -+ -+ /* num of i2c addr for current ACPI device */ -+ unsigned int i2c_num; -+ -+ /* current sensor_addr */ -+ unsigned short sensor_addr; -+ -+ /* physical i2c addr */ -+ unsigned short phy_i2c_addr; -+ -+ /* last mapped addr */ -+ unsigned short sensor_map_addr; -+ -+ /* current serializer_addr */ -+ unsigned short ser_addr; -+ -+ /* last mapped addr */ -+ unsigned short ser_map_addr; -+ -+ /* 2nd group of mapped addr for 2x sensors */ -+ unsigned short sensor_map_addr_2; -+ unsigned short ser_map_addr_2; -+ -+ /* current gpio_powerup_seq */ -+ unsigned int gpio_powerup_seq; -+ -+ /* current module flag */ -+ unsigned int module_flags; -+ -+ /* counter for total camera sensor connected */ -+ unsigned int sensor_num; -+ -+ /* counter for total deser connected */ -+ unsigned int deser_num; -+}; -diff --git a/include/media/ipu-acpi.h b/include/media/ipu-acpi.h -new file mode 100644 -index 000000000000..e585ee842615 ---- /dev/null -+++ b/include/media/ipu-acpi.h -@@ -0,0 +1,222 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2016--2022 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#ifndef MEDIA_INTEL_IPU_ACPI_H -+#define MEDIA_INTEL_IPU_ACPI_H -+ -+#include -+ -+#define MAX_ACPI_SENSOR_NUM 4 -+#define MAX_ACPI_I2C_NUM 12 -+#define MAX_ACPI_GPIO_NUM 12 -+ -+#define GPIO_RESET 0x0 -+#define GPIO_POWER_EN 0xb -+#define GPIO_READY_STAT 0x13 -+#define GPIO_HDMI_DETECT 0x14 -+ -+void set_built_in_pdata(struct ipu_isys_subdev_pdata *pdata); -+ -+enum connection_type { -+ TYPE_DIRECT, -+ TYPE_SERDES -+}; -+ -+/* Data representation as it is in ACPI SSDB buffer */ -+struct sensor_bios_data_packed { -+ u8 version; -+ u8 sku; -+ u8 guid_csi2[16]; -+ u8 devfunction; -+ u8 bus; -+ u32 dphylinkenfuses; -+ u32 clockdiv; -+ u8 link; -+ u8 lanes; -+ u32 csiparams[10]; -+ u32 maxlanespeed; -+ u8 sensorcalibfileidx; -+ u8 sensorcalibfileidxInMBZ[3]; -+ u8 romtype; -+ u8 vcmtype; -+ u8 platforminfo; -+ u8 platformsubinfo; -+ u8 flash; -+ u8 privacyled; -+ u8 degree; -+ u8 mipilinkdefined; -+ u32 mclkspeed; -+ u8 controllogicid; -+ u8 mipidataformat; -+ u8 siliconversion; -+ u8 customerid; -+ u8 mclkport; -+ u8 pmicpos; -+ u8 voltagerail; -+ u8 pprval; -+ u8 pprunit; -+ u8 flashid; -+ u8 reserved2[8]; -+} __attribute__((__packed__)); -+ -+struct ipu_i2c_info { -+ unsigned short bus; -+ unsigned short addr; -+ char bdf[32]; -+}; -+ -+/* Fields needed by ipu driver */ -+/* Each I2C client can have 12 device */ -+struct sensor_bios_data { -+ struct device *dev; -+ u8 link; -+ u8 lanes; -+ u8 vcmtype; -+ u8 flash; -+ u8 degree; -+ u8 mclkport; -+ u32 mclkspeed; -+ u16 xshutdown; -+ u8 controllogicid; -+ u8 pprval; -+ u8 pprunit; -+ struct ipu_i2c_info i2c[MAX_ACPI_I2C_NUM]; -+ u64 i2c_num; -+}; -+ -+struct control_logic_data_packed { -+ u8 version; -+ u8 controllogictype; -+ u8 controllogicid; -+ u8 sensorcardsku; -+ u8 inputclk; -+ u8 platformid; -+ u8 subplatformid; -+ u8 customerid; -+ u8 wled1_maxflashcurrent; -+ u8 wled1_maxtorchcurrent; -+ u8 wled2_maxflashcurrent; -+ u8 wled2_maxtorchcurrent; -+ u8 wled1_type; -+ u8 wled2_type; -+ u8 pch_clk_src; -+ u8 reserved2[17]; -+} __attribute__((__packed__)); -+ -+struct ipu_gpio_info { -+ unsigned short init_state; -+ unsigned short pin; -+ unsigned short func; -+ bool valid; -+}; -+ -+struct ipu_irq_info { -+ int irq_pin; -+ char irq_pin_name[IPU_SPDATA_IRQ_PIN_NAME_LEN]; -+}; -+ -+/* Each I2C client linked to 1 set of CTL Logic */ -+struct control_logic_data { -+ struct device *dev; -+ u8 id; -+ u8 type; -+ u8 sku; -+ u64 gpio_num; -+ struct ipu_gpio_info gpio[MAX_ACPI_GPIO_NUM]; -+ bool completed; -+}; -+ -+int ipu_get_acpi_devices(void *driver_data, -+ struct device *dev, -+ struct ipu_isys_subdev_pdata **spdata, -+ struct ipu_isys_subdev_pdata **built_in_pdata, -+ int (*fn) -+ (struct device *, void *, -+ struct ipu6_isys_csi2_config *csi2, -+ bool reprobe)); -+ -+struct ipu_isys_subdev_pdata *get_built_in_pdata(void); -+ -+int ipu_acpi_get_cam_data(struct device *dev, -+ struct sensor_bios_data *sensor); -+ -+int ipu_acpi_get_dep_data(struct device *dev, -+ struct control_logic_data *ctl_data); -+ -+int ipu_acpi_get_control_logic_data(struct device *dev, -+ struct control_logic_data **ctl_data); -+ -+struct intel_ipu6_regulator { -+ char *src_dev_name; -+ char *src_rail; -+ char *dest_rail; -+}; -+ -+struct ipu_i2c_helper { -+ int (*fn)(struct device *dev, void *priv, -+ struct ipu6_isys_csi2_config *csi2, -+ bool reprobe); -+ void *driver_data; -+}; -+ -+struct ipu_i2c_new_dev { -+ struct list_head list; -+ struct i2c_board_info info; -+ unsigned short int bus; -+}; -+ -+struct ipu_camera_module_data { -+ struct list_head list; -+ struct ipu_isys_subdev_info sd; -+ struct ipu6_isys_csi2_config csi2; -+ unsigned int ext_clk; -+ void *pdata; /* Ptr to generated platform data*/ -+ void *priv; /* Private for specific subdevice */ -+}; -+ -+struct ipu_acpi_devices { -+ const char *hid_name; -+ const char *real_driver; -+ int (*get_platform_data)(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, -+ size_t size, -+ enum connection_type type, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name); -+ void *priv_data; -+ size_t priv_size; -+ enum connection_type connect; -+ const char *serdes_name; -+}; -+ -+#endif --- -2.43.0 - diff --git a/kernel_patches/patch_6.12_mainline/0057-fix-S4-issue-on-TWL.patch b/kernel_patches/patch_6.12_mainline/0057-fix-S4-issue-on-TWL.patch deleted file mode 100644 index 6aee2fe165e4..000000000000 --- a/kernel_patches/patch_6.12_mainline/0057-fix-S4-issue-on-TWL.patch +++ /dev/null @@ -1,37 +0,0 @@ -From c70b2c6806924750a1b8f2a45f9a0155ae8cbfa1 Mon Sep 17 00:00:00 2001 -From: zouxiaoh -Date: Wed, 2 Jul 2025 17:05:59 +0800 -Subject: [PATCH] fix S4 issue on TWL - -Signed-off-by: zouxiaoh ---- - drivers/media/i2c/max9x/max9296.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -index e3c41ff94..15f12b8e7 100644 ---- a/drivers/media/i2c/max9x/max9296.c -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -892,6 +892,11 @@ static int max9296_freeze(struct device *dev) - return max9296_suspend(dev); - } - -+static int max9296_thaw(struct device *dev) -+{ -+ return max9296_resume(dev); -+} -+ - static int max9296_restore(struct device *dev) - { - return max9296_resume(dev); -@@ -941,6 +946,7 @@ static const struct dev_pm_ops max9296_pm_ops = { - .resume = max9296_resume, - .freeze = max9296_freeze, - .restore = max9296_restore, -+ .thaw = max9296_thaw, - }; - - static struct i2c_device_id max9296_idtable[] = { --- -2.43.0 - diff --git a/kernel_patches/patch_6.12_mainline/0058-code-changes-for-link-frequency-and-sensor-physical-.patch b/kernel_patches/patch_6.12_mainline/0058-code-changes-for-link-frequency-and-sensor-physical-.patch deleted file mode 100644 index e04a29533fa6..000000000000 --- a/kernel_patches/patch_6.12_mainline/0058-code-changes-for-link-frequency-and-sensor-physical-.patch +++ /dev/null @@ -1,232 +0,0 @@ -From e004e22f20ab018a16db184da5e904bad29858f2 Mon Sep 17 00:00:00 2001 -From: zouxiaoh -Date: Wed, 2 Jul 2025 17:07:38 +0800 -Subject: [PATCH] code changes for link frequency and sensor physical - address - -Signed-off-by: zouxiaoh ---- - .../media/platform/intel/ipu6-acpi-common.c | 2 +- - .../media/platform/intel/ipu6-acpi-pdata.c | 41 +++++++++---------- - drivers/media/platform/intel/ipu6-acpi.c | 12 ++++-- - include/media/ipu-acpi-pdata.h | 4 +- - include/media/ipu-acpi.h | 8 +++- - 5 files changed, 38 insertions(+), 29 deletions(-) - -diff --git a/drivers/media/platform/intel/ipu6-acpi-common.c b/drivers/media/platform/intel/ipu6-acpi-common.c -index 07581ecb4..82d1f4755 100644 ---- a/drivers/media/platform/intel/ipu6-acpi-common.c -+++ b/drivers/media/platform/intel/ipu6-acpi-common.c -@@ -1,6 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0 - /* -- * Copyright (c) 2016--2024 Intel Corporation. -+ * Copyright (c) 2016-2024 Intel Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version -diff --git a/drivers/media/platform/intel/ipu6-acpi-pdata.c b/drivers/media/platform/intel/ipu6-acpi-pdata.c -index f1f84ec44..4fbae449d 100644 ---- a/drivers/media/platform/intel/ipu6-acpi-pdata.c -+++ b/drivers/media/platform/intel/ipu6-acpi-pdata.c -@@ -1,6 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0 - /* -- * Copyright (c) 2016--2025 Intel Corporation. -+ * Copyright (c) 2016-2025 Intel Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version -@@ -109,6 +109,10 @@ static void print_serdes_subdev(struct ipu_isys_subdev_info *sd) - pr_debug("\t\tFPD_gpio \t\t= %d", sd_pdata->FPD_gpio); - pr_debug("\t\tsuffix \t\t\t= %c", sd_pdata->suffix); - -+ pr_debug("\t\tlink_freq_mbps \t\t= %d", sd_pdata->link_freq_mbps); -+ pr_debug("\t\tdeser_nlanes \t\t= %d", sd_pdata->deser_nlanes); -+ pr_debug("\t\tser_nlanes \t\t= %d", sd_pdata->ser_nlanes); -+ - for (i = 0; i < serdes_info.rx_port; i++) { - sd_sdinfo = &sd_pdata->subdev_info[i]; - sd_mpdata = sd_sdinfo->board_info.platform_data; -@@ -592,7 +596,8 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, - unsigned int subdev_num, - unsigned int deser_lanes, - bool is_dummy, -- enum connection_type connect) -+ enum connection_type connect, -+ u32 link_freq) - { - if (connect == TYPE_DIRECT) { - struct sensor_platform_data *pdata; -@@ -636,11 +641,7 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, - } else - pr_err("IPU6 ACPI: Invalid MIPI Port : %d", port); - --#if IS_ENABLED(CONFIG_VIDEO_ISX031) -- if (!strcmp(sensor_name, ISX031_NAME)) -- pdata->link_freq_mbps = 1600; --#endif -- -+ pdata->link_freq_mbps = link_freq; - pdata->deser_nlanes = deser_lanes; - pdata->ser_nlanes = lanes; - set_serdes_subdev(sensor_sd, dev, &pdata, sensor_name, hid_name, lanes, addr, subdev_num); -@@ -654,7 +655,8 @@ static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, - - static void set_serdes_info(struct device *dev, const char *sensor_name, - const char *serdes_name, -- struct sensor_bios_data *cam_data) -+ struct sensor_bios_data *cam_data, -+ int sensor_physical_addr) - { - int i; - -@@ -673,13 +675,7 @@ static void set_serdes_info(struct device *dev, const char *sensor_name, - - serdes_info.gpio_powerup_seq = 0; - -- serdes_info.phy_i2c_addr = 0; -- --#if IS_ENABLED(CONFIG_VIDEO_ISX031) -- if (!strcmp(sensor_name, ISX031_NAME)) -- serdes_info.phy_i2c_addr = ISX031_I2C_ADDRESS; --#endif -- -+ serdes_info.phy_i2c_addr = sensor_physical_addr; - } - - static int populate_sensor_pdata(struct device *dev, -@@ -689,7 +685,9 @@ static int populate_sensor_pdata(struct device *dev, - enum connection_type connect, - const char *sensor_name, - const char *serdes_name, -- const char *hid_name) -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq) - { - int ret; - -@@ -741,14 +739,13 @@ static int populate_sensor_pdata(struct device *dev, - } - - /* local serdes info */ -- set_serdes_info(dev, sensor_name, serdes_name, cam_data); -+ set_serdes_info(dev, sensor_name, serdes_name, cam_data, sensor_physical_addr); - } - - /* Use last I2C device */ - ret = set_pdata(sensor_sd, dev, sensor_name, hid_name, ctl_data, cam_data->link, - cam_data->lanes, cam_data->i2c[cam_data->i2c_num - 1].addr, -- cam_data->pprunit, cam_data->pprval, false, connect); -- -+ cam_data->pprunit, cam_data->pprval, false, connect, link_freq); - if (ret) - return ret; - -@@ -763,7 +760,8 @@ int get_sensor_pdata(struct device *dev, - struct ipu_camera_module_data *data, - void *priv, size_t size, - enum connection_type connect, const char *sensor_name, -- const char *serdes_name, const char *hid_name) -+ const char *serdes_name, const char *hid_name, -+ int sensor_physical_addr, int link_freq) - { - struct sensor_bios_data *cam_data; - struct control_logic_data *ctl_data; -@@ -811,7 +809,8 @@ int get_sensor_pdata(struct device *dev, - - /* populate pdata */ - rval = populate_sensor_pdata(dev, &sensor_sd, cam_data, ctl_data, -- connect, sensor_name, serdes_name, hid_name); -+ connect, sensor_name, serdes_name, hid_name, -+ sensor_physical_addr, link_freq); - if (rval) { - kfree(sensor_sd); - kfree(cam_data); -diff --git a/drivers/media/platform/intel/ipu6-acpi.c b/drivers/media/platform/intel/ipu6-acpi.c -index beed2597d..b182f9eb3 100644 ---- a/drivers/media/platform/intel/ipu6-acpi.c -+++ b/drivers/media/platform/intel/ipu6-acpi.c -@@ -1,6 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0 - /* -- * Copyright (c) 2016--2025 Intel Corporation. -+ * Copyright (c) 2016-2025 Intel Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version -@@ -66,11 +66,13 @@ static struct ipu_camera_module_data *add_device_to_list( - - static const struct ipu_acpi_devices supported_devices[] = { - /* -- * { "ACPI ID", sensor_name, get_sensor_pdata, NULL, 0, TYPE, serdes_name }, // Custom HID -+ * { "ACPI ID", sensor_name, get_sensor_pdata, NULL, 0, TYPE, serdes_name, -+ * sensor_physical_addr, link_freq(mbps) }, // Custom HID - */ - #if IS_ENABLED(CONFIG_VIDEO_MAX9X) - #if IS_ENABLED(CONFIG_VIDEO_ISX031) -- { "INTC031M", ISX031_NAME, get_sensor_pdata, NULL, 0, TYPE_SERDES, "max9296" },// D3 ISX031 HID -+ { "INTC031M", ISX031_NAME, get_sensor_pdata, NULL, 0, TYPE_SERDES, "max9296", -+ ISX031_I2C_ADDRESS, 1600 }, // D3 ISX031 HID - #endif - #endif - }; -@@ -124,7 +126,9 @@ static int ipu_acpi_get_pdata(struct device *dev, int index) - supported_devices[index].connect, - supported_devices[index].real_driver, - supported_devices[index].serdes_name, -- supported_devices[index].hid_name); -+ supported_devices[index].hid_name, -+ supported_devices[index].sensor_physical_addr, -+ supported_devices[index].link_freq); - - if (rval) - return -EPROBE_DEFER; -diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h -index 3b3d5088f..0889e01ab 100644 ---- a/include/media/ipu-acpi-pdata.h -+++ b/include/media/ipu-acpi-pdata.h -@@ -20,7 +20,9 @@ int get_sensor_pdata(struct device *dev, - enum connection_type connect, - const char *sensor_name, - const char *serdes_name, -- const char *hid_name); -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq); - - struct ipu_isys_subdev_pdata *get_acpi_subdev_pdata(void); - -diff --git a/include/media/ipu-acpi.h b/include/media/ipu-acpi.h -index e585ee842..a4d966d83 100644 ---- a/include/media/ipu-acpi.h -+++ b/include/media/ipu-acpi.h -@@ -1,6 +1,6 @@ - /* SPDX-License-Identifier: GPL-2.0 */ - /* -- * Copyright (c) 2016--2022 Intel Corporation. -+ * Copyright (c) 2016-2025 Intel Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version -@@ -212,11 +212,15 @@ struct ipu_acpi_devices { - enum connection_type type, - const char *sensor_name, - const char *serdes_name, -- const char *hid_name); -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq); - void *priv_data; - size_t priv_size; - enum connection_type connect; - const char *serdes_name; -+ int sensor_physical_addr; -+ int link_freq; /* in mbps */ - }; - - #endif --- -2.43.0 - diff --git a/kernel_patches/patch_6.12_mainline/README b/kernel_patches/patch_6.12_mainline/README index 71d05b50ed3b..3f211e75bf32 100644 --- a/kernel_patches/patch_6.12_mainline/README +++ b/kernel_patches/patch_6.12_mainline/README @@ -56,10 +56,6 @@ build kernel driver with community kernel version 6.11 but not iot kernel git am 0052-media-pci-refine-PDATA-related-config.patch git am 0053-kernel-align-ACPI-PDATA-and-ACPI-fwnode-build-for-EC.patch git am 0055-media-i2c-add-gmsl-isx031-support.patch - - git am 0056-media-i2c-add-support-for-isx031-max9296.patch - git am 0057-fix-S4-issue-on-TWL.patch - git am 0058-code-changes-for-link-frequency-and-sensor-physical-.patch /* end */ c. for USB device need to apply below 2 patches, they are merged to kernel 6.13.