Linux kernel staging patches
 help / color / mirror / Atom feed
* [PATCH] staging: greybus: remove driver depending on nonexistent config option
From: Ethan Nelson-Moore @ 2026-06-09 20:26 UTC (permalink / raw)
  To: greybus-dev, linux-staging, linux-kernel
  Cc: Johan Hovold, Alex Elder, Greg Kroah-Hartman, Ethan Nelson-Moore,
	Jakub Kicinski, Namjae Jeon

The Greybus Arche Platform driver depends on the config option
USB_HSIC_USB3613, which has never existed in mainline Linux. Therefore,
it is impossible for anyone to be using it with unmodified mainline
kernels. Remove it and move the former maintainer to the CREDITS file.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 CREDITS                                  |   4 +
 MAINTAINERS                              |   7 -
 drivers/staging/greybus/Kconfig          |   9 -
 drivers/staging/greybus/Makefile         |   6 -
 drivers/staging/greybus/arche-apb-ctrl.c | 491 -----------------
 drivers/staging/greybus/arche-platform.c | 653 -----------------------
 drivers/staging/greybus/arche_platform.h |  28 -
 7 files changed, 4 insertions(+), 1194 deletions(-)
 delete mode 100644 drivers/staging/greybus/arche-apb-ctrl.c
 delete mode 100644 drivers/staging/greybus/arche-platform.c
 delete mode 100644 drivers/staging/greybus/arche_platform.h

diff --git a/CREDITS b/CREDITS
index 17962bdd6dbd..19a493aceabf 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1701,6 +1701,10 @@ S: Talstr. 1
 S: D - 72072 Tuebingen
 S: Germany
 
+N: Vaibhav Hiremath
+E: hvaibhav.linux@gmail.com
+D: Greybus Arche Platform driver
+
 N: Richard Hirst
 E: richard@sleepie.demon.co.uk
 E: rhirst@linuxcare.com
diff --git a/MAINTAINERS b/MAINTAINERS
index e035a3be797c..2461e8a6a45a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11122,13 +11122,6 @@ M:	Bryan O'Donoghue <pure.logic@nexus-software.ie>
 S:	Maintained
 F:	drivers/staging/greybus/loopback.c
 
-GREYBUS PLATFORM DRIVERS
-M:	Vaibhav Hiremath <hvaibhav.linux@gmail.com>
-S:	Maintained
-F:	drivers/staging/greybus/arche-apb-ctrl.c
-F:	drivers/staging/greybus/arche-platform.c
-F:	drivers/staging/greybus/arche_platform.h
-
 GREYBUS SDIO/GPIO/SPI PROTOCOLS DRIVERS
 M:	Rui Miguel Silva <rmfrfs@gmail.com>
 S:	Maintained
diff --git a/drivers/staging/greybus/Kconfig b/drivers/staging/greybus/Kconfig
index 7635f3e850fa..c25da61570eb 100644
--- a/drivers/staging/greybus/Kconfig
+++ b/drivers/staging/greybus/Kconfig
@@ -204,13 +204,4 @@ config GREYBUS_USB
 
 endif	# GREYBUS_BRIDGED_PHY
 
-config GREYBUS_ARCHE
-	tristate "Greybus Arche Platform driver"
-	depends on USB_HSIC_USB3613 || COMPILE_TEST
-	help
-	  Select this option if you have an Arche device.
-
-	  To compile this code as a module, chose M here: the module
-	  will be called gb-arche.ko
-
 endif	# GREYBUS
diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile
index 7c5e89622334..7b9807f8a1a3 100644
--- a/drivers/staging/greybus/Makefile
+++ b/drivers/staging/greybus/Makefile
@@ -65,9 +65,3 @@ obj-$(CONFIG_GREYBUS_SDIO)		+= gb-sdio.o
 obj-$(CONFIG_GREYBUS_SPI)		+= gb-spi.o gb-spilib.o
 obj-$(CONFIG_GREYBUS_UART)		+= gb-uart.o
 obj-$(CONFIG_GREYBUS_USB)		+= gb-usb.o
-
-
-# Greybus Platform driver
-gb-arche-y	:= arche-platform.o arche-apb-ctrl.o
-
-obj-$(CONFIG_GREYBUS_ARCHE)	+= gb-arche.o
diff --git a/drivers/staging/greybus/arche-apb-ctrl.c b/drivers/staging/greybus/arche-apb-ctrl.c
deleted file mode 100644
index 19a6e59b6d5c..000000000000
--- a/drivers/staging/greybus/arche-apb-ctrl.c
+++ /dev/null
@@ -1,491 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Arche Platform driver to control APB.
- *
- * Copyright 2014-2015 Google Inc.
- * Copyright 2014-2015 Linaro Ltd.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/property.h>
-#include <linux/regulator/consumer.h>
-#include <linux/spinlock.h>
-#include <linux/mod_devicetable.h>
-#include "arche_platform.h"
-
-static void apb_bootret_deassert(struct device *dev);
-
-struct arche_apb_ctrl_drvdata {
-	/* Control GPIO signals to and from AP <=> AP Bridges */
-	struct gpio_desc *resetn;
-	struct gpio_desc *boot_ret;
-	struct gpio_desc *pwroff;
-	struct gpio_desc *wake_in;
-	struct gpio_desc *wake_out;
-	struct gpio_desc *pwrdn;
-
-	enum arche_platform_state state;
-	bool init_disabled;
-
-	struct regulator *vcore;
-	struct regulator *vio;
-
-	struct gpio_desc *clk_en;
-	struct clk *clk;
-
-	struct pinctrl *pinctrl;
-	struct pinctrl_state *pin_default;
-
-	/* V2: SPI Bus control  */
-	struct gpio_desc *spi_en;
-	bool spi_en_polarity_high;
-};
-
-/*
- * Note that these low level api's are active high
- */
-static inline void deassert_reset(struct gpio_desc *gpio)
-{
-	gpiod_set_raw_value(gpio, 1);
-}
-
-static inline void assert_reset(struct gpio_desc *gpio)
-{
-	gpiod_set_raw_value(gpio, 0);
-}
-
-/*
- * Note: Please do not modify the below sequence, as it is as per the spec
- */
-static int coldboot_seq(struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
-	int ret;
-
-	if (apb->init_disabled ||
-	    apb->state == ARCHE_PLATFORM_STATE_ACTIVE)
-		return 0;
-
-	/* Hold APB in reset state */
-	assert_reset(apb->resetn);
-
-	if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && apb->spi_en)
-		devm_gpiod_put(dev, apb->spi_en);
-
-	/* Enable power to APB */
-	if (!IS_ERR(apb->vcore)) {
-		ret = regulator_enable(apb->vcore);
-		if (ret) {
-			dev_err(dev, "failed to enable core regulator\n");
-			return ret;
-		}
-	}
-
-	if (!IS_ERR(apb->vio)) {
-		ret = regulator_enable(apb->vio);
-		if (ret) {
-			dev_err(dev, "failed to enable IO regulator\n");
-			return ret;
-		}
-	}
-
-	apb_bootret_deassert(dev);
-
-	/* On DB3 clock was not mandatory */
-	if (apb->clk_en)
-		gpiod_set_value(apb->clk_en, 1);
-
-	usleep_range(100, 200);
-
-	/* deassert reset to APB : Active-low signal */
-	deassert_reset(apb->resetn);
-
-	apb->state = ARCHE_PLATFORM_STATE_ACTIVE;
-
-	return 0;
-}
-
-static int fw_flashing_seq(struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
-	int ret;
-
-	if (apb->init_disabled ||
-	    apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
-		return 0;
-
-	ret = regulator_enable(apb->vcore);
-	if (ret) {
-		dev_err(dev, "failed to enable core regulator\n");
-		return ret;
-	}
-
-	ret = regulator_enable(apb->vio);
-	if (ret) {
-		dev_err(dev, "failed to enable IO regulator\n");
-		return ret;
-	}
-
-	if (apb->spi_en) {
-		unsigned long flags;
-
-		if (apb->spi_en_polarity_high)
-			flags = GPIOD_OUT_HIGH;
-		else
-			flags = GPIOD_OUT_LOW;
-
-		apb->spi_en = devm_gpiod_get(dev, "spi-en", flags);
-		if (IS_ERR(apb->spi_en)) {
-			ret = PTR_ERR(apb->spi_en);
-			dev_err(dev, "Failed requesting SPI bus en GPIO: %d\n",
-				ret);
-			return ret;
-		}
-	}
-
-	/* for flashing device should be in reset state */
-	assert_reset(apb->resetn);
-	apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
-
-	return 0;
-}
-
-static int standby_boot_seq(struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
-
-	if (apb->init_disabled)
-		return 0;
-
-	/*
-	 * Even if it is in OFF state,
-	 * then we do not want to change the state
-	 */
-	if (apb->state == ARCHE_PLATFORM_STATE_STANDBY ||
-	    apb->state == ARCHE_PLATFORM_STATE_OFF)
-		return 0;
-
-	if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && apb->spi_en)
-		devm_gpiod_put(dev, apb->spi_en);
-
-	/*
-	 * As per WDM spec, do nothing
-	 *
-	 * Pasted from WDM spec,
-	 *  - A falling edge on POWEROFF_L is detected (a)
-	 *  - WDM enters standby mode, but no output signals are changed
-	 */
-
-	/* TODO: POWEROFF_L is input to WDM module  */
-	apb->state = ARCHE_PLATFORM_STATE_STANDBY;
-	return 0;
-}
-
-static void poweroff_seq(struct platform_device *pdev)
-{
-	struct device *dev = &pdev->dev;
-	struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
-
-	if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF)
-		return;
-
-	if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && apb->spi_en)
-		devm_gpiod_put(dev, apb->spi_en);
-
-	/* disable the clock */
-	if (apb->clk_en)
-		gpiod_set_value(apb->clk_en, 0);
-
-	if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0)
-		regulator_disable(apb->vcore);
-
-	if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0)
-		regulator_disable(apb->vio);
-
-	/* As part of exit, put APB back in reset state */
-	assert_reset(apb->resetn);
-	apb->state = ARCHE_PLATFORM_STATE_OFF;
-
-	/* TODO: May have to send an event to SVC about this exit */
-}
-
-static void apb_bootret_deassert(struct device *dev)
-{
-	struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
-
-	gpiod_set_value(apb->boot_ret, 0);
-}
-
-int apb_ctrl_coldboot(struct device *dev)
-{
-	return coldboot_seq(to_platform_device(dev));
-}
-
-int apb_ctrl_fw_flashing(struct device *dev)
-{
-	return fw_flashing_seq(to_platform_device(dev));
-}
-
-int apb_ctrl_standby_boot(struct device *dev)
-{
-	return standby_boot_seq(to_platform_device(dev));
-}
-
-void apb_ctrl_poweroff(struct device *dev)
-{
-	poweroff_seq(to_platform_device(dev));
-}
-
-static ssize_t state_store(struct device *dev,
-			   struct device_attribute *attr,
-			   const char *buf, size_t count)
-{
-	struct platform_device *pdev = to_platform_device(dev);
-	struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
-	int ret = 0;
-	bool is_disabled;
-
-	if (sysfs_streq(buf, "off")) {
-		if (apb->state == ARCHE_PLATFORM_STATE_OFF)
-			return count;
-
-		poweroff_seq(pdev);
-	} else if (sysfs_streq(buf, "active")) {
-		if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE)
-			return count;
-
-		poweroff_seq(pdev);
-		is_disabled = apb->init_disabled;
-		apb->init_disabled = false;
-		ret = coldboot_seq(pdev);
-		if (ret)
-			apb->init_disabled = is_disabled;
-	} else if (sysfs_streq(buf, "standby")) {
-		if (apb->state == ARCHE_PLATFORM_STATE_STANDBY)
-			return count;
-
-		ret = standby_boot_seq(pdev);
-	} else if (sysfs_streq(buf, "fw_flashing")) {
-		if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
-			return count;
-
-		/*
-		 * First we want to make sure we power off everything
-		 * and then enter FW flashing state
-		 */
-		poweroff_seq(pdev);
-		ret = fw_flashing_seq(pdev);
-	} else {
-		dev_err(dev, "unknown state\n");
-		ret = -EINVAL;
-	}
-
-	return ret ? ret : count;
-}
-
-static ssize_t state_show(struct device *dev,
-			  struct device_attribute *attr, char *buf)
-{
-	struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
-
-	switch (apb->state) {
-	case ARCHE_PLATFORM_STATE_OFF:
-		return sysfs_emit(buf, "off%s\n",
-				apb->init_disabled ? ",disabled" : "");
-	case ARCHE_PLATFORM_STATE_ACTIVE:
-		return sysfs_emit(buf, "active\n");
-	case ARCHE_PLATFORM_STATE_STANDBY:
-		return sysfs_emit(buf, "standby\n");
-	case ARCHE_PLATFORM_STATE_FW_FLASHING:
-		return sysfs_emit(buf, "fw_flashing\n");
-	default:
-		return sysfs_emit(buf, "unknown state\n");
-	}
-}
-
-static DEVICE_ATTR_RW(state);
-
-static int apb_ctrl_get_fw_data(struct platform_device *pdev,
-				struct arche_apb_ctrl_drvdata *apb)
-{
-	struct device *dev = &pdev->dev;
-	int ret;
-
-	apb->resetn = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(apb->resetn)) {
-		ret = PTR_ERR(apb->resetn);
-		dev_err(dev, "Failed requesting reset GPIO: %d\n", ret);
-		return ret;
-	}
-
-	apb->boot_ret = devm_gpiod_get(dev, "boot-ret", GPIOD_OUT_LOW);
-	if (IS_ERR(apb->boot_ret)) {
-		ret = PTR_ERR(apb->boot_ret);
-		dev_err(dev, "Failed requesting bootret GPIO: %d\n", ret);
-		return ret;
-	}
-
-	/* It's not mandatory to support power management interface */
-	apb->pwroff = devm_gpiod_get_optional(dev, "pwr-off", GPIOD_IN);
-	if (IS_ERR(apb->pwroff)) {
-		ret = PTR_ERR(apb->pwroff);
-		dev_err(dev, "Failed requesting pwroff_n GPIO: %d\n", ret);
-		return ret;
-	}
-
-	/* Do not make clock mandatory as of now (for DB3) */
-	apb->clk_en = devm_gpiod_get_optional(dev, "clock-en", GPIOD_OUT_LOW);
-	if (IS_ERR(apb->clk_en)) {
-		ret = PTR_ERR(apb->clk_en);
-		dev_err(dev, "Failed requesting APB clock en GPIO: %d\n", ret);
-		return ret;
-	}
-
-	apb->pwrdn = devm_gpiod_get(dev, "pwr-down", GPIOD_OUT_LOW);
-	if (IS_ERR(apb->pwrdn)) {
-		ret = PTR_ERR(apb->pwrdn);
-		dev_warn(dev, "Failed requesting power down GPIO: %d\n", ret);
-		return ret;
-	}
-
-	/* Regulators are optional, as we may have fixed supply coming in */
-	apb->vcore = devm_regulator_get(dev, "vcore");
-	if (IS_ERR(apb->vcore))
-		dev_warn(dev, "no core regulator found\n");
-
-	apb->vio = devm_regulator_get(dev, "vio");
-	if (IS_ERR(apb->vio))
-		dev_warn(dev, "no IO regulator found\n");
-
-	apb->pinctrl = devm_pinctrl_get(&pdev->dev);
-	if (IS_ERR(apb->pinctrl)) {
-		dev_err(&pdev->dev, "could not get pinctrl handle\n");
-		return PTR_ERR(apb->pinctrl);
-	}
-	apb->pin_default = pinctrl_lookup_state(apb->pinctrl, "default");
-	if (IS_ERR(apb->pin_default)) {
-		dev_err(&pdev->dev, "could not get default pin state\n");
-		return PTR_ERR(apb->pin_default);
-	}
-
-	/* Only applicable for platform >= V2 */
-	if (device_property_read_bool(&pdev->dev, "gb,spi-en-active-high"))
-		apb->spi_en_polarity_high = true;
-
-	return 0;
-}
-
-static int arche_apb_ctrl_probe(struct platform_device *pdev)
-{
-	int ret;
-	struct arche_apb_ctrl_drvdata *apb;
-	struct device *dev = &pdev->dev;
-
-	apb = devm_kzalloc(&pdev->dev, sizeof(*apb), GFP_KERNEL);
-	if (!apb)
-		return -ENOMEM;
-
-	ret = apb_ctrl_get_fw_data(pdev, apb);
-	if (ret) {
-		dev_err(dev, "failed to get apb devicetree data %d\n", ret);
-		return ret;
-	}
-
-	/* Initially set APB to OFF state */
-	apb->state = ARCHE_PLATFORM_STATE_OFF;
-	/* Check whether device needs to be enabled on boot */
-	if (device_property_read_bool(&pdev->dev, "arche,init-disable"))
-		apb->init_disabled = true;
-
-	platform_set_drvdata(pdev, apb);
-
-	/* Create sysfs interface to allow user to change state dynamically */
-	ret = device_create_file(dev, &dev_attr_state);
-	if (ret) {
-		dev_err(dev, "failed to create state file in sysfs\n");
-		return ret;
-	}
-
-	dev_info(&pdev->dev, "Device registered successfully\n");
-	return 0;
-}
-
-static void arche_apb_ctrl_remove(struct platform_device *pdev)
-{
-	device_remove_file(&pdev->dev, &dev_attr_state);
-	poweroff_seq(pdev);
-	platform_set_drvdata(pdev, NULL);
-}
-
-static int __maybe_unused arche_apb_ctrl_suspend(struct device *dev)
-{
-	/*
-	 * If timing profile permits, we may shutdown bridge
-	 * completely
-	 *
-	 * TODO: sequence ??
-	 *
-	 * Also, need to make sure we meet precondition for unipro suspend
-	 * Precondition: Definition ???
-	 */
-	return 0;
-}
-
-static int __maybe_unused arche_apb_ctrl_resume(struct device *dev)
-{
-	/*
-	 * At least for ES2 we have to meet the delay requirement between
-	 * unipro switch and AP bridge init, depending on whether bridge is in
-	 * OFF state or standby state.
-	 *
-	 * Based on whether bridge is in standby or OFF state we may have to
-	 * assert multiple signals. Please refer to WDM spec, for more info.
-	 *
-	 */
-	return 0;
-}
-
-static void arche_apb_ctrl_shutdown(struct platform_device *pdev)
-{
-	apb_ctrl_poweroff(&pdev->dev);
-}
-
-static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend,
-			 arche_apb_ctrl_resume);
-
-static const struct of_device_id arche_apb_ctrl_of_match[] = {
-	{ .compatible = "usbffff,2", },
-	{ },
-};
-MODULE_DEVICE_TABLE(of, arche_apb_ctrl_of_match);
-
-static struct platform_driver arche_apb_ctrl_device_driver = {
-	.probe		= arche_apb_ctrl_probe,
-	.remove		= arche_apb_ctrl_remove,
-	.shutdown	= arche_apb_ctrl_shutdown,
-	.driver		= {
-		.name	= "arche-apb-ctrl",
-		.pm	= &arche_apb_ctrl_pm_ops,
-		.of_match_table = arche_apb_ctrl_of_match,
-	}
-};
-
-int __init arche_apb_init(void)
-{
-	return platform_driver_register(&arche_apb_ctrl_device_driver);
-}
-
-void __exit arche_apb_exit(void)
-{
-	platform_driver_unregister(&arche_apb_ctrl_device_driver);
-}
diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c
deleted file mode 100644
index de5de59ea8ab..000000000000
--- a/drivers/staging/greybus/arche-platform.c
+++ /dev/null
@@ -1,653 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Arche Platform driver to enable Unipro link.
- *
- * Copyright 2014-2015 Google Inc.
- * Copyright 2014-2015 Linaro Ltd.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/of_platform.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/suspend.h>
-#include <linux/time.h>
-#include <linux/greybus.h>
-#include <linux/of.h>
-#include "arche_platform.h"
-
-#if IS_ENABLED(CONFIG_USB_HSIC_USB3613)
-#include <linux/usb/usb3613.h>
-#else
-static inline int usb3613_hub_mode_ctrl(bool unused)
-{
-	return 0;
-}
-#endif
-
-#define WD_COLDBOOT_PULSE_WIDTH_MS	30
-
-enum svc_wakedetect_state {
-	WD_STATE_IDLE,			/* Default state = pulled high/low */
-	WD_STATE_BOOT_INIT,		/* WD = falling edge (low) */
-	WD_STATE_COLDBOOT_TRIG,		/* WD = rising edge (high), > 30msec */
-	WD_STATE_STANDBYBOOT_TRIG,	/* As of now not used ?? */
-	WD_STATE_COLDBOOT_START,	/* Cold boot process started */
-	WD_STATE_STANDBYBOOT_START,	/* Not used */
-};
-
-struct arche_platform_drvdata {
-	/* Control GPIO signals to and from AP <=> SVC */
-	struct gpio_desc *svc_reset;
-	bool is_reset_act_hi;
-	struct gpio_desc *svc_sysboot;
-	struct gpio_desc *wake_detect; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
-
-	enum arche_platform_state state;
-
-	struct gpio_desc *svc_refclk_req;
-	struct clk *svc_ref_clk;
-
-	struct pinctrl *pinctrl;
-	struct pinctrl_state *pin_default;
-
-	int num_apbs;
-
-	enum svc_wakedetect_state wake_detect_state;
-	int wake_detect_irq;
-	spinlock_t wake_lock;			/* Protect wake_detect_state */
-	struct mutex platform_state_mutex;	/* Protect state */
-	unsigned long wake_detect_start;
-	struct notifier_block pm_notifier;
-
-	struct device *dev;
-};
-
-/* Requires calling context to hold arche_pdata->platform_state_mutex */
-static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
-				     enum arche_platform_state state)
-{
-	arche_pdata->state = state;
-}
-
-/* Requires arche_pdata->wake_lock is held by calling context */
-static void arche_platform_set_wake_detect_state(struct arche_platform_drvdata *arche_pdata,
-						 enum svc_wakedetect_state state)
-{
-	arche_pdata->wake_detect_state = state;
-}
-
-static inline void svc_reset_onoff(struct gpio_desc *gpio, bool onoff)
-{
-	gpiod_set_raw_value(gpio, onoff);
-}
-
-static int apb_cold_boot(struct device *dev, void *data)
-{
-	int ret;
-
-	ret = apb_ctrl_coldboot(dev);
-	if (ret)
-		dev_warn(dev, "failed to coldboot\n");
-
-	/*Child nodes are independent, so do not exit coldboot operation */
-	return 0;
-}
-
-static int apb_poweroff(struct device *dev, void *data)
-{
-	apb_ctrl_poweroff(dev);
-
-	/* Enable HUB3613 into HUB mode. */
-	if (usb3613_hub_mode_ctrl(false))
-		dev_warn(dev, "failed to control hub device\n");
-
-	return 0;
-}
-
-static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata)
-{
-	/* Enable interrupt here, to read event back from SVC */
-	enable_irq(arche_pdata->wake_detect_irq);
-}
-
-static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
-{
-	struct arche_platform_drvdata *arche_pdata = devid;
-	unsigned long flags;
-
-	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
-	if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
-		/* Something is wrong */
-		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
-		return IRQ_HANDLED;
-	}
-
-	arche_platform_set_wake_detect_state(arche_pdata,
-					     WD_STATE_COLDBOOT_START);
-	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
-
-	/* It should complete power cycle, so first make sure it is poweroff */
-	device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
-
-	/* Bring APB out of reset: cold boot sequence */
-	device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
-
-	/* Enable HUB3613 into HUB mode. */
-	if (usb3613_hub_mode_ctrl(true))
-		dev_warn(arche_pdata->dev, "failed to control hub device\n");
-
-	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
-	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
-	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
-
-	return IRQ_HANDLED;
-}
-
-static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
-{
-	struct arche_platform_drvdata *arche_pdata = devid;
-	unsigned long flags;
-
-	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
-
-	if (gpiod_get_value(arche_pdata->wake_detect)) {
-		/* wake/detect rising */
-
-		/*
-		 * If wake/detect line goes high after low, within less than
-		 * 30msec, then standby boot sequence is initiated, which is not
-		 * supported/implemented as of now. So ignore it.
-		 */
-		if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
-			if (time_before(jiffies,
-					arche_pdata->wake_detect_start +
-					msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
-				arche_platform_set_wake_detect_state(arche_pdata,
-								     WD_STATE_IDLE);
-			} else {
-				/*
-				 * Check we are not in middle of irq thread
-				 * already
-				 */
-				if (arche_pdata->wake_detect_state !=
-						WD_STATE_COLDBOOT_START) {
-					arche_platform_set_wake_detect_state(arche_pdata,
-									     WD_STATE_COLDBOOT_TRIG);
-					spin_unlock_irqrestore(&arche_pdata->wake_lock,
-							       flags);
-					return IRQ_WAKE_THREAD;
-				}
-			}
-		}
-	} else {
-		/* wake/detect falling */
-		if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
-			arche_pdata->wake_detect_start = jiffies;
-			/*
-			 * In the beginning, when wake/detect goes low
-			 * (first time), we assume it is meant for coldboot
-			 * and set the flag. If wake/detect line stays low
-			 * beyond 30msec, then it is coldboot else fallback
-			 * to standby boot.
-			 */
-			arche_platform_set_wake_detect_state(arche_pdata,
-							     WD_STATE_BOOT_INIT);
-		}
-	}
-
-	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
-
-	return IRQ_HANDLED;
-}
-
-/*
- * Requires arche_pdata->platform_state_mutex to be held
- */
-static int
-arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
-{
-	int ret;
-
-	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
-		return 0;
-
-	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
-
-	svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi);
-
-	gpiod_set_value(arche_pdata->svc_sysboot, 0);
-	usleep_range(100, 200);
-
-	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
-	if (ret) {
-		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
-			ret);
-		return ret;
-	}
-
-	/* bring SVC out of reset */
-	svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi);
-
-	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
-
-	return 0;
-}
-
-/*
- * Requires arche_pdata->platform_state_mutex to be held
- */
-static int
-arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
-{
-	int ret;
-
-	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
-		return 0;
-
-	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
-
-	svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi);
-
-	gpiod_set_value(arche_pdata->svc_sysboot, 1);
-
-	usleep_range(100, 200);
-
-	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
-	if (ret) {
-		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
-			ret);
-		return ret;
-	}
-
-	svc_reset_onoff(arche_pdata->svc_reset,	!arche_pdata->is_reset_act_hi);
-
-	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
-
-	return 0;
-}
-
-/*
- * Requires arche_pdata->platform_state_mutex to be held
- */
-static void
-arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
-{
-	unsigned long flags;
-
-	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
-		return;
-
-	/* If in fw_flashing mode, then no need to repeate things again */
-	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
-		disable_irq(arche_pdata->wake_detect_irq);
-
-		spin_lock_irqsave(&arche_pdata->wake_lock, flags);
-		arche_platform_set_wake_detect_state(arche_pdata,
-						     WD_STATE_IDLE);
-		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
-	}
-
-	clk_disable_unprepare(arche_pdata->svc_ref_clk);
-
-	/* As part of exit, put APB back in reset state */
-	svc_reset_onoff(arche_pdata->svc_reset,	arche_pdata->is_reset_act_hi);
-
-	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
-}
-
-static ssize_t state_store(struct device *dev,
-			   struct device_attribute *attr,
-			   const char *buf, size_t count)
-{
-	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
-	int ret = 0;
-
-	mutex_lock(&arche_pdata->platform_state_mutex);
-
-	if (sysfs_streq(buf, "off")) {
-		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
-			goto exit;
-
-		/*  If SVC goes down, bring down APB's as well */
-		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
-
-		arche_platform_poweroff_seq(arche_pdata);
-
-	} else if (sysfs_streq(buf, "active")) {
-		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
-			goto exit;
-
-		/* First we want to make sure we power off everything
-		 * and then activate back again
-		 */
-		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
-		arche_platform_poweroff_seq(arche_pdata);
-
-		arche_platform_wd_irq_en(arche_pdata);
-		ret = arche_platform_coldboot_seq(arche_pdata);
-		if (ret)
-			goto exit;
-
-	} else if (sysfs_streq(buf, "standby")) {
-		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
-			goto exit;
-
-		dev_warn(arche_pdata->dev, "standby state not supported\n");
-	} else if (sysfs_streq(buf, "fw_flashing")) {
-		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
-			goto exit;
-
-		/*
-		 * Here we only control SVC.
-		 *
-		 * In case of FW_FLASHING mode we do not want to control
-		 * APBs, as in case of V2, SPI bus is shared between both
-		 * the APBs. So let user chose which APB he wants to flash.
-		 */
-		arche_platform_poweroff_seq(arche_pdata);
-
-		ret = arche_platform_fw_flashing_seq(arche_pdata);
-		if (ret)
-			goto exit;
-	} else {
-		dev_err(arche_pdata->dev, "unknown state\n");
-		ret = -EINVAL;
-	}
-
-exit:
-	mutex_unlock(&arche_pdata->platform_state_mutex);
-	return ret ? ret : count;
-}
-
-static ssize_t state_show(struct device *dev,
-			  struct device_attribute *attr, char *buf)
-{
-	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
-
-	switch (arche_pdata->state) {
-	case ARCHE_PLATFORM_STATE_OFF:
-		return sysfs_emit(buf, "off\n");
-	case ARCHE_PLATFORM_STATE_ACTIVE:
-		return sysfs_emit(buf, "active\n");
-	case ARCHE_PLATFORM_STATE_STANDBY:
-		return sysfs_emit(buf, "standby\n");
-	case ARCHE_PLATFORM_STATE_FW_FLASHING:
-		return sysfs_emit(buf, "fw_flashing\n");
-	default:
-		return sysfs_emit(buf, "unknown state\n");
-	}
-}
-
-static DEVICE_ATTR_RW(state);
-
-static int arche_platform_pm_notifier(struct notifier_block *notifier,
-				      unsigned long pm_event, void *unused)
-{
-	struct arche_platform_drvdata *arche_pdata =
-		container_of(notifier, struct arche_platform_drvdata,
-			     pm_notifier);
-	int ret = NOTIFY_DONE;
-
-	mutex_lock(&arche_pdata->platform_state_mutex);
-	switch (pm_event) {
-	case PM_SUSPEND_PREPARE:
-		if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
-			ret = NOTIFY_STOP;
-			break;
-		}
-		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
-		arche_platform_poweroff_seq(arche_pdata);
-		break;
-	case PM_POST_SUSPEND:
-		if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF)
-			break;
-
-		arche_platform_wd_irq_en(arche_pdata);
-		arche_platform_coldboot_seq(arche_pdata);
-		break;
-	default:
-		break;
-	}
-	mutex_unlock(&arche_pdata->platform_state_mutex);
-
-	return ret;
-}
-
-static int arche_platform_probe(struct platform_device *pdev)
-{
-	struct arche_platform_drvdata *arche_pdata;
-	struct device *dev = &pdev->dev;
-	struct device_node *np = dev->of_node;
-	int ret;
-	unsigned int flags;
-
-	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata),
-				   GFP_KERNEL);
-	if (!arche_pdata)
-		return -ENOMEM;
-
-	/* setup svc reset gpio */
-	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
-							     "svc,reset-active-high");
-	if (arche_pdata->is_reset_act_hi)
-		flags = GPIOD_OUT_HIGH;
-	else
-		flags = GPIOD_OUT_LOW;
-
-	arche_pdata->svc_reset = devm_gpiod_get(dev, "svc,reset", flags);
-	if (IS_ERR(arche_pdata->svc_reset)) {
-		ret = PTR_ERR(arche_pdata->svc_reset);
-		dev_err(dev, "failed to request svc-reset GPIO: %d\n", ret);
-		return ret;
-	}
-	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
-
-	arche_pdata->svc_sysboot = devm_gpiod_get(dev, "svc,sysboot",
-						  GPIOD_OUT_LOW);
-	if (IS_ERR(arche_pdata->svc_sysboot)) {
-		ret = PTR_ERR(arche_pdata->svc_sysboot);
-		dev_err(dev, "failed to request sysboot0 GPIO: %d\n", ret);
-		return ret;
-	}
-
-	/* setup the clock request gpio first */
-	arche_pdata->svc_refclk_req = devm_gpiod_get(dev, "svc,refclk-req",
-						     GPIOD_IN);
-	if (IS_ERR(arche_pdata->svc_refclk_req)) {
-		ret = PTR_ERR(arche_pdata->svc_refclk_req);
-		dev_err(dev, "failed to request svc-clk-req GPIO: %d\n", ret);
-		return ret;
-	}
-
-	/* setup refclk2 to follow the pin */
-	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
-	if (IS_ERR(arche_pdata->svc_ref_clk)) {
-		ret = PTR_ERR(arche_pdata->svc_ref_clk);
-		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
-		return ret;
-	}
-
-	platform_set_drvdata(pdev, arche_pdata);
-
-	arche_pdata->num_apbs = of_get_child_count(np);
-	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
-
-	arche_pdata->wake_detect = devm_gpiod_get(dev, "svc,wake-detect",
-						  GPIOD_IN);
-	if (IS_ERR(arche_pdata->wake_detect)) {
-		ret = PTR_ERR(arche_pdata->wake_detect);
-		dev_err(dev, "Failed requesting wake_detect GPIO: %d\n", ret);
-		return ret;
-	}
-
-	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
-
-	arche_pdata->dev = &pdev->dev;
-
-	spin_lock_init(&arche_pdata->wake_lock);
-	mutex_init(&arche_pdata->platform_state_mutex);
-	arche_pdata->wake_detect_irq =
-		gpiod_to_irq(arche_pdata->wake_detect);
-
-	ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
-					arche_platform_wd_irq,
-					arche_platform_wd_irq_thread,
-					IRQF_TRIGGER_FALLING |
-					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-					dev_name(dev), arche_pdata);
-	if (ret) {
-		dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
-		return ret;
-	}
-	disable_irq(arche_pdata->wake_detect_irq);
-
-	ret = device_create_file(dev, &dev_attr_state);
-	if (ret) {
-		dev_err(dev, "failed to create state file in sysfs\n");
-		return ret;
-	}
-
-	ret = of_platform_populate(np, NULL, NULL, dev);
-	if (ret) {
-		dev_err(dev, "failed to populate child nodes %d\n", ret);
-		goto err_device_remove;
-	}
-
-	arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
-	ret = register_pm_notifier(&arche_pdata->pm_notifier);
-	if (ret) {
-		dev_err(dev, "failed to register pm notifier %d\n", ret);
-		goto err_depopulate;
-	}
-
-	/* Explicitly power off if requested */
-	if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) {
-		mutex_lock(&arche_pdata->platform_state_mutex);
-		ret = arche_platform_coldboot_seq(arche_pdata);
-		if (ret) {
-			mutex_unlock(&arche_pdata->platform_state_mutex);
-			dev_err(dev, "Failed to cold boot svc %d\n", ret);
-			goto err_unregister_pm_notifier;
-		}
-		arche_platform_wd_irq_en(arche_pdata);
-		mutex_unlock(&arche_pdata->platform_state_mutex);
-	}
-
-	dev_info(dev, "Device registered successfully\n");
-	return 0;
-
-err_unregister_pm_notifier:
-	unregister_pm_notifier(&arche_pdata->pm_notifier);
-err_depopulate:
-	of_platform_depopulate(dev);
-err_device_remove:
-	device_remove_file(&pdev->dev, &dev_attr_state);
-	return ret;
-}
-
-static void arche_platform_remove(struct platform_device *pdev)
-{
-	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
-
-	unregister_pm_notifier(&arche_pdata->pm_notifier);
-	device_remove_file(&pdev->dev, &dev_attr_state);
-	of_platform_depopulate(&pdev->dev);
-	arche_platform_poweroff_seq(arche_pdata);
-
-	if (usb3613_hub_mode_ctrl(false))
-		dev_warn(arche_pdata->dev, "failed to control hub device\n");
-}
-
-static __maybe_unused int arche_platform_suspend(struct device *dev)
-{
-	/*
-	 * If timing profile permits, we may shutdown bridge
-	 * completely
-	 *
-	 * TODO: define shutdown sequence
-	 *
-	 * Also, need to make sure we meet precondition for unipro suspend
-	 * Precondition: Definition ???
-	 */
-	return 0;
-}
-
-static __maybe_unused int arche_platform_resume(struct device *dev)
-{
-	/*
-	 * At least for ES2 we have to meet the delay requirement between
-	 * unipro switch and AP bridge init, depending on whether bridge is in
-	 * OFF state or standby state.
-	 *
-	 * Based on whether bridge is in standby or OFF state we may have to
-	 * assert multiple signals. Please refer to WDM spec, for more info.
-	 *
-	 */
-	return 0;
-}
-
-static void arche_platform_shutdown(struct platform_device *pdev)
-{
-	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
-
-	arche_platform_poweroff_seq(arche_pdata);
-
-	usb3613_hub_mode_ctrl(false);
-}
-
-static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
-			arche_platform_suspend,
-			arche_platform_resume);
-
-static const struct of_device_id arche_platform_of_match[] = {
-	/* Use PID/VID of SVC device */
-	{ .compatible = "google,arche-platform", },
-	{ },
-};
-MODULE_DEVICE_TABLE(of, arche_platform_of_match);
-
-static struct platform_driver arche_platform_device_driver = {
-	.probe		= arche_platform_probe,
-	.remove		= arche_platform_remove,
-	.shutdown	= arche_platform_shutdown,
-	.driver		= {
-		.name	= "arche-platform-ctrl",
-		.pm	= &arche_platform_pm_ops,
-		.of_match_table = arche_platform_of_match,
-	}
-};
-
-static int __init arche_init(void)
-{
-	int retval;
-
-	retval = platform_driver_register(&arche_platform_device_driver);
-	if (retval)
-		return retval;
-
-	retval = arche_apb_init();
-	if (retval)
-		platform_driver_unregister(&arche_platform_device_driver);
-
-	return retval;
-}
-module_init(arche_init);
-
-static void __exit arche_exit(void)
-{
-	arche_apb_exit();
-	platform_driver_unregister(&arche_platform_device_driver);
-}
-module_exit(arche_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
-MODULE_DESCRIPTION("Arche Platform Driver");
diff --git a/drivers/staging/greybus/arche_platform.h b/drivers/staging/greybus/arche_platform.h
deleted file mode 100644
index 9d997f2d6517..000000000000
--- a/drivers/staging/greybus/arche_platform.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Arche Platform driver to enable Unipro link.
- *
- * Copyright 2015-2016 Google Inc.
- * Copyright 2015-2016 Linaro Ltd.
- */
-
-#ifndef __ARCHE_PLATFORM_H
-#define __ARCHE_PLATFORM_H
-
-enum arche_platform_state {
-	ARCHE_PLATFORM_STATE_OFF,
-	ARCHE_PLATFORM_STATE_ACTIVE,
-	ARCHE_PLATFORM_STATE_STANDBY,
-	ARCHE_PLATFORM_STATE_FW_FLASHING,
-};
-
-int __init arche_apb_init(void);
-void __exit arche_apb_exit(void);
-
-/* Operational states for the APB device */
-int apb_ctrl_coldboot(struct device *dev);
-int apb_ctrl_fw_flashing(struct device *dev);
-int apb_ctrl_standby_boot(struct device *dev);
-void apb_ctrl_poweroff(struct device *dev);
-
-#endif	/* __ARCHE_PLATFORM_H */
-- 
2.43.0


^ permalink raw reply related

* [PATCH] staging: media: atomisp: use kvmalloc_objs() in make_histogram()
From: Rodrigo Gobbi @ 2026-06-09 21:46 UTC (permalink / raw)
  To: andy, hansg, mchehab, sakari.ailus, gregkh, feng
  Cc: ~lkcamp/patches, linux-kernel-mentees, linux-kernel, linux-media,
	linux-staging

Replace kvmalloc() with multiply with kvmalloc_objs(), which handles
the size multiplication internally with overflow checking, silenting
checkpatch warn.

Signed-off-by: Rodrigo Gobbi <rodrigo.gobbi.7@gmail.com>
---
Hi, all,
There is a ongoing effort like this for other files from atomisp
at [1], yet, it is not covering the same file.
Tks and regards.

[1] https://lore.kernel.org/all/20260413112904.98864-1-feng@innora.ai/
---
 drivers/staging/media/atomisp/pci/sh_css_metrics.c | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/media/atomisp/pci/sh_css_metrics.c b/drivers/staging/media/atomisp/pci/sh_css_metrics.c
index edf473dd86ca..d3ae737f1764 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_metrics.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_metrics.c
@@ -59,16 +59,13 @@ make_histogram(struct sh_css_pc_histogram *histogram, unsigned int length)
 		return;
 	if (histogram->run)
 		return;
-	histogram->run = kvmalloc(length * sizeof(*histogram->run),
-				  GFP_KERNEL);
+	histogram->run = kvmalloc_objs(*histogram->run, length);
 	if (!histogram->run)
 		return;
-	histogram->stall = kvmalloc(length * sizeof(*histogram->stall),
-				    GFP_KERNEL);
+	histogram->stall = kvmalloc_objs(*histogram->stall, length);
 	if (!histogram->stall)
 		return;
-	histogram->msink = kvmalloc(length * sizeof(*histogram->msink),
-				    GFP_KERNEL);
+	histogram->msink = kvmalloc_objs(*histogram->msink, length);
 	if (!histogram->msink)
 		return;
 
-- 
2.48.1


^ permalink raw reply related

* [PATCH v3] staging: rtl8723bs: fix unnamed parameters warning detected at checkpatch
From: Rodrigo Gobbi @ 2026-06-09 22:07 UTC (permalink / raw)
  To: gregkh
  Cc: ~lkcamp/patches, linux-kernel-mentees, linux-staging,
	linux-kernel, a.nasrolahi01

Detected the following WARNING: function definition argument
'struct adapter *' should also have an identifier name.
Add explicit names on those definitions.

Signed-off-by: Rodrigo Gobbi <rodrigo.gobbi.7@gmail.com>
---
Hi,

v2 was not applying anymore after a rebase, sending v3.
Tks and regards.

Changelog:
v3: rebase; the rtl8723bs/include/rtw_btcoex.h from v2 is not needed anymore.
v2: https://lore.kernel.org/all/20260523142312.9223-1-rodrigo.gobbi.7@gmail.com/
v1: https://lore.kernel.org/all/20260407133416.13983-1-rodrigo.gobbi.7@gmail.com/
---
 drivers/staging/rtl8723bs/include/hal_intf.h    | 2 +-
 drivers/staging/rtl8723bs/include/rtw_pwrctrl.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/rtl8723bs/include/hal_intf.h b/drivers/staging/rtl8723bs/include/hal_intf.h
index ee8ad26325b5..8a85e8419e47 100644
--- a/drivers/staging/rtl8723bs/include/hal_intf.h
+++ b/drivers/staging/rtl8723bs/include/hal_intf.h
@@ -239,7 +239,7 @@ c2h_id_filter rtw_hal_c2h_id_filter_ccx(struct adapter *adapter);
 s32 rtw_hal_macid_sleep(struct adapter *padapter, u32 macid);
 s32 rtw_hal_macid_wakeup(struct adapter *padapter, u32 macid);
 
-s32 rtw_hal_fill_h2c_cmd(struct adapter *, u8 ElementID, u32 CmdLen, u8 *pCmdBuffer);
+s32 rtw_hal_fill_h2c_cmd(struct adapter *adapter, u8 ElementID, u32 CmdLen, u8 *pCmdBuffer);
 
 void SetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val);
 void GetHwReg8723BS(struct adapter *padapter, u8 variable, u8 *val);
