Skip to content

Commit

Permalink
drm/panfrost: Add initial panfrost driver
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 23 changed files with 3,564 additions and 0 deletions.
9 changes: 9 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -1180,6 +1180,15 @@ F: drivers/gpu/drm/arm/
F: Documentation/devicetree/bindings/display/arm,malidp.txt
F: Documentation/gpu/afbc.rst

ARM MALI PANFROST DRM DRIVER
M: Rob Herring <robh@kernel.org>
M: Tomeu Vizoso <tomeu.vizoso@collabora.com>
L: dri-devel@lists.freedesktop.org
S: Supported
T: git git://anongit.freedesktop.org/drm/drm-misc
F: drivers/gpu/drm/panfrost/
F: include/uapi/drm/panfrost_drm.h

ARM MFM AND FLOPPY DRIVERS
M: Ian Molton <spyro@f2s.com>
S: Maintained
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ source "drivers/gpu/drm/vboxvideo/Kconfig"

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

source "drivers/gpu/drm/panfrost/Kconfig"

source "drivers/gpu/drm/aspeed/Kconfig"

# Keep legacy drivers last
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,5 @@ obj-$(CONFIG_DRM_TVE200) += tve200/
obj-$(CONFIG_DRM_XEN) += xen/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_DRM_LIMA) += lima/
obj-$(CONFIG_DRM_PANFROST) += panfrost/
obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
14 changes: 14 additions & 0 deletions drivers/gpu/drm/panfrost/Kconfig
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.
12 changes: 12 additions & 0 deletions drivers/gpu/drm/panfrost/Makefile
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
27 changes: 27 additions & 0 deletions drivers/gpu/drm/panfrost/TODO
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)

218 changes: 218 additions & 0 deletions drivers/gpu/drm/panfrost/panfrost_devfreq.c
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;
}
14 changes: 14 additions & 0 deletions drivers/gpu/drm/panfrost/panfrost_devfreq.h
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__ */
Loading

0 comments on commit f3ba912

Please sign in to comment.