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 * Currently only single-core rendering using the binner and renderer
11 * is supported. The TFU (texture formatting unit) and V3D 4.x's CSD
12 * (compute shader dispatch) are not yet supported.
15 #include <linux/clk.h>
16 #include <linux/device.h>
18 #include <linux/module.h>
19 #include <linux/of_platform.h>
20 #include <linux/platform_device.h>
21 #include <linux/pm_runtime.h>
22 #include <drm/drm_fb_cma_helper.h>
23 #include <drm/drm_fb_helper.h>
25 #include "uapi/drm/v3d_drm.h"
29 #define DRIVER_NAME "v3d"
30 #define DRIVER_DESC "Broadcom V3D graphics"
31 #define DRIVER_DATE "20180419"
32 #define DRIVER_MAJOR 1
33 #define DRIVER_MINOR 0
34 #define DRIVER_PATCHLEVEL 0
37 static int v3d_runtime_suspend(struct device
*dev
)
39 struct drm_device
*drm
= dev_get_drvdata(dev
);
40 struct v3d_dev
*v3d
= to_v3d_dev(drm
);
44 clk_disable_unprepare(v3d
->clk
);
49 static int v3d_runtime_resume(struct device
*dev
)
51 struct drm_device
*drm
= dev_get_drvdata(dev
);
52 struct v3d_dev
*v3d
= to_v3d_dev(drm
);
55 ret
= clk_prepare_enable(v3d
->clk
);
61 v3d_mmu_set_page_table(v3d
);
68 static const struct dev_pm_ops v3d_v3d_pm_ops
= {
69 SET_RUNTIME_PM_OPS(v3d_runtime_suspend
, v3d_runtime_resume
, NULL
)
72 static int v3d_get_param_ioctl(struct drm_device
*dev
, void *data
,
73 struct drm_file
*file_priv
)
75 struct v3d_dev
*v3d
= to_v3d_dev(dev
);
76 struct drm_v3d_get_param
*args
= data
;
78 static const u32 reg_map
[] = {
79 [DRM_V3D_PARAM_V3D_UIFCFG
] = V3D_HUB_UIFCFG
,
80 [DRM_V3D_PARAM_V3D_HUB_IDENT1
] = V3D_HUB_IDENT1
,
81 [DRM_V3D_PARAM_V3D_HUB_IDENT2
] = V3D_HUB_IDENT2
,
82 [DRM_V3D_PARAM_V3D_HUB_IDENT3
] = V3D_HUB_IDENT3
,
83 [DRM_V3D_PARAM_V3D_CORE0_IDENT0
] = V3D_CTL_IDENT0
,
84 [DRM_V3D_PARAM_V3D_CORE0_IDENT1
] = V3D_CTL_IDENT1
,
85 [DRM_V3D_PARAM_V3D_CORE0_IDENT2
] = V3D_CTL_IDENT2
,
91 /* Note that DRM_V3D_PARAM_V3D_CORE0_IDENT0 is 0, so we need
92 * to explicitly allow it in the "the register in our
93 * parameter map" check.
95 if (args
->param
< ARRAY_SIZE(reg_map
) &&
96 (reg_map
[args
->param
] ||
97 args
->param
== DRM_V3D_PARAM_V3D_CORE0_IDENT0
)) {
98 u32 offset
= reg_map
[args
->param
];
100 if (args
->value
!= 0)
103 ret
= pm_runtime_get_sync(v3d
->dev
);
104 if (args
->param
>= DRM_V3D_PARAM_V3D_CORE0_IDENT0
&&
105 args
->param
<= DRM_V3D_PARAM_V3D_CORE0_IDENT2
) {
106 args
->value
= V3D_CORE_READ(0, offset
);
108 args
->value
= V3D_READ(offset
);
110 pm_runtime_mark_last_busy(v3d
->dev
);
111 pm_runtime_put_autosuspend(v3d
->dev
);
116 switch (args
->param
) {
117 case DRM_V3D_PARAM_SUPPORTS_TFU
:
121 DRM_DEBUG("Unknown parameter %d\n", args
->param
);
127 v3d_open(struct drm_device
*dev
, struct drm_file
*file
)
129 struct v3d_dev
*v3d
= to_v3d_dev(dev
);
130 struct v3d_file_priv
*v3d_priv
;
131 struct drm_sched_rq
*rq
;
134 v3d_priv
= kzalloc(sizeof(*v3d_priv
), GFP_KERNEL
);
140 for (i
= 0; i
< V3D_MAX_QUEUES
; i
++) {
141 rq
= &v3d
->queue
[i
].sched
.sched_rq
[DRM_SCHED_PRIORITY_NORMAL
];
142 drm_sched_entity_init(&v3d_priv
->sched_entity
[i
], &rq
, 1, NULL
);
145 file
->driver_priv
= v3d_priv
;
151 v3d_postclose(struct drm_device
*dev
, struct drm_file
*file
)
153 struct v3d_file_priv
*v3d_priv
= file
->driver_priv
;
156 for (q
= 0; q
< V3D_MAX_QUEUES
; q
++) {
157 drm_sched_entity_destroy(&v3d_priv
->sched_entity
[q
]);
163 static const struct file_operations v3d_drm_fops
= {
164 .owner
= THIS_MODULE
,
166 .release
= drm_release
,
167 .unlocked_ioctl
= drm_ioctl
,
171 .compat_ioctl
= drm_compat_ioctl
,
172 .llseek
= noop_llseek
,
175 /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP
176 * protection between clients. Note that render nodes would be be
177 * able to submit CLs that could access BOs from clients authenticated
178 * with the master node. The TFU doesn't use the GMP, so it would
179 * need to stay DRM_AUTH until we do buffer size/offset validation.
181 static const struct drm_ioctl_desc v3d_drm_ioctls
[] = {
182 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CL
, v3d_submit_cl_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
183 DRM_IOCTL_DEF_DRV(V3D_WAIT_BO
, v3d_wait_bo_ioctl
, DRM_RENDER_ALLOW
),
184 DRM_IOCTL_DEF_DRV(V3D_CREATE_BO
, v3d_create_bo_ioctl
, DRM_RENDER_ALLOW
),
185 DRM_IOCTL_DEF_DRV(V3D_MMAP_BO
, v3d_mmap_bo_ioctl
, DRM_RENDER_ALLOW
),
186 DRM_IOCTL_DEF_DRV(V3D_GET_PARAM
, v3d_get_param_ioctl
, DRM_RENDER_ALLOW
),
187 DRM_IOCTL_DEF_DRV(V3D_GET_BO_OFFSET
, v3d_get_bo_offset_ioctl
, DRM_RENDER_ALLOW
),
188 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_TFU
, v3d_submit_tfu_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
191 static const struct vm_operations_struct v3d_vm_ops
= {
192 .fault
= v3d_gem_fault
,
193 .open
= drm_gem_vm_open
,
194 .close
= drm_gem_vm_close
,
197 static struct drm_driver v3d_drm_driver
= {
198 .driver_features
= (DRIVER_GEM
|
204 .postclose
= v3d_postclose
,
206 #if defined(CONFIG_DEBUG_FS)
207 .debugfs_init
= v3d_debugfs_init
,
210 .gem_free_object_unlocked
= v3d_free_object
,
211 .gem_vm_ops
= &v3d_vm_ops
,
213 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
214 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
215 .gem_prime_import
= drm_gem_prime_import
,
216 .gem_prime_export
= drm_gem_prime_export
,
217 .gem_prime_res_obj
= v3d_prime_res_obj
,
218 .gem_prime_get_sg_table
= v3d_prime_get_sg_table
,
219 .gem_prime_import_sg_table
= v3d_prime_import_sg_table
,
220 .gem_prime_mmap
= v3d_prime_mmap
,
222 .ioctls
= v3d_drm_ioctls
,
223 .num_ioctls
= ARRAY_SIZE(v3d_drm_ioctls
),
224 .fops
= &v3d_drm_fops
,
229 .major
= DRIVER_MAJOR
,
230 .minor
= DRIVER_MINOR
,
231 .patchlevel
= DRIVER_PATCHLEVEL
,
234 static const struct of_device_id v3d_of_match
[] = {
235 { .compatible
= "brcm,7268-v3d" },
236 { .compatible
= "brcm,7278-v3d" },
239 MODULE_DEVICE_TABLE(of
, v3d_of_match
);
242 map_regs(struct v3d_dev
*v3d
, void __iomem
**regs
, const char *name
)
244 struct resource
*res
=
245 platform_get_resource_byname(v3d
->pdev
, IORESOURCE_MEM
, name
);
247 *regs
= devm_ioremap_resource(v3d
->dev
, res
);
248 return PTR_ERR_OR_ZERO(*regs
);
251 static int v3d_platform_drm_probe(struct platform_device
*pdev
)
253 struct device
*dev
= &pdev
->dev
;
254 struct drm_device
*drm
;
259 dev
->coherent_dma_mask
= DMA_BIT_MASK(36);
261 v3d
= kzalloc(sizeof(*v3d
), GFP_KERNEL
);
268 ret
= map_regs(v3d
, &v3d
->bridge_regs
, "bridge");
272 ret
= map_regs(v3d
, &v3d
->hub_regs
, "hub");
276 ret
= map_regs(v3d
, &v3d
->core_regs
[0], "core0");
280 ident1
= V3D_READ(V3D_HUB_IDENT1
);
281 v3d
->ver
= (V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_TVER
) * 10 +
282 V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_REV
));
283 v3d
->cores
= V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_NCORES
);
284 WARN_ON(v3d
->cores
> 1); /* multicore not yet implemented */
287 ret
= map_regs(v3d
, &v3d
->gca_regs
, "gca");
292 v3d
->mmu_scratch
= dma_alloc_wc(dev
, 4096, &v3d
->mmu_scratch_paddr
,
293 GFP_KERNEL
| __GFP_NOWARN
| __GFP_ZERO
);
294 if (!v3d
->mmu_scratch
) {
295 dev_err(dev
, "Failed to allocate MMU scratch page\n");
300 pm_runtime_use_autosuspend(dev
);
301 pm_runtime_set_autosuspend_delay(dev
, 50);
302 pm_runtime_enable(dev
);
304 ret
= drm_dev_init(&v3d
->drm
, &v3d_drm_driver
, dev
);
308 platform_set_drvdata(pdev
, drm
);
309 drm
->dev_private
= v3d
;
311 ret
= v3d_gem_init(drm
);
317 ret
= drm_dev_register(drm
, 0);
324 v3d_gem_destroy(drm
);
328 dma_free_wc(dev
, 4096, v3d
->mmu_scratch
, v3d
->mmu_scratch_paddr
);
334 static int v3d_platform_drm_remove(struct platform_device
*pdev
)
336 struct drm_device
*drm
= platform_get_drvdata(pdev
);
337 struct v3d_dev
*v3d
= to_v3d_dev(drm
);
339 drm_dev_unregister(drm
);
341 v3d_gem_destroy(drm
);
345 dma_free_wc(v3d
->dev
, 4096, v3d
->mmu_scratch
, v3d
->mmu_scratch_paddr
);
350 static struct platform_driver v3d_platform_driver
= {
351 .probe
= v3d_platform_drm_probe
,
352 .remove
= v3d_platform_drm_remove
,
355 .of_match_table
= v3d_of_match
,
359 static int __init
v3d_drm_register(void)
361 return platform_driver_register(&v3d_platform_driver
);
364 static void __exit
v3d_drm_unregister(void)
366 platform_driver_unregister(&v3d_platform_driver
);
369 module_init(v3d_drm_register
);
370 module_exit(v3d_drm_unregister
);
372 MODULE_ALIAS("platform:v3d-drm");
373 MODULE_DESCRIPTION("Broadcom V3D DRM Driver");
374 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
375 MODULE_LICENSE("GPL v2");