[RFC 06/12] staging: media: max96712: add I2C mux support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux