@@ -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@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)