[PATCH v3 1/3] drm/bridge: tfp410: Support basic I2C interface

Neil Armstrong neil.armstrong at linaro.org
Fri Mar 31 08:59:55 UTC 2023


On 20/02/2023 23:10, Jonathan Cormier wrote:
> From: Michael Williamson <michael.williamson at criticallink.com>
> 
> The TFP410 driver does not support I2C.  As such, the device remains in
> Power Down if the I2C is enabled by the bootstrap pins.
> 
> Add basic support for the I2C interface, and provide support to take
> the device out of power down when enabled.  Also read the bootstrap mode
> pins via the CTL_1_MODE register when using the I2C bus.
> 
> Signed-off-by: Michael Williamson <michael.williamson at criticallink.com>
> Signed-off-by: Jonathan Cormier <jcormier at criticallink.com>
> ---
>   drivers/gpu/drm/bridge/ti-tfp410.c | 93 +++++++++++++++++++++++++++-----------
>   1 file changed, 67 insertions(+), 26 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
> index b9635abbad16..bb3f8d0ff207 100644
> --- a/drivers/gpu/drm/bridge/ti-tfp410.c
> +++ b/drivers/gpu/drm/bridge/ti-tfp410.c
> @@ -6,6 +6,7 @@
>   
>   #include <linux/gpio/consumer.h>
>   #include <linux/i2c.h>
> +#include <linux/regmap.h>
>   #include <linux/media-bus-format.h>
>   #include <linux/module.h>
>   #include <linux/of_graph.h>
> @@ -21,6 +22,20 @@
>   
>   #define HOTPLUG_DEBOUNCE_MS		1100
>   
> +#define TFP410_REG_CTL_1_MODE	0x08
> +#define TFP410_BIT_PD   BIT(0)
> +#define TFP410_BIT_EDGE BIT(1)
> +#define TFP410_BIT_BSEL BIT(2)
> +#define TFP410_BIT_DSEL BIT(3)
> +
> +static const struct regmap_config tfp410_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +
> +	.max_register = 0xff,
> +	.cache_type = REGCACHE_NONE,
> +};
> +
>   struct tfp410 {
>   	struct drm_bridge	bridge;
>   	struct drm_connector	connector;
> @@ -33,6 +48,8 @@ struct tfp410 {
>   	struct drm_bridge	*next_bridge;
>   
>   	struct device *dev;
> +	struct i2c_client *i2c;
> +	struct regmap *regmap;
>   };
>   
>   static inline struct tfp410 *
> @@ -183,6 +200,9 @@ static void tfp410_enable(struct drm_bridge *bridge)
>   {
>   	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
>   
> +	if (dvi->i2c)
> +		regmap_set_bits(dvi->regmap, TFP410_REG_CTL_1_MODE, TFP410_BIT_PD);
> +
>   	gpiod_set_value_cansleep(dvi->powerdown, 0);
>   }
>   
> @@ -190,6 +210,9 @@ static void tfp410_disable(struct drm_bridge *bridge)
>   {
>   	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
>   
> +	if (dvi->i2c)
> +		regmap_clear_bits(dvi->regmap, TFP410_REG_CTL_1_MODE, TFP410_BIT_PD);
> +
>   	gpiod_set_value_cansleep(dvi->powerdown, 1);
>   }
>   
> @@ -221,38 +244,48 @@ static const struct drm_bridge_timings tfp410_default_timings = {
>   	.hold_time_ps = 1300,
>   };
>   
> -static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
> +static int tfp410_parse_timings(struct tfp410 *dvi)
>   {
>   	struct drm_bridge_timings *timings = &dvi->timings;
>   	struct device_node *ep;
>   	u32 pclk_sample = 0;
>   	u32 bus_width = 24;
>   	u32 deskew = 0;
> +	unsigned int val = 0;
> +	int ret = 0;
>   
>   	/* Start with defaults. */
>   	*timings = tfp410_default_timings;
>   
> -	if (i2c)
> +	if (dvi->i2c) {
>   		/*
> -		 * In I2C mode timings are configured through the I2C interface.
> -		 * As the driver doesn't support I2C configuration yet, we just
> -		 * go with the defaults (BSEL=1, DSEL=1, DKEN=0, EDGE=1).
> +		 * For now, assume settings are latched from pins on reset / power up.
> +		 * Should add options to optionally set them out of DT properties.
>   		 */
> -		return 0;
> -
> -	/*
> -	 * In non-I2C mode, timings are configured through the BSEL, DSEL, DKEN
> -	 * and EDGE pins. They are specified in DT through endpoint properties
> -	 * and vendor-specific properties.
> -	 */
> -	ep = of_graph_get_endpoint_by_regs(dvi->dev->of_node, 0, 0);
> -	if (!ep)
> -		return -EINVAL;
> -
> -	/* Get the sampling edge from the endpoint. */
> -	of_property_read_u32(ep, "pclk-sample", &pclk_sample);
> -	of_property_read_u32(ep, "bus-width", &bus_width);
> -	of_node_put(ep);
> +		ret = regmap_read(dvi->regmap, TFP410_REG_CTL_1_MODE, &val);
> +		if (ret) {
> +			dev_err(dvi->dev, "Read failed on CTL_1_MODE\n");
> +			return ret;
> +		}
> +		pclk_sample = (val & TFP410_BIT_EDGE) ? 1 : 0;
> +		bus_width = (val & TFP410_BIT_BSEL) ? 24 : 12;
> +		dev_dbg(dvi->dev, "(0x%02X) : detected %d bus width, %s edge sampling\n",
> +			val, bus_width, pclk_sample ? "positive" : "negative");
> +	} else {
> +		/*
> +		 * In non-I2C mode, timings are configured through the BSEL, DSEL, DKEN
> +		 * and EDGE pins. They are specified in DT through endpoint properties
> +		 * and vendor-specific properties.
> +		 */
> +		ep = of_graph_get_endpoint_by_regs(dvi->dev->of_node, 0, 0);
> +		if (!ep)
> +			return -EINVAL;
> +
> +		/* Get the sampling edge from the endpoint. */
> +		of_property_read_u32(ep, "pclk-sample", &pclk_sample);
> +		of_property_read_u32(ep, "bus-width", &bus_width);
> +		of_node_put(ep);
> +	}
>   
>   	timings->input_bus_flags = DRM_BUS_FLAG_DE_HIGH;
>   
> @@ -291,7 +324,7 @@ static int tfp410_parse_timings(struct tfp410 *dvi, bool i2c)
>   	return 0;
>   }
>   
> -static int tfp410_init(struct device *dev, bool i2c)
> +static int tfp410_init(struct device *dev, struct i2c_client *i2c)
>   {
>   	struct device_node *node;
>   	struct tfp410 *dvi;
> @@ -313,15 +346,24 @@ static int tfp410_init(struct device *dev, bool i2c)
>   	dvi->bridge.of_node = dev->of_node;
>   	dvi->bridge.timings = &dvi->timings;
>   	dvi->bridge.type = DRM_MODE_CONNECTOR_DVID;
> +	dvi->i2c = i2c;
> +
> +	if (i2c) {
> +		dvi->regmap = devm_regmap_init_i2c(i2c, &tfp410_regmap_config);
> +		if (IS_ERR(dvi->regmap))
> +			return PTR_ERR(dvi->regmap);
> +	}
>   
> -	ret = tfp410_parse_timings(dvi, i2c);
> +	ret = tfp410_parse_timings(dvi);
>   	if (ret)
>   		return ret;
>   
>   	/* Get the next bridge, connected to port at 1. */
>   	node = of_graph_get_remote_node(dev->of_node, 1, -1);
> -	if (!node)
> +	if (!node) {
> +		dev_err(dev, "Could not find remote node\n");
>   		return -ENODEV;
> +	}
>   
>   	dvi->next_bridge = of_drm_find_bridge(node);
>   	of_node_put(node);
> @@ -352,7 +394,7 @@ static void tfp410_fini(struct device *dev)
>   
>   static int tfp410_probe(struct platform_device *pdev)
>   {
> -	return tfp410_init(&pdev->dev, false);
> +	return tfp410_init(&pdev->dev, NULL);
>   }
>   
>   static int tfp410_remove(struct platform_device *pdev)
> @@ -378,7 +420,6 @@ static struct platform_driver tfp410_platform_driver = {
>   };
>   
>   #if IS_ENABLED(CONFIG_I2C)
> -/* There is currently no i2c functionality. */
>   static int tfp410_i2c_probe(struct i2c_client *client,
>   			    const struct i2c_device_id *id)
>   {
> @@ -391,7 +432,7 @@ static int tfp410_i2c_probe(struct i2c_client *client,
>   		return -ENXIO;
>   	}
>   
> -	return tfp410_init(&client->dev, true);
> +	return tfp410_init(&client->dev, client);
>   }
>   
>   static void tfp410_i2c_remove(struct i2c_client *client)
> 

Reviewed-by: Neil Armstrong <neil.armstrong at linaro.org>


More information about the dri-devel mailing list