[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250702132104.1537926-19-demonsingur@gmail.com>
Date: Wed, 2 Jul 2025 16:20:44 +0300
From: Cosmin Tanislav <demonsingur@...il.com>
To: Cosmin Tanislav <cosmin.tanislav@...log.com>,
Tomi Valkeinen <tomi.valkeinen+renesas@...asonboard.com>,
Mauro Carvalho Chehab <mchehab@...nel.org>,
Rob Herring <robh@...nel.org>,
Niklas Söderlund <niklas.soderlund@...natech.se>,
Julien Massot <julien.massot@...labora.com>,
Sakari Ailus <sakari.ailus@...ux.intel.com>,
Laurent Pinchart <laurent.pinchart@...asonboard.com>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Linus Walleij <linus.walleij@...aro.org>
Cc: linux-media@...r.kernel.org,
devicetree@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org,
linux-staging@...ts.linux.dev,
linux-gpio@...r.kernel.org,
Cosmin Tanislav <demonsingur@...il.com>
Subject: [PATCH v5 18/24] media: i2c: maxim-serdes: add MAX96717 driver
Add a new MAX96717 driver that also supports MAX9295A, MAX96717F and
MAX96793.
Integrate it with the common serializer 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 | 16 +
drivers/media/i2c/maxim-serdes/Makefile | 1 +
drivers/media/i2c/maxim-serdes/max96717.c | 1685 +++++++++++++++++++++
3 files changed, 1702 insertions(+)
create mode 100644 drivers/media/i2c/maxim-serdes/max96717.c
diff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig
index cae1d5a1293e..648cb891eefe 100644
--- a/drivers/media/i2c/maxim-serdes/Kconfig
+++ b/drivers/media/i2c/maxim-serdes/Kconfig
@@ -14,3 +14,19 @@ config VIDEO_MAXIM_SERDES
To compile this driver as a module, choose M here: the module
will be called max_serdes.
+
+config VIDEO_MAX96717
+ tristate "Maxim MAX96717 Serializer support"
+ depends on COMMON_CLK
+ select VIDEO_MAXIM_SERDES
+ select GENERIC_PINCONF
+ select GENERIC_PINCTRL_GROUPS
+ select GENERIC_PINMUX_FUNCTIONS
+ select GPIOLIB
+ help
+ This driver supports the Maxim MAX9295A, MAX96717, MAX96717F,
+ MAX96793 Serializers, which receive video on a MIPI CSI-2
+ interface and output it on a GMSL2/3 link.
+
+ To compile this driver as a module, choose M here: the module
+ will be called max96717.
diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile
index b54326a5c81b..04abda6a5437 100644
--- a/drivers/media/i2c/maxim-serdes/Makefile
+++ b/drivers/media/i2c/maxim-serdes/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
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
diff --git a/drivers/media/i2c/maxim-serdes/max96717.c b/drivers/media/i2c/maxim-serdes/max96717.c
new file mode 100644
index 000000000000..60b285e547b7
--- /dev/null
+++ b/drivers/media/i2c/maxim-serdes/max96717.c
@@ -0,0 +1,1685 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim MAX96717 GMSL2 Serializer Driver
+ *
+ * Copyright (C) 2025 Analog Devices Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/gpio/driver.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/regmap.h>
+
+#include "max_ser.h"
+
+#define MAX96717_REG0 0x0
+
+#define MAX96717_REG2 0x2
+#define MAX96717_REG2_VID_TX_EN_P(p) BIT(4 + (p))
+
+#define MAX96717_REG3 0x3
+#define MAX96717_REG3_RCLKSEL GENMASK(1, 0)
+#define MAX96717_REG3_RCLK_ALT BIT(2)
+
+#define MAX96717_REG6 0x6
+#define MAX96717_REG6_RCLKEN BIT(5)
+
+#define MAX96717_I2C_2(x) (0x42 + (x) * 0x2)
+#define MAX96717_I2C_2_SRC GENMASK(7, 1)
+
+#define MAX96717_I2C_3(x) (0x43 + (x) * 0x2)
+#define MAX96717_I2C_3_DST GENMASK(7, 1)
+
+#define MAX96717_TX3(p) (0x53 + (p) * 0x4)
+#define MAX96717_TX3_TX_STR_SEL GENMASK(1, 0)
+
+#define MAX96717_VIDEO_TX0(p) (0x100 + (p) * 0x8)
+#define MAX96717_VIDEO_TX0_AUTO_BPP BIT(3)
+
+#define MAX96717_VIDEO_TX1(p) (0x101 + (p) * 0x8)
+#define MAX96717_VIDEO_TX1_BPP GENMASK(5, 0)
+
+#define MAX96717_VIDEO_TX2(p) (0x102 + (p) * 0x8)
+#define MAX96717_VIDEO_TX2_PCLKDET BIT(7)
+#define MAX96717_VIDEO_TX2_DRIFT_DET_EN BIT(1)
+
+#define MAX96717_VTX0(p) (0x1c8 + (p) * 0x43)
+#define MAX96717_VTX0_VTG_MODE GENMASK(1, 0)
+#define MAX96717_VTX0_VTG_MODE_FREE_RUNNING 0b11
+#define MAX96717_VTX0_DE_INV BIT(2)
+#define MAX96717_VTX0_HS_INV BIT(3)
+#define MAX96717_VTX0_VS_INV BIT(4)
+#define MAX96717_VTX0_GEN_DE BIT(5)
+#define MAX96717_VTX0_GEN_HS BIT(6)
+#define MAX96717_VTX0_GEN_VS BIT(7)
+
+#define MAX96717_VTX1(p) (0x1c9 + (p) * 0x43)
+#define MAX96717_VTX1_PATGEN_CLK_SRC GENMASK(3, 1)
+#define MAX96717_VTX1_PATGEN_CLK_SRC_25MHZ 0b100
+#define MAX96717_VTX1_PATGEN_CLK_SRC_75MHZ 0b101
+#define MAX96717_VTX1_PATGEN_CLK_SRC_150MHZ 0b110
+#define MAX96717_VTX1_PATGEN_CLK_SRC_375MHZ 0b111
+
+#define MAX96717_VTX2_VS_DLY_2(p) (0x1ca + (p) * 0x43)
+#define MAX96717_VTX5_VS_HIGH_2(p) (0x1cd + (p) * 0x43)
+#define MAX96717_VTX8_VS_LOW_2(p) (0x1d0 + (p) * 0x43)
+#define MAX96717_VTX11_V2H_2(p) (0x1d3 + (p) * 0x43)
+#define MAX96717_VTX14_HS_HIGH_1(p) (0x1d6 + (p) * 0x43)
+#define MAX96717_VTX16_HS_LOW_1(p) (0x1d8 + (p) * 0x43)
+#define MAX96717_VTX18_HS_CNT_1(p) (0x1da + (p) * 0x43)
+#define MAX96717_VTX20_V2D_2(p) (0x1dc + (p) * 0x43)
+#define MAX96717_VTX23_DE_HIGH_1(p) (0x1df + (p) * 0x43)
+#define MAX96717_VTX25_DE_LOW_1(p) (0x1e1 + (p) * 0x43)
+#define MAX96717_VTX27_DE_CNT_1(p) (0x1e3 + (p) * 0x43)
+#define MAX96717_VTX29(p) (0x1e5 + (p) * 0x43)
+
+#define MAX96717_VTX29_PATGEN_MODE GENMASK(1, 0)
+#define MAX96717_VTX29_PATGEN_MODE_DISABLED 0b00
+#define MAX96717_VTX29_PATGEN_MODE_CHECKER 0b01
+#define MAX96717_VTX29_PATGEN_MODE_GRADIENT 0b10
+
+#define MAX96717_VTX30_GRAD_INCR(p) (0x1e6 + (p) * 0x43)
+#define MAX96717_VTX31_CHKR_A_L(p) (0x1e7 + (p) * 0x43)
+#define MAX96717_VTX34_CHKR_B_L(p) (0x1ea + (p) * 0x43)
+#define MAX96717_VTX37_CHKR_RPT_A(p) (0x1ed + (p) * 0x43)
+#define MAX96717_VTX38_CHKR_RPT_B(p) (0x1ee + (p) * 0x43)
+#define MAX96717_VTX39_CHKR_ALT(p) (0x1ef + (p) * 0x43)
+
+#define MAX96717_GPIO_A(x) (0x2be + (x) * 0x3)
+#define MAX96717_GPIO_A_GPIO_OUT_DIS BIT(0)
+#define MAX96717_GPIO_A_GPIO_TX_EN BIT(1)
+#define MAX96717_GPIO_A_GPIO_RX_EN BIT(2)
+#define MAX96717_GPIO_A_GPIO_IN BIT(3)
+#define MAX96717_GPIO_A_GPIO_OUT BIT(4)
+#define MAX96717_GPIO_A_TX_COMP_EN BIT(5)
+#define MAX96717_GPIO_A_RES_CFG BIT(7)
+
+#define MAX96717_GPIO_B(x) (0x2bf + (x) * 0x3)
+#define MAX96717_GPIO_B_GPIO_TX_ID GENMASK(4, 0)
+#define MAX96717_GPIO_B_OUT_TYPE BIT(5)
+#define MAX96717_GPIO_B_PULL_UPDN_SEL GENMASK(7, 6)
+#define MAX96717_GPIO_B_PULL_UPDN_SEL_NONE 0b00
+#define MAX96717_GPIO_B_PULL_UPDN_SEL_PU 0b01
+#define MAX96717_GPIO_B_PULL_UPDN_SEL_PD 0b10
+
+#define MAX96717_GPIO_C(x) (0x2c0 + (x) * 0x3)
+#define MAX96717_GPIO_C_GPIO_RX_ID GENMASK(4, 0)
+
+#define MAX96717_CMU2 0x302
+#define MAX96717_CMU2_PFDDIV_RSHORT GENMASK(6, 4)
+#define MAX96717_CMU2_PFDDIV_RSHORT_1_1V 0b001
+
+#define MAX96717_FRONTTOP_0 0x308
+#define MAX96717_FRONTTOP_0_CLK_SEL_P(x) BIT(x)
+#define MAX96717_FRONTTOP_0_START_PORT(x) BIT((x) + 4)
+
+#define MAX96717_FRONTTOP_1(p) (0x309 + (p) * 0x2)
+#define MAX96717_FRONTTOP_2(p) (0x30a + (p) * 0x2)
+
+#define MAX96717_FRONTTOP_9 0x311
+#define MAX96717_FRONTTOP_9_START_PORT(p, x) BIT((p) + (x) * 4)
+
+#define MAX96717_FRONTTOP_10 0x312
+#define MAX96717_FRONTTOP_10_BPP8DBL(p) BIT(p)
+
+#define MAX96717_FRONTTOP_11 0x313
+#define MAX96717_FRONTTOP_11_BPP10DBL(p) BIT(p)
+#define MAX96717_FRONTTOP_11_BPP12DBL(p) BIT((p) + 4)
+
+#define MAX96717_FRONTTOP_12(p, x) (0x314 + (p) * 0x2 + (x))
+#define MAX96717_MEM_DT_SEL GENMASK(5, 0)
+#define MAX96717_MEM_DT_EN BIT(6)
+
+#define MAX96717_FRONTTOP_20(p) (0x31c + (p) * 0x1)
+#define MAX96717_FRONTTOP_20_SOFT_BPP_EN BIT(5)
+#define MAX96717_FRONTTOP_20_SOFT_BPP GENMASK(4, 0)
+
+#define MAX96717_MIPI_RX0 0x330
+#define MAX96717_MIPI_RX0_NONCONTCLK_EN BIT(6)
+
+#define MAX96717_MIPI_RX1 0x331
+#define MAX96717_MIPI_RX1_CTRL_NUM_LANES GENMASK(5, 4)
+
+#define MAX96717_MIPI_RX2 0x332
+#define MAX96717_MIPI_RX2_PHY1_LANE_MAP GENMASK(7, 4)
+
+#define MAX96717_MIPI_RX3 0x333
+#define MAX96717_MIPI_RX3_PHY2_LANE_MAP GENMASK(3, 0)
+
+#define MAX96717_MIPI_RX4 0x334
+#define MAX96717_MIPI_RX4_PHY1_POL_MAP GENMASK(5, 4)
+
+#define MAX96717_MIPI_RX5 0x335
+#define MAX96717_MIPI_RX5_PHY2_POL_MAP GENMASK(1, 0)
+#define MAX96717_MIPI_RX5_PHY2_POL_MAP_CLK BIT(2)
+
+#define MAX96717_EXTA(x) (0x3dc + (x))
+
+#define MAX96717_EXT11 0x383
+#define MAX96717_EXT11_TUN_MODE BIT(7)
+
+#define MAX96717_EXT21 0x38d
+#define MAX96717_EXT22 0x38e
+#define MAX96717_EXT23 0x38f
+#define MAX96717_EXT24 0x390
+
+#define MAX96717_REF_VTG0 0x3f0
+#define MAX96717_REF_VTG0_REFGEN_EN BIT(0)
+#define MAX96717_REF_VTG0_REFGEN_RST BIT(1)
+#define MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ_ALT\
+ BIT(3)
+#define MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ GENMASK(5, 4)
+
+#define MAX96717_PIO_SLEW_0 0x56f
+#define MAX96717_PIO_SLEW_0_PIO00_SLEW GENMASK(1, 0)
+#define MAX96717_PIO_SLEW_0_PIO01_SLEW GENMASK(3, 2)
+#define MAX96717_PIO_SLEW_0_PIO02_SLEW GENMASK(5, 4)
+
+#define MAX96717_PIO_SLEW_1 0x570
+#define MAX96717_PIO_SLEW_1_PIO05_SLEW GENMASK(3, 2)
+#define MAX96717_PIO_SLEW_1_PIO06_SLEW GENMASK(5, 4)
+
+#define MAX96717_PIO_SLEW_2 0x571
+#define MAX96717_PIO_SLEW_2_PIO010_SLEW GENMASK(5, 4)
+#define MAX96717_PIO_SLEW_2_PIO011_SLEW GENMASK(7, 6)
+
+#define MAX96717_PIO_SLEW_FASTEST 0b00
+
+#define MAX96717_BIAS_PULL_STRENGTH_1000000_OHM 1000000U
+#define MAX96717_BIAS_PULL_STRENGTH_40000_OHM 40000U
+
+#define MAX96717_DEFAULT_CLKOUT_RATE 24000000UL
+
+#define MAX96717_NAME "max96717"
+#define MAX96717_PINCTRL_NAME MAX96717_NAME "-pinctrl"
+#define MAX96717_GPIOCHIP_NAME MAX96717_NAME "-gpiochip"
+#define MAX96717_GPIO_NUM 11
+#define MAX96717_RCLK_ALT_MFP 2
+#define MAX96717_RCLK_MFP 4
+#define MAX96717_PIPES_NUM 4
+#define MAX96717_PHYS_NUM 2
+
+struct max96717_priv {
+ struct max_ser ser;
+ struct pinctrl_desc pctldesc;
+ struct gpio_chip gc;
+ const struct max96717_chip_info *info;
+
+ struct device *dev;
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct pinctrl_dev *pctldev;
+
+ struct clk_hw clk_hw;
+ u8 pll_predef_index;
+};
+
+struct max96717_chip_info {
+ bool supports_3_data_lanes;
+ bool supports_noncontinuous_clock;
+ bool supports_pkt_cnt;
+ unsigned int modes;
+ unsigned int num_pipes;
+ unsigned int num_dts_per_pipe;
+ unsigned int pipe_hw_ids[MAX96717_PIPES_NUM];
+ unsigned int num_phys;
+ unsigned int phy_hw_ids[MAX96717_PHYS_NUM];
+};
+
+#define ser_to_priv(_ser) \
+ container_of(_ser, struct max96717_priv, ser)
+
+static inline struct max96717_priv *clk_hw_to_priv(struct clk_hw *hw)
+{
+ return container_of(hw, struct max96717_priv, clk_hw);
+}
+
+static const struct regmap_config max96717_i2c_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x1f00,
+};
+
+static int max96717_wait_for_device(struct max96717_priv *priv)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < 10; i++) {
+ unsigned int val;
+
+ ret = regmap_read(priv->regmap, MAX96717_REG0, &val);
+ if (!ret && val)
+ return 0;
+
+ msleep(100);
+
+ dev_err(priv->dev, "Retry %u waiting for serializer: %d\n", i, ret);
+ }
+
+ return ret;
+}
+
+#define MAX96717_PIN(n) \
+ PINCTRL_PIN(n, "mfp" __stringify(n))
+
+static const struct pinctrl_pin_desc max96717_pins[] = {
+ MAX96717_PIN(0),
+ MAX96717_PIN(1),
+ MAX96717_PIN(2),
+ MAX96717_PIN(3),
+ MAX96717_PIN(4),
+ MAX96717_PIN(5),
+ MAX96717_PIN(6),
+ MAX96717_PIN(7),
+ MAX96717_PIN(8),
+ MAX96717_PIN(9),
+ MAX96717_PIN(10),
+};
+
+#define MAX96717_GROUP_PINS(name, ...) \
+ static const unsigned int name ## _pins[] = { __VA_ARGS__ }
+
+MAX96717_GROUP_PINS(mfp0, 0);
+MAX96717_GROUP_PINS(mfp1, 1);
+MAX96717_GROUP_PINS(mfp2, 2);
+MAX96717_GROUP_PINS(mfp3, 3);
+MAX96717_GROUP_PINS(mfp4, 4);
+MAX96717_GROUP_PINS(mfp5, 5);
+MAX96717_GROUP_PINS(mfp6, 6);
+MAX96717_GROUP_PINS(mfp7, 7);
+MAX96717_GROUP_PINS(mfp8, 8);
+MAX96717_GROUP_PINS(mfp9, 9);
+MAX96717_GROUP_PINS(mfp10, 10);
+
+#define MAX96717_GROUP(name) \
+ PINCTRL_PINGROUP(__stringify(name), name ## _pins, ARRAY_SIZE(name ## _pins))
+
+static const struct pingroup max96717_ctrl_groups[] = {
+ MAX96717_GROUP(mfp0),
+ MAX96717_GROUP(mfp1),
+ MAX96717_GROUP(mfp2),
+ MAX96717_GROUP(mfp3),
+ MAX96717_GROUP(mfp4),
+ MAX96717_GROUP(mfp5),
+ MAX96717_GROUP(mfp6),
+ MAX96717_GROUP(mfp7),
+ MAX96717_GROUP(mfp8),
+ MAX96717_GROUP(mfp9),
+ MAX96717_GROUP(mfp10),
+};
+
+#define MAX96717_FUNC_GROUPS(name, ...) \
+ static const char * const name ## _groups[] = { __VA_ARGS__ }
+
+MAX96717_FUNC_GROUPS(gpio, "mfp0", "mfp1", "mfp2", "mfp3", "mfp4", "mfp5",
+ "mfp6", "mfp7", "mfp8", "mfp9", "mfp10");
+MAX96717_FUNC_GROUPS(rclkout, "mfp2", "mfp4");
+
+enum max96717_func {
+ max96717_func_gpio,
+ max96717_func_rclkout,
+};
+
+#define MAX96717_FUNC(name) \
+ [max96717_func_ ## name] = \
+ PINCTRL_PINFUNCTION(__stringify(name), name ## _groups, \
+ ARRAY_SIZE(name ## _groups))
+
+static const struct pinfunction max96717_functions[] = {
+ MAX96717_FUNC(gpio),
+ MAX96717_FUNC(rclkout),
+};
+
+#define MAX96717_PINCTRL_X(x) (PIN_CONFIG_END + (x))
+#define MAX96717_PINCTRL_JITTER_COMPENSATION_EN MAX96717_PINCTRL_X(1)
+#define MAX96717_PINCTRL_TX_ID MAX96717_PINCTRL_X(2)
+#define MAX96717_PINCTRL_RX_ID MAX96717_PINCTRL_X(3)
+#define MAX96717_PINCTRL_PULL_STRENGTH_HIGH MAX96717_PINCTRL_X(4)
+#define MAX96717_PINCTRL_INPUT_VALUE MAX96717_PINCTRL_X(5)
+#define MAX96717_PINCTRL_TX_EN MAX96717_PINCTRL_X(6)
+#define MAX96717_PINCTRL_RX_EN MAX96717_PINCTRL_X(7)
+
+static const struct pinconf_generic_params max96717_cfg_params[] = {
+ { "maxim,jitter-compensation", MAX96717_PINCTRL_JITTER_COMPENSATION_EN, 0 },
+ { "maxim,tx-id", MAX96717_PINCTRL_TX_ID, 0 },
+ { "maxim,rx-id", MAX96717_PINCTRL_RX_ID, 0 },
+};
+
+static int max96717_ctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(max96717_ctrl_groups);
+}
+
+static const char *max96717_ctrl_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ return max96717_ctrl_groups[selector].name;
+}
+
+static int max96717_ctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const unsigned int **pins,
+ unsigned int *num_pins)
+{
+ *pins = (unsigned int *)max96717_ctrl_groups[selector].pins;
+ *num_pins = max96717_ctrl_groups[selector].npins;
+
+ return 0;
+}
+
+static int max96717_get_pin_config_reg(unsigned int offset, u32 param,
+ unsigned int *reg, unsigned int *mask,
+ unsigned int *val)
+{
+ *reg = MAX96717_GPIO_A(offset);
+
+ switch (param) {
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ *mask = MAX96717_GPIO_A_GPIO_OUT_DIS;
+ *val = 0b0;
+ return 0;
+ case PIN_CONFIG_INPUT_ENABLE:
+ *mask = MAX96717_GPIO_A_GPIO_OUT_DIS;
+ *val = 0b1;
+ return 0;
+ case MAX96717_PINCTRL_TX_EN:
+ *mask = MAX96717_GPIO_A_GPIO_TX_EN;
+ *val = 0b1;
+ return 0;
+ case MAX96717_PINCTRL_RX_EN:
+ *mask = MAX96717_GPIO_A_GPIO_RX_EN;
+ *val = 0b1;
+ return 0;
+ case MAX96717_PINCTRL_INPUT_VALUE:
+ *mask = MAX96717_GPIO_A_GPIO_IN;
+ *val = 0b1;
+ return 0;
+ case PIN_CONFIG_OUTPUT:
+ *mask = MAX96717_GPIO_A_GPIO_OUT;
+ *val = 0b1;
+ return 0;
+ case MAX96717_PINCTRL_JITTER_COMPENSATION_EN:
+ *mask = MAX96717_GPIO_A_TX_COMP_EN;
+ *val = 0b1;
+ return 0;
+ case MAX96717_PINCTRL_PULL_STRENGTH_HIGH:
+ *mask = MAX96717_GPIO_A_RES_CFG;
+ *val = 0b1;
+ return 0;
+ }
+
+ *reg = MAX96717_GPIO_B(offset);
+
+ switch (param) {
+ case MAX96717_PINCTRL_TX_ID:
+ *mask = MAX96717_GPIO_B_GPIO_TX_ID;
+ return 0;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ *mask = MAX96717_GPIO_B_OUT_TYPE;
+ *val = 0b0;
+ return 0;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ *mask = MAX96717_GPIO_B_OUT_TYPE;
+ *val = 0b1;
+ return 0;
+ case PIN_CONFIG_BIAS_DISABLE:
+ *mask = MAX96717_GPIO_B_PULL_UPDN_SEL;
+ *val = MAX96717_GPIO_B_PULL_UPDN_SEL_NONE;
+ return 0;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ *mask = MAX96717_GPIO_B_PULL_UPDN_SEL;
+ *val = MAX96717_GPIO_B_PULL_UPDN_SEL_PD;
+ return 0;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ *mask = MAX96717_GPIO_B_PULL_UPDN_SEL;
+ *val = MAX96717_GPIO_B_PULL_UPDN_SEL_PU;
+ return 0;
+ }
+
+ switch (param) {
+ case PIN_CONFIG_SLEW_RATE:
+ if (offset < 3) {
+ *reg = MAX96717_PIO_SLEW_0;
+ if (offset == 0)
+ *mask = MAX96717_PIO_SLEW_0_PIO00_SLEW;
+ else if (offset == 1)
+ *mask = MAX96717_PIO_SLEW_0_PIO01_SLEW;
+ else
+ *mask = MAX96717_PIO_SLEW_0_PIO02_SLEW;
+ } else if (offset < 5) {
+ *reg = MAX96717_PIO_SLEW_1;
+ if (offset == 3)
+ *mask = MAX96717_PIO_SLEW_1_PIO05_SLEW;
+ else
+ *mask = MAX96717_PIO_SLEW_1_PIO06_SLEW;
+ } else if (offset < 7) {
+ return -EINVAL;
+ } else if (offset < 9) {
+ *reg = MAX96717_PIO_SLEW_2;
+ if (offset == 7)
+ *mask = MAX96717_PIO_SLEW_2_PIO010_SLEW;
+ else
+ *mask = MAX96717_PIO_SLEW_2_PIO011_SLEW;
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+ case MAX96717_PINCTRL_RX_ID:
+ *reg = MAX96717_GPIO_C(offset);
+ *mask = MAX96717_GPIO_C_GPIO_RX_ID;
+ return 0;
+ default:
+ return -ENOTSUPP;
+ }
+}
+
+static int max96717_conf_pin_config_get(struct pinctrl_dev *pctldev,
+ unsigned int offset,
+ unsigned long *config)
+{
+ struct max96717_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+ u32 param = pinconf_to_config_param(*config);
+ unsigned int reg, mask, val, en_val;
+ int ret;
+
+ ret = max96717_get_pin_config_reg(offset, param, ®, &mask, &en_val);
+ if (ret)
+ return ret;
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ case MAX96717_PINCTRL_JITTER_COMPENSATION_EN:
+ case MAX96717_PINCTRL_TX_EN:
+ case MAX96717_PINCTRL_RX_EN:
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ case PIN_CONFIG_INPUT_ENABLE:
+ ret = regmap_read(priv->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ val = field_get(mask, val) == en_val;
+ if (!val)
+ return -EINVAL;
+
+ break;
+ case MAX96717_PINCTRL_PULL_STRENGTH_HIGH:
+ case MAX96717_PINCTRL_INPUT_VALUE:
+ case PIN_CONFIG_OUTPUT:
+ ret = regmap_read(priv->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ val = field_get(mask, val) == en_val;
+ break;
+ case MAX96717_PINCTRL_TX_ID:
+ case MAX96717_PINCTRL_RX_ID:
+ case PIN_CONFIG_SLEW_RATE:
+ ret = regmap_read(priv->regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ val = field_get(mask, val);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ *config = pinconf_to_config_packed(MAX96717_PINCTRL_PULL_STRENGTH_HIGH, 0);
+
+ ret = max96717_conf_pin_config_get(pctldev, offset, config);
+ if (ret)
+ return ret;
+
+ val = pinconf_to_config_argument(*config);
+ if (val)
+ val = MAX96717_BIAS_PULL_STRENGTH_1000000_OHM;
+ else
+ val = MAX96717_BIAS_PULL_STRENGTH_40000_OHM;
+
+ break;
+ case MAX96717_PINCTRL_TX_ID:
+ *config = pinconf_to_config_packed(MAX96717_PINCTRL_TX_EN, 0);
+
+ ret = max96717_conf_pin_config_get(pctldev, offset, config);
+ if (ret)
+ return ret;
+
+ break;
+ case MAX96717_PINCTRL_RX_ID:
+ *config = pinconf_to_config_packed(MAX96717_PINCTRL_RX_EN, 0);
+
+ ret = max96717_conf_pin_config_get(pctldev, offset, config);
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ break;
+ }
+
+ *config = pinconf_to_config_packed(param, val);
+
+ return 0;
+}
+
+static int max96717_conf_pin_config_set_one(struct max96717_priv *priv,
+ unsigned int offset,
+ unsigned long config)
+{
+ u32 param = pinconf_to_config_param(config);
+ u32 arg = pinconf_to_config_argument(config);
+ unsigned int reg, mask, val, en_val;
+ int ret;
+
+ ret = max96717_get_pin_config_reg(offset, param, ®, &mask, &en_val);
+ if (ret)
+ return ret;
+
+ switch (param) {
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ val = field_prep(mask, en_val);
+
+ ret = regmap_update_bits(priv->regmap, reg, mask, val);
+ break;
+ case MAX96717_PINCTRL_JITTER_COMPENSATION_EN:
+ case MAX96717_PINCTRL_PULL_STRENGTH_HIGH:
+ case MAX96717_PINCTRL_TX_EN:
+ case MAX96717_PINCTRL_RX_EN:
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ case PIN_CONFIG_INPUT_ENABLE:
+ case PIN_CONFIG_OUTPUT:
+ val = field_prep(mask, arg ? en_val : ~en_val);
+
+ ret = regmap_update_bits(priv->regmap, reg, mask, val);
+ break;
+ case MAX96717_PINCTRL_TX_ID:
+ case MAX96717_PINCTRL_RX_ID:
+ case PIN_CONFIG_SLEW_RATE:
+ val = field_prep(mask, arg);
+
+ ret = regmap_update_bits(priv->regmap, reg, mask, val);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ if (ret)
+ return ret;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ arg = arg >= MAX96717_BIAS_PULL_STRENGTH_1000000_OHM;
+ config = pinconf_to_config_packed(MAX96717_PINCTRL_PULL_STRENGTH_HIGH, arg);
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+ case PIN_CONFIG_OUTPUT:
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT_ENABLE, 1);
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ config = pinconf_to_config_packed(MAX96717_PINCTRL_RX_EN, 0);
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+ case MAX96717_PINCTRL_TX_ID:
+ config = pinconf_to_config_packed(MAX96717_PINCTRL_TX_EN, 1);
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+ case MAX96717_PINCTRL_RX_ID:
+ config = pinconf_to_config_packed(MAX96717_PINCTRL_RX_EN, 1);
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int max96717_conf_pin_config_set(struct pinctrl_dev *pctldev,
+ unsigned int offset,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct max96717_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+ int ret;
+
+ while (num_configs--) {
+ unsigned long config = *configs;
+
+ ret = max96717_conf_pin_config_set_one(priv, offset, config);
+ if (ret)
+ return ret;
+
+ configs++;
+ }
+
+ return 0;
+}
+
+static int max96717_mux_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(max96717_functions);
+}
+
+static const char *max96717_mux_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ return max96717_functions[selector].name;
+}
+
+static int max96717_mux_get_groups(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const char * const **groups,
+ unsigned int * const num_groups)
+{
+ *groups = max96717_functions[selector].groups;
+ *num_groups = max96717_functions[selector].ngroups;
+
+ return 0;
+}
+
+static int max96717_mux_set_rclkout(struct max96717_priv *priv, unsigned int group)
+{
+ unsigned long config;
+ int ret;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_SLEW_RATE,
+ MAX96717_PIO_SLEW_FASTEST);
+ ret = max96717_conf_pin_config_set_one(priv, group, config);
+ if (ret)
+ return ret;
+
+ return regmap_assign_bits(priv->regmap, MAX96717_REG3,
+ MAX96717_REG3_RCLK_ALT,
+ group == MAX96717_RCLK_ALT_MFP);
+}
+
+static int max96717_mux_set(struct pinctrl_dev *pctldev, unsigned int selector,
+ unsigned int group)
+{
+ struct max96717_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+ switch (selector) {
+ case max96717_func_rclkout:
+ return max96717_mux_set_rclkout(priv, group);
+ }
+
+ return 0;
+}
+
+static int max96717_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ unsigned long config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT_ENABLE, 0);
+ struct max96717_priv *priv = gpiochip_get_data(gc);
+ int ret;
+
+ ret = max96717_conf_pin_config_get(priv->pctldev, offset, &config);
+ if (ret)
+ return ret;
+
+ return pinconf_to_config_argument(config) ? GPIO_LINE_DIRECTION_OUT
+ : GPIO_LINE_DIRECTION_IN;
+}
+
+static int max96717_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+ unsigned long config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1);
+ struct max96717_priv *priv = gpiochip_get_data(gc);
+
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+}
+
+static int max96717_gpio_direction_output(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ unsigned long config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
+ struct max96717_priv *priv = gpiochip_get_data(gc);
+
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+}
+
+static int max96717_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ unsigned long config = pinconf_to_config_packed(MAX96717_PINCTRL_INPUT_VALUE, 0);
+ struct max96717_priv *priv = gpiochip_get_data(gc);
+ int ret;
+
+ ret = max96717_conf_pin_config_get(priv->pctldev, offset, &config);
+ if (ret)
+ return ret;
+
+ return pinconf_to_config_argument(config);
+}
+
+static int max96717_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ unsigned long config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value);
+ struct max96717_priv *priv = gpiochip_get_data(gc);
+
+ return max96717_conf_pin_config_set_one(priv, offset, config);
+}
+
+static unsigned int max96717_pipe_id(struct max96717_priv *priv,
+ struct max_ser_pipe *pipe)
+{
+ return priv->info->pipe_hw_ids[pipe->index];
+}
+
+static unsigned int max96717_phy_id(struct max96717_priv *priv,
+ struct max_ser_phy *phy)
+{
+ return priv->info->phy_hw_ids[phy->index];
+}
+
+static int max96717_set_pipe_enable(struct max_ser *ser,
+ struct max_ser_pipe *pipe, bool enable)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+ unsigned int mask = MAX96717_REG2_VID_TX_EN_P(index);
+
+ return regmap_assign_bits(priv->regmap, MAX96717_REG2, mask, enable);
+}
+
+static int max96717_reg_read(struct max_ser *ser, unsigned int reg,
+ unsigned int *val)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+
+ return regmap_read(priv->regmap, reg, val);
+}
+
+static int max96717_reg_write(struct max_ser *ser, unsigned int reg,
+ unsigned int val)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+
+ return regmap_write(priv->regmap, reg, val);
+}
+
+static int max96717_set_pipe_dt_en(struct max_ser *ser, struct max_ser_pipe *pipe,
+ unsigned int i, bool enable)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+ unsigned int reg;
+
+ if (i < 2)
+ reg = MAX96717_FRONTTOP_12(index, i);
+ else
+ /*
+ * DT 7 and 8 are only supported on MAX96717, no need for pipe
+ * index to be taken into account.
+ */
+ reg = MAX96717_EXTA(i - 2);
+
+ return regmap_assign_bits(priv->regmap, reg, MAX96717_MEM_DT_EN, enable);
+}
+
+static int max96717_set_pipe_dt(struct max_ser *ser, struct max_ser_pipe *pipe,
+ unsigned int i, unsigned int dt)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+ unsigned int reg;
+
+ if (i < 2)
+ reg = MAX96717_FRONTTOP_12(index, i);
+ else
+ reg = MAX96717_EXTA(i - 2);
+
+ return regmap_update_bits(priv->regmap, reg, MAX96717_MEM_DT_SEL,
+ FIELD_PREP(MAX96717_MEM_DT_SEL, dt));
+}
+
+static int max96717_set_pipe_vcs(struct max_ser *ser,
+ struct max_ser_pipe *pipe,
+ unsigned int vcs)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+ int ret;
+
+ ret = regmap_write(priv->regmap, MAX96717_FRONTTOP_1(index),
+ (vcs >> 0) & 0xff);
+ if (ret)
+ return ret;
+
+ return regmap_write(priv->regmap, MAX96717_FRONTTOP_2(index),
+ (vcs >> 8) & 0xff);
+}
+
+static int max96717_log_status(struct max_ser *ser)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int val;
+ int ret;
+
+ if (!(priv->info->modes & BIT(MAX_SERDES_GMSL_TUNNEL_MODE)))
+ return 0;
+
+ ret = regmap_read(priv->regmap, MAX96717_EXT23, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "tun_pkt_cnt: %u\n", val);
+
+ return 0;
+}
+
+static int max96717_log_pipe_status(struct max_ser *ser,
+ struct max_ser_pipe *pipe)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(priv->regmap, MAX96717_VIDEO_TX2(index), &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tpclkdet: %u\n",
+ !!(val & MAX96717_VIDEO_TX2_PCLKDET));
+
+ return 0;
+}
+
+static int max96717_log_phy_status(struct max_ser *ser,
+ struct max_ser_phy *phy)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int val;
+ int ret;
+
+ if (!priv->info->supports_pkt_cnt)
+ return 0;
+
+ ret = regmap_read(priv->regmap, MAX96717_EXT21, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tphy_pkt_cnt: %u\n", val);
+
+ ret = regmap_read(priv->regmap, MAX96717_EXT22, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tcsi_pkt_cnt: %u\n", val);
+
+ ret = regmap_read(priv->regmap, MAX96717_EXT24, &val);
+ if (ret)
+ return ret;
+
+ dev_info(priv->dev, "\tphy_clk_cnt: %u\n", val);
+
+ return 0;
+}
+
+static int max96717_init_phy(struct max_ser *ser,
+ struct max_ser_phy *phy)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int num_data_lanes = phy->mipi.num_data_lanes;
+ unsigned int used_data_lanes = 0;
+ unsigned int val;
+ unsigned int i;
+ int ret;
+
+ if (num_data_lanes == 3 && !priv->info->supports_3_data_lanes) {
+ dev_err(priv->dev, "Unsupported 3 data lane mode\n");
+ return -EINVAL;
+ }
+
+ if (phy->mipi.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK &&
+ !priv->info->supports_noncontinuous_clock) {
+ dev_err(priv->dev, "Unsupported non-continuous mode\n");
+ return -EINVAL;
+ }
+
+ /* Configure a lane count. */
+ ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX1,
+ MAX96717_MIPI_RX1_CTRL_NUM_LANES,
+ FIELD_PREP(MAX96717_MIPI_RX1_CTRL_NUM_LANES,
+ num_data_lanes - 1));
+ if (ret)
+ return ret;
+
+ /* Configure lane mapping. */
+ val = 0;
+ for (i = 0; i < 4; 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);
+ }
+
+ ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX3,
+ MAX96717_MIPI_RX3_PHY2_LANE_MAP,
+ FIELD_PREP(MAX96717_MIPI_RX3_PHY2_LANE_MAP, val));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX2,
+ MAX96717_MIPI_RX2_PHY1_LANE_MAP,
+ FIELD_PREP(MAX96717_MIPI_RX2_PHY1_LANE_MAP, val >> 4));
+ 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);
+
+ ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX5,
+ MAX96717_MIPI_RX5_PHY2_POL_MAP,
+ FIELD_PREP(MAX96717_MIPI_RX5_PHY2_POL_MAP, val));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, MAX96717_MIPI_RX4,
+ MAX96717_MIPI_RX4_PHY1_POL_MAP,
+ FIELD_PREP(MAX96717_MIPI_RX4_PHY1_POL_MAP, val >> 2));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_MIPI_RX5,
+ MAX96717_MIPI_RX5_PHY2_POL_MAP_CLK,
+ phy->mipi.lane_polarities[0]);
+ if (ret)
+ return ret;
+
+ if (priv->info->supports_noncontinuous_clock) {
+ ret = regmap_assign_bits(priv->regmap, MAX96717_MIPI_RX0,
+ MAX96717_MIPI_RX0_NONCONTCLK_EN,
+ phy->mipi.flags &
+ V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int max96717_set_phy_active(struct max_ser *ser, struct max_ser_phy *phy,
+ bool enable)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_phy_id(priv, phy);
+
+ return regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_0,
+ MAX96717_FRONTTOP_0_START_PORT(index), enable);
+}
+
+static int max96717_set_pipe_stream_id(struct max_ser *ser,
+ struct max_ser_pipe *pipe,
+ unsigned int stream_id)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+
+ return regmap_update_bits(priv->regmap, MAX96717_TX3(index),
+ MAX96717_TX3_TX_STR_SEL,
+ FIELD_PREP(MAX96717_TX3_TX_STR_SEL, stream_id));
+}
+
+static int max96717_set_pipe_phy(struct max_ser *ser, struct max_ser_pipe *pipe,
+ struct max_ser_phy *phy)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+ unsigned int phy_id = max96717_phy_id(priv, phy);
+ int ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_0,
+ MAX96717_FRONTTOP_0_CLK_SEL_P(index),
+ phy_id == 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_9,
+ MAX96717_FRONTTOP_9_START_PORT(index, 0),
+ phy_id == 0);
+ if (ret)
+ return ret;
+
+ return regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_9,
+ MAX96717_FRONTTOP_9_START_PORT(index, 1),
+ phy_id == 1);
+}
+
+static int max96717_set_pipe_mode(struct max_ser *ser,
+ struct max_ser_pipe *pipe,
+ struct max_ser_pipe_mode *mode)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ unsigned int index = max96717_pipe_id(priv, pipe);
+ int ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_VIDEO_TX0(index),
+ MAX96717_VIDEO_TX0_AUTO_BPP, !mode->bpp);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, MAX96717_VIDEO_TX1(index),
+ MAX96717_VIDEO_TX1_BPP,
+ FIELD_PREP(MAX96717_VIDEO_TX1_BPP, mode->bpp));
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_VIDEO_TX2(index),
+ MAX96717_VIDEO_TX2_DRIFT_DET_EN, !mode->bpp);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_10,
+ MAX96717_FRONTTOP_10_BPP8DBL(index),
+ mode->dbl8);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_11,
+ MAX96717_FRONTTOP_11_BPP10DBL(index),
+ mode->dbl10);
+ if (ret)
+ return ret;
+
+ ret = regmap_assign_bits(priv->regmap, MAX96717_FRONTTOP_11,
+ MAX96717_FRONTTOP_11_BPP12DBL(index),
+ mode->dbl12);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(priv->regmap, MAX96717_FRONTTOP_20(index),
+ MAX96717_FRONTTOP_20_SOFT_BPP |
+ MAX96717_FRONTTOP_20_SOFT_BPP_EN,
+ FIELD_PREP(MAX96717_FRONTTOP_20_SOFT_BPP,
+ mode->soft_bpp) |
+ FIELD_PREP(MAX96717_FRONTTOP_20_SOFT_BPP_EN,
+ !!mode->soft_bpp));
+}
+
+static int max96717_set_i2c_xlate(struct max_ser *ser, unsigned int i,
+ struct max_serdes_i2c_xlate *xlate)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, MAX96717_I2C_2(i),
+ MAX96717_I2C_2_SRC,
+ FIELD_PREP(MAX96717_I2C_2_SRC, xlate->src));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(priv->regmap, MAX96717_I2C_3(i),
+ MAX96717_I2C_3_DST,
+ FIELD_PREP(MAX96717_I2C_3_DST, xlate->dst));
+}
+
+static int max96717_set_tunnel_enable(struct max_ser *ser, bool enable)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+
+ return regmap_assign_bits(priv->regmap, MAX96717_EXT11,
+ MAX96717_EXT11_TUN_MODE, enable);
+}
+
+static int max96717_set_tpg_timings(struct max96717_priv *priv,
+ const struct max_serdes_tpg_timings *tm,
+ unsigned int index)
+{
+ const struct reg_sequence regs[] = {
+ REG_SEQUENCE_3(MAX96717_VTX2_VS_DLY_2(index), tm->vs_dly),
+ REG_SEQUENCE_3(MAX96717_VTX5_VS_HIGH_2(index), tm->vs_high),
+ REG_SEQUENCE_3(MAX96717_VTX8_VS_LOW_2(index), tm->vs_low),
+ REG_SEQUENCE_3(MAX96717_VTX11_V2H_2(index), tm->v2h),
+ REG_SEQUENCE_2(MAX96717_VTX14_HS_HIGH_1(index), tm->hs_high),
+ REG_SEQUENCE_2(MAX96717_VTX16_HS_LOW_1(index), tm->hs_low),
+ REG_SEQUENCE_2(MAX96717_VTX18_HS_CNT_1(index), tm->hs_cnt),
+ REG_SEQUENCE_3(MAX96717_VTX20_V2D_2(index), tm->v2d),
+ REG_SEQUENCE_2(MAX96717_VTX23_DE_HIGH_1(index), tm->de_high),
+ REG_SEQUENCE_2(MAX96717_VTX25_DE_LOW_1(index), tm->de_low),
+ REG_SEQUENCE_2(MAX96717_VTX27_DE_CNT_1(index), 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, MAX96717_VTX0(index),
+ FIELD_PREP(MAX96717_VTX0_VTG_MODE,
+ MAX96717_VTX0_VTG_MODE_FREE_RUNNING) |
+ FIELD_PREP(MAX96717_VTX0_DE_INV, tm->de_inv) |
+ FIELD_PREP(MAX96717_VTX0_HS_INV, tm->hs_inv) |
+ FIELD_PREP(MAX96717_VTX0_VS_INV, tm->vs_inv) |
+ FIELD_PREP(MAX96717_VTX0_GEN_DE, tm->gen_de) |
+ FIELD_PREP(MAX96717_VTX0_GEN_HS, tm->gen_hs) |
+ FIELD_PREP(MAX96717_VTX0_GEN_VS, tm->gen_vs));
+}
+
+static int max96717_set_tpg_clk(struct max96717_priv *priv, u32 clock,
+ unsigned int index)
+{
+ u8 pclk_src;
+
+ switch (clock) {
+ case 25000000:
+ pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_25MHZ;
+ break;
+ case 75000000:
+ pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_75MHZ;
+ break;
+ case 150000000:
+ pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_150MHZ;
+ break;
+ case 375000000:
+ pclk_src = MAX96717_VTX1_PATGEN_CLK_SRC_375MHZ;
+ break;
+ case 0:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(priv->regmap, MAX96717_VTX1(index),
+ MAX96717_VTX1_PATGEN_CLK_SRC,
+ FIELD_PREP(MAX96717_VTX1_PATGEN_CLK_SRC,
+ pclk_src));
+}
+
+static int max96717_set_tpg_mode(struct max96717_priv *priv, bool enable,
+ unsigned int index)
+{
+ unsigned int patgen_mode;
+
+ switch (priv->ser.tpg_pattern) {
+ case MAX_SERDES_TPG_PATTERN_GRADIENT:
+ patgen_mode = MAX96717_VTX29_PATGEN_MODE_GRADIENT;
+ break;
+ case MAX_SERDES_TPG_PATTERN_CHECKERBOARD:
+ patgen_mode = MAX96717_VTX29_PATGEN_MODE_CHECKER;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(priv->regmap, MAX96717_VTX29(index),
+ MAX96717_VTX29_PATGEN_MODE,
+ FIELD_PREP(MAX96717_VTX29_PATGEN_MODE,
+ enable ? patgen_mode
+ : MAX96717_VTX29_PATGEN_MODE_DISABLED));
+}
+
+static int max96717_set_tpg(struct max_ser *ser,
+ const struct max_serdes_tpg_entry *entry)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ /*
+ * MAX9295A supports multiple pipes, each with a pattern generator,
+ * use only the first pipe for simplicity.
+ */
+ unsigned int index = max96717_pipe_id(priv, &ser->pipes[0]);
+ struct max_serdes_tpg_timings timings = { 0 };
+ int ret;
+
+ ret = max_serdes_get_tpg_timings(entry, &timings);
+ if (ret)
+ return ret;
+
+ ret = max96717_set_tpg_timings(priv, &timings, index);
+ if (ret)
+ return ret;
+
+ ret = max96717_set_tpg_clk(priv, timings.clock, index);
+ if (ret)
+ return ret;
+
+ return max96717_set_tpg_mode(priv, entry, index);
+}
+
+static const struct max_serdes_phys_config max96717_phys_configs[] = {
+ { { 4 } },
+};
+
+static int max96717_init_tpg(struct max_ser *ser)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ /*
+ * MAX9295A supports multiple pipes, each with a pattern generator,
+ * use only the first pipe for simplicity.
+ */
+ unsigned int index = max96717_pipe_id(priv, &ser->pipes[0]);
+
+ const struct reg_sequence regs[] = {
+ { MAX96717_VTX30_GRAD_INCR(index), MAX_SERDES_GRAD_INCR },
+ REG_SEQUENCE_3_LE(MAX96717_VTX31_CHKR_A_L(index),
+ MAX_SERDES_CHECKER_COLOR_A),
+ REG_SEQUENCE_3_LE(MAX96717_VTX34_CHKR_B_L(index),
+ MAX_SERDES_CHECKER_COLOR_B),
+ { MAX96717_VTX37_CHKR_RPT_A(index), MAX_SERDES_CHECKER_SIZE },
+ { MAX96717_VTX38_CHKR_RPT_B(index), MAX_SERDES_CHECKER_SIZE },
+ { MAX96717_VTX39_CHKR_ALT(index), MAX_SERDES_CHECKER_SIZE },
+ };
+
+ return regmap_multi_reg_write(priv->regmap, regs, ARRAY_SIZE(regs));
+}
+
+static int max96717_init(struct max_ser *ser)
+{
+ struct max96717_priv *priv = ser_to_priv(ser);
+ int ret;
+
+ /*
+ * Set CMU2 PFDDIV to 1.1V for correct functionality of the device,
+ * as mentioned in the datasheet, under section MANDATORY REGISTER PROGRAMMING.
+ */
+ ret = regmap_update_bits(priv->regmap, MAX96717_CMU2,
+ MAX96717_CMU2_PFDDIV_RSHORT,
+ FIELD_PREP(MAX96717_CMU2_PFDDIV_RSHORT,
+ MAX96717_CMU2_PFDDIV_RSHORT_1_1V));
+ if (ret)
+ return ret;
+
+ if (ser->ops->set_tunnel_enable) {
+ ret = ser->ops->set_tunnel_enable(ser, false);
+ if (ret)
+ return ret;
+ }
+
+ return max96717_init_tpg(ser);
+}
+
+static const struct pinctrl_ops max96717_ctrl_ops = {
+ .get_groups_count = max96717_ctrl_get_groups_count,
+ .get_group_name = max96717_ctrl_get_group_name,
+ .get_group_pins = max96717_ctrl_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+ .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static const struct pinconf_ops max96717_conf_ops = {
+ .pin_config_get = max96717_conf_pin_config_get,
+ .pin_config_set = max96717_conf_pin_config_set,
+ .is_generic = true,
+};
+
+static const struct pinmux_ops max96717_mux_ops = {
+ .get_functions_count = max96717_mux_get_functions_count,
+ .get_function_name = max96717_mux_get_function_name,
+ .get_function_groups = max96717_mux_get_groups,
+ .set_mux = max96717_mux_set,
+};
+
+static const struct max_serdes_tpg_entry max96717_tpg_entries[] = {
+ MAX_TPG_ENTRY_640X480P60_RGB888,
+ MAX_TPG_ENTRY_1920X1080P30_RGB888,
+ MAX_TPG_ENTRY_1920X1080P60_RGB888,
+};
+
+static const struct max_ser_ops max96717_ops = {
+ .num_i2c_xlates = 2,
+ .phys_configs = {
+ .num_configs = ARRAY_SIZE(max96717_phys_configs),
+ .configs = max96717_phys_configs,
+ },
+ .tpg_entries = {
+ .num_entries = ARRAY_SIZE(max96717_tpg_entries),
+ .entries = max96717_tpg_entries,
+ },
+ .tpg_mode = MAX_SERDES_GMSL_PIXEL_MODE,
+ .tpg_patterns = BIT(MAX_SERDES_TPG_PATTERN_CHECKERBOARD) |
+ BIT(MAX_SERDES_TPG_PATTERN_GRADIENT),
+ .reg_read = max96717_reg_read,
+ .reg_write = max96717_reg_write,
+ .log_status = max96717_log_status,
+ .log_pipe_status = max96717_log_pipe_status,
+ .log_phy_status = max96717_log_phy_status,
+ .init = max96717_init,
+ .set_i2c_xlate = max96717_set_i2c_xlate,
+ .set_tpg = max96717_set_tpg,
+ .init_phy = max96717_init_phy,
+ .set_phy_active = max96717_set_phy_active,
+ .set_pipe_enable = max96717_set_pipe_enable,
+ .set_pipe_dt = max96717_set_pipe_dt,
+ .set_pipe_dt_en = max96717_set_pipe_dt_en,
+ .set_pipe_vcs = max96717_set_pipe_vcs,
+ .set_pipe_mode = max96717_set_pipe_mode,
+ .set_pipe_stream_id = max96717_set_pipe_stream_id,
+ .set_pipe_phy = max96717_set_pipe_phy,
+};
+
+struct max96717_pll_predef_freq {
+ unsigned long freq;
+ bool is_rclk;
+ bool is_alt;
+ u8 val;
+ u8 rclksel;
+};
+
+static const struct max96717_pll_predef_freq max96717_predef_freqs[] = {
+ { 6250000, true, false, 0, 2 },
+ { 12500000, true, false, 0, 1 },
+ { 13500000, false, true, 0, 3 },
+ { 19200000, false, false, 0, 3 },
+ { 24000000, false, true, 1, 3 },
+ { 25000000, true, false, 0, 0 },
+ { 27000000, false, false, 1, 3 },
+ { 37125000, false, false, 2, 3 },
+ { 74250000, false, false, 3, 3 },
+};
+
+static unsigned long
+max96717_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct max96717_priv *priv = clk_hw_to_priv(hw);
+
+ return max96717_predef_freqs[priv->pll_predef_index].freq;
+}
+
+static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv,
+ unsigned long rate)
+{
+ unsigned int i, idx = 0;
+ unsigned long diff_new, diff_old = U32_MAX;
+
+ for (i = 0; i < ARRAY_SIZE(max96717_predef_freqs); i++) {
+ diff_new = abs(rate - max96717_predef_freqs[i].freq);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ idx = i;
+ }
+ }
+
+ return idx;
+}
+
+static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct max96717_priv *priv = clk_hw_to_priv(hw);
+ struct device *dev = &priv->client->dev;
+ unsigned int idx;
+
+ idx = max96717_clk_find_best_index(priv, rate);
+
+ if (rate != max96717_predef_freqs[idx].freq) {
+ dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n",
+ rate, max96717_predef_freqs[idx].freq);
+ }
+
+ return max96717_predef_freqs[idx].freq;
+}
+
+static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ const struct max96717_pll_predef_freq *predef_freq;
+ struct max96717_priv *priv = clk_hw_to_priv(hw);
+ unsigned int val, idx;
+ int ret = 0;
+
+ idx = max96717_clk_find_best_index(priv, rate);
+ predef_freq = &max96717_predef_freqs[idx];
+
+ ret = regmap_update_bits(priv->regmap, MAX96717_REG3,
+ MAX96717_REG3_RCLKSEL,
+ FIELD_PREP(MAX96717_REG3_RCLKSEL,
+ predef_freq->rclksel));
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ,
+ predef_freq->val);
+
+ if (predef_freq->is_alt)
+ val |= MAX96717_REF_VTG0_REFGEN_PREDEF_FREQ_ALT;
+ if (!predef_freq->is_rclk)
+ val |= MAX96717_REF_VTG0_REFGEN_EN;
+
+ val |= MAX96717_REF_VTG0_REFGEN_RST;
+
+ ret = regmap_write(priv->regmap, MAX96717_REF_VTG0, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(priv->regmap, MAX96717_REF_VTG0,
+ MAX96717_REF_VTG0_REFGEN_RST);
+ if (ret)
+ return ret;
+
+ priv->pll_predef_index = idx;
+
+ return 0;
+}
+
+static int max96717_clk_prepare(struct clk_hw *hw)
+{
+ struct max96717_priv *priv = clk_hw_to_priv(hw);
+
+ return regmap_set_bits(priv->regmap, MAX96717_REG6, MAX96717_REG6_RCLKEN);
+}
+
+static void max96717_clk_unprepare(struct clk_hw *hw)
+{
+ struct max96717_priv *priv = clk_hw_to_priv(hw);
+
+ regmap_clear_bits(priv->regmap, MAX96717_REG6, MAX96717_REG6_RCLKEN);
+}
+
+static const struct clk_ops max96717_clk_ops = {
+ .prepare = max96717_clk_prepare,
+ .unprepare = max96717_clk_unprepare,
+ .set_rate = max96717_clk_set_rate,
+ .recalc_rate = max96717_clk_recalc_rate,
+ .round_rate = max96717_clk_round_rate,
+};
+
+static int max96717_register_clkout(struct max96717_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct clk_init_data init = { .ops = &max96717_clk_ops };
+ int ret;
+
+ ret = max96717_mux_set_rclkout(priv, MAX96717_RCLK_MFP);
+ if (ret)
+ return ret;
+
+ init.name = kasprintf(GFP_KERNEL, "max96717.%s.clk_out", dev_name(dev));
+ if (!init.name)
+ return -ENOMEM;
+
+ priv->clk_hw.init = &init;
+
+ ret = max96717_clk_set_rate(&priv->clk_hw,
+ MAX96717_DEFAULT_CLKOUT_RATE, 0);
+ if (ret)
+ goto free_init_name;
+
+ ret = devm_clk_hw_register(dev, &priv->clk_hw);
+ kfree(init.name);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register clock HW\n");
+
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &priv->clk_hw);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Cannot add OF clock provider\n");
+
+ return 0;
+
+free_init_name:
+ kfree(init.name);
+ return ret;
+}
+
+static int max96717_gpiochip_probe(struct max96717_priv *priv)
+{
+ struct device *dev = priv->dev;
+ int ret;
+
+ priv->pctldesc = (struct pinctrl_desc) {
+ .owner = THIS_MODULE,
+ .name = MAX96717_PINCTRL_NAME,
+ .pins = max96717_pins,
+ .npins = ARRAY_SIZE(max96717_pins),
+ .pctlops = &max96717_ctrl_ops,
+ .confops = &max96717_conf_ops,
+ .pmxops = &max96717_mux_ops,
+ .custom_params = max96717_cfg_params,
+ .num_custom_params = ARRAY_SIZE(max96717_cfg_params),
+ };
+
+ ret = devm_pinctrl_register_and_init(dev, &priv->pctldesc, priv, &priv->pctldev);
+ if (ret)
+ return ret;
+
+ ret = pinctrl_enable(priv->pctldev);
+ if (ret)
+ return ret;
+
+ priv->gc = (struct gpio_chip) {
+ .owner = THIS_MODULE,
+ .label = MAX96717_GPIOCHIP_NAME,
+ .base = -1,
+ .ngpio = MAX96717_GPIO_NUM,
+ .parent = dev,
+ .can_sleep = true,
+ .request = gpiochip_generic_request,
+ .free = gpiochip_generic_free,
+ .set_config = gpiochip_generic_config,
+ .get_direction = max96717_gpio_get_direction,
+ .direction_input = max96717_gpio_direction_input,
+ .direction_output = max96717_gpio_direction_output,
+ .get = max96717_gpio_get,
+ .set_rv = max96717_gpio_set,
+ };
+
+ return devm_gpiochip_add_data(dev, &priv->gc, priv);
+}
+
+static int max96717_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct max96717_priv *priv;
+ struct max_ser_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, &max96717_i2c_regmap);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ *ops = max96717_ops;
+
+ if (priv->info->modes & BIT(MAX_SERDES_GMSL_TUNNEL_MODE))
+ ops->set_tunnel_enable = max96717_set_tunnel_enable;
+
+ ops->modes = priv->info->modes;
+ ops->num_pipes = priv->info->num_pipes;
+ ops->num_dts_per_pipe = priv->info->num_dts_per_pipe;
+ ops->num_phys = priv->info->num_phys;
+ priv->ser.ops = ops;
+
+ ret = max96717_wait_for_device(priv);
+ if (ret)
+ return ret;
+
+ ret = max96717_gpiochip_probe(priv);
+ if (ret)
+ return ret;
+
+ ret = max96717_register_clkout(priv);
+ if (ret)
+ return ret;
+
+ return max_ser_probe(client, &priv->ser);
+}
+
+static void max96717_remove(struct i2c_client *client)
+{
+ struct max96717_priv *priv = i2c_get_clientdata(client);
+
+ max_ser_remove(&priv->ser);
+}
+
+static const struct max96717_chip_info max9295a_info = {
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE),
+ .num_pipes = 4,
+ .num_dts_per_pipe = 2,
+ .pipe_hw_ids = { 0, 1, 2, 3 },
+ .num_phys = 1,
+ .phy_hw_ids = { 1 },
+};
+
+static const struct max96717_chip_info max96717_info = {
+ .modes = BIT(MAX_SERDES_GMSL_PIXEL_MODE) |
+ BIT(MAX_SERDES_GMSL_TUNNEL_MODE),
+ .supports_3_data_lanes = true,
+ .supports_pkt_cnt = true,
+ .supports_noncontinuous_clock = true,
+ .num_pipes = 1,
+ .num_dts_per_pipe = 4,
+ .pipe_hw_ids = { 2 },
+ .num_phys = 1,
+ .phy_hw_ids = { 1 },
+};
+
+static const struct of_device_id max96717_of_ids[] = {
+ { .compatible = "maxim,max9295a", .data = &max9295a_info },
+ { .compatible = "maxim,max96717", .data = &max96717_info },
+ { .compatible = "maxim,max96717f", .data = &max96717_info },
+ { .compatible = "maxim,max96793", .data = &max96717_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max96717_of_ids);
+
+static struct i2c_driver max96717_i2c_driver = {
+ .driver = {
+ .name = MAX96717_NAME,
+ .of_match_table = max96717_of_ids,
+ },
+ .probe = max96717_probe,
+ .remove = max96717_remove,
+};
+
+module_i2c_driver(max96717_i2c_driver);
+
+MODULE_DESCRIPTION("MAX96717 GMSL2 Serializer Driver");
+MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@...log.com>");
+MODULE_LICENSE("GPL");
--
2.50.0
Powered by blists - more mailing lists