* [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8
@ 2020-04-16 10:40 Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 01/20] media: i2c: Add MAX9286 driver Jacopo Mondi
` (19 more replies)
0 siblings, 20 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
Hello Kieran,
please fine here a slight rework of your fixup series to be applied
on top of max9286-v7.
The main changes are:
- overlap window: write registers 0x63 and 0x64 directly. Going through the
function breaks RDACM21 operations. To be investigated.
- pixel rate control: Make mandatory for remotes to report pixel rate, and
use it calculate max9286 one.
- rebase on latest media-master
On top of that I have added v8 of RDACM20.
The changes compared to v7 are considerable, and reported in the single
patch change log. I have 30+ fixup patches on top of rdacm20-v7 that could
be singularly reviewed, but not to flood the list I squashed them in in
this version. As detailed in the patch change log the changes are available
singularly in a repository in my git tree.
I have tested this version on Salvator-X with 4 RDACM20.
Ideally, there could be a bit more cleanup to do in the max9286 probe routines
as suggested in the review of your series, but I left it out not to take away
all the fun from you :p
After a sanity check, I would propose to squash everything in and move
to linux-media to have this hopefully collected!
Thanks
j
Jacopo Mondi (5):
squash!: max9286: Rebase on latest media/master
squash! max9286: Calculate pixel rate
squash! max9286: Put of node on error
dt-bindings: media: i2c: Add bindings for IMI RDACM2x
media: i2c: Add RDACM20 driver
Kieran Bingham (15):
media: i2c: Add MAX9286 driver
fixup! media: i2c: Add MAX9286 driver
squash! max9286: Update the bound_sources mask on unbind
squash! max9286: convert probe kzalloc
squash! max9286: Fix cleanup path from GPIO powerdown
squash! max9286: cleanup GPIO device registration fail path
squash! max9286: Convert to use devm_regulator_get()
squash! max9286: Fit max9286_parse_dt print on one line
squash! max9286: Move multi-device workarounds out of upstream
squash! max9286: Remove I2C mod-table
sqaush! max9286: Lock format changes
squash! max9286: Implement Pixelrate control
squash! max9286: Disable overlap window
sqaush! max9286: Describe pad index usage
sqaush! max9286: Remove poc_enabled workaround
.../bindings/media/i2c/imi,rdacm2x-gmsl.yaml | 161 ++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 22 +
drivers/media/i2c/Kconfig | 25 +
drivers/media/i2c/Makefile | 3 +
drivers/media/i2c/max9271.c | 330 ++++
drivers/media/i2c/max9271.h | 224 +++
drivers/media/i2c/max9286.c | 1349 +++++++++++++++++
drivers/media/i2c/rdacm20.c | 668 ++++++++
9 files changed, 2784 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
create mode 100644 drivers/media/i2c/max9271.c
create mode 100644 drivers/media/i2c/max9271.h
create mode 100644 drivers/media/i2c/max9286.c
create mode 100644 drivers/media/i2c/rdacm20.c
--
2.26.0
^ permalink raw reply [flat|nested] 29+ messages in thread
* [v8-rc1 01/20] media: i2c: Add MAX9286 driver
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 02/20] fixup! " Jacopo Mondi
` (18 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam, Laurent Pinchart, Niklas Söderlund
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
The MAX9286 is a 4-channel GMSL deserializer with coax or STP input and
CSI-2 output. The device supports multicamera streaming applications,
and features the ability to synchronise the attached cameras.
CSI-2 output can be configured with 1 to 4 lanes, and a control channel
is supported over I2C, which implements an I2C mux to facilitate
communications with connected cameras across the reverse control
channel.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
--
v2:
- Fix MAINTAINERS entry
This posting is released with the following modifications to work
without Sakari's VC developments:
- max9286_g_mbus_config() re-instated
- max9286_get_frame_desc() is not bus/csi aware
- max9286_{get,set}_routing() removed
v3:
- Initialise notifier with v4l2_async_notifier_init
- Update for new mbus csi2 format V4L2_MBUS_CSI2_DPHY
v4: - Re-introduce required code to function with the VC series.
- Implement max9286_get_routing, max9286_set_routing
- Remove max9286_g_mbus_config
v5: (internal release)
- Fix printk formatting for hex value
- max9286->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE (add |)
- MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER -> MEDIA_ENT_F_VID_IF_BRIDGE
- Remove 'device is bound' workaround
v6:
- v4l2_subdev_krouting instead of v4l2_subdev_routing separated
to allow integration without the VC/V4L2-Mux series.
- convert sd_to_max9286 to inline function
- rename max9286_device to max9286_priv
- Cleanup the v4l2_async_notifier
- Extend MODULE_AUTHOR
- Replace of_graph_get_endpoint_by_regs with fwnode_graph_get_endpoint_by_id
- Pass default bus type when parsing fwnode endpoint (Manivannan Sadhasivam)
- Use new YAML file reference in MAINTAINERS
- Parse new i2c-mux node in max9286_get_i2c_by_id
(This could/should be refactored to parse these separately first)
- Spelling and calculation fixes in the FSYNC_LOCKED check comments
- Identify each enabled i2c-mux channel in a single pass
- max9286: Improve mux-state readbility [v2]
- Fix frame sync lock durations
- Add comment to describe /why/ we must open the mux in s_stream
- use -EXDEV as return code for failed link synchronisation.
- Fix reference counting of the dt nodeS
- Convert to probe_new for I2C
- Remove redundant max9286_i2c_mux_state
- Provide optional enable-gpio (max9286-pwdn)
v7:
[Kieran]
- Ensure powerdown lines are optional
- Add a 4ms power-up delay
- Add max9286_check_config_link() to core
- Add GPIO chip controller for GPIO0OUT and GPIO1OUT
- Fix GPIO registration
- max9286: Split out async registration
(fixes regulator -EPROBE_DEFERs failures)
- Collect all V4L2 registrations
- balance v4l2_async refcnting
- Rename max9286_v4l2_async_ => max9286_v4l2_notifier_
[Jacopo]
- Remove redundanct MAXIM_I2C_SPEED macros
- Move notifiers operations
- Add delay after reverse channel reconfiguration
- Move link setup to completion
- Fix up max9286_check_config_link() implementation
- Remove redundant dual configuration of reverse channel
---
MAINTAINERS | 10 +
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/max9286.c | 1278 +++++++++++++++++++++++++++++++++++
4 files changed, 1300 insertions(+)
create mode 100644 drivers/media/i2c/max9286.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 297197b05562..242ec17644c3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10268,6 +10268,16 @@ F: Documentation/hwmon/max6697.rst
F: drivers/hwmon/max6697.c
F: include/linux/platform_data/max6697.h
+MAX9286 QUAD GMSL DESERIALIZER DRIVER
+M: Jacopo Mondi <jacopo+renesas@jmondi.org>
+M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
+M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/maxim,max9286.yaml
+F: drivers/media/i2c/max9286.c
+
MAX9860 MONO AUDIO VOICE CODEC DRIVER
M: Peter Rosin <peda@axentia.se>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 3abc80373ec0..04186787a2f0 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -464,6 +464,17 @@ config VIDEO_VPX3220
To compile this driver as a module, choose M here: the
module will be called vpx3220.
+config VIDEO_MAX9286
+ tristate "Maxim MAX9286 GMSL deserializer support"
+ depends on I2C && I2C_MUX
+ depends on VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This driver supports the Maxim MAX9286 GMSL deserializer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max9286.
+
comment "Video and audio decoders"
config VIDEO_SAA717X
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 77bf7d0b691f..29379008d624 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -118,5 +118,6 @@ obj-$(CONFIG_VIDEO_IMX290) += imx290.o
obj-$(CONFIG_VIDEO_IMX319) += imx319.o
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
+obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
new file mode 100644
index 000000000000..a20829297ef6
--- /dev/null
+++ b/drivers/media/i2c/max9286.c
@@ -0,0 +1,1278 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Maxim MAX9286 GMSL Deserializer Driver
+ *
+ * Copyright (C) 2017-2019 Jacopo Mondi
+ * Copyright (C) 2017-2019 Kieran Bingham
+ * Copyright (C) 2017-2019 Laurent Pinchart
+ * Copyright (C) 2017-2019 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Register 0x00 */
+#define MAX9286_MSTLINKSEL_AUTO (7 << 5)
+#define MAX9286_MSTLINKSEL(n) ((n) << 5)
+#define MAX9286_EN_VS_GEN BIT(4)
+#define MAX9286_LINKEN(n) (1 << (n))
+/* Register 0x01 */
+#define MAX9286_FSYNCMODE_ECU (3 << 6)
+#define MAX9286_FSYNCMODE_EXT (2 << 6)
+#define MAX9286_FSYNCMODE_INT_OUT (1 << 6)
+#define MAX9286_FSYNCMODE_INT_HIZ (0 << 6)
+#define MAX9286_GPIEN BIT(5)
+#define MAX9286_ENLMO_RSTFSYNC BIT(2)
+#define MAX9286_FSYNCMETH_AUTO (2 << 0)
+#define MAX9286_FSYNCMETH_SEMI_AUTO (1 << 0)
+#define MAX9286_FSYNCMETH_MANUAL (0 << 0)
+#define MAX9286_REG_FSYNC_PERIOD_L 0x06
+#define MAX9286_REG_FSYNC_PERIOD_M 0x07
+#define MAX9286_REG_FSYNC_PERIOD_H 0x08
+/* Register 0x0a */
+#define MAX9286_FWDCCEN(n) (1 << ((n) + 4))
+#define MAX9286_REVCCEN(n) (1 << (n))
+/* Register 0x0c */
+#define MAX9286_HVEN BIT(7)
+#define MAX9286_EDC_6BIT_HAMMING (2 << 5)
+#define MAX9286_EDC_6BIT_CRC (1 << 5)
+#define MAX9286_EDC_1BIT_PARITY (0 << 5)
+#define MAX9286_DESEL BIT(4)
+#define MAX9286_INVVS BIT(3)
+#define MAX9286_INVHS BIT(2)
+#define MAX9286_HVSRC_D0 (2 << 0)
+#define MAX9286_HVSRC_D14 (1 << 0)
+#define MAX9286_HVSRC_D18 (0 << 0)
+/* Register 0x0f */
+#define MAX9286_0X0F_RESERVED BIT(3)
+/* Register 0x12 */
+#define MAX9286_CSILANECNT(n) (((n) - 1) << 6)
+#define MAX9286_CSIDBL BIT(5)
+#define MAX9286_DBL BIT(4)
+#define MAX9286_DATATYPE_USER_8BIT (11 << 0)
+#define MAX9286_DATATYPE_USER_YUV_12BIT (10 << 0)
+#define MAX9286_DATATYPE_USER_24BIT (9 << 0)
+#define MAX9286_DATATYPE_RAW14 (8 << 0)
+#define MAX9286_DATATYPE_RAW11 (7 << 0)
+#define MAX9286_DATATYPE_RAW10 (6 << 0)
+#define MAX9286_DATATYPE_RAW8 (5 << 0)
+#define MAX9286_DATATYPE_YUV422_10BIT (4 << 0)
+#define MAX9286_DATATYPE_YUV422_8BIT (3 << 0)
+#define MAX9286_DATATYPE_RGB555 (2 << 0)
+#define MAX9286_DATATYPE_RGB565 (1 << 0)
+#define MAX9286_DATATYPE_RGB888 (0 << 0)
+/* Register 0x15 */
+#define MAX9286_VC(n) ((n) << 5)
+#define MAX9286_VCTYPE BIT(4)
+#define MAX9286_CSIOUTEN BIT(3)
+#define MAX9286_0X15_RESV (3 << 0)
+/* Register 0x1b */
+#define MAX9286_SWITCHIN(n) (1 << ((n) + 4))
+#define MAX9286_ENEQ(n) (1 << (n))
+/* Register 0x27 */
+#define MAX9286_LOCKED BIT(7)
+/* Register 0x31 */
+#define MAX9286_FSYNC_LOCKED BIT(6)
+/* Register 0x34 */
+#define MAX9286_I2CLOCACK BIT(7)
+#define MAX9286_I2CSLVSH_1046NS_469NS (3 << 5)
+#define MAX9286_I2CSLVSH_938NS_352NS (2 << 5)
+#define MAX9286_I2CSLVSH_469NS_234NS (1 << 5)
+#define MAX9286_I2CSLVSH_352NS_117NS (0 << 5)
+#define MAX9286_I2CMSTBT_837KBPS (7 << 2)
+#define MAX9286_I2CMSTBT_533KBPS (6 << 2)
+#define MAX9286_I2CMSTBT_339KBPS (5 << 2)
+#define MAX9286_I2CMSTBT_173KBPS (4 << 2)
+#define MAX9286_I2CMSTBT_105KBPS (3 << 2)
+#define MAX9286_I2CMSTBT_84KBPS (2 << 2)
+#define MAX9286_I2CMSTBT_28KBPS (1 << 2)
+#define MAX9286_I2CMSTBT_8KBPS (0 << 2)
+#define MAX9286_I2CSLVTO_NONE (3 << 0)
+#define MAX9286_I2CSLVTO_1024US (2 << 0)
+#define MAX9286_I2CSLVTO_256US (1 << 0)
+#define MAX9286_I2CSLVTO_64US (0 << 0)
+/* Register 0x3b */
+#define MAX9286_REV_TRF(n) ((n) << 4)
+#define MAX9286_REV_AMP(n) ((((n) - 30) / 10) << 1) /* in mV */
+#define MAX9286_REV_AMP_X BIT(0)
+/* Register 0x3f */
+#define MAX9286_EN_REV_CFG BIT(6)
+#define MAX9286_REV_FLEN(n) ((n) - 20)
+/* Register 0x49 */
+#define MAX9286_VIDEO_DETECT_MASK 0x0f
+/* Register 0x69 */
+#define MAX9286_LFLTBMONMASKED BIT(7)
+#define MAX9286_LOCKMONMASKED BIT(6)
+#define MAX9286_AUTOCOMBACKEN BIT(5)
+#define MAX9286_AUTOMASKEN BIT(4)
+#define MAX9286_MASKLINK(n) ((n) << 0)
+
+#define MAX9286_NUM_GMSL 4
+#define MAX9286_N_SINKS 4
+#define MAX9286_N_PADS 5
+#define MAX9286_SRC_PAD 4
+
+struct max9286_source {
+ struct v4l2_async_subdev asd;
+ struct v4l2_subdev *sd;
+ struct fwnode_handle *fwnode;
+};
+
+#define asd_to_max9286_source(_asd) \
+ container_of(_asd, struct max9286_source, asd)
+
+struct max9286_priv {
+ struct i2c_client *client;
+ struct gpio_desc *gpiod_pwdn;
+ struct v4l2_subdev sd;
+ struct media_pad pads[MAX9286_N_PADS];
+ struct regulator *regulator;
+ bool poc_enabled;
+
+ struct gpio_chip gpio;
+ u8 gpio_state;
+
+ struct i2c_mux_core *mux;
+ unsigned int mux_channel;
+ bool mux_open;
+
+ struct v4l2_ctrl_handler ctrls;
+
+ struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
+
+ unsigned int nsources;
+ unsigned int source_mask;
+ unsigned int route_mask;
+ unsigned int bound_sources;
+ unsigned int csi2_data_lanes;
+ struct max9286_source sources[MAX9286_NUM_GMSL];
+ struct v4l2_async_notifier notifier;
+};
+
+static struct max9286_source *next_source(struct max9286_priv *priv,
+ struct max9286_source *source)
+{
+ if (!source)
+ source = &priv->sources[0];
+ else
+ source++;
+
+ for (; source < &priv->sources[MAX9286_NUM_GMSL]; source++) {
+ if (source->fwnode)
+ return source;
+ }
+
+ return NULL;
+}
+
+#define for_each_source(priv, source) \
+ for ((source) = NULL; ((source) = next_source((priv), (source))); )
+
+#define to_index(priv, source) ((source) - &(priv)->sources[0])
+
+static inline struct max9286_priv *sd_to_max9286(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct max9286_priv, sd);
+}
+
+/* -----------------------------------------------------------------------------
+ * I2C IO
+ */
+
+static int max9286_read(struct max9286_priv *priv, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(priv->client, reg);
+ if (ret < 0)
+ dev_err(&priv->client->dev,
+ "%s: register 0x%02x read failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(priv->client, reg, val);
+ if (ret < 0)
+ dev_err(&priv->client->dev,
+ "%s: register 0x%02x write failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * I2C Multiplexer
+ */
+
+static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf)
+{
+ max9286_write(priv, 0x0a, conf);
+
+ /*
+ * We must sleep after any change to the forward or reverse channel
+ * configuration.
+ */
+ usleep_range(3000, 5000);
+}
+
+static void max9286_i2c_mux_open(struct max9286_priv *priv)
+{
+ /* Open all channels on the MAX9286 */
+ max9286_i2c_mux_configure(priv, 0xff);
+
+ priv->mux_open = true;
+}
+
+static void max9286_i2c_mux_close(struct max9286_priv *priv)
+{
+ /*
+ * Ensure that both the forward and reverse channel are disabled on the
+ * mux, and that the channel ID is invalidated to ensure we reconfigure
+ * on the next max9286_i2c_mux_select() call.
+ */
+ max9286_i2c_mux_configure(priv, 0x00);
+
+ priv->mux_open = false;
+ priv->mux_channel = -1;
+}
+
+static int max9286_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct max9286_priv *priv = i2c_mux_priv(muxc);
+
+ /* Channel select is disabled when configured in the opened state. */
+ if (priv->mux_open)
+ return 0;
+
+ if (priv->mux_channel == chan)
+ return 0;
+
+ priv->mux_channel = chan;
+
+ max9286_i2c_mux_configure(priv,
+ MAX9286_FWDCCEN(chan) |
+ MAX9286_REVCCEN(chan));
+
+ return 0;
+}
+
+static int max9286_i2c_mux_init(struct max9286_priv *priv)
+{
+ struct max9286_source *source;
+ int ret;
+
+ if (!i2c_check_functionality(priv->client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -ENODEV;
+
+ priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev,
+ priv->nsources, 0, I2C_MUX_LOCKED,
+ max9286_i2c_mux_select, NULL);
+ if (!priv->mux)
+ return -ENOMEM;
+
+ priv->mux->priv = priv;
+
+ for_each_source(priv, source) {
+ unsigned int index = to_index(priv, source);
+
+ ret = i2c_mux_add_adapter(priv->mux, 0, index, 0);
+ if (ret < 0)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ i2c_mux_del_adapters(priv->mux);
+ return ret;
+}
+
+static void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
+{
+ u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
+ MAX9286_I2CMSTBT_105KBPS;
+
+ if (localack)
+ config |= MAX9286_I2CLOCACK;
+
+ max9286_write(priv, 0x34, config);
+ usleep_range(3000, 5000);
+}
+
+/*
+ * max9286_check_video_links() - Make sure video links are detected and locked
+ *
+ * Performs safety checks on video link status. Make sure they are detected
+ * and all enabled links are locked.
+ *
+ * Returns 0 for success, -EIO for errors.
+ */
+static int max9286_check_video_links(struct max9286_priv *priv)
+{
+ unsigned int i;
+ int ret;
+
+ /*
+ * Make sure valid video links are detected.
+ * The delay is not characterized in de-serializer manual, wait up
+ * to 5 ms.
+ */
+ for (i = 0; i < 10; i++) {
+ ret = max9286_read(priv, 0x49);
+ if (ret < 0)
+ return -EIO;
+
+ if ((ret & MAX9286_VIDEO_DETECT_MASK) == priv->source_mask)
+ break;
+
+ usleep_range(350, 500);
+ }
+
+ if (i == 10) {
+ dev_err(&priv->client->dev,
+ "Unable to detect video links: 0x%02x\n", ret);
+ return -EIO;
+ }
+
+ /* Make sure all enabled links are locked (4ms max). */
+ for (i = 0; i < 10; i++) {
+ ret = max9286_read(priv, 0x27);
+ if (ret < 0)
+ return -EIO;
+
+ if (ret & MAX9286_LOCKED)
+ break;
+
+ usleep_range(350, 450);
+ }
+
+ if (i == 10) {
+ dev_err(&priv->client->dev, "Not all enabled links locked\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * max9286_check_config_link() - Detect and wait for configuration links
+ *
+ * Determine if the configuration channel is up and settled for a link.
+ *
+ * Returns 0 for success, -EIO for errors.
+ */
+static int max9286_check_config_link(struct max9286_priv *priv,
+ unsigned int source_mask)
+{
+ unsigned int conflink_mask = (source_mask & 0x0f) << 4;
+ unsigned int i;
+ int ret;
+
+ /*
+ * Make sure requested configuration links are detected.
+ * The delay is not characterized in the chip manual: wait up
+ * to 5 milliseconds.
+ */
+ for (i = 0; i < 10; i++) {
+ ret = max9286_read(priv, 0x49) & 0xf0;
+ if (ret < 0)
+ return -EIO;
+
+ if (ret == conflink_mask)
+ break;
+
+ usleep_range(350, 500);
+ }
+
+ if (ret != conflink_mask) {
+ dev_err(&priv->client->dev,
+ "Unable to detect configuration links: 0x%02x expected 0x%02x\n",
+ ret, conflink_mask);
+ return -EIO;
+ }
+
+ dev_info(&priv->client->dev,
+ "Successfully detected configuration links after %u loops: 0x%02x\n",
+ i, conflink_mask);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdev
+ */
+
+static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct max9286_priv *priv = sd_to_max9286(notifier->sd);
+ struct max9286_source *source = asd_to_max9286_source(asd);
+ unsigned int index = to_index(priv, source);
+ unsigned int src_pad;
+ int ret;
+
+ ret = media_entity_get_fwnode_pad(&subdev->entity,
+ source->fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (ret < 0) {
+ dev_err(&priv->client->dev,
+ "Failed to find pad for %s\n", subdev->name);
+ return ret;
+ }
+
+ priv->bound_sources |= BIT(index);
+ source->sd = subdev;
+ src_pad = ret;
+
+ ret = media_create_pad_link(&source->sd->entity, src_pad,
+ &priv->sd.entity, index,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err(&priv->client->dev,
+ "Unable to link %s:%u -> %s:%u\n",
+ source->sd->name, src_pad, priv->sd.name, index);
+ return ret;
+ }
+
+ dev_dbg(&priv->client->dev, "Bound %s pad: %u on index %u\n",
+ subdev->name, src_pad, index);
+
+ /*
+ * We can only register v4l2_async_notifiers which do not provide a
+ * means to register a complete callback. bound_sources allows us to
+ * identify when all remove serializers have completed their probe.
+ */
+ if (priv->bound_sources != priv->source_mask)
+ return 0;
+
+ /*
+ * All enabled sources have probed and enabled their reverse control
+ * channels:
+ *
+ * - Verify all configuration links are properly detected
+ * - Disable auto-ack as communication on the control channel are now
+ * stable.
+ */
+ max9286_check_config_link(priv, priv->source_mask);
+
+ /*
+ * Re-configure I2C with local acknowledge disabled after cameras have
+ * probed.
+ */
+ max9286_configure_i2c(priv, false);
+
+ return 0;
+}
+
+static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct max9286_source *source = asd_to_max9286_source(asd);
+
+ source->sd = NULL;
+}
+
+static const struct v4l2_async_notifier_operations max9286_notify_ops = {
+ .bound = max9286_notify_bound,
+ .unbind = max9286_notify_unbind,
+};
+
+static int max9286_v4l2_notifier_register(struct max9286_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct max9286_source *source = NULL;
+ int ret;
+
+ if (!priv->nsources)
+ return 0;
+
+ v4l2_async_notifier_init(&priv->notifier);
+
+ for_each_source(priv, source) {
+ unsigned int i = to_index(priv, source);
+
+ source->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ source->asd.match.fwnode = source->fwnode;
+
+ ret = v4l2_async_notifier_add_subdev(&priv->notifier,
+ &source->asd);
+ if (ret) {
+ dev_err(dev, "Failed to add subdev for source %d", i);
+ v4l2_async_notifier_cleanup(&priv->notifier);
+ return ret;
+ }
+
+ /*
+ * Balance the reference counting handled through
+ * v4l2_async_notifier_cleanup()
+ */
+ fwnode_handle_get(source->fwnode);
+ }
+
+ priv->notifier.ops = &max9286_notify_ops;
+
+ ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
+ if (ret) {
+ dev_err(dev, "Failed to register subdev_notifier");
+ v4l2_async_notifier_cleanup(&priv->notifier);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void max9286_v4l2_notifier_unregister(struct max9286_priv *priv)
+{
+ if (!priv->nsources)
+ return;
+
+ v4l2_async_notifier_unregister(&priv->notifier);
+ v4l2_async_notifier_cleanup(&priv->notifier);
+}
+
+static int max9286_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+ struct max9286_source *source;
+ unsigned int i;
+ bool sync = false;
+ int ret;
+
+ if (enable) {
+ /*
+ * The frame sync between cameras is transmitted across the
+ * reverse channel as GPIO. We must open all channels while
+ * streaming to allow this synchronisation signal to be shared.
+ */
+ max9286_i2c_mux_open(priv);
+
+ /* Start all cameras. */
+ for_each_source(priv, source) {
+ ret = v4l2_subdev_call(source->sd, video, s_stream, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = max9286_check_video_links(priv);
+ if (ret)
+ return ret;
+
+ /*
+ * Wait until frame synchronization is locked.
+ *
+ * Manual says frame sync locking should take ~6 VTS.
+ * From practical experience at least 8 are required. Give
+ * 12 complete frames time (~400ms at 30 fps) to achieve frame
+ * locking before returning error.
+ */
+ for (i = 0; i < 40; i++) {
+ if (max9286_read(priv, 0x31) & MAX9286_FSYNC_LOCKED) {
+ sync = true;
+ break;
+ }
+ usleep_range(9000, 11000);
+ }
+
+ if (!sync) {
+ dev_err(&priv->client->dev,
+ "Failed to get frame synchronization\n");
+ return -EXDEV; /* Invalid cross-device link */
+ }
+
+ /*
+ * Enable CSI output, VC set according to link number.
+ * Bit 7 must be set (chip manual says it's 0 and reserved).
+ */
+ max9286_write(priv, 0x15, 0x80 | MAX9286_VCTYPE |
+ MAX9286_CSIOUTEN | MAX9286_0X15_RESV);
+ } else {
+ max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+
+ /* Stop all cameras. */
+ for_each_source(priv, source)
+ v4l2_subdev_call(source->sd, video, s_stream, 0);
+
+ max9286_i2c_mux_close(priv);
+ }
+
+ return 0;
+}
+
+static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index > 0)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_UYVY8_2X8;
+
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+max9286_get_pad_format(struct max9286_priv *priv,
+ struct v4l2_subdev_pad_config *cfg,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(&priv->sd, cfg, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &priv->fmt[pad];
+ default:
+ return NULL;
+ }
+}
+
+static int max9286_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+ struct v4l2_mbus_framefmt *cfg_fmt;
+
+ if (format->pad >= MAX9286_SRC_PAD)
+ return -EINVAL;
+
+ /* Refuse non YUV422 formats as we hardcode DT to 8 bit YUV422 */
+ switch (format->format.code) {
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ break;
+ default:
+ format->format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+ break;
+ }
+
+ cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which);
+ if (!cfg_fmt)
+ return -EINVAL;
+
+ *cfg_fmt = format->format;
+
+ return 0;
+}
+
+static int max9286_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct max9286_priv *priv = sd_to_max9286(sd);
+ struct v4l2_mbus_framefmt *cfg_fmt;
+
+ if (format->pad >= MAX9286_SRC_PAD)
+ return -EINVAL;
+
+ cfg_fmt = max9286_get_pad_format(priv, cfg, format->pad, format->which);
+ if (!cfg_fmt)
+ return -EINVAL;
+
+ format->format = *cfg_fmt;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops max9286_video_ops = {
+ .s_stream = max9286_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops max9286_pad_ops = {
+ .enum_mbus_code = max9286_enum_mbus_code,
+ .get_fmt = max9286_get_fmt,
+ .set_fmt = max9286_set_fmt,
+};
+
+static const struct v4l2_subdev_ops max9286_subdev_ops = {
+ .video = &max9286_video_ops,
+ .pad = &max9286_pad_ops,
+};
+
+static void max9286_init_format(struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = 1280;
+ fmt->height = 800;
+ fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int max9286_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format;
+ unsigned int i;
+
+ for (i = 0; i < MAX9286_N_SINKS; i++) {
+ format = v4l2_subdev_get_try_format(subdev, fh->pad, i);
+ max9286_init_format(format);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
+ .open = max9286_open,
+};
+
+static int max9286_v4l2_register(struct max9286_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct fwnode_handle *ep;
+ int ret;
+ int i;
+
+ /* Register v4l2 async notifiers for connected Camera subdevices */
+ ret = max9286_v4l2_notifier_register(priv);
+ if (ret) {
+ dev_err(dev, "Unable to register V4L2 async notifiers\n");
+ return ret;
+ }
+
+ /* Configure V4L2 for the MAX9286 itself */
+
+ v4l2_i2c_subdev_init(&priv->sd, priv->client, &max9286_subdev_ops);
+ priv->sd.internal_ops = &max9286_subdev_internal_ops;
+ priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ v4l2_ctrl_handler_init(&priv->ctrls, 1);
+ /*
+ * FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
+ * hardcoded frequency in the BSP CSI-2 receiver driver.
+ */
+ v4l2_ctrl_new_std(&priv->ctrls, NULL, V4L2_CID_PIXEL_RATE,
+ 50000000, 50000000, 1, 50000000);
+ priv->sd.ctrl_handler = &priv->ctrls;
+ ret = priv->ctrls.error;
+ if (ret)
+ goto err_async;
+
+ priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+
+ priv->pads[MAX9286_SRC_PAD].flags = MEDIA_PAD_FL_SOURCE;
+ for (i = 0; i < MAX9286_SRC_PAD; i++)
+ priv->pads[i].flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&priv->sd.entity, MAX9286_N_PADS,
+ priv->pads);
+ if (ret)
+ goto err_async;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), MAX9286_SRC_PAD,
+ 0, 0);
+ if (!ep) {
+ dev_err(dev, "Unable to retrieve endpoint on \"port@4\"\n");
+ ret = -ENOENT;
+ goto err_async;
+ }
+ priv->sd.fwnode = ep;
+
+ ret = v4l2_async_register_subdev(&priv->sd);
+ if (ret < 0) {
+ dev_err(dev, "Unable to register subdevice\n");
+ goto err_put_node;
+ }
+
+ return 0;
+
+err_put_node:
+ fwnode_handle_put(ep);
+err_async:
+ max9286_v4l2_notifier_unregister(priv);
+
+ return ret;
+}
+
+static void max9286_v4l2_unregister(struct max9286_priv *priv)
+{
+ fwnode_handle_put(priv->sd.fwnode);
+ v4l2_async_unregister_subdev(&priv->sd);
+ max9286_v4l2_notifier_unregister(priv);
+}
+
+/* -----------------------------------------------------------------------------
+ * Probe/Remove
+ */
+
+static int max9286_setup(struct max9286_priv *priv)
+{
+ /*
+ * Link ordering values for all enabled links combinations. Orders must
+ * be assigned sequentially from 0 to the number of enabled links
+ * without leaving any hole for disabled links. We thus assign orders to
+ * enabled links first, and use the remaining order values for disabled
+ * links are all links must have a different order value;
+ */
+ static const u8 link_order[] = {
+ (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxxx */
+ (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xxx0 */
+ (3 << 6) | (2 << 4) | (0 << 2) | (1 << 0), /* xx0x */
+ (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* xx10 */
+ (3 << 6) | (0 << 4) | (2 << 2) | (1 << 0), /* x0xx */
+ (3 << 6) | (1 << 4) | (2 << 2) | (0 << 0), /* x1x0 */
+ (3 << 6) | (1 << 4) | (0 << 2) | (2 << 0), /* x10x */
+ (3 << 6) | (1 << 4) | (1 << 2) | (0 << 0), /* x210 */
+ (0 << 6) | (3 << 4) | (2 << 2) | (1 << 0), /* 0xxx */
+ (1 << 6) | (3 << 4) | (2 << 2) | (0 << 0), /* 1xx0 */
+ (1 << 6) | (3 << 4) | (0 << 2) | (2 << 0), /* 1x0x */
+ (2 << 6) | (3 << 4) | (1 << 2) | (0 << 0), /* 2x10 */
+ (1 << 6) | (0 << 4) | (3 << 2) | (2 << 0), /* 10xx */
+ (2 << 6) | (1 << 4) | (3 << 2) | (0 << 0), /* 21x0 */
+ (2 << 6) | (1 << 4) | (0 << 2) | (3 << 0), /* 210x */
+ (3 << 6) | (2 << 4) | (1 << 2) | (0 << 0), /* 3210 */
+ };
+
+ /*
+ * Set the I2C bus speed.
+ *
+ * Enable I2C Local Acknowledge during the probe sequences of the camera
+ * only. This should be disabled after the mux is initialised.
+ */
+ max9286_configure_i2c(priv, true);
+
+ /*
+ * Reverse channel setup.
+ *
+ * - Enable custom reverse channel configuration (through register 0x3f)
+ * and set the first pulse length to 35 clock cycles.
+ * - Increase the reverse channel amplitude to 170mV to accommodate the
+ * high threshold enabled by the serializer driver.
+ */
+ max9286_write(priv, 0x3f, MAX9286_EN_REV_CFG | MAX9286_REV_FLEN(35));
+ max9286_write(priv, 0x3b, MAX9286_REV_TRF(1) | MAX9286_REV_AMP(70) |
+ MAX9286_REV_AMP_X);
+ usleep_range(2000, 2500);
+
+ /*
+ * Enable GMSL links, mask unused ones and autodetect link
+ * used as CSI clock source.
+ */
+ max9286_write(priv, 0x00, MAX9286_MSTLINKSEL_AUTO | priv->route_mask);
+ max9286_write(priv, 0x0b, link_order[priv->route_mask]);
+ max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
+
+ /*
+ * Video format setup:
+ * Disable CSI output, VC is set according to Link number.
+ */
+ max9286_write(priv, 0x15, MAX9286_VCTYPE | MAX9286_0X15_RESV);
+
+ /* Enable CSI-2 Lane D0-D3 only, DBL mode, YUV422 8-bit. */
+ max9286_write(priv, 0x12, MAX9286_CSIDBL | MAX9286_DBL |
+ MAX9286_CSILANECNT(priv->csi2_data_lanes) |
+ MAX9286_DATATYPE_YUV422_8BIT);
+
+ /* Automatic: FRAMESYNC taken from the slowest Link. */
+ max9286_write(priv, 0x01, MAX9286_FSYNCMODE_INT_HIZ |
+ MAX9286_FSYNCMETH_AUTO);
+
+ /* Enable HS/VS encoding, use D14/15 for HS/VS, invert VS. */
+ max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
+ MAX9286_HVSRC_D14);
+
+ /*
+ * Wait for 2ms to allow the link to resynchronize after the
+ * configuration change.
+ */
+ usleep_range(2000, 5000);
+
+ return 0;
+}
+
+static const struct of_device_id max9286_dt_ids[] = {
+ { .compatible = "maxim,max9286" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max9286_dt_ids);
+
+static void max9286_gpio_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct max9286_priv *priv = gpiochip_get_data(chip);
+
+ if (value)
+ priv->gpio_state |= BIT(offset);
+ else
+ priv->gpio_state &= ~BIT(offset);
+
+ max9286_write(priv, 0x0f, MAX9286_0X0F_RESERVED | priv->gpio_state);
+}
+
+static int max9286_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct max9286_priv *priv = gpiochip_get_data(chip);
+
+ return priv->gpio_state & BIT(offset);
+}
+
+static int max9286_gpio(struct max9286_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct gpio_chip *gpio = &priv->gpio;
+ int ret;
+
+ static const char * const names[] = {
+ "GPIO0OUT",
+ "GPIO1OUT",
+ };
+
+ /* Configure the GPIO */
+ gpio->label = dev_name(dev);
+ gpio->parent = dev;
+ gpio->owner = THIS_MODULE;
+ gpio->of_node = dev->of_node;
+ gpio->ngpio = 2;
+ gpio->base = -1;
+ gpio->set = max9286_gpio_set;
+ gpio->get = max9286_gpio_get;
+ gpio->can_sleep = true;
+ gpio->names = names;
+
+ /* GPIO values default to high */
+ priv->gpio_state = BIT(0) | BIT(1);
+
+ ret = devm_gpiochip_add_data(dev, gpio, priv);
+ if (ret)
+ dev_err(dev, "Unable to create gpio_chip\n");
+
+ return ret;
+}
+
+static int max9286_init(struct device *dev)
+{
+ struct max9286_priv *priv;
+ struct i2c_client *client;
+ int ret;
+
+ /* Skip non-max9286 devices. */
+ if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
+ return 0;
+
+ client = to_i2c_client(dev);
+ priv = i2c_get_clientdata(client);
+
+ /* Enable the bus power. */
+ ret = regulator_enable(priv->regulator);
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable to turn PoC on\n");
+ return ret;
+ }
+
+ priv->poc_enabled = true;
+
+ ret = max9286_setup(priv);
+ if (ret) {
+ dev_err(dev, "Unable to setup max9286\n");
+ goto err_regulator;
+ }
+
+ /*
+ * Register all V4L2 interactions for the MAX9286 and notifiers for
+ * any subdevices connected.
+ */
+ ret = max9286_v4l2_register(priv);
+ if (ret) {
+ dev_err(dev, "Failed to register with V4L2\n");
+ goto err_regulator;
+ }
+
+ ret = max9286_i2c_mux_init(priv);
+ if (ret) {
+ dev_err(dev, "Unable to initialize I2C multiplexer\n");
+ goto err_v4l2_register;
+ }
+
+ /* Leave the mux channels disabled until they are selected. */
+ max9286_i2c_mux_close(priv);
+
+ return 0;
+
+err_v4l2_register:
+ max9286_v4l2_unregister(priv);
+err_regulator:
+ regulator_disable(priv->regulator);
+ priv->poc_enabled = false;
+
+ return ret;
+}
+
+static void max9286_cleanup_dt(struct max9286_priv *priv)
+{
+ struct max9286_source *source;
+
+ for_each_source(priv, source) {
+ fwnode_handle_put(source->fwnode);
+ source->fwnode = NULL;
+ }
+}
+
+static int max9286_parse_dt(struct max9286_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ struct device_node *i2c_mux;
+ struct device_node *node = NULL;
+ unsigned int i2c_mux_mask = 0;
+
+ of_node_get(dev->of_node);
+ i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux");
+ if (!i2c_mux) {
+ dev_err(dev, "Failed to find i2c-mux node\n");
+ return -EINVAL;
+ }
+
+ /* Identify which i2c-mux channels are enabled */
+ for_each_child_of_node(i2c_mux, node) {
+ u32 id = 0;
+
+ of_property_read_u32(node, "reg", &id);
+ if (id >= MAX9286_NUM_GMSL)
+ continue;
+
+ if (!of_device_is_available(node)) {
+ dev_dbg(dev, "Skipping disabled I2C bus port %u\n",
+ id);
+ continue;
+ }
+
+ i2c_mux_mask |= BIT(id);
+ }
+ of_node_put(node);
+ of_node_put(i2c_mux);
+
+ /* Parse the endpoints */
+ for_each_endpoint_of_node(dev->of_node, node) {
+ struct max9286_source *source;
+ struct of_endpoint ep;
+
+ of_graph_parse_endpoint(node, &ep);
+ dev_dbg(dev, "Endpoint %pOF on port %d",
+ ep.local_node, ep.port);
+
+ if (ep.port > MAX9286_NUM_GMSL) {
+ dev_err(dev, "Invalid endpoint %s on port %d",
+ of_node_full_name(ep.local_node), ep.port);
+ continue;
+ }
+
+ /* For the source endpoint just parse the bus configuration. */
+ if (ep.port == MAX9286_SRC_PAD) {
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
+ int ret;
+
+ ret = v4l2_fwnode_endpoint_parse(
+ of_fwnode_handle(node), &vep);
+ if (ret) {
+ of_node_put(node);
+ return ret;
+ }
+
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
+ dev_err(dev,
+ "Media bus %u type not supported\n",
+ vep.bus_type);
+ v4l2_fwnode_endpoint_free(&vep);
+ of_node_put(node);
+ return -EINVAL;
+ }
+
+ priv->csi2_data_lanes =
+ vep.bus.mipi_csi2.num_data_lanes;
+ v4l2_fwnode_endpoint_free(&vep);
+
+ continue;
+ }
+
+ /* Skip if the corresponding GMSL link is unavailable. */
+ if (!(i2c_mux_mask & BIT(ep.port)))
+ continue;
+
+ if (priv->sources[ep.port].fwnode) {
+ dev_err(dev,
+ "Multiple port endpoints are not supported: %d",
+ ep.port);
+
+ continue;
+ }
+
+ source = &priv->sources[ep.port];
+ source->fwnode = fwnode_graph_get_remote_endpoint(
+ of_fwnode_handle(node));
+ if (!source->fwnode) {
+ dev_err(dev,
+ "Endpoint %pOF has no remote endpoint connection\n",
+ ep.local_node);
+
+ continue;
+ }
+
+ priv->source_mask |= BIT(ep.port);
+ priv->nsources++;
+ }
+ of_node_put(node);
+
+ priv->route_mask = priv->source_mask;
+
+ return 0;
+}
+
+static int max9286_probe(struct i2c_client *client)
+{
+ struct max9286_priv *priv;
+ unsigned int i;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->client = client;
+ i2c_set_clientdata(client, priv);
+
+ for (i = 0; i < MAX9286_N_SINKS; i++)
+ max9286_init_format(&priv->fmt[i]);
+
+ ret = max9286_parse_dt(priv);
+ if (ret)
+ return ret;
+
+ priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->gpiod_pwdn))
+ return PTR_ERR(priv->gpiod_pwdn);
+
+ gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
+ gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
+
+ /* Wait at least 4ms before the I2C lines latch to the address */
+ if (priv->gpiod_pwdn)
+ usleep_range(4000, 5000);
+
+ ret = max9286_gpio(priv);
+ if (ret)
+ return ret;
+
+ priv->regulator = regulator_get(&client->dev, "poc");
+ if (IS_ERR(priv->regulator)) {
+ if (PTR_ERR(priv->regulator) != -EPROBE_DEFER)
+ dev_err(&client->dev,
+ "Unable to get PoC regulator (%ld)\n",
+ PTR_ERR(priv->regulator));
+ ret = PTR_ERR(priv->regulator);
+ priv->regulator = NULL;
+ goto err_free;
+ }
+
+ /*
+ * We can have multiple MAX9286 instances on the same physical I2C
+ * bus, and I2C children behind ports of separate MAX9286 instances
+ * having the same I2C address. As the MAX9286 starts by default with
+ * all ports enabled, we need to disable all ports on all MAX9286
+ * instances before proceeding to further initialize the devices and
+ * instantiate children.
+ *
+ * Start by just disabling all channels on the current device. Then,
+ * if all other MAX9286 on the parent bus have been probed, proceed
+ * to initialize them all, including the current one.
+ */
+ max9286_i2c_mux_close(priv);
+
+ /*
+ * The MAX9286 initialises with auto-acknowledge enabled by default.
+ * This means that if multiple MAX9286 devices are connected to an I2C
+ * bus, another MAX9286 could ack I2C transfers meant for a device on
+ * the other side of the GMSL links for this MAX9286 (such as a
+ * MAX9271). To prevent that disable auto-acknowledge early on; it
+ * will be enabled later as needed.
+ */
+ max9286_configure_i2c(priv, false);
+
+ ret = max9286_init(&client->dev);
+ if (ret < 0)
+ goto err_regulator;
+
+ return 0;
+
+err_regulator:
+ regulator_put(priv->regulator);
+ max9286_i2c_mux_close(priv);
+ max9286_configure_i2c(priv, false);
+err_free:
+ max9286_cleanup_dt(priv);
+ kfree(priv);
+
+ return ret;
+}
+
+static int max9286_remove(struct i2c_client *client)
+{
+ struct max9286_priv *priv = i2c_get_clientdata(client);
+
+ i2c_mux_del_adapters(priv->mux);
+
+ max9286_v4l2_unregister(priv);
+
+ if (priv->poc_enabled)
+ regulator_disable(priv->regulator);
+ regulator_put(priv->regulator);
+
+ max9286_cleanup_dt(priv);
+
+ gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
+
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id max9286_id[] = {
+ { "max9286", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9286_id);
+
+static struct i2c_driver max9286_i2c_driver = {
+ .driver = {
+ .name = "max9286",
+ .of_match_table = of_match_ptr(max9286_dt_ids),
+ },
+ .probe_new = max9286_probe,
+ .remove = max9286_remove,
+ .id_table = max9286_id,
+};
+
+module_i2c_driver(max9286_i2c_driver);
+
+MODULE_DESCRIPTION("Maxim MAX9286 GMSL Deserializer Driver");
+MODULE_AUTHOR("Jacopo Mondi, Kieran Bingham, Laurent Pinchart, Niklas Söderlund, Vladimir Barinov");
+MODULE_LICENSE("GPL");
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 02/20] fixup! media: i2c: Add MAX9286 driver
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 01/20] media: i2c: Add MAX9286 driver Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 03/20] squash!: max9286: Rebase on latest media/master Jacopo Mondi
` (17 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index a20829297ef6..cb58782e5143 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -463,9 +463,9 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
subdev->name, src_pad, index);
/*
- * We can only register v4l2_async_notifiers which do not provide a
+ * We can only register v4l2_async_notifiers, which do not provide a
* means to register a complete callback. bound_sources allows us to
- * identify when all remove serializers have completed their probe.
+ * identify when all remote serializers have completed their probe.
*/
if (priv->bound_sources != priv->source_mask)
return 0;
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 03/20] squash!: max9286: Rebase on latest media/master
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 01/20] media: i2c: Add MAX9286 driver Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 02/20] fixup! " Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:54 ` Kieran Bingham
2020-04-16 10:40 ` [v8-rc1 04/20] squash! max9286: Update the bound_sources mask on unbind Jacopo Mondi
` (16 subsequent siblings)
19 siblings, 1 reply; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
Adapt Kconfig to latest upstream changes
Fixes: 32a363d0b0b1 ("media: Kconfig files: use select for V4L2 subdevs and MC")
---
drivers/media/i2c/Kconfig | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 04186787a2f0..5d810f6f230e 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -467,8 +467,9 @@ config VIDEO_VPX3220
config VIDEO_MAX9286
tristate "Maxim MAX9286 GMSL deserializer support"
depends on I2C && I2C_MUX
- depends on VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
help
This driver supports the Maxim MAX9286 GMSL deserializer.
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 04/20] squash! max9286: Update the bound_sources mask on unbind
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (2 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 03/20] squash!: max9286: Rebase on latest media/master Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 05/20] squash! max9286: convert probe kzalloc Jacopo Mondi
` (15 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
The bound_sources bit mask tracks sources which have been successfully
bound through the v4l2 async notifier system.
Ensure that the mask is updated accordingly when unbinding.
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index cb58782e5143..b84d2daa6561 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -493,9 +493,12 @@ static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
{
+ struct max9286_priv *priv = sd_to_max9286(notifier->sd);
struct max9286_source *source = asd_to_max9286_source(asd);
+ unsigned int index = to_index(priv, source);
source->sd = NULL;
+ priv->bound_sources &= ~BIT(index);
}
static const struct v4l2_async_notifier_operations max9286_notify_ops = {
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 05/20] squash! max9286: convert probe kzalloc
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (3 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 04/20] squash! max9286: Update the bound_sources mask on unbind Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 06/20] squash! max9286: Fix cleanup path from GPIO powerdown Jacopo Mondi
` (14 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
v8:
- Convert probe kzalloc usage to devm_ variant
---
drivers/media/i2c/max9286.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index b84d2daa6561..0a43137b8112 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1155,7 +1155,7 @@ static int max9286_probe(struct i2c_client *client)
unsigned int i;
int ret;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1232,7 +1232,6 @@ static int max9286_probe(struct i2c_client *client)
max9286_configure_i2c(priv, false);
err_free:
max9286_cleanup_dt(priv);
- kfree(priv);
return ret;
}
@@ -1253,8 +1252,6 @@ static int max9286_remove(struct i2c_client *client)
gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
- kfree(priv);
-
return 0;
}
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 06/20] squash! max9286: Fix cleanup path from GPIO powerdown
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (4 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 05/20] squash! max9286: convert probe kzalloc Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 07/20] squash! max9286: cleanup GPIO device registration fail path Jacopo Mondi
` (13 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
- Fix up cleanup path from GPIO PowerDown registration
---
drivers/media/i2c/max9286.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 0a43137b8112..cc99740b34c5 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1171,8 +1171,10 @@ static int max9286_probe(struct i2c_client *client)
priv->gpiod_pwdn = devm_gpiod_get_optional(&client->dev, "enable",
GPIOD_OUT_HIGH);
- if (IS_ERR(priv->gpiod_pwdn))
- return PTR_ERR(priv->gpiod_pwdn);
+ if (IS_ERR(priv->gpiod_pwdn)) {
+ ret = PTR_ERR(priv->gpiod_pwdn);
+ goto err_cleanup_dt;
+ }
gpiod_set_consumer_name(priv->gpiod_pwdn, "max9286-pwdn");
gpiod_set_value_cansleep(priv->gpiod_pwdn, 1);
@@ -1193,7 +1195,7 @@ static int max9286_probe(struct i2c_client *client)
PTR_ERR(priv->regulator));
ret = PTR_ERR(priv->regulator);
priv->regulator = NULL;
- goto err_free;
+ goto err_cleanup_dt;
}
/*
@@ -1230,7 +1232,7 @@ static int max9286_probe(struct i2c_client *client)
regulator_put(priv->regulator);
max9286_i2c_mux_close(priv);
max9286_configure_i2c(priv, false);
-err_free:
+err_cleanup_dt:
max9286_cleanup_dt(priv);
return ret;
@@ -1248,10 +1250,10 @@ static int max9286_remove(struct i2c_client *client)
regulator_disable(priv->regulator);
regulator_put(priv->regulator);
- max9286_cleanup_dt(priv);
-
gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
+ max9286_cleanup_dt(priv);
+
return 0;
}
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 07/20] squash! max9286: cleanup GPIO device registration fail path
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (5 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 06/20] squash! max9286: Fix cleanup path from GPIO powerdown Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 08/20] squash! max9286: Convert to use devm_regulator_get() Jacopo Mondi
` (12 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
- cleanup GPIO device registration fail path
---
drivers/media/i2c/max9286.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index cc99740b34c5..d0749c537152 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1185,7 +1185,7 @@ static int max9286_probe(struct i2c_client *client)
ret = max9286_gpio(priv);
if (ret)
- return ret;
+ goto err_powerdown;
priv->regulator = regulator_get(&client->dev, "poc");
if (IS_ERR(priv->regulator)) {
@@ -1194,8 +1194,7 @@ static int max9286_probe(struct i2c_client *client)
"Unable to get PoC regulator (%ld)\n",
PTR_ERR(priv->regulator));
ret = PTR_ERR(priv->regulator);
- priv->regulator = NULL;
- goto err_cleanup_dt;
+ goto err_powerdown;
}
/*
@@ -1232,6 +1231,8 @@ static int max9286_probe(struct i2c_client *client)
regulator_put(priv->regulator);
max9286_i2c_mux_close(priv);
max9286_configure_i2c(priv, false);
+err_powerdown:
+ gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
err_cleanup_dt:
max9286_cleanup_dt(priv);
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 08/20] squash! max9286: Convert to use devm_regulator_get()
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (6 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 07/20] squash! max9286: cleanup GPIO device registration fail path Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 09/20] squash! max9286: Fit max9286_parse_dt print on one line Jacopo Mondi
` (11 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
- Convert to use devm_regulator_get()
---
drivers/media/i2c/max9286.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index d0749c537152..08fd946c25f0 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1187,7 +1187,7 @@ static int max9286_probe(struct i2c_client *client)
if (ret)
goto err_powerdown;
- priv->regulator = regulator_get(&client->dev, "poc");
+ priv->regulator = devm_regulator_get(&client->dev, "poc");
if (IS_ERR(priv->regulator)) {
if (PTR_ERR(priv->regulator) != -EPROBE_DEFER)
dev_err(&client->dev,
@@ -1249,7 +1249,6 @@ static int max9286_remove(struct i2c_client *client)
if (priv->poc_enabled)
regulator_disable(priv->regulator);
- regulator_put(priv->regulator);
gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 09/20] squash! max9286: Fit max9286_parse_dt print on one line
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (7 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 08/20] squash! max9286: Convert to use devm_regulator_get() Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 10/20] squash! max9286: Move multi-device workarounds out of upstream Jacopo Mondi
` (10 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
- Fit max9286_parse_dt print on one line
---
drivers/media/i2c/max9286.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 08fd946c25f0..8f7e8928af56 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1061,8 +1061,7 @@ static int max9286_parse_dt(struct max9286_priv *priv)
continue;
if (!of_device_is_available(node)) {
- dev_dbg(dev, "Skipping disabled I2C bus port %u\n",
- id);
+ dev_dbg(dev, "Skipping disabled I2C bus port %u\n", id);
continue;
}
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 10/20] squash! max9286: Move multi-device workarounds out of upstream
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (8 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 09/20] squash! max9286: Fit max9286_parse_dt print on one line Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 11/20] squash! max9286: Remove I2C mod-table Jacopo Mondi
` (9 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
- Move multi-device workarounds out of upstream driver
---
drivers/media/i2c/max9286.c | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 8f7e8928af56..3fd70ddbcce8 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -911,12 +911,6 @@ static int max9286_setup(struct max9286_priv *priv)
return 0;
}
-static const struct of_device_id max9286_dt_ids[] = {
- { .compatible = "maxim,max9286" },
- {},
-};
-MODULE_DEVICE_TABLE(of, max9286_dt_ids);
-
static void max9286_gpio_set(struct gpio_chip *chip,
unsigned int offset, int value)
{
@@ -976,10 +970,6 @@ static int max9286_init(struct device *dev)
struct i2c_client *client;
int ret;
- /* Skip non-max9286 devices. */
- if (!dev->of_node || !of_match_node(max9286_dt_ids, dev->of_node))
- return 0;
-
client = to_i2c_client(dev);
priv = i2c_get_clientdata(client);
@@ -1256,6 +1246,12 @@ static int max9286_remove(struct i2c_client *client)
return 0;
}
+static const struct of_device_id max9286_dt_ids[] = {
+ { .compatible = "maxim,max9286" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max9286_dt_ids);
+
static const struct i2c_device_id max9286_id[] = {
{ "max9286", 0 },
{ }
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 11/20] squash! max9286: Remove I2C mod-table
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (9 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 10/20] squash! max9286: Move multi-device workarounds out of upstream Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 12/20] sqaush! max9286: Lock format changes Jacopo Mondi
` (8 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
'Technically' this table is still required for module autoloading, until
some solution such as [0] is merged:
[0] [PATCH RFC] modpost: Support I2C Aliases from OF tables
https://lore.kernel.org/lkml/20190710193918.31135-1-kieran.bingham+renesas@ideasonboard.com/
However in practice, I think the only restriction that gets lost is
device instantiation from sysfs, which wouldn't really work for this
driver anyway, because it requires a description of connected endpoints.
So - it should be quite safe to drop it.
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 7 -------
1 file changed, 7 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 3fd70ddbcce8..84173b8c2706 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1252,12 +1252,6 @@ static const struct of_device_id max9286_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, max9286_dt_ids);
-static const struct i2c_device_id max9286_id[] = {
- { "max9286", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max9286_id);
-
static struct i2c_driver max9286_i2c_driver = {
.driver = {
.name = "max9286",
@@ -1265,7 +1259,6 @@ static struct i2c_driver max9286_i2c_driver = {
},
.probe_new = max9286_probe,
.remove = max9286_remove,
- .id_table = max9286_id,
};
module_i2c_driver(max9286_i2c_driver);
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 12/20] sqaush! max9286: Lock format changes
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (10 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 11/20] squash! max9286: Remove I2C mod-table Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 13/20] squash! max9286: Implement Pixelrate control Jacopo Mondi
` (7 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Provide a mutex to protect against format changes on the pads. The
mutex can also be used to protect against control changes, or other
userspace facing interactions as necessary
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 84173b8c2706..49b4d1830747 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -18,6 +18,7 @@
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of_graph.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -157,6 +158,9 @@ struct max9286_priv {
struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
+ /* Protects controls and fmt structures */
+ struct mutex mutex;
+
unsigned int nsources;
unsigned int source_mask;
unsigned int route_mask;
@@ -680,7 +684,9 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
if (!cfg_fmt)
return -EINVAL;
+ mutex_lock(&priv->mutex);
*cfg_fmt = format->format;
+ mutex_unlock(&priv->mutex);
return 0;
}
@@ -699,7 +705,9 @@ static int max9286_get_fmt(struct v4l2_subdev *sd,
if (!cfg_fmt)
return -EINVAL;
+ mutex_lock(&priv->mutex);
format->format = *cfg_fmt;
+ mutex_unlock(&priv->mutex);
return 0;
}
@@ -1148,6 +1156,8 @@ static int max9286_probe(struct i2c_client *client)
if (!priv)
return -ENOMEM;
+ mutex_init(&priv->mutex);
+
priv->client = client;
i2c_set_clientdata(client, priv);
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 13/20] squash! max9286: Implement Pixelrate control
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (11 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 12/20] sqaush! max9286: Lock format changes Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 14/20] squash! max9286: Calculate pixel rate Jacopo Mondi
` (6 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Determine the (CSI2) pixel rate control by providing a control to read,
and checking the rate from the upstream camera sensors, and their
appropriate formats.
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 38 +++++++++++++++++++++++++++++++------
1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 49b4d1830747..2d71205a1aad 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -155,6 +155,7 @@ struct max9286_priv {
bool mux_open;
struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *pixelrate;
struct v4l2_mbus_framefmt fmt[MAX9286_N_SINKS];
@@ -429,6 +430,11 @@ static int max9286_check_config_link(struct max9286_priv *priv,
* V4L2 Subdev
*/
+static int max9286_set_pixelrate(struct max9286_priv *priv, s64 rate)
+{
+ return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, rate);
+}
+
static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
@@ -664,6 +670,7 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
{
struct max9286_priv *priv = sd_to_max9286(sd);
struct v4l2_mbus_framefmt *cfg_fmt;
+ s64 pixelrate;
if (format->pad >= MAX9286_SRC_PAD)
return -EINVAL;
@@ -688,6 +695,12 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
*cfg_fmt = format->format;
mutex_unlock(&priv->mutex);
+ /* Update pixel rate for the CSI2 receiver */
+ pixelrate = cfg_fmt->width * cfg_fmt->height
+ * priv->nsources * 30 /*FPS*/;
+
+ max9286_set_pixelrate(priv, pixelrate);
+
return 0;
}
@@ -756,6 +769,20 @@ static const struct v4l2_subdev_internal_ops max9286_subdev_internal_ops = {
.open = max9286_open,
};
+static int max9286_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_PIXEL_RATE:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ctrl_ops max9286_ctrl_ops = {
+ .s_ctrl = max9286_s_ctrl,
+};
+
static int max9286_v4l2_register(struct max9286_priv *priv)
{
struct device *dev = &priv->client->dev;
@@ -777,12 +804,11 @@ static int max9286_v4l2_register(struct max9286_priv *priv)
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
v4l2_ctrl_handler_init(&priv->ctrls, 1);
- /*
- * FIXME: Compute the real pixel rate. The 50 MP/s value comes from the
- * hardcoded frequency in the BSP CSI-2 receiver driver.
- */
- v4l2_ctrl_new_std(&priv->ctrls, NULL, V4L2_CID_PIXEL_RATE,
- 50000000, 50000000, 1, 50000000);
+ priv->pixelrate = v4l2_ctrl_new_std(&priv->ctrls,
+ &max9286_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ 1, INT_MAX, 1, 50000000);
+
priv->sd.ctrl_handler = &priv->ctrls;
ret = priv->ctrls.error;
if (ret)
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 14/20] squash! max9286: Calculate pixel rate
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (12 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 13/20] squash! max9286: Implement Pixelrate control Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:45 ` Kieran Bingham
2020-04-16 10:40 ` [v8-rc1 15/20] squash! max9286: Disable overlap window Jacopo Mondi
` (5 subsequent siblings)
19 siblings, 1 reply; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
Calculate the CSI-2 transmitter pixel rate using the one reported from
sources.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
drivers/media/i2c/max9286.c | 50 +++++++++++++++++++++++++++++--------
1 file changed, 40 insertions(+), 10 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 2d71205a1aad..3ef74ba10074 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -430,9 +430,46 @@ static int max9286_check_config_link(struct max9286_priv *priv,
* V4L2 Subdev
*/
-static int max9286_set_pixelrate(struct max9286_priv *priv, s64 rate)
+static int max9286_set_pixelrate(struct max9286_priv *priv)
{
- return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, rate);
+ struct max9286_source *source = NULL;
+ u64 pixelrate = 0;
+
+ for_each_source(priv, source) {
+ struct v4l2_ctrl *ctrl;
+ u64 source_rate = 0;
+
+ /* Pixel rate is mandatory to be reported by sources. */
+ ctrl = v4l2_ctrl_find(source->sd->ctrl_handler,
+ V4L2_CID_PIXEL_RATE);
+ if (!ctrl) {
+ pixelrate = 0;
+ break;
+ }
+
+ /* All source must report the same pixel rate. */
+ source_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
+ if (!pixelrate) {
+ pixelrate = source_rate;
+ } else if (pixelrate != source_rate) {
+ dev_err(&priv->client->dev,
+ "Unable to calculate pixel rate\n");
+ return -EINVAL;
+ }
+ }
+
+ if (!pixelrate) {
+ dev_err(&priv->client->dev,
+ "No pixel rate control available in sources\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The CSI-2 transmitter pixel rate is the single source rate multiplied
+ * by the number of available sources.
+ */
+ return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
+ pixelrate * priv->nsources);
}
static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
@@ -496,7 +533,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
*/
max9286_configure_i2c(priv, false);
- return 0;
+ return max9286_set_pixelrate(priv);
}
static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
@@ -670,7 +707,6 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
{
struct max9286_priv *priv = sd_to_max9286(sd);
struct v4l2_mbus_framefmt *cfg_fmt;
- s64 pixelrate;
if (format->pad >= MAX9286_SRC_PAD)
return -EINVAL;
@@ -695,12 +731,6 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
*cfg_fmt = format->format;
mutex_unlock(&priv->mutex);
- /* Update pixel rate for the CSI2 receiver */
- pixelrate = cfg_fmt->width * cfg_fmt->height
- * priv->nsources * 30 /*FPS*/;
-
- max9286_set_pixelrate(priv, pixelrate);
-
return 0;
}
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 15/20] squash! max9286: Disable overlap window
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (13 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 14/20] squash! max9286: Calculate pixel rate Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:50 ` Kieran Bingham
2020-04-16 10:40 ` [v8-rc1 16/20] sqaush! max9286: Describe pad index usage Jacopo Mondi
` (4 subsequent siblings)
19 siblings, 1 reply; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
v2:
[Jacopo]
- Write register 0x63 and 0x64 directly as going through the function
breaks RDACM21 operations
---
drivers/media/i2c/max9286.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 3ef74ba10074..e0c637d4a7de 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -966,6 +966,18 @@ static int max9286_setup(struct max9286_priv *priv)
max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
MAX9286_HVSRC_D14);
+ /*
+ * The overlap window seems to provide additional validation by tracking
+ * the delay between vsync and frame sync, generating an error if the
+ * delay is bigger than the programmed window, though it's not yet clear
+ * what value should be set.
+ *
+ * As it's an optional value and can be disabled, we do so by setting
+ * a 0 overlap value.
+ */
+ max9286_write(priv, 0x63, 0);
+ max9286_write(priv, 0x64, 0);
+
/*
* Wait for 2ms to allow the link to resynchronize after the
* configuration change.
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 16/20] sqaush! max9286: Describe pad index usage
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (14 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 15/20] squash! max9286: Disable overlap window Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 17/20] sqaush! max9286: Remove poc_enabled workaround Jacopo Mondi
` (3 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Sakari commented that the sink/source pad indexes are not the same as
the OF port numbers. We define them such that they match, so that they
can be used interchangably - but of course they are not the 'same
thing'. Document this in a comment at the definition of the pad sizings.
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index e0c637d4a7de..6d63b4ca0a42 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -125,6 +125,10 @@
#define MAX9286_AUTOMASKEN BIT(4)
#define MAX9286_MASKLINK(n) ((n) << 0)
+/*
+ * The sink and source pads are created to match the OF graph port numbers so
+ * that their indexes can be used interchangeably.
+ */
#define MAX9286_NUM_GMSL 4
#define MAX9286_N_SINKS 4
#define MAX9286_N_PADS 5
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 17/20] sqaush! max9286: Remove poc_enabled workaround
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (15 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 16/20] sqaush! max9286: Describe pad index usage Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 18/20] squash! max9286: Put of node on error Jacopo Mondi
` (2 subsequent siblings)
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
This boolean is a flag used to handle the regulator when our
multi-max9286 workaround is in place. It shouldn't be in the upstream
driver, and is moved out.
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
---
drivers/media/i2c/max9286.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 6d63b4ca0a42..69e81b879a9b 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -149,7 +149,6 @@ struct max9286_priv {
struct v4l2_subdev sd;
struct media_pad pads[MAX9286_N_PADS];
struct regulator *regulator;
- bool poc_enabled;
struct gpio_chip gpio;
u8 gpio_state;
@@ -1060,8 +1059,6 @@ static int max9286_init(struct device *dev)
return ret;
}
- priv->poc_enabled = true;
-
ret = max9286_setup(priv);
if (ret) {
dev_err(dev, "Unable to setup max9286\n");
@@ -1093,7 +1090,6 @@ static int max9286_init(struct device *dev)
max9286_v4l2_unregister(priv);
err_regulator:
regulator_disable(priv->regulator);
- priv->poc_enabled = false;
return ret;
}
@@ -1318,8 +1314,7 @@ static int max9286_remove(struct i2c_client *client)
max9286_v4l2_unregister(priv);
- if (priv->poc_enabled)
- regulator_disable(priv->regulator);
+ regulator_disable(priv->regulator);
gpiod_set_value_cansleep(priv->gpiod_pwdn, 0);
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 18/20] squash! max9286: Put of node on error
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (16 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 17/20] sqaush! max9286: Remove poc_enabled workaround Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 19/20] dt-bindings: media: i2c: Add bindings for IMI RDACM2x Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 20/20] media: i2c: Add RDACM20 driver Jacopo Mondi
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam
Put the device of node in case of dt parsing error.
Fixes: 9eed4185c7a0 ("media: i2c: Add MAX9286 driver")
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
---
drivers/media/i2c/max9286.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 69e81b879a9b..ccd9041b25e7 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1115,6 +1115,7 @@ static int max9286_parse_dt(struct max9286_priv *priv)
i2c_mux = of_find_node_by_name(dev->of_node, "i2c-mux");
if (!i2c_mux) {
dev_err(dev, "Failed to find i2c-mux node\n");
+ of_node_put(dev->of_node);
return -EINVAL;
}
@@ -1162,6 +1163,7 @@ static int max9286_parse_dt(struct max9286_priv *priv)
of_fwnode_handle(node), &vep);
if (ret) {
of_node_put(node);
+ of_node_put(dev->of_node);
return ret;
}
@@ -1171,6 +1173,7 @@ static int max9286_parse_dt(struct max9286_priv *priv)
vep.bus_type);
v4l2_fwnode_endpoint_free(&vep);
of_node_put(node);
+ of_node_put(dev->of_node);
return -EINVAL;
}
@@ -1208,6 +1211,7 @@ static int max9286_parse_dt(struct max9286_priv *priv)
priv->nsources++;
}
of_node_put(node);
+ of_node_put(dev->of_node);
priv->route_mask = priv->source_mask;
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 19/20] dt-bindings: media: i2c: Add bindings for IMI RDACM2x
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (17 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 18/20] squash! max9286: Put of node on error Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 20/20] media: i2c: Add RDACM20 driver Jacopo Mondi
19 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc, robh
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam, devicetree
The IMI RDACM20 and IMI RDACM21 are Gigabit Multimedia Serial Link (GMSL) camera
capable of transmitting video and I2C control messages on a coax cable
physical link for automotive applications.
Document their device tree bindings.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
v2:
- Provide imi vendor prefix
- Fix minor spelling
v3:
- update binding descriptions
v4:
- No change
v5:
- Specify optional third reg address for the MCU
v7:
[Jacopo]
- Rename to imi,rdacm2x-gmsl.yaml
- Exand bindings to describe RDACM21
v8:
- no changes
---
.../bindings/media/i2c/imi,rdacm2x-gmsl.yaml | 161 ++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
2 files changed, 163 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
diff --git a/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
new file mode 100644
index 000000000000..8476e99b46b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
@@ -0,0 +1,161 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+# Copyright (C) 2019 Renesas Electronics Corp.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/imi,rdacm2x-gmsl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: IMI D&D RDACM20 and RDACM21 Automotive Camera Platforms
+
+maintainers:
+ - Jacopo Mondi <jacopo+renesas@jmondi.org>
+ - Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
+ - Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+ - Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+
+description: -|
+ The IMI D&D RDACM20 and RDACM21 are GMSL-compatible camera designed for
+ automotive applications.
+
+ The RDACM20 camera module encloses a Maxim Integrated MAX9271 GMSL serializer,
+ coupled with an OV10635 image sensor and an embedded MCU. Both the MCU and
+ the image sensor are connected to the serializer local I2C bus and are
+ accessible by the host SoC by direct addressing.
+
+ The RDACM21 camera module encloses the same serializer, coupled with an
+ OV10640 image sensor and an OV490 ISP. Only the OV490 ISP is interfaced to
+ the serializer local I2C bus while the image sensor is not accessible from
+ the host SoC.
+
+ They both connect to a remote GMSL endpoint through a coaxial cable.
+
+ IMI RDACM20
+ +---------------+ +--------------------------------+
+ | GMSL | <- Video Stream | <- Video--------\ |
+ | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV10635 |
+ | de-serializer | <- I2C messages -> | \<-->MCU |
+ +---------------+ +--------------------------------+
+
+ IMI RDACM21
+ +---------------+ +--------------------------------+
+ | GMSL | <- Video Stream | <- Video--------\ |
+ | |< === GMSL Link ====== >|MAX9271<- I2C bus-> <-->OV490 |
+ | | <- I2C messages -> | | |
+ | de-serializer | | OV10640 <-------| |
+ +---------------+ +--------------------------------+
+
+ Both camera modules serialize video data generated by the embedded camera
+ sensor on the GMSL serial channel to a remote GMSL de-serializer. They also
+ receive and transmit I2C messages encapsulated and transmitted on the GMSL
+ bidirectional control channel.
+
+ All I2C traffic received on the GMSL link not directed to the serializer is
+ propagated on the local I2C bus to the remote device there connected. All the
+ I2C traffic generated on the local I2C bus not directed to the serializer is
+ propagated to the remote de-serializer encapsulated in the GMSL control
+ channel.
+
+ The RDACM20 and RDACM21 DT node should be a direct child of the GMSL
+ deserializer's I2C bus corresponding to the GMSL link that the camera is
+ attached to.
+
+properties:
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - imi,rdacm20
+ - imi,rdacm21
+
+ reg:
+ description: -|
+ I2C device addresses, the first to be assigned to the serializer, the
+ following ones to be assigned to the remote devices.
+
+ For RDACM20 the second entry of the property is assigned to the
+ OV10635 image sensor and the optional third one to the embedded MCU.
+
+ For RDACM21 the second entry is assigned to the OV490 ISP and the optional
+ third one ignored.
+
+ minItems: 2
+ maxItems: 3
+
+ port:
+ type: object
+ additionalProperties: false
+ description: -|
+ Connection to the remote GMSL endpoint are modelled using the OF graph
+ bindings in accordance with the video interface bindings defined in
+ Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+ The device node contains a single "port" child node with a single
+ "endpoint" sub-device.
+
+ properties:
+ endpoint:
+ type: object
+ additionalProperties: false
+
+ properties:
+ remote-endpoint:
+ description: -|
+ phandle to the remote GMSL endpoint sub-node in the remote node
+ port.
+ maxItems: 1
+
+ required:
+ - remote-endpoint
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - port
+
+examples:
+ - |
+ i2c@e66d8000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg = <0 0xe66d8000 0 0x40>;
+
+ camera@31 {
+ compatible = "imi,rdacm20";
+ reg = <0x31>, <0x41>, <0x51>;
+
+ port {
+ rdacm20_out0: endpoint {
+ remote-endpoint = <&max9286_in0>;
+ };
+ };
+ };
+ };
+
+ - |
+ i2c@e66d8000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg = <0 0xe66d8000 0 0x40>;
+
+ camera@31 {
+ compatible = "imi,rdacm21";
+ reg = <0x31>, <0x41>;
+
+ port {
+ rdacm21_out0: endpoint {
+ remote-endpoint = <&max9286_in0>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index d3891386d671..dcbecdc58731 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -453,6 +453,8 @@ patternProperties:
description: Ingenieurburo Fur Ic-Technologie (I/F/I)
"^ilitek,.*":
description: ILI Technology Corporation (ILITEK)
+ "^imi,.*":
+ description: Integrated Micro-Electronics Inc.
"^img,.*":
description: Imagination Technologies Ltd.
"^incircuit,.*":
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* [v8-rc1 20/20] media: i2c: Add RDACM20 driver
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
` (18 preceding siblings ...)
2020-04-16 10:40 ` [v8-rc1 19/20] dt-bindings: media: i2c: Add bindings for IMI RDACM2x Jacopo Mondi
@ 2020-04-16 10:40 ` Jacopo Mondi
2020-04-16 11:08 ` Jacopo Mondi
19 siblings, 1 reply; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 10:40 UTC (permalink / raw)
To: kieran.bingham+renesas, linux-renesas-soc
Cc: Jacopo Mondi, laurent.pinchart, niklas.soderlund, hyunk,
manivannan.sadhasivam, Laurent Pinchart, Niklas Söderlund,
Rob Herring
The RDACM20 is a GMSL camera supporting 1280x800 resolution images
developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271
GMSL serializer.
The GMSL link carries power, control (I2C) and video data over a
single coax cable.
Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
v2:
- Fix MAINTAINERS entry
v3:
- Use new V4L2_MBUS_CSI2_DPHY bus type
- Remove 'always zero' error print
- Fix module description
v5:
- use sleep rather than busy loops for 10 ms delays
- Return ov10635_set_regs directly
- Use devm_kzalloc instead of kzalloc in probe()
- Or in the flags: dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE
- Ensure v4l2_ctrl_handler_free() is called
- rdacm20_probe converted to use .probe_new and drop i2c device id
tables
- Remove rdacm20_g_mbus_config
v7:
- Use new i2c_new_dummy_device API
---
MAINTAINERS | 12 +
drivers/media/i2c/Kconfig | 13 +
drivers/media/i2c/Makefile | 2 +
drivers/media/i2c/max9271.c | 330 ++++++++++++++++++
drivers/media/i2c/max9271.h | 224 ++++++++++++
drivers/media/i2c/rdacm20.c | 668 ++++++++++++++++++++++++++++++++++++
6 files changed, 1249 insertions(+)
create mode 100644 drivers/media/i2c/max9271.c
create mode 100644 drivers/media/i2c/max9271.h
create mode 100644 drivers/media/i2c/rdacm20.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 242ec17644c3..47930cd43ef3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14197,6 +14197,18 @@ S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
F: tools/testing/selftests/rcutorture
+RDACM20 Camera Sensor
+M: Jacopo Mondi <jacopo+renesas@jmondi.org>
+M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
+M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
+M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
+F: drivers/media/i2c/rdacm20.c
+F: drivers/media/i2c/max9271.c
+F: drivers/media/i2c/max9271.h
+
RDC R-321X SoC
M: Florian Fainelli <florian@openwrt.org>
S: Maintained
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5d810f6f230e..e5d6dced20e0 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1156,6 +1156,19 @@ config VIDEO_NOON010PC30
source "drivers/media/i2c/m5mols/Kconfig"
+config VIDEO_RDACM20
+ tristate "IMI RDACM20 camera support"
+ depends on I2C
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ help
+ This driver supports the IMI RDACM20 GMSL camera, used in
+ ADAS systems.
+
+ This camera should be used in conjunction with a GMSL
+ deserialiser such as the MAX9286.
+
config VIDEO_RJ54N1
tristate "Sharp RJ54N1CB0C sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 29379008d624..93baaca8469e 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -119,5 +119,7 @@ obj-$(CONFIG_VIDEO_IMX319) += imx319.o
obj-$(CONFIG_VIDEO_IMX355) += imx355.o
obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
+rdacm20-camera_module-objs := rdacm20.o max9271.o
+obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o
obj-$(CONFIG_SDR_MAX2175) += max2175.o
diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
new file mode 100644
index 000000000000..0fde36e9a8c7
--- /dev/null
+++ b/drivers/media/i2c/max9271.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2020 Jacopo Mondi
+ * Copyright (C) 2017-2020 Kieran Bingham
+ * Copyright (C) 2017-2020 Laurent Pinchart
+ * Copyright (C) 2017-2020 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * This file exports functions to control Maxim MAX9271 GMSL serializer
+ * chip. This is not a self-contained driver, as MAX9271 is usually embedded in
+ * camera modules with at least one image sensor and optional additional
+ * components, such as uController units or ISPs/DSPs.
+ *
+ * Drivers for the camera modules (ie rdacm20/21) are expected to use functions
+ * exported from this library driver to maximize code re-use across.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+
+#include "max9271.h"
+
+static int max9271_read(struct max9271_device *dev, u8 reg)
+{
+ int ret;
+
+ dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
+
+ ret = i2c_smbus_read_byte_data(dev->client, reg);
+ if (ret < 0)
+ dev_dbg(&dev->client->dev,
+ "%s: register 0x%02x read failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
+{
+ int ret;
+
+ dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
+
+ ret = i2c_smbus_write_byte_data(dev->client, reg, val);
+ if (ret < 0)
+ dev_err(&dev->client->dev,
+ "%s: register 0x%02x write failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+/*
+ * max9271_pclk_detect() - Detect valid pixel clock from image sensor
+ *
+ * Wait up to 10ms for a valid pixel clock.
+ *
+ * Returns 0 for success, < 0 for pixel clock not properly detected
+ */
+static int max9271_pclk_detect(struct max9271_device *dev)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < 100; i++) {
+ ret = max9271_read(dev, 0x15);
+ if (ret < 0)
+ return ret;
+
+ if (ret & MAX9271_PCLKDET)
+ return 0;
+
+ usleep_range(50, 100);
+ }
+
+ dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
+ return -EIO;
+}
+
+int max9271_set_serial_link(struct max9271_device *dev, bool enable)
+{
+ int ret;
+ u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
+
+ if (enable) {
+ ret = max9271_pclk_detect(dev);
+ if (ret)
+ return ret;
+
+ val |= MAX9271_SEREN;
+ } else {
+ val |= MAX9271_CLINKEN;
+ }
+
+ /*
+ * The serializer temporarily disables the reverse control channel for
+ * 350µs after starting/stopping the forward serial link, but the
+ * deserializer synchronization time isn't clearly documented.
+ *
+ * According to the serializer datasheet we should wait 3ms, while
+ * according to the deserializer datasheet we should wait 5ms.
+ *
+ * Short delays here appear to show bit-errors in the writes following.
+ * Therefore a conservative delay seems best here.
+ */
+ max9271_write(dev, 0x04, val);
+ usleep_range(5000, 8000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_serial_link);
+
+int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
+{
+ int ret;
+
+ ret = max9271_write(dev, 0x0d, i2c_config);
+ if (ret)
+ return ret;
+
+ /* The delay required after an I2C bus configuration change is not
+ * characterized in the serializer manual. Sleep up to 5msec to
+ * stay safe.
+ */
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_configure_i2c);
+
+int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
+{
+ int ret = max9271_read(dev, 0x08);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Enable or disable reverse channel high threshold to increase
+ * immunity to power supply noise.
+ */
+ max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
+ usleep_range(2000, 2500);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_high_threshold);
+
+int max9271_configure_gmsl_link(struct max9271_device *dev)
+{
+ /*
+ * Configure the GMSL link:
+ *
+ * - Double input mode, high data rate, 24-bit mode
+ * - Latch input data on PCLKIN rising edge
+ * - Enable HS/VS encoding
+ * - 1-bit parity error detection
+ *
+ * TODO: Make the GMSL link configuration parametric.
+ */
+ max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
+ MAX9271_EDC_1BIT_PARITY);
+ usleep_range(5000, 8000);
+
+ /*
+ * Adjust spread spectrum to +4% and auto-detect pixel clock
+ * and serial link rate.
+ */
+ max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
+ MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT);
+ usleep_range(5000, 8000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link);
+
+int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+ int ret = max9271_read(dev, 0x0f);
+ if (ret < 0)
+ return 0;
+
+ ret |= gpio_mask;
+ ret = max9271_write(dev, 0x0f, ret);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_gpios);
+
+int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+ int ret = max9271_read(dev, 0x0f);
+ if (ret < 0)
+ return 0;
+
+ ret &= ~gpio_mask;
+ ret = max9271_write(dev, 0x0f, ret);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_clear_gpios);
+
+int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+ int ret = max9271_read(dev, 0x0f);
+ if (ret < 0)
+ return 0;
+
+ /* BIT(0) reserved: GPO is always enabled. */
+ ret |= gpio_mask | BIT(0);
+ ret = max9271_write(dev, 0x0e, ret);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_enable_gpios);
+
+int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask)
+{
+ int ret = max9271_read(dev, 0x0f);
+ if (ret < 0)
+ return 0;
+
+ /* BIT(0) reserved: GPO cannot be disabled */
+ ret &= (~gpio_mask | BIT(0));
+ ret = max9271_write(dev, 0x0e, ret);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret);
+ return ret;
+ }
+
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_disable_gpios);
+
+int max9271_verify_id(struct max9271_device *dev)
+{
+ int ret;
+
+ ret = max9271_read(dev, 0x1e);
+ if (ret < 0) {
+ dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
+ ret);
+ return ret;
+ }
+
+ if (ret != MAX9271_ID) {
+ dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
+ ret);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_verify_id);
+
+int max9271_set_address(struct max9271_device *dev, u8 addr)
+{
+ int ret;
+
+ ret = max9271_write(dev, 0x00, addr << 1);
+ if (ret < 0) {
+ dev_err(&dev->client->dev,
+ "MAX9271 I2C address change failed (%d)\n", ret);
+ return ret;
+ }
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_address);
+
+int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr)
+{
+ int ret;
+
+ ret = max9271_write(dev, 0x01, addr << 1);
+ if (ret < 0) {
+ dev_err(&dev->client->dev,
+ "MAX9271 deserializer address set failed (%d)\n", ret);
+ return ret;
+ }
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_deserializer_address);
+
+int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest)
+{
+ int ret;
+
+ ret = max9271_write(dev, 0x09, source << 1);
+ if (ret < 0) {
+ dev_err(&dev->client->dev,
+ "MAX9271 I2C translation setup failed (%d)\n", ret);
+ return ret;
+ }
+ usleep_range(3500, 5000);
+
+ ret = max9271_write(dev, 0x0a, dest << 1);
+ if (ret < 0) {
+ dev_err(&dev->client->dev,
+ "MAX9271 I2C translation setup failed (%d)\n", ret);
+ return ret;
+ }
+ usleep_range(3500, 5000);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(max9271_set_translation);
diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271.h
new file mode 100644
index 000000000000..d78fb21441e9
--- /dev/null
+++ b/drivers/media/i2c/max9271.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2017-2020 Jacopo Mondi
+ * Copyright (C) 2017-2020 Kieran Bingham
+ * Copyright (C) 2017-2020 Laurent Pinchart
+ * Copyright (C) 2017-2020 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+#include <linux/i2c.h>
+
+#define MAX9271_DEFAULT_ADDR 0x40
+
+/* Register 0x02 */
+#define MAX9271_SPREAD_SPECT_0 (0 << 5)
+#define MAX9271_SPREAD_SPECT_05 (1 << 5)
+#define MAX9271_SPREAD_SPECT_15 (2 << 5)
+#define MAX9271_SPREAD_SPECT_1 (5 << 5)
+#define MAX9271_SPREAD_SPECT_2 (3 << 5)
+#define MAX9271_SPREAD_SPECT_3 (6 << 5)
+#define MAX9271_SPREAD_SPECT_4 (7 << 5)
+#define MAX9271_R02_RES BIT(4)
+#define MAX9271_PCLK_AUTODETECT (3 << 2)
+#define MAX9271_SERIAL_AUTODETECT (0x03)
+/* Register 0x04 */
+#define MAX9271_SEREN BIT(7)
+#define MAX9271_CLINKEN BIT(6)
+#define MAX9271_PRBSEN BIT(5)
+#define MAX9271_SLEEP BIT(4)
+#define MAX9271_INTTYPE_I2C (0 << 2)
+#define MAX9271_INTTYPE_UART (1 << 2)
+#define MAX9271_INTTYPE_NONE (2 << 2)
+#define MAX9271_REVCCEN BIT(1)
+#define MAX9271_FWDCCEN BIT(0)
+/* Register 0x07 */
+#define MAX9271_DBL BIT(7)
+#define MAX9271_DRS BIT(6)
+#define MAX9271_BWS BIT(5)
+#define MAX9271_ES BIT(4)
+#define MAX9271_HVEN BIT(2)
+#define MAX9271_EDC_1BIT_PARITY (0 << 0)
+#define MAX9271_EDC_6BIT_CRC (1 << 0)
+#define MAX9271_EDC_6BIT_HAMMING (2 << 0)
+/* Register 0x08 */
+#define MAX9271_INVVS BIT(7)
+#define MAX9271_INVHS BIT(6)
+#define MAX9271_REV_LOGAIN BIT(3)
+#define MAX9271_REV_HIVTH BIT(0)
+/* Register 0x09 */
+#define MAX9271_ID 0x09
+/* Register 0x0d */
+#define MAX9271_I2CLOCACK BIT(7)
+#define MAX9271_I2CSLVSH_1046NS_469NS (3 << 5)
+#define MAX9271_I2CSLVSH_938NS_352NS (2 << 5)
+#define MAX9271_I2CSLVSH_469NS_234NS (1 << 5)
+#define MAX9271_I2CSLVSH_352NS_117NS (0 << 5)
+#define MAX9271_I2CMSTBT_837KBPS (7 << 2)
+#define MAX9271_I2CMSTBT_533KBPS (6 << 2)
+#define MAX9271_I2CMSTBT_339KBPS (5 << 2)
+#define MAX9271_I2CMSTBT_173KBPS (4 << 2)
+#define MAX9271_I2CMSTBT_105KBPS (3 << 2)
+#define MAX9271_I2CMSTBT_84KBPS (2 << 2)
+#define MAX9271_I2CMSTBT_28KBPS (1 << 2)
+#define MAX9271_I2CMSTBT_8KBPS (0 << 2)
+#define MAX9271_I2CSLVTO_NONE (3 << 0)
+#define MAX9271_I2CSLVTO_1024US (2 << 0)
+#define MAX9271_I2CSLVTO_256US (1 << 0)
+#define MAX9271_I2CSLVTO_64US (0 << 0)
+/* Register 0x0f */
+#define MAX9271_GPIO5OUT BIT(5)
+#define MAX9271_GPIO4OUT BIT(4)
+#define MAX9271_GPIO3OUT BIT(3)
+#define MAX9271_GPIO2OUT BIT(2)
+#define MAX9271_GPIO1OUT BIT(1)
+#define MAX9271_GPO BIT(0)
+/* Register 0x15 */
+#define MAX9271_PCLKDET BIT(0)
+
+/**
+ * struct max9271_device - max9271 device
+ * @client: The i2c client for the max9271 instance
+ */
+struct max9271_device {
+ struct i2c_client *client;
+};
+
+/**
+ * max9271_set_serial_link() - Enable/disable serial link
+ * @dev: The max9271 device
+ * @enable: Serial link enable/disable flag
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_serial_link(struct max9271_device *dev, bool enable);
+
+/**
+ * max9271_configure_i2c() - Configure I2C bus parameters
+ * @dev: The max9271 device
+ * @i2c_config: The I2C bus configuration bit mask
+ *
+ * Configure MAX9271 I2C interface. The bus configuration provided in the
+ * @i2c_config parameter shall be assembled using bit values defined by the
+ * MAX9271_I2C* macros.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config);
+
+/**
+ * max9271_set_high_threshold() - Enable or disable reverse channel high
+ * threshold
+ * @dev: The max9271 device
+ * @enable: High threshold enable/disable flag
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_high_threshold(struct max9271_device *dev, bool enable);
+
+/**
+ * max9271_configure_gmsl_link() - Configure the GMSL link
+ * @dev: The max9271 device
+ *
+ * FIXME: the GMSL link configuration is currently hardcoded and performed
+ * by programming registers 0x04, 0x07 and 0x02.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_configure_gmsl_link(struct max9271_device *dev);
+
+/**
+ * max9271_set_gpios() - Set gpio lines to physical high value
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to set to high value
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]*
+ * bit values.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_clear_gpios() - Set gpio lines to physical low value
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to set to low value
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]*
+ * bit values.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_enable_gpios() - Enable gpio lines
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to enable
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO*
+ * bit values. GPO line is always enabled by default.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_disable_gpios() - Disable gpio lines
+ * @dev: The max9271 device
+ * @gpio_mask: The mask of gpio lines to disable
+ *
+ * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO*
+ * bit values. GPO line is always enabled by default and cannot be disabled.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask);
+
+/**
+ * max9271_verify_id() - Read and verify MAX9271 id
+ * @dev: The max9271 device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_verify_id(struct max9271_device *dev);
+
+/**
+ * max9271_set_address() - Program a new I2C address
+ * @dev: The max9271 device
+ * @addr: The new I2C address in 7-bit format
+ *
+ * This function only takes care of programming the new I2C address @addr to
+ * in the MAX9271 chip registers, it is responsiblity of the caller to set
+ * the i2c address client to the @addr value to be able to communicate with
+ * the MAX9271 chip using the I2C framework APIs after this function returns.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_address(struct max9271_device *dev, u8 addr);
+
+/**
+ * max9271_set_deserializer_address() - Program the remote deserializer address
+ * @dev: The max9271 device
+ * @addr: The deserializer I2C address in 7-bit format
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr);
+
+/**
+ * max9271_set_translation() - Program I2C address translation
+ * @dev: The max9271 device
+ * @source: The I2C source address
+ * @dest: The I2C destination address
+ *
+ * Program address translation from @source to @dest. This is required to
+ * communicate with local devices that do not support address reprogramming.
+ *
+ * TODO: The device supports translation of two address, this function currently
+ * supports a single one.
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest);
diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
new file mode 100644
index 000000000000..37786998878b
--- /dev/null
+++ b/drivers/media/i2c/rdacm20.c
@@ -0,0 +1,668 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMI RDACM20 GMSL Camera Driver
+ *
+ * Copyright (C) 2017-2020 Jacopo Mondi
+ * Copyright (C) 2017-2020 Kieran Bingham
+ * Copyright (C) 2017-2019 Laurent Pinchart
+ * Copyright (C) 2017-2019 Niklas Söderlund
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ */
+
+/*
+ * The camera is made of an Omnivision OV10635 sensor connected to a Maxim
+ * MAX9271 GMSL serializer.
+ */
+
+#include <linux/delay.h>
+#include <linux/fwnode.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include "max9271.h"
+
+#define OV10635_I2C_ADDRESS 0x30
+
+#define OV10635_SOFTWARE_RESET 0x0103
+#define OV10635_PID 0x300a
+#define OV10635_VER 0x300b
+#define OV10635_SC_CMMN_SCCB_ID 0x300c
+#define OV10635_SC_CMMN_SCCB_ID_SELECT BIT(0)
+#define OV10635_VERSION 0xa635
+
+#define OV10635_WIDTH 1280
+#define OV10635_HEIGHT 800
+#define OV10635_FORMAT MEDIA_BUS_FMT_UYVY8_2X8
+
+/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */
+#define OV10635_HTS 1572
+/* FPS = 29,9998 */
+#define OV10635_VTS 933
+
+/*
+ * As the drivers supports a single MEDIA_BUS_FMT_UYVY8_2X8 format we
+ * can harcode the pixel rate.
+ *
+ * PCLK is fed through the system clock, programmed @88MHz.
+ * MEDIA_BUS_FMT_UYVY8_2X8 format = 2 samples per pixel.
+ *
+ * Pixelrate = PCLK / 2
+ * FPS = (OV10635_VTS * OV10635_HTS) / PixelRate
+ * = 29,9998
+ */
+#define OV10635_PIXEL_RATE (44000000)
+
+static const struct ov10635_reg {
+ u16 reg;
+ u8 val;
+} ov10635_regs_wizard[] = {
+ { 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x3011, 0x42 },
+ { 0x6900, 0x0c }, { 0x6901, 0x19 }, { 0x3503, 0x10 }, { 0x3025, 0x03 },
+ { 0x3003, 0x16 }, { 0x3004, 0x30 }, { 0x3005, 0x40 }, { 0x3006, 0x91 },
+ { 0x3600, 0x74 }, { 0x3601, 0x2b }, { 0x3612, 0x00 }, { 0x3611, 0x67 },
+ { 0x3633, 0xca }, { 0x3602, 0xaf }, { 0x3603, 0x04 }, { 0x3630, 0x28 },
+ { 0x3631, 0x16 }, { 0x3714, 0x10 }, { 0x371d, 0x01 }, { 0x4300, 0x3a },
+ { 0x3007, 0x01 }, { 0x3024, 0x03 }, { 0x3020, 0x0a }, { 0x3702, 0x0d },
+ { 0x3703, 0x20 }, { 0x3704, 0x15 }, { 0x3709, 0xa8 }, { 0x370c, 0xc7 },
+ { 0x370d, 0x80 }, { 0x3712, 0x00 }, { 0x3713, 0x20 }, { 0x3715, 0x04 },
+ { 0x381d, 0x40 }, { 0x381c, 0x00 }, { 0x3822, 0x50 }, { 0x3824, 0x10 },
+ { 0x3815, 0x8c }, { 0x3804, 0x05 }, { 0x3805, 0x1f }, { 0x3800, 0x00 },
+ { 0x3801, 0x00 }, { 0x3806, 0x03 }, { 0x3807, 0x28 }, { 0x3802, 0x00 },
+ { 0x3803, 0x07 }, { 0x3808, 0x05 }, { 0x3809, 0x00 }, { 0x380a, 0x03 },
+ { 0x380b, 0x20 }, { 0x380c, OV10635_HTS >> 8 },
+ { 0x380d, OV10635_HTS & 0xff }, { 0x380e, OV10635_VTS >> 8 },
+ { 0x380f, OV10635_VTS & 0xff }, { 0x3813, 0x02 }, { 0x3811, 0x08 },
+ { 0x381f, 0x0c }, { 0x3819, 0x04 }, { 0x3804, 0x01 }, { 0x3805, 0x00 },
+ { 0x3828, 0x03 }, { 0x3829, 0x10 }, { 0x382a, 0x10 }, { 0x3621, 0x63 },
+ { 0x5005, 0x08 }, { 0x56d5, 0x00 }, { 0x56d6, 0x80 }, { 0x56d7, 0x00 },
+ { 0x56d8, 0x00 }, { 0x56d9, 0x00 }, { 0x56da, 0x80 }, { 0x56db, 0x00 },
+ { 0x56dc, 0x00 }, { 0x56e8, 0x00 }, { 0x56e9, 0x7f }, { 0x56ea, 0x00 },
+ { 0x56eb, 0x7f }, { 0x5100, 0x00 }, { 0x5101, 0x80 }, { 0x5102, 0x00 },
+ { 0x5103, 0x80 }, { 0x5104, 0x00 }, { 0x5105, 0x80 }, { 0x5106, 0x00 },
+ { 0x5107, 0x80 }, { 0x5108, 0x00 }, { 0x5109, 0x00 }, { 0x510a, 0x00 },
+ { 0x510b, 0x00 }, { 0x510c, 0x00 }, { 0x510d, 0x00 }, { 0x510e, 0x00 },
+ { 0x510f, 0x00 }, { 0x5110, 0x00 }, { 0x5111, 0x80 }, { 0x5112, 0x00 },
+ { 0x5113, 0x80 }, { 0x5114, 0x00 }, { 0x5115, 0x80 }, { 0x5116, 0x00 },
+ { 0x5117, 0x80 }, { 0x5118, 0x00 }, { 0x5119, 0x00 }, { 0x511a, 0x00 },
+ { 0x511b, 0x00 }, { 0x511c, 0x00 }, { 0x511d, 0x00 }, { 0x511e, 0x00 },
+ { 0x511f, 0x00 }, { 0x56d0, 0x00 }, { 0x5006, 0x04 }, { 0x5608, 0x05 },
+ { 0x52d7, 0x06 }, { 0x528d, 0x08 }, { 0x5293, 0x12 }, { 0x52d3, 0x12 },
+ { 0x5288, 0x06 }, { 0x5289, 0x20 }, { 0x52c8, 0x06 }, { 0x52c9, 0x20 },
+ { 0x52cd, 0x04 }, { 0x5381, 0x00 }, { 0x5382, 0xff }, { 0x5589, 0x76 },
+ { 0x558a, 0x47 }, { 0x558b, 0xef }, { 0x558c, 0xc9 }, { 0x558d, 0x49 },
+ { 0x558e, 0x30 }, { 0x558f, 0x67 }, { 0x5590, 0x3f }, { 0x5591, 0xf0 },
+ { 0x5592, 0x10 }, { 0x55a2, 0x6d }, { 0x55a3, 0x55 }, { 0x55a4, 0xc3 },
+ { 0x55a5, 0xb5 }, { 0x55a6, 0x43 }, { 0x55a7, 0x38 }, { 0x55a8, 0x5f },
+ { 0x55a9, 0x4b }, { 0x55aa, 0xf0 }, { 0x55ab, 0x10 }, { 0x5581, 0x52 },
+ { 0x5300, 0x01 }, { 0x5301, 0x00 }, { 0x5302, 0x00 }, { 0x5303, 0x0e },
+ { 0x5304, 0x00 }, { 0x5305, 0x0e }, { 0x5306, 0x00 }, { 0x5307, 0x36 },
+ { 0x5308, 0x00 }, { 0x5309, 0xd9 }, { 0x530a, 0x00 }, { 0x530b, 0x0f },
+ { 0x530c, 0x00 }, { 0x530d, 0x2c }, { 0x530e, 0x00 }, { 0x530f, 0x59 },
+ { 0x5310, 0x00 }, { 0x5311, 0x7b }, { 0x5312, 0x00 }, { 0x5313, 0x22 },
+ { 0x5314, 0x00 }, { 0x5315, 0xd5 }, { 0x5316, 0x00 }, { 0x5317, 0x13 },
+ { 0x5318, 0x00 }, { 0x5319, 0x18 }, { 0x531a, 0x00 }, { 0x531b, 0x26 },
+ { 0x531c, 0x00 }, { 0x531d, 0xdc }, { 0x531e, 0x00 }, { 0x531f, 0x02 },
+ { 0x5320, 0x00 }, { 0x5321, 0x24 }, { 0x5322, 0x00 }, { 0x5323, 0x56 },
+ { 0x5324, 0x00 }, { 0x5325, 0x85 }, { 0x5326, 0x00 }, { 0x5327, 0x20 },
+ { 0x5609, 0x01 }, { 0x560a, 0x40 }, { 0x560b, 0x01 }, { 0x560c, 0x40 },
+ { 0x560d, 0x00 }, { 0x560e, 0xfa }, { 0x560f, 0x00 }, { 0x5610, 0xfa },
+ { 0x5611, 0x02 }, { 0x5612, 0x80 }, { 0x5613, 0x02 }, { 0x5614, 0x80 },
+ { 0x5615, 0x01 }, { 0x5616, 0x2c }, { 0x5617, 0x01 }, { 0x5618, 0x2c },
+ { 0x563b, 0x01 }, { 0x563c, 0x01 }, { 0x563d, 0x01 }, { 0x563e, 0x01 },
+ { 0x563f, 0x03 }, { 0x5640, 0x03 }, { 0x5641, 0x03 }, { 0x5642, 0x05 },
+ { 0x5643, 0x09 }, { 0x5644, 0x05 }, { 0x5645, 0x05 }, { 0x5646, 0x05 },
+ { 0x5647, 0x05 }, { 0x5651, 0x00 }, { 0x5652, 0x80 }, { 0x521a, 0x01 },
+ { 0x521b, 0x03 }, { 0x521c, 0x06 }, { 0x521d, 0x0a }, { 0x521e, 0x0e },
+ { 0x521f, 0x12 }, { 0x5220, 0x16 }, { 0x5223, 0x02 }, { 0x5225, 0x04 },
+ { 0x5227, 0x08 }, { 0x5229, 0x0c }, { 0x522b, 0x12 }, { 0x522d, 0x18 },
+ { 0x522f, 0x1e }, { 0x5241, 0x04 }, { 0x5242, 0x01 }, { 0x5243, 0x03 },
+ { 0x5244, 0x06 }, { 0x5245, 0x0a }, { 0x5246, 0x0e }, { 0x5247, 0x12 },
+ { 0x5248, 0x16 }, { 0x524a, 0x03 }, { 0x524c, 0x04 }, { 0x524e, 0x08 },
+ { 0x5250, 0x0c }, { 0x5252, 0x12 }, { 0x5254, 0x18 }, { 0x5256, 0x1e },
+ /* fifo_line_length = 2*hts */
+ { 0x4606, (2 * OV10635_HTS) >> 8 }, { 0x4607, (2 * OV10635_HTS) & 0xff },
+ /* fifo_hsync_start = 2*(hts - xres) */
+ { 0x460a, (2 * (OV10635_HTS - OV10635_WIDTH)) >> 8 },
+ { 0x460b, (2 * (OV10635_HTS - OV10635_WIDTH)) & 0xff },
+ { 0x460c, 0x00 }, { 0x4620, 0x0e },
+ /* BT601: 0x08 is also acceptable as HS/VS mode */
+ { 0x4700, 0x04 }, { 0x4701, 0x00 }, { 0x4702, 0x01 }, { 0x4004, 0x04 },
+ { 0x4005, 0x18 }, { 0x4001, 0x06 }, { 0x4050, 0x22 }, { 0x4051, 0x24 },
+ { 0x4052, 0x02 }, { 0x4057, 0x9c }, { 0x405a, 0x00 }, { 0x4202, 0x02 },
+ { 0x3023, 0x10 }, { 0x0100, 0x01 }, { 0x0100, 0x01 }, { 0x6f10, 0x07 },
+ { 0x6f11, 0x82 }, { 0x6f12, 0x04 }, { 0x6f13, 0x00 }, { 0xd000, 0x19 },
+ { 0xd001, 0xa0 }, { 0xd002, 0x00 }, { 0xd003, 0x01 }, { 0xd004, 0xa9 },
+ { 0xd005, 0xad }, { 0xd006, 0x10 }, { 0xd007, 0x40 }, { 0xd008, 0x44 },
+ { 0xd009, 0x00 }, { 0xd00a, 0x68 }, { 0xd00b, 0x00 }, { 0xd00c, 0x15 },
+ { 0xd00d, 0x00 }, { 0xd00e, 0x00 }, { 0xd00f, 0x00 }, { 0xd040, 0x9c },
+ { 0xd041, 0x21 }, { 0xd042, 0xff }, { 0xd043, 0xf8 }, { 0xd044, 0xd4 },
+ { 0xd045, 0x01 }, { 0xd046, 0x48 }, { 0xd047, 0x00 }, { 0xd048, 0xd4 },
+ { 0xd049, 0x01 }, { 0xd04a, 0x50 }, { 0xd04b, 0x04 }, { 0xd04c, 0x18 },
+ { 0xd04d, 0x60 }, { 0xd04e, 0x00 }, { 0xd04f, 0x01 }, { 0xd050, 0xa8 },
+ { 0xd051, 0x63 }, { 0xd052, 0x02 }, { 0xd053, 0xa4 }, { 0xd054, 0x85 },
+ { 0xd055, 0x43 }, { 0xd056, 0x00 }, { 0xd057, 0x00 }, { 0xd058, 0x18 },
+ { 0xd059, 0x60 }, { 0xd05a, 0x00 }, { 0xd05b, 0x01 }, { 0xd05c, 0xa8 },
+ { 0xd05d, 0x63 }, { 0xd05e, 0x03 }, { 0xd05f, 0xf0 }, { 0xd060, 0x98 },
+ { 0xd061, 0xa3 }, { 0xd062, 0x00 }, { 0xd063, 0x00 }, { 0xd064, 0x8c },
+ { 0xd065, 0x6a }, { 0xd066, 0x00 }, { 0xd067, 0x6e }, { 0xd068, 0xe5 },
+ { 0xd069, 0x85 }, { 0xd06a, 0x18 }, { 0xd06b, 0x00 }, { 0xd06c, 0x10 },
+ { 0xd06d, 0x00 }, { 0xd06e, 0x00 }, { 0xd06f, 0x10 }, { 0xd070, 0x9c },
+ { 0xd071, 0x80 }, { 0xd072, 0x00 }, { 0xd073, 0x03 }, { 0xd074, 0x18 },
+ { 0xd075, 0x60 }, { 0xd076, 0x00 }, { 0xd077, 0x01 }, { 0xd078, 0xa8 },
+ { 0xd079, 0x63 }, { 0xd07a, 0x07 }, { 0xd07b, 0x80 }, { 0xd07c, 0x07 },
+ { 0xd07d, 0xff }, { 0xd07e, 0xf9 }, { 0xd07f, 0x03 }, { 0xd080, 0x8c },
+ { 0xd081, 0x63 }, { 0xd082, 0x00 }, { 0xd083, 0x00 }, { 0xd084, 0xa5 },
+ { 0xd085, 0x6b }, { 0xd086, 0x00 }, { 0xd087, 0xff }, { 0xd088, 0x18 },
+ { 0xd089, 0x80 }, { 0xd08a, 0x00 }, { 0xd08b, 0x01 }, { 0xd08c, 0xa8 },
+ { 0xd08d, 0x84 }, { 0xd08e, 0x01 }, { 0xd08f, 0x04 }, { 0xd090, 0xe1 },
+ { 0xd091, 0x6b }, { 0xd092, 0x58 }, { 0xd093, 0x00 }, { 0xd094, 0x94 },
+ { 0xd095, 0x6a }, { 0xd096, 0x00 }, { 0xd097, 0x70 }, { 0xd098, 0xe1 },
+ { 0xd099, 0x6b }, { 0xd09a, 0x20 }, { 0xd09b, 0x00 }, { 0xd09c, 0x95 },
+ { 0xd09d, 0x6b }, { 0xd09e, 0x00 }, { 0xd09f, 0x00 }, { 0xd0a0, 0xe4 },
+ { 0xd0a1, 0x8b }, { 0xd0a2, 0x18 }, { 0xd0a3, 0x00 }, { 0xd0a4, 0x0c },
+ { 0xd0a5, 0x00 }, { 0xd0a6, 0x00 }, { 0xd0a7, 0x23 }, { 0xd0a8, 0x15 },
+ { 0xd0a9, 0x00 }, { 0xd0aa, 0x00 }, { 0xd0ab, 0x00 }, { 0xd0ac, 0x18 },
+ { 0xd0ad, 0x60 }, { 0xd0ae, 0x80 }, { 0xd0af, 0x06 }, { 0xd0b0, 0xa8 },
+ { 0xd0b1, 0x83 }, { 0xd0b2, 0x40 }, { 0xd0b3, 0x08 }, { 0xd0b4, 0xa8 },
+ { 0xd0b5, 0xe3 }, { 0xd0b6, 0x38 }, { 0xd0b7, 0x2a }, { 0xd0b8, 0xa8 },
+ { 0xd0b9, 0xc3 }, { 0xd0ba, 0x40 }, { 0xd0bb, 0x09 }, { 0xd0bc, 0xa8 },
+ { 0xd0bd, 0xa3 }, { 0xd0be, 0x38 }, { 0xd0bf, 0x29 }, { 0xd0c0, 0x8c },
+ { 0xd0c1, 0x65 }, { 0xd0c2, 0x00 }, { 0xd0c3, 0x00 }, { 0xd0c4, 0xd8 },
+ { 0xd0c5, 0x04 }, { 0xd0c6, 0x18 }, { 0xd0c7, 0x00 }, { 0xd0c8, 0x8c },
+ { 0xd0c9, 0x67 }, { 0xd0ca, 0x00 }, { 0xd0cb, 0x00 }, { 0xd0cc, 0xd8 },
+ { 0xd0cd, 0x06 }, { 0xd0ce, 0x18 }, { 0xd0cf, 0x00 }, { 0xd0d0, 0x18 },
+ { 0xd0d1, 0x60 }, { 0xd0d2, 0x80 }, { 0xd0d3, 0x06 }, { 0xd0d4, 0xa8 },
+ { 0xd0d5, 0xe3 }, { 0xd0d6, 0x67 }, { 0xd0d7, 0x02 }, { 0xd0d8, 0xa9 },
+ { 0xd0d9, 0x03 }, { 0xd0da, 0x67 }, { 0xd0db, 0x03 }, { 0xd0dc, 0xa8 },
+ { 0xd0dd, 0xc3 }, { 0xd0de, 0x3d }, { 0xd0df, 0x05 }, { 0xd0e0, 0x8c },
+ { 0xd0e1, 0x66 }, { 0xd0e2, 0x00 }, { 0xd0e3, 0x00 }, { 0xd0e4, 0xb8 },
+ { 0xd0e5, 0x63 }, { 0xd0e6, 0x00 }, { 0xd0e7, 0x18 }, { 0xd0e8, 0xb8 },
+ { 0xd0e9, 0x63 }, { 0xd0ea, 0x00 }, { 0xd0eb, 0x98 }, { 0xd0ec, 0xbc },
+ { 0xd0ed, 0x03 }, { 0xd0ee, 0x00 }, { 0xd0ef, 0x00 }, { 0xd0f0, 0x10 },
+ { 0xd0f1, 0x00 }, { 0xd0f2, 0x00 }, { 0xd0f3, 0x16 }, { 0xd0f4, 0xb8 },
+ { 0xd0f5, 0x83 }, { 0xd0f6, 0x00 }, { 0xd0f7, 0x19 }, { 0xd0f8, 0x8c },
+ { 0xd0f9, 0x67 }, { 0xd0fa, 0x00 }, { 0xd0fb, 0x00 }, { 0xd0fc, 0xb8 },
+ { 0xd0fd, 0xa4 }, { 0xd0fe, 0x00 }, { 0xd0ff, 0x98 }, { 0xd100, 0xb8 },
+ { 0xd101, 0x83 }, { 0xd102, 0x00 }, { 0xd103, 0x08 }, { 0xd104, 0x8c },
+ { 0xd105, 0x68 }, { 0xd106, 0x00 }, { 0xd107, 0x00 }, { 0xd108, 0xe0 },
+ { 0xd109, 0x63 }, { 0xd10a, 0x20 }, { 0xd10b, 0x04 }, { 0xd10c, 0xe0 },
+ { 0xd10d, 0x65 }, { 0xd10e, 0x18 }, { 0xd10f, 0x00 }, { 0xd110, 0xa4 },
+ { 0xd111, 0x83 }, { 0xd112, 0xff }, { 0xd113, 0xff }, { 0xd114, 0xb8 },
+ { 0xd115, 0x64 }, { 0xd116, 0x00 }, { 0xd117, 0x48 }, { 0xd118, 0xd8 },
+ { 0xd119, 0x07 }, { 0xd11a, 0x18 }, { 0xd11b, 0x00 }, { 0xd11c, 0xd8 },
+ { 0xd11d, 0x08 }, { 0xd11e, 0x20 }, { 0xd11f, 0x00 }, { 0xd120, 0x9c },
+ { 0xd121, 0x60 }, { 0xd122, 0x00 }, { 0xd123, 0x00 }, { 0xd124, 0xd8 },
+ { 0xd125, 0x06 }, { 0xd126, 0x18 }, { 0xd127, 0x00 }, { 0xd128, 0x00 },
+ { 0xd129, 0x00 }, { 0xd12a, 0x00 }, { 0xd12b, 0x08 }, { 0xd12c, 0x15 },
+ { 0xd12d, 0x00 }, { 0xd12e, 0x00 }, { 0xd12f, 0x00 }, { 0xd130, 0x8c },
+ { 0xd131, 0x6a }, { 0xd132, 0x00 }, { 0xd133, 0x76 }, { 0xd134, 0xbc },
+ { 0xd135, 0x23 }, { 0xd136, 0x00 }, { 0xd137, 0x00 }, { 0xd138, 0x13 },
+ { 0xd139, 0xff }, { 0xd13a, 0xff }, { 0xd13b, 0xe6 }, { 0xd13c, 0x18 },
+ { 0xd13d, 0x60 }, { 0xd13e, 0x80 }, { 0xd13f, 0x06 }, { 0xd140, 0x03 },
+ { 0xd141, 0xff }, { 0xd142, 0xff }, { 0xd143, 0xdd }, { 0xd144, 0xa8 },
+ { 0xd145, 0x83 }, { 0xd146, 0x40 }, { 0xd147, 0x08 }, { 0xd148, 0x85 },
+ { 0xd149, 0x21 }, { 0xd14a, 0x00 }, { 0xd14b, 0x00 }, { 0xd14c, 0x85 },
+ { 0xd14d, 0x41 }, { 0xd14e, 0x00 }, { 0xd14f, 0x04 }, { 0xd150, 0x44 },
+ { 0xd151, 0x00 }, { 0xd152, 0x48 }, { 0xd153, 0x00 }, { 0xd154, 0x9c },
+ { 0xd155, 0x21 }, { 0xd156, 0x00 }, { 0xd157, 0x08 }, { 0x6f0e, 0x03 },
+ { 0x6f0f, 0x00 }, { 0x460e, 0x08 }, { 0x460f, 0x01 }, { 0x4610, 0x00 },
+ { 0x4611, 0x01 }, { 0x4612, 0x00 }, { 0x4613, 0x01 },
+ /* 8 bits */
+ { 0x4605, 0x08 },
+ /* Swap data bits order [9:0] -> [0:9] */
+ { 0x4709, 0x10 }, { 0x4608, 0x00 }, { 0x4609, 0x08 }, { 0x6804, 0x00 },
+ { 0x6805, 0x06 }, { 0x6806, 0x00 }, { 0x5120, 0x00 }, { 0x3510, 0x00 },
+ { 0x3504, 0x00 }, { 0x6800, 0x00 }, { 0x6f0d, 0x01 },
+ /* PCLK falling edge */
+ { 0x4708, 0x01 }, { 0x5000, 0xff }, { 0x5001, 0xbf }, { 0x5002, 0x7e },
+ { 0x503d, 0x00 }, { 0xc450, 0x01 }, { 0xc452, 0x04 }, { 0xc453, 0x00 },
+ { 0xc454, 0x00 }, { 0xc455, 0x01 }, { 0xc456, 0x01 }, { 0xc457, 0x00 },
+ { 0xc458, 0x00 }, { 0xc459, 0x00 }, { 0xc45b, 0x00 }, { 0xc45c, 0x01 },
+ { 0xc45d, 0x00 }, { 0xc45e, 0x00 }, { 0xc45f, 0x00 }, { 0xc460, 0x00 },
+ { 0xc461, 0x01 }, { 0xc462, 0x01 }, { 0xc464, 0x03 }, { 0xc465, 0x00 },
+ { 0xc466, 0x8a }, { 0xc467, 0x00 }, { 0xc468, 0x86 }, { 0xc469, 0x00 },
+ { 0xc46a, 0x40 }, { 0xc46b, 0x50 }, { 0xc46c, 0x30 }, { 0xc46d, 0x28 },
+ { 0xc46e, 0x60 }, { 0xc46f, 0x40 }, { 0xc47c, 0x01 }, { 0xc47d, 0x38 },
+ { 0xc47e, 0x00 }, { 0xc47f, 0x00 }, { 0xc480, 0x00 }, { 0xc481, 0xff },
+ { 0xc482, 0x00 }, { 0xc483, 0x40 }, { 0xc484, 0x00 }, { 0xc485, 0x18 },
+ { 0xc486, 0x00 }, { 0xc487, 0x18 },
+ { 0xc488, (OV10635_VTS - 8) * 16 >> 8},
+ { 0xc489, (OV10635_VTS - 8) * 16 & 0xff},
+ { 0xc48a, (OV10635_VTS - 8) * 16 >> 8},
+ { 0xc48b, (OV10635_VTS - 8) * 16 & 0xff}, { 0xc48c, 0x00 },
+ { 0xc48d, 0x04 }, { 0xc48e, 0x00 }, { 0xc48f, 0x04 }, { 0xc490, 0x03 },
+ { 0xc492, 0x20 }, { 0xc493, 0x08 }, { 0xc498, 0x02 }, { 0xc499, 0x00 },
+ { 0xc49a, 0x02 }, { 0xc49b, 0x00 }, { 0xc49c, 0x02 }, { 0xc49d, 0x00 },
+ { 0xc49e, 0x02 }, { 0xc49f, 0x60 }, { 0xc4a0, 0x03 }, { 0xc4a1, 0x00 },
+ { 0xc4a2, 0x04 }, { 0xc4a3, 0x00 }, { 0xc4a4, 0x00 }, { 0xc4a5, 0x10 },
+ { 0xc4a6, 0x00 }, { 0xc4a7, 0x40 }, { 0xc4a8, 0x00 }, { 0xc4a9, 0x80 },
+ { 0xc4aa, 0x0d }, { 0xc4ab, 0x00 }, { 0xc4ac, 0x0f }, { 0xc4ad, 0xc0 },
+ { 0xc4b4, 0x01 }, { 0xc4b5, 0x01 }, { 0xc4b6, 0x00 }, { 0xc4b7, 0x01 },
+ { 0xc4b8, 0x00 }, { 0xc4b9, 0x01 }, { 0xc4ba, 0x01 }, { 0xc4bb, 0x00 },
+ { 0xc4bc, 0x01 }, { 0xc4bd, 0x60 }, { 0xc4be, 0x02 }, { 0xc4bf, 0x33 },
+ { 0xc4c8, 0x03 }, { 0xc4c9, 0xd0 }, { 0xc4ca, 0x0e }, { 0xc4cb, 0x00 },
+ { 0xc4cc, 0x0e }, { 0xc4cd, 0x51 }, { 0xc4ce, 0x0e }, { 0xc4cf, 0x51 },
+ { 0xc4d0, 0x04 }, { 0xc4d1, 0x80 }, { 0xc4e0, 0x04 }, { 0xc4e1, 0x02 },
+ { 0xc4e2, 0x01 }, { 0xc4e4, 0x10 }, { 0xc4e5, 0x20 }, { 0xc4e6, 0x30 },
+ { 0xc4e7, 0x40 }, { 0xc4e8, 0x50 }, { 0xc4e9, 0x60 }, { 0xc4ea, 0x70 },
+ { 0xc4eb, 0x80 }, { 0xc4ec, 0x90 }, { 0xc4ed, 0xa0 }, { 0xc4ee, 0xb0 },
+ { 0xc4ef, 0xc0 }, { 0xc4f0, 0xd0 }, { 0xc4f1, 0xe0 }, { 0xc4f2, 0xf0 },
+ { 0xc4f3, 0x80 }, { 0xc4f4, 0x00 }, { 0xc4f5, 0x20 }, { 0xc4f6, 0x02 },
+ { 0xc4f7, 0x00 }, { 0xc4f8, 0x00 }, { 0xc4f9, 0x00 }, { 0xc4fa, 0x00 },
+ { 0xc4fb, 0x01 }, { 0xc4fc, 0x01 }, { 0xc4fd, 0x00 }, { 0xc4fe, 0x04 },
+ { 0xc4ff, 0x02 }, { 0xc500, 0x48 }, { 0xc501, 0x74 }, { 0xc502, 0x58 },
+ { 0xc503, 0x80 }, { 0xc504, 0x05 }, { 0xc505, 0x80 }, { 0xc506, 0x03 },
+ { 0xc507, 0x80 }, { 0xc508, 0x01 }, { 0xc509, 0xc0 }, { 0xc50a, 0x01 },
+ { 0xc50b, 0xa0 }, { 0xc50c, 0x01 }, { 0xc50d, 0x2c }, { 0xc50e, 0x01 },
+ { 0xc50f, 0x0a }, { 0xc510, 0x00 }, { 0xc511, 0x00 }, { 0xc512, 0xe5 },
+ { 0xc513, 0x14 }, { 0xc514, 0x04 }, { 0xc515, 0x00 }, { 0xc518, OV10635_VTS >> 8},
+ { 0xc519, OV10635_VTS & 0xff}, { 0xc51a, OV10635_HTS >> 8},
+ { 0xc51b, OV10635_HTS & 0xff}, { 0xc2e0, 0x00 }, { 0xc2e1, 0x51 },
+ { 0xc2e2, 0x00 }, { 0xc2e3, 0xd6 }, { 0xc2e4, 0x01 }, { 0xc2e5, 0x5e },
+ { 0xc2e9, 0x01 }, { 0xc2ea, 0x7a }, { 0xc2eb, 0x90 }, { 0xc2ed, 0x00 },
+ { 0xc2ee, 0x7a }, { 0xc2ef, 0x64 }, { 0xc308, 0x00 }, { 0xc309, 0x00 },
+ { 0xc30a, 0x00 }, { 0xc30c, 0x00 }, { 0xc30d, 0x01 }, { 0xc30e, 0x00 },
+ { 0xc30f, 0x00 }, { 0xc310, 0x01 }, { 0xc311, 0x60 }, { 0xc312, 0xff },
+ { 0xc313, 0x08 }, { 0xc314, 0x01 }, { 0xc315, 0x00 }, { 0xc316, 0xff },
+ { 0xc317, 0x0b }, { 0xc318, 0x00 }, { 0xc319, 0x0c }, { 0xc31a, 0x00 },
+ { 0xc31b, 0xe0 }, { 0xc31c, 0x00 }, { 0xc31d, 0x14 }, { 0xc31e, 0x00 },
+ { 0xc31f, 0xc5 }, { 0xc320, 0xff }, { 0xc321, 0x4b }, { 0xc322, 0xff },
+ { 0xc323, 0xf0 }, { 0xc324, 0xff }, { 0xc325, 0xe8 }, { 0xc326, 0x00 },
+ { 0xc327, 0x46 }, { 0xc328, 0xff }, { 0xc329, 0xd2 }, { 0xc32a, 0xff },
+ { 0xc32b, 0xe4 }, { 0xc32c, 0xff }, { 0xc32d, 0xbb }, { 0xc32e, 0x00 },
+ { 0xc32f, 0x61 }, { 0xc330, 0xff }, { 0xc331, 0xf9 }, { 0xc332, 0x00 },
+ { 0xc333, 0xd9 }, { 0xc334, 0x00 }, { 0xc335, 0x2e }, { 0xc336, 0x00 },
+ { 0xc337, 0xb1 }, { 0xc338, 0xff }, { 0xc339, 0x64 }, { 0xc33a, 0xff },
+ { 0xc33b, 0xeb }, { 0xc33c, 0xff }, { 0xc33d, 0xe8 }, { 0xc33e, 0x00 },
+ { 0xc33f, 0x48 }, { 0xc340, 0xff }, { 0xc341, 0xd0 }, { 0xc342, 0xff },
+ { 0xc343, 0xed }, { 0xc344, 0xff }, { 0xc345, 0xad }, { 0xc346, 0x00 },
+ { 0xc347, 0x66 }, { 0xc348, 0x01 }, { 0xc349, 0x00 }, { 0x6700, 0x04 },
+ { 0x6701, 0x7b }, { 0x6702, 0xfd }, { 0x6703, 0xf9 }, { 0x6704, 0x3d },
+ { 0x6705, 0x71 }, { 0x6706, 0x78 }, { 0x6708, 0x05 }, { 0x6f06, 0x6f },
+ { 0x6f07, 0x00 }, { 0x6f0a, 0x6f }, { 0x6f0b, 0x00 }, { 0x6f00, 0x03 },
+ { 0xc34c, 0x01 }, { 0xc34d, 0x00 }, { 0xc34e, 0x46 }, { 0xc34f, 0x55 },
+ { 0xc350, 0x00 }, { 0xc351, 0x40 }, { 0xc352, 0x00 }, { 0xc353, 0xff },
+ { 0xc354, 0x04 }, { 0xc355, 0x08 }, { 0xc356, 0x01 }, { 0xc357, 0xef },
+ { 0xc358, 0x30 }, { 0xc359, 0x01 }, { 0xc35a, 0x64 }, { 0xc35b, 0x46 },
+ { 0xc35c, 0x00 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+ { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+ { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+ { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+ { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+ { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
+ { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0xc261, 0x01 },
+ { 0x301b, 0xf0 }, { 0x301c, 0xf0 }, { 0x301a, 0xf0 }, { 0x6f00, 0xc3 },
+ { 0xc46a, 0x30 }, { 0xc46d, 0x20 }, { 0xc464, 0x84 }, { 0xc465, 0x00 },
+ { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, { 0x381c, 0x00 }, { 0x381d, 0x40 },
+ { 0xc454, 0x01 }, { 0x6f00, 0xc3 }, { 0xc454, 0x00 }, { 0xc4b1, 0x02 },
+ { 0xc4b2, 0x01 }, { 0xc4b3, 0x03 }, { 0x6f00, 0x03 }, { 0x6f00, 0x43 },
+ /* enable FSIN (FRAMESYNC input) functionality */
+ { 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
+ { 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
+ { 0x3834, OV10635_VTS >> 8 }, { 0x3835, OV10635_VTS & 0xff },
+ { 0x302e, 0x01 },
+};
+
+struct rdacm20_device {
+ struct device *dev;
+ struct max9271_device *serializer;
+ struct i2c_client *sensor;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrls;
+ u32 addrs[2];
+};
+
+static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct rdacm20_device, sd);
+}
+
+static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
+{
+ return sd_to_rdacm20(i2c_get_clientdata(client));
+}
+
+static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
+{
+ u8 buf[2] = { reg >> 8, reg & 0xff };
+ int ret;
+
+ ret = i2c_master_send(dev->sensor, buf, 2);
+ if (ret != 2) {
+ dev_dbg(dev->dev, "%s: register 0x%04x write failed (%d)\n",
+ __func__, reg, ret);
+ return ret;
+ }
+
+ ret = i2c_master_recv(dev->sensor, buf, 2);
+ if (ret < 0) {
+ dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n",
+ __func__, reg, ret);
+ return ret;
+ }
+
+ return (buf[0] << 8) | buf[1];
+}
+
+static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
+{
+ u8 buf[3] = { reg >> 8, reg & 0xff, val };
+ int ret;
+
+ dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
+
+ ret = i2c_master_send(dev->sensor, buf, 3);
+ return ret < 0 ? ret : 0;
+}
+
+static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
+{
+ int ret;
+
+ ret = __ov10635_write(dev, reg, val);
+ if (ret < 0)
+ dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n",
+ __func__, reg, ret);
+
+ return ret;
+}
+
+static int ov10635_set_regs(struct rdacm20_device *dev,
+ const struct ov10635_reg *regs,
+ unsigned int nr_regs)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < nr_regs; i++) {
+ ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
+ if (ret) {
+ dev_err(dev->dev,
+ "%s: register %u (0x%04x) write failed (%d)\n",
+ __func__, i, regs[i].reg, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct rdacm20_device *dev = sd_to_rdacm20(sd);
+
+ return max9271_set_serial_link(dev->serializer, enable);
+}
+
+static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad || code->index > 0)
+ return -EINVAL;
+
+ code->code = OV10635_FORMAT;
+
+ return 0;
+}
+
+static int rdacm20_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *mf = &format->format;
+
+ if (format->pad)
+ return -EINVAL;
+
+ mf->width = OV10635_WIDTH;
+ mf->height = OV10635_HEIGHT;
+ mf->code = OV10635_FORMAT;
+ mf->colorspace = V4L2_COLORSPACE_RAW;
+ mf->field = V4L2_FIELD_NONE;
+ mf->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ mf->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ mf->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops rdacm20_video_ops = {
+ .s_stream = rdacm20_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
+ .enum_mbus_code = rdacm20_enum_mbus_code,
+ .get_fmt = rdacm20_get_fmt,
+ .set_fmt = rdacm20_get_fmt,
+};
+
+static struct v4l2_subdev_ops rdacm20_subdev_ops = {
+ .video = &rdacm20_video_ops,
+ .pad = &rdacm20_subdev_pad_ops,
+};
+
+static int rdacm20_initialize(struct rdacm20_device *dev)
+{
+ unsigned int retry = 3;
+ int ret;
+
+ /* Verify communication with the MAX9271: ping to wakeup. */
+ dev->serializer->client->addr = MAX9271_DEFAULT_ADDR;
+ i2c_smbus_read_byte(dev->serializer->client);
+
+ /* Serial link disabled during config as it needs a valid pixel clock. */
+ ret = max9271_set_serial_link(dev->serializer, false);
+ if (ret)
+ return ret;
+
+ /*
+ * Ensure that we have a good link configuration before attempting to
+ * identify the device.
+ */
+ max9271_configure_i2c(dev->serializer, MAX9271_I2CSLVSH_469NS_234NS |
+ MAX9271_I2CSLVTO_1024US |
+ MAX9271_I2CMSTBT_105KBPS);
+
+ max9271_configure_gmsl_link(dev->serializer);
+
+ ret = max9271_verify_id(dev->serializer);
+ if (ret < 0)
+ return ret;
+
+ ret = max9271_set_address(dev->serializer, dev->addrs[0]);
+ if (ret < 0)
+ return ret;
+ dev->serializer->client->addr = dev->addrs[0];
+
+ /*
+ * Reset the sensor by cycling the OV10635 reset signal connected to the
+ * MAX9271 GPIO1 and verify communication with the OV10635.
+ */
+ max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT);
+ mdelay(10);
+ max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT);
+ mdelay(10);
+
+again:
+ ret = ov10635_read16(dev, OV10635_PID);
+ if (ret < 0) {
+ if (retry--)
+ goto again;
+
+ dev_err(dev->dev, "OV10635 ID read failed (%d)\n",
+ ret);
+ return -ENXIO;
+ }
+
+ if (ret != OV10635_VERSION) {
+ if (retry--)
+ goto again;
+
+ dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n",
+ ret);
+ return -ENXIO;
+ }
+
+ /* Change the sensor I2C address. */
+ ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
+ (dev->addrs[1] << 1) |
+ OV10635_SC_CMMN_SCCB_ID_SELECT);
+ if (ret < 0) {
+ dev_err(dev->dev,
+ "OV10635 I2C address change failed (%d)\n", ret);
+ return ret;
+ }
+ dev->sensor->addr = dev->addrs[1];
+ usleep_range(3500, 5000);
+
+ /* Program the 0V10635 initial configuration. */
+ ret = ov10635_set_regs(dev, ov10635_regs_wizard,
+ ARRAY_SIZE(ov10635_regs_wizard));
+ if (ret)
+ return ret;
+
+ dev_info(dev->dev, "Identified MAX9271 + OV10635 device\n");
+
+ return 0;
+}
+
+static int rdacm20_probe(struct i2c_client *client)
+{
+ struct rdacm20_device *dev;
+ struct fwnode_handle *ep;
+ int ret;
+
+ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ dev->dev = &client->dev;
+
+ dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer),
+ GFP_KERNEL);
+ if (!dev->serializer)
+ return -ENOMEM;
+
+ dev->serializer->client = client;
+
+ ret = of_property_read_u32_array(client->dev.of_node, "reg",
+ dev->addrs, 2);
+ if (ret < 0) {
+ dev_err(dev->dev, "Invalid DT reg property: %d\n", ret);
+ return -EINVAL;
+ }
+
+ /* Create the dummy I2C client for the sensor. */
+ dev->sensor = i2c_new_dummy_device(client->adapter,
+ OV10635_I2C_ADDRESS);
+ if (IS_ERR(dev->sensor)) {
+ ret = PTR_ERR(dev->sensor);
+ goto error;
+ }
+
+ /* Initialize the hardware. */
+ ret = rdacm20_initialize(dev);
+ if (ret < 0)
+ goto error;
+
+ /* Initialize and register the subdevice. */
+ v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
+ dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ v4l2_ctrl_handler_init(&dev->ctrls, 1);
+ v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE,
+ OV10635_PIXEL_RATE, OV10635_PIXEL_RATE, 1,
+ OV10635_PIXEL_RATE);
+ dev->sd.ctrl_handler = &dev->ctrls;
+
+ ret = dev->ctrls.error;
+ if (ret)
+ goto error_free_ctrls;
+
+ dev->pad.flags = MEDIA_PAD_FL_SOURCE;
+ dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
+ if (ret < 0)
+ goto error_free_ctrls;
+
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
+ if (!ep) {
+ dev_err(&client->dev,
+ "Unable to get endpoint in node %pOF\n",
+ client->dev.of_node);
+ ret = -ENOENT;
+ goto error_free_ctrls;
+ }
+ dev->sd.fwnode = ep;
+
+ ret = v4l2_async_register_subdev(&dev->sd);
+ if (ret)
+ goto error_put_node;
+
+ return 0;
+
+error_put_node:
+ fwnode_handle_put(ep);
+error_free_ctrls:
+ v4l2_ctrl_handler_free(&dev->ctrls);
+error:
+ media_entity_cleanup(&dev->sd.entity);
+ if (dev->sensor)
+ i2c_unregister_device(dev->sensor);
+
+ dev_err(&client->dev, "probe failed\n");
+
+ return ret;
+}
+
+static int rdacm20_remove(struct i2c_client *client)
+{
+ struct rdacm20_device *dev = i2c_to_rdacm20(client);
+
+ fwnode_handle_put(dev->sd.fwnode);
+ v4l2_async_unregister_subdev(&dev->sd);
+ v4l2_ctrl_handler_free(&dev->ctrls);
+ media_entity_cleanup(&dev->sd.entity);
+ i2c_unregister_device(dev->sensor);
+
+ return 0;
+}
+
+static void rdacm20_shutdown(struct i2c_client *client)
+{
+ struct rdacm20_device *dev = i2c_to_rdacm20(client);
+
+ /* make sure stream off during shutdown (reset/reboot) */
+ rdacm20_s_stream(&dev->sd, 0);
+}
+
+static const struct of_device_id rdacm20_of_ids[] = {
+ { .compatible = "imi,rdacm20", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
+
+static struct i2c_driver rdacm20_i2c_driver = {
+ .driver = {
+ .name = "rdacm20",
+ .of_match_table = rdacm20_of_ids,
+ },
+ .probe_new = rdacm20_probe,
+ .remove = rdacm20_remove,
+ .shutdown = rdacm20_shutdown,
+};
+
+module_i2c_driver(rdacm20_i2c_driver);
+
+MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_LICENSE("GPL");
--
2.26.0
^ permalink raw reply related [flat|nested] 29+ messages in thread
* Re: [v8-rc1 14/20] squash! max9286: Calculate pixel rate
2020-04-16 10:40 ` [v8-rc1 14/20] squash! max9286: Calculate pixel rate Jacopo Mondi
@ 2020-04-16 10:45 ` Kieran Bingham
2020-04-16 11:34 ` Jacopo Mondi
0 siblings, 1 reply; 29+ messages in thread
From: Kieran Bingham @ 2020-04-16 10:45 UTC (permalink / raw)
To: Jacopo Mondi, linux-renesas-soc
Cc: laurent.pinchart, niklas.soderlund, hyunk, manivannan.sadhasivam
Hi Jacopo,
There we go - looks like a viable solution to me.
I wonder/suspect if this is the issue the BSP team were having some
weeks ago too?
Anyway, I'm certainly happy with this.
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
On 16/04/2020 11:40, Jacopo Mondi wrote:
> Calculate the CSI-2 transmitter pixel rate using the one reported from
> sources.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> ---
> drivers/media/i2c/max9286.c | 50 +++++++++++++++++++++++++++++--------
> 1 file changed, 40 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> index 2d71205a1aad..3ef74ba10074 100644
> --- a/drivers/media/i2c/max9286.c
> +++ b/drivers/media/i2c/max9286.c
> @@ -430,9 +430,46 @@ static int max9286_check_config_link(struct max9286_priv *priv,
> * V4L2 Subdev
> */
>
> -static int max9286_set_pixelrate(struct max9286_priv *priv, s64 rate)
> +static int max9286_set_pixelrate(struct max9286_priv *priv)
> {
> - return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, rate);
> + struct max9286_source *source = NULL;
> + u64 pixelrate = 0;
> +
> + for_each_source(priv, source) {
> + struct v4l2_ctrl *ctrl;
> + u64 source_rate = 0;
> +
> + /* Pixel rate is mandatory to be reported by sources. */
> + ctrl = v4l2_ctrl_find(source->sd->ctrl_handler,
> + V4L2_CID_PIXEL_RATE);
> + if (!ctrl) {
> + pixelrate = 0;
> + break;
> + }
> +
> + /* All source must report the same pixel rate. */
> + source_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
> + if (!pixelrate) {
> + pixelrate = source_rate;
> + } else if (pixelrate != source_rate) {
> + dev_err(&priv->client->dev,
> + "Unable to calculate pixel rate\n");
Hrm ... I would report "Source pixel rates are different\n"
... but I don't think that's a big deal.
> + return -EINVAL;
> + }
> + }
> +
> + if (!pixelrate) {
> + dev_err(&priv->client->dev,
> + "No pixel rate control available in sources\n");
> + return -EINVAL;
> + }
> +
> + /*
> + * The CSI-2 transmitter pixel rate is the single source rate multiplied
> + * by the number of available sources.
> + */
> + return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
> + pixelrate * priv->nsources);
> }
>
> static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> @@ -496,7 +533,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> */
> max9286_configure_i2c(priv, false);
>
> - return 0;
> + return max9286_set_pixelrate(priv);
Aha - at first I thought this would call for every bind event, but we
already filter so this tail end of the function only executes on the
last bind :-)
> }
>
> static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> @@ -670,7 +707,6 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
> {
> struct max9286_priv *priv = sd_to_max9286(sd);
> struct v4l2_mbus_framefmt *cfg_fmt;
> - s64 pixelrate;
>
> if (format->pad >= MAX9286_SRC_PAD)
> return -EINVAL;
> @@ -695,12 +731,6 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
> *cfg_fmt = format->format;
> mutex_unlock(&priv->mutex);
>
> - /* Update pixel rate for the CSI2 receiver */
> - pixelrate = cfg_fmt->width * cfg_fmt->height
> - * priv->nsources * 30 /*FPS*/;
> -
> - max9286_set_pixelrate(priv, pixelrate);
> -
> return 0;
> }
>
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [v8-rc1 15/20] squash! max9286: Disable overlap window
2020-04-16 10:40 ` [v8-rc1 15/20] squash! max9286: Disable overlap window Jacopo Mondi
@ 2020-04-16 10:50 ` Kieran Bingham
2020-04-16 11:31 ` Jacopo Mondi
0 siblings, 1 reply; 29+ messages in thread
From: Kieran Bingham @ 2020-04-16 10:50 UTC (permalink / raw)
To: Jacopo Mondi, linux-renesas-soc
Cc: laurent.pinchart, niklas.soderlund, hyunk, manivannan.sadhasivam
Hi Jacopo,
On 16/04/2020 11:40, Jacopo Mondi wrote:
> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>
> ---
> v2:
> [Jacopo]
> - Write register 0x63 and 0x64 directly as going through the function
> breaks RDACM21 operations
> ---
> drivers/media/i2c/max9286.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>
> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> index 3ef74ba10074..e0c637d4a7de 100644
> --- a/drivers/media/i2c/max9286.c
> +++ b/drivers/media/i2c/max9286.c
> @@ -966,6 +966,18 @@ static int max9286_setup(struct max9286_priv *priv)
> max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
> MAX9286_HVSRC_D14);
>
> + /*
> + * The overlap window seems to provide additional validation by tracking
> + * the delay between vsync and frame sync, generating an error if the
> + * delay is bigger than the programmed window, though it's not yet clear
> + * what value should be set.
> + *
> + * As it's an optional value and can be disabled, we do so by setting
> + * a 0 overlap value.
Are you happy to add the following as part of the removal of the function?
*
* The overlap window is a 13 bit value, and register 0x64 is
* shared with ENFSINLAST in BIT(5) which is also set zero.
*
> + */
> + max9286_write(priv, 0x63, 0);
> + max9286_write(priv, 0x64, 0);
> +
> /*
> * Wait for 2ms to allow the link to resynchronize after the
> * configuration change.
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [v8-rc1 03/20] squash!: max9286: Rebase on latest media/master
2020-04-16 10:40 ` [v8-rc1 03/20] squash!: max9286: Rebase on latest media/master Jacopo Mondi
@ 2020-04-16 10:54 ` Kieran Bingham
0 siblings, 0 replies; 29+ messages in thread
From: Kieran Bingham @ 2020-04-16 10:54 UTC (permalink / raw)
To: Jacopo Mondi, linux-renesas-soc
Cc: laurent.pinchart, niklas.soderlund, hyunk, manivannan.sadhasivam
Hi Jacopo,
On 16/04/2020 11:40, Jacopo Mondi wrote:
> Adapt Kconfig to latest upstream changes
> Fixes: 32a363d0b0b1 ("media: Kconfig files: use select for V4L2 subdevs and MC")
Aha, I hadn't hit this yet, I guess this is part of the move to the
/next/ linux-media/master :-D
Thanks.
Oh - I see - it's just a change in 'policy'.
Well - it matches the tree so ...
Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> ---
> drivers/media/i2c/Kconfig | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 04186787a2f0..5d810f6f230e 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -467,8 +467,9 @@ config VIDEO_VPX3220
> config VIDEO_MAX9286
> tristate "Maxim MAX9286 GMSL deserializer support"
> depends on I2C && I2C_MUX
> - depends on VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER
> select V4L2_FWNODE
> + select VIDEO_V4L2_SUBDEV_API
> + select MEDIA_CONTROLLER
> help
> This driver supports the Maxim MAX9286 GMSL deserializer.
>
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [v8-rc1 20/20] media: i2c: Add RDACM20 driver
2020-04-16 10:40 ` [v8-rc1 20/20] media: i2c: Add RDACM20 driver Jacopo Mondi
@ 2020-04-16 11:08 ` Jacopo Mondi
0 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 11:08 UTC (permalink / raw)
To: Jacopo Mondi
Cc: kieran.bingham+renesas, linux-renesas-soc, laurent.pinchart,
niklas.soderlund, hyunk, manivannan.sadhasivam, Laurent Pinchart,
Niklas Söderlund, Rob Herring
Hello again,
forgot to copy&paste the patch changelog.
On Thu, Apr 16, 2020 at 12:40:52PM +0200, Jacopo Mondi wrote:
> The RDACM20 is a GMSL camera supporting 1280x800 resolution images
> developed by IMI based on an Omnivision 10635 sensor and a Maxim MAX9271
> GMSL serializer.
>
> The GMSL link carries power, control (I2C) and video data over a
> single coax cable.
>
> Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> Reviewed-by: Rob Herring <robh@kernel.org>
>
> ---
> v2:
> - Fix MAINTAINERS entry
>
> v3:
> - Use new V4L2_MBUS_CSI2_DPHY bus type
> - Remove 'always zero' error print
> - Fix module description
>
> v5:
> - use sleep rather than busy loops for 10 ms delays
> - Return ov10635_set_regs directly
> - Use devm_kzalloc instead of kzalloc in probe()
> - Or in the flags: dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE
> - Ensure v4l2_ctrl_handler_free() is called
> - rdacm20_probe converted to use .probe_new and drop i2c device id
> tables
> - Remove rdacm20_g_mbus_config
>
> v7:
> - Use new i2c_new_dummy_device API
v8:
- Almost a new version, main changes are:
- Split max9271 out
- Rework bindings to yaml format and include forthcoming RDACM21
- Remove unecessary header file
- Add pixel rate calculation
- Drop unused fields and dead code
- Rebase on media-master v5.7-rc1
- A detailed list of changes provided as fixup patches is avilable at
git://jmondi.org/linux #gmsl/jmondi/media-master/max9286-v8+old-rdacm20+split
which includes the following list of patches:
squash!: rdacm20: Report pixel rate
squash!: rdacm20: Force sensor ID re-read
squash!: rdamc20: Check register programming result
squash!: rdacm20: Update to use the new max9271 APIs
squash!: rdacm20: Start with serial link disabled
squash!: rdacm20: Hard reset the sensor
squash!: rdacm20: Cache i2c addresses
squash!: rdacm20: Adjust to use new max9271 API
squash!: rdacm20: Drop unused format definition
squash!: rdacm20: Refresh file header
squash!: max9271: Add two functions to control addresses
squash!: max9271: Do not rewrite i2c address
squash!: max9271: Improve gpio handling
squash!: max9271: Rework core functions
squash!: max9271 refresh
squash!: rdacm20: Update maintainers file
squash!: rdacm20: Remove unsued/duplicated defines
squash!: rdacm20: Remove header file
squash!: rdacm20: Move content of header into C file
squash!: rdacm20: Reorganize register table
squash!: rdacm20: Fix subdevice flag creation
squash!: rdacm20: Drop i2c id table
squash!: rdacm20: Release control handler
squash!: rdacm20: Better handle i2c dummy error
squash!: rdacm20: Use devm_kzalloc
squash!: rdacm20: Use probe_new()
squash!: rdacm20: Drop deprecated g_mbus_config
media: i2c: rdacm20: Break max9271 out from rdacm20
squash!: rdacm20: Rebase on latest media/maste
media: i2c: Add RDACM20 driver
dt-bindings: media: i2c: Add bindings for IMI RDACM2x
> ---
> MAINTAINERS | 12 +
> drivers/media/i2c/Kconfig | 13 +
> drivers/media/i2c/Makefile | 2 +
> drivers/media/i2c/max9271.c | 330 ++++++++++++++++++
> drivers/media/i2c/max9271.h | 224 ++++++++++++
> drivers/media/i2c/rdacm20.c | 668 ++++++++++++++++++++++++++++++++++++
> 6 files changed, 1249 insertions(+)
> create mode 100644 drivers/media/i2c/max9271.c
> create mode 100644 drivers/media/i2c/max9271.h
> create mode 100644 drivers/media/i2c/rdacm20.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 242ec17644c3..47930cd43ef3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14197,6 +14197,18 @@ S: Supported
> T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
> F: tools/testing/selftests/rcutorture
>
> +RDACM20 Camera Sensor
> +M: Jacopo Mondi <jacopo+renesas@jmondi.org>
> +M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> +M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> +M: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
> +L: linux-media@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/media/i2c/imi,rdacm2x-gmsl.yaml
> +F: drivers/media/i2c/rdacm20.c
> +F: drivers/media/i2c/max9271.c
> +F: drivers/media/i2c/max9271.h
> +
> RDC R-321X SoC
> M: Florian Fainelli <florian@openwrt.org>
> S: Maintained
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 5d810f6f230e..e5d6dced20e0 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -1156,6 +1156,19 @@ config VIDEO_NOON010PC30
>
> source "drivers/media/i2c/m5mols/Kconfig"
>
> +config VIDEO_RDACM20
> + tristate "IMI RDACM20 camera support"
> + depends on I2C
> + select V4L2_FWNODE
> + select VIDEO_V4L2_SUBDEV_API
> + select MEDIA_CONTROLLER
> + help
> + This driver supports the IMI RDACM20 GMSL camera, used in
> + ADAS systems.
> +
> + This camera should be used in conjunction with a GMSL
> + deserialiser such as the MAX9286.
> +
> config VIDEO_RJ54N1
> tristate "Sharp RJ54N1CB0C sensor support"
> depends on I2C && VIDEO_V4L2
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 29379008d624..93baaca8469e 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -119,5 +119,7 @@ obj-$(CONFIG_VIDEO_IMX319) += imx319.o
> obj-$(CONFIG_VIDEO_IMX355) += imx355.o
> obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o
> obj-$(CONFIG_VIDEO_MAX9286) += max9286.o
> +rdacm20-camera_module-objs := rdacm20.o max9271.o
> +obj-$(CONFIG_VIDEO_RDACM20) += rdacm20-camera_module.o
>
> obj-$(CONFIG_SDR_MAX2175) += max2175.o
> diff --git a/drivers/media/i2c/max9271.c b/drivers/media/i2c/max9271.c
> new file mode 100644
> index 000000000000..0fde36e9a8c7
> --- /dev/null
> +++ b/drivers/media/i2c/max9271.c
> @@ -0,0 +1,330 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2017-2020 Jacopo Mondi
> + * Copyright (C) 2017-2020 Kieran Bingham
> + * Copyright (C) 2017-2020 Laurent Pinchart
> + * Copyright (C) 2017-2020 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + *
> + * This file exports functions to control Maxim MAX9271 GMSL serializer
> + * chip. This is not a self-contained driver, as MAX9271 is usually embedded in
> + * camera modules with at least one image sensor and optional additional
> + * components, such as uController units or ISPs/DSPs.
> + *
> + * Drivers for the camera modules (ie rdacm20/21) are expected to use functions
> + * exported from this library driver to maximize code re-use across.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +
> +#include "max9271.h"
> +
> +static int max9271_read(struct max9271_device *dev, u8 reg)
> +{
> + int ret;
> +
> + dev_dbg(&dev->client->dev, "%s(0x%02x)\n", __func__, reg);
> +
> + ret = i2c_smbus_read_byte_data(dev->client, reg);
> + if (ret < 0)
> + dev_dbg(&dev->client->dev,
> + "%s: register 0x%02x read failed (%d)\n",
> + __func__, reg, ret);
> +
> + return ret;
> +}
> +
> +static int max9271_write(struct max9271_device *dev, u8 reg, u8 val)
> +{
> + int ret;
> +
> + dev_dbg(&dev->client->dev, "%s(0x%02x, 0x%02x)\n", __func__, reg, val);
> +
> + ret = i2c_smbus_write_byte_data(dev->client, reg, val);
> + if (ret < 0)
> + dev_err(&dev->client->dev,
> + "%s: register 0x%02x write failed (%d)\n",
> + __func__, reg, ret);
> +
> + return ret;
> +}
> +
> +/*
> + * max9271_pclk_detect() - Detect valid pixel clock from image sensor
> + *
> + * Wait up to 10ms for a valid pixel clock.
> + *
> + * Returns 0 for success, < 0 for pixel clock not properly detected
> + */
> +static int max9271_pclk_detect(struct max9271_device *dev)
> +{
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < 100; i++) {
> + ret = max9271_read(dev, 0x15);
> + if (ret < 0)
> + return ret;
> +
> + if (ret & MAX9271_PCLKDET)
> + return 0;
> +
> + usleep_range(50, 100);
> + }
> +
> + dev_err(&dev->client->dev, "Unable to detect valid pixel clock\n");
> + return -EIO;
> +}
> +
> +int max9271_set_serial_link(struct max9271_device *dev, bool enable)
> +{
> + int ret;
> + u8 val = MAX9271_REVCCEN | MAX9271_FWDCCEN;
> +
> + if (enable) {
> + ret = max9271_pclk_detect(dev);
> + if (ret)
> + return ret;
> +
> + val |= MAX9271_SEREN;
> + } else {
> + val |= MAX9271_CLINKEN;
> + }
> +
> + /*
> + * The serializer temporarily disables the reverse control channel for
> + * 350µs after starting/stopping the forward serial link, but the
> + * deserializer synchronization time isn't clearly documented.
> + *
> + * According to the serializer datasheet we should wait 3ms, while
> + * according to the deserializer datasheet we should wait 5ms.
> + *
> + * Short delays here appear to show bit-errors in the writes following.
> + * Therefore a conservative delay seems best here.
> + */
> + max9271_write(dev, 0x04, val);
> + usleep_range(5000, 8000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_set_serial_link);
> +
> +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config)
> +{
> + int ret;
> +
> + ret = max9271_write(dev, 0x0d, i2c_config);
> + if (ret)
> + return ret;
> +
> + /* The delay required after an I2C bus configuration change is not
> + * characterized in the serializer manual. Sleep up to 5msec to
> + * stay safe.
> + */
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_configure_i2c);
> +
> +int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
> +{
> + int ret = max9271_read(dev, 0x08);
> + if (ret < 0)
> + return ret;
> +
> + /*
> + * Enable or disable reverse channel high threshold to increase
> + * immunity to power supply noise.
> + */
> + max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
> + usleep_range(2000, 2500);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_set_high_threshold);
> +
> +int max9271_configure_gmsl_link(struct max9271_device *dev)
> +{
> + /*
> + * Configure the GMSL link:
> + *
> + * - Double input mode, high data rate, 24-bit mode
> + * - Latch input data on PCLKIN rising edge
> + * - Enable HS/VS encoding
> + * - 1-bit parity error detection
> + *
> + * TODO: Make the GMSL link configuration parametric.
> + */
> + max9271_write(dev, 0x07, MAX9271_DBL | MAX9271_HVEN |
> + MAX9271_EDC_1BIT_PARITY);
> + usleep_range(5000, 8000);
> +
> + /*
> + * Adjust spread spectrum to +4% and auto-detect pixel clock
> + * and serial link rate.
> + */
> + max9271_write(dev, 0x02, MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
> + MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT);
> + usleep_range(5000, 8000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_configure_gmsl_link);
> +
> +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask)
> +{
> + int ret = max9271_read(dev, 0x0f);
> + if (ret < 0)
> + return 0;
> +
> + ret |= gpio_mask;
> + ret = max9271_write(dev, 0x0f, ret);
> + if (ret < 0) {
> + dev_err(&dev->client->dev, "Failed to set gpio (%d)\n", ret);
> + return ret;
> + }
> +
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_set_gpios);
> +
> +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask)
> +{
> + int ret = max9271_read(dev, 0x0f);
> + if (ret < 0)
> + return 0;
> +
> + ret &= ~gpio_mask;
> + ret = max9271_write(dev, 0x0f, ret);
> + if (ret < 0) {
> + dev_err(&dev->client->dev, "Failed to clear gpio (%d)\n", ret);
> + return ret;
> + }
> +
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_clear_gpios);
> +
> +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask)
> +{
> + int ret = max9271_read(dev, 0x0f);
> + if (ret < 0)
> + return 0;
> +
> + /* BIT(0) reserved: GPO is always enabled. */
> + ret |= gpio_mask | BIT(0);
> + ret = max9271_write(dev, 0x0e, ret);
> + if (ret < 0) {
> + dev_err(&dev->client->dev, "Failed to enable gpio (%d)\n", ret);
> + return ret;
> + }
> +
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_enable_gpios);
> +
> +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask)
> +{
> + int ret = max9271_read(dev, 0x0f);
> + if (ret < 0)
> + return 0;
> +
> + /* BIT(0) reserved: GPO cannot be disabled */
> + ret &= (~gpio_mask | BIT(0));
> + ret = max9271_write(dev, 0x0e, ret);
> + if (ret < 0) {
> + dev_err(&dev->client->dev, "Failed to disable gpio (%d)\n", ret);
> + return ret;
> + }
> +
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_disable_gpios);
> +
> +int max9271_verify_id(struct max9271_device *dev)
> +{
> + int ret;
> +
> + ret = max9271_read(dev, 0x1e);
> + if (ret < 0) {
> + dev_err(&dev->client->dev, "MAX9271 ID read failed (%d)\n",
> + ret);
> + return ret;
> + }
> +
> + if (ret != MAX9271_ID) {
> + dev_err(&dev->client->dev, "MAX9271 ID mismatch (0x%02x)\n",
> + ret);
> + return -ENXIO;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_verify_id);
> +
> +int max9271_set_address(struct max9271_device *dev, u8 addr)
> +{
> + int ret;
> +
> + ret = max9271_write(dev, 0x00, addr << 1);
> + if (ret < 0) {
> + dev_err(&dev->client->dev,
> + "MAX9271 I2C address change failed (%d)\n", ret);
> + return ret;
> + }
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_set_address);
> +
> +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr)
> +{
> + int ret;
> +
> + ret = max9271_write(dev, 0x01, addr << 1);
> + if (ret < 0) {
> + dev_err(&dev->client->dev,
> + "MAX9271 deserializer address set failed (%d)\n", ret);
> + return ret;
> + }
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_set_deserializer_address);
> +
> +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest)
> +{
> + int ret;
> +
> + ret = max9271_write(dev, 0x09, source << 1);
> + if (ret < 0) {
> + dev_err(&dev->client->dev,
> + "MAX9271 I2C translation setup failed (%d)\n", ret);
> + return ret;
> + }
> + usleep_range(3500, 5000);
> +
> + ret = max9271_write(dev, 0x0a, dest << 1);
> + if (ret < 0) {
> + dev_err(&dev->client->dev,
> + "MAX9271 I2C translation setup failed (%d)\n", ret);
> + return ret;
> + }
> + usleep_range(3500, 5000);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(max9271_set_translation);
> diff --git a/drivers/media/i2c/max9271.h b/drivers/media/i2c/max9271.h
> new file mode 100644
> index 000000000000..d78fb21441e9
> --- /dev/null
> +++ b/drivers/media/i2c/max9271.h
> @@ -0,0 +1,224 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) 2017-2020 Jacopo Mondi
> + * Copyright (C) 2017-2020 Kieran Bingham
> + * Copyright (C) 2017-2020 Laurent Pinchart
> + * Copyright (C) 2017-2020 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + */
> +
> +#include <linux/i2c.h>
> +
> +#define MAX9271_DEFAULT_ADDR 0x40
> +
> +/* Register 0x02 */
> +#define MAX9271_SPREAD_SPECT_0 (0 << 5)
> +#define MAX9271_SPREAD_SPECT_05 (1 << 5)
> +#define MAX9271_SPREAD_SPECT_15 (2 << 5)
> +#define MAX9271_SPREAD_SPECT_1 (5 << 5)
> +#define MAX9271_SPREAD_SPECT_2 (3 << 5)
> +#define MAX9271_SPREAD_SPECT_3 (6 << 5)
> +#define MAX9271_SPREAD_SPECT_4 (7 << 5)
> +#define MAX9271_R02_RES BIT(4)
> +#define MAX9271_PCLK_AUTODETECT (3 << 2)
> +#define MAX9271_SERIAL_AUTODETECT (0x03)
> +/* Register 0x04 */
> +#define MAX9271_SEREN BIT(7)
> +#define MAX9271_CLINKEN BIT(6)
> +#define MAX9271_PRBSEN BIT(5)
> +#define MAX9271_SLEEP BIT(4)
> +#define MAX9271_INTTYPE_I2C (0 << 2)
> +#define MAX9271_INTTYPE_UART (1 << 2)
> +#define MAX9271_INTTYPE_NONE (2 << 2)
> +#define MAX9271_REVCCEN BIT(1)
> +#define MAX9271_FWDCCEN BIT(0)
> +/* Register 0x07 */
> +#define MAX9271_DBL BIT(7)
> +#define MAX9271_DRS BIT(6)
> +#define MAX9271_BWS BIT(5)
> +#define MAX9271_ES BIT(4)
> +#define MAX9271_HVEN BIT(2)
> +#define MAX9271_EDC_1BIT_PARITY (0 << 0)
> +#define MAX9271_EDC_6BIT_CRC (1 << 0)
> +#define MAX9271_EDC_6BIT_HAMMING (2 << 0)
> +/* Register 0x08 */
> +#define MAX9271_INVVS BIT(7)
> +#define MAX9271_INVHS BIT(6)
> +#define MAX9271_REV_LOGAIN BIT(3)
> +#define MAX9271_REV_HIVTH BIT(0)
> +/* Register 0x09 */
> +#define MAX9271_ID 0x09
> +/* Register 0x0d */
> +#define MAX9271_I2CLOCACK BIT(7)
> +#define MAX9271_I2CSLVSH_1046NS_469NS (3 << 5)
> +#define MAX9271_I2CSLVSH_938NS_352NS (2 << 5)
> +#define MAX9271_I2CSLVSH_469NS_234NS (1 << 5)
> +#define MAX9271_I2CSLVSH_352NS_117NS (0 << 5)
> +#define MAX9271_I2CMSTBT_837KBPS (7 << 2)
> +#define MAX9271_I2CMSTBT_533KBPS (6 << 2)
> +#define MAX9271_I2CMSTBT_339KBPS (5 << 2)
> +#define MAX9271_I2CMSTBT_173KBPS (4 << 2)
> +#define MAX9271_I2CMSTBT_105KBPS (3 << 2)
> +#define MAX9271_I2CMSTBT_84KBPS (2 << 2)
> +#define MAX9271_I2CMSTBT_28KBPS (1 << 2)
> +#define MAX9271_I2CMSTBT_8KBPS (0 << 2)
> +#define MAX9271_I2CSLVTO_NONE (3 << 0)
> +#define MAX9271_I2CSLVTO_1024US (2 << 0)
> +#define MAX9271_I2CSLVTO_256US (1 << 0)
> +#define MAX9271_I2CSLVTO_64US (0 << 0)
> +/* Register 0x0f */
> +#define MAX9271_GPIO5OUT BIT(5)
> +#define MAX9271_GPIO4OUT BIT(4)
> +#define MAX9271_GPIO3OUT BIT(3)
> +#define MAX9271_GPIO2OUT BIT(2)
> +#define MAX9271_GPIO1OUT BIT(1)
> +#define MAX9271_GPO BIT(0)
> +/* Register 0x15 */
> +#define MAX9271_PCLKDET BIT(0)
> +
> +/**
> + * struct max9271_device - max9271 device
> + * @client: The i2c client for the max9271 instance
> + */
> +struct max9271_device {
> + struct i2c_client *client;
> +};
> +
> +/**
> + * max9271_set_serial_link() - Enable/disable serial link
> + * @dev: The max9271 device
> + * @enable: Serial link enable/disable flag
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_set_serial_link(struct max9271_device *dev, bool enable);
> +
> +/**
> + * max9271_configure_i2c() - Configure I2C bus parameters
> + * @dev: The max9271 device
> + * @i2c_config: The I2C bus configuration bit mask
> + *
> + * Configure MAX9271 I2C interface. The bus configuration provided in the
> + * @i2c_config parameter shall be assembled using bit values defined by the
> + * MAX9271_I2C* macros.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_configure_i2c(struct max9271_device *dev, u8 i2c_config);
> +
> +/**
> + * max9271_set_high_threshold() - Enable or disable reverse channel high
> + * threshold
> + * @dev: The max9271 device
> + * @enable: High threshold enable/disable flag
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_set_high_threshold(struct max9271_device *dev, bool enable);
> +
> +/**
> + * max9271_configure_gmsl_link() - Configure the GMSL link
> + * @dev: The max9271 device
> + *
> + * FIXME: the GMSL link configuration is currently hardcoded and performed
> + * by programming registers 0x04, 0x07 and 0x02.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_configure_gmsl_link(struct max9271_device *dev);
> +
> +/**
> + * max9271_set_gpios() - Set gpio lines to physical high value
> + * @dev: The max9271 device
> + * @gpio_mask: The mask of gpio lines to set to high value
> + *
> + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]*
> + * bit values.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_set_gpios(struct max9271_device *dev, u8 gpio_mask);
> +
> +/**
> + * max9271_clear_gpios() - Set gpio lines to physical low value
> + * @dev: The max9271 device
> + * @gpio_mask: The mask of gpio lines to set to low value
> + *
> + * The @gpio_mask parameter shall be assembled using the MAX9271_GP[IO|O]*
> + * bit values.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_clear_gpios(struct max9271_device *dev, u8 gpio_mask);
> +
> +/**
> + * max9271_enable_gpios() - Enable gpio lines
> + * @dev: The max9271 device
> + * @gpio_mask: The mask of gpio lines to enable
> + *
> + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO*
> + * bit values. GPO line is always enabled by default.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_enable_gpios(struct max9271_device *dev, u8 gpio_mask);
> +
> +/**
> + * max9271_disable_gpios() - Disable gpio lines
> + * @dev: The max9271 device
> + * @gpio_mask: The mask of gpio lines to disable
> + *
> + * The @gpio_mask parameter shall be assembled using the MAX9271_GPIO*
> + * bit values. GPO line is always enabled by default and cannot be disabled.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_disable_gpios(struct max9271_device *dev, u8 gpio_mask);
> +
> +/**
> + * max9271_verify_id() - Read and verify MAX9271 id
> + * @dev: The max9271 device
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_verify_id(struct max9271_device *dev);
> +
> +/**
> + * max9271_set_address() - Program a new I2C address
> + * @dev: The max9271 device
> + * @addr: The new I2C address in 7-bit format
> + *
> + * This function only takes care of programming the new I2C address @addr to
> + * in the MAX9271 chip registers, it is responsiblity of the caller to set
> + * the i2c address client to the @addr value to be able to communicate with
> + * the MAX9271 chip using the I2C framework APIs after this function returns.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_set_address(struct max9271_device *dev, u8 addr);
> +
> +/**
> + * max9271_set_deserializer_address() - Program the remote deserializer address
> + * @dev: The max9271 device
> + * @addr: The deserializer I2C address in 7-bit format
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_set_deserializer_address(struct max9271_device *dev, u8 addr);
> +
> +/**
> + * max9271_set_translation() - Program I2C address translation
> + * @dev: The max9271 device
> + * @source: The I2C source address
> + * @dest: The I2C destination address
> + *
> + * Program address translation from @source to @dest. This is required to
> + * communicate with local devices that do not support address reprogramming.
> + *
> + * TODO: The device supports translation of two address, this function currently
> + * supports a single one.
> + *
> + * Return 0 on success or a negative error code on failure
> + */
> +int max9271_set_translation(struct max9271_device *dev, u8 source, u8 dest);
> diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
> new file mode 100644
> index 000000000000..37786998878b
> --- /dev/null
> +++ b/drivers/media/i2c/rdacm20.c
> @@ -0,0 +1,668 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * IMI RDACM20 GMSL Camera Driver
> + *
> + * Copyright (C) 2017-2020 Jacopo Mondi
> + * Copyright (C) 2017-2020 Kieran Bingham
> + * Copyright (C) 2017-2019 Laurent Pinchart
> + * Copyright (C) 2017-2019 Niklas Söderlund
> + * Copyright (C) 2016 Renesas Electronics Corporation
> + * Copyright (C) 2015 Cogent Embedded, Inc.
> + */
> +
> +/*
> + * The camera is made of an Omnivision OV10635 sensor connected to a Maxim
> + * MAX9271 GMSL serializer.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/fwnode.h>
> +#include <linux/init.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +
> +#include "max9271.h"
> +
> +#define OV10635_I2C_ADDRESS 0x30
> +
> +#define OV10635_SOFTWARE_RESET 0x0103
> +#define OV10635_PID 0x300a
> +#define OV10635_VER 0x300b
> +#define OV10635_SC_CMMN_SCCB_ID 0x300c
> +#define OV10635_SC_CMMN_SCCB_ID_SELECT BIT(0)
> +#define OV10635_VERSION 0xa635
> +
> +#define OV10635_WIDTH 1280
> +#define OV10635_HEIGHT 800
> +#define OV10635_FORMAT MEDIA_BUS_FMT_UYVY8_2X8
> +
> +/* VTS = PCLK / FPS / HTS / 2 (= 88MHz / 1572 / 30 / 2) */
> +#define OV10635_HTS 1572
> +/* FPS = 29,9998 */
> +#define OV10635_VTS 933
> +
> +/*
> + * As the drivers supports a single MEDIA_BUS_FMT_UYVY8_2X8 format we
> + * can harcode the pixel rate.
> + *
> + * PCLK is fed through the system clock, programmed @88MHz.
> + * MEDIA_BUS_FMT_UYVY8_2X8 format = 2 samples per pixel.
> + *
> + * Pixelrate = PCLK / 2
> + * FPS = (OV10635_VTS * OV10635_HTS) / PixelRate
> + * = 29,9998
> + */
> +#define OV10635_PIXEL_RATE (44000000)
> +
> +static const struct ov10635_reg {
> + u16 reg;
> + u8 val;
> +} ov10635_regs_wizard[] = {
> + { 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x3011, 0x42 },
> + { 0x6900, 0x0c }, { 0x6901, 0x19 }, { 0x3503, 0x10 }, { 0x3025, 0x03 },
> + { 0x3003, 0x16 }, { 0x3004, 0x30 }, { 0x3005, 0x40 }, { 0x3006, 0x91 },
> + { 0x3600, 0x74 }, { 0x3601, 0x2b }, { 0x3612, 0x00 }, { 0x3611, 0x67 },
> + { 0x3633, 0xca }, { 0x3602, 0xaf }, { 0x3603, 0x04 }, { 0x3630, 0x28 },
> + { 0x3631, 0x16 }, { 0x3714, 0x10 }, { 0x371d, 0x01 }, { 0x4300, 0x3a },
> + { 0x3007, 0x01 }, { 0x3024, 0x03 }, { 0x3020, 0x0a }, { 0x3702, 0x0d },
> + { 0x3703, 0x20 }, { 0x3704, 0x15 }, { 0x3709, 0xa8 }, { 0x370c, 0xc7 },
> + { 0x370d, 0x80 }, { 0x3712, 0x00 }, { 0x3713, 0x20 }, { 0x3715, 0x04 },
> + { 0x381d, 0x40 }, { 0x381c, 0x00 }, { 0x3822, 0x50 }, { 0x3824, 0x10 },
> + { 0x3815, 0x8c }, { 0x3804, 0x05 }, { 0x3805, 0x1f }, { 0x3800, 0x00 },
> + { 0x3801, 0x00 }, { 0x3806, 0x03 }, { 0x3807, 0x28 }, { 0x3802, 0x00 },
> + { 0x3803, 0x07 }, { 0x3808, 0x05 }, { 0x3809, 0x00 }, { 0x380a, 0x03 },
> + { 0x380b, 0x20 }, { 0x380c, OV10635_HTS >> 8 },
> + { 0x380d, OV10635_HTS & 0xff }, { 0x380e, OV10635_VTS >> 8 },
> + { 0x380f, OV10635_VTS & 0xff }, { 0x3813, 0x02 }, { 0x3811, 0x08 },
> + { 0x381f, 0x0c }, { 0x3819, 0x04 }, { 0x3804, 0x01 }, { 0x3805, 0x00 },
> + { 0x3828, 0x03 }, { 0x3829, 0x10 }, { 0x382a, 0x10 }, { 0x3621, 0x63 },
> + { 0x5005, 0x08 }, { 0x56d5, 0x00 }, { 0x56d6, 0x80 }, { 0x56d7, 0x00 },
> + { 0x56d8, 0x00 }, { 0x56d9, 0x00 }, { 0x56da, 0x80 }, { 0x56db, 0x00 },
> + { 0x56dc, 0x00 }, { 0x56e8, 0x00 }, { 0x56e9, 0x7f }, { 0x56ea, 0x00 },
> + { 0x56eb, 0x7f }, { 0x5100, 0x00 }, { 0x5101, 0x80 }, { 0x5102, 0x00 },
> + { 0x5103, 0x80 }, { 0x5104, 0x00 }, { 0x5105, 0x80 }, { 0x5106, 0x00 },
> + { 0x5107, 0x80 }, { 0x5108, 0x00 }, { 0x5109, 0x00 }, { 0x510a, 0x00 },
> + { 0x510b, 0x00 }, { 0x510c, 0x00 }, { 0x510d, 0x00 }, { 0x510e, 0x00 },
> + { 0x510f, 0x00 }, { 0x5110, 0x00 }, { 0x5111, 0x80 }, { 0x5112, 0x00 },
> + { 0x5113, 0x80 }, { 0x5114, 0x00 }, { 0x5115, 0x80 }, { 0x5116, 0x00 },
> + { 0x5117, 0x80 }, { 0x5118, 0x00 }, { 0x5119, 0x00 }, { 0x511a, 0x00 },
> + { 0x511b, 0x00 }, { 0x511c, 0x00 }, { 0x511d, 0x00 }, { 0x511e, 0x00 },
> + { 0x511f, 0x00 }, { 0x56d0, 0x00 }, { 0x5006, 0x04 }, { 0x5608, 0x05 },
> + { 0x52d7, 0x06 }, { 0x528d, 0x08 }, { 0x5293, 0x12 }, { 0x52d3, 0x12 },
> + { 0x5288, 0x06 }, { 0x5289, 0x20 }, { 0x52c8, 0x06 }, { 0x52c9, 0x20 },
> + { 0x52cd, 0x04 }, { 0x5381, 0x00 }, { 0x5382, 0xff }, { 0x5589, 0x76 },
> + { 0x558a, 0x47 }, { 0x558b, 0xef }, { 0x558c, 0xc9 }, { 0x558d, 0x49 },
> + { 0x558e, 0x30 }, { 0x558f, 0x67 }, { 0x5590, 0x3f }, { 0x5591, 0xf0 },
> + { 0x5592, 0x10 }, { 0x55a2, 0x6d }, { 0x55a3, 0x55 }, { 0x55a4, 0xc3 },
> + { 0x55a5, 0xb5 }, { 0x55a6, 0x43 }, { 0x55a7, 0x38 }, { 0x55a8, 0x5f },
> + { 0x55a9, 0x4b }, { 0x55aa, 0xf0 }, { 0x55ab, 0x10 }, { 0x5581, 0x52 },
> + { 0x5300, 0x01 }, { 0x5301, 0x00 }, { 0x5302, 0x00 }, { 0x5303, 0x0e },
> + { 0x5304, 0x00 }, { 0x5305, 0x0e }, { 0x5306, 0x00 }, { 0x5307, 0x36 },
> + { 0x5308, 0x00 }, { 0x5309, 0xd9 }, { 0x530a, 0x00 }, { 0x530b, 0x0f },
> + { 0x530c, 0x00 }, { 0x530d, 0x2c }, { 0x530e, 0x00 }, { 0x530f, 0x59 },
> + { 0x5310, 0x00 }, { 0x5311, 0x7b }, { 0x5312, 0x00 }, { 0x5313, 0x22 },
> + { 0x5314, 0x00 }, { 0x5315, 0xd5 }, { 0x5316, 0x00 }, { 0x5317, 0x13 },
> + { 0x5318, 0x00 }, { 0x5319, 0x18 }, { 0x531a, 0x00 }, { 0x531b, 0x26 },
> + { 0x531c, 0x00 }, { 0x531d, 0xdc }, { 0x531e, 0x00 }, { 0x531f, 0x02 },
> + { 0x5320, 0x00 }, { 0x5321, 0x24 }, { 0x5322, 0x00 }, { 0x5323, 0x56 },
> + { 0x5324, 0x00 }, { 0x5325, 0x85 }, { 0x5326, 0x00 }, { 0x5327, 0x20 },
> + { 0x5609, 0x01 }, { 0x560a, 0x40 }, { 0x560b, 0x01 }, { 0x560c, 0x40 },
> + { 0x560d, 0x00 }, { 0x560e, 0xfa }, { 0x560f, 0x00 }, { 0x5610, 0xfa },
> + { 0x5611, 0x02 }, { 0x5612, 0x80 }, { 0x5613, 0x02 }, { 0x5614, 0x80 },
> + { 0x5615, 0x01 }, { 0x5616, 0x2c }, { 0x5617, 0x01 }, { 0x5618, 0x2c },
> + { 0x563b, 0x01 }, { 0x563c, 0x01 }, { 0x563d, 0x01 }, { 0x563e, 0x01 },
> + { 0x563f, 0x03 }, { 0x5640, 0x03 }, { 0x5641, 0x03 }, { 0x5642, 0x05 },
> + { 0x5643, 0x09 }, { 0x5644, 0x05 }, { 0x5645, 0x05 }, { 0x5646, 0x05 },
> + { 0x5647, 0x05 }, { 0x5651, 0x00 }, { 0x5652, 0x80 }, { 0x521a, 0x01 },
> + { 0x521b, 0x03 }, { 0x521c, 0x06 }, { 0x521d, 0x0a }, { 0x521e, 0x0e },
> + { 0x521f, 0x12 }, { 0x5220, 0x16 }, { 0x5223, 0x02 }, { 0x5225, 0x04 },
> + { 0x5227, 0x08 }, { 0x5229, 0x0c }, { 0x522b, 0x12 }, { 0x522d, 0x18 },
> + { 0x522f, 0x1e }, { 0x5241, 0x04 }, { 0x5242, 0x01 }, { 0x5243, 0x03 },
> + { 0x5244, 0x06 }, { 0x5245, 0x0a }, { 0x5246, 0x0e }, { 0x5247, 0x12 },
> + { 0x5248, 0x16 }, { 0x524a, 0x03 }, { 0x524c, 0x04 }, { 0x524e, 0x08 },
> + { 0x5250, 0x0c }, { 0x5252, 0x12 }, { 0x5254, 0x18 }, { 0x5256, 0x1e },
> + /* fifo_line_length = 2*hts */
> + { 0x4606, (2 * OV10635_HTS) >> 8 }, { 0x4607, (2 * OV10635_HTS) & 0xff },
> + /* fifo_hsync_start = 2*(hts - xres) */
> + { 0x460a, (2 * (OV10635_HTS - OV10635_WIDTH)) >> 8 },
> + { 0x460b, (2 * (OV10635_HTS - OV10635_WIDTH)) & 0xff },
> + { 0x460c, 0x00 }, { 0x4620, 0x0e },
> + /* BT601: 0x08 is also acceptable as HS/VS mode */
> + { 0x4700, 0x04 }, { 0x4701, 0x00 }, { 0x4702, 0x01 }, { 0x4004, 0x04 },
> + { 0x4005, 0x18 }, { 0x4001, 0x06 }, { 0x4050, 0x22 }, { 0x4051, 0x24 },
> + { 0x4052, 0x02 }, { 0x4057, 0x9c }, { 0x405a, 0x00 }, { 0x4202, 0x02 },
> + { 0x3023, 0x10 }, { 0x0100, 0x01 }, { 0x0100, 0x01 }, { 0x6f10, 0x07 },
> + { 0x6f11, 0x82 }, { 0x6f12, 0x04 }, { 0x6f13, 0x00 }, { 0xd000, 0x19 },
> + { 0xd001, 0xa0 }, { 0xd002, 0x00 }, { 0xd003, 0x01 }, { 0xd004, 0xa9 },
> + { 0xd005, 0xad }, { 0xd006, 0x10 }, { 0xd007, 0x40 }, { 0xd008, 0x44 },
> + { 0xd009, 0x00 }, { 0xd00a, 0x68 }, { 0xd00b, 0x00 }, { 0xd00c, 0x15 },
> + { 0xd00d, 0x00 }, { 0xd00e, 0x00 }, { 0xd00f, 0x00 }, { 0xd040, 0x9c },
> + { 0xd041, 0x21 }, { 0xd042, 0xff }, { 0xd043, 0xf8 }, { 0xd044, 0xd4 },
> + { 0xd045, 0x01 }, { 0xd046, 0x48 }, { 0xd047, 0x00 }, { 0xd048, 0xd4 },
> + { 0xd049, 0x01 }, { 0xd04a, 0x50 }, { 0xd04b, 0x04 }, { 0xd04c, 0x18 },
> + { 0xd04d, 0x60 }, { 0xd04e, 0x00 }, { 0xd04f, 0x01 }, { 0xd050, 0xa8 },
> + { 0xd051, 0x63 }, { 0xd052, 0x02 }, { 0xd053, 0xa4 }, { 0xd054, 0x85 },
> + { 0xd055, 0x43 }, { 0xd056, 0x00 }, { 0xd057, 0x00 }, { 0xd058, 0x18 },
> + { 0xd059, 0x60 }, { 0xd05a, 0x00 }, { 0xd05b, 0x01 }, { 0xd05c, 0xa8 },
> + { 0xd05d, 0x63 }, { 0xd05e, 0x03 }, { 0xd05f, 0xf0 }, { 0xd060, 0x98 },
> + { 0xd061, 0xa3 }, { 0xd062, 0x00 }, { 0xd063, 0x00 }, { 0xd064, 0x8c },
> + { 0xd065, 0x6a }, { 0xd066, 0x00 }, { 0xd067, 0x6e }, { 0xd068, 0xe5 },
> + { 0xd069, 0x85 }, { 0xd06a, 0x18 }, { 0xd06b, 0x00 }, { 0xd06c, 0x10 },
> + { 0xd06d, 0x00 }, { 0xd06e, 0x00 }, { 0xd06f, 0x10 }, { 0xd070, 0x9c },
> + { 0xd071, 0x80 }, { 0xd072, 0x00 }, { 0xd073, 0x03 }, { 0xd074, 0x18 },
> + { 0xd075, 0x60 }, { 0xd076, 0x00 }, { 0xd077, 0x01 }, { 0xd078, 0xa8 },
> + { 0xd079, 0x63 }, { 0xd07a, 0x07 }, { 0xd07b, 0x80 }, { 0xd07c, 0x07 },
> + { 0xd07d, 0xff }, { 0xd07e, 0xf9 }, { 0xd07f, 0x03 }, { 0xd080, 0x8c },
> + { 0xd081, 0x63 }, { 0xd082, 0x00 }, { 0xd083, 0x00 }, { 0xd084, 0xa5 },
> + { 0xd085, 0x6b }, { 0xd086, 0x00 }, { 0xd087, 0xff }, { 0xd088, 0x18 },
> + { 0xd089, 0x80 }, { 0xd08a, 0x00 }, { 0xd08b, 0x01 }, { 0xd08c, 0xa8 },
> + { 0xd08d, 0x84 }, { 0xd08e, 0x01 }, { 0xd08f, 0x04 }, { 0xd090, 0xe1 },
> + { 0xd091, 0x6b }, { 0xd092, 0x58 }, { 0xd093, 0x00 }, { 0xd094, 0x94 },
> + { 0xd095, 0x6a }, { 0xd096, 0x00 }, { 0xd097, 0x70 }, { 0xd098, 0xe1 },
> + { 0xd099, 0x6b }, { 0xd09a, 0x20 }, { 0xd09b, 0x00 }, { 0xd09c, 0x95 },
> + { 0xd09d, 0x6b }, { 0xd09e, 0x00 }, { 0xd09f, 0x00 }, { 0xd0a0, 0xe4 },
> + { 0xd0a1, 0x8b }, { 0xd0a2, 0x18 }, { 0xd0a3, 0x00 }, { 0xd0a4, 0x0c },
> + { 0xd0a5, 0x00 }, { 0xd0a6, 0x00 }, { 0xd0a7, 0x23 }, { 0xd0a8, 0x15 },
> + { 0xd0a9, 0x00 }, { 0xd0aa, 0x00 }, { 0xd0ab, 0x00 }, { 0xd0ac, 0x18 },
> + { 0xd0ad, 0x60 }, { 0xd0ae, 0x80 }, { 0xd0af, 0x06 }, { 0xd0b0, 0xa8 },
> + { 0xd0b1, 0x83 }, { 0xd0b2, 0x40 }, { 0xd0b3, 0x08 }, { 0xd0b4, 0xa8 },
> + { 0xd0b5, 0xe3 }, { 0xd0b6, 0x38 }, { 0xd0b7, 0x2a }, { 0xd0b8, 0xa8 },
> + { 0xd0b9, 0xc3 }, { 0xd0ba, 0x40 }, { 0xd0bb, 0x09 }, { 0xd0bc, 0xa8 },
> + { 0xd0bd, 0xa3 }, { 0xd0be, 0x38 }, { 0xd0bf, 0x29 }, { 0xd0c0, 0x8c },
> + { 0xd0c1, 0x65 }, { 0xd0c2, 0x00 }, { 0xd0c3, 0x00 }, { 0xd0c4, 0xd8 },
> + { 0xd0c5, 0x04 }, { 0xd0c6, 0x18 }, { 0xd0c7, 0x00 }, { 0xd0c8, 0x8c },
> + { 0xd0c9, 0x67 }, { 0xd0ca, 0x00 }, { 0xd0cb, 0x00 }, { 0xd0cc, 0xd8 },
> + { 0xd0cd, 0x06 }, { 0xd0ce, 0x18 }, { 0xd0cf, 0x00 }, { 0xd0d0, 0x18 },
> + { 0xd0d1, 0x60 }, { 0xd0d2, 0x80 }, { 0xd0d3, 0x06 }, { 0xd0d4, 0xa8 },
> + { 0xd0d5, 0xe3 }, { 0xd0d6, 0x67 }, { 0xd0d7, 0x02 }, { 0xd0d8, 0xa9 },
> + { 0xd0d9, 0x03 }, { 0xd0da, 0x67 }, { 0xd0db, 0x03 }, { 0xd0dc, 0xa8 },
> + { 0xd0dd, 0xc3 }, { 0xd0de, 0x3d }, { 0xd0df, 0x05 }, { 0xd0e0, 0x8c },
> + { 0xd0e1, 0x66 }, { 0xd0e2, 0x00 }, { 0xd0e3, 0x00 }, { 0xd0e4, 0xb8 },
> + { 0xd0e5, 0x63 }, { 0xd0e6, 0x00 }, { 0xd0e7, 0x18 }, { 0xd0e8, 0xb8 },
> + { 0xd0e9, 0x63 }, { 0xd0ea, 0x00 }, { 0xd0eb, 0x98 }, { 0xd0ec, 0xbc },
> + { 0xd0ed, 0x03 }, { 0xd0ee, 0x00 }, { 0xd0ef, 0x00 }, { 0xd0f0, 0x10 },
> + { 0xd0f1, 0x00 }, { 0xd0f2, 0x00 }, { 0xd0f3, 0x16 }, { 0xd0f4, 0xb8 },
> + { 0xd0f5, 0x83 }, { 0xd0f6, 0x00 }, { 0xd0f7, 0x19 }, { 0xd0f8, 0x8c },
> + { 0xd0f9, 0x67 }, { 0xd0fa, 0x00 }, { 0xd0fb, 0x00 }, { 0xd0fc, 0xb8 },
> + { 0xd0fd, 0xa4 }, { 0xd0fe, 0x00 }, { 0xd0ff, 0x98 }, { 0xd100, 0xb8 },
> + { 0xd101, 0x83 }, { 0xd102, 0x00 }, { 0xd103, 0x08 }, { 0xd104, 0x8c },
> + { 0xd105, 0x68 }, { 0xd106, 0x00 }, { 0xd107, 0x00 }, { 0xd108, 0xe0 },
> + { 0xd109, 0x63 }, { 0xd10a, 0x20 }, { 0xd10b, 0x04 }, { 0xd10c, 0xe0 },
> + { 0xd10d, 0x65 }, { 0xd10e, 0x18 }, { 0xd10f, 0x00 }, { 0xd110, 0xa4 },
> + { 0xd111, 0x83 }, { 0xd112, 0xff }, { 0xd113, 0xff }, { 0xd114, 0xb8 },
> + { 0xd115, 0x64 }, { 0xd116, 0x00 }, { 0xd117, 0x48 }, { 0xd118, 0xd8 },
> + { 0xd119, 0x07 }, { 0xd11a, 0x18 }, { 0xd11b, 0x00 }, { 0xd11c, 0xd8 },
> + { 0xd11d, 0x08 }, { 0xd11e, 0x20 }, { 0xd11f, 0x00 }, { 0xd120, 0x9c },
> + { 0xd121, 0x60 }, { 0xd122, 0x00 }, { 0xd123, 0x00 }, { 0xd124, 0xd8 },
> + { 0xd125, 0x06 }, { 0xd126, 0x18 }, { 0xd127, 0x00 }, { 0xd128, 0x00 },
> + { 0xd129, 0x00 }, { 0xd12a, 0x00 }, { 0xd12b, 0x08 }, { 0xd12c, 0x15 },
> + { 0xd12d, 0x00 }, { 0xd12e, 0x00 }, { 0xd12f, 0x00 }, { 0xd130, 0x8c },
> + { 0xd131, 0x6a }, { 0xd132, 0x00 }, { 0xd133, 0x76 }, { 0xd134, 0xbc },
> + { 0xd135, 0x23 }, { 0xd136, 0x00 }, { 0xd137, 0x00 }, { 0xd138, 0x13 },
> + { 0xd139, 0xff }, { 0xd13a, 0xff }, { 0xd13b, 0xe6 }, { 0xd13c, 0x18 },
> + { 0xd13d, 0x60 }, { 0xd13e, 0x80 }, { 0xd13f, 0x06 }, { 0xd140, 0x03 },
> + { 0xd141, 0xff }, { 0xd142, 0xff }, { 0xd143, 0xdd }, { 0xd144, 0xa8 },
> + { 0xd145, 0x83 }, { 0xd146, 0x40 }, { 0xd147, 0x08 }, { 0xd148, 0x85 },
> + { 0xd149, 0x21 }, { 0xd14a, 0x00 }, { 0xd14b, 0x00 }, { 0xd14c, 0x85 },
> + { 0xd14d, 0x41 }, { 0xd14e, 0x00 }, { 0xd14f, 0x04 }, { 0xd150, 0x44 },
> + { 0xd151, 0x00 }, { 0xd152, 0x48 }, { 0xd153, 0x00 }, { 0xd154, 0x9c },
> + { 0xd155, 0x21 }, { 0xd156, 0x00 }, { 0xd157, 0x08 }, { 0x6f0e, 0x03 },
> + { 0x6f0f, 0x00 }, { 0x460e, 0x08 }, { 0x460f, 0x01 }, { 0x4610, 0x00 },
> + { 0x4611, 0x01 }, { 0x4612, 0x00 }, { 0x4613, 0x01 },
> + /* 8 bits */
> + { 0x4605, 0x08 },
> + /* Swap data bits order [9:0] -> [0:9] */
> + { 0x4709, 0x10 }, { 0x4608, 0x00 }, { 0x4609, 0x08 }, { 0x6804, 0x00 },
> + { 0x6805, 0x06 }, { 0x6806, 0x00 }, { 0x5120, 0x00 }, { 0x3510, 0x00 },
> + { 0x3504, 0x00 }, { 0x6800, 0x00 }, { 0x6f0d, 0x01 },
> + /* PCLK falling edge */
> + { 0x4708, 0x01 }, { 0x5000, 0xff }, { 0x5001, 0xbf }, { 0x5002, 0x7e },
> + { 0x503d, 0x00 }, { 0xc450, 0x01 }, { 0xc452, 0x04 }, { 0xc453, 0x00 },
> + { 0xc454, 0x00 }, { 0xc455, 0x01 }, { 0xc456, 0x01 }, { 0xc457, 0x00 },
> + { 0xc458, 0x00 }, { 0xc459, 0x00 }, { 0xc45b, 0x00 }, { 0xc45c, 0x01 },
> + { 0xc45d, 0x00 }, { 0xc45e, 0x00 }, { 0xc45f, 0x00 }, { 0xc460, 0x00 },
> + { 0xc461, 0x01 }, { 0xc462, 0x01 }, { 0xc464, 0x03 }, { 0xc465, 0x00 },
> + { 0xc466, 0x8a }, { 0xc467, 0x00 }, { 0xc468, 0x86 }, { 0xc469, 0x00 },
> + { 0xc46a, 0x40 }, { 0xc46b, 0x50 }, { 0xc46c, 0x30 }, { 0xc46d, 0x28 },
> + { 0xc46e, 0x60 }, { 0xc46f, 0x40 }, { 0xc47c, 0x01 }, { 0xc47d, 0x38 },
> + { 0xc47e, 0x00 }, { 0xc47f, 0x00 }, { 0xc480, 0x00 }, { 0xc481, 0xff },
> + { 0xc482, 0x00 }, { 0xc483, 0x40 }, { 0xc484, 0x00 }, { 0xc485, 0x18 },
> + { 0xc486, 0x00 }, { 0xc487, 0x18 },
> + { 0xc488, (OV10635_VTS - 8) * 16 >> 8},
> + { 0xc489, (OV10635_VTS - 8) * 16 & 0xff},
> + { 0xc48a, (OV10635_VTS - 8) * 16 >> 8},
> + { 0xc48b, (OV10635_VTS - 8) * 16 & 0xff}, { 0xc48c, 0x00 },
> + { 0xc48d, 0x04 }, { 0xc48e, 0x00 }, { 0xc48f, 0x04 }, { 0xc490, 0x03 },
> + { 0xc492, 0x20 }, { 0xc493, 0x08 }, { 0xc498, 0x02 }, { 0xc499, 0x00 },
> + { 0xc49a, 0x02 }, { 0xc49b, 0x00 }, { 0xc49c, 0x02 }, { 0xc49d, 0x00 },
> + { 0xc49e, 0x02 }, { 0xc49f, 0x60 }, { 0xc4a0, 0x03 }, { 0xc4a1, 0x00 },
> + { 0xc4a2, 0x04 }, { 0xc4a3, 0x00 }, { 0xc4a4, 0x00 }, { 0xc4a5, 0x10 },
> + { 0xc4a6, 0x00 }, { 0xc4a7, 0x40 }, { 0xc4a8, 0x00 }, { 0xc4a9, 0x80 },
> + { 0xc4aa, 0x0d }, { 0xc4ab, 0x00 }, { 0xc4ac, 0x0f }, { 0xc4ad, 0xc0 },
> + { 0xc4b4, 0x01 }, { 0xc4b5, 0x01 }, { 0xc4b6, 0x00 }, { 0xc4b7, 0x01 },
> + { 0xc4b8, 0x00 }, { 0xc4b9, 0x01 }, { 0xc4ba, 0x01 }, { 0xc4bb, 0x00 },
> + { 0xc4bc, 0x01 }, { 0xc4bd, 0x60 }, { 0xc4be, 0x02 }, { 0xc4bf, 0x33 },
> + { 0xc4c8, 0x03 }, { 0xc4c9, 0xd0 }, { 0xc4ca, 0x0e }, { 0xc4cb, 0x00 },
> + { 0xc4cc, 0x0e }, { 0xc4cd, 0x51 }, { 0xc4ce, 0x0e }, { 0xc4cf, 0x51 },
> + { 0xc4d0, 0x04 }, { 0xc4d1, 0x80 }, { 0xc4e0, 0x04 }, { 0xc4e1, 0x02 },
> + { 0xc4e2, 0x01 }, { 0xc4e4, 0x10 }, { 0xc4e5, 0x20 }, { 0xc4e6, 0x30 },
> + { 0xc4e7, 0x40 }, { 0xc4e8, 0x50 }, { 0xc4e9, 0x60 }, { 0xc4ea, 0x70 },
> + { 0xc4eb, 0x80 }, { 0xc4ec, 0x90 }, { 0xc4ed, 0xa0 }, { 0xc4ee, 0xb0 },
> + { 0xc4ef, 0xc0 }, { 0xc4f0, 0xd0 }, { 0xc4f1, 0xe0 }, { 0xc4f2, 0xf0 },
> + { 0xc4f3, 0x80 }, { 0xc4f4, 0x00 }, { 0xc4f5, 0x20 }, { 0xc4f6, 0x02 },
> + { 0xc4f7, 0x00 }, { 0xc4f8, 0x00 }, { 0xc4f9, 0x00 }, { 0xc4fa, 0x00 },
> + { 0xc4fb, 0x01 }, { 0xc4fc, 0x01 }, { 0xc4fd, 0x00 }, { 0xc4fe, 0x04 },
> + { 0xc4ff, 0x02 }, { 0xc500, 0x48 }, { 0xc501, 0x74 }, { 0xc502, 0x58 },
> + { 0xc503, 0x80 }, { 0xc504, 0x05 }, { 0xc505, 0x80 }, { 0xc506, 0x03 },
> + { 0xc507, 0x80 }, { 0xc508, 0x01 }, { 0xc509, 0xc0 }, { 0xc50a, 0x01 },
> + { 0xc50b, 0xa0 }, { 0xc50c, 0x01 }, { 0xc50d, 0x2c }, { 0xc50e, 0x01 },
> + { 0xc50f, 0x0a }, { 0xc510, 0x00 }, { 0xc511, 0x00 }, { 0xc512, 0xe5 },
> + { 0xc513, 0x14 }, { 0xc514, 0x04 }, { 0xc515, 0x00 }, { 0xc518, OV10635_VTS >> 8},
> + { 0xc519, OV10635_VTS & 0xff}, { 0xc51a, OV10635_HTS >> 8},
> + { 0xc51b, OV10635_HTS & 0xff}, { 0xc2e0, 0x00 }, { 0xc2e1, 0x51 },
> + { 0xc2e2, 0x00 }, { 0xc2e3, 0xd6 }, { 0xc2e4, 0x01 }, { 0xc2e5, 0x5e },
> + { 0xc2e9, 0x01 }, { 0xc2ea, 0x7a }, { 0xc2eb, 0x90 }, { 0xc2ed, 0x00 },
> + { 0xc2ee, 0x7a }, { 0xc2ef, 0x64 }, { 0xc308, 0x00 }, { 0xc309, 0x00 },
> + { 0xc30a, 0x00 }, { 0xc30c, 0x00 }, { 0xc30d, 0x01 }, { 0xc30e, 0x00 },
> + { 0xc30f, 0x00 }, { 0xc310, 0x01 }, { 0xc311, 0x60 }, { 0xc312, 0xff },
> + { 0xc313, 0x08 }, { 0xc314, 0x01 }, { 0xc315, 0x00 }, { 0xc316, 0xff },
> + { 0xc317, 0x0b }, { 0xc318, 0x00 }, { 0xc319, 0x0c }, { 0xc31a, 0x00 },
> + { 0xc31b, 0xe0 }, { 0xc31c, 0x00 }, { 0xc31d, 0x14 }, { 0xc31e, 0x00 },
> + { 0xc31f, 0xc5 }, { 0xc320, 0xff }, { 0xc321, 0x4b }, { 0xc322, 0xff },
> + { 0xc323, 0xf0 }, { 0xc324, 0xff }, { 0xc325, 0xe8 }, { 0xc326, 0x00 },
> + { 0xc327, 0x46 }, { 0xc328, 0xff }, { 0xc329, 0xd2 }, { 0xc32a, 0xff },
> + { 0xc32b, 0xe4 }, { 0xc32c, 0xff }, { 0xc32d, 0xbb }, { 0xc32e, 0x00 },
> + { 0xc32f, 0x61 }, { 0xc330, 0xff }, { 0xc331, 0xf9 }, { 0xc332, 0x00 },
> + { 0xc333, 0xd9 }, { 0xc334, 0x00 }, { 0xc335, 0x2e }, { 0xc336, 0x00 },
> + { 0xc337, 0xb1 }, { 0xc338, 0xff }, { 0xc339, 0x64 }, { 0xc33a, 0xff },
> + { 0xc33b, 0xeb }, { 0xc33c, 0xff }, { 0xc33d, 0xe8 }, { 0xc33e, 0x00 },
> + { 0xc33f, 0x48 }, { 0xc340, 0xff }, { 0xc341, 0xd0 }, { 0xc342, 0xff },
> + { 0xc343, 0xed }, { 0xc344, 0xff }, { 0xc345, 0xad }, { 0xc346, 0x00 },
> + { 0xc347, 0x66 }, { 0xc348, 0x01 }, { 0xc349, 0x00 }, { 0x6700, 0x04 },
> + { 0x6701, 0x7b }, { 0x6702, 0xfd }, { 0x6703, 0xf9 }, { 0x6704, 0x3d },
> + { 0x6705, 0x71 }, { 0x6706, 0x78 }, { 0x6708, 0x05 }, { 0x6f06, 0x6f },
> + { 0x6f07, 0x00 }, { 0x6f0a, 0x6f }, { 0x6f0b, 0x00 }, { 0x6f00, 0x03 },
> + { 0xc34c, 0x01 }, { 0xc34d, 0x00 }, { 0xc34e, 0x46 }, { 0xc34f, 0x55 },
> + { 0xc350, 0x00 }, { 0xc351, 0x40 }, { 0xc352, 0x00 }, { 0xc353, 0xff },
> + { 0xc354, 0x04 }, { 0xc355, 0x08 }, { 0xc356, 0x01 }, { 0xc357, 0xef },
> + { 0xc358, 0x30 }, { 0xc359, 0x01 }, { 0xc35a, 0x64 }, { 0xc35b, 0x46 },
> + { 0xc35c, 0x00 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
> + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
> + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
> + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
> + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
> + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 },
> + { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0x3042, 0xf0 }, { 0xc261, 0x01 },
> + { 0x301b, 0xf0 }, { 0x301c, 0xf0 }, { 0x301a, 0xf0 }, { 0x6f00, 0xc3 },
> + { 0xc46a, 0x30 }, { 0xc46d, 0x20 }, { 0xc464, 0x84 }, { 0xc465, 0x00 },
> + { 0x6f00, 0x03 }, { 0x6f00, 0x43 }, { 0x381c, 0x00 }, { 0x381d, 0x40 },
> + { 0xc454, 0x01 }, { 0x6f00, 0xc3 }, { 0xc454, 0x00 }, { 0xc4b1, 0x02 },
> + { 0xc4b2, 0x01 }, { 0xc4b3, 0x03 }, { 0x6f00, 0x03 }, { 0x6f00, 0x43 },
> + /* enable FSIN (FRAMESYNC input) functionality */
> + { 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
> + { 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
> + { 0x3834, OV10635_VTS >> 8 }, { 0x3835, OV10635_VTS & 0xff },
> + { 0x302e, 0x01 },
> +};
> +
> +struct rdacm20_device {
> + struct device *dev;
> + struct max9271_device *serializer;
> + struct i2c_client *sensor;
> + struct v4l2_subdev sd;
> + struct media_pad pad;
> + struct v4l2_ctrl_handler ctrls;
> + u32 addrs[2];
> +};
> +
> +static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
> +{
> + return container_of(sd, struct rdacm20_device, sd);
> +}
> +
> +static inline struct rdacm20_device *i2c_to_rdacm20(struct i2c_client *client)
> +{
> + return sd_to_rdacm20(i2c_get_clientdata(client));
> +}
> +
> +static int ov10635_read16(struct rdacm20_device *dev, u16 reg)
> +{
> + u8 buf[2] = { reg >> 8, reg & 0xff };
> + int ret;
> +
> + ret = i2c_master_send(dev->sensor, buf, 2);
> + if (ret != 2) {
> + dev_dbg(dev->dev, "%s: register 0x%04x write failed (%d)\n",
> + __func__, reg, ret);
> + return ret;
> + }
> +
> + ret = i2c_master_recv(dev->sensor, buf, 2);
> + if (ret < 0) {
> + dev_dbg(dev->dev, "%s: register 0x%04x read failed (%d)\n",
> + __func__, reg, ret);
> + return ret;
> + }
> +
> + return (buf[0] << 8) | buf[1];
> +}
> +
> +static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
> +{
> + u8 buf[3] = { reg >> 8, reg & 0xff, val };
> + int ret;
> +
> + dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
> +
> + ret = i2c_master_send(dev->sensor, buf, 3);
> + return ret < 0 ? ret : 0;
> +}
> +
> +static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
> +{
> + int ret;
> +
> + ret = __ov10635_write(dev, reg, val);
> + if (ret < 0)
> + dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n",
> + __func__, reg, ret);
> +
> + return ret;
> +}
> +
> +static int ov10635_set_regs(struct rdacm20_device *dev,
> + const struct ov10635_reg *regs,
> + unsigned int nr_regs)
> +{
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < nr_regs; i++) {
> + ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
> + if (ret) {
> + dev_err(dev->dev,
> + "%s: register %u (0x%04x) write failed (%d)\n",
> + __func__, i, regs[i].reg, ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> + struct rdacm20_device *dev = sd_to_rdacm20(sd);
> +
> + return max9271_set_serial_link(dev->serializer, enable);
> +}
> +
> +static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_mbus_code_enum *code)
> +{
> + if (code->pad || code->index > 0)
> + return -EINVAL;
> +
> + code->code = OV10635_FORMAT;
> +
> + return 0;
> +}
> +
> +static int rdacm20_get_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *format)
> +{
> + struct v4l2_mbus_framefmt *mf = &format->format;
> +
> + if (format->pad)
> + return -EINVAL;
> +
> + mf->width = OV10635_WIDTH;
> + mf->height = OV10635_HEIGHT;
> + mf->code = OV10635_FORMAT;
> + mf->colorspace = V4L2_COLORSPACE_RAW;
> + mf->field = V4L2_FIELD_NONE;
> + mf->ycbcr_enc = V4L2_YCBCR_ENC_601;
> + mf->quantization = V4L2_QUANTIZATION_FULL_RANGE;
> + mf->xfer_func = V4L2_XFER_FUNC_NONE;
> +
> + return 0;
> +}
> +
> +static struct v4l2_subdev_video_ops rdacm20_video_ops = {
> + .s_stream = rdacm20_s_stream,
> +};
> +
> +static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
> + .enum_mbus_code = rdacm20_enum_mbus_code,
> + .get_fmt = rdacm20_get_fmt,
> + .set_fmt = rdacm20_get_fmt,
> +};
> +
> +static struct v4l2_subdev_ops rdacm20_subdev_ops = {
> + .video = &rdacm20_video_ops,
> + .pad = &rdacm20_subdev_pad_ops,
> +};
> +
> +static int rdacm20_initialize(struct rdacm20_device *dev)
> +{
> + unsigned int retry = 3;
> + int ret;
> +
> + /* Verify communication with the MAX9271: ping to wakeup. */
> + dev->serializer->client->addr = MAX9271_DEFAULT_ADDR;
> + i2c_smbus_read_byte(dev->serializer->client);
> +
> + /* Serial link disabled during config as it needs a valid pixel clock. */
> + ret = max9271_set_serial_link(dev->serializer, false);
> + if (ret)
> + return ret;
> +
> + /*
> + * Ensure that we have a good link configuration before attempting to
> + * identify the device.
> + */
> + max9271_configure_i2c(dev->serializer, MAX9271_I2CSLVSH_469NS_234NS |
> + MAX9271_I2CSLVTO_1024US |
> + MAX9271_I2CMSTBT_105KBPS);
> +
> + max9271_configure_gmsl_link(dev->serializer);
> +
> + ret = max9271_verify_id(dev->serializer);
> + if (ret < 0)
> + return ret;
> +
> + ret = max9271_set_address(dev->serializer, dev->addrs[0]);
> + if (ret < 0)
> + return ret;
> + dev->serializer->client->addr = dev->addrs[0];
> +
> + /*
> + * Reset the sensor by cycling the OV10635 reset signal connected to the
> + * MAX9271 GPIO1 and verify communication with the OV10635.
> + */
> + max9271_clear_gpios(dev->serializer, MAX9271_GPIO1OUT);
> + mdelay(10);
> + max9271_set_gpios(dev->serializer, MAX9271_GPIO1OUT);
> + mdelay(10);
> +
> +again:
> + ret = ov10635_read16(dev, OV10635_PID);
> + if (ret < 0) {
> + if (retry--)
> + goto again;
> +
> + dev_err(dev->dev, "OV10635 ID read failed (%d)\n",
> + ret);
> + return -ENXIO;
> + }
> +
> + if (ret != OV10635_VERSION) {
> + if (retry--)
> + goto again;
> +
> + dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n",
> + ret);
> + return -ENXIO;
> + }
> +
> + /* Change the sensor I2C address. */
> + ret = ov10635_write(dev, OV10635_SC_CMMN_SCCB_ID,
> + (dev->addrs[1] << 1) |
> + OV10635_SC_CMMN_SCCB_ID_SELECT);
> + if (ret < 0) {
> + dev_err(dev->dev,
> + "OV10635 I2C address change failed (%d)\n", ret);
> + return ret;
> + }
> + dev->sensor->addr = dev->addrs[1];
> + usleep_range(3500, 5000);
> +
> + /* Program the 0V10635 initial configuration. */
> + ret = ov10635_set_regs(dev, ov10635_regs_wizard,
> + ARRAY_SIZE(ov10635_regs_wizard));
> + if (ret)
> + return ret;
> +
> + dev_info(dev->dev, "Identified MAX9271 + OV10635 device\n");
> +
> + return 0;
> +}
> +
> +static int rdacm20_probe(struct i2c_client *client)
> +{
> + struct rdacm20_device *dev;
> + struct fwnode_handle *ep;
> + int ret;
> +
> + dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
> + if (!dev)
> + return -ENOMEM;
> + dev->dev = &client->dev;
> +
> + dev->serializer = devm_kzalloc(&client->dev, sizeof(*dev->serializer),
> + GFP_KERNEL);
> + if (!dev->serializer)
> + return -ENOMEM;
> +
> + dev->serializer->client = client;
> +
> + ret = of_property_read_u32_array(client->dev.of_node, "reg",
> + dev->addrs, 2);
> + if (ret < 0) {
> + dev_err(dev->dev, "Invalid DT reg property: %d\n", ret);
> + return -EINVAL;
> + }
> +
> + /* Create the dummy I2C client for the sensor. */
> + dev->sensor = i2c_new_dummy_device(client->adapter,
> + OV10635_I2C_ADDRESS);
> + if (IS_ERR(dev->sensor)) {
> + ret = PTR_ERR(dev->sensor);
> + goto error;
> + }
> +
> + /* Initialize the hardware. */
> + ret = rdacm20_initialize(dev);
> + if (ret < 0)
> + goto error;
> +
> + /* Initialize and register the subdevice. */
> + v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
> + dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +
> + v4l2_ctrl_handler_init(&dev->ctrls, 1);
> + v4l2_ctrl_new_std(&dev->ctrls, NULL, V4L2_CID_PIXEL_RATE,
> + OV10635_PIXEL_RATE, OV10635_PIXEL_RATE, 1,
> + OV10635_PIXEL_RATE);
> + dev->sd.ctrl_handler = &dev->ctrls;
> +
> + ret = dev->ctrls.error;
> + if (ret)
> + goto error_free_ctrls;
> +
> + dev->pad.flags = MEDIA_PAD_FL_SOURCE;
> + dev->sd.entity.flags |= MEDIA_ENT_F_CAM_SENSOR;
> + ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
> + if (ret < 0)
> + goto error_free_ctrls;
> +
> + ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
> + if (!ep) {
> + dev_err(&client->dev,
> + "Unable to get endpoint in node %pOF\n",
> + client->dev.of_node);
> + ret = -ENOENT;
> + goto error_free_ctrls;
> + }
> + dev->sd.fwnode = ep;
> +
> + ret = v4l2_async_register_subdev(&dev->sd);
> + if (ret)
> + goto error_put_node;
> +
> + return 0;
> +
> +error_put_node:
> + fwnode_handle_put(ep);
> +error_free_ctrls:
> + v4l2_ctrl_handler_free(&dev->ctrls);
> +error:
> + media_entity_cleanup(&dev->sd.entity);
> + if (dev->sensor)
> + i2c_unregister_device(dev->sensor);
> +
> + dev_err(&client->dev, "probe failed\n");
> +
> + return ret;
> +}
> +
> +static int rdacm20_remove(struct i2c_client *client)
> +{
> + struct rdacm20_device *dev = i2c_to_rdacm20(client);
> +
> + fwnode_handle_put(dev->sd.fwnode);
> + v4l2_async_unregister_subdev(&dev->sd);
> + v4l2_ctrl_handler_free(&dev->ctrls);
> + media_entity_cleanup(&dev->sd.entity);
> + i2c_unregister_device(dev->sensor);
> +
> + return 0;
> +}
> +
> +static void rdacm20_shutdown(struct i2c_client *client)
> +{
> + struct rdacm20_device *dev = i2c_to_rdacm20(client);
> +
> + /* make sure stream off during shutdown (reset/reboot) */
> + rdacm20_s_stream(&dev->sd, 0);
> +}
> +
> +static const struct of_device_id rdacm20_of_ids[] = {
> + { .compatible = "imi,rdacm20", },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, rdacm20_of_ids);
> +
> +static struct i2c_driver rdacm20_i2c_driver = {
> + .driver = {
> + .name = "rdacm20",
> + .of_match_table = rdacm20_of_ids,
> + },
> + .probe_new = rdacm20_probe,
> + .remove = rdacm20_remove,
> + .shutdown = rdacm20_shutdown,
> +};
> +
> +module_i2c_driver(rdacm20_i2c_driver);
> +
> +MODULE_DESCRIPTION("GMSL Camera driver for RDACM20");
> +MODULE_AUTHOR("Vladimir Barinov");
> +MODULE_LICENSE("GPL");
> --
> 2.26.0
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [v8-rc1 15/20] squash! max9286: Disable overlap window
2020-04-16 10:50 ` Kieran Bingham
@ 2020-04-16 11:31 ` Jacopo Mondi
2020-04-16 11:31 ` Kieran Bingham
0 siblings, 1 reply; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 11:31 UTC (permalink / raw)
To: Kieran Bingham
Cc: Jacopo Mondi, linux-renesas-soc, laurent.pinchart,
niklas.soderlund, hyunk, manivannan.sadhasivam
Hi Kieran,
On Thu, Apr 16, 2020 at 11:50:07AM +0100, Kieran Bingham wrote:
> Hi Jacopo,
>
> On 16/04/2020 11:40, Jacopo Mondi wrote:
> > From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >
> > Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >
> > ---
> > v2:
> > [Jacopo]
> > - Write register 0x63 and 0x64 directly as going through the function
> > breaks RDACM21 operations
> > ---
> > drivers/media/i2c/max9286.c | 12 ++++++++++++
> > 1 file changed, 12 insertions(+)
> >
> > diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> > index 3ef74ba10074..e0c637d4a7de 100644
> > --- a/drivers/media/i2c/max9286.c
> > +++ b/drivers/media/i2c/max9286.c
> > @@ -966,6 +966,18 @@ static int max9286_setup(struct max9286_priv *priv)
> > max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
> > MAX9286_HVSRC_D14);
> >
> > + /*
> > + * The overlap window seems to provide additional validation by tracking
> > + * the delay between vsync and frame sync, generating an error if the
> > + * delay is bigger than the programmed window, though it's not yet clear
> > + * what value should be set.
> > + *
> > + * As it's an optional value and can be disabled, we do so by setting
> > + * a 0 overlap value.
>
> Are you happy to add the following as part of the removal of the function?
>
> *
> * The overlap window is a 13 bit value, and register 0x64 is
> * shared with ENFSINLAST in BIT(5) which is also set zero.
> *
Not sure, as I noticed failres for RDACM21 when not writing the
supposidely reserved bits.
As we blindly write the full registers I would leave details out until
we figure out what's happening.
>
> > + */
> > + max9286_write(priv, 0x63, 0);
> > + max9286_write(priv, 0x64, 0);
> > +
> > /*
> > * Wait for 2ms to allow the link to resynchronize after the
> > * configuration change.
> >
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [v8-rc1 15/20] squash! max9286: Disable overlap window
2020-04-16 11:31 ` Jacopo Mondi
@ 2020-04-16 11:31 ` Kieran Bingham
2020-04-16 11:44 ` Jacopo Mondi
0 siblings, 1 reply; 29+ messages in thread
From: Kieran Bingham @ 2020-04-16 11:31 UTC (permalink / raw)
To: Jacopo Mondi
Cc: Jacopo Mondi, linux-renesas-soc, laurent.pinchart,
niklas.soderlund, hyunk, manivannan.sadhasivam
On 16/04/2020 12:31, Jacopo Mondi wrote:
> Hi Kieran,
>
> On Thu, Apr 16, 2020 at 11:50:07AM +0100, Kieran Bingham wrote:
>> Hi Jacopo,
>>
>> On 16/04/2020 11:40, Jacopo Mondi wrote:
>>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>
>>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>>>
>>> ---
>>> v2:
>>> [Jacopo]
>>> - Write register 0x63 and 0x64 directly as going through the function
>>> breaks RDACM21 operations
>>> ---
>>> drivers/media/i2c/max9286.c | 12 ++++++++++++
>>> 1 file changed, 12 insertions(+)
>>>
>>> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
>>> index 3ef74ba10074..e0c637d4a7de 100644
>>> --- a/drivers/media/i2c/max9286.c
>>> +++ b/drivers/media/i2c/max9286.c
>>> @@ -966,6 +966,18 @@ static int max9286_setup(struct max9286_priv *priv)
>>> max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
>>> MAX9286_HVSRC_D14);
>>>
>>> + /*
>>> + * The overlap window seems to provide additional validation by tracking
>>> + * the delay between vsync and frame sync, generating an error if the
>>> + * delay is bigger than the programmed window, though it's not yet clear
>>> + * what value should be set.
>>> + *
>>> + * As it's an optional value and can be disabled, we do so by setting
>>> + * a 0 overlap value.
>>
>> Are you happy to add the following as part of the removal of the function?
>>
>> *
>> * The overlap window is a 13 bit value, and register 0x64 is
>> * shared with ENFSINLAST in BIT(5) which is also set zero.
>> *
>
> Not sure, as I noticed failres for RDACM21 when not writing the
> supposidely reserved bits.
>
> As we blindly write the full registers I would leave details out until
> we figure out what's happening.
But that's precisely why I think we should document it :-) Otherwise -
some other person could hit this issue in a different way...
>
>>
>>> + */
>>> + max9286_write(priv, 0x63, 0);
>>> + max9286_write(priv, 0x64, 0);
>>> +
>>> /*
>>> * Wait for 2ms to allow the link to resynchronize after the
>>> * configuration change.
>>>
>>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [v8-rc1 14/20] squash! max9286: Calculate pixel rate
2020-04-16 10:45 ` Kieran Bingham
@ 2020-04-16 11:34 ` Jacopo Mondi
0 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 11:34 UTC (permalink / raw)
To: Kieran Bingham
Cc: Jacopo Mondi, linux-renesas-soc, laurent.pinchart,
niklas.soderlund, hyunk, manivannan.sadhasivam
Hi Kieran,
On Thu, Apr 16, 2020 at 11:45:27AM +0100, Kieran Bingham wrote:
> Hi Jacopo,
>
> There we go - looks like a viable solution to me.
>
> I wonder/suspect if this is the issue the BSP team were having some
> weeks ago too?
Could be. Let's revive the thread once we get this sent to linux-media
>
> Anyway, I'm certainly happy with this.
>
> Reviewed-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
>
>
> On 16/04/2020 11:40, Jacopo Mondi wrote:
> > Calculate the CSI-2 transmitter pixel rate using the one reported from
> > sources.
> >
> > Signed-off-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
> > ---
> > drivers/media/i2c/max9286.c | 50 +++++++++++++++++++++++++++++--------
> > 1 file changed, 40 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> > index 2d71205a1aad..3ef74ba10074 100644
> > --- a/drivers/media/i2c/max9286.c
> > +++ b/drivers/media/i2c/max9286.c
> > @@ -430,9 +430,46 @@ static int max9286_check_config_link(struct max9286_priv *priv,
> > * V4L2 Subdev
> > */
> >
> > -static int max9286_set_pixelrate(struct max9286_priv *priv, s64 rate)
> > +static int max9286_set_pixelrate(struct max9286_priv *priv)
> > {
> > - return v4l2_ctrl_s_ctrl_int64(priv->pixelrate, rate);
> > + struct max9286_source *source = NULL;
> > + u64 pixelrate = 0;
> > +
> > + for_each_source(priv, source) {
> > + struct v4l2_ctrl *ctrl;
> > + u64 source_rate = 0;
> > +
> > + /* Pixel rate is mandatory to be reported by sources. */
> > + ctrl = v4l2_ctrl_find(source->sd->ctrl_handler,
> > + V4L2_CID_PIXEL_RATE);
> > + if (!ctrl) {
> > + pixelrate = 0;
> > + break;
> > + }
> > +
> > + /* All source must report the same pixel rate. */
> > + source_rate = v4l2_ctrl_g_ctrl_int64(ctrl);
> > + if (!pixelrate) {
> > + pixelrate = source_rate;
> > + } else if (pixelrate != source_rate) {
> > + dev_err(&priv->client->dev,
> > + "Unable to calculate pixel rate\n");
>
> Hrm ... I would report "Source pixel rates are different\n"
> ... but I don't think that's a big deal.
That's fine, as you wish :)
>
>
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + if (!pixelrate) {
> > + dev_err(&priv->client->dev,
> > + "No pixel rate control available in sources\n");
> > + return -EINVAL;
> > + }
> > +
> > + /*
> > + * The CSI-2 transmitter pixel rate is the single source rate multiplied
> > + * by the number of available sources.
> > + */
> > + return v4l2_ctrl_s_ctrl_int64(priv->pixelrate,
> > + pixelrate * priv->nsources);
> > }
> >
> > static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> > @@ -496,7 +533,7 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
> > */
> > max9286_configure_i2c(priv, false);
> >
> > - return 0;
> > + return max9286_set_pixelrate(priv);
>
> Aha - at first I thought this would call for every bind event, but we
> already filter so this tail end of the function only executes on the
> last bind :-)
Yes, we get there only when all remotes have been bound.
I wonder what happens though if something goes wrong and we end up returning
an error from _bound(). I assume the media graph won't complete and
that's desirable, so this should be enough
>
>
>
> > }
> >
> > static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
> > @@ -670,7 +707,6 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
> > {
> > struct max9286_priv *priv = sd_to_max9286(sd);
> > struct v4l2_mbus_framefmt *cfg_fmt;
> > - s64 pixelrate;
> >
> > if (format->pad >= MAX9286_SRC_PAD)
> > return -EINVAL;
> > @@ -695,12 +731,6 @@ static int max9286_set_fmt(struct v4l2_subdev *sd,
> > *cfg_fmt = format->format;
> > mutex_unlock(&priv->mutex);
> >
> > - /* Update pixel rate for the CSI2 receiver */
> > - pixelrate = cfg_fmt->width * cfg_fmt->height
> > - * priv->nsources * 30 /*FPS*/;
> > -
> > - max9286_set_pixelrate(priv, pixelrate);
> > -
> > return 0;
> > }
> >
> >
>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [v8-rc1 15/20] squash! max9286: Disable overlap window
2020-04-16 11:31 ` Kieran Bingham
@ 2020-04-16 11:44 ` Jacopo Mondi
0 siblings, 0 replies; 29+ messages in thread
From: Jacopo Mondi @ 2020-04-16 11:44 UTC (permalink / raw)
To: Kieran Bingham
Cc: Jacopo Mondi, linux-renesas-soc, laurent.pinchart,
niklas.soderlund, hyunk, manivannan.sadhasivam
Hi Kieran,
On Thu, Apr 16, 2020 at 12:31:32PM +0100, Kieran Bingham wrote:
> On 16/04/2020 12:31, Jacopo Mondi wrote:
> > Hi Kieran,
> >
> > On Thu, Apr 16, 2020 at 11:50:07AM +0100, Kieran Bingham wrote:
> >> Hi Jacopo,
> >>
> >> On 16/04/2020 11:40, Jacopo Mondi wrote:
> >>> From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >>>
> >>> Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
> >>>
> >>> ---
> >>> v2:
> >>> [Jacopo]
> >>> - Write register 0x63 and 0x64 directly as going through the function
> >>> breaks RDACM21 operations
> >>> ---
> >>> drivers/media/i2c/max9286.c | 12 ++++++++++++
> >>> 1 file changed, 12 insertions(+)
> >>>
> >>> diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
> >>> index 3ef74ba10074..e0c637d4a7de 100644
> >>> --- a/drivers/media/i2c/max9286.c
> >>> +++ b/drivers/media/i2c/max9286.c
> >>> @@ -966,6 +966,18 @@ static int max9286_setup(struct max9286_priv *priv)
> >>> max9286_write(priv, 0x0c, MAX9286_HVEN | MAX9286_INVVS |
> >>> MAX9286_HVSRC_D14);
> >>>
> >>> + /*
> >>> + * The overlap window seems to provide additional validation by tracking
> >>> + * the delay between vsync and frame sync, generating an error if the
> >>> + * delay is bigger than the programmed window, though it's not yet clear
> >>> + * what value should be set.
> >>> + *
> >>> + * As it's an optional value and can be disabled, we do so by setting
> >>> + * a 0 overlap value.
> >>
> >> Are you happy to add the following as part of the removal of the function?
> >>
> >> *
> >> * The overlap window is a 13 bit value, and register 0x64 is
> >> * shared with ENFSINLAST in BIT(5) which is also set zero.
> >> *
> >
> > Not sure, as I noticed failres for RDACM21 when not writing the
> > supposidely reserved bits.
> >
> > As we blindly write the full registers I would leave details out until
> > we figure out what's happening.
>
> But that's precisely why I think we should document it :-) Otherwise -
> some other person could hit this issue in a different way...
In that case yes, I was concerned about the "reserved bits", but
please go ahead and add that comment.
>
> >
> >>
> >>> + */
> >>> + max9286_write(priv, 0x63, 0);
> >>> + max9286_write(priv, 0x64, 0);
> >>> +
> >>> /*
> >>> * Wait for 2ms to allow the link to resynchronize after the
> >>> * configuration change.
> >>>
> >>
>
^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~2020-04-16 11:44 UTC | newest]
Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-16 10:40 [v8-rc1 00/20] GMSL: max9286-v8-rc1 + RDAMC20-v8 Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 01/20] media: i2c: Add MAX9286 driver Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 02/20] fixup! " Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 03/20] squash!: max9286: Rebase on latest media/master Jacopo Mondi
2020-04-16 10:54 ` Kieran Bingham
2020-04-16 10:40 ` [v8-rc1 04/20] squash! max9286: Update the bound_sources mask on unbind Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 05/20] squash! max9286: convert probe kzalloc Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 06/20] squash! max9286: Fix cleanup path from GPIO powerdown Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 07/20] squash! max9286: cleanup GPIO device registration fail path Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 08/20] squash! max9286: Convert to use devm_regulator_get() Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 09/20] squash! max9286: Fit max9286_parse_dt print on one line Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 10/20] squash! max9286: Move multi-device workarounds out of upstream Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 11/20] squash! max9286: Remove I2C mod-table Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 12/20] sqaush! max9286: Lock format changes Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 13/20] squash! max9286: Implement Pixelrate control Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 14/20] squash! max9286: Calculate pixel rate Jacopo Mondi
2020-04-16 10:45 ` Kieran Bingham
2020-04-16 11:34 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 15/20] squash! max9286: Disable overlap window Jacopo Mondi
2020-04-16 10:50 ` Kieran Bingham
2020-04-16 11:31 ` Jacopo Mondi
2020-04-16 11:31 ` Kieran Bingham
2020-04-16 11:44 ` Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 16/20] sqaush! max9286: Describe pad index usage Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 17/20] sqaush! max9286: Remove poc_enabled workaround Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 18/20] squash! max9286: Put of node on error Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 19/20] dt-bindings: media: i2c: Add bindings for IMI RDACM2x Jacopo Mondi
2020-04-16 10:40 ` [v8-rc1 20/20] media: i2c: Add RDACM20 driver Jacopo Mondi
2020-04-16 11:08 ` Jacopo Mondi
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.