diff options
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_tcon.c')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_tcon.c | 92 |
1 files changed, 45 insertions, 47 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index 08747fc3ee71..3fb084f802e2 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -17,7 +17,6 @@ #include <drm/drm_encoder.h> #include <drm/drm_modes.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <uapi/drm/drm_mode.h> @@ -418,9 +417,6 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, const struct drm_display_mode *mode) { - struct drm_panel *panel = tcon->panel; - struct drm_connector *connector = panel->connector; - struct drm_display_info display_info = connector->display_info; unsigned int bp, hsync, vsync; u8 clk_delay; u32 val = 0; @@ -478,27 +474,6 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, if (mode->flags & DRM_MODE_FLAG_PVSYNC) val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; - /* - * On A20 and similar SoCs, the only way to achieve Positive Edge - * (Rising Edge), is setting dclk clock phase to 2/3(240°). - * By default TCON works in Negative Edge(Falling Edge), - * this is why phase is set to 0 in that case. - * Unfortunately there's no way to logically invert dclk through - * IO_POL register. - * The only acceptable way to work, triple checked with scope, - * is using clock phase set to 0° for Negative Edge and set to 240° - * for Positive Edge. - * On A33 and similar SoCs there would be a 90° phase option, - * but it divides also dclk by 2. - * Following code is a way to avoid quirks all around TCON - * and DOTCLOCK drivers. - */ - if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) - clk_set_phase(tcon->dclk, 240); - - if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) - clk_set_phase(tcon->dclk, 0); - regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, val); @@ -791,12 +766,14 @@ static int sun4i_tcon_init_regmap(struct device *dev, */ static struct sunxi_engine * sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv, - struct device_node *node) + struct device_node *node, + u32 port_id) { struct device_node *port, *ep, *remote; struct sunxi_engine *engine = ERR_PTR(-EINVAL); + u32 reg = 0; - port = of_graph_get_port_by_id(node, 0); + port = of_graph_get_port_by_id(node, port_id); if (!port) return ERR_PTR(-EINVAL); @@ -826,8 +803,21 @@ sun4i_tcon_find_engine_traverse(struct sun4i_drv *drv, if (remote == engine->node) goto out_put_remote; + /* + * According to device tree binding input ports have even id + * number and output ports have odd id. Since component with + * more than one input and one output (TCON TOP) exits, correct + * remote input id has to be calculated by subtracting 1 from + * remote output id. If this for some reason can't be done, 0 + * is used as input port id. + */ + of_node_put(port); + port = of_graph_get_remote_port(ep); + if (!of_property_read_u32(port, "reg", ®) && reg > 0) + reg -= 1; + /* keep looking through upstream ports */ - engine = sun4i_tcon_find_engine_traverse(drv, remote); + engine = sun4i_tcon_find_engine_traverse(drv, remote, reg); out_put_remote: of_node_put(remote); @@ -950,7 +940,7 @@ static struct sunxi_engine *sun4i_tcon_find_engine(struct sun4i_drv *drv, /* Fallback to old method by traversing input endpoints */ of_node_put(port); - return sun4i_tcon_find_engine_traverse(drv, node); + return sun4i_tcon_find_engine_traverse(drv, node, 0); } static int sun4i_tcon_bind(struct device *dev, struct device *master, @@ -1092,23 +1082,25 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master, goto err_free_dotclock; } - /* - * If we have an LVDS panel connected to the TCON, we should - * just probe the LVDS connector. Otherwise, just probe RGB as - * we used to. - */ - remote = of_graph_get_remote_node(dev->of_node, 1, 0); - if (of_device_is_compatible(remote, "panel-lvds")) - if (can_lvds) - ret = sun4i_lvds_init(drm, tcon); + if (tcon->quirks->has_channel_0) { + /* + * If we have an LVDS panel connected to the TCON, we should + * just probe the LVDS connector. Otherwise, just probe RGB as + * we used to. + */ + remote = of_graph_get_remote_node(dev->of_node, 1, 0); + if (of_device_is_compatible(remote, "panel-lvds")) + if (can_lvds) + ret = sun4i_lvds_init(drm, tcon); + else + ret = -EINVAL; else - ret = -EINVAL; - else - ret = sun4i_rgb_init(drm, tcon); - of_node_put(remote); + ret = sun4i_rgb_init(drm, tcon); + of_node_put(remote); - if (ret < 0) - goto err_free_dotclock; + if (ret < 0) + goto err_free_dotclock; + } if (tcon->quirks->needs_de_be_mux) { /* @@ -1162,13 +1154,19 @@ static const struct component_ops sun4i_tcon_ops = { static int sun4i_tcon_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + const struct sun4i_tcon_quirks *quirks; struct drm_bridge *bridge; struct drm_panel *panel; int ret; - ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge); - if (ret == -EPROBE_DEFER) - return ret; + quirks = of_device_get_match_data(&pdev->dev); + + /* panels and bridges are present only on TCONs with channel 0 */ + if (quirks->has_channel_0) { + ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge); + if (ret == -EPROBE_DEFER) + return ret; + } return component_add(&pdev->dev, &sun4i_tcon_ops); } |