2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-03-02 13:01:12 -08:00
/*
* Copyright ( c ) 2014 The Linux Foundation . All rights reserved .
* Copyright ( C ) 2013 Red Hat
* Author : Rob Clark < robdclark @ gmail . com >
*/
2017-05-18 13:29:38 +09:00
# include <linux/clk.h>
# include <linux/component.h>
2019-07-16 08:42:07 +02:00
# include <linux/platform_device.h>
2017-05-18 13:29:38 +09:00
# include <linux/pm_runtime.h>
2019-07-16 08:42:07 +02:00
2015-03-02 13:01:12 -08:00
# include "vc4_drv.h"
# include "vc4_regs.h"
2019-02-20 13:03:38 -08:00
static const struct debugfs_reg32 v3d_regs [ ] = {
VC4_REG32 ( V3D_IDENT0 ) ,
VC4_REG32 ( V3D_IDENT1 ) ,
VC4_REG32 ( V3D_IDENT2 ) ,
VC4_REG32 ( V3D_SCRATCH ) ,
VC4_REG32 ( V3D_L2CACTL ) ,
VC4_REG32 ( V3D_SLCACTL ) ,
VC4_REG32 ( V3D_INTCTL ) ,
VC4_REG32 ( V3D_INTENA ) ,
VC4_REG32 ( V3D_INTDIS ) ,
VC4_REG32 ( V3D_CT0CS ) ,
VC4_REG32 ( V3D_CT1CS ) ,
VC4_REG32 ( V3D_CT0EA ) ,
VC4_REG32 ( V3D_CT1EA ) ,
VC4_REG32 ( V3D_CT0CA ) ,
VC4_REG32 ( V3D_CT1CA ) ,
VC4_REG32 ( V3D_CT00RA0 ) ,
VC4_REG32 ( V3D_CT01RA0 ) ,
VC4_REG32 ( V3D_CT0LC ) ,
VC4_REG32 ( V3D_CT1LC ) ,
VC4_REG32 ( V3D_CT0PC ) ,
VC4_REG32 ( V3D_CT1PC ) ,
VC4_REG32 ( V3D_PCS ) ,
VC4_REG32 ( V3D_BFC ) ,
VC4_REG32 ( V3D_RFC ) ,
VC4_REG32 ( V3D_BPCA ) ,
VC4_REG32 ( V3D_BPCS ) ,
VC4_REG32 ( V3D_BPOA ) ,
VC4_REG32 ( V3D_BPOS ) ,
VC4_REG32 ( V3D_BXCF ) ,
VC4_REG32 ( V3D_SQRSV0 ) ,
VC4_REG32 ( V3D_SQRSV1 ) ,
VC4_REG32 ( V3D_SQCNTL ) ,
VC4_REG32 ( V3D_SRQPC ) ,
VC4_REG32 ( V3D_SRQUA ) ,
VC4_REG32 ( V3D_SRQUL ) ,
VC4_REG32 ( V3D_SRQCS ) ,
VC4_REG32 ( V3D_VPACNTL ) ,
VC4_REG32 ( V3D_VPMBASE ) ,
VC4_REG32 ( V3D_PCTRC ) ,
VC4_REG32 ( V3D_PCTRE ) ,
VC4_REG32 ( V3D_PCTR ( 0 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 0 ) ) ,
VC4_REG32 ( V3D_PCTR ( 1 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 1 ) ) ,
VC4_REG32 ( V3D_PCTR ( 2 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 2 ) ) ,
VC4_REG32 ( V3D_PCTR ( 3 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 3 ) ) ,
VC4_REG32 ( V3D_PCTR ( 4 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 4 ) ) ,
VC4_REG32 ( V3D_PCTR ( 5 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 5 ) ) ,
VC4_REG32 ( V3D_PCTR ( 6 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 6 ) ) ,
VC4_REG32 ( V3D_PCTR ( 7 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 7 ) ) ,
VC4_REG32 ( V3D_PCTR ( 8 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 8 ) ) ,
VC4_REG32 ( V3D_PCTR ( 9 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 9 ) ) ,
VC4_REG32 ( V3D_PCTR ( 10 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 10 ) ) ,
VC4_REG32 ( V3D_PCTR ( 11 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 11 ) ) ,
VC4_REG32 ( V3D_PCTR ( 12 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 12 ) ) ,
VC4_REG32 ( V3D_PCTR ( 13 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 13 ) ) ,
VC4_REG32 ( V3D_PCTR ( 14 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 14 ) ) ,
VC4_REG32 ( V3D_PCTR ( 15 ) ) ,
VC4_REG32 ( V3D_PCTRS ( 15 ) ) ,
VC4_REG32 ( V3D_DBGE ) ,
VC4_REG32 ( V3D_FDBGO ) ,
VC4_REG32 ( V3D_FDBGB ) ,
VC4_REG32 ( V3D_FDBGR ) ,
VC4_REG32 ( V3D_FDBGS ) ,
VC4_REG32 ( V3D_ERRSTAT ) ,
2015-03-02 13:01:12 -08:00
} ;
2019-04-01 11:35:58 -07:00
static int vc4_v3d_debugfs_ident ( struct seq_file * m , void * unused )
2015-03-02 13:01:12 -08:00
{
2022-12-19 09:06:18 -03:00
struct drm_debugfs_entry * entry = m - > private ;
struct drm_device * dev = entry - > dev ;
2015-03-02 13:01:12 -08:00
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
2019-02-20 13:03:43 -08:00
int ret = vc4_v3d_pm_get ( vc4 ) ;
2015-03-02 13:01:12 -08:00
2019-02-20 13:03:43 -08:00
if ( ret = = 0 ) {
uint32_t ident1 = V3D_READ ( V3D_IDENT1 ) ;
uint32_t nslc = VC4_GET_FIELD ( ident1 , V3D_IDENT1_NSLC ) ;
uint32_t tups = VC4_GET_FIELD ( ident1 , V3D_IDENT1_TUPS ) ;
uint32_t qups = VC4_GET_FIELD ( ident1 , V3D_IDENT1_QUPS ) ;
seq_printf ( m , " Revision: %d \n " ,
VC4_GET_FIELD ( ident1 , V3D_IDENT1_REV ) ) ;
seq_printf ( m , " Slices: %d \n " , nslc ) ;
seq_printf ( m , " TMUs: %d \n " , nslc * tups ) ;
seq_printf ( m , " QPUs: %d \n " , nslc * qups ) ;
seq_printf ( m , " Semaphores: %d \n " ,
VC4_GET_FIELD ( ident1 , V3D_IDENT1_NSEM ) ) ;
vc4_v3d_pm_put ( vc4 ) ;
}
2015-03-02 13:01:12 -08:00
return 0 ;
}
2020-11-16 17:41:09 +00:00
/*
2019-02-20 13:03:42 -08:00
* Wraps pm_runtime_get_sync ( ) in a refcount , so that we can reliably
* get the pm_runtime refcount to 0 in vc4_reset ( ) .
*/
int
vc4_v3d_pm_get ( struct vc4_dev * vc4 )
{
2022-06-10 13:51:49 +02:00
if ( WARN_ON_ONCE ( vc4 - > is_vc5 ) )
return - ENODEV ;
2019-02-20 13:03:42 -08:00
mutex_lock ( & vc4 - > power_lock ) ;
if ( vc4 - > power_refcount + + = = 0 ) {
int ret = pm_runtime_get_sync ( & vc4 - > v3d - > pdev - > dev ) ;
if ( ret < 0 ) {
vc4 - > power_refcount - - ;
mutex_unlock ( & vc4 - > power_lock ) ;
return ret ;
}
}
mutex_unlock ( & vc4 - > power_lock ) ;
return 0 ;
}
void
vc4_v3d_pm_put ( struct vc4_dev * vc4 )
{
2022-06-10 13:51:49 +02:00
if ( WARN_ON_ONCE ( vc4 - > is_vc5 ) )
return ;
2019-02-20 13:03:42 -08:00
mutex_lock ( & vc4 - > power_lock ) ;
if ( - - vc4 - > power_refcount = = 0 ) {
pm_runtime_mark_last_busy ( & vc4 - > v3d - > pdev - > dev ) ;
pm_runtime_put_autosuspend ( & vc4 - > v3d - > pdev - > dev ) ;
}
mutex_unlock ( & vc4 - > power_lock ) ;
}
2015-03-02 13:01:12 -08:00
static void vc4_v3d_init_hw ( struct drm_device * dev )
{
struct vc4_dev * vc4 = to_vc4_dev ( dev ) ;
/* Take all the memory that would have been reserved for user
* QPU programs , since we don ' t have an interface for running
* them , anyway .
*/
V3D_WRITE ( V3D_VPMBASE , 0 ) ;
}
2017-03-27 16:10:25 -07:00
int vc4_v3d_get_bin_slot ( struct vc4_dev * vc4 )
{
2020-10-29 20:01:03 +01:00
struct drm_device * dev = & vc4 - > base ;
2017-03-27 16:10:25 -07:00
unsigned long irqflags ;
int slot ;
uint64_t seqno = 0 ;
struct vc4_exec_info * exec ;
2022-06-10 13:51:49 +02:00
if ( WARN_ON_ONCE ( vc4 - > is_vc5 ) )
return - ENODEV ;
2017-03-27 16:10:25 -07:00
try_again :
spin_lock_irqsave ( & vc4 - > job_lock , irqflags ) ;
slot = ffs ( ~ vc4 - > bin_alloc_used ) ;
if ( slot ! = 0 ) {
/* Switch from ffs() bit index to a 0-based index. */
slot - - ;
vc4 - > bin_alloc_used | = BIT ( slot ) ;
spin_unlock_irqrestore ( & vc4 - > job_lock , irqflags ) ;
return slot ;
}
/* Couldn't find an open slot. Wait for render to complete
* and try again .
*/
exec = vc4_last_render_job ( vc4 ) ;
if ( exec )
seqno = exec - > seqno ;
spin_unlock_irqrestore ( & vc4 - > job_lock , irqflags ) ;
if ( seqno ) {
int ret = vc4_wait_for_seqno ( dev , seqno , ~ 0ull , true ) ;
if ( ret = = 0 )
goto try_again ;
return ret ;
}
return - ENOMEM ;
}
2020-11-16 17:41:09 +00:00
/*
2019-05-16 16:55:41 +02:00
* bin_bo_alloc ( ) - allocates the memory that will be used for
2017-03-27 16:10:25 -07:00
* tile binning .
*
* The binner has a limitation that the addresses in the tile state
* buffer that point into the tile alloc buffer or binner overflow
* memory only have 28 bits ( 256 MB ) , and the top 4 on the bus for
* tile alloc references end up coming from the tile state buffer ' s
* address .
*
* To work around this , we allocate a single large buffer while V3D is
* in use , make sure that it has the top 4 bits constant across its
* entire extent , and then put the tile state , tile alloc , and binner
* overflow memory inside that buffer .
*
* This creates a limitation where we may not be able to execute a job
* if it doesn ' t fit within the buffer that we allocated up front .
* However , it turns out that 16 MB is " enough for anybody " , and
* real - world applications run into allocation failures from the
2022-08-02 02:04:03 +02:00
* overall DMA pool before they make scenes complicated enough to run
2017-03-27 16:10:25 -07:00
* out of bin space .
*/
2019-05-16 16:55:41 +02:00
static int bin_bo_alloc ( struct vc4_dev * vc4 )
2017-03-27 16:10:25 -07:00
{
struct vc4_v3d * v3d = vc4 - > v3d ;
uint32_t size = 16 * 1024 * 1024 ;
int ret = 0 ;
struct list_head list ;
2019-05-16 16:55:42 +02:00
if ( ! v3d )
return - ENODEV ;
2017-03-27 16:10:25 -07:00
/* We may need to try allocating more than once to get a BO
* that doesn ' t cross 256 MB . Track the ones we ' ve allocated
* that failed so far , so that we can free them when we ' ve got
* one that succeeded ( if we freed them right away , our next
* allocation would probably be the same chunk of memory ) .
*/
INIT_LIST_HEAD ( & list ) ;
while ( true ) {
2020-10-29 20:01:03 +01:00
struct vc4_bo * bo = vc4_bo_create ( & vc4 - > base , size , true ,
drm/vc4: Add an ioctl for labeling GEM BOs for summary stats
This has proven immensely useful for debugging memory leaks and
overallocation (which is a rather serious concern on the platform,
given that we typically run at about 256MB of CMA out of up to 1GB
total memory, with framebuffers that are about 8MB ecah).
The state of the art without this is to dump debug logs from every GL
application, guess as to kernel allocations based on bo_stats, and try
to merge that all together into a global picture of memory allocation
state. With this, you can add a couple of calls to the debug build of
the 3D driver and get a pretty detailed view of GPU memory usage from
/debug/dri/0/bo_stats (or when we debug print to dmesg on allocation
failure).
The Mesa side currently labels at the gallium resource level (so you
see that a 1920x20 pixmap has been created, presumably for the window
system panel), but we could extend that to be even more useful with
glObjectLabel() names being sent all the way down to the kernel.
(partial) example of sorted debugfs output with Mesa labeling all
resources:
kernel BO cache: 16392kb BOs (3)
tiling shadow 1920x1080: 8160kb BOs (1)
resource 1920x1080@32/0: 8160kb BOs (1)
scanout resource 1920x1080@32/0: 8100kb BOs (1)
kernel: 8100kb BOs (1)
v2: Use strndup_user(), use lockdep assertion instead of just a
comment, fix an array[-1] reference, extend comment about name
freeing.
Signed-off-by: Eric Anholt <eric@anholt.net>
Link: https://patchwork.freedesktop.org/patch/msgid/20170725182718.31468-2-eric@anholt.net
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
2017-07-25 11:27:17 -07:00
VC4_BO_TYPE_BIN ) ;
2017-03-27 16:10:25 -07:00
if ( IS_ERR ( bo ) ) {
ret = PTR_ERR ( bo ) ;
dev_err ( & v3d - > pdev - > dev ,
" Failed to allocate memory for tile binning: "
2022-08-02 02:04:03 +02:00
" %d. You may need to enable DMA or give it "
2017-03-27 16:10:25 -07:00
" more memory. " ,
ret ) ;
break ;
}
/* Check if this BO won't trigger the addressing bug. */
2022-08-02 02:04:04 +02:00
if ( ( bo - > base . dma_addr & 0xf0000000 ) = =
( ( bo - > base . dma_addr + bo - > base . base . size - 1 ) & 0xf0000000 ) ) {
2017-03-27 16:10:25 -07:00
vc4 - > bin_bo = bo ;
/* Set up for allocating 512KB chunks of
* binner memory . The biggest allocation we
* need to do is for the initial tile alloc +
* tile state buffer . We can render to a
* maximum of ( ( 2048 * 2048 ) / ( 32 * 32 ) = 4096
* tiles in a frame ( until we do floating
* point rendering , at which point it would be
* 8192 ) . Tile state is 48 b / tile ( rounded to
* a page ) , and tile alloc is 32 b / tile
* ( rounded to a page ) , plus a page of extra ,
* for a total of 320 kb for our worst - case .
* We choose 512 kb so that it divides evenly
* into our 16 MB , and the rest of the 512 kb
* will be used as storage for the overflow
* from the initial 32 b CL per bin .
*/
vc4 - > bin_alloc_size = 512 * 1024 ;
vc4 - > bin_alloc_used = 0 ;
vc4 - > bin_alloc_overflow = 0 ;
WARN_ON_ONCE ( sizeof ( vc4 - > bin_alloc_used ) * 8 ! =
bo - > base . base . size / vc4 - > bin_alloc_size ) ;
2019-05-16 16:55:44 +02:00
kref_init ( & vc4 - > bin_bo_kref ) ;
/* Enable the out-of-memory interrupt to set our
* newly - allocated binner BO , potentially from an
* already - pending - but - masked interrupt .
*/
V3D_WRITE ( V3D_INTENA , V3D_INT_OUTOMEM ) ;
2017-03-27 16:10:25 -07:00
break ;
}
/* Put it on the list to free later, and try again. */
list_add ( & bo - > unref_head , & list ) ;
}
/* Free all the BOs we allocated but didn't choose. */
while ( ! list_empty ( & list ) ) {
struct vc4_bo * bo = list_last_entry ( & list ,
struct vc4_bo , unref_head ) ;
list_del ( & bo - > unref_head ) ;
2020-05-15 10:51:13 +01:00
drm_gem_object_put ( & bo - > base . base ) ;
2017-03-27 16:10:25 -07:00
}
return ret ;
}
2019-05-16 16:55:44 +02:00
int vc4_v3d_bin_bo_get ( struct vc4_dev * vc4 , bool * used )
{
int ret = 0 ;
2022-06-10 13:51:49 +02:00
if ( WARN_ON_ONCE ( vc4 - > is_vc5 ) )
return - ENODEV ;
2019-05-16 16:55:44 +02:00
mutex_lock ( & vc4 - > bin_bo_lock ) ;
if ( used & & * used )
goto complete ;
if ( vc4 - > bin_bo )
kref_get ( & vc4 - > bin_bo_kref ) ;
else
ret = bin_bo_alloc ( vc4 ) ;
if ( ret = = 0 & & used )
* used = true ;
complete :
mutex_unlock ( & vc4 - > bin_bo_lock ) ;
return ret ;
}
static void bin_bo_release ( struct kref * ref )
{
struct vc4_dev * vc4 = container_of ( ref , struct vc4_dev , bin_bo_kref ) ;
if ( WARN_ON_ONCE ( ! vc4 - > bin_bo ) )
return ;
2020-05-15 10:51:13 +01:00
drm_gem_object_put ( & vc4 - > bin_bo - > base . base ) ;
2019-05-16 16:55:44 +02:00
vc4 - > bin_bo = NULL ;
}
void vc4_v3d_bin_bo_put ( struct vc4_dev * vc4 )
{
2022-06-10 13:51:49 +02:00
if ( WARN_ON_ONCE ( vc4 - > is_vc5 ) )
return ;
2019-05-16 16:55:44 +02:00
mutex_lock ( & vc4 - > bin_bo_lock ) ;
kref_put ( & vc4 - > bin_bo_kref , bin_bo_release ) ;
mutex_unlock ( & vc4 - > bin_bo_lock ) ;
}
2016-02-05 17:41:49 -08:00
# ifdef CONFIG_PM
static int vc4_v3d_runtime_suspend ( struct device * dev )
{
struct vc4_v3d * v3d = dev_get_drvdata ( dev ) ;
struct vc4_dev * vc4 = v3d - > vc4 ;
2021-08-03 11:07:02 +02:00
vc4_irq_disable ( & vc4 - > base ) ;
2016-02-05 17:41:49 -08:00
2017-04-28 15:42:21 -07:00
clk_disable_unprepare ( v3d - > clk ) ;
2016-02-05 17:41:49 -08:00
return 0 ;
}
static int vc4_v3d_runtime_resume ( struct device * dev )
{
struct vc4_v3d * v3d = dev_get_drvdata ( dev ) ;
struct vc4_dev * vc4 = v3d - > vc4 ;
2017-03-27 16:10:25 -07:00
int ret ;
2017-04-28 15:42:21 -07:00
ret = clk_prepare_enable ( v3d - > clk ) ;
if ( ret ! = 0 )
return ret ;
2020-10-29 20:01:03 +01:00
vc4_v3d_init_hw ( & vc4 - > base ) ;
2017-12-29 17:05:43 +01:00
2021-08-03 11:07:02 +02:00
vc4_irq_enable ( & vc4 - > base ) ;
2016-02-05 17:41:49 -08:00
return 0 ;
}
# endif
2022-07-11 19:39:34 +02:00
int vc4_v3d_debugfs_init ( struct drm_minor * minor )
{
struct drm_device * drm = minor - > dev ;
struct vc4_dev * vc4 = to_vc4_dev ( drm ) ;
struct vc4_v3d * v3d = vc4 - > v3d ;
if ( ! vc4 - > v3d )
return - ENODEV ;
2022-12-19 09:06:18 -03:00
drm_debugfs_add_file ( drm , " v3d_ident " , vc4_v3d_debugfs_ident , NULL ) ;
2022-07-11 19:39:34 +02:00
2022-12-19 09:06:18 -03:00
vc4_debugfs_add_regset32 ( drm , " v3d_regs " , & v3d - > regset ) ;
2022-07-11 19:39:34 +02:00
return 0 ;
}
2015-03-02 13:01:12 -08:00
static int vc4_v3d_bind ( struct device * dev , struct device * master , void * data )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct drm_device * drm = dev_get_drvdata ( master ) ;
struct vc4_dev * vc4 = to_vc4_dev ( drm ) ;
struct vc4_v3d * v3d = NULL ;
2015-11-30 12:13:37 -08:00
int ret ;
2015-03-02 13:01:12 -08:00
v3d = devm_kzalloc ( & pdev - > dev , sizeof ( * v3d ) , GFP_KERNEL ) ;
if ( ! v3d )
return - ENOMEM ;
2016-02-05 17:41:49 -08:00
dev_set_drvdata ( dev , v3d ) ;
2015-03-02 13:01:12 -08:00
v3d - > pdev = pdev ;
v3d - > regs = vc4_ioremap_regs ( pdev , 0 ) ;
if ( IS_ERR ( v3d - > regs ) )
return PTR_ERR ( v3d - > regs ) ;
2019-02-20 13:03:38 -08:00
v3d - > regset . base = v3d - > regs ;
v3d - > regset . regs = v3d_regs ;
v3d - > regset . nregs = ARRAY_SIZE ( v3d_regs ) ;
2015-03-02 13:01:12 -08:00
vc4 - > v3d = v3d ;
2016-02-05 17:41:49 -08:00
v3d - > vc4 = vc4 ;
2015-03-02 13:01:12 -08:00
2017-04-28 15:42:21 -07:00
v3d - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( v3d - > clk ) ) {
int ret = PTR_ERR ( v3d - > clk ) ;
if ( ret = = - ENOENT ) {
/* bcm2835 didn't have a clock reference in the DT. */
ret = 0 ;
v3d - > clk = NULL ;
} else {
if ( ret ! = - EPROBE_DEFER )
dev_err ( dev , " Failed to get V3D clock: %d \n " ,
ret ) ;
return ret ;
}
}
drm/vc4: v3d: Rework the runtime_pm setup
At bind time, vc4_v3d_bind() will read a register to retrieve the v3d
version and make sure it's a version we're compatible with.
However, the v3d has an optional clock that is enabled only after the
register read-out and a power domain that wasn't enabled at all in the bind
implementation. This was working fine at boot because both were enabled,
but resulted in the version check failing if we were unbinding and
rebinding the driver because the unbinding would have turned them off.
The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the
register access to power up the power domain though.
Indeed, the runtime_resume implementation will enable the clock mentioned
above, call vc4_v3d_init_hw() and then vc4_irq_enable().
Prior to the previous patch, vc4_irq_enable() needed to occur after our
call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable()
used to call enable_irq() and vc4_irq_install() will call request_irq().
vc4_irq_install() will also do some register access, so needs the power
domain to be on. So we ended up in a situation where
vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called
before, and vc4_irq_install() needed vc4_v3d_runtime_resume().
The previous patch removed the enable_irq() call in vc4_irq_enable() and
thus removed the dependency of vc4_v3d_runtime_resume() on
vc4_irq_install().
Thus, we can now rework our bind implementation to call
pm_runtime_resume_and_get() before our register access to make sure the
power domain is on. vc4_v3d_runtime_resume() also takes care of turning the
clock on and calling vc4_v3d_init_hw() so we can remove them from bind.
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220711173939.1132294-69-maxime@cerno.tech
2022-07-11 19:39:38 +02:00
ret = platform_get_irq ( pdev , 0 ) ;
if ( ret < 0 )
return ret ;
vc4 - > irq = ret ;
2022-07-11 19:39:39 +02:00
ret = devm_pm_runtime_enable ( dev ) ;
if ( ret )
return ret ;
drm/vc4: v3d: Rework the runtime_pm setup
At bind time, vc4_v3d_bind() will read a register to retrieve the v3d
version and make sure it's a version we're compatible with.
However, the v3d has an optional clock that is enabled only after the
register read-out and a power domain that wasn't enabled at all in the bind
implementation. This was working fine at boot because both were enabled,
but resulted in the version check failing if we were unbinding and
rebinding the driver because the unbinding would have turned them off.
The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the
register access to power up the power domain though.
Indeed, the runtime_resume implementation will enable the clock mentioned
above, call vc4_v3d_init_hw() and then vc4_irq_enable().
Prior to the previous patch, vc4_irq_enable() needed to occur after our
call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable()
used to call enable_irq() and vc4_irq_install() will call request_irq().
vc4_irq_install() will also do some register access, so needs the power
domain to be on. So we ended up in a situation where
vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called
before, and vc4_irq_install() needed vc4_v3d_runtime_resume().
The previous patch removed the enable_irq() call in vc4_irq_enable() and
thus removed the dependency of vc4_v3d_runtime_resume() on
vc4_irq_install().
Thus, we can now rework our bind implementation to call
pm_runtime_resume_and_get() before our register access to make sure the
power domain is on. vc4_v3d_runtime_resume() also takes care of turning the
clock on and calling vc4_v3d_init_hw() so we can remove them from bind.
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220711173939.1132294-69-maxime@cerno.tech
2022-07-11 19:39:38 +02:00
ret = pm_runtime_resume_and_get ( dev ) ;
if ( ret )
2022-07-11 19:39:39 +02:00
return ret ;
drm/vc4: v3d: Rework the runtime_pm setup
At bind time, vc4_v3d_bind() will read a register to retrieve the v3d
version and make sure it's a version we're compatible with.
However, the v3d has an optional clock that is enabled only after the
register read-out and a power domain that wasn't enabled at all in the bind
implementation. This was working fine at boot because both were enabled,
but resulted in the version check failing if we were unbinding and
rebinding the driver because the unbinding would have turned them off.
The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the
register access to power up the power domain though.
Indeed, the runtime_resume implementation will enable the clock mentioned
above, call vc4_v3d_init_hw() and then vc4_irq_enable().
Prior to the previous patch, vc4_irq_enable() needed to occur after our
call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable()
used to call enable_irq() and vc4_irq_install() will call request_irq().
vc4_irq_install() will also do some register access, so needs the power
domain to be on. So we ended up in a situation where
vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called
before, and vc4_irq_install() needed vc4_v3d_runtime_resume().
The previous patch removed the enable_irq() call in vc4_irq_enable() and
thus removed the dependency of vc4_v3d_runtime_resume() on
vc4_irq_install().
Thus, we can now rework our bind implementation to call
pm_runtime_resume_and_get() before our register access to make sure the
power domain is on. vc4_v3d_runtime_resume() also takes care of turning the
clock on and calling vc4_v3d_init_hw() so we can remove them from bind.
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220711173939.1132294-69-maxime@cerno.tech
2022-07-11 19:39:38 +02:00
2015-03-02 13:01:12 -08:00
if ( V3D_READ ( V3D_IDENT0 ) ! = V3D_EXPECTED_IDENT0 ) {
DRM_ERROR ( " V3D_IDENT0 read 0x%08x instead of 0x%08x \n " ,
V3D_READ ( V3D_IDENT0 ) , V3D_EXPECTED_IDENT0 ) ;
drm/vc4: v3d: Rework the runtime_pm setup
At bind time, vc4_v3d_bind() will read a register to retrieve the v3d
version and make sure it's a version we're compatible with.
However, the v3d has an optional clock that is enabled only after the
register read-out and a power domain that wasn't enabled at all in the bind
implementation. This was working fine at boot because both were enabled,
but resulted in the version check failing if we were unbinding and
rebinding the driver because the unbinding would have turned them off.
The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the
register access to power up the power domain though.
Indeed, the runtime_resume implementation will enable the clock mentioned
above, call vc4_v3d_init_hw() and then vc4_irq_enable().
Prior to the previous patch, vc4_irq_enable() needed to occur after our
call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable()
used to call enable_irq() and vc4_irq_install() will call request_irq().
vc4_irq_install() will also do some register access, so needs the power
domain to be on. So we ended up in a situation where
vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called
before, and vc4_irq_install() needed vc4_v3d_runtime_resume().
The previous patch removed the enable_irq() call in vc4_irq_enable() and
thus removed the dependency of vc4_v3d_runtime_resume() on
vc4_irq_install().
Thus, we can now rework our bind implementation to call
pm_runtime_resume_and_get() before our register access to make sure the
power domain is on. vc4_v3d_runtime_resume() also takes care of turning the
clock on and calling vc4_v3d_init_hw() so we can remove them from bind.
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220711173939.1132294-69-maxime@cerno.tech
2022-07-11 19:39:38 +02:00
ret = - EINVAL ;
goto err_put_runtime_pm ;
2015-03-02 13:01:12 -08:00
}
2015-11-30 12:13:37 -08:00
/* Reset the binner overflow address/size at setup, to be sure
* we don ' t reuse an old one .
*/
V3D_WRITE ( V3D_BPOA , 0 ) ;
V3D_WRITE ( V3D_BPOS , 0 ) ;
2021-08-03 11:07:02 +02:00
ret = vc4_irq_install ( drm , vc4 - > irq ) ;
2015-11-30 12:13:37 -08:00
if ( ret ) {
DRM_ERROR ( " Failed to install IRQ handler \n " ) ;
drm/vc4: v3d: Rework the runtime_pm setup
At bind time, vc4_v3d_bind() will read a register to retrieve the v3d
version and make sure it's a version we're compatible with.
However, the v3d has an optional clock that is enabled only after the
register read-out and a power domain that wasn't enabled at all in the bind
implementation. This was working fine at boot because both were enabled,
but resulted in the version check failing if we were unbinding and
rebinding the driver because the unbinding would have turned them off.
The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the
register access to power up the power domain though.
Indeed, the runtime_resume implementation will enable the clock mentioned
above, call vc4_v3d_init_hw() and then vc4_irq_enable().
Prior to the previous patch, vc4_irq_enable() needed to occur after our
call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable()
used to call enable_irq() and vc4_irq_install() will call request_irq().
vc4_irq_install() will also do some register access, so needs the power
domain to be on. So we ended up in a situation where
vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called
before, and vc4_irq_install() needed vc4_v3d_runtime_resume().
The previous patch removed the enable_irq() call in vc4_irq_enable() and
thus removed the dependency of vc4_v3d_runtime_resume() on
vc4_irq_install().
Thus, we can now rework our bind implementation to call
pm_runtime_resume_and_get() before our register access to make sure the
power domain is on. vc4_v3d_runtime_resume() also takes care of turning the
clock on and calling vc4_v3d_init_hw() so we can remove them from bind.
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220711173939.1132294-69-maxime@cerno.tech
2022-07-11 19:39:38 +02:00
goto err_put_runtime_pm ;
2015-11-30 12:13:37 -08:00
}
2016-11-04 15:58:38 -07:00
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_set_autosuspend_delay ( dev , 40 ) ; /* a little over 2 frames. */
2016-02-05 17:41:49 -08:00
2015-03-02 13:01:12 -08:00
return 0 ;
drm/vc4: v3d: Rework the runtime_pm setup
At bind time, vc4_v3d_bind() will read a register to retrieve the v3d
version and make sure it's a version we're compatible with.
However, the v3d has an optional clock that is enabled only after the
register read-out and a power domain that wasn't enabled at all in the bind
implementation. This was working fine at boot because both were enabled,
but resulted in the version check failing if we were unbinding and
rebinding the driver because the unbinding would have turned them off.
The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the
register access to power up the power domain though.
Indeed, the runtime_resume implementation will enable the clock mentioned
above, call vc4_v3d_init_hw() and then vc4_irq_enable().
Prior to the previous patch, vc4_irq_enable() needed to occur after our
call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable()
used to call enable_irq() and vc4_irq_install() will call request_irq().
vc4_irq_install() will also do some register access, so needs the power
domain to be on. So we ended up in a situation where
vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called
before, and vc4_irq_install() needed vc4_v3d_runtime_resume().
The previous patch removed the enable_irq() call in vc4_irq_enable() and
thus removed the dependency of vc4_v3d_runtime_resume() on
vc4_irq_install().
Thus, we can now rework our bind implementation to call
pm_runtime_resume_and_get() before our register access to make sure the
power domain is on. vc4_v3d_runtime_resume() also takes care of turning the
clock on and calling vc4_v3d_init_hw() so we can remove them from bind.
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Link: https://lore.kernel.org/r/20220711173939.1132294-69-maxime@cerno.tech
2022-07-11 19:39:38 +02:00
err_put_runtime_pm :
pm_runtime_put ( dev ) ;
return ret ;
2015-03-02 13:01:12 -08:00
}
static void vc4_v3d_unbind ( struct device * dev , struct device * master ,
void * data )
{
struct drm_device * drm = dev_get_drvdata ( master ) ;
struct vc4_dev * vc4 = to_vc4_dev ( drm ) ;
2021-08-03 11:07:02 +02:00
vc4_irq_uninstall ( drm ) ;
2015-11-30 12:13:37 -08:00
/* Disable the binner's overflow memory address, so the next
* driver probe ( if any ) doesn ' t try to reuse our old
* allocation .
*/
V3D_WRITE ( V3D_BPOA , 0 ) ;
V3D_WRITE ( V3D_BPOS , 0 ) ;
2015-03-02 13:01:12 -08:00
vc4 - > v3d = NULL ;
}
2016-02-05 17:41:49 -08:00
static const struct dev_pm_ops vc4_v3d_pm_ops = {
SET_RUNTIME_PM_OPS ( vc4_v3d_runtime_suspend , vc4_v3d_runtime_resume , NULL )
} ;
2015-03-02 13:01:12 -08:00
static const struct component_ops vc4_v3d_ops = {
. bind = vc4_v3d_bind ,
. unbind = vc4_v3d_unbind ,
} ;
static int vc4_v3d_dev_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & vc4_v3d_ops ) ;
}
2023-05-07 18:26:15 +02:00
static void vc4_v3d_dev_remove ( struct platform_device * pdev )
2015-03-02 13:01:12 -08:00
{
component_del ( & pdev - > dev , & vc4_v3d_ops ) ;
}
2019-04-01 11:35:59 -07:00
const struct of_device_id vc4_v3d_dt_match [ ] = {
2016-03-04 12:32:07 -08:00
{ . compatible = " brcm,bcm2835-v3d " } ,
2017-04-28 15:42:23 -07:00
{ . compatible = " brcm,cygnus-v3d " } ,
2015-03-02 13:01:12 -08:00
{ . compatible = " brcm,vc4-v3d " } ,
{ }
} ;
struct platform_driver vc4_v3d_driver = {
. probe = vc4_v3d_dev_probe ,
2023-05-07 18:26:15 +02:00
. remove_new = vc4_v3d_dev_remove ,
2015-03-02 13:01:12 -08:00
. driver = {
. name = " vc4_v3d " ,
. of_match_table = vc4_v3d_dt_match ,
2016-02-05 17:41:49 -08:00
. pm = & vc4_v3d_pm_ops ,
2015-03-02 13:01:12 -08:00
} ,
} ;