Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c8b75bca EA |
2 | /* |
3 | * Copyright (C) 2014-2015 Broadcom | |
4 | * Copyright (C) 2013 Red Hat | |
c8b75bca EA |
5 | */ |
6 | ||
b787963a EA |
7 | /** |
8 | * DOC: Broadcom VC4 Graphics Driver | |
9 | * | |
10 | * The Broadcom VideoCore 4 (present in the Raspberry Pi) contains a | |
11 | * OpenGL ES 2.0-compatible 3D engine called V3D, and a highly | |
12 | * configurable display output pipeline that supports HDMI, DSI, DPI, | |
13 | * and Composite TV output. | |
14 | * | |
15 | * The 3D engine also has an interface for submitting arbitrary | |
16 | * compute shader-style jobs using the same shader processor as is | |
17 | * used for vertex and fragment shaders in GLES 2.0. However, given | |
18 | * that the hardware isn't able to expose any standard interfaces like | |
19 | * OpenGL compute shaders or OpenCL, it isn't supported by this | |
20 | * driver. | |
21 | */ | |
22 | ||
c8b75bca EA |
23 | #include <linux/clk.h> |
24 | #include <linux/component.h> | |
25 | #include <linux/device.h> | |
fd6d6d80 | 26 | #include <linux/dma-mapping.h> |
c8b75bca EA |
27 | #include <linux/io.h> |
28 | #include <linux/module.h> | |
29 | #include <linux/of_platform.h> | |
30 | #include <linux/platform_device.h> | |
af713795 | 31 | #include <linux/pm_runtime.h> |
fd6d6d80 | 32 | |
6848c291 | 33 | #include <drm/drm_aperture.h> |
fd6d6d80 SR |
34 | #include <drm/drm_atomic_helper.h> |
35 | #include <drm/drm_drv.h> | |
b7e8e25b | 36 | #include <drm/drm_fb_cma_helper.h> |
44adece5 | 37 | #include <drm/drm_fb_helper.h> |
fd6d6d80 | 38 | #include <drm/drm_vblank.h> |
c8b75bca | 39 | |
c406ad5e MR |
40 | #include <soc/bcm2835/raspberrypi-firmware.h> |
41 | ||
d5bc60f6 | 42 | #include "uapi/drm/vc4_drm.h" |
fd6d6d80 | 43 | |
c8b75bca EA |
44 | #include "vc4_drv.h" |
45 | #include "vc4_regs.h" | |
46 | ||
47 | #define DRIVER_NAME "vc4" | |
48 | #define DRIVER_DESC "Broadcom VC4 graphics" | |
49 | #define DRIVER_DATE "20140616" | |
50 | #define DRIVER_MAJOR 0 | |
51 | #define DRIVER_MINOR 0 | |
52 | #define DRIVER_PATCHLEVEL 0 | |
53 | ||
54 | /* Helper function for mapping the regs on a platform device. */ | |
23019ff2 | 55 | void __iomem *vc4_ioremap_regs(struct platform_device *pdev, int index) |
c8b75bca | 56 | { |
c8b75bca EA |
57 | void __iomem *map; |
58 | ||
23019ff2 | 59 | map = devm_platform_ioremap_resource(pdev, index); |
ca0b0c1f | 60 | if (IS_ERR(map)) |
c8b75bca | 61 | return map; |
c8b75bca EA |
62 | |
63 | return map; | |
64 | } | |
65 | ||
3d763742 MR |
66 | int vc4_dumb_fixup_args(struct drm_mode_create_dumb *args) |
67 | { | |
68 | int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); | |
69 | ||
70 | if (args->pitch < min_pitch) | |
71 | args->pitch = min_pitch; | |
72 | ||
73 | if (args->size < args->pitch * args->height) | |
74 | args->size = args->pitch * args->height; | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
538f1111 MR |
79 | static int vc5_dumb_create(struct drm_file *file_priv, |
80 | struct drm_device *dev, | |
81 | struct drm_mode_create_dumb *args) | |
82 | { | |
83 | int ret; | |
84 | ||
85 | ret = vc4_dumb_fixup_args(args); | |
86 | if (ret) | |
87 | return ret; | |
88 | ||
89 | return drm_gem_cma_dumb_create_internal(file_priv, dev, args); | |
90 | } | |
91 | ||
af713795 EA |
92 | static int vc4_get_param_ioctl(struct drm_device *dev, void *data, |
93 | struct drm_file *file_priv) | |
94 | { | |
95 | struct vc4_dev *vc4 = to_vc4_dev(dev); | |
96 | struct drm_vc4_get_param *args = data; | |
97 | int ret; | |
98 | ||
99 | if (args->pad != 0) | |
100 | return -EINVAL; | |
101 | ||
30f8c74c MR |
102 | if (WARN_ON_ONCE(vc4->is_vc5)) |
103 | return -ENODEV; | |
104 | ||
ffc26740 EA |
105 | if (!vc4->v3d) |
106 | return -ENODEV; | |
107 | ||
af713795 EA |
108 | switch (args->param) { |
109 | case DRM_VC4_PARAM_V3D_IDENT0: | |
cb74f6ee EA |
110 | ret = vc4_v3d_pm_get(vc4); |
111 | if (ret) | |
af713795 EA |
112 | return ret; |
113 | args->value = V3D_READ(V3D_IDENT0); | |
cb74f6ee | 114 | vc4_v3d_pm_put(vc4); |
af713795 EA |
115 | break; |
116 | case DRM_VC4_PARAM_V3D_IDENT1: | |
cb74f6ee EA |
117 | ret = vc4_v3d_pm_get(vc4); |
118 | if (ret) | |
af713795 EA |
119 | return ret; |
120 | args->value = V3D_READ(V3D_IDENT1); | |
cb74f6ee | 121 | vc4_v3d_pm_put(vc4); |
af713795 EA |
122 | break; |
123 | case DRM_VC4_PARAM_V3D_IDENT2: | |
cb74f6ee EA |
124 | ret = vc4_v3d_pm_get(vc4); |
125 | if (ret) | |
af713795 EA |
126 | return ret; |
127 | args->value = V3D_READ(V3D_IDENT2); | |
cb74f6ee | 128 | vc4_v3d_pm_put(vc4); |
af713795 | 129 | break; |
7363cee5 | 130 | case DRM_VC4_PARAM_SUPPORTS_BRANCHES: |
7154d76f | 131 | case DRM_VC4_PARAM_SUPPORTS_ETC1: |
c778cc5d | 132 | case DRM_VC4_PARAM_SUPPORTS_THREADED_FS: |
3be8eddd | 133 | case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER: |
b9f19259 | 134 | case DRM_VC4_PARAM_SUPPORTS_MADVISE: |
65101d8c | 135 | case DRM_VC4_PARAM_SUPPORTS_PERFMON: |
7363cee5 EA |
136 | args->value = true; |
137 | break; | |
af713795 EA |
138 | default: |
139 | DRM_DEBUG("Unknown parameter %d\n", args->param); | |
140 | return -EINVAL; | |
141 | } | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
65101d8c BB |
146 | static int vc4_open(struct drm_device *dev, struct drm_file *file) |
147 | { | |
30f8c74c | 148 | struct vc4_dev *vc4 = to_vc4_dev(dev); |
65101d8c BB |
149 | struct vc4_file *vc4file; |
150 | ||
30f8c74c MR |
151 | if (WARN_ON_ONCE(vc4->is_vc5)) |
152 | return -ENODEV; | |
153 | ||
65101d8c BB |
154 | vc4file = kzalloc(sizeof(*vc4file), GFP_KERNEL); |
155 | if (!vc4file) | |
156 | return -ENOMEM; | |
30f8c74c | 157 | vc4file->dev = vc4; |
65101d8c BB |
158 | |
159 | vc4_perfmon_open_file(vc4file); | |
160 | file->driver_priv = vc4file; | |
161 | return 0; | |
162 | } | |
163 | ||
164 | static void vc4_close(struct drm_device *dev, struct drm_file *file) | |
165 | { | |
35c8b4b2 | 166 | struct vc4_dev *vc4 = to_vc4_dev(dev); |
65101d8c BB |
167 | struct vc4_file *vc4file = file->driver_priv; |
168 | ||
30f8c74c MR |
169 | if (WARN_ON_ONCE(vc4->is_vc5)) |
170 | return; | |
171 | ||
35c8b4b2 PK |
172 | if (vc4file->bin_bo_used) |
173 | vc4_v3d_bin_bo_put(vc4); | |
174 | ||
65101d8c | 175 | vc4_perfmon_close_file(vc4file); |
72cb0d89 | 176 | kfree(vc4file); |
65101d8c BB |
177 | } |
178 | ||
fa49fdbe | 179 | DEFINE_DRM_GEM_FOPS(vc4_drm_fops); |
c8b75bca EA |
180 | |
181 | static const struct drm_ioctl_desc vc4_drm_ioctls[] = { | |
b10c22e5 HJ |
182 | DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, DRM_RENDER_ALLOW), |
183 | DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, DRM_RENDER_ALLOW), | |
184 | DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, DRM_RENDER_ALLOW), | |
185 | DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, DRM_RENDER_ALLOW), | |
186 | DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, DRM_RENDER_ALLOW), | |
187 | DRM_IOCTL_DEF_DRV(VC4_CREATE_SHADER_BO, vc4_create_shader_bo_ioctl, DRM_RENDER_ALLOW), | |
21461365 EA |
188 | DRM_IOCTL_DEF_DRV(VC4_GET_HANG_STATE, vc4_get_hang_state_ioctl, |
189 | DRM_ROOT_ONLY), | |
af713795 | 190 | DRM_IOCTL_DEF_DRV(VC4_GET_PARAM, vc4_get_param_ioctl, DRM_RENDER_ALLOW), |
83753117 EA |
191 | DRM_IOCTL_DEF_DRV(VC4_SET_TILING, vc4_set_tiling_ioctl, DRM_RENDER_ALLOW), |
192 | DRM_IOCTL_DEF_DRV(VC4_GET_TILING, vc4_get_tiling_ioctl, DRM_RENDER_ALLOW), | |
f3099462 | 193 | DRM_IOCTL_DEF_DRV(VC4_LABEL_BO, vc4_label_bo_ioctl, DRM_RENDER_ALLOW), |
b9f19259 | 194 | DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW), |
65101d8c BB |
195 | DRM_IOCTL_DEF_DRV(VC4_PERFMON_CREATE, vc4_perfmon_create_ioctl, DRM_RENDER_ALLOW), |
196 | DRM_IOCTL_DEF_DRV(VC4_PERFMON_DESTROY, vc4_perfmon_destroy_ioctl, DRM_RENDER_ALLOW), | |
197 | DRM_IOCTL_DEF_DRV(VC4_PERFMON_GET_VALUES, vc4_perfmon_get_values_ioctl, DRM_RENDER_ALLOW), | |
c8b75bca EA |
198 | }; |
199 | ||
538f1111 | 200 | static const struct drm_driver vc4_drm_driver = { |
c8b75bca EA |
201 | .driver_features = (DRIVER_MODESET | |
202 | DRIVER_ATOMIC | | |
203 | DRIVER_GEM | | |
0cd3e274 | 204 | DRIVER_RENDER | |
c720d891 | 205 | DRIVER_SYNCOBJ), |
65101d8c BB |
206 | .open = vc4_open, |
207 | .postclose = vc4_close, | |
d5b1a78a | 208 | |
c8b75bca EA |
209 | #if defined(CONFIG_DEBUG_FS) |
210 | .debugfs_init = vc4_debugfs_init, | |
c8b75bca EA |
211 | #endif |
212 | ||
c826a6e1 | 213 | .gem_create_object = vc4_create_object, |
c8b75bca | 214 | |
dd2dfd44 | 215 | DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(vc4_bo_dumb_create), |
c8b75bca EA |
216 | |
217 | .ioctls = vc4_drm_ioctls, | |
218 | .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), | |
219 | .fops = &vc4_drm_fops, | |
220 | ||
221 | .name = DRIVER_NAME, | |
222 | .desc = DRIVER_DESC, | |
223 | .date = DRIVER_DATE, | |
224 | .major = DRIVER_MAJOR, | |
225 | .minor = DRIVER_MINOR, | |
226 | .patchlevel = DRIVER_PATCHLEVEL, | |
227 | }; | |
228 | ||
538f1111 MR |
229 | static const struct drm_driver vc5_drm_driver = { |
230 | .driver_features = (DRIVER_MODESET | | |
231 | DRIVER_ATOMIC | | |
232 | DRIVER_GEM), | |
233 | ||
234 | #if defined(CONFIG_DEBUG_FS) | |
235 | .debugfs_init = vc4_debugfs_init, | |
236 | #endif | |
237 | ||
238 | DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(vc5_dumb_create), | |
239 | ||
240 | .fops = &vc4_drm_fops, | |
241 | ||
242 | .name = DRIVER_NAME, | |
243 | .desc = DRIVER_DESC, | |
244 | .date = DRIVER_DATE, | |
245 | .major = DRIVER_MAJOR, | |
246 | .minor = DRIVER_MINOR, | |
247 | .patchlevel = DRIVER_PATCHLEVEL, | |
248 | }; | |
249 | ||
c8b75bca EA |
250 | static void vc4_match_add_drivers(struct device *dev, |
251 | struct component_match **match, | |
252 | struct platform_driver *const *drivers, | |
253 | int count) | |
254 | { | |
255 | int i; | |
256 | ||
257 | for (i = 0; i < count; i++) { | |
258 | struct device_driver *drv = &drivers[i]->driver; | |
259 | struct device *p = NULL, *d; | |
260 | ||
36f3313d | 261 | while ((d = platform_find_device_by_driver(p, drv))) { |
c8b75bca | 262 | put_device(p); |
947f019e | 263 | component_match_add(dev, match, component_compare_dev, d); |
c8b75bca EA |
264 | p = d; |
265 | } | |
266 | put_device(p); | |
267 | } | |
268 | } | |
269 | ||
270 | static int vc4_drm_bind(struct device *dev) | |
271 | { | |
272 | struct platform_device *pdev = to_platform_device(dev); | |
538f1111 | 273 | const struct drm_driver *driver; |
c406ad5e | 274 | struct rpi_firmware *firmware = NULL; |
c8b75bca | 275 | struct drm_device *drm; |
c8b75bca | 276 | struct vc4_dev *vc4; |
ffc26740 | 277 | struct device_node *node; |
875a4d53 | 278 | struct drm_crtc *crtc; |
1cbc91eb | 279 | bool is_vc5; |
c8b75bca EA |
280 | int ret = 0; |
281 | ||
282 | dev->coherent_dma_mask = DMA_BIT_MASK(32); | |
283 | ||
1cbc91eb | 284 | is_vc5 = of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"); |
538f1111 MR |
285 | if (is_vc5) |
286 | driver = &vc5_drm_driver; | |
287 | else | |
288 | driver = &vc4_drm_driver; | |
1cbc91eb | 289 | |
538f1111 | 290 | vc4 = devm_drm_dev_alloc(dev, driver, struct vc4_dev, base); |
84d7d472 MR |
291 | if (IS_ERR(vc4)) |
292 | return PTR_ERR(vc4); | |
1cbc91eb | 293 | vc4->is_vc5 = is_vc5; |
84d7d472 MR |
294 | |
295 | drm = &vc4->base; | |
c8b75bca | 296 | platform_set_drvdata(pdev, drm); |
c9be804c | 297 | INIT_LIST_HEAD(&vc4->debugfs_list); |
c8b75bca | 298 | |
257add94 MR |
299 | if (!is_vc5) { |
300 | mutex_init(&vc4->bin_bo_lock); | |
35c8b4b2 | 301 | |
257add94 MR |
302 | ret = vc4_bo_cache_init(drm); |
303 | if (ret) | |
304 | return ret; | |
305 | } | |
c826a6e1 | 306 | |
e46e5330 MR |
307 | ret = drmm_mode_config_init(drm); |
308 | if (ret) | |
84d7d472 | 309 | return ret; |
c8b75bca | 310 | |
257add94 MR |
311 | if (!is_vc5) { |
312 | ret = vc4_gem_init(drm); | |
313 | if (ret) | |
314 | return ret; | |
315 | } | |
d5b1a78a | 316 | |
c406ad5e MR |
317 | node = of_find_compatible_node(NULL, NULL, "raspberrypi,bcm2835-firmware"); |
318 | if (node) { | |
319 | firmware = rpi_firmware_get(node); | |
320 | of_node_put(node); | |
321 | ||
322 | if (!firmware) | |
323 | return -EPROBE_DEFER; | |
324 | } | |
325 | ||
538f1111 | 326 | ret = drm_aperture_remove_framebuffers(false, driver); |
c8b75bca | 327 | if (ret) |
84d7d472 | 328 | return ret; |
c8b75bca | 329 | |
c406ad5e MR |
330 | if (firmware) { |
331 | ret = rpi_firmware_property(firmware, | |
332 | RPI_FIRMWARE_NOTIFY_DISPLAY_DONE, | |
333 | NULL, 0); | |
334 | if (ret) | |
335 | drm_warn(drm, "Couldn't stop firmware display driver: %d\n", ret); | |
336 | ||
337 | rpi_firmware_put(firmware); | |
338 | } | |
339 | ||
a7e6f3d8 | 340 | ret = component_bind_all(dev, drm); |
0c2a50f1 | 341 | if (ret) |
a7e6f3d8 | 342 | return ret; |
0c2a50f1 | 343 | |
a7e6f3d8 | 344 | ret = vc4_plane_create_additional_planes(drm); |
6848c291 TZ |
345 | if (ret) |
346 | goto unbind_all; | |
b3a15f6d | 347 | |
d7f9b839 | 348 | ret = vc4_kms_load(drm); |
c8b75bca EA |
349 | if (ret < 0) |
350 | goto unbind_all; | |
351 | ||
875a4d53 MR |
352 | drm_for_each_crtc(crtc, drm) |
353 | vc4_crtc_disable_at_boot(crtc); | |
354 | ||
d7f9b839 NT |
355 | ret = drm_dev_register(drm, 0); |
356 | if (ret < 0) | |
357 | goto unbind_all; | |
c8b75bca | 358 | |
f741b28f | 359 | drm_fbdev_generic_setup(drm, 16); |
233386d8 | 360 | |
c8b75bca EA |
361 | return 0; |
362 | ||
c8b75bca EA |
363 | unbind_all: |
364 | component_unbind_all(dev, drm); | |
84d7d472 | 365 | |
c8b75bca EA |
366 | return ret; |
367 | } | |
368 | ||
369 | static void vc4_drm_unbind(struct device *dev) | |
370 | { | |
9f900de3 | 371 | struct drm_device *drm = dev_get_drvdata(dev); |
48666d56 | 372 | |
c167df44 DV |
373 | drm_dev_unregister(drm); |
374 | ||
9bac4a01 | 375 | drm_atomic_helper_shutdown(drm); |
c8b75bca EA |
376 | } |
377 | ||
378 | static const struct component_master_ops vc4_drm_ops = { | |
379 | .bind = vc4_drm_bind, | |
380 | .unbind = vc4_drm_unbind, | |
381 | }; | |
382 | ||
7c900570 MR |
383 | /* |
384 | * This list determines the binding order of our components, and we have | |
385 | * a few constraints: | |
386 | * - The TXP driver needs to be bound before the PixelValves (CRTC) | |
387 | * but after the HVS to set the possible_crtc field properly | |
388 | * - The HDMI driver needs to be bound after the HVS so that we can | |
389 | * lookup the HVS maximum core clock rate and figure out if we | |
390 | * support 4kp60 or not. | |
391 | */ | |
c8b75bca | 392 | static struct platform_driver *const component_drivers[] = { |
7c900570 | 393 | &vc4_hvs_driver, |
c8b75bca | 394 | &vc4_hdmi_driver, |
e4b81f8c | 395 | &vc4_vec_driver, |
08302c35 | 396 | &vc4_dpi_driver, |
4078f575 | 397 | &vc4_dsi_driver, |
247c12fc | 398 | &vc4_txp_driver, |
7a100969 | 399 | &vc4_crtc_driver, |
d3f5168a | 400 | &vc4_v3d_driver, |
c8b75bca EA |
401 | }; |
402 | ||
403 | static int vc4_platform_drm_probe(struct platform_device *pdev) | |
404 | { | |
405 | struct component_match *match = NULL; | |
406 | struct device *dev = &pdev->dev; | |
407 | ||
408 | vc4_match_add_drivers(dev, &match, | |
409 | component_drivers, ARRAY_SIZE(component_drivers)); | |
410 | ||
411 | return component_master_add_with_match(dev, &vc4_drm_ops, match); | |
412 | } | |
413 | ||
414 | static int vc4_platform_drm_remove(struct platform_device *pdev) | |
415 | { | |
416 | component_master_del(&pdev->dev, &vc4_drm_ops); | |
417 | ||
418 | return 0; | |
419 | } | |
420 | ||
421 | static const struct of_device_id vc4_of_match[] = { | |
f437bc1e | 422 | { .compatible = "brcm,bcm2711-vc5", }, |
c8b75bca | 423 | { .compatible = "brcm,bcm2835-vc4", }, |
b3f7787b | 424 | { .compatible = "brcm,cygnus-vc4", }, |
c8b75bca EA |
425 | {}, |
426 | }; | |
427 | MODULE_DEVICE_TABLE(of, vc4_of_match); | |
428 | ||
429 | static struct platform_driver vc4_platform_driver = { | |
430 | .probe = vc4_platform_drm_probe, | |
431 | .remove = vc4_platform_drm_remove, | |
432 | .driver = { | |
433 | .name = "vc4-drm", | |
c8b75bca EA |
434 | .of_match_table = vc4_of_match, |
435 | }, | |
436 | }; | |
437 | ||
438 | static int __init vc4_drm_register(void) | |
439 | { | |
cc2e6da1 PZ |
440 | int ret; |
441 | ||
d62b9bee MR |
442 | if (drm_firmware_drivers_only()) |
443 | return -ENODEV; | |
444 | ||
cc2e6da1 PZ |
445 | ret = platform_register_drivers(component_drivers, |
446 | ARRAY_SIZE(component_drivers)); | |
447 | if (ret) | |
448 | return ret; | |
c8b75bca | 449 | |
c8b75bca EA |
450 | return platform_driver_register(&vc4_platform_driver); |
451 | } | |
452 | ||
453 | static void __exit vc4_drm_unregister(void) | |
454 | { | |
cc2e6da1 PZ |
455 | platform_unregister_drivers(component_drivers, |
456 | ARRAY_SIZE(component_drivers)); | |
c8b75bca EA |
457 | platform_driver_unregister(&vc4_platform_driver); |
458 | } | |
459 | ||
460 | module_init(vc4_drm_register); | |
461 | module_exit(vc4_drm_unregister); | |
462 | ||
463 | MODULE_ALIAS("platform:vc4-drm"); | |
464 | MODULE_DESCRIPTION("Broadcom VC4 DRM Driver"); | |
465 | MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); | |
466 | MODULE_LICENSE("GPL v2"); |