lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