[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <ab41191cb9aab730414c4a93a2d37ccabd394fe0.camel@collabora.com>
Date: Thu, 03 Jul 2025 14:19:19 +0200
From: Julien Massot <julien.massot@...labora.com>
To: Cosmin Tanislav <demonsingur@...il.com>, 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>, 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
Subject: Re: [PATCH v5 20/24] media: i2c: maxim-serdes: add MAX9296A driver
On Wed, 2025-07-02 at 16:20 +0300, Cosmin Tanislav wrote:
> 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),
#ifdef CONFIG_VIDEO_ADV_DEBUG
> + .reg_read = max9296a_reg_read,
> + .reg_write = max9296a_reg_write,
#endif
To make it clear that the registers are not read or modified outside of this file ?
> + .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 reg_sequence max96714_rlms_reg_sequence[] = {
{ 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 },
};
> +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,
Instead of having a boolean here and a max96714 specific reg_sequence
in max9296a_init_link_rlms,
then you can have
.rlms_adjust_sequence = &max96714_rlms_reg_sequence,
> + .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");
Regards,
--
Julien
Powered by blists - more mailing lists