xref: /openbmc/linux/drivers/media/i2c/max9286.c (revision defcedfb)
166d8c9d2SKieran Bingham // SPDX-License-Identifier: GPL-2.0+
266d8c9d2SKieran Bingham /*
366d8c9d2SKieran Bingham  * Maxim MAX9286 GMSL Deserializer Driver
466d8c9d2SKieran Bingham  *
566d8c9d2SKieran Bingham  * Copyright (C) 2017-2019 Jacopo Mondi
666d8c9d2SKieran Bingham  * Copyright (C) 2017-2019 Kieran Bingham
766d8c9d2SKieran Bingham  * Copyright (C) 2017-2019 Laurent Pinchart
866d8c9d2SKieran Bingham  * Copyright (C) 2017-2019 Niklas Söderlund
966d8c9d2SKieran Bingham  * Copyright (C) 2016 Renesas Electronics Corporation
1066d8c9d2SKieran Bingham  * Copyright (C) 2015 Cogent Embedded, Inc.
1166d8c9d2SKieran Bingham  */
1266d8c9d2SKieran Bingham 
1366d8c9d2SKieran Bingham #include <linux/delay.h>
1466d8c9d2SKieran Bingham #include <linux/device.h>
1566d8c9d2SKieran Bingham #include <linux/fwnode.h>
1666d8c9d2SKieran Bingham #include <linux/gpio/consumer.h>
1766d8c9d2SKieran Bingham #include <linux/gpio/driver.h>
18c9352df7SJacopo Mondi #include <linux/gpio/machine.h>
1966d8c9d2SKieran Bingham #include <linux/i2c.h>
2066d8c9d2SKieran Bingham #include <linux/i2c-mux.h>
2166d8c9d2SKieran Bingham #include <linux/module.h>
2266d8c9d2SKieran Bingham #include <linux/mutex.h>
2366d8c9d2SKieran Bingham #include <linux/of_graph.h>
2466d8c9d2SKieran Bingham #include <linux/regulator/consumer.h>
2566d8c9d2SKieran Bingham #include <linux/slab.h>
2666d8c9d2SKieran Bingham 
2766d8c9d2SKieran Bingham #include <media/v4l2-async.h>
2866d8c9d2SKieran Bingham #include <media/v4l2-ctrls.h>
2966d8c9d2SKieran Bingham #include <media/v4l2-device.h>
3066d8c9d2SKieran Bingham #include <media/v4l2-fwnode.h>
3166d8c9d2SKieran Bingham #include <media/v4l2-subdev.h>
3266d8c9d2SKieran Bingham 
3366d8c9d2SKieran Bingham /* Register 0x00 */
3466d8c9d2SKieran Bingham #define MAX9286_MSTLINKSEL_AUTO		(7 << 5)
3566d8c9d2SKieran Bingham #define MAX9286_MSTLINKSEL(n)		((n) << 5)
3666d8c9d2SKieran Bingham #define MAX9286_EN_VS_GEN		BIT(4)
3766d8c9d2SKieran Bingham #define MAX9286_LINKEN(n)		(1 << (n))
3866d8c9d2SKieran Bingham /* Register 0x01 */
3966d8c9d2SKieran Bingham #define MAX9286_FSYNCMODE_ECU		(3 << 6)
4066d8c9d2SKieran Bingham #define MAX9286_FSYNCMODE_EXT		(2 << 6)
4166d8c9d2SKieran Bingham #define MAX9286_FSYNCMODE_INT_OUT	(1 << 6)
4266d8c9d2SKieran Bingham #define MAX9286_FSYNCMODE_INT_HIZ	(0 << 6)
4366d8c9d2SKieran Bingham #define MAX9286_GPIEN			BIT(5)
4466d8c9d2SKieran Bingham #define MAX9286_ENLMO_RSTFSYNC		BIT(2)
4566d8c9d2SKieran Bingham #define MAX9286_FSYNCMETH_AUTO		(2 << 0)
4666d8c9d2SKieran Bingham #define MAX9286_FSYNCMETH_SEMI_AUTO	(1 << 0)
4766d8c9d2SKieran Bingham #define MAX9286_FSYNCMETH_MANUAL	(0 << 0)
4866d8c9d2SKieran Bingham #define MAX9286_REG_FSYNC_PERIOD_L	0x06
4966d8c9d2SKieran Bingham #define MAX9286_REG_FSYNC_PERIOD_M	0x07
5066d8c9d2SKieran Bingham #define MAX9286_REG_FSYNC_PERIOD_H	0x08
5166d8c9d2SKieran Bingham /* Register 0x0a */
5266d8c9d2SKieran Bingham #define MAX9286_FWDCCEN(n)		(1 << ((n) + 4))
5366d8c9d2SKieran Bingham #define MAX9286_REVCCEN(n)		(1 << (n))
5466d8c9d2SKieran Bingham /* Register 0x0c */
5566d8c9d2SKieran Bingham #define MAX9286_HVEN			BIT(7)
5666d8c9d2SKieran Bingham #define MAX9286_EDC_6BIT_HAMMING	(2 << 5)
5766d8c9d2SKieran Bingham #define MAX9286_EDC_6BIT_CRC		(1 << 5)
5866d8c9d2SKieran Bingham #define MAX9286_EDC_1BIT_PARITY		(0 << 5)
5966d8c9d2SKieran Bingham #define MAX9286_DESEL			BIT(4)
6066d8c9d2SKieran Bingham #define MAX9286_INVVS			BIT(3)
6166d8c9d2SKieran Bingham #define MAX9286_INVHS			BIT(2)
6266d8c9d2SKieran Bingham #define MAX9286_HVSRC_D0		(2 << 0)
6366d8c9d2SKieran Bingham #define MAX9286_HVSRC_D14		(1 << 0)
6466d8c9d2SKieran Bingham #define MAX9286_HVSRC_D18		(0 << 0)
6566d8c9d2SKieran Bingham /* Register 0x0f */
6666d8c9d2SKieran Bingham #define MAX9286_0X0F_RESERVED		BIT(3)
6766d8c9d2SKieran Bingham /* Register 0x12 */
6866d8c9d2SKieran Bingham #define MAX9286_CSILANECNT(n)		(((n) - 1) << 6)
6966d8c9d2SKieran Bingham #define MAX9286_CSIDBL			BIT(5)
7066d8c9d2SKieran Bingham #define MAX9286_DBL			BIT(4)
7166d8c9d2SKieran Bingham #define MAX9286_DATATYPE_USER_8BIT	(11 << 0)
7266d8c9d2SKieran Bingham #define MAX9286_DATATYPE_USER_YUV_12BIT	(10 << 0)
7366d8c9d2SKieran Bingham #define MAX9286_DATATYPE_USER_24BIT	(9 << 0)
7466d8c9d2SKieran Bingham #define MAX9286_DATATYPE_RAW14		(8 << 0)
75b904512bSLaurent Pinchart #define MAX9286_DATATYPE_RAW12		(7 << 0)
7666d8c9d2SKieran Bingham #define MAX9286_DATATYPE_RAW10		(6 << 0)
7766d8c9d2SKieran Bingham #define MAX9286_DATATYPE_RAW8		(5 << 0)
7866d8c9d2SKieran Bingham #define MAX9286_DATATYPE_YUV422_10BIT	(4 << 0)
7966d8c9d2SKieran Bingham #define MAX9286_DATATYPE_YUV422_8BIT	(3 << 0)
8066d8c9d2SKieran Bingham #define MAX9286_DATATYPE_RGB555		(2 << 0)
8166d8c9d2SKieran Bingham #define MAX9286_DATATYPE_RGB565		(1 << 0)
8266d8c9d2SKieran Bingham #define MAX9286_DATATYPE_RGB888		(0 << 0)
8366d8c9d2SKieran Bingham /* Register 0x15 */
84cdcb186eSLaurent Pinchart #define MAX9286_CSI_IMAGE_TYP		BIT(7)
8566d8c9d2SKieran Bingham #define MAX9286_VC(n)			((n) << 5)
8666d8c9d2SKieran Bingham #define MAX9286_VCTYPE			BIT(4)
8766d8c9d2SKieran Bingham #define MAX9286_CSIOUTEN		BIT(3)
88cdcb186eSLaurent Pinchart #define MAX9286_SWP_ENDIAN		BIT(2)
89cdcb186eSLaurent Pinchart #define MAX9286_EN_CCBSYB_CLK_STR	BIT(1)
90cdcb186eSLaurent Pinchart #define MAX9286_EN_GPI_CCBSYB		BIT(0)
9166d8c9d2SKieran Bingham /* Register 0x1b */
9266d8c9d2SKieran Bingham #define MAX9286_SWITCHIN(n)		(1 << ((n) + 4))
9366d8c9d2SKieran Bingham #define MAX9286_ENEQ(n)			(1 << (n))
943697f108SLaurent Pinchart /* Register 0x1c */
953697f108SLaurent Pinchart #define MAX9286_HIGHIMM(n)		BIT((n) + 4)
963697f108SLaurent Pinchart #define MAX9286_I2CSEL			BIT(2)
973697f108SLaurent Pinchart #define MAX9286_HIBW			BIT(1)
983697f108SLaurent Pinchart #define MAX9286_BWS			BIT(0)
9966d8c9d2SKieran Bingham /* Register 0x27 */
10066d8c9d2SKieran Bingham #define MAX9286_LOCKED			BIT(7)
10166d8c9d2SKieran Bingham /* Register 0x31 */
10266d8c9d2SKieran Bingham #define MAX9286_FSYNC_LOCKED		BIT(6)
10366d8c9d2SKieran Bingham /* Register 0x34 */
10466d8c9d2SKieran Bingham #define MAX9286_I2CLOCACK		BIT(7)
10566d8c9d2SKieran Bingham #define MAX9286_I2CSLVSH_1046NS_469NS	(3 << 5)
10666d8c9d2SKieran Bingham #define MAX9286_I2CSLVSH_938NS_352NS	(2 << 5)
10766d8c9d2SKieran Bingham #define MAX9286_I2CSLVSH_469NS_234NS	(1 << 5)
10866d8c9d2SKieran Bingham #define MAX9286_I2CSLVSH_352NS_117NS	(0 << 5)
10966d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_837KBPS	(7 << 2)
11066d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_533KBPS	(6 << 2)
11166d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_339KBPS	(5 << 2)
11266d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_173KBPS	(4 << 2)
11366d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_105KBPS	(3 << 2)
11466d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_84KBPS		(2 << 2)
11566d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_28KBPS		(1 << 2)
11666d8c9d2SKieran Bingham #define MAX9286_I2CMSTBT_8KBPS		(0 << 2)
11766d8c9d2SKieran Bingham #define MAX9286_I2CSLVTO_NONE		(3 << 0)
11866d8c9d2SKieran Bingham #define MAX9286_I2CSLVTO_1024US		(2 << 0)
11966d8c9d2SKieran Bingham #define MAX9286_I2CSLVTO_256US		(1 << 0)
12066d8c9d2SKieran Bingham #define MAX9286_I2CSLVTO_64US		(0 << 0)
12166d8c9d2SKieran Bingham /* Register 0x3b */
12266d8c9d2SKieran Bingham #define MAX9286_REV_TRF(n)		((n) << 4)
12366d8c9d2SKieran Bingham #define MAX9286_REV_AMP(n)		((((n) - 30) / 10) << 1) /* in mV */
12466d8c9d2SKieran Bingham #define MAX9286_REV_AMP_X		BIT(0)
125731c24ffSJacopo Mondi #define MAX9286_REV_AMP_HIGH		170
12666d8c9d2SKieran Bingham /* Register 0x3f */
12766d8c9d2SKieran Bingham #define MAX9286_EN_REV_CFG		BIT(6)
12866d8c9d2SKieran Bingham #define MAX9286_REV_FLEN(n)		((n) - 20)
12966d8c9d2SKieran Bingham /* Register 0x49 */
13066d8c9d2SKieran Bingham #define MAX9286_VIDEO_DETECT_MASK	0x0f
13166d8c9d2SKieran Bingham /* Register 0x69 */
13266d8c9d2SKieran Bingham #define MAX9286_LFLTBMONMASKED		BIT(7)
13366d8c9d2SKieran Bingham #define MAX9286_LOCKMONMASKED		BIT(6)
13466d8c9d2SKieran Bingham #define MAX9286_AUTOCOMBACKEN		BIT(5)
13566d8c9d2SKieran Bingham #define MAX9286_AUTOMASKEN		BIT(4)
13666d8c9d2SKieran Bingham #define MAX9286_MASKLINK(n)		((n) << 0)
13766d8c9d2SKieran Bingham 
13866d8c9d2SKieran Bingham /*
13966d8c9d2SKieran Bingham  * The sink and source pads are created to match the OF graph port numbers so
14066d8c9d2SKieran Bingham  * that their indexes can be used interchangeably.
14166d8c9d2SKieran Bingham  */
14266d8c9d2SKieran Bingham #define MAX9286_NUM_GMSL		4
14366d8c9d2SKieran Bingham #define MAX9286_N_SINKS			4
14466d8c9d2SKieran Bingham #define MAX9286_N_PADS			5
14566d8c9d2SKieran Bingham #define MAX9286_SRC_PAD			4
14666d8c9d2SKieran Bingham 
147f1403802SLaurent Pinchart struct max9286_format_info {
148f1403802SLaurent Pinchart 	u32 code;
149f1403802SLaurent Pinchart 	u8 datatype;
150f1403802SLaurent Pinchart };
151f1403802SLaurent Pinchart 
152e332061bSLaurent Pinchart struct max9286_i2c_speed {
153e332061bSLaurent Pinchart 	u32 rate;
154e332061bSLaurent Pinchart 	u8 mstbt;
155e332061bSLaurent Pinchart };
156e332061bSLaurent Pinchart 
15766d8c9d2SKieran Bingham struct max9286_source {
15866d8c9d2SKieran Bingham 	struct v4l2_subdev *sd;
15966d8c9d2SKieran Bingham 	struct fwnode_handle *fwnode;
160817660f4SThomas Nizan 	struct regulator *regulator;
16166d8c9d2SKieran Bingham };
16266d8c9d2SKieran Bingham 
16386d37bf3SLaurent Pinchart struct max9286_asd {
16486d37bf3SLaurent Pinchart 	struct v4l2_async_subdev base;
16586d37bf3SLaurent Pinchart 	struct max9286_source *source;
16686d37bf3SLaurent Pinchart };
16786d37bf3SLaurent Pinchart 
16886d37bf3SLaurent Pinchart static inline struct max9286_asd *to_max9286_asd(struct v4l2_async_subdev *asd)
16986d37bf3SLaurent Pinchart {
17086d37bf3SLaurent Pinchart 	return container_of(asd, struct max9286_asd, base);
17186d37bf3SLaurent Pinchart }
17266d8c9d2SKieran Bingham 
17366d8c9d2SKieran Bingham struct max9286_priv {
17466d8c9d2SKieran Bingham 	struct i2c_client *client;
17566d8c9d2SKieran Bingham 	struct gpio_desc *gpiod_pwdn;
17666d8c9d2SKieran Bingham 	struct v4l2_subdev sd;
17766d8c9d2SKieran Bingham 	struct media_pad pads[MAX9286_N_PADS];
17866d8c9d2SKieran Bingham 	struct regulator *regulator;
17966d8c9d2SKieran Bingham 
18066d8c9d2SKieran Bingham 	struct gpio_chip gpio;
18166d8c9d2SKieran Bingham 	u8 gpio_state;
18266d8c9d2SKieran Bingham 
18366d8c9d2SKieran Bingham 	struct i2c_mux_core *mux;
18466d8c9d2SKieran Bingham 	unsigned int mux_channel;
18566d8c9d2SKieran Bingham 	bool mux_open;
18666d8c9d2SKieran Bingham 
187f78723ebSJacopo Mondi 	/* The initial reverse control channel amplitude. */
188f78723ebSJacopo Mondi 	u32 init_rev_chan_mv;
189902edc2aSJacopo Mondi 	u32 rev_chan_mv;
190e332061bSLaurent Pinchart 	u8 i2c_mstbt;
1913697f108SLaurent Pinchart 	u32 bus_width;
19285cb767cSJacopo Mondi 
193817660f4SThomas Nizan 	bool use_gpio_poc;
194c9352df7SJacopo Mondi 	u32 gpio_poc[2];
195c9352df7SJacopo Mondi 
19666d8c9d2SKieran Bingham 	struct v4l2_ctrl_handler ctrls;
197cffc9fb1SLaurent Pinchart 	struct v4l2_ctrl *pixelrate_ctrl;
198cffc9fb1SLaurent Pinchart 	unsigned int pixelrate;
19966d8c9d2SKieran Bingham 
20066d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
201cffc9fb1SLaurent Pinchart 	struct v4l2_fract interval;
20266d8c9d2SKieran Bingham 
20366d8c9d2SKieran Bingham 	/* Protects controls and fmt structures */
20466d8c9d2SKieran Bingham 	struct mutex mutex;
20566d8c9d2SKieran Bingham 
20666d8c9d2SKieran Bingham 	unsigned int nsources;
20766d8c9d2SKieran Bingham 	unsigned int source_mask;
20866d8c9d2SKieran Bingham 	unsigned int route_mask;
20966d8c9d2SKieran Bingham 	unsigned int bound_sources;
21066d8c9d2SKieran Bingham 	unsigned int csi2_data_lanes;
21166d8c9d2SKieran Bingham 	struct max9286_source sources[MAX9286_NUM_GMSL];
21266d8c9d2SKieran Bingham 	struct v4l2_async_notifier notifier;
21366d8c9d2SKieran Bingham };
21466d8c9d2SKieran Bingham 
21566d8c9d2SKieran Bingham static struct max9286_source *next_source(struct max9286_priv *priv,
21666d8c9d2SKieran Bingham 					  struct max9286_source *source)
21766d8c9d2SKieran Bingham {
21866d8c9d2SKieran Bingham 	if (!source)
21966d8c9d2SKieran Bingham 		source = &priv->sources[0];
22066d8c9d2SKieran Bingham 	else
22166d8c9d2SKieran Bingham 		source++;
22266d8c9d2SKieran Bingham 
22366d8c9d2SKieran Bingham 	for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) {
22466d8c9d2SKieran Bingham 		if (source->fwnode)
22566d8c9d2SKieran Bingham 			return source;
22666d8c9d2SKieran Bingham 	}
22766d8c9d2SKieran Bingham 
22866d8c9d2SKieran Bingham 	return NULL;
22966d8c9d2SKieran Bingham }
23066d8c9d2SKieran Bingham 
23166d8c9d2SKieran Bingham #define for_each_source(priv, source) \
23266d8c9d2SKieran Bingham 	for ((source) = NULL; ((source) = next_source((priv), (source))); )
23366d8c9d2SKieran Bingham 
23466d8c9d2SKieran Bingham #define to_index(priv, source) ((source) - &(priv)->sources[0])
23566d8c9d2SKieran Bingham 
23666d8c9d2SKieran Bingham static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
23766d8c9d2SKieran Bingham {
23866d8c9d2SKieran Bingham 	return container_of(sd, struct max9286_priv, sd);
23966d8c9d2SKieran Bingham }
24066d8c9d2SKieran Bingham 
241f1403802SLaurent Pinchart static const struct max9286_format_info max9286_formats[] = {
242f1403802SLaurent Pinchart 	{
243f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_UYVY8_1X16,
244f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
245f1403802SLaurent Pinchart 	}, {
246f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_VYUY8_1X16,
247f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
248f1403802SLaurent Pinchart 	}, {
249f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_YUYV8_1X16,
250f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
251f1403802SLaurent Pinchart 	}, {
252f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_YVYU8_1X16,
253f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
254f1403802SLaurent Pinchart 	}, {
255f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
256f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
257f1403802SLaurent Pinchart 	}, {
258f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
259f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
260f1403802SLaurent Pinchart 	}, {
261f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
262f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
263f1403802SLaurent Pinchart 	}, {
264f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
265f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
266f1403802SLaurent Pinchart 	},
267f1403802SLaurent Pinchart };
268f1403802SLaurent Pinchart 
269e332061bSLaurent Pinchart static const struct max9286_i2c_speed max9286_i2c_speeds[] = {
270e332061bSLaurent Pinchart 	{ .rate =   8470, .mstbt = MAX9286_I2CMSTBT_8KBPS },
271e332061bSLaurent Pinchart 	{ .rate =  28300, .mstbt = MAX9286_I2CMSTBT_28KBPS },
272e332061bSLaurent Pinchart 	{ .rate =  84700, .mstbt = MAX9286_I2CMSTBT_84KBPS },
273e332061bSLaurent Pinchart 	{ .rate = 105000, .mstbt = MAX9286_I2CMSTBT_105KBPS },
274e332061bSLaurent Pinchart 	{ .rate = 173000, .mstbt = MAX9286_I2CMSTBT_173KBPS },
275e332061bSLaurent Pinchart 	{ .rate = 339000, .mstbt = MAX9286_I2CMSTBT_339KBPS },
276e332061bSLaurent Pinchart 	{ .rate = 533000, .mstbt = MAX9286_I2CMSTBT_533KBPS },
277e332061bSLaurent Pinchart 	{ .rate = 837000, .mstbt = MAX9286_I2CMSTBT_837KBPS },
278e332061bSLaurent Pinchart };
279e332061bSLaurent Pinchart 
28066d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
28166d8c9d2SKieran Bingham  * I2C IO
28266d8c9d2SKieran Bingham  */
28366d8c9d2SKieran Bingham 
28466d8c9d2SKieran Bingham static int max9286_read(struct max9286_priv *priv, u8 reg)
28566d8c9d2SKieran Bingham {
28666d8c9d2SKieran Bingham 	int ret;
28766d8c9d2SKieran Bingham 
28866d8c9d2SKieran Bingham 	ret = i2c_smbus_read_byte_data(priv->client, reg);
28966d8c9d2SKieran Bingham 	if (ret < 0)
29066d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
29166d8c9d2SKieran Bingham 			"%s: register 0x%02x read failed (%d)\n",
29266d8c9d2SKieran Bingham 			__func__, reg, ret);
29366d8c9d2SKieran Bingham 
29466d8c9d2SKieran Bingham 	return ret;
29566d8c9d2SKieran Bingham }
29666d8c9d2SKieran Bingham 
29766d8c9d2SKieran Bingham static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val)
29866d8c9d2SKieran Bingham {
29966d8c9d2SKieran Bingham 	int ret;
30066d8c9d2SKieran Bingham 
30166d8c9d2SKieran Bingham 	ret = i2c_smbus_write_byte_data(priv->client, reg, val);
30266d8c9d2SKieran Bingham 	if (ret < 0)
30366d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
30466d8c9d2SKieran Bingham 			"%s: register 0x%02x write failed (%d)\n",
30566d8c9d2SKieran Bingham 			__func__, reg, ret);
30666d8c9d2SKieran Bingham 
30766d8c9d2SKieran Bingham 	return ret;
30866d8c9d2SKieran Bingham }
30966d8c9d2SKieran Bingham 
31066d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
31166d8c9d2SKieran Bingham  * I2C Multiplexer
31266d8c9d2SKieran Bingham  */
31366d8c9d2SKieran Bingham 
31466d8c9d2SKieran Bingham static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf)
31566d8c9d2SKieran Bingham {
31666d8c9d2SKieran Bingham 	max9286_write(priv, 0x0a, conf);
31766d8c9d2SKieran Bingham 
31866d8c9d2SKieran Bingham 	/*
31966d8c9d2SKieran Bingham 	 * We must sleep after any change to the forward or reverse channel
32066d8c9d2SKieran Bingham 	 * configuration.
32166d8c9d2SKieran Bingham 	 */
32266d8c9d2SKieran Bingham 	usleep_range(3000, 5000);
32366d8c9d2SKieran Bingham }
32466d8c9d2SKieran Bingham 
32566d8c9d2SKieran Bingham static void max9286_i2c_mux_open(struct max9286_priv *priv)
32666d8c9d2SKieran Bingham {
32766d8c9d2SKieran Bingham 	/* Open all channels on the MAX9286 */
32866d8c9d2SKieran Bingham 	max9286_i2c_mux_configure(priv, 0xff);
32966d8c9d2SKieran Bingham 
33066d8c9d2SKieran Bingham 	priv->mux_open = true;
33166d8c9d2SKieran Bingham }
33266d8c9d2SKieran Bingham 
33366d8c9d2SKieran Bingham static void max9286_i2c_mux_close(struct max9286_priv *priv)
33466d8c9d2SKieran Bingham {
33566d8c9d2SKieran Bingham 	/*
33666d8c9d2SKieran Bingham 	 * Ensure that both the forward and reverse channel are disabled on the
33766d8c9d2SKieran Bingham 	 * mux, and that the channel ID is invalidated to ensure we reconfigure
33866d8c9d2SKieran Bingham 	 * on the next max9286_i2c_mux_select() call.
33966d8c9d2SKieran Bingham 	 */
34066d8c9d2SKieran Bingham 	max9286_i2c_mux_configure(priv, 0x00);
34166d8c9d2SKieran Bingham 
34266d8c9d2SKieran Bingham 	priv->mux_open = false;
34366d8c9d2SKieran Bingham 	priv->mux_channel = -1;
34466d8c9d2SKieran Bingham }
34566d8c9d2SKieran Bingham 
34666d8c9d2SKieran Bingham static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
34766d8c9d2SKieran Bingham {
34866d8c9d2SKieran Bingham 	struct max9286_priv *priv = i2c_mux_priv(muxc);
34966d8c9d2SKieran Bingham 
35066d8c9d2SKieran Bingham 	/* Channel select is disabled when configured in the opened state. */
35166d8c9d2SKieran Bingham 	if (priv->mux_open)
35266d8c9d2SKieran Bingham 		return 0;
35366d8c9d2SKieran Bingham 
35466d8c9d2SKieran Bingham 	if (priv->mux_channel == chan)
35566d8c9d2SKieran Bingham 		return 0;
35666d8c9d2SKieran Bingham 
35766d8c9d2SKieran Bingham 	priv->mux_channel = chan;
35866d8c9d2SKieran Bingham 
3593de09c7aSJacopo Mondi 	max9286_i2c_mux_configure(priv, MAX9286_FWDCCEN(chan) |
36066d8c9d2SKieran Bingham 					MAX9286_REVCCEN(chan));
36166d8c9d2SKieran Bingham 
36266d8c9d2SKieran Bingham 	return 0;
36366d8c9d2SKieran Bingham }
36466d8c9d2SKieran Bingham 
36566d8c9d2SKieran Bingham static int max9286_i2c_mux_init(struct max9286_priv *priv)
36666d8c9d2SKieran Bingham {
36766d8c9d2SKieran Bingham 	struct max9286_source *source;
36866d8c9d2SKieran Bingham 	int ret;
36966d8c9d2SKieran Bingham 
37066d8c9d2SKieran Bingham 	if (!i2c_check_functionality(priv->client->adapter,
37166d8c9d2SKieran Bingham 				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
37266d8c9d2SKieran Bingham 		return -ENODEV;
37366d8c9d2SKieran Bingham 
37466d8c9d2SKieran Bingham 	priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,
37566d8c9d2SKieran Bingham 				  priv->nsources, 0, I2C_MUX_LOCKED,
37666d8c9d2SKieran Bingham 				  max9286_i2c_mux_select, NULL);
37766d8c9d2SKieran Bingham 	if (!priv->mux)
37866d8c9d2SKieran Bingham 		return -ENOMEM;
37966d8c9d2SKieran Bingham 
38066d8c9d2SKieran Bingham 	priv->mux->priv = priv;
38166d8c9d2SKieran Bingham 
38266d8c9d2SKieran Bingham 	for_each_source(priv, source) {
38366d8c9d2SKieran Bingham 		unsigned int index = to_index(priv, source);
38466d8c9d2SKieran Bingham 
38566d8c9d2SKieran Bingham 		ret = i2c_mux_add_adapter(priv->mux, 0, index, 0);
38666d8c9d2SKieran Bingham 		if (ret < 0)
38766d8c9d2SKieran Bingham 			goto error;
38866d8c9d2SKieran Bingham 	}
38966d8c9d2SKieran Bingham 
39066d8c9d2SKieran Bingham 	return 0;
39166d8c9d2SKieran Bingham 
39266d8c9d2SKieran Bingham error:
39366d8c9d2SKieran Bingham 	i2c_mux_del_adapters(priv->mux);
39466d8c9d2SKieran Bingham 	return ret;
39566d8c9d2SKieran Bingham }
39666d8c9d2SKieran Bingham 
39766d8c9d2SKieran Bingham static void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
39866d8c9d2SKieran Bingham {
39966d8c9d2SKieran Bingham 	u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
400e332061bSLaurent Pinchart 		    priv->i2c_mstbt;
40166d8c9d2SKieran Bingham 
40266d8c9d2SKieran Bingham 	if (localack)
40366d8c9d2SKieran Bingham 		config |= MAX9286_I2CLOCACK;
40466d8c9d2SKieran Bingham 
40566d8c9d2SKieran Bingham 	max9286_write(priv, 0x34, config);
40666d8c9d2SKieran Bingham 	usleep_range(3000, 5000);
40766d8c9d2SKieran Bingham }
40866d8c9d2SKieran Bingham 
4095a386b1fSJacopo Mondi static void max9286_reverse_channel_setup(struct max9286_priv *priv,
4105a386b1fSJacopo Mondi 					  unsigned int chan_amplitude)
41102b57eb3SJacopo Mondi {
412902edc2aSJacopo Mondi 	u8 chan_config;
413902edc2aSJacopo Mondi 
414902edc2aSJacopo Mondi 	if (priv->rev_chan_mv == chan_amplitude)
415902edc2aSJacopo Mondi 		return;
416902edc2aSJacopo Mondi 
417902edc2aSJacopo Mondi 	priv->rev_chan_mv = chan_amplitude;
418902edc2aSJacopo Mondi 
4195a386b1fSJacopo Mondi 	/* Reverse channel transmission time: default to 1. */
420902edc2aSJacopo Mondi 	chan_config = MAX9286_REV_TRF(1);
4215a386b1fSJacopo Mondi 
42202b57eb3SJacopo Mondi 	/*
42302b57eb3SJacopo Mondi 	 * Reverse channel setup.
42402b57eb3SJacopo Mondi 	 *
42502b57eb3SJacopo Mondi 	 * - Enable custom reverse channel configuration (through register 0x3f)
42602b57eb3SJacopo Mondi 	 *   and set the first pulse length to 35 clock cycles.
4275a386b1fSJacopo Mondi 	 * - Adjust reverse channel amplitude: values > 130 are programmed
4285a386b1fSJacopo Mondi 	 *   using the additional +100mV REV_AMP_X boost flag
42902b57eb3SJacopo Mondi 	 */
43002b57eb3SJacopo Mondi 	max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
4315a386b1fSJacopo Mondi 
4325a386b1fSJacopo Mondi 	if (chan_amplitude > 100) {
4335a386b1fSJacopo Mondi 		/* It is not possible to express values (100 < x < 130) */
4345a386b1fSJacopo Mondi 		chan_amplitude = max(30U, chan_amplitude - 100);
4355a386b1fSJacopo Mondi 		chan_config |= MAX9286_REV_AMP_X;
4365a386b1fSJacopo Mondi 	}
4375a386b1fSJacopo Mondi 	max9286_write(priv, 0x3b, chan_config | MAX9286_REV_AMP(chan_amplitude));
43802b57eb3SJacopo Mondi 	usleep_range(2000, 2500);
43902b57eb3SJacopo Mondi }
44002b57eb3SJacopo Mondi 
44166d8c9d2SKieran Bingham /*
44266d8c9d2SKieran Bingham  * max9286_check_video_links() - Make sure video links are detected and locked
44366d8c9d2SKieran Bingham  *
44466d8c9d2SKieran Bingham  * Performs safety checks on video link status. Make sure they are detected
44566d8c9d2SKieran Bingham  * and all enabled links are locked.
44666d8c9d2SKieran Bingham  *
44766d8c9d2SKieran Bingham  * Returns 0 for success, -EIO for errors.
44866d8c9d2SKieran Bingham  */
44966d8c9d2SKieran Bingham static int max9286_check_video_links(struct max9286_priv *priv)
45066d8c9d2SKieran Bingham {
45166d8c9d2SKieran Bingham 	unsigned int i;
45266d8c9d2SKieran Bingham 	int ret;
45366d8c9d2SKieran Bingham 
45466d8c9d2SKieran Bingham 	/*
45566d8c9d2SKieran Bingham 	 * Make sure valid video links are detected.
45666d8c9d2SKieran Bingham 	 * The delay is not characterized in de-serializer manual, wait up
45766d8c9d2SKieran Bingham 	 * to 5 ms.
45866d8c9d2SKieran Bingham 	 */
45966d8c9d2SKieran Bingham 	for (i = 0; i < 10; i++) {
46066d8c9d2SKieran Bingham 		ret = max9286_read(priv, 0x49);
46166d8c9d2SKieran Bingham 		if (ret < 0)
46266d8c9d2SKieran Bingham 			return -EIO;
46366d8c9d2SKieran Bingham 
46466d8c9d2SKieran Bingham 		if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask)
46566d8c9d2SKieran Bingham 			break;
46666d8c9d2SKieran Bingham 
46766d8c9d2SKieran Bingham 		usleep_range(350, 500);
46866d8c9d2SKieran Bingham 	}
46966d8c9d2SKieran Bingham 
47066d8c9d2SKieran Bingham 	if (i == 10) {
47166d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
47266d8c9d2SKieran Bingham 			"Unable to detect video links: 0x%02x\n", ret);
47366d8c9d2SKieran Bingham 		return -EIO;
47466d8c9d2SKieran Bingham 	}
47566d8c9d2SKieran Bingham 
47666d8c9d2SKieran Bingham 	/* Make sure all enabled links are locked (4ms max). */
47766d8c9d2SKieran Bingham 	for (i = 0; i < 10; i++) {
47866d8c9d2SKieran Bingham 		ret = max9286_read(priv, 0x27);
47966d8c9d2SKieran Bingham 		if (ret < 0)
48066d8c9d2SKieran Bingham 			return -EIO;
48166d8c9d2SKieran Bingham 
48266d8c9d2SKieran Bingham 		if (ret & MAX9286_LOCKED)
48366d8c9d2SKieran Bingham 			break;
48466d8c9d2SKieran Bingham 
48566d8c9d2SKieran Bingham 		usleep_range(350, 450);
48666d8c9d2SKieran Bingham 	}
48766d8c9d2SKieran Bingham 
48866d8c9d2SKieran Bingham 	if (i == 10) {
48966d8c9d2SKieran Bingham 		dev_err(&priv->client->dev, "Not all enabled links locked\n");
49066d8c9d2SKieran Bingham 		return -EIO;
49166d8c9d2SKieran Bingham 	}
49266d8c9d2SKieran Bingham 
49366d8c9d2SKieran Bingham 	return 0;
49466d8c9d2SKieran Bingham }
49566d8c9d2SKieran Bingham 
49666d8c9d2SKieran Bingham /*
49766d8c9d2SKieran Bingham  * max9286_check_config_link() - Detect and wait for configuration links
49866d8c9d2SKieran Bingham  *
49966d8c9d2SKieran Bingham  * Determine if the configuration channel is up and settled for a link.
50066d8c9d2SKieran Bingham  *
50166d8c9d2SKieran Bingham  * Returns 0 for success, -EIO for errors.
50266d8c9d2SKieran Bingham  */
50366d8c9d2SKieran Bingham static int max9286_check_config_link(struct max9286_priv *priv,
50466d8c9d2SKieran Bingham 				     unsigned int source_mask)
50566d8c9d2SKieran Bingham {
50666d8c9d2SKieran Bingham 	unsigned int conflink_mask = (source_mask & 0x0f) << 4;
50766d8c9d2SKieran Bingham 	unsigned int i;
50866d8c9d2SKieran Bingham 	int ret;
50966d8c9d2SKieran Bingham 
51066d8c9d2SKieran Bingham 	/*
51166d8c9d2SKieran Bingham 	 * Make sure requested configuration links are detected.
51266d8c9d2SKieran Bingham 	 * The delay is not characterized in the chip manual: wait up
51366d8c9d2SKieran Bingham 	 * to 5 milliseconds.
51466d8c9d2SKieran Bingham 	 */
51566d8c9d2SKieran Bingham 	for (i = 0; i < 10; i++) {
516e5b95c8fSColin Ian King 		ret = max9286_read(priv, 0x49);
51766d8c9d2SKieran Bingham 		if (ret < 0)
51866d8c9d2SKieran Bingham 			return -EIO;
51966d8c9d2SKieran Bingham 
520e5b95c8fSColin Ian King 		ret &= 0xf0;
52166d8c9d2SKieran Bingham 		if (ret == conflink_mask)
52266d8c9d2SKieran Bingham 			break;
52366d8c9d2SKieran Bingham 
52466d8c9d2SKieran Bingham 		usleep_range(350, 500);
52566d8c9d2SKieran Bingham 	}
52666d8c9d2SKieran Bingham 
52766d8c9d2SKieran Bingham 	if (ret != conflink_mask) {
52866d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
52966d8c9d2SKieran Bingham 			"Unable to detect configuration links: 0x%02x expected 0x%02x\n",
53066d8c9d2SKieran Bingham 			ret, conflink_mask);
53166d8c9d2SKieran Bingham 		return -EIO;
53266d8c9d2SKieran Bingham 	}
53366d8c9d2SKieran Bingham 
53466d8c9d2SKieran Bingham 	dev_info(&priv->client->dev,
53566d8c9d2SKieran Bingham 		 "Successfully detected configuration links after %u loops: 0x%02x\n",
53666d8c9d2SKieran Bingham 		 i, conflink_mask);
53766d8c9d2SKieran Bingham 
53866d8c9d2SKieran Bingham 	return 0;
53966d8c9d2SKieran Bingham }
54066d8c9d2SKieran Bingham 
541f1403802SLaurent Pinchart static void max9286_set_video_format(struct max9286_priv *priv,
542f1403802SLaurent Pinchart 				     const struct v4l2_mbus_framefmt *format)
543f1403802SLaurent Pinchart {
544f1403802SLaurent Pinchart 	const struct max9286_format_info *info = NULL;
545f1403802SLaurent Pinchart 	unsigned int i;
546f1403802SLaurent Pinchart 
547f1403802SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
548f1403802SLaurent Pinchart 		if (max9286_formats[i].code == format->code) {
549f1403802SLaurent Pinchart 			info = &max9286_formats[i];
550f1403802SLaurent Pinchart 			break;
551f1403802SLaurent Pinchart 		}
552f1403802SLaurent Pinchart 	}
553f1403802SLaurent Pinchart 
554f1403802SLaurent Pinchart 	if (WARN_ON(!info))
555f1403802SLaurent Pinchart 		return;
556f1403802SLaurent Pinchart 
557f1403802SLaurent Pinchart 	/*
558cdcb186eSLaurent Pinchart 	 * Video format setup: disable CSI output, set VC according to Link
559cdcb186eSLaurent Pinchart 	 * number, enable I2C clock stretching when CCBSY is low, enable CCBSY
560cdcb186eSLaurent Pinchart 	 * in external GPI-to-GPO mode.
561f1403802SLaurent Pinchart 	 */
562cdcb186eSLaurent Pinchart 	max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_EN_CCBSYB_CLK_STR |
563cdcb186eSLaurent Pinchart 		      MAX9286_EN_GPI_CCBSYB);
564f1403802SLaurent Pinchart 
565f1403802SLaurent Pinchart 	/* Enable CSI-2 Lane D0-D3 only, DBL mode. */
566f1403802SLaurent Pinchart 	max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
567f1403802SLaurent Pinchart 		      MAX9286_CSILANECNT(priv->csi2_data_lanes) |
568f1403802SLaurent Pinchart 		      info->datatype);
569f1403802SLaurent Pinchart 
57040f75457SLaurent Pinchart 	/*
57140f75457SLaurent Pinchart 	 * Enable HS/VS encoding, use HS as line valid source, use D14/15 for
57240f75457SLaurent Pinchart 	 * HS/VS, invert VS.
57340f75457SLaurent Pinchart 	 */
57440f75457SLaurent Pinchart 	max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_DESEL |
57540f75457SLaurent Pinchart 		      MAX9286_INVVS | MAX9286_HVSRC_D14);
576f1403802SLaurent Pinchart }
577f1403802SLaurent Pinchart 
578cffc9fb1SLaurent Pinchart static void max9286_set_fsync_period(struct max9286_priv *priv)
579cffc9fb1SLaurent Pinchart {
580cffc9fb1SLaurent Pinchart 	u32 fsync;
581cffc9fb1SLaurent Pinchart 
582cffc9fb1SLaurent Pinchart 	if (!priv->interval.numerator || !priv->interval.denominator) {
583cffc9fb1SLaurent Pinchart 		/*
584cffc9fb1SLaurent Pinchart 		 * Special case, a null interval enables automatic FRAMESYNC
585cffc9fb1SLaurent Pinchart 		 * mode. FRAMESYNC is taken from the slowest link.
586cffc9fb1SLaurent Pinchart 		 */
587cffc9fb1SLaurent Pinchart 		max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
588cffc9fb1SLaurent Pinchart 			      MAX9286_FSYNCMETH_AUTO);
589cffc9fb1SLaurent Pinchart 		return;
590cffc9fb1SLaurent Pinchart 	}
591cffc9fb1SLaurent Pinchart 
592cffc9fb1SLaurent Pinchart 	/*
593cffc9fb1SLaurent Pinchart 	 * Manual FRAMESYNC
594cffc9fb1SLaurent Pinchart 	 *
595cffc9fb1SLaurent Pinchart 	 * The FRAMESYNC generator is configured with a period expressed as a
596cffc9fb1SLaurent Pinchart 	 * number of PCLK periods.
597cffc9fb1SLaurent Pinchart 	 */
598cffc9fb1SLaurent Pinchart 	fsync = div_u64((u64)priv->pixelrate * priv->interval.numerator,
599cffc9fb1SLaurent Pinchart 			priv->interval.denominator);
600cffc9fb1SLaurent Pinchart 
601cffc9fb1SLaurent Pinchart 	dev_dbg(&priv->client->dev, "fsync period %u (pclk %u)\n", fsync,
602cffc9fb1SLaurent Pinchart 		priv->pixelrate);
603cffc9fb1SLaurent Pinchart 
604cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_OUT |
605cffc9fb1SLaurent Pinchart 		      MAX9286_FSYNCMETH_MANUAL);
606cffc9fb1SLaurent Pinchart 
607cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x06, (fsync >> 0) & 0xff);
608cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x07, (fsync >> 8) & 0xff);
609cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x08, (fsync >> 16) & 0xff);
610cffc9fb1SLaurent Pinchart }
611cffc9fb1SLaurent Pinchart 
61266d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
61366d8c9d2SKieran Bingham  * V4L2 Subdev
61466d8c9d2SKieran Bingham  */
61566d8c9d2SKieran Bingham 
61666d8c9d2SKieran Bingham static int max9286_set_pixelrate(struct max9286_priv *priv)
61766d8c9d2SKieran Bingham {
61866d8c9d2SKieran Bingham 	struct max9286_source *source = NULL;
61966d8c9d2SKieran Bingham 	u64 pixelrate = 0;
62066d8c9d2SKieran Bingham 
62166d8c9d2SKieran Bingham 	for_each_source(priv, source) {
62266d8c9d2SKieran Bingham 		struct v4l2_ctrl *ctrl;
62366d8c9d2SKieran Bingham 		u64 source_rate = 0;
62466d8c9d2SKieran Bingham 
62566d8c9d2SKieran Bingham 		/* Pixel rate is mandatory to be reported by sources. */
62666d8c9d2SKieran Bingham 		ctrl = v4l2_ctrl_find(source->sd->ctrl_handler,
62766d8c9d2SKieran Bingham 				      V4L2_CID_PIXEL_RATE);
62866d8c9d2SKieran Bingham 		if (!ctrl) {
62966d8c9d2SKieran Bingham 			pixelrate = 0;
63066d8c9d2SKieran Bingham 			break;
63166d8c9d2SKieran Bingham 		}
63266d8c9d2SKieran Bingham 
63366d8c9d2SKieran Bingham 		/* All source must report the same pixel rate. */
63466d8c9d2SKieran Bingham 		source_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
63566d8c9d2SKieran Bingham 		if (!pixelrate) {
63666d8c9d2SKieran Bingham 			pixelrate = source_rate;
63766d8c9d2SKieran Bingham 		} else if (pixelrate != source_rate) {
63866d8c9d2SKieran Bingham 			dev_err(&priv->client->dev,
63966d8c9d2SKieran Bingham 				"Unable to calculate pixel rate\n");
64066d8c9d2SKieran Bingham 			return -EINVAL;
64166d8c9d2SKieran Bingham 		}
64266d8c9d2SKieran Bingham 	}
64366d8c9d2SKieran Bingham 
64466d8c9d2SKieran Bingham 	if (!pixelrate) {
64566d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
64666d8c9d2SKieran Bingham 			"No pixel rate control available in sources\n");
64766d8c9d2SKieran Bingham 		return -EINVAL;
64866d8c9d2SKieran Bingham 	}
64966d8c9d2SKieran Bingham 
650cffc9fb1SLaurent Pinchart 	priv->pixelrate = pixelrate;
651cffc9fb1SLaurent Pinchart 
65266d8c9d2SKieran Bingham 	/*
65366d8c9d2SKieran Bingham 	 * The CSI-2 transmitter pixel rate is the single source rate multiplied
65466d8c9d2SKieran Bingham 	 * by the number of available sources.
65566d8c9d2SKieran Bingham 	 */
656cffc9fb1SLaurent Pinchart 	return v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
65766d8c9d2SKieran Bingham 				      pixelrate * priv->nsources);
65866d8c9d2SKieran Bingham }
65966d8c9d2SKieran Bingham 
66066d8c9d2SKieran Bingham static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
66166d8c9d2SKieran Bingham 				struct v4l2_subdev *subdev,
66266d8c9d2SKieran Bingham 				struct v4l2_async_subdev *asd)
66366d8c9d2SKieran Bingham {
66466d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(notifier->sd);
66586d37bf3SLaurent Pinchart 	struct max9286_source *source = to_max9286_asd(asd)->source;
66666d8c9d2SKieran Bingham 	unsigned int index = to_index(priv, source);
66766d8c9d2SKieran Bingham 	unsigned int src_pad;
66866d8c9d2SKieran Bingham 	int ret;
66966d8c9d2SKieran Bingham 
67066d8c9d2SKieran Bingham 	ret = media_entity_get_fwnode_pad(&subdev->entity,
67166d8c9d2SKieran Bingham 					  source->fwnode,
67266d8c9d2SKieran Bingham 					  MEDIA_PAD_FL_SOURCE);
67366d8c9d2SKieran Bingham 	if (ret < 0) {
67466d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
67566d8c9d2SKieran Bingham 			"Failed to find pad for %s\n", subdev->name);
67666d8c9d2SKieran Bingham 		return ret;
67766d8c9d2SKieran Bingham 	}
67866d8c9d2SKieran Bingham 
67966d8c9d2SKieran Bingham 	priv->bound_sources |= BIT(index);
68066d8c9d2SKieran Bingham 	source->sd = subdev;
68166d8c9d2SKieran Bingham 	src_pad = ret;
68266d8c9d2SKieran Bingham 
68366d8c9d2SKieran Bingham 	ret = media_create_pad_link(&source->sd->entity, src_pad,
68466d8c9d2SKieran Bingham 				    &priv->sd.entity, index,
68566d8c9d2SKieran Bingham 				    MEDIA_LNK_FL_ENABLED |
68666d8c9d2SKieran Bingham 				    MEDIA_LNK_FL_IMMUTABLE);
68766d8c9d2SKieran Bingham 	if (ret) {
68866d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
68966d8c9d2SKieran Bingham 			"Unable to link %s:%u -> %s:%u\n",
69066d8c9d2SKieran Bingham 			source->sd->name, src_pad, priv->sd.name, index);
69166d8c9d2SKieran Bingham 		return ret;
69266d8c9d2SKieran Bingham 	}
69366d8c9d2SKieran Bingham 
69466d8c9d2SKieran Bingham 	dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n",
69566d8c9d2SKieran Bingham 		subdev->name, src_pad, index);
69666d8c9d2SKieran Bingham 
69766d8c9d2SKieran Bingham 	/*
6984ff5278dSJacopo Mondi 	 * As we register a subdev notifiers we won't get a .complete() callback
6994ff5278dSJacopo Mondi 	 * here, so we have to use bound_sources to identify when all remote
7004ff5278dSJacopo Mondi 	 * serializers have probed.
70166d8c9d2SKieran Bingham 	 */
70266d8c9d2SKieran Bingham 	if (priv->bound_sources != priv->source_mask)
70366d8c9d2SKieran Bingham 		return 0;
70466d8c9d2SKieran Bingham 
70566d8c9d2SKieran Bingham 	/*
70666d8c9d2SKieran Bingham 	 * All enabled sources have probed and enabled their reverse control
70766d8c9d2SKieran Bingham 	 * channels:
70866d8c9d2SKieran Bingham 	 *
70985cb767cSJacopo Mondi 	 * - Increase the reverse channel amplitude to compensate for the
710731c24ffSJacopo Mondi 	 *   remote ends high threshold
71166d8c9d2SKieran Bingham 	 * - Verify all configuration links are properly detected
71266d8c9d2SKieran Bingham 	 * - Disable auto-ack as communication on the control channel are now
71366d8c9d2SKieran Bingham 	 *   stable.
71466d8c9d2SKieran Bingham 	 */
715731c24ffSJacopo Mondi 	max9286_reverse_channel_setup(priv, MAX9286_REV_AMP_HIGH);
71666d8c9d2SKieran Bingham 	max9286_check_config_link(priv, priv->source_mask);
71766d8c9d2SKieran Bingham 	max9286_configure_i2c(priv, false);
71866d8c9d2SKieran Bingham 
71966d8c9d2SKieran Bingham 	return max9286_set_pixelrate(priv);
72066d8c9d2SKieran Bingham }
72166d8c9d2SKieran Bingham 
72266d8c9d2SKieran Bingham static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
72366d8c9d2SKieran Bingham 				  struct v4l2_subdev *subdev,
72466d8c9d2SKieran Bingham 				  struct v4l2_async_subdev *asd)
72566d8c9d2SKieran Bingham {
72666d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(notifier->sd);
72786d37bf3SLaurent Pinchart 	struct max9286_source *source = to_max9286_asd(asd)->source;
72866d8c9d2SKieran Bingham 	unsigned int index = to_index(priv, source);
72966d8c9d2SKieran Bingham 
73066d8c9d2SKieran Bingham 	source->sd = NULL;
73166d8c9d2SKieran Bingham 	priv->bound_sources &= ~BIT(index);
73266d8c9d2SKieran Bingham }
73366d8c9d2SKieran Bingham 
73466d8c9d2SKieran Bingham static const struct v4l2_async_notifier_operations max9286_notify_ops = {
73566d8c9d2SKieran Bingham 	.bound = max9286_notify_bound,
73666d8c9d2SKieran Bingham 	.unbind = max9286_notify_unbind,
73766d8c9d2SKieran Bingham };
73866d8c9d2SKieran Bingham 
73966d8c9d2SKieran Bingham static int max9286_v4l2_notifier_register(struct max9286_priv *priv)
74066d8c9d2SKieran Bingham {
74166d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
74266d8c9d2SKieran Bingham 	struct max9286_source *source = NULL;
74366d8c9d2SKieran Bingham 	int ret;
74466d8c9d2SKieran Bingham 
74566d8c9d2SKieran Bingham 	if (!priv->nsources)
74666d8c9d2SKieran Bingham 		return 0;
74766d8c9d2SKieran Bingham 
7483c8c1539SSakari Ailus 	v4l2_async_nf_init(&priv->notifier);
74966d8c9d2SKieran Bingham 
75066d8c9d2SKieran Bingham 	for_each_source(priv, source) {
75166d8c9d2SKieran Bingham 		unsigned int i = to_index(priv, source);
752b01edcbdSLaurent Pinchart 		struct max9286_asd *mas;
75366d8c9d2SKieran Bingham 
7543c8c1539SSakari Ailus 		mas = v4l2_async_nf_add_fwnode(&priv->notifier, source->fwnode,
755b01edcbdSLaurent Pinchart 					       struct max9286_asd);
756b01edcbdSLaurent Pinchart 		if (IS_ERR(mas)) {
75786d37bf3SLaurent Pinchart 			dev_err(dev, "Failed to add subdev for source %u: %ld",
758b01edcbdSLaurent Pinchart 				i, PTR_ERR(mas));
7593c8c1539SSakari Ailus 			v4l2_async_nf_cleanup(&priv->notifier);
760b01edcbdSLaurent Pinchart 			return PTR_ERR(mas);
76166d8c9d2SKieran Bingham 		}
76266d8c9d2SKieran Bingham 
763b01edcbdSLaurent Pinchart 		mas->source = source;
76466d8c9d2SKieran Bingham 	}
76566d8c9d2SKieran Bingham 
76666d8c9d2SKieran Bingham 	priv->notifier.ops = &max9286_notify_ops;
76766d8c9d2SKieran Bingham 
7683c8c1539SSakari Ailus 	ret = v4l2_async_subdev_nf_register(&priv->sd, &priv->notifier);
76966d8c9d2SKieran Bingham 	if (ret) {
77066d8c9d2SKieran Bingham 		dev_err(dev, "Failed to register subdev_notifier");
7713c8c1539SSakari Ailus 		v4l2_async_nf_cleanup(&priv->notifier);
77266d8c9d2SKieran Bingham 		return ret;
77366d8c9d2SKieran Bingham 	}
77466d8c9d2SKieran Bingham 
77566d8c9d2SKieran Bingham 	return 0;
77666d8c9d2SKieran Bingham }
77766d8c9d2SKieran Bingham 
77866d8c9d2SKieran Bingham static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv)
77966d8c9d2SKieran Bingham {
78066d8c9d2SKieran Bingham 	if (!priv->nsources)
78166d8c9d2SKieran Bingham 		return;
78266d8c9d2SKieran Bingham 
7833c8c1539SSakari Ailus 	v4l2_async_nf_unregister(&priv->notifier);
7843c8c1539SSakari Ailus 	v4l2_async_nf_cleanup(&priv->notifier);
78566d8c9d2SKieran Bingham }
78666d8c9d2SKieran Bingham 
78766d8c9d2SKieran Bingham static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
78866d8c9d2SKieran Bingham {
78966d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(sd);
79066d8c9d2SKieran Bingham 	struct max9286_source *source;
79166d8c9d2SKieran Bingham 	unsigned int i;
79266d8c9d2SKieran Bingham 	bool sync = false;
79366d8c9d2SKieran Bingham 	int ret;
79466d8c9d2SKieran Bingham 
79566d8c9d2SKieran Bingham 	if (enable) {
796f1403802SLaurent Pinchart 		const struct v4l2_mbus_framefmt *format;
797f1403802SLaurent Pinchart 
798f1403802SLaurent Pinchart 		/*
799f1403802SLaurent Pinchart 		 * Get the format from the first used sink pad, as all sink
800f1403802SLaurent Pinchart 		 * formats must be identical.
801f1403802SLaurent Pinchart 		 */
802f1403802SLaurent Pinchart 		format = &priv->fmt[__ffs(priv->bound_sources)];
803f1403802SLaurent Pinchart 
804f1403802SLaurent Pinchart 		max9286_set_video_format(priv, format);
805cffc9fb1SLaurent Pinchart 		max9286_set_fsync_period(priv);
806cffc9fb1SLaurent Pinchart 
80766d8c9d2SKieran Bingham 		/*
80866d8c9d2SKieran Bingham 		 * The frame sync between cameras is transmitted across the
80966d8c9d2SKieran Bingham 		 * reverse channel as GPIO. We must open all channels while
81066d8c9d2SKieran Bingham 		 * streaming to allow this synchronisation signal to be shared.
81166d8c9d2SKieran Bingham 		 */
81266d8c9d2SKieran Bingham 		max9286_i2c_mux_open(priv);
81366d8c9d2SKieran Bingham 
81466d8c9d2SKieran Bingham 		/* Start all cameras. */
81566d8c9d2SKieran Bingham 		for_each_source(priv, source) {
81666d8c9d2SKieran Bingham 			ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
81766d8c9d2SKieran Bingham 			if (ret)
81866d8c9d2SKieran Bingham 				return ret;
81966d8c9d2SKieran Bingham 		}
82066d8c9d2SKieran Bingham 
82166d8c9d2SKieran Bingham 		ret = max9286_check_video_links(priv);
82266d8c9d2SKieran Bingham 		if (ret)
82366d8c9d2SKieran Bingham 			return ret;
82466d8c9d2SKieran Bingham 
82566d8c9d2SKieran Bingham 		/*
82666d8c9d2SKieran Bingham 		 * Wait until frame synchronization is locked.
82766d8c9d2SKieran Bingham 		 *
82866d8c9d2SKieran Bingham 		 * Manual says frame sync locking should take ~6 VTS.
82966d8c9d2SKieran Bingham 		 * From practical experience at least 8 are required. Give
83066d8c9d2SKieran Bingham 		 * 12 complete frames time (~400ms at 30 fps) to achieve frame
83166d8c9d2SKieran Bingham 		 * locking before returning error.
83266d8c9d2SKieran Bingham 		 */
83366d8c9d2SKieran Bingham 		for (i = 0; i < 40; i++) {
83466d8c9d2SKieran Bingham 			if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) {
83566d8c9d2SKieran Bingham 				sync = true;
83666d8c9d2SKieran Bingham 				break;
83766d8c9d2SKieran Bingham 			}
83866d8c9d2SKieran Bingham 			usleep_range(9000, 11000);
83966d8c9d2SKieran Bingham 		}
84066d8c9d2SKieran Bingham 
84166d8c9d2SKieran Bingham 		if (!sync) {
84266d8c9d2SKieran Bingham 			dev_err(&priv->client->dev,
84366d8c9d2SKieran Bingham 				"Failed to get frame synchronization\n");
84466d8c9d2SKieran Bingham 			return -EXDEV; /* Invalid cross-device link */
84566d8c9d2SKieran Bingham 		}
84666d8c9d2SKieran Bingham 
84766d8c9d2SKieran Bingham 		/*
848cdcb186eSLaurent Pinchart 		 * Configure the CSI-2 output to line interleaved mode (W x (N
849cdcb186eSLaurent Pinchart 		 * x H), as opposed to the (N x W) x H mode that outputs the
850cdcb186eSLaurent Pinchart 		 * images stitched side-by-side) and enable it.
85166d8c9d2SKieran Bingham 		 */
852cdcb186eSLaurent Pinchart 		max9286_write(priv, 0x15, MAX9286_CSI_IMAGE_TYP | MAX9286_VCTYPE |
853cdcb186eSLaurent Pinchart 			      MAX9286_CSIOUTEN | MAX9286_EN_CCBSYB_CLK_STR |
854cdcb186eSLaurent Pinchart 			      MAX9286_EN_GPI_CCBSYB);
85566d8c9d2SKieran Bingham 	} else {
856cdcb186eSLaurent Pinchart 		max9286_write(priv, 0x15, MAX9286_VCTYPE |
857cdcb186eSLaurent Pinchart 			      MAX9286_EN_CCBSYB_CLK_STR |
858cdcb186eSLaurent Pinchart 			      MAX9286_EN_GPI_CCBSYB);
85966d8c9d2SKieran Bingham 
86066d8c9d2SKieran Bingham 		/* Stop all cameras. */
86166d8c9d2SKieran Bingham 		for_each_source(priv, source)
86266d8c9d2SKieran Bingham 			v4l2_subdev_call(source->sd, video, s_stream, 0);
86366d8c9d2SKieran Bingham 
86466d8c9d2SKieran Bingham 		max9286_i2c_mux_close(priv);
86566d8c9d2SKieran Bingham 	}
86666d8c9d2SKieran Bingham 
86766d8c9d2SKieran Bingham 	return 0;
86866d8c9d2SKieran Bingham }
86966d8c9d2SKieran Bingham 
870cffc9fb1SLaurent Pinchart static int max9286_g_frame_interval(struct v4l2_subdev *sd,
871cffc9fb1SLaurent Pinchart 				    struct v4l2_subdev_frame_interval *interval)
872cffc9fb1SLaurent Pinchart {
873cffc9fb1SLaurent Pinchart 	struct max9286_priv *priv = sd_to_max9286(sd);
874cffc9fb1SLaurent Pinchart 
875cffc9fb1SLaurent Pinchart 	if (interval->pad != MAX9286_SRC_PAD)
876cffc9fb1SLaurent Pinchart 		return -EINVAL;
877cffc9fb1SLaurent Pinchart 
878cffc9fb1SLaurent Pinchart 	interval->interval = priv->interval;
879cffc9fb1SLaurent Pinchart 
880cffc9fb1SLaurent Pinchart 	return 0;
881cffc9fb1SLaurent Pinchart }
882cffc9fb1SLaurent Pinchart 
883cffc9fb1SLaurent Pinchart static int max9286_s_frame_interval(struct v4l2_subdev *sd,
884cffc9fb1SLaurent Pinchart 				    struct v4l2_subdev_frame_interval *interval)
885cffc9fb1SLaurent Pinchart {
886cffc9fb1SLaurent Pinchart 	struct max9286_priv *priv = sd_to_max9286(sd);
887cffc9fb1SLaurent Pinchart 
888cffc9fb1SLaurent Pinchart 	if (interval->pad != MAX9286_SRC_PAD)
889cffc9fb1SLaurent Pinchart 		return -EINVAL;
890cffc9fb1SLaurent Pinchart 
891cffc9fb1SLaurent Pinchart 	priv->interval = interval->interval;
892cffc9fb1SLaurent Pinchart 
893cffc9fb1SLaurent Pinchart 	return 0;
894cffc9fb1SLaurent Pinchart }
895cffc9fb1SLaurent Pinchart 
89666d8c9d2SKieran Bingham static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
8970d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
89866d8c9d2SKieran Bingham 				  struct v4l2_subdev_mbus_code_enum *code)
89966d8c9d2SKieran Bingham {
90066d8c9d2SKieran Bingham 	if (code->pad || code->index > 0)
90166d8c9d2SKieran Bingham 		return -EINVAL;
90266d8c9d2SKieran Bingham 
90366d8c9d2SKieran Bingham 	code->code = MEDIA_BUS_FMT_UYVY8_1X16;
90466d8c9d2SKieran Bingham 
90566d8c9d2SKieran Bingham 	return 0;
90666d8c9d2SKieran Bingham }
90766d8c9d2SKieran Bingham 
90866d8c9d2SKieran Bingham static struct v4l2_mbus_framefmt *
90966d8c9d2SKieran Bingham max9286_get_pad_format(struct max9286_priv *priv,
9100d346d2aSTomi Valkeinen 		       struct v4l2_subdev_state *sd_state,
91166d8c9d2SKieran Bingham 		       unsigned int pad, u32 which)
91266d8c9d2SKieran Bingham {
91366d8c9d2SKieran Bingham 	switch (which) {
91466d8c9d2SKieran Bingham 	case V4L2_SUBDEV_FORMAT_TRY:
9150d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad);
91666d8c9d2SKieran Bingham 	case V4L2_SUBDEV_FORMAT_ACTIVE:
91766d8c9d2SKieran Bingham 		return &priv->fmt[pad];
91866d8c9d2SKieran Bingham 	default:
91966d8c9d2SKieran Bingham 		return NULL;
92066d8c9d2SKieran Bingham 	}
92166d8c9d2SKieran Bingham }
92266d8c9d2SKieran Bingham 
92366d8c9d2SKieran Bingham static int max9286_set_fmt(struct v4l2_subdev *sd,
9240d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
92566d8c9d2SKieran Bingham 			   struct v4l2_subdev_format *format)
92666d8c9d2SKieran Bingham {
92766d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(sd);
92866d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt *cfg_fmt;
929f1403802SLaurent Pinchart 	unsigned int i;
93066d8c9d2SKieran Bingham 
93166d8c9d2SKieran Bingham 	if (format->pad == MAX9286_SRC_PAD)
93266d8c9d2SKieran Bingham 		return -EINVAL;
93366d8c9d2SKieran Bingham 
934f1403802SLaurent Pinchart 	/* Validate the format. */
935f1403802SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
936f1403802SLaurent Pinchart 		if (max9286_formats[i].code == format->format.code)
93766d8c9d2SKieran Bingham 			break;
93866d8c9d2SKieran Bingham 	}
93966d8c9d2SKieran Bingham 
940f1403802SLaurent Pinchart 	if (i == ARRAY_SIZE(max9286_formats))
941f1403802SLaurent Pinchart 		format->format.code = max9286_formats[0].code;
942f1403802SLaurent Pinchart 
9430d346d2aSTomi Valkeinen 	cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad,
9440d346d2aSTomi Valkeinen 					 format->which);
94566d8c9d2SKieran Bingham 	if (!cfg_fmt)
94666d8c9d2SKieran Bingham 		return -EINVAL;
94766d8c9d2SKieran Bingham 
94866d8c9d2SKieran Bingham 	mutex_lock(&priv->mutex);
94966d8c9d2SKieran Bingham 	*cfg_fmt = format->format;
95066d8c9d2SKieran Bingham 	mutex_unlock(&priv->mutex);
95166d8c9d2SKieran Bingham 
95266d8c9d2SKieran Bingham 	return 0;
95366d8c9d2SKieran Bingham }
95466d8c9d2SKieran Bingham 
95566d8c9d2SKieran Bingham static int max9286_get_fmt(struct v4l2_subdev *sd,
9560d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
95766d8c9d2SKieran Bingham 			   struct v4l2_subdev_format *format)
95866d8c9d2SKieran Bingham {
95966d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(sd);
96066d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt *cfg_fmt;
96166d8c9d2SKieran Bingham 	unsigned int pad = format->pad;
96266d8c9d2SKieran Bingham 
96366d8c9d2SKieran Bingham 	/*
96466d8c9d2SKieran Bingham 	 * Multiplexed Stream Support: Support link validation by returning the
96566d8c9d2SKieran Bingham 	 * format of the first bound link. All links must have the same format,
96666d8c9d2SKieran Bingham 	 * as we do not support mixing and matching of cameras connected to the
96766d8c9d2SKieran Bingham 	 * max9286.
96866d8c9d2SKieran Bingham 	 */
96966d8c9d2SKieran Bingham 	if (pad == MAX9286_SRC_PAD)
97066d8c9d2SKieran Bingham 		pad = __ffs(priv->bound_sources);
97166d8c9d2SKieran Bingham 
9720d346d2aSTomi Valkeinen 	cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which);
97366d8c9d2SKieran Bingham 	if (!cfg_fmt)
97466d8c9d2SKieran Bingham 		return -EINVAL;
97566d8c9d2SKieran Bingham 
97666d8c9d2SKieran Bingham 	mutex_lock(&priv->mutex);
97766d8c9d2SKieran Bingham 	format->format = *cfg_fmt;
97866d8c9d2SKieran Bingham 	mutex_unlock(&priv->mutex);
97966d8c9d2SKieran Bingham 
98066d8c9d2SKieran Bingham 	return 0;
98166d8c9d2SKieran Bingham }
98266d8c9d2SKieran Bingham 
98366d8c9d2SKieran Bingham static const struct v4l2_subdev_video_ops max9286_video_ops = {
98466d8c9d2SKieran Bingham 	.s_stream	= max9286_s_stream,
985cffc9fb1SLaurent Pinchart 	.g_frame_interval = max9286_g_frame_interval,
986cffc9fb1SLaurent Pinchart 	.s_frame_interval = max9286_s_frame_interval,
98766d8c9d2SKieran Bingham };
98866d8c9d2SKieran Bingham 
98966d8c9d2SKieran Bingham static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
99066d8c9d2SKieran Bingham 	.enum_mbus_code = max9286_enum_mbus_code,
99166d8c9d2SKieran Bingham 	.get_fmt	= max9286_get_fmt,
99266d8c9d2SKieran Bingham 	.set_fmt	= max9286_set_fmt,
99366d8c9d2SKieran Bingham };
99466d8c9d2SKieran Bingham 
99566d8c9d2SKieran Bingham static const struct v4l2_subdev_ops max9286_subdev_ops = {
99666d8c9d2SKieran Bingham 	.video		= &max9286_video_ops,
99766d8c9d2SKieran Bingham 	.pad		= &max9286_pad_ops,
99866d8c9d2SKieran Bingham };
99966d8c9d2SKieran Bingham 
1000f1403802SLaurent Pinchart static const struct v4l2_mbus_framefmt max9286_default_format = {
1001f1403802SLaurent Pinchart 	.width		= 1280,
1002f1403802SLaurent Pinchart 	.height		= 800,
1003f1403802SLaurent Pinchart 	.code		= MEDIA_BUS_FMT_UYVY8_1X16,
1004f1403802SLaurent Pinchart 	.colorspace	= V4L2_COLORSPACE_SRGB,
1005f1403802SLaurent Pinchart 	.field		= V4L2_FIELD_NONE,
1006f1403802SLaurent Pinchart 	.ycbcr_enc	= V4L2_YCBCR_ENC_DEFAULT,
1007f1403802SLaurent Pinchart 	.quantization	= V4L2_QUANTIZATION_DEFAULT,
1008f1403802SLaurent Pinchart 	.xfer_func	= V4L2_XFER_FUNC_DEFAULT,
1009f1403802SLaurent Pinchart };
1010f1403802SLaurent Pinchart 
101166d8c9d2SKieran Bingham static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
101266d8c9d2SKieran Bingham {
1013f1403802SLaurent Pinchart 	*fmt = max9286_default_format;
101466d8c9d2SKieran Bingham }
101566d8c9d2SKieran Bingham 
101666d8c9d2SKieran Bingham static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
101766d8c9d2SKieran Bingham {
101866d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt *format;
101966d8c9d2SKieran Bingham 	unsigned int i;
102066d8c9d2SKieran Bingham 
102166d8c9d2SKieran Bingham 	for (i = 0; i < MAX9286_N_SINKS; i++) {
10220d346d2aSTomi Valkeinen 		format = v4l2_subdev_get_try_format(subdev, fh->state, i);
102366d8c9d2SKieran Bingham 		max9286_init_format(format);
102466d8c9d2SKieran Bingham 	}
102566d8c9d2SKieran Bingham 
102666d8c9d2SKieran Bingham 	return 0;
102766d8c9d2SKieran Bingham }
102866d8c9d2SKieran Bingham 
102966d8c9d2SKieran Bingham static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
103066d8c9d2SKieran Bingham 	.open = max9286_open,
103166d8c9d2SKieran Bingham };
103266d8c9d2SKieran Bingham 
10331fe6ae4eSLaurent Pinchart static const struct media_entity_operations max9286_media_ops = {
10341fe6ae4eSLaurent Pinchart 	.link_validate = v4l2_subdev_link_validate
10351fe6ae4eSLaurent Pinchart };
10361fe6ae4eSLaurent Pinchart 
103766d8c9d2SKieran Bingham static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
103866d8c9d2SKieran Bingham {
103966d8c9d2SKieran Bingham 	switch (ctrl->id) {
104066d8c9d2SKieran Bingham 	case V4L2_CID_PIXEL_RATE:
104166d8c9d2SKieran Bingham 		return 0;
104266d8c9d2SKieran Bingham 	default:
104366d8c9d2SKieran Bingham 		return -EINVAL;
104466d8c9d2SKieran Bingham 	}
104566d8c9d2SKieran Bingham }
104666d8c9d2SKieran Bingham 
104766d8c9d2SKieran Bingham static const struct v4l2_ctrl_ops max9286_ctrl_ops = {
104866d8c9d2SKieran Bingham 	.s_ctrl = max9286_s_ctrl,
104966d8c9d2SKieran Bingham };
105066d8c9d2SKieran Bingham 
105166d8c9d2SKieran Bingham static int max9286_v4l2_register(struct max9286_priv *priv)
105266d8c9d2SKieran Bingham {
105366d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
105466d8c9d2SKieran Bingham 	struct fwnode_handle *ep;
105566d8c9d2SKieran Bingham 	int ret;
105666d8c9d2SKieran Bingham 	int i;
105766d8c9d2SKieran Bingham 
105866d8c9d2SKieran Bingham 	/* Register v4l2 async notifiers for connected Camera subdevices */
105966d8c9d2SKieran Bingham 	ret = max9286_v4l2_notifier_register(priv);
106066d8c9d2SKieran Bingham 	if (ret) {
106166d8c9d2SKieran Bingham 		dev_err(dev, "Unable to register V4L2 async notifiers\n");
106266d8c9d2SKieran Bingham 		return ret;
106366d8c9d2SKieran Bingham 	}
106466d8c9d2SKieran Bingham 
106566d8c9d2SKieran Bingham 	/* Configure V4L2 for the MAX9286 itself */
106666d8c9d2SKieran Bingham 
106766d8c9d2SKieran Bingham 	for (i = 0; i < MAX9286_N_SINKS; i++)
106866d8c9d2SKieran Bingham 		max9286_init_format(&priv->fmt[i]);
106966d8c9d2SKieran Bingham 
107066d8c9d2SKieran Bingham 	v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops);
107166d8c9d2SKieran Bingham 	priv->sd.internal_ops = &max9286_subdev_internal_ops;
107266d8c9d2SKieran Bingham 	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
107366d8c9d2SKieran Bingham 
107466d8c9d2SKieran Bingham 	v4l2_ctrl_handler_init(&priv->ctrls, 1);
1075cffc9fb1SLaurent Pinchart 	priv->pixelrate_ctrl = v4l2_ctrl_new_std(&priv->ctrls,
107666d8c9d2SKieran Bingham 						 &max9286_ctrl_ops,
107766d8c9d2SKieran Bingham 						 V4L2_CID_PIXEL_RATE,
107866d8c9d2SKieran Bingham 						 1, INT_MAX, 1, 50000000);
107966d8c9d2SKieran Bingham 
108066d8c9d2SKieran Bingham 	priv->sd.ctrl_handler = &priv->ctrls;
108166d8c9d2SKieran Bingham 	ret = priv->ctrls.error;
108266d8c9d2SKieran Bingham 	if (ret)
108366d8c9d2SKieran Bingham 		goto err_async;
108466d8c9d2SKieran Bingham 
108566d8c9d2SKieran Bingham 	priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
10861fe6ae4eSLaurent Pinchart 	priv->sd.entity.ops = &max9286_media_ops;
108766d8c9d2SKieran Bingham 
108866d8c9d2SKieran Bingham 	priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE;
108966d8c9d2SKieran Bingham 	for (i = 0; i < MAX9286_SRC_PAD; i++)
109066d8c9d2SKieran Bingham 		priv->pads[i].flags = MEDIA_PAD_FL_SINK;
109166d8c9d2SKieran Bingham 	ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS,
109266d8c9d2SKieran Bingham 				     priv->pads);
109366d8c9d2SKieran Bingham 	if (ret)
109466d8c9d2SKieran Bingham 		goto err_async;
109566d8c9d2SKieran Bingham 
109666d8c9d2SKieran Bingham 	ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD,
109766d8c9d2SKieran Bingham 					     0, 0);
109866d8c9d2SKieran Bingham 	if (!ep) {
109966d8c9d2SKieran Bingham 		dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n");
110066d8c9d2SKieran Bingham 		ret = -ENOENT;
110166d8c9d2SKieran Bingham 		goto err_async;
110266d8c9d2SKieran Bingham 	}
110366d8c9d2SKieran Bingham 	priv->sd.fwnode = ep;
110466d8c9d2SKieran Bingham 
110566d8c9d2SKieran Bingham 	ret = v4l2_async_register_subdev(&priv->sd);
110666d8c9d2SKieran Bingham 	if (ret < 0) {
110766d8c9d2SKieran Bingham 		dev_err(dev, "Unable to register subdevice\n");
110866d8c9d2SKieran Bingham 		goto err_put_node;
110966d8c9d2SKieran Bingham 	}
111066d8c9d2SKieran Bingham 
111166d8c9d2SKieran Bingham 	return 0;
111266d8c9d2SKieran Bingham 
111366d8c9d2SKieran Bingham err_put_node:
111466d8c9d2SKieran Bingham 	fwnode_handle_put(ep);
111566d8c9d2SKieran Bingham err_async:
111666d8c9d2SKieran Bingham 	max9286_v4l2_notifier_unregister(priv);
111766d8c9d2SKieran Bingham 
111866d8c9d2SKieran Bingham 	return ret;
111966d8c9d2SKieran Bingham }
112066d8c9d2SKieran Bingham 
112166d8c9d2SKieran Bingham static void max9286_v4l2_unregister(struct max9286_priv *priv)
112266d8c9d2SKieran Bingham {
112366d8c9d2SKieran Bingham 	fwnode_handle_put(priv->sd.fwnode);
112466d8c9d2SKieran Bingham 	v4l2_async_unregister_subdev(&priv->sd);
112566d8c9d2SKieran Bingham 	max9286_v4l2_notifier_unregister(priv);
112666d8c9d2SKieran Bingham }
112766d8c9d2SKieran Bingham 
112866d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
112966d8c9d2SKieran Bingham  * Probe/Remove
113066d8c9d2SKieran Bingham  */
113166d8c9d2SKieran Bingham 
113266d8c9d2SKieran Bingham static int max9286_setup(struct max9286_priv *priv)
113366d8c9d2SKieran Bingham {
113466d8c9d2SKieran Bingham 	/*
113566d8c9d2SKieran Bingham 	 * Link ordering values for all enabled links combinations. Orders must
113666d8c9d2SKieran Bingham 	 * be assigned sequentially from 0 to the number of enabled links
113766d8c9d2SKieran Bingham 	 * without leaving any hole for disabled links. We thus assign orders to
113866d8c9d2SKieran Bingham 	 * enabled links first, and use the remaining order values for disabled
113966d8c9d2SKieran Bingham 	 * links are all links must have a different order value;
114066d8c9d2SKieran Bingham 	 */
114166d8c9d2SKieran Bingham 	static const u8 link_order[] = {
114266d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
114366d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
114466d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
114566d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
114666d8c9d2SKieran Bingham 		(3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
114766d8c9d2SKieran Bingham 		(3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
114866d8c9d2SKieran Bingham 		(3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
114966d8c9d2SKieran Bingham 		(3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
115066d8c9d2SKieran Bingham 		(0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
115166d8c9d2SKieran Bingham 		(1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
115266d8c9d2SKieran Bingham 		(1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
115366d8c9d2SKieran Bingham 		(2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
115466d8c9d2SKieran Bingham 		(1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
115566d8c9d2SKieran Bingham 		(2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
115666d8c9d2SKieran Bingham 		(2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
115766d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
115866d8c9d2SKieran Bingham 	};
1159*defcedfbSLaurent Pinchart 	int cfg;
116066d8c9d2SKieran Bingham 
116166d8c9d2SKieran Bingham 	/*
116266d8c9d2SKieran Bingham 	 * Set the I2C bus speed.
116366d8c9d2SKieran Bingham 	 *
116466d8c9d2SKieran Bingham 	 * Enable I2C Local Acknowledge during the probe sequences of the camera
116566d8c9d2SKieran Bingham 	 * only. This should be disabled after the mux is initialised.
116666d8c9d2SKieran Bingham 	 */
116766d8c9d2SKieran Bingham 	max9286_configure_i2c(priv, true);
1168f78723ebSJacopo Mondi 	max9286_reverse_channel_setup(priv, priv->init_rev_chan_mv);
116966d8c9d2SKieran Bingham 
117066d8c9d2SKieran Bingham 	/*
117166d8c9d2SKieran Bingham 	 * Enable GMSL links, mask unused ones and autodetect link
117266d8c9d2SKieran Bingham 	 * used as CSI clock source.
117366d8c9d2SKieran Bingham 	 */
117466d8c9d2SKieran Bingham 	max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask);
117566d8c9d2SKieran Bingham 	max9286_write(priv, 0x0b, link_order[priv->route_mask]);
117666d8c9d2SKieran Bingham 	max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
117766d8c9d2SKieran Bingham 
1178f1403802SLaurent Pinchart 	max9286_set_video_format(priv, &max9286_default_format);
1179cffc9fb1SLaurent Pinchart 	max9286_set_fsync_period(priv);
118066d8c9d2SKieran Bingham 
1181*defcedfbSLaurent Pinchart 	cfg = max9286_read(priv, 0x1c);
1182*defcedfbSLaurent Pinchart 	if (cfg < 0)
1183*defcedfbSLaurent Pinchart 		return cfg;
1184*defcedfbSLaurent Pinchart 
1185*defcedfbSLaurent Pinchart 	dev_dbg(&priv->client->dev, "power-up config: %s immunity, %u-bit bus\n",
1186*defcedfbSLaurent Pinchart 		cfg & MAX9286_HIGHIMM(0) ? "high" : "legacy",
1187*defcedfbSLaurent Pinchart 		cfg & MAX9286_BWS ? 32 : cfg & MAX9286_HIBW ? 27 : 24);
1188*defcedfbSLaurent Pinchart 
11893697f108SLaurent Pinchart 	if (priv->bus_width) {
1190*defcedfbSLaurent Pinchart 		cfg &= ~(MAX9286_HIBW | MAX9286_BWS);
11913697f108SLaurent Pinchart 
11923697f108SLaurent Pinchart 		if (priv->bus_width == 27)
1193*defcedfbSLaurent Pinchart 			cfg |= MAX9286_HIBW;
11943697f108SLaurent Pinchart 		else if (priv->bus_width == 32)
1195*defcedfbSLaurent Pinchart 			cfg |= MAX9286_BWS;
11963697f108SLaurent Pinchart 
1197*defcedfbSLaurent Pinchart 		max9286_write(priv, 0x1c, cfg);
11983697f108SLaurent Pinchart 	}
11993697f108SLaurent Pinchart 
120066d8c9d2SKieran Bingham 	/*
120166d8c9d2SKieran Bingham 	 * The overlap window seems to provide additional validation by tracking
120266d8c9d2SKieran Bingham 	 * the delay between vsync and frame sync, generating an error if the
120366d8c9d2SKieran Bingham 	 * delay is bigger than the programmed window, though it's not yet clear
120466d8c9d2SKieran Bingham 	 * what value should be set.
120566d8c9d2SKieran Bingham 	 *
120666d8c9d2SKieran Bingham 	 * As it's an optional value and can be disabled, we do so by setting
120766d8c9d2SKieran Bingham 	 * a 0 overlap value.
120866d8c9d2SKieran Bingham 	 */
120966d8c9d2SKieran Bingham 	max9286_write(priv, 0x63, 0);
121066d8c9d2SKieran Bingham 	max9286_write(priv, 0x64, 0);
121166d8c9d2SKieran Bingham 
121266d8c9d2SKieran Bingham 	/*
121366d8c9d2SKieran Bingham 	 * Wait for 2ms to allow the link to resynchronize after the
121466d8c9d2SKieran Bingham 	 * configuration change.
121566d8c9d2SKieran Bingham 	 */
121666d8c9d2SKieran Bingham 	usleep_range(2000, 5000);
121766d8c9d2SKieran Bingham 
121866d8c9d2SKieran Bingham 	return 0;
121966d8c9d2SKieran Bingham }
122066d8c9d2SKieran Bingham 
1221c9352df7SJacopo Mondi static int max9286_gpio_set(struct max9286_priv *priv, unsigned int offset,
1222c9352df7SJacopo Mondi 			    int value)
122366d8c9d2SKieran Bingham {
122466d8c9d2SKieran Bingham 	if (value)
122566d8c9d2SKieran Bingham 		priv->gpio_state |= BIT(offset);
122666d8c9d2SKieran Bingham 	else
122766d8c9d2SKieran Bingham 		priv->gpio_state &= ~BIT(offset);
122866d8c9d2SKieran Bingham 
1229c9352df7SJacopo Mondi 	return max9286_write(priv, 0x0f,
1230c9352df7SJacopo Mondi 			     MAX9286_0X0F_RESERVED | priv->gpio_state);
123166d8c9d2SKieran Bingham }
123266d8c9d2SKieran Bingham 
1233c9352df7SJacopo Mondi static void max9286_gpiochip_set(struct gpio_chip *chip,
1234c9352df7SJacopo Mondi 				 unsigned int offset, int value)
1235c9352df7SJacopo Mondi {
1236c9352df7SJacopo Mondi 	struct max9286_priv *priv = gpiochip_get_data(chip);
1237c9352df7SJacopo Mondi 
1238c9352df7SJacopo Mondi 	max9286_gpio_set(priv, offset, value);
1239c9352df7SJacopo Mondi }
1240c9352df7SJacopo Mondi 
1241c9352df7SJacopo Mondi static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset)
124266d8c9d2SKieran Bingham {
124366d8c9d2SKieran Bingham 	struct max9286_priv *priv = gpiochip_get_data(chip);
124466d8c9d2SKieran Bingham 
124566d8c9d2SKieran Bingham 	return priv->gpio_state & BIT(offset);
124666d8c9d2SKieran Bingham }
124766d8c9d2SKieran Bingham 
124866d8c9d2SKieran Bingham static int max9286_register_gpio(struct max9286_priv *priv)
124966d8c9d2SKieran Bingham {
125066d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
125166d8c9d2SKieran Bingham 	struct gpio_chip *gpio = &priv->gpio;
125266d8c9d2SKieran Bingham 	int ret;
125366d8c9d2SKieran Bingham 
125466d8c9d2SKieran Bingham 	/* Configure the GPIO */
125566d8c9d2SKieran Bingham 	gpio->label = dev_name(dev);
125666d8c9d2SKieran Bingham 	gpio->parent = dev;
125766d8c9d2SKieran Bingham 	gpio->owner = THIS_MODULE;
125866d8c9d2SKieran Bingham 	gpio->ngpio = 2;
125966d8c9d2SKieran Bingham 	gpio->base = -1;
1260c9352df7SJacopo Mondi 	gpio->set = max9286_gpiochip_set;
1261c9352df7SJacopo Mondi 	gpio->get = max9286_gpiochip_get;
126266d8c9d2SKieran Bingham 	gpio->can_sleep = true;
126366d8c9d2SKieran Bingham 
126466d8c9d2SKieran Bingham 	ret = devm_gpiochip_add_data(dev, gpio, priv);
126566d8c9d2SKieran Bingham 	if (ret)
126666d8c9d2SKieran Bingham 		dev_err(dev, "Unable to create gpio_chip\n");
126766d8c9d2SKieran Bingham 
126866d8c9d2SKieran Bingham 	return ret;
126966d8c9d2SKieran Bingham }
127066d8c9d2SKieran Bingham 
1271c9352df7SJacopo Mondi static int max9286_parse_gpios(struct max9286_priv *priv)
1272c9352df7SJacopo Mondi {
1273c9352df7SJacopo Mondi 	struct device *dev = &priv->client->dev;
1274c9352df7SJacopo Mondi 	int ret;
1275c9352df7SJacopo Mondi 
1276c9352df7SJacopo Mondi 	/*
1277c9352df7SJacopo Mondi 	 * Parse the "gpio-poc" vendor property. If the property is not
1278c9352df7SJacopo Mondi 	 * specified the camera power is controlled by a regulator.
1279c9352df7SJacopo Mondi 	 */
1280c9352df7SJacopo Mondi 	ret = of_property_read_u32_array(dev->of_node, "maxim,gpio-poc",
1281c9352df7SJacopo Mondi 					 priv->gpio_poc, 2);
1282c9352df7SJacopo Mondi 	if (ret == -EINVAL) {
1283c9352df7SJacopo Mondi 		/*
1284c9352df7SJacopo Mondi 		 * If gpio lines are not used for the camera power, register
1285c9352df7SJacopo Mondi 		 * a gpio controller for consumers.
1286c9352df7SJacopo Mondi 		 */
1287817660f4SThomas Nizan 		return max9286_register_gpio(priv);
1288c9352df7SJacopo Mondi 	}
1289c9352df7SJacopo Mondi 
1290c9352df7SJacopo Mondi 	/* If the property is specified make sure it is well formed. */
1291c9352df7SJacopo Mondi 	if (ret || priv->gpio_poc[0] > 1 ||
1292c9352df7SJacopo Mondi 	    (priv->gpio_poc[1] != GPIO_ACTIVE_HIGH &&
1293c9352df7SJacopo Mondi 	     priv->gpio_poc[1] != GPIO_ACTIVE_LOW)) {
1294c9352df7SJacopo Mondi 		dev_err(dev, "Invalid 'gpio-poc' property\n");
1295c9352df7SJacopo Mondi 		return -EINVAL;
1296c9352df7SJacopo Mondi 	}
1297c9352df7SJacopo Mondi 
1298817660f4SThomas Nizan 	priv->use_gpio_poc = true;
1299c9352df7SJacopo Mondi 	return 0;
1300c9352df7SJacopo Mondi }
1301c9352df7SJacopo Mondi 
1302817660f4SThomas Nizan static int max9286_poc_power_on(struct max9286_priv *priv)
1303817660f4SThomas Nizan {
1304817660f4SThomas Nizan 	struct max9286_source *source;
1305817660f4SThomas Nizan 	unsigned int enabled = 0;
1306817660f4SThomas Nizan 	int ret;
1307817660f4SThomas Nizan 
1308817660f4SThomas Nizan 	/* Enable the global regulator if available. */
1309817660f4SThomas Nizan 	if (priv->regulator)
1310817660f4SThomas Nizan 		return regulator_enable(priv->regulator);
1311817660f4SThomas Nizan 
1312817660f4SThomas Nizan 	if (priv->use_gpio_poc)
1313817660f4SThomas Nizan 		return max9286_gpio_set(priv, priv->gpio_poc[0],
1314817660f4SThomas Nizan 					!priv->gpio_poc[1]);
1315817660f4SThomas Nizan 
1316817660f4SThomas Nizan 	/* Otherwise use the per-port regulators. */
1317817660f4SThomas Nizan 	for_each_source(priv, source) {
1318817660f4SThomas Nizan 		ret = regulator_enable(source->regulator);
1319817660f4SThomas Nizan 		if (ret < 0)
1320817660f4SThomas Nizan 			goto error;
1321817660f4SThomas Nizan 
1322817660f4SThomas Nizan 		enabled |= BIT(to_index(priv, source));
1323817660f4SThomas Nizan 	}
1324817660f4SThomas Nizan 
1325817660f4SThomas Nizan 	return 0;
1326817660f4SThomas Nizan 
1327817660f4SThomas Nizan error:
1328817660f4SThomas Nizan 	for_each_source(priv, source) {
1329817660f4SThomas Nizan 		if (enabled & BIT(to_index(priv, source)))
1330817660f4SThomas Nizan 			regulator_disable(source->regulator);
1331817660f4SThomas Nizan 	}
1332817660f4SThomas Nizan 
1333817660f4SThomas Nizan 	return ret;
1334817660f4SThomas Nizan }
1335817660f4SThomas Nizan 
1336817660f4SThomas Nizan static int max9286_poc_power_off(struct max9286_priv *priv)
1337817660f4SThomas Nizan {
1338817660f4SThomas Nizan 	struct max9286_source *source;
1339817660f4SThomas Nizan 	int ret = 0;
1340817660f4SThomas Nizan 
1341817660f4SThomas Nizan 	if (priv->regulator)
1342817660f4SThomas Nizan 		return regulator_disable(priv->regulator);
1343817660f4SThomas Nizan 
1344817660f4SThomas Nizan 	if (priv->use_gpio_poc)
1345817660f4SThomas Nizan 		return max9286_gpio_set(priv, priv->gpio_poc[0],
1346817660f4SThomas Nizan 					priv->gpio_poc[1]);
1347817660f4SThomas Nizan 
1348817660f4SThomas Nizan 	for_each_source(priv, source) {
1349817660f4SThomas Nizan 		int err;
1350817660f4SThomas Nizan 
1351817660f4SThomas Nizan 		err = regulator_disable(source->regulator);
1352817660f4SThomas Nizan 		if (!ret)
1353817660f4SThomas Nizan 			ret = err;
1354817660f4SThomas Nizan 	}
1355817660f4SThomas Nizan 
1356817660f4SThomas Nizan 	return ret;
1357817660f4SThomas Nizan }
1358817660f4SThomas Nizan 
1359c9352df7SJacopo Mondi static int max9286_poc_enable(struct max9286_priv *priv, bool enable)
1360c9352df7SJacopo Mondi {
1361c9352df7SJacopo Mondi 	int ret;
1362c9352df7SJacopo Mondi 
1363817660f4SThomas Nizan 	if (enable)
1364817660f4SThomas Nizan 		ret = max9286_poc_power_on(priv);
1365c9352df7SJacopo Mondi 	else
1366817660f4SThomas Nizan 		ret = max9286_poc_power_off(priv);
1367c9352df7SJacopo Mondi 
1368c9352df7SJacopo Mondi 	if (ret < 0)
1369c9352df7SJacopo Mondi 		dev_err(&priv->client->dev, "Unable to turn power %s\n",
1370c9352df7SJacopo Mondi 			enable ? "on" : "off");
1371c9352df7SJacopo Mondi 
1372c9352df7SJacopo Mondi 	return ret;
1373c9352df7SJacopo Mondi }
1374c9352df7SJacopo Mondi 
1375365ab7ebSLaurentiu Palcu static int max9286_init(struct max9286_priv *priv)
137666d8c9d2SKieran Bingham {
1377365ab7ebSLaurentiu Palcu 	struct i2c_client *client = priv->client;
137866d8c9d2SKieran Bingham 	int ret;
137966d8c9d2SKieran Bingham 
1380c9352df7SJacopo Mondi 	ret = max9286_poc_enable(priv, true);
1381c9352df7SJacopo Mondi 	if (ret)
138266d8c9d2SKieran Bingham 		return ret;
138366d8c9d2SKieran Bingham 
138466d8c9d2SKieran Bingham 	ret = max9286_setup(priv);
138566d8c9d2SKieran Bingham 	if (ret) {
1386365ab7ebSLaurentiu Palcu 		dev_err(&client->dev, "Unable to setup max9286\n");
1387c9352df7SJacopo Mondi 		goto err_poc_disable;
138866d8c9d2SKieran Bingham 	}
138966d8c9d2SKieran Bingham 
139066d8c9d2SKieran Bingham 	/*
139166d8c9d2SKieran Bingham 	 * Register all V4L2 interactions for the MAX9286 and notifiers for
139266d8c9d2SKieran Bingham 	 * any subdevices connected.
139366d8c9d2SKieran Bingham 	 */
139466d8c9d2SKieran Bingham 	ret = max9286_v4l2_register(priv);
139566d8c9d2SKieran Bingham 	if (ret) {
1396365ab7ebSLaurentiu Palcu 		dev_err(&client->dev, "Failed to register with V4L2\n");
1397c9352df7SJacopo Mondi 		goto err_poc_disable;
139866d8c9d2SKieran Bingham 	}
139966d8c9d2SKieran Bingham 
140066d8c9d2SKieran Bingham 	ret = max9286_i2c_mux_init(priv);
140166d8c9d2SKieran Bingham 	if (ret) {
1402365ab7ebSLaurentiu Palcu 		dev_err(&client->dev, "Unable to initialize I2C multiplexer\n");
140366d8c9d2SKieran Bingham 		goto err_v4l2_register;
140466d8c9d2SKieran Bingham 	}
140566d8c9d2SKieran Bingham 
140666d8c9d2SKieran Bingham 	/* Leave the mux channels disabled until they are selected. */
140766d8c9d2SKieran Bingham 	max9286_i2c_mux_close(priv);
140866d8c9d2SKieran Bingham 
140966d8c9d2SKieran Bingham 	return 0;
141066d8c9d2SKieran Bingham 
141166d8c9d2SKieran Bingham err_v4l2_register:
141266d8c9d2SKieran Bingham 	max9286_v4l2_unregister(priv);
1413c9352df7SJacopo Mondi err_poc_disable:
1414c9352df7SJacopo Mondi 	max9286_poc_enable(priv, false);
141566d8c9d2SKieran Bingham 
141666d8c9d2SKieran Bingham 	return ret;
141766d8c9d2SKieran Bingham }
141866d8c9d2SKieran Bingham 
141966d8c9d2SKieran Bingham static void max9286_cleanup_dt(struct max9286_priv *priv)
142066d8c9d2SKieran Bingham {
142166d8c9d2SKieran Bingham 	struct max9286_source *source;
142266d8c9d2SKieran Bingham 
142366d8c9d2SKieran Bingham 	for_each_source(priv, source) {
142466d8c9d2SKieran Bingham 		fwnode_handle_put(source->fwnode);
142566d8c9d2SKieran Bingham 		source->fwnode = NULL;
142666d8c9d2SKieran Bingham 	}
142766d8c9d2SKieran Bingham }
142866d8c9d2SKieran Bingham 
142966d8c9d2SKieran Bingham static int max9286_parse_dt(struct max9286_priv *priv)
143066d8c9d2SKieran Bingham {
143166d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
143266d8c9d2SKieran Bingham 	struct device_node *i2c_mux;
143366d8c9d2SKieran Bingham 	struct device_node *node = NULL;
143466d8c9d2SKieran Bingham 	unsigned int i2c_mux_mask = 0;
143585cb767cSJacopo Mondi 	u32 reverse_channel_microvolt;
1436e332061bSLaurent Pinchart 	u32 i2c_clk_freq = 105000;
1437e332061bSLaurent Pinchart 	unsigned int i;
143866d8c9d2SKieran Bingham 
143966d8c9d2SKieran Bingham 	/* Balance the of_node_put() performed by of_find_node_by_name(). */
144066d8c9d2SKieran Bingham 	of_node_get(dev->of_node);
144166d8c9d2SKieran Bingham 	i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux");
144266d8c9d2SKieran Bingham 	if (!i2c_mux) {
144366d8c9d2SKieran Bingham 		dev_err(dev, "Failed to find i2c-mux node\n");
144466d8c9d2SKieran Bingham 		return -EINVAL;
144566d8c9d2SKieran Bingham 	}
144666d8c9d2SKieran Bingham 
144766d8c9d2SKieran Bingham 	/* Identify which i2c-mux channels are enabled */
144866d8c9d2SKieran Bingham 	for_each_child_of_node(i2c_mux, node) {
144966d8c9d2SKieran Bingham 		u32 id = 0;
145066d8c9d2SKieran Bingham 
145166d8c9d2SKieran Bingham 		of_property_read_u32(node, "reg", &id);
145266d8c9d2SKieran Bingham 		if (id >= MAX9286_NUM_GMSL)
145366d8c9d2SKieran Bingham 			continue;
145466d8c9d2SKieran Bingham 
145566d8c9d2SKieran Bingham 		if (!of_device_is_available(node)) {
145666d8c9d2SKieran Bingham 			dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id);
145766d8c9d2SKieran Bingham 			continue;
145866d8c9d2SKieran Bingham 		}
145966d8c9d2SKieran Bingham 
146066d8c9d2SKieran Bingham 		i2c_mux_mask |= BIT(id);
146166d8c9d2SKieran Bingham 	}
146266d8c9d2SKieran Bingham 	of_node_put(node);
146366d8c9d2SKieran Bingham 	of_node_put(i2c_mux);
146466d8c9d2SKieran Bingham 
146566d8c9d2SKieran Bingham 	/* Parse the endpoints */
146666d8c9d2SKieran Bingham 	for_each_endpoint_of_node(dev->of_node, node) {
146766d8c9d2SKieran Bingham 		struct max9286_source *source;
146866d8c9d2SKieran Bingham 		struct of_endpoint ep;
146966d8c9d2SKieran Bingham 
147066d8c9d2SKieran Bingham 		of_graph_parse_endpoint(node, &ep);
147166d8c9d2SKieran Bingham 		dev_dbg(dev, "Endpoint %pOF on port %d",
147266d8c9d2SKieran Bingham 			ep.local_node, ep.port);
147366d8c9d2SKieran Bingham 
147466d8c9d2SKieran Bingham 		if (ep.port > MAX9286_NUM_GMSL) {
147566d8c9d2SKieran Bingham 			dev_err(dev, "Invalid endpoint %s on port %d",
147666d8c9d2SKieran Bingham 				of_node_full_name(ep.local_node), ep.port);
147766d8c9d2SKieran Bingham 			continue;
147866d8c9d2SKieran Bingham 		}
147966d8c9d2SKieran Bingham 
148066d8c9d2SKieran Bingham 		/* For the source endpoint just parse the bus configuration. */
148166d8c9d2SKieran Bingham 		if (ep.port == MAX9286_SRC_PAD) {
148266d8c9d2SKieran Bingham 			struct v4l2_fwnode_endpoint vep = {
148366d8c9d2SKieran Bingham 				.bus_type = V4L2_MBUS_CSI2_DPHY
148466d8c9d2SKieran Bingham 			};
148566d8c9d2SKieran Bingham 			int ret;
148666d8c9d2SKieran Bingham 
148766d8c9d2SKieran Bingham 			ret = v4l2_fwnode_endpoint_parse(
148866d8c9d2SKieran Bingham 					of_fwnode_handle(node), &vep);
148966d8c9d2SKieran Bingham 			if (ret) {
149066d8c9d2SKieran Bingham 				of_node_put(node);
149166d8c9d2SKieran Bingham 				return ret;
149266d8c9d2SKieran Bingham 			}
149366d8c9d2SKieran Bingham 
149466d8c9d2SKieran Bingham 			priv->csi2_data_lanes =
149566d8c9d2SKieran Bingham 				vep.bus.mipi_csi2.num_data_lanes;
149666d8c9d2SKieran Bingham 
149766d8c9d2SKieran Bingham 			continue;
149866d8c9d2SKieran Bingham 		}
149966d8c9d2SKieran Bingham 
150066d8c9d2SKieran Bingham 		/* Skip if the corresponding GMSL link is unavailable. */
150166d8c9d2SKieran Bingham 		if (!(i2c_mux_mask & BIT(ep.port)))
150266d8c9d2SKieran Bingham 			continue;
150366d8c9d2SKieran Bingham 
150466d8c9d2SKieran Bingham 		if (priv->sources[ep.port].fwnode) {
150566d8c9d2SKieran Bingham 			dev_err(dev,
150666d8c9d2SKieran Bingham 				"Multiple port endpoints are not supported: %d",
150766d8c9d2SKieran Bingham 				ep.port);
150866d8c9d2SKieran Bingham 
150966d8c9d2SKieran Bingham 			continue;
151066d8c9d2SKieran Bingham 		}
151166d8c9d2SKieran Bingham 
151266d8c9d2SKieran Bingham 		source = &priv->sources[ep.port];
151366d8c9d2SKieran Bingham 		source->fwnode = fwnode_graph_get_remote_endpoint(
151466d8c9d2SKieran Bingham 						of_fwnode_handle(node));
151566d8c9d2SKieran Bingham 		if (!source->fwnode) {
151666d8c9d2SKieran Bingham 			dev_err(dev,
151766d8c9d2SKieran Bingham 				"Endpoint %pOF has no remote endpoint connection\n",
151866d8c9d2SKieran Bingham 				ep.local_node);
151966d8c9d2SKieran Bingham 
152066d8c9d2SKieran Bingham 			continue;
152166d8c9d2SKieran Bingham 		}
152266d8c9d2SKieran Bingham 
152366d8c9d2SKieran Bingham 		priv->source_mask |= BIT(ep.port);
152466d8c9d2SKieran Bingham 		priv->nsources++;
152566d8c9d2SKieran Bingham 	}
152666d8c9d2SKieran Bingham 	of_node_put(node);
152766d8c9d2SKieran Bingham 
15283697f108SLaurent Pinchart 	of_property_read_u32(dev->of_node, "maxim,bus-width", &priv->bus_width);
15293697f108SLaurent Pinchart 	switch (priv->bus_width) {
15303697f108SLaurent Pinchart 	case 0:
15313697f108SLaurent Pinchart 		/*
15323697f108SLaurent Pinchart 		 * The property isn't specified in the device tree, the driver
15333697f108SLaurent Pinchart 		 * will keep the default value selected by the BWS pin.
15343697f108SLaurent Pinchart 		 */
15353697f108SLaurent Pinchart 	case 24:
15363697f108SLaurent Pinchart 	case 27:
15373697f108SLaurent Pinchart 	case 32:
15383697f108SLaurent Pinchart 		break;
15393697f108SLaurent Pinchart 	default:
15403697f108SLaurent Pinchart 		dev_err(dev, "Invalid %s value %u\n", "maxim,bus-width",
15413697f108SLaurent Pinchart 			priv->bus_width);
15423697f108SLaurent Pinchart 		return -EINVAL;
15433697f108SLaurent Pinchart 	}
15443697f108SLaurent Pinchart 
1545e332061bSLaurent Pinchart 	of_property_read_u32(dev->of_node, "maxim,i2c-remote-bus-hz",
1546e332061bSLaurent Pinchart 			     &i2c_clk_freq);
1547e332061bSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(max9286_i2c_speeds); ++i) {
1548e332061bSLaurent Pinchart 		const struct max9286_i2c_speed *speed = &max9286_i2c_speeds[i];
1549e332061bSLaurent Pinchart 
1550e332061bSLaurent Pinchart 		if (speed->rate == i2c_clk_freq) {
1551e332061bSLaurent Pinchart 			priv->i2c_mstbt = speed->mstbt;
1552e332061bSLaurent Pinchart 			break;
1553e332061bSLaurent Pinchart 		}
1554e332061bSLaurent Pinchart 	}
1555e332061bSLaurent Pinchart 
1556e332061bSLaurent Pinchart 	if (i == ARRAY_SIZE(max9286_i2c_speeds)) {
1557e332061bSLaurent Pinchart 		dev_err(dev, "Invalid %s value %u\n", "maxim,i2c-remote-bus-hz",
1558e332061bSLaurent Pinchart 			i2c_clk_freq);
1559e332061bSLaurent Pinchart 		return -EINVAL;
1560e332061bSLaurent Pinchart 	}
1561e332061bSLaurent Pinchart 
156285cb767cSJacopo Mondi 	/*
156385cb767cSJacopo Mondi 	 * Parse the initial value of the reverse channel amplitude from
156485cb767cSJacopo Mondi 	 * the firmware interface and convert it to millivolts.
156585cb767cSJacopo Mondi 	 *
156685cb767cSJacopo Mondi 	 * Default it to 170mV for backward compatibility with DTBs that do not
156785cb767cSJacopo Mondi 	 * provide the property.
156885cb767cSJacopo Mondi 	 */
156985cb767cSJacopo Mondi 	if (of_property_read_u32(dev->of_node,
157085cb767cSJacopo Mondi 				 "maxim,reverse-channel-microvolt",
157185cb767cSJacopo Mondi 				 &reverse_channel_microvolt))
1572f78723ebSJacopo Mondi 		priv->init_rev_chan_mv = 170;
157385cb767cSJacopo Mondi 	else
1574f78723ebSJacopo Mondi 		priv->init_rev_chan_mv = reverse_channel_microvolt / 1000U;
157585cb767cSJacopo Mondi 
157666d8c9d2SKieran Bingham 	priv->route_mask = priv->source_mask;
157766d8c9d2SKieran Bingham 
157866d8c9d2SKieran Bingham 	return 0;
157966d8c9d2SKieran Bingham }
158066d8c9d2SKieran Bingham 
1581817660f4SThomas Nizan static int max9286_get_poc_supplies(struct max9286_priv *priv)
1582817660f4SThomas Nizan {
1583817660f4SThomas Nizan 	struct device *dev = &priv->client->dev;
1584817660f4SThomas Nizan 	struct max9286_source *source;
1585817660f4SThomas Nizan 	int ret;
1586817660f4SThomas Nizan 
1587817660f4SThomas Nizan 	/* Start by getting the global regulator. */
1588817660f4SThomas Nizan 	priv->regulator = devm_regulator_get_optional(dev, "poc");
1589817660f4SThomas Nizan 	if (!IS_ERR(priv->regulator))
1590817660f4SThomas Nizan 		return 0;
1591817660f4SThomas Nizan 
1592817660f4SThomas Nizan 	if (PTR_ERR(priv->regulator) != -ENODEV)
1593817660f4SThomas Nizan 		return dev_err_probe(dev, PTR_ERR(priv->regulator),
1594817660f4SThomas Nizan 				     "Unable to get PoC regulator\n");
1595817660f4SThomas Nizan 
1596817660f4SThomas Nizan 	/* If there's no global regulator, get per-port regulators. */
1597817660f4SThomas Nizan 	dev_dbg(dev,
1598817660f4SThomas Nizan 		"No global PoC regulator, looking for per-port regulators\n");
1599817660f4SThomas Nizan 	priv->regulator = NULL;
1600817660f4SThomas Nizan 
1601817660f4SThomas Nizan 	for_each_source(priv, source) {
1602817660f4SThomas Nizan 		unsigned int index = to_index(priv, source);
1603817660f4SThomas Nizan 		char name[10];
1604817660f4SThomas Nizan 
1605817660f4SThomas Nizan 		snprintf(name, sizeof(name), "port%u-poc", index);
1606817660f4SThomas Nizan 		source->regulator = devm_regulator_get(dev, name);
1607817660f4SThomas Nizan 		if (IS_ERR(source->regulator)) {
1608817660f4SThomas Nizan 			ret = PTR_ERR(source->regulator);
1609817660f4SThomas Nizan 			dev_err_probe(dev, ret,
1610817660f4SThomas Nizan 				      "Unable to get port %u PoC regulator\n",
1611817660f4SThomas Nizan 				      index);
1612817660f4SThomas Nizan 			return ret;
1613817660f4SThomas Nizan 		}
1614817660f4SThomas Nizan 	}
1615817660f4SThomas Nizan 
1616817660f4SThomas Nizan 	return 0;
1617817660f4SThomas Nizan }
1618817660f4SThomas Nizan 
161966d8c9d2SKieran Bingham static int max9286_probe(struct i2c_client *client)
162066d8c9d2SKieran Bingham {
162166d8c9d2SKieran Bingham 	struct max9286_priv *priv;
162266d8c9d2SKieran Bingham 	int ret;
162366d8c9d2SKieran Bingham 
162466d8c9d2SKieran Bingham 	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
162566d8c9d2SKieran Bingham 	if (!priv)
162666d8c9d2SKieran Bingham 		return -ENOMEM;
162766d8c9d2SKieran Bingham 
162866d8c9d2SKieran Bingham 	mutex_init(&priv->mutex);
162966d8c9d2SKieran Bingham 
163066d8c9d2SKieran Bingham 	priv->client = client;
163166d8c9d2SKieran Bingham 
1632817660f4SThomas Nizan 	/* GPIO values default to high */
1633817660f4SThomas Nizan 	priv->gpio_state = BIT(0) | BIT(1);
1634817660f4SThomas Nizan 
1635e332061bSLaurent Pinchart 	ret = max9286_parse_dt(priv);
1636e332061bSLaurent Pinchart 	if (ret)
1637e332061bSLaurent Pinchart 		goto err_cleanup_dt;
1638e332061bSLaurent Pinchart 
163966d8c9d2SKieran Bingham 	priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
164066d8c9d2SKieran Bingham 						   GPIOD_OUT_HIGH);
1641e332061bSLaurent Pinchart 	if (IS_ERR(priv->gpiod_pwdn)) {
1642e332061bSLaurent Pinchart 		ret = PTR_ERR(priv->gpiod_pwdn);
1643e332061bSLaurent Pinchart 		goto err_cleanup_dt;
1644e332061bSLaurent Pinchart 	}
164566d8c9d2SKieran Bingham 
164666d8c9d2SKieran Bingham 	gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
164766d8c9d2SKieran Bingham 	gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
164866d8c9d2SKieran Bingham 
164966d8c9d2SKieran Bingham 	/* Wait at least 4ms before the I2C lines latch to the address */
165066d8c9d2SKieran Bingham 	if (priv->gpiod_pwdn)
165166d8c9d2SKieran Bingham 		usleep_range(4000, 5000);
165266d8c9d2SKieran Bingham 
165366d8c9d2SKieran Bingham 	/*
165466d8c9d2SKieran Bingham 	 * The MAX9286 starts by default with all ports enabled, we disable all
165566d8c9d2SKieran Bingham 	 * ports early to ensure that all channels are disabled if we error out
165666d8c9d2SKieran Bingham 	 * and keep the bus consistent.
165766d8c9d2SKieran Bingham 	 */
165866d8c9d2SKieran Bingham 	max9286_i2c_mux_close(priv);
165966d8c9d2SKieran Bingham 
166066d8c9d2SKieran Bingham 	/*
166166d8c9d2SKieran Bingham 	 * The MAX9286 initialises with auto-acknowledge enabled by default.
166266d8c9d2SKieran Bingham 	 * This can be invasive to other transactions on the same bus, so
166366d8c9d2SKieran Bingham 	 * disable it early. It will be enabled only as and when needed.
166466d8c9d2SKieran Bingham 	 */
166566d8c9d2SKieran Bingham 	max9286_configure_i2c(priv, false);
166666d8c9d2SKieran Bingham 
1667c9352df7SJacopo Mondi 	ret = max9286_parse_gpios(priv);
166866d8c9d2SKieran Bingham 	if (ret)
166966d8c9d2SKieran Bingham 		goto err_powerdown;
167066d8c9d2SKieran Bingham 
1671817660f4SThomas Nizan 	if (!priv->use_gpio_poc) {
1672817660f4SThomas Nizan 		ret = max9286_get_poc_supplies(priv);
1673817660f4SThomas Nizan 		if (ret)
1674817660f4SThomas Nizan 			goto err_cleanup_dt;
1675817660f4SThomas Nizan 	}
167666d8c9d2SKieran Bingham 
1677365ab7ebSLaurentiu Palcu 	ret = max9286_init(priv);
167866d8c9d2SKieran Bingham 	if (ret < 0)
167966d8c9d2SKieran Bingham 		goto err_cleanup_dt;
168066d8c9d2SKieran Bingham 
168166d8c9d2SKieran Bingham 	return 0;
168266d8c9d2SKieran Bingham 
168366d8c9d2SKieran Bingham err_powerdown:
168466d8c9d2SKieran Bingham 	gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
1685e332061bSLaurent Pinchart err_cleanup_dt:
1686e332061bSLaurent Pinchart 	max9286_cleanup_dt(priv);
168766d8c9d2SKieran Bingham 
168866d8c9d2SKieran Bingham 	return ret;
168966d8c9d2SKieran Bingham }
169066d8c9d2SKieran Bingham 
1691ed5c2f5fSUwe Kleine-König static void max9286_remove(struct i2c_client *client)
169266d8c9d2SKieran Bingham {
1693365ab7ebSLaurentiu Palcu 	struct max9286_priv *priv = sd_to_max9286(i2c_get_clientdata(client));
169466d8c9d2SKieran Bingham 
169566d8c9d2SKieran Bingham 	i2c_mux_del_adapters(priv->mux);
169666d8c9d2SKieran Bingham 
169766d8c9d2SKieran Bingham 	max9286_v4l2_unregister(priv);
169866d8c9d2SKieran Bingham 
1699c9352df7SJacopo Mondi 	max9286_poc_enable(priv, false);
170066d8c9d2SKieran Bingham 
170166d8c9d2SKieran Bingham 	gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
170266d8c9d2SKieran Bingham 
170366d8c9d2SKieran Bingham 	max9286_cleanup_dt(priv);
170466d8c9d2SKieran Bingham }
170566d8c9d2SKieran Bingham 
170666d8c9d2SKieran Bingham static const struct of_device_id max9286_dt_ids[] = {
170766d8c9d2SKieran Bingham 	{ .compatible = "maxim,max9286" },
170866d8c9d2SKieran Bingham 	{},
170966d8c9d2SKieran Bingham };
171066d8c9d2SKieran Bingham MODULE_DEVICE_TABLE(of, max9286_dt_ids);
171166d8c9d2SKieran Bingham 
171266d8c9d2SKieran Bingham static struct i2c_driver max9286_i2c_driver = {
171366d8c9d2SKieran Bingham 	.driver	= {
171466d8c9d2SKieran Bingham 		.name		= "max9286",
171566d8c9d2SKieran Bingham 		.of_match_table	= of_match_ptr(max9286_dt_ids),
171666d8c9d2SKieran Bingham 	},
171766d8c9d2SKieran Bingham 	.probe_new	= max9286_probe,
171866d8c9d2SKieran Bingham 	.remove		= max9286_remove,
171966d8c9d2SKieran Bingham };
172066d8c9d2SKieran Bingham 
172166d8c9d2SKieran Bingham module_i2c_driver(max9286_i2c_driver);
172266d8c9d2SKieran Bingham 
172366d8c9d2SKieran Bingham MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
172466d8c9d2SKieran Bingham MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov");
172566d8c9d2SKieran Bingham MODULE_LICENSE("GPL");
1726