Skip to content

Commit f3ba912

Browse files
committed
drm/panfrost: Add initial panfrost driver
This adds the initial driver for panfrost which supports Arm Mali Midgard and Bifrost family of GPUs. Currently, only the T860 and T760 Midgard GPUs have been tested. v2: - Add GPU reset on job hangs (Tomeu) - Add RuntimePM and devfreq support (Tomeu) - Fix T760 support (Tomeu) - Add a TODO file (Rob, Tomeu) - Support multiple in fences (Tomeu) - Drop support for shared fences (Tomeu) - Fill in MMU de-init (Rob) - Move register definitions back to single header (Rob) - Clean-up hardcoded job submit todos (Rob) - Implement feature setup based on features/issues (Rob) - Add remaining Midgard DT compatible strings (Rob) v3: - Add support for reset lines (Neil) - Add a MAINTAINERS entry (Rob) - Call dma_set_mask_and_coherent (Rob) - Do MMU invalidate on map and unmap. Restructure to do a single operation per map/unmap call. (Rob) - Add a missing explicit padding to struct drm_panfrost_create_bo (Rob) - Fix 0-day error: "panfrost_devfreq.c:151:9-16: ERROR: PTR_ERR applied after initialization to constant on line 150" - Drop HW_FEATURE_AARCH64_MMU conditional (Rob) - s/DRM_PANFROST_PARAM_GPU_ID/DRM_PANFROST_PARAM_GPU_PROD_ID/ (Rob) - Check drm_gem_shmem_prime_import_sg_table() error code (Rob) - Re-order power on sequence (Rob) - Move panfrost_acquire_object_fences() before scheduling job (Rob) - Add NULL checks on array pointers in job clean-up (Rob) - Rework devfreq (Tomeu) - Fix devfreq init with no regulator (Rob) - Various WS and comments clean-up (Rob) Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Maxime Ripard <maxime.ripard@bootlin.com> Cc: Sean Paul <sean@poorly.run> Cc: David Airlie <airlied@linux.ie> Cc: Daniel Vetter <daniel@ffwll.ch> Cc: Lyude Paul <lyude@redhat.com> Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io> Reviewed-by: Eric Anholt <eric@anholt.net> Reviewed-by: Steven Price <steven.price@arm.com> Signed-off-by: Marty E. Plummer <hanetzer@startmail.com> Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Signed-off-by: Rob Herring <robh@kernel.org> Link: https://patchwork.freedesktop.org/patch/msgid/20190409205427.6943-4-robh@kernel.org
1 parent c117aa4 commit f3ba912

23 files changed

+3564
-0
lines changed

MAINTAINERS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,15 @@ F: drivers/gpu/drm/arm/
11801180
F: Documentation/devicetree/bindings/display/arm,malidp.txt
11811181
F: Documentation/gpu/afbc.rst
11821182

1183+
ARM MALI PANFROST DRM DRIVER
1184+
M: Rob Herring <robh@kernel.org>
1185+
M: Tomeu Vizoso <tomeu.vizoso@collabora.com>
1186+
L: dri-devel@lists.freedesktop.org
1187+
S: Supported
1188+
T: git git://anongit.freedesktop.org/drm/drm-misc
1189+
F: drivers/gpu/drm/panfrost/
1190+
F: include/uapi/drm/panfrost_drm.h
1191+
11831192
ARM MFM AND FLOPPY DRIVERS
11841193
M: Ian Molton <spyro@f2s.com>
11851194
S: Maintained

drivers/gpu/drm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,8 @@ source "drivers/gpu/drm/vboxvideo/Kconfig"
337337

338338
source "drivers/gpu/drm/lima/Kconfig"
339339

340+
source "drivers/gpu/drm/panfrost/Kconfig"
341+
340342
source "drivers/gpu/drm/aspeed/Kconfig"
341343

342344
# Keep legacy drivers last

drivers/gpu/drm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,5 @@ obj-$(CONFIG_DRM_TVE200) += tve200/
112112
obj-$(CONFIG_DRM_XEN) += xen/
113113
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
114114
obj-$(CONFIG_DRM_LIMA) += lima/
115+
obj-$(CONFIG_DRM_PANFROST) += panfrost/
115116
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/

drivers/gpu/drm/panfrost/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
config DRM_PANFROST
4+
tristate "Panfrost (DRM support for ARM Mali Midgard/Bifrost GPUs)"
5+
depends on DRM
6+
depends on ARM || ARM64 || COMPILE_TEST
7+
depends on MMU
8+
select DRM_SCHED
9+
select IOMMU_SUPPORT
10+
select IOMMU_IO_PGTABLE_LPAE
11+
select DRM_GEM_SHMEM_HELPER
12+
help
13+
DRM driver for ARM Mali Midgard (T6xx, T7xx, T8xx) and
14+
Bifrost (G3x, G5x, G7x) GPUs.

drivers/gpu/drm/panfrost/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
3+
panfrost-y := \
4+
panfrost_drv.o \
5+
panfrost_device.o \
6+
panfrost_devfreq.o \
7+
panfrost_gem.o \
8+
panfrost_gpu.o \
9+
panfrost_job.o \
10+
panfrost_mmu.o
11+
12+
obj-$(CONFIG_DRM_PANFROST) += panfrost.o

