drm/rcar-du: Rework output routing support
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Mon, 17 Jun 2013 01:13:11 +0000 (03:13 +0200)
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Fri, 9 Aug 2013 21:17:52 +0000 (23:17 +0200)
Split the output routing specification between SoC-internal data,
specified in the rcar_du_device_info structure, and board data, passed
through platform data.

The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). SoC-internal
output routing data specify which output are valid, which CRTCs can be
connected to the valid outputs, and the type of in-SoC encoder for the
output.

Platform data then specifies external encoders and the output they are
connected to.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/rcar-du/rcar_du_crtc.h
drivers/gpu/drm/rcar-du/rcar_du_drv.c
drivers/gpu/drm/rcar-du/rcar_du_drv.h
drivers/gpu/drm/rcar-du/rcar_du_encoder.c
drivers/gpu/drm/rcar-du/rcar_du_encoder.h
drivers/gpu/drm/rcar-du/rcar_du_group.c
drivers/gpu/drm/rcar-du/rcar_du_kms.c
include/linux/platform_data/rcar-du.h

index a340224..680606e 100644 (file)
@@ -129,14 +129,16 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
        rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
 }
 
-void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output)
+void rcar_du_crtc_route_output(struct drm_crtc *crtc,
+                              enum rcar_du_output output)
 {
        struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+       struct rcar_du_device *rcdu = rcrtc->group->dev;
 
        /* Store the route from the CRTC output to the DU output. The DU will be
         * configured when starting the CRTC.
         */
-       rcrtc->outputs |= 1 << output;
+       rcrtc->outputs |= BIT(output);
 }
 
 void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
index 542a7fe..39a983d 100644 (file)
@@ -15,6 +15,7 @@
 #define __RCAR_DU_CRTC_H__
 
 #include <linux/mutex.h>
+#include <linux/platform_data/rcar-du.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
@@ -45,7 +46,8 @@ void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
 void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc);
 void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
 
-void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output);
+void rcar_du_crtc_route_output(struct drm_crtc *crtc,
+                              enum rcar_du_output output);
 void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
 
 #endif /* __RCAR_DU_CRTC_H__ */
index f878535..4bc3997 100644 (file)
@@ -219,12 +219,42 @@ static int rcar_du_remove(struct platform_device *pdev)
 static const struct rcar_du_device_info rcar_du_r8a7779_info = {
        .features = 0,
        .num_crtcs = 2,
+       .routes = {
+               /* R8A7779 has two RGB outputs and one (currently unsupported)
+                * TCON output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(0),
+                       .encoder_type = DRM_MODE_ENCODER_NONE,
+               },
+               [RCAR_DU_OUTPUT_DPAD1] = {
+                       .possible_crtcs = BIT(1) | BIT(0),
+                       .encoder_type = DRM_MODE_ENCODER_NONE,
+               },
+       },
 };
 
 static const struct rcar_du_device_info rcar_du_r8a7790_info = {
        .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B
                  | RCAR_DU_FEATURE_DEFR8,
        .num_crtcs = 3,
+       .routes = {
+               /* R8A7790 has one RGB output, two LVDS outputs and one
+                * (currently unsupported) TCON output.
+                */
+               [RCAR_DU_OUTPUT_DPAD0] = {
+                       .possible_crtcs = BIT(2) | BIT(1) | BIT(0),
+                       .encoder_type = DRM_MODE_ENCODER_NONE,
+               },
+               [RCAR_DU_OUTPUT_LVDS0] = {
+                       .possible_crtcs = BIT(0),
+                       .encoder_type = DRM_MODE_ENCODER_LVDS,
+               },
+               [RCAR_DU_OUTPUT_LVDS1] = {
+                       .possible_crtcs = BIT(2) | BIT(1),
+                       .encoder_type = DRM_MODE_ENCODER_LVDS,
+               },
+       },
 };
 
 static const struct platform_device_id rcar_du_id_table[] = {
index 70c335f..d5243f4 100644 (file)
@@ -30,13 +30,29 @@ struct rcar_du_device;
 #define RCAR_DU_FEATURE_DEFR8          (1 << 2)        /* Has DEFR8 register */
 
 /*
+ * struct rcar_du_output_routing - Output routing specification
+ * @possible_crtcs: bitmask of possible CRTCs for the output
+ * @encoder_type: DRM type of the internal encoder associated with the output
+ *
+ * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
+ * specify the valid SoC outputs, which CRTCs can drive the output, and the type
+ * of in-SoC encoder for the output.
+ */
+struct rcar_du_output_routing {
+       unsigned int possible_crtcs;
+       unsigned int encoder_type;
+};
+
+/*
  * struct rcar_du_device_info - DU model-specific information
  * @features: device features (RCAR_DU_FEATURE_*)
  * @num_crtcs: total number of CRTCs
+ * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
  */
 struct rcar_du_device_info {
        unsigned int features;
        unsigned int num_crtcs;
+       struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
 };
 
 struct rcar_du_device {
index 0d0375c..2aac28d 100644 (file)
@@ -115,10 +115,12 @@ static const struct drm_encoder_funcs encoder_funcs = {
 };
 
 int rcar_du_encoder_init(struct rcar_du_device *rcdu,
-                        enum rcar_du_encoder_type type, unsigned int output,
+                        enum rcar_du_encoder_type type,
+                        enum rcar_du_output output,
                         const struct rcar_du_encoder_data *data)
 {
        struct rcar_du_encoder *renc;
+       unsigned int encoder_type;
        int ret;
 
        renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
@@ -127,19 +129,33 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 
        renc->output = output;
 
+       switch (type) {
+       case RCAR_DU_ENCODER_VGA:
+               encoder_type = DRM_MODE_ENCODER_DAC;
+               break;
+       case RCAR_DU_ENCODER_LVDS:
+               encoder_type = DRM_MODE_ENCODER_LVDS;
+               break;
+       case RCAR_DU_ENCODER_NONE:
+       default:
+               /* No external encoder, use the internal encoder type. */
+               encoder_type = rcdu->info->routes[output].encoder_type;
+               break;
+       }
+
        ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
-                              type);
+                              encoder_type);
        if (ret < 0)
                return ret;
 
        drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
 
-       switch (type) {
-       case RCAR_DU_ENCODER_LVDS:
+       switch (encoder_type) {
+       case DRM_MODE_ENCODER_LVDS:
                return rcar_du_lvds_connector_init(rcdu, renc,
                                                   &data->connector.lvds.panel);
 
-       case RCAR_DU_ENCODER_VGA:
+       case DRM_MODE_ENCODER_DAC:
                return rcar_du_vga_connector_init(rcdu, renc);
 
        default:
index 08cde12..2310416 100644 (file)
@@ -22,7 +22,7 @@ struct rcar_du_device;
 
 struct rcar_du_encoder {
        struct drm_encoder encoder;
-       unsigned int output;
+       enum rcar_du_output output;
 };
 
 #define to_rcar_encoder(e) \
@@ -40,7 +40,8 @@ struct drm_encoder *
 rcar_du_connector_best_encoder(struct drm_connector *connector);
 
 int rcar_du_encoder_init(struct rcar_du_device *rcdu,
-                        enum rcar_du_encoder_type type, unsigned int output,
+                        enum rcar_du_encoder_type type,
+                        enum rcar_du_output output,
                         const struct rcar_du_encoder_data *data);
 
 #endif /* __RCAR_DU_ENCODER_H__ */
index f3ba0ca..9df6fb6 100644 (file)
@@ -135,11 +135,11 @@ void rcar_du_group_set_routing(struct rcar_du_group *rgrp)
 
        dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK);
 
-       /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and
-        * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by
-        * default.
+       /* Set the DPAD1 pins sources. Select CRTC 0 if explicitly requested and
+        * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1
+        * by default.
         */
-       if (crtc0->outputs & (1 << 1))
+       if (crtc0->outputs & BIT(RCAR_DU_OUTPUT_DPAD1))
                dorcr |= DORCR_PG2D_DS1;
        else
                dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2;
index 816963c..2b92e68 100644 (file)
@@ -220,11 +220,14 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
        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 >= rcdu->num_crtcs) {
+               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);
@@ -234,15 +237,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
                rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata);
        }
 
