mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
drm/imagination: Get GPU resources
Acquire clock and register resources, and enable/map as appropriate. Changes since v8: - Corrected license identifiers Changes since v3: - Remove regulator resource (not used on supported platform) - Use devm helpers - Use devm_clk_get_optional() for optional clocks - Don't prepare clocks on resource acquisition - Drop pvr_device_clk_core_get_freq() helper - Drop pvr_device_reg_fini() - Drop NULLing of clocks in pvr_device_clk_init() - Use dev_err_probe() on clock acquisition failure - Remove PVR_CR_READ/WRITE helper macros - Improve documentation for GPU clocks - Remove regs resource (not used in this commit) Co-developed-by: Frank Binns <frank.binns@imgtec.com> Signed-off-by: Frank Binns <frank.binns@imgtec.com> Co-developed-by: Matt Coster <matt.coster@imgtec.com> Signed-off-by: Matt Coster <matt.coster@imgtec.com> Signed-off-by: Sarah Walker <sarah.walker@imgtec.com> Signed-off-by: Donald Robson <donald.robson@imgtec.com> Reviewed-by: Maxime Ripard <mripard@kernel.org> Link: https://lore.kernel.org/r/579027bd5be4eb3218c9784050ded2326ecbc352.1700668843.git.donald.robson@imgtec.com Signed-off-by: Maxime Ripard <mripard@kernel.org>
This commit is contained in:
parent
4babef0708
commit
1f88f017e6
@ -4,6 +4,7 @@
|
|||||||
subdir-ccflags-y := -I$(srctree)/$(src)
|
subdir-ccflags-y := -I$(srctree)/$(src)
|
||||||
|
|
||||||
powervr-y := \
|
powervr-y := \
|
||||||
|
pvr_device.o \
|
||||||
pvr_drv.o \
|
pvr_drv.o \
|
||||||
|
|
||||||
obj-$(CONFIG_DRM_POWERVR) += powervr.o
|
obj-$(CONFIG_DRM_POWERVR) += powervr.o
|
||||||
|
147
drivers/gpu/drm/imagination/pvr_device.c
Normal file
147
drivers/gpu/drm/imagination/pvr_device.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||||
|
/* Copyright (c) 2023 Imagination Technologies Ltd. */
|
||||||
|
|
||||||
|
#include "pvr_device.h"
|
||||||
|
|
||||||
|
#include <drm/drm_print.h>
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/compiler_attributes.h>
|
||||||
|
#include <linux/compiler_types.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_device_reg_init() - Initialize kernel access to a PowerVR device's
|
||||||
|
* control registers.
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
*
|
||||||
|
* Sets struct pvr_device->regs.
|
||||||
|
*
|
||||||
|
* This method of mapping the device control registers into memory ensures that
|
||||||
|
* they are unmapped when the driver is detached (i.e. no explicit cleanup is
|
||||||
|
* required).
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* * 0 on success, or
|
||||||
|
* * Any error returned by devm_platform_ioremap_resource().
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
pvr_device_reg_init(struct pvr_device *pvr_dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
|
||||||
|
struct platform_device *plat_dev = to_platform_device(drm_dev->dev);
|
||||||
|
void __iomem *regs;
|
||||||
|
|
||||||
|
pvr_dev->regs = NULL;
|
||||||
|
|
||||||
|
regs = devm_platform_ioremap_resource(plat_dev, 0);
|
||||||
|
if (IS_ERR(regs))
|
||||||
|
return dev_err_probe(drm_dev->dev, PTR_ERR(regs),
|
||||||
|
"failed to ioremap gpu registers\n");
|
||||||
|
|
||||||
|
pvr_dev->regs = regs;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_device_clk_init() - Initialize clocks required by a PowerVR device
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
*
|
||||||
|
* Sets struct pvr_device->core_clk, struct pvr_device->sys_clk and
|
||||||
|
* struct pvr_device->mem_clk.
|
||||||
|
*
|
||||||
|
* Three clocks are required by the PowerVR device: core, sys and mem. On
|
||||||
|
* return, this function guarantees that the clocks are in one of the following
|
||||||
|
* states:
|
||||||
|
*
|
||||||
|
* * All successfully initialized,
|
||||||
|
* * Core errored, sys and mem uninitialized,
|
||||||
|
* * Core deinitialized, sys errored, mem uninitialized, or
|
||||||
|
* * Core and sys deinitialized, mem errored.
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* * 0 on success,
|
||||||
|
* * Any error returned by devm_clk_get(), or
|
||||||
|
* * Any error returned by devm_clk_get_optional().
|
||||||
|
*/
|
||||||
|
static int pvr_device_clk_init(struct pvr_device *pvr_dev)
|
||||||
|
{
|
||||||
|
struct drm_device *drm_dev = from_pvr_device(pvr_dev);
|
||||||
|
struct clk *core_clk;
|
||||||
|
struct clk *sys_clk;
|
||||||
|
struct clk *mem_clk;
|
||||||
|
|
||||||
|
core_clk = devm_clk_get(drm_dev->dev, "core");
|
||||||
|
if (IS_ERR(core_clk))
|
||||||
|
return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk),
|
||||||
|
"failed to get core clock\n");
|
||||||
|
|
||||||
|
sys_clk = devm_clk_get_optional(drm_dev->dev, "sys");
|
||||||
|
if (IS_ERR(sys_clk))
|
||||||
|
return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk),
|
||||||
|
"failed to get sys clock\n");
|
||||||
|
|
||||||
|
mem_clk = devm_clk_get_optional(drm_dev->dev, "mem");
|
||||||
|
if (IS_ERR(mem_clk))
|
||||||
|
return dev_err_probe(drm_dev->dev, PTR_ERR(core_clk),
|
||||||
|
"failed to get mem clock\n");
|
||||||
|
|
||||||
|
pvr_dev->core_clk = core_clk;
|
||||||
|
pvr_dev->sys_clk = sys_clk;
|
||||||
|
pvr_dev->mem_clk = mem_clk;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_device_init() - Initialize a PowerVR device
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
*
|
||||||
|
* If this function returns successfully, the device will have been fully
|
||||||
|
* initialized. Otherwise, any parts of the device initialized before an error
|
||||||
|
* occurs will be de-initialized before returning.
|
||||||
|
*
|
||||||
|
* NOTE: The initialization steps currently taken are the bare minimum required
|
||||||
|
* to read from the control registers. The device is unlikely to function
|
||||||
|
* until further initialization steps are added. [This note should be
|
||||||
|
* removed when that happens.]
|
||||||
|
*
|
||||||
|
* Return:
|
||||||
|
* * 0 on success,
|
||||||
|
* * Any error returned by pvr_device_reg_init(),
|
||||||
|
* * Any error returned by pvr_device_clk_init(), or
|
||||||
|
* * Any error returned by pvr_device_gpu_init().
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
pvr_device_init(struct pvr_device *pvr_dev)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Enable and initialize clocks required for the device to operate. */
|
||||||
|
err = pvr_device_clk_init(pvr_dev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* Map the control registers into memory. */
|
||||||
|
return pvr_device_reg_init(pvr_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_device_fini() - Deinitialize a PowerVR device
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
pvr_device_fini(struct pvr_device *pvr_dev)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Deinitialization stages are performed in reverse order compared to
|
||||||
|
* the initialization stages in pvr_device_init().
|
||||||
|
*/
|
||||||
|
}
|
@ -11,9 +11,22 @@
|
|||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
#include <linux/compiler_attributes.h>
|
#include <linux/compiler_attributes.h>
|
||||||
#include <linux/compiler_types.h>
|
#include <linux/compiler_types.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/math.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/timer.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/xarray.h>
|
||||||
|
|
||||||
|
/* Forward declaration from <linux/clk.h>. */
|
||||||
|
struct clk;
|
||||||
|
|
||||||
|
/* Forward declaration from <linux/firmware.h>. */
|
||||||
|
struct firmware;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct pvr_device - powervr-specific wrapper for &struct drm_device
|
* struct pvr_device - powervr-specific wrapper for &struct drm_device
|
||||||
@ -26,6 +39,37 @@ struct pvr_device {
|
|||||||
* from_pvr_device().
|
* from_pvr_device().
|
||||||
*/
|
*/
|
||||||
struct drm_device base;
|
struct drm_device base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @regs: Device control registers.
|
||||||
|
*
|
||||||
|
* These are mapped into memory when the device is initialized; that
|
||||||
|
* location is where this pointer points.
|
||||||
|
*/
|
||||||
|
void __iomem *regs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @core_clk: General core clock.
|
||||||
|
*
|
||||||
|
* This is the primary clock used by the entire GPU core.
|
||||||
|
*/
|
||||||
|
struct clk *core_clk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @sys_clk: Optional system bus clock.
|
||||||
|
*
|
||||||
|
* This may be used on some platforms to provide an independent clock to the SoC Interface
|
||||||
|
* (SOCIF). If present, this needs to be enabled/disabled together with @core_clk.
|
||||||
|
*/
|
||||||
|
struct clk *sys_clk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mem_clk: Optional memory clock.
|
||||||
|
*
|
||||||
|
* This may be used on some platforms to provide an independent clock to the Memory
|
||||||
|
* Interface (MEMIF). If present, this needs to be enabled/disabled together with @core_clk.
|
||||||
|
*/
|
||||||
|
struct clk *mem_clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -56,6 +100,114 @@ struct pvr_file {
|
|||||||
|
|
||||||
#define to_pvr_file(file) ((file)->driver_priv)
|
#define to_pvr_file(file) ((file)->driver_priv)
|
||||||
|
|
||||||
|
int pvr_device_init(struct pvr_device *pvr_dev);
|
||||||
|
void pvr_device_fini(struct pvr_device *pvr_dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PVR_CR_FIELD_GET() - Extract a single field from a PowerVR control register
|
||||||
|
* @val: Value of the target register.
|
||||||
|
* @field: Field specifier, as defined in "pvr_rogue_cr_defs.h".
|
||||||
|
*
|
||||||
|
* Return: The extracted field.
|
||||||
|
*/
|
||||||
|
#define PVR_CR_FIELD_GET(val, field) FIELD_GET(~ROGUE_CR_##field##_CLRMSK, val)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_cr_read32() - Read a 32-bit register from a PowerVR device
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
* @reg: Target register.
|
||||||
|
*
|
||||||
|
* Return: The value of the requested register.
|
||||||
|
*/
|
||||||
|
static __always_inline u32
|
||||||
|
pvr_cr_read32(struct pvr_device *pvr_dev, u32 reg)
|
||||||
|
{
|
||||||
|
return ioread32(pvr_dev->regs + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_cr_read64() - Read a 64-bit register from a PowerVR device
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
* @reg: Target register.
|
||||||
|
*
|
||||||
|
* Return: The value of the requested register.
|
||||||
|
*/
|
||||||
|
static __always_inline u64
|
||||||
|
pvr_cr_read64(struct pvr_device *pvr_dev, u32 reg)
|
||||||
|
{
|
||||||
|
return ioread64(pvr_dev->regs + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_cr_write32() - Write to a 32-bit register in a PowerVR device
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
* @reg: Target register.
|
||||||
|
* @val: Value to write.
|
||||||
|
*/
|
||||||
|
static __always_inline void
|
||||||
|
pvr_cr_write32(struct pvr_device *pvr_dev, u32 reg, u32 val)
|
||||||
|
{
|
||||||
|
iowrite32(val, pvr_dev->regs + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_cr_write64() - Write to a 64-bit register in a PowerVR device
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
* @reg: Target register.
|
||||||
|
* @val: Value to write.
|
||||||
|
*/
|
||||||
|
static __always_inline void
|
||||||
|
pvr_cr_write64(struct pvr_device *pvr_dev, u32 reg, u64 val)
|
||||||
|
{
|
||||||
|
iowrite64(val, pvr_dev->regs + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_cr_poll_reg32() - Wait for a 32-bit register to match a given value by
|
||||||
|
* polling
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
* @reg_addr: Address of register.
|
||||||
|
* @reg_value: Expected register value (after masking).
|
||||||
|
* @reg_mask: Mask of bits valid for comparison with @reg_value.
|
||||||
|
* @timeout_usec: Timeout length, in us.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* * 0 on success, or
|
||||||
|
* * -%ETIMEDOUT on timeout.
|
||||||
|
*/
|
||||||
|
static __always_inline int
|
||||||
|
pvr_cr_poll_reg32(struct pvr_device *pvr_dev, u32 reg_addr, u32 reg_value,
|
||||||
|
u32 reg_mask, u64 timeout_usec)
|
||||||
|
{
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
return readl_poll_timeout(pvr_dev->regs + reg_addr, value,
|
||||||
|
(value & reg_mask) == reg_value, 0, timeout_usec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pvr_cr_poll_reg64() - Wait for a 64-bit register to match a given value by
|
||||||
|
* polling
|
||||||
|
* @pvr_dev: Target PowerVR device.
|
||||||
|
* @reg_addr: Address of register.
|
||||||
|
* @reg_value: Expected register value (after masking).
|
||||||
|
* @reg_mask: Mask of bits valid for comparison with @reg_value.
|
||||||
|
* @timeout_usec: Timeout length, in us.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* * 0 on success, or
|
||||||
|
* * -%ETIMEDOUT on timeout.
|
||||||
|
*/
|
||||||
|
static __always_inline int
|
||||||
|
pvr_cr_poll_reg64(struct pvr_device *pvr_dev, u32 reg_addr, u64 reg_value,
|
||||||
|
u64 reg_mask, u64 timeout_usec)
|
||||||
|
{
|
||||||
|
u64 value;
|
||||||
|
|
||||||
|
return readq_poll_timeout(pvr_dev->regs + reg_addr, value,
|
||||||
|
(value & reg_mask) == reg_value, 0, timeout_usec);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DOC: IOCTL validation helpers
|
* DOC: IOCTL validation helpers
|
||||||
*
|
*
|
||||||
|
@ -464,6 +464,7 @@ pvr_probe(struct platform_device *plat_dev)
|
|||||||
{
|
{
|
||||||
struct pvr_device *pvr_dev;
|
struct pvr_device *pvr_dev;
|
||||||
struct drm_device *drm_dev;
|
struct drm_device *drm_dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
pvr_dev = devm_drm_dev_alloc(&plat_dev->dev, &pvr_drm_driver,
|
pvr_dev = devm_drm_dev_alloc(&plat_dev->dev, &pvr_drm_driver,
|
||||||
struct pvr_device, base);
|
struct pvr_device, base);
|
||||||
@ -474,14 +475,29 @@ pvr_probe(struct platform_device *plat_dev)
|
|||||||
|
|
||||||
platform_set_drvdata(plat_dev, drm_dev);
|
platform_set_drvdata(plat_dev, drm_dev);
|
||||||
|
|
||||||
return drm_dev_register(drm_dev, 0);
|
err = pvr_device_init(pvr_dev);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = drm_dev_register(drm_dev, 0);
|
||||||
|
if (err)
|
||||||
|
goto err_device_fini;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_device_fini:
|
||||||
|
pvr_device_fini(pvr_dev);
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pvr_remove(struct platform_device *plat_dev)
|
pvr_remove(struct platform_device *plat_dev)
|
||||||
{
|
{
|
||||||
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
|
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
|
||||||
|
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
|
||||||
|
|
||||||
|
pvr_device_fini(pvr_dev);
|
||||||
drm_dev_unplug(drm_dev);
|
drm_dev_unplug(drm_dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user