diff --git a/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h b/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h
index 6977892a5742..c8592c4753c2 100644
--- a/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h
+++ b/drivers/staging/rtl8723bs/include/rtw_pwrctrl.h
@@ -215,8 +215,8 @@ struct pwrctrl_priv {
 extern void rtw_init_pwrctrl_priv(struct adapter *adapter);
 extern void rtw_free_pwrctrl_priv(struct adapter *adapter);
 
-s32 rtw_register_task_alive(struct adapter *, u32 task);
-void rtw_unregister_task_alive(struct adapter *, u32 task);
+s32 rtw_register_task_alive(struct adapter *adapter, u32 task);
+void rtw_unregister_task_alive(struct adapter *adapter, u32 task);
 extern s32 rtw_register_tx_alive(struct adapter *padapter);
 extern void rtw_unregister_tx_alive(struct adapter *padapter);
 extern s32 rtw_register_cmd_alive(struct adapter *padapter);
-- 
2.48.1


^ permalink raw reply related

* Re: [PATCH] staging: greybus: remove driver depending on nonexistent config option
From: Agatha Isabelle Moreira @ 2026-06-09 23:56 UTC (permalink / raw)
  To: Ethan Nelson-Moore
  Cc: greybus-dev, linux-staging, linux-kernel, Johan Hovold,
	Alex Elder, Greg Kroah-Hartman, Jakub Kicinski, Namjae Jeon
In-Reply-To: <20260609202705.183875-1-enelsonmoore@gmail.com>

On Tue, Jun 09, 2026 at 01:26:58PM -0700, Ethan Nelson-Moore wrote:
> The Greybus Arche Platform driver depends on the config option
> USB_HSIC_USB3613, which has never existed in mainline Linux. Therefore,

Actually it dosn't. The `depends on` line says:

     depends on USB_HSIC_USB3613 || COMPILE_TEST

That's a logical OR operation, it depends on USB_HSIC_USB3613 OR
COMPILE_TEST.

> it is impossible for anyone to be using it with unmodified mainline
> kernels. Remove it and move the former maintainer to the CREDITS file.

Indeed, the impossible for anyone to be using holds true, as mentioned
in:
https://lore.kernel.org/all/aVuPidYUPZxCOdRp@stanley.mountain/

I think this should be removed, but I think the commit message could be
worked.

Sincerely,
Agatha Isabelle Moreira

^ permalink raw reply

* Re: [PATCH] staging: sm750fb: make g_fbmode array const
From: Brock Haftner @ 2026-06-10  0:58 UTC (permalink / raw)
  To: Ahmet Sezgin Duran
  Cc: Sudip Mukherjee, Teddy Wang, Greg Kroah-Hartman, outreachy,
	linux-fbdev, linux-staging, linux-kernel
In-Reply-To: <0d8d9f38-3cce-4da2-9ba8-f8e99f7b4dee@sezginduran.net>

On 6/8/26, Ahmet Sezgin Duran wrote:
> Did you compile this patch while enabling sm750fb driver in the config?

No, I did not. I apologize for the mistake.
I ran make drivers/staging/sm750fb/ prior to submitting, however I
failed to realize the driver was not enabled in my .config.

Cheers,
Brock

^ permalink raw reply

* Re: [PATCH] staging: greybus: remove driver depending on nonexistent config option
From: Ethan Nelson-Moore @ 2026-06-10  1:14 UTC (permalink / raw)
  To: Agatha Isabelle Moreira
  Cc: greybus-dev, linux-staging, linux-kernel, Johan Hovold,
	Alex Elder, Greg Kroah-Hartman, Jakub Kicinski, Namjae Jeon
In-Reply-To: <guidaiFoSi.541328.2026169aiie51mhcpgO149o_23655_LINUXKERNEL_AGATHA_@links.agatha.dev>

Hi, Agatha,

On Tue, Jun 9, 2026 at 4:56 PM Agatha Isabelle Moreira <code@agatha.dev> wrote:
> On Tue, Jun 09, 2026 at 01:26:58PM -0700, Ethan Nelson-Moore wrote:
> > The Greybus Arche Platform driver depends on the config option
> > USB_HSIC_USB3613, which has never existed in mainline Linux. Therefore,
>
> Actually it dosn't. The `depends on` line says:
>
>      depends on USB_HSIC_USB3613 || COMPILE_TEST
>
> That's a logical OR operation, it depends on USB_HSIC_USB3613 OR
> COMPILE_TEST.

The function of COMPILE_TEST is to allow drivers to be compiled in
environments where they do not work, to enable better code coverage
and easier testing of tree-wide changes without cross compilers. It
should never be enabled for production use cases. So yes, technically,
it does depend on either option, but only on one "real" option.
Therefore, I don't think there is anything wrong with the commit
message.

Ethan

^ permalink raw reply

* [PATCH 1/2] staging: rtl8723bs: drop GEN_CMD_CODE macro and capitalize labels
From: Aiman Najjar @ 2026-06-10  1:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: kernel-janitors, linux-staging, linux-kernel, Aiman Najjar
In-Reply-To: <20260609-rtl8723bs-code-style-v1-0-daa0e85359a6@linux.com>

The use of GEN_CMD_CODE macro to generate cmd enum label names is applied
inconsistently and is confusing, it also makes it harder to make use of
tools such as clangd when looking up symbols.

Replace them with writing the enum labels directly and adopting
new capitalized names instead of the current camel case ones.

Signed-off-by: Aiman Najjar <aiman@linux.com>
---
 drivers/staging/rtl8723bs/core/rtw_ap.c       |   4 +-
 drivers/staging/rtl8723bs/core/rtw_cmd.c      | 190 +++++++++++++-------------
 drivers/staging/rtl8723bs/core/rtw_mlme.c     |   4 +-
 drivers/staging/rtl8723bs/core/rtw_mlme_ext.c |  18 +--
 drivers/staging/rtl8723bs/include/rtw_cmd.h   | 154 ++++++++++-----------
 5 files changed, 181 insertions(+), 189 deletions(-)

diff --git a/drivers/staging/rtl8723bs/core/rtw_ap.c b/drivers/staging/rtl8723bs/core/rtw_ap.c
index 4b4012411011..5a183b169282 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ap.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ap.c
@@ -1237,7 +1237,7 @@ u8 rtw_ap_set_pairwise_key(struct adapter *padapter, struct sta_info *psta)
 		goto exit;
 	}
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+	init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, SET_STA_KEY_CMD);
 
 	psetstakey_para->algorithm = (u8)psta->dot118021XPrivacy;
 
@@ -1300,7 +1300,7 @@ static int rtw_ap_set_key(struct adapter *padapter,
 
 	memcpy(&psetkeyparm->key[0], key, keylen);
 
-	pcmd->cmdcode = _SetKey_CMD_;
+	pcmd->cmdcode = SET_KEY_CMD;
 	pcmd->parmbuf = (u8 *)psetkeyparm;
 	pcmd->cmdsz =  (sizeof(struct setkey_parm));
 	pcmd->rsp = NULL;
diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c
index c1185c25ed36..b3637796187d 100644
--- a/drivers/staging/rtl8723bs/core/rtw_cmd.c
+++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c
@@ -11,78 +11,78 @@
 #include <linux/delay.h>
 
 static struct _cmd_callback rtw_cmd_callback[] = {
-	{GEN_CMD_CODE(_Read_MACREG), NULL}, /*0*/
-	{GEN_CMD_CODE(_Write_MACREG), NULL},
-	{GEN_CMD_CODE(_Read_BBREG), &rtw_getbbrfreg_cmdrsp_callback},
-	{GEN_CMD_CODE(_Write_BBREG), NULL},
-	{GEN_CMD_CODE(_Read_RFREG), &rtw_getbbrfreg_cmdrsp_callback},
-	{GEN_CMD_CODE(_Write_RFREG), NULL}, /*5*/
-	{GEN_CMD_CODE(_Read_EEPROM), NULL},
-	{GEN_CMD_CODE(_Write_EEPROM), NULL},
-	{GEN_CMD_CODE(_Read_EFUSE), NULL},
-	{GEN_CMD_CODE(_Write_EFUSE), NULL},
-
-	{GEN_CMD_CODE(_Read_CAM),	NULL},	/*10*/
-	{GEN_CMD_CODE(_Write_CAM),	 NULL},
-	{GEN_CMD_CODE(_setBCNITV), NULL},
-	{GEN_CMD_CODE(_setMBIDCFG), NULL},
-	{GEN_CMD_CODE(_JoinBss), &rtw_joinbss_cmd_callback},  /*14*/
-	{GEN_CMD_CODE(_DisConnect), &rtw_disassoc_cmd_callback}, /*15*/
-	{GEN_CMD_CODE(_CreateBss), &rtw_createbss_cmd_callback},
-	{GEN_CMD_CODE(_SetOpMode), NULL},
-	{GEN_CMD_CODE(_SiteSurvey), &rtw_survey_cmd_callback}, /*18*/
-	{GEN_CMD_CODE(_SetAuth), NULL},
-
-	{GEN_CMD_CODE(_SetKey), NULL},	/*20*/
-	{GEN_CMD_CODE(_SetStaKey), &rtw_setstaKey_cmdrsp_callback},
-	{GEN_CMD_CODE(_SetAssocSta), &rtw_setassocsta_cmdrsp_callback},
-	{GEN_CMD_CODE(_DelAssocSta), NULL},
-	{GEN_CMD_CODE(_SetStaPwrState), NULL},
-	{GEN_CMD_CODE(_SetBasicRate), NULL}, /*25*/
-	{GEN_CMD_CODE(_GetBasicRate), NULL},
-	{GEN_CMD_CODE(_SetDataRate), NULL},
-	{GEN_CMD_CODE(_GetDataRate), NULL},
-	{GEN_CMD_CODE(_SetPhyInfo), NULL},
-
-	{GEN_CMD_CODE(_GetPhyInfo), NULL}, /*30*/
-	{GEN_CMD_CODE(_SetPhy), NULL},
-	{GEN_CMD_CODE(_GetPhy), NULL},
-	{GEN_CMD_CODE(_readRssi), NULL},
-	{GEN_CMD_CODE(_readGain), NULL},
-	{GEN_CMD_CODE(_SetAtim), NULL}, /*35*/
-	{GEN_CMD_CODE(_SetPwrMode), NULL},
-	{GEN_CMD_CODE(_JoinbssRpt), NULL},
-	{GEN_CMD_CODE(_SetRaTable), NULL},
-	{GEN_CMD_CODE(_GetRaTable), NULL},
-
-	{GEN_CMD_CODE(_GetCCXReport), NULL}, /*40*/
-	{GEN_CMD_CODE(_GetDTMReport),	NULL},
-	{GEN_CMD_CODE(_GetTXRateStatistics), NULL},
-	{GEN_CMD_CODE(_SetUsbSuspend), NULL},
-	{GEN_CMD_CODE(_SetH2cLbk), NULL},
-	{GEN_CMD_CODE(_AddBAReq), NULL}, /*45*/
-	{GEN_CMD_CODE(_SetChannel), NULL},		/*46*/
-	{GEN_CMD_CODE(_SetTxPower), NULL},
-	{GEN_CMD_CODE(_SwitchAntenna), NULL},
-	{GEN_CMD_CODE(_SetCrystalCap), NULL},
-	{GEN_CMD_CODE(_SetSingleCarrierTx), NULL},	/*50*/
-
-	{GEN_CMD_CODE(_SetSingleToneTx), NULL}, /*51*/
-	{GEN_CMD_CODE(_SetCarrierSuppressionTx), NULL},
-	{GEN_CMD_CODE(_SetContinuousTx), NULL},
-	{GEN_CMD_CODE(_SwitchBandwidth), NULL},		/*54*/
-	{GEN_CMD_CODE(_TX_Beacon), NULL},/*55*/
-
-	{GEN_CMD_CODE(_Set_MLME_EVT), NULL},/*56*/
-	{GEN_CMD_CODE(_Set_Drv_Extra), NULL},/*57*/
-	{GEN_CMD_CODE(_Set_H2C_MSG), NULL},/*58*/
-	{GEN_CMD_CODE(_SetChannelPlan), NULL},/*59*/
-
-	{GEN_CMD_CODE(_SetChannelSwitch), NULL},/*60*/
-	{GEN_CMD_CODE(_TDLS), NULL},/*61*/
-	{GEN_CMD_CODE(_ChkBMCSleepq), NULL}, /*62*/
-
-	{GEN_CMD_CODE(_RunInThreadCMD), NULL},/*63*/
+	{READ_MACREG_CMD, NULL}, /*0*/
+	{WRITE_MACREG_CMD, NULL},
+	{READ_BBREG_CMD, &rtw_getbbrfreg_cmdrsp_callback},
+	{WRITE_BBREG_CMD, NULL},
+	{READ_RFREG_CMD, &rtw_getbbrfreg_cmdrsp_callback},
+	{WRITE_RFREG_CMD, NULL}, /*5*/
+	{READ_EEPROM_CMD, NULL},
+	{WRITE_EEPROM_CMD, NULL},
+	{READ_EFUSE_CMD, NULL},
+	{WRITE_EFUSE_CMD, NULL},
+
+	{READ_CAM_CMD, NULL},	/*10*/
+	{WRITE_CAM_CMD, NULL},
+	{SET_BCNITV_CMD, NULL},
+	{SET_MBIDCFG_CMD, NULL},
+	{JOIN_BSS_CMD, &rtw_joinbss_cmd_callback},  /*14*/
+	{DISCONNECT_CMD, &rtw_disassoc_cmd_callback}, /*15*/
+	{CREATE_BSS_CMD, &rtw_createbss_cmd_callback},
+	{SET_OP_MODE_CMD, NULL},
+	{SITE_SURVEY_CMD, &rtw_survey_cmd_callback}, /*18*/
+	{SET_AUTH_CMD, NULL},
+
+	{SET_KEY_CMD, NULL},	/*20*/
+	{SET_STA_KEY_CMD, &rtw_setstaKey_cmdrsp_callback},
+	{SET_ASSOC_STA_CMD, &rtw_setassocsta_cmdrsp_callback},
+	{DEL_ASSOC_STA_CMD, NULL},
+	{SET_STA_PWR_STATE_CMD, NULL},
+	{SET_BASIC_RATE_CMD, NULL}, /*25*/
+	{GET_BASIC_RATE_CMD, NULL},
+	{SET_DATA_RATE_CMD, NULL},
+	{GET_DATA_RATE_CMD, NULL},
+	{SET_PHY_INFO_CMD, NULL},
+
+	{GET_PHY_INFO_CMD, NULL}, /*30*/
+	{SET_PHY_CMD, NULL},
+	{GET_PHY_CMD, NULL},
+	{READ_RSSI_CMD, NULL},
+	{READ_GAIN_CMD, NULL},
+	{SET_ATIM_CMD, NULL}, /*35*/
+	{SET_PWR_MODE_CMD, NULL},
+	{JOIN_BSS_RPT_CMD, NULL},
+	{SET_RA_TABLE_CMD, NULL},
+	{GET_RA_TABLE_CMD, NULL},
+
+	{GET_CCX_REPORT_CMD, NULL}, /*40*/
+	{GET_DTM_REPORT_CMD,	NULL},
+	{GET_TX_RATE_STATISTICS_CMD, NULL},
+	{SET_USB_SUSPEND_CMD, NULL},
+	{SET_H2C_LBK_CMD, NULL},
+	{ADD_BA_REQ_CMD, NULL}, /*45*/
+	{SET_CHANNEL_CMD, NULL},		/*46*/
+	{SET_TX_POWER_CMD, NULL},
+	{SWITCH_ANTENNA_CMD, NULL},
+	{SET_CRYSTAL_CAP_CMD, NULL},
+	{SET_SINGLE_CARRIER_TX_CMD, NULL},	/*50*/
+
+	{SET_SINGLE_TONE_TX_CMD, NULL}, /*51*/
+	{SET_CARRIER_SUPPRESSION_TX_CMD, NULL},
+	{SET_CONTINUOUS_TX_CMD, NULL},
+	{SWITCH_BANDWIDTH_CMD, NULL},		/*54*/
+	{TX_BEACON_CMD, NULL},/*55*/
+
+	{SET_MLME_EVT_CMD, NULL},/*56*/
+	{SET_DRV_EXTRA_CMD, NULL},/*57*/
+	{SET_H2C_MSG_CMD, NULL},/*58*/
+	{SET_CHANNEL_PLAN_CMD, NULL},/*59*/
+
+	{SET_CHANNEL_SWITCH_CMD, NULL},/*60*/
+	{TDLS_CMD, NULL},/*61*/
+	{CHK_BMC_SLEEPQ_CMD, NULL}, /*62*/
+
+	{RUN_IN_THREAD_CMD, NULL},/*63*/
 };
 
 static struct cmd_hdl wlancmds[] = {
@@ -301,7 +301,7 @@ int rtw_cmd_filter(struct cmd_priv *pcmdpriv, struct cmd_obj *cmd_obj)
 {
 	u8 bAllow = false; /* set to true to allow enqueuing cmd when hw_init_completed is false */
 
-	if (cmd_obj->cmdcode == GEN_CMD_CODE(_SetChannelPlan))
+	if (cmd_obj->cmdcode == SET_CHANNEL_PLAN_CMD)
 		bAllow = true;
 
 	if ((!pcmdpriv->padapter->hw_init_completed && !bAllow) ||
@@ -343,8 +343,8 @@ struct	cmd_obj	*rtw_dequeue_cmd(struct cmd_priv *pcmdpriv)
 
 void rtw_free_cmd_obj(struct cmd_obj *pcmd)
 {
-	if ((pcmd->cmdcode != _JoinBss_CMD_) &&
-	    (pcmd->cmdcode != _CreateBss_CMD_)) {
+	if ((pcmd->cmdcode != JOIN_BSS_CMD) &&
+	    (pcmd->cmdcode != CREATE_BSS_CMD)) {
 		/* free parmbuf in cmd_obj */
 		kfree(pcmd->parmbuf);
 	}
@@ -501,7 +501,7 @@ int rtw_cmd_thread(void *context)
 			break;
 		}
 
-		if (pcmd->cmdcode == GEN_CMD_CODE(_Set_Drv_Extra)) {
+		if (pcmd->cmdcode == SET_DRV_EXTRA_CMD) {
 			extra_parm = (struct drvextra_cmd_parm *)pcmd->parmbuf;
 			if (extra_parm->pbuf && extra_parm->size > 0)
 				kfree(extra_parm->pbuf);
@@ -546,7 +546,7 @@ u8 rtw_sitesurvey_cmd(struct adapter  *padapter, struct ndis_802_11_ssid *ssid,
 
 	rtw_free_network_queue(padapter, false);
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, GEN_CMD_CODE(_SiteSurvey));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, SITE_SURVEY_CMD);
 
 	/* psurveyPara->bsslimit = 48; */
 	psurveyPara->scan_mode = pmlmepriv->scan_mode;
@@ -609,7 +609,7 @@ u8 rtw_createbss_cmd(struct adapter  *padapter)
 	}
 
 	INIT_LIST_HEAD(&pcmd->list);
-	pcmd->cmdcode = _CreateBss_CMD_;
+	pcmd->cmdcode = CREATE_BSS_CMD;
 	pcmd->parmbuf = (unsigned char *)pdev_network;
 	pcmd->cmdsz = get_wlan_bssid_ex_sz((struct wlan_bssid_ex *)pdev_network);
 	pcmd->rsp = NULL;
@@ -642,7 +642,7 @@ int rtw_startbss_cmd(struct adapter  *padapter, int flags)
 		}
 
 		INIT_LIST_HEAD(&pcmd->list);
-		pcmd->cmdcode = GEN_CMD_CODE(_CreateBss);
+		pcmd->cmdcode = CREATE_BSS_CMD;
 		pcmd->parmbuf = NULL;
 		pcmd->cmdsz =  0;
 		pcmd->rsp = NULL;
@@ -777,7 +777,7 @@ u8 rtw_joinbss_cmd(struct adapter  *padapter, struct wlan_network *pnetwork)
 	pcmd->cmdsz = get_wlan_bssid_ex_sz(psecnetwork);/* get cmdsz before endian conversion */
 
 	INIT_LIST_HEAD(&pcmd->list);
-	pcmd->cmdcode = _JoinBss_CMD_;/* GEN_CMD_CODE(_JoinBss) */
+	pcmd->cmdcode = JOIN_BSS_CMD;
 	pcmd->parmbuf = (unsigned char *)psecnetwork;
 	pcmd->rsp = NULL;
 	pcmd->rspsz = 0;
@@ -811,7 +811,7 @@ u8 rtw_disassoc_cmd(struct adapter *padapter, u32 deauth_timeout_ms, bool enqueu
 			kfree(param);
 			goto exit;
 		}
-		init_h2fwcmd_w_parm_no_rsp(cmdobj, param, _DisConnect_CMD_);
+		init_h2fwcmd_w_parm_no_rsp(cmdobj, param, DISCONNECT_CMD);
 		res = rtw_enqueue_cmd(cmdpriv, cmdobj);
 	} else {
 		/* no need to enqueue, do the cmd hdl directly and free cmd parameter */
@@ -847,7 +847,7 @@ u8 rtw_setopmode_cmd(struct adapter  *padapter, enum ndis_802_11_network_infrast
 			goto exit;
 		}
 
-		init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, _SetOpMode_CMD_);
+		init_h2fwcmd_w_parm_no_rsp(ph2c, psetop, SET_OP_MODE_CMD);
 		res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 	} else {
 		setopmode_hdl(padapter, (u8 *)psetop);
@@ -904,7 +904,7 @@ u8 rtw_setstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 unicast_
 			goto exit;
 		}
 
-		init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+		init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, SET_STA_KEY_CMD);
 		ph2c->rsp = (u8 *)psetstakey_rsp;
 		ph2c->rspsz = sizeof(struct set_stakey_rsp);
 		res = rtw_enqueue_cmd(pcmdpriv, ph2c);
@@ -955,7 +955,7 @@ u8 rtw_clearstakey_cmd(struct adapter *padapter, struct sta_info *sta, u8 enqueu
 			goto exit;
 		}
 
-		init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, _SetStaKey_CMD_);
+		init_h2fwcmd_w_parm_no_rsp(ph2c, psetstakey_para, SET_STA_KEY_CMD);
 		ph2c->rsp = (u8 *)psetstakey_rsp;
 		ph2c->rspsz = sizeof(struct set_stakey_rsp);
 
@@ -993,7 +993,7 @@ u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr)
 	paddbareq_parm->tid = tid;
 	memcpy(paddbareq_parm->addr, addr, ETH_ALEN);
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, GEN_CMD_CODE(_AddBAReq));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, paddbareq_parm, ADD_BA_REQ_CMD);
 
 	/* rtw_enqueue_cmd(pcmdpriv, ph2c); */
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
@@ -1028,7 +1028,7 @@ u8 rtw_reset_securitypriv_cmd(struct adapter *padapter)
 	pdrvextra_cmd_parm->size = 0;
 	pdrvextra_cmd_parm->pbuf = NULL;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	/* rtw_enqueue_cmd(pcmdpriv, ph2c); */
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
@@ -1061,7 +1061,7 @@ u8 rtw_free_assoc_resources_cmd(struct adapter *padapter)
 	pdrvextra_cmd_parm->size = 0;
 	pdrvextra_cmd_parm->pbuf = NULL;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	/* rtw_enqueue_cmd(pcmdpriv, ph2c); */
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
@@ -1094,7 +1094,7 @@ u8 rtw_dynamic_chk_wk_cmd(struct adapter *padapter)
 	pdrvextra_cmd_parm->type = 0;
 	pdrvextra_cmd_parm->size = 0;
 	pdrvextra_cmd_parm->pbuf = NULL;
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	/* rtw_enqueue_cmd(pcmdpriv, ph2c); */
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
@@ -1343,7 +1343,7 @@ u8 rtw_lps_ctrl_wk_cmd(struct adapter *padapter, u8 lps_ctrl_type, u8 enqueue)
 		pdrvextra_cmd_parm->size = 0;
 		pdrvextra_cmd_parm->pbuf = NULL;
 
-		init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+		init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 		res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 	} else {
@@ -1384,7 +1384,7 @@ u8 rtw_dm_in_lps_wk_cmd(struct adapter *padapter)
 	pdrvextra_cmd_parm->size = 0;
 	pdrvextra_cmd_parm->pbuf = NULL;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 
@@ -1446,7 +1446,7 @@ u8 rtw_dm_ra_mask_wk_cmd(struct adapter *padapter, u8 *psta)
 	pdrvextra_cmd_parm->size = 0;
 	pdrvextra_cmd_parm->pbuf = psta;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 
@@ -1479,7 +1479,7 @@ u8 rtw_ps_cmd(struct adapter *padapter)
 	pdrvextra_cmd_parm->type = 0;
 	pdrvextra_cmd_parm->size = 0;
 	pdrvextra_cmd_parm->pbuf = NULL;
-	init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ppscmd, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ppscmd);
 
@@ -1550,7 +1550,7 @@ u8 rtw_chk_hi_queue_cmd(struct adapter *padapter)
 	pdrvextra_cmd_parm->size = 0;
 	pdrvextra_cmd_parm->pbuf = NULL;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 
@@ -1642,7 +1642,7 @@ u8 rtw_c2h_packet_wk_cmd(struct adapter *padapter, u8 *pbuf, u16 length)
 	pdrvextra_cmd_parm->size = length;
 	pdrvextra_cmd_parm->pbuf = pbuf;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 
@@ -1677,7 +1677,7 @@ u8 rtw_c2h_wk_cmd(struct adapter *padapter, u8 *c2h_evt)
 	pdrvextra_cmd_parm->size =  c2h_evt ? 16 : 0;
 	pdrvextra_cmd_parm->pbuf = c2h_evt;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, GEN_CMD_CODE(_Set_Drv_Extra));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, pdrvextra_cmd_parm, SET_DRV_EXTRA_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c
index ddfc56f0253d..ddddd5e462b4 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c
@@ -1862,7 +1862,7 @@ signed int rtw_set_auth(struct adapter *adapter, struct security_priv *psecurity
 
 	psetauthparm->mode = (unsigned char)psecuritypriv->dot11AuthAlgrthm;
 
-	pcmd->cmdcode = _SetAuth_CMD_;
+	pcmd->cmdcode = SET_AUTH_CMD;
 	pcmd->parmbuf = (unsigned char *)psetauthparm;
 	pcmd->cmdsz =  (sizeof(struct setauth_parm));
 	pcmd->rsp = NULL;
@@ -1933,7 +1933,7 @@ signed int rtw_set_key(struct adapter *adapter, struct security_priv *psecurityp
 			goto exit;
 		}
 
-		pcmd->cmdcode = _SetKey_CMD_;
+		pcmd->cmdcode = SET_KEY_CMD;
 		pcmd->parmbuf = (u8 *)psetkeyparm;
 		pcmd->cmdsz =  (sizeof(struct setkey_parm));
 		pcmd->rsp = NULL;
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
index 5f00fe282d1b..abcc666d5dbe 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
@@ -4391,7 +4391,7 @@ void report_survey_event(struct adapter *padapter, union recv_frame *precv_frame
 
 	INIT_LIST_HEAD(&pcmd_obj->list);
 
-	pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+	pcmd_obj->cmdcode = SET_MLME_EVT_CMD;
 	pcmd_obj->cmdsz = cmdsz;
 	pcmd_obj->parmbuf = pevtcmd;
 
@@ -4444,7 +4444,7 @@ void report_surveydone_event(struct adapter *padapter)
 
 	INIT_LIST_HEAD(&pcmd_obj->list);
 
-	pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+	pcmd_obj->cmdcode = SET_MLME_EVT_CMD;
 	pcmd_obj->cmdsz = cmdsz;
 	pcmd_obj->parmbuf = pevtcmd;
 
@@ -4489,7 +4489,7 @@ void report_join_res(struct adapter *padapter, int res)
 
 	INIT_LIST_HEAD(&pcmd_obj->list);
 
-	pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+	pcmd_obj->cmdcode = SET_MLME_EVT_CMD;
 	pcmd_obj->cmdsz = cmdsz;
 	pcmd_obj->parmbuf = pevtcmd;
 
@@ -4538,7 +4538,7 @@ void report_wmm_edca_update(struct adapter *padapter)
 
 	INIT_LIST_HEAD(&pcmd_obj->list);
 
-	pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+	pcmd_obj->cmdcode = SET_MLME_EVT_CMD;
 	pcmd_obj->cmdsz = cmdsz;
 	pcmd_obj->parmbuf = pevtcmd;
 
@@ -4584,7 +4584,7 @@ void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsi
 
 	INIT_LIST_HEAD(&pcmd_obj->list);
 
-	pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+	pcmd_obj->cmdcode = SET_MLME_EVT_CMD;
 	pcmd_obj->cmdsz = cmdsz;
 	pcmd_obj->parmbuf = pevtcmd;
 
@@ -4635,7 +4635,7 @@ void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr, int
 
 	INIT_LIST_HEAD(&pcmd_obj->list);
 
-	pcmd_obj->cmdcode = GEN_CMD_CODE(_Set_MLME_EVT);
+	pcmd_obj->cmdcode = SET_MLME_EVT_CMD;
 	pcmd_obj->cmdsz = cmdsz;
 	pcmd_obj->parmbuf = pevtcmd;
 
@@ -5085,7 +5085,7 @@ void survey_timer_hdl(struct timer_list *t)
 			return;
 		}
 
-		init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, GEN_CMD_CODE(_SiteSurvey));
+		init_h2fwcmd_w_parm_no_rsp(ph2c, psurveyPara, SITE_SURVEY_CMD);
 		rtw_enqueue_cmd(pcmdpriv, ph2c);
 	}
 }
@@ -5696,7 +5696,7 @@ u8 chk_bmc_sleepq_cmd(struct adapter *padapter)
 		goto exit;
 	}
 
-	init_h2fwcmd_w_parm_no_parm_rsp(ph2c, GEN_CMD_CODE(_ChkBMCSleepq));
+	init_h2fwcmd_w_parm_no_parm_rsp(ph2c, CHK_BMC_SLEEPQ_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 
@@ -5734,7 +5734,7 @@ u8 set_tx_beacon_cmd(struct adapter *padapter)
 				      pmlmeinfo->hidden_ssid_mode);
 	ptxBeacon_parm->network.ie_length += len_diff;
 
-	init_h2fwcmd_w_parm_no_rsp(ph2c, ptxBeacon_parm, GEN_CMD_CODE(_TX_Beacon));
+	init_h2fwcmd_w_parm_no_rsp(ph2c, ptxBeacon_parm, TX_BEACON_CMD);
 
 	res = rtw_enqueue_cmd(pcmdpriv, ph2c);
 
diff --git a/drivers/staging/rtl8723bs/include/rtw_cmd.h b/drivers/staging/rtl8723bs/include/rtw_cmd.h
index c4c3edee809d..af94d31425ba 100644
--- a/drivers/staging/rtl8723bs/include/rtw_cmd.h
+++ b/drivers/staging/rtl8723bs/include/rtw_cmd.h
@@ -551,9 +551,6 @@ struct RunInThread_param {
 };
 
 
-#define GEN_CMD_CODE(cmd)	cmd ## _CMD_
-
-
 /*
 
 Result:
@@ -630,86 +627,81 @@ struct _cmd_callback {
 };
 
 enum {
-	GEN_CMD_CODE(_Read_MACREG),	/*0*/
-	GEN_CMD_CODE(_Write_MACREG),
-	GEN_CMD_CODE(_Read_BBREG),
-	GEN_CMD_CODE(_Write_BBREG),
-	GEN_CMD_CODE(_Read_RFREG),
-	GEN_CMD_CODE(_Write_RFREG), /*5*/
-	GEN_CMD_CODE(_Read_EEPROM),
-	GEN_CMD_CODE(_Write_EEPROM),
-	GEN_CMD_CODE(_Read_EFUSE),
-	GEN_CMD_CODE(_Write_EFUSE),
-
-	GEN_CMD_CODE(_Read_CAM),	/*10*/
-	GEN_CMD_CODE(_Write_CAM),
-	GEN_CMD_CODE(_setBCNITV),
-	GEN_CMD_CODE(_setMBIDCFG),
-	GEN_CMD_CODE(_JoinBss),   /*14*/
-	GEN_CMD_CODE(_DisConnect), /*15*/
-	GEN_CMD_CODE(_CreateBss),
-	GEN_CMD_CODE(_SetOpMode),
-	GEN_CMD_CODE(_SiteSurvey),  /*18*/
-	GEN_CMD_CODE(_SetAuth),
-
-	GEN_CMD_CODE(_SetKey),	/*20*/
-	GEN_CMD_CODE(_SetStaKey),
-	GEN_CMD_CODE(_SetAssocSta),
-	GEN_CMD_CODE(_DelAssocSta),
-	GEN_CMD_CODE(_SetStaPwrState),
-	GEN_CMD_CODE(_SetBasicRate), /*25*/
-	GEN_CMD_CODE(_GetBasicRate),
-	GEN_CMD_CODE(_SetDataRate),
-	GEN_CMD_CODE(_GetDataRate),
-	GEN_CMD_CODE(_SetPhyInfo),
-
-	GEN_CMD_CODE(_GetPhyInfo),	/*30*/
-	GEN_CMD_CODE(_SetPhy),
-	GEN_CMD_CODE(_GetPhy),
-	GEN_CMD_CODE(_readRssi),
-	GEN_CMD_CODE(_readGain),
-	GEN_CMD_CODE(_SetAtim), /*35*/
-	GEN_CMD_CODE(_SetPwrMode),
-	GEN_CMD_CODE(_JoinbssRpt),
-	GEN_CMD_CODE(_SetRaTable),
-	GEN_CMD_CODE(_GetRaTable),
-
-	GEN_CMD_CODE(_GetCCXReport), /*40*/
-	GEN_CMD_CODE(_GetDTMReport),
-	GEN_CMD_CODE(_GetTXRateStatistics),
-	GEN_CMD_CODE(_SetUsbSuspend),
-	GEN_CMD_CODE(_SetH2cLbk),
-	GEN_CMD_CODE(_AddBAReq), /*45*/
-	GEN_CMD_CODE(_SetChannel), /*46*/
-	GEN_CMD_CODE(_SetTxPower),
-	GEN_CMD_CODE(_SwitchAntenna),
-	GEN_CMD_CODE(_SetCrystalCap),
-	GEN_CMD_CODE(_SetSingleCarrierTx), /*50*/
-
-	GEN_CMD_CODE(_SetSingleToneTx),/*51*/
-	GEN_CMD_CODE(_SetCarrierSuppressionTx),
-	GEN_CMD_CODE(_SetContinuousTx),
-	GEN_CMD_CODE(_SwitchBandwidth), /*54*/
-	GEN_CMD_CODE(_TX_Beacon), /*55*/
-
-	GEN_CMD_CODE(_Set_MLME_EVT), /*56*/
-	GEN_CMD_CODE(_Set_Drv_Extra), /*57*/
-	GEN_CMD_CODE(_Set_H2C_MSG), /*58*/
-
-	GEN_CMD_CODE(_SetChannelPlan), /*59*/
-
-	GEN_CMD_CODE(_SetChannelSwitch), /*60*/
-	GEN_CMD_CODE(_TDLS), /*61*/
-	GEN_CMD_CODE(_ChkBMCSleepq), /*62*/
-
-	GEN_CMD_CODE(_RunInThreadCMD), /*63*/
+	READ_MACREG_CMD,	/*0*/
+	WRITE_MACREG_CMD,
+	READ_BBREG_CMD,
+	WRITE_BBREG_CMD,
+	READ_RFREG_CMD,
+	WRITE_RFREG_CMD, /*5*/
+	READ_EEPROM_CMD,
+	WRITE_EEPROM_CMD,
+	READ_EFUSE_CMD,
+	WRITE_EFUSE_CMD,
+
+	READ_CAM_CMD,	/*10*/
+	WRITE_CAM_CMD,
+	SET_BCNITV_CMD,
+	SET_MBIDCFG_CMD,
+	JOIN_BSS_CMD,   /*14*/
+	DISCONNECT_CMD, /*15*/
+	CREATE_BSS_CMD,
+	SET_OP_MODE_CMD,
+	SITE_SURVEY_CMD,  /*18*/
+	SET_AUTH_CMD,
+
+	SET_KEY_CMD,	/*20*/
+	SET_STA_KEY_CMD,
+	SET_ASSOC_STA_CMD,
+	DEL_ASSOC_STA_CMD,
+	SET_STA_PWR_STATE_CMD,
+	SET_BASIC_RATE_CMD, /*25*/
+	GET_BASIC_RATE_CMD,
+	SET_DATA_RATE_CMD,
+	GET_DATA_RATE_CMD,
+	SET_PHY_INFO_CMD,
+
+	GET_PHY_INFO_CMD,	/*30*/
+	SET_PHY_CMD,
+	GET_PHY_CMD,
+	READ_RSSI_CMD,
+	READ_GAIN_CMD,
+	SET_ATIM_CMD, /*35*/
+	SET_PWR_MODE_CMD,
+	JOIN_BSS_RPT_CMD,
+	SET_RA_TABLE_CMD,
+	GET_RA_TABLE_CMD,
+
+	GET_CCX_REPORT_CMD, /*40*/
+	GET_DTM_REPORT_CMD,
+	GET_TX_RATE_STATISTICS_CMD,
+	SET_USB_SUSPEND_CMD,
+	SET_H2C_LBK_CMD,
+	ADD_BA_REQ_CMD, /*45*/
+	SET_CHANNEL_CMD, /*46*/
+	SET_TX_POWER_CMD,
+	SWITCH_ANTENNA_CMD,
+	SET_CRYSTAL_CAP_CMD,
+	SET_SINGLE_CARRIER_TX_CMD, /*50*/
+
+	SET_SINGLE_TONE_TX_CMD,/*51*/
+	SET_CARRIER_SUPPRESSION_TX_CMD,
+	SET_CONTINUOUS_TX_CMD,
+	SWITCH_BANDWIDTH_CMD, /*54*/
+	TX_BEACON_CMD, /*55*/
+
+	SET_MLME_EVT_CMD, /*56*/
+	SET_DRV_EXTRA_CMD, /*57*/
+	SET_H2C_MSG_CMD, /*58*/
+
+	SET_CHANNEL_PLAN_CMD, /*59*/
+
+	SET_CHANNEL_SWITCH_CMD, /*60*/
+	TDLS_CMD, /*61*/
+	CHK_BMC_SLEEPQ_CMD, /*62*/
+
+	RUN_IN_THREAD_CMD, /*63*/
 
 	MAX_H2CCMD
 };
 