-       /* Set the possible CRTCs and possible clones. All encoders can be
-        * driven by the CRTC associated with the output they're connected to,
-        * as well as by CRTC 0.
+       /* 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
+        * possible clones field.
         */
        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
                struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+               const struct rcar_du_output_routing *route =
+                       &rcdu->info->routes[renc->output];
 
-               encoder->possible_crtcs = (1 << 0) | (1 << renc->output);
-               encoder->possible_clones = 1 << 0;
+               encoder->possible_crtcs = route->possible_crtcs;
+               encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1;
        }
 
        /* Now that the CRTCs have been initialized register the planes. */
index 64cd863..1a2e990 100644 (file)
 
 #include <drm/drm_mode.h>
 
+enum rcar_du_output {
+       RCAR_DU_OUTPUT_DPAD0,
+       RCAR_DU_OUTPUT_DPAD1,
+       RCAR_DU_OUTPUT_LVDS0,
+       RCAR_DU_OUTPUT_LVDS1,
+       RCAR_DU_OUTPUT_TCON,
+       RCAR_DU_OUTPUT_MAX,
+};
+
 enum rcar_du_encoder_type {
        RCAR_DU_ENCODER_UNUSED = 0,
+       RCAR_DU_ENCODER_NONE,
        RCAR_DU_ENCODER_VGA,
        RCAR_DU_ENCODER_LVDS,
 };
@@ -39,13 +49,16 @@ struct rcar_du_connector_vga_data {
 /*
  * struct rcar_du_encoder_data - Encoder platform data
  * @type: the encoder type (RCAR_DU_ENCODER_*)
- * @output: the DU output the connector is connected to
+ * @output: the DU output the connector is connected to (RCAR_DU_OUTPUT_*)
  * @connector.lvds: platform data for LVDS connectors
  * @connector.vga: platform data for VGA connectors
+ *
+ * Encoder platform data describes an on-board encoder, its associated DU SoC
+ * output, and the connector.
  */
 struct rcar_du_encoder_data {
        enum rcar_du_encoder_type type;
-       unsigned int output;
+       enum rcar_du_output output;
 
        union {
                struct rcar_du_connector_lvds_data lvds;