Commit 8a105aaa25f4504d26ca828f12d709d2213a230e
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
Merge branch 'drm-armada-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into drm-next
Merge armada changes, I've confirmed the componenet changes are same as in Greg's tree. * 'drm-armada-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm: drm/armada: register crtc with port drm/armada: permit CRTCs to be registered as separate devices dt-bindings: add Marvell Dove LCD controller documentation drm/armada: update Armada 510 (Dove) to use "ext_ref_clk1" as the clock drm/armada: convert to componentized support drm: add of_graph endpoint helper to find possible CRTCs component: fix bug with legacy API drm/armada: make variant a CRTC thing drm/armada: move variant initialisation to CRTC init drm/armada: use number of CRTCs registered drm/armada: move IRQ handling into CRTC component: add support for component match array component: ignore multiple additions of the same component component: fix missed cleanup in case of devres failure
Showing 12 changed files Side-by-side Diff
- Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt
- drivers/base/component.c
- drivers/gpu/drm/Makefile
- drivers/gpu/drm/armada/armada_510.c
- drivers/gpu/drm/armada/armada_crtc.c
- drivers/gpu/drm/armada/armada_crtc.h
- drivers/gpu/drm/armada/armada_drm.h
- drivers/gpu/drm/armada/armada_drv.c
- drivers/gpu/drm/drm_of.c
- include/drm/drm_crtc.h
- include/drm/drm_of.h
- include/linux/component.h
Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt
1 | +Device Tree bindings for Armada DRM CRTC driver | |
2 | + | |
3 | +Required properties: | |
4 | + - compatible: value should be "marvell,dove-lcd". | |
5 | + - reg: base address and size of the LCD controller | |
6 | + - interrupts: single interrupt number for the LCD controller | |
7 | + - port: video output port with endpoints, as described by graph.txt | |
8 | + | |
9 | +Optional properties: | |
10 | + | |
11 | + - clocks: as described by clock-bindings.txt | |
12 | + - clock-names: as described by clock-bindings.txt | |
13 | + "axiclk" - axi bus clock for pixel clock | |
14 | + "plldivider" - pll divider clock for pixel clock | |
15 | + "ext_ref_clk0" - external clock 0 for pixel clock | |
16 | + "ext_ref_clk1" - external clock 1 for pixel clock | |
17 | + | |
18 | +Note: all clocks are optional but at least one must be specified. | |
19 | +Further clocks may be added in the future according to requirements of | |
20 | +different SoCs. | |
21 | + | |
22 | +Example: | |
23 | + | |
24 | + lcd0: lcd-controller@820000 { | |
25 | + compatible = "marvell,dove-lcd"; | |
26 | + reg = <0x820000 0x1000>; | |
27 | + interrupts = <47>; | |
28 | + clocks = <&si5351 0>; | |
29 | + clock-names = "ext_ref_clk_1"; | |
30 | + }; |
drivers/base/component.c
... | ... | @@ -18,6 +18,15 @@ |
18 | 18 | #include <linux/mutex.h> |
19 | 19 | #include <linux/slab.h> |
20 | 20 | |
21 | +struct component_match { | |
22 | + size_t alloc; | |
23 | + size_t num; | |
24 | + struct { | |
25 | + void *data; | |
26 | + int (*fn)(struct device *, void *); | |
27 | + } compare[0]; | |
28 | +}; | |
29 | + | |
21 | 30 | struct master { |
22 | 31 | struct list_head node; |
23 | 32 | struct list_head components; |
... | ... | @@ -25,6 +34,7 @@ |
25 | 34 | |
26 | 35 | const struct component_master_ops *ops; |
27 | 36 | struct device *dev; |
37 | + struct component_match *match; | |
28 | 38 | }; |
29 | 39 | |
30 | 40 | struct component { |
... | ... | @@ -69,6 +79,11 @@ |
69 | 79 | c->master = NULL; |
70 | 80 | } |
71 | 81 | |
82 | +/* | |
83 | + * Add a component to a master, finding the component via the compare | |
84 | + * function and compare data. This is safe to call for duplicate matches | |
85 | + * and will not result in the same component being added multiple times. | |
86 | + */ | |
72 | 87 | int component_master_add_child(struct master *master, |
73 | 88 | int (*compare)(struct device *, void *), void *compare_data) |
74 | 89 | { |
75 | 90 | |
... | ... | @@ -76,11 +91,12 @@ |
76 | 91 | int ret = -ENXIO; |
77 | 92 | |
78 | 93 | list_for_each_entry(c, &component_list, node) { |
79 | - if (c->master) | |
94 | + if (c->master && c->master != master) | |
80 | 95 | continue; |
81 | 96 | |
82 | 97 | if (compare(c->dev, compare_data)) { |
83 | - component_attach_master(master, c); | |
98 | + if (!c->master) | |
99 | + component_attach_master(master, c); | |
84 | 100 | ret = 0; |
85 | 101 | break; |
86 | 102 | } |
... | ... | @@ -90,6 +106,34 @@ |
90 | 106 | } |
91 | 107 | EXPORT_SYMBOL_GPL(component_master_add_child); |
92 | 108 | |
109 | +static int find_components(struct master *master) | |
110 | +{ | |
111 | + struct component_match *match = master->match; | |
112 | + size_t i; | |
113 | + int ret = 0; | |
114 | + | |
115 | + if (!match) { | |
116 | + /* | |
117 | + * Search the list of components, looking for components that | |
118 | + * belong to this master, and attach them to the master. | |
119 | + */ | |
120 | + return master->ops->add_components(master->dev, master); | |
121 | + } | |
122 | + | |
123 | + /* | |
124 | + * Scan the array of match functions and attach | |
125 | + * any components which are found to this master. | |
126 | + */ | |
127 | + for (i = 0; i < match->num; i++) { | |
128 | + ret = component_master_add_child(master, | |
129 | + match->compare[i].fn, | |
130 | + match->compare[i].data); | |
131 | + if (ret) | |
132 | + break; | |
133 | + } | |
134 | + return ret; | |
135 | +} | |
136 | + | |
93 | 137 | /* Detach all attached components from this master */ |
94 | 138 | static void master_remove_components(struct master *master) |
95 | 139 | { |
96 | 140 | |
97 | 141 | |
98 | 142 | |
99 | 143 | |
100 | 144 | |
101 | 145 | |
102 | 146 | |
... | ... | @@ -113,44 +157,44 @@ |
113 | 157 | static int try_to_bring_up_master(struct master *master, |
114 | 158 | struct component *component) |
115 | 159 | { |
116 | - int ret = 0; | |
160 | + int ret; | |
117 | 161 | |
118 | - if (!master->bound) { | |
119 | - /* | |
120 | - * Search the list of components, looking for components that | |
121 | - * belong to this master, and attach them to the master. | |
122 | - */ | |
123 | - if (master->ops->add_components(master->dev, master)) { | |
124 | - /* Failed to find all components */ | |
125 | - master_remove_components(master); | |
126 | - ret = 0; | |
127 | - goto out; | |
128 | - } | |
162 | + if (master->bound) | |
163 | + return 0; | |
129 | 164 | |
130 | - if (component && component->master != master) { | |
131 | - master_remove_components(master); | |
132 | - ret = 0; | |
133 | - goto out; | |
134 | - } | |
165 | + /* | |
166 | + * Search the list of components, looking for components that | |
167 | + * belong to this master, and attach them to the master. | |
168 | + */ | |
169 | + if (find_components(master)) { | |
170 | + /* Failed to find all components */ | |
171 | + ret = 0; | |
172 | + goto out; | |
173 | + } | |
135 | 174 | |
136 | - if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { | |
137 | - ret = -ENOMEM; | |
138 | - goto out; | |
139 | - } | |
175 | + if (component && component->master != master) { | |
176 | + ret = 0; | |
177 | + goto out; | |
178 | + } | |
140 | 179 | |
141 | - /* Found all components */ | |
142 | - ret = master->ops->bind(master->dev); | |
143 | - if (ret < 0) { | |
144 | - devres_release_group(master->dev, NULL); | |
145 | - dev_info(master->dev, "master bind failed: %d\n", ret); | |
146 | - master_remove_components(master); | |
147 | - goto out; | |
148 | - } | |
180 | + if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { | |
181 | + ret = -ENOMEM; | |
182 | + goto out; | |
183 | + } | |
149 | 184 | |
150 | - master->bound = true; | |
151 | - ret = 1; | |
185 | + /* Found all components */ | |
186 | + ret = master->ops->bind(master->dev); | |
187 | + if (ret < 0) { | |
188 | + devres_release_group(master->dev, NULL); | |
189 | + dev_info(master->dev, "master bind failed: %d\n", ret); | |
190 | + goto out; | |
152 | 191 | } |
192 | + | |
193 | + master->bound = true; | |
194 | + return 1; | |
195 | + | |
153 | 196 | out: |
197 | + master_remove_components(master); | |
154 | 198 | |
155 | 199 | return ret; |
156 | 200 | } |
157 | 201 | |
158 | 202 | |
159 | 203 | |
... | ... | @@ -180,18 +224,89 @@ |
180 | 224 | master_remove_components(master); |
181 | 225 | } |
182 | 226 | |
183 | -int component_master_add(struct device *dev, | |
184 | - const struct component_master_ops *ops) | |
227 | +static size_t component_match_size(size_t num) | |
185 | 228 | { |
229 | + return offsetof(struct component_match, compare[num]); | |
230 | +} | |
231 | + | |
232 | +static struct component_match *component_match_realloc(struct device *dev, | |
233 | + struct component_match *match, size_t num) | |
234 | +{ | |
235 | + struct component_match *new; | |
236 | + | |
237 | + if (match && match->alloc == num) | |
238 | + return match; | |
239 | + | |
240 | + new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL); | |
241 | + if (!new) | |
242 | + return ERR_PTR(-ENOMEM); | |
243 | + | |
244 | + if (match) { | |
245 | + memcpy(new, match, component_match_size(min(match->num, num))); | |
246 | + devm_kfree(dev, match); | |
247 | + } else { | |
248 | + new->num = 0; | |
249 | + } | |
250 | + | |
251 | + new->alloc = num; | |
252 | + | |
253 | + return new; | |
254 | +} | |
255 | + | |
256 | +/* | |
257 | + * Add a component to be matched. | |
258 | + * | |
259 | + * The match array is first created or extended if necessary. | |
260 | + */ | |
261 | +void component_match_add(struct device *dev, struct component_match **matchptr, | |
262 | + int (*compare)(struct device *, void *), void *compare_data) | |
263 | +{ | |
264 | + struct component_match *match = *matchptr; | |
265 | + | |
266 | + if (IS_ERR(match)) | |
267 | + return; | |
268 | + | |
269 | + if (!match || match->num == match->alloc) { | |
270 | + size_t new_size = match ? match->alloc + 16 : 15; | |
271 | + | |
272 | + match = component_match_realloc(dev, match, new_size); | |
273 | + | |
274 | + *matchptr = match; | |
275 | + | |
276 | + if (IS_ERR(match)) | |
277 | + return; | |
278 | + } | |
279 | + | |
280 | + match->compare[match->num].fn = compare; | |
281 | + match->compare[match->num].data = compare_data; | |
282 | + match->num++; | |
283 | +} | |
284 | +EXPORT_SYMBOL(component_match_add); | |
285 | + | |
286 | +int component_master_add_with_match(struct device *dev, | |
287 | + const struct component_master_ops *ops, | |
288 | + struct component_match *match) | |
289 | +{ | |
186 | 290 | struct master *master; |
187 | 291 | int ret; |
188 | 292 | |
293 | + if (ops->add_components && match) | |
294 | + return -EINVAL; | |
295 | + | |
296 | + if (match) { | |
297 | + /* Reallocate the match array for its true size */ | |
298 | + match = component_match_realloc(dev, match, match->num); | |
299 | + if (IS_ERR(match)) | |
300 | + return PTR_ERR(match); | |
301 | + } | |
302 | + | |
189 | 303 | master = kzalloc(sizeof(*master), GFP_KERNEL); |
190 | 304 | if (!master) |
191 | 305 | return -ENOMEM; |
192 | 306 | |
193 | 307 | master->dev = dev; |
194 | 308 | master->ops = ops; |
309 | + master->match = match; | |
195 | 310 | INIT_LIST_HEAD(&master->components); |
196 | 311 | |
197 | 312 | /* Add to the list of available masters. */ |
... | ... | @@ -208,6 +323,13 @@ |
208 | 323 | mutex_unlock(&component_mutex); |
209 | 324 | |
210 | 325 | return ret < 0 ? ret : 0; |
326 | +} | |
327 | +EXPORT_SYMBOL_GPL(component_master_add_with_match); | |
328 | + | |
329 | +int component_master_add(struct device *dev, | |
330 | + const struct component_master_ops *ops) | |
331 | +{ | |
332 | + return component_master_add_with_match(dev, ops, NULL); | |
211 | 333 | } |
212 | 334 | EXPORT_SYMBOL_GPL(component_master_add); |
213 | 335 |
drivers/gpu/drm/Makefile
drivers/gpu/drm/armada/armada_510.c
... | ... | @@ -15,20 +15,19 @@ |
15 | 15 | #include "armada_drm.h" |
16 | 16 | #include "armada_hw.h" |
17 | 17 | |
18 | -static int armada510_init(struct armada_private *priv, struct device *dev) | |
18 | +static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) | |
19 | 19 | { |
20 | - priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1"); | |
20 | + struct clk *clk; | |
21 | 21 | |
22 | - if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT) | |
23 | - priv->extclk[0] = ERR_PTR(-EPROBE_DEFER); | |
22 | + clk = devm_clk_get(dev, "ext_ref_clk1"); | |
23 | + if (IS_ERR(clk)) | |
24 | + return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk); | |
24 | 25 | |
25 | - return PTR_RET(priv->extclk[0]); | |
26 | -} | |
26 | + dcrtc->extclk[0] = clk; | |
27 | 27 | |
28 | -static int armada510_crtc_init(struct armada_crtc *dcrtc) | |
29 | -{ | |
30 | 28 | /* Lower the watermark so to eliminate jitter at higher bandwidths */ |
31 | 29 | armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); |
30 | + | |
32 | 31 | return 0; |
33 | 32 | } |
34 | 33 | |
... | ... | @@ -45,8 +44,7 @@ |
45 | 44 | static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, |
46 | 45 | const struct drm_display_mode *mode, uint32_t *sclk) |
47 | 46 | { |
48 | - struct armada_private *priv = dcrtc->crtc.dev->dev_private; | |
49 | - struct clk *clk = priv->extclk[0]; | |
47 | + struct clk *clk = dcrtc->extclk[0]; | |
50 | 48 | int ret; |
51 | 49 | |
52 | 50 | if (dcrtc->num == 1) |
... | ... | @@ -81,8 +79,7 @@ |
81 | 79 | const struct armada_variant armada510_ops = { |
82 | 80 | .has_spu_adv_reg = true, |
83 | 81 | .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, |
84 | - .init = armada510_init, | |
85 | - .crtc_init = armada510_crtc_init, | |
86 | - .crtc_compute_clock = armada510_crtc_compute_clock, | |
82 | + .init = armada510_crtc_init, | |
83 | + .compute_clock = armada510_crtc_compute_clock, | |
87 | 84 | }; |
drivers/gpu/drm/armada/armada_crtc.c
... | ... | @@ -7,6 +7,9 @@ |
7 | 7 | * published by the Free Software Foundation. |
8 | 8 | */ |
9 | 9 | #include <linux/clk.h> |
10 | +#include <linux/component.h> | |
11 | +#include <linux/of_device.h> | |
12 | +#include <linux/platform_device.h> | |
10 | 13 | #include <drm/drmP.h> |
11 | 14 | #include <drm/drm_crtc_helper.h> |
12 | 15 | #include "armada_crtc.h" |
13 | 16 | |
14 | 17 | |
15 | 18 | |
... | ... | @@ -332,24 +335,23 @@ |
332 | 335 | static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, |
333 | 336 | const struct drm_display_mode *mode, struct drm_display_mode *adj) |
334 | 337 | { |
335 | - struct armada_private *priv = crtc->dev->dev_private; | |
336 | 338 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
337 | 339 | int ret; |
338 | 340 | |
339 | 341 | /* We can't do interlaced modes if we don't have the SPU_ADV_REG */ |
340 | - if (!priv->variant->has_spu_adv_reg && | |
342 | + if (!dcrtc->variant->has_spu_adv_reg && | |
341 | 343 | adj->flags & DRM_MODE_FLAG_INTERLACE) |
342 | 344 | return false; |
343 | 345 | |
344 | 346 | /* Check whether the display mode is possible */ |
345 | - ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL); | |
347 | + ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL); | |
346 | 348 | if (ret) |
347 | 349 | return false; |
348 | 350 | |
349 | 351 | return true; |
350 | 352 | } |
351 | 353 | |
352 | -void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) | |
354 | +static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) | |
353 | 355 | { |
354 | 356 | struct armada_vbl_event *e, *n; |
355 | 357 | void __iomem *base = dcrtc->base; |
... | ... | @@ -410,6 +412,27 @@ |
410 | 412 | } |
411 | 413 | } |
412 | 414 | |
415 | +static irqreturn_t armada_drm_irq(int irq, void *arg) | |
416 | +{ | |
417 | + struct armada_crtc *dcrtc = arg; | |
418 | + u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); | |
419 | + | |
420 | + /* | |
421 | + * This is rediculous - rather than writing bits to clear, we | |
422 | + * have to set the actual status register value. This is racy. | |
423 | + */ | |
424 | + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); | |
425 | + | |
426 | + /* Mask out those interrupts we haven't enabled */ | |
427 | + v = stat & dcrtc->irq_ena; | |
428 | + | |
429 | + if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { | |
430 | + armada_drm_crtc_irq(dcrtc, stat); | |
431 | + return IRQ_HANDLED; | |
432 | + } | |
433 | + return IRQ_NONE; | |
434 | +} | |
435 | + | |
413 | 436 | /* These are locked by dev->vbl_lock */ |
414 | 437 | void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) |
415 | 438 | { |
... | ... | @@ -470,7 +493,6 @@ |
470 | 493 | struct drm_display_mode *mode, struct drm_display_mode *adj, |
471 | 494 | int x, int y, struct drm_framebuffer *old_fb) |
472 | 495 | { |
473 | - struct armada_private *priv = crtc->dev->dev_private; | |
474 | 496 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
475 | 497 | struct armada_regs regs[17]; |
476 | 498 | uint32_t lm, rm, tm, bm, val, sclk; |
... | ... | @@ -515,7 +537,7 @@ |
515 | 537 | } |
516 | 538 | |
517 | 539 | /* Now compute the divider for real */ |
518 | - priv->variant->crtc_compute_clock(dcrtc, adj, &sclk); | |
540 | + dcrtc->variant->compute_clock(dcrtc, adj, &sclk); | |
519 | 541 | |
520 | 542 | /* Ensure graphic fifo is enabled */ |
521 | 543 | armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); |
... | ... | @@ -537,7 +559,7 @@ |
537 | 559 | dcrtc->v[1].spu_v_porch = tm << 16 | bm; |
538 | 560 | val = adj->crtc_hsync_start; |
539 | 561 | dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | |
540 | - priv->variant->spu_adv_reg; | |
562 | + dcrtc->variant->spu_adv_reg; | |
541 | 563 | |
542 | 564 | if (interlaced) { |
543 | 565 | /* Odd interlaced frame */ |
... | ... | @@ -546,7 +568,7 @@ |
546 | 568 | dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; |
547 | 569 | val = adj->crtc_hsync_start - adj->crtc_htotal / 2; |
548 | 570 | dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | |
549 | - priv->variant->spu_adv_reg; | |
571 | + dcrtc->variant->spu_adv_reg; | |
550 | 572 | } else { |
551 | 573 | dcrtc->v[0] = dcrtc->v[1]; |
552 | 574 | } |
... | ... | @@ -561,7 +583,7 @@ |
561 | 583 | armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, |
562 | 584 | LCD_SPUT_V_H_TOTAL); |
563 | 585 | |
564 | - if (priv->variant->has_spu_adv_reg) { | |
586 | + if (dcrtc->variant->has_spu_adv_reg) { | |
565 | 587 | armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, |
566 | 588 | ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | |
567 | 589 | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); |
568 | 590 | |
... | ... | @@ -805,12 +827,11 @@ |
805 | 827 | { |
806 | 828 | struct drm_device *dev = crtc->dev; |
807 | 829 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
808 | - struct armada_private *priv = crtc->dev->dev_private; | |
809 | 830 | struct armada_gem_object *obj = NULL; |
810 | 831 | int ret; |
811 | 832 | |
812 | 833 | /* If no cursor support, replicate drm's return value */ |
813 | - if (!priv->variant->has_spu_adv_reg) | |
834 | + if (!dcrtc->variant->has_spu_adv_reg) | |
814 | 835 | return -ENXIO; |
815 | 836 | |
816 | 837 | if (handle && w > 0 && h > 0) { |
817 | 838 | |
... | ... | @@ -858,11 +879,10 @@ |
858 | 879 | { |
859 | 880 | struct drm_device *dev = crtc->dev; |
860 | 881 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
861 | - struct armada_private *priv = crtc->dev->dev_private; | |
862 | 882 | int ret; |
863 | 883 | |
864 | 884 | /* If no cursor support, replicate drm's return value */ |
865 | - if (!priv->variant->has_spu_adv_reg) | |
885 | + if (!dcrtc->variant->has_spu_adv_reg) | |
866 | 886 | return -EFAULT; |
867 | 887 | |
868 | 888 | mutex_lock(&dev->struct_mutex); |
... | ... | @@ -888,6 +908,10 @@ |
888 | 908 | if (!IS_ERR(dcrtc->clk)) |
889 | 909 | clk_disable_unprepare(dcrtc->clk); |
890 | 910 | |
911 | + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); | |
912 | + | |
913 | + of_node_put(dcrtc->crtc.port); | |
914 | + | |
891 | 915 | kfree(dcrtc); |
892 | 916 | } |
893 | 917 | |
894 | 918 | |
895 | 919 | |
896 | 920 | |
... | ... | @@ -1027,19 +1051,20 @@ |
1027 | 1051 | return 0; |
1028 | 1052 | } |
1029 | 1053 | |
1030 | -int armada_drm_crtc_create(struct drm_device *dev, unsigned num, | |
1031 | - struct resource *res) | |
1054 | +int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, | |
1055 | + struct resource *res, int irq, const struct armada_variant *variant, | |
1056 | + struct device_node *port) | |
1032 | 1057 | { |
1033 | - struct armada_private *priv = dev->dev_private; | |
1058 | + struct armada_private *priv = drm->dev_private; | |
1034 | 1059 | struct armada_crtc *dcrtc; |
1035 | 1060 | void __iomem *base; |
1036 | 1061 | int ret; |
1037 | 1062 | |
1038 | - ret = armada_drm_crtc_create_properties(dev); | |
1063 | + ret = armada_drm_crtc_create_properties(drm); | |
1039 | 1064 | if (ret) |
1040 | 1065 | return ret; |
1041 | 1066 | |
1042 | - base = devm_request_and_ioremap(dev->dev, res); | |
1067 | + base = devm_request_and_ioremap(dev, res); | |
1043 | 1068 | if (!base) { |
1044 | 1069 | DRM_ERROR("failed to ioremap register\n"); |
1045 | 1070 | return -ENOMEM; |
1046 | 1071 | |
... | ... | @@ -1051,8 +1076,12 @@ |
1051 | 1076 | return -ENOMEM; |
1052 | 1077 | } |
1053 | 1078 | |
1079 | + if (dev != drm->dev) | |
1080 | + dev_set_drvdata(dev, dcrtc); | |
1081 | + | |
1082 | + dcrtc->variant = variant; | |
1054 | 1083 | dcrtc->base = base; |
1055 | - dcrtc->num = num; | |
1084 | + dcrtc->num = drm->mode_config.num_crtc; | |
1056 | 1085 | dcrtc->clk = ERR_PTR(-EINVAL); |
1057 | 1086 | dcrtc->csc_yuv_mode = CSC_AUTO; |
1058 | 1087 | dcrtc->csc_rgb_mode = CSC_AUTO; |
1059 | 1088 | |
... | ... | @@ -1074,9 +1103,18 @@ |
1074 | 1103 | CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); |
1075 | 1104 | writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); |
1076 | 1105 | writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN); |
1106 | + writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); | |
1107 | + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); | |
1077 | 1108 | |
1078 | - if (priv->variant->crtc_init) { | |
1079 | - ret = priv->variant->crtc_init(dcrtc); | |
1109 | + ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc", | |
1110 | + dcrtc); | |
1111 | + if (ret < 0) { | |
1112 | + kfree(dcrtc); | |
1113 | + return ret; | |
1114 | + } | |
1115 | + | |
1116 | + if (dcrtc->variant->init) { | |
1117 | + ret = dcrtc->variant->init(dcrtc, dev); | |
1080 | 1118 | if (ret) { |
1081 | 1119 | kfree(dcrtc); |
1082 | 1120 | return ret; |
... | ... | @@ -1088,7 +1126,8 @@ |
1088 | 1126 | |
1089 | 1127 | priv->dcrtc[dcrtc->num] = dcrtc; |
1090 | 1128 | |
1091 | - drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs); | |
1129 | + dcrtc->crtc.port = port; | |
1130 | + drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs); | |
1092 | 1131 | drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); |
1093 | 1132 | |
1094 | 1133 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, |
1095 | 1134 | |
... | ... | @@ -1096,6 +1135,108 @@ |
1096 | 1135 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, |
1097 | 1136 | dcrtc->csc_rgb_mode); |
1098 | 1137 | |
1099 | - return armada_overlay_plane_create(dev, 1 << dcrtc->num); | |
1138 | + return armada_overlay_plane_create(drm, 1 << dcrtc->num); | |
1100 | 1139 | } |
1140 | + | |
1141 | +static int | |
1142 | +armada_lcd_bind(struct device *dev, struct device *master, void *data) | |
1143 | +{ | |
1144 | + struct platform_device *pdev = to_platform_device(dev); | |
1145 | + struct drm_device *drm = data; | |
1146 | + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
1147 | + int irq = platform_get_irq(pdev, 0); | |
1148 | + const struct armada_variant *variant; | |
1149 | + struct device_node *port = NULL; | |
1150 | + | |
1151 | + if (irq < 0) | |
1152 | + return irq; | |
1153 | + | |
1154 | + if (!dev->of_node) { | |
1155 | + const struct platform_device_id *id; | |
1156 | + | |
1157 | + id = platform_get_device_id(pdev); | |
1158 | + if (!id) | |
1159 | + return -ENXIO; | |
1160 | + | |
1161 | + variant = (const struct armada_variant *)id->driver_data; | |
1162 | + } else { | |
1163 | + const struct of_device_id *match; | |
1164 | + struct device_node *np, *parent = dev->of_node; | |
1165 | + | |
1166 | + match = of_match_device(dev->driver->of_match_table, dev); | |
1167 | + if (!match) | |
1168 | + return -ENXIO; | |
1169 | + | |
1170 | + np = of_get_child_by_name(parent, "ports"); | |
1171 | + if (np) | |
1172 | + parent = np; | |
1173 | + port = of_get_child_by_name(parent, "port"); | |
1174 | + of_node_put(np); | |
1175 | + if (!port) { | |
1176 | + dev_err(dev, "no port node found in %s\n", | |
1177 | + parent->full_name); | |
1178 | + return -ENXIO; | |
1179 | + } | |
1180 | + | |
1181 | + variant = match->data; | |
1182 | + } | |
1183 | + | |
1184 | + return armada_drm_crtc_create(drm, dev, res, irq, variant, port); | |
1185 | +} | |
1186 | + | |
1187 | +static void | |
1188 | +armada_lcd_unbind(struct device *dev, struct device *master, void *data) | |
1189 | +{ | |
1190 | + struct armada_crtc *dcrtc = dev_get_drvdata(dev); | |
1191 | + | |
1192 | + armada_drm_crtc_destroy(&dcrtc->crtc); | |
1193 | +} | |
1194 | + | |
1195 | +static const struct component_ops armada_lcd_ops = { | |
1196 | + .bind = armada_lcd_bind, | |
1197 | + .unbind = armada_lcd_unbind, | |
1198 | +}; | |
1199 | + | |
1200 | +static int armada_lcd_probe(struct platform_device *pdev) | |
1201 | +{ | |
1202 | + return component_add(&pdev->dev, &armada_lcd_ops); | |
1203 | +} | |
1204 | + | |
1205 | +static int armada_lcd_remove(struct platform_device *pdev) | |
1206 | +{ | |
1207 | + component_del(&pdev->dev, &armada_lcd_ops); | |
1208 | + return 0; | |
1209 | +} | |
1210 | + | |
1211 | +static struct of_device_id armada_lcd_of_match[] = { | |
1212 | + { | |
1213 | + .compatible = "marvell,dove-lcd", | |
1214 | + .data = &armada510_ops, | |
1215 | + }, | |
1216 | + {} | |
1217 | +}; | |
1218 | +MODULE_DEVICE_TABLE(of, armada_lcd_of_match); | |
1219 | + | |
1220 | +static const struct platform_device_id armada_lcd_platform_ids[] = { | |
1221 | + { | |
1222 | + .name = "armada-lcd", | |
1223 | + .driver_data = (unsigned long)&armada510_ops, | |
1224 | + }, { | |
1225 | + .name = "armada-510-lcd", | |
1226 | + .driver_data = (unsigned long)&armada510_ops, | |
1227 | + }, | |
1228 | + { }, | |
1229 | +}; | |
1230 | +MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids); | |
1231 | + | |
1232 | +struct platform_driver armada_lcd_platform_driver = { | |
1233 | + .probe = armada_lcd_probe, | |
1234 | + .remove = armada_lcd_remove, | |
1235 | + .driver = { | |
1236 | + .name = "armada-lcd", | |
1237 | + .owner = THIS_MODULE, | |
1238 | + .of_match_table = armada_lcd_of_match, | |
1239 | + }, | |
1240 | + .id_table = armada_lcd_platform_ids, | |
1241 | +}; |
drivers/gpu/drm/armada/armada_crtc.h
... | ... | @@ -32,12 +32,15 @@ |
32 | 32 | armada_reg_queue_mod(_r, _i, 0, 0, ~0) |
33 | 33 | |
34 | 34 | struct armada_frame_work; |
35 | +struct armada_variant; | |
35 | 36 | |
36 | 37 | struct armada_crtc { |
37 | 38 | struct drm_crtc crtc; |
39 | + const struct armada_variant *variant; | |
38 | 40 | unsigned num; |
39 | 41 | void __iomem *base; |
40 | 42 | struct clk *clk; |
43 | + struct clk *extclk[2]; | |
41 | 44 | struct { |
42 | 45 | uint32_t spu_v_h_total; |
43 | 46 | uint32_t spu_v_porch; |
44 | 47 | |
45 | 48 | |
... | ... | @@ -72,13 +75,17 @@ |
72 | 75 | }; |
73 | 76 | #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) |
74 | 77 | |
75 | -int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *); | |
78 | +struct device_node; | |
79 | +int armada_drm_crtc_create(struct drm_device *, struct device *, | |
80 | + struct resource *, int, const struct armada_variant *, | |
81 | + struct device_node *); | |
76 | 82 | void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); |
77 | 83 | void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); |
78 | -void armada_drm_crtc_irq(struct armada_crtc *, u32); | |
79 | 84 | void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); |
80 | 85 | void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); |
81 | 86 | void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); |
87 | + | |
88 | +extern struct platform_driver armada_lcd_platform_driver; | |
82 | 89 | |
83 | 90 | #endif |
drivers/gpu/drm/armada/armada_drm.h
... | ... | @@ -59,26 +59,23 @@ |
59 | 59 | struct armada_private; |
60 | 60 | |
61 | 61 | struct armada_variant { |
62 | - bool has_spu_adv_reg; | |
62 | + bool has_spu_adv_reg; | |
63 | 63 | uint32_t spu_adv_reg; |
64 | - int (*init)(struct armada_private *, struct device *); | |
65 | - int (*crtc_init)(struct armada_crtc *); | |
66 | - int (*crtc_compute_clock)(struct armada_crtc *, | |
67 | - const struct drm_display_mode *, | |
68 | - uint32_t *); | |
64 | + int (*init)(struct armada_crtc *, struct device *); | |
65 | + int (*compute_clock)(struct armada_crtc *, | |
66 | + const struct drm_display_mode *, | |
67 | + uint32_t *); | |
69 | 68 | }; |
70 | 69 | |
71 | 70 | /* Variant ops */ |
72 | 71 | extern const struct armada_variant armada510_ops; |
73 | 72 | |
74 | 73 | struct armada_private { |
75 | - const struct armada_variant *variant; | |
76 | 74 | struct work_struct fb_unref_work; |
77 | 75 | DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); |
78 | 76 | struct drm_fb_helper *fbdev; |
79 | 77 | struct armada_crtc *dcrtc[2]; |
80 | 78 | struct drm_mm linear; |
81 | - struct clk *extclk[2]; | |
82 | 79 | struct drm_property *csc_yuv_prop; |
83 | 80 | struct drm_property *csc_rgb_prop; |
84 | 81 | struct drm_property *colorkey_prop; |
drivers/gpu/drm/armada/armada_drv.c
... | ... | @@ -6,7 +6,9 @@ |
6 | 6 | * published by the Free Software Foundation. |
7 | 7 | */ |
8 | 8 | #include <linux/clk.h> |
9 | +#include <linux/component.h> | |
9 | 10 | #include <linux/module.h> |
11 | +#include <linux/of_graph.h> | |
10 | 12 | #include <drm/drmP.h> |
11 | 13 | #include <drm/drm_crtc_helper.h> |
12 | 14 | #include "armada_crtc.h" |
... | ... | @@ -52,6 +54,11 @@ |
52 | 54 | }; |
53 | 55 | #endif |
54 | 56 | |
57 | +static bool is_componentized(struct device *dev) | |
58 | +{ | |
59 | + return dev->of_node || dev->platform_data; | |
60 | +} | |
61 | + | |
55 | 62 | static void armada_drm_unref_work(struct work_struct *work) |
56 | 63 | { |
57 | 64 | struct armada_private *priv = |
... | ... | @@ -85,6 +92,7 @@ |
85 | 92 | static int armada_drm_load(struct drm_device *dev, unsigned long flags) |
86 | 93 | { |
87 | 94 | const struct platform_device_id *id; |
95 | + const struct armada_variant *variant; | |
88 | 96 | struct armada_private *priv; |
89 | 97 | struct resource *res[ARRAY_SIZE(priv->dcrtc)]; |
90 | 98 | struct resource *mem = NULL; |
... | ... | @@ -107,7 +115,7 @@ |
107 | 115 | return -EINVAL; |
108 | 116 | } |
109 | 117 | |
110 | - if (!res[0] || !mem) | |
118 | + if (!mem) | |
111 | 119 | return -ENXIO; |
112 | 120 | |
113 | 121 | if (!devm_request_mem_region(dev->dev, mem->start, |
114 | 122 | |
... | ... | @@ -128,12 +136,8 @@ |
128 | 136 | if (!id) |
129 | 137 | return -ENXIO; |
130 | 138 | |
131 | - priv->variant = (struct armada_variant *)id->driver_data; | |
139 | + variant = (const struct armada_variant *)id->driver_data; | |
132 | 140 | |
133 | - ret = priv->variant->init(priv, dev->dev); | |
134 | - if (ret) | |
135 | - return ret; | |
136 | - | |
137 | 141 | INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); |
138 | 142 | INIT_KFIFO(priv->fb_unref); |
139 | 143 | |
140 | 144 | |
141 | 145 | |
142 | 146 | |
143 | 147 | |
144 | 148 | |
145 | 149 | |
146 | 150 | |
147 | 151 | |
148 | 152 | |
... | ... | @@ -155,40 +159,50 @@ |
155 | 159 | |
156 | 160 | /* Create all LCD controllers */ |
157 | 161 | for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { |
162 | + int irq; | |
163 | + | |
158 | 164 | if (!res[n]) |
159 | 165 | break; |
160 | 166 | |
161 | - ret = armada_drm_crtc_create(dev, n, res[n]); | |
167 | + irq = platform_get_irq(dev->platformdev, n); | |
168 | + if (irq < 0) | |
169 | + goto err_kms; | |
170 | + | |
171 | + ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq, | |
172 | + variant, NULL); | |
162 | 173 | if (ret) |
163 | 174 | goto err_kms; |
164 | 175 | } |
165 | 176 | |
177 | + if (is_componentized(dev->dev)) { | |
178 | + ret = component_bind_all(dev->dev, dev); | |
179 | + if (ret) | |
180 | + goto err_kms; | |
181 | + } else { | |
166 | 182 | #ifdef CONFIG_DRM_ARMADA_TDA1998X |
167 | - ret = armada_drm_connector_slave_create(dev, &tda19988_config); | |
168 | - if (ret) | |
169 | - goto err_kms; | |
183 | + ret = armada_drm_connector_slave_create(dev, &tda19988_config); | |
184 | + if (ret) | |
185 | + goto err_kms; | |
170 | 186 | #endif |
187 | + } | |
171 | 188 | |
172 | - ret = drm_vblank_init(dev, n); | |
189 | + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); | |
173 | 190 | if (ret) |
174 | - goto err_kms; | |
191 | + goto err_comp; | |
175 | 192 | |
176 | - ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); | |
177 | - if (ret) | |
178 | - goto err_kms; | |
179 | - | |
180 | 193 | dev->vblank_disable_allowed = 1; |
181 | 194 | |
182 | 195 | ret = armada_fbdev_init(dev); |
183 | 196 | if (ret) |
184 | - goto err_irq; | |
197 | + goto err_comp; | |
185 | 198 | |
186 | 199 | drm_kms_helper_poll_init(dev); |
187 | 200 | |
188 | 201 | return 0; |
189 | 202 | |
190 | - err_irq: | |
191 | - drm_irq_uninstall(dev); | |
203 | + err_comp: | |
204 | + if (is_componentized(dev->dev)) | |
205 | + component_unbind_all(dev->dev, dev); | |
192 | 206 | err_kms: |
193 | 207 | drm_mode_config_cleanup(dev); |
194 | 208 | drm_mm_takedown(&priv->linear); |
... | ... | @@ -203,7 +217,10 @@ |
203 | 217 | |
204 | 218 | drm_kms_helper_poll_fini(dev); |
205 | 219 | armada_fbdev_fini(dev); |
206 | - drm_irq_uninstall(dev); | |
220 | + | |
221 | + if (is_componentized(dev->dev)) | |
222 | + component_unbind_all(dev->dev, dev); | |
223 | + | |
207 | 224 | drm_mode_config_cleanup(dev); |
208 | 225 | drm_mm_takedown(&priv->linear); |
209 | 226 | flush_work(&priv->fb_unref_work); |
... | ... | @@ -259,52 +276,6 @@ |
259 | 276 | armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA); |
260 | 277 | } |
261 | 278 | |
262 | -static irqreturn_t armada_drm_irq_handler(int irq, void *arg) | |
263 | -{ | |
264 | - struct drm_device *dev = arg; | |
265 | - struct armada_private *priv = dev->dev_private; | |
266 | - struct armada_crtc *dcrtc = priv->dcrtc[0]; | |
267 | - uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); | |
268 | - irqreturn_t handled = IRQ_NONE; | |
269 | - | |
270 | - /* | |
271 | - * This is rediculous - rather than writing bits to clear, we | |
272 | - * have to set the actual status register value. This is racy. | |
273 | - */ | |
274 | - writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); | |
275 | - | |
276 | - /* Mask out those interrupts we haven't enabled */ | |
277 | - v = stat & dcrtc->irq_ena; | |
278 | - | |
279 | - if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { | |
280 | - armada_drm_crtc_irq(dcrtc, stat); | |
281 | - handled = IRQ_HANDLED; | |
282 | - } | |
283 | - | |
284 | - return handled; | |
285 | -} | |
286 | - | |
287 | -static int armada_drm_irq_postinstall(struct drm_device *dev) | |
288 | -{ | |
289 | - struct armada_private *priv = dev->dev_private; | |
290 | - struct armada_crtc *dcrtc = priv->dcrtc[0]; | |
291 | - | |
292 | - spin_lock_irq(&dev->vbl_lock); | |
293 | - writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); | |
294 | - writel(0, dcrtc->base + LCD_SPU_IRQ_ISR); | |
295 | - spin_unlock_irq(&dev->vbl_lock); | |
296 | - | |
297 | - return 0; | |
298 | -} | |
299 | - | |
300 | -static void armada_drm_irq_uninstall(struct drm_device *dev) | |
301 | -{ | |
302 | - struct armada_private *priv = dev->dev_private; | |
303 | - struct armada_crtc *dcrtc = priv->dcrtc[0]; | |
304 | - | |
305 | - writel(0, dcrtc->base + LCD_SPU_IRQ_ENA); | |
306 | -} | |
307 | - | |
308 | 279 | static struct drm_ioctl_desc armada_ioctls[] = { |
309 | 280 | DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl, |
310 | 281 | DRM_UNLOCKED), |
... | ... | @@ -340,9 +311,6 @@ |
340 | 311 | .get_vblank_counter = drm_vblank_count, |
341 | 312 | .enable_vblank = armada_drm_enable_vblank, |
342 | 313 | .disable_vblank = armada_drm_disable_vblank, |
343 | - .irq_handler = armada_drm_irq_handler, | |
344 | - .irq_postinstall = armada_drm_irq_postinstall, | |
345 | - .irq_uninstall = armada_drm_irq_uninstall, | |
346 | 314 | #ifdef CONFIG_DEBUG_FS |
347 | 315 | .debugfs_init = armada_drm_debugfs_init, |
348 | 316 | .debugfs_cleanup = armada_drm_debugfs_cleanup, |
349 | 317 | |
350 | 318 | |
351 | 319 | |
... | ... | @@ -362,19 +330,140 @@ |
362 | 330 | .desc = "Armada SoC DRM", |
363 | 331 | .date = "20120730", |
364 | 332 | .driver_features = DRIVER_GEM | DRIVER_MODESET | |
365 | - DRIVER_HAVE_IRQ | DRIVER_PRIME, | |
333 | + DRIVER_PRIME, | |
366 | 334 | .ioctls = armada_ioctls, |
367 | 335 | .fops = &armada_drm_fops, |
368 | 336 | }; |
369 | 337 | |
338 | +static int armada_drm_bind(struct device *dev) | |
339 | +{ | |
340 | + return drm_platform_init(&armada_drm_driver, to_platform_device(dev)); | |
341 | +} | |
342 | + | |
343 | +static void armada_drm_unbind(struct device *dev) | |
344 | +{ | |
345 | + drm_put_dev(dev_get_drvdata(dev)); | |
346 | +} | |
347 | + | |
348 | +static int compare_of(struct device *dev, void *data) | |
349 | +{ | |
350 | + return dev->of_node == data; | |
351 | +} | |
352 | + | |
353 | +static int compare_dev_name(struct device *dev, void *data) | |
354 | +{ | |
355 | + const char *name = data; | |
356 | + return !strcmp(dev_name(dev), name); | |
357 | +} | |
358 | + | |
359 | +static void armada_add_endpoints(struct device *dev, | |
360 | + struct component_match **match, struct device_node *port) | |
361 | +{ | |
362 | + struct device_node *ep, *remote; | |
363 | + | |
364 | + for_each_child_of_node(port, ep) { | |
365 | + remote = of_graph_get_remote_port_parent(ep); | |
366 | + if (!remote || !of_device_is_available(remote)) { | |
367 | + of_node_put(remote); | |
368 | + continue; | |
369 | + } else if (!of_device_is_available(remote->parent)) { | |
370 | + dev_warn(dev, "parent device of %s is not available\n", | |
371 | + remote->full_name); | |
372 | + of_node_put(remote); | |
373 | + continue; | |
374 | + } | |
375 | + | |
376 | + component_match_add(dev, match, compare_of, remote); | |
377 | + of_node_put(remote); | |
378 | + } | |
379 | +} | |
380 | + | |
381 | +static int armada_drm_find_components(struct device *dev, | |
382 | + struct component_match **match) | |
383 | +{ | |
384 | + struct device_node *port; | |
385 | + int i; | |
386 | + | |
387 | + if (dev->of_node) { | |
388 | + struct device_node *np = dev->of_node; | |
389 | + | |
390 | + for (i = 0; ; i++) { | |
391 | + port = of_parse_phandle(np, "ports", i); | |
392 | + if (!port) | |
393 | + break; | |
394 | + | |
395 | + component_match_add(dev, match, compare_of, port); | |
396 | + of_node_put(port); | |
397 | + } | |
398 | + | |
399 | + if (i == 0) { | |
400 | + dev_err(dev, "missing 'ports' property\n"); | |
401 | + return -ENODEV; | |
402 | + } | |
403 | + | |
404 | + for (i = 0; ; i++) { | |
405 | + port = of_parse_phandle(np, "ports", i); | |
406 | + if (!port) | |
407 | + break; | |
408 | + | |
409 | + armada_add_endpoints(dev, match, port); | |
410 | + of_node_put(port); | |
411 | + } | |
412 | + } else if (dev->platform_data) { | |
413 | + char **devices = dev->platform_data; | |
414 | + struct device *d; | |
415 | + | |
416 | + for (i = 0; devices[i]; i++) | |
417 | + component_match_add(dev, match, compare_dev_name, | |
418 | + devices[i]); | |
419 | + | |
420 | + if (i == 0) { | |
421 | + dev_err(dev, "missing 'ports' property\n"); | |
422 | + return -ENODEV; | |
423 | + } | |
424 | + | |
425 | + for (i = 0; devices[i]; i++) { | |
426 | + d = bus_find_device_by_name(&platform_bus_type, NULL, | |
427 | + devices[i]); | |
428 | + if (d && d->of_node) { | |
429 | + for_each_child_of_node(d->of_node, port) | |
430 | + armada_add_endpoints(dev, match, port); | |
431 | + } | |
432 | + put_device(d); | |
433 | + } | |
434 | + } | |
435 | + | |
436 | + return 0; | |
437 | +} | |
438 | + | |
439 | +static const struct component_master_ops armada_master_ops = { | |
440 | + .bind = armada_drm_bind, | |
441 | + .unbind = armada_drm_unbind, | |
442 | +}; | |
443 | + | |
370 | 444 | static int armada_drm_probe(struct platform_device *pdev) |
371 | 445 | { |
372 | - return drm_platform_init(&armada_drm_driver, pdev); | |
446 | + if (is_componentized(&pdev->dev)) { | |
447 | + struct component_match *match = NULL; | |
448 | + int ret; | |
449 | + | |
450 | + ret = armada_drm_find_components(&pdev->dev, &match); | |
451 | + if (ret < 0) | |
452 | + return ret; | |
453 | + | |
454 | + return component_master_add_with_match(&pdev->dev, | |
455 | + &armada_master_ops, match); | |
456 | + } else { | |
457 | + return drm_platform_init(&armada_drm_driver, pdev); | |
458 | + } | |
373 | 459 | } |
374 | 460 | |
375 | 461 | static int armada_drm_remove(struct platform_device *pdev) |
376 | 462 | { |
377 | - drm_put_dev(platform_get_drvdata(pdev)); | |
463 | + if (is_componentized(&pdev->dev)) | |
464 | + component_master_del(&pdev->dev, &armada_master_ops); | |
465 | + else | |
466 | + drm_put_dev(platform_get_drvdata(pdev)); | |
378 | 467 | return 0; |
379 | 468 | } |
380 | 469 | |
381 | 470 | |
382 | 471 | |
... | ... | @@ -402,14 +491,24 @@ |
402 | 491 | |
403 | 492 | static int __init armada_drm_init(void) |
404 | 493 | { |
494 | + int ret; | |
495 | + | |
405 | 496 | armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls); |
406 | - return platform_driver_register(&armada_drm_platform_driver); | |
497 | + | |
498 | + ret = platform_driver_register(&armada_lcd_platform_driver); | |
499 | + if (ret) | |
500 | + return ret; | |
501 | + ret = platform_driver_register(&armada_drm_platform_driver); | |
502 | + if (ret) | |
503 | + platform_driver_unregister(&armada_lcd_platform_driver); | |
504 | + return ret; | |
407 | 505 | } |
408 | 506 | module_init(armada_drm_init); |
409 | 507 | |
410 | 508 | static void __exit armada_drm_exit(void) |
411 | 509 | { |
412 | 510 | platform_driver_unregister(&armada_drm_platform_driver); |
511 | + platform_driver_unregister(&armada_lcd_platform_driver); | |
413 | 512 | } |
414 | 513 | module_exit(armada_drm_exit); |
415 | 514 |
drivers/gpu/drm/drm_of.c
1 | +#include <linux/export.h> | |
2 | +#include <linux/list.h> | |
3 | +#include <linux/of_graph.h> | |
4 | +#include <drm/drmP.h> | |
5 | +#include <drm/drm_crtc.h> | |
6 | +#include <drm/drm_of.h> | |
7 | + | |
8 | +/** | |
9 | + * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node | |
10 | + * @dev: DRM device | |
11 | + * @port: port OF node | |
12 | + * | |
13 | + * Given a port OF node, return the possible mask of the corresponding | |
14 | + * CRTC within a device's list of CRTCs. Returns zero if not found. | |
15 | + */ | |
16 | +static uint32_t drm_crtc_port_mask(struct drm_device *dev, | |
17 | + struct device_node *port) | |
18 | +{ | |
19 | + unsigned int index = 0; | |
20 | + struct drm_crtc *tmp; | |
21 | + | |
22 | + list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { | |
23 | + if (tmp->port == port) | |
24 | + return 1 << index; | |
25 | + | |
26 | + index++; | |
27 | + } | |
28 | + | |
29 | + return 0; | |
30 | +} | |
31 | + | |
32 | +/** | |
33 | + * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port | |
34 | + * @dev: DRM device | |
35 | + * @port: encoder port to scan for endpoints | |
36 | + * | |
37 | + * Scan all endpoints attached to a port, locate their attached CRTCs, | |
38 | + * and generate the DRM mask of CRTCs which may be attached to this | |
39 | + * encoder. | |
40 | + * | |
41 | + * See Documentation/devicetree/bindings/graph.txt for the bindings. | |
42 | + */ | |
43 | +uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, | |
44 | + struct device_node *port) | |
45 | +{ | |
46 | + struct device_node *remote_port, *ep = NULL; | |
47 | + uint32_t possible_crtcs = 0; | |
48 | + | |
49 | + do { | |
50 | + ep = of_graph_get_next_endpoint(port, ep); | |
51 | + if (!ep) | |
52 | + break; | |
53 | + | |
54 | + remote_port = of_graph_get_remote_port(ep); | |
55 | + if (!remote_port) { | |
56 | + of_node_put(ep); | |
57 | + return 0; | |
58 | + } | |
59 | + | |
60 | + possible_crtcs |= drm_crtc_port_mask(dev, remote_port); | |
61 | + | |
62 | + of_node_put(remote_port); | |
63 | + } while (1); | |
64 | + | |
65 | + return possible_crtcs; | |
66 | +} | |
67 | +EXPORT_SYMBOL(drm_of_find_possible_crtcs); |
include/drm/drm_crtc.h
... | ... | @@ -41,6 +41,7 @@ |
41 | 41 | struct drm_object_properties; |
42 | 42 | struct drm_file; |
43 | 43 | struct drm_clip_rect; |
44 | +struct device_node; | |
44 | 45 | |
45 | 46 | #define DRM_MODE_OBJECT_CRTC 0xcccccccc |
46 | 47 | #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 |
... | ... | @@ -314,6 +315,7 @@ |
314 | 315 | */ |
315 | 316 | struct drm_crtc { |
316 | 317 | struct drm_device *dev; |
318 | + struct device_node *port; | |
317 | 319 | struct list_head head; |
318 | 320 | |
319 | 321 | /** |
include/drm/drm_of.h
1 | +#ifndef __DRM_OF_H__ | |
2 | +#define __DRM_OF_H__ | |
3 | + | |
4 | +struct drm_device; | |
5 | +struct device_node; | |
6 | + | |
7 | +#ifdef CONFIG_OF | |
8 | +extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, | |
9 | + struct device_node *port); | |
10 | +#else | |
11 | +static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, | |
12 | + struct device_node *port) | |
13 | +{ | |
14 | + return 0; | |
15 | +} | |
16 | +#endif | |
17 | + | |
18 | +#endif /* __DRM_OF_H__ */ |
include/linux/component.h
... | ... | @@ -29,5 +29,12 @@ |
29 | 29 | int component_master_add_child(struct master *master, |
30 | 30 | int (*compare)(struct device *, void *), void *compare_data); |
31 | 31 | |
32 | +struct component_match; | |
33 | + | |
34 | +int component_master_add_with_match(struct device *, | |
35 | + const struct component_master_ops *, struct component_match *); | |
36 | +void component_match_add(struct device *, struct component_match **, | |
37 | + int (*compare)(struct device *, void *), void *compare_data); | |
38 | + | |
32 | 39 | #endif |