18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Maxim MAX9286 GMSL Deserializer Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2017-2019 Jacopo Mondi
68c2ecf20Sopenharmony_ci * Copyright (C) 2017-2019 Kieran Bingham
78c2ecf20Sopenharmony_ci * Copyright (C) 2017-2019 Laurent Pinchart
88c2ecf20Sopenharmony_ci * Copyright (C) 2017-2019 Niklas Söderlund
98c2ecf20Sopenharmony_ci * Copyright (C) 2016 Renesas Electronics Corporation
108c2ecf20Sopenharmony_ci * Copyright (C) 2015 Cogent Embedded, Inc.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/device.h>
158c2ecf20Sopenharmony_ci#include <linux/fwnode.h>
168c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
178c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h>
188c2ecf20Sopenharmony_ci#include <linux/i2c.h>
198c2ecf20Sopenharmony_ci#include <linux/i2c-mux.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/mutex.h>
228c2ecf20Sopenharmony_ci#include <linux/of_graph.h>
238c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <media/v4l2-async.h>
278c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
288c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
298c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h>
308c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Register 0x00 */
338c2ecf20Sopenharmony_ci#define MAX9286_MSTLINKSEL_AUTO		(7 << 5)
348c2ecf20Sopenharmony_ci#define MAX9286_MSTLINKSEL(n)		((n) << 5)
358c2ecf20Sopenharmony_ci#define MAX9286_EN_VS_GEN		BIT(4)
368c2ecf20Sopenharmony_ci#define MAX9286_LINKEN(n)		(1 << (n))
378c2ecf20Sopenharmony_ci/* Register 0x01 */
388c2ecf20Sopenharmony_ci#define MAX9286_FSYNCMODE_ECU		(3 << 6)
398c2ecf20Sopenharmony_ci#define MAX9286_FSYNCMODE_EXT		(2 << 6)
408c2ecf20Sopenharmony_ci#define MAX9286_FSYNCMODE_INT_OUT	(1 << 6)
418c2ecf20Sopenharmony_ci#define MAX9286_FSYNCMODE_INT_HIZ	(0 << 6)
428c2ecf20Sopenharmony_ci#define MAX9286_GPIEN			BIT(5)
438c2ecf20Sopenharmony_ci#define MAX9286_ENLMO_RSTFSYNC		BIT(2)
448c2ecf20Sopenharmony_ci#define MAX9286_FSYNCMETH_AUTO		(2 << 0)
458c2ecf20Sopenharmony_ci#define MAX9286_FSYNCMETH_SEMI_AUTO	(1 << 0)
468c2ecf20Sopenharmony_ci#define MAX9286_FSYNCMETH_MANUAL	(0 << 0)
478c2ecf20Sopenharmony_ci#define MAX9286_REG_FSYNC_PERIOD_L	0x06
488c2ecf20Sopenharmony_ci#define MAX9286_REG_FSYNC_PERIOD_M	0x07
498c2ecf20Sopenharmony_ci#define MAX9286_REG_FSYNC_PERIOD_H	0x08
508c2ecf20Sopenharmony_ci/* Register 0x0a */
518c2ecf20Sopenharmony_ci#define MAX9286_FWDCCEN(n)		(1 << ((n) + 4))
528c2ecf20Sopenharmony_ci#define MAX9286_REVCCEN(n)		(1 << (n))
538c2ecf20Sopenharmony_ci/* Register 0x0c */
548c2ecf20Sopenharmony_ci#define MAX9286_HVEN			BIT(7)
558c2ecf20Sopenharmony_ci#define MAX9286_EDC_6BIT_HAMMING	(2 << 5)
568c2ecf20Sopenharmony_ci#define MAX9286_EDC_6BIT_CRC		(1 << 5)
578c2ecf20Sopenharmony_ci#define MAX9286_EDC_1BIT_PARITY		(0 << 5)
588c2ecf20Sopenharmony_ci#define MAX9286_DESEL			BIT(4)
598c2ecf20Sopenharmony_ci#define MAX9286_INVVS			BIT(3)
608c2ecf20Sopenharmony_ci#define MAX9286_INVHS			BIT(2)
618c2ecf20Sopenharmony_ci#define MAX9286_HVSRC_D0		(2 << 0)
628c2ecf20Sopenharmony_ci#define MAX9286_HVSRC_D14		(1 << 0)
638c2ecf20Sopenharmony_ci#define MAX9286_HVSRC_D18		(0 << 0)
648c2ecf20Sopenharmony_ci/* Register 0x0f */
658c2ecf20Sopenharmony_ci#define MAX9286_0X0F_RESERVED		BIT(3)
668c2ecf20Sopenharmony_ci/* Register 0x12 */
678c2ecf20Sopenharmony_ci#define MAX9286_CSILANECNT(n)		(((n) - 1) << 6)
688c2ecf20Sopenharmony_ci#define MAX9286_CSIDBL			BIT(5)
698c2ecf20Sopenharmony_ci#define MAX9286_DBL			BIT(4)
708c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_USER_8BIT	(11 << 0)
718c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_USER_YUV_12BIT	(10 << 0)
728c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_USER_24BIT	(9 << 0)
738c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_RAW14		(8 << 0)
748c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_RAW11		(7 << 0)
758c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_RAW10		(6 << 0)
768c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_RAW8		(5 << 0)
778c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_YUV422_10BIT	(4 << 0)
788c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_YUV422_8BIT	(3 << 0)
798c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_RGB555		(2 << 0)
808c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_RGB565		(1 << 0)
818c2ecf20Sopenharmony_ci#define MAX9286_DATATYPE_RGB888		(0 << 0)
828c2ecf20Sopenharmony_ci/* Register 0x15 */
838c2ecf20Sopenharmony_ci#define MAX9286_VC(n)			((n) << 5)
848c2ecf20Sopenharmony_ci#define MAX9286_VCTYPE			BIT(4)
858c2ecf20Sopenharmony_ci#define MAX9286_CSIOUTEN		BIT(3)
868c2ecf20Sopenharmony_ci#define MAX9286_0X15_RESV		(3 << 0)
878c2ecf20Sopenharmony_ci/* Register 0x1b */
888c2ecf20Sopenharmony_ci#define MAX9286_SWITCHIN(n)		(1 << ((n) + 4))
898c2ecf20Sopenharmony_ci#define MAX9286_ENEQ(n)			(1 << (n))
908c2ecf20Sopenharmony_ci/* Register 0x27 */
918c2ecf20Sopenharmony_ci#define MAX9286_LOCKED			BIT(7)
928c2ecf20Sopenharmony_ci/* Register 0x31 */
938c2ecf20Sopenharmony_ci#define MAX9286_FSYNC_LOCKED		BIT(6)
948c2ecf20Sopenharmony_ci/* Register 0x34 */
958c2ecf20Sopenharmony_ci#define MAX9286_I2CLOCACK		BIT(7)
968c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVSH_1046NS_469NS	(3 << 5)
978c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVSH_938NS_352NS	(2 << 5)
988c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVSH_469NS_234NS	(1 << 5)
998c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVSH_352NS_117NS	(0 << 5)
1008c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_837KBPS	(7 << 2)
1018c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_533KBPS	(6 << 2)
1028c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_339KBPS	(5 << 2)
1038c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_173KBPS	(4 << 2)
1048c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_105KBPS	(3 << 2)
1058c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_84KBPS		(2 << 2)
1068c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_28KBPS		(1 << 2)
1078c2ecf20Sopenharmony_ci#define MAX9286_I2CMSTBT_8KBPS		(0 << 2)
1088c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVTO_NONE		(3 << 0)
1098c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVTO_1024US		(2 << 0)
1108c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVTO_256US		(1 << 0)
1118c2ecf20Sopenharmony_ci#define MAX9286_I2CSLVTO_64US		(0 << 0)
1128c2ecf20Sopenharmony_ci/* Register 0x3b */
1138c2ecf20Sopenharmony_ci#define MAX9286_REV_TRF(n)		((n) << 4)
1148c2ecf20Sopenharmony_ci#define MAX9286_REV_AMP(n)		((((n) - 30) / 10) << 1) /* in mV */
1158c2ecf20Sopenharmony_ci#define MAX9286_REV_AMP_X		BIT(0)
1168c2ecf20Sopenharmony_ci/* Register 0x3f */
1178c2ecf20Sopenharmony_ci#define MAX9286_EN_REV_CFG		BIT(6)
1188c2ecf20Sopenharmony_ci#define MAX9286_REV_FLEN(n)		((n) - 20)
1198c2ecf20Sopenharmony_ci/* Register 0x49 */
1208c2ecf20Sopenharmony_ci#define MAX9286_VIDEO_DETECT_MASK	0x0f
1218c2ecf20Sopenharmony_ci/* Register 0x69 */
1228c2ecf20Sopenharmony_ci#define MAX9286_LFLTBMONMASKED		BIT(7)
1238c2ecf20Sopenharmony_ci#define MAX9286_LOCKMONMASKED		BIT(6)
1248c2ecf20Sopenharmony_ci#define MAX9286_AUTOCOMBACKEN		BIT(5)
1258c2ecf20Sopenharmony_ci#define MAX9286_AUTOMASKEN		BIT(4)
1268c2ecf20Sopenharmony_ci#define MAX9286_MASKLINK(n)		((n) << 0)
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/*
1298c2ecf20Sopenharmony_ci * The sink and source pads are created to match the OF graph port numbers so
1308c2ecf20Sopenharmony_ci * that their indexes can be used interchangeably.
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_ci#define MAX9286_NUM_GMSL		4
1338c2ecf20Sopenharmony_ci#define MAX9286_N_SINKS			4
1348c2ecf20Sopenharmony_ci#define MAX9286_N_PADS			5
1358c2ecf20Sopenharmony_ci#define MAX9286_SRC_PAD			4
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistruct max9286_source {
1388c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
1398c2ecf20Sopenharmony_ci	struct fwnode_handle *fwnode;
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistruct max9286_asd {
1438c2ecf20Sopenharmony_ci	struct v4l2_async_subdev base;
1448c2ecf20Sopenharmony_ci	struct max9286_source *source;
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic inline struct max9286_asd *to_max9286_asd(struct v4l2_async_subdev *asd)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	return container_of(asd, struct max9286_asd, base);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistruct max9286_priv {
1538c2ecf20Sopenharmony_ci	struct i2c_client *client;
1548c2ecf20Sopenharmony_ci	struct gpio_desc *gpiod_pwdn;
1558c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
1568c2ecf20Sopenharmony_ci	struct media_pad pads[MAX9286_N_PADS];
1578c2ecf20Sopenharmony_ci	struct regulator *regulator;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	struct gpio_chip gpio;
1608c2ecf20Sopenharmony_ci	u8 gpio_state;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	struct i2c_mux_core *mux;
1638c2ecf20Sopenharmony_ci	unsigned int mux_channel;
1648c2ecf20Sopenharmony_ci	bool mux_open;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler ctrls;
1678c2ecf20Sopenharmony_ci	struct v4l2_ctrl *pixelrate;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* Protects controls and fmt structures */
1728c2ecf20Sopenharmony_ci	struct mutex mutex;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	unsigned int nsources;
1758c2ecf20Sopenharmony_ci	unsigned int source_mask;
1768c2ecf20Sopenharmony_ci	unsigned int route_mask;
1778c2ecf20Sopenharmony_ci	unsigned int bound_sources;
1788c2ecf20Sopenharmony_ci	unsigned int csi2_data_lanes;
1798c2ecf20Sopenharmony_ci	struct max9286_source sources[MAX9286_NUM_GMSL];
1808c2ecf20Sopenharmony_ci	struct v4l2_async_notifier notifier;
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic struct max9286_source *next_source(struct max9286_priv *priv,
1848c2ecf20Sopenharmony_ci					  struct max9286_source *source)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	if (!source)
1878c2ecf20Sopenharmony_ci		source = &priv->sources[0];
1888c2ecf20Sopenharmony_ci	else
1898c2ecf20Sopenharmony_ci		source++;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) {
1928c2ecf20Sopenharmony_ci		if (source->fwnode)
1938c2ecf20Sopenharmony_ci			return source;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return NULL;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#define for_each_source(priv, source) \
2008c2ecf20Sopenharmony_ci	for ((source) = NULL; ((source) = next_source((priv), (source))); )
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci#define to_index(priv, source) ((source) - &(priv)->sources[0])
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return container_of(sd, struct max9286_priv, sd);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
2108c2ecf20Sopenharmony_ci * I2C IO
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int max9286_read(struct max9286_priv *priv, u8 reg)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(priv->client, reg);
2188c2ecf20Sopenharmony_ci	if (ret < 0)
2198c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev,
2208c2ecf20Sopenharmony_ci			"%s: register 0x%02x read failed (%d)\n",
2218c2ecf20Sopenharmony_ci			__func__, reg, ret);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return ret;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int max9286_write(struct max9286_priv *priv, u8 reg, u8 val)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	int ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(priv->client, reg, val);
2318c2ecf20Sopenharmony_ci	if (ret < 0)
2328c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev,
2338c2ecf20Sopenharmony_ci			"%s: register 0x%02x write failed (%d)\n",
2348c2ecf20Sopenharmony_ci			__func__, reg, ret);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return ret;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
2408c2ecf20Sopenharmony_ci * I2C Multiplexer
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	max9286_write(priv, 0x0a, conf);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/*
2488c2ecf20Sopenharmony_ci	 * We must sleep after any change to the forward or reverse channel
2498c2ecf20Sopenharmony_ci	 * configuration.
2508c2ecf20Sopenharmony_ci	 */
2518c2ecf20Sopenharmony_ci	usleep_range(3000, 5000);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic void max9286_i2c_mux_open(struct max9286_priv *priv)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	/* Open all channels on the MAX9286 */
2578c2ecf20Sopenharmony_ci	max9286_i2c_mux_configure(priv, 0xff);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	priv->mux_open = true;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic void max9286_i2c_mux_close(struct max9286_priv *priv)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	/*
2658c2ecf20Sopenharmony_ci	 * Ensure that both the forward and reverse channel are disabled on the
2668c2ecf20Sopenharmony_ci	 * mux, and that the channel ID is invalidated to ensure we reconfigure
2678c2ecf20Sopenharmony_ci	 * on the next max9286_i2c_mux_select() call.
2688c2ecf20Sopenharmony_ci	 */
2698c2ecf20Sopenharmony_ci	max9286_i2c_mux_configure(priv, 0x00);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	priv->mux_open = false;
2728c2ecf20Sopenharmony_ci	priv->mux_channel = -1;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct max9286_priv *priv = i2c_mux_priv(muxc);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* Channel select is disabled when configured in the opened state. */
2808c2ecf20Sopenharmony_ci	if (priv->mux_open)
2818c2ecf20Sopenharmony_ci		return 0;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (priv->mux_channel == chan)
2848c2ecf20Sopenharmony_ci		return 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	priv->mux_channel = chan;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	max9286_i2c_mux_configure(priv,
2898c2ecf20Sopenharmony_ci				  MAX9286_FWDCCEN(chan) |
2908c2ecf20Sopenharmony_ci				  MAX9286_REVCCEN(chan));
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return 0;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int max9286_i2c_mux_init(struct max9286_priv *priv)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct max9286_source *source;
2988c2ecf20Sopenharmony_ci	int ret;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(priv->client->adapter,
3018c2ecf20Sopenharmony_ci				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
3028c2ecf20Sopenharmony_ci		return -ENODEV;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,
3058c2ecf20Sopenharmony_ci				  priv->nsources, 0, I2C_MUX_LOCKED,
3068c2ecf20Sopenharmony_ci				  max9286_i2c_mux_select, NULL);
3078c2ecf20Sopenharmony_ci	if (!priv->mux)
3088c2ecf20Sopenharmony_ci		return -ENOMEM;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	priv->mux->priv = priv;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	for_each_source(priv, source) {
3138c2ecf20Sopenharmony_ci		unsigned int index = to_index(priv, source);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		ret = i2c_mux_add_adapter(priv->mux, 0, index, 0);
3168c2ecf20Sopenharmony_ci		if (ret < 0)
3178c2ecf20Sopenharmony_ci			goto error;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	return 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cierror:
3238c2ecf20Sopenharmony_ci	i2c_mux_del_adapters(priv->mux);
3248c2ecf20Sopenharmony_ci	return ret;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
3308c2ecf20Sopenharmony_ci		    MAX9286_I2CMSTBT_105KBPS;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if (localack)
3338c2ecf20Sopenharmony_ci		config |= MAX9286_I2CLOCACK;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	max9286_write(priv, 0x34, config);
3368c2ecf20Sopenharmony_ci	usleep_range(3000, 5000);
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci/*
3408c2ecf20Sopenharmony_ci * max9286_check_video_links() - Make sure video links are detected and locked
3418c2ecf20Sopenharmony_ci *
3428c2ecf20Sopenharmony_ci * Performs safety checks on video link status. Make sure they are detected
3438c2ecf20Sopenharmony_ci * and all enabled links are locked.
3448c2ecf20Sopenharmony_ci *
3458c2ecf20Sopenharmony_ci * Returns 0 for success, -EIO for errors.
3468c2ecf20Sopenharmony_ci */
3478c2ecf20Sopenharmony_cistatic int max9286_check_video_links(struct max9286_priv *priv)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	unsigned int i;
3508c2ecf20Sopenharmony_ci	int ret;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/*
3538c2ecf20Sopenharmony_ci	 * Make sure valid video links are detected.
3548c2ecf20Sopenharmony_ci	 * The delay is not characterized in de-serializer manual, wait up
3558c2ecf20Sopenharmony_ci	 * to 5 ms.
3568c2ecf20Sopenharmony_ci	 */
3578c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
3588c2ecf20Sopenharmony_ci		ret = max9286_read(priv, 0x49);
3598c2ecf20Sopenharmony_ci		if (ret < 0)
3608c2ecf20Sopenharmony_ci			return -EIO;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask)
3638c2ecf20Sopenharmony_ci			break;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		usleep_range(350, 500);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (i == 10) {
3698c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev,
3708c2ecf20Sopenharmony_ci			"Unable to detect video links: 0x%02x\n", ret);
3718c2ecf20Sopenharmony_ci		return -EIO;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* Make sure all enabled links are locked (4ms max). */
3758c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
3768c2ecf20Sopenharmony_ci		ret = max9286_read(priv, 0x27);
3778c2ecf20Sopenharmony_ci		if (ret < 0)
3788c2ecf20Sopenharmony_ci			return -EIO;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci		if (ret & MAX9286_LOCKED)
3818c2ecf20Sopenharmony_ci			break;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		usleep_range(350, 450);
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (i == 10) {
3878c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev, "Not all enabled links locked\n");
3888c2ecf20Sopenharmony_ci		return -EIO;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/*
3958c2ecf20Sopenharmony_ci * max9286_check_config_link() - Detect and wait for configuration links
3968c2ecf20Sopenharmony_ci *
3978c2ecf20Sopenharmony_ci * Determine if the configuration channel is up and settled for a link.
3988c2ecf20Sopenharmony_ci *
3998c2ecf20Sopenharmony_ci * Returns 0 for success, -EIO for errors.
4008c2ecf20Sopenharmony_ci */
4018c2ecf20Sopenharmony_cistatic int max9286_check_config_link(struct max9286_priv *priv,
4028c2ecf20Sopenharmony_ci				     unsigned int source_mask)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	unsigned int conflink_mask = (source_mask & 0x0f) << 4;
4058c2ecf20Sopenharmony_ci	unsigned int i;
4068c2ecf20Sopenharmony_ci	int ret;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	/*
4098c2ecf20Sopenharmony_ci	 * Make sure requested configuration links are detected.
4108c2ecf20Sopenharmony_ci	 * The delay is not characterized in the chip manual: wait up
4118c2ecf20Sopenharmony_ci	 * to 5 milliseconds.
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
4148c2ecf20Sopenharmony_ci		ret = max9286_read(priv, 0x49);
4158c2ecf20Sopenharmony_ci		if (ret < 0)
4168c2ecf20Sopenharmony_ci			return -EIO;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		ret &= 0xf0;
4198c2ecf20Sopenharmony_ci		if (ret == conflink_mask)
4208c2ecf20Sopenharmony_ci			break;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		usleep_range(350, 500);
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (ret != conflink_mask) {
4268c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev,
4278c2ecf20Sopenharmony_ci			"Unable to detect configuration links: 0x%02x expected 0x%02x\n",
4288c2ecf20Sopenharmony_ci			ret, conflink_mask);
4298c2ecf20Sopenharmony_ci		return -EIO;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	dev_info(&priv->client->dev,
4338c2ecf20Sopenharmony_ci		 "Successfully detected configuration links after %u loops: 0x%02x\n",
4348c2ecf20Sopenharmony_ci		 i, conflink_mask);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	return 0;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
4408c2ecf20Sopenharmony_ci * V4L2 Subdev
4418c2ecf20Sopenharmony_ci */
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int max9286_set_pixelrate(struct max9286_priv *priv)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct max9286_source *source = NULL;
4468c2ecf20Sopenharmony_ci	u64 pixelrate = 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	for_each_source(priv, source) {
4498c2ecf20Sopenharmony_ci		struct v4l2_ctrl *ctrl;
4508c2ecf20Sopenharmony_ci		u64 source_rate = 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		/* Pixel rate is mandatory to be reported by sources. */
4538c2ecf20Sopenharmony_ci		ctrl = v4l2_ctrl_find(source->sd->ctrl_handler,
4548c2ecf20Sopenharmony_ci				      V4L2_CID_PIXEL_RATE);
4558c2ecf20Sopenharmony_ci		if (!ctrl) {
4568c2ecf20Sopenharmony_ci			pixelrate = 0;
4578c2ecf20Sopenharmony_ci			break;
4588c2ecf20Sopenharmony_ci		}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		/* All source must report the same pixel rate. */
4618c2ecf20Sopenharmony_ci		source_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
4628c2ecf20Sopenharmony_ci		if (!pixelrate) {
4638c2ecf20Sopenharmony_ci			pixelrate = source_rate;
4648c2ecf20Sopenharmony_ci		} else if (pixelrate != source_rate) {
4658c2ecf20Sopenharmony_ci			dev_err(&priv->client->dev,
4668c2ecf20Sopenharmony_ci				"Unable to calculate pixel rate\n");
4678c2ecf20Sopenharmony_ci			return -EINVAL;
4688c2ecf20Sopenharmony_ci		}
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (!pixelrate) {
4728c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev,
4738c2ecf20Sopenharmony_ci			"No pixel rate control available in sources\n");
4748c2ecf20Sopenharmony_ci		return -EINVAL;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/*
4788c2ecf20Sopenharmony_ci	 * The CSI-2 transmitter pixel rate is the single source rate multiplied
4798c2ecf20Sopenharmony_ci	 * by the number of available sources.
4808c2ecf20Sopenharmony_ci	 */
4818c2ecf20Sopenharmony_ci	return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
4828c2ecf20Sopenharmony_ci				      pixelrate * priv->nsources);
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int max9286_notify_bound(struct v4l2_async_notifier *notifier,
4868c2ecf20Sopenharmony_ci				struct v4l2_subdev *subdev,
4878c2ecf20Sopenharmony_ci				struct v4l2_async_subdev *asd)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	struct max9286_priv *priv = sd_to_max9286(notifier->sd);
4908c2ecf20Sopenharmony_ci	struct max9286_source *source = to_max9286_asd(asd)->source;
4918c2ecf20Sopenharmony_ci	unsigned int index = to_index(priv, source);
4928c2ecf20Sopenharmony_ci	unsigned int src_pad;
4938c2ecf20Sopenharmony_ci	int ret;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	ret = media_entity_get_fwnode_pad(&subdev->entity,
4968c2ecf20Sopenharmony_ci					  source->fwnode,
4978c2ecf20Sopenharmony_ci					  MEDIA_PAD_FL_SOURCE);
4988c2ecf20Sopenharmony_ci	if (ret < 0) {
4998c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev,
5008c2ecf20Sopenharmony_ci			"Failed to find pad for %s\n", subdev->name);
5018c2ecf20Sopenharmony_ci		return ret;
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	priv->bound_sources |= BIT(index);
5058c2ecf20Sopenharmony_ci	source->sd = subdev;
5068c2ecf20Sopenharmony_ci	src_pad = ret;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	ret = media_create_pad_link(&source->sd->entity, src_pad,
5098c2ecf20Sopenharmony_ci				    &priv->sd.entity, index,
5108c2ecf20Sopenharmony_ci				    MEDIA_LNK_FL_ENABLED |
5118c2ecf20Sopenharmony_ci				    MEDIA_LNK_FL_IMMUTABLE);
5128c2ecf20Sopenharmony_ci	if (ret) {
5138c2ecf20Sopenharmony_ci		dev_err(&priv->client->dev,
5148c2ecf20Sopenharmony_ci			"Unable to link %s:%u -> %s:%u\n",
5158c2ecf20Sopenharmony_ci			source->sd->name, src_pad, priv->sd.name, index);
5168c2ecf20Sopenharmony_ci		return ret;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n",
5208c2ecf20Sopenharmony_ci		subdev->name, src_pad, index);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	/*
5238c2ecf20Sopenharmony_ci	 * We can only register v4l2_async_notifiers, which do not provide a
5248c2ecf20Sopenharmony_ci	 * means to register a complete callback. bound_sources allows us to
5258c2ecf20Sopenharmony_ci	 * identify when all remote serializers have completed their probe.
5268c2ecf20Sopenharmony_ci	 */
5278c2ecf20Sopenharmony_ci	if (priv->bound_sources != priv->source_mask)
5288c2ecf20Sopenharmony_ci		return 0;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	/*
5318c2ecf20Sopenharmony_ci	 * All enabled sources have probed and enabled their reverse control
5328c2ecf20Sopenharmony_ci	 * channels:
5338c2ecf20Sopenharmony_ci	 *
5348c2ecf20Sopenharmony_ci	 * - Verify all configuration links are properly detected
5358c2ecf20Sopenharmony_ci	 * - Disable auto-ack as communication on the control channel are now
5368c2ecf20Sopenharmony_ci	 *   stable.
5378c2ecf20Sopenharmony_ci	 */
5388c2ecf20Sopenharmony_ci	max9286_check_config_link(priv, priv->source_mask);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/*
5418c2ecf20Sopenharmony_ci	 * Re-configure I2C with local acknowledge disabled after cameras have
5428c2ecf20Sopenharmony_ci	 * probed.
5438c2ecf20Sopenharmony_ci	 */
5448c2ecf20Sopenharmony_ci	max9286_configure_i2c(priv, false);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	return max9286_set_pixelrate(priv);
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
5508c2ecf20Sopenharmony_ci				  struct v4l2_subdev *subdev,
5518c2ecf20Sopenharmony_ci				  struct v4l2_async_subdev *asd)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct max9286_priv *priv = sd_to_max9286(notifier->sd);
5548c2ecf20Sopenharmony_ci	struct max9286_source *source = to_max9286_asd(asd)->source;
5558c2ecf20Sopenharmony_ci	unsigned int index = to_index(priv, source);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	source->sd = NULL;
5588c2ecf20Sopenharmony_ci	priv->bound_sources &= ~BIT(index);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic const struct v4l2_async_notifier_operations max9286_notify_ops = {
5628c2ecf20Sopenharmony_ci	.bound = max9286_notify_bound,
5638c2ecf20Sopenharmony_ci	.unbind = max9286_notify_unbind,
5648c2ecf20Sopenharmony_ci};
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_cistatic int max9286_v4l2_notifier_register(struct max9286_priv *priv)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct device *dev = &priv->client->dev;
5698c2ecf20Sopenharmony_ci	struct max9286_source *source = NULL;
5708c2ecf20Sopenharmony_ci	int ret;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (!priv->nsources)
5738c2ecf20Sopenharmony_ci		return 0;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	v4l2_async_notifier_init(&priv->notifier);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	for_each_source(priv, source) {
5788c2ecf20Sopenharmony_ci		unsigned int i = to_index(priv, source);
5798c2ecf20Sopenharmony_ci		struct v4l2_async_subdev *asd;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci		asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier,
5828c2ecf20Sopenharmony_ci							    source->fwnode,
5838c2ecf20Sopenharmony_ci							    sizeof(struct max9286_asd));
5848c2ecf20Sopenharmony_ci		if (IS_ERR(asd)) {
5858c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to add subdev for source %u: %ld",
5868c2ecf20Sopenharmony_ci				i, PTR_ERR(asd));
5878c2ecf20Sopenharmony_ci			v4l2_async_notifier_cleanup(&priv->notifier);
5888c2ecf20Sopenharmony_ci			return PTR_ERR(asd);
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		to_max9286_asd(asd)->source = source;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	priv->notifier.ops = &max9286_notify_ops;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
5978c2ecf20Sopenharmony_ci	if (ret) {
5988c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register subdev_notifier");
5998c2ecf20Sopenharmony_ci		v4l2_async_notifier_cleanup(&priv->notifier);
6008c2ecf20Sopenharmony_ci		return ret;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic void max9286_v4l2_notifier_unregister(struct max9286_priv *priv)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	if (!priv->nsources)
6098c2ecf20Sopenharmony_ci		return;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	v4l2_async_notifier_unregister(&priv->notifier);
6128c2ecf20Sopenharmony_ci	v4l2_async_notifier_cleanup(&priv->notifier);
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int max9286_s_stream(struct v4l2_subdev *sd, int enable)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	struct max9286_priv *priv = sd_to_max9286(sd);
6188c2ecf20Sopenharmony_ci	struct max9286_source *source;
6198c2ecf20Sopenharmony_ci	unsigned int i;
6208c2ecf20Sopenharmony_ci	bool sync = false;
6218c2ecf20Sopenharmony_ci	int ret;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (enable) {
6248c2ecf20Sopenharmony_ci		/*
6258c2ecf20Sopenharmony_ci		 * The frame sync between cameras is transmitted across the
6268c2ecf20Sopenharmony_ci		 * reverse channel as GPIO. We must open all channels while
6278c2ecf20Sopenharmony_ci		 * streaming to allow this synchronisation signal to be shared.
6288c2ecf20Sopenharmony_ci		 */
6298c2ecf20Sopenharmony_ci		max9286_i2c_mux_open(priv);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		/* Start all cameras. */
6328c2ecf20Sopenharmony_ci		for_each_source(priv, source) {
6338c2ecf20Sopenharmony_ci			ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
6348c2ecf20Sopenharmony_ci			if (ret)
6358c2ecf20Sopenharmony_ci				return ret;
6368c2ecf20Sopenharmony_ci		}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci		ret = max9286_check_video_links(priv);
6398c2ecf20Sopenharmony_ci		if (ret)
6408c2ecf20Sopenharmony_ci			return ret;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci		/*
6438c2ecf20Sopenharmony_ci		 * Wait until frame synchronization is locked.
6448c2ecf20Sopenharmony_ci		 *
6458c2ecf20Sopenharmony_ci		 * Manual says frame sync locking should take ~6 VTS.
6468c2ecf20Sopenharmony_ci		 * From practical experience at least 8 are required. Give
6478c2ecf20Sopenharmony_ci		 * 12 complete frames time (~400ms at 30 fps) to achieve frame
6488c2ecf20Sopenharmony_ci		 * locking before returning error.
6498c2ecf20Sopenharmony_ci		 */
6508c2ecf20Sopenharmony_ci		for (i = 0; i < 40; i++) {
6518c2ecf20Sopenharmony_ci			if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) {
6528c2ecf20Sopenharmony_ci				sync = true;
6538c2ecf20Sopenharmony_ci				break;
6548c2ecf20Sopenharmony_ci			}
6558c2ecf20Sopenharmony_ci			usleep_range(9000, 11000);
6568c2ecf20Sopenharmony_ci		}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci		if (!sync) {
6598c2ecf20Sopenharmony_ci			dev_err(&priv->client->dev,
6608c2ecf20Sopenharmony_ci				"Failed to get frame synchronization\n");
6618c2ecf20Sopenharmony_ci			return -EXDEV; /* Invalid cross-device link */
6628c2ecf20Sopenharmony_ci		}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci		/*
6658c2ecf20Sopenharmony_ci		 * Enable CSI output, VC set according to link number.
6668c2ecf20Sopenharmony_ci		 * Bit 7 must be set (chip manual says it's 0 and reserved).
6678c2ecf20Sopenharmony_ci		 */
6688c2ecf20Sopenharmony_ci		max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE |
6698c2ecf20Sopenharmony_ci			      MAX9286_CSIOUTEN | MAX9286_0X15_RESV);
6708c2ecf20Sopenharmony_ci	} else {
6718c2ecf20Sopenharmony_ci		max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci		/* Stop all cameras. */
6748c2ecf20Sopenharmony_ci		for_each_source(priv, source)
6758c2ecf20Sopenharmony_ci			v4l2_subdev_call(source->sd, video, s_stream, 0);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		max9286_i2c_mux_close(priv);
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return 0;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic int max9286_enum_mbus_code(struct v4l2_subdev *sd,
6848c2ecf20Sopenharmony_ci				  struct v4l2_subdev_pad_config *cfg,
6858c2ecf20Sopenharmony_ci				  struct v4l2_subdev_mbus_code_enum *code)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	if (code->pad || code->index > 0)
6888c2ecf20Sopenharmony_ci		return -EINVAL;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	code->code = MEDIA_BUS_FMT_UYVY8_1X16;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt *
6968c2ecf20Sopenharmony_cimax9286_get_pad_format(struct max9286_priv *priv,
6978c2ecf20Sopenharmony_ci		       struct v4l2_subdev_pad_config *cfg,
6988c2ecf20Sopenharmony_ci		       unsigned int pad, u32 which)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	switch (which) {
7018c2ecf20Sopenharmony_ci	case V4L2_SUBDEV_FORMAT_TRY:
7028c2ecf20Sopenharmony_ci		return v4l2_subdev_get_try_format(&priv->sd, cfg, pad);
7038c2ecf20Sopenharmony_ci	case V4L2_SUBDEV_FORMAT_ACTIVE:
7048c2ecf20Sopenharmony_ci		return &priv->fmt[pad];
7058c2ecf20Sopenharmony_ci	default:
7068c2ecf20Sopenharmony_ci		return NULL;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic int max9286_set_fmt(struct v4l2_subdev *sd,
7118c2ecf20Sopenharmony_ci			   struct v4l2_subdev_pad_config *cfg,
7128c2ecf20Sopenharmony_ci			   struct v4l2_subdev_format *format)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	struct max9286_priv *priv = sd_to_max9286(sd);
7158c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *cfg_fmt;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	if (format->pad == MAX9286_SRC_PAD)
7188c2ecf20Sopenharmony_ci		return -EINVAL;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
7218c2ecf20Sopenharmony_ci	switch (format->format.code) {
7228c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_UYVY8_1X16:
7238c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_VYUY8_1X16:
7248c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_YUYV8_1X16:
7258c2ecf20Sopenharmony_ci	case MEDIA_BUS_FMT_YVYU8_1X16:
7268c2ecf20Sopenharmony_ci		break;
7278c2ecf20Sopenharmony_ci	default:
7288c2ecf20Sopenharmony_ci		format->format.code = MEDIA_BUS_FMT_UYVY8_1X16;
7298c2ecf20Sopenharmony_ci		break;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which);
7338c2ecf20Sopenharmony_ci	if (!cfg_fmt)
7348c2ecf20Sopenharmony_ci		return -EINVAL;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	mutex_lock(&priv->mutex);
7378c2ecf20Sopenharmony_ci	*cfg_fmt = format->format;
7388c2ecf20Sopenharmony_ci	mutex_unlock(&priv->mutex);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	return 0;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic int max9286_get_fmt(struct v4l2_subdev *sd,
7448c2ecf20Sopenharmony_ci			   struct v4l2_subdev_pad_config *cfg,
7458c2ecf20Sopenharmony_ci			   struct v4l2_subdev_format *format)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	struct max9286_priv *priv = sd_to_max9286(sd);
7488c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *cfg_fmt;
7498c2ecf20Sopenharmony_ci	unsigned int pad = format->pad;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	/*
7528c2ecf20Sopenharmony_ci	 * Multiplexed Stream Support: Support link validation by returning the
7538c2ecf20Sopenharmony_ci	 * format of the first bound link. All links must have the same format,
7548c2ecf20Sopenharmony_ci	 * as we do not support mixing and matching of cameras connected to the
7558c2ecf20Sopenharmony_ci	 * max9286.
7568c2ecf20Sopenharmony_ci	 */
7578c2ecf20Sopenharmony_ci	if (pad == MAX9286_SRC_PAD)
7588c2ecf20Sopenharmony_ci		pad = __ffs(priv->bound_sources);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	cfg_fmt = max9286_get_pad_format(priv, cfg, pad, format->which);
7618c2ecf20Sopenharmony_ci	if (!cfg_fmt)
7628c2ecf20Sopenharmony_ci		return -EINVAL;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	mutex_lock(&priv->mutex);
7658c2ecf20Sopenharmony_ci	format->format = *cfg_fmt;
7668c2ecf20Sopenharmony_ci	mutex_unlock(&priv->mutex);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	return 0;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops max9286_video_ops = {
7728c2ecf20Sopenharmony_ci	.s_stream	= max9286_s_stream,
7738c2ecf20Sopenharmony_ci};
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops max9286_pad_ops = {
7768c2ecf20Sopenharmony_ci	.enum_mbus_code = max9286_enum_mbus_code,
7778c2ecf20Sopenharmony_ci	.get_fmt	= max9286_get_fmt,
7788c2ecf20Sopenharmony_ci	.set_fmt	= max9286_set_fmt,
7798c2ecf20Sopenharmony_ci};
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops max9286_subdev_ops = {
7828c2ecf20Sopenharmony_ci	.video		= &max9286_video_ops,
7838c2ecf20Sopenharmony_ci	.pad		= &max9286_pad_ops,
7848c2ecf20Sopenharmony_ci};
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
7878c2ecf20Sopenharmony_ci{
7888c2ecf20Sopenharmony_ci	fmt->width		= 1280;
7898c2ecf20Sopenharmony_ci	fmt->height		= 800;
7908c2ecf20Sopenharmony_ci	fmt->code		= MEDIA_BUS_FMT_UYVY8_1X16;
7918c2ecf20Sopenharmony_ci	fmt->colorspace		= V4L2_COLORSPACE_SRGB;
7928c2ecf20Sopenharmony_ci	fmt->field		= V4L2_FIELD_NONE;
7938c2ecf20Sopenharmony_ci	fmt->ycbcr_enc		= V4L2_YCBCR_ENC_DEFAULT;
7948c2ecf20Sopenharmony_ci	fmt->quantization	= V4L2_QUANTIZATION_DEFAULT;
7958c2ecf20Sopenharmony_ci	fmt->xfer_func		= V4L2_XFER_FUNC_DEFAULT;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *format;
8018c2ecf20Sopenharmony_ci	unsigned int i;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	for (i = 0; i < MAX9286_N_SINKS; i++) {
8048c2ecf20Sopenharmony_ci		format = v4l2_subdev_get_try_format(subdev, fh->pad, i);
8058c2ecf20Sopenharmony_ci		max9286_init_format(format);
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	return 0;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
8128c2ecf20Sopenharmony_ci	.open = max9286_open,
8138c2ecf20Sopenharmony_ci};
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	switch (ctrl->id) {
8188c2ecf20Sopenharmony_ci	case V4L2_CID_PIXEL_RATE:
8198c2ecf20Sopenharmony_ci		return 0;
8208c2ecf20Sopenharmony_ci	default:
8218c2ecf20Sopenharmony_ci		return -EINVAL;
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops max9286_ctrl_ops = {
8268c2ecf20Sopenharmony_ci	.s_ctrl = max9286_s_ctrl,
8278c2ecf20Sopenharmony_ci};
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic int max9286_v4l2_register(struct max9286_priv *priv)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	struct device *dev = &priv->client->dev;
8328c2ecf20Sopenharmony_ci	struct fwnode_handle *ep;
8338c2ecf20Sopenharmony_ci	int ret;
8348c2ecf20Sopenharmony_ci	int i;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	/* Register v4l2 async notifiers for connected Camera subdevices */
8378c2ecf20Sopenharmony_ci	ret = max9286_v4l2_notifier_register(priv);
8388c2ecf20Sopenharmony_ci	if (ret) {
8398c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to register V4L2 async notifiers\n");
8408c2ecf20Sopenharmony_ci		return ret;
8418c2ecf20Sopenharmony_ci	}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	/* Configure V4L2 for the MAX9286 itself */
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	for (i = 0; i < MAX9286_N_SINKS; i++)
8468c2ecf20Sopenharmony_ci		max9286_init_format(&priv->fmt[i]);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops);
8498c2ecf20Sopenharmony_ci	priv->sd.internal_ops = &max9286_subdev_internal_ops;
8508c2ecf20Sopenharmony_ci	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&priv->ctrls, 1);
8538c2ecf20Sopenharmony_ci	priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls,
8548c2ecf20Sopenharmony_ci					    &max9286_ctrl_ops,
8558c2ecf20Sopenharmony_ci					    V4L2_CID_PIXEL_RATE,
8568c2ecf20Sopenharmony_ci					    1, INT_MAX, 1, 50000000);
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	priv->sd.ctrl_handler = &priv->ctrls;
8598c2ecf20Sopenharmony_ci	ret = priv->ctrls.error;
8608c2ecf20Sopenharmony_ci	if (ret)
8618c2ecf20Sopenharmony_ci		goto err_async;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE;
8668c2ecf20Sopenharmony_ci	for (i = 0; i < MAX9286_SRC_PAD; i++)
8678c2ecf20Sopenharmony_ci		priv->pads[i].flags = MEDIA_PAD_FL_SINK;
8688c2ecf20Sopenharmony_ci	ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS,
8698c2ecf20Sopenharmony_ci				     priv->pads);
8708c2ecf20Sopenharmony_ci	if (ret)
8718c2ecf20Sopenharmony_ci		goto err_async;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD,
8748c2ecf20Sopenharmony_ci					     0, 0);
8758c2ecf20Sopenharmony_ci	if (!ep) {
8768c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n");
8778c2ecf20Sopenharmony_ci		ret = -ENOENT;
8788c2ecf20Sopenharmony_ci		goto err_async;
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci	priv->sd.fwnode = ep;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	ret = v4l2_async_register_subdev(&priv->sd);
8838c2ecf20Sopenharmony_ci	if (ret < 0) {
8848c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to register subdevice\n");
8858c2ecf20Sopenharmony_ci		goto err_put_node;
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	return 0;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_cierr_put_node:
8918c2ecf20Sopenharmony_ci	fwnode_handle_put(ep);
8928c2ecf20Sopenharmony_cierr_async:
8938c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&priv->ctrls);
8948c2ecf20Sopenharmony_ci	max9286_v4l2_notifier_unregister(priv);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	return ret;
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_cistatic void max9286_v4l2_unregister(struct max9286_priv *priv)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	fwnode_handle_put(priv->sd.fwnode);
9028c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&priv->ctrls);
9038c2ecf20Sopenharmony_ci	v4l2_async_unregister_subdev(&priv->sd);
9048c2ecf20Sopenharmony_ci	max9286_v4l2_notifier_unregister(priv);
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
9088c2ecf20Sopenharmony_ci * Probe/Remove
9098c2ecf20Sopenharmony_ci */
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic int max9286_setup(struct max9286_priv *priv)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	/*
9148c2ecf20Sopenharmony_ci	 * Link ordering values for all enabled links combinations. Orders must
9158c2ecf20Sopenharmony_ci	 * be assigned sequentially from 0 to the number of enabled links
9168c2ecf20Sopenharmony_ci	 * without leaving any hole for disabled links. We thus assign orders to
9178c2ecf20Sopenharmony_ci	 * enabled links first, and use the remaining order values for disabled
9188c2ecf20Sopenharmony_ci	 * links are all links must have a different order value;
9198c2ecf20Sopenharmony_ci	 */
9208c2ecf20Sopenharmony_ci	static const u8 link_order[] = {
9218c2ecf20Sopenharmony_ci		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
9228c2ecf20Sopenharmony_ci		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
9238c2ecf20Sopenharmony_ci		(3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
9248c2ecf20Sopenharmony_ci		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
9258c2ecf20Sopenharmony_ci		(3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
9268c2ecf20Sopenharmony_ci		(3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
9278c2ecf20Sopenharmony_ci		(3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
9288c2ecf20Sopenharmony_ci		(3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
9298c2ecf20Sopenharmony_ci		(0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
9308c2ecf20Sopenharmony_ci		(1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
9318c2ecf20Sopenharmony_ci		(1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
9328c2ecf20Sopenharmony_ci		(2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
9338c2ecf20Sopenharmony_ci		(1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
9348c2ecf20Sopenharmony_ci		(2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
9358c2ecf20Sopenharmony_ci		(2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
9368c2ecf20Sopenharmony_ci		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
9378c2ecf20Sopenharmony_ci	};
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	/*
9408c2ecf20Sopenharmony_ci	 * Set the I2C bus speed.
9418c2ecf20Sopenharmony_ci	 *
9428c2ecf20Sopenharmony_ci	 * Enable I2C Local Acknowledge during the probe sequences of the camera
9438c2ecf20Sopenharmony_ci	 * only. This should be disabled after the mux is initialised.
9448c2ecf20Sopenharmony_ci	 */
9458c2ecf20Sopenharmony_ci	max9286_configure_i2c(priv, true);
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	/*
9488c2ecf20Sopenharmony_ci	 * Reverse channel setup.
9498c2ecf20Sopenharmony_ci	 *
9508c2ecf20Sopenharmony_ci	 * - Enable custom reverse channel configuration (through register 0x3f)
9518c2ecf20Sopenharmony_ci	 *   and set the first pulse length to 35 clock cycles.
9528c2ecf20Sopenharmony_ci	 * - Increase the reverse channel amplitude to 170mV to accommodate the
9538c2ecf20Sopenharmony_ci	 *   high threshold enabled by the serializer driver.
9548c2ecf20Sopenharmony_ci	 */
9558c2ecf20Sopenharmony_ci	max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
9568c2ecf20Sopenharmony_ci	max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) |
9578c2ecf20Sopenharmony_ci		      MAX9286_REV_AMP_X);
9588c2ecf20Sopenharmony_ci	usleep_range(2000, 2500);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	/*
9618c2ecf20Sopenharmony_ci	 * Enable GMSL links, mask unused ones and autodetect link
9628c2ecf20Sopenharmony_ci	 * used as CSI clock source.
9638c2ecf20Sopenharmony_ci	 */
9648c2ecf20Sopenharmony_ci	max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask);
9658c2ecf20Sopenharmony_ci	max9286_write(priv, 0x0b, link_order[priv->route_mask]);
9668c2ecf20Sopenharmony_ci	max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	/*
9698c2ecf20Sopenharmony_ci	 * Video format setup:
9708c2ecf20Sopenharmony_ci	 * Disable CSI output, VC is set according to Link number.
9718c2ecf20Sopenharmony_ci	 */
9728c2ecf20Sopenharmony_ci	max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
9758c2ecf20Sopenharmony_ci	max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
9768c2ecf20Sopenharmony_ci		      MAX9286_CSILANECNT(priv->csi2_data_lanes) |
9778c2ecf20Sopenharmony_ci		      MAX9286_DATATYPE_YUV422_8BIT);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	/* Automatic: FRAMESYNC taken from the slowest Link. */
9808c2ecf20Sopenharmony_ci	max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
9818c2ecf20Sopenharmony_ci		      MAX9286_FSYNCMETH_AUTO);
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
9848c2ecf20Sopenharmony_ci	max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
9858c2ecf20Sopenharmony_ci		      MAX9286_HVSRC_D14);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	/*
9888c2ecf20Sopenharmony_ci	 * The overlap window seems to provide additional validation by tracking
9898c2ecf20Sopenharmony_ci	 * the delay between vsync and frame sync, generating an error if the
9908c2ecf20Sopenharmony_ci	 * delay is bigger than the programmed window, though it's not yet clear
9918c2ecf20Sopenharmony_ci	 * what value should be set.
9928c2ecf20Sopenharmony_ci	 *
9938c2ecf20Sopenharmony_ci	 * As it's an optional value and can be disabled, we do so by setting
9948c2ecf20Sopenharmony_ci	 * a 0 overlap value.
9958c2ecf20Sopenharmony_ci	 */
9968c2ecf20Sopenharmony_ci	max9286_write(priv, 0x63, 0);
9978c2ecf20Sopenharmony_ci	max9286_write(priv, 0x64, 0);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	/*
10008c2ecf20Sopenharmony_ci	 * Wait for 2ms to allow the link to resynchronize after the
10018c2ecf20Sopenharmony_ci	 * configuration change.
10028c2ecf20Sopenharmony_ci	 */
10038c2ecf20Sopenharmony_ci	usleep_range(2000, 5000);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	return 0;
10068c2ecf20Sopenharmony_ci}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_cistatic void max9286_gpio_set(struct gpio_chip *chip,
10098c2ecf20Sopenharmony_ci			     unsigned int offset, int value)
10108c2ecf20Sopenharmony_ci{
10118c2ecf20Sopenharmony_ci	struct max9286_priv *priv = gpiochip_get_data(chip);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (value)
10148c2ecf20Sopenharmony_ci		priv->gpio_state |= BIT(offset);
10158c2ecf20Sopenharmony_ci	else
10168c2ecf20Sopenharmony_ci		priv->gpio_state &= ~BIT(offset);
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state);
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	struct max9286_priv *priv = gpiochip_get_data(chip);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	return priv->gpio_state & BIT(offset);
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_cistatic int max9286_register_gpio(struct max9286_priv *priv)
10298c2ecf20Sopenharmony_ci{
10308c2ecf20Sopenharmony_ci	struct device *dev = &priv->client->dev;
10318c2ecf20Sopenharmony_ci	struct gpio_chip *gpio = &priv->gpio;
10328c2ecf20Sopenharmony_ci	int ret;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	/* Configure the GPIO */
10358c2ecf20Sopenharmony_ci	gpio->label = dev_name(dev);
10368c2ecf20Sopenharmony_ci	gpio->parent = dev;
10378c2ecf20Sopenharmony_ci	gpio->owner = THIS_MODULE;
10388c2ecf20Sopenharmony_ci	gpio->of_node = dev->of_node;
10398c2ecf20Sopenharmony_ci	gpio->ngpio = 2;
10408c2ecf20Sopenharmony_ci	gpio->base = -1;
10418c2ecf20Sopenharmony_ci	gpio->set = max9286_gpio_set;
10428c2ecf20Sopenharmony_ci	gpio->get = max9286_gpio_get;
10438c2ecf20Sopenharmony_ci	gpio->can_sleep = true;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	/* GPIO values default to high */
10468c2ecf20Sopenharmony_ci	priv->gpio_state = BIT(0) | BIT(1);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	ret = devm_gpiochip_add_data(dev, gpio, priv);
10498c2ecf20Sopenharmony_ci	if (ret)
10508c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to create gpio_chip\n");
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	return ret;
10538c2ecf20Sopenharmony_ci}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_cistatic int max9286_init(struct device *dev)
10568c2ecf20Sopenharmony_ci{
10578c2ecf20Sopenharmony_ci	struct max9286_priv *priv;
10588c2ecf20Sopenharmony_ci	struct i2c_client *client;
10598c2ecf20Sopenharmony_ci	int ret;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	client = to_i2c_client(dev);
10628c2ecf20Sopenharmony_ci	priv = i2c_get_clientdata(client);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	/* Enable the bus power. */
10658c2ecf20Sopenharmony_ci	ret = regulator_enable(priv->regulator);
10668c2ecf20Sopenharmony_ci	if (ret < 0) {
10678c2ecf20Sopenharmony_ci		dev_err(&client->dev, "Unable to turn PoC on\n");
10688c2ecf20Sopenharmony_ci		return ret;
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	ret = max9286_setup(priv);
10728c2ecf20Sopenharmony_ci	if (ret) {
10738c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to setup max9286\n");
10748c2ecf20Sopenharmony_ci		goto err_regulator;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	/*
10788c2ecf20Sopenharmony_ci	 * Register all V4L2 interactions for the MAX9286 and notifiers for
10798c2ecf20Sopenharmony_ci	 * any subdevices connected.
10808c2ecf20Sopenharmony_ci	 */
10818c2ecf20Sopenharmony_ci	ret = max9286_v4l2_register(priv);
10828c2ecf20Sopenharmony_ci	if (ret) {
10838c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register with V4L2\n");
10848c2ecf20Sopenharmony_ci		goto err_regulator;
10858c2ecf20Sopenharmony_ci	}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	ret = max9286_i2c_mux_init(priv);
10888c2ecf20Sopenharmony_ci	if (ret) {
10898c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to initialize I2C multiplexer\n");
10908c2ecf20Sopenharmony_ci		goto err_v4l2_register;
10918c2ecf20Sopenharmony_ci	}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	/* Leave the mux channels disabled until they are selected. */
10948c2ecf20Sopenharmony_ci	max9286_i2c_mux_close(priv);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	return 0;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cierr_v4l2_register:
10998c2ecf20Sopenharmony_ci	max9286_v4l2_unregister(priv);
11008c2ecf20Sopenharmony_cierr_regulator:
11018c2ecf20Sopenharmony_ci	regulator_disable(priv->regulator);
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	return ret;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cistatic void max9286_cleanup_dt(struct max9286_priv *priv)
11078c2ecf20Sopenharmony_ci{
11088c2ecf20Sopenharmony_ci	struct max9286_source *source;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	for_each_source(priv, source) {
11118c2ecf20Sopenharmony_ci		fwnode_handle_put(source->fwnode);
11128c2ecf20Sopenharmony_ci		source->fwnode = NULL;
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_cistatic int max9286_parse_dt(struct max9286_priv *priv)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct device *dev = &priv->client->dev;
11198c2ecf20Sopenharmony_ci	struct device_node *i2c_mux;
11208c2ecf20Sopenharmony_ci	struct device_node *node = NULL;
11218c2ecf20Sopenharmony_ci	unsigned int i2c_mux_mask = 0;
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	/* Balance the of_node_put() performed by of_find_node_by_name(). */
11248c2ecf20Sopenharmony_ci	of_node_get(dev->of_node);
11258c2ecf20Sopenharmony_ci	i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux");
11268c2ecf20Sopenharmony_ci	if (!i2c_mux) {
11278c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to find i2c-mux node\n");
11288c2ecf20Sopenharmony_ci		return -EINVAL;
11298c2ecf20Sopenharmony_ci	}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	/* Identify which i2c-mux channels are enabled */
11328c2ecf20Sopenharmony_ci	for_each_child_of_node(i2c_mux, node) {
11338c2ecf20Sopenharmony_ci		u32 id = 0;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci		of_property_read_u32(node, "reg", &id);
11368c2ecf20Sopenharmony_ci		if (id >= MAX9286_NUM_GMSL)
11378c2ecf20Sopenharmony_ci			continue;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci		if (!of_device_is_available(node)) {
11408c2ecf20Sopenharmony_ci			dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id);
11418c2ecf20Sopenharmony_ci			continue;
11428c2ecf20Sopenharmony_ci		}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci		i2c_mux_mask |= BIT(id);
11458c2ecf20Sopenharmony_ci	}
11468c2ecf20Sopenharmony_ci	of_node_put(i2c_mux);
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	/* Parse the endpoints */
11498c2ecf20Sopenharmony_ci	for_each_endpoint_of_node(dev->of_node, node) {
11508c2ecf20Sopenharmony_ci		struct max9286_source *source;
11518c2ecf20Sopenharmony_ci		struct of_endpoint ep;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci		of_graph_parse_endpoint(node, &ep);
11548c2ecf20Sopenharmony_ci		dev_dbg(dev, "Endpoint %pOF on port %d",
11558c2ecf20Sopenharmony_ci			ep.local_node, ep.port);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci		if (ep.port > MAX9286_NUM_GMSL) {
11588c2ecf20Sopenharmony_ci			dev_err(dev, "Invalid endpoint %s on port %d",
11598c2ecf20Sopenharmony_ci				of_node_full_name(ep.local_node), ep.port);
11608c2ecf20Sopenharmony_ci			continue;
11618c2ecf20Sopenharmony_ci		}
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci		/* For the source endpoint just parse the bus configuration. */
11648c2ecf20Sopenharmony_ci		if (ep.port == MAX9286_SRC_PAD) {
11658c2ecf20Sopenharmony_ci			struct v4l2_fwnode_endpoint vep = {
11668c2ecf20Sopenharmony_ci				.bus_type = V4L2_MBUS_CSI2_DPHY
11678c2ecf20Sopenharmony_ci			};
11688c2ecf20Sopenharmony_ci			int ret;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci			ret = v4l2_fwnode_endpoint_parse(
11718c2ecf20Sopenharmony_ci					of_fwnode_handle(node), &vep);
11728c2ecf20Sopenharmony_ci			if (ret) {
11738c2ecf20Sopenharmony_ci				of_node_put(node);
11748c2ecf20Sopenharmony_ci				return ret;
11758c2ecf20Sopenharmony_ci			}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci			priv->csi2_data_lanes =
11788c2ecf20Sopenharmony_ci				vep.bus.mipi_csi2.num_data_lanes;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci			continue;
11818c2ecf20Sopenharmony_ci		}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci		/* Skip if the corresponding GMSL link is unavailable. */
11848c2ecf20Sopenharmony_ci		if (!(i2c_mux_mask & BIT(ep.port)))
11858c2ecf20Sopenharmony_ci			continue;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		if (priv->sources[ep.port].fwnode) {
11888c2ecf20Sopenharmony_ci			dev_err(dev,
11898c2ecf20Sopenharmony_ci				"Multiple port endpoints are not supported: %d",
11908c2ecf20Sopenharmony_ci				ep.port);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci			continue;
11938c2ecf20Sopenharmony_ci		}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci		source = &priv->sources[ep.port];
11968c2ecf20Sopenharmony_ci		source->fwnode = fwnode_graph_get_remote_endpoint(
11978c2ecf20Sopenharmony_ci						of_fwnode_handle(node));
11988c2ecf20Sopenharmony_ci		if (!source->fwnode) {
11998c2ecf20Sopenharmony_ci			dev_err(dev,
12008c2ecf20Sopenharmony_ci				"Endpoint %pOF has no remote endpoint connection\n",
12018c2ecf20Sopenharmony_ci				ep.local_node);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci			continue;
12048c2ecf20Sopenharmony_ci		}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci		priv->source_mask |= BIT(ep.port);
12078c2ecf20Sopenharmony_ci		priv->nsources++;
12088c2ecf20Sopenharmony_ci	}
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	priv->route_mask = priv->source_mask;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	return 0;
12138c2ecf20Sopenharmony_ci}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_cistatic int max9286_probe(struct i2c_client *client)
12168c2ecf20Sopenharmony_ci{
12178c2ecf20Sopenharmony_ci	struct max9286_priv *priv;
12188c2ecf20Sopenharmony_ci	int ret;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
12218c2ecf20Sopenharmony_ci	if (!priv)
12228c2ecf20Sopenharmony_ci		return -ENOMEM;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	mutex_init(&priv->mutex);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	priv->client = client;
12278c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, priv);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
12308c2ecf20Sopenharmony_ci						   GPIOD_OUT_HIGH);
12318c2ecf20Sopenharmony_ci	if (IS_ERR(priv->gpiod_pwdn))
12328c2ecf20Sopenharmony_ci		return PTR_ERR(priv->gpiod_pwdn);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
12358c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* Wait at least 4ms before the I2C lines latch to the address */
12388c2ecf20Sopenharmony_ci	if (priv->gpiod_pwdn)
12398c2ecf20Sopenharmony_ci		usleep_range(4000, 5000);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	/*
12428c2ecf20Sopenharmony_ci	 * The MAX9286 starts by default with all ports enabled, we disable all
12438c2ecf20Sopenharmony_ci	 * ports early to ensure that all channels are disabled if we error out
12448c2ecf20Sopenharmony_ci	 * and keep the bus consistent.
12458c2ecf20Sopenharmony_ci	 */
12468c2ecf20Sopenharmony_ci	max9286_i2c_mux_close(priv);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	/*
12498c2ecf20Sopenharmony_ci	 * The MAX9286 initialises with auto-acknowledge enabled by default.
12508c2ecf20Sopenharmony_ci	 * This can be invasive to other transactions on the same bus, so
12518c2ecf20Sopenharmony_ci	 * disable it early. It will be enabled only as and when needed.
12528c2ecf20Sopenharmony_ci	 */
12538c2ecf20Sopenharmony_ci	max9286_configure_i2c(priv, false);
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	ret = max9286_register_gpio(priv);
12568c2ecf20Sopenharmony_ci	if (ret)
12578c2ecf20Sopenharmony_ci		goto err_powerdown;
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	priv->regulator = devm_regulator_get(&client->dev, "poc");
12608c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regulator)) {
12618c2ecf20Sopenharmony_ci		if (PTR_ERR(priv->regulator) != -EPROBE_DEFER)
12628c2ecf20Sopenharmony_ci			dev_err(&client->dev,
12638c2ecf20Sopenharmony_ci				"Unable to get PoC regulator (%ld)\n",
12648c2ecf20Sopenharmony_ci				PTR_ERR(priv->regulator));
12658c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv->regulator);
12668c2ecf20Sopenharmony_ci		goto err_powerdown;
12678c2ecf20Sopenharmony_ci	}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	ret = max9286_parse_dt(priv);
12708c2ecf20Sopenharmony_ci	if (ret)
12718c2ecf20Sopenharmony_ci		goto err_powerdown;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	ret = max9286_init(&client->dev);
12748c2ecf20Sopenharmony_ci	if (ret < 0)
12758c2ecf20Sopenharmony_ci		goto err_cleanup_dt;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	return 0;
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_cierr_cleanup_dt:
12808c2ecf20Sopenharmony_ci	max9286_cleanup_dt(priv);
12818c2ecf20Sopenharmony_cierr_powerdown:
12828c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	return ret;
12858c2ecf20Sopenharmony_ci}
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_cistatic int max9286_remove(struct i2c_client *client)
12888c2ecf20Sopenharmony_ci{
12898c2ecf20Sopenharmony_ci	struct max9286_priv *priv = i2c_get_clientdata(client);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	i2c_mux_del_adapters(priv->mux);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	max9286_v4l2_unregister(priv);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	regulator_disable(priv->regulator);
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	max9286_cleanup_dt(priv);
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	return 0;
13028c2ecf20Sopenharmony_ci}
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_cistatic const struct of_device_id max9286_dt_ids[] = {
13058c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max9286" },
13068c2ecf20Sopenharmony_ci	{},
13078c2ecf20Sopenharmony_ci};
13088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max9286_dt_ids);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_cistatic struct i2c_driver max9286_i2c_driver = {
13118c2ecf20Sopenharmony_ci	.driver	= {
13128c2ecf20Sopenharmony_ci		.name		= "max9286",
13138c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(max9286_dt_ids),
13148c2ecf20Sopenharmony_ci	},
13158c2ecf20Sopenharmony_ci	.probe_new	= max9286_probe,
13168c2ecf20Sopenharmony_ci	.remove		= max9286_remove,
13178c2ecf20Sopenharmony_ci};
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_cimodule_i2c_driver(max9286_i2c_driver);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
13228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov");
13238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1324