Re: [PATCH v6 0/8] i2c-atr and FPDLink

Tomi Valkeinen
2023-01-05T14:06:48Z

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 */