diff options
| author | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2014-01-21 09:57:26 -0500 |
|---|---|---|
| committer | Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> | 2014-09-15 04:55:47 -0400 |
| commit | 96c026911890ceacee238da00a0b140ad634cc43 (patch) | |
| tree | b9052e516de723067b1fbd91da74cbd32ef54e2c | |
| parent | 1d46fea7d091f9dc2d4fd3fcb9f0117ca288f9a5 (diff) | |
drm/rcar-du: Add OF support
Implement support for the R-Car DU DT bindings in the rcar-du DRM
driver.
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.c | 170 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.h | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 11 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_kms.c | 231 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | 30 | ||||
| -rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h | 3 |
7 files changed, 344 insertions, 106 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 941d422fc410..d212efa6a495 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/io.h> | 15 | #include <linux/io.h> |
| 16 | #include <linux/mm.h> | 16 | #include <linux/mm.h> |
| 17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
| 18 | #include <linux/of_device.h> | ||
| 18 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
| 19 | #include <linux/pm.h> | 20 | #include <linux/pm.h> |
| 20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
| @@ -30,6 +31,97 @@ | |||
| 30 | #include "rcar_du_regs.h" | 31 | #include "rcar_du_regs.h" |
| 31 | 32 | ||
| 32 | /* ----------------------------------------------------------------------------- | 33 | /* ----------------------------------------------------------------------------- |
| 34 | * Device Information | ||
| 35 | */ | ||
| 36 | |||
| 37 | static const struct rcar_du_device_info rcar_du_r8a7779_info = { | ||
| 38 | .features = 0, | ||
| 39 | .num_crtcs = 2, | ||
| 40 | .routes = { | ||
| 41 | /* R8A7779 has two RGB outputs and one (currently unsupported) | ||
| 42 | * TCON output. | ||
| 43 | */ | ||
| 44 | [RCAR_DU_OUTPUT_DPAD0] = { | ||
| 45 | .possible_crtcs = BIT(0), | ||
| 46 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 47 | .port = 0, | ||
| 48 | }, | ||
| 49 | [RCAR_DU_OUTPUT_DPAD1] = { | ||
| 50 | .possible_crtcs = BIT(1) | BIT(0), | ||
| 51 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 52 | .port = 1, | ||
| 53 | }, | ||
| 54 | }, | ||
| 55 | .num_lvds = 0, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static const struct rcar_du_device_info rcar_du_r8a7790_info = { | ||
| 59 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, | ||
| 60 | .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, | ||
| 61 | .num_crtcs = 3, | ||
| 62 | .routes = { | ||
| 63 | /* R8A7790 has one RGB output, two LVDS outputs and one | ||
| 64 | * (currently unsupported) TCON output. | ||
| 65 | */ | ||
| 66 | [RCAR_DU_OUTPUT_DPAD0] = { | ||
| 67 | .possible_crtcs = BIT(2) | BIT(1) | BIT(0), | ||
| 68 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 69 | .port = 0, | ||
| 70 | }, | ||
| 71 | [RCAR_DU_OUTPUT_LVDS0] = { | ||
| 72 | .possible_crtcs = BIT(0), | ||
| 73 | .encoder_type = DRM_MODE_ENCODER_LVDS, | ||
| 74 | .port = 1, | ||
| 75 | }, | ||
| 76 | [RCAR_DU_OUTPUT_LVDS1] = { | ||
| 77 | .possible_crtcs = BIT(2) | BIT(1), | ||
| 78 | .encoder_type = DRM_MODE_ENCODER_LVDS, | ||
| 79 | .port = 2, | ||
| 80 | }, | ||
| 81 | }, | ||
| 82 | .num_lvds = 2, | ||
| 83 | }; | ||
| 84 | |||
| 85 | static const struct rcar_du_device_info rcar_du_r8a7791_info = { | ||
| 86 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, | ||
| 87 | .num_crtcs = 2, | ||
| 88 | .routes = { | ||
| 89 | /* R8A7791 has one RGB output, one LVDS output and one | ||
| 90 | * (currently unsupported) TCON output. | ||
| 91 | */ | ||
| 92 | [RCAR_DU_OUTPUT_DPAD0] = { | ||
| 93 | .possible_crtcs = BIT(1), | ||
| 94 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 95 | .port = 0, | ||
| 96 | }, | ||
| 97 | [RCAR_DU_OUTPUT_LVDS0] = { | ||
| 98 | .possible_crtcs = BIT(0), | ||
| 99 | .encoder_type = DRM_MODE_ENCODER_LVDS, | ||
| 100 | .port = 1, | ||
| 101 | }, | ||
| 102 | }, | ||
| 103 | .num_lvds = 1, | ||
| 104 | }; | ||
| 105 | |||
| 106 | static const struct platform_device_id rcar_du_id_table[] = { | ||
| 107 | { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info }, | ||
| 108 | { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info }, | ||
| 109 | { "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info }, | ||
| 110 | { } | ||
| 111 | }; | ||
| 112 | |||
| 113 | MODULE_DEVICE_TABLE(platform, rcar_du_id_table); | ||
| 114 | |||
| 115 | static const struct of_device_id rcar_du_of_table[] = { | ||
| 116 | { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info }, | ||
| 117 | { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info }, | ||
| 118 | { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info }, | ||
| 119 | { } | ||
| 120 | }; | ||
| 121 | |||
| 122 | MODULE_DEVICE_TABLE(of, rcar_du_of_table); | ||
| 123 | |||
| 124 | /* ----------------------------------------------------------------------------- | ||
| 33 | * DRM operations | 125 | * DRM operations |
| 34 | */ | 126 | */ |
| 35 | 127 | ||
| @@ -53,12 +145,13 @@ static int rcar_du_unload(struct drm_device *dev) | |||
| 53 | static int rcar_du_load(struct drm_device *dev, unsigned long flags) | 145 | static int rcar_du_load(struct drm_device *dev, unsigned long flags) |
| 54 | { | 146 | { |
| 55 | struct platform_device *pdev = dev->platformdev; | 147 | struct platform_device *pdev = dev->platformdev; |
| 148 | struct device_node *np = pdev->dev.of_node; | ||
| 56 | struct rcar_du_platform_data *pdata = pdev->dev.platform_data; | 149 | struct rcar_du_platform_data *pdata = pdev->dev.platform_data; |
| 57 | struct rcar_du_device *rcdu; | 150 | struct rcar_du_device *rcdu; |
| 58 | struct resource *mem; | 151 | struct resource *mem; |
| 59 | int ret; | 152 | int ret; |
| 60 | 153 | ||
| 61 | if (pdata == NULL) { | 154 | if (pdata == NULL && np == NULL) { |
| 62 | dev_err(dev->dev, "no platform data\n"); | 155 | dev_err(dev->dev, "no platform data\n"); |
| 63 | return -ENODEV; | 156 | return -ENODEV; |
| 64 | } | 157 | } |
| @@ -71,7 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) | |||
| 71 | 164 | ||
| 72 | rcdu->dev = &pdev->dev; | 165 | rcdu->dev = &pdev->dev; |
| 73 | rcdu->pdata = pdata; | 166 | rcdu->pdata = pdata; |
| 74 | rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data; | 167 | rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data |
| 168 | : (void *)platform_get_device_id(pdev)->driver_data; | ||
| 75 | rcdu->ddev = dev; | 169 | rcdu->ddev = dev; |
| 76 | dev->dev_private = rcdu; | 170 | dev->dev_private = rcdu; |
| 77 | 171 | ||
| @@ -232,77 +326,6 @@ static int rcar_du_remove(struct platform_device *pdev) | |||
| 232 | return 0; | 326 | return 0; |
| 233 | } | 327 | } |
| 234 | 328 | ||
| 235 | static const struct rcar_du_device_info rcar_du_r8a7779_info = { | ||
| 236 | .features = 0, | ||
| 237 | .num_crtcs = 2, | ||
| 238 | .routes = { | ||
| 239 | /* R8A7779 has two RGB outputs and one (currently unsupported) | ||
| 240 | * TCON output. | ||
| 241 | */ | ||
| 242 | [RCAR_DU_OUTPUT_DPAD0] = { | ||
| 243 | .possible_crtcs = BIT(0), | ||
| 244 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 245 | }, | ||
| 246 | [RCAR_DU_OUTPUT_DPAD1] = { | ||
| 247 | .possible_crtcs = BIT(1) | BIT(0), | ||
| 248 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 249 | }, | ||
| 250 | }, | ||
| 251 | .num_lvds = 0, | ||
| 252 | }; | ||
| 253 | |||
| 254 | static const struct rcar_du_device_info rcar_du_r8a7790_info = { | ||
| 255 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, | ||
| 256 | .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, | ||
| 257 | .num_crtcs = 3, | ||
| 258 | .routes = { | ||
| 259 | /* R8A7790 has one RGB output, two LVDS outputs and one | ||
| 260 | * (currently unsupported) TCON output. | ||
| 261 | */ | ||
| 262 | [RCAR_DU_OUTPUT_DPAD0] = { | ||
| 263 | .possible_crtcs = BIT(2) | BIT(1) | BIT(0), | ||
| 264 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 265 | }, | ||
| 266 | [RCAR_DU_OUTPUT_LVDS0] = { | ||
| 267 | .possible_crtcs = BIT(0), | ||
| 268 | .encoder_type = DRM_MODE_ENCODER_LVDS, | ||
| 269 | }, | ||
| 270 | [RCAR_DU_OUTPUT_LVDS1] = { | ||
| 271 | .possible_crtcs = BIT(2) | BIT(1), | ||
| 272 | .encoder_type = DRM_MODE_ENCODER_LVDS, | ||
| 273 | }, | ||
| 274 | }, | ||
| 275 | .num_lvds = 2, | ||
| 276 | }; | ||
| 277 | |||
| 278 | static const struct rcar_du_device_info rcar_du_r8a7791_info = { | ||
| 279 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_DEFR8, | ||
| 280 | .num_crtcs = 2, | ||
| 281 | .routes = { | ||
| 282 | /* R8A7791 has one RGB output, one LVDS output and one | ||
| 283 | * (currently unsupported) TCON output. | ||
| 284 | */ | ||
| 285 | [RCAR_DU_OUTPUT_DPAD0] = { | ||
| 286 | .possible_crtcs = BIT(1), | ||
| 287 | .encoder_type = DRM_MODE_ENCODER_NONE, | ||
| 288 | }, | ||
| 289 | [RCAR_DU_OUTPUT_LVDS0] = { | ||
| 290 | .possible_crtcs = BIT(0), | ||
| 291 | .encoder_type = DRM_MODE_ENCODER_LVDS, | ||
| 292 | }, | ||
| 293 | }, | ||
| 294 | .num_lvds = 1, | ||
| 295 | }; | ||
| 296 | |||
| 297 | static const struct platform_device_id rcar_du_id_table[] = { | ||
| 298 | { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info }, | ||
| 299 | { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info }, | ||
| 300 | { "rcar-du-r8a7791", (kernel_ulong_t)&rcar_du_r8a7791_info }, | ||
| 301 | { } | ||
| 302 | }; | ||
| 303 | |||
| 304 | MODULE_DEVICE_TABLE(platform, rcar_du_id_table); | ||
| 305 | |||
| 306 | static struct platform_driver rcar_du_platform_driver = { | 329 | static struct platform_driver rcar_du_platform_driver = { |
| 307 | .probe = rcar_du_probe, | 330 | .probe = rcar_du_probe, |
| 308 | .remove = rcar_du_remove, | 331 | .remove = rcar_du_remove, |
| @@ -310,6 +333,7 @@ static struct platform_driver rcar_du_platform_driver = { | |||
| 310 | .owner = THIS_MODULE, | 333 | .owner = THIS_MODULE, |
| 311 | .name = "rcar-du", | 334 | .name = "rcar-du", |
| 312 | .pm = &rcar_du_pm_ops, | 335 | .pm = &rcar_du_pm_ops, |
| 336 | .of_match_table = rcar_du_of_table, | ||
| 313 | }, | 337 | }, |
| 314 | .id_table = rcar_du_id_table, | 338 | .id_table = rcar_du_id_table, |
| 315 | }; | 339 | }; |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 2066ec6f4f22..8e494633c3b3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h | |||
| @@ -37,6 +37,7 @@ struct rcar_du_lvdsenc; | |||
| 37 | * struct rcar_du_output_routing - Output routing specification | 37 | * struct rcar_du_output_routing - Output routing specification |
| 38 | * @possible_crtcs: bitmask of possible CRTCs for the output | 38 | * @possible_crtcs: bitmask of possible CRTCs for the output |
| 39 | * @encoder_type: DRM type of the internal encoder associated with the output | 39 | * @encoder_type: DRM type of the internal encoder associated with the output |
| 40 | * @port: device tree port number corresponding to this output route | ||
| 40 | * | 41 | * |
| 41 | * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data | 42 | * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data |
| 42 | * specify the valid SoC outputs, which CRTCs can drive the output, and the type | 43 | * specify the valid SoC outputs, which CRTCs can drive the output, and the type |
| @@ -45,6 +46,7 @@ struct rcar_du_lvdsenc; | |||
| 45 | struct rcar_du_output_routing { | 46 | struct rcar_du_output_routing { |
| 46 | unsigned int possible_crtcs; | 47 | unsigned int possible_crtcs; |
| 47 | unsigned int encoder_type; | 48 | unsigned int encoder_type; |
| 49 | unsigned int port; | ||
| 48 | }; | 50 | }; |
| 49 | 51 | ||
| 50 | /* | 52 | /* |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index fd152d0406aa..7c0ec95915ef 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c | |||
| @@ -142,7 +142,8 @@ static const struct drm_encoder_funcs encoder_funcs = { | |||
| 142 | int rcar_du_encoder_init(struct rcar_du_device *rcdu, | 142 | int rcar_du_encoder_init(struct rcar_du_device *rcdu, |
| 143 | enum rcar_du_encoder_type type, | 143 | enum rcar_du_encoder_type type, |
| 144 | enum rcar_du_output output, | 144 | enum rcar_du_output output, |
| 145 | const struct rcar_du_encoder_data *data) | 145 | const struct rcar_du_encoder_data *data, |
| 146 | struct device_node *np) | ||
| 146 | { | 147 | { |
| 147 | struct rcar_du_encoder *renc; | 148 | struct rcar_du_encoder *renc; |
| 148 | unsigned int encoder_type; | 149 | unsigned int encoder_type; |
| @@ -189,9 +190,11 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, | |||
| 189 | drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); | 190 | drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); |
| 190 | 191 | ||
| 191 | switch (encoder_type) { | 192 | switch (encoder_type) { |
| 192 | case DRM_MODE_ENCODER_LVDS: | 193 | case DRM_MODE_ENCODER_LVDS: { |
| 193 | return rcar_du_lvds_connector_init(rcdu, renc, | 194 | const struct rcar_du_panel_data *pdata = |
| 194 | &data->connector.lvds.panel); | 195 | data ? &data->connector.lvds.panel : NULL; |
| 196 | return rcar_du_lvds_connector_init(rcdu, renc, pdata, np); | ||
| 197 | } | ||
| 195 | 198 | ||
| 196 | case DRM_MODE_ENCODER_DAC: | 199 | case DRM_MODE_ENCODER_DAC: |
| 197 | return rcar_du_vga_connector_init(rcdu, renc); | 200 | return rcar_du_vga_connector_init(rcdu, renc); |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 5836007fa8e7..bd624135ef1f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h | |||
| @@ -44,6 +44,7 @@ rcar_du_connector_best_encoder(struct drm_connector *connector); | |||
| 44 | int rcar_du_encoder_init(struct rcar_du_device *rcdu, | 44 | int rcar_du_encoder_init(struct rcar_du_device *rcdu, |
| 45 | enum rcar_du_encoder_type type, | 45 | enum rcar_du_encoder_type type, |
| 46 | enum rcar_du_output output, | 46 | enum rcar_du_output output, |
| 47 | const struct rcar_du_encoder_data *data); | 47 | const struct rcar_du_encoder_data *data, |
| 48 | struct device_node *np); | ||
| 48 | 49 | ||
| 49 | #endif /* __RCAR_DU_ENCODER_H__ */ | 50 | #endif /* __RCAR_DU_ENCODER_H__ */ |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 7eabbd7b49cb..6c24ad7d03ef 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c | |||
| @@ -17,6 +17,8 @@ | |||
| 17 | #include <drm/drm_fb_cma_helper.h> | 17 | #include <drm/drm_fb_cma_helper.h> |
| 18 | #include <drm/drm_gem_cma_helper.h> | 18 | #include <drm/drm_gem_cma_helper.h> |
| 19 | 19 | ||
| 20 | #include <linux/of_graph.h> | ||
| 21 | |||
| 20 | #include "rcar_du_crtc.h" | 22 | #include "rcar_du_crtc.h" |
| 21 | #include "rcar_du_drv.h" | 23 | #include "rcar_du_drv.h" |
| 22 | #include "rcar_du_encoder.h" | 24 | #include "rcar_du_encoder.h" |
| @@ -188,6 +190,205 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { | |||
| 188 | .output_poll_changed = rcar_du_output_poll_changed, | 190 | .output_poll_changed = rcar_du_output_poll_changed, |
| 189 | }; | 191 | }; |
| 190 | 192 | ||
| 193 | static int rcar_du_encoders_init_pdata(struct rcar_du_device *rcdu) | ||
| 194 | { | ||
| 195 | unsigned int num_encoders = 0; | ||
| 196 | unsigned int i; | ||
| 197 | int ret; | ||
| 198 | |||
| 199 | for (i = 0; i < rcdu->pdata->num_encoders; ++i) { | ||
| 200 | const struct rcar_du_encoder_data *pdata = | ||
| 201 | &rcdu->pdata->encoders[i]; | ||
| 202 | const struct rcar_du_output_routing *route = | ||
| 203 | &rcdu->info->routes[pdata->output]; | ||
| 204 | |||
| 205 | if (pdata->type == RCAR_DU_ENCODER_UNUSED) | ||
| 206 | continue; | ||
| 207 | |||
| 208 | if (pdata->output >= RCAR_DU_OUTPUT_MAX || | ||
| 209 | route->possible_crtcs == 0) { | ||
| 210 | dev_warn(rcdu->dev, | ||
| 211 | "encoder %u references unexisting output %u, skipping\n", | ||
| 212 | i, pdata->output); | ||
| 213 | continue; | ||
| 214 | } | ||
| 215 | |||
| 216 | ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output, | ||
| 217 | pdata, NULL); | ||
| 218 | if (ret < 0) | ||
| 219 | return ret; | ||
| 220 | |||
| 221 | num_encoders++; | ||
| 222 | } | ||
| 223 | |||
| 224 | return num_encoders; | ||
| 225 | } | ||
| 226 | |||
| 227 | static int rcar_du_encoders_init_dt_one(struct rcar_du_device *rcdu, | ||
| 228 | enum rcar_du_output output, | ||
| 229 | struct of_endpoint *ep) | ||
| 230 | { | ||
| 231 | static const struct { | ||
| 232 | const char *compatible; | ||
| 233 | enum rcar_du_encoder_type type; | ||
| 234 | } encoders[] = { | ||
| 235 | { "adi,adv7123", RCAR_DU_ENCODER_VGA }, | ||
| 236 | { "thine,thc63lvdm83d", RCAR_DU_ENCODER_LVDS }, | ||
| 237 | }; | ||
| 238 | |||
| 239 | enum rcar_du_encoder_type enc_type = RCAR_DU_ENCODER_NONE; | ||
| 240 | struct device_node *connector = NULL; | ||
| 241 | struct device_node *encoder = NULL; | ||
| 242 | struct device_node *prev = NULL; | ||
| 243 | struct device_node *entity_ep_node; | ||
| 244 | struct device_node *entity; | ||
| 245 | int ret; | ||
| 246 | |||
| 247 | /* | ||
| 248 | * Locate the connected entity and infer its type from the number of | ||
| 249 | * endpoints. | ||
| 250 | */ | ||
| 251 | entity = of_graph_get_remote_port_parent(ep->local_node); | ||
| 252 | if (!entity) { | ||
| 253 | dev_dbg(rcdu->dev, "unconnected endpoint %s, skipping\n", | ||
| 254 | ep->local_node->full_name); | ||
| 255 | return 0; | ||
| 256 | } | ||
| 257 | |||
| 258 | entity_ep_node = of_parse_phandle(ep->local_node, "remote-endpoint", 0); | ||
| 259 | |||
| 260 | while (1) { | ||
| 261 | struct device_node *ep_node; | ||
| 262 | |||
| 263 | ep_node = of_graph_get_next_endpoint(entity, prev); | ||
| 264 | of_node_put(prev); | ||
| 265 | prev = ep_node; | ||
| 266 | |||
| 267 | if (!ep_node) | ||
| 268 | break; | ||
| 269 | |||
| 270 | if (ep_node == entity_ep_node) | ||
| 271 | continue; | ||
| 272 | |||
| 273 | /* | ||
| 274 | * We've found one endpoint other than the input, this must | ||
| 275 | * be an encoder. Locate the connector. | ||
| 276 | */ | ||
| 277 | encoder = entity; | ||
| 278 | connector = of_graph_get_remote_port_parent(ep_node); | ||
| 279 | of_node_put(ep_node); | ||
| 280 | |||
| 281 | if (!connector) { | ||
| 282 | dev_warn(rcdu->dev, | ||
| 283 | "no connector for encoder %s, skipping\n", | ||
| 284 | encoder->full_name); | ||
| 285 | of_node_put(entity_ep_node); | ||
| 286 | of_node_put(encoder); | ||
| 287 | return 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | break; | ||
| 291 | } | ||
| 292 | |||
| 293 | of_node_put(entity_ep_node); | ||
| 294 | |||
| 295 | if (encoder) { | ||
| 296 | /* | ||
| 297 | * If an encoder has been found, get its type based on its | ||
| 298 | * compatible string. | ||
| 299 | */ | ||
| 300 | unsigned int i; | ||
| 301 | |||
| 302 | for (i = 0; i < ARRAY_SIZE(encoders); ++i) { | ||
| 303 | if (of_device_is_compatible(encoder, | ||
| 304 | encoders[i].compatible)) { | ||
| 305 | enc_type = encoders[i].type; | ||
| 306 | break; | ||
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | if (i == ARRAY_SIZE(encoders)) { | ||
| 311 | dev_warn(rcdu->dev, | ||
| 312 | "unknown encoder type for %s, skipping\n", | ||
| 313 | encoder->full_name); | ||
| 314 | of_node_put(encoder); | ||
| 315 | of_node_put(connector); | ||
| 316 | return 0; | ||
| 317 | } | ||
| 318 | } else { | ||
| 319 | /* | ||
| 320 | * If no encoder has been found the entity must be the | ||
| 321 | * connector. | ||
| 322 | */ | ||
| 323 | connector = entity; | ||
| 324 | } | ||
| 325 | |||
| 326 | ret = rcar_du_encoder_init(rcdu, enc_type, output, NULL, connector); | ||
| 327 | of_node_put(encoder); | ||
| 328 | of_node_put(connector); | ||
| 329 | |||
| 330 | return ret < 0 ? ret : 1; | ||
| 331 | } | ||
| 332 | |||
| 333 | static int rcar_du_encoders_init_dt(struct rcar_du_device *rcdu) | ||
| 334 | { | ||
| 335 | struct device_node *np = rcdu->dev->of_node; | ||
| 336 | struct device_node *prev = NULL; | ||
| 337 | unsigned int num_encoders = 0; | ||
| 338 | |||
| 339 | /* | ||
| 340 | * Iterate over the endpoints and create one encoder for each output | ||
| 341 | * pipeline. | ||
| 342 | */ | ||
| 343 | while (1) { | ||
| 344 | struct device_node *ep_node; | ||
| 345 | enum rcar_du_output output; | ||
| 346 | struct of_endpoint ep; | ||
| 347 | unsigned int i; | ||
| 348 | int ret; | ||
| 349 | |||
| 350 | ep_node = of_graph_get_next_endpoint(np, prev); | ||
| 351 | of_node_put(prev); | ||
| 352 | prev = ep_node; | ||
| 353 | |||
| 354 | if (ep_node == NULL) | ||
| 355 | break; | ||
| 356 | |||
| 357 | ret = of_graph_parse_endpoint(ep_node, &ep); | ||
| 358 | if (ret < 0) { | ||
| 359 | of_node_put(ep_node); | ||
| 360 | return ret; | ||
| 361 | } | ||
| 362 | |||
| 363 | /* Find the output route corresponding to the port number. */ | ||
| 364 | for (i = 0; i < RCAR_DU_OUTPUT_MAX; ++i) { | ||
| 365 | if (rcdu->info->routes[i].possible_crtcs && | ||
| 366 | rcdu->info->routes[i].port == ep.port) { | ||
| 367 | output = i; | ||
| 368 | break; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | if (i == RCAR_DU_OUTPUT_MAX) { | ||
| 373 | dev_warn(rcdu->dev, | ||
| 374 | "port %u references unexisting output, skipping\n", | ||
| 375 | ep.port); | ||
| 376 | continue; | ||
| 377 | } | ||
| 378 | |||
| 379 | /* Process the output pipeline. */ | ||
| 380 | ret = rcar_du_encoders_init_dt_one(rcdu, output, &ep); | ||
| 381 | if (ret < 0) { | ||
| 382 | of_node_put(ep_node); | ||
| 383 | return ret; | ||
| 384 | } | ||
| 385 | |||
| 386 | num_encoders += ret; | ||
| 387 | } | ||
| 388 | |||
| 389 | return num_encoders; | ||
| 390 | } | ||
| 391 | |||
| 191 | int rcar_du_modeset_init(struct rcar_du_device *rcdu) | 392 | int rcar_du_modeset_init(struct rcar_du_device *rcdu) |
| 192 | { | 393 | { |
| 193 | static const unsigned int mmio_offsets[] = { | 394 | static const unsigned int mmio_offsets[] = { |
| @@ -197,6 +398,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) | |||
| 197 | struct drm_device *dev = rcdu->ddev; | 398 | struct drm_device *dev = rcdu->ddev; |
| 198 | struct drm_encoder *encoder; | 399 | struct drm_encoder *encoder; |
| 199 | struct drm_fbdev_cma *fbdev; | 400 | struct drm_fbdev_cma *fbdev; |
| 401 | unsigned int num_encoders; | ||
| 200 | unsigned int num_groups; | 402 | unsigned int num_groups; |
| 201 | unsigned int i; | 403 | unsigned int i; |
| 202 | int ret; | 404 | int ret; |
| @@ -240,28 +442,15 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) | |||
| 240 | if (ret < 0) | 442 | if (ret < 0) |
| 241 | return ret; | 443 | return ret; |
| 242 | 444 | ||
| 243 | for (i = 0; i < rcdu->pdata->num_encoders; ++i) { | 445 | if (rcdu->pdata) |
| 244 | const struct rcar_du_encoder_data *pdata = | 446 | ret = rcar_du_encoders_init_pdata(rcdu); |
| 245 | &rcdu->pdata->encoders[i]; | 447 | else |
| 246 | const struct rcar_du_output_routing *route = | 448 | ret = rcar_du_encoders_init_dt(rcdu); |
| 247 | &rcdu->info->routes[pdata->output]; | ||
| 248 | |||
| 249 | if (pdata->type == RCAR_DU_ENCODER_UNUSED) | ||
| 250 | continue; | ||
| 251 | 449 | ||
| 252 | if (pdata->output >= RCAR_DU_OUTPUT_MAX || | 450 | if (ret < 0) |
| 253 | route->possible_crtcs == 0) { | 451 | return ret; |
| 254 | dev_warn(rcdu->dev, | ||
| 255 | "encoder %u references unexisting output %u, skipping\n", | ||
| 256 | i, pdata->output); | ||
| 257 | continue; | ||
| 258 | } | ||
| 259 | 452 | ||
| 260 | ret = rcar_du_encoder_init(rcdu, pdata->type, pdata->output, | 453 | num_encoders = ret; |
| 261 | pdata); | ||
| 262 | if (ret < 0) | ||
| 263 | return ret; | ||
| 264 | } | ||
| 265 | 454 | ||
| 266 | /* Set the possible CRTCs and possible clones. There's always at least | 455 | /* Set the possible CRTCs and possible clones. There's always at least |
| 267 | * one way for all encoders to clone each other, set all bits in the | 456 | * 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) | |||
| 273 | &rcdu->info->routes[renc->output]; | 462 | &rcdu->info->routes[renc->output]; |
| 274 | 463 | ||
| 275 | encoder->possible_crtcs = route->possible_crtcs; | 464 | encoder->possible_crtcs = route->possible_crtcs; |
| 276 | encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1; | 465 | encoder->possible_clones = (1 << num_encoders) - 1; |
| 277 | } | 466 | } |
| 278 | 467 | ||
| 279 | /* Now that the CRTCs have been initialized register the planes. */ | 468 | /* Now that the CRTCs have been initialized register the planes. */ |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index d29544121658..115eed20db12 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c | |||
| @@ -15,6 +15,10 @@ | |||
| 15 | #include <drm/drm_crtc.h> | 15 | #include <drm/drm_crtc.h> |
| 16 | #include <drm/drm_crtc_helper.h> | 16 | #include <drm/drm_crtc_helper.h> |
| 17 | 17 | ||
| 18 | #include <video/display_timing.h> | ||
| 19 | #include <video/of_display_timing.h> | ||
| 20 | #include <video/videomode.h> | ||
| 21 | |||
| 18 | #include "rcar_du_drv.h" | 22 | #include "rcar_du_drv.h" |
| 19 | #include "rcar_du_encoder.h" | 23 | #include "rcar_du_encoder.h" |
| 20 | #include "rcar_du_kms.h" | 24 | #include "rcar_du_kms.h" |
| @@ -23,7 +27,7 @@ | |||
| 23 | struct rcar_du_lvds_connector { | 27 | struct rcar_du_lvds_connector { |
| 24 | struct rcar_du_connector connector; | 28 | struct rcar_du_connector connector; |
| 25 | 29 | ||
| 26 | const struct rcar_du_panel_data *panel; | 30 | struct rcar_du_panel_data panel; |
| 27 | }; | 31 | }; |
| 28 | 32 | ||
| 29 | #define to_rcar_lvds_connector(c) \ | 33 | #define to_rcar_lvds_connector(c) \ |
| @@ -41,7 +45,7 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) | |||
| 41 | 45 | ||
| 42 | mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; | 46 | mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; |
| 43 | 47 | ||
| 44 | drm_display_mode_from_videomode(&lvdscon->panel->mode, mode); | 48 | drm_display_mode_from_videomode(&lvdscon->panel.mode, mode); |
| 45 | 49 | ||
| 46 | drm_mode_probed_add(connector, mode); | 50 | drm_mode_probed_add(connector, mode); |
| 47 | 51 | ||
| @@ -74,7 +78,8 @@ static const struct drm_connector_funcs connector_funcs = { | |||
| 74 | 78 | ||
| 75 | int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, | 79 | int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, |
| 76 | struct rcar_du_encoder *renc, | 80 | struct rcar_du_encoder *renc, |
| 77 | const struct rcar_du_panel_data *panel) | 81 | const struct rcar_du_panel_data *panel, |
| 82 | /* TODO const */ struct device_node *np) | ||
| 78 | { | 83 | { |
| 79 | struct rcar_du_lvds_connector *lvdscon; | 84 | struct rcar_du_lvds_connector *lvdscon; |
| 80 | struct drm_connector *connector; | 85 | struct drm_connector *connector; |
| @@ -84,11 +89,24 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, | |||
| 84 | if (lvdscon == NULL) | 89 | if (lvdscon == NULL) |
| 85 | return -ENOMEM; | 90 | return -ENOMEM; |
| 86 | 91 | ||
| 87 | lvdscon->panel = panel; | 92 | if (panel) { |
| 93 | lvdscon->panel = *panel; | ||
| 94 | } else { | ||
| 95 | struct display_timing timing; | ||
| 96 | |||
| 97 | ret = of_get_display_timing(np, "panel-timing", &timing); | ||
| 98 | if (ret < 0) | ||
| 99 | return ret; | ||
| 100 | |||
| 101 | videomode_from_timing(&timing, &lvdscon->panel.mode); | ||
| 102 | |||
| 103 | of_property_read_u32(np, "width-mm", &lvdscon->panel.width_mm); | ||
| 104 | of_property_read_u32(np, "height-mm", &lvdscon->panel.height_mm); | ||
| 105 | } | ||
| 88 | 106 | ||
| 89 | connector = &lvdscon->connector.connector; | 107 | connector = &lvdscon->connector.connector; |
| 90 | connector->display_info.width_mm = panel->width_mm; | 108 | connector->display_info.width_mm = lvdscon->panel.width_mm; |
| 91 | connector->display_info.height_mm = panel->height_mm; | 109 | connector->display_info.height_mm = lvdscon->panel.height_mm; |
| 92 | 110 | ||
| 93 | ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, | 111 | ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs, |
| 94 | DRM_MODE_CONNECTOR_LVDS); | 112 | DRM_MODE_CONNECTOR_LVDS); |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h index 6d878a129a79..d11424d537f9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h | |||
| @@ -20,6 +20,7 @@ struct rcar_du_panel_data; | |||
| 20 | 20 | ||
| 21 | int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, | 21 | int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, |
| 22 | struct rcar_du_encoder *renc, | 22 | struct rcar_du_encoder *renc, |
| 23 | const struct rcar_du_panel_data *panel); | 23 | const struct rcar_du_panel_data *panel, |
| 24 | struct device_node *np); | ||
| 24 | 25 | ||
| 25 | #endif /* __RCAR_DU_LVDSCON_H__ */ | 26 | #endif /* __RCAR_DU_LVDSCON_H__ */ |
