]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
media: i2c: ds90ub960: Configure serializer using back-channel
authorJai Luthra <jai.luthra@ideasonboard.com>
Mon, 3 Mar 2025 16:02:21 +0000 (21:32 +0530)
committerHans Verkuil <hverkuil@xs4all.nl>
Fri, 25 Apr 2025 08:15:11 +0000 (10:15 +0200)
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: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
drivers/media/i2c/ds90ub953.h
drivers/media/i2c/ds90ub960.c

index 8bb28f0daee96044a7bf838a680e506af4f70f10..de606474493f8d95a412e5564b0fac21885e581d 100644 (file)
@@ -92,4 +92,8 @@
 #define UB953_IND_PGEN_VFP                     0x0f
 #define UB953_IND_PGEN_COLOR(n)                        (0x10 + (n)) /* n <= 15 */
 
+/* UB971 Registers */
+
+#define UB971_ENH_BC_CHK                       0x4b
+
 #endif /* __MEDIA_I2C_DS90UB953_H__ */
index 56b1a88c1c29fbefecae60f4c9976e8ede554447..cad25dcbca11bf6597d00eede6dfa9110f445886 100644 (file)
@@ -52,6 +52,8 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
+#include "ds90ub953.h"
+
 #define MHZ(v) ((u32)((v) * HZ_PER_MHZ))
 
 /*
 
 #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))
@@ -486,7 +491,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;
@@ -1984,6 +1991,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: serializer address missing, skip 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, UB971_ENH_BC_CHK, 0x02, &ret);
+       /* Decrease link detect timer */
+       ub960_rxport_serializer_write(rxport, UB953_REG_BC_CTRL, 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];
@@ -2860,6 +2939,36 @@ static int ub960_init_rx_ports_ub9702(struct ub960_data *priv)
        if (ret)
                return ret;
 
+       for_each_active_rxport(priv, it) {
+               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, it) {
+               if (fwnode_device_is_compatible(it.rxport->ser.fwnode,
+                                               "ti,ds90ub971-q1")) {
+                       ret = ub960_rxport_bc_ser_config(it.rxport);
+                       if (ret)
+                               return ret;
+               }
+       }
+
        for_each_active_rxport_fpd4(priv, it) {
                /* Hold state machine in reset */
                ub960_rxport_write(priv, it.nport, UB9702_RR_RX_SM_SEL_2, 0x10,
@@ -2988,16 +3097,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;
        }
@@ -4156,6 +4266,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;
@@ -4267,6 +4378,13 @@ ub960_parse_dt_rxport_link_properties(struct ub960_data *priv,
                return -EINVAL;
        }
 
+       ret = fwnode_property_read_u32(rxport->ser.fwnode, "reg",
+                                      &ser_i2c_addr);
+       if (ret)
+               rxport->ser.addr = -EINVAL;
+       else
+               rxport->ser.addr = ser_i2c_addr;
+
        return 0;
 }