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__ */ |