The deserializer chip allows communicating with remote serializers over an I2C control channel within the GMSL link. However, to avoid address collisions, we need to enable only the I2C CC corresponding to a certain GMSL link and disable the other ones. Hence, add support for I2C multiplexer which will allow us to do just that. Signed-off-by: Laurentiu Palcu <laurentiu.palcu@xxxxxxxxxxx> --- drivers/staging/media/max96712/max96712.c | 76 ++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c index 546660e4b3d1e..f68a1d241b846 100644 --- a/drivers/staging/media/max96712/max96712.c +++ b/drivers/staging/media/max96712/max96712.c @@ -8,6 +8,7 @@ #include <linux/delay.h> #include <linux/i2c.h> +#include <linux/i2c-mux.h> #include <linux/module.h> #include <linux/of_graph.h> #include <linux/regmap.h> @@ -17,6 +18,17 @@ #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> +/* DEV */ +#define MAX96712_DEV_REG3 CCI_REG8(0x0003) +#define DIS_REM_CC_A_MASK GENMASK(1, 0) +#define DIS_REM_CC_A_SHIFT 0 +#define DIS_REM_CC_B_MASK GENMASK(3, 2) +#define DIS_REM_CC_B_SHIFT 2 +#define DIS_REM_CC_C_MASK GENMASK(5, 4) +#define DIS_REM_CC_C_SHIFT 4 +#define DIS_REM_CC_D_MASK GENMASK(7, 6) +#define DIS_REM_CC_D_SHIFT 6 + /* TOP_CTRL */ #define MAX96712_DEBUG_EXTRA_REG CCI_REG8(0x0009) #define DEBUG_EXTRA_PCLK_25MHZ 0x00 @@ -162,6 +174,9 @@ struct max96712_priv { struct regmap *regmap; struct gpio_desc *gpiod_pwdn; + struct i2c_mux_core *mux; + int mux_chan; + const struct max96712_info *info; bool cphy; @@ -489,6 +504,61 @@ static int max96712_v4l2_register(struct max96712_priv *priv) return ret; } +static int max96712_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct max96712_priv *priv = i2c_mux_priv(muxc); + u8 val = 0xff; + + if (priv->mux_chan == chan) + return 0; + + val &= ~(0x3 << (chan * 2)); + val |= 0x2 << (chan * 2); + max96712_write(priv, MAX96712_DEV_REG3, val); + + priv->mux_chan = chan; + + return 0; +} + +static int max96712_i2c_init(struct max96712_priv *priv) +{ + int link; + int ret; + + if (!i2c_check_functionality(priv->client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + priv->mux_chan = -1; + + priv->mux = i2c_mux_alloc(priv->client->adapter, &priv->client->dev, + priv->n_rx_ports, 0, I2C_MUX_LOCKED, + max96712_i2c_mux_select, NULL); + if (!priv->mux) { + dev_err(&priv->client->dev, "Could not alloc I2C multiplexer.\n"); + return -ENOMEM; + } + + priv->mux->priv = priv; + + for (link = 0; link < MAX96712_MAX_RX_PORTS; link++) { + if (!(priv->rx_port_mask & BIT(link))) + continue; + + ret = i2c_mux_add_adapter(priv->mux, 0, link); + if (ret < 0) { + dev_err(&priv->client->dev, "Could not add I2C mux adapter.\n"); + goto error; + } + } + + return 0; + +error: + i2c_mux_del_adapters(priv->mux); + return ret; +} + static int max96712_parse_rx_ports(struct max96712_priv *priv, struct device_node *node, struct of_endpoint *ep) { @@ -665,7 +735,11 @@ static int max96712_probe(struct i2c_client *client) max96712_mipi_configure(priv); - return max96712_v4l2_register(priv); + ret = max96712_v4l2_register(priv); + if (ret) + return ret; + + return max96712_i2c_init(priv); } static void max96712_remove(struct i2c_client *client) -- 2.44.1