diff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig index 9d3621ae8d90..d0c70d678699 100644 --- a/drivers/media/i2c/maxim-serdes/Kconfig +++ b/drivers/media/i2c/maxim-serdes/Kconfig @@ -47,3 +47,17 @@ 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" + depends on I2C + depends on VIDEO_DEV + 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..7b25b54e63de --- /dev/null +++ b/drivers/media/i2c/maxim-serdes/max9296a.c @@ -0,0 +1,1461 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Maxim MAX9296A Dual GMSL2/3 Deserializer Driver + * + * Copyright (C) 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#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_CTRL3 0x13 +#define MAX9296A_CTRL3_LOCKED BIT(3) + +#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 0b01 +#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_BACKTOP30 0x325 +#define MAX9296A_BACKTOP30_W_FRAME BIT(7) + +#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_CTRL9 0x5009 +#define MAX9296A_CTRL9_LOCKED_B BIT(3) + +#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_info *info; + const struct max_des_ops *ops; + const struct reg_sequence *rlms_adjust_sequence; + unsigned int rlms_adjust_sequence_len; + unsigned int max_register; + unsigned int pipe_hw_ids[MAX9296A_PIPES_NUM]; + unsigned int phy_hw_ids[MAX9296A_PHYS_NUM]; + bool has_per_link_lock; + 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; +}; + +#define des_to_priv(_des) \ + container_of(_des, struct max9296a_priv, des) + +static int max9296a_wait_for_device(struct max9296a_priv *priv) +{ + unsigned int val; + int ret, err; + + err = read_poll_timeout(regmap_read, ret, + !ret && val, + 100 * USEC_PER_MSEC, + 1 * USEC_PER_SEC, false, + priv->regmap, MAX9296A_REG0, &val); + if (err) + dev_err(priv->dev, "Timeout waiting for deserializer: %d\n", ret); + + return err; +} + +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 __maybe_unused 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 __maybe_unused 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 max9296a_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) +{ + struct max9296a_priv *priv = des_to_priv(des); + int ret; + + if (priv->info->rlms_adjust_sequence) { + ret = regmap_multi_reg_write(priv->regmap, + priv->info->rlms_adjust_sequence, + priv->info->rlms_adjust_sequence_len); + if (ret) + return ret; + } + + /* + * Hold off MIPI packet requests until a frame start is seen. + * Assures the first frame out of the line buffer is whole. + */ + ret = regmap_set_bits(priv->regmap, MAX9296A_BACKTOP30, + MAX9296A_BACKTOP30_W_FRAME); + if (ret) + return ret; + + 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 = is_cphy ? phy->link_frequency + : 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(hw_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(hw_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 (!is_cphy && 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, 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_read_link_locked(struct max9296a_priv *priv, + unsigned int index, bool *locked) +{ + unsigned int reg, mask, val; + int ret; + + if (index == 0) { + reg = MAX9296A_CTRL3; + mask = MAX9296A_CTRL3_LOCKED; + } else { + reg = MAX9296A_CTRL9; + mask = MAX9296A_CTRL9_LOCKED_B; + } + + ret = regmap_read(priv->regmap, reg, &val); + if (ret) + return ret; + + *locked = val & mask; + + return 0; +} + +static int max9296a_wait_for_link_lock(struct max9296a_priv *priv, + unsigned int mask) +{ + unsigned int i; + bool locked; + int ret, err; + + if (!priv->info->has_per_link_lock) { + err = read_poll_timeout(max9296a_read_link_locked, ret, + ret || locked, + 10 * USEC_PER_MSEC, + 200 * USEC_PER_MSEC, true, + priv, 0, &locked); + if (ret) + return ret; + + if (err) + dev_dbg(priv->dev, "Timeout waiting for link lock\n"); + + return err; + } + + for (i = 0; i < priv->des.info->num_links; i++) { + if (!(mask & BIT(i))) + continue; + + err = read_poll_timeout(max9296a_read_link_locked, ret, + ret || locked, + 10 * USEC_PER_MSEC, + 200 * USEC_PER_MSEC, true, + priv, i, &locked); + if (ret) + return ret; + + if (err) { + dev_dbg(priv->dev, + "Timeout waiting for link %u lock\n", i); + + return err; + } + } + + 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->info->num_links == 1) { + ret = max9296a_reset_link(priv, 0); + if (ret) + return ret; + + return max9296a_wait_for_link_lock(priv, BIT(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; + } + + return max9296a_wait_for_link_lock(priv, mask); +} + +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_12GBPS; + unsigned int reg, mask, val; + int ret; + + if (des->info->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_12GBPS) + 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->info->versions & BIT(MAX_SERDES_GMSL_3_12GBPS))) + 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; + unsigned int index; + 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, routed to the first pipe. */ + index = max9296a_pipe_id(priv, &priv->des.pipes[0]); + + ret = regmap_update_bits(priv->regmap, MAX9296A_VPRBS(index), + 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, +}; + +#ifdef CONFIG_VIDEO_ADV_DEBUG +#define MAX9296A_DEBUG_OPS \ + .reg_read = max9296a_reg_read, \ + .reg_write = max9296a_reg_write, +#else +#define MAX9296A_DEBUG_OPS +#endif + +#define MAX9296A_COMMON_INFO \ + .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) + +#define MAX9296A_COMMON_OPS \ + MAX9296A_DEBUG_OPS \ + .log_pipe_status = max9296a_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, \ + .select_links = max9296a_select_links, \ + .set_link_version = max9296a_set_link_version + +static void max9296a_power_off(void *data) +{ + struct max9296a_priv *priv = data; + + gpiod_set_value_cansleep(priv->gpiod_pwdn, 1); +} + +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; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + 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); + + ret = devm_add_action_or_reset(dev, max9296a_power_off, + priv); + if (ret) + return ret; + } + + priv->des.info = priv->info->info; + priv->des.ops = priv->info->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); +} + +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_info max9296a_des_info = { + MAX9296A_COMMON_INFO, + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE), + .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, + .use_atr = true, + .num_pipes = 4, + .num_phys = 2, + .num_links = 2, +}; + +static const struct max_des_ops max9296a_ops = { + MAX9296A_COMMON_OPS, + .set_pipe_stream_id = max9296a_set_pipe_stream_id, + .set_pipe_enable = max9296a_set_pipe_enable, +}; + +static const struct max9296a_chip_info max9296a_info = { + .info = &max9296a_des_info, + .ops = &max9296a_ops, + .max_register = 0x1f00, + .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_info max96714_des_info = { + MAX9296A_COMMON_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), + .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 max_des_ops max96714_ops = { + MAX9296A_COMMON_OPS, + .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, +}; + +/* + * 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. + */ +static const struct reg_sequence max96714_rlms_reg_sequence[] = { + { MAX9296A_RLMS3E(0), 0xfd }, + { MAX9296A_RLMS3F(0), 0x3d }, + { MAX9296A_RLMS49(0), 0xf5 }, + { MAX9296A_RLMS7E(0), 0xa8 }, + { MAX9296A_RLMS7F(0), 0x68 }, + { MAX9296A_RLMSA3(0), 0x30 }, + { MAX9296A_RLMSA5(0), 0x70 }, + { MAX9296A_RLMSD8(0), 0x07 }, +}; + +static const struct max9296a_chip_info max96714_info = { + .info = &max96714_des_info, + .ops = &max96714_ops, + .max_register = 0x5011, + .polarity_on_physical_lanes = true, + .supports_phy_log = true, + .rlms_adjust_sequence = max96714_rlms_reg_sequence, + .rlms_adjust_sequence_len = ARRAY_SIZE(max96714_rlms_reg_sequence), + .pipe_hw_ids = { 1 }, + .phy_hw_ids = { 1 }, +}; + +static const struct max_des_info max96714f_des_info = { + MAX9296A_COMMON_INFO, + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .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 max_des_ops max96714f_ops = { + MAX9296A_COMMON_OPS, + .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, +}; + +static const struct max9296a_chip_info max96714f_info = { + .info = &max96714f_des_info, + .ops = &max96714f_ops, + .max_register = 0x5011, + .polarity_on_physical_lanes = true, + .supports_phy_log = true, + .rlms_adjust_sequence = max96714_rlms_reg_sequence, + .rlms_adjust_sequence_len = ARRAY_SIZE(max96714_rlms_reg_sequence), + .pipe_hw_ids = { 1 }, + .phy_hw_ids = { 1 }, +}; + +static const struct max_des_info max96716a_des_info = { + MAX9296A_COMMON_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), + .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 max_des_ops max96716a_ops = { + MAX9296A_COMMON_OPS, + .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, +}; + +static const struct max9296a_chip_info max96716a_info = { + .info = &max96716a_des_info, + .ops = &max96716a_ops, + .max_register = 0x52d6, + .has_per_link_lock = true, + .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_info max96792a_des_info = { + MAX9296A_COMMON_INFO, + .versions = BIT(MAX_SERDES_GMSL_2_3GBPS) | + BIT(MAX_SERDES_GMSL_2_6GBPS) | + BIT(MAX_SERDES_GMSL_3_12GBPS), + .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) | + BIT(MAX_SERDES_GMSL_TUNNEL_MODE), + .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 max_des_ops max96792a_ops = { + MAX9296A_COMMON_OPS, + .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, +}; + +static const struct max9296a_chip_info max96792a_info = { + .info = &max96792a_des_info, + .ops = &max96792a_ops, + .max_register = 0x52d6, + .has_per_link_lock = true, + .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_IMPORT_NS("MAX_SERDES"); +MODULE_DESCRIPTION("Maxim MAX9296A Dual GMSL2/3 Deserializer Driver"); +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_LICENSE("GPL");