Commit | Line | Data |
---|---|---|
57692c94 EA |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* Copyright (C) 2014-2018 Broadcom */ | |
3 | ||
4 | /** | |
5 | * DOC: Broadcom V3D Graphics Driver | |
6 | * | |
7 | * This driver supports the Broadcom V3D 3.3 and 4.1 OpenGL ES GPUs. | |
8 | * For V3D 2.x support, see the VC4 driver. | |
9 | * | |
d223f98f EA |
10 | * The V3D GPU includes a tiled render (composed of a bin and render |
11 | * pipelines), the TFU (texture formatting unit), and the CSD (compute | |
12 | * shader dispatch). | |
57692c94 EA |
13 | */ |
14 | ||
15 | #include <linux/clk.h> | |
16 | #include <linux/device.h> | |
220989e7 | 17 | #include <linux/dma-mapping.h> |
57692c94 EA |
18 | #include <linux/io.h> |
19 | #include <linux/module.h> | |
20 | #include <linux/of_platform.h> | |
21 | #include <linux/platform_device.h> | |
22 | #include <linux/pm_runtime.h> | |
eea9b97b | 23 | #include <linux/reset.h> |
220989e7 SR |
24 | |
25 | #include <drm/drm_drv.h> | |
57692c94 EA |
26 | #include <drm/drm_fb_cma_helper.h> |
27 | #include <drm/drm_fb_helper.h> | |
ea3aa620 | 28 | #include <drm/drm_managed.h> |
220989e7 | 29 | #include <uapi/drm/v3d_drm.h> |
57692c94 | 30 | |
57692c94 EA |
31 | #include "v3d_drv.h" |
32 | #include "v3d_regs.h" | |
33 | ||
34 | #define DRIVER_NAME "v3d" | |
35 | #define DRIVER_DESC "Broadcom V3D graphics" | |
36 | #define DRIVER_DATE "20180419" | |
37 | #define DRIVER_MAJOR 1 | |
38 | #define DRIVER_MINOR 0 | |
39 | #define DRIVER_PATCHLEVEL 0 | |
40 | ||
57692c94 EA |
41 | static int v3d_get_param_ioctl(struct drm_device *dev, void *data, |
42 | struct drm_file *file_priv) | |
43 | { | |
44 | struct v3d_dev *v3d = to_v3d_dev(dev); | |
45 | struct drm_v3d_get_param *args = data; | |
46 | int ret; | |
47 | static const u32 reg_map[] = { | |
48 | [DRM_V3D_PARAM_V3D_UIFCFG] = V3D_HUB_UIFCFG, | |
49 | [DRM_V3D_PARAM_V3D_HUB_IDENT1] = V3D_HUB_IDENT1, | |
50 | [DRM_V3D_PARAM_V3D_HUB_IDENT2] = V3D_HUB_IDENT2, | |
51 | [DRM_V3D_PARAM_V3D_HUB_IDENT3] = V3D_HUB_IDENT3, | |
52 | [DRM_V3D_PARAM_V3D_CORE0_IDENT0] = V3D_CTL_IDENT0, | |
53 | [DRM_V3D_PARAM_V3D_CORE0_IDENT1] = V3D_CTL_IDENT1, | |
54 | [DRM_V3D_PARAM_V3D_CORE0_IDENT2] = V3D_CTL_IDENT2, | |
55 | }; | |
56 | ||
57 | if (args->pad != 0) | |
58 | return -EINVAL; | |
59 | ||
60 | /* Note that DRM_V3D_PARAM_V3D_CORE0_IDENT0 is 0, so we need | |
61 | * to explicitly allow it in the "the register in our | |
62 | * parameter map" check. | |
63 | */ | |
64 | if (args->param < ARRAY_SIZE(reg_map) && | |
65 | (reg_map[args->param] || | |
66 | args->param == DRM_V3D_PARAM_V3D_CORE0_IDENT0)) { | |
67 | u32 offset = reg_map[args->param]; | |
68 | ||
69 | if (args->value != 0) | |
70 | return -EINVAL; | |
71 | ||
bc662528 | 72 | ret = pm_runtime_get_sync(v3d->drm.dev); |
3c77ff8f KL |
73 | if (ret < 0) |
74 | return ret; | |
57692c94 EA |
75 | if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 && |
76 | args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) { | |
77 | args->value = V3D_CORE_READ(0, offset); | |
78 | } else { | |
79 | args->value = V3D_READ(offset); | |
80 | } | |
bc662528 DV |
81 | pm_runtime_mark_last_busy(v3d->drm.dev); |
82 | pm_runtime_put_autosuspend(v3d->drm.dev); | |
57692c94 EA |
83 | return 0; |
84 | } | |
85 | ||
1584f16c EA |
86 | switch (args->param) { |
87 | case DRM_V3D_PARAM_SUPPORTS_TFU: | |
88 | args->value = 1; | |
89 | return 0; | |
d223f98f EA |
90 | case DRM_V3D_PARAM_SUPPORTS_CSD: |
91 | args->value = v3d_has_csd(v3d); | |
92 | return 0; | |
455d56ce ITQ |
93 | case DRM_V3D_PARAM_SUPPORTS_CACHE_FLUSH: |
94 | args->value = 1; | |
95 | return 0; | |
26a4dc29 JSR |
96 | case DRM_V3D_PARAM_SUPPORTS_PERFMON: |
97 | args->value = (v3d->ver >= 40); | |
98 | return 0; | |
e4165ae8 MW |
99 | case DRM_V3D_PARAM_SUPPORTS_MULTISYNC_EXT: |
100 | args->value = 1; | |
101 | return 0; | |
1584f16c EA |
102 | default: |
103 | DRM_DEBUG("Unknown parameter %d\n", args->param); | |
104 | return -EINVAL; | |
105 | } | |
57692c94 EA |
106 | } |
107 | ||
108 | static int | |
109 | v3d_open(struct drm_device *dev, struct drm_file *file) | |
110 | { | |
111 | struct v3d_dev *v3d = to_v3d_dev(dev); | |
112 | struct v3d_file_priv *v3d_priv; | |
b3ac1766 | 113 | struct drm_gpu_scheduler *sched; |
57692c94 EA |
114 | int i; |
115 | ||
116 | v3d_priv = kzalloc(sizeof(*v3d_priv), GFP_KERNEL); | |
117 | if (!v3d_priv) | |
118 | return -ENOMEM; | |
119 | ||
120 | v3d_priv->v3d = v3d; | |
121 | ||
122 | for (i = 0; i < V3D_MAX_QUEUES; i++) { | |
b3ac1766 ND |
123 | sched = &v3d->queue[i].sched; |
124 | drm_sched_entity_init(&v3d_priv->sched_entity[i], | |
125 | DRM_SCHED_PRIORITY_NORMAL, &sched, | |
126 | 1, NULL); | |
57692c94 EA |
127 | } |
128 | ||
26a4dc29 | 129 | v3d_perfmon_open_file(v3d_priv); |
57692c94 EA |
130 | file->driver_priv = v3d_priv; |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | static void | |
136 | v3d_postclose(struct drm_device *dev, struct drm_file *file) | |
137 | { | |
57692c94 EA |
138 | struct v3d_file_priv *v3d_priv = file->driver_priv; |
139 | enum v3d_queue q; | |
140 | ||
e4165ae8 | 141 | for (q = 0; q < V3D_MAX_QUEUES; q++) |
cdc50176 | 142 | drm_sched_entity_destroy(&v3d_priv->sched_entity[q]); |
57692c94 | 143 | |
26a4dc29 | 144 | v3d_perfmon_close_file(v3d_priv); |
57692c94 EA |
145 | kfree(v3d_priv); |
146 | } | |
147 | ||
eee9a2e0 | 148 | DEFINE_DRM_GEM_FOPS(v3d_drm_fops); |
57692c94 EA |
149 | |
150 | /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP | |
bb3425ef | 151 | * protection between clients. Note that render nodes would be |
57692c94 | 152 | * able to submit CLs that could access BOs from clients authenticated |
1584f16c EA |
153 | * with the master node. The TFU doesn't use the GMP, so it would |
154 | * need to stay DRM_AUTH until we do buffer size/offset validation. | |
57692c94 EA |
155 | */ |
156 | static const struct drm_ioctl_desc v3d_drm_ioctls[] = { | |
157 | DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CL, v3d_submit_cl_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), | |
158 | DRM_IOCTL_DEF_DRV(V3D_WAIT_BO, v3d_wait_bo_ioctl, DRM_RENDER_ALLOW), | |
159 | DRM_IOCTL_DEF_DRV(V3D_CREATE_BO, v3d_create_bo_ioctl, DRM_RENDER_ALLOW), | |
160 | DRM_IOCTL_DEF_DRV(V3D_MMAP_BO, v3d_mmap_bo_ioctl, DRM_RENDER_ALLOW), | |
161 | DRM_IOCTL_DEF_DRV(V3D_GET_PARAM, v3d_get_param_ioctl, DRM_RENDER_ALLOW), | |
162 | DRM_IOCTL_DEF_DRV(V3D_GET_BO_OFFSET, v3d_get_bo_offset_ioctl, DRM_RENDER_ALLOW), | |
1584f16c | 163 | DRM_IOCTL_DEF_DRV(V3D_SUBMIT_TFU, v3d_submit_tfu_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), |
d223f98f | 164 | DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CSD, v3d_submit_csd_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), |
26a4dc29 JSR |
165 | DRM_IOCTL_DEF_DRV(V3D_PERFMON_CREATE, v3d_perfmon_create_ioctl, DRM_RENDER_ALLOW), |
166 | DRM_IOCTL_DEF_DRV(V3D_PERFMON_DESTROY, v3d_perfmon_destroy_ioctl, DRM_RENDER_ALLOW), | |
167 | DRM_IOCTL_DEF_DRV(V3D_PERFMON_GET_VALUES, v3d_perfmon_get_values_ioctl, DRM_RENDER_ALLOW), | |
57692c94 EA |
168 | }; |
169 | ||
70a59dd8 | 170 | static const struct drm_driver v3d_drm_driver = { |
57692c94 EA |
171 | .driver_features = (DRIVER_GEM | |
172 | DRIVER_RENDER | | |
57692c94 EA |
173 | DRIVER_SYNCOBJ), |
174 | ||
175 | .open = v3d_open, | |
176 | .postclose = v3d_postclose, | |
177 | ||
178 | #if defined(CONFIG_DEBUG_FS) | |
179 | .debugfs_init = v3d_debugfs_init, | |
180 | #endif | |
181 | ||
40609d48 | 182 | .gem_create_object = v3d_create_object, |
57692c94 EA |
183 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
184 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
57692c94 | 185 | .gem_prime_import_sg_table = v3d_prime_import_sg_table, |
40609d48 | 186 | .gem_prime_mmap = drm_gem_prime_mmap, |
57692c94 EA |
187 | |
188 | .ioctls = v3d_drm_ioctls, | |
189 | .num_ioctls = ARRAY_SIZE(v3d_drm_ioctls), | |
190 | .fops = &v3d_drm_fops, | |
191 | ||
192 | .name = DRIVER_NAME, | |
193 | .desc = DRIVER_DESC, | |
194 | .date = DRIVER_DATE, | |
195 | .major = DRIVER_MAJOR, | |
196 | .minor = DRIVER_MINOR, | |
197 | .patchlevel = DRIVER_PATCHLEVEL, | |
198 | }; | |
199 | ||
200 | static const struct of_device_id v3d_of_match[] = { | |
201 | { .compatible = "brcm,7268-v3d" }, | |
202 | { .compatible = "brcm,7278-v3d" }, | |
203 | {}, | |
204 | }; | |
205 | MODULE_DEVICE_TABLE(of, v3d_of_match); | |
206 | ||
207 | static int | |
208 | map_regs(struct v3d_dev *v3d, void __iomem **regs, const char *name) | |
209 | { | |
c3c7d70b | 210 | *regs = devm_platform_ioremap_resource_byname(v3d_to_pdev(v3d), name); |
57692c94 EA |
211 | return PTR_ERR_OR_ZERO(*regs); |
212 | } | |
213 | ||
214 | static int v3d_platform_drm_probe(struct platform_device *pdev) | |
215 | { | |
216 | struct device *dev = &pdev->dev; | |
217 | struct drm_device *drm; | |
218 | struct v3d_dev *v3d; | |
219 | int ret; | |
091d6283 | 220 | u32 mmu_debug; |
57692c94 | 221 | u32 ident1; |
4a391561 | 222 | u64 mask; |
57692c94 | 223 | |
235b7e7d DV |
224 | v3d = devm_drm_dev_alloc(dev, &v3d_drm_driver, struct v3d_dev, drm); |
225 | if (IS_ERR(v3d)) | |
226 | return PTR_ERR(v3d); | |
227 | ||
57692c94 EA |
228 | drm = &v3d->drm; |
229 | ||
ea3aa620 | 230 | platform_set_drvdata(pdev, drm); |
ea3aa620 | 231 | |
57692c94 EA |
232 | ret = map_regs(v3d, &v3d->hub_regs, "hub"); |
233 | if (ret) | |
235b7e7d | 234 | return ret; |
57692c94 EA |
235 | |
236 | ret = map_regs(v3d, &v3d->core_regs[0], "core0"); | |
237 | if (ret) | |
235b7e7d | 238 | return ret; |
57692c94 | 239 | |
091d6283 | 240 | mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO); |
4a391561 JJ |
241 | mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH)); |
242 | ret = dma_set_mask_and_coherent(dev, mask); | |
243 | if (ret) | |
244 | return ret; | |
245 | ||
38c2c791 | 246 | v3d->va_width = 30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_VA_WIDTH); |
091d6283 | 247 | |
57692c94 EA |
248 | ident1 = V3D_READ(V3D_HUB_IDENT1); |
249 | v3d->ver = (V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_TVER) * 10 + | |
250 | V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_REV)); | |
251 | v3d->cores = V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_NCORES); | |
252 | WARN_ON(v3d->cores > 1); /* multicore not yet implemented */ | |
253 | ||
eea9b97b EA |
254 | v3d->reset = devm_reset_control_get_exclusive(dev, NULL); |
255 | if (IS_ERR(v3d->reset)) { | |
256 | ret = PTR_ERR(v3d->reset); | |
257 | ||
258 | if (ret == -EPROBE_DEFER) | |
235b7e7d | 259 | return ret; |
eea9b97b EA |
260 | |
261 | v3d->reset = NULL; | |
262 | ret = map_regs(v3d, &v3d->bridge_regs, "bridge"); | |
263 | if (ret) { | |
264 | dev_err(dev, | |
265 | "Failed to get reset control or bridge regs\n"); | |
235b7e7d | 266 | return ret; |
eea9b97b EA |
267 | } |
268 | } | |
269 | ||
57692c94 EA |
270 | if (v3d->ver < 41) { |
271 | ret = map_regs(v3d, &v3d->gca_regs, "gca"); | |
272 | if (ret) | |
235b7e7d | 273 | return ret; |
57692c94 EA |
274 | } |
275 | ||
276 | v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr, | |
277 | GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO); | |
278 | if (!v3d->mmu_scratch) { | |
279 | dev_err(dev, "Failed to allocate MMU scratch page\n"); | |
235b7e7d | 280 | return -ENOMEM; |
57692c94 EA |
281 | } |
282 | ||
283 | pm_runtime_use_autosuspend(dev); | |
284 | pm_runtime_set_autosuspend_delay(dev, 50); | |
285 | pm_runtime_enable(dev); | |
286 | ||
57692c94 EA |
287 | ret = v3d_gem_init(drm); |
288 | if (ret) | |
ea3aa620 | 289 | goto dma_free; |
57692c94 | 290 | |
fc227715 EA |
291 | ret = v3d_irq_init(v3d); |
292 | if (ret) | |
293 | goto gem_destroy; | |
57692c94 EA |
294 | |
295 | ret = drm_dev_register(drm, 0); | |
296 | if (ret) | |
fc227715 | 297 | goto irq_disable; |
57692c94 EA |
298 | |
299 | return 0; | |
300 | ||
fc227715 EA |
301 | irq_disable: |
302 | v3d_irq_disable(v3d); | |
57692c94 EA |
303 | gem_destroy: |
304 | v3d_gem_destroy(drm); | |
57692c94 EA |
305 | dma_free: |
306 | dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr); | |
57692c94 EA |
307 | return ret; |
308 | } | |
309 | ||
310 | static int v3d_platform_drm_remove(struct platform_device *pdev) | |
311 | { | |
312 | struct drm_device *drm = platform_get_drvdata(pdev); | |
313 | struct v3d_dev *v3d = to_v3d_dev(drm); | |
314 | ||
315 | drm_dev_unregister(drm); | |
316 | ||
317 | v3d_gem_destroy(drm); | |
318 | ||
bc662528 DV |
319 | dma_free_wc(v3d->drm.dev, 4096, v3d->mmu_scratch, |
320 | v3d->mmu_scratch_paddr); | |
57692c94 EA |
321 | |
322 | return 0; | |
323 | } | |
324 | ||
325 | static struct platform_driver v3d_platform_driver = { | |
326 | .probe = v3d_platform_drm_probe, | |
327 | .remove = v3d_platform_drm_remove, | |
328 | .driver = { | |
329 | .name = "v3d", | |
330 | .of_match_table = v3d_of_match, | |
331 | }, | |
332 | }; | |
333 | ||
ad28cd69 | 334 | module_platform_driver(v3d_platform_driver); |
57692c94 EA |
335 | |
336 | MODULE_ALIAS("platform:v3d-drm"); | |
337 | MODULE_DESCRIPTION("Broadcom V3D DRM Driver"); | |
338 | MODULE_AUTHOR("Eric Anholt <eric@anholt.net>"); | |
339 | MODULE_LICENSE("GPL v2"); |