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: <20250702144523.GB3830050@ragnatech.se>
Date: Wed, 2 Jul 2025 16:45:23 +0200
From: Niklas Söderlund <niklas.soderlund@...natech.se>
To: Cosmin Tanislav <demonsingur@...il.com>
Cc: Cosmin Tanislav <cosmin.tanislav@...log.com>,
	Tomi Valkeinen <tomi.valkeinen+renesas@...asonboard.com>,
	Mauro Carvalho Chehab <mchehab@...nel.org>,
	Rob Herring <robh@...nel.org>,
	Julien Massot <julien.massot@...labora.com>,
	Sakari Ailus <sakari.ailus@...ux.intel.com>,
	Laurent Pinchart <laurent.pinchart@...asonboard.com>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	Linus Walleij <linus.walleij@...aro.org>,
	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 19/24] media: i2c: maxim-serdes: add MAX96724 driver

Hi Cosmin,

Thanks for your great work, and for taking the time to consider the TPG 
use-case I had with the driver in staging, much appreciated!

On 2025-07-02 16:20:45 +0300, Cosmin Tanislav wrote:
> Add a new MAX96724 driver that also supports MAX96712, MAX96724F
> and MAX96724R.
> 
> Integrate it with the common deserializer framework, while keeping
> compatibility with existing usecases, avoiding code duplication, and
> also enabling more features across all chips.
> 
> Signed-off-by: Cosmin Tanislav <demonsingur@...il.com>

I have tested this on R-Car V3U that uses MAX96712 in D-PHY mode, V4H 
that uses MAX96712 in C-PHY mode and V4M that uses MAX96724 in D-PHY 
mode. And all my test-cases using the TPG (both patterns) pass just as 
before with the driver in staging!

For the TPG usage to replace the driver in staging,

Tested-by: Niklas Söderlund <niklas.soderlund+renesas@...natech.se>

I did have to pull in this patch from Tomi [1], as without it the change 
in how link freqs are retrieved do not function with the R-Car pipeline.  

1.  https://lore.kernel.org/all/20250530-rcar-streams-v3-1-026655df7138@ideasonboard.com/

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

-- 
Kind Regards,
Niklas Söderlund

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