xref: /openbmc/linux/drivers/media/i2c/max9286.c (revision 9bceb077)
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 {
164adb2dcd5SSakari Ailus 	struct v4l2_async_connection base;
16586d37bf3SLaurent Pinchart 	struct max9286_source *source;
16686d37bf3SLaurent Pinchart };
16786d37bf3SLaurent Pinchart 
168adb2dcd5SSakari Ailus static inline struct max9286_asd *
to_max9286_asd(struct v4l2_async_connection * asd)169adb2dcd5SSakari Ailus to_max9286_asd(struct v4l2_async_connection *asd)
17086d37bf3SLaurent Pinchart {
17186d37bf3SLaurent Pinchart 	return container_of(asd, struct max9286_asd, base);
17286d37bf3SLaurent Pinchart }
17366d8c9d2SKieran Bingham 
17466d8c9d2SKieran Bingham struct max9286_priv {
17566d8c9d2SKieran Bingham 	struct i2c_client *client;
17666d8c9d2SKieran Bingham 	struct gpio_desc *gpiod_pwdn;
17766d8c9d2SKieran Bingham 	struct v4l2_subdev sd;
17866d8c9d2SKieran Bingham 	struct media_pad pads[MAX9286_N_PADS];
17966d8c9d2SKieran Bingham 	struct regulator *regulator;
18066d8c9d2SKieran Bingham 
18166d8c9d2SKieran Bingham 	struct gpio_chip gpio;
18266d8c9d2SKieran Bingham 	u8 gpio_state;
18366d8c9d2SKieran Bingham 
18466d8c9d2SKieran Bingham 	struct i2c_mux_core *mux;
18566d8c9d2SKieran Bingham 	unsigned int mux_channel;
18666d8c9d2SKieran Bingham 	bool mux_open;
18766d8c9d2SKieran Bingham 
188f78723ebSJacopo Mondi 	/* The initial reverse control channel amplitude. */
189f78723ebSJacopo Mondi 	u32 init_rev_chan_mv;
190902edc2aSJacopo Mondi 	u32 rev_chan_mv;
191e332061bSLaurent Pinchart 	u8 i2c_mstbt;
1923697f108SLaurent Pinchart 	u32 bus_width;
19385cb767cSJacopo Mondi 
194817660f4SThomas Nizan 	bool use_gpio_poc;
195c9352df7SJacopo Mondi 	u32 gpio_poc[2];
196c9352df7SJacopo Mondi 
19766d8c9d2SKieran Bingham 	struct v4l2_ctrl_handler ctrls;
198cffc9fb1SLaurent Pinchart 	struct v4l2_ctrl *pixelrate_ctrl;
199cffc9fb1SLaurent Pinchart 	unsigned int pixelrate;
20066d8c9d2SKieran Bingham 
20166d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
202cffc9fb1SLaurent Pinchart 	struct v4l2_fract interval;
20366d8c9d2SKieran Bingham 
20466d8c9d2SKieran Bingham 	/* Protects controls and fmt structures */
20566d8c9d2SKieran Bingham 	struct mutex mutex;
20666d8c9d2SKieran Bingham 
20766d8c9d2SKieran Bingham 	unsigned int nsources;
20866d8c9d2SKieran Bingham 	unsigned int source_mask;
20966d8c9d2SKieran Bingham 	unsigned int route_mask;
21066d8c9d2SKieran Bingham 	unsigned int bound_sources;
21166d8c9d2SKieran Bingham 	unsigned int csi2_data_lanes;
21266d8c9d2SKieran Bingham 	struct max9286_source sources[MAX9286_NUM_GMSL];
21366d8c9d2SKieran Bingham 	struct v4l2_async_notifier notifier;
21466d8c9d2SKieran Bingham };
21566d8c9d2SKieran Bingham 
next_source(struct max9286_priv * priv,struct max9286_source * source)21666d8c9d2SKieran Bingham static struct max9286_source *next_source(struct max9286_priv *priv,
21766d8c9d2SKieran Bingham 					  struct max9286_source *source)
21866d8c9d2SKieran Bingham {
21966d8c9d2SKieran Bingham 	if (!source)
22066d8c9d2SKieran Bingham 		source = &priv->sources[0];
22166d8c9d2SKieran Bingham 	else
22266d8c9d2SKieran Bingham 		source++;
22366d8c9d2SKieran Bingham 
22466d8c9d2SKieran Bingham 	for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) {
22566d8c9d2SKieran Bingham 		if (source->fwnode)
22666d8c9d2SKieran Bingham 			return source;
22766d8c9d2SKieran Bingham 	}
22866d8c9d2SKieran Bingham 
22966d8c9d2SKieran Bingham 	return NULL;
23066d8c9d2SKieran Bingham }
23166d8c9d2SKieran Bingham 
23266d8c9d2SKieran Bingham #define for_each_source(priv, source) \
23366d8c9d2SKieran Bingham 	for ((source) = NULL; ((source) = next_source((priv), (source))); )
23466d8c9d2SKieran Bingham 
23566d8c9d2SKieran Bingham #define to_index(priv, source) ((source) - &(priv)->sources[0])
23666d8c9d2SKieran Bingham 
sd_to_max9286(struct v4l2_subdev * sd)23766d8c9d2SKieran Bingham static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
23866d8c9d2SKieran Bingham {
23966d8c9d2SKieran Bingham 	return container_of(sd, struct max9286_priv, sd);
24066d8c9d2SKieran Bingham }
24166d8c9d2SKieran Bingham 
242f1403802SLaurent Pinchart static const struct max9286_format_info max9286_formats[] = {
243f1403802SLaurent Pinchart 	{
244f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_UYVY8_1X16,
245f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
246f1403802SLaurent Pinchart 	}, {
247f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_VYUY8_1X16,
248f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
249f1403802SLaurent Pinchart 	}, {
250f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_YUYV8_1X16,
251f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
252f1403802SLaurent Pinchart 	}, {
253f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_YVYU8_1X16,
254f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_YUV422_8BIT,
255f1403802SLaurent Pinchart 	}, {
256f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SBGGR12_1X12,
257f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
258f1403802SLaurent Pinchart 	}, {
259f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SGBRG12_1X12,
260f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
261f1403802SLaurent Pinchart 	}, {
262f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SGRBG12_1X12,
263f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
264f1403802SLaurent Pinchart 	}, {
265f1403802SLaurent Pinchart 		.code = MEDIA_BUS_FMT_SRGGB12_1X12,
266f1403802SLaurent Pinchart 		.datatype = MAX9286_DATATYPE_RAW12,
267f1403802SLaurent Pinchart 	},
268f1403802SLaurent Pinchart };
269f1403802SLaurent Pinchart 
270e332061bSLaurent Pinchart static const struct max9286_i2c_speed max9286_i2c_speeds[] = {
271e332061bSLaurent Pinchart 	{ .rate =   8470, .mstbt = MAX9286_I2CMSTBT_8KBPS },
272e332061bSLaurent Pinchart 	{ .rate =  28300, .mstbt = MAX9286_I2CMSTBT_28KBPS },
273e332061bSLaurent Pinchart 	{ .rate =  84700, .mstbt = MAX9286_I2CMSTBT_84KBPS },
274e332061bSLaurent Pinchart 	{ .rate = 105000, .mstbt = MAX9286_I2CMSTBT_105KBPS },
275e332061bSLaurent Pinchart 	{ .rate = 173000, .mstbt = MAX9286_I2CMSTBT_173KBPS },
276e332061bSLaurent Pinchart 	{ .rate = 339000, .mstbt = MAX9286_I2CMSTBT_339KBPS },
277e332061bSLaurent Pinchart 	{ .rate = 533000, .mstbt = MAX9286_I2CMSTBT_533KBPS },
278e332061bSLaurent Pinchart 	{ .rate = 837000, .mstbt = MAX9286_I2CMSTBT_837KBPS },
279e332061bSLaurent Pinchart };
280e332061bSLaurent Pinchart 
28166d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
28266d8c9d2SKieran Bingham  * I2C IO
28366d8c9d2SKieran Bingham  */
28466d8c9d2SKieran Bingham 
max9286_read(struct max9286_priv * priv,u8 reg)28566d8c9d2SKieran Bingham static int max9286_read(struct max9286_priv *priv, u8 reg)
28666d8c9d2SKieran Bingham {
28766d8c9d2SKieran Bingham 	int ret;
28866d8c9d2SKieran Bingham 
28966d8c9d2SKieran Bingham 	ret = i2c_smbus_read_byte_data(priv->client, reg);
29066d8c9d2SKieran Bingham 	if (ret < 0)
29166d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
29266d8c9d2SKieran Bingham 			"%s: register 0x%02x read failed (%d)\n",
29366d8c9d2SKieran Bingham 			__func__, reg, ret);
29466d8c9d2SKieran Bingham 
29566d8c9d2SKieran Bingham 	return ret;
29666d8c9d2SKieran Bingham }
29766d8c9d2SKieran Bingham 
max9286_write(struct max9286_priv * priv,u8 reg,u8 val)29866d8c9d2SKieran Bingham static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val)
29966d8c9d2SKieran Bingham {
30066d8c9d2SKieran Bingham 	int ret;
30166d8c9d2SKieran Bingham 
30266d8c9d2SKieran Bingham 	ret = i2c_smbus_write_byte_data(priv->client, reg, val);
30366d8c9d2SKieran Bingham 	if (ret < 0)
30466d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
30566d8c9d2SKieran Bingham 			"%s: register 0x%02x write failed (%d)\n",
30666d8c9d2SKieran Bingham 			__func__, reg, ret);
30766d8c9d2SKieran Bingham 
30866d8c9d2SKieran Bingham 	return ret;
30966d8c9d2SKieran Bingham }
31066d8c9d2SKieran Bingham 
31166d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
31266d8c9d2SKieran Bingham  * I2C Multiplexer
31366d8c9d2SKieran Bingham  */
31466d8c9d2SKieran Bingham 
max9286_i2c_mux_configure(struct max9286_priv * priv,u8 conf)31566d8c9d2SKieran Bingham static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf)
31666d8c9d2SKieran Bingham {
31766d8c9d2SKieran Bingham 	max9286_write(priv, 0x0a, conf);
31866d8c9d2SKieran Bingham 
31966d8c9d2SKieran Bingham 	/*
32066d8c9d2SKieran Bingham 	 * We must sleep after any change to the forward or reverse channel
32166d8c9d2SKieran Bingham 	 * configuration.
32266d8c9d2SKieran Bingham 	 */
32366d8c9d2SKieran Bingham 	usleep_range(3000, 5000);
32466d8c9d2SKieran Bingham }
32566d8c9d2SKieran Bingham 
max9286_i2c_mux_open(struct max9286_priv * priv)32666d8c9d2SKieran Bingham static void max9286_i2c_mux_open(struct max9286_priv *priv)
32766d8c9d2SKieran Bingham {
32866d8c9d2SKieran Bingham 	/* Open all channels on the MAX9286 */
32966d8c9d2SKieran Bingham 	max9286_i2c_mux_configure(priv, 0xff);
33066d8c9d2SKieran Bingham 
33166d8c9d2SKieran Bingham 	priv->mux_open = true;
33266d8c9d2SKieran Bingham }
33366d8c9d2SKieran Bingham 
max9286_i2c_mux_close(struct max9286_priv * priv)33466d8c9d2SKieran Bingham static void max9286_i2c_mux_close(struct max9286_priv *priv)
33566d8c9d2SKieran Bingham {
33666d8c9d2SKieran Bingham 	/*
33766d8c9d2SKieran Bingham 	 * Ensure that both the forward and reverse channel are disabled on the
33866d8c9d2SKieran Bingham 	 * mux, and that the channel ID is invalidated to ensure we reconfigure
33966d8c9d2SKieran Bingham 	 * on the next max9286_i2c_mux_select() call.
34066d8c9d2SKieran Bingham 	 */
34166d8c9d2SKieran Bingham 	max9286_i2c_mux_configure(priv, 0x00);
34266d8c9d2SKieran Bingham 
34366d8c9d2SKieran Bingham 	priv->mux_open = false;
34466d8c9d2SKieran Bingham 	priv->mux_channel = -1;
34566d8c9d2SKieran Bingham }
34666d8c9d2SKieran Bingham 
max9286_i2c_mux_select(struct i2c_mux_core * muxc,u32 chan)34766d8c9d2SKieran Bingham static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
34866d8c9d2SKieran Bingham {
34966d8c9d2SKieran Bingham 	struct max9286_priv *priv = i2c_mux_priv(muxc);
35066d8c9d2SKieran Bingham 
35166d8c9d2SKieran Bingham 	/* Channel select is disabled when configured in the opened state. */
35266d8c9d2SKieran Bingham 	if (priv->mux_open)
35366d8c9d2SKieran Bingham 		return 0;
35466d8c9d2SKieran Bingham 
35566d8c9d2SKieran Bingham 	if (priv->mux_channel == chan)
35666d8c9d2SKieran Bingham 		return 0;
35766d8c9d2SKieran Bingham 
35866d8c9d2SKieran Bingham 	priv->mux_channel = chan;
35966d8c9d2SKieran Bingham 
3603de09c7aSJacopo Mondi 	max9286_i2c_mux_configure(priv, MAX9286_FWDCCEN(chan) |
36166d8c9d2SKieran Bingham 					MAX9286_REVCCEN(chan));
36266d8c9d2SKieran Bingham 
36366d8c9d2SKieran Bingham 	return 0;
36466d8c9d2SKieran Bingham }
36566d8c9d2SKieran Bingham 
max9286_i2c_mux_init(struct max9286_priv * priv)36666d8c9d2SKieran Bingham static int max9286_i2c_mux_init(struct max9286_priv *priv)
36766d8c9d2SKieran Bingham {
36866d8c9d2SKieran Bingham 	struct max9286_source *source;
36966d8c9d2SKieran Bingham 	int ret;
37066d8c9d2SKieran Bingham 
37166d8c9d2SKieran Bingham 	if (!i2c_check_functionality(priv->client->adapter,
37266d8c9d2SKieran Bingham 				     I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
37366d8c9d2SKieran Bingham 		return -ENODEV;
37466d8c9d2SKieran Bingham 
37566d8c9d2SKieran Bingham 	priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,
37666d8c9d2SKieran Bingham 				  priv->nsources, 0, I2C_MUX_LOCKED,
37766d8c9d2SKieran Bingham 				  max9286_i2c_mux_select, NULL);
37866d8c9d2SKieran Bingham 	if (!priv->mux)
37966d8c9d2SKieran Bingham 		return -ENOMEM;
38066d8c9d2SKieran Bingham 
38166d8c9d2SKieran Bingham 	priv->mux->priv = priv;
38266d8c9d2SKieran Bingham 
38366d8c9d2SKieran Bingham 	for_each_source(priv, source) {
38466d8c9d2SKieran Bingham 		unsigned int index = to_index(priv, source);
38566d8c9d2SKieran Bingham 
38666d8c9d2SKieran Bingham 		ret = i2c_mux_add_adapter(priv->mux, 0, index, 0);
38766d8c9d2SKieran Bingham 		if (ret < 0)
38866d8c9d2SKieran Bingham 			goto error;
38966d8c9d2SKieran Bingham 	}
39066d8c9d2SKieran Bingham 
39166d8c9d2SKieran Bingham 	return 0;
39266d8c9d2SKieran Bingham 
39366d8c9d2SKieran Bingham error:
39466d8c9d2SKieran Bingham 	i2c_mux_del_adapters(priv->mux);
39566d8c9d2SKieran Bingham 	return ret;
39666d8c9d2SKieran Bingham }
39766d8c9d2SKieran Bingham 
max9286_configure_i2c(struct max9286_priv * priv,bool localack)39866d8c9d2SKieran Bingham static void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
39966d8c9d2SKieran Bingham {
40066d8c9d2SKieran Bingham 	u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
401e332061bSLaurent Pinchart 		    priv->i2c_mstbt;
40266d8c9d2SKieran Bingham 
40366d8c9d2SKieran Bingham 	if (localack)
40466d8c9d2SKieran Bingham 		config |= MAX9286_I2CLOCACK;
40566d8c9d2SKieran Bingham 
40666d8c9d2SKieran Bingham 	max9286_write(priv, 0x34, config);
40766d8c9d2SKieran Bingham 	usleep_range(3000, 5000);
40866d8c9d2SKieran Bingham }
40966d8c9d2SKieran Bingham 
max9286_reverse_channel_setup(struct max9286_priv * priv,unsigned int chan_amplitude)4105a386b1fSJacopo Mondi static void max9286_reverse_channel_setup(struct max9286_priv *priv,
4115a386b1fSJacopo Mondi 					  unsigned int chan_amplitude)
41202b57eb3SJacopo Mondi {
413902edc2aSJacopo Mondi 	u8 chan_config;
414902edc2aSJacopo Mondi 
415902edc2aSJacopo Mondi 	if (priv->rev_chan_mv == chan_amplitude)
416902edc2aSJacopo Mondi 		return;
417902edc2aSJacopo Mondi 
418902edc2aSJacopo Mondi 	priv->rev_chan_mv = chan_amplitude;
419902edc2aSJacopo Mondi 
4205a386b1fSJacopo Mondi 	/* Reverse channel transmission time: default to 1. */
421902edc2aSJacopo Mondi 	chan_config = MAX9286_REV_TRF(1);
4225a386b1fSJacopo Mondi 
42302b57eb3SJacopo Mondi 	/*
42402b57eb3SJacopo Mondi 	 * Reverse channel setup.
42502b57eb3SJacopo Mondi 	 *
42602b57eb3SJacopo Mondi 	 * - Enable custom reverse channel configuration (through register 0x3f)
42702b57eb3SJacopo Mondi 	 *   and set the first pulse length to 35 clock cycles.
4285a386b1fSJacopo Mondi 	 * - Adjust reverse channel amplitude: values > 130 are programmed
4295a386b1fSJacopo Mondi 	 *   using the additional +100mV REV_AMP_X boost flag
43002b57eb3SJacopo Mondi 	 */
43102b57eb3SJacopo Mondi 	max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
4325a386b1fSJacopo Mondi 
4335a386b1fSJacopo Mondi 	if (chan_amplitude > 100) {
4345a386b1fSJacopo Mondi 		/* It is not possible to express values (100 < x < 130) */
4355a386b1fSJacopo Mondi 		chan_amplitude = max(30U, chan_amplitude - 100);
4365a386b1fSJacopo Mondi 		chan_config |= MAX9286_REV_AMP_X;
4375a386b1fSJacopo Mondi 	}
4385a386b1fSJacopo Mondi 	max9286_write(priv, 0x3b, chan_config | MAX9286_REV_AMP(chan_amplitude));
43902b57eb3SJacopo Mondi 	usleep_range(2000, 2500);
44002b57eb3SJacopo Mondi }
44102b57eb3SJacopo Mondi 
44266d8c9d2SKieran Bingham /*
44366d8c9d2SKieran Bingham  * max9286_check_video_links() - Make sure video links are detected and locked
44466d8c9d2SKieran Bingham  *
44566d8c9d2SKieran Bingham  * Performs safety checks on video link status. Make sure they are detected
44666d8c9d2SKieran Bingham  * and all enabled links are locked.
44766d8c9d2SKieran Bingham  *
44866d8c9d2SKieran Bingham  * Returns 0 for success, -EIO for errors.
44966d8c9d2SKieran Bingham  */
max9286_check_video_links(struct max9286_priv * priv)45066d8c9d2SKieran Bingham static int max9286_check_video_links(struct max9286_priv *priv)
45166d8c9d2SKieran Bingham {
45266d8c9d2SKieran Bingham 	unsigned int i;
45366d8c9d2SKieran Bingham 	int ret;
45466d8c9d2SKieran Bingham 
45566d8c9d2SKieran Bingham 	/*
45666d8c9d2SKieran Bingham 	 * Make sure valid video links are detected.
45766d8c9d2SKieran Bingham 	 * The delay is not characterized in de-serializer manual, wait up
45866d8c9d2SKieran Bingham 	 * to 5 ms.
45966d8c9d2SKieran Bingham 	 */
46066d8c9d2SKieran Bingham 	for (i = 0; i < 10; i++) {
46166d8c9d2SKieran Bingham 		ret = max9286_read(priv, 0x49);
46266d8c9d2SKieran Bingham 		if (ret < 0)
46366d8c9d2SKieran Bingham 			return -EIO;
46466d8c9d2SKieran Bingham 
46566d8c9d2SKieran Bingham 		if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask)
46666d8c9d2SKieran Bingham 			break;
46766d8c9d2SKieran Bingham 
46866d8c9d2SKieran Bingham 		usleep_range(350, 500);
46966d8c9d2SKieran Bingham 	}
47066d8c9d2SKieran Bingham 
47166d8c9d2SKieran Bingham 	if (i == 10) {
47266d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
47366d8c9d2SKieran Bingham 			"Unable to detect video links: 0x%02x\n", ret);
47466d8c9d2SKieran Bingham 		return -EIO;
47566d8c9d2SKieran Bingham 	}
47666d8c9d2SKieran Bingham 
47766d8c9d2SKieran Bingham 	/* Make sure all enabled links are locked (4ms max). */
47866d8c9d2SKieran Bingham 	for (i = 0; i < 10; i++) {
47966d8c9d2SKieran Bingham 		ret = max9286_read(priv, 0x27);
48066d8c9d2SKieran Bingham 		if (ret < 0)
48166d8c9d2SKieran Bingham 			return -EIO;
48266d8c9d2SKieran Bingham 
48366d8c9d2SKieran Bingham 		if (ret & MAX9286_LOCKED)
48466d8c9d2SKieran Bingham 			break;
48566d8c9d2SKieran Bingham 
48666d8c9d2SKieran Bingham 		usleep_range(350, 450);
48766d8c9d2SKieran Bingham 	}
48866d8c9d2SKieran Bingham 
48966d8c9d2SKieran Bingham 	if (i == 10) {
49066d8c9d2SKieran Bingham 		dev_err(&priv->client->dev, "Not all enabled links locked\n");
49166d8c9d2SKieran Bingham 		return -EIO;
49266d8c9d2SKieran Bingham 	}
49366d8c9d2SKieran Bingham 
49466d8c9d2SKieran Bingham 	return 0;
49566d8c9d2SKieran Bingham }
49666d8c9d2SKieran Bingham 
49766d8c9d2SKieran Bingham /*
49866d8c9d2SKieran Bingham  * max9286_check_config_link() - Detect and wait for configuration links
49966d8c9d2SKieran Bingham  *
50066d8c9d2SKieran Bingham  * Determine if the configuration channel is up and settled for a link.
50166d8c9d2SKieran Bingham  *
50266d8c9d2SKieran Bingham  * Returns 0 for success, -EIO for errors.
50366d8c9d2SKieran Bingham  */
max9286_check_config_link(struct max9286_priv * priv,unsigned int source_mask)50466d8c9d2SKieran Bingham static int max9286_check_config_link(struct max9286_priv *priv,
50566d8c9d2SKieran Bingham 				     unsigned int source_mask)
50666d8c9d2SKieran Bingham {
50766d8c9d2SKieran Bingham 	unsigned int conflink_mask = (source_mask & 0x0f) << 4;
50866d8c9d2SKieran Bingham 	unsigned int i;
50966d8c9d2SKieran Bingham 	int ret;
51066d8c9d2SKieran Bingham 
51166d8c9d2SKieran Bingham 	/*
51266d8c9d2SKieran Bingham 	 * Make sure requested configuration links are detected.
51366d8c9d2SKieran Bingham 	 * The delay is not characterized in the chip manual: wait up
51466d8c9d2SKieran Bingham 	 * to 5 milliseconds.
51566d8c9d2SKieran Bingham 	 */
51666d8c9d2SKieran Bingham 	for (i = 0; i < 10; i++) {
517e5b95c8fSColin Ian King 		ret = max9286_read(priv, 0x49);
51866d8c9d2SKieran Bingham 		if (ret < 0)
51966d8c9d2SKieran Bingham 			return -EIO;
52066d8c9d2SKieran Bingham 
521e5b95c8fSColin Ian King 		ret &= 0xf0;
52266d8c9d2SKieran Bingham 		if (ret == conflink_mask)
52366d8c9d2SKieran Bingham 			break;
52466d8c9d2SKieran Bingham 
52566d8c9d2SKieran Bingham 		usleep_range(350, 500);
52666d8c9d2SKieran Bingham 	}
52766d8c9d2SKieran Bingham 
52866d8c9d2SKieran Bingham 	if (ret != conflink_mask) {
52966d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
53066d8c9d2SKieran Bingham 			"Unable to detect configuration links: 0x%02x expected 0x%02x\n",
53166d8c9d2SKieran Bingham 			ret, conflink_mask);
53266d8c9d2SKieran Bingham 		return -EIO;
53366d8c9d2SKieran Bingham 	}
53466d8c9d2SKieran Bingham 
53566d8c9d2SKieran Bingham 	dev_info(&priv->client->dev,
53666d8c9d2SKieran Bingham 		 "Successfully detected configuration links after %u loops: 0x%02x\n",
53766d8c9d2SKieran Bingham 		 i, conflink_mask);
53866d8c9d2SKieran Bingham 
53966d8c9d2SKieran Bingham 	return 0;
54066d8c9d2SKieran Bingham }
54166d8c9d2SKieran Bingham 
max9286_set_video_format(struct max9286_priv * priv,const struct v4l2_mbus_framefmt * format)542f1403802SLaurent Pinchart static void max9286_set_video_format(struct max9286_priv *priv,
543f1403802SLaurent Pinchart 				     const struct v4l2_mbus_framefmt *format)
544f1403802SLaurent Pinchart {
545f1403802SLaurent Pinchart 	const struct max9286_format_info *info = NULL;
546f1403802SLaurent Pinchart 	unsigned int i;
547f1403802SLaurent Pinchart 
548f1403802SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
549f1403802SLaurent Pinchart 		if (max9286_formats[i].code == format->code) {
550f1403802SLaurent Pinchart 			info = &max9286_formats[i];
551f1403802SLaurent Pinchart 			break;
552f1403802SLaurent Pinchart 		}
553f1403802SLaurent Pinchart 	}
554f1403802SLaurent Pinchart 
555f1403802SLaurent Pinchart 	if (WARN_ON(!info))
556f1403802SLaurent Pinchart 		return;
557f1403802SLaurent Pinchart 
558f1403802SLaurent Pinchart 	/*
559cdcb186eSLaurent Pinchart 	 * Video format setup: disable CSI output, set VC according to Link
560cdcb186eSLaurent Pinchart 	 * number, enable I2C clock stretching when CCBSY is low, enable CCBSY
561cdcb186eSLaurent Pinchart 	 * in external GPI-to-GPO mode.
562f1403802SLaurent Pinchart 	 */
563cdcb186eSLaurent Pinchart 	max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_EN_CCBSYB_CLK_STR |
564cdcb186eSLaurent Pinchart 		      MAX9286_EN_GPI_CCBSYB);
565f1403802SLaurent Pinchart 
566f1403802SLaurent Pinchart 	/* Enable CSI-2 Lane D0-D3 only, DBL mode. */
567f1403802SLaurent Pinchart 	max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
568f1403802SLaurent Pinchart 		      MAX9286_CSILANECNT(priv->csi2_data_lanes) |
569f1403802SLaurent Pinchart 		      info->datatype);
570f1403802SLaurent Pinchart 
57140f75457SLaurent Pinchart 	/*
57240f75457SLaurent Pinchart 	 * Enable HS/VS encoding, use HS as line valid source, use D14/15 for
57340f75457SLaurent Pinchart 	 * HS/VS, invert VS.
57440f75457SLaurent Pinchart 	 */
57540f75457SLaurent Pinchart 	max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_DESEL |
57640f75457SLaurent Pinchart 		      MAX9286_INVVS | MAX9286_HVSRC_D14);
577f1403802SLaurent Pinchart }
578f1403802SLaurent Pinchart 
max9286_set_fsync_period(struct max9286_priv * priv)579cffc9fb1SLaurent Pinchart static void max9286_set_fsync_period(struct max9286_priv *priv)
580cffc9fb1SLaurent Pinchart {
581cffc9fb1SLaurent Pinchart 	u32 fsync;
582cffc9fb1SLaurent Pinchart 
583cffc9fb1SLaurent Pinchart 	if (!priv->interval.numerator || !priv->interval.denominator) {
584cffc9fb1SLaurent Pinchart 		/*
585cffc9fb1SLaurent Pinchart 		 * Special case, a null interval enables automatic FRAMESYNC
586cffc9fb1SLaurent Pinchart 		 * mode. FRAMESYNC is taken from the slowest link.
587cffc9fb1SLaurent Pinchart 		 */
588cffc9fb1SLaurent Pinchart 		max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
589cffc9fb1SLaurent Pinchart 			      MAX9286_FSYNCMETH_AUTO);
590cffc9fb1SLaurent Pinchart 		return;
591cffc9fb1SLaurent Pinchart 	}
592cffc9fb1SLaurent Pinchart 
593cffc9fb1SLaurent Pinchart 	/*
594cffc9fb1SLaurent Pinchart 	 * Manual FRAMESYNC
595cffc9fb1SLaurent Pinchart 	 *
596cffc9fb1SLaurent Pinchart 	 * The FRAMESYNC generator is configured with a period expressed as a
597cffc9fb1SLaurent Pinchart 	 * number of PCLK periods.
598cffc9fb1SLaurent Pinchart 	 */
599cffc9fb1SLaurent Pinchart 	fsync = div_u64((u64)priv->pixelrate * priv->interval.numerator,
600cffc9fb1SLaurent Pinchart 			priv->interval.denominator);
601cffc9fb1SLaurent Pinchart 
602cffc9fb1SLaurent Pinchart 	dev_dbg(&priv->client->dev, "fsync period %u (pclk %u)\n", fsync,
603cffc9fb1SLaurent Pinchart 		priv->pixelrate);
604cffc9fb1SLaurent Pinchart 
605cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_OUT |
606cffc9fb1SLaurent Pinchart 		      MAX9286_FSYNCMETH_MANUAL);
607cffc9fb1SLaurent Pinchart 
608cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x06, (fsync >> 0) & 0xff);
609cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x07, (fsync >> 8) & 0xff);
610cffc9fb1SLaurent Pinchart 	max9286_write(priv, 0x08, (fsync >> 16) & 0xff);
611cffc9fb1SLaurent Pinchart }
612cffc9fb1SLaurent Pinchart 
61366d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
61466d8c9d2SKieran Bingham  * V4L2 Subdev
61566d8c9d2SKieran Bingham  */
61666d8c9d2SKieran Bingham 
max9286_set_pixelrate(struct max9286_priv * priv)61766d8c9d2SKieran Bingham static int max9286_set_pixelrate(struct max9286_priv *priv)
61866d8c9d2SKieran Bingham {
61966d8c9d2SKieran Bingham 	struct max9286_source *source = NULL;
62066d8c9d2SKieran Bingham 	u64 pixelrate = 0;
62166d8c9d2SKieran Bingham 
62266d8c9d2SKieran Bingham 	for_each_source(priv, source) {
62366d8c9d2SKieran Bingham 		struct v4l2_ctrl *ctrl;
62466d8c9d2SKieran Bingham 		u64 source_rate = 0;
62566d8c9d2SKieran Bingham 
62666d8c9d2SKieran Bingham 		/* Pixel rate is mandatory to be reported by sources. */
62766d8c9d2SKieran Bingham 		ctrl = v4l2_ctrl_find(source->sd->ctrl_handler,
62866d8c9d2SKieran Bingham 				      V4L2_CID_PIXEL_RATE);
62966d8c9d2SKieran Bingham 		if (!ctrl) {
63066d8c9d2SKieran Bingham 			pixelrate = 0;
63166d8c9d2SKieran Bingham 			break;
63266d8c9d2SKieran Bingham 		}
63366d8c9d2SKieran Bingham 
63466d8c9d2SKieran Bingham 		/* All source must report the same pixel rate. */
63566d8c9d2SKieran Bingham 		source_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
63666d8c9d2SKieran Bingham 		if (!pixelrate) {
63766d8c9d2SKieran Bingham 			pixelrate = source_rate;
63866d8c9d2SKieran Bingham 		} else if (pixelrate != source_rate) {
63966d8c9d2SKieran Bingham 			dev_err(&priv->client->dev,
64066d8c9d2SKieran Bingham 				"Unable to calculate pixel rate\n");
64166d8c9d2SKieran Bingham 			return -EINVAL;
64266d8c9d2SKieran Bingham 		}
64366d8c9d2SKieran Bingham 	}
64466d8c9d2SKieran Bingham 
64566d8c9d2SKieran Bingham 	if (!pixelrate) {
64666d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
64766d8c9d2SKieran Bingham 			"No pixel rate control available in sources\n");
64866d8c9d2SKieran Bingham 		return -EINVAL;
64966d8c9d2SKieran Bingham 	}
65066d8c9d2SKieran Bingham 
651cffc9fb1SLaurent Pinchart 	priv->pixelrate = pixelrate;
652cffc9fb1SLaurent Pinchart 
65366d8c9d2SKieran Bingham 	/*
65466d8c9d2SKieran Bingham 	 * The CSI-2 transmitter pixel rate is the single source rate multiplied
65566d8c9d2SKieran Bingham 	 * by the number of available sources.
65666d8c9d2SKieran Bingham 	 */
657cffc9fb1SLaurent Pinchart 	return v4l2_ctrl_s_ctrl_int64(priv->pixelrate_ctrl,
65866d8c9d2SKieran Bingham 				      pixelrate * priv->nsources);
65966d8c9d2SKieran Bingham }
66066d8c9d2SKieran Bingham 
max9286_notify_bound(struct v4l2_async_notifier * notifier,struct v4l2_subdev * subdev,struct v4l2_async_connection * asd)66166d8c9d2SKieran Bingham static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
66266d8c9d2SKieran Bingham 				struct v4l2_subdev *subdev,
663adb2dcd5SSakari Ailus 				struct v4l2_async_connection *asd)
66466d8c9d2SKieran Bingham {
66566d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(notifier->sd);
66686d37bf3SLaurent Pinchart 	struct max9286_source *source = to_max9286_asd(asd)->source;
66766d8c9d2SKieran Bingham 	unsigned int index = to_index(priv, source);
66866d8c9d2SKieran Bingham 	unsigned int src_pad;
66966d8c9d2SKieran Bingham 	int ret;
67066d8c9d2SKieran Bingham 
67166d8c9d2SKieran Bingham 	ret = media_entity_get_fwnode_pad(&subdev->entity,
67266d8c9d2SKieran Bingham 					  source->fwnode,
67366d8c9d2SKieran Bingham 					  MEDIA_PAD_FL_SOURCE);
67466d8c9d2SKieran Bingham 	if (ret < 0) {
67566d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
67666d8c9d2SKieran Bingham 			"Failed to find pad for %s\n", subdev->name);
67766d8c9d2SKieran Bingham 		return ret;
67866d8c9d2SKieran Bingham 	}
67966d8c9d2SKieran Bingham 
68066d8c9d2SKieran Bingham 	priv->bound_sources |= BIT(index);
68166d8c9d2SKieran Bingham 	source->sd = subdev;
68266d8c9d2SKieran Bingham 	src_pad = ret;
68366d8c9d2SKieran Bingham 
68466d8c9d2SKieran Bingham 	ret = media_create_pad_link(&source->sd->entity, src_pad,
68566d8c9d2SKieran Bingham 				    &priv->sd.entity, index,
68666d8c9d2SKieran Bingham 				    MEDIA_LNK_FL_ENABLED |
68766d8c9d2SKieran Bingham 				    MEDIA_LNK_FL_IMMUTABLE);
68866d8c9d2SKieran Bingham 	if (ret) {
68966d8c9d2SKieran Bingham 		dev_err(&priv->client->dev,
69066d8c9d2SKieran Bingham 			"Unable to link %s:%u -> %s:%u\n",
69166d8c9d2SKieran Bingham 			source->sd->name, src_pad, priv->sd.name, index);
69266d8c9d2SKieran Bingham 		return ret;
69366d8c9d2SKieran Bingham 	}
69466d8c9d2SKieran Bingham 
69566d8c9d2SKieran Bingham 	dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n",
69666d8c9d2SKieran Bingham 		subdev->name, src_pad, index);
69766d8c9d2SKieran Bingham 
69866d8c9d2SKieran Bingham 	/*
6994ff5278dSJacopo Mondi 	 * As we register a subdev notifiers we won't get a .complete() callback
7004ff5278dSJacopo Mondi 	 * here, so we have to use bound_sources to identify when all remote
7014ff5278dSJacopo Mondi 	 * serializers have probed.
70266d8c9d2SKieran Bingham 	 */
70366d8c9d2SKieran Bingham 	if (priv->bound_sources != priv->source_mask)
70466d8c9d2SKieran Bingham 		return 0;
70566d8c9d2SKieran Bingham 
70666d8c9d2SKieran Bingham 	/*
70766d8c9d2SKieran Bingham 	 * All enabled sources have probed and enabled their reverse control
70866d8c9d2SKieran Bingham 	 * channels:
70966d8c9d2SKieran Bingham 	 *
71085cb767cSJacopo Mondi 	 * - Increase the reverse channel amplitude to compensate for the
711731c24ffSJacopo Mondi 	 *   remote ends high threshold
71266d8c9d2SKieran Bingham 	 * - Verify all configuration links are properly detected
71366d8c9d2SKieran Bingham 	 * - Disable auto-ack as communication on the control channel are now
71466d8c9d2SKieran Bingham 	 *   stable.
71566d8c9d2SKieran Bingham 	 */
716731c24ffSJacopo Mondi 	max9286_reverse_channel_setup(priv, MAX9286_REV_AMP_HIGH);
71766d8c9d2SKieran Bingham 	max9286_check_config_link(priv, priv->source_mask);
71866d8c9d2SKieran Bingham 	max9286_configure_i2c(priv, false);
71966d8c9d2SKieran Bingham 
72066d8c9d2SKieran Bingham 	return max9286_set_pixelrate(priv);
72166d8c9d2SKieran Bingham }
72266d8c9d2SKieran Bingham 
max9286_notify_unbind(struct v4l2_async_notifier * notifier,struct v4l2_subdev * subdev,struct v4l2_async_connection * asd)72366d8c9d2SKieran Bingham static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
72466d8c9d2SKieran Bingham 				  struct v4l2_subdev *subdev,
725adb2dcd5SSakari Ailus 				  struct v4l2_async_connection *asd)
72666d8c9d2SKieran Bingham {
72766d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(notifier->sd);
72886d37bf3SLaurent Pinchart 	struct max9286_source *source = to_max9286_asd(asd)->source;
72966d8c9d2SKieran Bingham 	unsigned int index = to_index(priv, source);
73066d8c9d2SKieran Bingham 
73166d8c9d2SKieran Bingham 	source->sd = NULL;
73266d8c9d2SKieran Bingham 	priv->bound_sources &= ~BIT(index);
73366d8c9d2SKieran Bingham }
73466d8c9d2SKieran Bingham 
73566d8c9d2SKieran Bingham static const struct v4l2_async_notifier_operations max9286_notify_ops = {
73666d8c9d2SKieran Bingham 	.bound = max9286_notify_bound,
73766d8c9d2SKieran Bingham 	.unbind = max9286_notify_unbind,
73866d8c9d2SKieran Bingham };
73966d8c9d2SKieran Bingham 
max9286_v4l2_notifier_register(struct max9286_priv * priv)74066d8c9d2SKieran Bingham static int max9286_v4l2_notifier_register(struct max9286_priv *priv)
74166d8c9d2SKieran Bingham {
74266d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
74366d8c9d2SKieran Bingham 	struct max9286_source *source = NULL;
74466d8c9d2SKieran Bingham 	int ret;
74566d8c9d2SKieran Bingham 
74666d8c9d2SKieran Bingham 	if (!priv->nsources)
74766d8c9d2SKieran Bingham 		return 0;
74866d8c9d2SKieran Bingham 
749*b8ec754aSSakari Ailus 	v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd);
75066d8c9d2SKieran Bingham 
75166d8c9d2SKieran Bingham 	for_each_source(priv, source) {
75266d8c9d2SKieran Bingham 		unsigned int i = to_index(priv, source);
753b01edcbdSLaurent Pinchart 		struct max9286_asd *mas;
75466d8c9d2SKieran Bingham 
7553c8c1539SSakari Ailus 		mas = v4l2_async_nf_add_fwnode(&priv->notifier, source->fwnode,
756b01edcbdSLaurent Pinchart 					       struct max9286_asd);
757b01edcbdSLaurent Pinchart 		if (IS_ERR(mas)) {
75886d37bf3SLaurent Pinchart 			dev_err(dev, "Failed to add subdev for source %u: %ld",
759b01edcbdSLaurent Pinchart 				i, PTR_ERR(mas));
7603c8c1539SSakari Ailus 			v4l2_async_nf_cleanup(&priv->notifier);
761b01edcbdSLaurent Pinchart 			return PTR_ERR(mas);
76266d8c9d2SKieran Bingham 		}
76366d8c9d2SKieran Bingham 
764b01edcbdSLaurent Pinchart 		mas->source = source;
76566d8c9d2SKieran Bingham 	}
76666d8c9d2SKieran Bingham 
76766d8c9d2SKieran Bingham 	priv->notifier.ops = &max9286_notify_ops;
76866d8c9d2SKieran Bingham 
769*b8ec754aSSakari Ailus 	ret = v4l2_async_nf_register(&priv->notifier);
77066d8c9d2SKieran Bingham 	if (ret) {
77166d8c9d2SKieran Bingham 		dev_err(dev, "Failed to register subdev_notifier");
7723c8c1539SSakari Ailus 		v4l2_async_nf_cleanup(&priv->notifier);
77366d8c9d2SKieran Bingham 		return ret;
77466d8c9d2SKieran Bingham 	}
77566d8c9d2SKieran Bingham 
77666d8c9d2SKieran Bingham 	return 0;
77766d8c9d2SKieran Bingham }
77866d8c9d2SKieran Bingham 
max9286_v4l2_notifier_unregister(struct max9286_priv * priv)77966d8c9d2SKieran Bingham static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv)
78066d8c9d2SKieran Bingham {
78166d8c9d2SKieran Bingham 	if (!priv->nsources)
78266d8c9d2SKieran Bingham 		return;
78366d8c9d2SKieran Bingham 
7843c8c1539SSakari Ailus 	v4l2_async_nf_unregister(&priv->notifier);
7853c8c1539SSakari Ailus 	v4l2_async_nf_cleanup(&priv->notifier);
78666d8c9d2SKieran Bingham }
78766d8c9d2SKieran Bingham 
max9286_s_stream(struct v4l2_subdev * sd,int enable)78866d8c9d2SKieran Bingham static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
78966d8c9d2SKieran Bingham {
79066d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(sd);
79166d8c9d2SKieran Bingham 	struct max9286_source *source;
79266d8c9d2SKieran Bingham 	unsigned int i;
79366d8c9d2SKieran Bingham 	bool sync = false;
79466d8c9d2SKieran Bingham 	int ret;
79566d8c9d2SKieran Bingham 
79666d8c9d2SKieran Bingham 	if (enable) {
797f1403802SLaurent Pinchart 		const struct v4l2_mbus_framefmt *format;
798f1403802SLaurent Pinchart 
799f1403802SLaurent Pinchart 		/*
800f1403802SLaurent Pinchart 		 * Get the format from the first used sink pad, as all sink
801f1403802SLaurent Pinchart 		 * formats must be identical.
802f1403802SLaurent Pinchart 		 */
803f1403802SLaurent Pinchart 		format = &priv->fmt[__ffs(priv->bound_sources)];
804f1403802SLaurent Pinchart 
805f1403802SLaurent Pinchart 		max9286_set_video_format(priv, format);
806cffc9fb1SLaurent Pinchart 		max9286_set_fsync_period(priv);
807cffc9fb1SLaurent Pinchart 
80866d8c9d2SKieran Bingham 		/*
80966d8c9d2SKieran Bingham 		 * The frame sync between cameras is transmitted across the
81066d8c9d2SKieran Bingham 		 * reverse channel as GPIO. We must open all channels while
81166d8c9d2SKieran Bingham 		 * streaming to allow this synchronisation signal to be shared.
81266d8c9d2SKieran Bingham 		 */
81366d8c9d2SKieran Bingham 		max9286_i2c_mux_open(priv);
81466d8c9d2SKieran Bingham 
81566d8c9d2SKieran Bingham 		/* Start all cameras. */
81666d8c9d2SKieran Bingham 		for_each_source(priv, source) {
81766d8c9d2SKieran Bingham 			ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
81866d8c9d2SKieran Bingham 			if (ret)
81966d8c9d2SKieran Bingham 				return ret;
82066d8c9d2SKieran Bingham 		}
82166d8c9d2SKieran Bingham 
82266d8c9d2SKieran Bingham 		ret = max9286_check_video_links(priv);
82366d8c9d2SKieran Bingham 		if (ret)
82466d8c9d2SKieran Bingham 			return ret;
82566d8c9d2SKieran Bingham 
82666d8c9d2SKieran Bingham 		/*
82766d8c9d2SKieran Bingham 		 * Wait until frame synchronization is locked.
82866d8c9d2SKieran Bingham 		 *
82966d8c9d2SKieran Bingham 		 * Manual says frame sync locking should take ~6 VTS.
83066d8c9d2SKieran Bingham 		 * From practical experience at least 8 are required. Give
83166d8c9d2SKieran Bingham 		 * 12 complete frames time (~400ms at 30 fps) to achieve frame
83266d8c9d2SKieran Bingham 		 * locking before returning error.
83366d8c9d2SKieran Bingham 		 */
83466d8c9d2SKieran Bingham 		for (i = 0; i < 40; i++) {
83566d8c9d2SKieran Bingham 			if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) {
83666d8c9d2SKieran Bingham 				sync = true;
83766d8c9d2SKieran Bingham 				break;
83866d8c9d2SKieran Bingham 			}
83966d8c9d2SKieran Bingham 			usleep_range(9000, 11000);
84066d8c9d2SKieran Bingham 		}
84166d8c9d2SKieran Bingham 
84266d8c9d2SKieran Bingham 		if (!sync) {
84366d8c9d2SKieran Bingham 			dev_err(&priv->client->dev,
84466d8c9d2SKieran Bingham 				"Failed to get frame synchronization\n");
84566d8c9d2SKieran Bingham 			return -EXDEV; /* Invalid cross-device link */
84666d8c9d2SKieran Bingham 		}
84766d8c9d2SKieran Bingham 
84866d8c9d2SKieran Bingham 		/*
849cdcb186eSLaurent Pinchart 		 * Configure the CSI-2 output to line interleaved mode (W x (N
850cdcb186eSLaurent Pinchart 		 * x H), as opposed to the (N x W) x H mode that outputs the
851cdcb186eSLaurent Pinchart 		 * images stitched side-by-side) and enable it.
85266d8c9d2SKieran Bingham 		 */
853cdcb186eSLaurent Pinchart 		max9286_write(priv, 0x15, MAX9286_CSI_IMAGE_TYP | MAX9286_VCTYPE |
854cdcb186eSLaurent Pinchart 			      MAX9286_CSIOUTEN | MAX9286_EN_CCBSYB_CLK_STR |
855cdcb186eSLaurent Pinchart 			      MAX9286_EN_GPI_CCBSYB);
85666d8c9d2SKieran Bingham 	} else {
857cdcb186eSLaurent Pinchart 		max9286_write(priv, 0x15, MAX9286_VCTYPE |
858cdcb186eSLaurent Pinchart 			      MAX9286_EN_CCBSYB_CLK_STR |
859cdcb186eSLaurent Pinchart 			      MAX9286_EN_GPI_CCBSYB);
86066d8c9d2SKieran Bingham 
86166d8c9d2SKieran Bingham 		/* Stop all cameras. */
86266d8c9d2SKieran Bingham 		for_each_source(priv, source)
86366d8c9d2SKieran Bingham 			v4l2_subdev_call(source->sd, video, s_stream, 0);
86466d8c9d2SKieran Bingham 
86566d8c9d2SKieran Bingham 		max9286_i2c_mux_close(priv);
86666d8c9d2SKieran Bingham 	}
86766d8c9d2SKieran Bingham 
86866d8c9d2SKieran Bingham 	return 0;
86966d8c9d2SKieran Bingham }
87066d8c9d2SKieran Bingham 
max9286_g_frame_interval(struct v4l2_subdev * sd,struct v4l2_subdev_frame_interval * interval)871cffc9fb1SLaurent Pinchart static int max9286_g_frame_interval(struct v4l2_subdev *sd,
872cffc9fb1SLaurent Pinchart 				    struct v4l2_subdev_frame_interval *interval)
873cffc9fb1SLaurent Pinchart {
874cffc9fb1SLaurent Pinchart 	struct max9286_priv *priv = sd_to_max9286(sd);
875cffc9fb1SLaurent Pinchart 
876cffc9fb1SLaurent Pinchart 	if (interval->pad != MAX9286_SRC_PAD)
877cffc9fb1SLaurent Pinchart 		return -EINVAL;
878cffc9fb1SLaurent Pinchart 
879cffc9fb1SLaurent Pinchart 	interval->interval = priv->interval;
880cffc9fb1SLaurent Pinchart 
881cffc9fb1SLaurent Pinchart 	return 0;
882cffc9fb1SLaurent Pinchart }
883cffc9fb1SLaurent Pinchart 
max9286_s_frame_interval(struct v4l2_subdev * sd,struct v4l2_subdev_frame_interval * interval)884cffc9fb1SLaurent Pinchart static int max9286_s_frame_interval(struct v4l2_subdev *sd,
885cffc9fb1SLaurent Pinchart 				    struct v4l2_subdev_frame_interval *interval)
886cffc9fb1SLaurent Pinchart {
887cffc9fb1SLaurent Pinchart 	struct max9286_priv *priv = sd_to_max9286(sd);
888cffc9fb1SLaurent Pinchart 
889cffc9fb1SLaurent Pinchart 	if (interval->pad != MAX9286_SRC_PAD)
890cffc9fb1SLaurent Pinchart 		return -EINVAL;
891cffc9fb1SLaurent Pinchart 
892cffc9fb1SLaurent Pinchart 	priv->interval = interval->interval;
893cffc9fb1SLaurent Pinchart 
894cffc9fb1SLaurent Pinchart 	return 0;
895cffc9fb1SLaurent Pinchart }
896cffc9fb1SLaurent Pinchart 
max9286_enum_mbus_code(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_mbus_code_enum * code)89766d8c9d2SKieran Bingham static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
8980d346d2aSTomi Valkeinen 				  struct v4l2_subdev_state *sd_state,
89966d8c9d2SKieran Bingham 				  struct v4l2_subdev_mbus_code_enum *code)
90066d8c9d2SKieran Bingham {
90166d8c9d2SKieran Bingham 	if (code->pad || code->index > 0)
90266d8c9d2SKieran Bingham 		return -EINVAL;
90366d8c9d2SKieran Bingham 
90466d8c9d2SKieran Bingham 	code->code = MEDIA_BUS_FMT_UYVY8_1X16;
90566d8c9d2SKieran Bingham 
90666d8c9d2SKieran Bingham 	return 0;
90766d8c9d2SKieran Bingham }
90866d8c9d2SKieran Bingham 
90966d8c9d2SKieran Bingham static struct v4l2_mbus_framefmt *
max9286_get_pad_format(struct max9286_priv * priv,struct v4l2_subdev_state * sd_state,unsigned int pad,u32 which)91066d8c9d2SKieran Bingham max9286_get_pad_format(struct max9286_priv *priv,
9110d346d2aSTomi Valkeinen 		       struct v4l2_subdev_state *sd_state,
91266d8c9d2SKieran Bingham 		       unsigned int pad, u32 which)
91366d8c9d2SKieran Bingham {
91466d8c9d2SKieran Bingham 	switch (which) {
91566d8c9d2SKieran Bingham 	case V4L2_SUBDEV_FORMAT_TRY:
9160d346d2aSTomi Valkeinen 		return v4l2_subdev_get_try_format(&priv->sd, sd_state, pad);
91766d8c9d2SKieran Bingham 	case V4L2_SUBDEV_FORMAT_ACTIVE:
91866d8c9d2SKieran Bingham 		return &priv->fmt[pad];
91966d8c9d2SKieran Bingham 	default:
92066d8c9d2SKieran Bingham 		return NULL;
92166d8c9d2SKieran Bingham 	}
92266d8c9d2SKieran Bingham }
92366d8c9d2SKieran Bingham 
max9286_set_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)92466d8c9d2SKieran Bingham static int max9286_set_fmt(struct v4l2_subdev *sd,
9250d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
92666d8c9d2SKieran Bingham 			   struct v4l2_subdev_format *format)
92766d8c9d2SKieran Bingham {
92866d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(sd);
92966d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt *cfg_fmt;
930f1403802SLaurent Pinchart 	unsigned int i;
93166d8c9d2SKieran Bingham 
93266d8c9d2SKieran Bingham 	if (format->pad == MAX9286_SRC_PAD)
93366d8c9d2SKieran Bingham 		return -EINVAL;
93466d8c9d2SKieran Bingham 
935f1403802SLaurent Pinchart 	/* Validate the format. */
936f1403802SLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(max9286_formats); ++i) {
937f1403802SLaurent Pinchart 		if (max9286_formats[i].code == format->format.code)
93866d8c9d2SKieran Bingham 			break;
93966d8c9d2SKieran Bingham 	}
94066d8c9d2SKieran Bingham 
941f1403802SLaurent Pinchart 	if (i == ARRAY_SIZE(max9286_formats))
942f1403802SLaurent Pinchart 		format->format.code = max9286_formats[0].code;
943f1403802SLaurent Pinchart 
9440d346d2aSTomi Valkeinen 	cfg_fmt = max9286_get_pad_format(priv, sd_state, format->pad,
9450d346d2aSTomi Valkeinen 					 format->which);
94666d8c9d2SKieran Bingham 	if (!cfg_fmt)
94766d8c9d2SKieran Bingham 		return -EINVAL;
94866d8c9d2SKieran Bingham 
94966d8c9d2SKieran Bingham 	mutex_lock(&priv->mutex);
95066d8c9d2SKieran Bingham 	*cfg_fmt = format->format;
95166d8c9d2SKieran Bingham 	mutex_unlock(&priv->mutex);
95266d8c9d2SKieran Bingham 
95366d8c9d2SKieran Bingham 	return 0;
95466d8c9d2SKieran Bingham }
95566d8c9d2SKieran Bingham 
max9286_get_fmt(struct v4l2_subdev * sd,struct v4l2_subdev_state * sd_state,struct v4l2_subdev_format * format)95666d8c9d2SKieran Bingham static int max9286_get_fmt(struct v4l2_subdev *sd,
9570d346d2aSTomi Valkeinen 			   struct v4l2_subdev_state *sd_state,
95866d8c9d2SKieran Bingham 			   struct v4l2_subdev_format *format)
95966d8c9d2SKieran Bingham {
96066d8c9d2SKieran Bingham 	struct max9286_priv *priv = sd_to_max9286(sd);
96166d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt *cfg_fmt;
96266d8c9d2SKieran Bingham 	unsigned int pad = format->pad;
96366d8c9d2SKieran Bingham 
96466d8c9d2SKieran Bingham 	/*
96566d8c9d2SKieran Bingham 	 * Multiplexed Stream Support: Support link validation by returning the
96666d8c9d2SKieran Bingham 	 * format of the first bound link. All links must have the same format,
96766d8c9d2SKieran Bingham 	 * as we do not support mixing and matching of cameras connected to the
96866d8c9d2SKieran Bingham 	 * max9286.
96966d8c9d2SKieran Bingham 	 */
97066d8c9d2SKieran Bingham 	if (pad == MAX9286_SRC_PAD)
97166d8c9d2SKieran Bingham 		pad = __ffs(priv->bound_sources);
97266d8c9d2SKieran Bingham 
9730d346d2aSTomi Valkeinen 	cfg_fmt = max9286_get_pad_format(priv, sd_state, pad, format->which);
97466d8c9d2SKieran Bingham 	if (!cfg_fmt)
97566d8c9d2SKieran Bingham 		return -EINVAL;
97666d8c9d2SKieran Bingham 
97766d8c9d2SKieran Bingham 	mutex_lock(&priv->mutex);
97866d8c9d2SKieran Bingham 	format->format = *cfg_fmt;
97966d8c9d2SKieran Bingham 	mutex_unlock(&priv->mutex);
98066d8c9d2SKieran Bingham 
98166d8c9d2SKieran Bingham 	return 0;
98266d8c9d2SKieran Bingham }
98366d8c9d2SKieran Bingham 
98466d8c9d2SKieran Bingham static const struct v4l2_subdev_video_ops max9286_video_ops = {
98566d8c9d2SKieran Bingham 	.s_stream	= max9286_s_stream,
986cffc9fb1SLaurent Pinchart 	.g_frame_interval = max9286_g_frame_interval,
987cffc9fb1SLaurent Pinchart 	.s_frame_interval = max9286_s_frame_interval,
98866d8c9d2SKieran Bingham };
98966d8c9d2SKieran Bingham 
99066d8c9d2SKieran Bingham static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
99166d8c9d2SKieran Bingham 	.enum_mbus_code = max9286_enum_mbus_code,
99266d8c9d2SKieran Bingham 	.get_fmt	= max9286_get_fmt,
99366d8c9d2SKieran Bingham 	.set_fmt	= max9286_set_fmt,
99466d8c9d2SKieran Bingham };
99566d8c9d2SKieran Bingham 
99666d8c9d2SKieran Bingham static const struct v4l2_subdev_ops max9286_subdev_ops = {
99766d8c9d2SKieran Bingham 	.video		= &max9286_video_ops,
99866d8c9d2SKieran Bingham 	.pad		= &max9286_pad_ops,
99966d8c9d2SKieran Bingham };
100066d8c9d2SKieran Bingham 
1001f1403802SLaurent Pinchart static const struct v4l2_mbus_framefmt max9286_default_format = {
1002f1403802SLaurent Pinchart 	.width		= 1280,
1003f1403802SLaurent Pinchart 	.height		= 800,
1004f1403802SLaurent Pinchart 	.code		= MEDIA_BUS_FMT_UYVY8_1X16,
1005f1403802SLaurent Pinchart 	.colorspace	= V4L2_COLORSPACE_SRGB,
1006f1403802SLaurent Pinchart 	.field		= V4L2_FIELD_NONE,
1007f1403802SLaurent Pinchart 	.ycbcr_enc	= V4L2_YCBCR_ENC_DEFAULT,
1008f1403802SLaurent Pinchart 	.quantization	= V4L2_QUANTIZATION_DEFAULT,
1009f1403802SLaurent Pinchart 	.xfer_func	= V4L2_XFER_FUNC_DEFAULT,
1010f1403802SLaurent Pinchart };
1011f1403802SLaurent Pinchart 
max9286_init_format(struct v4l2_mbus_framefmt * fmt)101266d8c9d2SKieran Bingham static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
101366d8c9d2SKieran Bingham {
1014f1403802SLaurent Pinchart 	*fmt = max9286_default_format;
101566d8c9d2SKieran Bingham }
101666d8c9d2SKieran Bingham 
max9286_open(struct v4l2_subdev * subdev,struct v4l2_subdev_fh * fh)101766d8c9d2SKieran Bingham static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
101866d8c9d2SKieran Bingham {
101966d8c9d2SKieran Bingham 	struct v4l2_mbus_framefmt *format;
102066d8c9d2SKieran Bingham 	unsigned int i;
102166d8c9d2SKieran Bingham 
102266d8c9d2SKieran Bingham 	for (i = 0; i < MAX9286_N_SINKS; i++) {
10230d346d2aSTomi Valkeinen 		format = v4l2_subdev_get_try_format(subdev, fh->state, i);
102466d8c9d2SKieran Bingham 		max9286_init_format(format);
102566d8c9d2SKieran Bingham 	}
102666d8c9d2SKieran Bingham 
102766d8c9d2SKieran Bingham 	return 0;
102866d8c9d2SKieran Bingham }
102966d8c9d2SKieran Bingham 
103066d8c9d2SKieran Bingham static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
103166d8c9d2SKieran Bingham 	.open = max9286_open,
103266d8c9d2SKieran Bingham };
103366d8c9d2SKieran Bingham 
10341fe6ae4eSLaurent Pinchart static const struct media_entity_operations max9286_media_ops = {
10351fe6ae4eSLaurent Pinchart 	.link_validate = v4l2_subdev_link_validate
10361fe6ae4eSLaurent Pinchart };
10371fe6ae4eSLaurent Pinchart 
max9286_s_ctrl(struct v4l2_ctrl * ctrl)103866d8c9d2SKieran Bingham static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
103966d8c9d2SKieran Bingham {
104066d8c9d2SKieran Bingham 	switch (ctrl->id) {
104166d8c9d2SKieran Bingham 	case V4L2_CID_PIXEL_RATE:
104266d8c9d2SKieran Bingham 		return 0;
104366d8c9d2SKieran Bingham 	default:
104466d8c9d2SKieran Bingham 		return -EINVAL;
104566d8c9d2SKieran Bingham 	}
104666d8c9d2SKieran Bingham }
104766d8c9d2SKieran Bingham 
104866d8c9d2SKieran Bingham static const struct v4l2_ctrl_ops max9286_ctrl_ops = {
104966d8c9d2SKieran Bingham 	.s_ctrl = max9286_s_ctrl,
105066d8c9d2SKieran Bingham };
105166d8c9d2SKieran Bingham 
max9286_v4l2_register(struct max9286_priv * priv)105266d8c9d2SKieran Bingham static int max9286_v4l2_register(struct max9286_priv *priv)
105366d8c9d2SKieran Bingham {
105466d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
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 	ret = v4l2_async_register_subdev(&priv->sd);
109766d8c9d2SKieran Bingham 	if (ret < 0) {
109866d8c9d2SKieran Bingham 		dev_err(dev, "Unable to register subdevice\n");
10991029939bSSakari Ailus 		goto err_async;
110066d8c9d2SKieran Bingham 	}
110166d8c9d2SKieran Bingham 
110266d8c9d2SKieran Bingham 	return 0;
110366d8c9d2SKieran Bingham 
110466d8c9d2SKieran Bingham err_async:
11058636c5fcSShang XiaoJing 	v4l2_ctrl_handler_free(&priv->ctrls);
110666d8c9d2SKieran Bingham 	max9286_v4l2_notifier_unregister(priv);
110766d8c9d2SKieran Bingham 
110866d8c9d2SKieran Bingham 	return ret;
110966d8c9d2SKieran Bingham }
111066d8c9d2SKieran Bingham 
max9286_v4l2_unregister(struct max9286_priv * priv)111166d8c9d2SKieran Bingham static void max9286_v4l2_unregister(struct max9286_priv *priv)
111266d8c9d2SKieran Bingham {
1113bfce6a12SLaurent Pinchart 	v4l2_ctrl_handler_free(&priv->ctrls);
111466d8c9d2SKieran Bingham 	v4l2_async_unregister_subdev(&priv->sd);
111566d8c9d2SKieran Bingham 	max9286_v4l2_notifier_unregister(priv);
111666d8c9d2SKieran Bingham }
111766d8c9d2SKieran Bingham 
111866d8c9d2SKieran Bingham /* -----------------------------------------------------------------------------
111966d8c9d2SKieran Bingham  * Probe/Remove
112066d8c9d2SKieran Bingham  */
112166d8c9d2SKieran Bingham 
max9286_setup(struct max9286_priv * priv)112266d8c9d2SKieran Bingham static int max9286_setup(struct max9286_priv *priv)
112366d8c9d2SKieran Bingham {
112466d8c9d2SKieran Bingham 	/*
112566d8c9d2SKieran Bingham 	 * Link ordering values for all enabled links combinations. Orders must
112666d8c9d2SKieran Bingham 	 * be assigned sequentially from 0 to the number of enabled links
112766d8c9d2SKieran Bingham 	 * without leaving any hole for disabled links. We thus assign orders to
112866d8c9d2SKieran Bingham 	 * enabled links first, and use the remaining order values for disabled
112966d8c9d2SKieran Bingham 	 * links are all links must have a different order value;
113066d8c9d2SKieran Bingham 	 */
113166d8c9d2SKieran Bingham 	static const u8 link_order[] = {
113266d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
113366d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
113466d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
113566d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
113666d8c9d2SKieran Bingham 		(3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
113766d8c9d2SKieran Bingham 		(3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
113866d8c9d2SKieran Bingham 		(3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
113966d8c9d2SKieran Bingham 		(3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
114066d8c9d2SKieran Bingham 		(0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
114166d8c9d2SKieran Bingham 		(1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
114266d8c9d2SKieran Bingham 		(1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
114366d8c9d2SKieran Bingham 		(2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
114466d8c9d2SKieran Bingham 		(1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
114566d8c9d2SKieran Bingham 		(2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
114666d8c9d2SKieran Bingham 		(2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
114766d8c9d2SKieran Bingham 		(3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
114866d8c9d2SKieran Bingham 	};
1149defcedfbSLaurent Pinchart 	int cfg;
115066d8c9d2SKieran Bingham 
115166d8c9d2SKieran Bingham 	/*
115266d8c9d2SKieran Bingham 	 * Set the I2C bus speed.
115366d8c9d2SKieran Bingham 	 *
115466d8c9d2SKieran Bingham 	 * Enable I2C Local Acknowledge during the probe sequences of the camera
115566d8c9d2SKieran Bingham 	 * only. This should be disabled after the mux is initialised.
115666d8c9d2SKieran Bingham 	 */
115766d8c9d2SKieran Bingham 	max9286_configure_i2c(priv, true);
1158f78723ebSJacopo Mondi 	max9286_reverse_channel_setup(priv, priv->init_rev_chan_mv);
115966d8c9d2SKieran Bingham 
116066d8c9d2SKieran Bingham 	/*
116166d8c9d2SKieran Bingham 	 * Enable GMSL links, mask unused ones and autodetect link
116266d8c9d2SKieran Bingham 	 * used as CSI clock source.
116366d8c9d2SKieran Bingham 	 */
116466d8c9d2SKieran Bingham 	max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask);
116566d8c9d2SKieran Bingham 	max9286_write(priv, 0x0b, link_order[priv->route_mask]);
116666d8c9d2SKieran Bingham 	max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
116766d8c9d2SKieran Bingham 
1168f1403802SLaurent Pinchart 	max9286_set_video_format(priv, &max9286_default_format);
1169cffc9fb1SLaurent Pinchart 	max9286_set_fsync_period(priv);
117066d8c9d2SKieran Bingham 
1171defcedfbSLaurent Pinchart 	cfg = max9286_read(priv, 0x1c);
1172defcedfbSLaurent Pinchart 	if (cfg < 0)
1173defcedfbSLaurent Pinchart 		return cfg;
1174defcedfbSLaurent Pinchart 
1175defcedfbSLaurent Pinchart 	dev_dbg(&priv->client->dev, "power-up config: %s immunity, %u-bit bus\n",
1176defcedfbSLaurent Pinchart 		cfg & MAX9286_HIGHIMM(0) ? "high" : "legacy",
1177defcedfbSLaurent Pinchart 		cfg & MAX9286_BWS ? 32 : cfg & MAX9286_HIBW ? 27 : 24);
1178defcedfbSLaurent Pinchart 
11793697f108SLaurent Pinchart 	if (priv->bus_width) {
1180defcedfbSLaurent Pinchart 		cfg &= ~(MAX9286_HIBW | MAX9286_BWS);
11813697f108SLaurent Pinchart 
11823697f108SLaurent Pinchart 		if (priv->bus_width == 27)
1183defcedfbSLaurent Pinchart 			cfg |= MAX9286_HIBW;
11843697f108SLaurent Pinchart 		else if (priv->bus_width == 32)
1185defcedfbSLaurent Pinchart 			cfg |= MAX9286_BWS;
11863697f108SLaurent Pinchart 
1187defcedfbSLaurent Pinchart 		max9286_write(priv, 0x1c, cfg);
11883697f108SLaurent Pinchart 	}
11893697f108SLaurent Pinchart 
119066d8c9d2SKieran Bingham 	/*
119166d8c9d2SKieran Bingham 	 * The overlap window seems to provide additional validation by tracking
119266d8c9d2SKieran Bingham 	 * the delay between vsync and frame sync, generating an error if the
119366d8c9d2SKieran Bingham 	 * delay is bigger than the programmed window, though it's not yet clear
119466d8c9d2SKieran Bingham 	 * what value should be set.
119566d8c9d2SKieran Bingham 	 *
119666d8c9d2SKieran Bingham 	 * As it's an optional value and can be disabled, we do so by setting
119766d8c9d2SKieran Bingham 	 * a 0 overlap value.
119866d8c9d2SKieran Bingham 	 */
119966d8c9d2SKieran Bingham 	max9286_write(priv, 0x63, 0);
120066d8c9d2SKieran Bingham 	max9286_write(priv, 0x64, 0);
120166d8c9d2SKieran Bingham 
120266d8c9d2SKieran Bingham 	/*
120366d8c9d2SKieran Bingham 	 * Wait for 2ms to allow the link to resynchronize after the
120466d8c9d2SKieran Bingham 	 * configuration change.
120566d8c9d2SKieran Bingham 	 */
120666d8c9d2SKieran Bingham 	usleep_range(2000, 5000);
120766d8c9d2SKieran Bingham 
120866d8c9d2SKieran Bingham 	return 0;
120966d8c9d2SKieran Bingham }
121066d8c9d2SKieran Bingham 
max9286_gpio_set(struct max9286_priv * priv,unsigned int offset,int value)1211c9352df7SJacopo Mondi static int max9286_gpio_set(struct max9286_priv *priv, unsigned int offset,
1212c9352df7SJacopo Mondi 			    int value)
121366d8c9d2SKieran Bingham {
121466d8c9d2SKieran Bingham 	if (value)
121566d8c9d2SKieran Bingham 		priv->gpio_state |= BIT(offset);
121666d8c9d2SKieran Bingham 	else
121766d8c9d2SKieran Bingham 		priv->gpio_state &= ~BIT(offset);
121866d8c9d2SKieran Bingham 
1219c9352df7SJacopo Mondi 	return max9286_write(priv, 0x0f,
1220c9352df7SJacopo Mondi 			     MAX9286_0X0F_RESERVED | priv->gpio_state);
122166d8c9d2SKieran Bingham }
122266d8c9d2SKieran Bingham 
max9286_gpiochip_set(struct gpio_chip * chip,unsigned int offset,int value)1223c9352df7SJacopo Mondi static void max9286_gpiochip_set(struct gpio_chip *chip,
1224c9352df7SJacopo Mondi 				 unsigned int offset, int value)
1225c9352df7SJacopo Mondi {
1226c9352df7SJacopo Mondi 	struct max9286_priv *priv = gpiochip_get_data(chip);
1227c9352df7SJacopo Mondi 
1228c9352df7SJacopo Mondi 	max9286_gpio_set(priv, offset, value);
1229c9352df7SJacopo Mondi }
1230c9352df7SJacopo Mondi 
max9286_gpiochip_get(struct gpio_chip * chip,unsigned int offset)1231c9352df7SJacopo Mondi static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset)
123266d8c9d2SKieran Bingham {
123366d8c9d2SKieran Bingham 	struct max9286_priv *priv = gpiochip_get_data(chip);
123466d8c9d2SKieran Bingham 
123566d8c9d2SKieran Bingham 	return priv->gpio_state & BIT(offset);
123666d8c9d2SKieran Bingham }
123766d8c9d2SKieran Bingham 
max9286_register_gpio(struct max9286_priv * priv)123866d8c9d2SKieran Bingham static int max9286_register_gpio(struct max9286_priv *priv)
123966d8c9d2SKieran Bingham {
124066d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
124166d8c9d2SKieran Bingham 	struct gpio_chip *gpio = &priv->gpio;
124266d8c9d2SKieran Bingham 	int ret;
124366d8c9d2SKieran Bingham 
124466d8c9d2SKieran Bingham 	/* Configure the GPIO */
124566d8c9d2SKieran Bingham 	gpio->label = dev_name(dev);
124666d8c9d2SKieran Bingham 	gpio->parent = dev;
124766d8c9d2SKieran Bingham 	gpio->owner = THIS_MODULE;
124866d8c9d2SKieran Bingham 	gpio->ngpio = 2;
124966d8c9d2SKieran Bingham 	gpio->base = -1;
1250c9352df7SJacopo Mondi 	gpio->set = max9286_gpiochip_set;
1251c9352df7SJacopo Mondi 	gpio->get = max9286_gpiochip_get;
125266d8c9d2SKieran Bingham 	gpio->can_sleep = true;
125366d8c9d2SKieran Bingham 
125466d8c9d2SKieran Bingham 	ret = devm_gpiochip_add_data(dev, gpio, priv);
125566d8c9d2SKieran Bingham 	if (ret)
125666d8c9d2SKieran Bingham 		dev_err(dev, "Unable to create gpio_chip\n");
125766d8c9d2SKieran Bingham 
125866d8c9d2SKieran Bingham 	return ret;
125966d8c9d2SKieran Bingham }
126066d8c9d2SKieran Bingham 
max9286_parse_gpios(struct max9286_priv * priv)1261c9352df7SJacopo Mondi static int max9286_parse_gpios(struct max9286_priv *priv)
1262c9352df7SJacopo Mondi {
1263c9352df7SJacopo Mondi 	struct device *dev = &priv->client->dev;
1264c9352df7SJacopo Mondi 	int ret;
1265c9352df7SJacopo Mondi 
1266c9352df7SJacopo Mondi 	/*
1267c9352df7SJacopo Mondi 	 * Parse the "gpio-poc" vendor property. If the property is not
1268c9352df7SJacopo Mondi 	 * specified the camera power is controlled by a regulator.
1269c9352df7SJacopo Mondi 	 */
1270c9352df7SJacopo Mondi 	ret = of_property_read_u32_array(dev->of_node, "maxim,gpio-poc",
1271c9352df7SJacopo Mondi 					 priv->gpio_poc, 2);
1272c9352df7SJacopo Mondi 	if (ret == -EINVAL) {
1273c9352df7SJacopo Mondi 		/*
1274c9352df7SJacopo Mondi 		 * If gpio lines are not used for the camera power, register
1275c9352df7SJacopo Mondi 		 * a gpio controller for consumers.
1276c9352df7SJacopo Mondi 		 */
1277817660f4SThomas Nizan 		return max9286_register_gpio(priv);
1278c9352df7SJacopo Mondi 	}
1279c9352df7SJacopo Mondi 
1280c9352df7SJacopo Mondi 	/* If the property is specified make sure it is well formed. */
1281c9352df7SJacopo Mondi 	if (ret || priv->gpio_poc[0] > 1 ||
1282c9352df7SJacopo Mondi 	    (priv->gpio_poc[1] != GPIO_ACTIVE_HIGH &&
1283c9352df7SJacopo Mondi 	     priv->gpio_poc[1] != GPIO_ACTIVE_LOW)) {
1284c9352df7SJacopo Mondi 		dev_err(dev, "Invalid 'gpio-poc' property\n");
1285c9352df7SJacopo Mondi 		return -EINVAL;
1286c9352df7SJacopo Mondi 	}
1287c9352df7SJacopo Mondi 
1288817660f4SThomas Nizan 	priv->use_gpio_poc = true;
1289c9352df7SJacopo Mondi 	return 0;
1290c9352df7SJacopo Mondi }
1291c9352df7SJacopo Mondi 
max9286_poc_power_on(struct max9286_priv * priv)1292817660f4SThomas Nizan static int max9286_poc_power_on(struct max9286_priv *priv)
1293817660f4SThomas Nizan {
1294817660f4SThomas Nizan 	struct max9286_source *source;
1295817660f4SThomas Nizan 	unsigned int enabled = 0;
1296817660f4SThomas Nizan 	int ret;
1297817660f4SThomas Nizan 
1298817660f4SThomas Nizan 	/* Enable the global regulator if available. */
1299817660f4SThomas Nizan 	if (priv->regulator)
1300817660f4SThomas Nizan 		return regulator_enable(priv->regulator);
1301817660f4SThomas Nizan 
1302817660f4SThomas Nizan 	if (priv->use_gpio_poc)
1303817660f4SThomas Nizan 		return max9286_gpio_set(priv, priv->gpio_poc[0],
1304817660f4SThomas Nizan 					!priv->gpio_poc[1]);
1305817660f4SThomas Nizan 
1306817660f4SThomas Nizan 	/* Otherwise use the per-port regulators. */
1307817660f4SThomas Nizan 	for_each_source(priv, source) {
1308817660f4SThomas Nizan 		ret = regulator_enable(source->regulator);
1309817660f4SThomas Nizan 		if (ret < 0)
1310817660f4SThomas Nizan 			goto error;
1311817660f4SThomas Nizan 
1312817660f4SThomas Nizan 		enabled |= BIT(to_index(priv, source));
1313817660f4SThomas Nizan 	}
1314817660f4SThomas Nizan 
1315817660f4SThomas Nizan 	return 0;
1316817660f4SThomas Nizan 
1317817660f4SThomas Nizan error:
1318817660f4SThomas Nizan 	for_each_source(priv, source) {
1319817660f4SThomas Nizan 		if (enabled & BIT(to_index(priv, source)))
1320817660f4SThomas Nizan 			regulator_disable(source->regulator);
1321817660f4SThomas Nizan 	}
1322817660f4SThomas Nizan 
1323817660f4SThomas Nizan 	return ret;
1324817660f4SThomas Nizan }
1325817660f4SThomas Nizan 
max9286_poc_power_off(struct max9286_priv * priv)1326817660f4SThomas Nizan static int max9286_poc_power_off(struct max9286_priv *priv)
1327817660f4SThomas Nizan {
1328817660f4SThomas Nizan 	struct max9286_source *source;
1329817660f4SThomas Nizan 	int ret = 0;
1330817660f4SThomas Nizan 
1331817660f4SThomas Nizan 	if (priv->regulator)
1332817660f4SThomas Nizan 		return regulator_disable(priv->regulator);
1333817660f4SThomas Nizan 
1334817660f4SThomas Nizan 	if (priv->use_gpio_poc)
1335817660f4SThomas Nizan 		return max9286_gpio_set(priv, priv->gpio_poc[0],
1336817660f4SThomas Nizan 					priv->gpio_poc[1]);
1337817660f4SThomas Nizan 
1338817660f4SThomas Nizan 	for_each_source(priv, source) {
1339817660f4SThomas Nizan 		int err;
1340817660f4SThomas Nizan 
1341817660f4SThomas Nizan 		err = regulator_disable(source->regulator);
1342817660f4SThomas Nizan 		if (!ret)
1343817660f4SThomas Nizan 			ret = err;
1344817660f4SThomas Nizan 	}
1345817660f4SThomas Nizan 
1346817660f4SThomas Nizan 	return ret;
1347817660f4SThomas Nizan }
1348817660f4SThomas Nizan 
max9286_poc_enable(struct max9286_priv * priv,bool enable)1349c9352df7SJacopo Mondi static int max9286_poc_enable(struct max9286_priv *priv, bool enable)
1350c9352df7SJacopo Mondi {
1351c9352df7SJacopo Mondi 	int ret;
1352c9352df7SJacopo Mondi 
1353817660f4SThomas Nizan 	if (enable)
1354817660f4SThomas Nizan 		ret = max9286_poc_power_on(priv);
1355c9352df7SJacopo Mondi 	else
1356817660f4SThomas Nizan 		ret = max9286_poc_power_off(priv);
1357c9352df7SJacopo Mondi 
1358c9352df7SJacopo Mondi 	if (ret < 0)
1359c9352df7SJacopo Mondi 		dev_err(&priv->client->dev, "Unable to turn power %s\n",
1360c9352df7SJacopo Mondi 			enable ? "on" : "off");
1361c9352df7SJacopo Mondi 
1362c9352df7SJacopo Mondi 	return ret;
1363c9352df7SJacopo Mondi }
1364c9352df7SJacopo Mondi 
max9286_init(struct max9286_priv * priv)1365365ab7ebSLaurentiu Palcu static int max9286_init(struct max9286_priv *priv)
136666d8c9d2SKieran Bingham {
1367365ab7ebSLaurentiu Palcu 	struct i2c_client *client = priv->client;
136866d8c9d2SKieran Bingham 	int ret;
136966d8c9d2SKieran Bingham 
1370c9352df7SJacopo Mondi 	ret = max9286_poc_enable(priv, true);
1371c9352df7SJacopo Mondi 	if (ret)
137266d8c9d2SKieran Bingham 		return ret;
137366d8c9d2SKieran Bingham 
137466d8c9d2SKieran Bingham 	ret = max9286_setup(priv);
137566d8c9d2SKieran Bingham 	if (ret) {
1376365ab7ebSLaurentiu Palcu 		dev_err(&client->dev, "Unable to setup max9286\n");
1377c9352df7SJacopo Mondi 		goto err_poc_disable;
137866d8c9d2SKieran Bingham 	}
137966d8c9d2SKieran Bingham 
138066d8c9d2SKieran Bingham 	/*
138166d8c9d2SKieran Bingham 	 * Register all V4L2 interactions for the MAX9286 and notifiers for
138266d8c9d2SKieran Bingham 	 * any subdevices connected.
138366d8c9d2SKieran Bingham 	 */
138466d8c9d2SKieran Bingham 	ret = max9286_v4l2_register(priv);
138566d8c9d2SKieran Bingham 	if (ret) {
1386365ab7ebSLaurentiu Palcu 		dev_err(&client->dev, "Failed to register with V4L2\n");
1387c9352df7SJacopo Mondi 		goto err_poc_disable;
138866d8c9d2SKieran Bingham 	}
138966d8c9d2SKieran Bingham 
139066d8c9d2SKieran Bingham 	ret = max9286_i2c_mux_init(priv);
139166d8c9d2SKieran Bingham 	if (ret) {
1392365ab7ebSLaurentiu Palcu 		dev_err(&client->dev, "Unable to initialize I2C multiplexer\n");
139366d8c9d2SKieran Bingham 		goto err_v4l2_register;
139466d8c9d2SKieran Bingham 	}
139566d8c9d2SKieran Bingham 
139666d8c9d2SKieran Bingham 	/* Leave the mux channels disabled until they are selected. */
139766d8c9d2SKieran Bingham 	max9286_i2c_mux_close(priv);
139866d8c9d2SKieran Bingham 
139966d8c9d2SKieran Bingham 	return 0;
140066d8c9d2SKieran Bingham 
140166d8c9d2SKieran Bingham err_v4l2_register:
140266d8c9d2SKieran Bingham 	max9286_v4l2_unregister(priv);
1403c9352df7SJacopo Mondi err_poc_disable:
1404c9352df7SJacopo Mondi 	max9286_poc_enable(priv, false);
140566d8c9d2SKieran Bingham 
140666d8c9d2SKieran Bingham 	return ret;
140766d8c9d2SKieran Bingham }
140866d8c9d2SKieran Bingham 
max9286_cleanup_dt(struct max9286_priv * priv)140966d8c9d2SKieran Bingham static void max9286_cleanup_dt(struct max9286_priv *priv)
141066d8c9d2SKieran Bingham {
141166d8c9d2SKieran Bingham 	struct max9286_source *source;
141266d8c9d2SKieran Bingham 
141366d8c9d2SKieran Bingham 	for_each_source(priv, source) {
141466d8c9d2SKieran Bingham 		fwnode_handle_put(source->fwnode);
141566d8c9d2SKieran Bingham 		source->fwnode = NULL;
141666d8c9d2SKieran Bingham 	}
141766d8c9d2SKieran Bingham }
141866d8c9d2SKieran Bingham 
max9286_parse_dt(struct max9286_priv * priv)141966d8c9d2SKieran Bingham static int max9286_parse_dt(struct max9286_priv *priv)
142066d8c9d2SKieran Bingham {
142166d8c9d2SKieran Bingham 	struct device *dev = &priv->client->dev;
142266d8c9d2SKieran Bingham 	struct device_node *i2c_mux;
142366d8c9d2SKieran Bingham 	struct device_node *node = NULL;
142466d8c9d2SKieran Bingham 	unsigned int i2c_mux_mask = 0;
142585cb767cSJacopo Mondi 	u32 reverse_channel_microvolt;
1426e332061bSLaurent Pinchart 	u32 i2c_clk_freq = 105000;
1427e332061bSLaurent Pinchart 	unsigned int i;
142866d8c9d2SKieran Bingham 
142966d8c9d2SKieran Bingham 	/* Balance the of_node_put() performed by of_find_node_by_name(). */
143066d8c9d2SKieran Bingham 	of_node_get(dev->of_node);
143166d8c9d2SKieran Bingham 	i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux");
143266d8c9d2SKieran Bingham 	if (!i2c_mux) {
143366d8c9d2SKieran Bingham 		dev_err(dev, "Failed to find i2c-mux node\n");
143466d8c9d2SKieran Bingham 		return -EINVAL;
143566d8c9d2SKieran Bingham 	}
143666d8c9d2SKieran Bingham 
143766d8c9d2SKieran Bingham 	/* Identify which i2c-mux channels are enabled */
143866d8c9d2SKieran Bingham 	for_each_child_of_node(i2c_mux, node) {
143966d8c9d2SKieran Bingham 		u32 id = 0;
144066d8c9d2SKieran Bingham 
144166d8c9d2SKieran Bingham 		of_property_read_u32(node, "reg", &id);
144266d8c9d2SKieran Bingham 		if (id >= MAX9286_NUM_GMSL)
144366d8c9d2SKieran Bingham 			continue;
144466d8c9d2SKieran Bingham 
144566d8c9d2SKieran Bingham 		if (!of_device_is_available(node)) {
144666d8c9d2SKieran Bingham 			dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id);
144766d8c9d2SKieran Bingham 			continue;
144866d8c9d2SKieran Bingham 		}
144966d8c9d2SKieran Bingham 
145066d8c9d2SKieran Bingham 		i2c_mux_mask |= BIT(id);
145166d8c9d2SKieran Bingham 	}
145266d8c9d2SKieran Bingham 	of_node_put(i2c_mux);
145366d8c9d2SKieran Bingham 
145466d8c9d2SKieran Bingham 	/* Parse the endpoints */
145566d8c9d2SKieran Bingham 	for_each_endpoint_of_node(dev->of_node, node) {
145666d8c9d2SKieran Bingham 		struct max9286_source *source;
145766d8c9d2SKieran Bingham 		struct of_endpoint ep;
145866d8c9d2SKieran Bingham 
145966d8c9d2SKieran Bingham 		of_graph_parse_endpoint(node, &ep);
146066d8c9d2SKieran Bingham 		dev_dbg(dev, "Endpoint %pOF on port %d",
146166d8c9d2SKieran Bingham 			ep.local_node, ep.port);
146266d8c9d2SKieran Bingham 
146366d8c9d2SKieran Bingham 		if (ep.port > MAX9286_NUM_GMSL) {
146466d8c9d2SKieran Bingham 			dev_err(dev, "Invalid endpoint %s on port %d",
146566d8c9d2SKieran Bingham 				of_node_full_name(ep.local_node), ep.port);
146666d8c9d2SKieran Bingham 			continue;
146766d8c9d2SKieran Bingham 		}
146866d8c9d2SKieran Bingham 
146966d8c9d2SKieran Bingham 		/* For the source endpoint just parse the bus configuration. */
147066d8c9d2SKieran Bingham 		if (ep.port == MAX9286_SRC_PAD) {
147166d8c9d2SKieran Bingham 			struct v4l2_fwnode_endpoint vep = {
147266d8c9d2SKieran Bingham 				.bus_type = V4L2_MBUS_CSI2_DPHY
147366d8c9d2SKieran Bingham 			};
147466d8c9d2SKieran Bingham 			int ret;
147566d8c9d2SKieran Bingham 
147666d8c9d2SKieran Bingham 			ret = v4l2_fwnode_endpoint_parse(
147766d8c9d2SKieran Bingham 					of_fwnode_handle(node), &vep);
147866d8c9d2SKieran Bingham 			if (ret) {
147966d8c9d2SKieran Bingham 				of_node_put(node);
148066d8c9d2SKieran Bingham 				return ret;
148166d8c9d2SKieran Bingham 			}
148266d8c9d2SKieran Bingham 
148366d8c9d2SKieran Bingham 			priv->csi2_data_lanes =
148466d8c9d2SKieran Bingham 				vep.bus.mipi_csi2.num_data_lanes;
148566d8c9d2SKieran Bingham 
148666d8c9d2SKieran Bingham 			continue;
148766d8c9d2SKieran Bingham 		}
148866d8c9d2SKieran Bingham 
148966d8c9d2SKieran Bingham 		/* Skip if the corresponding GMSL link is unavailable. */
149066d8c9d2SKieran Bingham 		if (!(i2c_mux_mask & BIT(ep.port)))
149166d8c9d2SKieran Bingham 			continue;
149266d8c9d2SKieran Bingham 
149366d8c9d2SKieran Bingham 		if (priv->sources[ep.port].fwnode) {
149466d8c9d2SKieran Bingham 			dev_err(dev,
149566d8c9d2SKieran Bingham 				"Multiple port endpoints are not supported: %d",
149666d8c9d2SKieran Bingham 				ep.port);
149766d8c9d2SKieran Bingham 
149866d8c9d2SKieran Bingham 			continue;
149966d8c9d2SKieran Bingham 		}
150066d8c9d2SKieran Bingham 
150166d8c9d2SKieran Bingham 		source = &priv->sources[ep.port];
150266d8c9d2SKieran Bingham 		source->fwnode = fwnode_graph_get_remote_endpoint(
150366d8c9d2SKieran Bingham 						of_fwnode_handle(node));
150466d8c9d2SKieran Bingham 		if (!source->fwnode) {
150566d8c9d2SKieran Bingham 			dev_err(dev,
150666d8c9d2SKieran Bingham 				"Endpoint %pOF has no remote endpoint connection\n",
150766d8c9d2SKieran Bingham 				ep.local_node);
150866d8c9d2SKieran Bingham 
150966d8c9d2SKieran Bingham 			continue;
151066d8c9d2SKieran Bingham 		}
151166d8c9d2SKieran Bingham 
151266d8c9d2SKieran Bingham 		priv->source_mask |= BIT(ep.port);
151366d8c9d2SKieran Bingham 		priv->nsources++;
151466d8c9d2SKieran Bingham 	}
151566d8c9d2SKieran Bingham 
15163697f108SLaurent Pinchart 	of_property_read_u32(dev->of_node, "maxim,bus-width", &priv->bus_width);
15173697f108SLaurent Pinchart 	switch (priv->bus_width) {
15183697f108SLaurent Pinchart 	case 0:
15193697f108SLaurent Pinchart 		/*
15203697f108SLaurent Pinchart 		 * The property isn't specified in the device tree, the driver
15213697f108SLaurent Pinchart 		 * will keep the default value selected by the BWS pin.
15223697f108SLaurent Pinchart 		 */
15233697f108SLaurent Pinchart 	case 24:
15243697f108SLaurent Pinchart 	case 27:
15253697f108SLaurent Pinchart 	case 32:
15263697f108SLaurent Pinchart 		break;
15273697f108SLaurent Pinchart 	default:
15283697f108SLaurent Pinchart 		dev_err(dev, "Invalid %s value %u\n", "maxim,bus-width",
15293697f108SLaurent Pinchart 			priv->bus_width);
15303697f108SLaurent Pinchart 		return -EINVAL;
15313697f108SLaurent Pinchart 	}
15323697f108SLaurent Pinchart 
1533e332061bSLaurent Pinchart 	of_property_read_u32(dev->of_node, "maxim,i2c-remote-bus-hz",
1534e332061bSLaurent Pinchart 			     &i2c_clk_freq);
1535e332061bSLaurent Pinchart 	for (i = 0; i < ARRAY_SIZE(max9286_i2c_speeds); ++i) {
1536e332061bSLaurent Pinchart 		const struct max9286_i2c_speed *speed = &max9286_i2c_speeds[i];
1537e332061bSLaurent Pinchart 
1538e332061bSLaurent Pinchart 		if (speed->rate == i2c_clk_freq) {
1539e332061bSLaurent Pinchart 			priv->i2c_mstbt = speed->mstbt;
1540e332061bSLaurent Pinchart 			break;
1541e332061bSLaurent Pinchart 		}
1542e332061bSLaurent Pinchart 	}
1543e332061bSLaurent Pinchart 
1544e332061bSLaurent Pinchart 	if (i == ARRAY_SIZE(max9286_i2c_speeds)) {
1545e332061bSLaurent Pinchart 		dev_err(dev, "Invalid %s value %u\n", "maxim,i2c-remote-bus-hz",
1546e332061bSLaurent Pinchart 			i2c_clk_freq);
1547e332061bSLaurent Pinchart 		return -EINVAL;
1548e332061bSLaurent Pinchart 	}
1549e332061bSLaurent Pinchart 
155085cb767cSJacopo Mondi 	/*
155185cb767cSJacopo Mondi 	 * Parse the initial value of the reverse channel amplitude from
155285cb767cSJacopo Mondi 	 * the firmware interface and convert it to millivolts.
155385cb767cSJacopo Mondi 	 *
155485cb767cSJacopo Mondi 	 * Default it to 170mV for backward compatibility with DTBs that do not
155585cb767cSJacopo Mondi 	 * provide the property.
155685cb767cSJacopo Mondi 	 */
155785cb767cSJacopo Mondi 	if (of_property_read_u32(dev->of_node,
155885cb767cSJacopo Mondi 				 "maxim,reverse-channel-microvolt",
155985cb767cSJacopo Mondi 				 &reverse_channel_microvolt))
1560f78723ebSJacopo Mondi 		priv->init_rev_chan_mv = 170;
156185cb767cSJacopo Mondi 	else
1562f78723ebSJacopo Mondi 		priv->init_rev_chan_mv = reverse_channel_microvolt / 1000U;
156385cb767cSJacopo Mondi 
156466d8c9d2SKieran Bingham 	priv->route_mask = priv->source_mask;
156566d8c9d2SKieran Bingham 
156666d8c9d2SKieran Bingham 	return 0;
156766d8c9d2SKieran Bingham }
156866d8c9d2SKieran Bingham 
max9286_get_poc_supplies(struct max9286_priv * priv)1569817660f4SThomas Nizan static int max9286_get_poc_supplies(struct max9286_priv *priv)
1570817660f4SThomas Nizan {
1571817660f4SThomas Nizan 	struct device *dev = &priv->client->dev;
1572817660f4SThomas Nizan 	struct max9286_source *source;
1573817660f4SThomas Nizan 	int ret;
1574817660f4SThomas Nizan 
1575817660f4SThomas Nizan 	/* Start by getting the global regulator. */
1576817660f4SThomas Nizan 	priv->regulator = devm_regulator_get_optional(dev, "poc");
1577817660f4SThomas Nizan 	if (!IS_ERR(priv->regulator))
1578817660f4SThomas Nizan 		return 0;
1579817660f4SThomas Nizan 
1580817660f4SThomas Nizan 	if (PTR_ERR(priv->regulator) != -ENODEV)
1581817660f4SThomas Nizan 		return dev_err_probe(dev, PTR_ERR(priv->regulator),
1582817660f4SThomas Nizan 				     "Unable to get PoC regulator\n");
1583817660f4SThomas Nizan 
1584817660f4SThomas Nizan 	/* If there's no global regulator, get per-port regulators. */
1585817660f4SThomas Nizan 	dev_dbg(dev,
1586817660f4SThomas Nizan 		"No global PoC regulator, looking for per-port regulators\n");
1587817660f4SThomas Nizan 	priv->regulator = NULL;
1588817660f4SThomas Nizan 
1589817660f4SThomas Nizan 	for_each_source(priv, source) {
1590817660f4SThomas Nizan 		unsigned int index = to_index(priv, source);
1591817660f4SThomas Nizan 		char name[10];
1592817660f4SThomas Nizan 
1593817660f4SThomas Nizan 		snprintf(name, sizeof(name), "port%u-poc", index);
1594817660f4SThomas Nizan 		source->regulator = devm_regulator_get(dev, name);
1595817660f4SThomas Nizan 		if (IS_ERR(source->regulator)) {
1596817660f4SThomas Nizan 			ret = PTR_ERR(source->regulator);
1597817660f4SThomas Nizan 			dev_err_probe(dev, ret,
1598817660f4SThomas Nizan 				      "Unable to get port %u PoC regulator\n",
1599817660f4SThomas Nizan 				      index);
1600817660f4SThomas Nizan 			return ret;
1601817660f4SThomas Nizan 		}
1602817660f4SThomas Nizan 	}
1603817660f4SThomas Nizan 
1604817660f4SThomas Nizan 	return 0;
1605817660f4SThomas Nizan }
1606817660f4SThomas Nizan 
max9286_probe(struct i2c_client * client)160766d8c9d2SKieran Bingham static int max9286_probe(struct i2c_client *client)
160866d8c9d2SKieran Bingham {
160966d8c9d2SKieran Bingham 	struct max9286_priv *priv;
161066d8c9d2SKieran Bingham 	int ret;
161166d8c9d2SKieran Bingham 
161266d8c9d2SKieran Bingham 	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
161366d8c9d2SKieran Bingham 	if (!priv)
161466d8c9d2SKieran Bingham 		return -ENOMEM;
161566d8c9d2SKieran Bingham 
161666d8c9d2SKieran Bingham 	mutex_init(&priv->mutex);
161766d8c9d2SKieran Bingham 
161866d8c9d2SKieran Bingham 	priv->client = client;
161966d8c9d2SKieran Bingham 
1620817660f4SThomas Nizan 	/* GPIO values default to high */
1621817660f4SThomas Nizan 	priv->gpio_state = BIT(0) | BIT(1);
1622817660f4SThomas Nizan 
1623e332061bSLaurent Pinchart 	ret = max9286_parse_dt(priv);
1624e332061bSLaurent Pinchart 	if (ret)
1625e332061bSLaurent Pinchart 		goto err_cleanup_dt;
1626e332061bSLaurent Pinchart 
162766d8c9d2SKieran Bingham 	priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
162866d8c9d2SKieran Bingham 						   GPIOD_OUT_HIGH);
1629e332061bSLaurent Pinchart 	if (IS_ERR(priv->gpiod_pwdn)) {
1630e332061bSLaurent Pinchart 		ret = PTR_ERR(priv->gpiod_pwdn);
1631e332061bSLaurent Pinchart 		goto err_cleanup_dt;
1632e332061bSLaurent Pinchart 	}
163366d8c9d2SKieran Bingham 
163466d8c9d2SKieran Bingham 	gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
163566d8c9d2SKieran Bingham 	gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
163666d8c9d2SKieran Bingham 
163766d8c9d2SKieran Bingham 	/* Wait at least 4ms before the I2C lines latch to the address */
163866d8c9d2SKieran Bingham 	if (priv->gpiod_pwdn)
163966d8c9d2SKieran Bingham 		usleep_range(4000, 5000);
164066d8c9d2SKieran Bingham 
164166d8c9d2SKieran Bingham 	/*
164266d8c9d2SKieran Bingham 	 * The MAX9286 starts by default with all ports enabled, we disable all
164366d8c9d2SKieran Bingham 	 * ports early to ensure that all channels are disabled if we error out
164466d8c9d2SKieran Bingham 	 * and keep the bus consistent.
164566d8c9d2SKieran Bingham 	 */
164666d8c9d2SKieran Bingham 	max9286_i2c_mux_close(priv);
164766d8c9d2SKieran Bingham 
164866d8c9d2SKieran Bingham 	/*
164966d8c9d2SKieran Bingham 	 * The MAX9286 initialises with auto-acknowledge enabled by default.
165066d8c9d2SKieran Bingham 	 * This can be invasive to other transactions on the same bus, so
165166d8c9d2SKieran Bingham 	 * disable it early. It will be enabled only as and when needed.
165266d8c9d2SKieran Bingham 	 */
165366d8c9d2SKieran Bingham 	max9286_configure_i2c(priv, false);
165466d8c9d2SKieran Bingham 
1655c9352df7SJacopo Mondi 	ret = max9286_parse_gpios(priv);
165666d8c9d2SKieran Bingham 	if (ret)
165766d8c9d2SKieran Bingham 		goto err_powerdown;
165866d8c9d2SKieran Bingham 
1659817660f4SThomas Nizan 	if (!priv->use_gpio_poc) {
1660817660f4SThomas Nizan 		ret = max9286_get_poc_supplies(priv);
1661817660f4SThomas Nizan 		if (ret)
1662817660f4SThomas Nizan 			goto err_cleanup_dt;
1663817660f4SThomas Nizan 	}
166466d8c9d2SKieran Bingham 
1665365ab7ebSLaurentiu Palcu 	ret = max9286_init(priv);
166666d8c9d2SKieran Bingham 	if (ret < 0)
166766d8c9d2SKieran Bingham 		goto err_cleanup_dt;
166866d8c9d2SKieran Bingham 
166966d8c9d2SKieran Bingham 	return 0;
167066d8c9d2SKieran Bingham 
167166d8c9d2SKieran Bingham err_powerdown:
167266d8c9d2SKieran Bingham 	gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
1673e332061bSLaurent Pinchart err_cleanup_dt:
1674e332061bSLaurent Pinchart 	max9286_cleanup_dt(priv);
167566d8c9d2SKieran Bingham 
167666d8c9d2SKieran Bingham 	return ret;
167766d8c9d2SKieran Bingham }
167866d8c9d2SKieran Bingham 
max9286_remove(struct i2c_client * client)1679ed5c2f5fSUwe Kleine-König static void max9286_remove(struct i2c_client *client)
168066d8c9d2SKieran Bingham {
1681365ab7ebSLaurentiu Palcu 	struct max9286_priv *priv = sd_to_max9286(i2c_get_clientdata(client));
168266d8c9d2SKieran Bingham 
168366d8c9d2SKieran Bingham 	i2c_mux_del_adapters(priv->mux);
168466d8c9d2SKieran Bingham 
168566d8c9d2SKieran Bingham 	max9286_v4l2_unregister(priv);
168666d8c9d2SKieran Bingham 
1687c9352df7SJacopo Mondi 	max9286_poc_enable(priv, false);
168866d8c9d2SKieran Bingham 
168966d8c9d2SKieran Bingham 	gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
169066d8c9d2SKieran Bingham 
169166d8c9d2SKieran Bingham 	max9286_cleanup_dt(priv);
169266d8c9d2SKieran Bingham }
169366d8c9d2SKieran Bingham 
169466d8c9d2SKieran Bingham static const struct of_device_id max9286_dt_ids[] = {
169566d8c9d2SKieran Bingham 	{ .compatible = "maxim,max9286" },
169666d8c9d2SKieran Bingham 	{},
169766d8c9d2SKieran Bingham };
169866d8c9d2SKieran Bingham MODULE_DEVICE_TABLE(of, max9286_dt_ids);
169966d8c9d2SKieran Bingham 
170066d8c9d2SKieran Bingham static struct i2c_driver max9286_i2c_driver = {
170166d8c9d2SKieran Bingham 	.driver	= {
170266d8c9d2SKieran Bingham 		.name		= "max9286",
1703db657dfbSKrzysztof Kozlowski 		.of_match_table	= max9286_dt_ids,
170466d8c9d2SKieran Bingham 	},
1705aaeb31c0SUwe Kleine-König 	.probe		= max9286_probe,
170666d8c9d2SKieran Bingham 	.remove		= max9286_remove,
170766d8c9d2SKieran Bingham };
170866d8c9d2SKieran Bingham 
170966d8c9d2SKieran Bingham module_i2c_driver(max9286_i2c_driver);
171066d8c9d2SKieran Bingham 
171166d8c9d2SKieran Bingham MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
171266d8c9d2SKieran Bingham MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov");
171366d8c9d2SKieran Bingham MODULE_LICENSE("GPL");
1714