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