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/pm_runtime.h>
23 #include <linux/reset.h>
25 #include <drm/drm_drv.h>
26 #include <drm/drm_fb_cma_helper.h>
27 #include <drm/drm_fb_helper.h>
28 #include <uapi/drm/v3d_drm.h>
33 #define DRIVER_NAME "v3d"
34 #define DRIVER_DESC "Broadcom V3D graphics"
35 #define DRIVER_DATE "20180419"
36 #define DRIVER_MAJOR 1
37 #define DRIVER_MINOR 0
38 #define DRIVER_PATCHLEVEL 0
41 static int v3d_runtime_suspend(struct device
*dev
)
43 struct drm_device
*drm
= dev_get_drvdata(dev
);
44 struct v3d_dev
*v3d
= to_v3d_dev(drm
);
48 clk_disable_unprepare(v3d
->clk
);
53 static int v3d_runtime_resume(struct device
*dev
)
55 struct drm_device
*drm
= dev_get_drvdata(dev
);
56 struct v3d_dev
*v3d
= to_v3d_dev(drm
);
59 ret
= clk_prepare_enable(v3d
->clk
);
65 v3d_mmu_set_page_table(v3d
);
72 static const struct dev_pm_ops v3d_v3d_pm_ops
= {
73 SET_RUNTIME_PM_OPS(v3d_runtime_suspend
, v3d_runtime_resume
, NULL
)
76 static int v3d_get_param_ioctl(struct drm_device
*dev
, void *data
,
77 struct drm_file
*file_priv
)
79 struct v3d_dev
*v3d
= to_v3d_dev(dev
);
80 struct drm_v3d_get_param
*args
= data
;
82 static const u32 reg_map
[] = {
83 [DRM_V3D_PARAM_V3D_UIFCFG
] = V3D_HUB_UIFCFG
,
84 [DRM_V3D_PARAM_V3D_HUB_IDENT1
] = V3D_HUB_IDENT1
,
85 [DRM_V3D_PARAM_V3D_HUB_IDENT2
] = V3D_HUB_IDENT2
,
86 [DRM_V3D_PARAM_V3D_HUB_IDENT3
] = V3D_HUB_IDENT3
,
87 [DRM_V3D_PARAM_V3D_CORE0_IDENT0
] = V3D_CTL_IDENT0
,
88 [DRM_V3D_PARAM_V3D_CORE0_IDENT1
] = V3D_CTL_IDENT1
,
89 [DRM_V3D_PARAM_V3D_CORE0_IDENT2
] = V3D_CTL_IDENT2
,
95 /* Note that DRM_V3D_PARAM_V3D_CORE0_IDENT0 is 0, so we need
96 * to explicitly allow it in the "the register in our
97 * parameter map" check.
99 if (args
->param
< ARRAY_SIZE(reg_map
) &&
100 (reg_map
[args
->param
] ||
101 args
->param
== DRM_V3D_PARAM_V3D_CORE0_IDENT0
)) {
102 u32 offset
= reg_map
[args
->param
];
104 if (args
->value
!= 0)
107 ret
= pm_runtime_get_sync(v3d
->dev
);
110 if (args
->param
>= DRM_V3D_PARAM_V3D_CORE0_IDENT0
&&
111 args
->param
<= DRM_V3D_PARAM_V3D_CORE0_IDENT2
) {
112 args
->value
= V3D_CORE_READ(0, offset
);
114 args
->value
= V3D_READ(offset
);
116 pm_runtime_mark_last_busy(v3d
->dev
);
117 pm_runtime_put_autosuspend(v3d
->dev
);
122 switch (args
->param
) {
123 case DRM_V3D_PARAM_SUPPORTS_TFU
:
126 case DRM_V3D_PARAM_SUPPORTS_CSD
:
127 args
->value
= v3d_has_csd(v3d
);
129 case DRM_V3D_PARAM_SUPPORTS_CACHE_FLUSH
:
133 DRM_DEBUG("Unknown parameter %d\n", args
->param
);
139 v3d_open(struct drm_device
*dev
, struct drm_file
*file
)
141 struct v3d_dev
*v3d
= to_v3d_dev(dev
);
142 struct v3d_file_priv
*v3d_priv
;
143 struct drm_gpu_scheduler
*sched
;
146 v3d_priv
= kzalloc(sizeof(*v3d_priv
), GFP_KERNEL
);
152 for (i
= 0; i
< V3D_MAX_QUEUES
; i
++) {
153 sched
= &v3d
->queue
[i
].sched
;
154 drm_sched_entity_init(&v3d_priv
->sched_entity
[i
],
155 DRM_SCHED_PRIORITY_NORMAL
, &sched
,
159 file
->driver_priv
= v3d_priv
;
165 v3d_postclose(struct drm_device
*dev
, struct drm_file
*file
)
167 struct v3d_file_priv
*v3d_priv
= file
->driver_priv
;
170 for (q
= 0; q
< V3D_MAX_QUEUES
; q
++) {
171 drm_sched_entity_destroy(&v3d_priv
->sched_entity
[q
]);
177 DEFINE_DRM_GEM_FOPS(v3d_drm_fops
);
179 /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP
180 * protection between clients. Note that render nodes would be be
181 * able to submit CLs that could access BOs from clients authenticated
182 * with the master node. The TFU doesn't use the GMP, so it would
183 * need to stay DRM_AUTH until we do buffer size/offset validation.
185 static const struct drm_ioctl_desc v3d_drm_ioctls
[] = {
186 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CL
, v3d_submit_cl_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
187 DRM_IOCTL_DEF_DRV(V3D_WAIT_BO
, v3d_wait_bo_ioctl
, DRM_RENDER_ALLOW
),
188 DRM_IOCTL_DEF_DRV(V3D_CREATE_BO
, v3d_create_bo_ioctl
, DRM_RENDER_ALLOW
),
189 DRM_IOCTL_DEF_DRV(V3D_MMAP_BO
, v3d_mmap_bo_ioctl
, DRM_RENDER_ALLOW
),
190 DRM_IOCTL_DEF_DRV(V3D_GET_PARAM
, v3d_get_param_ioctl
, DRM_RENDER_ALLOW
),
191 DRM_IOCTL_DEF_DRV(V3D_GET_BO_OFFSET
, v3d_get_bo_offset_ioctl
, DRM_RENDER_ALLOW
),
192 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_TFU
, v3d_submit_tfu_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
193 DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CSD
, v3d_submit_csd_ioctl
, DRM_RENDER_ALLOW
| DRM_AUTH
),
196 static struct drm_driver v3d_drm_driver
= {
197 .driver_features
= (DRIVER_GEM
|
202 .postclose
= v3d_postclose
,
204 #if defined(CONFIG_DEBUG_FS)
205 .debugfs_init
= v3d_debugfs_init
,
208 .gem_create_object
= v3d_create_object
,
209 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
210 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
211 .gem_prime_import_sg_table
= v3d_prime_import_sg_table
,
212 .gem_prime_mmap
= drm_gem_prime_mmap
,
214 .ioctls
= v3d_drm_ioctls
,
215 .num_ioctls
= ARRAY_SIZE(v3d_drm_ioctls
),
216 .fops
= &v3d_drm_fops
,
221 .major
= DRIVER_MAJOR
,
222 .minor
= DRIVER_MINOR
,
223 .patchlevel
= DRIVER_PATCHLEVEL
,
226 static const struct of_device_id v3d_of_match
[] = {
227 { .compatible
= "brcm,7268-v3d" },
228 { .compatible
= "brcm,7278-v3d" },
231 MODULE_DEVICE_TABLE(of
, v3d_of_match
);
234 map_regs(struct v3d_dev
*v3d
, void __iomem
**regs
, const char *name
)
236 struct resource
*res
=
237 platform_get_resource_byname(v3d
->pdev
, IORESOURCE_MEM
, name
);
239 *regs
= devm_ioremap_resource(v3d
->dev
, res
);
240 return PTR_ERR_OR_ZERO(*regs
);
243 static int v3d_platform_drm_probe(struct platform_device
*pdev
)
245 struct device
*dev
= &pdev
->dev
;
246 struct drm_device
*drm
;
253 v3d
= kzalloc(sizeof(*v3d
), GFP_KERNEL
);
260 ret
= map_regs(v3d
, &v3d
->hub_regs
, "hub");
264 ret
= map_regs(v3d
, &v3d
->core_regs
[0], "core0");
268 mmu_debug
= V3D_READ(V3D_MMU_DEBUG_INFO
);
269 dev
->coherent_dma_mask
=
270 DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug
, V3D_MMU_PA_WIDTH
));
271 v3d
->va_width
= 30 + V3D_GET_FIELD(mmu_debug
, V3D_MMU_VA_WIDTH
);
273 ident1
= V3D_READ(V3D_HUB_IDENT1
);
274 v3d
->ver
= (V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_TVER
) * 10 +
275 V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_REV
));
276 v3d
->cores
= V3D_GET_FIELD(ident1
, V3D_HUB_IDENT1_NCORES
);
277 WARN_ON(v3d
->cores
> 1); /* multicore not yet implemented */
279 v3d
->reset
= devm_reset_control_get_exclusive(dev
, NULL
);
280 if (IS_ERR(v3d
->reset
)) {
281 ret
= PTR_ERR(v3d
->reset
);
283 if (ret
== -EPROBE_DEFER
)
287 ret
= map_regs(v3d
, &v3d
->bridge_regs
, "bridge");
290 "Failed to get reset control or bridge regs\n");
296 ret
= map_regs(v3d
, &v3d
->gca_regs
, "gca");
301 v3d
->mmu_scratch
= dma_alloc_wc(dev
, 4096, &v3d
->mmu_scratch_paddr
,
302 GFP_KERNEL
| __GFP_NOWARN
| __GFP_ZERO
);
303 if (!v3d
->mmu_scratch
) {
304 dev_err(dev
, "Failed to allocate MMU scratch page\n");
309 pm_runtime_use_autosuspend(dev
);
310 pm_runtime_set_autosuspend_delay(dev
, 50);
311 pm_runtime_enable(dev
);
313 ret
= drm_dev_init(&v3d
->drm
, &v3d_drm_driver
, dev
);
317 platform_set_drvdata(pdev
, drm
);
318 drm
->dev_private
= v3d
;
320 ret
= v3d_gem_init(drm
);
324 ret
= v3d_irq_init(v3d
);
328 ret
= drm_dev_register(drm
, 0);
335 v3d_irq_disable(v3d
);
337 v3d_gem_destroy(drm
);
341 dma_free_wc(dev
, 4096, v3d
->mmu_scratch
, v3d
->mmu_scratch_paddr
);
347 static int v3d_platform_drm_remove(struct platform_device
*pdev
)
349 struct drm_device
*drm
= platform_get_drvdata(pdev
);
350 struct v3d_dev
*v3d
= to_v3d_dev(drm
);
352 drm_dev_unregister(drm
);
354 v3d_gem_destroy(drm
);
358 dma_free_wc(v3d
->dev
, 4096, v3d
->mmu_scratch
, v3d
->mmu_scratch_paddr
);
363 static struct platform_driver v3d_platform_driver
= {
364 .probe
= v3d_platform_drm_probe
,
365 .remove
= v3d_platform_drm_remove
,
368 .of_match_table
= v3d_of_match
,
372 static int __init
v3d_drm_register(void)
374 return platform_driver_register(&v3d_platform_driver
);
377 static void __exit
v3d_drm_unregister(void)
379 platform_driver_unregister(&v3d_platform_driver
);
382 module_init(v3d_drm_register
);
383 module_exit(v3d_drm_unregister
);
385 MODULE_ALIAS("platform:v3d-drm");
386 MODULE_DESCRIPTION("Broadcom V3D DRM Driver");
387 MODULE_AUTHOR("Eric Anholt <eric@anholt.net>");
388 MODULE_LICENSE("GPL v2");