|
|
@@ -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", ®);
|
|
|
+ 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);
|