diff mbox series

[RFC,10/12] staging: media: max96712: add gpiochip functionality

Message ID 20250131163408.2019144-11-laurentiu.palcu@oss.nxp.com
State New
Headers show
Series [RFC,01/12] media: mc: Add INTERNAL pad flag | expand

Commit Message

Laurentiu Palcu Jan. 31, 2025, 4:34 p.m. UTC
The deserializer has GPIOs that can be used for various purposes. Add
support for gpiochip.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@oss.nxp.com>
---
 drivers/staging/media/max96712/max96712.c | 140 ++++++++++++++++++++++
 1 file changed, 140 insertions(+)
diff mbox series

Patch

diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c
index ed1d46ea98cb9..307b2f1d3a6be 100644
--- a/drivers/staging/media/max96712/max96712.c
+++ b/drivers/staging/media/max96712/max96712.c
@@ -7,6 +7,7 @@ 
  */
 
 #include <linux/delay.h>
+#include <linux/gpio/driver.h>
 #include <linux/i2c.h>
 #include <linux/i2c-mux.h>
 #include <linux/module.h>
@@ -108,6 +109,41 @@ 
 #define   CSI2_LANE_CNT_MASK				GENMASK(7, 6)
 #define   CSI2_LANE_CNT_SHIFT				6
 
+/* GPIO_A: 0 <= gpio < 11 */
+#define MAX96712_GPIO_A_A(gpio)				CCI_REG8(0x0300 + (gpio) * 0x03)
+#define   GPIO_OUT_DIS					BIT(0)
+#define   GPIO_TX_EN_A					BIT(1)
+#define   GPIO_RX_EN_A					BIT(2)
+#define   GPIO_IN					BIT(3)
+#define   GPIO_OUT					BIT(4)
+#define   TX_COMP_EN_A					BIT(5)
+#define   RES_CFG					BIT(7)
+#define MAX96712_GPIO_A_B(gpio)				CCI_REG8(0x0301 + (gpio) * 0x03)
+#define   GPIO_TX_ID_A_MASK				GENMASK(4, 0)
+#define   GPIO_TX_ID_A_SHIFT				0
+#define   OUT_TYPE					BIT(5)
+#define   PULL_UPDN_SEL_MASK				GENMASK(7, 6)
+#define   PULL_UPDN_SEL_SHIFT				6
+#define MAX96712_GPIO_A_C(gpio)				CCI_REG8(0x0302 + (gpio) * 0x03)
+#define   GPIO_RX_ID_A_MASK				GENMASK(4, 0)
+#define   GPIO_RX_ID_A_SHIFT				0
+#define   GPIO_RECVED_A					BIT(6)
+#define   OVR_RES_CFG					BIT(7)
+
+/* GPIO_B, GPIO_C, GPIO_D: 0 <= gpio < 11, link: 1, 2, 3 */
+#define MAX96712_GPIO_B(gpio)				CCI_REG8(0x0301 + (link) * 0x36 + \
+								 (gpio) * 0x03)
+#define   GPIO_TX_ID_MASK				GENMASK(4, 0)
+#define   GPIO_TX_ID_SHIFT				0
+#define   GPIO_TX_EN					BIT(5)
+#define   TX_COMP_EN					BIT(6)
+#define MAX96712_GPIO_C(gpio)				CCI_REG8(0x0302 + (link) * 0x36 + \
+								 (gpio) * 0x03)
+#define   GPIO_RX_ID_MASK				GENMASK(4, 0)
+#define   GPIO_RX_ID_SHIFT				0
+#define   GPIO_RX_EN					BIT(5)
+#define   GPIO_RECVED					BIT(6)
+
 /* VRX_PATGEN */
 #define MAX96712_VRX_PATGEN_0				CCI_REG8(0x1050)
 #define   VTG_MODE_MASK					GENMASK(1, 0)
@@ -160,6 +196,8 @@ 
 
 #define MHZ(f)						((f) * 1000000U)
 
