diff options
Diffstat (limited to 'drivers/gpu/drm/rcar-du/rcar_du_encoder.c')
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 67 |
1 files changed, 61 insertions, 6 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 8ee4e762f4e5..3cd83a030a04 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -9,6 +9,7 @@ #include <linux/export.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_panel.h> @@ -16,6 +17,7 @@ #include "rcar_du_drv.h" #include "rcar_du_encoder.h" #include "rcar_du_kms.h" +#include "rcar_lvds.h" /* ----------------------------------------------------------------------------- * Encoder @@ -28,13 +30,33 @@ static const struct drm_encoder_funcs encoder_funcs = { .destroy = drm_encoder_cleanup, }; +static unsigned int rcar_du_encoder_count_ports(struct device_node *node) +{ + struct device_node *ports; + struct device_node *port; + unsigned int num_ports = 0; + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = of_node_get(node); + + for_each_child_of_node(ports, port) { + if (of_node_name_eq(port, "port")) + num_ports++; + } + + of_node_put(ports); + + return num_ports; +} + int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_output output, struct device_node *enc_node) { struct rcar_du_encoder *renc; struct drm_encoder *encoder; - struct drm_bridge *bridge = NULL; + struct drm_bridge *bridge; int ret; renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); @@ -48,11 +70,44 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n", enc_node, output); - /* Locate the DRM bridge from the encoder DT node. */ - bridge = of_drm_find_bridge(enc_node); - if (!bridge) { - ret = -EPROBE_DEFER; - goto done; + /* + * Locate the DRM bridge from the DT node. For the DPAD outputs, if the + * DT node has a single port, assume that it describes a panel and + * create a panel bridge. + */ + if ((output == RCAR_DU_OUTPUT_DPAD0 || + output == RCAR_DU_OUTPUT_DPAD1) && + rcar_du_encoder_count_ports(enc_node) == 1) { + struct drm_panel *panel = of_drm_find_panel(enc_node); + + if (IS_ERR(panel)) { + ret = PTR_ERR(panel); + goto done; + } + + bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel, + DRM_MODE_CONNECTOR_DPI); + if (IS_ERR(bridge)) { + ret = PTR_ERR(bridge); + goto done; + } + } else { + bridge = of_drm_find_bridge(enc_node); + if (!bridge) { + ret = -EPROBE_DEFER; + goto done; + } + } + + /* + * On Gen3 skip the LVDS1 output if the LVDS1 encoder is used as a + * companion for LVDS0 in dual-link mode. + */ + if (rcdu->info->gen >= 3 && output == RCAR_DU_OUTPUT_LVDS1) { + if (rcar_lvds_dual_link(bridge)) { + ret = -ENOLINK; + goto done; + } } ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, |