[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250702132104.1537926-20-demonsingur@gmail.com>
Date: Wed, 2 Jul 2025 16:20:45 +0300
From: Cosmin Tanislav <demonsingur@...il.com>
To: Cosmin Tanislav <cosmin.tanislav@...log.com>,
Tomi Valkeinen <tomi.valkeinen+renesas@...asonboard.com>,
Mauro Carvalho Chehab <mchehab@...nel.org>,
Rob Herring <robh@...nel.org>,
Niklas Söderlund <niklas.soderlund@...natech.se>,
Julien Massot <julien.massot@...labora.com>,
Sakari Ailus <sakari.ailus@...ux.intel.com>,
Laurent Pinchart <laurent.pinchart@...asonboard.com>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Linus Walleij <linus.walleij@...aro.org>
Cc: linux-media@...r.kernel.org,
devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org,
linux-staging@...ts.linux.dev,
linux-gpio@...r.kernel.org,
Cosmin Tanislav <demonsingur@...il.com>
Subject: [PATCH v5 19/24] media: i2c: maxim-serdes: add MAX96724 driver
Add a new MAX96724 driver that also supports MAX96712, MAX96724F
and MAX96724R.
Integrate it with the common deserializer framework, while keeping
compatibility with existing usecases, avoiding code duplication, and
also enabling more features across all chips.
Signed-off-by: Cosmin Tanislav <demonsingur@...il.com>
---
drivers/media/i2c/maxim-serdes/Kconfig | 11 +
drivers/media/i2c/maxim-serdes/Makefile | 1 +
drivers/media/i2c/maxim-serdes/max96724.c | 1180 +++++++++++++++++++++
3 files changed, 1192 insertions(+)
create mode 100644 drivers/media/i2c/maxim-serdes/max96724.c
diff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig
index 648cb891eefe..2acd96cdbfa4 100644
--- a/drivers/media/i2c/maxim-serdes/Kconfig
+++ b/drivers/media/i2c/maxim-serdes/Kconfig
@@ -30,3 +30,14 @@ config VIDEO_MAX96717
To compile this driver as a module, choose M here: the module
will be called max96717.
+
+config VIDEO_MAX96724
+ tristate "Maxim MAX96724 Quad Deserializer support"
+ select VIDEO_MAXIM_SERDES
+ help
+ This driver supports the Maxim MAX96712, MAX96724, MAX96724F,
+ MAX96724R Quad Deserializers, which convert from four GMSL2
+ links to up to four MIPI D-PHY or C-PHY outputs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called max96724.
diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile
index 04abda6a5437..b6d5aebfaee1 100644
--- a/drivers/media/i2c/maxim-serdes/Makefile
+++ b/drivers/media/i2c/maxim-serdes/Makefile
@@ -2,3 +2,4 @@
max-serdes-objs := max_serdes.o max_ser.o max_des.o
obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o
obj-$(CONFIG_VIDEO_MAX96717) += max96717.o
+obj-$(CONFIG_VIDEO_MAX96724) += max96724.o
diff --git a/drivers/media/i2c/maxim-serdes/max96724.c b/drivers/media/i2c/maxim-serdes/max96724.c
new file mode 100644
index 000000000000..701c9445fbd1
--- /dev/null
+++ b/drivers/media/i2c/maxim-serdes/max96724.c
@@ -0,0 +1,1180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim MAX96724 Quad GMSL2 Deserializer Driver
+ *
+ * Copyright (C) 2025 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+
+#include "max_des.h"
+
+#define MAX96724_REG0 0x0
+
+#define MAX96724_REG6 0x6
+#define MAX96724_REG6_LINK_EN GENMASK(3, 0)
+
+#define MAX96724_DEBUG_EXTRA 0x9
+#define MAX96724_DEBUG_EXTRA_PCLK_SRC GENMASK(1, 0)
+#define MAX96724_DEBUG_EXTRA_PCLK_SRC_25MHZ 0b00
+#define MAX96724_DEBUG_EXTRA_PCLK_SRC_75MHZ 0b01
+#define MAX96724_DEBUG_EXTRA_PCLK_SRC_USE_PIPE 0b10
+
+#define MAX96724_REG26(x) (0x10 + (x) / 2)
+#define MAX96724_REG26_RX_RATE_PHY(x) (GENMASK(1, 0) << (4 * ((x) % 2)))
+#define MAX96724_REG26_RX_RATE_3GBPS 0b01
+#define MAX96724_REG26_RX_RATE_6GBPS 0b10
+
+#define MAX96724_PWR1 0x13
+#define MAX96724_PWR1_RESET_ALL BIT(6)
+
+#define MAX96724_CTRL1 0x18
+#define MAX96724_CTRL1_RESET_ONESHOT GENMASK(3, 0)
+
+#define MAX96724_VIDEO_PIPE_SEL(p) (0xf0 + (p) / 2)
+#define MAX96724_VIDEO_PIPE_SEL_STREAM(p) (GENMASK(1, 0) << (4 * ((p) % 2)))
+#define MAX96724_VIDEO_PIPE_SEL_LINK(p) (GENMASK(3, 2) << (4 * ((p) % 2)))
+
+#define MAX96724_VIDEO_PIPE_EN 0xf4
+#define MAX96724_VIDEO_PIPE_EN_MASK(p) BIT(p)
+#define MAX96724_VIDEO_PIPE_EN_STREAM_SEL_ALL BIT(4)
+
+#define MAX96724_VPRBS(p) (0x1dc + (p) * 0x20)
+#define MAX96724_VPRBS_VIDEO_LOCK BIT(0)
+#define MAX96724_VPRBS_PATGEN_CLK_SRC BIT(7)
+#define MAX96724_VPRBS_PATGEN_CLK_SRC_150MHZ 0b0
+#define MAX96724_VPRBS_PATGEN_CLK_SRC_375MHZ 0b1
+
+#define MAX96724_BACKTOP12 0x40b
+#define MAX96724_BACKTOP12_CSI_OUT_EN BIT(1)
+
+#define MAX96724_BACKTOP21(p) (0x414 + (p) / 4 * 0x20)
+#define MAX96724_BACKTOP21_BPP8DBL(p) BIT(4 + (p) % 4)
+
+#define MAX96724_BACKTOP22(x) (0x415 + (x) * 0x3)
+#define MAX96724_BACKTOP22_PHY_CSI_TX_DPLL GENMASK(4, 0)
+#define MAX96724_BACKTOP22_PHY_CSI_TX_DPLL_EN BIT(5)
+
+#define MAX96724_BACKTOP24(p) (0x417 + (p) / 4 * 0x20)
+#define MAX96724_BACKTOP24_BPP8DBL_MODE(p) BIT(4 + (p) % 4)
+
+#define MAX96724_BACKTOP30(p) (0x41d + (p) / 4 * 0x20)
+#define MAX96724_BACKTOP30_BPP10DBL3 BIT(4)
+#define MAX96724_BACKTOP30_BPP10DBL3_MODE BIT(5)
+
+#define MAX96724_BACKTOP31(p) (0x41e + (p) / 4 * 0x20)
+#define MAX96724_BACKTOP31_BPP10DBL2 BIT(6)
+#define MAX96724_BACKTOP31_BPP10DBL2_MODE BIT(7)
+
+#define MAX96724_BACKTOP32(p) (0x41f + (p) / 4 * 0x20)
+#define MAX96724_BACKTOP32_BPP12(p) BIT(p)
+#define MAX96724_BACKTOP32_BPP10DBL0 BIT(4)
+#define MAX96724_BACKTOP32_BPP10DBL0_MODE BIT(5)
+#define MAX96724_BACKTOP32_BPP10DBL1 BIT(6)
+#define MAX96724_BACKTOP32_BPP10DBL1_MODE BIT(7)
+
+#define MAX96724_MIPI_PHY0 0x8a0
+#define MAX96724_MIPI_PHY0_PHY_CONFIG GENMASK(4, 0)
+#define MAX96724_MIPI_PHY0_PHY_4X2 BIT(0)
+#define MAX96724_MIPI_PHY0_PHY_2X4 BIT(2)
+#define MAX96724_MIPI_PHY0_PHY_1X4A_2X2 BIT(3)
+#define MAX96724_MIPI_PHY0_PHY_1X4B_2X2 BIT(4)
+#define MAX96724_MIPI_PHY0_FORCE_CSI_OUT_EN BIT(7)
+
+#define MAX96724_MIPI_PHY2 0x8a2
+#define MAX96724_MIPI_PHY2_PHY_STDB_N_4(x) (GENMASK(5, 4) << ((x) / 2 * 2))
+#define MAX96724_MIPI_PHY2_PHY_STDB_N_2(x) (BIT(4 + (x)))
+
+#define MAX96724_MIPI_PHY3(x) (0x8a3 + (x) / 2)
+#define MAX96724_MIPI_PHY3_PHY_LANE_MAP_4 GENMASK(7, 0)
+#define MAX96724_MIPI_PHY3_PHY_LANE_MAP_2(x) (GENMASK(3, 0) << (4 * ((x) % 2)))
+
+#define MAX96724_MIPI_PHY5(x) (0x8a5 + (x) / 2)
+#define MAX96724_MIPI_PHY5_PHY_POL_MAP_4_0_1 GENMASK(1, 0)
+#define MAX96724_MIPI_PHY5_PHY_POL_MAP_4_2_3 GENMASK(4, 3)
+#define MAX96724_MIPI_PHY5_PHY_POL_MAP_4_CLK BIT(5)
+#define MAX96724_MIPI_PHY5_PHY_POL_MAP_2(x) (GENMASK(1, 0) << (3 * ((x) % 2)))
+#define MAX96724_MIPI_PHY5_PHY_POL_MAP_2_CLK(x) BIT(2 + 3 * ((x) % 2))
+
+#define MAX96724_MIPI_PHY13 0x8ad
+#define MAX96724_MIPI_PHY13_T_T3_PREBEGIN GENMASK(5, 0)
+#define MAX96724_MIPI_PHY13_T_T3_PREBEGIN_64X7 FIELD_PREP(MAX96724_MIPI_PHY13_T_T3_PREBEGIN, 63)
+
+#define MAX96724_MIPI_PHY14 0x8ae
+#define MAX96724_MIPI_PHY14_T_T3_PREP GENMASK(1, 0)
+#define MAX96724_MIPI_PHY14_T_T3_PREP_55NS FIELD_PREP(MAX96724_MIPI_PHY14_T_T3_PREP, 0b01)
+#define MAX96724_MIPI_PHY14_T_T3_POST GENMASK(6, 2)
+#define MAX96724_MIPI_PHY14_T_T3_POST_32X7 FIELD_PREP(MAX96724_MIPI_PHY14_T_T3_POST, 31)
+
+#define MAX96724_MIPI_CTRL_SEL 0x8ca
+#define MAX96724_MIPI_CTRL_SEL_MASK(p) (GENMASK(1, 0) << ((p) * 2))
+
+#define MAX96724_MIPI_PHY25(x) (0x8d0 + (x) / 2)
+#define MAX96724_MIPI_PHY25_CSI2_TX_PKT_CNT(x) (GENMASK(3, 0) << (4 * ((x) % 2)))
+
+#define MAX96724_MIPI_PHY27(x) (0x8d2 + (x) / 2)
+#define MAX96724_MIPI_PHY27_PHY_PKT_CNT(x) (GENMASK(3, 0) << (4 * ((x) % 2)))
+
+#define MAX96724_MIPI_TX3(x) (0x903 + (x) * 0x40)
+#define MAX96724_MIPI_TX3_DESKEW_INIT_8X32K FIELD_PREP(GENMASK(2, 0), 0b001)
+#define MAX96724_MIPI_TX3_DESKEW_INIT_AUTO BIT(7)
+
+#define MAX96724_MIPI_TX4(x) (0x904 + (x) * 0x40)
+#define MAX96724_MIPI_TX4_DESKEW_PER_2K FIELD_PREP(GENMASK(2, 0), 0b001)
+#define MAX96724_MIPI_TX4_DESKEW_PER_AUTO BIT(7)
+
+#define MAX96724_MIPI_TX10(x) (0x90a + (x) * 0x40)
+#define MAX96724_MIPI_TX10_CSI2_CPHY_EN BIT(5)
+#define MAX96724_MIPI_TX10_CSI2_LANE_CNT GENMASK(7, 6)
+
+#define MAX96724_MIPI_TX11(p) (0x90b + (p) * 0x40)
+#define MAX96724_MIPI_TX12(p) (0x90c + (p) * 0x40)
+
+#define MAX96724_MIPI_TX13(p, x) (0x90d + (p) * 0x40 + (x) * 0x2)
+#define MAX96724_MIPI_TX13_MAP_SRC_DT GENMASK(5, 0)
+#define MAX96724_MIPI_TX13_MAP_SRC_VC GENMASK(7, 6)
+
+#define MAX96724_MIPI_TX14(p, x) (0x90e + (p) * 0x40 + (x) * 0x2)
+#define MAX96724_MIPI_TX14_MAP_DST_DT GENMASK(5, 0)
+#define MAX96724_MIPI_TX14_MAP_DST_VC GENMASK(7, 6)
+
+#define MAX96724_MIPI_TX45(p, x) (0x92d + (p) * 0x40 + (x) / 4)
+#define MAX96724_MIPI_TX45_MAP_DPHY_DEST(x) (GENMASK(1, 0) << (2 * ((x) % 4)))
+
+#define MAX96724_MIPI_TX51(x) (0x933 + (x) * 0x40)
+#define MAX96724_MIPI_TX51_ALT_MEM_MAP_12 BIT(0)
+#define MAX96724_MIPI_TX51_ALT_MEM_MAP_8 BIT(1)
+#define MAX96724_MIPI_TX51_ALT_MEM_MAP_10 BIT(2)
+#define MAX96724_MIPI_TX51_ALT2_MEM_MAP_8 BIT(4)
+
+#define MAX96724_MIPI_TX54(x) (0x936 + (x) * 0x40)
+#define MAX96724_MIPI_TX54_TUN_EN BIT(0)
+
+#define MAX96724_MIPI_TX57(x) (0x939 + (x) * 0x40)
+#define MAX96724_MIPI_TX57_TUN_DEST GENMASK(5, 4)
+#define MAX96724_MIPI_TX57_DIS_AUTO_TUN_DET BIT(6)
+#define MAX96724_DET(p) BIT(p)
+
+#define MAX96724_PATGEN_0 0x1050
+#define MAX96724_PATGEN_0_VTG_MODE GENMASK(1, 0)
+#define MAX96724_PATGEN_0_VTG_MODE_FREE_RUNNING 0b11
+#define MAX96724_PATGEN_0_DE_INV BIT(2)
+#define MAX96724_PATGEN_0_HS_INV BIT(3)
+#define MAX96724_PATGEN_0_VS_INV BIT(4)
+#define MAX96724_PATGEN_0_GEN_DE BIT(5)
+#define MAX96724_PATGEN_0_GEN_HS BIT(6)
+#define MAX96724_PATGEN_0_GEN_VS BIT(7)
+
+#define MAX96724_PATGEN_1 0x1051
+#define MAX96724_PATGEN_1_PATGEN_MODE GENMASK(5, 4)
+#define MAX96724_PATGEN_1_PATGEN_MODE_DISABLED 0b00
+#define MAX96724_PATGEN_1_PATGEN_MODE_CHECKER 0b01
+#define MAX96724_PATGEN_1_PATGEN_MODE_GRADIENT 0b10
+
+#define MAX96724_VS_DLY_2 0x1052
+#define MAX96724_VS_HIGH_2 0x1055
+#define MAX96724_VS_LOW_2 0x1058
+#define MAX96724_V2H_2 0x105b
+#define MAX96724_HS_HIGH_1 0x105e
+#define MAX96724_HS_LOW_1 0x1060
+#define MAX96724_HS_CNT_1 0x1062
+#define MAX96724_V2D_2 0x1064
+#define MAX96724_DE_HIGH_1 0x1067
+#define MAX96724_DE_LOW_1 0x1069
+#define MAX96724_DE_CNT_1 0x106b
+#define MAX96724_GRAD_INCR 0x106d
+#define MAX96724_CHKR_COLOR_A_L 0x106e
+#define MAX96724_CHKR_COLOR_B_L 0x1071
+#define MAX96724_CHKR_RPT_A 0x1074
+#define MAX96724_CHKR_RPT_B 0x1075
+#define MAX96724_CHKR_ALT 0x1076
+
+#define MAX96724_DE_DET 0x11f0
+#define MAX96724_HS_DET 0x11f1
+#define MAX96724_VS_DET 0x11f2
+#define MAX96724_HS_POL 0x11f3
+#define MAX96724_VS_POL 0x11f4
+#define MAX96724_DET(p) BIT(p)
+
+#define MAX96724_DPLL_0(x) (0x1c00 + (x) * 0x100)
+#define MAX96724_DPLL_0_CONFIG_SOFT_RST_N BIT(0)
+
+#define MAX96724_PHY1_ALT_CLOCK 5
+
+static const struct regmap_config max96724_i2c_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x1f00,
+};
+
+struct max96724_priv {
+ struct max_des des;
+ const struct max96724_chip_info *info;
+
+ struct device *dev;
+ struct i2c_client *client;
+ struct regmap *regmap;
+
+ struct gpio_desc *gpiod_enable;
+};
+
+struct max96724_chip_info {
+ unsigned int versions;
+ unsigned int modes;
+ bool supports_pipe_stream_autoselect;
+ unsigned int num_pipes;
+
+ int (*set_pipe_phy)(struct max_des *des, struct max_des_pipe *pipe,
+ struct max_des_phy *phy);
+ int (*set_pipe_tunnel_phy)(struct max_des *des, struct max_des_pipe *pipe,
+ struct max_des_phy *phy);
+ int (*set_pipe_tunnel_enable)(struct max_des *des, struct max_des_pipe *pipe,
+ bool enable);
+};
+
+#define des_to_priv(_des) \
+ container_of(_des, struct max96724_priv, des)
+
+static int max96724_wait_for_device(struct max96724_priv *priv)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < 10; i++) {
+ unsigned int val;
+
+ ret = regmap_read(priv->regmap, MAX96724_REG0, &val);
+ if (!ret && val)
+ return 0;
+
+ msleep(100);
+
+ dev_err(priv->dev, "Retry %u waiting for deserializer: %d\n", i, ret);
+ }
+
+ return ret;
+}
+
+static int max96724_reset(struct max96724_priv *priv)
+{
+ int ret;
+
+ ret = max96724_wait_for_device(priv);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, MAX96724_PWR1,
+ MAX96724_PWR1_RESET_ALL,
+ FIELD_PREP(MAX96724_PWR1_RESET_ALL, 1));
+ if (ret)
+ return ret;
+
+ fsleep(10000);
+
+ return max96724_wait_for_device(priv);
+}
+
+static int max96724_reg_read(struct max_des *des, unsigned int reg,
+ unsigned int *val)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+
+ return regmap_read(priv->regmap, reg, val);
+}
+
+static int max96724_reg_write(struct max_des *des, unsigned int reg,
+ unsigned int val)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+
+ return regmap_write(priv->regmap, reg, val);
+}
+
+static unsigned int max96724_phy_id(struct max_des *des, struct max_des_phy *phy)
+{
+ unsigned int num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy);
+
+ /* PHY 1 is the master PHY when combining PHY 0 and PHY 1. */
+ if (phy->index == 0 && num_hw_data_lanes == 4)
+ return 1;
+
+ if (phy->index == 1 && !des->phys[1].enabled)
+ return 0;
+
+ return phy->index;
+}
+
+static int max96724_log_pipe_status(struct max_des *des,
+ struct max_des_pipe *pipe)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+ unsigned int val, mask;
+ int ret;
+
+ ret = regmap_read(priv->regmap, MAX96724_VPRBS(index), &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tvideo_lock: %u\n",
+ !!(val & MAX96724_VPRBS_VIDEO_LOCK));
+
+ mask = MAX96724_DET(index);
+
+ ret = regmap_read(priv->regmap, MAX96724_DE_DET, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tde_det: %u\n", !!(val & mask));
+
+ ret = regmap_read(priv->regmap, MAX96724_HS_DET, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\ths_det: %u\n", !!(val & mask));
+
+ ret = regmap_read(priv->regmap, MAX96724_VS_DET, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tvs_det: %u\n", !!(val & mask));
+
+ ret = regmap_read(priv->regmap, MAX96724_HS_POL, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\ths_pol: %u\n", !!(val & mask));
+
+ ret = regmap_read(priv->regmap, MAX96724_VS_POL, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tvs_pol: %u\n", !!(val & mask));
+
+ return 0;
+}
+
+static int max96724_log_phy_status(struct max_des *des,
+ struct max_des_phy *phy)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = max96724_phy_id(des, phy);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(priv->regmap, MAX96724_MIPI_PHY25(index), &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tcsi2_pkt_cnt: %lu\n",
+ field_get(MAX96724_MIPI_PHY25_CSI2_TX_PKT_CNT(index), val));
+
+ ret = regmap_read(priv->regmap, MAX96724_MIPI_PHY27(index), &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tphy_pkt_cnt: %lu\n",
+ field_get(MAX96724_MIPI_PHY27_PHY_PKT_CNT(index), val));
+
+ return 0;
+}
+
+static int max96724_set_enable(struct max_des *des, bool enable)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+
+ return regmap_assign_bits(priv->regmap, MAX96724_BACKTOP12,
+ MAX96724_BACKTOP12_CSI_OUT_EN, enable);
+}
+
+static const unsigned int max96724_phys_configs_reg_val[] = {
+ MAX96724_MIPI_PHY0_PHY_1X4A_2X2,
+ MAX96724_MIPI_PHY0_PHY_2X4,
+
+ MAX96724_MIPI_PHY0_PHY_4X2,
+ MAX96724_MIPI_PHY0_PHY_1X4A_2X2,
+ MAX96724_MIPI_PHY0_PHY_1X4B_2X2,
+ MAX96724_MIPI_PHY0_PHY_2X4,
+};
+
+static const struct max_serdes_phys_config max96724_phys_configs[] = {
+ /*
+ * PHY 1 can be in 4-lane mode (combining lanes of PHY 0 and PHY 1)
+ * but only use the data lanes of PHY0, while continuing to use the
+ * clock lane of PHY 1.
+ * Specifying clock-lanes as 5 turns on alternate clocking mode.
+ */
+ { { 2, 0, 2, 2 }, { MAX96724_PHY1_ALT_CLOCK, 0, 0, 0 } },
+ { { 2, 0, 4, 0 }, { MAX96724_PHY1_ALT_CLOCK, 0, 0, 0 } },
+
+ /*
+ * When combining PHY 0 and PHY 1 to make them function in 4-lane mode,
+ * PHY 1 is the master PHY, but we use PHY 0 here to maintain
+ * compatibility.
+ */
+ { { 2, 2, 2, 2 } },
+ { { 4, 0, 2, 2 } },
+ { { 2, 2, 4, 0 } },
+ { { 4, 0, 4, 0 } },
+};
+
+static int max96724_init_tpg(struct max_des *des)
+{
+ const struct reg_sequence regs[] = {
+ { MAX96724_GRAD_INCR, MAX_SERDES_GRAD_INCR },
+ REG_SEQUENCE_3_LE(MAX96724_CHKR_COLOR_A_L,
+ MAX_SERDES_CHECKER_COLOR_A),
+ REG_SEQUENCE_3_LE(MAX96724_CHKR_COLOR_B_L,
+ MAX_SERDES_CHECKER_COLOR_B),
+ { MAX96724_CHKR_RPT_A, MAX_SERDES_CHECKER_SIZE },
+ { MAX96724_CHKR_RPT_B, MAX_SERDES_CHECKER_SIZE },
+ { MAX96724_CHKR_ALT, MAX_SERDES_CHECKER_SIZE },
+ };
+ struct max96724_priv *priv = des_to_priv(des);
+
+ return regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs));
+}
+
+static int max96724_init(struct max_des *des)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int i;
+ int ret;
+
+ if (priv->info->set_pipe_tunnel_enable) {
+ for (i = 0; i < des->ops->num_pipes; i++) {
+ ret = regmap_set_bits(priv->regmap, MAX96724_MIPI_TX57(i),
+ MAX96724_MIPI_TX57_DIS_AUTO_TUN_DET);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (priv->info->supports_pipe_stream_autoselect) {
+ /* Enable stream autoselect. */
+ ret = regmap_set_bits(priv->regmap, MAX96724_VIDEO_PIPE_EN,
+ MAX96724_VIDEO_PIPE_EN_STREAM_SEL_ALL);
+ if (ret)
+ return ret;
+ }
+
+ /* Set PHY mode. */
+ ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY0,
+ MAX96724_MIPI_PHY0_PHY_CONFIG,
+ max96724_phys_configs_reg_val[des->phys_config]);
+ if (ret)
+ return ret;
+
+ return max96724_init_tpg(des);
+}
+
+static int max96724_init_phy(struct max_des *des, struct max_des_phy *phy)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ bool is_cphy = phy->bus_type == V4L2_MBUS_CSI2_CPHY;
+ unsigned int num_data_lanes = phy->mipi.num_data_lanes;
+ unsigned int dpll_freq = phy->link_frequency * 2;
+ unsigned int num_hw_data_lanes;
+ unsigned int index;
+ unsigned int used_data_lanes = 0;
+ unsigned int val, mask;
+ unsigned int i;
+ int ret;
+
+ index = max96724_phy_id(des, phy);
+ num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy);
+
+ ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_TX10(index),
+ MAX96724_MIPI_TX10_CSI2_LANE_CNT,
+ FIELD_PREP(MAX96724_MIPI_TX10_CSI2_LANE_CNT,
+ num_data_lanes - 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX10(index),
+ MAX96724_MIPI_TX10_CSI2_CPHY_EN, is_cphy);
+ if (ret)
+ return ret;
+
+ /* Configure lane mapping. */
+ val = 0;
+ for (i = 0; i < num_hw_data_lanes ; i++) {
+ unsigned int map;
+
+ if (i < num_data_lanes)
+ map = phy->mipi.data_lanes[i] - 1;
+ else
+ map = ffz(used_data_lanes);
+
+ val |= map << (i * 2);
+ used_data_lanes |= BIT(map);
+ }
+
+ if (num_hw_data_lanes == 4)
+ mask = MAX96724_MIPI_PHY3_PHY_LANE_MAP_4;
+ else
+ mask = MAX96724_MIPI_PHY3_PHY_LANE_MAP_2(index);
+
+ ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY3(index),
+ mask, field_prep(mask, val));
+ if (ret)
+ return ret;
+
+ /* Configure lane polarity. */
+ for (i = 0, val = 0; i < num_data_lanes; i++)
+ if (phy->mipi.lane_polarities[i + 1])
+ val |= BIT(i);
+
+ if (num_hw_data_lanes == 4) {
+ ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY5(index),
+ MAX96724_MIPI_PHY5_PHY_POL_MAP_4_0_1 |
+ MAX96724_MIPI_PHY5_PHY_POL_MAP_4_2_3,
+ FIELD_PREP(MAX96724_MIPI_PHY5_PHY_POL_MAP_4_0_1,
+ val) |
+ FIELD_PREP(MAX96724_MIPI_PHY5_PHY_POL_MAP_4_2_3,
+ val >> 2));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY5(index),
+ MAX96724_MIPI_PHY5_PHY_POL_MAP_4_CLK,
+ phy->mipi.lane_polarities[0]);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_update_bits(priv->regmap, MAX96724_MIPI_PHY5(index),
+ MAX96724_MIPI_PHY5_PHY_POL_MAP_2(index),
+ field_prep(MAX96724_MIPI_PHY5_PHY_POL_MAP_2(index), val));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY5(index),
+ MAX96724_MIPI_PHY5_PHY_POL_MAP_2_CLK(index),
+ phy->mipi.lane_polarities[0]);
+ if (ret)
+ return ret;
+ }
+
+ if (!is_cphy && dpll_freq > 1500000000ull) {
+ /* Enable initial deskew with 2 x 32k UI. */
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_TX3(index),
+ MAX96724_MIPI_TX3_DESKEW_INIT_AUTO |
+ MAX96724_MIPI_TX3_DESKEW_INIT_8X32K);
+ if (ret)
+ return ret;
+
+ /* Enable periodic deskew with 2 x 1k UI.. */
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_TX4(index),
+ MAX96724_MIPI_TX4_DESKEW_PER_AUTO |
+ MAX96724_MIPI_TX4_DESKEW_PER_2K);
+ if (ret)
+ return ret;
+ } else {
+ /* Disable initial deskew. */
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_TX3(index), 0x0);
+ if (ret)
+ return ret;
+
+ /* Disable periodic deskew. */
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_TX4(index), 0x0);
+ if (ret)
+ return ret;
+ }
+
+ if (is_cphy) {
+ /* Configure C-PHY timings. */
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_PHY13,
+ MAX96724_MIPI_PHY13_T_T3_PREBEGIN_64X7);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_PHY14,
+ MAX96724_MIPI_PHY14_T_T3_PREP_55NS |
+ MAX96724_MIPI_PHY14_T_T3_POST_32X7);
+ if (ret)
+ return ret;
+ }
+
+ /* Put DPLL block into reset. */
+ ret = regmap_clear_bits(priv->regmap, MAX96724_DPLL_0(index),
+ MAX96724_DPLL_0_CONFIG_SOFT_RST_N);
+ if (ret)
+ return ret;
+
+ /* Set DPLL frequency. */
+ ret = regmap_update_bits(priv->regmap, MAX96724_BACKTOP22(index),
+ MAX96724_BACKTOP22_PHY_CSI_TX_DPLL,
+ FIELD_PREP(MAX96724_BACKTOP22_PHY_CSI_TX_DPLL,
+ div_u64(dpll_freq, 100000000)));
+ if (ret)
+ return ret;
+
+ /* Enable DPLL frequency. */
+ ret = regmap_set_bits(priv->regmap, MAX96724_BACKTOP22(index),
+ MAX96724_BACKTOP22_PHY_CSI_TX_DPLL_EN);
+ if (ret)
+ return ret;
+
+ /* Pull DPLL block out of reset. */
+ return regmap_set_bits(priv->regmap, MAX96724_DPLL_0(index),
+ MAX96724_DPLL_0_CONFIG_SOFT_RST_N);
+}
+
+static int max96724_set_phy_mode(struct max_des *des, struct max_des_phy *phy,
+ struct max_des_phy_mode *mode)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = max96724_phy_id(des, phy);
+ int ret;
+
+ /* Set alternate memory map modes. */
+ ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index),
+ MAX96724_MIPI_TX51_ALT_MEM_MAP_12,
+ mode->alt_mem_map12);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index),
+ MAX96724_MIPI_TX51_ALT_MEM_MAP_8,
+ mode->alt_mem_map8);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index),
+ MAX96724_MIPI_TX51_ALT_MEM_MAP_10,
+ mode->alt_mem_map10);
+ if (ret)
+ return ret;
+
+ return regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX51(index),
+ MAX96724_MIPI_TX51_ALT2_MEM_MAP_8,
+ mode->alt2_mem_map8);
+}
+
+static int max96724_set_phy_enable(struct max_des *des, struct max_des_phy *phy,
+ bool enable)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = max96724_phy_id(des, phy);
+ unsigned int num_hw_data_lanes;
+ unsigned int mask;
+
+ num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy);
+
+ if (num_hw_data_lanes == 4)
+ /* PHY 1 -> bits [1:0] */
+ /* PHY 2 -> bits [3:2] */
+ mask = MAX96724_MIPI_PHY2_PHY_STDB_N_4(index);
+ else
+ mask = MAX96724_MIPI_PHY2_PHY_STDB_N_2(index);
+
+ return regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY2, mask, enable);
+}
+
+static int max96724_set_pipe_remap(struct max_des *des,
+ struct max_des_pipe *pipe,
+ unsigned int i,
+ struct max_des_remap *remap)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ struct max_des_phy *phy = &des->phys[remap->phy];
+ unsigned int phy_id = max96724_phy_id(des, phy);
+ unsigned int index = pipe->index;
+ int ret;
+
+ /* Set source Data Type and Virtual Channel. */
+ /* TODO: implement extended Virtual Channel. */
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_TX13(index, i),
+ FIELD_PREP(MAX96724_MIPI_TX13_MAP_SRC_DT,
+ remap->from_dt) |
+ FIELD_PREP(MAX96724_MIPI_TX13_MAP_SRC_VC,
+ remap->from_vc));
+ if (ret)
+ return ret;
+
+ /* Set destination Data Type and Virtual Channel. */
+ /* TODO: implement extended Virtual Channel. */
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_TX14(index, i),
+ FIELD_PREP(MAX96724_MIPI_TX14_MAP_DST_DT,
+ remap->to_dt) |
+ FIELD_PREP(MAX96724_MIPI_TX14_MAP_DST_VC,
+ remap->to_vc));
+ if (ret)
+ return ret;
+
+ /* Set destination PHY. */
+ return regmap_update_bits(priv->regmap, MAX96724_MIPI_TX45(index, i),
+ MAX96724_MIPI_TX45_MAP_DPHY_DEST(i),
+ field_prep(MAX96724_MIPI_TX45_MAP_DPHY_DEST(i),
+ phy_id));
+}
+
+static int max96724_set_pipe_remaps_enable(struct max_des *des,
+ struct max_des_pipe *pipe,
+ unsigned int mask)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+ int ret;
+
+ ret = regmap_write(priv->regmap, MAX96724_MIPI_TX11(index), mask);
+ if (ret)
+ return ret;
+
+ return regmap_write(priv->regmap, MAX96724_MIPI_TX12(index), mask >> 8);
+}
+
+static int max96724_set_pipe_tunnel_phy(struct max_des *des,
+ struct max_des_pipe *pipe,
+ struct max_des_phy *phy)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int phy_index = max96724_phy_id(des, phy);
+
+ return regmap_update_bits(priv->regmap, MAX96724_MIPI_TX57(pipe->index),
+ MAX96724_MIPI_TX57_TUN_DEST,
+ FIELD_PREP(MAX96724_MIPI_TX57_TUN_DEST,
+ phy_index));
+}
+
+static int max96724_set_pipe_phy(struct max_des *des, struct max_des_pipe *pipe,
+ struct max_des_phy *phy)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int phy_index = max96724_phy_id(des, phy);
+
+ return regmap_update_bits(priv->regmap, MAX96724_MIPI_CTRL_SEL,
+ MAX96724_MIPI_CTRL_SEL_MASK(pipe->index),
+ field_prep(MAX96724_MIPI_CTRL_SEL_MASK(pipe->index),
+ phy_index));
+}
+
+static int max96724_set_pipe_enable(struct max_des *des, struct max_des_pipe *pipe,
+ bool enable)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+
+ return regmap_assign_bits(priv->regmap, MAX96724_VIDEO_PIPE_EN,
+ MAX96724_VIDEO_PIPE_EN_MASK(index), enable);
+}
+
+static int max96724_set_pipe_stream_id(struct max_des *des, struct max_des_pipe *pipe,
+ unsigned int stream_id)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+
+ return regmap_update_bits(priv->regmap, MAX96724_VIDEO_PIPE_SEL(index),
+ MAX96724_VIDEO_PIPE_SEL_STREAM(index),
+ field_prep(MAX96724_VIDEO_PIPE_SEL_STREAM(index),
+ stream_id));
+}
+
+static int max96724_set_pipe_link(struct max_des *des, struct max_des_pipe *pipe,
+ struct max_des_link *link)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+
+ return regmap_update_bits(priv->regmap, MAX96724_VIDEO_PIPE_SEL(index),
+ MAX96724_VIDEO_PIPE_SEL_LINK(index),
+ field_prep(MAX96724_VIDEO_PIPE_SEL_LINK(index),
+ link->index));
+}
+
+static int max96724_set_pipe_mode(struct max_des *des,
+ struct max_des_pipe *pipe,
+ struct max_des_pipe_mode *mode)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+ unsigned int reg, mask, mode_mask;
+ int ret;
+
+ /* Set 8bit double mode. */
+ ret = regmap_assign_bits(priv->regmap, MAX96724_BACKTOP21(index),
+ MAX96724_BACKTOP21_BPP8DBL(index), mode->dbl8);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96724_BACKTOP24(index),
+ MAX96724_BACKTOP24_BPP8DBL_MODE(index),
+ mode->dbl8mode);
+ if (ret)
+ return ret;
+
+ /* Set 10bit double mode. */
+ if (index % 4 == 3) {
+ reg = MAX96724_BACKTOP30(index);
+ mask = MAX96724_BACKTOP30_BPP10DBL3;
+ mode_mask = MAX96724_BACKTOP30_BPP10DBL3_MODE;
+ } else if (index % 4 == 2) {
+ reg = MAX96724_BACKTOP31(index);
+ mask = MAX96724_BACKTOP31_BPP10DBL2;
+ mode_mask = MAX96724_BACKTOP31_BPP10DBL2_MODE;
+ } else if (index % 4 == 1) {
+ reg = MAX96724_BACKTOP32(index);
+ mask = MAX96724_BACKTOP32_BPP10DBL1;
+ mode_mask = MAX96724_BACKTOP32_BPP10DBL1_MODE;
+ } else {
+ reg = MAX96724_BACKTOP32(index);
+ mask = MAX96724_BACKTOP32_BPP10DBL0;
+ mode_mask = MAX96724_BACKTOP32_BPP10DBL0_MODE;
+ }
+
+ ret = regmap_assign_bits(priv->regmap, reg, mask, mode->dbl10);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, reg, mode_mask, mode->dbl10mode);
+ if (ret)
+ return ret;
+
+ /* Set 12bit double mode. */
+ return regmap_assign_bits(priv->regmap, MAX96724_BACKTOP32(index),
+ MAX96724_BACKTOP32_BPP12(index), mode->dbl12);
+}
+
+static int max96724_set_pipe_tunnel_enable(struct max_des *des,
+ struct max_des_pipe *pipe, bool enable)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+
+ return regmap_assign_bits(priv->regmap, MAX96724_MIPI_TX54(pipe->index),
+ MAX96724_MIPI_TX54_TUN_EN, enable);
+}
+
+static int max96724_select_links(struct max_des *des, unsigned int mask)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, MAX96724_REG6, MAX96724_REG6_LINK_EN,
+ field_prep(MAX96724_REG6_LINK_EN, mask));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(priv->regmap, MAX96724_CTRL1,
+ MAX96724_CTRL1_RESET_ONESHOT);
+ if (ret)
+ return ret;
+
+ msleep(60);
+
+ return 0;
+}
+
+static int max96724_set_link_version(struct max_des *des,
+ struct max_des_link *link,
+ enum max_serdes_gmsl_version version)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ unsigned int index = link->index;
+ unsigned int val;
+
+ if (version == MAX_SERDES_GMSL_2_6GBPS)
+ val = MAX96724_REG26_RX_RATE_6GBPS;
+ else
+ val = MAX96724_REG26_RX_RATE_3GBPS;
+
+ return regmap_update_bits(priv->regmap, MAX96724_REG26(index),
+ MAX96724_REG26_RX_RATE_PHY(index),
+ field_prep(MAX96724_REG26_RX_RATE_PHY(index), val));
+}
+
+static int max96724_set_tpg_timings(struct max96724_priv *priv,
+ const struct max_serdes_tpg_timings *tm)
+{
+ const struct reg_sequence regs[] = {
+ REG_SEQUENCE_3(MAX96724_VS_DLY_2, tm->vs_dly),
+ REG_SEQUENCE_3(MAX96724_VS_HIGH_2, tm->vs_high),
+ REG_SEQUENCE_3(MAX96724_VS_LOW_2, tm->vs_low),
+ REG_SEQUENCE_3(MAX96724_V2H_2, tm->v2h),
+ REG_SEQUENCE_2(MAX96724_HS_HIGH_1, tm->hs_high),
+ REG_SEQUENCE_2(MAX96724_HS_LOW_1, tm->hs_low),
+ REG_SEQUENCE_2(MAX96724_HS_CNT_1, tm->hs_cnt),
+ REG_SEQUENCE_3(MAX96724_V2D_2, tm->v2d),
+ REG_SEQUENCE_2(MAX96724_DE_HIGH_1, tm->de_high),
+ REG_SEQUENCE_2(MAX96724_DE_LOW_1, tm->de_low),
+ REG_SEQUENCE_2(MAX96724_DE_CNT_1, tm->de_cnt),
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs));
+ if (ret)
+ return ret;
+
+ return regmap_write(priv->regmap, MAX96724_PATGEN_0,
+ FIELD_PREP(MAX96724_PATGEN_0_VTG_MODE,
+ MAX96724_PATGEN_0_VTG_MODE_FREE_RUNNING) |
+ FIELD_PREP(MAX96724_PATGEN_0_DE_INV, tm->de_inv) |
+ FIELD_PREP(MAX96724_PATGEN_0_HS_INV, tm->hs_inv) |
+ FIELD_PREP(MAX96724_PATGEN_0_VS_INV, tm->vs_inv) |
+ FIELD_PREP(MAX96724_PATGEN_0_GEN_DE, tm->gen_de) |
+ FIELD_PREP(MAX96724_PATGEN_0_GEN_HS, tm->gen_hs) |
+ FIELD_PREP(MAX96724_PATGEN_0_GEN_VS, tm->gen_vs));
+}
+
+static int max96724_set_tpg_clk(struct max96724_priv *priv, u32 clock)
+{
+ bool patgen_clk_src = 0;
+ u8 pclk_src;
+ int ret;
+
+ switch (clock) {
+ case 25000000:
+ pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_25MHZ;
+ break;
+ case 75000000:
+ pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_75MHZ;
+ break;
+ case 150000000:
+ pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_USE_PIPE;
+ patgen_clk_src = MAX96724_VPRBS_PATGEN_CLK_SRC_150MHZ;
+ break;
+ case 375000000:
+ pclk_src = MAX96724_DEBUG_EXTRA_PCLK_SRC_USE_PIPE;
+ patgen_clk_src = MAX96724_VPRBS_PATGEN_CLK_SRC_375MHZ;
+ break;
+ case 0:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * TPG data is always injected on link 0, which is always routed to
+ * pipe 0.
+ */
+ ret = regmap_update_bits(priv->regmap, MAX96724_VPRBS(0),
+ MAX96724_VPRBS_PATGEN_CLK_SRC,
+ FIELD_PREP(MAX96724_VPRBS_PATGEN_CLK_SRC,
+ patgen_clk_src));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(priv->regmap, MAX96724_DEBUG_EXTRA,
+ MAX96724_DEBUG_EXTRA_PCLK_SRC,
+ FIELD_PREP(MAX96724_DEBUG_EXTRA_PCLK_SRC,
+ pclk_src));
+}
+
+static int max96724_set_tpg_mode(struct max96724_priv *priv, bool enable)
+{
+ unsigned int patgen_mode;
+
+ switch (priv->des.tpg_pattern) {
+ case MAX_SERDES_TPG_PATTERN_GRADIENT:
+ patgen_mode = MAX96724_PATGEN_1_PATGEN_MODE_GRADIENT;
+ break;
+ case MAX_SERDES_TPG_PATTERN_CHECKERBOARD:
+ patgen_mode = MAX96724_PATGEN_1_PATGEN_MODE_CHECKER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(priv->regmap, MAX96724_PATGEN_1,
+ MAX96724_PATGEN_1_PATGEN_MODE,
+ FIELD_PREP(MAX96724_PATGEN_1_PATGEN_MODE,
+ enable ? patgen_mode
+ : MAX96724_PATGEN_1_PATGEN_MODE_DISABLED));
+}
+
+static int max96724_set_tpg(struct max_des *des,
+ const struct max_serdes_tpg_entry *entry)
+{
+ struct max96724_priv *priv = des_to_priv(des);
+ struct max_serdes_tpg_timings timings = { 0 };
+ int ret;
+
+ ret = max_serdes_get_tpg_timings(entry, &timings);
+ if (ret)
+ return ret;
+
+ ret = max96724_set_tpg_timings(priv, &timings);
+ if (ret)
+ return ret;
+
+ ret = max96724_set_tpg_clk(priv, timings.clock);
+ if (ret)
+ return ret;
+
+ ret = max96724_set_tpg_mode(priv, entry);
+ if (ret)
+ return ret;
+
+ return regmap_assign_bits(priv->regmap, MAX96724_MIPI_PHY0,
+ MAX96724_MIPI_PHY0_FORCE_CSI_OUT_EN, !!entry);
+}
+
+static const struct max_serdes_tpg_entry max96724_tpg_entries[] = {
+ MAX_TPG_ENTRY_640X480P60_RGB888,
+ MAX_TPG_ENTRY_1920X1080P30_RGB888,
+ MAX_TPG_ENTRY_1920X1080P60_RGB888,
+};
+
+static const struct max_des_ops max96724_ops = {
+ .num_phys = 4,
+ .num_links = 4,
+ .num_remaps_per_pipe = 16,
+ .phys_configs = {
+ .num_configs = ARRAY_SIZE(max96724_phys_configs),
+ .configs = max96724_phys_configs,
+ },
+ .tpg_entries = {
+ .num_entries = ARRAY_SIZE(max96724_tpg_entries),
+ .entries = max96724_tpg_entries,
+ },
+ .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE,
+ .tpg_patterns = BIT(MAX_SERDES_TPG_PATTERN_CHECKERBOARD) |
+ BIT(MAX_SERDES_TPG_PATTERN_GRADIENT),
+ .use_atr = true,
+ .reg_read = max96724_reg_read,
+ .reg_write = max96724_reg_write,
+ .log_pipe_status = max96724_log_pipe_status,
+ .log_phy_status = max96724_log_phy_status,
+ .set_enable = max96724_set_enable,
+ .init = max96724_init,
+ .init_phy = max96724_init_phy,
+ .set_phy_mode = max96724_set_phy_mode,
+ .set_phy_enable = max96724_set_phy_enable,
+ .set_pipe_stream_id = max96724_set_pipe_stream_id,
+ .set_pipe_link = max96724_set_pipe_link,
+ .set_pipe_enable = max96724_set_pipe_enable,
+ .set_pipe_remap = max96724_set_pipe_remap,
+ .set_pipe_remaps_enable = max96724_set_pipe_remaps_enable,
+ .set_pipe_mode = max96724_set_pipe_mode,
+ .set_tpg = max96724_set_tpg,
+ .select_links = max96724_select_links,
+ .set_link_version = max96724_set_link_version,
+};
+
+static const struct max96724_chip_info max96724_info = {
+ .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) |
+ BIT(MAX_SERDES_GMSL_2_6GBPS),
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) |
+ BIT(MAX_SERDES_GMSL_TUNNEL_MODE),
+ .set_pipe_tunnel_enable = max96724_set_pipe_tunnel_enable,
+ .set_pipe_phy = max96724_set_pipe_phy,
+ .set_pipe_tunnel_phy = max96724_set_pipe_tunnel_phy,
+ .supports_pipe_stream_autoselect = true,
+ .num_pipes = 4,
+};
+
+static const struct max96724_chip_info max96724f_info = {
+ .versions = BIT(MAX_SERDES_GMSL_2_3GBPS),
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) |
+ BIT(MAX_SERDES_GMSL_TUNNEL_MODE),
+ .set_pipe_tunnel_enable = max96724_set_pipe_tunnel_enable,
+ .set_pipe_phy = max96724_set_pipe_phy,
+ .set_pipe_tunnel_phy = max96724_set_pipe_tunnel_phy,
+ .supports_pipe_stream_autoselect = true,
+ .num_pipes = 4,
+};
+
+static const struct max96724_chip_info max96712_info = {
+ .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) |
+ BIT(MAX_SERDES_GMSL_2_6GBPS),
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE),
+ .num_pipes = 8,
+};
+
+static int max96724_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct max96724_priv *priv;
+ struct max_des_ops *ops;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
+ if (!ops)
+ return -ENOMEM;
+
+ priv->info = device_get_match_data(dev);
+ if (!priv->info) {
+ dev_err(dev, "Failed to get match data\n");
+ return -ENODEV;
+ }
+
+ priv->dev = dev;
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+
+ priv->regmap = devm_regmap_init_i2c(client, &max96724_i2c_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->gpiod_enable = devm_gpiod_get_optional(&client->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_enable))
+ return PTR_ERR(priv->gpiod_enable);
+
+ if (priv->gpiod_enable) {
+ /* PWDN must be held for 1us for reset */
+ udelay(1);
+
+ gpiod_set_value_cansleep(priv->gpiod_enable, 1);
+
+ /* Maximum power-up time (tLOCK) 4ms */
+ usleep_range(4000, 5000);
+ }
+
+ *ops = max96724_ops;
+ ops->versions = priv->info->versions;
+ ops->modes = priv->info->modes;
+ ops->num_pipes = priv->info->num_pipes;
+ ops->set_pipe_tunnel_enable = priv->info->set_pipe_tunnel_enable;
+ ops->set_pipe_phy = priv->info->set_pipe_phy;
+ ops->set_pipe_tunnel_phy = priv->info->set_pipe_tunnel_phy;
+ priv->des.ops = ops;
+
+ ret = max96724_reset(priv);
+ if (ret)
+ return ret;
+
+ return max_des_probe(client, &priv->des);
+}
+
+static void max96724_remove(struct i2c_client *client)
+{
+ struct max96724_priv *priv = i2c_get_clientdata(client);
+
+ max_des_remove(&priv->des);
+
+ gpiod_set_value_cansleep(priv->gpiod_enable, 0);
+}
+
+static const struct of_device_id max96724_of_table[] = {
+ { .compatible = "maxim,max96712", .data = &max96712_info },
+ { .compatible = "maxim,max96724", .data = &max96724_info },
+ { .compatible = "maxim,max96724f", .data = &max96724f_info },
+ { .compatible = "maxim,max96724r", .data = &max96724f_info },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max96724_of_table);
+
+static struct i2c_driver max96724_i2c_driver = {
+ .driver = {
+ .name = "max96724",
+ .of_match_table = max96724_of_table,
+ },
+ .probe = max96724_probe,
+ .remove = max96724_remove,
+};
+
+module_i2c_driver(max96724_i2c_driver);
+
+MODULE_DESCRIPTION("Maxim MAX96724 Quad GMSL2 Deserializer Driver");
+MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@...log.com>");
+MODULE_LICENSE("GPL");
--
2.50.0
Powered by blists - more mailing lists