1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright (C) 2014-2018 Broadcom */
5 * DOC: Broadcom V3D Graphics Driver
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.
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
15 #include <linux/clk.h>
16 #include <linux/device.h>
17 #include <linux/dma-mapping.h>
19 #include <linux/module.h>
20 #include <linux/of_platform.h>
21 #include <linux/platform_device.h>
22 #include <linux/sched/clock.h>
23 #include <linux/reset.h>
25 #include <drm/drm_drv.h>
26 #include <drm/drm_managed.h>
27 #include <uapi/drm/v3d_drm.h>
32 #define DRIVER_NAME "v3d"
33 #define DRIVER_DESC "Broadcom V3D graphics"
34 #define DRIVER_DATE "20180419"
35 #define DRIVER_MAJOR 1
36 #define DRIVER_MINOR 0
37 #define DRIVER_PATCHLEVEL 0
39 /* Only expose the `super_pages` modparam if THP is enabled. */
40 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
41 bool super_pages
= true;
42 module_param_named(super_pages
, super_pages
, bool, 0400);
43 MODULE_PARM_DESC(super_pages
, "Enable/Disable Super Pages support.");
46 static int v3d_get_param_ioctl(struct drm_device
*dev
, void *data
,
47 struct drm_file
*file_priv
)
49 struct v3d_dev
*v3d
= to_v3d_dev(dev
);
50 struct drm_v3d_get_param
*args
= data
;
51 static const u32 reg_map
[] = {
52 [DRM_V3D_PARAM_V3D_UIFCFG
] = V3D_HUB_UIFCFG
,
53 [DRM_V3D_PARAM_V3D_HUB_IDENT1
] = V3D_HUB_IDENT1
,
54 [DRM_V3D_PARAM_V3D_HUB_IDENT2
] = V3D_HUB_IDENT2
,
55 [DRM_V3D_PARAM_V3D_HUB_IDENT3
] = V3D_HUB_IDENT3
,
56 [DRM_V3D_PARAM_V3D_CORE0_IDENT0
] = V3D_CTL_IDENT0
,
57 [DRM_V3D_PARAM_V3D_CORE0_IDENT1
] = V3D_CTL_IDENT1
,
58 [DRM_V3D_PARAM_V3D_CORE0_IDENT2
] = V3D_CTL_IDENT2
,
64 /* Note that DRM_V3D_PARAM_V3D_CORE0_IDENT0 is 0, so we need
65 * to explicitly allow it in the "the register in our
66 * parameter map" check.
68 if (args
->param
< ARRAY_SIZE(reg_map
) &&
69 (reg_map
[args
->param
] ||
70 args
->param
== DRM_V3D_PARAM_V3D_CORE0_IDENT0
)) {
71 u32 offset
= reg_map
[args
->param
];
76 if (args
->param
>= DRM_V3D_PARAM_V3D_CORE0_IDENT0
&&
77 args
->param
<= DRM_V3D_PARAM_V3D_CORE0_IDENT2
) {
78 args
->value
= V3D_CORE_READ(0, offset
);
80 args
->value
= V3D_READ(offset
);
85 switch (args
->param
) {
86 case DRM_V3D_PARAM_SUPPORTS_TFU
:
89 case DRM_V3D_PARAM_SUPPORTS_CSD
:
90 args
->value
= v3d_has_csd(v3d
);
92 case DRM_V3D_PARAM_SUPPORTS_CACHE_FLUSH
:
95 case DRM_V3D_PARAM_SUPPORTS_PERFMON
:
96 args
->value
= (v3d
->ver
>= 40);
98 case DRM_V3D_PARAM_SUPPORTS_MULTISYNC_EXT
:
101 case DRM_V3D_PARAM_SUPPORTS_CPU_QUEUE
:
104 case DRM_V3D_PARAM_MAX_PERF_COUNTERS
:
105 args
->value
= v3d
->perfmon_info
.max_counters
;
107 case DRM_V3D_PARAM_SUPPORTS_SUPER_PAGES
:
108 args
->value
= !!v3d
->gemfs
;
111 DRM_DEBUG("Unknown parameter %d\n", args
->param
);
117 v3d_open(struct drm_device
*dev
, struct drm_file
*file
)
119 struct v3d_dev
*v3d
= to_v3d_dev(dev
);
120 struct v3d_file_priv
*v3d_priv
;
121 struct drm_gpu_scheduler
*sched
;
124 v3d_priv
= kzalloc(sizeof(*v3d_priv
), GFP_KERNEL
);
130 for (i
= 0; i
< V3D_MAX_QUEUES
; i
++) {
131 sched
= &v3d
->queue
[i
].sched
;
132 drm_sched_entity_init(&v3d_priv
->sched_entity
[i
],
133 DRM_SCHED_PRIORITY_NORMAL
, &sched
,
136 memset(&v3d_priv
->stats
[i
], 0, sizeof(v3d_priv
->stats
[i
]));
137 seqcount_init(&v3d_priv
->stats
[i
].lock
);
140 v3d_perfmon_open_file(v3d_priv
);
141 file
->driver_priv
= v3d_priv
;
147 v3d_postclose(struct drm_device
*dev
, struct drm_file
*file
)
149 struct v3d_file_priv
*v3d_priv
= file
->driver_priv
;
152 for (q
= 0; q
< V3D_MAX_QUEUES
; q
++)
153 drm_sched_entity_destroy(&v3d_priv
->sched_entity
[q
]);
155 v3d_perfmon_close_file(v3d_priv
);
159 void v3d_get_stats(const struct v3d_stats
*stats
, u64 timestamp
,
160 u64
*active_runtime
, u64
*jobs_completed
)
165 seq
= read_seqcount_begin(&stats
->lock
);
166 *active_runtime
= stats
->enabled_ns
;
168 *active_runtime
+= timestamp
- stats
->start_ns
;
169 *jobs_completed
= stats
->jobs_completed
;
170 } while (read_seqcount_retry(&stats
->lock
, seq
));
173 static void v3d_show_fdinfo(struct drm_printer
*p
, struct drm_file
*file
)
175 struct v3d_file_priv
*file_priv
= file
->driver_priv
;
176 u64 timestamp
= local_clock();
177 enum v3d_queue queue
;
179 for (queue
= 0; queue
< V3D_MAX_QUEUES
; queue
++) {
180 struct v3d_stats
*stats
= &file_priv
->stats
[queue
];
181 u64 active_runtime
, jobs_completed
;
183 v3d_get_stats(stats
, timestamp
, &active_runtime
, &jobs_completed
);
185 /* Note that, in case of a GPU reset, the time spent during an
186 * attempt of executing the job is not computed in the runtime.
188 drm_printf(p
, "drm-engine-%s: \t%llu ns\n",
189 v3d_queue_to_string(queue
), active_runtime
);
191 /* Note that we only count jobs that completed. Therefore, jobs
192 * that were resubmitted due to a GPU reset are not computed.
194 drm_printf(p
, "v3d-jobs-%s: \t%llu jobs\n",
195 v3d_queue_to_string(queue
), jobs_completed
);
198 drm_show_memory_stats(p
, file
);
201 static const struct file_operations v3d_drm_fops
= {
202 .owner
= THIS_MODULE
,
204 .show_fdinfo
= drm_show_fdinfo
,
207 /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP
208 * protection between clients. Note that render nodes would be
209 * able to submit CLs that could access BOs from clients authenticated
210 * with the master node. The TFU doesn't use the GMP, so it would
211 * need to stay DRM_AUTH until we do buffer size/offset validation.
213 static const struct drm_ioctl_desc v3d_drm_ioctls
[] = {
214 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CL
, v3d_submit_cl_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
215 DRM_IOCTL_DEF_DRV(V3D_WAIT_BO
, v3d_wait_bo_ioctl
, DRM_RENDER_ALLOW
),
216 DRM_IOCTL_DEF_DRV(V3D_CREATE_BO
, v3d_create_bo_ioctl
, DRM_RENDER_ALLOW
),
217 DRM_IOCTL_DEF_DRV(V3D_MMAP_BO
, v3d_mmap_bo_ioctl
, DRM_RENDER_ALLOW
),
218 DRM_IOCTL_DEF_DRV(V3D_GET_PARAM
, v3d_get_param_ioctl
, DRM_RENDER_ALLOW
),
219 DRM_IOCTL_DEF_DRV(V3D_GET_BO_OFFSET
, v3d_get_bo_offset_ioctl
, DRM_RENDER_ALLOW
),
220 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_TFU
, v3d_submit_tfu_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
221 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CSD
, v3d_submit_csd_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
222 DRM_IOCTL_DEF_DRV(V3D_PERFMON_CREATE
, v3d_perfmon_create_ioctl
, DRM_RENDER_ALLOW
),
223 DRM_IOCTL_DEF_DRV(V3D_PERFMON_DESTROY
, v3d_perfmon_destroy_ioctl
, DRM_RENDER_ALLOW
),
224 DRM_IOCTL_DEF_DRV(V3D_PERFMON_GET_VALUES
, v3d_perfmon_get_values_ioctl
, DRM_RENDER_ALLOW
),
225 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CPU
, v3d_submit_cpu_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
226 DRM_IOCTL_DEF_DRV(V3D_PERFMON_GET_COUNTER
, v3d_perfmon_get_counter_ioctl
, DRM_RENDER_ALLOW
),
229 static const struct drm_driver v3d_drm_driver
= {
230 .driver_features
= (DRIVER_GEM
|
235 .postclose
= v3d_postclose
,
237 #if defined(CONFIG_DEBUG_FS)
238 .debugfs_init
= v3d_debugfs_init
,
241 .gem_create_object
= v3d_create_object
,
242 .gem_prime_import_sg_table
= v3d_prime_import_sg_table
,
244 .ioctls
= v3d_drm_ioctls
,
245 .num_ioctls
= ARRAY_SIZE(v3d_drm_ioctls
),
246 .fops
= &v3d_drm_fops
,
247 .show_fdinfo
= v3d_show_fdinfo
,
252 .major
= DRIVER_MAJOR
,
253 .minor
= DRIVER_MINOR
,
254 .patchlevel
= DRIVER_PATCHLEVEL
,
257 static const struct of_device_id v3d_of_match
[] = {
258 { .compatible
= "brcm,2711-v3d" },
259 { .compatible
= "brcm,2712-v3d" },
260 { .compatible
= "brcm,7268-v3d" },
261 { .compatible
= "brcm,7278-v3d" },
264 MODULE_DEVICE_TABLE(of
, v3d_of_match
);
267 map_regs(struct v3d_dev
*v3d
, void __iomem
**regs
, const char *name
)
269 *regs
= devm_platform_ioremap_resource_byname(v3d_to_pdev(v3d
), name
);
270 return PTR_ERR_OR_ZERO(*regs
);
273 static int v3d_platform_drm_probe(struct platform_device
*pdev
)
275 struct device
*dev
= &pdev
->dev
;
276 struct drm_device
*drm
;
283 v3d
= devm_drm_dev_alloc(dev
, &v3d_drm_driver
, struct v3d_dev
, drm
);
289 platform_set_drvdata(pdev
, drm
);
291 ret
= map_regs(v3d
, &v3d
->hub_regs
, "hub");
295 ret
= map_regs(v3d
, &v3d
->core_regs
[0], "core0");
299 mmu_debug
= V3D_READ(V3D_MMU_DEBUG_INFO
);
300 mask
= DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug
, V3D_MMU_PA_WIDTH
));
301 ret
= dma_set_mask_and_coherent(dev
, mask
);
305 v3d
->va_width
= 30 + V3D_GET_FIELD(mmu_debug
, V3D_MMU_VA_WIDTH
);
307 ident1
= V3D_READ(V3D_HUB_IDENT1
);
308 v3d
->ver
= (V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_TVER
) * 10 +
309 V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_REV
));
310 v3d
->cores
= V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_NCORES
);
311 WARN_ON(v3d
->cores
> 1); /* multicore not yet implemented */
313 ident3
= V3D_READ(V3D_HUB_IDENT3
);
314 v3d
->rev
= V3D_GET_FIELD(ident3
, V3D_HUB_IDENT3_IPREV
);
316 v3d_perfmon_init(v3d
);
318 v3d
->reset
= devm_reset_control_get_exclusive(dev
, NULL
);
319 if (IS_ERR(v3d
->reset
)) {
320 ret
= PTR_ERR(v3d
->reset
);
322 if (ret
== -EPROBE_DEFER
)
326 ret
= map_regs(v3d
, &v3d
->bridge_regs
, "bridge");
329 "Failed to get reset control or bridge regs\n");
335 ret
= map_regs(v3d
, &v3d
->gca_regs
, "gca");
340 v3d
->mmu_scratch
= dma_alloc_wc(dev
, 4096, &v3d
->mmu_scratch_paddr
,
341 GFP_KERNEL
| __GFP_NOWARN
| __GFP_ZERO
);
342 if (!v3d
->mmu_scratch
) {
343 dev_err(dev
, "Failed to allocate MMU scratch page\n");
347 ret
= v3d_gem_init(drm
);
351 ret
= v3d_irq_init(v3d
);
355 ret
= drm_dev_register(drm
, 0);
359 ret
= v3d_sysfs_init(dev
);
366 drm_dev_unregister(drm
);
368 v3d_irq_disable(v3d
);
370 v3d_gem_destroy(drm
);
372 dma_free_wc(dev
, 4096, v3d
->mmu_scratch
, v3d
->mmu_scratch_paddr
);
376 static void v3d_platform_drm_remove(struct platform_device
*pdev
)
378 struct drm_device
*drm
= platform_get_drvdata(pdev
);
379 struct v3d_dev
*v3d
= to_v3d_dev(drm
);
380 struct device
*dev
= &pdev
->dev
;
382 v3d_sysfs_destroy(dev
);
384 drm_dev_unregister(drm
);
386 v3d_gem_destroy(drm
);
388 dma_free_wc(v3d
->drm
.dev
, 4096, v3d
->mmu_scratch
,
389 v3d
->mmu_scratch_paddr
);
392 static struct platform_driver v3d_platform_driver
= {
393 .probe
= v3d_platform_drm_probe
,
394 .remove
= v3d_platform_drm_remove
,
397 .of_match_table
= v3d_of_match
,
401 module_platform_driver(v3d_platform_driver
);
403 MODULE_ALIAS("platform:v3d-drm");
404 MODULE_DESCRIPTION("Broadcom V3D DRM Driver");
405 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
406 MODULE_LICENSE("GPL v2");