-#define _GetBBReg_CMD_		_Read_BBREG_CMD_
-#define _SetBBReg_CMD_		_Write_BBREG_CMD_
-#define _GetRFReg_CMD_		_Read_RFREG_CMD_
-#define _SetRFReg_CMD_		_Write_RFREG_CMD_
-
 #endif /*  _CMD_H_ */

-- 
2.54.0


^ permalink raw reply related

* [PATCH 2/2] staging: rtl8723bs: drop GEN_EVT_CODE macro and capitalize labels
From: Aiman Najjar @ 2026-06-10  1:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: kernel-janitors, linux-staging, linux-kernel, Aiman Najjar
In-Reply-To: <20260609-rtl8723bs-code-style-v1-0-daa0e85359a6@linux.com>

The use of GEN_EVT_CODE macro to generate event enum label name is applied
inconsistently and is confusing, it also makes it harder to make use of
tools such as clangd when looking up symbols.

Replace them with writing the enum labels directly and adopting
new capitalized names instead of the current camel case ones.

Signed-off-by: Aiman Najjar <aiman@linux.com>
---
 drivers/staging/rtl8723bs/core/rtw_mlme_ext.c    | 12 +++---
 drivers/staging/rtl8723bs/include/rtw_event.h    |  4 --
 drivers/staging/rtl8723bs/include/rtw_mlme_ext.h | 54 ++++++++++++------------
 3 files changed, 33 insertions(+), 37 deletions(-)

diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
index abcc666d5dbe..833f802ffe62 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c
@@ -4400,7 +4400,7 @@ void report_survey_event(struct adapter *padapter, union recv_frame *precv_frame
 
 	pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
 	pc2h_evt_hdr->len = sizeof(struct survey_event);
-	pc2h_evt_hdr->ID = GEN_EVT_CODE(_Survey);
+	pc2h_evt_hdr->ID = SURVEY_EVENT;
 	pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
 
 	psurvey_evt = (struct survey_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
@@ -4453,7 +4453,7 @@ void report_surveydone_event(struct adapter *padapter)
 
 	pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
 	pc2h_evt_hdr->len = sizeof(struct surveydone_event);
-	pc2h_evt_hdr->ID = GEN_EVT_CODE(_SurveyDone);
+	pc2h_evt_hdr->ID = SURVEY_DONE_EVENT;
 	pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
 
 	psurveydone_evt = (struct surveydone_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
@@ -4498,7 +4498,7 @@ void report_join_res(struct adapter *padapter, int res)
 
 	pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
 	pc2h_evt_hdr->len = sizeof(struct joinbss_event);
-	pc2h_evt_hdr->ID = GEN_EVT_CODE(_JoinBss);
+	pc2h_evt_hdr->ID = JOIN_BSS_EVENT;
 	pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
 
 	pjoinbss_evt = (struct joinbss_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
@@ -4547,7 +4547,7 @@ void report_wmm_edca_update(struct adapter *padapter)
 
 	pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
 	pc2h_evt_hdr->len = sizeof(struct wmm_event);
-	pc2h_evt_hdr->ID = GEN_EVT_CODE(_WMM);
+	pc2h_evt_hdr->ID = WMM_EVENT;
 	pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
 
 	pwmm_event = (struct wmm_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
@@ -4593,7 +4593,7 @@ void report_del_sta_event(struct adapter *padapter, unsigned char *MacAddr, unsi
 
 	pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
 	pc2h_evt_hdr->len = sizeof(struct stadel_event);
-	pc2h_evt_hdr->ID = GEN_EVT_CODE(_DelSTA);
+	pc2h_evt_hdr->ID = DEL_STA_EVENT;
 	pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
 
 	pdel_sta_evt = (struct stadel_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
@@ -4644,7 +4644,7 @@ void report_add_sta_event(struct adapter *padapter, unsigned char *MacAddr, int
 
 	pc2h_evt_hdr = (struct C2HEvent_Header *)(pevtcmd);
 	pc2h_evt_hdr->len = sizeof(struct stassoc_event);
-	pc2h_evt_hdr->ID = GEN_EVT_CODE(_AddSTA);
+	pc2h_evt_hdr->ID = ADD_STA_EVENT;
 	pc2h_evt_hdr->seq = atomic_inc_return(&pmlmeext->event_seq);
 
 	padd_sta_evt = (struct stassoc_event *)(pevtcmd + sizeof(struct C2HEvent_Header));
diff --git a/drivers/staging/rtl8723bs/include/rtw_event.h b/drivers/staging/rtl8723bs/include/rtw_event.h
index 62e0dec249ad..e5cb46c2a731 100644
--- a/drivers/staging/rtl8723bs/include/rtw_event.h
+++ b/drivers/staging/rtl8723bs/include/rtw_event.h
@@ -64,10 +64,6 @@ struct wmm_event {
 	unsigned char wmm;
 };
 
-#define GEN_EVT_CODE(event)	event ## _EVT_
-
-
-
 struct fwevent {
 	u32 parmsize;
 	void (*event_callback)(struct adapter *dev, u8 *pbuf);
diff --git a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h
index 95769f90d196..5adcff9a734b 100644
--- a/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h
+++ b/drivers/staging/rtl8723bs/include/rtw_mlme_ext.h
@@ -679,33 +679,33 @@ void rtw_dummy_event_callback(struct adapter *adapter, u8 *pbuf);
 void rtw_fwdbg_event_callback(struct adapter *adapter, u8 *pbuf);
 
 enum {
-	GEN_EVT_CODE(_Read_MACREG) = 0, /*0*/
-	GEN_EVT_CODE(_Read_BBREG),
-	GEN_EVT_CODE(_Read_RFREG),
-	GEN_EVT_CODE(_Read_EEPROM),
-	GEN_EVT_CODE(_Read_EFUSE),
-	GEN_EVT_CODE(_Read_CAM),			/*5*/
-	GEN_EVT_CODE(_Get_BasicRate),
-	GEN_EVT_CODE(_Get_DataRate),
-	GEN_EVT_CODE(_Survey),	 /*8*/
-	GEN_EVT_CODE(_SurveyDone),	 /*9*/
-
-	GEN_EVT_CODE(_JoinBss), /*10*/
-	GEN_EVT_CODE(_AddSTA),
-	GEN_EVT_CODE(_DelSTA),
-	GEN_EVT_CODE(_AtimDone),
-	GEN_EVT_CODE(_TX_Report),
-	GEN_EVT_CODE(_CCX_Report),			/*15*/
-	GEN_EVT_CODE(_DTM_Report),
-	GEN_EVT_CODE(_TX_Rate_Statistics),
-	GEN_EVT_CODE(_C2HLBK),
-	GEN_EVT_CODE(_FWDBG),
-	GEN_EVT_CODE(_C2HFEEDBACK),               /*20*/
-	GEN_EVT_CODE(_ADDBA),
-	GEN_EVT_CODE(_C2HBCN),
-	GEN_EVT_CODE(_ReportPwrState),		/* filen: only for PCIE, USB */
-	GEN_EVT_CODE(_CloseRF),				/* filen: only for PCIE, work around ASPM */
-	GEN_EVT_CODE(_WMM),					/*25*/
+	READ_MACREG_EVENT = 0,		/*0*/
+	READ_BBREG_EVENT,
+	READ_RFREG_EVENT,
+	READ_EEPROM_EVENT,
+	READ_EFUSE_EVENT,
+	READ_CAM_EVENT,			/*5*/
+	GET_BASICRATE_EVENT,
+	GET_DATARATE_EVENT,
+	SURVEY_EVENT,			/*8*/
+	SURVEY_DONE_EVENT,		/*9*/
+
+	JOIN_BSS_EVENT,			/*10*/
+	ADD_STA_EVENT,
+	DEL_STA_EVENT,
+	ATIM_DONE_EVENT,
+	TX_REPORT_EVENT,
+	CCX_REPORT_EVENT,		/*15*/
+	DTM_REPORT_EVENT,
+	TX_RATE_STATISTICS_EVENT,
+	C2HLBK_EVENT,
+	FWDBG_EVENT,
+	C2HFEEDBACK_EVENT,               /*20*/
+	ADDBA_EVENT,
+	C2HBCN_EVENT,
+	REPORT_PWR_STATE_EVENT,		/* filen: only for PCIE, USB */
+	CLOSE_RF_EVENT,			/* filen: only for PCIE, work around ASPM */
+	WMM_EVENT,			/*25*/
 	MAX_C2HEVT
 };
 

-- 
2.54.0


^ permalink raw reply related

* [PATCH 0/2] staging: rtl8723bs: enum labels naming, coding style
From: Aiman Najjar @ 2026-06-10  1:42 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: kernel-janitors, linux-staging, linux-kernel, Aiman Najjar

Enum labels generated by token-pasting macros adopt camel case. This
patch removes the use of macro to generate the enum label names and
adopts new captialized names.

Signed-off-by: Aiman Najjar <aiman@linux.com>
---
Aiman Najjar (2):
      staging: rtl8723bs: drop GEN_CMD_CODE macro and capitalize labels
      staging: rtl8723bs: drop GEN_EVT_CODE macro and capitalize labels

 drivers/staging/rtl8723bs/core/rtw_ap.c          |   4 +-
 drivers/staging/rtl8723bs/core/rtw_cmd.c         | 190 +++++++++++------------
 drivers/staging/rtl8723bs/core/rtw_mlme.c        |   4 +-
 drivers/staging/rtl8723bs/core/rtw_mlme_ext.c    |  30 ++--
 drivers/staging/rtl8723bs/include/rtw_cmd.h      | 154 +++++++++---------
 drivers/staging/rtl8723bs/include/rtw_event.h    |   4 -
 drivers/staging/rtl8723bs/include/rtw_mlme_ext.h |  54 +++----
 7 files changed, 214 insertions(+), 226 deletions(-)
---
base-commit: ad4605d69bab07941460db0a0cba88ac5c3302cf
change-id: 20260609-rtl8723bs-code-style-0871b80b6799

Best regards,
--  
Aiman Najjar <aiman@linux.com>


^ permalink raw reply

* Re: [PATCH] staging: media: atomisp: use kvmalloc_objs() in make_histogram()
From: Andy Shevchenko @ 2026-06-10 13:36 UTC (permalink / raw)
  To: Rodrigo Gobbi
  Cc: andy, hansg, mchehab, sakari.ailus, gregkh, feng, ~lkcamp/patches,
	linux-kernel-mentees, linux-kernel, linux-media, linux-staging
In-Reply-To: <20260609215110.118860-1-rodrigo.gobbi.7@gmail.com>

On Tue, Jun 09, 2026 at 06:46:31PM -0300, Rodrigo Gobbi wrote:
> Replace kvmalloc() with multiply with kvmalloc_objs(), which handles
> the size multiplication internally with overflow checking, silenting
> checkpatch warn.
> 
> Signed-off-by: Rodrigo Gobbi <rodrigo.gobbi.7@gmail.com>
> ---
> Hi, all,
> There is a ongoing effort like this for other files from atomisp
> at [1], yet, it is not covering the same file.
> Tks and regards.
> 
> [1] https://lore.kernel.org/all/20260413112904.98864-1-feng@innora.ai/

Yeah, the problem is that the activity seems stale. Can you pickup all
the patches from the mailing list that have not been yet applied (regarding
k*alloc() uses) and combine them into series or so and update regarding to
Sakari's comments?


-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v13 14/22] media: i2c: add Maxim GMSL2/3 serializer framework
From: Niklas Söderlund @ 2026-06-10 14:32 UTC (permalink / raw)
  To: dumitru.ceclan
  Cc: Tomi Valkeinen, Mauro Carvalho Chehab, Sakari Ailus,
	Laurent Pinchart, Julien Massot, Rob Herring, Greg Kroah-Hartman,
	mitrutzceclan, linux-media, linux-kernel, devicetree,
	linux-staging, linux-gpio, Martin Hecht, Cosmin Tanislav
In-Reply-To: <20260604-gmsl2-3_serdes-v13-14-9d8a4919983b@analog.com>

Hello,

Small nit which I'm not sure is correct "fixing", but running 
checkpatch,

    total: 0 errors, 187 warnings, 0 checks, 2335 lines checked

A quick look at the warnings suggest all are of this type,

    WARNING: 'ser' may be misspelled - perhaps 'set'?
    #72: FILE: drivers/media/i2c/maxim-serdes/max_ser.c:25:
    +	struct max_ser *ser;
                        ^^^
So no need to change anything, but maybe worth doing to make life easier 
for the future? I'm OK with it just thought I mention it.

On 2026-06-04 17:14:01 +0300, Dumitru Ceclan via B4 Relay wrote:
> From: Cosmin Tanislav <demonsingur@gmail.com>
> 
> These drivers are meant to be used as a common framework for Maxim
> GMSL2/3 serializers.
> 
> This framework enables support for the following new features across
> all the chips:
>  * Full Streams API support
>  * .get_frame_desc()
>  * I2C ATR
>  * automatic GMSL link version negotiation
>  * automatic stream id selection
>  * automatic VC remapping
>  * automatic pixel mode / tunnel mode selection
>  * automatic double mode selection / data padding
>  * logging of internal state and chip status registers via .log_status()
>  * PHY modes
>  * serializer pinctrl
>  * TPG
> 
> Signed-off-by: Cosmin Tanislav <demonsingur@gmail.com>
> ---
>  drivers/media/i2c/maxim-serdes/Makefile  |    2 +-
>  drivers/media/i2c/maxim-serdes/max_ser.c | 2184 ++++++++++++++++++++++++++++++
>  drivers/media/i2c/maxim-serdes/max_ser.h |  147 ++
>  3 files changed, 2332 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile
> index 630fbb486bab..17511cb03369 100644
> --- a/drivers/media/i2c/maxim-serdes/Makefile
> +++ b/drivers/media/i2c/maxim-serdes/Makefile
> @@ -1,3 +1,3 @@
>  # SPDX-License-Identifier: GPL-2.0
> -max-serdes-objs := max_serdes.o
> +max-serdes-objs := max_serdes.o max_ser.o
>  obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o
> diff --git a/drivers/media/i2c/maxim-serdes/max_ser.c b/drivers/media/i2c/maxim-serdes/max_ser.c
> new file mode 100644
> index 000000000000..a193381435e6
> --- /dev/null
> +++ b/drivers/media/i2c/maxim-serdes/max_ser.c
> @@ -0,0 +1,2184 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Maxim GMSL2 Serializer Driver
> + *
> + * Copyright (C) 2025 Analog Devices Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/i2c-atr.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/module.h>
> +
> +#include <media/mipi-csi2.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "max_ser.h"
> +#include "max_serdes.h"
> +
> +#define MAX_SER_NUM_LINKS	1
> +#define MAX_SER_NUM_PHYS	1
> +
> +struct max_ser_priv {
> +	struct max_ser *ser;
> +	struct device *dev;
> +	struct i2c_client *client;
> +
> +	struct i2c_atr *atr;
> +	struct i2c_mux_core *mux;
> +
> +	struct media_pad *pads;
> +	struct max_serdes_source *sources;
> +	u64 *streams_masks;
> +	u32 double_bpps;
> +
> +	struct v4l2_subdev sd;
> +	struct v4l2_async_notifier nf;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +};
> +
> +struct max_ser_route_hw {
> +	struct max_serdes_source *source;
> +	struct max_ser_pipe *pipe;
> +	struct v4l2_mbus_frame_desc_entry entry;
> +	bool is_tpg;
> +};
> +
> +static inline struct max_ser_priv *sd_to_priv(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct max_ser_priv, sd);
> +}
> +
> +static inline struct max_ser_priv *nf_to_priv(struct v4l2_async_notifier *nf)
> +{
> +	return container_of(nf, struct max_ser_priv, nf);
> +}
> +
> +static inline struct max_ser_priv *ctrl_to_priv(struct v4l2_ctrl_handler *handler)
> +{
> +	return container_of(handler, struct max_ser_priv, ctrl_handler);
> +}
> +
> +static inline bool max_ser_pad_is_sink(struct max_ser *ser, u32 pad)
> +{
> +	return pad < ser->ops->num_phys;
> +}
> +
> +static inline bool max_ser_pad_is_source(struct max_ser *ser, u32 pad)
> +{
> +	return pad >= ser->ops->num_phys &&
> +	       pad < ser->ops->num_phys + MAX_SER_NUM_LINKS;
> +}
> +
> +static inline u32 max_ser_source_pad(struct max_ser *ser)
> +{
> +	return ser->ops->num_phys;
> +}
> +
> +static inline bool max_ser_pad_is_tpg(struct max_ser *ser, u32 pad)
> +{
> +	return pad >= ser->ops->num_phys + MAX_SER_NUM_LINKS;
> +}
> +
> +static inline unsigned int max_ser_phy_to_pad(struct max_ser *ser,
> +					      struct max_ser_phy *phy)
> +{
> +	return phy->index;
> +}
> +
> +static inline unsigned int max_ser_num_pads(struct max_ser *ser)
> +{
> +	return ser->ops->num_phys + MAX_SER_NUM_LINKS +
> +	       (ser->ops->set_tpg ? 1 : 0);
> +}
> +
> +static struct max_ser_phy *max_ser_pad_to_phy(struct max_ser *ser, u32 pad)
> +{
> +	if (!max_ser_pad_is_sink(ser, pad))
> +		return NULL;
> +
> +	return &ser->phys[pad];
> +}
> +
> +static struct max_ser_pipe *
> +max_ser_find_phy_pipe(struct max_ser *ser, struct max_ser_phy *phy)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ser->ops->num_pipes; i++) {
> +		struct max_ser_pipe *pipe = &ser->pipes[i];
> +
> +		if (pipe->phy_id == phy->index)
> +			return pipe;
> +	}
> +
> +	return NULL;
> +}
> +
> +static struct max_serdes_source *
> +max_ser_get_phy_source(struct max_ser_priv *priv, struct max_ser_phy *phy)
> +{
> +	return &priv->sources[phy->index];
> +}
> +
> +static const struct max_serdes_tpg_entry *
> +max_ser_find_tpg_entry(struct max_ser *ser, u32 target_index,
> +		       u32 width, u32 height, u32 code,
> +		       u32 numerator, u32 denominator)
> +{
> +	const struct max_serdes_tpg_entry *entry;
> +	unsigned int index = 0;
> +	unsigned int i;
> +
> +	for (i = 0; i < ser->ops->tpg_entries.num_entries; i++) {
> +		entry = &ser->ops->tpg_entries.entries[i];
> +
> +		if ((width != 0 && width != entry->width) ||
> +		    (height != 0 && height != entry->height) ||
> +		    (code != 0 && code != entry->code) ||
> +		    (numerator != 0 && numerator != entry->interval.numerator) ||
> +		    (denominator != 0 && denominator != entry->interval.denominator))
> +			continue;
> +
> +		if (index == target_index)
> +			break;
> +
> +		index++;
> +	}
> +
> +	if (i == ser->ops->tpg_entries.num_entries)
> +		return NULL;
> +
> +	return &ser->ops->tpg_entries.entries[i];
> +}
> +
> +static const struct max_serdes_tpg_entry *
> +max_ser_find_state_tpg_entry(struct max_ser *ser, struct v4l2_subdev_state *state,
> +			     unsigned int pad)
> +{
> +	struct v4l2_mbus_framefmt *fmt;
> +	struct v4l2_fract *in;
> +
> +	fmt = v4l2_subdev_state_get_format(state, pad, MAX_SERDES_TPG_STREAM);
> +	if (!fmt)
> +		return NULL;
> +
> +	in = v4l2_subdev_state_get_interval(state, pad, MAX_SERDES_TPG_STREAM);
> +	if (!in)
> +		return NULL;
> +
> +	return max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height, fmt->code,
> +				      in->numerator, in->denominator);
> +}
> +
> +static int max_ser_get_tpg_fd_entry_state(struct max_ser *ser,
> +					  struct v4l2_subdev_state *state,
> +					  struct v4l2_mbus_frame_desc_entry *fd_entry,
> +					  unsigned int pad)
> +{
> +	const struct max_serdes_tpg_entry *entry;
> +
> +	entry = max_ser_find_state_tpg_entry(ser, state, pad);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	fd_entry->stream = MAX_SERDES_TPG_STREAM;
> +	fd_entry->flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
> +	fd_entry->length = entry->width * entry->height * entry->bpp / 8;
> +	fd_entry->pixelcode = entry->code;
> +	fd_entry->bus.csi2.vc = 0;
> +	fd_entry->bus.csi2.dt = entry->dt;
> +
> +	return 0;
> +}
> +
> +static int max_ser_tpg_route_to_hw(struct max_ser_priv *priv,
> +				   struct v4l2_subdev_state *state,
> +				   struct v4l2_subdev_route *route,
> +				   struct max_ser_route_hw *hw)
> +{
> +	struct max_ser *ser = priv->ser;
> +
> +	hw->pipe = &ser->pipes[0];
> +
> +	return max_ser_get_tpg_fd_entry_state(ser, state, &hw->entry,
> +					      route->sink_pad);
> +}
> +
> +static int max_ser_route_to_hw(struct max_ser_priv *priv,
> +			       struct v4l2_subdev_state *state,
> +			       struct v4l2_subdev_route *route,
> +			       struct max_ser_route_hw *hw)
> +{
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_mbus_frame_desc fd = {};
> +	struct max_ser_phy *phy;
> +	unsigned int i;
> +	int ret;
> +
> +	memset(hw, 0, sizeof(*hw));
> +
> +	hw->is_tpg = max_ser_pad_is_tpg(ser, route->sink_pad);
> +	if (hw->is_tpg)
> +		return max_ser_tpg_route_to_hw(priv, state, route, hw);
> +
> +	phy = max_ser_pad_to_phy(ser, route->sink_pad);
> +	if (!phy)
> +		return -ENOENT;
> +
> +	hw->pipe = max_ser_find_phy_pipe(ser, phy);
> +	if (!hw->pipe)
> +		return -ENOENT;
> +
> +	hw->source = max_ser_get_phy_source(priv, phy);
> +	if (!hw->source->sd)
> +		return 0;
> +
> +	ret = v4l2_subdev_call(hw->source->sd, pad, get_frame_desc,
> +			       hw->source->pad, &fd);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < fd.num_entries; i++)
> +		if (fd.entry[i].stream == route->sink_stream)
> +			break;
> +
> +	if (i == fd.num_entries)
> +		return -ENOENT;
> +
> +	hw->entry = fd.entry[i];
> +
> +	return 0;
> +}
> +
> +static int max_ser_phy_set_active(struct max_ser *ser, struct max_ser_phy *phy,
> +				  bool active)
> +{
> +	int ret;
> +
> +	if (ser->ops->set_phy_active) {
> +		ret = ser->ops->set_phy_active(ser, phy, active);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	phy->active = active;
> +
> +	return 0;
> +}
> +
> +static int max_ser_set_pipe_dts(struct max_ser_priv *priv, struct max_ser_pipe *pipe,
> +				unsigned int *dts, unsigned int num_dts)
> +{
> +	struct max_ser *ser = priv->ser;
> +	unsigned int i;
> +	int ret;
> +
> +	if (!ser->ops->set_pipe_dt || !ser->ops->set_pipe_dt_en)
> +		return 0;
> +
> +	for (i = 0; i < num_dts; i++) {
> +		ret = ser->ops->set_pipe_dt(ser, pipe, i, dts[i]);
> +		if (ret)
> +			return ret;
> +
> +		ret = ser->ops->set_pipe_dt_en(ser, pipe, i, true);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (num_dts == pipe->num_dts)
> +		return 0;
> +
> +	for (i = num_dts; i < ser->ops->num_dts_per_pipe; i++) {
> +		ret = ser->ops->set_pipe_dt_en(ser, pipe, i, false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_ser_set_pipe_mode(struct max_ser_priv *priv, struct max_ser_pipe *pipe,
> +				 struct max_ser_pipe_mode *mode)
> +{
> +	struct max_ser *ser = priv->ser;
> +
> +	if (mode->bpp == pipe->mode.bpp &&
> +	    mode->soft_bpp == pipe->mode.soft_bpp &&
> +	    mode->dbl8 == pipe->mode.dbl8 &&
> +	    mode->dbl10 == pipe->mode.dbl10 &&
> +	    mode->dbl12 == pipe->mode.dbl12)
> +		return 0;
> +
> +	if (!ser->ops->set_pipe_mode)
> +		return 0;
> +
> +	return ser->ops->set_pipe_mode(ser, pipe, mode);
> +}
> +
> +static int max_ser_i2c_atr_attach_addr(struct i2c_atr *atr, u32 chan_id,
> +				       u16 addr, u16 alias)
> +{
> +	struct max_serdes_i2c_xlate xlate = {
> +		.src = alias,
> +		.dst = addr,
> +		.en = true,
> +	};
> +	struct max_ser_priv *priv = i2c_atr_get_driver_data(atr);
> +	struct max_ser *ser = priv->ser;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ser->ops->num_i2c_xlates; i++)
> +		if (!ser->i2c_xlates[i].en)
> +			break;
> +
> +	if (i == ser->ops->num_i2c_xlates) {
> +		dev_err(priv->dev,
> +			"Reached maximum number of I2C translations\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = ser->ops->set_i2c_xlate(ser, i, &xlate);
> +	if (ret)
> +		return ret;
> +
> +	ser->i2c_xlates[i] = xlate;
> +
> +	return 0;
> +}
> +
> +static void max_ser_i2c_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr)
> +{
> +	struct max_ser_priv *priv = i2c_atr_get_driver_data(atr);
> +	struct max_ser *ser = priv->ser;
> +	struct max_serdes_i2c_xlate xlate = { 0 };
> +	unsigned int i;
> +
> +	/* Find index of matching I2C translation. */
> +	for (i = 0; i < ser->ops->num_i2c_xlates; i++)
> +		if (ser->i2c_xlates[i].dst == addr)
> +			break;
> +
> +	if (WARN_ON(i == ser->ops->num_i2c_xlates))
> +		return;
> +
> +	ser->ops->set_i2c_xlate(ser, i, &xlate);
> +	ser->i2c_xlates[i] = xlate;
> +}
> +
> +static const struct i2c_atr_ops max_ser_i2c_atr_ops = {
> +	.attach_addr = max_ser_i2c_atr_attach_addr,
> +	.detach_addr = max_ser_i2c_atr_detach_addr,
> +};
> +
> +static void max_ser_i2c_atr_deinit(struct max_ser_priv *priv)
> +{
> +	/* Deleting adapters that haven't been added does no harm. */
> +	i2c_atr_del_adapter(priv->atr, 0);
> +
> +	i2c_atr_delete(priv->atr);
> +}
> +
> +static int max_ser_i2c_atr_init(struct max_ser_priv *priv)
> +{
> +	struct i2c_atr_adap_desc desc = {
> +		.chan_id = 0,
> +	};
> +	int ret;
> +
> +	if (!i2c_check_functionality(priv->client->adapter,
> +				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
> +		return -ENODEV;
> +
> +	priv->atr = i2c_atr_new(priv->client->adapter, priv->dev,
> +				&max_ser_i2c_atr_ops, 1, 0);
> +	if (IS_ERR(priv->atr))
> +		return PTR_ERR(priv->atr);
> +
> +	i2c_atr_set_driver_data(priv->atr, priv);
> +
> +	ret = i2c_atr_add_adapter(priv->atr, &desc);
> +	if (ret) {
> +		i2c_atr_delete(priv->atr);
> +		priv->atr = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int max_ser_i2c_mux_select(struct i2c_mux_core *mux, u32 chan)
> +{
> +	return 0;
> +}
> +
> +static void max_ser_i2c_mux_deinit(struct max_ser_priv *priv)
> +{
> +	i2c_mux_del_adapters(priv->mux);
> +}
> +
> +static int max_ser_i2c_mux_init(struct max_ser_priv *priv)
> +{
> +	priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,
> +				  1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
> +				  max_ser_i2c_mux_select, NULL);
> +	if (!priv->mux)
> +		return -ENOMEM;
> +
> +	return i2c_mux_add_adapter(priv->mux, 0, 0);
> +}
> +
> +static int max_ser_i2c_adapter_init(struct max_ser_priv *priv)
> +{
> +	struct fwnode_handle *fwnode;
> +
> +	fwnode = device_get_named_child_node(priv->dev, "i2c-gate");
> +	if (fwnode) {
> +		fwnode_handle_put(fwnode);
> +		return max_ser_i2c_mux_init(priv);
> +	}
> +
> +	return max_ser_i2c_atr_init(priv);
> +}
> +
> +static void max_ser_i2c_adapter_deinit(struct max_ser_priv *priv)
> +{
> +	if (device_get_named_child_node(priv->dev, "i2c-gate"))
> +		max_ser_i2c_mux_deinit(priv);
> +	else
> +		max_ser_i2c_atr_deinit(priv);
> +}
> +
> +static int max_ser_set_tpg_fmt(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_state *state,
> +			       struct v4l2_subdev_format *format)
> +{
> +	struct v4l2_mbus_framefmt *fmt = &format->format;
> +	struct max_ser_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_ser *ser = priv->ser;
> +	const struct max_serdes_tpg_entry *entry;
> +	struct v4l2_fract *in;
> +
> +	if (format->stream != MAX_SERDES_TPG_STREAM)
> +		return -EINVAL;
> +
> +	entry = max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height,
> +				       fmt->code, 0, 0);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	in = v4l2_subdev_state_get_interval(state, format->pad, format->stream);
> +	if (!in)
> +		return -EINVAL;
> +
> +	in->numerator = entry->interval.numerator;
> +	in->denominator = entry->interval.denominator;
> +
> +	return 0;
> +}
> +
> +static int max_ser_set_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_state *state,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct max_ser_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_mbus_framefmt *fmt;
> +	int ret;
> +
> +	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active)
> +		return -EBUSY;
> +
> +	/* No transcoding, source and sink formats must match. */
> +	if (max_ser_pad_is_source(ser, format->pad))
> +		return v4l2_subdev_get_fmt(sd, state, format);
> +
> +	if (max_ser_pad_is_tpg(ser, format->pad)) {
> +		ret = max_ser_set_tpg_fmt(sd, state, format);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (format->format.code == ~0U)
> +		format->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
> +
> +	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	*fmt = format->format;
> +
> +	fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
> +							   format->stream);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	*fmt = format->format;
> +
> +	return 0;
> +}
> +
> +static int max_ser_log_status(struct v4l2_subdev *sd)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_subdev_state *state;
> +	unsigned int i, j;
> +	int ret;
> +
> +	state = v4l2_subdev_lock_and_get_active_state(&priv->sd);
> +
> +	v4l2_info(sd, "mode: %s\n", max_serdes_gmsl_mode_str(ser->mode));
> +	if (ser->ops->set_tpg) {
> +		const struct max_serdes_tpg_entry *entry = ser->tpg_entry;
> +
> +		if (entry) {
> +			v4l2_info(sd, "tpg: %ux%u@%u/%u, code: %u, dt: %u, bpp: %u\n",
> +				  entry->width, entry->height,
> +				  entry->interval.numerator,
> +				  entry->interval.denominator,
> +				  entry->code, entry->dt,  entry->bpp);
> +		} else {
> +			v4l2_info(sd, "tpg: disabled\n");
> +		}
> +	}
> +	if (ser->ops->log_status) {
> +		ret = ser->ops->log_status(ser);
> +		if (ret)
> +			return ret;
> +	}
> +	v4l2_info(sd, "i2c_xlates:\n");
> +	for (i = 0; i < ser->ops->num_i2c_xlates; i++) {
> +		v4l2_info(sd, "\ten: %u, src: 0x%02x dst: 0x%02x\n",
> +			  ser->i2c_xlates[i].en, ser->i2c_xlates[i].src,
> +			  ser->i2c_xlates[i].dst);
> +		if (!ser->i2c_xlates[i].en)
> +			break;
> +	}
> +	v4l2_info(sd, "\n");
> +	if (ser->ops->set_vc_remap) {
> +		v4l2_info(sd, "vc_remaps: %u\n", ser->num_vc_remaps);
> +		for (j = 0; j < ser->num_vc_remaps; j++) {
> +			v4l2_info(sd, "\tvc_remap: src: %u, dst: %u\n",
> +				  ser->vc_remaps[j].src, ser->vc_remaps[j].dst);
> +		}
> +	}
> +	v4l2_info(sd, "\n");
> +
> +	for (i = 0; i < ser->ops->num_pipes; i++) {
> +		struct max_ser_pipe *pipe = &ser->pipes[i];
> +
> +		v4l2_info(sd, "pipe: %u\n", pipe->index);
> +		v4l2_info(sd, "\tenabled: %u\n", pipe->enabled);
> +
> +		if (!pipe->enabled) {
> +			v4l2_info(sd, "\n");
> +			continue;
> +		}
> +
> +		v4l2_info(sd, "\tphy_id: %u\n", pipe->phy_id);
> +		v4l2_info(sd, "\tstream_id: %u\n", pipe->stream_id);
> +		if (ser->ops->set_pipe_phy)
> +			v4l2_info(sd, "\tphy_id: %u\n", pipe->phy_id);
> +		if (ser->ops->set_pipe_dt) {
> +			v4l2_info(sd, "\tdts: %u\n", pipe->num_dts);
> +			for (j = 0; j < pipe->num_dts; j++)
> +				v4l2_info(sd, "\t\tdt: 0x%02x\n", pipe->dts[j]);
> +		}
> +		if (ser->ops->set_pipe_vcs)
> +			v4l2_info(sd, "\tvcs: 0x%08x\n", pipe->vcs);
> +		if (ser->ops->set_pipe_mode) {
> +			v4l2_info(sd, "\tdbl8: %u\n", pipe->mode.dbl8);
> +			v4l2_info(sd, "\tdbl10: %u\n", pipe->mode.dbl10);
> +			v4l2_info(sd, "\tdbl12: %u\n", pipe->mode.dbl12);
> +			v4l2_info(sd, "\tsoft_bpp: %u\n", pipe->mode.soft_bpp);
> +			v4l2_info(sd, "\tbpp: %u\n", pipe->mode.bpp);
> +		}
> +		if (ser->ops->log_pipe_status) {
> +			ret = ser->ops->log_pipe_status(ser, pipe);
> +			if (ret)
> +				goto out_unlock;
> +		}
> +		v4l2_info(sd, "\n");
> +	}
> +
> +	for (i = 0; i < ser->ops->num_phys; i++) {
> +		struct max_ser_phy *phy = &ser->phys[i];
> +
> +		v4l2_info(sd, "phy: %u\n", phy->index);
> +		v4l2_info(sd, "\tenabled: %u\n", phy->enabled);
> +
> +		if (!phy->enabled) {
> +			v4l2_info(sd, "\n");
> +			continue;
> +		}
> +
> +		v4l2_info(sd, "\tactive: %u\n", phy->active);
> +		v4l2_info(sd, "\tnum_data_lanes: %u\n", phy->mipi.num_data_lanes);
> +		v4l2_info(sd, "\tclock_lane: %u\n", phy->mipi.clock_lane);
> +		v4l2_info(sd, "\tnoncontinuous_clock: %u\n",
> +			  !!(phy->mipi.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK));
> +		if (ser->ops->log_phy_status) {
> +			ret = ser->ops->log_phy_status(ser, phy);
> +			if (ret)
> +				goto out_unlock;
> +		}
> +		v4l2_info(sd, "\n");
> +	}
> +
> +	ret = 0;
> +
> +out_unlock:
> +	v4l2_subdev_unlock_state(state);
> +
> +	return ret;
> +}
> +
> +static int max_ser_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct max_ser_priv *priv = ctrl_to_priv(ctrl->handler);
> +	struct max_ser *ser = priv->ser;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_TEST_PATTERN:
> +		ser->tpg_pattern = ctrl->val;
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int max_ser_enum_frame_interval(struct v4l2_subdev *sd,
> +				       struct v4l2_subdev_state *state,
> +				       struct v4l2_subdev_frame_interval_enum *fie)
> +{
> +	struct max_ser_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_ser *ser = priv->ser;
> +	const struct max_serdes_tpg_entry *entry;
> +
> +	if (!max_ser_pad_is_tpg(ser, fie->pad) ||
> +	    fie->stream != MAX_SERDES_TPG_STREAM)
> +		return -ENOTTY;
> +
> +	entry = max_ser_find_tpg_entry(ser, fie->index, fie->width, fie->height,
> +				       fie->code, fie->interval.denominator,
> +				       fie->interval.numerator);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	fie->interval.numerator = entry->interval.numerator;
> +	fie->interval.denominator = entry->interval.denominator;
> +
> +	return 0;
> +}
> +
> +static int max_ser_set_frame_interval(struct v4l2_subdev *sd,
> +				      struct v4l2_subdev_state *state,
> +				      struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct max_ser_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_ser *ser = priv->ser;
> +	const struct max_serdes_tpg_entry *entry;
> +	struct v4l2_mbus_framefmt *fmt;
> +	struct v4l2_fract *in;
> +
> +	if (!max_ser_pad_is_tpg(ser, fi->pad) ||
> +	    fi->stream != MAX_SERDES_TPG_STREAM)
> +		return -ENOTTY;
> +
> +	if (fi->which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active)
> +		return -EBUSY;
> +
> +	fmt = v4l2_subdev_state_get_format(state, fi->pad, fi->stream);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	entry = max_ser_find_tpg_entry(ser, 0, fmt->width, fmt->height,
> +				       fmt->code, fi->interval.denominator,
> +				       fi->interval.numerator);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	in = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream);
> +	if (!in)
> +		return -EINVAL;
> +
> +	in->numerator = fi->interval.numerator;
> +	in->denominator = fi->interval.denominator;
> +
> +	return 0;
> +}
> +
> +static int max_ser_get_frame_interval(struct v4l2_subdev *sd,
> +				      struct v4l2_subdev_state *state,
> +				      struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct max_ser_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_ser *ser = priv->ser;
> +
> +	if (!max_ser_pad_is_tpg(ser, fi->pad) ||
> +	    fi->stream != MAX_SERDES_TPG_STREAM)
> +		return -ENOTTY;
> +
> +	return v4l2_subdev_get_frame_interval(sd, state, fi);
> +}
> +
> +static int max_ser_get_frame_desc_state(struct v4l2_subdev *sd,
> +					struct v4l2_subdev_state *state,
> +					struct v4l2_mbus_frame_desc *fd,
> +					unsigned int pad)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_subdev_route *route;
> +	int ret;
> +
> +	if (!max_ser_pad_is_source(ser, pad))
> +		return -ENOENT;
> +
> +	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_ser_route_hw hw;
> +
> +		if (pad != route->source_pad)
> +			continue;
> +
> +		ret = max_ser_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		hw.entry.stream = route->source_stream;
> +
> +		fd->entry[fd->num_entries++] = hw.entry;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_ser_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
> +				  struct v4l2_mbus_frame_desc *fd)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct v4l2_subdev_state *state;
> +	int ret;
> +
> +	state = v4l2_subdev_lock_and_get_active_state(&priv->sd);
> +
> +	ret = max_ser_get_frame_desc_state(sd, state, fd, pad);
> +
> +	v4l2_subdev_unlock_state(state);
> +
> +	return ret;
> +}
> +
> +static int max_ser_set_tpg_routing(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_state *state,
> +				   struct v4l2_subdev_krouting *routing)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	const struct max_serdes_tpg_entry *entry;
> +	struct v4l2_mbus_framefmt fmt = { 0 };
> +	int ret;
> +
> +	ret = max_serdes_validate_tpg_routing(routing);
> +	if (ret)
> +		return ret;
> +
> +	entry = &ser->ops->tpg_entries.entries[0];
> +
> +	fmt.width = entry->width;
> +	fmt.height = entry->height;
> +	fmt.code = entry->code;
> +
> +	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt);
> +}
> +
> +static int __max_ser_set_routing(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *state,
> +				 struct v4l2_subdev_krouting *routing)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_subdev_route *route;
> +	bool is_tpg = false;
> +	int ret;
> +
> +	ret = v4l2_subdev_routing_validate(sd, routing,
> +					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
> +					   V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX);
> +	if (ret)
> +		return ret;
> +
> +	for_each_active_route(routing, route) {
> +		if (max_ser_pad_is_tpg(ser, route->sink_pad)) {
> +			is_tpg = true;
> +			break;
> +		}
> +	}
> +
> +	if (is_tpg)
> +		return max_ser_set_tpg_routing(sd, state, routing);
> +
> +	static const struct v4l2_mbus_framefmt format = {
> +			.code = MEDIA_BUS_FMT_Y8_1X8,
> +			.field = V4L2_FIELD_NONE,
> +			.width = 640,
> +			.height = 480,
> +		};
> +
> +	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
> +}
> +
> +static int max_ser_set_routing(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_state *state,
> +			       enum v4l2_subdev_format_whence which,
> +			       struct v4l2_subdev_krouting *routing)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +
> +	if (which == V4L2_SUBDEV_FORMAT_ACTIVE && ser->active)
> +		return -EBUSY;
> +
> +	return __max_ser_set_routing(sd, state, routing);
> +}
> +
> +static int max_ser_get_pipe_vcs_dts(struct max_ser_priv *priv,
> +				    struct v4l2_subdev_state *state,
> +				    struct max_ser_pipe *pipe,
> +				    unsigned int *vcs,
> +				    unsigned int *dts, unsigned int *num_dts,
> +				    u64 *streams_masks)
> +{
> +	struct v4l2_subdev_route *route;
> +	struct max_ser *ser = priv->ser;
> +	unsigned int i;
> +	int ret;
> +
> +	*vcs = 0;
> +	*num_dts = 0;
> +
> +	if (ser->mode != MAX_SERDES_GMSL_PIXEL_MODE)
> +		return 0;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_ser_route_hw hw;
> +		unsigned int vc, dt;
> +
> +		if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_ser_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (hw.pipe != pipe)
> +			continue;
> +
> +		vc = hw.entry.bus.csi2.vc;
> +		dt = hw.entry.bus.csi2.dt;
> +
> +		if (vc >= MAX_SERDES_VC_ID_NUM)
> +			return -E2BIG;
> +
> +		*vcs |= BIT(vc);
> +
> +		/* Skip already added DT. */
> +		for (i = 0; i < *num_dts; i++)
> +			if (dts[i] == dt)
> +				break;
> +
> +		if (i < *num_dts)
> +			continue;
> +
> +		if (*num_dts >= ser->ops->num_dts_per_pipe)
> +			return -EINVAL;
> +
> +		dts[*num_dts] = dt;
> +		(*num_dts)++;
> +	}
> +
> +	/*
> +	 * Hardware cannot distinguish between different pairs of VC and DT,
> +	 * issue a warning.
> +	 */
> +	for_each_active_route(&state->routing, route) {
> +		struct max_ser_route_hw hw;
> +		unsigned int vc, dt;
> +
> +		/*
> +		 * Skip enabled streams, we only want to check for leaks
> +		 * among the disabled streams.
> +		 */
> +		if ((BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_ser_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (hw.pipe != pipe)
> +			continue;
> +
> +		vc = hw.entry.bus.csi2.vc;
> +		dt = hw.entry.bus.csi2.dt;
> +
> +		if (vc >= MAX_SERDES_VC_ID_NUM)
> +			return -E2BIG;
> +
> +		if (!(*vcs & BIT(vc)))
> +			continue;
> +
> +		for (i = 0; i < *num_dts; i++)
> +			if (dts[i] == dt)
> +				break;
> +
> +		if (i == *num_dts)
> +			continue;
> +
> +		dev_warn(priv->dev, "Leaked disabled stream %u:%u with VC: %u, DT: %u",
> +			 route->source_pad, route->source_stream, vc, dt);
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_ser_get_pipe_mode(struct max_ser_priv *priv,
> +				 struct v4l2_subdev_state *state,
> +				 struct max_ser_pipe *pipe,
> +				 struct max_ser_pipe_mode *mode)
> +{
> +	struct v4l2_subdev_route *route;
> +	struct max_ser *ser = priv->ser;
> +	bool force_set_bpp = false;
> +	unsigned int doubled_bpp = 0;
> +	unsigned int min_bpp;
> +	unsigned int max_bpp;
> +	u32 bpps = 0;
> +	int ret;
> +
> +	if (ser->mode != MAX_SERDES_GMSL_PIXEL_MODE)
> +		return 0;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_ser_route_hw hw;
> +		unsigned int bpp;
> +
> +		ret = max_ser_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (hw.pipe != pipe)
> +			continue;
> +
> +		if (hw.is_tpg)
> +			force_set_bpp = true;
> +
> +		ret = max_serdes_get_fd_bpp(&hw.entry, &bpp);
> +		if (ret)
> +			return ret;
> +
> +		bpps |= BIT(bpp);
> +	}
> +
> +	ret = max_serdes_process_bpps(priv->dev, bpps, priv->double_bpps, &doubled_bpp);
> +	if (ret)
> +		return ret;
> +
> +	if (doubled_bpp == 8)
> +		mode->dbl8 = true;
> +	else if (doubled_bpp == 10)
> +		mode->dbl10 = true;
> +	else if (doubled_bpp == 12)
> +		mode->dbl12 = true;
> +
> +	if (doubled_bpp) {
> +		bpps &= ~BIT(doubled_bpp);
> +		bpps |= BIT(doubled_bpp * 2);
> +	}
> +
> +	if (!bpps)
> +		return 0;
> +
> +	min_bpp = __ffs(bpps);
> +	max_bpp = __fls(bpps);
> +
> +	if (doubled_bpp)
> +		mode->soft_bpp = min_bpp;
> +
> +	if (min_bpp != max_bpp || force_set_bpp)
> +		mode->bpp = max_bpp;
> +
> +	return 0;
> +}
> +
> +static int max_ser_update_pipe_enable(struct max_ser_priv *priv,
> +				      struct max_ser_pipe *pipe,
> +				      struct v4l2_subdev_state *state,
> +				      u64 *streams_masks)
> +{
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_subdev_route *route;
> +	bool enable = false;
> +	int ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_ser_route_hw hw;
> +
> +		if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_ser_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (hw.pipe != pipe)
> +			continue;
> +
> +		enable = true;
> +		break;
> +	}
> +
> +	if (enable == pipe->enabled)
> +		return 0;
> +
> +	ret = ser->ops->set_pipe_enable(ser, pipe, enable);
> +	if (ret)
> +		return ret;
> +
> +	pipe->enabled = enable;
> +
> +	return 0;
> +}
> +
> +static int max_ser_update_pipe(struct max_ser_priv *priv,
> +			       struct max_ser_pipe *pipe,
> +			       struct v4l2_subdev_state *state,
> +			       u64 *streams_masks)
> +{
> +	struct max_ser *ser = priv->ser;
> +	struct max_ser_pipe_mode mode = { 0 };
> +	unsigned int num_dts;
> +	unsigned int *dts;
> +	unsigned int vcs;
> +	int ret;
> +
> +	if (!ser->ops->num_dts_per_pipe)
> +		return 0;
> +
> +	dts = devm_kcalloc(priv->dev, ser->ops->num_dts_per_pipe, sizeof(*dts),
> +			   GFP_KERNEL);
> +	if (!dts)
> +		return -ENOMEM;
> +
> +	ret = max_ser_get_pipe_vcs_dts(priv, state, pipe, &vcs, dts, &num_dts,
> +				       streams_masks);
> +	if (ret)
> +		goto err_free_dts;
> +
> +	ret = max_ser_get_pipe_mode(priv, state, pipe, &mode);
> +	if (ret)
> +		goto err_free_dts;
> +
> +	if (ser->ops->set_pipe_vcs) {
> +		ret = ser->ops->set_pipe_vcs(ser, pipe, vcs);
> +		if (ret)
> +			goto err_free_dts;
> +	}
> +
> +	ret = max_ser_set_pipe_mode(priv, pipe, &mode);
> +	if (ret)
> +		goto err_revert_vcs;
> +
> +	ret = max_ser_set_pipe_dts(priv, pipe, dts, num_dts);
> +	if (ret)
> +		goto err_revert_mode;
> +
> +	pipe->vcs = vcs;
> +	pipe->mode = mode;
> +
> +	if (pipe->dts)
> +		devm_kfree(priv->dev, pipe->dts);
> +
> +	pipe->dts = dts;
> +	pipe->num_dts = num_dts;
> +
> +	return 0;
> +
> +err_revert_mode:
> +	max_ser_set_pipe_mode(priv, pipe, &pipe->mode);
> +
> +err_revert_vcs:
> +	if (ser->ops->set_pipe_vcs)
> +		ser->ops->set_pipe_vcs(ser, pipe, pipe->vcs);
> +
> +err_free_dts:
> +	devm_kfree(priv->dev, dts);
> +
> +	return ret;
> +}
> +
> +static int max_ser_update_phy(struct max_ser_priv *priv,
> +			      struct v4l2_subdev_state *state,
> +			      struct max_ser_phy *phy, u64 *streams_masks)
> +{
> +	struct max_ser *ser = priv->ser;
> +	u32 pad = max_ser_phy_to_pad(ser, phy);
> +	bool enable_changed = !streams_masks[pad] != !priv->streams_masks[pad];
> +	bool enable = !!streams_masks[pad];
> +	struct max_ser_pipe *pipe;
> +	int ret;
> +
> +	pipe = max_ser_find_phy_pipe(ser, phy);
> +	if (!pipe)
> +		return -ENOENT;
> +
> +	if (!enable && enable_changed) {
> +		ret = max_ser_phy_set_active(ser, phy, enable);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = max_ser_update_pipe(priv, pipe, state, streams_masks);
> +	if (ret)
> +		goto err_revert_phy_disable;
> +
> +	ret = max_ser_update_pipe_enable(priv, pipe, state, streams_masks);
> +	if (ret)
> +		goto err_revert_pipe_update;
> +
> +	if (enable && enable_changed) {
> +		ret = max_ser_phy_set_active(ser, phy, enable);
> +		if (ret)
> +			goto err_revert_update_pipe_enable;
> +	}
> +
> +	return 0;
> +
> +err_revert_update_pipe_enable:
> +	max_ser_update_pipe_enable(priv, pipe, state, priv->streams_masks);
> +
> +err_revert_pipe_update:
> +	max_ser_update_pipe(priv, pipe, state, priv->streams_masks);
> +
> +err_revert_phy_disable:
> +	if (!enable && enable_changed)
> +		max_ser_phy_set_active(ser, phy, !enable);
> +
> +	return ret;
> +}
> +
> +static int max_ser_update_phys(struct max_ser_priv *priv,
> +			       struct v4l2_subdev_state *state,
> +			       u64 *streams_masks)
> +{
> +	struct max_ser *ser = priv->ser;
> +	unsigned int failed_update_phy_id = ser->ops->num_phys;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ser->ops->num_phys; i++) {
> +		struct max_ser_phy *phy = &ser->phys[i];
> +
> +		ret = max_ser_update_phy(priv, state, phy, streams_masks);
> +		if (ret) {
> +			failed_update_phy_id = i;
> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +
> +err:
> +	for (i = 0; i < failed_update_phy_id; i++) {
> +		struct max_ser_phy *phy = &ser->phys[i];
> +
> +		max_ser_update_phy(priv, state, phy, priv->streams_masks);
> +	}
> +
> +	return ret;
> +}
> +
> +static int max_ser_enable_disable_streams(struct max_ser_priv *priv,
> +					  struct v4l2_subdev_state *state,
> +					  u32 pad, u64 updated_streams_mask,
> +					  bool enable)
> +{
> +	struct max_ser *ser = priv->ser;
> +
> +	return max_serdes_xlate_enable_disable_streams(priv->sources, 0, state,
> +						       pad, updated_streams_mask, 0,
> +						       ser->ops->num_phys, enable);
> +}
> +
> +static bool max_ser_is_tpg_routed(struct max_ser_priv *priv,
> +				  struct v4l2_subdev_state *state)
> +{
> +	struct v4l2_subdev_route *route;
> +	int ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_ser_route_hw hw;
> +
> +		ret = max_ser_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return false;
> +
> +		if (hw.is_tpg)
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +static int max_ser_update_tpg(struct max_ser_priv *priv,
> +			      struct v4l2_subdev_state *state,
> +			      u64 *streams_masks)
> +{
> +	const struct max_serdes_tpg_entry *entry = NULL;
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_subdev_route *route;
> +	int ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_ser_route_hw hw;
> +
> +		if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_ser_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.is_tpg)
> +			continue;
> +
> +		entry = max_ser_find_state_tpg_entry(ser, state, route->sink_pad);
> +		break;
> +	}
> +
> +	if (entry == ser->tpg_entry)
> +		return 0;
> +
> +	ret = ser->ops->set_tpg(ser, entry);
> +	if (ret)
> +		return ret;
> +
> +	ser->tpg_entry = entry;
> +
> +	return 0;
> +}
> +
> +static int max_ser_update_streams(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *state,
> +				  u32 pad, u64 updated_streams_mask, bool enable)
> +{
> +	struct max_ser_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_ser *ser = priv->ser;
> +	unsigned int num_pads = max_ser_num_pads(ser);
> +	u64 *streams_masks;
> +	int ret;
> +
> +	ret = max_serdes_get_streams_masks(priv->dev, state, pad, updated_streams_mask,
> +					   num_pads, priv->streams_masks, &streams_masks,
> +					   enable);
> +	if (ret)
> +		return ret;
> +
> +	if (!enable) {
> +		ret = max_ser_enable_disable_streams(priv, state, pad,
> +						     updated_streams_mask, enable);
> +		if (ret)
> +			goto err_free_streams_masks;
> +	}
> +
> +	ret = max_ser_update_tpg(priv, state, streams_masks);
> +	if (ret)
> +		goto err_revert_streams_disable;
> +
> +	ret = max_ser_update_phys(priv, state, streams_masks);
> +	if (ret)
> +		goto err_revert_update_tpg;
> +
> +	if (enable) {
> +		ret = max_ser_enable_disable_streams(priv, state, pad,
> +						     updated_streams_mask, enable);
> +		if (ret)
> +			goto err_revert_phys_update;
> +	}
> +
> +	devm_kfree(priv->dev, priv->streams_masks);
> +	priv->streams_masks = streams_masks;
> +	ser->active = !!streams_masks[pad];
> +
> +	return 0;
> +
> +err_revert_phys_update:
> +	max_ser_update_phys(priv, state, priv->streams_masks);
> +
> +err_revert_update_tpg:
> +	max_ser_update_tpg(priv, state, priv->streams_masks);
> +
> +err_revert_streams_disable:
> +	if (!enable)
> +		max_ser_enable_disable_streams(priv, state, pad,
> +					       updated_streams_mask, !enable);
> +
> +err_free_streams_masks:
> +	devm_kfree(priv->dev, streams_masks);
> +
> +	return ret;
> +}
> +
> +static int max_ser_enable_streams(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *state,
> +				  u32 pad, u64 streams_mask)
> +{
> +	return max_ser_update_streams(sd, state, pad, streams_mask, true);
> +}
> +
> +static int max_ser_disable_streams(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_state *state,
> +				   u32 pad, u64 streams_mask)
> +{
> +	return max_ser_update_streams(sd, state, pad, streams_mask, false);
> +}
> +
> +static int max_ser_init_state(struct v4l2_subdev *sd,
> +			      struct v4l2_subdev_state *state)
> +{
> +	struct v4l2_subdev_route routes[MAX_SER_NUM_PHYS] = { 0 };
> +	struct v4l2_subdev_krouting routing = {
> +		.routes = routes,
> +	};
> +	struct max_ser_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_ser *ser = priv->ser;
> +	unsigned int stream = 0;
> +	unsigned int i;
> +
> +	for (i = 0; i < ser->ops->num_phys; i++) {
> +		struct max_ser_phy *phy = &ser->phys[i];
> +
> +		if (!phy->enabled)
> +			continue;
> +
> +		routing.routes[routing.num_routes++] = (struct v4l2_subdev_route) {
> +			.sink_pad = max_ser_phy_to_pad(ser, phy),
> +			.sink_stream = 0,
> +			.source_pad = max_ser_source_pad(ser),
> +			.source_stream = stream,
> +			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
> +		};
> +		stream++;
> +
> +		/*
> +		 * The Streams API is an experimental feature.
> +		 * If multiple routes are provided here, userspace will not be
> +		 * able to configure them unless the Streams API is enabled.
> +		 * Provide a single route until it is enabled.
> +		 */
> +		break;
> +	}
> +
> +	return __max_ser_set_routing(sd, state, &routing);
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int max_ser_g_register(struct v4l2_subdev *sd,
> +			      struct v4l2_dbg_register *reg)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	unsigned int val;
> +	int ret;
> +
> +	ret = ser->ops->reg_read(ser, reg->reg, &val);
> +	if (ret)
> +		return ret;
> +
> +	reg->val = val;
> +	reg->size = 1;
> +
> +	return 0;
> +}
> +
> +static int max_ser_s_register(struct v4l2_subdev *sd,
> +			      const struct v4l2_dbg_register *reg)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +
> +	return ser->ops->reg_write(ser, reg->reg, reg->val);
> +}
> +#endif
> +
> +static const struct v4l2_subdev_core_ops max_ser_core_ops = {
> +	.log_status = max_ser_log_status,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.g_register = max_ser_g_register,
> +	.s_register = max_ser_s_register,
> +#endif
> +};
> +
> +static const struct v4l2_ctrl_ops max_ser_ctrl_ops = {
> +	.s_ctrl = max_ser_s_ctrl,
> +};
> +
> +static const struct v4l2_subdev_pad_ops max_ser_pad_ops = {
> +	.enable_streams = max_ser_enable_streams,
> +	.disable_streams = max_ser_disable_streams,
> +
> +	.set_routing = max_ser_set_routing,
> +	.get_frame_desc = max_ser_get_frame_desc,
> +
> +	.get_fmt = v4l2_subdev_get_fmt,
> +	.set_fmt = max_ser_set_fmt,
> +
> +	.enum_frame_interval = max_ser_enum_frame_interval,
> +	.get_frame_interval = max_ser_get_frame_interval,
> +	.set_frame_interval = max_ser_set_frame_interval,
> +};
> +
> +static const struct v4l2_subdev_ops max_ser_subdev_ops = {
> +	.core = &max_ser_core_ops,
> +	.pad = &max_ser_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops max_ser_internal_ops = {
> +	.init_state = &max_ser_init_state,
> +};
> +
> +static const struct media_entity_operations max_ser_media_ops = {
> +	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
> +	.has_pad_interdep = v4l2_subdev_has_pad_interdep,
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static int max_ser_init(struct max_ser_priv *priv)
> +{
> +	struct max_ser *ser = priv->ser;
> +	unsigned int i;
> +	int ret;
> +
> +	if (ser->ops->init) {
> +		ret = ser->ops->init(ser);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (ser->ops->set_tunnel_enable) {
> +		ret = ser->ops->set_tunnel_enable(ser, false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < ser->ops->num_phys; i++) {
> +		struct max_ser_phy *phy = &ser->phys[i];
> +
> +		if (phy->enabled) {
> +			ret = ser->ops->init_phy(ser, phy);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (ser->ops->set_phy_active) {
> +			ret = ser->ops->set_phy_active(ser, phy, false);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < ser->ops->num_pipes; i++) {
> +		struct max_ser_pipe *pipe = &ser->pipes[i];
> +		struct max_ser_phy *phy = &ser->phys[pipe->phy_id];
> +
> +		ret = ser->ops->set_pipe_enable(ser, pipe, false);
> +		if (ret)
> +			return ret;
> +
> +		if (ser->ops->set_pipe_stream_id) {
> +			ret = ser->ops->set_pipe_stream_id(ser, pipe, pipe->stream_id);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (ser->ops->set_pipe_phy) {
> +			ret = ser->ops->set_pipe_phy(ser, pipe, phy);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (ser->ops->set_pipe_vcs) {
> +			ret = ser->ops->set_pipe_vcs(ser, pipe, 0);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (ser->ops->set_pipe_mode) {
> +			ret = ser->ops->set_pipe_mode(ser, pipe, &pipe->mode);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = max_ser_set_pipe_dts(priv, pipe, NULL, 0);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_ser_notify_bound(struct v4l2_async_notifier *nf,
> +				struct v4l2_subdev *subdev,
> +				struct v4l2_async_connection *base_asc)
> +{
> +	struct max_ser_priv *priv = nf_to_priv(nf);
> +	struct max_serdes_asc *asc = asc_to_max(base_asc);
> +	struct max_serdes_source *source = asc->source;
> +	u32 pad = source->index;
> +	int ret;
> +
> +	ret = media_entity_get_fwnode_pad(&subdev->entity,
> +					  source->ep_fwnode,
> +					  MEDIA_PAD_FL_SOURCE);
> +	if (ret < 0) {
> +		dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name);
> +		return ret;
> +	}
> +
> +	source->sd = subdev;
> +	source->pad = ret;
> +
> +	ret = media_create_pad_link(&source->sd->entity, source->pad,
> +				    &priv->sd.entity, pad,
> +				    MEDIA_LNK_FL_ENABLED |
> +				    MEDIA_LNK_FL_IMMUTABLE);
> +	if (ret) {
> +		dev_err(priv->dev, "Unable to link %s:%u -> %s:%u\n",
> +			source->sd->name, source->pad, priv->sd.name, pad);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void max_ser_notify_unbind(struct v4l2_async_notifier *nf,
> +				  struct v4l2_subdev *subdev,
> +				  struct v4l2_async_connection *base_asc)
> +{
> +	struct max_serdes_asc *asc = asc_to_max(base_asc);
> +	struct max_serdes_source *source = asc->source;
> +
> +	source->sd = NULL;
> +}
> +
> +static const struct v4l2_async_notifier_operations max_ser_notify_ops = {
> +	.bound = max_ser_notify_bound,
> +	.unbind = max_ser_notify_unbind,
> +};
> +
> +static int max_ser_v4l2_notifier_register(struct max_ser_priv *priv)
> +{
> +	struct max_ser *ser = priv->ser;
> +	unsigned int i;
> +	int ret;
> +
> +	v4l2_async_subdev_nf_init(&priv->nf, &priv->sd);
> +
> +	for (i = 0; i < ser->ops->num_phys; i++) {
> +		struct max_ser_phy *phy = &ser->phys[i];
> +		struct max_serdes_source *source;
> +		struct max_serdes_asc *asc;
> +
> +		source = max_ser_get_phy_source(priv, phy);
> +		if (!source->ep_fwnode)
> +			continue;
> +
> +		asc = v4l2_async_nf_add_fwnode(&priv->nf, source->ep_fwnode,
> +					       struct max_serdes_asc);
> +		if (IS_ERR(asc)) {
> +			dev_err(priv->dev,
> +				"Failed to add subdev for source %u: %pe", i,
> +				asc);
> +
> +			v4l2_async_nf_cleanup(&priv->nf);
> +
> +			return PTR_ERR(asc);
> +		}
> +
> +		asc->source = source;
> +	}
> +
> +	priv->nf.ops = &max_ser_notify_ops;
> +
> +	ret = v4l2_async_nf_register(&priv->nf);
> +	if (ret) {
> +		dev_err(priv->dev, "Failed to register subdev notifier");
> +		v4l2_async_nf_cleanup(&priv->nf);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void max_ser_v4l2_notifier_unregister(struct max_ser_priv *priv)
> +{
> +	v4l2_async_nf_unregister(&priv->nf);
> +	v4l2_async_nf_cleanup(&priv->nf);
> +}
> +
> +static int max_ser_v4l2_register(struct max_ser_priv *priv)
> +{
> +	struct v4l2_subdev *sd = &priv->sd;
> +	struct max_ser *ser = priv->ser;
> +	void *data = i2c_get_clientdata(priv->client);
> +	unsigned int num_pads = max_ser_num_pads(ser);
> +	unsigned int i;
> +	int ret;
> +
> +	v4l2_i2c_subdev_init(sd, priv->client, &max_ser_subdev_ops);
> +	i2c_set_clientdata(priv->client, data);
> +	sd->internal_ops = &max_ser_internal_ops;
> +	sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> +	sd->entity.ops = &max_ser_media_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
> +
> +	priv->pads = devm_kcalloc(priv->dev, num_pads, sizeof(*priv->pads), GFP_KERNEL);
> +	if (!priv->pads)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < num_pads; i++) {
> +		if (max_ser_pad_is_sink(ser, i))
> +			priv->pads[i].flags = MEDIA_PAD_FL_SINK;
> +		else if (max_ser_pad_is_source(ser, i))
> +			priv->pads[i].flags = MEDIA_PAD_FL_SOURCE;
> +		else if (max_ser_pad_is_tpg(ser, i))
> +			priv->pads[i].flags = MEDIA_PAD_FL_SINK |
> +					      MEDIA_PAD_FL_INTERNAL;
> +		else
> +			return -EINVAL;
> +	}
> +
> +	v4l2_set_subdevdata(sd, priv);
> +
> +	if (ser->ops->tpg_patterns) {
> +		v4l2_ctrl_handler_init(&priv->ctrl_handler, 1);
> +		priv->sd.ctrl_handler = &priv->ctrl_handler;
> +
> +		v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,
> +					     &max_ser_ctrl_ops,
> +					     V4L2_CID_TEST_PATTERN,
> +					     MAX_SERDES_TPG_PATTERN_MAX,
> +					     ~ser->ops->tpg_patterns,
> +					     __ffs(ser->ops->tpg_patterns),
> +					     max_serdes_tpg_patterns);
> +		if (priv->ctrl_handler.error) {
> +			ret = priv->ctrl_handler.error;
> +			goto err_free_ctrl;
> +		}
> +	}
> +
> +	ret = media_entity_pads_init(&sd->entity, num_pads, priv->pads);
> +	if (ret)
> +		goto err_free_ctrl;
> +
> +	ret = max_ser_v4l2_notifier_register(priv);
> +	if (ret)
> +		goto err_media_entity_cleanup;
> +
> +	ret = v4l2_subdev_init_finalize(sd);
> +	if (ret)
> +		goto err_nf_cleanup;
> +
> +	ret = v4l2_async_register_subdev(sd);
> +	if (ret)
> +		goto err_sd_cleanup;
> +
> +	return 0;
> +
> +err_sd_cleanup:
> +	v4l2_subdev_cleanup(sd);
> +err_nf_cleanup:
> +	max_ser_v4l2_notifier_unregister(priv);
> +err_media_entity_cleanup:
> +	media_entity_cleanup(&sd->entity);
> +err_free_ctrl:
> +	v4l2_ctrl_handler_free(&priv->ctrl_handler);
> +
> +	return ret;
> +}
> +
> +static void max_ser_v4l2_unregister(struct max_ser_priv *priv)
> +{
> +	struct v4l2_subdev *sd = &priv->sd;
> +
> +	max_ser_v4l2_notifier_unregister(priv);
> +	v4l2_async_unregister_subdev(sd);
> +	v4l2_subdev_cleanup(sd);
> +	media_entity_cleanup(&sd->entity);
> +	v4l2_ctrl_handler_free(&priv->ctrl_handler);
> +}
> +
> +static int max_ser_parse_sink_dt_endpoint(struct max_ser_priv *priv,
> +					  struct max_ser_phy *phy,
> +					  struct max_serdes_source *source,
> +					  struct fwnode_handle *fwnode)
> +{
> +	struct max_ser *ser = priv->ser;
> +	u32 pad = max_ser_phy_to_pad(ser, phy);
> +	struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
> +	struct fwnode_handle *ep;
> +	int ret;
> +
> +	ep = fwnode_graph_get_endpoint_by_id(fwnode, pad, 0, 0);
> +	if (!ep)
> +		return 0;
> +
> +	source->ep_fwnode = fwnode_graph_get_remote_endpoint(ep);
> +	if (!source->ep_fwnode) {
> +		fwnode_handle_put(ep);
> +		dev_err(priv->dev,
> +			"Failed to get remote endpoint on port %u\n", pad);
> +		return -EINVAL;
> +	}
> +
> +	ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
> +	fwnode_handle_put(ep);
> +	if (ret) {
> +		dev_err(priv->dev, "Could not parse endpoint on port %u\n", pad);
> +		return ret;
> +	}
> +
> +	phy->mipi = v4l2_ep.bus.mipi_csi2;
> +	phy->enabled = true;
> +
> +	return 0;
> +}
> +
> +static int max_ser_find_phys_config(struct max_ser_priv *priv)
> +{
> +	struct max_ser *ser = priv->ser;
> +	const struct max_serdes_phys_configs *configs = &ser->ops->phys_configs;
> +	struct max_ser_phy *phy;
> +	unsigned int i, j;
> +
> +	if (!configs->num_configs)
> +		return 0;
> +
> +	for (i = 0; i < configs->num_configs; i++) {
> +		const struct max_serdes_phys_config *config = &configs->configs[i];
> +		bool matching = true;
> +
> +		for (j = 0; j < ser->ops->num_phys; j++) {
> +			phy = &ser->phys[j];
> +
> +			if (!phy->enabled)
> +				continue;
> +
> +			if (phy->mipi.num_data_lanes <= config->lanes[j])
> +				continue;
> +
> +			matching = false;
> +
> +			break;
> +		}
> +
> +		if (matching)
> +			break;
> +	}
> +
> +	if (i == configs->num_configs) {
> +		dev_err(priv->dev, "Invalid lane configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	ser->phys_config = i;
> +
> +	return 0;
> +}
> +
> +static int max_ser_parse_dt(struct max_ser_priv *priv)
> +{
> +	struct fwnode_handle *fwnode = dev_fwnode(priv->dev);
> +	struct max_ser *ser = priv->ser;
> +	struct max_ser_pipe *pipe;
> +	struct max_ser_phy *phy;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ser->ops->num_phys; i++) {
> +		phy = &ser->phys[i];
> +		phy->index = i;
> +	}
> +
> +	for (i = 0; i < ser->ops->num_pipes; i++) {
> +		pipe = &ser->pipes[i];
> +		pipe->index = i;
> +		pipe->phy_id = i % ser->ops->num_phys;
> +		pipe->stream_id = i % MAX_SERDES_STREAMS_NUM;
> +	}
> +
> +	for (i = 0; i < ser->ops->num_phys; i++) {
> +		struct max_ser_phy *phy = &ser->phys[i];
> +		struct max_serdes_source *source;
> +
> +		source = max_ser_get_phy_source(priv, phy);
> +		source->index = i;
> +
> +		ret = max_ser_parse_sink_dt_endpoint(priv, phy, source, fwnode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return max_ser_find_phys_config(priv);
> +}
> +
> +static int max_ser_allocate(struct max_ser_priv *priv)
> +{
> +	struct max_ser *ser = priv->ser;
> +	unsigned int num_pads = max_ser_num_pads(ser);
> +
> +	ser->phys = devm_kcalloc(priv->dev, ser->ops->num_phys,
> +				 sizeof(*ser->phys), GFP_KERNEL);
> +	if (!ser->phys)
> +		return -ENOMEM;
> +
> +	ser->pipes = devm_kcalloc(priv->dev, ser->ops->num_pipes,
> +				  sizeof(*ser->pipes), GFP_KERNEL);
> +	if (!ser->pipes)
> +		return -ENOMEM;
> +
> +	ser->vc_remaps = devm_kcalloc(priv->dev, ser->ops->num_vc_remaps,
> +				      sizeof(*ser->vc_remaps), GFP_KERNEL);
> +	if (!ser->vc_remaps)
> +		return -ENOMEM;
> +
> +	ser->i2c_xlates = devm_kcalloc(priv->dev, ser->ops->num_i2c_xlates,
> +				       sizeof(*ser->i2c_xlates), GFP_KERNEL);
> +	if (!ser->i2c_xlates)
> +		return -ENOMEM;
> +
> +	priv->sources = devm_kcalloc(priv->dev, ser->ops->num_phys,
> +				     sizeof(*priv->sources), GFP_KERNEL);
> +	if (!priv->sources)
> +		return -ENOMEM;
> +
> +	priv->streams_masks = devm_kcalloc(priv->dev, num_pads,
> +					   sizeof(*priv->streams_masks),
> +					   GFP_KERNEL);
> +	if (!priv->streams_masks)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +int max_ser_probe(struct i2c_client *client, struct max_ser *ser)
> +{
> +	struct device *dev = &client->dev;
> +	struct max_ser_priv *priv;
> +	int ret;
> +
> +	if (ser->ops->num_phys > MAX_SER_NUM_PHYS)
> +		return -E2BIG;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->client = client;
> +	priv->dev = dev;
> +	priv->ser = ser;
> +	ser->priv = priv;
> +	ser->mode = __ffs(ser->ops->modes);
> +
> +	ret = max_ser_allocate(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_ser_parse_dt(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_ser_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_ser_i2c_adapter_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_ser_v4l2_register(priv);
> +	if (ret)
> +		goto err_i2c_adapter_deinit;
> +
> +	return 0;
> +
> +err_i2c_adapter_deinit:
> +	max_ser_i2c_adapter_deinit(priv);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_NS_GPL(max_ser_probe, "MAX_SERDES");
> +
> +int max_ser_remove(struct max_ser *ser)
> +{
> +	struct max_ser_priv *priv = ser->priv;
> +
> +	max_ser_v4l2_unregister(priv);
> +
> +	max_ser_i2c_adapter_deinit(priv);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(max_ser_remove, "MAX_SERDES");
> +
> +int max_ser_set_double_bpps(struct v4l2_subdev *sd, u32 double_bpps)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +
> +	priv->double_bpps = double_bpps;
> +
> +	return 0;
> +}
> +
> +int max_ser_set_stream_id(struct v4l2_subdev *sd, unsigned int stream_id)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	struct max_ser_pipe *pipe = &ser->pipes[0];
> +
> +	if (!ser->ops->set_pipe_stream_id)
> +		return -EOPNOTSUPP;
> +
> +	return ser->ops->set_pipe_stream_id(ser, pipe, stream_id);
> +}
> +
> +int max_ser_get_stream_id(struct v4l2_subdev *sd, unsigned int *stream_id)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	struct max_ser_pipe *pipe = &ser->pipes[0];
> +
> +	if (!ser->ops->get_pipe_stream_id)
> +		return -EOPNOTSUPP;
> +
> +	*stream_id = ser->ops->get_pipe_stream_id(ser, pipe);
> +
> +	return 0;
> +}
> +
> +unsigned int max_ser_get_supported_modes(struct v4l2_subdev *sd)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	struct v4l2_subdev_state *state;
> +	unsigned int modes = ser->ops->modes;
> +
> +	state = v4l2_subdev_lock_and_get_active_state(&priv->sd);
> +
> +	if (max_ser_is_tpg_routed(priv, state))
> +		modes = BIT(ser->ops->tpg_mode);
> +
> +	v4l2_subdev_unlock_state(state);
> +
> +	return modes;
> +}
> +
> +bool max_ser_supports_vc_remap(struct v4l2_subdev *sd)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +
> +	return !!ser->ops->set_vc_remap;
> +}
> +
> +int max_ser_set_mode(struct v4l2_subdev *sd, enum max_serdes_gmsl_mode mode)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	int ret;
> +
> +	if (!(ser->ops->modes & BIT(mode)))
> +		return -EINVAL;
> +
> +	if (ser->mode == mode)
> +		return 0;
> +
> +	if (ser->ops->set_tunnel_enable) {
> +		bool tunnel_enable = mode == MAX_SERDES_GMSL_TUNNEL_MODE;
> +
> +		ret = ser->ops->set_tunnel_enable(ser, tunnel_enable);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ser->mode = mode;
> +
> +	return 0;
> +}
> +
> +int max_ser_set_vc_remaps(struct v4l2_subdev *sd,
> +			  struct max_serdes_vc_remap *vc_remaps,
> +			  int num_vc_remaps)
> +{
> +	struct max_ser_priv *priv = sd_to_priv(sd);
> +	struct max_ser *ser = priv->ser;
> +	unsigned int mask = 0;
> +	unsigned int i;
> +	int ret;
> +
> +	if (!ser->ops->set_vc_remap)
> +		return -EOPNOTSUPP;
> +
> +	if (num_vc_remaps > ser->ops->num_vc_remaps)
> +		return -E2BIG;
> +
> +	for (i = 0; i < num_vc_remaps; i++) {
> +		ret = ser->ops->set_vc_remap(ser, i, &vc_remaps[i]);
> +		if (ret)
> +			return ret;
> +
> +		mask |= BIT(i);
> +	}
> +
> +	ret = ser->ops->set_vc_remaps_enable(ser, mask);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < num_vc_remaps; i++)
> +		ser->vc_remaps[i] = vc_remaps[i];
> +
> +	ser->num_vc_remaps = num_vc_remaps;
> +
> +	return 0;
> +}
> +
> +static int max_ser_read_reg(struct i2c_adapter *adapter, u8 addr,
> +			    u16 reg, u8 *val)
> +{
> +	u8 buf[2] = { reg >> 8, reg & 0xff };
> +	struct i2c_msg msg[2] = {
> +		{
> +			.addr = addr,
> +			.flags = 0,
> +			.buf = buf,
> +			.len = sizeof(buf),
> +		},
> +		{
> +			.addr = addr,
> +			.flags = I2C_M_RD,
> +			.buf = buf,
> +			.len = 1,
> +		},
> +	};
> +	int ret;
> +
> +	ret = i2c_transfer(adapter, msg, ARRAY_SIZE(msg));
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = buf[0];
> +
> +	return 0;
> +}
> +
> +static int max_ser_write_reg(struct i2c_adapter *adapter, u8 addr,
> +			     u16 reg, u8 val)
> +{
> +	u8 buf[3] = { reg >> 8, reg & 0xff, val };
> +	struct i2c_msg msg[1] = {
> +		{
> +			.addr = addr,
> +			.flags = 0,
> +			.buf = buf,
> +			.len = sizeof(buf),
> +		},
> +	};
> +	int ret;
> +
> +	ret = i2c_transfer(adapter, msg, ARRAY_SIZE(msg));
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +int max_ser_reset(struct i2c_adapter *adapter, u8 addr)
> +{
> +	int ret;
> +	u8 val;
> +
> +	ret = max_ser_read_reg(adapter, addr, MAX_SER_CTRL0, &val);
> +	if (ret)
> +		return ret;
> +
> +	val |= MAX_SER_CTRL0_RESET_ALL;
> +
> +	return max_ser_write_reg(adapter, addr, MAX_SER_CTRL0, val);
> +}
> +
> +int max_ser_wait_for_multiple(struct i2c_adapter *adapter, u8 *addrs,
> +			      unsigned int num_addrs, u8 *current_addr)
> +{
> +	unsigned int i, j;
> +	int ret = 0;
> +	u8 val;
> +
> +	for (i = 0; i < 10; i++) {
> +		for (j = 0; j < num_addrs; j++) {
> +			ret = max_ser_read_reg(adapter, addrs[j], MAX_SER_REG0, &val);
> +			if (!ret && val) {
> +				*current_addr = addrs[j];
> +				return 0;
> +			}
> +
> +			msleep(100);
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +int max_ser_wait(struct i2c_adapter *adapter, u8 addr)
> +{
> +	u8 current_addr;
> +
> +	return max_ser_wait_for_multiple(adapter, &addr, 1, &current_addr);
> +}
> +
> +int max_ser_fix_tx_ids(struct i2c_adapter *adapter, u8 addr)
> +{
> +	unsigned int addr_regs[] = {
> +		MAX_SER_CFGI_INFOFR_TR3,
> +		MAX_SER_CFGL_SPI_TR3,
> +		MAX_SER_CFGC_CC_TR3,
> +		MAX_SER_CFGC_GPIO_TR3,
> +		MAX_SER_CFGL_IIC_X_TR3,
> +		MAX_SER_CFGL_IIC_Y_TR3,
> +	};
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(addr_regs); i++) {
> +		ret = max_ser_write_reg(adapter, addr, addr_regs[i], addr);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +int max_ser_change_address(struct i2c_adapter *adapter, u8 addr, u8 new_addr)
> +{
> +	u8 val = FIELD_PREP(MAX_SER_REG0_DEV_ADDR, new_addr);
> +
> +	return max_ser_write_reg(adapter, addr, MAX_SER_REG0, val);
> +}
> +
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("I2C_ATR");
> diff --git a/drivers/media/i2c/maxim-serdes/max_ser.h b/drivers/media/i2c/maxim-serdes/max_ser.h
> new file mode 100644
> index 000000000000..a9365be5e62d
> --- /dev/null
> +++ b/drivers/media/i2c/maxim-serdes/max_ser.h
> @@ -0,0 +1,147 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2025 Analog Devices Inc.
> + */
> +
> +#ifndef MAX_SER_H
> +#define MAX_SER_H
> +
> +#include <linux/i2c.h>
> +
> +#include <media/v4l2-mediabus.h>
> +
> +#include "max_serdes.h"
> +
> +#define MAX_SER_REG0				0x0
> +#define MAX_SER_REG0_DEV_ADDR			GENMASK(7, 1)
> +
> +#define MAX_SER_CTRL0				0x10
> +#define MAX_SER_CTRL0_RESET_ALL			BIT(7)
> +
> +#define MAX_SER_CFGI_INFOFR_TR3			0x7b
> +#define MAX_SER_CFGL_SPI_TR3			0x83
> +#define MAX_SER_CFGC_CC_TR3			0x8b
> +#define MAX_SER_CFGC_GPIO_TR3			0x93
> +#define MAX_SER_CFGL_IIC_X_TR3			0xa3
> +#define MAX_SER_CFGL_IIC_Y_TR3			0xab
> +
> +struct max_ser_phy {
> +	unsigned int index;
> +	struct v4l2_mbus_config_mipi_csi2 mipi;
> +	bool enabled;
> +	bool active;
> +};
> +
> +struct max_ser_pipe_mode {
> +	unsigned int soft_bpp;
> +	unsigned int bpp;
> +	bool dbl8;
> +	bool dbl10;
> +	bool dbl12;
> +};
> +
> +struct max_ser_pipe {
> +	unsigned int index;
> +	unsigned int phy_id;
> +	unsigned int stream_id;
> +	unsigned int *dts;
> +	unsigned int num_dts;
> +	unsigned int vcs;
> +	struct max_ser_pipe_mode mode;
> +	bool enabled;
> +};
> +
> +struct max_ser;
> +
> +struct max_ser_ops {
> +	unsigned int modes;
> +	unsigned int num_pipes;
> +	unsigned int num_dts_per_pipe;
> +	unsigned int num_phys;
> +	unsigned int num_i2c_xlates;
> +	unsigned int num_vc_remaps;
> +
> +	struct max_serdes_phys_configs phys_configs;
> +	struct max_serdes_tpg_entries tpg_entries;
> +	enum max_serdes_gmsl_mode tpg_mode;
> +	unsigned int tpg_patterns;
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	int (*reg_read)(struct max_ser *ser, unsigned int reg, unsigned int *val);
> +	int (*reg_write)(struct max_ser *ser, unsigned int reg, unsigned int val);
> +#endif
> +	int (*log_status)(struct max_ser *ser);
> +	int (*log_pipe_status)(struct max_ser *ser, struct max_ser_pipe *pipe);
> +	int (*log_phy_status)(struct max_ser *ser, struct max_ser_phy *phy);
> +	int (*init)(struct max_ser *ser);
> +	int (*set_i2c_xlate)(struct max_ser *ser, unsigned int i,
> +			     struct max_serdes_i2c_xlate *i2c_xlate);
> +	int (*set_tunnel_enable)(struct max_ser *ser, bool enable);
> +	int (*set_tpg)(struct max_ser *ser, const struct max_serdes_tpg_entry *entry);
> +	int (*init_phy)(struct max_ser *ser, struct max_ser_phy *phy);
> +	int (*set_phy_active)(struct max_ser *ser, struct max_ser_phy *phy,
> +			      bool enable);
> +	int (*set_pipe_enable)(struct max_ser *ser, struct max_ser_pipe *pipe,
> +			       bool enable);
> +	int (*set_pipe_dt)(struct max_ser *ser, struct max_ser_pipe *pipe,
> +			   unsigned int i, unsigned int dt);
> +	int (*set_pipe_dt_en)(struct max_ser *ser, struct max_ser_pipe *pipe,
> +			      unsigned int i, bool enable);
> +	int (*set_pipe_vcs)(struct max_ser *ser, struct max_ser_pipe *pipe,
> +			    unsigned int vcs);
> +	int (*set_pipe_mode)(struct max_ser *ser, struct max_ser_pipe *pipe,
> +			     struct max_ser_pipe_mode *mode);
> +	int (*set_vc_remap)(struct max_ser *ser, unsigned int i,
> +			    struct max_serdes_vc_remap *vc_remap);
> +	int (*set_vc_remaps_enable)(struct max_ser *ser, unsigned int mask);
> +	int (*set_pipe_stream_id)(struct max_ser *ser, struct max_ser_pipe *pipe,
> +				  unsigned int stream_id);
> +	unsigned int (*get_pipe_stream_id)(struct max_ser *ser, struct max_ser_pipe *pipe);
> +	int (*set_pipe_phy)(struct max_ser *ser, struct max_ser_pipe *pipe,
> +			    struct max_ser_phy *phy);
> +};
> +
> +struct max_ser_priv;
> +
> +struct max_ser {
> +	struct max_ser_priv *priv;
> +
> +	const struct max_ser_ops *ops;
> +
> +	struct max_serdes_i2c_xlate *i2c_xlates;
> +
> +	struct max_ser_phy *phys;
> +	struct max_ser_pipe *pipes;
> +	const struct max_serdes_tpg_entry *tpg_entry;
> +	enum max_serdes_tpg_pattern tpg_pattern;
> +
> +	struct max_serdes_vc_remap *vc_remaps;
> +	unsigned int num_vc_remaps;
> +
> +	unsigned int phys_config;
> +	unsigned int active;
> +	enum max_serdes_gmsl_mode mode;
> +};
> +
> +int max_ser_probe(struct i2c_client *client, struct max_ser *ser);
> +
> +int max_ser_remove(struct max_ser *ser);
> +
> +int max_ser_set_double_bpps(struct v4l2_subdev *sd, u32 double_bpps);
> +unsigned int max_ser_get_supported_modes(struct v4l2_subdev *sd);
> +int max_ser_set_mode(struct v4l2_subdev *sd, enum max_serdes_gmsl_mode mode);
> +bool max_ser_supports_vc_remap(struct v4l2_subdev *sd);
> +int max_ser_set_stream_id(struct v4l2_subdev *sd, unsigned int stream_id);
> +int max_ser_get_stream_id(struct v4l2_subdev *sd, unsigned int *stream_id);
> +int max_ser_set_vc_remaps(struct v4l2_subdev *sd, struct max_serdes_vc_remap *vc_remaps,
> +			  int num_vc_remaps);
> +
> +int max_ser_reset(struct i2c_adapter *adapter, u8 addr);
> +int max_ser_wait(struct i2c_adapter *adapter, u8 addr);
> +int max_ser_wait_for_multiple(struct i2c_adapter *adapter, u8 *addrs,
> +			      unsigned int num_addrs, u8 *current_addr);
> +
> +int max_ser_change_address(struct i2c_adapter *adapter, u8 addr, u8 new_addr);
> +int max_ser_fix_tx_ids(struct i2c_adapter *adapter, u8 addr);
> +
> +#endif // MAX_SER_H
> 
> -- 
> 2.53.0
> 
> 

-- 
Kind Regards,
Niklas Söderlund

^ permalink raw reply

* Re: [PATCH v13 15/22] media: i2c: add Maxim GMSL2/3 deserializer framework
From: Niklas Söderlund @ 2026-06-10 14:35 UTC (permalink / raw)
  To: dumitru.ceclan
  Cc: Tomi Valkeinen, Mauro Carvalho Chehab, Sakari Ailus,
	Laurent Pinchart, Julien Massot, Rob Herring, Greg Kroah-Hartman,
	mitrutzceclan, linux-media, linux-kernel, devicetree,
	linux-staging, linux-gpio, Martin Hecht, Cosmin Tanislav
In-Reply-To: <20260604-gmsl2-3_serdes-v13-15-9d8a4919983b@analog.com>

Hello,

Thanks for your work.

On 2026-06-04 17:14:02 +0300, Dumitru Ceclan via B4 Relay wrote:
> From: Cosmin Tanislav <demonsingur@gmail.com>
> 
> These drivers are meant to be used as a common framework for Maxim
> GMSL2/3 deserializer.
> 
> This framework enables support for the following new features across
> all the chips:
>  * Full Streams API support
>  * .get_frame_desc()
>  * .get_mbus_config()
>  * I2C ATR
>  * automatic GMSL link version negotiation
>  * automatic stream id selection
>  * automatic VC remapping
>  * automatic pixel mode / tunnel mode selection
>  * automatic double mode selection / data padding
>  * logging of internal state and chip status registers via .log_status()
>  * PHY modes
>  * serializer pinctrl
>  * TPG
> 
> Signed-off-by: Cosmin Tanislav <demonsingur@gmail.com>
> ---
>  drivers/media/i2c/maxim-serdes/Makefile  |    2 +-
>  drivers/media/i2c/maxim-serdes/max_des.c | 3243 ++++++++++++++++++++++++++++++
>  drivers/media/i2c/maxim-serdes/max_des.h |  157 ++
>  3 files changed, 3401 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/media/i2c/maxim-serdes/Makefile b/drivers/media/i2c/maxim-serdes/Makefile
> index 17511cb03369..b54326a5c81b 100644
> --- a/drivers/media/i2c/maxim-serdes/Makefile
> +++ b/drivers/media/i2c/maxim-serdes/Makefile
> @@ -1,3 +1,3 @@
>  # SPDX-License-Identifier: GPL-2.0
> -max-serdes-objs := max_serdes.o max_ser.o
> +max-serdes-objs := max_serdes.o max_ser.o max_des.o
>  obj-$(CONFIG_VIDEO_MAXIM_SERDES) += max-serdes.o
> diff --git a/drivers/media/i2c/maxim-serdes/max_des.c b/drivers/media/i2c/maxim-serdes/max_des.c
> new file mode 100644
> index 000000000000..732e3ab83664
> --- /dev/null
> +++ b/drivers/media/i2c/maxim-serdes/max_des.c
> @@ -0,0 +1,3243 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Maxim GMSL2 Deserializer Driver
> + *
> + * Copyright (C) 2025 Analog Devices Inc.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/i2c-atr.h>
> +#include <linux/i2c-mux.h>
> +#include <linux/module.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/units.h>
> +
> +#include <media/mipi-csi2.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "max_des.h"
> +#include "max_ser.h"
> +#include "max_serdes.h"
> +
> +#define MAX_DES_LINK_FREQUENCY_MIN		(100  * HZ_PER_MHZ)
> +#define MAX_DES_LINK_FREQUENCY_DEFAULT		(750  * HZ_PER_MHZ)
> +#define MAX_DES_LINK_FREQUENCY_MAX		(1250 * HZ_PER_MHZ)
> +
> +#define MAX_DES_NUM_PHYS			4
> +#define MAX_DES_NUM_LINKS			4
> +#define MAX_DES_NUM_PIPES			8
> +
> +struct max_des_priv {
> +	struct max_des *des;
> +
> +	struct device *dev;
> +	struct i2c_client *client;
> +	struct i2c_atr *atr;
> +	struct i2c_mux_core *mux;
> +
> +	struct media_pad *pads;
> +	struct regulator **pocs;
> +	struct max_serdes_source *sources;
> +	u64 *streams_masks;
> +
> +	struct notifier_block i2c_nb;
> +	struct v4l2_subdev sd;
> +	struct v4l2_async_notifier nf;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +
> +	struct max_des_phy *unused_phy;
> +};
> +
> +struct max_des_remap_context {
> +	enum max_serdes_gmsl_mode mode;
> +	/* Mark whether TPG is enabled */
> +	bool tpg;
> +	/* Mark the PHYs to which each pipe is mapped. */
> +	unsigned long pipe_phy_masks[MAX_DES_NUM_PIPES];
> +	/* Mark the pipes in use. */
> +	bool pipe_in_use[MAX_DES_NUM_PIPES];
> +	/* Mark whether pipe has remapped VC ids. */
> +	bool vc_ids_remapped[MAX_DES_NUM_PIPES];
> +	/* Map between pipe VC ids and PHY VC ids. */
> +	unsigned int vc_ids_map[MAX_DES_NUM_PIPES][MAX_DES_NUM_PHYS][MAX_SERDES_VC_ID_NUM];
> +	/* Mark whether a pipe VC id has been mapped to a PHY VC id. */
> +	unsigned long vc_ids_masks[MAX_DES_NUM_PIPES][MAX_DES_NUM_PHYS];
> +	/* Mark whether a PHY VC id has been mapped. */
> +	unsigned long dst_vc_ids_masks[MAX_DES_NUM_PHYS];
> +};
> +
> +struct max_des_mode_context {
> +	bool phys_bpp8_shared_with_16[MAX_DES_NUM_PHYS];
> +	bool pipes_bpp8_shared_with_16[MAX_DES_NUM_PIPES];
> +	u32 phys_double_bpps[MAX_DES_NUM_PHYS];
> +	u32 pipes_double_bpps[MAX_DES_NUM_PIPES];
> +};
> +
> +struct max_des_route_hw {
> +	struct max_serdes_source *source;
> +	struct max_des_pipe *pipe;
> +	struct max_des_phy *phy;
> +	struct v4l2_mbus_frame_desc_entry entry;
> +	bool is_tpg;
> +};
> +
> +struct max_des_link_hw {
> +	struct max_serdes_source *source;
> +	struct max_des_link *link;
> +	struct max_des_pipe *pipe;
> +};
> +
> +static inline struct max_des_priv *sd_to_priv(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct max_des_priv, sd);
> +}
> +
> +static inline struct max_des_priv *nf_to_priv(struct v4l2_async_notifier *nf)
> +{
> +	return container_of(nf, struct max_des_priv, nf);
> +}
> +
> +static inline struct max_des_priv *ctrl_to_priv(struct v4l2_ctrl_handler *handler)
> +{
> +	return container_of(handler, struct max_des_priv, ctrl_handler);
> +}
> +
> +static inline bool max_des_pad_is_sink(struct max_des *des, u32 pad)
> +{
> +	return pad < des->info->num_links;
> +}
> +
> +static inline bool max_des_pad_is_source(struct max_des *des, u32 pad)
> +{
> +	return pad >= des->info->num_links &&
> +	       pad < des->info->num_links + des->info->num_phys;
> +}
> +
> +static inline bool max_des_pad_is_tpg(struct max_des *des, u32 pad)
> +{
> +	return pad == des->info->num_links + des->info->num_phys;
> +}
> +
> +static inline unsigned int max_des_link_to_pad(struct max_des *des,
> +					       struct max_des_link *link)
> +{
> +	return link->index;
> +}
> +
> +static inline unsigned int max_des_phy_to_pad(struct max_des *des,
> +					      struct max_des_phy *phy)
> +{
> +	return phy->index + des->info->num_links;
> +}
> +
> +static inline unsigned int max_des_num_pads(struct max_des *des)
> +{
> +	return des->info->num_links + des->info->num_phys +
> +	       (des->ops->set_tpg ? 1 : 0);
> +}
> +
> +static struct max_des_phy *max_des_pad_to_phy(struct max_des *des, u32 pad)
> +{
> +	if (!max_des_pad_is_source(des, pad))
> +		return NULL;
> +
> +	return &des->phys[pad - des->info->num_links];
> +}
> +
> +static struct max_des_link *max_des_pad_to_link(struct max_des *des, u32 pad)
> +{
> +	if (!max_des_pad_is_sink(des, pad))
> +		return NULL;
> +
> +	return &des->links[pad];
> +}
> +
> +static struct max_des_pipe *
> +max_des_find_link_pipe(struct max_des *des, struct max_des_link *link)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < des->info->num_pipes; i++) {
> +		struct max_des_pipe *pipe = &des->pipes[i];
> +
> +		if (pipe->link_id == link->index)
> +			return pipe;
> +	}
> +
> +	return NULL;
> +}
> +
> +static struct max_serdes_source *
> +max_des_get_link_source(struct max_des_priv *priv, struct max_des_link *link)
> +{
> +	return &priv->sources[link->index];
> +}
> +
> +static const struct max_serdes_tpg_entry *
> +max_des_find_tpg_entry(struct max_des *des, u32 target_index,
> +		       u32 width, u32 height, u32 code,
> +		       u32 numerator, u32 denominator)
> +{
> +	const struct max_serdes_tpg_entry *entry;
> +	unsigned int index = 0;
> +	unsigned int i;
> +
> +	for (i = 0; i < des->info->tpg_entries.num_entries; i++) {
> +		entry = &des->info->tpg_entries.entries[i];
> +
> +		if ((width != 0 && width != entry->width) ||
> +		    (height != 0 && height != entry->height) ||
> +		    (code != 0 && code != entry->code) ||
> +		    (numerator != 0 && numerator != entry->interval.numerator) ||
> +		    (denominator != 0 && denominator != entry->interval.denominator))
> +			continue;
> +
> +		if (index == target_index)
> +			break;
> +
> +		index++;
> +	}
> +
> +	if (i == des->info->tpg_entries.num_entries)
> +		return NULL;
> +
> +	return &des->info->tpg_entries.entries[i];
> +}
> +
> +static const struct max_serdes_tpg_entry *
> +max_des_find_state_tpg_entry(struct max_des *des, struct v4l2_subdev_state *state,
> +			     unsigned int pad)
> +{
> +	struct v4l2_mbus_framefmt *fmt;
> +	struct v4l2_fract *in;
> +
> +	fmt = v4l2_subdev_state_get_format(state, pad, MAX_SERDES_TPG_STREAM);
> +	if (!fmt)
> +		return NULL;
> +
> +	in = v4l2_subdev_state_get_interval(state, pad, MAX_SERDES_TPG_STREAM);
> +	if (!in)
> +		return NULL;
> +
> +	return max_des_find_tpg_entry(des, 0, fmt->width, fmt->height, fmt->code,
> +				      in->numerator, in->denominator);
> +}
> +
> +static int max_des_get_tpg_fd_entry_state(struct max_des *des,
> +					  struct v4l2_subdev_state *state,
> +					  struct v4l2_mbus_frame_desc_entry *fd_entry,
> +					  unsigned int pad)
> +{
> +	const struct max_serdes_tpg_entry *entry;
> +
> +	entry = max_des_find_state_tpg_entry(des, state, pad);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	fd_entry->stream = MAX_SERDES_TPG_STREAM;
> +	fd_entry->flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
> +	fd_entry->length = entry->width * entry->height * entry->bpp / 8;
> +	fd_entry->pixelcode = entry->code;
> +	fd_entry->bus.csi2.vc = 0;
> +	fd_entry->bus.csi2.dt = entry->dt;
> +
> +	return 0;
> +}
> +
> +static int max_des_tpg_route_to_hw(struct max_des_priv *priv,
> +				   struct v4l2_subdev_state *state,
> +				   struct v4l2_subdev_route *route,
> +				   struct max_des_route_hw *hw)
> +{
> +	struct max_des *des = priv->des;
> +
> +	/* TPG injects its data into all pipes, but use pipe 0 for simplicity. */
> +	hw->pipe = &des->pipes[0];
> +
> +	hw->phy = max_des_pad_to_phy(des, route->source_pad);
> +	if (!hw->phy)
> +		return -ENOENT;
> +
> +	return max_des_get_tpg_fd_entry_state(des, state, &hw->entry,
> +					      route->sink_pad);
> +}
> +
> +static int max_des_route_to_hw(struct max_des_priv *priv,
> +			       struct v4l2_subdev_state *state,
> +			       struct v4l2_subdev_route *route,
> +			       struct max_des_route_hw *hw)
> +{
> +	struct max_des *des = priv->des;
> +	struct v4l2_mbus_frame_desc fd = {};
> +	struct max_des_link *link;
> +	unsigned int i;
> +	int ret;
> +
> +	memset(hw, 0, sizeof(*hw));
> +
> +	hw->is_tpg = max_des_pad_is_tpg(des, route->sink_pad);
> +	if (hw->is_tpg)
> +		return max_des_tpg_route_to_hw(priv, state, route, hw);
> +
> +	link = max_des_pad_to_link(des, route->sink_pad);
> +	if (!link)
> +		return -ENOENT;
> +
> +	hw->phy = max_des_pad_to_phy(des, route->source_pad);
> +	if (!hw->phy)
> +		return -ENOENT;
> +
> +	hw->pipe = max_des_find_link_pipe(des, link);
> +	if (!hw->pipe)
> +		return -ENOENT;
> +
> +	hw->source = max_des_get_link_source(priv, link);
> +	if (!hw->source->sd)
> +		return 0;
> +
> +	ret = v4l2_subdev_call(hw->source->sd, pad, get_frame_desc,
> +			       hw->source->pad, &fd);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < fd.num_entries; i++)
> +		if (fd.entry[i].stream == route->sink_stream)
> +			break;
> +
> +	if (i == fd.num_entries)
> +		return -ENOENT;
> +
> +	hw->entry = fd.entry[i];
> +
> +	return 0;
> +}
> +
> +static int max_des_link_to_hw(struct max_des_priv *priv,
> +			      struct max_des_link *link,
> +			      struct max_des_link_hw *hw)
> +{
> +	struct max_des *des = priv->des;
> +
> +	memset(hw, 0, sizeof(*hw));
> +
> +	hw->link = link;
> +
> +	hw->pipe = max_des_find_link_pipe(des, hw->link);
> +	if (!hw->pipe)
> +		return -ENOENT;
> +
> +	hw->source = max_des_get_link_source(priv, hw->link);
> +
> +	return 0;
> +}
> +
> +static int max_des_link_index_to_hw(struct max_des_priv *priv, unsigned int i,
> +				    struct max_des_link_hw *hw)
> +{
> +	return max_des_link_to_hw(priv, &priv->des->links[i], hw);
> +}
> +
> +static int max_des_set_pipe_remaps(struct max_des_priv *priv,
> +				   struct max_des_pipe *pipe,
> +				   struct max_des_remap *remaps,
> +				   unsigned int num_remaps)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int mask = 0;
> +	unsigned int i;
> +	int ret;
> +
> +	if (!des->ops->set_pipe_remap)
> +		return 0;
> +
> +	for (i = 0; i < num_remaps; i++) {
> +		ret = des->ops->set_pipe_remap(des, pipe, i, &remaps[i]);
> +		if (ret)
> +			return ret;
> +
> +		mask |= BIT(i);
> +	}
> +
> +	return des->ops->set_pipe_remaps_enable(des, pipe, mask);
> +}
> +
> +static int max_des_set_pipe_vc_remaps(struct max_des_priv *priv,
> +				      struct max_des_pipe *pipe,
> +				      struct max_serdes_vc_remap *vc_remaps,
> +				      unsigned int num_vc_remaps)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int mask = 0;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < num_vc_remaps; i++) {
> +		ret = des->ops->set_pipe_vc_remap(des, pipe, i, &vc_remaps[i]);
> +		if (ret)
> +			return ret;
> +
> +		mask |= BIT(i);
> +	}
> +
> +	return des->ops->set_pipe_vc_remaps_enable(des, pipe, mask);
> +}
> +
> +static int max_des_map_src_dst_vc_id(struct max_des_remap_context *context,
> +				     unsigned int pipe_id, unsigned int phy_id,
> +				     unsigned int src_vc_id, bool keep_vc)
> +{
> +	unsigned int vc_id;
> +
> +	if (src_vc_id >= MAX_SERDES_VC_ID_NUM)
> +		return -E2BIG;
> +
> +	if (context->vc_ids_masks[pipe_id][phy_id] & BIT(src_vc_id))
> +		return 0;
> +
> +	if (keep_vc && !(context->dst_vc_ids_masks[phy_id] & BIT(src_vc_id)))
> +		vc_id = src_vc_id;
> +	else
> +		vc_id = ffz(context->dst_vc_ids_masks[phy_id]);
> +
> +	if (vc_id != src_vc_id)
> +		context->vc_ids_remapped[pipe_id] = true;
> +
> +	if (vc_id >= MAX_SERDES_VC_ID_NUM)
> +		return -E2BIG;
> +
> +	context->pipe_phy_masks[pipe_id] |= BIT(phy_id);
> +	context->dst_vc_ids_masks[phy_id] |= BIT(vc_id);
> +
> +	context->vc_ids_map[pipe_id][phy_id][src_vc_id] = vc_id;
> +	context->vc_ids_masks[pipe_id][phy_id] |= BIT(src_vc_id);
> +
> +	return 0;
> +}
> +
> +static int max_des_get_src_dst_vc_id(struct max_des_remap_context *context,
> +				     unsigned int pipe_id, unsigned int phy_id,
> +				     unsigned int src_vc_id, unsigned int *dst_vc_id)
> +{
> +	if (!(context->vc_ids_masks[pipe_id][phy_id] & BIT(src_vc_id)))
> +		return -ENOENT;
> +
> +	*dst_vc_id = context->vc_ids_map[pipe_id][phy_id][src_vc_id];
> +
> +	return 0;
> +}
> +
> +static int max_des_populate_remap_usage(struct max_des_priv *priv,
> +					struct max_des_remap_context *context,
> +					struct v4l2_subdev_state *state)
> +{
> +	struct v4l2_subdev_route *route;
> +	int ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (hw.is_tpg)
> +			context->tpg = true;
> +
> +		context->pipe_in_use[hw.pipe->index] = true;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_get_supported_modes(struct max_des_priv *priv,
> +				       struct max_des_remap_context *context,
> +				       unsigned int *modes)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	*modes = des->info->modes;
> +
> +	if (context->tpg)
> +		*modes = BIT(des->info->tpg_mode);
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link_hw hw;
> +
> +		ret = max_des_link_index_to_hw(priv, i, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.link->enabled)
> +			continue;
> +
> +		if (!hw.source->sd)
> +			continue;
> +
> +		if (!context->pipe_in_use[hw.pipe->index])
> +			continue;
> +
> +		*modes &= max_ser_get_supported_modes(hw.source->sd);
> +	}
> +
> +	/*
> +	 * Serializers need to all be in the same mode because of hardware
> +	 * issues when running them in mixed modes.
> +	 */
> +	if (!*modes)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int max_des_populate_remap_context_mode(struct max_des_priv *priv,
> +					       struct max_des_remap_context *context,
> +					       unsigned int modes)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	context->mode = MAX_SERDES_GMSL_PIXEL_MODE;
> +
> +	/*
> +	 * If pixel mode is the only supported mode, do not try to see if
> +	 * tunnel mode can be used.
> +	 */
> +	if (modes == BIT(MAX_SERDES_GMSL_PIXEL_MODE))
> +		return 0;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link_hw hw;
> +
> +		ret = max_des_link_index_to_hw(priv, i, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.link->enabled)
> +			continue;
> +
> +		if (!hw.source->sd)
> +			continue;
> +
> +		if (!context->pipe_in_use[hw.pipe->index])
> +			continue;
> +
> +		if (hweight_long(context->pipe_phy_masks[hw.pipe->index]) == 1 &&
> +		    (!context->vc_ids_remapped[hw.pipe->index] ||
> +		     max_ser_supports_vc_remap(hw.source->sd) ||
> +		     des->ops->set_pipe_vc_remap))
> +			continue;
> +
> +		return 0;
> +	}
> +
> +	context->mode = MAX_SERDES_GMSL_TUNNEL_MODE;
> +
> +	return 0;
> +}
> +
> +static int max_des_should_keep_vc(struct max_des_priv *priv,
> +				  struct max_des_route_hw *hw,
> +				  unsigned int modes)
> +{
> +	struct max_des *des = priv->des;
> +
> +	/* Pixel mode deserializers always have the ability to remap VCs. */
> +	if (modes == BIT(MAX_SERDES_GMSL_PIXEL_MODE))
> +		return false;
> +
> +	if (des->ops->set_pipe_vc_remap)
> +		return false;
> +
> +	if (!hw->is_tpg && hw->source && hw->source->sd &&
> +	    max_ser_supports_vc_remap(hw->source->sd))
> +		return false;
> +
> +	return true;
> +}
> +
> +static int max_des_populate_remap_context(struct max_des_priv *priv,
> +					  struct max_des_remap_context *context,
> +					  struct v4l2_subdev_state *state)
> +{
> +	struct v4l2_subdev_route *route;
> +	unsigned int modes;
> +	int ret;
> +
> +	ret = max_des_populate_remap_usage(priv, context, state);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_get_supported_modes(priv, context, &modes);
> +	if (ret)
> +		return ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +		bool keep_vc;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		keep_vc = max_des_should_keep_vc(priv, &hw, modes);
> +
> +		ret = max_des_map_src_dst_vc_id(context, hw.pipe->index, hw.phy->index,
> +						hw.entry.bus.csi2.vc, keep_vc);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return max_des_populate_remap_context_mode(priv, context, modes);
> +}
> +
> +static int max_des_populate_mode_context(struct max_des_priv *priv,
> +					 struct max_des_mode_context *context,
> +					 struct v4l2_subdev_state *state,
> +					 enum max_serdes_gmsl_mode mode)
> +{
> +	bool bpp8_not_shared_with_16_phys[MAX_DES_NUM_PHYS] = { 0 };
> +	u32 undoubled_bpps_phys[MAX_DES_NUM_PHYS] = { 0 };
> +	u32 bpps_pipes[MAX_DES_NUM_PIPES] = { 0 };
> +	struct max_des *des = priv->des;
> +	struct v4l2_subdev_route *route;
> +	unsigned int i;
> +	int ret;
> +
> +	if (mode != MAX_SERDES_GMSL_PIXEL_MODE)
> +		return 0;
> +
> +	/*
> +	 * Go over all streams and gather the bpps for all pipes.
> +	 *
> +	 * Then, go over all the streams again and check if the
> +	 * current stream is doubled.
> +	 *
> +	 * If the current stream is doubled, add it to a doubled mask for both
> +	 * the pipe and the PHY.
> +	 *
> +	 * If the current stream is not doubled, add it to a local undoubled
> +	 * mask for the PHY.
> +	 *
> +	 * Also, track whether an 8bpp stream is shared with any bpp > 8 on both
> +	 * the PHYs and the pipes, since that needs to be special cased.
> +	 *
> +	 * After going over all the streams, remove the undoubled streams from
> +	 * the doubled ones. Doubled and undoubled streams cannot be streamed
> +	 * over the same PHY.
> +	 *
> +	 * Then, do a second pass to remove the undoubled streams from the pipes.
> +	 *
> +	 * This operation cannot be done in a single pass because any pipe might
> +	 * generate an undoubled stream for a specific bpp, causing already
> +	 * processed pipes to need to have their doubled bpps updated.
> +	 */
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +		unsigned int bpp;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		ret = max_serdes_get_fd_bpp(&hw.entry, &bpp);
> +		if (ret)
> +			return ret;
> +
> +		bpps_pipes[hw.pipe->index] |= BIT(bpp);
> +	}
> +
> +	for_each_active_route(&state->routing, route) {
> +		unsigned int bpp, min_bpp, max_bpp, doubled_bpp;
> +		unsigned int pipe_id, phy_id;
> +		struct max_des_route_hw hw;
> +		u32 sink_bpps;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		ret = max_serdes_get_fd_bpp(&hw.entry, &bpp);
> +		if (ret)
> +			return ret;
> +
> +		sink_bpps = bpps_pipes[hw.pipe->index];
> +
> +		ret = max_serdes_process_bpps(priv->dev, sink_bpps, ~0U, &doubled_bpp);
> +		if (ret)
> +			return ret;
> +
> +		min_bpp = __ffs(sink_bpps);
> +		max_bpp = __fls(sink_bpps);
> +		pipe_id = hw.pipe->index;
> +		phy_id = hw.phy->index;
> +
> +		if (bpp == doubled_bpp) {
> +			context->phys_double_bpps[phy_id] |= BIT(bpp);
> +			context->pipes_double_bpps[pipe_id] |= BIT(bpp);
> +		} else {
> +			undoubled_bpps_phys[phy_id] |= BIT(bpp);
> +		}
> +
> +		if (min_bpp == 8 && max_bpp > 8) {
> +			context->phys_bpp8_shared_with_16[phy_id] = true;
> +			context->pipes_bpp8_shared_with_16[pipe_id] = true;
> +		} else if (min_bpp == 8 && max_bpp == 8) {
> +			bpp8_not_shared_with_16_phys[phy_id] = true;
> +		}
> +	}
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		if (context->phys_bpp8_shared_with_16[i] && bpp8_not_shared_with_16_phys[i]) {
> +			dev_err(priv->dev,
> +				"Cannot stream 8bpp coming from pipes padded to 16bpp "
> +				"and pipes not padded to 16bpp on the same PHY\n");

WARNING: quoted string split across lines
#747: FILE: drivers/media/i2c/maxim-serdes/max_des.c:699:
+				"Cannot stream 8bpp coming from pipes padded to 16bpp "
+				"and pipes not padded to 16bpp on the 
same PHY\n");



> +			return -EINVAL;
> +		}
> +	}
> +
> +	for (i = 0; i < des->info->num_phys; i++)
> +		context->phys_double_bpps[i] &= ~undoubled_bpps_phys[i];
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		context->pipes_double_bpps[hw.pipe->index] &=
> +			context->phys_double_bpps[hw.phy->index];
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_add_vc_remap(struct max_des *des, struct max_serdes_vc_remap *vc_remaps,
> +				unsigned int *num_vc_remaps, unsigned int src_vc_id,
> +				unsigned int dst_vc_id)
> +{
> +	struct max_serdes_vc_remap *vc_remap;
> +	unsigned int i;
> +
> +	for (i = 0; i < *num_vc_remaps; i++) {
> +		vc_remap = &vc_remaps[i];
> +
> +		if (vc_remap->src == src_vc_id && vc_remap->dst == dst_vc_id)
> +			return 0;
> +	}
> +
> +	if (*num_vc_remaps == MAX_SERDES_VC_ID_NUM)
> +		return -E2BIG;
> +
> +	vc_remaps[*num_vc_remaps].src = src_vc_id;
> +	vc_remaps[*num_vc_remaps].dst = dst_vc_id;
> +
> +	(*num_vc_remaps)++;
> +
> +	return 0;
> +}
> +
> +static int max_des_get_pipe_vc_remaps(struct max_des_priv *priv,
> +				      struct max_des_remap_context *context,
> +				      struct max_des_pipe *pipe,
> +				      struct max_serdes_vc_remap *vc_remaps,
> +				      unsigned int *num_vc_remaps,
> +				      struct v4l2_subdev_state *state,
> +				      u64 *streams_masks, bool with_tpg)
> +{
> +	struct max_des *des = priv->des;
> +	struct v4l2_subdev_route *route;
> +	int ret;
> +
> +	*num_vc_remaps = 0;
> +
> +	if (context->mode != MAX_SERDES_GMSL_TUNNEL_MODE)
> +		return 0;
> +
> +	for_each_active_route(&state->routing, route) {
> +		unsigned int src_vc_id, dst_vc_id;
> +		struct max_des_route_hw hw;
> +
> +		if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!with_tpg && hw.is_tpg)
> +			continue;
> +
> +		if (hw.pipe != pipe)
> +			continue;
> +
> +		src_vc_id = hw.entry.bus.csi2.vc;
> +
> +		ret = max_des_get_src_dst_vc_id(context, pipe->index, hw.phy->index,
> +						src_vc_id, &dst_vc_id);
> +		if (ret)
> +			return ret;
> +
> +		ret = max_des_add_vc_remap(des, vc_remaps, num_vc_remaps,
> +					   src_vc_id, dst_vc_id);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void max_des_get_pipe_mode(struct max_des_mode_context *context,
> +				  struct max_des_pipe *pipe,
> +				  struct max_des_pipe_mode *mode)
> +{
> +	u32 double_bpps = context->pipes_double_bpps[pipe->index];
> +
> +	if ((double_bpps & BIT(8)) &&
> +	    !context->pipes_bpp8_shared_with_16[pipe->index]) {
> +		mode->dbl8 = true;
> +		mode->dbl8mode = true;
> +	}
> +}
> +
> +static void max_des_get_phy_mode(struct max_des_mode_context *context,
> +				 struct max_des_phy *phy,
> +				 struct max_des_phy_mode *mode)
> +{
> +	bool bpp8_pipe_shared_with_16 = context->phys_bpp8_shared_with_16[phy->index];
> +	u32 double_bpps = context->phys_double_bpps[phy->index];
> +
> +	if (BIT(8) & double_bpps) {
> +		if (bpp8_pipe_shared_with_16)
> +			mode->alt2_mem_map8 = true;
> +		else
> +			mode->alt_mem_map8 = true;
> +	}
> +
> +	if (BIT(10) & double_bpps)
> +		mode->alt_mem_map10 = true;
> +
> +	if (BIT(12) & double_bpps)
> +		mode->alt_mem_map12 = true;
> +}
> +
> +static int max_des_set_modes(struct max_des_priv *priv,
> +			     struct max_des_mode_context *context)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		struct max_des_phy *phy = &des->phys[i];
> +		struct max_des_phy_mode mode = { 0 };
> +
> +		max_des_get_phy_mode(context, phy, &mode);
> +
> +		if (phy->mode.alt_mem_map8 == mode.alt_mem_map8 &&
> +		    phy->mode.alt_mem_map10 == mode.alt_mem_map10 &&
> +		    phy->mode.alt_mem_map12 == mode.alt_mem_map12 &&
> +		    phy->mode.alt2_mem_map8 == mode.alt2_mem_map8)
> +			continue;
> +
> +		if (des->ops->set_phy_mode) {
> +			ret = des->ops->set_phy_mode(des, phy, &mode);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		phy->mode = mode;
> +	}
> +
> +	for (i = 0; i < des->info->num_pipes; i++) {
> +		struct max_des_pipe *pipe = &des->pipes[i];
> +		struct max_des_pipe_mode mode = { 0 };
> +
> +		max_des_get_pipe_mode(context, pipe, &mode);
> +
> +		if (pipe->mode.dbl8 == mode.dbl8 &&
> +		    pipe->mode.dbl10 == mode.dbl10 &&
> +		    pipe->mode.dbl12 == mode.dbl12 &&
> +		    pipe->mode.dbl8mode == mode.dbl8mode &&
> +		    pipe->mode.dbl10mode == mode.dbl10mode)
> +			continue;
> +
> +		if (des->ops->set_pipe_mode) {
> +			ret = des->ops->set_pipe_mode(des, pipe, &mode);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		pipe->mode = mode;
> +	}
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link_hw hw;
> +		u32 pipe_double_bpps = 0;
> +
> +		ret = max_des_link_index_to_hw(priv, i, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.link->enabled)
> +			continue;
> +
> +		if (!hw.source->sd)
> +			continue;
> +
> +		pipe_double_bpps = context->pipes_double_bpps[hw.pipe->index];
> +
> +		ret = max_ser_set_double_bpps(hw.source->sd, pipe_double_bpps);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_set_tunnel(struct max_des_priv *priv,
> +			      struct max_des_remap_context *context)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	if (des->ops->set_pipe_tunnel_enable) {
> +		for (i = 0; i < des->info->num_pipes; i++) {
> +			struct max_des_pipe *pipe = &des->pipes[i];
> +			bool tunnel_mode = context->mode == MAX_SERDES_GMSL_TUNNEL_MODE;
> +
> +			ret = des->ops->set_pipe_tunnel_enable(des, pipe, tunnel_mode);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link_hw hw;
> +
> +		ret = max_des_link_index_to_hw(priv, i, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.link->enabled)
> +			continue;
> +
> +		if (!hw.source->sd)
> +			continue;
> +
> +		if (!context->pipe_in_use[hw.pipe->index])
> +			continue;
> +
> +		ret = max_ser_set_mode(hw.source->sd, context->mode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	des->mode = context->mode;
> +
> +	return 0;
> +}
> +
> +static int max_des_set_vc_remaps(struct max_des_priv *priv,
> +				 struct max_des_remap_context *context,
> +				 struct v4l2_subdev_state *state,
> +				 u64 *streams_masks)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	if (des->ops->set_pipe_vc_remap)
> +		return 0;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_serdes_vc_remap vc_remaps[MAX_SERDES_VC_ID_NUM];
> +		struct max_des_link_hw hw;
> +		unsigned int num_vc_remaps;
> +
> +		ret = max_des_link_index_to_hw(priv, i, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.link->enabled)
> +			continue;
> +
> +		if (!hw.source->sd)
> +			continue;
> +
> +		if (!max_ser_supports_vc_remap(hw.source->sd))
> +			continue;
> +
> +		ret = max_des_get_pipe_vc_remaps(priv, context, hw.pipe,
> +						 vc_remaps, &num_vc_remaps,
> +						 state, streams_masks, false);
> +		if (ret)
> +			return ret;
> +
> +		ret = max_ser_set_vc_remaps(hw.source->sd, vc_remaps, num_vc_remaps);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_set_pipes_stream_id(struct max_des_priv *priv)
> +{
> +	bool stream_id_usage[MAX_SERDES_STREAMS_NUM] = { 0 };
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link_hw hw;
> +		unsigned int stream_id;
> +
> +		ret = max_des_link_index_to_hw(priv, i, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.link->enabled)
> +			continue;
> +
> +		if (!hw.source->sd)
> +			continue;
> +
> +		stream_id = hw.pipe->stream_id;
> +
> +		ret = max_ser_set_stream_id(hw.source->sd, stream_id);
> +		if (ret == -EOPNOTSUPP) {
> +			/*
> +			 * Serializer does not support setting the stream id,
> +			 * retrieve its hardcoded stream id.
> +			 */
> +			ret = max_ser_get_stream_id(hw.source->sd, &stream_id);
> +		}
> +
> +		if (ret)
> +			return ret;
> +
> +		if (stream_id >= MAX_SERDES_STREAMS_NUM) {
> +			dev_err(priv->dev, "Invalid stream id %u\n", stream_id);
> +			return -EINVAL;
> +		}
> +
> +		if (stream_id_usage[stream_id] && des->info->needs_unique_stream_id) {
> +			dev_err(priv->dev, "Duplicate stream id %u\n", stream_id);
> +			return -EINVAL;
> +		}
> +
> +		ret = des->ops->set_pipe_stream_id(des, hw.pipe, stream_id);
> +		if (ret)
> +			return ret;
> +
> +		stream_id_usage[stream_id] = true;
> +		hw.pipe->stream_id = stream_id;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_set_pipes_phy(struct max_des_priv *priv,
> +				 struct max_des_remap_context *context)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	if (!des->ops->set_pipe_phy && !des->ops->set_pipe_tunnel_phy)
> +		return 0;
> +
> +	for (i = 0; i < des->info->num_pipes; i++) {
> +		struct max_des_pipe *pipe = &des->pipes[i];
> +		struct max_des_phy *phy;
> +		unsigned int phy_id;
> +
> +		phy_id = find_first_bit(&context->pipe_phy_masks[pipe->index],
> +					des->info->num_phys);
> +
> +		if (priv->unused_phy &&
> +		    (context->mode != MAX_SERDES_GMSL_TUNNEL_MODE ||
> +		     phy_id == des->info->num_phys))
> +			phy_id = priv->unused_phy->index;
> +
> +		if (phy_id != des->info->num_phys) {
> +			phy = &des->phys[phy_id];
> +
> +			if (context->mode == MAX_SERDES_GMSL_PIXEL_MODE &&
> +			    des->ops->set_pipe_phy)
> +				ret = des->ops->set_pipe_phy(des, pipe, phy);
> +			else if (context->mode == MAX_SERDES_GMSL_TUNNEL_MODE &&
> +				 des->ops->set_pipe_tunnel_phy)
> +				ret = des->ops->set_pipe_tunnel_phy(des, pipe, phy);
> +			else
> +				ret = 0;
> +
> +			if (ret)
> +				return ret;
> +		}
> +
> +		pipe->phy_id = phy_id;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_add_remap(struct max_des *des, struct max_des_remap *remaps,
> +			     unsigned int *num_remaps, unsigned int phy_id,
> +			     unsigned int src_vc_id, unsigned int dst_vc_id,
> +			     unsigned int dt)
> +{
> +	struct max_des_remap *remap;
> +	unsigned int i;
> +
> +	for (i = 0; i < *num_remaps; i++) {
> +		remap = &remaps[i];
> +
> +		if (remap->from_dt == dt && remap->to_dt == dt &&
> +		    remap->from_vc == src_vc_id && remap->to_vc == dst_vc_id &&
> +		    remap->phy == phy_id)
> +			return 0;
> +	}
> +
> +	if (*num_remaps == des->info->num_remaps_per_pipe)
> +		return -E2BIG;
> +
> +	remap = &remaps[*num_remaps];
> +	remap->from_dt = dt;
> +	remap->from_vc = src_vc_id;
> +	remap->to_dt = dt;
> +	remap->to_vc = dst_vc_id;
> +	remap->phy = phy_id;
> +
> +	(*num_remaps)++;
> +
> +	return 0;
> +}
> +
> +static int max_des_add_remaps(struct max_des *des, struct max_des_remap *remaps,
> +			      unsigned int *num_remaps, unsigned int phy_id,
> +			      unsigned int src_vc_id, unsigned int dst_vc_id,
> +			      unsigned int dt)
> +{
> +	int ret;
> +
> +	ret = max_des_add_remap(des, remaps, num_remaps, phy_id,
> +				src_vc_id, dst_vc_id, dt);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_add_remap(des, remaps, num_remaps, phy_id,
> +				src_vc_id, dst_vc_id, MIPI_CSI2_DT_FS);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_add_remap(des, remaps, num_remaps, phy_id,
> +				src_vc_id, dst_vc_id, MIPI_CSI2_DT_FE);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int max_des_get_pipe_remaps(struct max_des_priv *priv,
> +				   struct max_des_remap_context *context,
> +				   struct max_des_pipe *pipe,
> +				   struct max_des_remap *remaps,
> +				   unsigned int *num_remaps,
> +				   struct v4l2_subdev_state *state,
> +				   u64 *streams_masks)
> +{
> +	struct v4l2_mbus_frame_desc_entry tpg_entry = { 0 };
> +	struct max_des *des = priv->des;
> +	struct v4l2_subdev_route *route;
> +	bool is_tpg_pipe = true;
> +	int ret;
> +
> +	*num_remaps = 0;
> +
> +	if (context->mode != MAX_SERDES_GMSL_PIXEL_MODE)
> +		return 0;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +		unsigned int src_vc_id, dst_vc_id;
> +
> +		if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (hw.is_tpg && hw.pipe != pipe) {
> +			is_tpg_pipe = false;
> +			tpg_entry = hw.entry;
> +		}
> +
> +		if (hw.pipe != pipe)
> +			continue;
> +
> +		src_vc_id = hw.entry.bus.csi2.vc;
> +
> +		ret = max_des_get_src_dst_vc_id(context, pipe->index, hw.phy->index,
> +						src_vc_id, &dst_vc_id);
> +		if (ret)
> +			return ret;
> +
> +		ret = max_des_add_remaps(des, remaps, num_remaps, hw.phy->index,
> +					 src_vc_id, dst_vc_id,
> +					 hw.entry.bus.csi2.dt);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/*
> +	 * TPG mode is only handled on pipe 0, but the TPG pollutes other pipes
> +	 * with the same data.
> +	 * For devices that do not support setting the default PHY of a pipe,
> +	 * we want to filter out this data so it does not end up on the wrong
> +	 * PHY.
> +	 * Devices that support setting the default PHY of a pipe already use it
> +	 * to route unused pipes to an unused PHY.
> +	 */
> +	if (context->tpg && !is_tpg_pipe && !des->ops->set_pipe_phy &&
> +	    priv->unused_phy) {
> +		ret = max_des_add_remaps(des, remaps, num_remaps,
> +					 priv->unused_phy->index,
> +					 tpg_entry.bus.csi2.vc,
> +					 tpg_entry.bus.csi2.vc,
> +					 tpg_entry.bus.csi2.dt);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_update_pipe_vc_remaps(struct max_des_priv *priv,
> +					 struct max_des_remap_context *context,
> +					 struct max_des_pipe *pipe,
> +					 struct v4l2_subdev_state *state,
> +					 u64 *streams_masks)
> +{
> +	struct max_des *des = priv->des;
> +	struct max_serdes_vc_remap *vc_remaps;
> +	unsigned int num_vc_remaps;
> +	int ret;
> +
> +	if (!des->ops->set_pipe_vc_remap)
> +		return 0;
> +
> +	vc_remaps = devm_kcalloc(priv->dev, MAX_SERDES_VC_ID_NUM,
> +				 sizeof(*vc_remaps), GFP_KERNEL);
> +	if (!vc_remaps)
> +		return -ENOMEM;
> +
> +	ret = max_des_get_pipe_vc_remaps(priv, context, pipe, vc_remaps, &num_vc_remaps,
> +					 state, streams_masks, true);
> +	if (ret)
> +		goto err_free_new_vc_remaps;
> +
> +	ret = max_des_set_pipe_vc_remaps(priv, pipe, vc_remaps, num_vc_remaps);
> +	if (ret)
> +		goto err_free_new_vc_remaps;
> +
> +	if (pipe->vc_remaps)
> +		devm_kfree(priv->dev, pipe->vc_remaps);
> +
> +	pipe->vc_remaps = vc_remaps;
> +	pipe->num_vc_remaps = num_vc_remaps;
> +
> +	return 0;
> +
> +err_free_new_vc_remaps:
> +	devm_kfree(priv->dev, vc_remaps);
> +
> +	return ret;
> +}
> +
> +static int max_des_update_pipe_remaps(struct max_des_priv *priv,
> +				      struct max_des_remap_context *context,
> +				      struct max_des_pipe *pipe,
> +				      struct v4l2_subdev_state *state,
> +				      u64 *streams_masks)
> +{
> +	struct max_des *des = priv->des;
> +	struct max_des_remap *remaps;
> +	unsigned int num_remaps;
> +	int ret;
> +
> +	if (!des->ops->set_pipe_remap)
> +		return 0;
> +
> +	remaps = devm_kcalloc(priv->dev, des->info->num_remaps_per_pipe,
> +			      sizeof(*remaps), GFP_KERNEL);
> +	if (!remaps)
> +		return -ENOMEM;
> +
> +	ret = max_des_get_pipe_remaps(priv, context, pipe, remaps, &num_remaps,
> +				      state, streams_masks);
> +	if (ret)
> +		goto err_free_new_remaps;
> +
> +	ret = max_des_set_pipe_remaps(priv, pipe, remaps, num_remaps);
> +	if (ret)
> +		goto err_free_new_remaps;
> +
> +	if (pipe->remaps)
> +		devm_kfree(priv->dev, pipe->remaps);
> +
> +	pipe->remaps = remaps;
> +	pipe->num_remaps = num_remaps;
> +
> +	return 0;
> +
> +err_free_new_remaps:
> +	devm_kfree(priv->dev, remaps);
> +
> +	return ret;
> +}
> +
> +static int max_des_update_pipe_enable(struct max_des_priv *priv,
> +				      struct max_des_pipe *pipe,
> +				      struct v4l2_subdev_state *state,
> +				      u64 *streams_masks)
> +{
> +	struct max_des *des = priv->des;
> +	struct v4l2_subdev_route *route;
> +	bool enable = false;
> +	int ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +
> +		if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (hw.pipe != pipe)
> +			continue;
> +
> +		enable = true;
> +		break;
> +	}
> +
> +	if (enable == pipe->enabled)
> +		return 0;
> +
> +	ret = des->ops->set_pipe_enable(des, pipe, enable);
> +	if (ret)
> +		return ret;
> +
> +	pipe->enabled = enable;
> +
> +	return 0;
> +}
> +
> +static int max_des_update_pipe(struct max_des_priv *priv,
> +			       struct max_des_remap_context *context,
> +			       struct max_des_pipe *pipe,
> +			       struct v4l2_subdev_state *state,
> +			       u64 *streams_masks)
> +{
> +	int ret;
> +
> +	ret = max_des_update_pipe_remaps(priv, context, pipe,
> +					 state, streams_masks);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_update_pipe_vc_remaps(priv, context, pipe, state,
> +					    streams_masks);
> +	if (ret)
> +		goto err_revert_update_pipe_remaps;
> +
> +	ret = max_des_update_pipe_enable(priv, pipe, state, streams_masks);
> +	if (ret)
> +		goto err_revert_update_pipe_vc_remaps;
> +
> +	return 0;
> +
> +err_revert_update_pipe_vc_remaps:
> +	max_des_update_pipe_vc_remaps(priv, context, pipe, state,
> +				      priv->streams_masks);
> +
> +err_revert_update_pipe_remaps:
> +	max_des_update_pipe_remaps(priv, context, pipe, state,
> +				   priv->streams_masks);
> +
> +	return ret;
> +}
> +
> +static int max_des_init_link_ser_xlate(struct max_des_priv *priv,
> +				       struct max_des_link *link,
> +				       struct i2c_adapter *adapter,
> +				       u8 power_up_addr, u8 new_addr)
> +{
> +	struct max_des *des = priv->des;
> +	u8 addrs[] = { power_up_addr, new_addr };
> +	u8 current_addr;
> +	int ret;
> +
> +	ret = des->ops->select_links(des, BIT(link->index));
> +	if (ret)
> +		return ret;
> +
> +	ret = max_ser_wait_for_multiple(adapter, addrs, ARRAY_SIZE(addrs),
> +					&current_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to wait for serializer at 0x%02x or 0x%02x: %d\n",
> +			power_up_addr, new_addr, ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_reset(adapter, current_addr);
> +	if (ret) {
> +		dev_err(priv->dev, "Failed to reset serializer: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_wait(adapter, power_up_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to wait for serializer at 0x%02x: %d\n",
> +			power_up_addr, ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_change_address(adapter, power_up_addr, new_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to change serializer from 0x%02x to 0x%02x: %d\n",
> +			power_up_addr, new_addr, ret);
> +		return ret;
> +	}
> +
> +	ret = max_ser_wait(adapter, new_addr);
> +	if (ret) {
> +		dev_err(priv->dev,
> +			"Failed to wait for serializer at 0x%02x: %d\n",
> +			new_addr, ret);
> +		return ret;
> +	}
> +
> +	if (des->info->fix_tx_ids) {
> +		ret = max_ser_fix_tx_ids(adapter, new_addr);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +static int max_des_init(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	if (des->ops->init) {
> +		ret = des->ops->init(des);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (des->ops->set_enable) {
> +		ret = des->ops->set_enable(des, false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		struct max_des_phy *phy = &des->phys[i];
> +
> +		if (phy->enabled) {
> +			ret = des->ops->init_phy(des, phy);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = des->ops->set_phy_enable(des, phy, phy->enabled);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < des->info->num_pipes; i++) {
> +		struct max_des_pipe *pipe = &des->pipes[i];
> +		struct max_des_link *link = &des->links[pipe->link_id];
> +
> +		ret = des->ops->set_pipe_enable(des, pipe, false);
> +		if (ret)
> +			return ret;
> +
> +		if (des->ops->set_pipe_tunnel_enable) {
> +			ret = des->ops->set_pipe_tunnel_enable(des, pipe, false);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (des->ops->set_pipe_stream_id) {
> +			ret = des->ops->set_pipe_stream_id(des, pipe, pipe->stream_id);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (des->ops->set_pipe_link) {
> +			ret = des->ops->set_pipe_link(des, pipe, link);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = max_des_set_pipe_remaps(priv, pipe, pipe->remaps,
> +					      pipe->num_remaps);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (!des->ops->init_link)
> +		return 0;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		ret = des->ops->init_link(des, link);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void max_des_ser_find_version_range(struct max_des *des, int *min, int *max)
> +{
> +	unsigned int i;
> +
> +	*min = MAX_SERDES_GMSL_MIN;
> +	*max = MAX_SERDES_GMSL_MAX;
> +
> +	if (!des->info->needs_single_link_version)
> +		return;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		if (!link->ser_xlate.en)
> +			continue;
> +
> +		*min = *max = link->version;
> +
> +		return;
> +	}
> +}
> +
> +static int max_des_ser_attach_addr(struct max_des_priv *priv, u32 chan_id,
> +				   u16 addr, u16 alias)
> +{
> +	struct max_des *des = priv->des;
> +	struct max_des_link *link = &des->links[chan_id];
> +	int i, min, max;
> +	int ret = 0;
> +
> +	max_des_ser_find_version_range(des, &min, &max);
> +
> +	if (link->ser_xlate.en) {
> +		dev_err(priv->dev, "Serializer for link %u already bound\n",
> +			link->index);
> +		return -EINVAL;
> +	}
> +
> +	for (i = max; i >= min; i--) {
> +		if (!(des->info->versions & BIT(i)))
> +			continue;
> +
> +		if (des->ops->set_link_version) {
> +			ret = des->ops->set_link_version(des, link, i);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		ret = max_des_init_link_ser_xlate(priv, link, priv->client->adapter,
> +						  addr, alias);
> +		if (!ret)
> +			break;
> +	}
> +
> +	if (ret) {
> +		dev_err(priv->dev, "Cannot find serializer for link %u\n",
> +			link->index);
> +		return -ENOENT;
> +	}
> +
> +	link->version = i;
> +	link->ser_xlate.src = alias;
> +	link->ser_xlate.dst = addr;
> +	link->ser_xlate.en = true;
> +
> +	return 0;
> +}
> +
> +static int max_des_ser_atr_attach_addr(struct i2c_atr *atr, u32 chan_id,
> +				       u16 addr, u16 alias)
> +{
> +	struct max_des_priv *priv = i2c_atr_get_driver_data(atr);
> +
> +	return max_des_ser_attach_addr(priv, chan_id, addr, alias);
> +}
> +
> +static void max_des_ser_atr_detach_addr(struct i2c_atr *atr, u32 chan_id, u16 addr)
> +{
> +	/* Don't do anything. */
> +}
> +
> +static const struct i2c_atr_ops max_des_i2c_atr_ops = {
> +	.attach_addr = max_des_ser_atr_attach_addr,
> +	.detach_addr = max_des_ser_atr_detach_addr,
> +};
> +
> +static void max_des_i2c_atr_deinit(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		/* Deleting adapters that haven't been added does no harm. */
> +		i2c_atr_del_adapter(priv->atr, link->index);
> +	}
> +
> +	i2c_atr_delete(priv->atr);
> +}
> +
> +static int max_des_i2c_atr_init(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int mask = 0;
> +	unsigned int i;
> +	int ret;
> +
> +	if (!i2c_check_functionality(priv->client->adapter,
> +				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
> +		return -ENODEV;
> +
> +	priv->atr = i2c_atr_new(priv->client->adapter, priv->dev,
> +				&max_des_i2c_atr_ops, des->info->num_links,
> +				I2C_ATR_F_STATIC | I2C_ATR_F_PASSTHROUGH);
> +	if (IS_ERR(priv->atr))
> +		return PTR_ERR(priv->atr);
> +
> +	i2c_atr_set_driver_data(priv->atr, priv);
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +		struct i2c_atr_adap_desc desc = {
> +			.chan_id = i,
> +		};
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		ret = i2c_atr_add_adapter(priv->atr, &desc);
> +		if (ret)
> +			goto err_add_adapters;
> +	}
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		mask |= BIT(link->index);
> +	}
> +
> +	ret = des->ops->select_links(des, mask);
> +	if (ret)
> +		goto err_add_adapters;
> +
> +	return 0;
> +
> +err_add_adapters:
> +	max_des_i2c_atr_deinit(priv);
> +
> +	return ret;
> +}
> +
> +static void max_des_i2c_mux_deinit(struct max_des_priv *priv)
> +{
> +	i2c_mux_del_adapters(priv->mux);
> +	bus_unregister_notifier(&i2c_bus_type, &priv->i2c_nb);
> +}
> +
> +static int max_des_i2c_mux_bus_notifier_call(struct notifier_block *nb,
> +					     unsigned long event, void *device)
> +{
> +	struct max_des_priv *priv = container_of(nb, struct max_des_priv, i2c_nb);
> +	struct max_des *des = priv->des;
> +	struct device *dev = device;
> +	struct i2c_client *client;
> +	unsigned int i;
> +
> +	/*
> +	 * Ideally, we would want to negotiate the GMSL version on
> +	 * BUS_NOTIFY_ADD_DEVICE, but the adapters list is only populated with
> +	 * the new adapter after BUS_NOTIFY_ADD_DEVICE is issued.
> +	 */
> +	if (event != BUS_NOTIFY_BIND_DRIVER)
> +		return NOTIFY_DONE;
> +
> +	client = i2c_verify_client(dev);
> +	if (!client)
> +		return NOTIFY_DONE;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		if (des->links[i].enabled &&
> +		    client->adapter == des->links[i].adapter)
> +			break;
> +	}
> +
> +	if (i == des->info->num_links)
> +		return NOTIFY_DONE;
> +
> +	max_des_ser_attach_addr(priv, i, client->addr, client->addr);
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static int max_des_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
> +{
> +	struct max_des_priv *priv = i2c_mux_priv(muxc);
> +	struct max_des *des = priv->des;
> +
> +	if (!des->ops->select_links)
> +		return 0;
> +
> +	return des->ops->select_links(des, BIT(chan));
> +}
> +
> +static int max_des_i2c_mux_init(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	u32 flags = I2C_MUX_LOCKED;
> +	unsigned int i;
> +	int ret;
> +
> +	if (des->info->num_links == 1)
> +		flags |= I2C_MUX_GATE;
> +
> +	priv->mux = i2c_mux_alloc(priv->client->adapter, priv->dev,
> +				  des->info->num_links, 0, flags,
> +				  max_des_i2c_mux_select, NULL);
> +	if (!priv->mux)
> +		return -ENOMEM;
> +
> +	priv->mux->priv = priv;
> +
> +	priv->i2c_nb.notifier_call = max_des_i2c_mux_bus_notifier_call;
> +	ret = bus_register_notifier(&i2c_bus_type, &priv->i2c_nb);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		ret = i2c_mux_add_adapter(priv->mux, 0, i);
> +		if (ret)
> +			goto err_add_adapters;
> +
> +		link->adapter = priv->mux->adapter[priv->mux->num_adapters - 1];
> +	}
> +
> +	return 0;
> +
> +err_add_adapters:
> +	max_des_i2c_mux_deinit(priv);
> +
> +	return ret;
> +}
> +
> +static void max_des_i2c_adapter_deinit(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +
> +	if (des->info->use_atr)
> +		return max_des_i2c_atr_deinit(priv);
> +	else
> +		return max_des_i2c_mux_deinit(priv);
> +}
> +
> +static int max_des_i2c_adapter_init(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +
> +	if (des->info->use_atr)
> +		return max_des_i2c_atr_init(priv);
> +	else
> +		return max_des_i2c_mux_init(priv);
> +
> +	return 0;
> +}
> +
> +static int max_des_set_tpg_fmt(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_state *state,
> +			       struct v4l2_subdev_format *format)
> +{
> +	struct v4l2_mbus_framefmt *fmt = &format->format;
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +	const struct max_serdes_tpg_entry *entry;
> +	struct v4l2_fract *in;
> +
> +	if (format->stream != MAX_SERDES_TPG_STREAM)
> +		return -EINVAL;
> +
> +	entry = max_des_find_tpg_entry(des, 0, fmt->width, fmt->height,
> +				       fmt->code, 0, 0);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	in = v4l2_subdev_state_get_interval(state, format->pad, format->stream);
> +	if (!in)
> +		return -EINVAL;
> +
> +	in->numerator = entry->interval.numerator;
> +	in->denominator = entry->interval.denominator;
> +
> +	return 0;
> +}
> +
> +static int max_des_set_fmt(struct v4l2_subdev *sd,
> +			   struct v4l2_subdev_state *state,
> +			   struct v4l2_subdev_format *format)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +	struct v4l2_mbus_framefmt *fmt;
> +	int ret;
> +
> +	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active)
> +		return -EBUSY;
> +
> +	/* No transcoding, source and sink formats must match. */
> +	if (max_des_pad_is_source(des, format->pad))
> +		return v4l2_subdev_get_fmt(sd, state, format);
> +
> +	if (max_des_pad_is_tpg(des, format->pad)) {
> +		ret = max_des_set_tpg_fmt(sd, state, format);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	if (format->format.code == ~0U)
> +		format->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
> +
> +	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	*fmt = format->format;
> +
> +	fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
> +							   format->stream);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	*fmt = format->format;
> +
> +	return 0;
> +}
> +
> +static int max_des_enum_frame_interval(struct v4l2_subdev *sd,
> +				       struct v4l2_subdev_state *state,
> +				       struct v4l2_subdev_frame_interval_enum *fie)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +	const struct max_serdes_tpg_entry *entry;
> +
> +	if (!max_des_pad_is_tpg(des, fie->pad) ||
> +	    fie->stream != MAX_SERDES_TPG_STREAM)
> +		return -ENOTTY;
> +
> +	entry = max_des_find_tpg_entry(des, fie->index, fie->width, fie->height,
> +				       fie->code, fie->interval.denominator,
> +				       fie->interval.numerator);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	fie->interval.numerator = entry->interval.numerator;
> +	fie->interval.denominator = entry->interval.denominator;
> +
> +	return 0;
> +}
> +
> +static int max_des_get_frame_interval(struct v4l2_subdev *sd,
> +				      struct v4l2_subdev_state *state,
> +				      struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +
> +	if (!max_des_pad_is_tpg(des, fi->pad) ||
> +	    fi->stream != MAX_SERDES_TPG_STREAM)
> +		return -ENOTTY;
> +
> +	return v4l2_subdev_get_frame_interval(sd, state, fi);
> +}
> +
> +static int max_des_set_frame_interval(struct v4l2_subdev *sd,
> +				      struct v4l2_subdev_state *state,
> +				      struct v4l2_subdev_frame_interval *fi)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +	const struct max_serdes_tpg_entry *entry;
> +	struct v4l2_mbus_framefmt *fmt;
> +	struct v4l2_fract *in;
> +
> +	if (!max_des_pad_is_tpg(des, fi->pad) ||
> +	    fi->stream != MAX_SERDES_TPG_STREAM)
> +		return -ENOTTY;
> +
> +	if (fi->which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active)
> +		return -EBUSY;
> +
> +	fmt = v4l2_subdev_state_get_format(state, fi->pad, fi->stream);
> +	if (!fmt)
> +		return -EINVAL;
> +
> +	entry = max_des_find_tpg_entry(des, 0, fmt->width, fmt->height,
> +				       fmt->code, fi->interval.denominator,
> +				       fi->interval.numerator);
> +	if (!entry)
> +		return -EINVAL;
> +
> +	in = v4l2_subdev_state_get_interval(state, fi->pad, fi->stream);
> +	if (!in)
> +		return -EINVAL;
> +
> +	in->numerator = fi->interval.numerator;
> +	in->denominator = fi->interval.denominator;
> +
> +	return 0;
> +}
> +
> +static int max_des_log_status(struct v4l2_subdev *sd)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +	unsigned int i, j;
> +	int ret;
> +
> +	v4l2_info(sd, "active: %u\n", des->active);
> +	v4l2_info(sd, "mode: %s", max_serdes_gmsl_mode_str(des->mode));
> +	if (des->ops->set_tpg) {
> +		const struct max_serdes_tpg_entry *entry = des->tpg_entry;
> +
> +		if (entry) {
> +			v4l2_info(sd, "tpg: %ux%u@%u/%u, code: %u, dt: %u, bpp: %u\n",
> +				  entry->width, entry->height,
> +				  entry->interval.numerator,
> +				  entry->interval.denominator,
> +				  entry->code, entry->dt,  entry->bpp);
> +		} else {
> +			v4l2_info(sd, "tpg: disabled\n");
> +		}
> +	}
> +	if (des->ops->log_status) {
> +		ret = des->ops->log_status(des);
> +		if (ret)
> +			return ret;
> +	}
> +	v4l2_info(sd, "\n");
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		v4l2_info(sd, "link: %u\n", link->index);
> +		v4l2_info(sd, "\tenabled: %u\n", link->enabled);
> +
> +		if (!link->enabled) {
> +			v4l2_info(sd, "\n");
> +			continue;
> +		}
> +
> +		v4l2_info(sd, "\tversion: %s\n", max_serdes_gmsl_version_str(link->version));
> +		v4l2_info(sd, "\tser_xlate: en: %u, src: 0x%02x dst: 0x%02x\n",
> +			  link->ser_xlate.en, link->ser_xlate.src,
> +			  link->ser_xlate.dst);
> +		v4l2_info(sd, "\n");
> +	}
> +
> +	for (i = 0; i < des->info->num_pipes; i++) {
> +		struct max_des_pipe *pipe = &des->pipes[i];
> +
> +		v4l2_info(sd, "pipe: %u\n", pipe->index);
> +		v4l2_info(sd, "\tenabled: %u\n", pipe->enabled);
> +		if (pipe->phy_id == des->info->num_phys ||
> +		    (priv->unused_phy && pipe->phy_id == priv->unused_phy->index))
> +			v4l2_info(sd, "\tphy_id: invalid\n");
> +		else
> +			v4l2_info(sd, "\tphy_id: %u\n", pipe->phy_id);
> +		v4l2_info(sd, "\tlink_id: %u\n", pipe->link_id);
> +		if (des->ops->set_pipe_stream_id)
> +			v4l2_info(sd, "\tstream_id: %u\n", pipe->stream_id);
> +		if (des->ops->set_pipe_mode) {
> +			v4l2_info(sd, "\tdbl8: %u\n", pipe->mode.dbl8);
> +			v4l2_info(sd, "\tdbl8mode: %u\n", pipe->mode.dbl8mode);
> +			v4l2_info(sd, "\tdbl10: %u\n", pipe->mode.dbl10);
> +			v4l2_info(sd, "\tdbl10mode: %u\n", pipe->mode.dbl10mode);
> +			v4l2_info(sd, "\tdbl12: %u\n", pipe->mode.dbl12);
> +		}
> +		if (des->ops->set_pipe_remap) {
> +			v4l2_info(sd, "\tremaps: %u\n", pipe->num_remaps);
> +			for (j = 0; j < pipe->num_remaps; j++) {
> +				struct max_des_remap *remap = &pipe->remaps[j];
> +
> +				v4l2_info(sd, "\t\tremap: from: vc: %u, dt: 0x%02x\n",
> +					  remap->from_vc, remap->from_dt);
> +				v4l2_info(sd, "\t\t       to:   vc: %u, dt: 0x%02x, phy: %u\n",
> +					  remap->to_vc, remap->to_dt, remap->phy);
> +			}
> +		}
> +		if (des->ops->set_pipe_vc_remap) {
> +			v4l2_info(sd, "\tvc_remaps: %u\n", pipe->num_vc_remaps);
> +			for (j = 0; j < pipe->num_vc_remaps; j++) {
> +				v4l2_info(sd, "\t\tvc_remap: src: %u, dst: %u\n",
> +					  pipe->vc_remaps[j].src, pipe->vc_remaps[j].dst);
> +			}
> +		}
> +		if (des->ops->log_pipe_status) {
> +			ret = des->ops->log_pipe_status(des, pipe);
> +			if (ret)
> +				return ret;
> +		}
> +		v4l2_info(sd, "\n");
> +	}
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		struct max_des_phy *phy = &des->phys[i];
> +
> +		v4l2_info(sd, "phy: %u\n", phy->index);
> +		v4l2_info(sd, "\tenabled: %u\n", phy->enabled);
> +
> +		if (!phy->enabled) {
> +			v4l2_info(sd, "\n");
> +			continue;
> +		}
> +
> +		v4l2_info(sd, "\tlink_frequency: %llu\n", phy->link_frequency);
> +		v4l2_info(sd, "\tnum_data_lanes: %u\n", phy->mipi.num_data_lanes);
> +		v4l2_info(sd, "\tclock_lane: %u\n", phy->mipi.clock_lane);
> +		if (des->ops->set_phy_mode) {
> +			v4l2_info(sd, "\talt_mem_map8: %u\n", phy->mode.alt_mem_map8);
> +			v4l2_info(sd, "\talt2_mem_map8: %u\n", phy->mode.alt2_mem_map8);
> +			v4l2_info(sd, "\talt_mem_map10: %u\n", phy->mode.alt_mem_map10);
> +			v4l2_info(sd, "\talt_mem_map12: %u\n", phy->mode.alt_mem_map12);
> +		}
> +		if (des->ops->log_phy_status) {
> +			ret = des->ops->log_phy_status(des, phy);
> +			if (ret)
> +				return ret;
> +		}
> +		v4l2_info(sd, "\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct max_des_priv *priv = ctrl_to_priv(ctrl->handler);
> +	struct max_des *des = priv->des;
> +
> +	switch (ctrl->id) {
> +	case V4L2_CID_TEST_PATTERN:
> +		des->tpg_pattern = ctrl->val;
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int max_des_get_frame_desc_state(struct v4l2_subdev *sd,
> +					struct v4l2_subdev_state *state,
> +					struct v4l2_mbus_frame_desc *fd,
> +					unsigned int pad)
> +{
> +	struct max_des_remap_context context = { 0 };
> +	struct max_des_priv *priv = sd_to_priv(sd);
> +	struct v4l2_subdev_route *route;
> +	int ret;
> +
> +	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
> +	fd->num_entries = 0;
> +
> +	ret = max_des_populate_remap_context(priv, &context, state);
> +	if (ret)
> +		return ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +		unsigned int dst_vc_id;
> +
> +		if (pad != route->source_pad)
> +			continue;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		ret = max_des_get_src_dst_vc_id(&context, hw.pipe->index, hw.phy->index,
> +						hw.entry.bus.csi2.vc, &dst_vc_id);
> +		if (ret)
> +			return ret;
> +
> +		hw.entry.bus.csi2.vc = dst_vc_id;
> +		hw.entry.stream = route->source_stream;
> +
> +		fd->entry[fd->num_entries++] = hw.entry;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
> +				  struct v4l2_mbus_frame_desc *fd)
> +{
> +	struct max_des_priv *priv = sd_to_priv(sd);
> +	struct v4l2_subdev_state *state;
> +	int ret;
> +
> +	state = v4l2_subdev_lock_and_get_active_state(&priv->sd);
> +
> +	ret = max_des_get_frame_desc_state(sd, state, fd, pad);
> +
> +	v4l2_subdev_unlock_state(state);
> +
> +	return ret;
> +}
> +
> +static int max_des_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
> +				   struct v4l2_mbus_config *cfg)
> +{
> +	struct max_des_priv *priv = sd_to_priv(sd);
> +	struct max_des *des = priv->des;
> +	struct max_des_phy *phy;
> +
> +	phy = max_des_pad_to_phy(des, pad);
> +	if (!phy)
> +		return -EINVAL;
> +
> +	cfg->type = phy->bus_type;
> +	cfg->bus.mipi_csi2 = phy->mipi;
> +	cfg->link_freq = phy->link_frequency;
> +
> +	return 0;
> +}
> +
> +static int max_des_set_tpg_routing(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_state *state,
> +				   struct v4l2_subdev_krouting *routing)
> +{
> +	struct max_des_priv *priv = sd_to_priv(sd);
> +	struct max_des *des = priv->des;
> +	const struct max_serdes_tpg_entry *entry;
> +	struct v4l2_mbus_framefmt fmt = { 0 };
> +	int ret;
> +
> +	ret = max_serdes_validate_tpg_routing(routing);
> +	if (ret)
> +		return ret;
> +
> +	entry = &des->info->tpg_entries.entries[0];
> +
> +	fmt.width = entry->width;
> +	fmt.height = entry->height;
> +	fmt.code = entry->code;
> +
> +	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt);
> +}
> +
> +static int __max_des_set_routing(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_state *state,
> +				 struct v4l2_subdev_krouting *routing)
> +{
> +	struct max_des_priv *priv = sd_to_priv(sd);
> +	struct max_des *des = priv->des;
> +	struct v4l2_subdev_route *route;
> +	bool is_tpg = false;
> +	int ret;
> +
> +	ret = v4l2_subdev_routing_validate(sd, routing,
> +					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
> +					   V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX);
> +	if (ret)
> +		return ret;
> +
> +	for_each_active_route(routing, route) {
> +		if (max_des_pad_is_tpg(des, route->sink_pad)) {
> +			is_tpg = true;
> +			break;
> +		}
> +	}
> +
> +	if (is_tpg)
> +		return max_des_set_tpg_routing(sd, state, routing);
> +
> +	static const struct v4l2_mbus_framefmt format = {
> +			.code = MEDIA_BUS_FMT_Y8_1X8,
> +			.field = V4L2_FIELD_NONE,
> +			.width = 640,
> +			.height = 480,
> +		};
> +
> +	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
> +}
> +
> +static int max_des_set_routing(struct v4l2_subdev *sd,
> +			       struct v4l2_subdev_state *state,
> +			       enum v4l2_subdev_format_whence which,
> +			       struct v4l2_subdev_krouting *routing)
> +{
> +	struct max_des_priv *priv = sd_to_priv(sd);
> +	struct max_des *des = priv->des;
> +
> +	if (which == V4L2_SUBDEV_FORMAT_ACTIVE && des->active)
> +		return -EBUSY;
> +
> +	return __max_des_set_routing(sd, state, routing);
> +}
> +
> +static int max_des_update_link(struct max_des_priv *priv,
> +			       struct max_des_remap_context *context,
> +			       struct max_des_link *link,
> +			       struct v4l2_subdev_state *state,
> +			       u64 *streams_masks)
> +{
> +	struct max_des *des = priv->des;
> +	struct max_des_pipe *pipe;
> +	int ret;
> +
> +	pipe = max_des_find_link_pipe(des, link);
> +	if (!pipe)
> +		return -ENOENT;
> +
> +	ret = max_des_update_pipe(priv, context, pipe, state, streams_masks);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int max_des_update_tpg(struct max_des_priv *priv,
> +			      struct v4l2_subdev_state *state,
> +			      u64 *streams_masks)
> +{
> +	const struct max_serdes_tpg_entry *entry = NULL;
> +	struct max_des *des = priv->des;
> +	struct v4l2_subdev_route *route;
> +	int ret;
> +
> +	for_each_active_route(&state->routing, route) {
> +		struct max_des_route_hw hw;
> +
> +		if (!(BIT_ULL(route->sink_stream) & streams_masks[route->sink_pad]))
> +			continue;
> +
> +		ret = max_des_route_to_hw(priv, state, route, &hw);
> +		if (ret)
> +			return ret;
> +
> +		if (!hw.is_tpg)
> +			continue;
> +
> +		entry = max_des_find_state_tpg_entry(des, state, route->sink_pad);
> +		break;
> +	}
> +
> +	if (entry == des->tpg_entry)
> +		return 0;
> +
> +	ret = des->ops->set_tpg(des, entry);
> +	if (ret)
> +		return ret;
> +
> +	des->tpg_entry = entry;
> +
> +	return 0;
> +}
> +
> +static int max_des_update_active(struct max_des_priv *priv, u64 *streams_masks,
> +				 bool expected_active)
> +{
> +	struct max_des *des = priv->des;
> +	bool active = false;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		struct max_des_phy *phy = &des->phys[i];
> +		u32 pad = max_des_phy_to_pad(des, phy);
> +
> +		if (streams_masks[pad]) {
> +			active = true;
> +			break;
> +		}
> +	}
> +
> +	if (active != expected_active || des->active == active)
> +		return 0;
> +
> +	if (des->ops->set_enable) {
> +		ret = des->ops->set_enable(des, active);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	des->active = active;
> +
> +	return 0;
> +}
> +
> +static int max_des_update_links(struct max_des_priv *priv,
> +				struct max_des_remap_context *context,
> +				struct v4l2_subdev_state *state,
> +				u64 *streams_masks)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int failed_update_link_id = des->info->num_links;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		ret = max_des_update_link(priv, context, link, state,
> +					  streams_masks);
> +		if (ret) {
> +			failed_update_link_id = i;
> +			goto err;
> +		}
> +	}
> +
> +	return 0;
> +
> +err:
> +	for (i = 0; i < failed_update_link_id; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		max_des_update_link(priv, context, link, state,
> +				    priv->streams_masks);
> +	}
> +
> +	return ret;
> +}
> +
> +static int max_des_enable_disable_streams(struct max_des_priv *priv,
> +					  struct v4l2_subdev_state *state,
> +					  u32 pad, u64 updated_streams_mask,
> +					  bool enable)
> +{
> +	struct max_des *des = priv->des;
> +
> +	return max_serdes_xlate_enable_disable_streams(priv->sources, 0, state,
> +						       pad, updated_streams_mask, 0,
> +						       des->info->num_links, enable);
> +}
> +
> +static int max_des_update_streams(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *state,
> +				  u32 pad, u64 updated_streams_mask, bool enable)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des_remap_context context = { 0 };
> +	struct max_des_mode_context mode_context = { 0 };
> +	struct max_des *des = priv->des;
> +	unsigned int num_pads = max_des_num_pads(des);
> +	u64 *streams_masks;
> +	int ret;
> +
> +	ret = max_des_populate_remap_context(priv, &context, state);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_populate_mode_context(priv, &mode_context, state, context.mode);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_serdes_get_streams_masks(priv->dev, state, pad, updated_streams_mask,
> +					   num_pads, priv->streams_masks, &streams_masks,
> +					   enable);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_set_pipes_phy(priv, &context);
> +	if (ret)
> +		goto err_free_streams_masks;
> +
> +	ret = max_des_set_tunnel(priv, &context);
> +	if (ret)
> +		goto err_free_streams_masks;
> +
> +	ret = max_des_set_modes(priv, &mode_context);
> +	if (ret)
> +		goto err_free_streams_masks;
> +
> +	ret = max_des_set_vc_remaps(priv, &context, state, streams_masks);
> +	if (ret)
> +		goto err_free_streams_masks;
> +
> +	ret = max_des_set_pipes_stream_id(priv);
> +	if (ret)
> +		goto err_free_streams_masks;
> +
> +	if (!enable) {
> +		ret = max_des_enable_disable_streams(priv, state, pad,
> +						     updated_streams_mask, enable);
> +		if (ret)
> +			goto err_free_streams_masks;
> +	}
> +
> +	ret = max_des_update_active(priv, streams_masks, false);
> +	if (ret)
> +		goto err_revert_streams_disable;
> +
> +	ret = max_des_update_links(priv, &context, state, streams_masks);
> +	if (ret)
> +		goto err_revert_active_disable;
> +
> +	ret = max_des_update_tpg(priv, state, streams_masks);
> +	if (ret)
> +		goto err_revert_links_update;
> +
> +	ret = max_des_update_active(priv, streams_masks, true);
> +	if (ret)
> +		goto err_revert_tpg_update;
> +
> +	if (enable) {
> +		ret = max_des_enable_disable_streams(priv, state, pad,
> +						     updated_streams_mask, enable);
> +		if (ret)
> +			goto err_revert_active_enable;
> +	}
> +
> +	devm_kfree(priv->dev, priv->streams_masks);
> +	priv->streams_masks = streams_masks;
> +
> +	return 0;
> +
> +err_revert_active_enable:
> +	max_des_update_active(priv, priv->streams_masks, false);
> +
> +err_revert_tpg_update:
> +	max_des_update_tpg(priv, state, priv->streams_masks);
> +
> +err_revert_links_update:
> +	max_des_update_links(priv, &context, state, priv->streams_masks);
> +
> +err_revert_active_disable:
> +	max_des_update_active(priv, priv->streams_masks, true);
> +
> +err_revert_streams_disable:
> +	if (!enable)
> +		max_des_enable_disable_streams(priv, state, pad,
> +					       updated_streams_mask, !enable);
> +
> +err_free_streams_masks:
> +	devm_kfree(priv->dev, streams_masks);
> +
> +	return ret;
> +}
> +
> +static int max_des_enable_streams(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_state *state,
> +				  u32 pad, u64 streams_mask)
> +{
> +	return max_des_update_streams(sd, state, pad, streams_mask, true);
> +}
> +
> +static int max_des_disable_streams(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_state *state,
> +				   u32 pad, u64 streams_mask)
> +{
> +	return max_des_update_streams(sd, state, pad, streams_mask, false);
> +}
> +
> +static int max_des_init_state(struct v4l2_subdev *sd,
> +			      struct v4l2_subdev_state *state)
> +{
> +	struct v4l2_subdev_route routes[MAX_DES_NUM_LINKS] = { 0 };
> +	struct v4l2_subdev_krouting routing = {
> +		.routes = routes,
> +	};
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +	struct max_des_phy *phy = NULL;
> +	unsigned int stream = 0;
> +	unsigned int i;
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		if (des->phys[i].enabled) {
> +			phy = &des->phys[i];
> +			break;
> +		}
> +	}
> +
> +	if (!phy)
> +		return 0;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		routing.routes[routing.num_routes++] = (struct v4l2_subdev_route) {
> +			.sink_pad = max_des_link_to_pad(des, link),
> +			.sink_stream = 0,
> +			.source_pad = max_des_phy_to_pad(des, phy),
> +			.source_stream = stream,
> +			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
> +		};
> +		stream++;
> +
> +		/*
> +		 * The Streams API is an experimental feature.
> +		 * If multiple routes are provided here, userspace will not be
> +		 * able to configure them unless the Streams API is enabled.
> +		 * Provide a single route until it is enabled.
> +		 */
> +		break;
> +	}
> +
> +	return __max_des_set_routing(sd, state, &routing);
> +}
> +
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +static int max_des_g_register(struct v4l2_subdev *sd,
> +			      struct v4l2_dbg_register *reg)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +	unsigned int val;
> +	int ret;
> +
> +	ret = des->ops->reg_read(des, reg->reg, &val);
> +	if (ret)
> +		return ret;
> +
> +	reg->val = val;
> +	reg->size = 1;
> +
> +	return 0;
> +}
> +
> +static int max_des_s_register(struct v4l2_subdev *sd,
> +			      const struct v4l2_dbg_register *reg)
> +{
> +	struct max_des_priv *priv = v4l2_get_subdevdata(sd);
> +	struct max_des *des = priv->des;
> +
> +	return des->ops->reg_write(des, reg->reg, reg->val);
> +}
> +#endif
> +
> +static const struct v4l2_subdev_core_ops max_des_core_ops = {
> +	.log_status = max_des_log_status,
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.g_register = max_des_g_register,
> +	.s_register = max_des_s_register,
> +#endif
> +};
> +
> +static const struct v4l2_ctrl_ops max_des_ctrl_ops = {
> +	.s_ctrl = max_des_s_ctrl,
> +};
> +
> +static const struct v4l2_subdev_pad_ops max_des_pad_ops = {
> +	.enable_streams = max_des_enable_streams,
> +	.disable_streams = max_des_disable_streams,
> +
> +	.set_routing = max_des_set_routing,
> +	.get_frame_desc = max_des_get_frame_desc,
> +
> +	.get_mbus_config = max_des_get_mbus_config,
> +
> +	.get_fmt = v4l2_subdev_get_fmt,
> +	.set_fmt = max_des_set_fmt,
> +
> +	.enum_frame_interval = max_des_enum_frame_interval,
> +	.get_frame_interval = max_des_get_frame_interval,
> +	.set_frame_interval = max_des_set_frame_interval,
> +};
> +
> +static const struct v4l2_subdev_ops max_des_subdev_ops = {
> +	.core = &max_des_core_ops,
> +	.pad = &max_des_pad_ops,
> +};
> +
> +static const struct v4l2_subdev_internal_ops max_des_internal_ops = {
> +	.init_state = &max_des_init_state,
> +};
> +
> +static const struct media_entity_operations max_des_media_ops = {
> +	.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
> +	.has_pad_interdep = v4l2_subdev_has_pad_interdep,
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +static int max_des_notify_bound(struct v4l2_async_notifier *nf,
> +				struct v4l2_subdev *subdev,
> +				struct v4l2_async_connection *base_asc)
> +{
> +	struct max_des_priv *priv = nf_to_priv(nf);
> +	struct max_serdes_asc *asc = asc_to_max(base_asc);
> +	struct max_serdes_source *source = asc->source;
> +	struct max_des *des = priv->des;
> +	struct max_des_link *link = &des->links[source->index];
> +	u32 pad = max_des_link_to_pad(des, link);
> +	int ret;
> +
> +	ret = media_entity_get_fwnode_pad(&subdev->entity,
> +					  source->ep_fwnode,
> +					  MEDIA_PAD_FL_SOURCE);
> +	if (ret < 0) {
> +		dev_err(priv->dev, "Failed to find pad for %s\n", subdev->name);
> +		return ret;
> +	}
> +
> +	source->sd = subdev;
> +	source->pad = ret;
> +
> +	ret = media_create_pad_link(&source->sd->entity, source->pad,
> +				    &priv->sd.entity, pad,
> +				    MEDIA_LNK_FL_ENABLED |
> +				    MEDIA_LNK_FL_IMMUTABLE);
> +	if (ret) {
> +		dev_err(priv->dev, "Unable to link %s:%u -> %s:%u\n",
> +			source->sd->name, source->pad, priv->sd.name, pad);
> +		source->sd = NULL;
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void max_des_notify_unbind(struct v4l2_async_notifier *nf,
> +				  struct v4l2_subdev *subdev,
> +				  struct v4l2_async_connection *base_asc)
> +{
> +	struct max_serdes_asc *asc = asc_to_max(base_asc);
> +	struct max_serdes_source *source = asc->source;
> +
> +	source->sd = NULL;
> +}
> +
> +static const struct v4l2_async_notifier_operations max_des_notify_ops = {
> +	.bound = max_des_notify_bound,
> +	.unbind = max_des_notify_unbind,
> +};
> +
> +static int max_des_v4l2_notifier_register(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	v4l2_async_subdev_nf_init(&priv->nf, &priv->sd);
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +		struct max_serdes_source *source;
> +		struct max_serdes_asc *asc;
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		source = max_des_get_link_source(priv, link);
> +		if (!source->ep_fwnode)
> +			continue;
> +
> +		asc = v4l2_async_nf_add_fwnode(&priv->nf, source->ep_fwnode,
> +					       struct max_serdes_asc);
> +		if (IS_ERR(asc)) {
> +			dev_err(priv->dev,
> +				"Failed to add subdev for source %u: %pe", i,
> +				asc);
> +
> +			v4l2_async_nf_cleanup(&priv->nf);
> +
> +			return PTR_ERR(asc);
> +		}
> +
> +		asc->source = source;
> +	}
> +
> +	priv->nf.ops = &max_des_notify_ops;
> +
> +	ret = v4l2_async_nf_register(&priv->nf);
> +	if (ret) {
> +		dev_err(priv->dev, "Failed to register subdev notifier");
> +		v4l2_async_nf_cleanup(&priv->nf);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void max_des_v4l2_notifier_unregister(struct max_des_priv *priv)
> +{
> +	v4l2_async_nf_unregister(&priv->nf);
> +	v4l2_async_nf_cleanup(&priv->nf);
> +}
> +
> +static int max_des_v4l2_register(struct max_des_priv *priv)
> +{
> +	struct v4l2_subdev *sd = &priv->sd;
> +	struct max_des *des = priv->des;
> +	void *data = i2c_get_clientdata(priv->client);
> +	unsigned int num_pads = max_des_num_pads(des);
> +	unsigned int i;
> +	int ret;
> +
> +	v4l2_i2c_subdev_init(sd, priv->client, &max_des_subdev_ops);
> +	i2c_set_clientdata(priv->client, data);
> +	sd->internal_ops = &max_des_internal_ops;
> +	sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> +	sd->entity.ops = &max_des_media_ops;
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
> +
> +	for (i = 0; i < num_pads; i++) {
> +		if (max_des_pad_is_sink(des, i))
> +			priv->pads[i].flags = MEDIA_PAD_FL_SINK;
> +		else if (max_des_pad_is_source(des, i))
> +			priv->pads[i].flags = MEDIA_PAD_FL_SOURCE;
> +		else if (max_des_pad_is_tpg(des, i))
> +			priv->pads[i].flags = MEDIA_PAD_FL_SINK |
> +					      MEDIA_PAD_FL_INTERNAL;
> +		else
> +			return -EINVAL;
> +	}
> +
> +	v4l2_set_subdevdata(sd, priv);
> +
> +	if (des->info->tpg_patterns) {
> +		v4l2_ctrl_handler_init(&priv->ctrl_handler, 1);
> +		priv->sd.ctrl_handler = &priv->ctrl_handler;
> +
> +		v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,
> +					     &max_des_ctrl_ops,
> +					     V4L2_CID_TEST_PATTERN,
> +					     MAX_SERDES_TPG_PATTERN_MAX,
> +					     ~des->info->tpg_patterns,
> +					     __ffs(des->info->tpg_patterns),
> +					     max_serdes_tpg_patterns);
> +		if (priv->ctrl_handler.error) {
> +			ret = priv->ctrl_handler.error;
> +			goto err_free_ctrl;
> +		}
> +	}
> +
> +	ret = media_entity_pads_init(&sd->entity, num_pads, priv->pads);
> +	if (ret)
> +		goto err_free_ctrl;
> +
> +	ret = max_des_v4l2_notifier_register(priv);
> +	if (ret)
> +		goto err_media_entity_cleanup;
> +
> +	ret = v4l2_subdev_init_finalize(sd);
> +	if (ret)
> +		goto err_nf_cleanup;
> +
> +	ret = v4l2_async_register_subdev(sd);
> +	if (ret)
> +		goto err_sd_cleanup;
> +
> +	return 0;
> +
> +err_sd_cleanup:
> +	v4l2_subdev_cleanup(sd);
> +err_nf_cleanup:
> +	max_des_v4l2_notifier_unregister(priv);
> +err_media_entity_cleanup:
> +	media_entity_cleanup(&sd->entity);
> +err_free_ctrl:
> +	v4l2_ctrl_handler_free(&priv->ctrl_handler);
> +
> +	return ret;
> +}
> +
> +static void max_des_v4l2_unregister(struct max_des_priv *priv)
> +{
> +	struct v4l2_subdev *sd = &priv->sd;
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +
> +	v4l2_async_unregister_subdev(sd);
> +	v4l2_subdev_cleanup(sd);
> +	max_des_v4l2_notifier_unregister(priv);
> +	media_entity_cleanup(&sd->entity);
> +	v4l2_ctrl_handler_free(&priv->ctrl_handler);
> +
> +	for (i = 0; i < des->info->num_links; i++)
> +		fwnode_handle_put(priv->sources[i].ep_fwnode);
> +}
> +
> +static int max_des_update_pocs(struct max_des_priv *priv, bool enable)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		if (!priv->pocs[i])
> +			continue;
> +
> +		if (enable)
> +			ret = regulator_enable(priv->pocs[i]);
> +		else
> +			ret = regulator_disable(priv->pocs[i]);
> +
> +		if (ret) {
> +			dev_err(priv->dev,
> +				"Failed to set POC supply to %u: %u\n",
> +				enable, ret);
> +			if (!enable)
> +				return ret;
> +			goto err_rollback;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_rollback:
> +	while (i--) {
> +		struct max_des_link *link = &des->links[i];
> +
> +		if (!link->enabled)
> +			continue;
> +
> +		if (!priv->pocs[i])
> +			continue;
> +
> +		regulator_disable(priv->pocs[i]);
> +	}
> +
> +	return ret;
> +}
> +
> +static int max_des_parse_sink_dt_endpoint(struct max_des_priv *priv,
> +					  struct max_des_link *link,
> +					  struct max_serdes_source *source,
> +					  struct fwnode_handle *fwnode)
> +{
> +	struct max_des *des = priv->des;
> +	u32 pad = max_des_link_to_pad(des, link);
> +	unsigned int index = link->index;
> +	struct fwnode_handle *ep;
> +	char poc_name[10];
> +	int ret;
> +
> +	ep = fwnode_graph_get_endpoint_by_id(fwnode, pad, 0, 0);
> +	if (!ep)
> +		return 0;
> +
> +	source->ep_fwnode = fwnode_graph_get_remote_endpoint(ep);
> +	fwnode_handle_put(ep);
> +	if (!source->ep_fwnode) {
> +		dev_err(priv->dev,
> +			"Failed to get remote endpoint on port %u\n", pad);
> +		return -ENODEV;
> +	}
> +
> +	snprintf(poc_name, sizeof(poc_name), "port%u-poc", index);
> +	priv->pocs[index] = devm_regulator_get_optional(priv->dev, poc_name);
> +	if (IS_ERR(priv->pocs[index])) {
> +		ret = PTR_ERR(priv->pocs[index]);
> +		if (ret != -ENODEV) {
> +			dev_err(priv->dev,
> +				"Failed to get POC supply on port %u: %d\n",
> +				index, ret);
> +			goto err_put_source_ep_fwnode;
> +		}
> +
> +		priv->pocs[index] = NULL;
> +	}
> +
> +	link->enabled = true;
> +
> +	return 0;
> +
> +err_put_source_ep_fwnode:
> +	fwnode_handle_put(source->ep_fwnode);
> +
> +	return ret;
> +}
> +
> +static int max_des_parse_src_dt_endpoint(struct max_des_priv *priv,
> +					 struct max_des_phy *phy,
> +					 struct fwnode_handle *fwnode)
> +{
> +	struct max_des *des = priv->des;
> +	u32 pad = max_des_phy_to_pad(des, phy);
> +	struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_UNKNOWN };
> +	struct v4l2_mbus_config_mipi_csi2 *mipi = &v4l2_ep.bus.mipi_csi2;
> +	enum v4l2_mbus_type bus_type;
> +	struct fwnode_handle *ep;
> +	u64 link_frequency;
> +	unsigned int i;
> +	int ret;
> +
> +	ep = fwnode_graph_get_endpoint_by_id(fwnode, pad, 0, 0);
> +	if (!ep)
> +		return 0;
> +
> +	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &v4l2_ep);
> +	fwnode_handle_put(ep);
> +	if (ret) {
> +		dev_err(priv->dev, "Could not parse endpoint on port %u\n", pad);
> +		return ret;
> +	}
> +
> +	bus_type = v4l2_ep.bus_type;
> +	if (bus_type != V4L2_MBUS_CSI2_DPHY &&
> +	    bus_type != V4L2_MBUS_CSI2_CPHY) {
> +		v4l2_fwnode_endpoint_free(&v4l2_ep);
> +		dev_err(priv->dev, "Unsupported bus-type %u on port %u\n",
> +			pad, bus_type);
> +		return -EINVAL;
> +	}
> +
> +	if (v4l2_ep.nr_of_link_frequencies == 0)
> +		link_frequency = MAX_DES_LINK_FREQUENCY_DEFAULT;
> +	else if (v4l2_ep.nr_of_link_frequencies == 1)
> +		link_frequency = v4l2_ep.link_frequencies[0];
> +	else
> +		ret = -EINVAL;
> +
> +	v4l2_fwnode_endpoint_free(&v4l2_ep);
> +
> +	if (ret) {
> +		dev_err(priv->dev, "Invalid link frequencies %u on port %u\n",
> +			v4l2_ep.nr_of_link_frequencies, pad);
> +		return -EINVAL;
> +	}
> +
> +	if (link_frequency < MAX_DES_LINK_FREQUENCY_MIN ||
> +	    link_frequency > MAX_DES_LINK_FREQUENCY_MAX) {
> +		dev_err(priv->dev, "Invalid link frequency %llu on port %u\n",
> +			link_frequency, pad);
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < mipi->num_data_lanes; i++) {
> +		if (mipi->data_lanes[i] > mipi->num_data_lanes) {
> +			dev_err(priv->dev, "Invalid data lane %u on port %u\n",
> +				mipi->data_lanes[i], pad);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	phy->bus_type = bus_type;
> +	phy->mipi = *mipi;
> +	phy->link_frequency = link_frequency;
> +	phy->enabled = true;
> +
> +	return 0;
> +}
> +
> +int max_des_phy_hw_data_lanes(struct max_des *des, struct max_des_phy *phy)
> +{
> +	const struct max_serdes_phys_configs *configs = &des->info->phys_configs;
> +	const struct max_serdes_phys_config *config =
> +		&configs->configs[des->phys_config];
> +
> +	return config->lanes[phy->index];
> +}
> +EXPORT_SYMBOL_NS_GPL(max_des_phy_hw_data_lanes, "MAX_SERDES");
> +
> +static int max_des_find_phys_config(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	const struct max_serdes_phys_configs *configs = &des->info->phys_configs;
> +	struct max_des_phy *phy;
> +	unsigned int i, j;
> +
> +	if (!configs->num_configs)
> +		return 0;
> +
> +	for (i = 0; i < configs->num_configs; i++) {
> +		const struct max_serdes_phys_config *config = &configs->configs[i];
> +		bool matching = true;
> +
> +		for (j = 0; j < des->info->num_phys; j++) {
> +			phy = &des->phys[j];
> +
> +			if (!phy->enabled)
> +				continue;
> +
> +			if (phy->mipi.num_data_lanes <= config->lanes[j] &&
> +			    phy->mipi.clock_lane == config->clock_lane[j])
> +				continue;
> +
> +			matching = false;
> +
> +			break;
> +		}
> +
> +		if (matching)
> +			break;
> +	}
> +
> +	if (i == configs->num_configs) {
> +		dev_err(priv->dev, "Invalid lane configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	des->phys_config = i;
> +
> +	return 0;
> +}
> +
> +static int max_des_parse_dt(struct max_des_priv *priv)
> +{
> +	struct fwnode_handle *fwnode = dev_fwnode(priv->dev);
> +	struct max_des *des = priv->des;
> +	struct max_des_link *link;
> +	struct max_des_pipe *pipe;
> +	struct max_des_phy *phy;
> +	unsigned int i;
> +	int ret;
> +
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		phy = &des->phys[i];
> +		phy->index = i;
> +
> +		ret = max_des_parse_src_dt_endpoint(priv, phy, fwnode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = max_des_find_phys_config(priv);
> +	if (ret)
> +		return ret;
> +
> +	/* Find an unsed PHY to send unampped data to. */

WARNING: 'unsed' may be misspelled - perhaps 'unused'?
#3101: FILE: drivers/media/i2c/maxim-serdes/max_des.c:3053:
+	/* Find an unsed PHY to send unampped data to. */
                   ^^^^^
> +	for (i = 0; i < des->info->num_phys; i++) {
> +		phy = &des->phys[i];
> +
> +		if (!phy->enabled) {
> +			priv->unused_phy = phy;
> +			break;
> +		}
> +	}
> +
> +	for (i = 0; i < des->info->num_pipes; i++) {
> +		pipe = &des->pipes[i];
> +		pipe->index = i;
> +
> +		/*
> +		 * Serializers can send data on different stream ids over the
> +		 * same link, and some deserializers support stream id autoselect
> +		 * allowing them to receive data from all stream ids.
> +		 * Deserializers that support that feature should enable it.
> +		 * Deserializers that support per-link stream ids do not need
> +		 * to assign unique stream ids to each serializer.
> +		 */
> +		if (des->info->needs_unique_stream_id)
> +			pipe->stream_id = i;
> +		else
> +			pipe->stream_id = 0;
> +
> +		/*
> +		 * We already checked that num_pipes >= num_links.
> +		 * Set up pipe to receive data from the link with the same index.
> +		 * This is already the default for most chips, and some of them
> +		 * don't even support receiving pipe data from a different link.
> +		 */
> +		pipe->link_id = i % des->info->num_links;
> +	}
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		link = &des->links[i];
> +		link->index = i;
> +	}
> +
> +	for (i = 0; i < des->info->num_links; i++) {
> +		struct max_des_link *link = &des->links[i];
> +		struct max_serdes_source *source;
> +
> +		source = max_des_get_link_source(priv, link);
> +		source->index = i;
> +
> +		ret = max_des_parse_sink_dt_endpoint(priv, link, source, fwnode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int max_des_allocate(struct max_des_priv *priv)
> +{
> +	struct max_des *des = priv->des;
> +	unsigned int num_pads = max_des_num_pads(des);
> +
> +	des->phys = devm_kcalloc(priv->dev, des->info->num_phys,
> +				 sizeof(*des->phys), GFP_KERNEL);
> +	if (!des->phys)
> +		return -ENOMEM;
> +
> +	des->pipes = devm_kcalloc(priv->dev, des->info->num_pipes,
> +				  sizeof(*des->pipes), GFP_KERNEL);
> +	if (!des->pipes)
> +		return -ENOMEM;
> +
> +	des->links = devm_kcalloc(priv->dev, des->info->num_links,
> +				  sizeof(*des->links), GFP_KERNEL);
> +	if (!des->links)
> +		return -ENOMEM;
> +
> +	priv->sources = devm_kcalloc(priv->dev, des->info->num_links,
> +				     sizeof(*priv->sources), GFP_KERNEL);
> +	if (!priv->sources)
> +		return -ENOMEM;
> +
> +	priv->pocs = devm_kcalloc(priv->dev, des->info->num_links,
> +				  sizeof(*priv->pocs), GFP_KERNEL);
> +	if (!priv->pocs)
> +		return -ENOMEM;
> +
> +	priv->pads = devm_kcalloc(priv->dev, num_pads,
> +				  sizeof(*priv->pads), GFP_KERNEL);
> +	if (!priv->pads)
> +		return -ENOMEM;
> +
> +	priv->streams_masks = devm_kcalloc(priv->dev, num_pads,
> +					   sizeof(*priv->streams_masks),
> +					   GFP_KERNEL);
> +	if (!priv->streams_masks)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +int max_des_probe(struct i2c_client *client, struct max_des *des)
> +{
> +	struct device *dev = &client->dev;
> +	struct max_des_priv *priv;
> +	int ret;
> +
> +	if (des->info->num_phys > MAX_DES_NUM_PHYS)
> +		return -E2BIG;
> +
> +	if (des->info->num_pipes > MAX_DES_NUM_PIPES)
> +		return -E2BIG;
> +
> +	if (des->info->num_links > MAX_DES_NUM_LINKS)
> +		return -E2BIG;
> +
> +	if (des->info->num_links > des->info->num_pipes)
> +		return -E2BIG;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	if (des->ops->set_link_version && !des->ops->select_links) {
> +		dev_err(dev,
> +			"Cannot implement .select_link_version() without .select_links()\n");
> +		return -EINVAL;
> +	}
> +
> +	if (hweight_long(des->info->versions) >= 1 &&
> +	    !des->ops->set_link_version) {
> +		dev_err(dev, "Multiple version without .select_link_version()\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->client = client;
> +	priv->dev = dev;
> +	priv->des = des;
> +	des->priv = priv;
> +
> +	ret = max_des_allocate(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_parse_dt(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_update_pocs(priv, true);
> +	if (ret)
> +		return ret;
> +
> +	ret = max_des_i2c_adapter_init(priv);
> +	if (ret)
> +		goto err_disable_pocs;
> +
> +	ret = max_des_v4l2_register(priv);
> +	if (ret)
> +		goto err_i2c_adapter_deinit;
> +
> +	return 0;
> +
> +err_i2c_adapter_deinit:
> +	max_des_i2c_adapter_deinit(priv);
> +
> +err_disable_pocs:
> +	max_des_update_pocs(priv, false);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_NS_GPL(max_des_probe, "MAX_SERDES");
> +
> +int max_des_remove(struct max_des *des)
> +{
> +	struct max_des_priv *priv = des->priv;
> +
> +	max_des_v4l2_unregister(priv);
> +
> +	max_des_i2c_adapter_deinit(priv);
> +
> +	max_des_update_pocs(priv, false);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(max_des_remove, "MAX_SERDES");
> +
> +MODULE_LICENSE("GPL");
> +MODULE_IMPORT_NS("I2C_ATR");
> diff --git a/drivers/media/i2c/maxim-serdes/max_des.h b/drivers/media/i2c/maxim-serdes/max_des.h
> new file mode 100644
> index 000000000000..3ad8246b1981
> --- /dev/null
> +++ b/drivers/media/i2c/maxim-serdes/max_des.h
> @@ -0,0 +1,157 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2025 Analog Devices Inc.
> + */
> +
> +#ifndef MAX_DES_H
> +#define MAX_DES_H
> +
> +#include <media/v4l2-mediabus.h>
> +
> +#include "max_serdes.h"
> +
> +#define MAX_DES_DT_VC(dt, vc) (((vc) & 0x3) << 6 | ((dt) & 0x3f))
> +
> +struct max_des_remap {
> +	u8 from_dt;
> +	u8 from_vc;
> +	u8 to_dt;
> +	u8 to_vc;
> +	u8 phy;
> +};
> +
> +struct max_des_link {
> +	unsigned int index;
> +	bool enabled;
> +	enum max_serdes_gmsl_version version;
> +	struct max_serdes_i2c_xlate ser_xlate;
> +	struct i2c_adapter *adapter;
> +};
> +
> +struct max_des_pipe_mode {
> +	bool dbl8;
> +	bool dbl10;
> +	bool dbl12;
> +	bool dbl8mode;
> +	bool dbl10mode;
> +};
> +
> +struct max_des_pipe {
> +	unsigned int index;
> +	unsigned int stream_id;
> +	unsigned int link_id;
> +	unsigned int phy_id;
> +	struct max_des_remap *remaps;
> +	unsigned int num_remaps;
> +	struct max_serdes_vc_remap *vc_remaps;
> +	unsigned int num_vc_remaps;
> +	struct max_des_pipe_mode mode;
> +	bool enabled;
> +};
> +
> +struct max_des_phy_mode {
> +	bool alt_mem_map8;
> +	bool alt2_mem_map8;
> +	bool alt_mem_map10;
> +	bool alt_mem_map12;
> +};
> +
> +struct max_des_phy {
> +	unsigned int index;
> +	u64 link_frequency;
> +	struct v4l2_mbus_config_mipi_csi2 mipi;
> +	enum v4l2_mbus_type bus_type;
> +	struct max_des_phy_mode mode;
> +	bool enabled;
> +};
> +
> +struct max_des;
> +
> +struct max_des_info {
> +	unsigned int num_phys;
> +	unsigned int num_pipes;
> +	unsigned int num_links;
> +	unsigned int num_remaps_per_pipe;
> +	unsigned int versions;
> +	unsigned int modes;
> +	bool fix_tx_ids;
> +	bool use_atr;
> +	bool needs_single_link_version;
> +	bool needs_unique_stream_id;
> +
> +	struct max_serdes_phys_configs phys_configs;
> +	struct max_serdes_tpg_entries tpg_entries;
> +	enum max_serdes_gmsl_mode tpg_mode;
> +	unsigned int tpg_patterns;
> +};
> +
> +struct max_des_ops {
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	int (*reg_read)(struct max_des *des, unsigned int reg, unsigned int *val);
> +	int (*reg_write)(struct max_des *des, unsigned int reg, unsigned int val);
> +#endif
> +	int (*log_status)(struct max_des *des);
> +	int (*log_pipe_status)(struct max_des *des, struct max_des_pipe *pipe);
> +	int (*log_phy_status)(struct max_des *des, struct max_des_phy *phy);
> +	int (*set_enable)(struct max_des *des, bool enable);
> +	int (*set_tpg)(struct max_des *des, const struct max_serdes_tpg_entry *entry);
> +	int (*init)(struct max_des *des);
> +	int (*init_phy)(struct max_des *des, struct max_des_phy *phy);
> +	int (*set_phy_mode)(struct max_des *des, struct max_des_phy *phy,
> +			    struct max_des_phy_mode *mode);
> +	int (*set_phy_enable)(struct max_des *des, struct max_des_phy *phy,
> +			      bool active);
> +	int (*set_pipe_stream_id)(struct max_des *des, struct max_des_pipe *pipe,
> +				  unsigned int stream_id);
> +	int (*set_pipe_link)(struct max_des *des, struct max_des_pipe *pipe,
> +			     struct max_des_link *link);
> +	int (*set_pipe_phy)(struct max_des *des, struct max_des_pipe *pipe,
> +			    struct max_des_phy *phy);
> +	int (*set_pipe_tunnel_phy)(struct max_des *des, struct max_des_pipe *pipe,
> +				   struct max_des_phy *phy);
> +	int (*set_pipe_enable)(struct max_des *des, struct max_des_pipe *pipe,
> +			       bool enable);
> +	int (*set_pipe_remap)(struct max_des *des, struct max_des_pipe *pipe,
> +			      unsigned int i, struct max_des_remap *remap);
> +	int (*set_pipe_remaps_enable)(struct max_des *des, struct max_des_pipe *pipe,
> +				      unsigned int mask);
> +	int (*set_pipe_vc_remap)(struct max_des *des, struct max_des_pipe *pipe,
> +				 unsigned int i, struct max_serdes_vc_remap *vc_remap);
> +	int (*set_pipe_vc_remaps_enable)(struct max_des *des, struct max_des_pipe *pipe,
> +					 unsigned int mask);
> +	int (*set_pipe_mode)(struct max_des *des, struct max_des_pipe *pipe,
> +			     struct max_des_pipe_mode *mode);
> +	int (*set_pipe_tunnel_enable)(struct max_des *des, struct max_des_pipe *pipe,
> +				      bool enable);
> +	int (*init_link)(struct max_des *des, struct max_des_link *link);
> +	int (*select_links)(struct max_des *des, unsigned int mask);
> +	int (*set_link_version)(struct max_des *des, struct max_des_link *link,
> +				enum max_serdes_gmsl_version version);
> +};
> +
> +struct max_des_priv;
> +
> +struct max_des {
> +	struct max_des_priv *priv;
> +
> +	const struct max_des_info *info;
> +	const struct max_des_ops *ops;
> +
> +	struct max_des_phy *phys;
> +	struct max_des_pipe *pipes;
> +	struct max_des_link *links;
> +	const struct max_serdes_tpg_entry *tpg_entry;
> +	enum max_serdes_tpg_pattern tpg_pattern;
> +
> +	unsigned int phys_config;
> +	enum max_serdes_gmsl_mode mode;
> +	bool active;
> +};
> +
> +int max_des_probe(struct i2c_client *client, struct max_des *des);
> +
> +int max_des_remove(struct max_des *des);
> +
> +int max_des_phy_hw_data_lanes(struct max_des *des, struct max_des_phy *phy);
> +
> +#endif // MAX_DES_H
> 
> -- 
> 2.53.0
> 
> 

-- 
Kind Regards,
Niklas Söderlund

^ permalink raw reply

* Re: [PATCH v13 16/22] media: i2c: maxim-serdes: add MAX96717 driver
From: Niklas Söderlund @ 2026-06-10 14:39 UTC (permalink / raw)
  To: dumitru.ceclan
  Cc: Tomi Valkeinen, Mauro Carvalho Chehab, Sakari Ailus,
	Laurent Pinchart, Julien Massot, Rob Herring, Greg Kroah-Hartman,
	mitrutzceclan, linux-media, linux-kernel, devicetree,
	linux-staging, linux-gpio, Martin Hecht, Cosmin Tanislav
In-Reply-To: <20260604-gmsl2-3_serdes-v13-16-9d8a4919983b@analog.com>

On 2026-06-04 17:14:03 +0300, Dumitru Ceclan via B4 Relay wrote:
> From: Cosmin Tanislav <demonsingur@gmail.com>
> 
> 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@gmail.com>
> ---
>  drivers/media/i2c/maxim-serdes/Kconfig    |   19 +
>  drivers/media/i2c/maxim-serdes/Makefile   |    1 +
>  drivers/media/i2c/maxim-serdes/max96717.c | 1688 +++++++++++++++++++++++++++++
>  3 files changed, 1708 insertions(+)
> 
> diff --git a/drivers/media/i2c/maxim-serdes/Kconfig b/drivers/media/i2c/maxim-serdes/Kconfig
> index f5a4ca80a263..c811790c09b9 100644
> --- a/drivers/media/i2c/maxim-serdes/Kconfig
> +++ b/drivers/media/i2c/maxim-serdes/Kconfig
> @@ -15,3 +15,22 @@ 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
> +	depends on I2C
> +	depends on PINCTRL
> +	depends on VIDEO_DEV
> +	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..6cc4060e10f3
> --- /dev/null
> +++ b/drivers/media/i2c/maxim-serdes/max96717.c
> @@ -0,0 +1,1688 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Maxim MAX96717 GMSL2 Serializer Driver
> + *
> + * Copyright (C) 2025 Analog Devices Inc.
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/iopoll.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 val;
> +	int ret, err;
> +
> +	err = read_poll_timeout(regmap_read, ret,
> +				!ret && val,
> +				100 * USEC_PER_MSEC,
> +				1 * USEC_PER_SEC, false,
> +				priv->regmap, MAX96717_REG0, &val);
> +	if (err)
> +		dev_err(priv->dev, "Timeout waiting for serializer: %d\n", ret);
> +
> +	return err;
> +}
> +
> +#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_LEVEL:
> +		*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;

Is it possible to use EOPNOTSUPP? Same below.

> +	}
> +}
> +
> +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, &reg, &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:
> +		ret = regmap_read(priv->regmap, reg, &val);
> +		if (ret)
> +			return ret;
> +
> +		val = field_get(mask, val) == en_val;
> +		if (!val)
> +			return -EINVAL;
> +
> +		break;
> +	case PIN_CONFIG_OUTPUT_ENABLE:
> +	case PIN_CONFIG_INPUT_ENABLE:
> +	case MAX96717_PINCTRL_PULL_STRENGTH_HIGH:
> +	case MAX96717_PINCTRL_INPUT_VALUE:
> +	case PIN_CONFIG_LEVEL:
> +		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, &reg, &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_LEVEL:
> +		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_LEVEL:
> +		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_LEVEL, 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_LEVEL, 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 __maybe_unused 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 __maybe_unused 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),
> +#ifdef CONFIG_VIDEO_ADV_DEBUG
> +	.reg_read = max96717_reg_read,
> +	.reg_write = max96717_reg_write,
> +#endif
> +	.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 int max96717_clk_determine_rate(struct clk_hw *hw,
> +				       struct clk_rate_request *req)
> +{
> +	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, req->rate);
> +
> +	if (req->rate != max96717_predef_freqs[idx].freq) {
> +		dev_dbg(dev, "Request CLK freq:%lu, found CLK freq:%lu\n",
> +			req->rate, max96717_predef_freqs[idx].freq);
> +	}
> +
> +	req->rate = max96717_predef_freqs[idx].freq;
> +
> +	return 0;
> +}
> +
> +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,
> +	.determine_rate = max96717_clk_determine_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 = 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_IMPORT_NS("MAX_SERDES");
> +MODULE_DESCRIPTION("MAX96717 GMSL2 Serializer Driver");
> +MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>");
> +MODULE_LICENSE("GPL");
> 
> -- 
> 2.53.0
> 
> 

-- 
Kind Regards,
Niklas Söderlund

^ permalink raw reply

* Re: [PATCH v13 17/22] media: i2c: maxim-serdes: add MAX96724 driver
From: Niklas Söderlund @ 2026-06-10 14:42 UTC (permalink / raw)
  To: dumitru.ceclan
  Cc: Tomi Valkeinen, Mauro Carvalho Chehab, Sakari Ailus,
	Laurent Pinchart, Julien Massot, Rob Herring, Greg Kroah-Hartman,
	mitrutzceclan, linux-media, linux-kernel, devicetree,
	linux-staging, linux-gpio, Martin Hecht, Cosmin Tanislav,
	Cory Keitz
In-Reply-To: <20260604-gmsl2-3_serdes-v13-17-9d8a4919983b@analog.com>

Hi,

Thanks for your work.

This patch gives me new compiler warnings, can they be avoided?

  .../max96724.c:402 max96724_log_phy_status() warn: subtract is higher precedence than shift
  .../max96724.c:409 max96724_log_phy_status() warn: subtract is higher precedence than shift
  .../max96724.c:588 max96724_init_phy() warn: subtract is higher precedence than shift
  .../max96724.c:756 max96724_set_pipe_remap() warn: subtract is higher precedence than shift
  .../max96724.c:796 max96724_set_pipe_phy() warn: subtract is higher precedence than shift
  .../max96724.c:818 max96724_set_pipe_stream_id() warn: subtract is higher precedence than shift
  .../max96724.c:830 max96724_set_pipe_link() warn: subtract is higher precedence than shift
  .../max96724.c:942 max96724_set_link_version() warn: subtract is higher precedence than shift

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

-- 
Kind Regards,
Niklas Söderlund

^ permalink raw reply

* Re: [PATCH v13 17/22] media: i2c: maxim-serdes: add MAX96724 driver
From: Dan Carpenter @ 2026-06-10 15:06 UTC (permalink / raw)
  To: Niklas Söderlund
  Cc: dumitru.ceclan, Tomi Valkeinen, Mauro Carvalho Chehab,
	Sakari Ailus, Laurent Pinchart, Julien Massot, Rob Herring,
	Greg Kroah-Hartman, mitrutzceclan, linux-media, linux-kernel,
	devicetree, linux-staging, linux-gpio, Martin Hecht,
	Cosmin Tanislav, Cory Keitz
In-Reply-To: <20260610144242.GF2948@ragnatech.se>

On Wed, Jun 10, 2026 at 04:42:42PM +0200, Niklas Söderlund wrote:
> Hi,
> 
> Thanks for your work.
> 
> This patch gives me new compiler warnings, can they be avoided?
> 
>   .../max96724.c:402 max96724_log_phy_status() warn: subtract is higher precedence than shift
>   .../max96724.c:409 max96724_log_phy_status() warn: subtract is higher precedence than shift
>   .../max96724.c:588 max96724_init_phy() warn: subtract is higher precedence than shift
>   .../max96724.c:756 max96724_set_pipe_remap() warn: subtract is higher precedence than shift
>   .../max96724.c:796 max96724_set_pipe_phy() warn: subtract is higher precedence than shift
>   .../max96724.c:818 max96724_set_pipe_stream_id() warn: subtract is higher precedence than shift
>   .../max96724.c:830 max96724_set_pipe_link() warn: subtract is higher precedence than shift
>   .../max96724.c:942 max96724_set_link_version() warn: subtract is higher precedence than shift
> 

These are Smatch warnings.  I appologize for them.  I know about them
but I haven't looked at them.  I'll fix them by the end of the week.

regards,
dan carpenter


^ permalink raw reply

* Re: [PATCH] staging: greybus: remove driver depending on nonexistent config option
From: Agatha Isabelle Moreira @ 2026-06-10 15:56 UTC (permalink / raw)
  To: Ethan Nelson-Moore
  Cc: greybus-dev, linux-staging, linux-kernel, Johan Hovold,
	Alex Elder, Greg Kroah-Hartman, Jakub Kicinski, Namjae Jeon
In-Reply-To: <CADkSEUjVYOCYDRkr+WYD34_LH=gYGRyvHfwWH6q5jPLZBtXeTA@mail.gmail.com>

On Tue, Jun 09, 2026 at 06:14:41PM -0700, Ethan Nelson-Moore wrote:
> Hi, Agatha,

Hi, Ethan!

> 
> On Tue, Jun 9, 2026 at 4:56 PM Agatha Isabelle Moreira <code@agatha.dev> wrote:
> > On Tue, Jun 09, 2026 at 01:26:58PM -0700, Ethan Nelson-Moore wrote:
> > > The Greybus Arche Platform driver depends on the config option
> > > USB_HSIC_USB3613, which has never existed in mainline Linux. Therefore,
> >
> > Actually it dosn't. The `depends on` line says:
> >
> >      depends on USB_HSIC_USB3613 || COMPILE_TEST
> >
> > That's a logical OR operation, it depends on USB_HSIC_USB3613 OR
> > COMPILE_TEST.
> 
> The function of COMPILE_TEST is to allow drivers to be compiled in
> environments where they do not work, to enable better code coverage
> and easier testing of tree-wide changes without cross compilers. It

Yes, and that's been also discussed in the thread I linked on my
previous message as well. My point is just on commit message accuracy.

> should never be enabled for production use cases. So yes, technically,

Not the main point here, but I'd also note that no staging driver is
intended for production use cases.

> it does depend on either option, but only on one "real" option.
> Therefore, I don't think there is anything wrong with the commit
> message.

While I do understand that this driver only compiles and cannot be
loaded, I think this piece of information isn't accurately described in
the commit message, especially when considering it's been argued about
that in previous patches.

Sincerely, 
Agatha Isabelle Moreira

^ permalink raw reply

* [PATCH] staging: rtl8723bs: Drop unused parameter from rtw_sdio_if1_init()
From: Uwe Kleine-König @ 2026-06-10 16:43 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Nikolay Kulikov, Luka Gejak, Dan Carpenter, Omer El Idrissi,
	Michael Straube, Shyam Sunder Reddy Padira, Ethan Tidmore,
	Liang Jie, linux-staging, linux-kernel

The function doesn't use the pdid parameter, so drop that and adapt the
only caller accordingly.

Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
---
Hello,

I noticed this patch opportunity while working on a change to struct
sdio_device_id. Only compile tested, but should be safe.

Best regards
Uwe

 drivers/staging/rtl8723bs/os_dep/sdio_intf.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
index c43a0391a5ca..06f00d35ced1 100644
--- a/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
+++ b/drivers/staging/rtl8723bs/os_dep/sdio_intf.c
@@ -215,7 +215,7 @@ static void sd_intf_stop(struct adapter *padapter)
 }
 
 
-static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj, const struct sdio_device_id  *pdid)
+static struct adapter *rtw_sdio_if1_init(struct dvobj_priv *dvobj)
 {
 	int status = _FAIL;
 	struct net_device *pnetdev;
@@ -346,7 +346,7 @@ static int rtw_drv_init(
 	if (!dvobj)
 		goto exit;
 
-	if1 = rtw_sdio_if1_init(dvobj, id);
+	if1 = rtw_sdio_if1_init(dvobj);
 	if (!if1)
 		goto free_dvobj;
 

base-commit: abe651837cb394f76d738a7a747322fca3bf17ba
-- 
2.47.3


^ permalink raw reply related

* [PATCH v2] staging: rtl8723bs: core: avoid NULL pointer dereference in c2h_wk_callback
From: Nikoloz Bakuradze @ 2026-06-10 16:47 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Khushal Chitturi, Archit Anant, Minu Jin,
	Andy Shevchenko, Kees Cook, linux-staging, linux-kernel
  Cc: Nikoloz Bakuradze

kmalloc(16, GFP_ATOMIC) in c2h_wk_callback() could in theory return2
NULL, which would then be dereferenced in rtw_hal_c2h_valid().

A 16-byte allocation effectively cannot fail in practice, but add an
else continue; to the guard so the failure path exits the iteration
cleanly to make the code more robust.

Signed-off-by: Nikoloz Bakuradze <nbakuradze28@gmail.com>
---
Changes in v2:
- Dropped Fixes:/Cc:stable per Andy's review (kmalloc(16) effectively
  cannot fail in practice).
- Switched to Andy's else continue; form inside the existing
  if (c2h_evt) block.

 drivers/staging/rtl8723bs/core/rtw_cmd.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c
index c1185c25ed369..8d06b9d0e9bbc 100644
--- a/drivers/staging/rtl8723bs/core/rtw_cmd.c
+++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c
@@ -1708,6 +1708,8 @@ static void c2h_wk_callback(struct work_struct *work)
 					kfree(c2h_evt);
 					continue;
 				}
+			} else {
+				continue;
 			}
 		}
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH] staging: greybus: remove unused macro CAP_TIMEOUT_MS
From: Rhys Tumelty @ 2026-06-10 17:30 UTC (permalink / raw)
  To: Viresh Kumar, Johan Hovold, Alex Elder, Greg Kroah-Hartman
  Cc: greybus-dev, linux-staging, linux-kernel, Rhys Tumelty

The macro CAP_TIMEOUT_MS is defined but never used anywhere
in the kernel tree. Remove it to clean up code.

Signed-off-by: Rhys Tumelty <rhys@tumelty.co.uk>
---
 drivers/staging/greybus/authentication.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/staging/greybus/authentication.c b/drivers/staging/greybus/authentication.c
index 97b9937bbdc1..d8f2cd43201f 100644
--- a/drivers/staging/greybus/authentication.c
+++ b/drivers/staging/greybus/authentication.c
@@ -15,8 +15,6 @@
 #include "greybus_authentication.h"
 #include "firmware.h"
 
-#define CAP_TIMEOUT_MS		1000
-
 /*
  * Number of minor devices this driver supports.
  * There will be exactly one required per Interface.
-- 
2.54.0


^ permalink raw reply related

* [PATCH] media: atomisp: gmin: Use str_on_off() helper
From: Mert Seftali @ 2026-06-10 18:23 UTC (permalink / raw)
  To: Hans de Goede, Mauro Carvalho Chehab
  Cc: Sakari Ailus, Andy Shevchenko, Greg Kroah-Hartman, Kees Cook,
	linux-media, linux-staging, linux-kernel, Mert Seftali

Replace the open-coded "on" : "off" ternary with the standard
str_on_off() helper from <linux/string_choices.h>. This improves
readability and reuses the kernel's existing string helper instead of
duplicating the literals.

No functional change intended.

Signed-off-by: Mert Seftali <mertsftl@gmail.com>
---
 drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
index 4026e98c5845..322eca4a3755 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
@@ -13,6 +13,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
+#include <linux/string_choices.h>
 #include "../../include/linux/atomisp_platform.h"
 #include "../../include/linux/atomisp_gmin_platform.h"
 
@@ -917,7 +918,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on)
 		return 0;
 
 	dev_dbg(subdev->dev, "Setting power state to %s\n",
-		on ? "on" : "off");
+		str_on_off(on));
 
 	if (on)
 		ret = acpi_device_set_power(adev,
@@ -930,7 +931,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on)
 		gs->clock_on = on;
 	else
 		dev_err(subdev->dev, "Couldn't set power state to %s\n",
-			on ? "on" : "off");
+			str_on_off(on));
 
 	return ret;
 }
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v2] staging: rtl8723bs: core: avoid NULL pointer dereference in c2h_wk_callback
From: Andy Shevchenko @ 2026-06-10 20:39 UTC (permalink / raw)
  To: Nikoloz Bakuradze
  Cc: Greg Kroah-Hartman, Khushal Chitturi, Archit Anant, Minu Jin,
	Kees Cook, linux-staging, linux-kernel
In-Reply-To: <20260610164755.49626-1-nbakuradze28@gmail.com>

On Wed, Jun 10, 2026 at 08:47:54PM +0400, Nikoloz Bakuradze wrote:
> kmalloc(16, GFP_ATOMIC) in c2h_wk_callback() could in theory return2
> NULL, which would then be dereferenced in rtw_hal_c2h_valid().
> 
> A 16-byte allocation effectively cannot fail in practice, but add an
> else continue; to the guard so the failure path exits the iteration
> cleanly to make the code more robust.

OK.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH] media: atomisp: gmin: Use str_on_off() helper
From: Kees Cook @ 2026-06-10 22:19 UTC (permalink / raw)
  To: Mert Seftali
  Cc: Hans de Goede, Mauro Carvalho Chehab, Sakari Ailus,
	Andy Shevchenko, Greg Kroah-Hartman, linux-media, linux-staging,
	linux-kernel
In-Reply-To: <20260610182348.217892-1-mertsftl@gmail.com>

On Wed, Jun 10, 2026 at 08:23:48PM +0200, Mert Seftali wrote:
> Replace the open-coded "on" : "off" ternary with the standard
> str_on_off() helper from <linux/string_choices.h>. This improves
> readability and reuses the kernel's existing string helper instead of
> duplicating the literals.
> 
> No functional change intended.
> 
> Signed-off-by: Mert Seftali <mertsftl@gmail.com>

Reviewed-by: Kees Cook <kees@kernel.org>

-- 
Kees Cook

^ permalink raw reply

* [PATCH] staging: rtl8723bs: remove references to nonexistent CONFIG_IEEE80211W option
From: Ethan Nelson-Moore @ 2026-06-11  1:44 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Khushal Chitturi, Nikolay Kulikov,
	Michael Straube, Dan Carpenter, Lin YuChen, Sameeksha Sankpal,
	Ethan Tidmore, Bryant Boatright, Tomasz Unger, Ethan Nelson-Moore,
	Samasth Norway Ananda, linux-staging

Comments in the rtl8723bs driver contain many references to
CONFIG_IEEE80211W, which does not exist in the kernel (though it does
exist in wpa_supplicant, where this code was likely also used). Remove
them.

Discovered while searching for CONFIG_* symbols referenced in code but
not defined in any Kconfig file.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 drivers/staging/rtl8723bs/core/rtw_cmd.c      |  2 --
 .../staging/rtl8723bs/core/rtw_ioctl_set.c    |  1 -
 drivers/staging/rtl8723bs/core/rtw_mlme.c     |  3 ---
 drivers/staging/rtl8723bs/core/rtw_security.c | 26 ++++++-------------
 drivers/staging/rtl8723bs/include/drv_types.h |  2 +-
 drivers/staging/rtl8723bs/include/ieee80211.h |  4 +--
 drivers/staging/rtl8723bs/include/rtw_cmd.h   |  5 ++--
 drivers/staging/rtl8723bs/os_dep/os_intfs.c   |  1 -
 8 files changed, 13 insertions(+), 31 deletions(-)

diff --git a/drivers/staging/rtl8723bs/core/rtw_cmd.c b/drivers/staging/rtl8723bs/core/rtw_cmd.c
index c1185c25ed36..3b90ca548f98 100644
--- a/drivers/staging/rtl8723bs/core/rtw_cmd.c
+++ b/drivers/staging/rtl8723bs/core/rtw_cmd.c
@@ -1002,7 +1002,6 @@ u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr)
 	return res;
 }
 
-/* add for CONFIG_IEEE80211W, none 11w can use it */
 u8 rtw_reset_securitypriv_cmd(struct adapter *padapter)
 {
 	struct cmd_obj *ph2c;
@@ -1761,7 +1760,6 @@ u8 rtw_drvextra_cmd_hdl(struct adapter *padapter, unsigned char *pbuf)
 	case CHECK_HIQ_WK_CID:
 		rtw_chk_hi_queue_hdl(padapter);
 		break;
-	/* add for CONFIG_IEEE80211W, none 11w can use it */
 	case RESET_SECURITYPRIV:
 		rtw_reset_securitypriv(padapter);
 		break;
diff --git a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c
index c70541f95a73..b996ef9517a1 100644
--- a/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c
+++ b/drivers/staging/rtl8723bs/core/rtw_ioctl_set.c
@@ -350,7 +350,6 @@ u8 rtw_set_802_11_disassociate(struct adapter *padapter)
 	if (check_fwstate(pmlmepriv, _FW_LINKED) == true) {
 		rtw_disassoc_cmd(padapter, 0, true);
 		rtw_indicate_disconnect(padapter);
-		/* modify for CONFIG_IEEE80211W, none 11w can use it */
 		rtw_free_assoc_resources_cmd(padapter);
 		rtw_pwr_wakeup(padapter);
 	}
diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme.c b/drivers/staging/rtl8723bs/core/rtw_mlme.c
index ddfc56f0253d..95cca19d7c63 100644
--- a/drivers/staging/rtl8723bs/core/rtw_mlme.c
+++ b/drivers/staging/rtl8723bs/core/rtw_mlme.c
@@ -900,7 +900,6 @@ void rtw_indicate_disconnect(struct adapter *padapter)
 
 		rtw_cfg80211_indicate_disconnect(padapter);
 
-		/* modify for CONFIG_IEEE80211W, none 11w also can use the same command */
 		rtw_reset_securitypriv_cmd(padapter);
 
 		/* set ips_deny_time to avoid enter IPS before LPS leave */
@@ -1084,7 +1083,6 @@ void rtw_reset_securitypriv(struct adapter *adapter)
 	u8 backupPMKIDIndex = 0;
 	u8 backupTKIPCountermeasure = 0x00;
 	u32 backupTKIPcountermeasure_time = 0;
-	/*  add for CONFIG_IEEE80211W, none 11w also can use */
 	struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
 
 	spin_lock_bh(&adapter->security_key_mutex);
@@ -1134,7 +1132,6 @@ void rtw_reset_securitypriv(struct adapter *adapter)
 		psec_priv->ndisencryptstatus = Ndis802_11WEPDisabled;
 		/*  */
 	}
-	/*  add for CONFIG_IEEE80211W, none 11w also can use */
 	spin_unlock_bh(&adapter->security_key_mutex);
 }
 
diff --git a/drivers/staging/rtl8723bs/core/rtw_security.c b/drivers/staging/rtl8723bs/core/rtw_security.c
index a00504ff2910..3c4439e8d5ec 100644
--- a/drivers/staging/rtl8723bs/core/rtw_security.c
+++ b/drivers/staging/rtl8723bs/core/rtw_security.c
@@ -630,7 +630,7 @@ static void construct_mic_iv(u8 *mic_iv,
 			     u8 *mpdu,
 			     uint payload_length,
 			     u8 *pn_vector,
-			     uint frtype) /* add for CONFIG_IEEE80211W, none 11w also can use */
+			     uint frtype)
 {
 		signed int i;
 
@@ -671,7 +671,7 @@ static void construct_mic_iv(u8 *mic_iv,
 static void construct_mic_header1(u8 *mic_header1,
 				  signed int header_length,
 				  u8 *mpdu,
-				  uint frtype) /* for CONFIG_IEEE80211W, none 11w also can use */
+				  uint frtype)
 {
 		mic_header1[0] = (u8)((header_length - 2) / 256);
 		mic_header1[1] = (u8)((header_length - 2) % 256);
@@ -754,7 +754,7 @@ static void construct_ctr_preload(u8 *ctr_preload,
 				  u8 *mpdu,
 				  u8 *pn_vector,
 				  signed int c,
-				  uint frtype) /* for CONFIG_IEEE80211W, none 11w also can use */
+				  uint frtype)
 {
 	signed int i = 0;
 
@@ -819,7 +819,7 @@ static signed int aes_cipher(u8 *key, uint	hdrlen,
 		if (hdrlen !=  WLAN_HDR_A3_QOS_LEN)
 			hdrlen += 2;
 
-	} else if ((frtype == WIFI_DATA) && /*  add for CONFIG_IEEE80211W, none 11w also can use */
+	} else if ((frtype == WIFI_DATA) &&
 		   ((frsubtype == 0x08) ||
 		   (frsubtype == 0x09) ||
 		   (frsubtype == 0x0a) ||
@@ -845,12 +845,12 @@ static signed int aes_cipher(u8 *key, uint	hdrlen,
 			 pframe,	 /* message, */
 			 plen,
 			 pn_vector,
-			 frtype); /*  add for CONFIG_IEEE80211W, none 11w also can use */
+			 frtype);
 
 	construct_mic_header1(mic_header1,
 			      hdrlen,
 			      pframe,	/* message */
-			      frtype); /*  add for CONFIG_IEEE80211W, none 11w also can use */
+			      frtype);
 
 	construct_mic_header2(mic_header2,
 			      pframe,	/* message, */
@@ -899,7 +899,6 @@ static signed int aes_cipher(u8 *key, uint	hdrlen,
 	for (i = 0; i < num_blocks; i++) {
 		construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, /* message, */
 				      pn_vector, i + 1, frtype);
-		/*  add for CONFIG_IEEE80211W, none 11w also can use */
 		aes128k128d(key, ctr_preload, aes_out);
 		crypto_xor_cpy(chain_buffer, aes_out, &pframe[payload_index], 16);
 		for (j = 0; j < 16; j++)
@@ -911,7 +910,6 @@ static signed int aes_cipher(u8 *key, uint	hdrlen,
 		/* encrypt it and copy the unpadded part back   */
 		construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, /* message, */
 				      pn_vector, num_blocks + 1, frtype);
-		/*  add for CONFIG_IEEE80211W, none 11w also can use */
 
 		for (j = 0; j < 16; j++)
 			padded_buffer[j] = 0x00;
@@ -927,7 +925,6 @@ static signed int aes_cipher(u8 *key, uint	hdrlen,
 	/* Encrypt the MIC */
 	construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, /* message, */
 			      pn_vector, 0, frtype);
-	/*  add for CONFIG_IEEE80211W, none 11w also can use */
 
 	for (j = 0; j < 16; j++)
 		padded_buffer[j] = 0x00;
@@ -1037,7 +1034,7 @@ static signed int aes_decipher(u8 *key, uint	hdrlen,
 		if (hdrlen !=  WLAN_HDR_A3_QOS_LEN)
 			hdrlen += 2;
 
-	} else if ((frtype == WIFI_DATA) && /* only for data packet . add for CONFIG_IEEE80211W, none 11w also can use */
+	} else if ((frtype == WIFI_DATA) &&
 		   ((frsubtype == 0x08) ||
 		   (frsubtype == 0x09) ||
 		   (frsubtype == 0x0a) ||
@@ -1058,7 +1055,7 @@ static signed int aes_decipher(u8 *key, uint	hdrlen,
 		construct_ctr_preload(ctr_preload, a4_exists,
 				      qc_exists, pframe,
 				      pn_vector, i + 1,
-				      frtype); /*  add for CONFIG_IEEE80211W, none 11w also can use */
+				      frtype);
 
 		aes128k128d(key, ctr_preload, aes_out);
 		crypto_xor_cpy(chain_buffer, aes_out, &pframe[payload_index], 16);
@@ -1072,7 +1069,6 @@ static signed int aes_decipher(u8 *key, uint	hdrlen,
 		/* encrypt it and copy the unpadded part back   */
 		construct_ctr_preload(ctr_preload, a4_exists, qc_exists, pframe, pn_vector,
 				      num_blocks + 1, frtype);
-		/*  add for CONFIG_IEEE80211W, none 11w also can use */
 
 		for (j = 0; j < 16; j++)
 			padded_buffer[j] = 0x00;
@@ -1097,10 +1093,8 @@ static signed int aes_decipher(u8 *key, uint	hdrlen,
 	pn_vector[5] = pframe[hdrlen + 7];
 
 	construct_mic_iv(mic_iv, qc_exists, a4_exists, message, plen - 8, pn_vector, frtype);
-	/*  add for CONFIG_IEEE80211W, none 11w also can use */
 
 	construct_mic_header1(mic_header1, hdrlen, message, frtype);
-	/*  add for CONFIG_IEEE80211W, none 11w also can use */
 	construct_mic_header2(mic_header2, message, a4_exists, qc_exists);
 
 	payload_remainder = (plen - 8) % 16;
@@ -1145,7 +1139,6 @@ static signed int aes_decipher(u8 *key, uint	hdrlen,
 	for (i = 0; i < num_blocks; i++) {
 		construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, i + 1,
 				      frtype);
-		/*  add for CONFIG_IEEE80211W, none 11w also can use */
 		aes128k128d(key, ctr_preload, aes_out);
 		crypto_xor_cpy(chain_buffer, aes_out, &message[payload_index], 16);
 		for (j = 0; j < 16; j++)
@@ -1157,7 +1150,6 @@ static signed int aes_decipher(u8 *key, uint	hdrlen,
 		/* encrypt it and copy the unpadded part back   */
 		construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector,
 				      num_blocks + 1, frtype);
-		/*  add for CONFIG_IEEE80211W, none 11w also can use */
 
 		for (j = 0; j < 16; j++)
 			padded_buffer[j] = 0x00;
@@ -1172,7 +1164,6 @@ static signed int aes_decipher(u8 *key, uint	hdrlen,
 
 	/* Encrypt the MIC */
 	construct_ctr_preload(ctr_preload, a4_exists, qc_exists, message, pn_vector, 0, frtype);
-	/*  add for CONFIG_IEEE80211W, none 11w also can use */
 
 	for (j = 0; j < 16; j++)
 		padded_buffer[j] = 0x00;
@@ -1446,7 +1437,6 @@ static int omac1_aes_128_vector(u8 *key, size_t num_elem,
  * This is a mode for using block cipher (AES in this case) for authentication.
  * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
  * (SP) 800-38B.
- * modify for CONFIG_IEEE80211W
  */
 int omac1_aes_128(u8 *key, u8 *data, size_t data_len, u8 *mac)
 {
diff --git a/drivers/staging/rtl8723bs/include/drv_types.h b/drivers/staging/rtl8723bs/include/drv_types.h
index 7ed375ba18d8..b18762de49c4 100644
--- a/drivers/staging/rtl8723bs/include/drv_types.h
+++ b/drivers/staging/rtl8723bs/include/drv_types.h
@@ -286,7 +286,7 @@ struct adapter {
 	struct	recv_priv recvpriv;
 	struct	sta_priv stapriv;
 	struct	security_priv securitypriv;
-	spinlock_t   security_key_mutex; /*  add for CONFIG_IEEE80211W, none 11w also can use */
+	spinlock_t   security_key_mutex;
 	struct	registry_priv registrypriv;
 	struct	eeprom_priv eeprompriv;
 
diff --git a/drivers/staging/rtl8723bs/include/ieee80211.h b/drivers/staging/rtl8723bs/include/ieee80211.h
index fbb12fe31a6c..f63f9492088c 100644
--- a/drivers/staging/rtl8723bs/include/ieee80211.h
+++ b/drivers/staging/rtl8723bs/include/ieee80211.h
@@ -528,9 +528,9 @@ enum {
 	RTW_WLAN_CATEGORY_FT = 6,
 	RTW_WLAN_CATEGORY_HT = 7,
 	RTW_WLAN_CATEGORY_SA_QUERY = 8,
-	RTW_WLAN_CATEGORY_UNPROTECTED_WNM = 11, /*  add for CONFIG_IEEE80211W, none 11w also can use */
+	RTW_WLAN_CATEGORY_UNPROTECTED_WNM = 11,
 	RTW_WLAN_CATEGORY_TDLS = 12,
-	RTW_WLAN_CATEGORY_SELF_PROTECTED = 15, /*  add for CONFIG_IEEE80211W, none 11w also can use */
+	RTW_WLAN_CATEGORY_SELF_PROTECTED = 15,
 	RTW_WLAN_CATEGORY_WMM = 17,
 	RTW_WLAN_CATEGORY_P2P = 0x7f,/* P2P action frames */
 };
diff --git a/drivers/staging/rtl8723bs/include/rtw_cmd.h b/drivers/staging/rtl8723bs/include/rtw_cmd.h
index c4c3edee809d..a3dc7f6eea66 100644
--- a/drivers/staging/rtl8723bs/include/rtw_cmd.h
+++ b/drivers/staging/rtl8723bs/include/rtw_cmd.h
@@ -133,8 +133,8 @@ enum {
 	INTEl_WIDI_WK_CID,
 	C2H_WK_CID,
 	RTP_TIMER_CFG_WK_CID,
-	RESET_SECURITYPRIV, /*  add for CONFIG_IEEE80211W, none 11w also can use */
-	FREE_ASSOC_RESOURCES, /*  add for CONFIG_IEEE80211W, none 11w also can use */
+	RESET_SECURITYPRIV,
+	FREE_ASSOC_RESOURCES,
 	DM_IN_LPS_WK_CID,
 	DM_RA_MSK_WK_CID, /* add for STA update RAMask when bandwidth change. */
 	BEAMFORMING_WK_CID,
@@ -594,7 +594,6 @@ extern u8 rtw_setfwdig_cmd(struct adapter *padapter, u8 type);
 extern u8 rtw_setfwra_cmd(struct adapter *padapter, u8 type);
 
 extern u8 rtw_addbareq_cmd(struct adapter *padapter, u8 tid, u8 *addr);
-/*  add for CONFIG_IEEE80211W, none 11w also can use */
 extern u8 rtw_reset_securitypriv_cmd(struct adapter *padapter);
 extern u8 rtw_free_assoc_resources_cmd(struct adapter *padapter);
 extern u8 rtw_dynamic_chk_wk_cmd(struct adapter *adapter);
diff --git a/drivers/staging/rtl8723bs/os_dep/os_intfs.c b/drivers/staging/rtl8723bs/os_dep/os_intfs.c
index e943dcea1a21..56eb3bbf7aa8 100644
--- a/drivers/staging/rtl8723bs/os_dep/os_intfs.c
+++ b/drivers/staging/rtl8723bs/os_dep/os_intfs.c
@@ -658,7 +658,6 @@ u8 rtw_init_drv_sw(struct adapter *padapter)
 
 	if (_rtw_init_recv_priv(&padapter->recvpriv, padapter) == _FAIL)
 		goto free_xmit_priv;
-	/*  add for CONFIG_IEEE80211W, none 11w also can use */
 	spin_lock_init(&padapter->security_key_mutex);
 
 	/*  We don't need to memset padapter->XXX to zero, because adapter is allocated by vzalloc(). */
-- 
2.43.0


^ permalink raw reply related

* Re: [PATCH] media: atomisp: gmin: Use str_on_off() helper
From: Andy Shevchenko @ 2026-06-11  5:34 UTC (permalink / raw)
  To: Mert Seftali
  Cc: Hans de Goede, Mauro Carvalho Chehab, Sakari Ailus,
	Andy Shevchenko, Greg Kroah-Hartman, Kees Cook, linux-media,
	linux-staging, linux-kernel
In-Reply-To: <20260610182348.217892-1-mertsftl@gmail.com>

On Wed, Jun 10, 2026 at 08:23:48PM +0200, Mert Seftali wrote:
> Replace the open-coded "on" : "off" ternary with the standard
> str_on_off() helper from <linux/string_choices.h>. This improves
> readability and reuses the kernel's existing string helper instead of
> duplicating the literals.
> 
> No functional change intended.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
but see a couple of nit-picks below.

...

>  #include <linux/gpio/consumer.h>
>  #include <linux/gpio.h>
>  #include <linux/platform_device.h>
> +#include <linux/string_choices.h>

While at it, perhaps add a blank line here.

>  #include "../../include/linux/atomisp_platform.h"
>  #include "../../include/linux/atomisp_gmin_platform.h"

...

>  	dev_dbg(subdev->dev, "Setting power state to %s\n",
> -		on ? "on" : "off");
> +		str_on_off(on));

Now this goes to be perfectly one line.

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* [PATCH v2] media: atomisp: gmin: Use str_on_off() helper
From: Mert Seftali @ 2026-06-11  6:14 UTC (permalink / raw)
  To: Hans de Goede, Mauro Carvalho Chehab
  Cc: Andy Shevchenko, Sakari Ailus, Greg Kroah-Hartman, Kees Cook,
	linux-media, linux-staging, linux-kernel, Mert Seftali
In-Reply-To: <aipI6J4kDWESZgtA@ashevche-desk.local>

Replace the open-coded "on" : "off" ternary with the standard
str_on_off() helper from <linux/string_choices.h>. This improves
readability and reuses the kernel's existing string helper instead of
duplicating the literals.

No functional change intended.

Signed-off-by: Mert Seftali <mertsftl@gmail.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
---
Changes in v2:
- Add a blank line between the system and local includes.
- Join the dev_dbg() call onto a single line now that it fits.

 drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
index 4026e98c5845..cb60cb3fab48 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
@@ -13,6 +13,8 @@
 #include <linux/gpio/consumer.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
+#include <linux/string_choices.h>
+
 #include "../../include/linux/atomisp_platform.h"
 #include "../../include/linux/atomisp_gmin_platform.h"
 
@@ -916,8 +918,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on)
 	if (gs->clock_on == on)
 		return 0;
 
-	dev_dbg(subdev->dev, "Setting power state to %s\n",
-		on ? "on" : "off");
+	dev_dbg(subdev->dev, "Setting power state to %s\n", str_on_off(on));
 
 	if (on)
 		ret = acpi_device_set_power(adev,
@@ -930,7 +931,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on)
 		gs->clock_on = on;
 	else
 		dev_err(subdev->dev, "Couldn't set power state to %s\n",
-			on ? "on" : "off");
+			str_on_off(on));
 
 	return ret;
 }
-- 
2.54.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox