mbox series

[v6,0/8] i2c-atr and FPDLink

Message ID 20230105140307.272052-1-tomi.valkeinen@ideasonboard.com
Headers show
Series i2c-atr and FPDLink | expand

Message

Tomi Valkeinen Jan. 5, 2023, 2:02 p.m. UTC
Hi,

You can find the v5 from:

https://lore.kernel.org/all/20221208104006.316606-1-tomi.valkeinen@ideasonboard.com/

There has again been quite a lot of changes. I will send a diff of v5 to
v6 separately to give a better idea of the changes. Here's are some of
the changes:

- Added namespace for i2c exports
- Consistent naming for FPD-Link
- Added more DT descriptions and kdocs
- Various DT binding improvements suggested in the review
- MAINTAINERS entry for fpd-link
- Use defines instead of BIT(x) in the FPD-Link drivers
- Use pad source/sink defines instead of a number
- Dropped unnecessary debug prints
- UB953: added data-lanes DT property
- UB960: added hsync-active & vsync-active DT properties

 Tomi

Luca Ceresoli (2):
  i2c: core: let adapters be notified of client attach/detach
  i2c: add I2C Address Translator (ATR) support

Tomi Valkeinen (6):
  dt-bindings: media: add TI DS90UB913 FPD-Link III Serializer
  dt-bindings: media: add TI DS90UB953 FPD-Link III Serializer
  dt-bindings: media: add TI DS90UB960 FPD-Link III Deserializer
  media: i2c: add DS90UB960 driver
  media: i2c: add DS90UB913 driver
  media: i2c: add DS90UB953 driver

 .../bindings/media/i2c/ti,ds90ub913.yaml      |  133 +
 .../bindings/media/i2c/ti,ds90ub953.yaml      |  134 +
 .../bindings/media/i2c/ti,ds90ub960.yaml      |  402 ++
 Documentation/i2c/index.rst                   |    1 +
 Documentation/i2c/muxes/i2c-atr.rst           |   83 +
 MAINTAINERS                                   |   16 +
 drivers/i2c/Kconfig                           |    9 +
 drivers/i2c/Makefile                          |    1 +
 drivers/i2c/i2c-atr.c                         |  501 ++
 drivers/i2c/i2c-core-base.c                   |   21 +-
 drivers/media/i2c/Kconfig                     |   47 +
 drivers/media/i2c/Makefile                    |    3 +
 drivers/media/i2c/ds90ub913.c                 |  871 ++++
 drivers/media/i2c/ds90ub953.c                 | 1590 ++++++
 drivers/media/i2c/ds90ub960.c                 | 4295 +++++++++++++++++
 include/linux/i2c-atr.h                       |  118 +
 include/linux/i2c.h                           |   16 +
 include/media/i2c/ds90ub9xx.h                 |   16 +
 18 files changed, 8256 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml
 create mode 100644 Documentation/i2c/muxes/i2c-atr.rst
 create mode 100644 drivers/i2c/i2c-atr.c
 create mode 100644 drivers/media/i2c/ds90ub913.c
 create mode 100644 drivers/media/i2c/ds90ub953.c
 create mode 100644 drivers/media/i2c/ds90ub960.c
 create mode 100644 include/linux/i2c-atr.h
 create mode 100644 include/media/i2c/ds90ub9xx.h


base-commit: 76dcd734eca23168cb008912c0f69ff408905235
prerequisite-patch-id: 341bbdcbd14f0b6f38c88f1390f9437873a03430
prerequisite-patch-id: e5d34269150d94ecb7fd670ad31e14aaae78e099
prerequisite-patch-id: 24ff7b1c013fa9acd8b93c2506eae39eb2a1021c
prerequisite-patch-id: 15edf6091d7c2d340f25711961bb8459addc8d58
prerequisite-patch-id: 9beff6fd1d22dbfe565931de81b27739e45ced05
prerequisite-patch-id: 37bc00c7315371c9a906af0d718ecedd871a43e0
prerequisite-patch-id: 8218595c50635ab48a9c8cd8c580da9f431d539f
prerequisite-patch-id: 7f163276a04d212d009ee6e96fb545d5b781cc44
prerequisite-patch-id: 0db299d92565612520411ef624e097131feb2d97
prerequisite-patch-id: 6b521ad1868b9a507930dd6a2e0e2d19b5947456
prerequisite-patch-id: d32e1105a1285cceac8fd8e84649070174a6b23e
prerequisite-patch-id: 2927a4ef079c55e7807cda150d4b9eb29978136a
prerequisite-patch-id: 77546a2e202557910e9a3635534163cc80134ea4
prerequisite-patch-id: 462fabb2b21019636e1f7e793dafc51caf40400e
prerequisite-patch-id: 604c8c3f3e7fab7698eb73795bae573f88b808db
prerequisite-patch-id: 75a3d7b072e718fb2bd854743b9b2023d68a822b
prerequisite-patch-id: a9c64a3360938ac230261ffc3e0cbd8cb66550d3
prerequisite-patch-id: a5c57341047e53ecaeb28181319700dd3ace62aa
prerequisite-patch-id: e9b8da97c9fea3cd3268adf8387cb3df179649e5
prerequisite-patch-id: 2767b8af9e0742b52b81b2dbce3b5ce5203e3610

Comments

Tomi Valkeinen Jan. 5, 2023, 2:05 p.m. UTC | #1
On 05/01/2023 16:02, Tomi Valkeinen wrote:
> Hi,
> 
> You can find the v5 from:
> 
> https://lore.kernel.org/all/20221208104006.316606-1-tomi.valkeinen@ideasonboard.com/
> 
> There has again been quite a lot of changes. I will send a diff of v5 to
> v6 separately to give a better idea of the changes. Here's are some of
> the changes:

And here's the diff:

diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml
index 3a5b34c6bb64..f6612bb0f667 100644
--- a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub913.yaml
@@ -4,13 +4,13 @@
  $id: http://devicetree.org/schemas/media/i2c/ti,ds90ub913.yaml#
  $schema: http://devicetree.org/meta-schemas/core.yaml#
  
-title: Texas Instruments DS90UB913 FPD-Link 3 Serializer
+title: Texas Instruments DS90UB913 FPD-Link III Serializer
  
  maintainers:
    - Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
  
  description:
-  The TI DS90UB913 is an FPD-Link 3 video serializer for parallel video.
+  The TI DS90UB913 is an FPD-Link III video serializer for parallel video.
  
  properties:
    compatible:
@@ -19,6 +19,10 @@ properties:
  
    '#gpio-cells':
      const: 2
+    description:
+      First cell is the GPO pin number, second cell is the flags. The GPO pin
+      number must be in range of [0, 3]. Note that GPOs 2 and 3 are not
+      available in external oscillator mode.
  
    gpio-controller: true
  
@@ -41,17 +45,24 @@ properties:
        port@0:
          $ref: /schemas/graph.yaml#/$defs/port-base
          unevaluatedProperties: false
-        description: CSI-2 input port
+        description: Parallel input port
  
          properties:
            endpoint:
              $ref: /schemas/media/video-interfaces.yaml#
              unevaluatedProperties: false
  
+            required:
+              - pclk-sample
+
        port@1:
          $ref: /schemas/graph.yaml#/properties/port
          unevaluatedProperties: false
-        description: FPD-Link 3 output port
+        description: FPD-Link III output port
+
+    required:
+      - port@0
+      - port@1
  
    i2c:
      $ref: /schemas/i2c/i2c-controller.yaml#
@@ -89,6 +100,7 @@ examples:
            reg = <0>;
            ub913_in: endpoint {
              remote-endpoint = <&sensor_out>;
+            pclk-sample = <1>;
            };
          };
  
diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml
index fd7d25d93e2c..2030366994d1 100644
--- a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub953.yaml
@@ -4,13 +4,13 @@
  $id: http://devicetree.org/schemas/media/i2c/ti,ds90ub953.yaml#
  $schema: http://devicetree.org/meta-schemas/core.yaml#
  
-title: Texas Instruments DS90UB953 FPD-Link 3 Serializer
+title: Texas Instruments DS90UB953 FPD-Link III Serializer
  
  maintainers:
    - Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
  
  description:
-  The TI DS90UB953 is an FPD-Link 3 video serializer for MIPI CSI-2.
+  The TI DS90UB953 is an FPD-Link III video serializer for MIPI CSI-2.
  
  properties:
    compatible:
@@ -20,9 +20,21 @@ properties:
  
    '#gpio-cells':
      const: 2
+    description:
+      First cell is the GPIO pin number, second cell is the flags. The GPIO pin
+      number must be in range of [0, 3].
  
    gpio-controller: true
  
+  clocks:
+    maxItems: 1
+    description:
+      Reference clock connected to the CLKIN pin.
+
+  clock-names:
+    items:
+      - const: clkin
+
    '#clock-cells':
      const: 0
  
@@ -40,10 +52,17 @@ properties:
              $ref: /schemas/media/video-interfaces.yaml#
              unevaluatedProperties: false
  
+            required:
+              - data-lanes
+
        port@1:
          $ref: /schemas/graph.yaml#/properties/port
          unevaluatedProperties: false
-        description: FPD-Link 3 output port
+        description: FPD-Link III output port
+
+    required:
+      - port@0
+      - port@1
  
    i2c:
      $ref: /schemas/i2c/i2c-controller.yaml#
@@ -101,6 +120,9 @@ examples:
  
            reset-gpios = <&serializer 0 GPIO_ACTIVE_LOW>;
  
+          clocks = <&serializer>;
+          clock-names = "inck";
+
            port {
              sensor_out: endpoint {
                remote-endpoint = <&ub953_in>;
diff --git a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml
index d8b5e219d420..664799ae55be 100644
--- a/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ti,ds90ub960.yaml
@@ -21,8 +21,6 @@ properties:
  
    reg:
      maxItems: 1
-    description:
-      i2c addresses for the deserializer and the serializers
  
    clocks:
      maxItems: 1
@@ -41,12 +39,13 @@ properties:
    i2c-alias-pool:
      $ref: /schemas/types.yaml#/definitions/uint16-array
      description:
-      i2c alias pool is a pool of i2c addresses on the main i2c bus that can be
-      used to access the remote peripherals. The addresses must be available,
-      not used by any other peripheral. Each remote peripheral is assigned an
-      alias from the pool, and transactions to that address will be forwarded
-      to the remote peripheral, with the address translated to the remote
-      peripheral's real address.
+      I2C alias pool is a pool of I2C addresses on the main I2C bus that can be
+      used to access the remote peripherals on the serializer's I2C bus. The
+      addresses must be available, not used by any other peripheral. Each
+      remote peripheral is assigned an alias from the pool, and transactions to
+      that address will be forwarded to the remote peripheral, with the address
+      translated to the remote peripheral's real address. This property is not
+      needed if there are no I2C addressable remote peripherals.
  
    links:
      type: object
@@ -65,7 +64,7 @@ properties:
            Enable manual strobe position and EQ level
  
      patternProperties:
-      '^link@[0-9a-f]+$':
+      '^link@[0-3]$':
          type: object
          additionalProperties: false
          properties:
@@ -75,8 +74,8 @@ properties:
  
            i2c-alias:
              description:
-              The i2c address used for the serializer. Transactions to this
-              address on the i2c bus where the deserializer resides are
+              The I2C address used for the serializer. Transactions to this
+              address on the I2C bus where the deserializer resides are
                forwarded to the serializer.
  
            ti,rx-mode:
@@ -87,14 +86,18 @@ properties:
                - 2 # RAW12 LF
                - 3 # CSI2 SYNC
                - 4 # CSI2 NON-SYNC
-            description: FPD-Link Input Mode
+            description:
+              FPD-Link Input Mode. This should reflect the hardware and the
+              default mode of the connected camera module.
  
            ti,cdr-mode:
              $ref: /schemas/types.yaml#/definitions/uint32
              enum:
-              - 0 # FPD3
-              - 1 # FPD4
-            description: FPD-Link CDR Mode
+              - 0 # FPD-Link III
+              - 1 # FPD-Link IV
+            description:
+              FPD-Link CDR Mode. This should reflect the hardware and the
+              default mode of the connected camera module.
  
            ti,strobe-pos:
              $ref: /schemas/types.yaml#/definitions/int32
@@ -122,25 +125,57 @@ properties:
  
      properties:
        port@0:
-        $ref: /schemas/graph.yaml#/properties/port
+        $ref: /schemas/graph.yaml#/$defs/port-base
          unevaluatedProperties: false
          description: FPD-Link input 0
  
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            description:
+              Endpoint for FPD-Link port. If the RX mode for this port is RAW,
+              hsync-active and vsync-active must be defined.
+
        port@1:
-        $ref: /schemas/graph.yaml#/properties/port
+        $ref: /schemas/graph.yaml#/$defs/port-base
          unevaluatedProperties: false
          description: FPD-Link input 1
  
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            description:
+              Endpoint for FPD-Link port. If the RX mode for this port is RAW,
+              hsync-active and vsync-active must be defined.
+
        port@2:
-        $ref: /schemas/graph.yaml#/properties/port
+        $ref: /schemas/graph.yaml#/$defs/port-base
          unevaluatedProperties: false
          description: FPD-Link input 2
  
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            description:
+              Endpoint for FPD-Link port. If the RX mode for this port is RAW,
+              hsync-active and vsync-active must be defined.
+
        port@3:
-        $ref: /schemas/graph.yaml#/properties/port
+        $ref: /schemas/graph.yaml#/$defs/port-base
          unevaluatedProperties: false
          description: FPD-Link input 3
  
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            unevaluatedProperties: false
+            description:
+              Endpoint for FPD-Link port. If the RX mode for this port is RAW,
+              hsync-active and vsync-active must be defined.
+
        port@4:
          $ref: /schemas/graph.yaml#/$defs/port-base
          unevaluatedProperties: false
@@ -156,6 +191,9 @@ properties:
                  minItems: 1
                  maxItems: 4
  
+            required:
+              - data-lanes
+
        port@5:
          $ref: /schemas/graph.yaml#/$defs/port-base
          unevaluatedProperties: false
@@ -171,6 +209,9 @@ properties:
                  minItems: 1
                  maxItems: 4
  
+            required:
+              - data-lanes
+
  required:
    - compatible
    - reg
@@ -219,6 +260,8 @@ examples:
  
              ub960_fpd3_2_in: endpoint {
                remote-endpoint = <&ub913_2_out>;
+              hsync-active = <0>;
+              vsync-active = <1>;
              };
            };
  
@@ -321,6 +364,7 @@ examples:
                    reg = <0>;
                    ub913_2_in: endpoint {
                      remote-endpoint = <&sensor_2_out>;
+                    pclk-sample = <1>;
                    };
                  };
  
diff --git a/Documentation/i2c/muxes/i2c-atr.rst b/Documentation/i2c/muxes/i2c-atr.rst
index 14597c9ec19b..c7e060ca682d 100644
--- a/Documentation/i2c/muxes/i2c-atr.rst
+++ b/Documentation/i2c/muxes/i2c-atr.rst
@@ -76,3 +76,8 @@ Usage:
      alias_id parameter
   3. When the detach callback is called, deconfigure the alias from
      your chip and put it back in the pool for later usage
+
+I2C ATR functions and data structures
+-------------------------------------
+
+.. kernel-doc:: include/linux/i2c-atr.h
diff --git a/MAINTAINERS b/MAINTAINERS
index ba716f2861cf..d0dc8572191d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20626,6 +20626,14 @@ F:	drivers/misc/tifm*
  F:	drivers/mmc/host/tifm_sd.c
  F:	include/linux/tifm.h
  
+TI FPD-LINK DRIVERS
+M:	Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/media/i2c/ti,ds90*
+F:	drivers/media/i2c/ds90*
+F:	include/media/i2c/ds90*
+
  TI KEYSTONE MULTICORE NAVIGATOR DRIVERS
  M:	Nishanth Menon <nm@ti.com>
  M:	Santosh Shilimkar <ssantosh@kernel.org>
diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c
index 1d3b25a6550f..0668c98886ad 100644
--- a/drivers/i2c/i2c-atr.c
+++ b/drivers/i2c/i2c-atr.c
@@ -3,6 +3,7 @@
   * I2C Address Translator
   *
   * Copyright (c) 2019,2022 Luca Ceresoli <luca@lucaceresoli.net>
+ * Copyright (c) 2022,2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
   *
   * Originally based on i2c-mux.c
   */
@@ -19,7 +20,7 @@
  #define ATR_MAX_SYMLINK_LEN 16	/* Longest name is 10 chars: "channel-99" */
  
  /**
- * struct i2c_atr_cli2alias_pair - Hold the alias assigned to a client.
+ * struct i2c_atr_cli2alias_pair - Holds the alias assigned to a client.
   * @node:   List node
   * @client: Pointer to the client on the child bus
   * @alias:  I2C alias address assigned by the driver.
@@ -32,7 +33,17 @@ struct i2c_atr_cli2alias_pair {
  	u16 alias;
  };
  
-/* Data for each channel (child bus) */
+/**
+ * struct i2c_atr_chan - Data for a channel.
+ * @adap:            The &struct i2c_adapter for the channel
+ * @atr:             The parent I2C ATR
+ * @chan_id:         The ID of this channel
+ * @alias_list:      List of @struct i2c_atr_cli2alias_pair containing the
+ *                   assigned aliases
+ * @orig_addrs_lock: Mutex protecting @orig_addrs
+ * @orig_addrs:      Buffer used to store the original addresses during transmit
+ * @orig_addrs_size: Size of @orig_addrs
+ */
  struct i2c_atr_chan {
  	struct i2c_adapter adap;
  	struct i2c_atr *atr;
@@ -40,9 +51,36 @@ struct i2c_atr_chan {
  
  	struct list_head alias_list;
  
+	/* Lock orig_addrs during xfer */
+	struct mutex orig_addrs_lock;
  	u16 *orig_addrs;
  	unsigned int orig_addrs_size;
-	struct mutex orig_addrs_lock; /* Lock orig_addrs during xfer */
+};
+
+/**
+ * struct i2c_atr - The I2C ATR instance
+ * @parent:    The parent &struct i2c_adapter
+ * @dev:       The device that owns the I2C ATR instance
+ * @ops:       &struct i2c_atr_ops
+ * @priv:      Private driver data, set with i2c_atr_set_driver_data()
+ * @algo:      The &struct i2c_algorithm for adapters
+ * @lock:      Lock for the I2C bus segment (see &struct i2c_lock_operations)
+ * @max_adapters: Maximum number of adapters this I2C ATR can have
+ * @adapter:   Array of adapters
+ */
+struct i2c_atr {
+	struct i2c_adapter *parent;
+	struct device *dev;
+	const struct i2c_atr_ops *ops;
+
+	void *priv;
+
+	struct i2c_algorithm algo;
+	/* lock for the I2C bus segment (see struct i2c_lock_operations) */
+	struct mutex lock;
+	int max_adapters;
+
+	struct i2c_adapter *adapter[];
  };
  
  static struct i2c_atr_cli2alias_pair *
@@ -90,8 +128,7 @@ static int i2c_atr_map_msgs(struct i2c_atr_chan *chan, struct i2c_msg *msgs,
  	if (unlikely(chan->orig_addrs_size < num)) {
  		u16 *new_buf;
  
-		new_buf = kmalloc_array(num, sizeof(chan->orig_addrs[0]),
-					GFP_KERNEL);
+		new_buf = kmalloc_array(num, sizeof(*new_buf), GFP_KERNEL);
  		if (!new_buf)
  			return -ENOMEM;
  
@@ -252,10 +289,6 @@ static int i2c_atr_attach_client(struct i2c_adapter *adapter,
  				      &alias_id);
  	if (ret)
  		goto err_free;
-	if (alias_id == 0) {
-		ret = -EINVAL;
-		goto err_free;
-	}
  
  	c2a->client = client;
  	c2a->alias = alias_id;
@@ -265,6 +298,7 @@ static int i2c_atr_attach_client(struct i2c_adapter *adapter,
  
  err_free:
  	kfree(c2a);
+
  	return ret;
  }
  
@@ -289,27 +323,45 @@ static const struct i2c_attach_operations i2c_atr_attach_ops = {
  	.detach_client = i2c_atr_detach_client,
  };
  
-/**
- * i2c_atr_add_adapter - Create a child ("downstream") I2C bus.
- * @atr:        The I2C ATR
- * @chan_id:    Index of the new adapter (0 .. max_adapters-1).  This value is
- *              passed to the callbacks in `struct i2c_atr_ops`.
- * @bus_handle: The fwnode handle that points to the adapter's i2c
- *              peripherals, or NULL.
- *
- * After calling this function a new i2c bus will appear. Adding and
- * removing devices on the downstream bus will result in calls to the
- * `attach_client` and `detach_client` callbacks for the driver to assign
- * an alias to the device.
- *
- * The adapter's fwnode is set to 'bus_handle', or if 'bus_handle' is NULL the
- * function looks for a child node whose 'reg' property matches the chan_id
- * under the i2c-atr device's 'i2c-atr' node.
+struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
+			    const struct i2c_atr_ops *ops, int max_adapters)
+{
+	struct i2c_atr *atr;
+
+	if (max_adapters > ATR_MAX_ADAPTERS)
+		return ERR_PTR(-EINVAL);
+
+	if (!ops || !ops->attach_client || !ops->detach_client)
+		return ERR_PTR(-EINVAL);
+
+	atr = devm_kzalloc(dev, struct_size(atr, adapter, max_adapters),
+			   GFP_KERNEL);
+	if (!atr)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&atr->lock);
+
+	atr->parent = parent;
+	atr->dev = dev;
+	atr->ops = ops;
+	atr->max_adapters = max_adapters;
+
+	if (parent->algo->master_xfer)
+		atr->algo.master_xfer = i2c_atr_master_xfer;
+	if (parent->algo->smbus_xfer)
+		atr->algo.smbus_xfer = i2c_atr_smbus_xfer;
+	atr->algo.functionality = i2c_atr_functionality;
+
+	return atr;
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_new, I2C_ATR);
+
+void i2c_atr_delete(struct i2c_atr *atr)
+{
+	mutex_destroy(&atr->lock);
+}
+EXPORT_SYMBOL_NS_GPL(i2c_atr_delete, I2C_ATR);
  
- * Call i2c_atr_del_adapter() to remove the adapter.
- *
- * Return: 0 on success, a negative error code otherwise.
- */
  int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
  			struct fwnode_handle *bus_handle)
  {
@@ -375,7 +427,7 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
  	if (ret) {
  		dev_err(dev, "failed to add atr-adapter %u (error=%d)\n",
  			chan_id, ret);
-		goto err_mutex_destroy;
+		goto err_fwnode_put;
  	}
  
  	snprintf(symlink_name, sizeof(symlink_name), "channel-%u",
@@ -391,22 +443,17 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
  	dev_dbg(dev, "Added ATR child bus %d\n", i2c_adapter_id(&chan->adap));
  
  	atr->adapter[chan_id] = &chan->adap;
+
  	return 0;
  
-err_mutex_destroy:
+err_fwnode_put:
  	fwnode_handle_put(dev_fwnode(&chan->adap.dev));
  	mutex_destroy(&chan->orig_addrs_lock);
  	kfree(chan);
  	return ret;
  }
-EXPORT_SYMBOL_GPL(i2c_atr_add_adapter);
+EXPORT_SYMBOL_NS_GPL(i2c_atr_add_adapter, I2C_ATR);
  
-/**
- * i2c_atr_del_adapter - Remove a child ("downstream") I2C bus added by
- * i2c_atr_del_adapter().
- * @atr:     The I2C ATR
- * @chan_id: Index of the `adapter to be removed (0 .. max_adapters-1)
- */
  void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id)
  {
  	char symlink_name[ATR_MAX_SYMLINK_LEN];
@@ -416,10 +463,8 @@ void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id)
  	struct fwnode_handle *fwnode = dev_fwnode(&adap->dev);
  	struct device *dev = atr->dev;
  
-	if (!atr->adapter[chan_id]) {
-		dev_err(dev, "Adapter %d does not exist\n", chan_id);
+	if (!adap)
  		return;
-	}
  
  	dev_dbg(dev, "Removing ATR child bus %d\n", i2c_adapter_id(adap));
  
@@ -436,68 +481,21 @@ void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id)
  	kfree(chan->orig_addrs);
  	kfree(chan);
  }
-EXPORT_SYMBOL_GPL(i2c_atr_del_adapter);
+EXPORT_SYMBOL_NS_GPL(i2c_atr_del_adapter, I2C_ATR);
  
-/**
- * i2c_atr_new() - Allocate and initialize an I2C ATR helper.
- * @parent:       The parent (upstream) adapter
- * @dev:          The device acting as an ATR
- * @ops:          Driver-specific callbacks
- * @max_adapters: Maximum number of child adapters
- *
- * The new ATR helper is connected to the parent adapter but has no child
- * adapters. Call i2c_atr_add_adapter() to add some.
- *
- * Call i2c_atr_delete() to remove.
- *
- * Return: pointer to the new ATR helper object, or ERR_PTR
- */
-struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
-			    const struct i2c_atr_ops *ops, int max_adapters)
+void i2c_atr_set_driver_data(struct i2c_atr *atr, void *data)
  {
-	struct i2c_atr *atr;
-
-	if (max_adapters > ATR_MAX_ADAPTERS)
-		return ERR_PTR(-EINVAL);
-
-	if (!ops || !ops->attach_client || !ops->detach_client)
-		return ERR_PTR(-EINVAL);
-
-	atr = devm_kzalloc(dev, struct_size(atr, adapter, max_adapters),
-			   GFP_KERNEL);
-	if (!atr)
-		return ERR_PTR(-ENOMEM);
-
-	mutex_init(&atr->lock);
-
-	atr->parent = parent;
-	atr->dev = dev;
-	atr->ops = ops;
-	atr->max_adapters = max_adapters;
-
-	if (parent->algo->master_xfer)
-		atr->algo.master_xfer = i2c_atr_master_xfer;
-	if (parent->algo->smbus_xfer)
-		atr->algo.smbus_xfer = i2c_atr_smbus_xfer;
-	atr->algo.functionality = i2c_atr_functionality;
-
-	return atr;
+	atr->priv = data;
  }
-EXPORT_SYMBOL_GPL(i2c_atr_new);
+EXPORT_SYMBOL_NS_GPL(i2c_atr_set_driver_data, I2C_ATR);
  
-/**
- * i2c_atr_delete - Delete an I2C ATR helper.
- * @atr: I2C ATR helper to be deleted.
- *
- * Precondition: all the adapters added with i2c_atr_add_adapter() mumst be
- * removed by calling i2c_atr_del_adapter().
- */
-void i2c_atr_delete(struct i2c_atr *atr)
+void *i2c_atr_get_driver_data(struct i2c_atr *atr)
  {
-	mutex_destroy(&atr->lock);
+	return atr->priv;
  }
-EXPORT_SYMBOL_GPL(i2c_atr_delete);
+EXPORT_SYMBOL_NS_GPL(i2c_atr_get_driver_data, I2C_ATR);
  
-MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
  MODULE_DESCRIPTION("I2C Address Translator");
  MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 1bb60f256a1e..8a0888ba89e5 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -916,6 +916,7 @@ int i2c_dev_irq_from_resources(const struct resource *resources,
  struct i2c_client *
  i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
  {
+	const struct i2c_attach_operations *attach_ops;
  	struct i2c_client	*client;
  	int			status;
  
@@ -967,9 +968,10 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
  		}
  	}
  
-	if (adap->attach_ops &&
-	    adap->attach_ops->attach_client &&
-	    adap->attach_ops->attach_client(adap, info, client) != 0)
+	attach_ops = adap->attach_ops;
+
+	if (attach_ops && attach_ops->attach_client &&
+	    attach_ops->attach_client(adap, info, client))
  		goto out_remove_swnode;
  
  	status = device_register(&client->dev);
@@ -982,8 +984,8 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
  	return client;
  
  out_detach_client:
-	if (adap->attach_ops && adap->attach_ops->detach_client)
-		adap->attach_ops->detach_client(adap, client);
+	if (attach_ops && attach_ops->detach_client)
+		attach_ops->detach_client(adap, client);
  out_remove_swnode:
  	device_remove_software_node(&client->dev);
  out_err_put_of_node:
@@ -1005,16 +1007,17 @@ EXPORT_SYMBOL_GPL(i2c_new_client_device);
   */
  void i2c_unregister_device(struct i2c_client *client)
  {
+	const struct i2c_attach_operations *attach_ops;
  	struct i2c_adapter *adap;
  
  	if (IS_ERR_OR_NULL(client))
  		return;
  
  	adap = client->adapter;
+	attach_ops = adap->attach_ops;
  
-	if (adap->attach_ops &&
-	    adap->attach_ops->detach_client)
-		adap->attach_ops->detach_client(adap, client);
+	if (attach_ops && attach_ops->detach_client)
+		attach_ops->detach_client(adap, client);
  
  	if (client->dev.of_node) {
  		of_node_clear_flag(client->dev.of_node, OF_POPULATED);
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index b24f89f58807..29594c724307 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -1596,13 +1596,13 @@ config VIDEO_THS7303
  endmenu
  
  #
-# Video serializers and deserializers (e.g. FPDLink)
+# Video serializers and deserializers (e.g. FPD-Link)
  #
  
  menu "Video serializers and deserializers"
  
-config VIDEO_DS90UB960
-	tristate "TI DS90UB960 Deserializer"
+config VIDEO_DS90UB913
+	tristate "TI DS90UB913 Serializer"
  	depends on OF && I2C && VIDEO_DEV
  	select MEDIA_CONTROLLER
  	select VIDEO_V4L2_SUBDEV_API
@@ -1611,11 +1611,11 @@ config VIDEO_DS90UB960
  	select OF_GPIO
  	select I2C_ATR
  	help
-	  Device driver for the Texas Instruments DS90UB960
-	  FPD-Link III Deserializer
+	  Device driver for the Texas Instruments DS90UB913
+	  FPD-Link III Serializer.
  
-config VIDEO_DS90UB913
-	tristate "TI DS90UB913 Serializer"
+config VIDEO_DS90UB953
+	tristate "TI DS90UB953 Serializer"
  	depends on OF && I2C && VIDEO_DEV
  	select MEDIA_CONTROLLER
  	select VIDEO_V4L2_SUBDEV_API
@@ -1624,11 +1624,11 @@ config VIDEO_DS90UB913
  	select OF_GPIO
  	select I2C_ATR
  	help
-	  Device driver for the Texas Instruments DS90UB913
-	  FPD-Link III Serializer.
+	  Device driver for the Texas Instruments DS90UB953
+	  FPD-Link III Serializer and DS90UB971 FPD-Link IV Serializer.
  
-config VIDEO_DS90UB953
-	tristate "TI DS90UB953 Serializer"
+config VIDEO_DS90UB960
+	tristate "TI DS90UB960 Deserializer"
  	depends on OF && I2C && VIDEO_DEV
  	select MEDIA_CONTROLLER
  	select VIDEO_V4L2_SUBDEV_API
@@ -1637,8 +1637,8 @@ config VIDEO_DS90UB953
  	select OF_GPIO
  	select I2C_ATR
  	help
-	  Device driver for the Texas Instruments DS90UB953
-	  FPD-Link III Serializer.
+	  Device driver for the Texas Instruments DS90UB960
+	  FPD-Link III Deserializer and DS90UB9702 FPD-Link IV Deserializer.
  
  endmenu
  
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 377cdf6fa30b..efd5f717a5f7 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -142,6 +142,6 @@ obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
  obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
  obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
  obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
-obj-$(CONFIG_VIDEO_DS90UB960)	+= ds90ub960.o
  obj-$(CONFIG_VIDEO_DS90UB913)	+= ds90ub913.o
  obj-$(CONFIG_VIDEO_DS90UB953)	+= ds90ub953.o
+obj-$(CONFIG_VIDEO_DS90UB960)	+= ds90ub960.o
diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c
index 6001a622e622..0a60afb09cd3 100644
--- a/drivers/media/i2c/ds90ub913.c
+++ b/drivers/media/i2c/ds90ub913.c
@@ -37,7 +37,12 @@
  #define UB913_REG_RESET_CTL_DIGITAL_RESET_0	BIT(0)
  
  #define UB913_REG_GENERAL_CFG			0x03
+#define UB913_REG_GENERAL_CFG_CRC_ERR_RESET	BIT(5)
+#define UB913_REG_GENERAL_CFG_PCLK_RISING	BIT(0)
+
  #define UB913_REG_MODE_SEL			0x05
+#define UB913_REG_MODE_SEL_MODE_UP_TO_DATE	BIT(4)
+#define UB913_REG_MODE_SEL_MODE_OVERRIDE	BIT(5)
  
  #define UB913_REG_CRC_ERRORS_LSB		0x0a
  #define UB913_REG_CRC_ERRORS_MSB		0x0b
@@ -61,8 +66,6 @@ struct ub913_data {
  	struct regmap		*regmap;
  	struct clk		*clkin;
  
-	u32			gpio_func[UB913_NUM_GPIOS];
-
  	struct gpio_chip	gpio_chip;
  	char			gpio_chip_name[64];
  
@@ -81,8 +84,7 @@ struct ub913_data {
  
  	struct ds90ub9xx_platform_data *plat_data;
  
-	/* Have we succefully called i2c_atr_add_adapter() */
-	bool			has_i2c_adapter;
+	u32			pclk_polarity;
  };
  
  static inline struct ub913_data *sd_to_ub913(struct v4l2_subdev *sd)
@@ -200,26 +202,6 @@ static void ub913_gpiochip_remove(struct ub913_data *priv)
  	gpiochip_remove(&priv->gpio_chip);
  }
  
-static int ub913_parse_dt(struct ub913_data *priv)
-{
-	struct device_node *np = priv->client->dev.of_node;
-	struct device *dev = &priv->client->dev;
-	int ret;
-
-	if (!np) {
-		dev_err(dev, "OF: no device tree node!\n");
-		return -ENOENT;
-	}
-
-	/* optional, if absent all GPIO pins are unused */
-	ret = of_property_read_u32_array(np, "gpio-functions", priv->gpio_func,
-					 ARRAY_SIZE(priv->gpio_func));
-	if (ret && ret != -EINVAL)
-		dev_err(dev, "DT: invalid gpio-functions property (%d)", ret);
-
-	return 0;
-}
-
  static const struct regmap_config ub913_regmap_config = {
  	.name = "ds90ub913",
  	.reg_bits = 8,
@@ -290,7 +272,7 @@ static int _ub913_set_routing(struct v4l2_subdev *sd,
  			      struct v4l2_subdev_state *state,
  			      struct v4l2_subdev_krouting *routing)
  {
-	const struct v4l2_mbus_framefmt format = {
+	static const struct v4l2_mbus_framefmt format = {
  		.width = 640,
  		.height = 480,
  		.code = MEDIA_BUS_FMT_UYVY8_2X8,
@@ -361,9 +343,9 @@ static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  	struct v4l2_mbus_frame_desc source_fd;
  	struct v4l2_subdev_route *route;
  	struct v4l2_subdev_state *state;
-	int ret = 0;
+	int ret;
  
-	if (pad != 1) /* first tx pad */
+	if (pad != UB913_PAD_SOURCE)
  		return -EINVAL;
  
  	ret = ub913_get_source_frame_desc(priv, &source_fd);
@@ -379,16 +361,16 @@ static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  	fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL;
  
  	for_each_active_route(routing, route) {
-		unsigned int j;
+		unsigned int i;
  
  		if (route->source_pad != pad)
  			continue;
  
-		for (j = 0; j < source_fd.num_entries; ++j)
-			if (source_fd.entry[j].stream == route->sink_stream)
+		for (i = 0; i < source_fd.num_entries; ++i)
+			if (source_fd.entry[i].stream == route->sink_stream)
  				break;
  
-		if (j == source_fd.num_entries) {
+		if (i == source_fd.num_entries) {
  			dev_err(&priv->client->dev,
  				"Failed to find stream from source frame desc\n");
  			ret = -EPIPE;
@@ -396,12 +378,10 @@ static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  		}
  
  		fd->entry[fd->num_entries].stream = route->source_stream;
-
-		fd->entry[fd->num_entries].flags =
-			V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
-		fd->entry[fd->num_entries].length = source_fd.entry[j].length;
+		fd->entry[fd->num_entries].flags = source_fd.entry[i].flags;
+		fd->entry[fd->num_entries].length = source_fd.entry[i].length;
  		fd->entry[fd->num_entries].pixelcode =
-			source_fd.entry[j].pixelcode;
+			source_fd.entry[i].pixelcode;
  
  		fd->num_entries++;
  	}
@@ -424,7 +404,7 @@ static int ub913_set_fmt(struct v4l2_subdev *sd,
  		return -EBUSY;
  
  	/* No transcoding, source and sink formats must match. */
-	if (format->pad == 1)
+	if (format->pad == UB913_PAD_SOURCE)
  		return v4l2_subdev_get_fmt(sd, state, format);
  
  	/* Set sink format */
@@ -451,9 +431,9 @@ static int ub913_init_cfg(struct v4l2_subdev *sd,
  {
  	struct v4l2_subdev_route routes[] = {
  		{
-			.sink_pad = 0,
+			.sink_pad = UB913_PAD_SINK,
  			.sink_stream = 0,
-			.source_pad = 1,
+			.source_pad = UB913_PAD_SOURCE,
  			.source_stream = 0,
  			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
  		},
@@ -488,7 +468,7 @@ static int ub913_log_status(struct v4l2_subdev *sd)
  
  	/* clear CRC errors */
  	ub913_read(priv, UB913_REG_GENERAL_CFG, &v);
-	ub913_write(priv, UB913_REG_GENERAL_CFG, v | BIT(5));
+	ub913_write(priv, UB913_REG_GENERAL_CFG, v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET);
  	ub913_write(priv, UB913_REG_GENERAL_CFG, v);
  
  	return 0;
@@ -526,8 +506,6 @@ static int ub913_notify_bound(struct v4l2_async_notifier *notifier,
  	unsigned int src_pad;
  	int ret;
  
-	dev_dbg(dev, "Bind %s\n", source_subdev->name);
-
  	ret = media_entity_get_fwnode_pad(&source_subdev->entity,
  					  source_subdev->fwnode,
  					  MEDIA_PAD_FL_SOURCE);
@@ -541,7 +519,7 @@ static int ub913_notify_bound(struct v4l2_async_notifier *notifier,
  	src_pad = ret;
  
  	ret = media_create_pad_link(&source_subdev->entity, src_pad,
-				    &priv->sd.entity, 0,
+				    &priv->sd.entity, UB913_PAD_SINK,
  				    MEDIA_LNK_FL_ENABLED |
  				    MEDIA_LNK_FL_IMMUTABLE);
  	if (ret) {
@@ -550,26 +528,11 @@ static int ub913_notify_bound(struct v4l2_async_notifier *notifier,
  		return ret;
  	}
  
-	dev_dbg(dev, "Bound %s:%u\n", source_subdev->name, src_pad);
-
-	dev_dbg(dev, "All subdevs bound\n");
-
  	return 0;
  }
  
-static void ub913_notify_unbind(struct v4l2_async_notifier *notifier,
-				struct v4l2_subdev *source_subdev,
-				struct v4l2_async_subdev *asd)
-{
-	struct ub913_data *priv = sd_to_ub913(notifier->sd);
-	struct device *dev = &priv->client->dev;
-
-	dev_dbg(dev, "Unbind %s\n", source_subdev->name);
-}
-
  static const struct v4l2_async_notifier_operations ub913_notify_ops = {
  	.bound = ub913_notify_bound,
-	.unbind = ub913_notify_unbind,
  };
  
  static int ub913_v4l2_notifier_register(struct ub913_data *priv)
@@ -579,8 +542,6 @@ static int ub913_v4l2_notifier_register(struct ub913_data *priv)
  	struct device_node *ep_node;
  	int ret;
  
-	dev_dbg(dev, "register async notif\n");
-
  	ep_node = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
  	if (!ep_node) {
  		dev_err(dev, "No graph endpoint\n");
@@ -615,10 +576,6 @@ static int ub913_v4l2_notifier_register(struct ub913_data *priv)
  
  static void ub913_v4l2_nf_unregister(struct ub913_data *priv)
  {
-	struct device *dev = &priv->client->dev;
-
-	dev_dbg(dev, "Unregister async notif\n");
-
  	v4l2_async_nf_unregister(&priv->notifier);
  	v4l2_async_nf_cleanup(&priv->notifier);
  }
@@ -691,16 +648,37 @@ static int ub913_add_i2c_adapter(struct ub913_data *priv)
  	if (ret)
  		return ret;
  
-	priv->has_i2c_adapter = true;
-
  	return 0;
  }
  
-static void ub913_remove_i2c_adapter(struct ub913_data *priv)
+static int ub913_parse_dt(struct ub913_data *priv)
  {
-	if (priv->has_i2c_adapter)
-		i2c_atr_del_adapter(priv->plat_data->atr,
-				    priv->plat_data->port);
+	struct device_node *np = priv->client->dev.of_node;
+	struct device *dev = &priv->client->dev;
+	int ret;
+	struct device_node *ep_np;
+
+	if (!np) {
+		dev_err(dev, "OF: no device tree node!\n");
+		return -ENOENT;
+	}
+
+	ep_np = of_graph_get_endpoint_by_regs(np, 0, 0);
+	if (!ep_np) {
+		dev_err(dev, "OF: no endpoint\n");
+		return -ENOENT;
+	}
+
+	ret = of_property_read_u32(ep_np, "pclk-sample", &priv->pclk_polarity);
+
+	of_node_put(ep_np);
+
+	if (ret) {
+		dev_err(dev, "OF: failed to parse pclk-sample: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
  }
  
  static int ub913_probe(struct i2c_client *client)
@@ -712,8 +690,6 @@ static int ub913_probe(struct i2c_client *client)
  	bool mode_override;
  	u8 mode;
  
-	dev_dbg(dev, "probing, addr 0x%02x\n", client->addr);
-
  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  	if (!priv)
  		return -ENOMEM;
@@ -749,12 +725,12 @@ static int ub913_probe(struct i2c_client *client)
  	if (ret)
  		return ret;
  
-	if (!(v & BIT(4))) {
+	if (!(v & UB913_REG_MODE_SEL_MODE_UP_TO_DATE)) {
  		dev_err(dev, "Mode value not stabilized\n");
  		return -ENODEV;
  	}
  
-	mode_override = v & BIT(5);
+	mode_override = v & UB913_REG_MODE_SEL_MODE_OVERRIDE;
  	mode = v & 0xf;
  
  	dev_dbg(dev, "mode from %s: %#x\n",
@@ -778,6 +754,11 @@ static int ub913_probe(struct i2c_client *client)
  		goto err_gpiochip_remove;
  	}
  
+	ub913_read(priv, UB913_REG_GENERAL_CFG, &v);
+	v &= ~UB913_REG_GENERAL_CFG_PCLK_RISING;
+	v |= priv->pclk_polarity ? UB913_REG_GENERAL_CFG_PCLK_RISING : 0;
+	ub913_write(priv, UB913_REG_GENERAL_CFG, v);
+
  	v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops);
  	priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
  	priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
@@ -818,8 +799,6 @@ static int ub913_probe(struct i2c_client *client)
  		goto err_unreg_async_subdev;
  	}
  
-	dev_dbg(dev, "Successfully probed\n");
-
  	return 0;
  
  err_unreg_async_subdev:
@@ -844,9 +823,8 @@ static void ub913_remove(struct i2c_client *client)
  	struct v4l2_subdev *sd = i2c_get_clientdata(client);
  	struct ub913_data *priv = sd_to_ub913(sd);
  
-	dev_dbg(&client->dev, "Removing\n");
-
-	ub913_remove_i2c_adapter(priv);
+	i2c_atr_del_adapter(priv->plat_data->atr,
+			    priv->plat_data->port);
  
  	v4l2_async_unregister_subdev(&priv->sd);
  
@@ -890,3 +868,4 @@ MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("Texas Instruments DS90UB913 serializer driver");
  MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
  MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
+MODULE_IMPORT_NS(I2C_ATR);
diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index c7f5d08e07ef..251dc8e8adfa 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -38,7 +38,16 @@
  #define UB953_REG_RESET_CTL_DIGITAL_RESET_0	BIT(0)
  
  #define UB953_REG_GENERAL_CFG			0x02
+#define UB953_REG_GENERAL_CFG_CONT_CLK		BIT(6)
+#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT	4
+#define UB953_REG_GENERAL_CFG_CSI_LANE_SEL_MASK	GENMASK(5, 4)
+#define UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE	BIT(1)
+#define UB953_REG_GENERAL_CFG_I2C_STRAP_MODE	BIT(0)
+
  #define UB953_REG_MODE_SEL			0x03
+#define UB953_REG_MODE_SEL_MODE_DONE		BIT(3)
+#define UB953_REG_MODE_SEL_MODE_OVERRIDE	BIT(4)
+#define UB953_REG_MODE_SEL_MODE_MASK		GENMASK(2, 0)
  
  #define UB953_REG_CLKOUT_CTRL0			0x06
  #define UB953_REG_CLKOUT_CTRL1			0x07
@@ -105,13 +114,13 @@
  
  /* Note: Only sync mode supported for now */
  enum ub953_mode {
-	/* FPD-Link 3 CSI-2 synchronous mode */
+	/* FPD-Link III CSI-2 synchronous mode */
  	UB953_MODE_SYNC,
-	/* FPD-Link 3 CSI-2 non-synchronous mode, external ref clock */
+	/* FPD-Link III CSI-2 non-synchronous mode, external ref clock */
  	UB953_MODE_NONSYNC_EXT,
-	/* FPD-Link 3 CSI-2 non-synchronous mode, internal ref clock */
+	/* FPD-Link III CSI-2 non-synchronous mode, internal ref clock */
  	UB953_MODE_NONSYNC_INT,
-	/* FPD-Link 3 DVP mode */
+	/* FPD-Link III DVP mode */
  	UB953_MODE_DVP,
  };
  
@@ -154,9 +163,6 @@ struct ub953_data {
  	enum ub953_mode		mode;
  
  	struct ds90ub9xx_platform_data *plat_data;
-
-	/* Have we succefully called i2c_atr_add_adapter() */
-	bool			has_i2c_adapter;
  };
  
  static inline struct ub953_data *sd_to_ub953(struct v4l2_subdev *sd)
@@ -439,10 +445,10 @@ static int _ub953_set_routing(struct v4l2_subdev *sd,
  			      struct v4l2_subdev_state *state,
  			      struct v4l2_subdev_krouting *routing)
  {
-	const struct v4l2_mbus_framefmt format = {
+	static const struct v4l2_mbus_framefmt format = {
  		.width = 640,
  		.height = 480,
-		.code = MEDIA_BUS_FMT_UYVY8_2X8,
+		.code = MEDIA_BUS_FMT_UYVY8_1X16,
  		.field = V4L2_FIELD_NONE,
  		.colorspace = V4L2_COLORSPACE_SRGB,
  		.ycbcr_enc = V4L2_YCBCR_ENC_601,
@@ -510,9 +516,9 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  	struct v4l2_mbus_frame_desc source_fd;
  	struct v4l2_subdev_route *route;
  	struct v4l2_subdev_state *state;
-	int ret = 0;
+	int ret;
  
-	if (pad != 1) /* first tx pad */
+	if (pad != UB953_PAD_SOURCE)
  		return -EINVAL;
  
  	ret = ub953_get_source_frame_desc(priv, &source_fd);
@@ -529,14 +535,14 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  
  	for_each_active_route(routing, route) {
  		struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
-		unsigned int j;
+		unsigned int i;
  
  		if (route->source_pad != pad)
  			continue;
  
-		for (j = 0; j < source_fd.num_entries; ++j)
-			if (source_fd.entry[j].stream == route->sink_stream) {
-				source_entry = &source_fd.entry[j];
+		for (i = 0; i < source_fd.num_entries; ++i)
+			if (source_fd.entry[i].stream == route->sink_stream) {
+				source_entry = &source_fd.entry[i];
  				break;
  			}
  
@@ -548,9 +554,7 @@ static int ub953_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  		}
  
  		fd->entry[fd->num_entries].stream = route->source_stream;
-
-		fd->entry[fd->num_entries].flags =
-			V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
+		fd->entry[fd->num_entries].flags = source_entry->flags;
  		fd->entry[fd->num_entries].length = source_entry->length;
  		fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode;
  		fd->entry[fd->num_entries].bus.csi2.vc =
@@ -579,7 +583,7 @@ static int ub953_set_fmt(struct v4l2_subdev *sd,
  		return -EBUSY;
  
  	/* No transcoding, source and sink formats must match. */
-	if (format->pad == 1)
+	if (format->pad == UB953_PAD_SOURCE)
  		return v4l2_subdev_get_fmt(sd, state, format);
  
  	/* Set sink format */
@@ -606,9 +610,9 @@ static int ub953_init_cfg(struct v4l2_subdev *sd,
  {
  	struct v4l2_subdev_route routes[] = {
  		{
-			.sink_pad = 0,
+			.sink_pad = UB953_PAD_SINK,
  			.sink_stream = 0,
-			.source_pad = 1,
+			.source_pad = UB953_PAD_SOURCE,
  			.source_stream = 0,
  			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
  		},
@@ -784,7 +788,7 @@ static const char *const ub953_tpg_qmenu[] = {
  	"8 vertical color bars",
  };
  
-static void ub953_enable_tpg(struct ub953_data *priv, int tpg_num)
+static int ub953_enable_tpg(struct ub953_data *priv, int tpg_num)
  {
  	struct v4l2_subdev *sd = &priv->sd;
  	struct v4l2_subdev_state *state;
@@ -812,7 +816,15 @@ static void ub953_enable_tpg(struct ub953_data *priv, int tpg_num)
  
  	state = v4l2_subdev_get_locked_active_state(sd);
  
+	if (state->routing.num_routes != 1)
+		return -EINVAL;
+
  	fmt = v4l2_subdev_state_get_stream_format(state, UB953_PAD_SOURCE, 0);
+	if (!fmt)
+		return -EINVAL;
+
+	if (fmt->code != MEDIA_BUS_FMT_UYVY8_1X16)
+		return -EINVAL;
  
  	width = fmt->width;
  	height = fmt->height;
@@ -846,6 +858,8 @@ static void ub953_enable_tpg(struct ub953_data *priv, int tpg_num)
  			vbp);
  	ub953_write_ind(priv, UB953_IND_TARGET_PAT_GEN, UB953_IND_PGEN_VFP,
  			vfp);
+
+	return 0;
  }
  
  static void ub953_disable_tpg(struct ub953_data *priv)
@@ -858,17 +872,18 @@ static int ub953_s_ctrl(struct v4l2_ctrl *ctrl)
  {
  	struct ub953_data *priv =
  		container_of(ctrl->handler, struct ub953_data, ctrl_handler);
+	int ret = 0;
  
  	switch (ctrl->id) {
  	case V4L2_CID_TEST_PATTERN:
  		if (ctrl->val == 0)
  			ub953_disable_tpg(priv);
  		else
-			ub953_enable_tpg(priv, ctrl->val);
+			ret = ub953_enable_tpg(priv, ctrl->val);
  		break;
  	}
  
-	return 0;
+	return ret;
  }
  
  static const struct v4l2_ctrl_ops ub953_ctrl_ops = {
@@ -884,8 +899,6 @@ static int ub953_notify_bound(struct v4l2_async_notifier *notifier,
  	unsigned int src_pad;
  	int ret;
  
-	dev_dbg(dev, "Bind %s\n", source_subdev->name);
-
  	ret = media_entity_get_fwnode_pad(&source_subdev->entity,
  					  source_subdev->fwnode,
  					  MEDIA_PAD_FL_SOURCE);
@@ -907,26 +920,11 @@ static int ub953_notify_bound(struct v4l2_async_notifier *notifier,
  		return ret;
  	}
  
-	dev_dbg(dev, "Bound %s:%u\n", source_subdev->name, src_pad);
-
-	dev_dbg(dev, "All subdevs bound\n");
-
  	return 0;
  }
  
-static void ub953_notify_unbind(struct v4l2_async_notifier *notifier,
-				struct v4l2_subdev *source_subdev,
-				struct v4l2_async_subdev *asd)
-{
-	struct ub953_data *priv = sd_to_ub953(notifier->sd);
-	struct device *dev = &priv->client->dev;
-
-	dev_dbg(dev, "Unbind %s\n", source_subdev->name);
-}
-
  static const struct v4l2_async_notifier_operations ub953_notify_ops = {
  	.bound = ub953_notify_bound,
-	.unbind = ub953_notify_unbind,
  };
  
  static int ub953_v4l2_notifier_register(struct ub953_data *priv)
@@ -936,8 +934,6 @@ static int ub953_v4l2_notifier_register(struct ub953_data *priv)
  	struct device_node *ep_node;
  	int ret;
  
-	dev_dbg(dev, "register async notif\n");
-
  	ep_node = of_graph_get_endpoint_by_regs(dev->of_node, 0, 0);
  	if (!ep_node) {
  		dev_err(dev, "No graph endpoint\n");
@@ -972,10 +968,6 @@ static int ub953_v4l2_notifier_register(struct ub953_data *priv)
  
  static void ub953_v4l2_notifier_unregister(struct ub953_data *priv)
  {
-	struct device *dev = &priv->client->dev;
-
-	dev_dbg(dev, "Unregister async notif\n");
-
  	v4l2_async_nf_unregister(&priv->notifier);
  	v4l2_async_nf_cleanup(&priv->notifier);
  }
@@ -1006,48 +998,6 @@ static int ub953_i2c_master_init(struct ub953_data *priv)
  	return 0;
  }
  
-static int ub953_parse_dt(struct ub953_data *priv)
-{
-	struct device_node *np = priv->client->dev.of_node;
-	struct device *dev = &priv->client->dev;
-	struct device_node *ep_np;
-	int ret;
-
-	if (!np) {
-		dev_err(dev, "OF: no device tree node!\n");
-		return -ENOENT;
-	}
-
-	ep_np = of_graph_get_endpoint_by_regs(np, 0, 0);
-	if (!ep_np) {
-		dev_err(dev, "OF: no endpoint\n");
-		return -ENOENT;
-	}
-
-	ret = of_property_count_u32_elems(ep_np, "data-lanes");
-	if (ret <= 0) {
-		dev_err(dev, "OF: failed to parse data-lanes: %d\n", ret);
-		return ret;
-	}
-
-	if (ret != 1 && ret != 2 && ret != 4) {
-		dev_err(dev, "OF: bad number of data-lanes: %d\n", ret);
-		return -EINVAL;
-	}
-
-	priv->num_data_lanes = ret;
-
-	return 0;
-}
-
-static const struct regmap_config ub953_regmap_config = {
-	.name = "ds90ub953",
-	.reg_bits = 8,
-	.val_bits = 8,
-	.reg_format_endian = REGMAP_ENDIAN_DEFAULT,
-	.val_format_endian = REGMAP_ENDIAN_DEFAULT,
-};
-
  static u64 ub953_get_fc_rate(struct ub953_data *priv)
  {
  	if (priv->hw_data->ub971) {
@@ -1322,16 +1272,52 @@ static int ub953_add_i2c_adapter(struct ub953_data *priv)
  	if (ret)
  		return ret;
  
-	priv->has_i2c_adapter = true;
-
  	return 0;
  }
  
-static void ub953_remove_i2c_adapter(struct ub953_data *priv)
+static const struct regmap_config ub953_regmap_config = {
+	.name = "ds90ub953",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.reg_format_endian = REGMAP_ENDIAN_DEFAULT,
+	.val_format_endian = REGMAP_ENDIAN_DEFAULT,
+};
+
+static int ub953_parse_dt(struct ub953_data *priv)
  {
-	if (priv->has_i2c_adapter)
-		i2c_atr_del_adapter(priv->plat_data->atr,
-				    priv->plat_data->port);
+	struct device_node *np = priv->client->dev.of_node;
+	struct device *dev = &priv->client->dev;
+	struct device_node *ep_np;
+	int ret;
+
+	if (!np) {
+		dev_err(dev, "OF: no device tree node!\n");
+		return -ENOENT;
+	}
+
+	ep_np = of_graph_get_endpoint_by_regs(np, 0, 0);
+	if (!ep_np) {
+		dev_err(dev, "OF: no endpoint\n");
+		return -ENOENT;
+	}
+
+	ret = of_property_count_u32_elems(ep_np, "data-lanes");
+
+	of_node_put(ep_np);
+
+	if (ret <= 0) {
+		dev_err(dev, "OF: failed to parse data-lanes: %d\n", ret);
+		return ret;
+	}
+
+	if (ret != 1 && ret != 2 && ret != 4) {
+		dev_err(dev, "OF: bad number of data-lanes: %d\n", ret);
+		return -EINVAL;
+	}
+
+	priv->num_data_lanes = ret;
+
+	return 0;
  }
  
  static int ub953_probe(struct i2c_client *client)
@@ -1342,8 +1328,6 @@ static int ub953_probe(struct i2c_client *client)
  	u8 v;
  	bool mode_override;
  
-	dev_dbg(dev, "probing, addr 0x%02x\n", client->addr);
-
  	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  	if (!priv)
  		return -ENOMEM;
@@ -1383,15 +1367,15 @@ static int ub953_probe(struct i2c_client *client)
  	if (ret)
  		goto err_mutex_destroy;
  
-	if (!(v & BIT(3))) {
+	if (!(v & UB953_REG_MODE_SEL_MODE_DONE)) {
  		dev_err(dev, "Mode value not stabilized\n");
  		ret = -ENODEV;
  		goto err_mutex_destroy;
  	}
  
-	mode_override = v & BIT(4);
+	mode_override = v & UB953_REG_MODE_SEL_MODE_OVERRIDE;
  
-	switch (v & 0x7) {
+	switch (v & UB953_REG_MODE_SEL_MODE_MASK) {
  	case 0:
  		priv->mode = UB953_MODE_SYNC;
  		break;
@@ -1431,7 +1415,8 @@ static int ub953_probe(struct i2c_client *client)
  	if (ret)
  		goto err_mutex_destroy;
  
-	dev_dbg(dev, "i2c strap setting %s V\n", (v & 1) ? "1.8" : "3.3");
+	dev_dbg(dev, "i2c strap setting %s V\n",
+		(v & UB953_REG_GENERAL_CFG_I2C_STRAP_MODE) ? "1.8" : "3.3");
  
  	ret = ub953_i2c_master_init(priv);
  	if (ret) {
@@ -1446,9 +1431,9 @@ static int ub953_probe(struct i2c_client *client)
  	}
  
  	ub953_write(priv, UB953_REG_GENERAL_CFG,
-		    (1 << 6) | /* continuous clk */
-		    ((priv->num_data_lanes - 1) << 4) |
-		    (1 << 1)); /* CRC TX gen */
+		    UB953_REG_GENERAL_CFG_CONT_CLK |
+		    ((priv->num_data_lanes - 1) << UB953_REG_GENERAL_CFG_CSI_LANE_SEL_SHIFT) |
+		    UB953_REG_GENERAL_CFG_CRC_TX_GEN_ENABLE);
  
  	ret = ub953_register_clkout(priv);
  	if (ret) {
@@ -1513,8 +1498,6 @@ static int ub953_probe(struct i2c_client *client)
  		goto err_unreg_async_subdev;
  	}
  
-	dev_dbg(dev, "Successfully probed\n");
-
  	return 0;
  
  err_unreg_async_subdev:
@@ -1543,9 +1526,8 @@ static void ub953_remove(struct i2c_client *client)
  	struct v4l2_subdev *sd = i2c_get_clientdata(client);
  	struct ub953_data *priv = sd_to_ub953(sd);
  
-	dev_dbg(&client->dev, "Removing\n");
-
-	ub953_remove_i2c_adapter(priv);
+	i2c_atr_del_adapter(priv->plat_data->atr,
+			    priv->plat_data->port);
  
  	v4l2_async_unregister_subdev(&priv->sd);
  
@@ -1605,3 +1587,4 @@ MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("Texas Instruments DS90UB953 serializer driver");
  MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
  MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
+MODULE_IMPORT_NS(I2C_ATR);
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index fef704ee5529..b532a93f3da7 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -65,6 +65,10 @@
  
  #define UB960_SR_I2C_DEV_ID			0x00
  #define UB960_SR_RESET				0x01
+#define UB960_SR_RESET_DIGITAL_RESET1		BIT(1)
+#define UB960_SR_RESET_DIGITAL_RESET0		BIT(0)
+#define UB960_SR_RESET_GPIO_LOCK_RELEASE	BIT(5)
+
  #define UB960_SR_GEN_CONFIG			0x02
  #define UB960_SR_REV_MASK			0x03
  #define UB960_SR_DEVICE_STS			0x04
@@ -80,6 +84,10 @@
  #define UB960_SR_GPIO_PIN_STS			0x0E
  #define UB960_SR_GPIO_INPUT_CTL			0x0F
  #define UB960_SR_GPIO_PIN_CTL(n)		(0x10 + (n)) /* n < UB960_NUM_GPIOS */
+#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_SEL		5
+#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_SRC_SHIFT	2
+#define UB960_SR_GPIO_PIN_CTL_GPIO_OUT_EN		BIT(0)
+
  #define UB960_SR_FS_CTL				0x18
  #define UB960_SR_FS_HIGH_TIME_1			0x19
  #define UB960_SR_FS_HIGH_TIME_0			0x1A
@@ -135,7 +143,13 @@
  #define UB960_TR_CSI_TEST_PATT_LO		0x3A
  
  #define UB960_XR_SFILTER_CFG			0x41
+#define UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT	4
+#define UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT	0
+
  #define UB960_XR_AEQ_CTL1			0x42
+#define UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_SHIFT	4
+#define UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN	BIT(0)
+
  #define UB960_XR_AEQ_ERR_THOLD			0x43
  
  #define UB960_RR_BCC_ERR_CTL			0x46
@@ -213,6 +227,9 @@
  
  #define UB960_RR_CSI_ERR_COUNTER		0x7B
  #define UB960_RR_PORT_CONFIG2			0x7C
+#define UB960_RR_PORT_CONFIG2_LV_POL_LOW	BIT(1)
+#define UB960_RR_PORT_CONFIG2_FV_POL_LOW	BIT(0)
+
  #define UB960_RR_PORT_PASS_CTL			0x7D
  #define UB960_RR_SEN_INT_RISE_CTL		0x7E
  #define UB960_RR_SEN_INT_FALL_CTL		0x7F
@@ -247,9 +264,21 @@
  
  #define UB960_RR_PORT_DEBUG			0xD0
  #define UB960_RR_AEQ_CTL2			0xD2
+#define UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR		BIT(2)
+
  #define UB960_RR_AEQ_STATUS			0xD3
+
  #define UB960_RR_AEQ_BYPASS			0xD4
+#define UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_SHIFT	5
+#define UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK	GENMASK(7, 5)
+#define UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT	1
+#define UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK	GENMASK(3, 1)
+#define UB960_RR_AEQ_BYPASS_ENABLE			BIT(0)
+
  #define UB960_RR_AEQ_MIN_MAX			0xD5
+#define UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT	4
+#define UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT	0
+
  #define UB960_RR_SFILTER_STS_0			0xD6
  #define UB960_RR_SFILTER_STS_1			0xD7
  #define UB960_RR_PORT_ICR_HI			0xD8
@@ -295,7 +324,12 @@
  #define UB960_IR_PGEN_COLOR(n)			(0x10 + (n)) /* n < 15 */
  
  #define UB960_IR_RX_ANA_STROBE_SET_CLK		0x08
+#define UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY	BIT(3)
+#define UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK	GENMASK(2, 0)
+
  #define UB960_IR_RX_ANA_STROBE_SET_DATA		0x09
+#define UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY	BIT(3)
+#define UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK	GENMASK(2, 0)
  
  /* EQ related */
  
@@ -347,6 +381,8 @@ struct ub960_rxport {
  	struct i2c_client      *ser_client;	/* Serializer */
  	unsigned short          ser_alias;	/* Serializer i2c alias (lower 7 bits) */
  
+	u8			lv_fv_pol;	/* LV and FV polarities */
+
  	struct regulator	*vpoc;
  
  	/* EQ settings */
@@ -905,7 +941,7 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id,
  				   const struct i2c_client *client,
  				   u16 *alias_id)
  {
-	struct ub960_data *priv = i2c_atr_get_clientdata(atr);
+	struct ub960_data *priv = i2c_atr_get_driver_data(atr);
  	struct ub960_rxport *rxport = priv->rxports[chan_id];
  	struct device *dev = &priv->client->dev;
  	struct atr_alias_table_entry *entry = NULL;
@@ -978,7 +1014,7 @@ static int ub960_atr_attach_client(struct i2c_atr *atr, u32 chan_id,
  static void ub960_atr_detach_client(struct i2c_atr *atr, u32 chan_id,
  				    const struct i2c_client *client)
  {
-	struct ub960_data *priv = i2c_atr_get_clientdata(atr);
+	struct ub960_data *priv = i2c_atr_get_driver_data(atr);
  	struct ub960_rxport *rxport = priv->rxports[chan_id];
  	struct device *dev = &priv->client->dev;
  	struct atr_alias_table_entry *entry;
@@ -1134,6 +1170,51 @@ static void ub960_csi_handle_events(struct ub960_data *priv, u8 nport)
   * RX ports
   */
  
+static int ub960_rxport_enable_vpocs(struct ub960_data *priv)
+{
+	unsigned int nport;
+	int ret;
+
+	for (nport = 0; nport < priv->hw_data->num_rxports; ++nport) {
+		struct ub960_rxport *rxport = priv->rxports[nport];
+
+		if (!rxport || !rxport->vpoc)
+			continue;
+
+		ret = regulator_enable(rxport->vpoc);
+		if (ret)
+			goto err_disable_vpocs;
+	}
+
+	return 0;
+
+err_disable_vpocs:
+	for (; nport > 0; --nport) {
+		struct ub960_rxport *rxport = priv->rxports[nport - 1];
+
+		if (!rxport || !rxport->vpoc)
+			continue;
+
+		regulator_disable(rxport->vpoc);
+	}
+
+	return ret;
+}
+
+static void ub960_rxport_disable_vpocs(struct ub960_data *priv)
+{
+	unsigned int nport;
+
+	for (nport = 0; nport < priv->hw_data->num_rxports; ++nport) {
+		struct ub960_rxport *rxport = priv->rxports[nport];
+
+		if (!rxport || !rxport->vpoc)
+			continue;
+
+		regulator_disable(rxport->vpoc);
+	}
+}
+
  static void ub960_rxport_clear_errors(struct ub960_data *priv,
  				      unsigned int nport)
  {
@@ -1168,24 +1249,24 @@ static int ub960_rxport_get_strobe_pos(struct ub960_data *priv,
  	ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
  		       UB960_IR_RX_ANA_STROBE_SET_CLK, &v);
  
-	clk_delay = v & BIT(3) ? 0 : 6;
+	clk_delay = v & UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY ? 0 : 6;
  
  	ub960_read_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
  		       UB960_IR_RX_ANA_STROBE_SET_DATA, &v);
  
-	data_delay = v & BIT(3) ? 0 : 6;
+	data_delay = v & UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY ? 0 : 6;
  
  	ret = ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_0, &v);
  	if (ret)
  		return ret;
  
-	clk_delay += v & 0x7;
+	clk_delay += v & UB960_IR_RX_ANA_STROBE_SET_CLK_DELAY_MASK;
  
  	ub960_rxport_read(priv, nport, UB960_RR_SFILTER_STS_1, &v);
  	if (ret)
  		return ret;
  
-	data_delay += v & 0x7;
+	data_delay += v & UB960_IR_RX_ANA_STROBE_SET_DATA_DELAY_MASK;
  
  	*strobe_pos = data_delay - clk_delay;
  
@@ -1201,17 +1282,17 @@ static void ub960_rxport_set_strobe_pos(struct ub960_data *priv,
  		    strobe_pos > UB960_MAX_MANUAL_STROBE_POS))
  		return;
  
-	clk_delay = BIT(3);
-	data_delay = BIT(3);
+	clk_delay = UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
+	data_delay = UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
  
  	if (strobe_pos < -7)
  		clk_delay = abs(strobe_pos) - 6;
  	else if (strobe_pos > 7)
  		data_delay = strobe_pos - 6;
  	else if (strobe_pos < 0)
-		clk_delay = abs(strobe_pos) | BIT(3);
+		clk_delay = abs(strobe_pos) | UB960_IR_RX_ANA_STROBE_SET_CLK_NO_EXTRA_DELAY;
  	else if (strobe_pos > 0)
-		data_delay = strobe_pos | BIT(3);
+		data_delay = strobe_pos | UB960_IR_RX_ANA_STROBE_SET_DATA_NO_EXTRA_DELAY;
  
  	ub960_write_ind(priv, UB960_IND_TARGET_RX_ANA(nport),
  			UB960_IR_RX_ANA_STROBE_SET_CLK, clk_delay);
@@ -1230,7 +1311,8 @@ static void ub960_rxport_set_strobe_range(struct ub960_data *priv,
  	strobe_max += 7;
  
  	ub960_write(priv, UB960_XR_SFILTER_CFG,
-		    (u8)strobe_min | ((u8)strobe_max << 4));
+		    ((u8)strobe_min << UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) |
+		    ((u8)strobe_max << UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT));
  }
  
  static int ub960_rxport_get_eq_level(struct ub960_data *priv,
@@ -1267,10 +1349,11 @@ static void ub960_rxport_set_eq_level(struct ub960_data *priv,
  
  	ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v);
  
-	v &= ~((0x7 << 5) | (0x7 << 1));
-	v |= eq_stage_1_select_value << 5;
-	v |= eq_stage_2_select_value << 1;
-	v |= BIT(0); /* Enable AEQ Bypass */
+	v &= ~(UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_MASK |
+	       UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_MASK);
+	v |= eq_stage_1_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE1_VALUE_SHIFT;
+	v |= eq_stage_2_select_value << UB960_RR_AEQ_BYPASS_EQ_STAGE2_VALUE_SHIFT;
+	v |= UB960_RR_AEQ_BYPASS_ENABLE; /* Enable AEQ Bypass */
  
  	ub960_rxport_write(priv, nport, UB960_RR_AEQ_BYPASS, v);
  }
@@ -1279,11 +1362,13 @@ static void ub960_rxport_set_eq_range(struct ub960_data *priv,
  				      unsigned int nport, u8 eq_min, u8 eq_max)
  {
  	ub960_rxport_write(priv, nport, UB960_RR_AEQ_MIN_MAX,
-			   eq_min | (eq_max << 4));
+			   (eq_min << UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) |
+			   (eq_max << UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT));
  
  	/* Enable AEQ min setting */
-	ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_CTL2, BIT(2),
-				 BIT(2));
+	ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_CTL2,
+				 UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR,
+				 UB960_RR_AEQ_CTL2_SET_AEQ_FLOOR);
  }
  
  static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport)
@@ -1294,10 +1379,13 @@ static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport)
  
  	if (priv->strobe.manual) {
  		/* Disable AEQ_SFILTER_EN */
-		ub960_update_bits(priv, UB960_XR_AEQ_CTL1, BIT(0), 0);
+		ub960_update_bits(priv, UB960_XR_AEQ_CTL1,
+				  UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN, 0);
  	} else {
  		/* Enable SFILTER and error control */
-		ub960_write(priv, UB960_XR_AEQ_CTL1, (0x7 << 4) | BIT(0));
+		ub960_write(priv, UB960_XR_AEQ_CTL1,
+			    (0x7 << UB960_XR_AEQ_CTL1_AEQ_ERR_CTL_SHIFT) |
+				    UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN);
  
  		/* Set AEQ strobe range */
  		ub960_rxport_set_strobe_range(priv, priv->strobe.min,
@@ -1317,7 +1405,8 @@ static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport)
  
  		/* Enable AEQ Bypass */
  		ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS,
-					 BIT(0), BIT(0));
+					 UB960_RR_AEQ_BYPASS_ENABLE,
+					 UB960_RR_AEQ_BYPASS_ENABLE);
  	} else {
  		ub960_rxport_set_eq_range(priv, nport,
  					  rxport->eq.aeq.eq_level_min,
@@ -1325,7 +1414,7 @@ static void ub960_rxport_config_eq(struct ub960_data *priv, unsigned int nport)
  
  		/* Disable AEQ Bypass */
  		ub960_rxport_update_bits(priv, nport, UB960_RR_AEQ_BYPASS,
-					 BIT(0), 0);
+					 UB960_RR_AEQ_BYPASS_ENABLE, 0);
  	}
  }
  
@@ -1492,7 +1581,7 @@ static int ub960_init_atr(struct ub960_data *priv)
  	if (IS_ERR(priv->atr))
  		return PTR_ERR(priv->atr);
  
-	i2c_atr_set_clientdata(priv->atr, priv);
+	i2c_atr_set_driver_data(priv->atr, priv);
  
  	return 0;
  }
@@ -1800,7 +1889,8 @@ static void ub960_init_rx_port_ub960(struct ub960_data *priv,
  	}
  
  	/* LV_POLARITY & FV_POLARITY */
-	ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3, 0x1);
+	ub960_rxport_update_bits(priv, nport, UB960_RR_PORT_CONFIG2, 0x3,
+				 rxport->lv_fv_pol);
  
  	/* Enable all interrupt sources from this port */
  	ub960_rxport_write(priv, nport, UB960_RR_PORT_ICR_HI, 0x07);
@@ -2758,8 +2848,6 @@ static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  	struct device *dev = &priv->client->dev;
  	u8 vc_map[UB960_MAX_RX_NPORTS] = { 0 };
  
-	dev_dbg(dev, "%s for pad %d\n", __func__, pad);
-
  	if (!ub960_pad_is_source(priv, pad))
  		return -EINVAL;
  
@@ -2805,9 +2893,7 @@ static int ub960_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
  		}
  
  		fd->entry[fd->num_entries].stream = route->source_stream;
-
-		fd->entry[fd->num_entries].flags =
-			V4L2_MBUS_FRAME_DESC_FL_LEN_MAX;
+		fd->entry[fd->num_entries].flags = source_entry->flags;
  		fd->entry[fd->num_entries].length = source_entry->length;
  		fd->entry[fd->num_entries].pixelcode = source_entry->pixelcode;
  
@@ -3015,13 +3101,15 @@ static int ub960_log_status(struct v4l2_subdev *sd)
  		ub960_read(priv, UB960_XR_AEQ_CTL1, &v);
  
  		dev_info(dev, "\t%s strobe\n",
-			 (v & BIT(0)) ? "Adaptive" : "Manual");
+			 (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) ? "Adaptive" :
+								  "Manual");
  
-		if (v & BIT(0)) {
+		if (v & UB960_XR_AEQ_CTL1_AEQ_SFILTER_EN) {
  			ub960_read(priv, UB960_XR_SFILTER_CFG, &v);
  
  			dev_info(dev, "\tStrobe range [%d, %d]\n",
-				 (v & 0xf) - 7, ((v >> 4) & 0xf) - 7);
+				 ((v >> UB960_XR_SFILTER_CFG_SFILTER_MIN_SHIFT) & 0xf) - 7,
+				 ((v >> UB960_XR_SFILTER_CFG_SFILTER_MAX_SHIFT) & 0xf) - 7);
  		}
  
  		ub960_rxport_get_strobe_pos(priv, nport, &strobe_pos);
@@ -3033,14 +3121,15 @@ static int ub960_log_status(struct v4l2_subdev *sd)
  		ub960_rxport_read(priv, nport, UB960_RR_AEQ_BYPASS, &v);
  
  		dev_info(dev, "\t%s EQ\n",
-			 (v & BIT(0)) ? "Manual" : "Adaptive");
+			 (v & UB960_RR_AEQ_BYPASS_ENABLE) ? "Manual" :
+							    "Adaptive");
  
-		if (!(v & BIT(0))) {
-			ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX,
-					  &v);
+		if (!(v & UB960_RR_AEQ_BYPASS_ENABLE)) {
+			ub960_rxport_read(priv, nport, UB960_RR_AEQ_MIN_MAX, &v);
  
-			dev_info(dev, "\tEQ range [%u, %u]\n", v & 0xf,
-				 (v >> 4) & 0xf);
+			dev_info(dev, "\tEQ range [%u, %u]\n",
+				 (v >> UB960_RR_AEQ_MIN_MAX_AEQ_FLOOR_SHIFT) & 0xf,
+				 (v >> UB960_RR_AEQ_MIN_MAX_AEQ_MAX_SHIFT) & 0xf);
  		}
  
  		ub960_rxport_get_eq_level(priv, nport, &eq_level);
@@ -3270,9 +3359,6 @@ static void ub960_rxport_free_ports(struct ub960_data *priv)
  		if (!rxport)
  			continue;
  
-		if (rxport->vpoc)
-			regulator_disable(rxport->vpoc);
-
  		fwnode_handle_put(rxport->fwnode);
  		of_node_put(rxport->remote_of_node);
  
@@ -3416,6 +3502,51 @@ static int ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
  	return 0;
  }
  
+static int ub960_parse_dt_rxport_ep_properties(struct ub960_data *priv, struct ub960_rxport *rxport)
+{
+	struct device *dev = &priv->client->dev;
+	struct device_node *ep_np;
+	int ret;
+	u32 v;
+
+	switch (rxport->rx_mode) {
+	case RXPORT_MODE_RAW10:
+	case RXPORT_MODE_RAW12_HF:
+	case RXPORT_MODE_RAW12_LF:
+		break;
+	default:
+		return 0;
+	}
+
+	ep_np = of_graph_get_endpoint_by_regs(priv->client->dev.of_node,
+					      rxport->nport, 0);
+	if (!ep_np)
+		return -EINVAL;
+
+	ret = of_property_read_u32(ep_np, "hsync-active", &v);
+	if (ret) {
+		dev_err(dev, "Failed to parse 'hsync-active' for port %u: %d\n",
+			rxport->nport, ret);
+		goto err;
+	}
+
+	rxport->lv_fv_pol |= v ? UB960_RR_PORT_CONFIG2_LV_POL_LOW : 0;
+
+	ret = of_property_read_u32(ep_np, "vsync-active", &v);
+	if (ret) {
+		dev_err(dev, "Failed to parse 'vsync-active' for port %u: %d\n",
+			rxport->nport, ret);
+		goto err;
+	}
+
+	rxport->lv_fv_pol |= v ? UB960_RR_PORT_CONFIG2_FV_POL_LOW : 0;
+
+	return 0;
+err:
+	of_node_put(ep_np);
+	return ret;
+}
+
  static int ub960_parse_dt_rxport(struct ub960_data *priv,
  				 struct device_node *np)
  {
@@ -3501,16 +3632,13 @@ static int ub960_parse_dt_rxport(struct ub960_data *priv,
  
  	rxport->fwnode = remote_ep_node;
  
-	// XXX enable somewhere else?
-	if (rxport->vpoc) {
-		ret = regulator_enable(rxport->vpoc);
-		if (ret)
-			goto err_ep_node_put;
-	}
+	ret = ub960_parse_dt_rxport_ep_properties(priv, rxport);
+	if (ret)
+		goto err_remote_ep_node_put;
  
  	return 0;
  
-err_ep_node_put:
+err_remote_ep_node_put:
  	fwnode_handle_put(rxport->fwnode);
  err_ser_node_put:
  	of_node_put(rxport->remote_of_node);
@@ -3659,8 +3787,6 @@ static int ub960_notify_bound(struct v4l2_async_notifier *notifier,
  	unsigned int i;
  	int ret;
  
-	dev_dbg(dev, "Bind %s\n", subdev->name);
-
  	ret = media_entity_get_fwnode_pad(&subdev->entity, rxport->fwnode,
  					  MEDIA_PAD_FL_SOURCE);
  	if (ret < 0) {
@@ -3700,11 +3826,7 @@ static void ub960_notify_unbind(struct v4l2_async_notifier *notifier,
  				struct v4l2_subdev *subdev,
  				struct v4l2_async_subdev *asd)
  {
-	struct ub960_data *priv = sd_to_ub960(notifier->sd);
  	struct ub960_rxport *rxport = to_ub960_asd(asd)->rxport;
-	struct device *dev = &priv->client->dev;
-
-	dev_dbg(dev, "Unbind %s\n", subdev->name);
  
  	rxport->sd = NULL;
  }
@@ -3755,10 +3877,6 @@ static int ub960_v4l2_notifier_register(struct ub960_data *priv)
  
  static void ub960_v4l2_notifier_unregister(struct ub960_data *priv)
  {
-	struct device *dev = &priv->client->dev;
-
-	dev_dbg(dev, "Unregister async notif\n");
-
  	v4l2_async_nf_unregister(&priv->notifier);
  	v4l2_async_nf_cleanup(&priv->notifier);
  }
@@ -3870,7 +3988,8 @@ static void ub960_reset(struct ub960_data *priv, bool reset_regs)
  	int ret;
  	u8 bit;
  
-	bit = reset_regs ? BIT(1) : BIT(0);
+	bit = reset_regs ? UB960_SR_RESET_DIGITAL_RESET1 :
+			   UB960_SR_RESET_DIGITAL_RESET0;
  
  	ub960_write(priv, UB960_SR_RESET, bit);
  
@@ -4041,7 +4160,9 @@ static int ub960_probe(struct i2c_client *client)
  
  	/* release GPIO lock */
  	if (priv->hw_data->ub9702)
-		ub960_update_bits(priv, UB960_SR_RESET, BIT(5), BIT(5));
+		ub960_update_bits(priv, UB960_SR_RESET,
+				  UB960_SR_RESET_GPIO_LOCK_RELEASE,
+				  UB960_SR_RESET_GPIO_LOCK_RELEASE);
  
  	ret = ub960_parse_dt(priv);
  	if (ret)
@@ -4051,10 +4172,14 @@ static int ub960_probe(struct i2c_client *client)
  	if (ret)
  		goto err_free_ports;
  
-	ret = ub960_init_rx_ports(priv);
+	ret = ub960_rxport_enable_vpocs(priv);
  	if (ret)
  		goto err_free_ports;
  
+	ret = ub960_init_rx_ports(priv);
+	if (ret)
+		goto err_disable_vpocs;
+
  	ub960_reset(priv, false);
  
  	ub960_rxport_wait_locks(priv, 0xf, NULL);
@@ -4067,7 +4192,7 @@ static int ub960_probe(struct i2c_client *client)
  
  	ret = ub960_init_atr(priv);
  	if (ret)
-		goto err_free_ports;
+		goto err_disable_vpocs;
  
  	ret = ub960_rxport_add_serializers(priv);
  	if (ret)
@@ -4077,43 +4202,20 @@ static int ub960_probe(struct i2c_client *client)
  	if (ret)
  		goto err_free_sers;
  
-	if (client->irq) {
-		dev_dbg(dev, "using IRQ %d\n", client->irq);
+	if (client->irq)
+		dev_warn(dev, "irq support not implemented, using polling\n");
  
-		ret = devm_request_threaded_irq(dev, client->irq, NULL,
-						ub960_handle_events,
-						IRQF_ONESHOT, client->name,
-						priv);
-		if (ret) {
-			dev_err(dev, "Cannot enable IRQ (%d)\n", ret);
-			goto err_irq;
-		}
-
-		/* Disable GPIO3 as input */
-		ub960_update_bits(priv, UB960_SR_GPIO_INPUT_CTL, BIT(3), 0);
-		/* Enable GPIO3 as output, active low interrupt */
-		ub960_write(priv, UB960_SR_GPIO_PIN_CTL(3), 0xd1);
-
-		ub960_write(priv, UB960_SR_INTERRUPT_CTL,
-			    UB960_SR_INTERRUPT_CTL_ALL);
-	} else {
-		/* No IRQ, fallback to polling */
-		schedule_delayed_work(&priv->poll_work,
-				      msecs_to_jiffies(UB960_POLL_TIME_MS));
-
-		dev_dbg(dev, "using polling mode\n");
-	}
-
-	dev_info(dev, "Successfully probed\n");
+	schedule_delayed_work(&priv->poll_work,
+			      msecs_to_jiffies(UB960_POLL_TIME_MS));
  
  	return 0;
  
-err_irq:
-	ub960_destroy_subdev(priv);
  err_free_sers:
  	ub960_rxport_remove_serializers(priv);
  err_uninit_atr:
  	ub960_uninit_atr(priv);
+err_disable_vpocs:
+	ub960_rxport_disable_vpocs(priv);
  err_free_ports:
  	ub960_rxport_free_ports(priv);
  	ub960_txport_free_ports(priv);
@@ -4130,20 +4232,17 @@ static void ub960_remove(struct i2c_client *client)
  	struct v4l2_subdev *sd = i2c_get_clientdata(client);
  	struct ub960_data *priv = sd_to_ub960(sd);
  
-	dev_dbg(&client->dev, "Removing\n");
-
  	cancel_delayed_work_sync(&priv->poll_work);
  
  	ub960_destroy_subdev(priv);
  	ub960_rxport_remove_serializers(priv);
  	ub960_uninit_atr(priv);
+	ub960_rxport_disable_vpocs(priv);
  	ub960_rxport_free_ports(priv);
  	ub960_txport_free_ports(priv);
  	ub960_disable_core_hw(priv);
  	mutex_destroy(&priv->atr_alias_table.lock);
  	mutex_destroy(&priv->reg_lock);
-
-	dev_dbg(&client->dev, "Remove done\n");
  }
  
  static const struct ub960_hw_data ds90ub960_hw = {
@@ -4190,6 +4289,7 @@ static struct i2c_driver ds90ub960_driver = {
  module_i2c_driver(ds90ub960_driver);
  
  MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Texas Instruments DS90UB960-Q1 FPDLink-3 deserializer driver");
+MODULE_DESCRIPTION("Texas Instruments DS90UB960-Q1 FPD-Link deserializer driver");
  MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
  MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
+MODULE_IMPORT_NS(I2C_ATR);
diff --git a/include/linux/i2c-atr.h b/include/linux/i2c-atr.h
index 044c87c5b336..5b879115dfc6 100644
--- a/include/linux/i2c-atr.h
+++ b/include/linux/i2c-atr.h
@@ -3,6 +3,7 @@
   * I2C Address Translator
   *
   * Copyright (c) 2019,2022 Luca Ceresoli <luca@lucaceresoli.net>
+ * Copyright (c) 2022,2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
   *
   * Based on i2c-mux.h
   */
@@ -11,12 +12,11 @@
  #define _LINUX_I2C_ATR_H
  
  #include <linux/i2c.h>
-#include <linux/mutex.h>
  #include <linux/types.h>
  
  struct device;
-struct i2c_atr;
  struct fwnode_handle;
+struct i2c_atr;
  
  /**
   * struct i2c_atr_ops - Callbacks from ATR to the device driver.
@@ -42,41 +42,77 @@ struct i2c_atr_ops {
  };
  
  /**
- * struct i2c_atr - Represents the I2C ATR instance
+ * i2c_atr_new() - Allocate and initialize an I2C ATR helper.
+ * @parent:       The parent (upstream) adapter
+ * @dev:          The device acting as an ATR
+ * @ops:          Driver-specific callbacks
+ * @max_adapters: Maximum number of child adapters
+ *
+ * The new ATR helper is connected to the parent adapter but has no child
+ * adapters. Call i2c_atr_add_adapter() to add some.
+ *
+ * Call i2c_atr_delete() to remove.
+ *
+ * Return: pointer to the new ATR helper object, or ERR_PTR
   */
-struct i2c_atr {
-	/* private: internal use only */
-
-	struct i2c_adapter *parent;
-	struct device *dev;
-	const struct i2c_atr_ops *ops;
-
-	void *priv;
-
-	struct i2c_algorithm algo;
-	/* lock for the I2C bus segment (see struct i2c_lock_operations) */
-	struct mutex lock;
-	int max_adapters;
-
-	struct i2c_adapter *adapter[];
-};
-
  struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
  			    const struct i2c_atr_ops *ops, int max_adapters);
-void i2c_atr_delete(struct i2c_atr *atr);
-
-static inline void i2c_atr_set_clientdata(struct i2c_atr *atr, void *data)
-{
-	atr->priv = data;
-}
  
-static inline void *i2c_atr_get_clientdata(struct i2c_atr *atr)
-{
-	return atr->priv;
-}
+/**
+ * i2c_atr_delete - Delete an I2C ATR helper.
+ * @atr: I2C ATR helper to be deleted.
+ *
+ * Precondition: all the adapters added with i2c_atr_add_adapter() mumst be
+ * removed by calling i2c_atr_del_adapter().
+ */
+void i2c_atr_delete(struct i2c_atr *atr);
  
+/**
+ * i2c_atr_add_adapter - Create a child ("downstream") I2C bus.
+ * @atr:        The I2C ATR
+ * @chan_id:    Index of the new adapter (0 .. max_adapters-1).  This value is
+ *              passed to the callbacks in `struct i2c_atr_ops`.
+ * @bus_handle: The fwnode handle that points to the adapter's i2c
+ *              peripherals, or NULL.
+ *
+ * After calling this function a new i2c bus will appear. Adding and removing
+ * devices on the downstream bus will result in calls to the
+ * &i2c_atr_ops->attach_client and &i2c_atr_ops->detach_client callbacks for the
+ * driver to assign an alias to the device.
+ *
+ * The adapter's fwnode is set to @bus_handle, or if @bus_handle is NULL the
+ * function looks for a child node whose 'reg' property matches the chan_id
+ * under the i2c-atr device's 'i2c-atr' node.
+ *
+ * Call i2c_atr_del_adapter() to remove the adapter.
+ *
+ * Return: 0 on success, a negative error code otherwise.
+ */
  int i2c_atr_add_adapter(struct i2c_atr *atr, u32 chan_id,
-			struct fwnode_handle *bus_np);
+			struct fwnode_handle *bus_handle);
+
+/**
+ * i2c_atr_del_adapter - Remove a child ("downstream") I2C bus added by
+ *                       i2c_atr_add_adapter(). If no I2C bus has been added
+ *                       this function is a no-op.
+ * @atr:     The I2C ATR
+ * @chan_id: Index of the adapter to be removed (0 .. max_adapters-1)
+ */
  void i2c_atr_del_adapter(struct i2c_atr *atr, u32 chan_id);
  
+/**
+ * i2c_atr_set_driver_data - Set private driver data to the i2c-atr instance.
+ * @atr:  The I2C ATR
+ * @data: Pointer to the data to store
+ */
+void i2c_atr_set_driver_data(struct i2c_atr *atr, void *data);
+
+/**
+ * i2c_atr_get_driver_data - Get the stored drive data.
+ * @atr:     The I2C ATR
+ *
+ * Return: Pointer to the stored data
+ */
+void *i2c_atr_get_driver_data(struct i2c_atr *atr);
+
  #endif /* _LINUX_I2C_ATR_H */