+#define MAX96712_NUM_GPIO				12
+
 enum max96712_pattern {
 	MAX96712_PATTERN_CHECKERBOARD = 0,
 	MAX96712_PATTERN_GRADIENT,
@@ -179,6 +217,8 @@  struct max96712_priv {
 	struct regmap *regmap;
 	struct gpio_desc *gpiod_pwdn;
 
+	struct gpio_chip gpio_chip;
+
 	struct i2c_mux_core *mux;
 	int mux_chan;
 
@@ -830,6 +870,7 @@  static int max96712_v4l2_register(struct max96712_priv *priv)
 	return ret;
 }
 
+/* I2C Mux section */
 static int max96712_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
 {
 	struct max96712_priv *priv = i2c_mux_priv(muxc);
@@ -885,6 +926,101 @@  static int max96712_i2c_init(struct max96712_priv *priv)
 	return ret;
 }
 
+/* GPIO chip section */
+static int max96712_gpiochip_get(struct gpio_chip *gpiochip,
+				 unsigned int offset)
+{
+	struct max96712_priv *priv = gpiochip_get_data(gpiochip);
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, MAX96712_GPIO_A_A(offset), &val);
+	if (ret)
+		return ret;
+
+	if (val & GPIO_OUT_DIS)
+		return !!(val & GPIO_IN);
+	else
+		return !!(val & GPIO_OUT);
+}
+
+static void max96712_gpiochip_set(struct gpio_chip *gpiochip,
+				  unsigned int offset, int value)
+{
+	struct max96712_priv *priv = gpiochip_get_data(gpiochip);
+
+	regmap_update_bits(priv->regmap, MAX96712_GPIO_A_A(offset), GPIO_OUT,
+			   GPIO_OUT);
+}
+
+static int max96712_gpio_get_direction(struct gpio_chip *gpiochip,
+				       unsigned int offset)
+{
+	struct max96712_priv *priv = gpiochip_get_data(gpiochip);
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(priv->regmap, MAX96712_GPIO_A_A(offset), &val);
+	if (ret < 0)
+		return ret;
+
+	return !!(val & GPIO_OUT_DIS);
+}
+
+static int max96712_gpio_direction_out(struct gpio_chip *gpiochip,
+				       unsigned int offset, int value)
+{
+	struct max96712_priv *priv = gpiochip_get_data(gpiochip);
+
+	return regmap_update_bits(priv->regmap, MAX96712_GPIO_A_A(offset),
+				  GPIO_OUT_DIS | GPIO_OUT,
+				  value ? GPIO_OUT : 0);
+}
+
+static int max96712_gpio_direction_in(struct gpio_chip *gpiochip,
+				      unsigned int offset)
+{
+	struct max96712_priv *priv = gpiochip_get_data(gpiochip);
+
+	return regmap_update_bits(priv->regmap, MAX96712_GPIO_A_A(offset),
+				  GPIO_OUT_DIS, GPIO_OUT_DIS);
+}
+
+static int max96712_gpiochip_probe(struct max96712_priv *priv)
+{
+	struct device *dev = &priv->client->dev;
+	struct gpio_chip *gc = &priv->gpio_chip;
+	int i, ret = 0;
+
+	gc->label = dev_name(dev);
+	gc->parent = dev;
+	gc->owner = THIS_MODULE;
+	gc->ngpio = MAX96712_NUM_GPIO;
+	gc->base = -1;
+	gc->can_sleep = true;
+	gc->get_direction = max96712_gpio_get_direction;
+	gc->direction_input = max96712_gpio_direction_in;
+	gc->direction_output = max96712_gpio_direction_out;
+	gc->request = gpiochip_generic_request;
+	gc->set = max96712_gpiochip_set;
+	gc->get = max96712_gpiochip_get;
+	gc->of_gpio_n_cells = 2;
+
+	/* Disable GPIO forwarding */
+	for (i = 0; i < gc->ngpio; i++)
+		regmap_update_bits(priv->regmap, MAX96712_GPIO_A_A(i),
+				   GPIO_RX_EN_A | GPIO_TX_EN_A, 0);
+
+	ret = devm_gpiochip_add_data(dev, gc, priv);
+	if (ret) {
+		dev_err(dev, "Unable to create gpio_chip\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/* DT parsing section */
 static int max96712_parse_rx_ports(struct max96712_priv *priv, struct device_node *node,
 				   struct of_endpoint *ep)
 {
@@ -1061,6 +1197,10 @@  static int max96712_probe(struct i2c_client *client)
 
 	max96712_mipi_configure(priv);
 
+	ret = max96712_gpiochip_probe(priv);
+	if (ret)
+		return ret;
+
 	ret = max96712_v4l2_register(priv);
 	if (ret)
 		return ret;