-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Rob Herring
committed
Apr 12, 2019
1 parent
c117aa4
commit f3ba912
Showing
23 changed files
with
3,564 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# SPDX-License-Identifier: GPL-2.0 | ||
|
||
config DRM_PANFROST | ||
tristate "Panfrost (DRM support for ARM Mali Midgard/Bifrost GPUs)" | ||
depends on DRM | ||
depends on ARM || ARM64 || COMPILE_TEST | ||
depends on MMU | ||
select DRM_SCHED | ||
select IOMMU_SUPPORT | ||
select IOMMU_IO_PGTABLE_LPAE | ||
select DRM_GEM_SHMEM_HELPER | ||
help | ||
DRM driver for ARM Mali Midgard (T6xx, T7xx, T8xx) and | ||
Bifrost (G3x, G5x, G7x) GPUs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# SPDX-License-Identifier: GPL-2.0 | ||
|
||
panfrost-y := \ | ||
panfrost_drv.o \ | ||
panfrost_device.o \ | ||
panfrost_devfreq.o \ | ||
panfrost_gem.o \ | ||
panfrost_gpu.o \ | ||
panfrost_job.o \ | ||
panfrost_mmu.o | ||
|
||
obj-$(CONFIG_DRM_PANFROST) += panfrost.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
- Thermal support. | ||
|
||
- Bifrost support: | ||
- DT bindings (Neil, WIP) | ||
- MMU page table format and address space setup | ||
- Bifrost specific feature and issue handling | ||
- Coherent DMA support | ||
|
||
- Support for 2MB pages. The io-pgtable code already supports this. Finishing | ||
support involves either copying or adapting the iommu API to handle passing | ||
aligned addresses and sizes to the io-pgtable code. | ||
|
||
- Per FD address space support. The h/w supports multiple addresses spaces. | ||
The hard part is handling when more address spaces are needed than what | ||
the h/w provides. | ||
|
||
- Support pinning pages on demand (GPU page faults). | ||
|
||
- Support userspace controlled GPU virtual addresses. Needed for Vulkan. (Tomeu) | ||
|
||
- Support for madvise and a shrinker. | ||
|
||
- Compute job support. So called 'compute only' jobs need to be plumbed up to | ||
userspace. | ||
|
||
- Performance counter support. (Boris) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright 2019 Collabora ltd. */ | ||
#include <linux/devfreq.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/pm_opp.h> | ||
#include <linux/clk.h> | ||
#include <linux/regulator/consumer.h> | ||
|
||
#include "panfrost_device.h" | ||
#include "panfrost_features.h" | ||
#include "panfrost_issues.h" | ||
#include "panfrost_gpu.h" | ||
#include "panfrost_regs.h" | ||
|
||
static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot); | ||
|
||
static int panfrost_devfreq_target(struct device *dev, unsigned long *freq, | ||
u32 flags) | ||
{ | ||
struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); | ||
struct dev_pm_opp *opp; | ||
unsigned long old_clk_rate = pfdev->devfreq.cur_freq; | ||
unsigned long target_volt, target_rate; | ||
int err; | ||
|
||
opp = devfreq_recommended_opp(dev, freq, flags); | ||
if (IS_ERR(opp)) | ||
return PTR_ERR(opp); | ||
|
||
target_rate = dev_pm_opp_get_freq(opp); | ||
target_volt = dev_pm_opp_get_voltage(opp); | ||
dev_pm_opp_put(opp); | ||
|
||
if (old_clk_rate == target_rate) | ||
return 0; | ||
|
||
/* | ||
* If frequency scaling from low to high, adjust voltage first. | ||
* If frequency scaling from high to low, adjust frequency first. | ||
*/ | ||
if (old_clk_rate < target_rate) { | ||
err = regulator_set_voltage(pfdev->regulator, target_volt, | ||
target_volt); | ||
if (err) { | ||
dev_err(dev, "Cannot set voltage %lu uV\n", | ||
target_volt); | ||
return err; | ||
} | ||
} | ||
|
||
err = clk_set_rate(pfdev->clock, target_rate); | ||
if (err) { | ||
dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate, | ||
err); | ||
regulator_set_voltage(pfdev->regulator, pfdev->devfreq.cur_volt, | ||
pfdev->devfreq.cur_volt); | ||
return err; | ||
} | ||
|
||
if (old_clk_rate > target_rate) { | ||
err = regulator_set_voltage(pfdev->regulator, target_volt, | ||
target_volt); | ||
if (err) | ||
dev_err(dev, "Cannot set voltage %lu uV\n", target_volt); | ||
} | ||
|
||
pfdev->devfreq.cur_freq = target_rate; | ||
pfdev->devfreq.cur_volt = target_volt; | ||
|
||
return 0; | ||
} | ||
|
||
static void panfrost_devfreq_reset(struct panfrost_device *pfdev) | ||
{ | ||
ktime_t now = ktime_get(); | ||
int i; | ||
|
||
for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
pfdev->devfreq.slot[i].busy_time = 0; | ||
pfdev->devfreq.slot[i].idle_time = 0; | ||
pfdev->devfreq.slot[i].time_last_update = now; | ||
} | ||
} | ||
|
||
static int panfrost_devfreq_get_dev_status(struct device *dev, | ||
struct devfreq_dev_status *status) | ||
{ | ||
struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); | ||
int i; | ||
|
||
for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
panfrost_devfreq_update_utilization(pfdev, i); | ||
} | ||
|
||
status->current_frequency = clk_get_rate(pfdev->clock); | ||
status->total_time = ktime_to_ns(ktime_add(pfdev->devfreq.slot[0].busy_time, | ||
pfdev->devfreq.slot[0].idle_time)); | ||
|
||
status->busy_time = 0; | ||
for (i = 0; i < NUM_JOB_SLOTS; i++) { | ||
status->busy_time += ktime_to_ns(pfdev->devfreq.slot[i].busy_time); | ||
} | ||
|
||
/* We're scheduling only to one core atm, so don't divide for now */ | ||
/* status->busy_time /= NUM_JOB_SLOTS; */ | ||
|
||
panfrost_devfreq_reset(pfdev); | ||
|
||
dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n", status->busy_time, | ||
status->total_time, | ||
status->busy_time / (status->total_time / 100), | ||
status->current_frequency / 1000 / 1000); | ||
|
||
return 0; | ||
} | ||
|
||
static int panfrost_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) | ||
{ | ||
struct panfrost_device *pfdev = platform_get_drvdata(to_platform_device(dev)); | ||
|
||
*freq = pfdev->devfreq.cur_freq; | ||
|
||
return 0; | ||
} | ||
|
||
static struct devfreq_dev_profile panfrost_devfreq_profile = { | ||
.polling_ms = 50, /* ~3 frames */ | ||
.target = panfrost_devfreq_target, | ||
.get_dev_status = panfrost_devfreq_get_dev_status, | ||
.get_cur_freq = panfrost_devfreq_get_cur_freq, | ||
}; | ||
|
||
int panfrost_devfreq_init(struct panfrost_device *pfdev) | ||
{ | ||
int ret; | ||
struct dev_pm_opp *opp; | ||
|
||
if (!pfdev->regulator) | ||
return 0; | ||
|
||
ret = dev_pm_opp_of_add_table(&pfdev->pdev->dev); | ||
if (ret == -ENODEV) /* Optional, continue without devfreq */ | ||
return 0; | ||
|
||
panfrost_devfreq_reset(pfdev); | ||
|
||
pfdev->devfreq.cur_freq = clk_get_rate(pfdev->clock); | ||
|
||
opp = devfreq_recommended_opp(&pfdev->pdev->dev, &pfdev->devfreq.cur_freq, 0); | ||
if (IS_ERR(opp)) | ||
return PTR_ERR(opp); | ||
|
||
panfrost_devfreq_profile.initial_freq = pfdev->devfreq.cur_freq; | ||
dev_pm_opp_put(opp); | ||
|
||
pfdev->devfreq.devfreq = devm_devfreq_add_device(&pfdev->pdev->dev, | ||
&panfrost_devfreq_profile, "simple_ondemand", NULL); | ||
if (IS_ERR(pfdev->devfreq.devfreq)) { | ||
DRM_DEV_ERROR(&pfdev->pdev->dev, "Couldn't initialize GPU devfreq\n"); | ||
ret = PTR_ERR(pfdev->devfreq.devfreq); | ||
pfdev->devfreq.devfreq = NULL; | ||
return ret; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
void panfrost_devfreq_resume(struct panfrost_device *pfdev) | ||
{ | ||
int i; | ||
|
||
if (!pfdev->devfreq.devfreq) | ||
return; | ||
|
||
panfrost_devfreq_reset(pfdev); | ||
for (i = 0; i < NUM_JOB_SLOTS; i++) | ||
pfdev->devfreq.slot[i].busy = false; | ||
|
||
devfreq_resume_device(pfdev->devfreq.devfreq); | ||
} | ||
|
||
void panfrost_devfreq_suspend(struct panfrost_device *pfdev) | ||
{ | ||
if (!pfdev->devfreq.devfreq) | ||
return; | ||
|
||
devfreq_suspend_device(pfdev->devfreq.devfreq); | ||
} | ||
|
||
static void panfrost_devfreq_update_utilization(struct panfrost_device *pfdev, int slot) | ||
{ | ||
struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot]; | ||
ktime_t now; | ||
ktime_t last; | ||
|
||
if (!pfdev->devfreq.devfreq) | ||
return; | ||
|
||
now = ktime_get(); | ||
last = pfdev->devfreq.slot[slot].time_last_update; | ||
|
||
/* If we last recorded a transition to busy, we have been idle since */ | ||
if (devfreq_slot->busy) | ||
pfdev->devfreq.slot[slot].busy_time += ktime_sub(now, last); | ||
else | ||
pfdev->devfreq.slot[slot].idle_time += ktime_sub(now, last); | ||
|
||
pfdev->devfreq.slot[slot].time_last_update = now; | ||
} | ||
|
||
/* The job scheduler is expected to call this at every transition busy <-> idle */ | ||
void panfrost_devfreq_record_transition(struct panfrost_device *pfdev, int slot) | ||
{ | ||
struct panfrost_devfreq_slot *devfreq_slot = &pfdev->devfreq.slot[slot]; | ||
|
||
panfrost_devfreq_update_utilization(pfdev, slot); | ||
devfreq_slot->busy = !devfreq_slot->busy; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright 2019 Collabora ltd. */ | ||
|
||
#ifndef __PANFROST_DEVFREQ_H__ | ||
#define __PANFROST_DEVFREQ_H__ | ||
|
||
int panfrost_devfreq_init(struct panfrost_device *pfdev); | ||
|
||
void panfrost_devfreq_resume(struct panfrost_device *pfdev); | ||
void panfrost_devfreq_suspend(struct panfrost_device *pfdev); | ||
|
||
void panfrost_devfreq_record_transition(struct panfrost_device *pfdev, int slot); | ||
|
||
#endif /* __PANFROST_DEVFREQ_H__ */ |
Oops, something went wrong.