diff mbox series

[18/19] media: i2c: ds90ub960: Configure serializer using back-channel

Message ID 20250110-ub9xx-improvements-v1-18-e0b9a1f644da@ideasonboard.com
State New
Headers show
Series media: i2c: ds90ub9xx: Error handling, UB9702 improvements | expand

Commit Message

Tomi Valkeinen Jan. 10, 2025, 9:14 a.m. UTC
From: Jai Luthra <jai.luthra@ideasonboard.com>

For DS90UB9702-Q1, it is recommended to configure some serializer
settings over the back-channel before the forward-channel is active.

This can only be done if the serializer's I2C address on the FPD-Link
bus is populated in the device tree node.

Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
 drivers/media/i2c/ds90ub960.c | 124 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 120 insertions(+), 4 deletions(-)

Comments

Tomi Valkeinen Jan. 24, 2025, 1:29 p.m. UTC | #1
Hi,

On 15/01/2025 16:29, Sakari Ailus wrote:
> Moi,
> 
> On Fri, Jan 10, 2025 at 11:14:18AM +0200, Tomi Valkeinen wrote:
>> @@ -2956,6 +3033,36 @@ static int ub960_init_rx_ports_ub9702(struct ub960_data *priv)
>>   	if (ret)
>>   		return ret;
>>   
>> +	for_each_active_rxport(priv) {
>> +		if (it.rxport->ser.addr >= 0) {
>> +			/*
>> +			 * Set serializer's I2C address if set in the dts file,
>> +			 * and freeze it to prevent updates from the FC.
>> +			 */
>> +			ub960_rxport_write(priv, it.nport, UB960_RR_SER_ID,
>> +					   it.rxport->ser.addr << 1 |
>> +					   UB960_RR_SER_ID_FREEZE_DEVICE_ID,
>> +					   &ret);
>> +		}
>> +
>> +		/* Set serializer I2C alias with auto-ack */
>> +		ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID,
>> +				   it.rxport->ser.alias << 1 |
>> +				   UB960_RR_SER_ALIAS_ID_AUTO_ACK, &ret);
>> +
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	for_each_active_rxport(priv) {
>> +		if (fwnode_device_is_compatible(it.rxport->ser.fwnode,
>> +						"ti,ds90ub971-q1")) {
> 
> I guess one instance is fine but consider using match data instead.

This is checking the model of the (remote) serializer, not the 
deserializer (which this driver deals with).

Unfortunately we need to do some early configuration before the 
serializer driver probes, and that configuration depends on the 
serializer model.

We could, perhaps, get the serializer models once and store it in the 
rxport data for later use.

  Tomi
diff mbox series

Patch

diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index a534d077f045..1ec3c060d4c6 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -248,13 +248,16 @@  MODULE_PARM_DESC(enable_sscg, "Enable SSCG (if available)");
 
 #define UB960_RR_BCC_CONFIG			0x58
 #define UB960_RR_BCC_CONFIG_BC_ALWAYS_ON	BIT(4)
+#define UB960_RR_BCC_CONFIG_AUTO_ACK_ALL	BIT(5)
 #define UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH	BIT(6)
 #define UB960_RR_BCC_CONFIG_BC_FREQ_SEL_MASK	GENMASK(2, 0)
 
 #define UB960_RR_DATAPATH_CTL1			0x59
 #define UB960_RR_DATAPATH_CTL2			0x5a
 #define UB960_RR_SER_ID				0x5b
+#define UB960_RR_SER_ID_FREEZE_DEVICE_ID	BIT(0)
 #define UB960_RR_SER_ALIAS_ID			0x5c
+#define UB960_RR_SER_ALIAS_ID_AUTO_ACK		BIT(0)
 
 /* For these two register sets: n < UB960_MAX_PORT_ALIASES */
 #define UB960_RR_SLAVE_ID(n)			(0x5d + (n))
@@ -507,7 +510,9 @@  struct ub960_rxport {
 		struct fwnode_handle *fwnode;
 		struct i2c_client *client;
 		unsigned short alias; /* I2C alias (lower 7 bits) */
+		short addr; /* Local I2C address (lower 7 bits) */
 		struct ds90ub9xx_platform_data pdata;
+		struct regmap *regmap;
 	} ser;
 
 	enum ub960_rxport_mode  rx_mode;
@@ -2005,6 +2010,78 @@  static unsigned long ub960_calc_bc_clk_rate_ub9702(struct ub960_data *priv,
 	}
 }
 
+static int ub960_rxport_serializer_write(struct ub960_rxport *rxport, u8 reg,
+					 u8 val, int *err)
+{
+	struct ub960_data *priv = rxport->priv;
+	struct device *dev = &priv->client->dev;
+	union i2c_smbus_data data;
+	int ret;
+
+	if (err && *err)
+		return *err;
+
+	data.byte = val;
+
+	ret = i2c_smbus_xfer(priv->client->adapter, rxport->ser.alias, 0,
+			     I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, &data);
+	if (ret)
+		dev_err(dev,
+			"rx%u: cannot write serializer register 0x%02x (%d)!\n",
+			rxport->nport, reg, ret);
+
+	if (ret && err)
+		*err = ret;
+
+	return ret;
+}
+
+static int ub960_rxport_bc_ser_config(struct ub960_rxport *rxport)
+{
+	struct ub960_data *priv = rxport->priv;
+	struct device *dev = &priv->client->dev;
+	u8 nport = rxport->nport;
+	int ret;
+
+	/* Skip port if serializer's address is not known */
+	if (rxport->ser.addr < 0) {
+		dev_dbg(dev,
+			"rx%u: 'i2c-addr' property missing, skip serializer configuration\n",
+			nport);
+		return 0;
+	}
+
+	/*
+	 * Note: the code here probably only works for CSI-2 serializers in
+	 * sync mode. To support other serializers the BC related configuration
+	 * should be done before calling this function.
+	 */
+
+	/* Enable I2C passthrough and auto-ack on BC */
+	ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG,
+				 UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH |
+					 UB960_RR_BCC_CONFIG_AUTO_ACK_ALL,
+				 UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH |
+					 UB960_RR_BCC_CONFIG_AUTO_ACK_ALL,
+				 &ret);
+
+	if (ret)
+		return ret;
+
+	/* Disable BC alternate mode auto detect */
+	ub960_rxport_serializer_write(rxport, 0x4b, 0x02, &ret);
+	/* Decrease link detect timer */
+	ub960_rxport_serializer_write(rxport, 0x49, 0x06, &ret);
+
+	/* Disable I2C passthrough and auto-ack on BC */
+	ub960_rxport_update_bits(priv, nport, UB960_RR_BCC_CONFIG,
+				 UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH |
+					 UB960_RR_BCC_CONFIG_AUTO_ACK_ALL,
+				 0x0, &ret);
+
+	return ret;
+}
+
 static int ub960_rxport_add_serializer(struct ub960_data *priv, u8 nport)
 {
 	struct ub960_rxport *rxport = priv->rxports[nport];
@@ -2956,6 +3033,36 @@  static int ub960_init_rx_ports_ub9702(struct ub960_data *priv)
 	if (ret)
 		return ret;
 
+	for_each_active_rxport(priv) {
+		if (it.rxport->ser.addr >= 0) {
+			/*
+			 * Set serializer's I2C address if set in the dts file,
+			 * and freeze it to prevent updates from the FC.
+			 */
+			ub960_rxport_write(priv, it.nport, UB960_RR_SER_ID,
+					   it.rxport->ser.addr << 1 |
+					   UB960_RR_SER_ID_FREEZE_DEVICE_ID,
+					   &ret);
+		}
+
+		/* Set serializer I2C alias with auto-ack */
+		ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID,
+				   it.rxport->ser.alias << 1 |
+				   UB960_RR_SER_ALIAS_ID_AUTO_ACK, &ret);
+
+		if (ret)
+			return ret;
+	}
+
+	for_each_active_rxport(priv) {
+		if (fwnode_device_is_compatible(it.rxport->ser.fwnode,
+						"ti,ds90ub971-q1")) {
+			ret = ub960_rxport_bc_ser_config(it.rxport);
+			if (ret)
+				return ret;
+		}
+	}
+
 	if (ub960_enable_sscg) {
 		ret = ub960_enable_sscg_ub9702(priv);
 		if (ret)
@@ -3090,16 +3197,17 @@  static int ub960_init_rx_ports_ub9702(struct ub960_data *priv)
 		ub960_rxport_write(priv, it.nport, UB960_RR_PORT_ICR_LO, 0x7f,
 				   &ret);
 
+		/* Clear serializer I2C alias auto-ack */
+		ub960_rxport_update_bits(priv, it.nport, UB960_RR_SER_ALIAS_ID,
+					 UB960_RR_SER_ALIAS_ID_AUTO_ACK, 0,
+					 &ret);
+
 		/* Enable I2C_PASS_THROUGH */
 		ub960_rxport_update_bits(priv, it.nport, UB960_RR_BCC_CONFIG,
 					 UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH,
 					 UB960_RR_BCC_CONFIG_I2C_PASS_THROUGH,
 					 &ret);
 
-		/* Enable I2C communication to the serializer via the alias */
-		ub960_rxport_write(priv, it.nport, UB960_RR_SER_ALIAS_ID,
-				   it.rxport->ser.alias << 1, &ret);
-
 		if (ret)
 			return ret;
 	}
@@ -4258,6 +4366,7 @@  ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
 	s32 strobe_pos;
 	u32 eq_level;
 	u32 ser_i2c_alias;
+	u32 ser_i2c_addr;
 	int ret;
 
 	cdr_mode = RXPORT_CDR_FPD3;
@@ -4363,6 +4472,13 @@  ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
 	}
 	rxport->ser.alias = ser_i2c_alias;
 
+	ret = fwnode_property_read_u32(link_fwnode, "i2c-addr",
+				       &ser_i2c_addr);
+	if (ret)
+		rxport->ser.addr = -EINVAL;
+	else
+		rxport->ser.addr = ser_i2c_addr;
+
 	rxport->ser.fwnode = fwnode_get_named_child_node(link_fwnode, "serializer");
 	if (!rxport->ser.fwnode) {
 		dev_err(dev, "rx%u: missing 'serializer' node\n", nport);