1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. |
4 | * |
5 | * Parts of this file were based on sources as follows: |
6 | * |
7 | * Copyright (c) 2006-2008 Intel Corporation |
8 | * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> |
9 | * Copyright (C) 2011 Texas Instruments |
10 | */ |
11 | |
12 | /** |
13 | * DOC: ARM PrimeCell PL110 and PL111 CLCD Driver |
14 | * |
15 | * The PL110/PL111 is a simple LCD controller that can support TFT |
16 | * and STN displays. This driver exposes a standard KMS interface |
17 | * for them. |
18 | * |
19 | * The driver currently doesn't expose the cursor. The DRM API for |
20 | * cursors requires support for 64x64 ARGB8888 cursor images, while |
21 | * the hardware can only support 64x64 monochrome with masking |
22 | * cursors. While one could imagine trying to hack something together |
23 | * to look at the ARGB8888 and program reasonable in monochrome, we |
24 | * just don't expose the cursor at all instead, and leave cursor |
25 | * support to the application software cursor layer. |
26 | * |
27 | * TODO: |
28 | * |
29 | * - Fix race between setting plane base address and getting IRQ for |
30 | * vsync firing the pageflip completion. |
31 | * |
32 | * - Read back hardware state at boot to skip reprogramming the |
33 | * hardware when doing a no-op modeset. |
34 | * |
35 | * - Use the CLKSEL bit to support switching between the two external |
36 | * clock parents. |
37 | */ |
38 | |
39 | #include <linux/amba/bus.h> |
40 | #include <linux/dma-buf.h> |
41 | #include <linux/module.h> |
42 | #include <linux/of.h> |
43 | #include <linux/of_graph.h> |
44 | #include <linux/of_reserved_mem.h> |
45 | #include <linux/shmem_fs.h> |
46 | #include <linux/slab.h> |
47 | |
48 | #include <drm/drm_atomic_helper.h> |
49 | #include <drm/drm_bridge.h> |
50 | #include <drm/drm_drv.h> |
51 | #include <drm/drm_fbdev_dma.h> |
52 | #include <drm/drm_fourcc.h> |
53 | #include <drm/drm_gem_dma_helper.h> |
54 | #include <drm/drm_gem_framebuffer_helper.h> |
55 | #include <drm/drm_of.h> |
56 | #include <drm/drm_panel.h> |
57 | #include <drm/drm_probe_helper.h> |
58 | #include <drm/drm_vblank.h> |
59 | |
60 | #include "pl111_drm.h" |
61 | #include "pl111_versatile.h" |
62 | #include "pl111_nomadik.h" |
63 | |
64 | #define DRIVER_DESC "DRM module for PL111" |
65 | |
66 | static const struct drm_mode_config_funcs mode_config_funcs = { |
67 | .fb_create = drm_gem_fb_create, |
68 | .atomic_check = drm_atomic_helper_check, |
69 | .atomic_commit = drm_atomic_helper_commit, |
70 | }; |
71 | |
72 | static int pl111_modeset_init(struct drm_device *dev) |
73 | { |
74 | struct drm_mode_config *mode_config; |
75 | struct pl111_drm_dev_private *priv = dev->dev_private; |
76 | struct device_node *np = dev->dev->of_node; |
77 | struct device_node *remote; |
78 | struct drm_panel *panel = NULL; |
79 | struct drm_bridge *bridge = NULL; |
80 | bool defer = false; |
81 | int ret; |
82 | int i; |
83 | |
84 | ret = drmm_mode_config_init(dev); |
85 | if (ret) |
86 | return ret; |
87 | |
88 | mode_config = &dev->mode_config; |
89 | mode_config->funcs = &mode_config_funcs; |
90 | mode_config->min_width = 1; |
91 | mode_config->max_width = 1024; |
92 | mode_config->min_height = 1; |
93 | mode_config->max_height = 768; |
94 | |
95 | i = 0; |
96 | for_each_endpoint_of_node(np, remote) { |
97 | struct drm_panel *tmp_panel; |
98 | struct drm_bridge *tmp_bridge; |
99 | |
100 | dev_dbg(dev->dev, "checking endpoint %d\n" , i); |
101 | |
102 | ret = drm_of_find_panel_or_bridge(np: dev->dev->of_node, |
103 | port: 0, endpoint: i, |
104 | panel: &tmp_panel, |
105 | bridge: &tmp_bridge); |
106 | if (ret) { |
107 | if (ret == -EPROBE_DEFER) { |
108 | /* |
109 | * Something deferred, but that is often just |
110 | * another way of saying -ENODEV, but let's |
111 | * cast a vote for later deferral. |
112 | */ |
113 | defer = true; |
114 | } else if (ret != -ENODEV) { |
115 | /* Continue, maybe something else is working */ |
116 | dev_err(dev->dev, |
117 | "endpoint %d returns %d\n" , i, ret); |
118 | } |
119 | } |
120 | |
121 | if (tmp_panel) { |
122 | dev_info(dev->dev, |
123 | "found panel on endpoint %d\n" , i); |
124 | panel = tmp_panel; |
125 | } |
126 | if (tmp_bridge) { |
127 | dev_info(dev->dev, |
128 | "found bridge on endpoint %d\n" , i); |
129 | bridge = tmp_bridge; |
130 | } |
131 | |
132 | i++; |
133 | } |
134 | |
135 | /* |
136 | * If we can't find neither panel nor bridge on any of the |
137 | * endpoints, and any of them retured -EPROBE_DEFER, then |
138 | * let's defer this driver too. |
139 | */ |
140 | if ((!panel && !bridge) && defer) |
141 | return -EPROBE_DEFER; |
142 | |
143 | if (panel) { |
144 | bridge = drm_panel_bridge_add_typed(panel, |
145 | DRM_MODE_CONNECTOR_Unknown); |
146 | if (IS_ERR(ptr: bridge)) { |
147 | ret = PTR_ERR(ptr: bridge); |
148 | goto finish; |
149 | } |
150 | } else if (bridge) { |
151 | dev_info(dev->dev, "Using non-panel bridge\n" ); |
152 | } else { |
153 | dev_err(dev->dev, "No bridge, exiting\n" ); |
154 | return -ENODEV; |
155 | } |
156 | |
157 | priv->bridge = bridge; |
158 | if (panel) { |
159 | priv->panel = panel; |
160 | priv->connector = drm_panel_bridge_connector(bridge); |
161 | } |
162 | |
163 | ret = pl111_display_init(dev); |
164 | if (ret != 0) { |
165 | dev_err(dev->dev, "Failed to init display\n" ); |
166 | goto out_bridge; |
167 | } |
168 | |
169 | ret = drm_simple_display_pipe_attach_bridge(pipe: &priv->pipe, |
170 | bridge); |
171 | if (ret) |
172 | return ret; |
173 | |
174 | if (!priv->variant->broken_vblank) { |
175 | ret = drm_vblank_init(dev, num_crtcs: 1); |
176 | if (ret != 0) { |
177 | dev_err(dev->dev, "Failed to init vblank\n" ); |
178 | goto out_bridge; |
179 | } |
180 | } |
181 | |
182 | drm_mode_config_reset(dev); |
183 | |
184 | drm_kms_helper_poll_init(dev); |
185 | |
186 | goto finish; |
187 | |
188 | out_bridge: |
189 | if (panel) |
190 | drm_panel_bridge_remove(bridge); |
191 | finish: |
192 | return ret; |
193 | } |
194 | |
195 | static struct drm_gem_object * |
196 | pl111_gem_import_sg_table(struct drm_device *dev, |
197 | struct dma_buf_attachment *attach, |
198 | struct sg_table *sgt) |
199 | { |
200 | struct pl111_drm_dev_private *priv = dev->dev_private; |
201 | |
202 | /* |
203 | * When using device-specific reserved memory we can't import |
204 | * DMA buffers: those are passed by reference in any global |
205 | * memory and we can only handle a specific range of memory. |
206 | */ |
207 | if (priv->use_device_memory) |
208 | return ERR_PTR(error: -EINVAL); |
209 | |
210 | return drm_gem_dma_prime_import_sg_table(dev, attach, sgt); |
211 | } |
212 | |
213 | DEFINE_DRM_GEM_DMA_FOPS(drm_fops); |
214 | |
215 | static const struct drm_driver pl111_drm_driver = { |
216 | .driver_features = |
217 | DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, |
218 | .ioctls = NULL, |
219 | .fops = &drm_fops, |
220 | .name = "pl111" , |
221 | .desc = DRIVER_DESC, |
222 | .date = "20170317" , |
223 | .major = 1, |
224 | .minor = 0, |
225 | .patchlevel = 0, |
226 | .dumb_create = drm_gem_dma_dumb_create, |
227 | .gem_prime_import_sg_table = pl111_gem_import_sg_table, |
228 | |
229 | #if defined(CONFIG_DEBUG_FS) |
230 | .debugfs_init = pl111_debugfs_init, |
231 | #endif |
232 | }; |
233 | |
234 | static int pl111_amba_probe(struct amba_device *amba_dev, |
235 | const struct amba_id *id) |
236 | { |
237 | struct device *dev = &amba_dev->dev; |
238 | struct pl111_drm_dev_private *priv; |
239 | const struct pl111_variant_data *variant = id->data; |
240 | struct drm_device *drm; |
241 | int ret; |
242 | |
243 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
244 | if (!priv) |
245 | return -ENOMEM; |
246 | |
247 | drm = drm_dev_alloc(driver: &pl111_drm_driver, parent: dev); |
248 | if (IS_ERR(ptr: drm)) |
249 | return PTR_ERR(ptr: drm); |
250 | amba_set_drvdata(amba_dev, drm); |
251 | priv->drm = drm; |
252 | drm->dev_private = priv; |
253 | priv->variant = variant; |
254 | |
255 | ret = of_reserved_mem_device_init(dev); |
256 | if (!ret) { |
257 | dev_info(dev, "using device-specific reserved memory\n" ); |
258 | priv->use_device_memory = true; |
259 | } |
260 | |
261 | if (of_property_read_u32(np: dev->of_node, propname: "max-memory-bandwidth" , |
262 | out_value: &priv->memory_bw)) { |
263 | dev_info(dev, "no max memory bandwidth specified, assume unlimited\n" ); |
264 | priv->memory_bw = 0; |
265 | } |
266 | |
267 | /* The two main variants swap this register */ |
268 | if (variant->is_pl110 || variant->is_lcdc) { |
269 | priv->ienb = CLCD_PL110_IENB; |
270 | priv->ctrl = CLCD_PL110_CNTL; |
271 | } else { |
272 | priv->ienb = CLCD_PL111_IENB; |
273 | priv->ctrl = CLCD_PL111_CNTL; |
274 | } |
275 | |
276 | priv->regs = devm_ioremap_resource(dev, res: &amba_dev->res); |
277 | if (IS_ERR(ptr: priv->regs)) { |
278 | dev_err(dev, "%s failed mmio\n" , __func__); |
279 | ret = PTR_ERR(ptr: priv->regs); |
280 | goto dev_put; |
281 | } |
282 | |
283 | /* This may override some variant settings */ |
284 | ret = pl111_versatile_init(dev, priv); |
285 | if (ret) |
286 | goto dev_put; |
287 | |
288 | pl111_nomadik_init(dev); |
289 | |
290 | /* turn off interrupts before requesting the irq */ |
291 | writel(val: 0, addr: priv->regs + priv->ienb); |
292 | |
293 | ret = devm_request_irq(dev, irq: amba_dev->irq[0], handler: pl111_irq, irqflags: 0, |
294 | devname: variant->name, dev_id: priv); |
295 | if (ret != 0) { |
296 | dev_err(dev, "%s failed irq %d\n" , __func__, ret); |
297 | return ret; |
298 | } |
299 | |
300 | ret = pl111_modeset_init(dev: drm); |
301 | if (ret != 0) |
302 | goto dev_put; |
303 | |
304 | ret = drm_dev_register(dev: drm, flags: 0); |
305 | if (ret < 0) |
306 | goto dev_put; |
307 | |
308 | drm_fbdev_dma_setup(dev: drm, preferred_bpp: priv->variant->fb_depth); |
309 | |
310 | return 0; |
311 | |
312 | dev_put: |
313 | drm_dev_put(dev: drm); |
314 | of_reserved_mem_device_release(dev); |
315 | |
316 | return ret; |
317 | } |
318 | |
319 | static void pl111_amba_remove(struct amba_device *amba_dev) |
320 | { |
321 | struct device *dev = &amba_dev->dev; |
322 | struct drm_device *drm = amba_get_drvdata(amba_dev); |
323 | struct pl111_drm_dev_private *priv = drm->dev_private; |
324 | |
325 | drm_dev_unregister(dev: drm); |
326 | drm_atomic_helper_shutdown(dev: drm); |
327 | if (priv->panel) |
328 | drm_panel_bridge_remove(bridge: priv->bridge); |
329 | drm_dev_put(dev: drm); |
330 | of_reserved_mem_device_release(dev); |
331 | } |
332 | |
333 | static void pl111_amba_shutdown(struct amba_device *amba_dev) |
334 | { |
335 | drm_atomic_helper_shutdown(amba_get_drvdata(amba_dev)); |
336 | } |
337 | |
338 | /* |
339 | * This early variant lacks the 565 and 444 pixel formats. |
340 | */ |
341 | static const u32 pl110_pixel_formats[] = { |
342 | DRM_FORMAT_ABGR8888, |
343 | DRM_FORMAT_XBGR8888, |
344 | DRM_FORMAT_ARGB8888, |
345 | DRM_FORMAT_XRGB8888, |
346 | DRM_FORMAT_ABGR1555, |
347 | DRM_FORMAT_XBGR1555, |
348 | DRM_FORMAT_ARGB1555, |
349 | DRM_FORMAT_XRGB1555, |
350 | }; |
351 | |
352 | static const struct pl111_variant_data pl110_variant = { |
353 | .name = "PL110" , |
354 | .is_pl110 = true, |
355 | .formats = pl110_pixel_formats, |
356 | .nformats = ARRAY_SIZE(pl110_pixel_formats), |
357 | .fb_depth = 16, |
358 | }; |
359 | |
360 | /* RealView, Versatile Express etc use this modern variant */ |
361 | static const u32 pl111_pixel_formats[] = { |
362 | DRM_FORMAT_ABGR8888, |
363 | DRM_FORMAT_XBGR8888, |
364 | DRM_FORMAT_ARGB8888, |
365 | DRM_FORMAT_XRGB8888, |
366 | DRM_FORMAT_BGR565, |
367 | DRM_FORMAT_RGB565, |
368 | DRM_FORMAT_ABGR1555, |
369 | DRM_FORMAT_XBGR1555, |
370 | DRM_FORMAT_ARGB1555, |
371 | DRM_FORMAT_XRGB1555, |
372 | DRM_FORMAT_ABGR4444, |
373 | DRM_FORMAT_XBGR4444, |
374 | DRM_FORMAT_ARGB4444, |
375 | DRM_FORMAT_XRGB4444, |
376 | }; |
377 | |
378 | static const struct pl111_variant_data pl111_variant = { |
379 | .name = "PL111" , |
380 | .formats = pl111_pixel_formats, |
381 | .nformats = ARRAY_SIZE(pl111_pixel_formats), |
382 | .fb_depth = 32, |
383 | }; |
384 | |
385 | static const u32 pl110_nomadik_pixel_formats[] = { |
386 | DRM_FORMAT_RGB888, |
387 | DRM_FORMAT_BGR888, |
388 | DRM_FORMAT_ABGR8888, |
389 | DRM_FORMAT_XBGR8888, |
390 | DRM_FORMAT_ARGB8888, |
391 | DRM_FORMAT_XRGB8888, |
392 | DRM_FORMAT_BGR565, |
393 | DRM_FORMAT_RGB565, |
394 | DRM_FORMAT_ABGR1555, |
395 | DRM_FORMAT_XBGR1555, |
396 | DRM_FORMAT_ARGB1555, |
397 | DRM_FORMAT_XRGB1555, |
398 | DRM_FORMAT_ABGR4444, |
399 | DRM_FORMAT_XBGR4444, |
400 | DRM_FORMAT_ARGB4444, |
401 | DRM_FORMAT_XRGB4444, |
402 | }; |
403 | |
404 | static const struct pl111_variant_data pl110_nomadik_variant = { |
405 | .name = "LCDC (PL110 Nomadik)" , |
406 | .formats = pl110_nomadik_pixel_formats, |
407 | .nformats = ARRAY_SIZE(pl110_nomadik_pixel_formats), |
408 | .is_lcdc = true, |
409 | .st_bitmux_control = true, |
410 | .broken_vblank = true, |
411 | .fb_depth = 16, |
412 | }; |
413 | |
414 | static const struct amba_id pl111_id_table[] = { |
415 | { |
416 | .id = 0x00041110, |
417 | .mask = 0x000fffff, |
418 | .data = (void *)&pl110_variant, |
419 | }, |
420 | { |
421 | .id = 0x00180110, |
422 | .mask = 0x00fffffe, |
423 | .data = (void *)&pl110_nomadik_variant, |
424 | }, |
425 | { |
426 | .id = 0x00041111, |
427 | .mask = 0x000fffff, |
428 | .data = (void *)&pl111_variant, |
429 | }, |
430 | {0, 0}, |
431 | }; |
432 | MODULE_DEVICE_TABLE(amba, pl111_id_table); |
433 | |
434 | static struct amba_driver pl111_amba_driver __maybe_unused = { |
435 | .drv = { |
436 | .name = "drm-clcd-pl111" , |
437 | }, |
438 | .probe = pl111_amba_probe, |
439 | .remove = pl111_amba_remove, |
440 | .shutdown = pl111_amba_shutdown, |
441 | .id_table = pl111_id_table, |
442 | }; |
443 | |
444 | #ifdef CONFIG_ARM_AMBA |
445 | module_amba_driver(pl111_amba_driver); |
446 | #endif |
447 | |
448 | MODULE_DESCRIPTION(DRIVER_DESC); |
449 | MODULE_AUTHOR("ARM Ltd." ); |
450 | MODULE_LICENSE("GPL" ); |
451 | |