diff options
-rw-r--r-- | Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt | 30 | ||||
-rw-r--r-- | drivers/base/component.c | 192 | ||||
-rw-r--r-- | drivers/gpu/drm/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_510.c | 23 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_crtc.c | 187 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_crtc.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_drm.h | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/armada/armada_drv.c | 245 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_of.c | 67 | ||||
-rw-r--r-- | include/drm/drm_crtc.h | 2 | ||||
-rw-r--r-- | include/drm/drm_of.h | 18 | ||||
-rw-r--r-- | include/linux/component.h | 7 |
12 files changed, 642 insertions, 154 deletions
diff --git a/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt b/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt new file mode 100644 index 000000000000..46525ea3e646 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt | |||
@@ -0,0 +1,30 @@ | |||
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 | }; | ||
diff --git a/drivers/base/component.c b/drivers/base/component.c index c4778995cd72..f748430bb654 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c | |||
@@ -18,6 +18,15 @@ | |||
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/slab.h> | 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 | struct master { | 30 | struct master { |
22 | struct list_head node; | 31 | struct list_head node; |
23 | struct list_head components; | 32 | struct list_head components; |
@@ -25,6 +34,7 @@ struct master { | |||
25 | 34 | ||
26 | const struct component_master_ops *ops; | 35 | const struct component_master_ops *ops; |
27 | struct device *dev; | 36 | struct device *dev; |
37 | struct component_match *match; | ||
28 | }; | 38 | }; |
29 | 39 | ||
30 | struct component { | 40 | struct component { |
@@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c) | |||
69 | c->master = NULL; | 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 | int component_master_add_child(struct master *master, | 87 | int component_master_add_child(struct master *master, |
73 | int (*compare)(struct device *, void *), void *compare_data) | 88 | int (*compare)(struct device *, void *), void *compare_data) |
74 | { | 89 | { |
@@ -76,11 +91,12 @@ int component_master_add_child(struct master *master, | |||
76 | int ret = -ENXIO; | 91 | int ret = -ENXIO; |
77 | 92 | ||
78 | list_for_each_entry(c, &component_list, node) { | 93 | list_for_each_entry(c, &component_list, node) { |
79 | if (c->master) | 94 | if (c->master && c->master != master) |
80 | continue; | 95 | continue; |
81 | 96 | ||
82 | if (compare(c->dev, compare_data)) { | 97 | if (compare(c->dev, compare_data)) { |
83 | component_attach_master(master, c); | 98 | if (!c->master) |
99 | component_attach_master(master, c); | ||
84 | ret = 0; | 100 | ret = 0; |
85 | break; | 101 | break; |
86 | } | 102 | } |
@@ -90,6 +106,34 @@ int component_master_add_child(struct master *master, | |||
90 | } | 106 | } |
91 | EXPORT_SYMBOL_GPL(component_master_add_child); | 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 | /* Detach all attached components from this master */ | 137 | /* Detach all attached components from this master */ |
94 | static void master_remove_components(struct master *master) | 138 | static void master_remove_components(struct master *master) |
95 | { | 139 | { |
@@ -113,44 +157,44 @@ static void master_remove_components(struct master *master) | |||
113 | static int try_to_bring_up_master(struct master *master, | 157 | static int try_to_bring_up_master(struct master *master, |
114 | struct component *component) | 158 | struct component *component) |
115 | { | 159 | { |
116 | int ret = 0; | 160 | int ret; |
117 | 161 | ||
118 | if (!master->bound) { | 162 | if (master->bound) |
119 | /* | 163 | return 0; |
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 | } | ||
129 | 164 | ||
130 | if (component && component->master != master) { | 165 | /* |
131 | master_remove_components(master); | 166 | * Search the list of components, looking for components that |
132 | ret = 0; | 167 | * belong to this master, and attach them to the master. |
133 | goto out; | 168 | */ |
134 | } | 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)) { | 175 | if (component && component->master != master) { |
137 | ret = -ENOMEM; | 176 | ret = 0; |
138 | goto out; | 177 | goto out; |
139 | } | 178 | } |
140 | 179 | ||
141 | /* Found all components */ | 180 | if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { |
142 | ret = master->ops->bind(master->dev); | 181 | ret = -ENOMEM; |
143 | if (ret < 0) { | 182 | goto out; |
144 | devres_release_group(master->dev, NULL); | 183 | } |
145 | dev_info(master->dev, "master bind failed: %d\n", ret); | ||
146 | master_remove_components(master); | ||
147 | goto out; | ||
148 | } | ||
149 | 184 | ||
150 | master->bound = true; | 185 | /* Found all components */ |
151 | ret = 1; | 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 | out: | 196 | out: |
197 | master_remove_components(master); | ||
154 | 198 | ||
155 | return ret; | 199 | return ret; |
156 | } | 200 | } |
@@ -180,18 +224,89 @@ static void take_down_master(struct master *master) | |||
180 | master_remove_components(master); | 224 | master_remove_components(master); |
181 | } | 225 | } |
182 | 226 | ||
183 | int component_master_add(struct device *dev, | 227 | static size_t component_match_size(size_t num) |
184 | const struct component_master_ops *ops) | 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) | ||
185 | { | 289 | { |
186 | struct master *master; | 290 | struct master *master; |
187 | int ret; | 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 | master = kzalloc(sizeof(*master), GFP_KERNEL); | 303 | master = kzalloc(sizeof(*master), GFP_KERNEL); |
190 | if (!master) | 304 | if (!master) |
191 | return -ENOMEM; | 305 | return -ENOMEM; |
192 | 306 | ||
193 | master->dev = dev; | 307 | master->dev = dev; |
194 | master->ops = ops; | 308 | master->ops = ops; |
309 | master->match = match; | ||
195 | INIT_LIST_HEAD(&master->components); | 310 | INIT_LIST_HEAD(&master->components); |
196 | 311 | ||
197 | /* Add to the list of available masters. */ | 312 | /* Add to the list of available masters. */ |
@@ -209,6 +324,13 @@ int component_master_add(struct device *dev, | |||
209 | 324 | ||
210 | return ret < 0 ? ret : 0; | 325 | return ret < 0 ? ret : 0; |
211 | } | 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); | ||
333 | } | ||
212 | EXPORT_SYMBOL_GPL(component_master_add); | 334 | EXPORT_SYMBOL_GPL(component_master_add); |
213 | 335 | ||
214 | void component_master_del(struct device *dev, | 336 | void component_master_del(struct device *dev, |
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index af9a609861c2..61d9e9c6bc10 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o | |||
20 | drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o | 20 | drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o |
21 | drm-$(CONFIG_PCI) += ati_pcigart.o | 21 | drm-$(CONFIG_PCI) += ati_pcigart.o |
22 | drm-$(CONFIG_DRM_PANEL) += drm_panel.o | 22 | drm-$(CONFIG_DRM_PANEL) += drm_panel.o |
23 | drm-$(CONFIG_OF) += drm_of.o | ||
23 | 24 | ||
24 | drm-usb-y := drm_usb.o | 25 | drm-usb-y := drm_usb.o |
25 | 26 | ||
diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index 59948eff6095..ad3d2ebf95c9 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c | |||
@@ -15,20 +15,19 @@ | |||
15 | #include "armada_drm.h" | 15 | #include "armada_drm.h" |
16 | #include "armada_hw.h" | 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) | 22 | clk = devm_clk_get(dev, "ext_ref_clk1"); |
23 | priv->extclk[0] = ERR_PTR(-EPROBE_DEFER); | 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 | dcrtc->extclk[0] = clk; |
26 | } | ||
27 | 27 | ||
28 | static int armada510_crtc_init(struct armada_crtc *dcrtc) | ||
29 | { | ||
30 | /* Lower the watermark so to eliminate jitter at higher bandwidths */ | 28 | /* Lower the watermark so to eliminate jitter at higher bandwidths */ |
31 | armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); | 29 | armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); |
30 | |||
32 | return 0; | 31 | return 0; |
33 | } | 32 | } |
34 | 33 | ||
@@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc) | |||
45 | static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, | 44 | static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, |
46 | const struct drm_display_mode *mode, uint32_t *sclk) | 45 | const struct drm_display_mode *mode, uint32_t *sclk) |
47 | { | 46 | { |
48 | struct armada_private *priv = dcrtc->crtc.dev->dev_private; | 47 | struct clk *clk = dcrtc->extclk[0]; |
49 | struct clk *clk = priv->extclk[0]; | ||
50 | int ret; | 48 | int ret; |
51 | 49 | ||
52 | if (dcrtc->num == 1) | 50 | if (dcrtc->num == 1) |
@@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, | |||
81 | const struct armada_variant armada510_ops = { | 79 | const struct armada_variant armada510_ops = { |
82 | .has_spu_adv_reg = true, | 80 | .has_spu_adv_reg = true, |
83 | .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, | 81 | .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, |
84 | .init = armada510_init, | 82 | .init = armada510_crtc_init, |
85 | .crtc_init = armada510_crtc_init, | 83 | .compute_clock = armada510_crtc_compute_clock, |
86 | .crtc_compute_clock = armada510_crtc_compute_clock, | ||
87 | }; | 84 | }; |
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 81c34f949dfc..3f620e21e06b 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c | |||
@@ -7,6 +7,9 @@ | |||
7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
8 | */ | 8 | */ |
9 | #include <linux/clk.h> | 9 | #include <linux/clk.h> |
10 | #include <linux/component.h> | ||
11 | #include <linux/of_device.h> | ||
12 | #include <linux/platform_device.h> | ||
10 | #include <drm/drmP.h> | 13 | #include <drm/drmP.h> |
11 | #include <drm/drm_crtc_helper.h> | 14 | #include <drm/drm_crtc_helper.h> |
12 | #include "armada_crtc.h" | 15 | #include "armada_crtc.h" |
@@ -332,24 +335,23 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc) | |||
332 | static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, | 335 | static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, |
333 | const struct drm_display_mode *mode, struct drm_display_mode *adj) | 336 | const struct drm_display_mode *mode, struct drm_display_mode *adj) |
334 | { | 337 | { |
335 | struct armada_private *priv = crtc->dev->dev_private; | ||
336 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | 338 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
337 | int ret; | 339 | int ret; |
338 | 340 | ||
339 | /* We can't do interlaced modes if we don't have the SPU_ADV_REG */ | 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 | adj->flags & DRM_MODE_FLAG_INTERLACE) | 343 | adj->flags & DRM_MODE_FLAG_INTERLACE) |
342 | return false; | 344 | return false; |
343 | 345 | ||
344 | /* Check whether the display mode is possible */ | 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 | if (ret) | 348 | if (ret) |
347 | return false; | 349 | return false; |
348 | 350 | ||
349 | return true; | 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 | struct armada_vbl_event *e, *n; | 356 | struct armada_vbl_event *e, *n; |
355 | void __iomem *base = dcrtc->base; | 357 | void __iomem *base = dcrtc->base; |
@@ -410,6 +412,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) | |||
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 | /* These are locked by dev->vbl_lock */ | 436 | /* These are locked by dev->vbl_lock */ |
414 | void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) | 437 | void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) |
415 | { | 438 | { |
@@ -470,7 +493,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, | |||
470 | struct drm_display_mode *mode, struct drm_display_mode *adj, | 493 | struct drm_display_mode *mode, struct drm_display_mode *adj, |
471 | int x, int y, struct drm_framebuffer *old_fb) | 494 | int x, int y, struct drm_framebuffer *old_fb) |
472 | { | 495 | { |
473 | struct armada_private *priv = crtc->dev->dev_private; | ||
474 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | 496 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
475 | struct armada_regs regs[17]; | 497 | struct armada_regs regs[17]; |
476 | uint32_t lm, rm, tm, bm, val, sclk; | 498 | uint32_t lm, rm, tm, bm, val, sclk; |
@@ -515,7 +537,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, | |||
515 | } | 537 | } |
516 | 538 | ||
517 | /* Now compute the divider for real */ | 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 | /* Ensure graphic fifo is enabled */ | 542 | /* Ensure graphic fifo is enabled */ |
521 | armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); | 543 | armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); |
@@ -537,7 +559,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, | |||
537 | dcrtc->v[1].spu_v_porch = tm << 16 | bm; | 559 | dcrtc->v[1].spu_v_porch = tm << 16 | bm; |
538 | val = adj->crtc_hsync_start; | 560 | val = adj->crtc_hsync_start; |
539 | dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | | 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 | if (interlaced) { | 564 | if (interlaced) { |
543 | /* Odd interlaced frame */ | 565 | /* Odd interlaced frame */ |
@@ -546,7 +568,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, | |||
546 | dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; | 568 | dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; |
547 | val = adj->crtc_hsync_start - adj->crtc_htotal / 2; | 569 | val = adj->crtc_hsync_start - adj->crtc_htotal / 2; |
548 | dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | | 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 | } else { | 572 | } else { |
551 | dcrtc->v[0] = dcrtc->v[1]; | 573 | dcrtc->v[0] = dcrtc->v[1]; |
552 | } | 574 | } |
@@ -561,7 +583,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, | |||
561 | armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, | 583 | armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, |
562 | LCD_SPUT_V_H_TOTAL); | 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 | armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, | 587 | armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, |
566 | ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | | 588 | ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | |
567 | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); | 589 | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); |
@@ -805,12 +827,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, | |||
805 | { | 827 | { |
806 | struct drm_device *dev = crtc->dev; | 828 | struct drm_device *dev = crtc->dev; |
807 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | 829 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
808 | struct armada_private *priv = crtc->dev->dev_private; | ||
809 | struct armada_gem_object *obj = NULL; | 830 | struct armada_gem_object *obj = NULL; |
810 | int ret; | 831 | int ret; |
811 | 832 | ||
812 | /* If no cursor support, replicate drm's return value */ | 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 | return -ENXIO; | 835 | return -ENXIO; |
815 | 836 | ||
816 | if (handle && w > 0 && h > 0) { | 837 | if (handle && w > 0 && h > 0) { |
@@ -858,11 +879,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) | |||
858 | { | 879 | { |
859 | struct drm_device *dev = crtc->dev; | 880 | struct drm_device *dev = crtc->dev; |
860 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); | 881 | struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); |
861 | struct armada_private *priv = crtc->dev->dev_private; | ||
862 | int ret; | 882 | int ret; |
863 | 883 | ||
864 | /* If no cursor support, replicate drm's return value */ | 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 | return -EFAULT; | 886 | return -EFAULT; |
867 | 887 | ||
868 | mutex_lock(&dev->struct_mutex); | 888 | mutex_lock(&dev->struct_mutex); |
@@ -888,6 +908,10 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) | |||
888 | if (!IS_ERR(dcrtc->clk)) | 908 | if (!IS_ERR(dcrtc->clk)) |
889 | clk_disable_unprepare(dcrtc->clk); | 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 | kfree(dcrtc); | 915 | kfree(dcrtc); |
892 | } | 916 | } |
893 | 917 | ||
@@ -1027,19 +1051,20 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) | |||
1027 | return 0; | 1051 | return 0; |
1028 | } | 1052 | } |
1029 | 1053 | ||
1030 | int armada_drm_crtc_create(struct drm_device *dev, unsigned num, | 1054 | int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, |
1031 | struct resource *res) | 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 | struct armada_crtc *dcrtc; | 1059 | struct armada_crtc *dcrtc; |
1035 | void __iomem *base; | 1060 | void __iomem *base; |
1036 | int ret; | 1061 | int ret; |
1037 | 1062 | ||
1038 | ret = armada_drm_crtc_create_properties(dev); | 1063 | ret = armada_drm_crtc_create_properties(drm); |
1039 | if (ret) | 1064 | if (ret) |
1040 | return ret; | 1065 | return ret; |
1041 | 1066 | ||
1042 | base = devm_request_and_ioremap(dev->dev, res); | 1067 | base = devm_request_and_ioremap(dev, res); |
1043 | if (!base) { | 1068 | if (!base) { |
1044 | DRM_ERROR("failed to ioremap register\n"); | 1069 | DRM_ERROR("failed to ioremap register\n"); |
1045 | return -ENOMEM; | 1070 | return -ENOMEM; |
@@ -1051,8 +1076,12 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, | |||
1051 | return -ENOMEM; | 1076 | return -ENOMEM; |
1052 | } | 1077 | } |
1053 | 1078 | ||
1079 | if (dev != drm->dev) | ||
1080 | dev_set_drvdata(dev, dcrtc); | ||
1081 | |||
1082 | dcrtc->variant = variant; | ||
1054 | dcrtc->base = base; | 1083 | dcrtc->base = base; |
1055 | dcrtc->num = num; | 1084 | dcrtc->num = drm->mode_config.num_crtc; |
1056 | dcrtc->clk = ERR_PTR(-EINVAL); | 1085 | dcrtc->clk = ERR_PTR(-EINVAL); |
1057 | dcrtc->csc_yuv_mode = CSC_AUTO; | 1086 | dcrtc->csc_yuv_mode = CSC_AUTO; |
1058 | dcrtc->csc_rgb_mode = CSC_AUTO; | 1087 | dcrtc->csc_rgb_mode = CSC_AUTO; |
@@ -1074,9 +1103,18 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, | |||
1074 | CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); | 1103 | CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); |
1075 | writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); | 1104 | writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); |
1076 | writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN); | 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); | ||
1108 | |||
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 | } | ||
1077 | 1115 | ||
1078 | if (priv->variant->crtc_init) { | 1116 | if (dcrtc->variant->init) { |
1079 | ret = priv->variant->crtc_init(dcrtc); | 1117 | ret = dcrtc->variant->init(dcrtc, dev); |
1080 | if (ret) { | 1118 | if (ret) { |
1081 | kfree(dcrtc); | 1119 | kfree(dcrtc); |
1082 | return ret; | 1120 | return ret; |
@@ -1088,7 +1126,8 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, | |||
1088 | 1126 | ||
1089 | priv->dcrtc[dcrtc->num] = dcrtc; | 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 | drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); | 1131 | drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); |
1093 | 1132 | ||
1094 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, | 1133 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, |
@@ -1096,5 +1135,107 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, | |||
1096 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, | 1135 | drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, |
1097 | dcrtc->csc_rgb_mode); | 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); |
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); | ||
1100 | } | 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 | }; | ||
diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 9c10a07e7492..98102a5a9af5 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h | |||
@@ -32,12 +32,15 @@ struct armada_regs { | |||
32 | armada_reg_queue_mod(_r, _i, 0, 0, ~0) | 32 | armada_reg_queue_mod(_r, _i, 0, 0, ~0) |
33 | 33 | ||
34 | struct armada_frame_work; | 34 | struct armada_frame_work; |
35 | struct armada_variant; | ||
35 | 36 | ||
36 | struct armada_crtc { | 37 | struct armada_crtc { |
37 | struct drm_crtc crtc; | 38 | struct drm_crtc crtc; |
39 | const struct armada_variant *variant; | ||
38 | unsigned num; | 40 | unsigned num; |
39 | void __iomem *base; | 41 | void __iomem *base; |
40 | struct clk *clk; | 42 | struct clk *clk; |
43 | struct clk *extclk[2]; | ||
41 | struct { | 44 | struct { |
42 | uint32_t spu_v_h_total; | 45 | uint32_t spu_v_h_total; |
43 | uint32_t spu_v_porch; | 46 | uint32_t spu_v_porch; |
@@ -72,12 +75,16 @@ struct armada_crtc { | |||
72 | }; | 75 | }; |
73 | #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) | 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 | void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); | 82 | void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); |
77 | void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); | 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 | void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); | 84 | void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); |
80 | void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); | 85 | void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); |
81 | void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); | 86 | void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); |
82 | 87 | ||
88 | extern struct platform_driver armada_lcd_platform_driver; | ||
89 | |||
83 | #endif | 90 | #endif |
diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index a72cae03b99b..ea63c6c7c66f 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h | |||
@@ -59,26 +59,23 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *, | |||
59 | struct armada_private; | 59 | struct armada_private; |
60 | 60 | ||
61 | struct armada_variant { | 61 | struct armada_variant { |
62 | bool has_spu_adv_reg; | 62 | bool has_spu_adv_reg; |
63 | uint32_t spu_adv_reg; | 63 | uint32_t spu_adv_reg; |
64 | int (*init)(struct armada_private *, struct device *); | 64 | int (*init)(struct armada_crtc *, struct device *); |
65 | int (*crtc_init)(struct armada_crtc *); | 65 | int (*compute_clock)(struct armada_crtc *, |
66 | int (*crtc_compute_clock)(struct armada_crtc *, | 66 | const struct drm_display_mode *, |
67 | const struct drm_display_mode *, | 67 | uint32_t *); |
68 | uint32_t *); | ||
69 | }; | 68 | }; |
70 | 69 | ||
71 | /* Variant ops */ | 70 | /* Variant ops */ |
72 | extern const struct armada_variant armada510_ops; | 71 | extern const struct armada_variant armada510_ops; |
73 | 72 | ||
74 | struct armada_private { | 73 | struct armada_private { |
75 | const struct armada_variant *variant; | ||
76 | struct work_struct fb_unref_work; | 74 | struct work_struct fb_unref_work; |
77 | DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); | 75 | DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); |
78 | struct drm_fb_helper *fbdev; | 76 | struct drm_fb_helper *fbdev; |
79 | struct armada_crtc *dcrtc[2]; | 77 | struct armada_crtc *dcrtc[2]; |
80 | struct drm_mm linear; | 78 | struct drm_mm linear; |
81 | struct clk *extclk[2]; | ||
82 | struct drm_property *csc_yuv_prop; | 79 | struct drm_property *csc_yuv_prop; |
83 | struct drm_property *csc_rgb_prop; | 80 | struct drm_property *csc_rgb_prop; |
84 | struct drm_property *colorkey_prop; | 81 | struct drm_property *colorkey_prop; |
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 8ab3cd1a8cdb..e2d5792b140f 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c | |||
@@ -6,7 +6,9 @@ | |||
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | #include <linux/clk.h> | 8 | #include <linux/clk.h> |
9 | #include <linux/component.h> | ||
9 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/of_graph.h> | ||
10 | #include <drm/drmP.h> | 12 | #include <drm/drmP.h> |
11 | #include <drm/drm_crtc_helper.h> | 13 | #include <drm/drm_crtc_helper.h> |
12 | #include "armada_crtc.h" | 14 | #include "armada_crtc.h" |
@@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = { | |||
52 | }; | 54 | }; |
53 | #endif | 55 | #endif |
54 | 56 | ||
57 | static bool is_componentized(struct device *dev) | ||
58 | { | ||
59 | return dev->of_node || dev->platform_data; | ||
60 | } | ||
61 | |||
55 | static void armada_drm_unref_work(struct work_struct *work) | 62 | static void armada_drm_unref_work(struct work_struct *work) |
56 | { | 63 | { |
57 | struct armada_private *priv = | 64 | struct armada_private *priv = |
@@ -85,6 +92,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev, | |||
85 | static int armada_drm_load(struct drm_device *dev, unsigned long flags) | 92 | static int armada_drm_load(struct drm_device *dev, unsigned long flags) |
86 | { | 93 | { |
87 | const struct platform_device_id *id; | 94 | const struct platform_device_id *id; |
95 | const struct armada_variant *variant; | ||
88 | struct armada_private *priv; | 96 | struct armada_private *priv; |
89 | struct resource *res[ARRAY_SIZE(priv->dcrtc)]; | 97 | struct resource *res[ARRAY_SIZE(priv->dcrtc)]; |
90 | struct resource *mem = NULL; | 98 | struct resource *mem = NULL; |
@@ -107,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) | |||
107 | return -EINVAL; | 115 | return -EINVAL; |
108 | } | 116 | } |
109 | 117 | ||
110 | if (!res[0] || !mem) | 118 | if (!mem) |
111 | return -ENXIO; | 119 | return -ENXIO; |
112 | 120 | ||
113 | if (!devm_request_mem_region(dev->dev, mem->start, | 121 | if (!devm_request_mem_region(dev->dev, mem->start, |
@@ -128,11 +136,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) | |||
128 | if (!id) | 136 | if (!id) |
129 | return -ENXIO; | 137 | return -ENXIO; |
130 | 138 | ||
131 | priv->variant = (struct armada_variant *)id->driver_data; | 139 | variant = (const struct armada_variant *)id->driver_data; |
132 | |||
133 | ret = priv->variant->init(priv, dev->dev); | ||
134 | if (ret) | ||
135 | return ret; | ||
136 | 140 | ||
137 | INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); | 141 | INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); |
138 | INIT_KFIFO(priv->fb_unref); | 142 | INIT_KFIFO(priv->fb_unref); |
@@ -155,40 +159,50 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) | |||
155 | 159 | ||
156 | /* Create all LCD controllers */ | 160 | /* Create all LCD controllers */ |
157 | for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { | 161 | for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { |
162 | int irq; | ||
163 | |||
158 | if (!res[n]) | 164 | if (!res[n]) |
159 | break; | 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 | if (ret) | 173 | if (ret) |
163 | goto err_kms; | 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 | #ifdef CONFIG_DRM_ARMADA_TDA1998X | 182 | #ifdef CONFIG_DRM_ARMADA_TDA1998X |
167 | ret = armada_drm_connector_slave_create(dev, &tda19988_config); | 183 | ret = armada_drm_connector_slave_create(dev, &tda19988_config); |
168 | if (ret) | 184 | if (ret) |
169 | goto err_kms; | 185 | goto err_kms; |
170 | #endif | 186 | #endif |
187 | } | ||
171 | 188 | ||
172 | ret = drm_vblank_init(dev, n); | 189 | ret = drm_vblank_init(dev, dev->mode_config.num_crtc); |
173 | if (ret) | ||
174 | goto err_kms; | ||
175 | |||
176 | ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); | ||
177 | if (ret) | 190 | if (ret) |
178 | goto err_kms; | 191 | goto err_comp; |
179 | 192 | ||
180 | dev->vblank_disable_allowed = 1; | 193 | dev->vblank_disable_allowed = 1; |
181 | 194 | ||
182 | ret = armada_fbdev_init(dev); | 195 | ret = armada_fbdev_init(dev); |
183 | if (ret) | 196 | if (ret) |
184 | goto err_irq; | 197 | goto err_comp; |
185 | 198 | ||
186 | drm_kms_helper_poll_init(dev); | 199 | drm_kms_helper_poll_init(dev); |
187 | 200 | ||
188 | return 0; | 201 | return 0; |
189 | 202 | ||
190 | err_irq: | 203 | err_comp: |
191 | drm_irq_uninstall(dev); | 204 | if (is_componentized(dev->dev)) |
205 | component_unbind_all(dev->dev, dev); | ||
192 | err_kms: | 206 | err_kms: |
193 | drm_mode_config_cleanup(dev); | 207 | drm_mode_config_cleanup(dev); |
194 | drm_mm_takedown(&priv->linear); | 208 | drm_mm_takedown(&priv->linear); |
@@ -203,7 +217,10 @@ static int armada_drm_unload(struct drm_device *dev) | |||
203 | 217 | ||
204 | drm_kms_helper_poll_fini(dev); | 218 | drm_kms_helper_poll_fini(dev); |
205 | armada_fbdev_fini(dev); | 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 | drm_mode_config_cleanup(dev); | 224 | drm_mode_config_cleanup(dev); |
208 | drm_mm_takedown(&priv->linear); | 225 | drm_mm_takedown(&priv->linear); |
209 | flush_work(&priv->fb_unref_work); | 226 | flush_work(&priv->fb_unref_work); |
@@ -259,52 +276,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc) | |||
259 | armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA); | 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 | static struct drm_ioctl_desc armada_ioctls[] = { | 279 | static struct drm_ioctl_desc armada_ioctls[] = { |
309 | DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl, | 280 | DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl, |
310 | DRM_UNLOCKED), | 281 | DRM_UNLOCKED), |
@@ -340,9 +311,6 @@ static struct drm_driver armada_drm_driver = { | |||
340 | .get_vblank_counter = drm_vblank_count, | 311 | .get_vblank_counter = drm_vblank_count, |
341 | .enable_vblank = armada_drm_enable_vblank, | 312 | .enable_vblank = armada_drm_enable_vblank, |
342 | .disable_vblank = armada_drm_disable_vblank, | 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 | #ifdef CONFIG_DEBUG_FS | 314 | #ifdef CONFIG_DEBUG_FS |
347 | .debugfs_init = armada_drm_debugfs_init, | 315 | .debugfs_init = armada_drm_debugfs_init, |
348 | .debugfs_cleanup = armada_drm_debugfs_cleanup, | 316 | .debugfs_cleanup = armada_drm_debugfs_cleanup, |
@@ -362,19 +330,140 @@ static struct drm_driver armada_drm_driver = { | |||
362 | .desc = "Armada SoC DRM", | 330 | .desc = "Armada SoC DRM", |
363 | .date = "20120730", | 331 | .date = "20120730", |
364 | .driver_features = DRIVER_GEM | DRIVER_MODESET | | 332 | .driver_features = DRIVER_GEM | DRIVER_MODESET | |
365 | DRIVER_HAVE_IRQ | DRIVER_PRIME, | 333 | DRIVER_PRIME, |
366 | .ioctls = armada_ioctls, | 334 | .ioctls = armada_ioctls, |
367 | .fops = &armada_drm_fops, | 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 | static int armada_drm_probe(struct platform_device *pdev) | 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 | static int armada_drm_remove(struct platform_device *pdev) | 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 | return 0; | 467 | return 0; |
379 | } | 468 | } |
380 | 469 | ||
@@ -402,14 +491,24 @@ static struct platform_driver armada_drm_platform_driver = { | |||
402 | 491 | ||
403 | static int __init armada_drm_init(void) | 492 | static int __init armada_drm_init(void) |
404 | { | 493 | { |
494 | int ret; | ||
495 | |||
405 | armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls); | 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 | module_init(armada_drm_init); | 506 | module_init(armada_drm_init); |
409 | 507 | ||
410 | static void __exit armada_drm_exit(void) | 508 | static void __exit armada_drm_exit(void) |
411 | { | 509 | { |
412 | platform_driver_unregister(&armada_drm_platform_driver); | 510 | platform_driver_unregister(&armada_drm_platform_driver); |
511 | platform_driver_unregister(&armada_lcd_platform_driver); | ||
413 | } | 512 | } |
414 | module_exit(armada_drm_exit); | 513 | module_exit(armada_drm_exit); |
415 | 514 | ||
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c new file mode 100644 index 000000000000..16150a00c237 --- /dev/null +++ b/drivers/gpu/drm/drm_of.c | |||
@@ -0,0 +1,67 @@ | |||
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); | ||
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index e529b68d5037..7f1bc7e4848b 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h | |||
@@ -41,6 +41,7 @@ struct drm_framebuffer; | |||
41 | struct drm_object_properties; | 41 | struct drm_object_properties; |
42 | struct drm_file; | 42 | struct drm_file; |
43 | struct drm_clip_rect; | 43 | struct drm_clip_rect; |
44 | struct device_node; | ||
44 | 45 | ||
45 | #define DRM_MODE_OBJECT_CRTC 0xcccccccc | 46 | #define DRM_MODE_OBJECT_CRTC 0xcccccccc |
46 | #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 | 47 | #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 |
@@ -314,6 +315,7 @@ struct drm_crtc_funcs { | |||
314 | */ | 315 | */ |
315 | struct drm_crtc { | 316 | struct drm_crtc { |
316 | struct drm_device *dev; | 317 | struct drm_device *dev; |
318 | struct device_node *port; | ||
317 | struct list_head head; | 319 | struct list_head head; |
318 | 320 | ||
319 | /** | 321 | /** |
diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h new file mode 100644 index 000000000000..2441f7112074 --- /dev/null +++ b/include/drm/drm_of.h | |||
@@ -0,0 +1,18 @@ | |||
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__ */ | ||
diff --git a/include/linux/component.h b/include/linux/component.h index 68870182ca1e..c00dcc302611 100644 --- a/include/linux/component.h +++ b/include/linux/component.h | |||
@@ -29,4 +29,11 @@ void component_master_del(struct device *, | |||
29 | int component_master_add_child(struct master *master, | 29 | int component_master_add_child(struct master *master, |
30 | int (*compare)(struct device *, void *), void *compare_data); | 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 | #endif | 39 | #endif |