diff options
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_drv.c')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_drv.c | 127 |
1 files changed, 90 insertions, 37 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 50d19605c38f..dd19d674055c 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -26,6 +26,7 @@ #include "sun4i_frontend.h" #include "sun4i_framebuffer.h" #include "sun4i_tcon.h" +#include "sun8i_tcon_top.h" DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); @@ -143,7 +144,7 @@ cleanup_mode_config: drm_mode_config_cleanup(drm); of_reserved_mem_device_release(dev); free_drm: - drm_dev_unref(drm); + drm_dev_put(drm); return ret; } @@ -156,7 +157,7 @@ static void sun4i_drv_unbind(struct device *dev) sun4i_framebuffer_free(drm); drm_mode_config_cleanup(drm); of_reserved_mem_device_release(dev); - drm_dev_unref(drm); + drm_dev_put(drm); } static const struct component_master_ops sun4i_drv_master_ops = { @@ -197,6 +198,28 @@ static bool sun4i_drv_node_is_tcon(struct device_node *node) return !!of_match_node(sun4i_tcon_of_table, node); } +static bool sun4i_drv_node_is_tcon_with_ch0(struct device_node *node) +{ + const struct of_device_id *match; + + match = of_match_node(sun4i_tcon_of_table, node); + if (match) { + struct sun4i_tcon_quirks *quirks; + + quirks = (struct sun4i_tcon_quirks *)match->data; + + return quirks->has_channel_0; + } + + return false; +} + +static bool sun4i_drv_node_is_tcon_top(struct device_node *node) +{ + return IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) && + !!of_match_node(sun8i_tcon_top_of_table, node); +} + static int compare_of(struct device *dev, void *data) { DRM_DEBUG_DRIVER("Comparing of node %pOF with %pOF\n", @@ -231,12 +254,69 @@ struct endpoint_list { DECLARE_KFIFO(fifo, struct device_node *, 16); }; +static void sun4i_drv_traverse_endpoints(struct endpoint_list *list, + struct device_node *node, + int port_id) +{ + struct device_node *ep, *remote, *port; + + port = of_graph_get_port_by_id(node, port_id); + if (!port) { + DRM_DEBUG_DRIVER("No output to bind on port %d\n", port_id); + return; + } + + for_each_available_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote) { + DRM_DEBUG_DRIVER("Error retrieving the output node\n"); + continue; + } + + if (sun4i_drv_node_is_tcon(node)) { + /* + * TCON TOP is always probed before TCON. However, TCON + * points back to TCON TOP when it is source for HDMI. + * We have to skip it here to prevent infinite looping + * between TCON TOP and TCON. + */ + if (sun4i_drv_node_is_tcon_top(remote)) { + DRM_DEBUG_DRIVER("TCON output endpoint is TCON TOP... skipping\n"); + of_node_put(remote); + continue; + } + + /* + * If the node is our TCON with channel 0, the first + * port is used for panel or bridges, and will not be + * part of the component framework. + */ + if (sun4i_drv_node_is_tcon_with_ch0(node)) { + struct of_endpoint endpoint; + + if (of_graph_parse_endpoint(ep, &endpoint)) { + DRM_DEBUG_DRIVER("Couldn't parse endpoint\n"); + of_node_put(remote); + continue; + } + + if (!endpoint.id) { + DRM_DEBUG_DRIVER("Endpoint is our panel... skipping\n"); + of_node_put(remote); + continue; + } + } + } + + kfifo_put(&list->fifo, remote); + } +} + static int sun4i_drv_add_endpoints(struct device *dev, struct endpoint_list *list, struct component_match **match, struct device_node *node) { - struct device_node *port, *ep, *remote; int count = 0; /* @@ -272,41 +352,13 @@ static int sun4i_drv_add_endpoints(struct device *dev, count++; } - /* Inputs are listed first, then outputs */ - port = of_graph_get_port_by_id(node, 1); - if (!port) { - DRM_DEBUG_DRIVER("No output to bind\n"); - return count; - } + /* each node has at least one output */ + sun4i_drv_traverse_endpoints(list, node, 1); - for_each_available_child_of_node(port, ep) { - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - DRM_DEBUG_DRIVER("Error retrieving the output node\n"); - of_node_put(remote); - continue; - } - - /* - * If the node is our TCON, the first port is used for - * panel or bridges, and will not be part of the - * component framework. - */ - if (sun4i_drv_node_is_tcon(node)) { - struct of_endpoint endpoint; - - if (of_graph_parse_endpoint(ep, &endpoint)) { - DRM_DEBUG_DRIVER("Couldn't parse endpoint\n"); - continue; - } - - if (!endpoint.id) { - DRM_DEBUG_DRIVER("Endpoint is our panel... skipping\n"); - continue; - } - } - - kfifo_put(&list->fifo, remote); + /* TCON TOP has second and third output */ + if (sun4i_drv_node_is_tcon_top(node)) { + sun4i_drv_traverse_endpoints(list, node, 3); + sun4i_drv_traverse_endpoints(list, node, 5); } return count; @@ -366,6 +418,7 @@ static const struct of_device_id sun4i_drv_of_table[] = { { .compatible = "allwinner,sun8i-a33-display-engine" }, { .compatible = "allwinner,sun8i-a83t-display-engine" }, { .compatible = "allwinner,sun8i-h3-display-engine" }, + { .compatible = "allwinner,sun8i-r40-display-engine" }, { .compatible = "allwinner,sun8i-v3s-display-engine" }, { .compatible = "allwinner,sun9i-a80-display-engine" }, { } |