aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/rcar-du/rcar_du_kms.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/rcar-du/rcar_du_kms.c')
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c233
1 files changed, 211 insertions, 22 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 76026104d000..6c24ad7d03ef 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -1,7 +1,7 @@
/*
* rcar_du_kms.c -- R-Car Display Unit Mode Setting
*
- * Copyright (C) 2013 Renesas Corporation
+ * Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -17,6 +17,8 @@
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
+#include <linux/of_graph.h>
+
#include "rcar_du_crtc.h"
#include "rcar_du_drv.h"
#include "rcar_du_encoder.h"
@@ -188,6 +190,205 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
.output_poll_changed = rcar_du_output_poll_changed,
};
+static int rcar_du_encoders_init_pdata(struct rcar_du_device *rcdu)
+{
+ unsigned int num_encoders = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
+ const struct rcar_du_encoder_data *pdata =
+ &rcdu->pdata->encoders[i];
+ const struct rcar_du_output_routing *route =
+ &rcdu->info->routes[pdata->output];
+
+ if (pdata->type == RCAR_DU_ENCODER_UNUSED)
+ continue;
+
+ if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
+ route->possible_crtcs == 0) {
+ dev_warn(rcdu->dev,
+ "encoder %u references unexisting output %u, skipping\n",
+ i, pdata->output);
+ continue;
+ }
+
+ ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output,
+ pdata, NULL);
+ if (ret < 0)
+ return ret;
+
+ num_encoders++;
+ }
+
+ return num_encoders;
+}
+
+static int rcar_du_encoders_init_dt_one(struct rcar_du_device *rcdu,
+ enum rcar_du_output output,
+ struct of_endpoint *ep)
+{
+ static const struct {
+ const char *compatible;
+ enum rcar_du_encoder_type type;
+ } encoders[] = {
+ { "adi,adv7123", RCAR_DU_ENCODER_VGA },
+ { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS },
+ };
+
+ enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE;
+ struct device_node *connector = NULL;
+ struct device_node *encoder = NULL;
+ struct device_node *prev = NULL;
+ struct device_node *entity_ep_node;
+ struct device_node *entity;
+ int ret;
+
+ /*
+ * Locate the connected entity and infer its type from the number of
+ * endpoints.
+ */
+ entity = of_graph_get_remote_port_parent(ep->local_node);
+ if (!entity) {
+ dev_dbg(rcdu->dev, "unconnected endpoint %s, skipping\n",
+ ep->local_node->full_name);
+ return 0;
+ }
+
+ entity_ep_node = of_parse_phandle(ep->local_node, "remote-endpoint", 0);
+
+ while (1) {
+ struct device_node *ep_node;
+
+ ep_node = of_graph_get_next_endpoint(entity, prev);
+ of_node_put(prev);
+ prev = ep_node;
+
+ if (!ep_node)
+ break;
+
+ if (ep_node == entity_ep_node)
+ continue;
+
+ /*
+ * We've found one endpoint other than the input, this must
+ * be an encoder. Locate the connector.
+ */
+ encoder = entity;
+ connector = of_graph_get_remote_port_parent(ep_node);
+ of_node_put(ep_node);
+
+ if (!connector) {
+ dev_warn(rcdu->dev,
+ "no connector for encoder %s, skipping\n",
+ encoder->full_name);
+ of_node_put(entity_ep_node);
+ of_node_put(encoder);
+ return 0;
+ }
+
+ break;
+ }
+
+ of_node_put(entity_ep_node);
+
+ if (encoder) {
+ /*
+ * If an encoder has been found, get its type based on its
+ * compatible string.
+ */
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(encoders); ++i) {
+ if (of_device_is_compatible(encoder,
+ encoders[i].compatible)) {
+ enc_type = encoders[i].type;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(encoders)) {
+ dev_warn(rcdu->dev,
+ "unknown encoder type for %s, skipping\n",
+ encoder->full_name);
+ of_node_put(encoder);
+ of_node_put(connector);
+ return 0;
+ }
+ } else {
+ /*
+ * If no encoder has been found the entity must be the
+ * connector.
+ */
+ connector = entity;
+ }
+
+ ret = rcar_du_encoder_init(rcdu, enc_type, output, NULL, connector);
+ of_node_put(encoder);
+ of_node_put(connector);
+
+ return ret < 0 ? ret : 1;
+}
+
+static int rcar_du_encoders_init_dt(struct rcar_du_device *rcdu)
+{
+ struct device_node *np = rcdu->dev->of_node;
+ struct device_node *prev = NULL;
+ unsigned int num_encoders = 0;
+
+ /*
+ * Iterate over the endpoints and create one encoder for each output
+ * pipeline.
+ */
+ while (1) {
+ struct device_node *ep_node;
+ enum rcar_du_output output;
+ struct of_endpoint ep;
+ unsigned int i;
+ int ret;
+
+ ep_node = of_graph_get_next_endpoint(np, prev);
+ of_node_put(prev);
+ prev = ep_node;
+
+ if (ep_node == NULL)
+ break;
+
+ ret = of_graph_parse_endpoint(ep_node, &ep);
+ if (ret < 0) {
+ of_node_put(ep_node);
+ return ret;
+ }
+
+ /* Find the output route corresponding to the port number. */
+ for (i = 0; i < RCAR_DU_OUTPUT_MAX; ++i) {
+ if (rcdu->info->routes[i].possible_crtcs &&
+ rcdu->info->routes[i].port == ep.port) {
+ output = i;
+ break;
+ }
+ }
+
+ if (i == RCAR_DU_OUTPUT_MAX) {
+ dev_warn(rcdu->dev,
+ "port %u references unexisting output, skipping\n",
+ ep.port);
+ continue;
+ }
+
+ /* Process the output pipeline. */
+ ret = rcar_du_encoders_init_dt_one(rcdu, output, &ep);
+ if (ret < 0) {
+ of_node_put(ep_node);
+ return ret;
+ }
+
+ num_encoders += ret;
+ }
+
+ return num_encoders;
+}
+
int rcar_du_modeset_init(struct rcar_du_device *rcdu)
{
static const unsigned int mmio_offsets[] = {
@@ -197,6 +398,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
struct drm_device *dev = rcdu->ddev;
struct drm_encoder *encoder;
struct drm_fbdev_cma *fbdev;
+ unsigned int num_encoders;
unsigned int num_groups;
unsigned int i;
int ret;
@@ -240,28 +442,15 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
if (ret < 0)
return ret;
- for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
- const struct rcar_du_encoder_data *pdata =
- &rcdu->pdata->encoders[i];
- const struct rcar_du_output_routing *route =
- &rcdu->info->routes[pdata->output];
-
- if (pdata->type == RCAR_DU_ENCODER_UNUSED)
- continue;
+ if (rcdu->pdata)
+ ret = rcar_du_encoders_init_pdata(rcdu);
+ else
+ ret = rcar_du_encoders_init_dt(rcdu);
- if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
- route->possible_crtcs == 0) {
- dev_warn(rcdu->dev,
- "encoder %u references unexisting output %u, skipping\n",
- i, pdata->output);
- continue;
- }
+ if (ret < 0)
+ return ret;
- ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output,
- pdata);
- if (ret < 0)
- return ret;
- }
+ num_encoders = ret;
/* Set the possible CRTCs and possible clones. There's always at least
* one way for all encoders to clone each other, set all bits in the
@@ -273,7 +462,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
&rcdu->info->routes[renc->output];
encoder->possible_crtcs = route->possible_crtcs;
- encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1;
+ encoder->possible_clones = (1 << num_encoders) - 1;
}
/* Now that the CRTCs have been initialized register the planes. */