summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/sun4i/sun4i_tcon.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_tcon.c')
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c98
1 files changed, 71 insertions, 27 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 652385f09735..cadacb517f95 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -59,11 +59,13 @@ void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
SUN4I_TCON0_CTL_TCON_ENABLE, 0);
clk_disable_unprepare(tcon->dclk);
- } else if (channel == 1) {
- regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
- SUN4I_TCON1_CTL_TCON_ENABLE, 0);
- clk_disable_unprepare(tcon->sclk1);
+ return;
}
+
+ WARN_ON(!tcon->has_channel_1);
+ regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+ SUN4I_TCON1_CTL_TCON_ENABLE, 0);
+ clk_disable_unprepare(tcon->sclk1);
}
EXPORT_SYMBOL(sun4i_tcon_channel_disable);
@@ -75,12 +77,14 @@ void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
SUN4I_TCON0_CTL_TCON_ENABLE,
SUN4I_TCON0_CTL_TCON_ENABLE);
clk_prepare_enable(tcon->dclk);
- } else if (channel == 1) {
- regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
- SUN4I_TCON1_CTL_TCON_ENABLE,
- SUN4I_TCON1_CTL_TCON_ENABLE);
- clk_prepare_enable(tcon->sclk1);
+ return;
}
+
+ WARN_ON(!tcon->has_channel_1);
+ regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+ SUN4I_TCON1_CTL_TCON_ENABLE,
+ SUN4I_TCON1_CTL_TCON_ENABLE);
+ clk_prepare_enable(tcon->sclk1);
}
EXPORT_SYMBOL(sun4i_tcon_channel_enable);
@@ -198,6 +202,8 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
u8 clk_delay;
u32 val;
+ WARN_ON(!tcon->has_channel_1);
+
/* Adjust clock delay */
clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
@@ -321,10 +327,12 @@ static int sun4i_tcon_init_clocks(struct device *dev,
return PTR_ERR(tcon->sclk0);
}
- tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
- if (IS_ERR(tcon->sclk1)) {
- dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
- return PTR_ERR(tcon->sclk1);
+ if (tcon->has_channel_1) {
+ tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
+ if (IS_ERR(tcon->sclk1)) {
+ dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
+ return PTR_ERR(tcon->sclk1);
+ }
}
return sun4i_dclk_create(dev, tcon);
@@ -374,10 +382,8 @@ static int sun4i_tcon_init_regmap(struct device *dev,
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs)) {
- dev_err(dev, "Couldn't map the TCON registers\n");
+ if (IS_ERR(regs))
return PTR_ERR(regs);
- }
tcon->regs = devm_regmap_init_mmio(dev, regs,
&sun4i_tcon_regmap_config);
@@ -398,7 +404,7 @@ static int sun4i_tcon_init_regmap(struct device *dev,
return 0;
}
-static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
+struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
{
struct device_node *port, *remote, *child;
struct device_node *end_node = NULL;
@@ -432,6 +438,40 @@ static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER);
}
+struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node)
+{
+ struct device_node *port, *remote, *child;
+ struct device_node *end_node = NULL;
+
+ /* Inputs are listed first, then outputs */
+ port = of_graph_get_port_by_id(node, 1);
+
+ /*
+ * Our first output is the RGB interface where the panel will
+ * be connected.
+ */
+ for_each_child_of_node(port, child) {
+ u32 reg;
+
+ of_property_read_u32(child, "reg", &reg);
+ if (reg == 0)
+ end_node = child;
+ }
+
+ if (!end_node) {
+ DRM_DEBUG_DRIVER("Missing bridge endpoint\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ remote = of_graph_get_remote_port_parent(end_node);
+ if (!remote) {
+ DRM_DEBUG_DRIVER("Enable to parse remote node\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER);
+}
+
static int sun4i_tcon_bind(struct device *dev, struct device *master,
void *data)
{
@@ -446,9 +486,15 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
dev_set_drvdata(dev, tcon);
drv->tcon = tcon;
tcon->drm = drm;
+ tcon->dev = dev;
- if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon"))
+ if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) {
tcon->has_mux = true;
+ tcon->has_channel_1 = true;
+ } else {
+ tcon->has_mux = false;
+ tcon->has_channel_1 = false;
+ }
tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
if (IS_ERR(tcon->lcd_rst)) {
@@ -484,12 +530,6 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
goto err_free_clocks;
}
- tcon->panel = sun4i_tcon_find_panel(dev->of_node);
- if (IS_ERR(tcon->panel)) {
- dev_info(dev, "No panel found... RGB output disabled\n");
- return 0;
- }
-
ret = sun4i_rgb_init(drm);
if (ret < 0)
goto err_free_clocks;
@@ -519,19 +559,22 @@ static struct component_ops sun4i_tcon_ops = {
static int sun4i_tcon_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
+ struct drm_bridge *bridge;
struct drm_panel *panel;
/*
- * The panel is not ready.
+ * Neither the bridge or the panel is ready.
* Defer the probe.
*/
panel = sun4i_tcon_find_panel(node);
+ bridge = sun4i_tcon_find_bridge(node);
/*
* If we don't have a panel endpoint, just go on
*/
- if (PTR_ERR(panel) == -EPROBE_DEFER) {
- DRM_DEBUG_DRIVER("Still waiting for our panel. Deferring...\n");
+ if ((PTR_ERR(panel) == -EPROBE_DEFER) &&
+ (PTR_ERR(bridge) == -EPROBE_DEFER)) {
+ DRM_DEBUG_DRIVER("Still waiting for our panel/bridge. Deferring...\n");
return -EPROBE_DEFER;
}
@@ -547,6 +590,7 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_tcon_of_table[] = {
{ .compatible = "allwinner,sun5i-a13-tcon" },
+ { .compatible = "allwinner,sun8i-a33-tcon" },
{ }
};
MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);