[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250702132104.1537926-21-demonsingur@gmail.com>
Date: Wed, 2 Jul 2025 16:20:46 +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 20/24] media: i2c: maxim-serdes: add MAX9296A driver
Add a new MAX9296A driver that also supports MAX96714, MAX96714F,
MAX96714R, MAX96716A and MAX96792A.
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 | 12 +
drivers/media/i2c/maxim-serdes/Makefile | 1 +
drivers/media/i2c/maxim-serdes/max9296a.c | 1354 +++++++++++++++++++++
3 files changed, 1367 insertions(+)
create mode 100644 drivers/media/i2c/maxim-serdes/max9296a.c
diff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig
index 2acd96cdbfa4..05868624f3b6 100644
--- a/drivers/media/i2c/maxim-serdes/Kconfig
+++ b/drivers/media/i2c/maxim-serdes/Kconfig
@@ -41,3 +41,15 @@ config VIDEO_MAX96724
To compile this driver as a module, choose M here: the module
will be called max96724.
+
+config VIDEO_MAX9296A
+ tristate "Maxim MAX9296A Dual Deserializer support"
+ select VIDEO_MAXIM_SERDES
+ help
+ This driver supports the Maxim MAX9296A, MAX96716A, MAX96792A
+ Dual Deserializers, and the MAX96714, MAX96714F, MAX96714R
+ Single Deserializers, which convert from up to two GMSL2/3
+ links to up to two MIPI D-PHY outputs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called max9296a.
diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile
index b6d5aebfaee1..ae306bc33bfb 100644
--- a/drivers/media/i2c/maxim-serdes/Makefile
+++ b/drivers/media/i2c/maxim-serdes/Makefile
@@ -3,3 +3,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
+obj-$(CONFIG_VIDEO_MAX9296A) += max9296a.o
diff --git a/drivers/media/i2c/maxim-serdes/max9296a.c b/drivers/media/i2c/maxim-serdes/max9296a.c
new file mode 100644
index 000000000000..946fdc402829
--- /dev/null
+++ b/drivers/media/i2c/maxim-serdes/max9296a.c
@@ -0,0 +1,1354 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim MAX9296A Quad GMSL2 Deserializer Driver
+ *
+ * Copyright (C) 2025 Analog Devices Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+
+#include <media/mipi-csi2.h>
+
+#include "max_des.h"
+
+#define MAX9296A_REG0 0x0
+
+#define MAX9296A_REG1 0x1
+#define MAX9296A_REG1_RX_RATE_A GENMASK(1, 0)
+#define MAX9296A_REG1_RX_RATE_3GBPS 0b01
+#define MAX9296A_REG1_RX_RATE_6GBPS 0b10
+#define MAX9296A_REG1_RX_RATE_12GBPS 0b11
+
+#define MAX9296A_REG2 0x2
+#define MAX9296A_REG2_VID_EN(p) BIT((p) + 4)
+
+#define MAX9296A_REG4 0x4
+#define MAX9296A_REG4_GMSL3_X(x) BIT((x) + 6)
+#define MAX9296A_REG4_RX_RATE_B GENMASK(1, 0)
+
+#define MAX9296A_REG6 0x6
+#define MAX9296A_REG6_GMSL2_X(x) BIT((x) + 6)
+
+#define MAX9296A_CTRL0 0x10
+#define MAX9296A_CTRL0_LINK_CFG GENMASK(1, 0)
+#define MAX9296A_CTRL0_AUTO_LINK BIT(4)
+#define MAX9296A_CTRL0_RESET_ONESHOT BIT(5)
+#define MAX9296A_CTRL0_RESET_ALL BIT(7)
+
+#define MAX9296A_CTRL2 0x12
+#define MAX9296A_CTRL2_RESET_ONESHOT_B BIT(5)
+
+#define MAX9296A_MIPI_TX0(x) (0x28 + (x) * 0x5000)
+#define MAX9296A_MIPI_TX0_RX_FEC_EN BIT(1)
+
+#define MAX9296A_IO_CHK0 0x38
+#define MAX9296A_IO_CHK0_PIN_DRV_EN_0 GENMASK(1, 0)
+#define MAX9296A_IO_CHK0_PIN_DRV_EN_0_25MHZ 0b00
+#define MAX9296A_IO_CHK0_PIN_DRV_EN_0_75MHZ 0b01
+#define MAX9296A_IO_CHK0_PIN_DRV_EN_0_USE_PIPE 0b10
+
+#define MAX9296A_RX50(p) (0x50 + (p))
+#define MAX9296A_RX50_STR_SEL GENMASK(1, 0)
+
+#define MAX9296A_VIDEO_PIPE_EN 0x160
+#define MAX9296A_VIDEO_PIPE_EN_MASK(p) BIT(p)
+
+#define MAX9296A_VIDEO_PIPE_SEL 0x161
+#define MAX9296A_VIDEO_PIPE_SEL_STREAM(p) (GENMASK(1, 0) << ((p) * 3))
+#define MAX9296A_VIDEO_PIPE_SEL_LINK(p) BIT(2 + (p) * 3)
+
+#define MAX9296A_VPRBS(p) (0x1fc + (p) * 0x20)
+#define MAX9296A_VPRBS_VIDEO_LOCK BIT(0)
+#define MAX9296A_VPRBS_PATGEN_CLK_SRC BIT(7)
+#define MAX9296A_VPRBS_PATGEN_CLK_SRC_150MHZ 0b0
+#define MAX9296A_VPRBS_PATGEN_CLK_SRC_600MHZ 0b1
+
+#define MAX9296A_PATGEN_0 0x240
+#define MAX9296A_PATGEN_0_VTG_MODE GENMASK(1, 0)
+#define MAX9296A_PATGEN_0_VTG_MODE_FREE_RUNNING 0b11
+#define MAX9296A_PATGEN_0_DE_INV BIT(2)
+#define MAX9296A_PATGEN_0_HS_INV BIT(3)
+#define MAX9296A_PATGEN_0_VS_INV BIT(4)
+#define MAX9296A_PATGEN_0_GEN_DE BIT(5)
+#define MAX9296A_PATGEN_0_GEN_HS BIT(6)
+#define MAX9296A_PATGEN_0_GEN_VS BIT(7)
+
+#define MAX9296A_PATGEN_1 0x241
+#define MAX9296A_PATGEN_1_PATGEN_MODE GENMASK(5, 4)
+#define MAX9296A_PATGEN_1_PATGEN_MODE_DISABLED 0b00
+#define MAX9296A_PATGEN_1_PATGEN_MODE_CHECKER 0b11
+#define MAX9296A_PATGEN_1_PATGEN_MODE_GRADIENT 0b10
+
+#define MAX9296A_VS_DLY_2 0x242
+#define MAX9296A_VS_HIGH_2 0x245
+#define MAX9296A_VS_LOW_2 0x248
+#define MAX9296A_V2H_2 0x24b
+#define MAX9296A_HS_HIGH_1 0x24e
+#define MAX9296A_HS_LOW_1 0x250
+#define MAX9296A_HS_CNT_1 0x252
+#define MAX9296A_V2D_2 0x254
+#define MAX9296A_DE_HIGH_1 0x257
+#define MAX9296A_DE_LOW_1 0x259
+#define MAX9296A_DE_CNT_1 0x25b
+#define MAX9296A_GRAD_INCR 0x25d
+#define MAX9296A_CHKR_COLOR_A_L 0x25e
+#define MAX9296A_CHKR_COLOR_B_L 0x261
+#define MAX9296A_CHKR_RPT_A 0x264
+#define MAX9296A_CHKR_RPT_B 0x265
+#define MAX9296A_CHKR_ALT 0x266
+
+#define MAX9296A_BACKTOP12 0x313
+#define MAX9296A_BACKTOP12_CSI_OUT_EN BIT(1)
+
+#define MAX9296A_BACKTOP21 0x31c
+#define MAX9296A_BACKTOP21_BPP8DBL(p) BIT(4 + (p))
+
+#define MAX9296A_BACKTOP22(x) (0x31d + (x) * 0x3)
+#define MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL GENMASK(4, 0)
+#define MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL_EN BIT(5)
+
+#define MAX9296A_BACKTOP24 0x31f
+#define MAX9296A_BACKTOP24_BPP8DBL_MODE(p) BIT(4 + (p))
+
+#define MAX9296A_BACKTOP32 0x327
+#define MAX9296A_BACKTOP32_BPP10DBL(p) BIT(p)
+#define MAX9296A_BACKTOP32_BPP10DBL_MODE(p) BIT(4 + (p))
+
+#define MAX9296A_BACKTOP33 0x328
+#define MAX9296A_BACKTOP32_BPP12DBL(p) BIT(p)
+
+#define MAX9296A_MIPI_PHY0 0x330
+#define MAX9296A_MIPI_PHY0_FORCE_CSI_OUT_EN BIT(7)
+
+#define MAX9296A_MIPI_PHY2 0x332
+#define MAX9296A_MIPI_PHY2_PHY_STDBY_N(x) (GENMASK(5, 4) << ((x) * 2))
+
+#define MAX9296A_MIPI_PHY3(x) (0x333 + (x))
+#define MAX9296A_MIPI_PHY3_PHY_LANE_MAP_4 GENMASK(7, 0)
+
+#define MAX9296A_MIPI_PHY5(x) (0x335 + (x))
+#define MAX9296A_MIPI_PHY5_PHY_POL_MAP_0_1 GENMASK(1, 0)
+#define MAX9296A_MIPI_PHY5_PHY_POL_MAP_2_3 GENMASK(4, 3)
+#define MAX9296A_MIPI_PHY5_PHY_POL_MAP_CLK(x) BIT((x) == 0 ? 5 : 2)
+
+#define MAX9296A_MIPI_PHY18 0x342
+#define MAX9296A_MIPI_PHY18_CSI2_TX_PKT_CNT(x) (GENMASK(3, 0) << (4 * (x)))
+
+#define MAX9296A_MIPI_PHY20(x) (0x344 + (x))
+
+#define MAX9296A_MIPI_TX3(x) (0x403 + (x) * 0x40)
+#define MAX9296A_MIPI_TX3_DESKEW_INIT_8X32K FIELD_PREP(GENMASK(2, 0), 0b001)
+#define MAX9296A_MIPI_TX3_DESKEW_INIT_AUTO BIT(7)
+
+#define MAX9296A_MIPI_TX4(x) (0x404 + (x) * 0x40)
+#define MAX9296A_MIPI_TX4_DESKEW_PER_2K FIELD_PREP(GENMASK(2, 0), 0b001)
+#define MAX9296A_MIPI_TX4_DESKEW_PER_AUTO BIT(7)
+
+#define MAX9296A_MIPI_TX10(x) (0x40a + (x) * 0x40)
+#define MAX9296A_MIPI_TX10_CSI2_LANE_CNT GENMASK(7, 6)
+#define MAX9296A_MIPI_TX10_CSI2_CPHY_EN BIT(5)
+
+#define MAX9296A_MIPI_TX11(p) (0x40b + (p) * 0x40)
+#define MAX9296A_MIPI_TX12(p) (0x40c + (p) * 0x40)
+
+#define MAX9296A_MIPI_TX13(p, x) (0x40d + (p) * 0x40 + (x) * 0x2)
+#define MAX9296A_MIPI_TX13_MAP_SRC_DT GENMASK(5, 0)
+#define MAX9296A_MIPI_TX13_MAP_SRC_VC GENMASK(7, 6)
+
+#define MAX9296A_MIPI_TX14(p, x) (0x40e + (p) * 0x40 + (x) * 0x2)
+#define MAX9296A_MIPI_TX14_MAP_DST_DT GENMASK(5, 0)
+#define MAX9296A_MIPI_TX14_MAP_DST_VC GENMASK(7, 6)
+
+#define MAX9296A_MIPI_TX45(p, x) (0x42d + (p) * 0x40 + (x) / 4)
+#define MAX9296A_MIPI_TX45_MAP_DPHY_DEST(x) (GENMASK(1, 0) << (2 * ((x) % 4)))
+
+#define MAX9296A_MIPI_TX51(x) (0x433 + (x) * 0x40)
+#define MAX9296A_MIPI_TX51_ALT_MEM_MAP_12 BIT(0)
+#define MAX9296A_MIPI_TX51_ALT_MEM_MAP_8 BIT(1)
+#define MAX9296A_MIPI_TX51_ALT_MEM_MAP_10 BIT(2)
+#define MAX9296A_MIPI_TX51_ALT2_MEM_MAP_8 BIT(4)
+
+#define MAX9296A_MIPI_TX52(x) (0x434 + (x) * 0x40)
+#define MAX9296A_MIPI_TX52_TUN_DEST BIT(1)
+#define MAX9296A_MIPI_TX52_TUN_EN BIT(0)
+
+#define MAX9296A_GMSL1_EN 0xf00
+#define MAX9296A_GMSL1_EN_LINK_EN GENMASK(1, 0)
+
+#define MAX9296A_RLMS3E(x) (0x143e + (x) * 0x100)
+#define MAX9296A_RLMS3F(x) (0x143f + (x) * 0x100)
+#define MAX9296A_RLMS49(x) (0x1449 + (x) * 0x100)
+#define MAX9296A_RLMS7E(x) (0x147e + (x) * 0x100)
+#define MAX9296A_RLMS7F(x) (0x147f + (x) * 0x100)
+#define MAX9296A_RLMSA3(x) (0x14a3 + (x) * 0x100)
+#define MAX9296A_RLMSA5(x) (0x14a5 + (x) * 0x100)
+#define MAX9296A_RLMSD8(x) (0x14d8 + (x) * 0x100)
+
+#define MAX9296A_DPLL_0(x) (0x1c00 + (x) * 0x100)
+#define MAX9296A_DPLL_0_CONFIG_SOFT_RST_N BIT(0)
+
+#define MAX9296A_PIPES_NUM 4
+#define MAX9296A_PHYS_NUM 2
+
+static const struct regmap_config max9296a_i2c_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+};
+
+struct max9296a_priv {
+ struct max_des des;
+ const struct max9296a_chip_info *info;
+
+ struct device *dev;
+ struct i2c_client *client;
+ struct regmap *regmap;
+
+ struct gpio_desc *gpiod_pwdn;
+};
+
+struct max9296a_chip_info {
+ const struct max_des_ops *ops;
+ unsigned int max_register;
+ unsigned int pipe_hw_ids[MAX9296A_PIPES_NUM];
+ unsigned int phy_hw_ids[MAX9296A_PHYS_NUM];
+ bool use_atr;
+ bool has_per_link_reset;
+ bool phy0_lanes_0_1_on_second_phy;
+ bool polarity_on_physical_lanes;
+ bool supports_cphy;
+ bool supports_phy_log;
+ bool adjust_rlms;
+};
+
+#define des_to_priv(_des) \
+ container_of(_des, struct max9296a_priv, des)
+
+static int max9296a_wait_for_device(struct max9296a_priv *priv)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < 10; i++) {
+ unsigned int val;
+
+ ret = regmap_read(priv->regmap, MAX9296A_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 max9296a_reset(struct max9296a_priv *priv)
+{
+ int ret;
+
+ ret = max9296a_wait_for_device(priv);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(priv->regmap, MAX9296A_CTRL0,
+ MAX9296A_CTRL0_RESET_ALL);
+ if (ret)
+ return ret;
+
+ msleep(100);
+
+ return max9296a_wait_for_device(priv);
+}
+
+static unsigned int max9296a_pipe_id(struct max9296a_priv *priv,
+ struct max_des_pipe *pipe)
+{
+ return priv->info->pipe_hw_ids[pipe->index];
+}
+
+static unsigned int max9296a_phy_id(struct max9296a_priv *priv,
+ struct max_des_phy *phy)
+{
+ return priv->info->phy_hw_ids[phy->index];
+}
+
+static int max9296a_reg_read(struct max_des *des, unsigned int reg,
+ unsigned int *val)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+
+ return regmap_read(priv->regmap, reg, val);
+}
+
+static int max9296a_reg_write(struct max_des *des, unsigned int reg,
+ unsigned int val)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+
+ return regmap_write(priv->regmap, reg, val);
+}
+
+static int max9626a_log_pipe_status(struct max_des *des,
+ struct max_des_pipe *pipe)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(priv->regmap, MAX9296A_VPRBS(index), &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tvideo_lock: %u\n",
+ !!(val & MAX9296A_VPRBS_VIDEO_LOCK));
+
+ return 0;
+}
+
+static int max9296a_log_phy_status(struct max_des *des,
+ struct max_des_phy *phy)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = phy->index;
+ unsigned int val;
+ int ret;
+
+ if (!priv->info->supports_phy_log)
+ return 0;
+
+ ret = regmap_read(priv->regmap, MAX9296A_MIPI_PHY18, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tcsi2_pkt_cnt: %lu\n",
+ field_get(MAX9296A_MIPI_PHY18_CSI2_TX_PKT_CNT(index), val));
+
+ ret = regmap_read(priv->regmap, MAX9296A_MIPI_PHY20(index), &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tphy_pkt_cnt: %u\n", val);
+
+ return 0;
+}
+
+static int max9296a_set_enable(struct max_des *des, bool enable)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP12,
+ MAX9296A_BACKTOP12_CSI_OUT_EN, enable);
+}
+
+static int max9296a_init_tpg(struct max_des *des)
+{
+ const struct reg_sequence regs[] = {
+ { MAX9296A_GRAD_INCR, MAX_SERDES_GRAD_INCR },
+ REG_SEQUENCE_3_LE(MAX9296A_CHKR_COLOR_A_L,
+ MAX_SERDES_CHECKER_COLOR_A),
+ REG_SEQUENCE_3_LE(MAX9296A_CHKR_COLOR_B_L,
+ MAX_SERDES_CHECKER_COLOR_B),
+ { MAX9296A_CHKR_RPT_A, MAX_SERDES_CHECKER_SIZE },
+ { MAX9296A_CHKR_RPT_B, MAX_SERDES_CHECKER_SIZE },
+ { MAX9296A_CHKR_ALT, MAX_SERDES_CHECKER_SIZE },
+ };
+ struct max9296a_priv *priv = des_to_priv(des);
+
+ return regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs));
+}
+
+static int max9296a_init(struct max_des *des)
+{
+ return max9296a_init_tpg(des);
+}
+
+static int max9296a_init_phy(struct max_des *des, struct max_des_phy *phy)
+{
+ struct max9296a_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 hw_index = max9296a_phy_id(priv, phy);
+ unsigned int index = phy->index;
+ unsigned int used_data_lanes = 0;
+ unsigned int val;
+ unsigned int i;
+ int ret;
+
+ if (is_cphy && !priv->info->supports_cphy) {
+ dev_err(priv->dev, "CPHY not supported\n");
+ return -EINVAL;
+ }
+
+ num_hw_data_lanes = max_des_phy_hw_data_lanes(des, phy);
+
+ /*
+ * MAX9296A has four PHYs, but does not support single-PHY configurations,
+ * only double-PHY configurations, even when only using two lanes.
+ * For PHY 0 + PHY 1, PHY 1 is the master PHY.
+ * For PHY 2 + PHY 3, PHY 2 is the master PHY.
+ * Clock is always on the master PHY.
+ * For first pair of PHYs, first lanes are on the master PHY.
+ * For second pair of PHYs, first lanes are on the master PHY too.
+ *
+ * PHY 0 + 1
+ * CLK = PHY 1
+ * PHY1 Lane 0 = D0
+ * PHY1 Lane 1 = D1
+ * PHY0 Lane 0 = D2
+ * PHY0 Lane 1 = D3
+ *
+ * PHY 2 + 3
+ * CLK = PHY 2
+ * PHY2 Lane 0 = D0
+ * PHY2 Lane 1 = D1
+ * PHY3 Lane 0 = D2
+ * PHY3 Lane 1 = D3
+ *
+ * MAX96714 only has two PHYs which cannot support single-PHY configurations.
+ * Clock is always on the master PHY, first lanes are on PHY 0, even if
+ * PHY 1 is the master PHY.
+ *
+ * PHY 0 + 1
+ * CLK = PHY 1
+ * PHY0 Lane 0 = D0
+ * PHY0 Lane 1 = D1
+ * PHY1 Lane 0 = D2
+ * PHY1 Lane 1 = D3
+ */
+
+ /* Configure a lane count. */
+ ret = regmap_update_bits(priv->regmap, MAX9296A_MIPI_TX10(hw_index),
+ MAX9296A_MIPI_TX10_CSI2_LANE_CNT,
+ FIELD_PREP(MAX9296A_MIPI_TX10_CSI2_LANE_CNT,
+ num_data_lanes - 1));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX10(hw_index),
+ MAX9296A_MIPI_TX10_CSI2_CPHY_EN, is_cphy);
+ if (ret)
+ return ret;
+
+ /* Configure lane mapping. */
+ /*
+ * The lane of each PHY can be mapped to physical lanes 0, 1, 2, and 3.
+ * This mapping is exclusive, multiple lanes, even if unused cannot be
+ * mapped to the same physical lane.
+ * Each lane mapping is represented as two bits.
+ */
+ 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 (phy->index == 0 && priv->info->phy0_lanes_0_1_on_second_phy)
+ val = ((val & 0xf) << 4) | ((val >> 4) & 0xf);
+
+ ret = regmap_update_bits(priv->regmap, MAX9296A_MIPI_PHY3(index),
+ MAX9296A_MIPI_PHY3_PHY_LANE_MAP_4,
+ FIELD_PREP(MAX9296A_MIPI_PHY3_PHY_LANE_MAP_4, val));
+ if (ret)
+ return ret;
+
+ /*
+ * Configure lane polarity.
+ *
+ * PHY 0 and 1 are on register 0x335.
+ * PHY 2 and 3 are on register 0x336.
+ *
+ * Each PHY has 3 bits of polarity configuration.
+ *
+ * On MAX9296A, each bit represents the lane polarity of logical lanes.
+ * Each of these lanes can be mapped to any physical lane.
+ * 0th bit is for lane 0.
+ * 1st bit is for lane 1.
+ * 2nd bit is for clock lane.
+ *
+ * On MAX96714, each bit represents the lane polarity of physical lanes.
+ * 0th bit for physical lane 0.
+ * 1st bit for physical lane 1.
+ * 2nd bit for clock lane of PHY 0, the slave PHY, which is unused.
+ *
+ * 3rd bit for physical lane 2.
+ * 4th bit for physical lane 3.
+ * 5th bit for clock lane of PHY 1, the master PHY.
+ */
+
+ for (i = 0, val = 0; i < num_data_lanes; i++) {
+ unsigned int map;
+
+ if (!phy->mipi.lane_polarities[i + 1])
+ continue;
+
+ /*
+ * The numbers inside the data_lanes array specify the hardware
+ * lane each logical lane maps to.
+ * If polarity is set for the physical lanes, retrieve the
+ * physical lane matching the logical lane from data_lanes.
+ * Otherwise, when polarity is set for the logical lanes
+ * the index of the polarity can be used.
+ */
+
+ if (priv->info->polarity_on_physical_lanes)
+ map = phy->mipi.data_lanes[i] - 1;
+ else
+ map = i;
+
+ val |= BIT(map);
+ }
+
+ if (phy->index == 0 && priv->info->phy0_lanes_0_1_on_second_phy)
+ val = ((val & 0x3) << 2) | ((val >> 2) & 0x3);
+
+ ret = regmap_update_bits(priv->regmap, MAX9296A_MIPI_PHY5(index),
+ MAX9296A_MIPI_PHY5_PHY_POL_MAP_0_1 |
+ MAX9296A_MIPI_PHY5_PHY_POL_MAP_2_3,
+ FIELD_PREP(MAX9296A_MIPI_PHY5_PHY_POL_MAP_0_1, val) |
+ FIELD_PREP(MAX9296A_MIPI_PHY5_PHY_POL_MAP_2_3, val >> 2));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_PHY5(index),
+ MAX9296A_MIPI_PHY5_PHY_POL_MAP_CLK(index),
+ phy->mipi.lane_polarities[0]);
+ if (ret)
+ return ret;
+
+ /* Put DPLL block into reset. */
+ ret = regmap_clear_bits(priv->regmap, MAX9296A_DPLL_0(hw_index),
+ MAX9296A_DPLL_0_CONFIG_SOFT_RST_N);
+ if (ret)
+ return ret;
+
+ /* Set DPLL frequency. */
+ ret = regmap_update_bits(priv->regmap, MAX9296A_BACKTOP22(index),
+ MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL,
+ FIELD_PREP(MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL,
+ div_u64(dpll_freq, 100000000)));
+ if (ret)
+ return ret;
+
+ /* Enable DPLL frequency. */
+ ret = regmap_set_bits(priv->regmap, MAX9296A_BACKTOP22(index),
+ MAX9296A_BACKTOP22_PHY_CSI_TX_DPLL_EN);
+ if (ret)
+ return ret;
+
+ /* Pull DPLL block out of reset. */
+ ret = regmap_set_bits(priv->regmap, MAX9296A_DPLL_0(hw_index),
+ MAX9296A_DPLL_0_CONFIG_SOFT_RST_N);
+ if (ret)
+ return ret;
+
+ if (dpll_freq > 1500000000ull) {
+ /* Enable initial deskew with 2 x 32k UI. */
+ ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX3(hw_index),
+ MAX9296A_MIPI_TX3_DESKEW_INIT_AUTO |
+ MAX9296A_MIPI_TX3_DESKEW_INIT_8X32K);
+ if (ret)
+ return ret;
+
+ /* Enable periodic deskew with 2 x 1k UI.. */
+ ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX4(hw_index),
+ MAX9296A_MIPI_TX4_DESKEW_PER_AUTO |
+ MAX9296A_MIPI_TX4_DESKEW_PER_2K);
+ if (ret)
+ return ret;
+ } else {
+ /* Disable initial deskew. */
+ ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX3(hw_index), 0x0);
+ if (ret)
+ return ret;
+
+ /* Disable periodic deskew. */
+ ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX4(hw_index), 0x0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max9296a_set_phy_mode(struct max_des *des, struct max_des_phy *phy,
+ struct max_des_phy_mode *mode)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int phy_id = max9296a_phy_id(priv, phy);
+ int ret;
+
+ /* Set alternate memory map modes. */
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id),
+ MAX9296A_MIPI_TX51_ALT_MEM_MAP_12,
+ mode->alt_mem_map12);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id),
+ MAX9296A_MIPI_TX51_ALT_MEM_MAP_8,
+ mode->alt_mem_map8);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id),
+ MAX9296A_MIPI_TX51_ALT_MEM_MAP_10,
+ mode->alt_mem_map10);
+ if (ret)
+ return ret;
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX51(phy_id),
+ MAX9296A_MIPI_TX51_ALT2_MEM_MAP_8,
+ mode->alt2_mem_map8);
+}
+
+static int max9296a_set_phy_enable(struct max_des *des, struct max_des_phy *phy,
+ bool enable)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_PHY2,
+ MAX9296A_MIPI_PHY2_PHY_STDBY_N(phy->index), enable);
+}
+
+static int max9296a_set_pipe_remap(struct max_des *des,
+ struct max_des_pipe *pipe,
+ unsigned int i,
+ struct max_des_remap *remap)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ struct max_des_phy *phy = &des->phys[remap->phy];
+ unsigned int phy_id = max9296a_phy_id(priv, phy);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+ int ret;
+
+ /* Set source Data Type and Virtual Channel. */
+ /* TODO: implement extended Virtual Channel. */
+ ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX13(index, i),
+ FIELD_PREP(MAX9296A_MIPI_TX13_MAP_SRC_DT,
+ remap->from_dt) |
+ FIELD_PREP(MAX9296A_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, MAX9296A_MIPI_TX14(index, i),
+ FIELD_PREP(MAX9296A_MIPI_TX14_MAP_DST_DT,
+ remap->to_dt) |
+ FIELD_PREP(MAX9296A_MIPI_TX14_MAP_DST_VC,
+ remap->to_vc));
+ if (ret)
+ return ret;
+
+ /* Set destination PHY. */
+ return regmap_update_bits(priv->regmap, MAX9296A_MIPI_TX45(index, i),
+ MAX9296A_MIPI_TX45_MAP_DPHY_DEST(i),
+ field_prep(MAX9296A_MIPI_TX45_MAP_DPHY_DEST(i),
+ phy_id));
+}
+
+static int max9296a_set_pipe_remaps_enable(struct max_des *des,
+ struct max_des_pipe *pipe,
+ unsigned int mask)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+ int ret;
+
+ ret = regmap_write(priv->regmap, MAX9296A_MIPI_TX11(index), mask);
+ if (ret)
+ return ret;
+
+ return regmap_write(priv->regmap, MAX9296A_MIPI_TX12(index), mask >> 8);
+}
+
+static int max9296a_set_pipe_enable(struct max_des *des, struct max_des_pipe *pipe,
+ bool enable)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_REG2,
+ MAX9296A_REG2_VID_EN(index), enable);
+}
+
+static int max96714_set_pipe_enable(struct max_des *des, struct max_des_pipe *pipe,
+ bool enable)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_VIDEO_PIPE_EN,
+ MAX9296A_VIDEO_PIPE_EN_MASK(index - 1), enable);
+}
+
+static int max96714_set_pipe_tunnel_enable(struct max_des *des,
+ struct max_des_pipe *pipe, bool enable)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX52(index),
+ MAX9296A_MIPI_TX52_TUN_EN, enable);
+}
+
+static int max9296a_set_pipe_stream_id(struct max_des *des, struct max_des_pipe *pipe,
+ unsigned int stream_id)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+
+ return regmap_update_bits(priv->regmap, MAX9296A_RX50(index), MAX9296A_RX50_STR_SEL,
+ FIELD_PREP(MAX9296A_RX50_STR_SEL, pipe->stream_id));
+}
+
+static int max96714_set_pipe_stream_id(struct max_des *des, struct max_des_pipe *pipe,
+ unsigned int stream_id)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+
+ return regmap_update_bits(priv->regmap, MAX9296A_VIDEO_PIPE_SEL,
+ MAX9296A_VIDEO_PIPE_SEL_STREAM(index),
+ field_prep(MAX9296A_VIDEO_PIPE_SEL_STREAM(index),
+ stream_id));
+}
+
+static int max96716a_set_pipe_link(struct max_des *des, struct max_des_pipe *pipe,
+ struct max_des_link *link)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = pipe->index;
+
+ return regmap_update_bits(priv->regmap, MAX9296A_VIDEO_PIPE_SEL,
+ MAX9296A_VIDEO_PIPE_SEL_LINK(index),
+ field_prep(MAX9296A_VIDEO_PIPE_SEL_LINK(index),
+ link->index));
+}
+
+static int max96716a_set_pipe_tunnel_phy(struct max_des *des,
+ struct max_des_pipe *pipe,
+ struct max_des_phy *phy)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX52(index),
+ MAX9296A_MIPI_TX52_TUN_DEST, phy->index);
+}
+
+static int max9296a_set_pipe_mode(struct max_des *des,
+ struct max_des_pipe *pipe,
+ struct max_des_pipe_mode *mode)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = max9296a_pipe_id(priv, pipe);
+ int ret;
+
+ /* Set 8bit double mode. */
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP21,
+ MAX9296A_BACKTOP21_BPP8DBL(index), mode->dbl8);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP24,
+ MAX9296A_BACKTOP24_BPP8DBL_MODE(index),
+ mode->dbl8mode);
+ if (ret)
+ return ret;
+
+ /* Set 10bit double mode. */
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP32,
+ MAX9296A_BACKTOP32_BPP10DBL(index), mode->dbl10);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP32,
+ MAX9296A_BACKTOP32_BPP10DBL_MODE(index),
+ mode->dbl10mode);
+ if (ret)
+ return ret;
+
+ /* Set 12bit double mode. */
+ /* TODO: check support for double mode on MAX96714. */
+ return regmap_assign_bits(priv->regmap, MAX9296A_BACKTOP33,
+ MAX9296A_BACKTOP32_BPP12DBL(index), mode->dbl12);
+}
+
+static int max9296a_reset_link(struct max9296a_priv *priv, unsigned int index)
+{
+ unsigned int reg, mask;
+
+ if (index == 0) {
+ reg = MAX9296A_CTRL0;
+ mask = MAX9296A_CTRL0_RESET_ONESHOT;
+ } else {
+ reg = MAX9296A_CTRL2;
+ mask = MAX9296A_CTRL2_RESET_ONESHOT_B;
+ }
+
+ return regmap_set_bits(priv->regmap, reg, mask);
+}
+
+static int max9296a_init_link_rlms(struct max9296a_priv *priv,
+ struct max_des_link *link)
+{
+ unsigned int index = link->index;
+ /*
+ * These register writes are described as required in MAX96714 datasheet
+ * Page 53, Section Register Map, to optimize link performance in 6Gbps
+ * and 3Gbps links for all cable lengths.
+ */
+ const struct reg_sequence regs[] = {
+ { MAX9296A_RLMS3E(index), 0xfd },
+ { MAX9296A_RLMS3F(index), 0x3d },
+ { MAX9296A_RLMS49(index), 0xf5 },
+ { MAX9296A_RLMS7E(index), 0xa8 },
+ { MAX9296A_RLMS7F(index), 0x68 },
+ { MAX9296A_RLMSA3(index), 0x30 },
+ { MAX9296A_RLMSA5(index), 0x70 },
+ { MAX9296A_RLMSD8(index), 0x07 },
+ };
+ int ret;
+
+ ret = regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs));
+ if (ret)
+ return ret;
+
+ return max9296a_reset_link(priv, link->index);
+}
+
+static int max9296a_init_link(struct max_des *des, struct max_des_link *link)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ int ret;
+
+ if (priv->info->adjust_rlms) {
+ ret = max9296a_init_link_rlms(priv, link);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max9296a_select_links(struct max_des *des, unsigned int mask)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ int ret;
+
+ if (des->ops->num_links == 1)
+ return 0;
+
+ if (!mask) {
+ dev_err(priv->dev, "Disable all links unsupported\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, MAX9296A_GMSL1_EN,
+ MAX9296A_GMSL1_EN_LINK_EN,
+ FIELD_PREP(MAX9296A_GMSL1_EN_LINK_EN, mask));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, MAX9296A_CTRL0,
+ MAX9296A_CTRL0_AUTO_LINK |
+ MAX9296A_CTRL0_LINK_CFG |
+ MAX9296A_CTRL0_RESET_ONESHOT,
+ FIELD_PREP(MAX9296A_CTRL0_LINK_CFG, mask) |
+ FIELD_PREP(MAX9296A_CTRL0_RESET_ONESHOT, 1));
+ if (ret)
+ return ret;
+
+ if (priv->info->has_per_link_reset) {
+ ret = max9296a_reset_link(priv, 1);
+ if (ret)
+ return ret;
+ }
+
+ msleep(200);
+
+ return 0;
+}
+
+static int max9296a_set_link_version(struct max_des *des,
+ struct max_des_link *link,
+ enum max_serdes_gmsl_version version)
+{
+ struct max9296a_priv *priv = des_to_priv(des);
+ unsigned int index = link->index;
+ bool gmsl3_en = version == MAX_SERDES_GMSL_3;
+ unsigned int reg, mask, val;
+ int ret;
+
+ if (des->ops->needs_single_link_version)
+ index = 0;
+
+ if (index == 0) {
+ reg = MAX9296A_REG1;
+ mask = MAX9296A_REG1_RX_RATE_A;
+ } else {
+ reg = MAX9296A_REG4;
+ mask = MAX9296A_REG4_RX_RATE_B;
+ }
+
+ if (version == MAX_SERDES_GMSL_3)
+ val = MAX9296A_REG1_RX_RATE_12GBPS;
+ else if (version == MAX_SERDES_GMSL_2_6GBPS)
+ val = MAX9296A_REG1_RX_RATE_6GBPS;
+ else
+ val = MAX9296A_REG1_RX_RATE_3GBPS;
+
+ ret = regmap_update_bits(priv->regmap, reg, mask, field_prep(mask, val));
+ if (ret)
+ return ret;
+
+ if (!(des->ops->versions & BIT(MAX_SERDES_GMSL_3)))
+ return 0;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_MIPI_TX0(index),
+ MAX9296A_MIPI_TX0_RX_FEC_EN, gmsl3_en);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX9296A_REG6,
+ MAX9296A_REG6_GMSL2_X(index), !gmsl3_en);
+ if (ret)
+ return ret;
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_REG4,
+ MAX9296A_REG4_GMSL3_X(index), gmsl3_en);
+}
+
+static int max9296a_set_tpg_timings(struct max9296a_priv *priv,
+ const struct max_serdes_tpg_timings *tm)
+{
+ const struct reg_sequence regs[] = {
+ REG_SEQUENCE_3(MAX9296A_VS_DLY_2, tm->vs_dly),
+ REG_SEQUENCE_3(MAX9296A_VS_HIGH_2, tm->vs_high),
+ REG_SEQUENCE_3(MAX9296A_VS_LOW_2, tm->vs_low),
+ REG_SEQUENCE_3(MAX9296A_V2H_2, tm->v2h),
+ REG_SEQUENCE_2(MAX9296A_HS_HIGH_1, tm->hs_high),
+ REG_SEQUENCE_2(MAX9296A_HS_LOW_1, tm->hs_low),
+ REG_SEQUENCE_2(MAX9296A_HS_CNT_1, tm->hs_cnt),
+ REG_SEQUENCE_3(MAX9296A_V2D_2, tm->v2d),
+ REG_SEQUENCE_2(MAX9296A_DE_HIGH_1, tm->de_high),
+ REG_SEQUENCE_2(MAX9296A_DE_LOW_1, tm->de_low),
+ REG_SEQUENCE_2(MAX9296A_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, MAX9296A_PATGEN_0,
+ FIELD_PREP(MAX9296A_PATGEN_0_VTG_MODE,
+ MAX9296A_PATGEN_0_VTG_MODE_FREE_RUNNING) |
+ FIELD_PREP(MAX9296A_PATGEN_0_DE_INV, tm->de_inv) |
+ FIELD_PREP(MAX9296A_PATGEN_0_HS_INV, tm->hs_inv) |
+ FIELD_PREP(MAX9296A_PATGEN_0_VS_INV, tm->vs_inv) |
+ FIELD_PREP(MAX9296A_PATGEN_0_GEN_DE, tm->gen_de) |
+ FIELD_PREP(MAX9296A_PATGEN_0_GEN_HS, tm->gen_hs) |
+ FIELD_PREP(MAX9296A_PATGEN_0_GEN_VS, tm->gen_vs));
+}
+
+static int max9296a_set_tpg_clk(struct max9296a_priv *priv, u32 clock)
+{
+ bool patgen_clk_src = 0;
+ u8 pin_drv_en;
+ int ret;
+
+ switch (clock) {
+ case 25000000:
+ pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_25MHZ;
+ break;
+ case 75000000:
+ pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_75MHZ;
+ break;
+ case 150000000:
+ pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_USE_PIPE;
+ patgen_clk_src = MAX9296A_VPRBS_PATGEN_CLK_SRC_150MHZ;
+ break;
+ case 600000000:
+ pin_drv_en = MAX9296A_IO_CHK0_PIN_DRV_EN_0_USE_PIPE;
+ patgen_clk_src = MAX9296A_VPRBS_PATGEN_CLK_SRC_600MHZ;
+ 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, MAX9296A_VPRBS(0),
+ MAX9296A_VPRBS_PATGEN_CLK_SRC,
+ FIELD_PREP(MAX9296A_VPRBS_PATGEN_CLK_SRC,
+ patgen_clk_src));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(priv->regmap, MAX9296A_IO_CHK0,
+ MAX9296A_IO_CHK0_PIN_DRV_EN_0,
+ FIELD_PREP(MAX9296A_IO_CHK0_PIN_DRV_EN_0,
+ pin_drv_en));
+}
+
+static int max9296a_set_tpg_mode(struct max9296a_priv *priv, bool enable)
+{
+ unsigned int patgen_mode;
+
+ switch (priv->des.tpg_pattern) {
+ case MAX_SERDES_TPG_PATTERN_GRADIENT:
+ patgen_mode = MAX9296A_PATGEN_1_PATGEN_MODE_GRADIENT;
+ break;
+ case MAX_SERDES_TPG_PATTERN_CHECKERBOARD:
+ patgen_mode = MAX9296A_PATGEN_1_PATGEN_MODE_CHECKER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(priv->regmap, MAX9296A_PATGEN_1,
+ MAX9296A_PATGEN_1_PATGEN_MODE,
+ FIELD_PREP(MAX9296A_PATGEN_1_PATGEN_MODE,
+ enable ? patgen_mode
+ : MAX9296A_PATGEN_1_PATGEN_MODE_DISABLED));
+}
+
+static int max9296a_set_tpg(struct max_des *des,
+ const struct max_serdes_tpg_entry *entry)
+{
+ struct max9296a_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 = max9296a_set_tpg_timings(priv, &timings);
+ if (ret)
+ return ret;
+
+ ret = max9296a_set_tpg_clk(priv, timings.clock);
+ if (ret)
+ return ret;
+
+ ret = max9296a_set_tpg_mode(priv, entry);
+ if (ret)
+ return ret;
+
+ return regmap_assign_bits(priv->regmap, MAX9296A_MIPI_PHY0,
+ MAX9296A_MIPI_PHY0_FORCE_CSI_OUT_EN, !!entry);
+}
+
+static const struct max_serdes_tpg_entry max9296a_tpg_entries[] = {
+ MAX_TPG_ENTRY_640X480P60_RGB888,
+ MAX_TPG_ENTRY_1920X1080P30_RGB888,
+ MAX_TPG_ENTRY_1920X1080P60_RGB888,
+};
+
+static const struct max_des_ops max9296a_common_ops = {
+ .num_remaps_per_pipe = 16,
+ .tpg_entries = {
+ .num_entries = ARRAY_SIZE(max9296a_tpg_entries),
+ .entries = max9296a_tpg_entries,
+ },
+ .tpg_patterns = BIT(MAX_SERDES_TPG_PATTERN_CHECKERBOARD) |
+ BIT(MAX_SERDES_TPG_PATTERN_GRADIENT),
+ .reg_read = max9296a_reg_read,
+ .reg_write = max9296a_reg_write,
+ .log_pipe_status = max9626a_log_pipe_status,
+ .log_phy_status = max9296a_log_phy_status,
+ .set_enable = max9296a_set_enable,
+ .init = max9296a_init,
+ .init_phy = max9296a_init_phy,
+ .set_phy_mode = max9296a_set_phy_mode,
+ .set_phy_enable = max9296a_set_phy_enable,
+ .set_pipe_remap = max9296a_set_pipe_remap,
+ .set_pipe_remaps_enable = max9296a_set_pipe_remaps_enable,
+ .set_pipe_mode = max9296a_set_pipe_mode,
+ .set_tpg = max9296a_set_tpg,
+ .init_link = max9296a_init_link,
+ .select_links = max9296a_select_links,
+ .set_link_version = max9296a_set_link_version,
+};
+
+static int max9296a_probe(struct i2c_client *client)
+{
+ struct regmap_config i2c_regmap = max9296a_i2c_regmap;
+ struct device *dev = &client->dev;
+ struct max9296a_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);
+
+ i2c_regmap.max_register = priv->info->max_register;
+ priv->regmap = devm_regmap_init_i2c(client, &i2c_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_pwdn))
+ return PTR_ERR(priv->gpiod_pwdn);
+
+ if (priv->gpiod_pwdn) {
+ /* PWDN must be held for 1us for reset */
+ udelay(1);
+
+ gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
+ /* Maximum power-up time (tLOCK) 4ms */
+ usleep_range(4000, 5000);
+ }
+
+ *ops = max9296a_common_ops;
+
+ ops->versions = priv->info->ops->versions;
+ ops->modes = priv->info->ops->modes;
+ ops->needs_single_link_version = priv->info->ops->needs_single_link_version;
+ ops->needs_unique_stream_id = priv->info->ops->needs_unique_stream_id;
+ ops->fix_tx_ids = priv->info->ops->fix_tx_ids;
+ ops->num_phys = priv->info->ops->num_phys;
+ ops->num_pipes = priv->info->ops->num_pipes;
+ ops->num_links = priv->info->ops->num_links;
+ ops->phys_configs = priv->info->ops->phys_configs;
+ ops->set_pipe_enable = priv->info->ops->set_pipe_enable;
+ ops->set_pipe_stream_id = priv->info->ops->set_pipe_stream_id;
+ ops->set_pipe_tunnel_phy = priv->info->ops->set_pipe_tunnel_phy;
+ ops->set_pipe_tunnel_enable = priv->info->ops->set_pipe_tunnel_enable;
+ ops->use_atr = priv->info->ops->use_atr;
+ ops->tpg_mode = priv->info->ops->tpg_mode;
+ priv->des.ops = ops;
+
+ ret = max9296a_reset(priv);
+ if (ret)
+ return ret;
+
+ return max_des_probe(client, &priv->des);
+}
+
+static void max9296a_remove(struct i2c_client *client)
+{
+ struct max9296a_priv *priv = i2c_get_clientdata(client);
+
+ max_des_remove(&priv->des);
+
+ gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
+}
+
+static const struct max_serdes_phys_config max9296a_phys_configs[] = {
+ { { 4, 4 } },
+};
+
+static const struct max_serdes_phys_config max96714_phys_configs[] = {
+ { { 4 } },
+};
+
+static const struct max_des_ops max9296a_ops = {
+ .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) |
+ BIT(MAX_SERDES_GMSL_2_6GBPS),
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE),
+ .set_pipe_stream_id = max9296a_set_pipe_stream_id,
+ .set_pipe_enable = max9296a_set_pipe_enable,
+ .needs_single_link_version = true,
+ .needs_unique_stream_id = true,
+ .phys_configs = {
+ .num_configs = ARRAY_SIZE(max9296a_phys_configs),
+ .configs = max9296a_phys_configs,
+ },
+ .fix_tx_ids = true,
+ .num_pipes = 4,
+ .num_phys = 2,
+ .num_links = 2,
+};
+
+static const struct max9296a_chip_info max9296a_info = {
+ .ops = &max9296a_ops,
+ .max_register = 0x1f00,
+ .use_atr = true,
+ .phy0_lanes_0_1_on_second_phy = true,
+ .pipe_hw_ids = { 0, 1, 2, 3 },
+ .phy_hw_ids = { 1, 2 },
+};
+
+static const struct max_des_ops max96714_ops = {
+ .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_stream_id = max96714_set_pipe_stream_id,
+ .set_pipe_enable = max96714_set_pipe_enable,
+ .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable,
+ .phys_configs = {
+ .num_configs = ARRAY_SIZE(max96714_phys_configs),
+ .configs = max96714_phys_configs,
+ },
+ .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE,
+ .num_pipes = 1,
+ .num_phys = 1,
+ .num_links = 1,
+};
+
+static const struct max9296a_chip_info max96714_info = {
+ .ops = &max96714_ops,
+ .max_register = 0x5011,
+ .polarity_on_physical_lanes = true,
+ .supports_phy_log = true,
+ .adjust_rlms = true,
+ .pipe_hw_ids = { 1 },
+ .phy_hw_ids = { 1 },
+};
+
+static const struct max_des_ops max96714f_ops = {
+ .versions = BIT(MAX_SERDES_GMSL_2_3GBPS),
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) |
+ BIT(MAX_SERDES_GMSL_TUNNEL_MODE),
+ .set_pipe_stream_id = max96714_set_pipe_stream_id,
+ .set_pipe_enable = max96714_set_pipe_enable,
+ .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable,
+ .phys_configs = {
+ .num_configs = ARRAY_SIZE(max96714_phys_configs),
+ .configs = max96714_phys_configs,
+ },
+ .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE,
+ .num_pipes = 1,
+ .num_phys = 1,
+ .num_links = 1,
+};
+
+static const struct max9296a_chip_info max96714f_info = {
+ .ops = &max96714f_ops,
+ .max_register = 0x5011,
+ .polarity_on_physical_lanes = true,
+ .supports_phy_log = true,
+ .adjust_rlms = true,
+ .pipe_hw_ids = { 1 },
+ .phy_hw_ids = { 1 },
+};
+
+static const struct max_des_ops max96716a_ops = {
+ .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_stream_id = max96714_set_pipe_stream_id,
+ .set_pipe_link = max96716a_set_pipe_link,
+ .set_pipe_enable = max96714_set_pipe_enable,
+ .set_pipe_tunnel_phy = max96716a_set_pipe_tunnel_phy,
+ .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable,
+ .use_atr = true,
+ .phys_configs = {
+ .num_configs = ARRAY_SIZE(max9296a_phys_configs),
+ .configs = max9296a_phys_configs,
+ },
+ .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE,
+ .num_pipes = 2,
+ .num_phys = 2,
+ .num_links = 2,
+};
+
+static const struct max9296a_chip_info max96716a_info = {
+ .ops = &max96716a_ops,
+ .max_register = 0x52d6,
+ .has_per_link_reset = true,
+ .phy0_lanes_0_1_on_second_phy = true,
+ .supports_cphy = true,
+ .supports_phy_log = true,
+ .pipe_hw_ids = { 1, 2 },
+ .phy_hw_ids = { 1, 2 },
+};
+
+static const struct max_des_ops max96792a_ops = {
+ .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) |
+ BIT(MAX_SERDES_GMSL_2_6GBPS) |
+ BIT(MAX_SERDES_GMSL_3),
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) |
+ BIT(MAX_SERDES_GMSL_TUNNEL_MODE),
+ .set_pipe_stream_id = max96714_set_pipe_stream_id,
+ .set_pipe_enable = max96714_set_pipe_enable,
+ .set_pipe_tunnel_phy = max96716a_set_pipe_tunnel_phy,
+ .set_pipe_tunnel_enable = max96714_set_pipe_tunnel_enable,
+ .use_atr = true,
+ .phys_configs = {
+ .num_configs = ARRAY_SIZE(max9296a_phys_configs),
+ .configs = max9296a_phys_configs,
+ },
+ .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE,
+ .num_pipes = 2,
+ .num_phys = 2,
+ .num_links = 2,
+};
+
+static const struct max9296a_chip_info max96792a_info = {
+ .ops = &max96792a_ops,
+ .max_register = 0x52d6,
+ .has_per_link_reset = true,
+ .phy0_lanes_0_1_on_second_phy = true,
+ .supports_cphy = true,
+ .supports_phy_log = true,
+ .pipe_hw_ids = { 1, 2 },
+ .phy_hw_ids = { 1, 2 },
+};
+
+static const struct of_device_id max9296a_of_table[] = {
+ { .compatible = "maxim,max9296a", .data = &max9296a_info },
+ { .compatible = "maxim,max96714", .data = &max96714_info },
+ { .compatible = "maxim,max96714f", .data = &max96714f_info },
+ { .compatible = "maxim,max96714r", .data = &max96714f_info },
+ { .compatible = "maxim,max96716a", .data = &max96716a_info },
+ { .compatible = "maxim,max96792a", .data = &max96792a_info },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max9296a_of_table);
+
+static struct i2c_driver max9296a_i2c_driver = {
+ .driver = {
+ .name = "max9296a",
+ .of_match_table = max9296a_of_table,
+ },
+ .probe = max9296a_probe,
+ .remove = max9296a_remove,
+};
+
+module_i2c_driver(max9296a_i2c_driver);
+
+MODULE_DESCRIPTION("Maxim MAX9296A Quad GMSL2 Deserializer Driver");
+MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@...log.com>");
+MODULE_LICENSE("GPL");
--
2.50.0
Powered by blists - more mailing lists