drivers/gpu/drm/panfrost/TODO

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
- Thermal support.
2+
3+
- Bifrost support:
4+
- DT bindings (Neil, WIP)
5+
- MMU page table format and address space setup
6+
- Bifrost specific feature and issue handling
7+
- Coherent DMA support
8+
9+
- Support for 2MB pages. The io-pgtable code already supports this. Finishing
10+
support involves either copying or adapting the iommu API to handle passing
11+
aligned addresses and sizes to the io-pgtable code.
12+
13+
- Per FD address space support. The h/w supports multiple addresses spaces.
14+
The hard part is handling when more address spaces are needed than what
15+
the h/w provides.
16+
17+
- Support pinning pages on demand (GPU page faults).
18+
19+
- Support userspace controlled GPU virtual addresses. Needed for Vulkan. (Tomeu)
20+
21+
- Support for madvise and a shrinker.
22+
23+
- Compute job support. So called 'compute only' jobs need to be plumbed up to
24+
userspace.
25+
26+
- Performance counter support. (Boris)
27+
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright 2019 Collabora ltd. */
3+
#include <linux/devfreq.h>
4+
#include <linux/platform_device.h>
5+
#include <linux/pm_opp.h>
6+
#include <linux/clk.h>
7+
#include <linux/regulator/consumer.h>
8+
9+
#include "panfrost_device.h"
10+
#include "panfrost_features.h"
11+
#include "panfrost_issues.h"
12+
#include "panfrost_gpu.h"
13+
#include "panfrost_regs.h"
14+
15+
static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot);
16+
17+
static int panfrost_devfreq_target(struct device *dev, unsigned long *freq,
18+
u32 flags)
19+
{
20+
struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev));
21+
struct dev_pm_opp *opp;
22+
unsigned long old_clk_rate = pfdev->devfreq.cur_freq;
23+
unsigned long target_volt, target_rate;
24+
int err;
25+
26+
opp = devfreq_recommended_opp(dev, freq, flags);
27+
if (IS_ERR(opp))
28+
return PTR_ERR(opp);
29+
30+
target_rate = dev_pm_opp_get_freq(opp);
31+
target_volt = dev_pm_opp_get_voltage(opp);
32+
dev_pm_opp_put(opp);
33+
34+
if (old_clk_rate == target_rate)
35+
return 0;
36+
37+
/*
38+
* If frequency scaling from low to high, adjust voltage first.
39+
* If frequency scaling from high to low, adjust frequency first.
40+
*/
41+
if (old_clk_rate < target_rate) {
42+
err = regulator_set_voltage(pfdev->regulator, target_volt,
43+
target_volt);
44+
if (err) {
45+
dev_err(dev, "Cannot set voltage %lu uV\n",
46+
target_volt);
47+
return err;
48+
}
49+
}
50+
51+
err = clk_set_rate(pfdev->clock, target_rate);
52+
if (err) {
53+
dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate,
54+
err);
55+
regulator_set_voltage(pfdev->regulator, pfdev->devfreq.cur_volt,
56+
pfdev->devfreq.cur_volt);
57+
return err;
58+
}
59+
60+
if (old_clk_rate > target_rate) {
61+
err = regulator_set_voltage(pfdev->regulator, target_volt,
62+
target_volt);
63+
if (err)
64+
dev_err(dev, "Cannot set voltage %lu uV\n", target_volt);
65+
}
66+
67+
pfdev->devfreq.cur_freq = target_rate;
68+
pfdev->devfreq.cur_volt = target_volt;
69+
70+
return 0;
71+
}
72+
73+
static void panfrost_devfreq_reset(struct panfrost_device *pfdev)
74+
{
75+
ktime_t now = ktime_get();
76+
int i;
77+
78+
for (i = 0; i < NUM_JOB_SLOTS; i++) {
79+
pfdev->devfreq.slot[i].busy_time = 0;
80+
pfdev->devfreq.slot[i].idle_time = 0;
81+
pfdev->devfreq.slot[i].time_last_update = now;
82+
}
83+
}
84+
85+
static int panfrost_devfreq_get_dev_status(struct device *dev,
86+
struct devfreq_dev_status *status)
87+
{
88+
struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev));
89+
int i;
90+
91+
for (i = 0; i < NUM_JOB_SLOTS; i++) {
92+
panfrost_devfreq_update_utilization(pfdev, i);
93+
}
94+
95+
status->current_frequency = clk_get_rate(pfdev->clock);
96+
status->total_time = ktime_to_ns(ktime_add(pfdev->devfreq.slot[0].busy_time,
97+
pfdev->devfreq.slot[0].idle_time));
98+
99+
status->busy_time = 0;
100+
for (i = 0; i < NUM_JOB_SLOTS; i++) {
101+
status->busy_time += ktime_to_ns(pfdev->devfreq.slot[i].busy_time);
102+
}
103+
104+
/* We're scheduling only to one core atm, so don't divide for now */
105+
/* status->busy_time /= NUM_JOB_SLOTS; */
106+
107+
panfrost_devfreq_reset(pfdev);
108+
109+
dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n", status->busy_time,
110+
status->total_time,
111+
status->busy_time / (status->total_time / 100),
112+
status->current_frequency / 1000 / 1000);
113+
114+
return 0;
115+
}
116+
117+
static int panfrost_devfreq_get_cur_freq(struct device *dev, unsigned long *freq)
118+
{
119+
struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev));
120+
121+
*freq = pfdev->devfreq.cur_freq;
122+
123+
return 0;
124+
}
125+
126+
static struct devfreq_dev_profile panfrost_devfreq_profile = {
127+
.polling_ms = 50, /* ~3 frames */
128+
.target = panfrost_devfreq_target,
129+
.get_dev_status = panfrost_devfreq_get_dev_status,
130+
.get_cur_freq = panfrost_devfreq_get_cur_freq,
131+
};
132+
133+
int panfrost_devfreq_init(struct panfrost_device *pfdev)
134+
{
135+
int ret;
136+
struct dev_pm_opp *opp;
137+
138+
if (!pfdev->regulator)
139+
return 0;
140+
141+
ret = dev_pm_opp_of_add_table(&pfdev->pdev->dev);
142+
if (ret == -ENODEV) /* Optional, continue without devfreq */
143+
return 0;
144+
145+
panfrost_devfreq_reset(pfdev);
146+
147+
pfdev->devfreq.cur_freq = clk_get_rate(pfdev->clock);
148+
149+
opp = devfreq_recommended_opp(&pfdev->pdev->dev, &pfdev->devfreq.cur_freq, 0);
150+
if (IS_ERR(opp))
151+
return PTR_ERR(opp);
152+
153+
panfrost_devfreq_profile.initial_freq = pfdev->devfreq.cur_freq;
154+
dev_pm_opp_put(opp);
155+
156+
pfdev->devfreq.devfreq = devm_devfreq_add_device(&pfdev->pdev->dev,
157+
&panfrost_devfreq_profile, "simple_ondemand", NULL);
158+
if (IS_ERR(pfdev->devfreq.devfreq)) {
159+
DRM_DEV_ERROR(&pfdev->pdev->dev, "Couldn't initialize GPU devfreq\n");
160+
ret = PTR_ERR(pfdev->devfreq.devfreq);
161+
pfdev->devfreq.devfreq = NULL;
162+
return ret;
163+
}
164+
165+
return 0;
166+
}
167+
168+
void panfrost_devfreq_resume(struct panfrost_device *pfdev)
169+
{
170+
int i;
171+
172+
if (!pfdev->devfreq.devfreq)
173+
return;
174+
175+
panfrost_devfreq_reset(pfdev);
176+
for (i = 0; i < NUM_JOB_SLOTS; i++)
177+
pfdev->devfreq.slot[i].busy = false;
178+
179+
devfreq_resume_device(pfdev->devfreq.devfreq);
180+
}
181+
182+
void panfrost_devfreq_suspend(struct panfrost_device *pfdev)
183+
{
184+
if (!pfdev->devfreq.devfreq)
185+
return;
186+
187+
devfreq_suspend_device(pfdev->devfreq.devfreq);
188+
}
189+
190+
static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot)
191+
{
192+
struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot];
193+
ktime_t now;
194+
ktime_t last;
195+
196+
if (!pfdev->devfreq.devfreq)
197+
return;
198+
199+
now = ktime_get();
200+
last = pfdev->devfreq.slot[slot].time_last_update;
201+
202+
/* If we last recorded a transition to busy, we have been idle since */
203+
if (devfreq_slot->busy)
204+
pfdev->devfreq.slot[slot].busy_time += ktime_sub(now, last);
205+
else
206+
pfdev->devfreq.slot[slot].idle_time += ktime_sub(now, last);
207+
208+
pfdev->devfreq.slot[slot].time_last_update = now;
209+
}
210+
211+
/* The job scheduler is expected to call this at every transition busy <-> idle */
212+
void panfrost_devfreq_record_transition(struct panfrost_device *pfdev, int slot)
213+
{
214+
struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot];
215+
216+
panfrost_devfreq_update_utilization(pfdev, slot);
217+
devfreq_slot->busy = !devfreq_slot->busy;
218+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright 2019 Collabora ltd. */
3+
4+
#ifndef __PANFROST_DEVFREQ_H__
5+
#define __PANFROST_DEVFREQ_H__
6+
7+
int panfrost_devfreq_init(struct panfrost_device *pfdev);
8+
9+
void panfrost_devfreq_resume(struct panfrost_device *pfdev);
10+
void panfrost_devfreq_suspend(struct panfrost_device *pfdev);
11+
12+
void panfrost_devfreq_record_transition(struct panfrost_device *pfdev, int slot);
13+
14+
#endif /* __PANFROST_DEVFREQ_H__ */

0 commit comments

Comments
 (0)