2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-12-14 13:16:13 +02:00
/*
* Copyright ( c ) 2015 , NVIDIA Corporation .
*/
# include <linux/clk.h>
2019-08-04 11:41:30 +02:00
# include <linux/delay.h>
2016-12-14 13:16:13 +02:00
# include <linux/host1x.h>
# include <linux/iommu.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/reset.h>
# include <soc/tegra/pmc.h>
# include "drm.h"
# include "falcon.h"
# include "vic.h"
struct vic_config {
const char * firmware ;
2018-05-16 17:08:04 +02:00
unsigned int version ;
2019-02-01 14:28:36 +01:00
bool supports_sid ;
2016-12-14 13:16:13 +02:00
} ;
struct vic {
struct falcon falcon ;
bool booted ;
void __iomem * regs ;
struct tegra_drm_client client ;
struct host1x_channel * channel ;
struct device * dev ;
struct clk * clk ;
2018-11-23 13:06:37 +01:00
struct reset_control * rst ;
2016-12-14 13:16:13 +02:00
/* Platform configuration */
const struct vic_config * config ;
} ;
static inline struct vic * to_vic ( struct tegra_drm_client * client )
{
return container_of ( client , struct vic , client ) ;
}
static void vic_writel ( struct vic * vic , u32 value , unsigned int offset )
{
writel ( value , vic - > regs + offset ) ;
}
static int vic_runtime_resume ( struct device * dev )
{
struct vic * vic = dev_get_drvdata ( dev ) ;
2018-11-23 13:06:37 +01:00
int err ;
err = clk_prepare_enable ( vic - > clk ) ;
if ( err < 0 )
return err ;
usleep_range ( 10 , 20 ) ;
err = reset_control_deassert ( vic - > rst ) ;
if ( err < 0 )
goto disable ;
usleep_range ( 10 , 20 ) ;
return 0 ;
2016-12-14 13:16:13 +02:00
2018-11-23 13:06:37 +01:00
disable :
clk_disable_unprepare ( vic - > clk ) ;
return err ;
2016-12-14 13:16:13 +02:00
}
static int vic_runtime_suspend ( struct device * dev )
{
struct vic * vic = dev_get_drvdata ( dev ) ;
2018-11-23 13:06:37 +01:00
int err ;
err = reset_control_assert ( vic - > rst ) ;
if ( err < 0 )
return err ;
usleep_range ( 2000 , 4000 ) ;
2016-12-14 13:16:13 +02:00
clk_disable_unprepare ( vic - > clk ) ;
vic - > booted = false ;
return 0 ;
}
static int vic_boot ( struct vic * vic )
{
2019-02-08 13:09:49 +01:00
# ifdef CONFIG_IOMMU_API
struct iommu_fwspec * spec = dev_iommu_fwspec_get ( vic - > dev ) ;
# endif
2016-12-14 13:16:13 +02:00
u32 fce_ucode_size , fce_bin_data_offset ;
void * hdr ;
int err = 0 ;
if ( vic - > booted )
return 0 ;
2019-02-18 12:00:50 +01:00
# ifdef CONFIG_IOMMU_API
2019-02-08 13:09:49 +01:00
if ( vic - > config - > supports_sid & & spec ) {
2019-02-01 14:28:36 +01:00
u32 value ;
value = TRANSCFG_ATT ( 1 , TRANSCFG_SID_FALCON ) |
TRANSCFG_ATT ( 0 , TRANSCFG_SID_HW ) ;
vic_writel ( vic , value , VIC_TFBIF_TRANSCFG ) ;
2019-02-08 13:09:49 +01:00
if ( spec - > num_ids > 0 ) {
2019-02-01 14:28:36 +01:00
value = spec - > ids [ 0 ] & 0xffff ;
vic_writel ( vic , value , VIC_THI_STREAMID0 ) ;
vic_writel ( vic , value , VIC_THI_STREAMID1 ) ;
}
}
2019-02-18 12:00:50 +01:00
# endif
2019-02-01 14:28:36 +01:00
2016-12-14 13:16:13 +02:00
/* setup clockgating registers */
vic_writel ( vic , CG_IDLE_CG_DLY_CNT ( 4 ) |
CG_IDLE_CG_EN |
CG_WAKEUP_DLY_CNT ( 4 ) ,
NV_PVIC_MISC_PRI_VIC_CG ) ;
err = falcon_boot ( & vic - > falcon ) ;
if ( err < 0 )
return err ;
2019-10-28 13:37:16 +01:00
hdr = vic - > falcon . firmware . virt ;
2016-12-14 13:16:13 +02:00
fce_bin_data_offset = * ( u32 * ) ( hdr + VIC_UCODE_FCE_DATA_OFFSET ) ;
2019-10-28 13:37:16 +01:00
hdr = vic - > falcon . firmware . virt +
2016-12-14 13:16:13 +02:00
* ( u32 * ) ( hdr + VIC_UCODE_FCE_HEADER_OFFSET ) ;
fce_ucode_size = * ( u32 * ) ( hdr + FCE_UCODE_SIZE_OFFSET ) ;
falcon_execute_method ( & vic - > falcon , VIC_SET_APPLICATION_ID , 1 ) ;
falcon_execute_method ( & vic - > falcon , VIC_SET_FCE_UCODE_SIZE ,
fce_ucode_size ) ;
falcon_execute_method ( & vic - > falcon , VIC_SET_FCE_UCODE_OFFSET ,
2019-10-28 13:37:16 +01:00
( vic - > falcon . firmware . iova + fce_bin_data_offset )
2016-12-14 13:16:13 +02:00
> > 8 ) ;
err = falcon_wait_idle ( & vic - > falcon ) ;
if ( err < 0 ) {
dev_err ( vic - > dev ,
" failed to set application ID and FCE base \n " ) ;
return err ;
}
vic - > booted = true ;
return 0 ;
}
static int vic_init ( struct host1x_client * client )
{
struct tegra_drm_client * drm = host1x_to_drm_client ( client ) ;
struct drm_device * dev = dev_get_drvdata ( client - > parent ) ;
struct tegra_drm * tegra = dev - > dev_private ;
struct vic * vic = to_vic ( drm ) ;
int err ;
2019-10-28 13:37:08 +01:00
err = host1x_client_iommu_attach ( client ) ;
2019-02-08 14:35:13 +01:00
if ( err < 0 ) {
2019-02-08 14:15:44 +01:00
dev_err ( vic - > dev , " failed to attach to domain: %d \n " , err ) ;
return err ;
2016-12-14 13:16:13 +02:00
}
2018-06-18 14:01:51 +02:00
vic - > channel = host1x_channel_request ( client ) ;
2016-12-14 13:16:13 +02:00
if ( ! vic - > channel ) {
err = - ENOMEM ;
2017-10-12 17:43:33 +02:00
goto detach ;
2016-12-14 13:16:13 +02:00
}
2017-08-30 12:48:31 +02:00
client - > syncpts [ 0 ] = host1x_syncpt_request ( client , 0 ) ;
2016-12-14 13:16:13 +02:00
if ( ! client - > syncpts [ 0 ] ) {
err = - ENOMEM ;
goto free_channel ;
}
err = tegra_drm_register_client ( tegra , drm ) ;
if ( err < 0 )
goto free_syncpt ;
2019-09-09 14:25:45 +02:00
/*
* Inherit the DMA parameters ( such as maximum segment size ) from the
* parent device .
*/
client - > dev - > dma_parms = client - > parent - > dma_parms ;
2016-12-14 13:16:13 +02:00
return 0 ;
free_syncpt :
host1x_syncpt_free ( client - > syncpts [ 0 ] ) ;
free_channel :
2017-06-15 02:18:42 +03:00
host1x_channel_put ( vic - > channel ) ;
2017-10-12 17:43:33 +02:00
detach :
2019-02-08 14:35:13 +01:00
host1x_client_iommu_detach ( client ) ;
2016-12-14 13:16:13 +02:00
return err ;
}
static int vic_exit ( struct host1x_client * client )
{
struct tegra_drm_client * drm = host1x_to_drm_client ( client ) ;
struct drm_device * dev = dev_get_drvdata ( client - > parent ) ;
struct tegra_drm * tegra = dev - > dev_private ;
struct vic * vic = to_vic ( drm ) ;
int err ;
2019-09-09 14:25:45 +02:00
/* avoid a dangling pointer just in case this disappears */
client - > dev - > dma_parms = NULL ;
2016-12-14 13:16:13 +02:00
err = tegra_drm_unregister_client ( tegra , drm ) ;
if ( err < 0 )
return err ;
host1x_syncpt_free ( client - > syncpts [ 0 ] ) ;
2017-06-15 02:18:42 +03:00
host1x_channel_put ( vic - > channel ) ;
2019-02-08 14:35:13 +01:00
host1x_client_iommu_detach ( client ) ;
2016-12-14 13:16:13 +02:00
2019-10-28 13:37:16 +01:00
if ( client - > group ) {
dma_unmap_single ( vic - > dev , vic - > falcon . firmware . phys ,
vic - > falcon . firmware . size , DMA_TO_DEVICE ) ;
2019-10-28 13:37:15 +01:00
tegra_drm_free ( tegra , vic - > falcon . firmware . size ,
2019-10-28 13:37:16 +01:00
vic - > falcon . firmware . virt ,
vic - > falcon . firmware . iova ) ;
} else {
2019-10-28 13:37:15 +01:00
dma_free_coherent ( vic - > dev , vic - > falcon . firmware . size ,
2019-10-28 13:37:16 +01:00
vic - > falcon . firmware . virt ,
vic - > falcon . firmware . iova ) ;
}
2019-10-28 13:37:15 +01:00
2016-12-14 13:16:13 +02:00
return 0 ;
}
static const struct host1x_client_ops vic_client_ops = {
. init = vic_init ,
. exit = vic_exit ,
} ;
2019-02-01 14:28:32 +01:00
static int vic_load_firmware ( struct vic * vic )
{
2019-10-28 13:37:15 +01:00
struct host1x_client * client = & vic - > client . base ;
struct tegra_drm * tegra = vic - > client . drm ;
2019-10-28 13:37:16 +01:00
dma_addr_t iova ;
2019-10-28 13:37:15 +01:00
size_t size ;
void * virt ;
2019-02-01 14:28:32 +01:00
int err ;
2019-10-28 13:37:16 +01:00
if ( vic - > falcon . firmware . virt )
2019-02-01 14:28:32 +01:00
return 0 ;
err = falcon_read_firmware ( & vic - > falcon , vic - > config - > firmware ) ;
if ( err < 0 )
2019-10-28 13:37:15 +01:00
return err ;
size = vic - > falcon . firmware . size ;
if ( ! client - > group ) {
2019-10-28 13:37:16 +01:00
virt = dma_alloc_coherent ( vic - > dev , size , & iova , GFP_KERNEL ) ;
2019-10-28 13:37:15 +01:00
2019-10-28 13:37:16 +01:00
err = dma_mapping_error ( vic - > dev , iova ) ;
2019-10-28 13:37:15 +01:00
if ( err < 0 )
return err ;
} else {
2019-10-28 13:37:16 +01:00
virt = tegra_drm_alloc ( tegra , size , & iova ) ;
2019-10-28 13:37:15 +01:00
}
2019-10-28 13:37:16 +01:00
vic - > falcon . firmware . virt = virt ;
vic - > falcon . firmware . iova = iova ;
2019-02-01 14:28:32 +01:00
err = falcon_load_firmware ( & vic - > falcon ) ;
if ( err < 0 )
goto cleanup ;
2019-10-28 13:37:15 +01:00
/*
* In this case we have received an IOVA from the shared domain , so we
* need to make sure to get the physical address so that the DMA API
* knows what memory pages to flush the cache for .
*/
if ( client - > group ) {
2019-10-28 13:37:16 +01:00
dma_addr_t phys ;
2019-10-28 13:37:15 +01:00
phys = dma_map_single ( vic - > dev , virt , size , DMA_TO_DEVICE ) ;
err = dma_mapping_error ( vic - > dev , phys ) ;
if ( err < 0 )
goto cleanup ;
2019-10-28 13:37:16 +01:00
vic - > falcon . firmware . phys = phys ;
2019-10-28 13:37:15 +01:00
}
2019-02-01 14:28:32 +01:00
return 0 ;
cleanup :
2019-10-28 13:37:15 +01:00
if ( ! client - > group )
2019-10-28 13:37:16 +01:00
dma_free_coherent ( vic - > dev , size , virt , iova ) ;
2019-10-28 13:37:15 +01:00
else
2019-10-28 13:37:16 +01:00
tegra_drm_free ( tegra , size , virt , iova ) ;
2019-10-28 13:37:15 +01:00
2019-02-01 14:28:32 +01:00
return err ;
}
2016-12-14 13:16:13 +02:00
static int vic_open_channel ( struct tegra_drm_client * client ,
struct tegra_drm_context * context )
{
struct vic * vic = to_vic ( client ) ;
int err ;
err = pm_runtime_get_sync ( vic - > dev ) ;
if ( err < 0 )
return err ;
2019-02-01 14:28:32 +01:00
err = vic_load_firmware ( vic ) ;
if ( err < 0 )
goto rpm_put ;
2016-12-14 13:16:13 +02:00
err = vic_boot ( vic ) ;
2019-02-01 14:28:32 +01:00
if ( err < 0 )
goto rpm_put ;
2016-12-14 13:16:13 +02:00
context - > channel = host1x_channel_get ( vic - > channel ) ;
if ( ! context - > channel ) {
2019-02-01 14:28:32 +01:00
err = - ENOMEM ;
goto rpm_put ;
2016-12-14 13:16:13 +02:00
}
return 0 ;
2019-02-01 14:28:32 +01:00
rpm_put :
pm_runtime_put ( vic - > dev ) ;
return err ;
2016-12-14 13:16:13 +02:00
}
static void vic_close_channel ( struct tegra_drm_context * context )
{
struct vic * vic = to_vic ( context - > client ) ;
host1x_channel_put ( context - > channel ) ;
pm_runtime_put ( vic - > dev ) ;
}
static const struct tegra_drm_client_ops vic_ops = {
. open_channel = vic_open_channel ,
. close_channel = vic_close_channel ,
. submit = tegra_drm_submit ,
} ;
2017-07-11 10:39:04 +02:00
# define NVIDIA_TEGRA_124_VIC_FIRMWARE "nvidia / tegra124 / vic03_ucode.bin"
2016-12-14 13:16:13 +02:00
static const struct vic_config vic_t124_config = {
2017-07-11 10:39:04 +02:00
. firmware = NVIDIA_TEGRA_124_VIC_FIRMWARE ,
2018-05-16 17:08:04 +02:00
. version = 0x40 ,
2019-02-01 14:28:36 +01:00
. supports_sid = false ,
2016-12-14 13:16:13 +02:00
} ;
2017-07-11 10:39:04 +02:00
# define NVIDIA_TEGRA_210_VIC_FIRMWARE "nvidia / tegra210 / vic04_ucode.bin"
2016-12-14 13:16:13 +02:00
static const struct vic_config vic_t210_config = {
2017-07-11 10:39:04 +02:00
. firmware = NVIDIA_TEGRA_210_VIC_FIRMWARE ,
2018-05-16 17:08:04 +02:00
. version = 0x21 ,
2019-02-01 14:28:36 +01:00
. supports_sid = false ,
2016-12-14 13:16:13 +02:00
} ;
2017-09-05 11:43:06 +03:00
# define NVIDIA_TEGRA_186_VIC_FIRMWARE "nvidia / tegra186 / vic04_ucode.bin"
static const struct vic_config vic_t186_config = {
. firmware = NVIDIA_TEGRA_186_VIC_FIRMWARE ,
2018-05-16 17:08:04 +02:00
. version = 0x18 ,
2019-02-01 14:28:36 +01:00
. supports_sid = true ,
2017-09-05 11:43:06 +03:00
} ;
2018-10-26 10:59:38 +02:00
# define NVIDIA_TEGRA_194_VIC_FIRMWARE "nvidia / tegra194 / vic.bin"
static const struct vic_config vic_t194_config = {
. firmware = NVIDIA_TEGRA_194_VIC_FIRMWARE ,
. version = 0x19 ,
2019-02-01 14:28:36 +01:00
. supports_sid = true ,
2018-10-26 10:59:38 +02:00
} ;
2016-12-14 13:16:13 +02:00
static const struct of_device_id vic_match [ ] = {
{ . compatible = " nvidia,tegra124-vic " , . data = & vic_t124_config } ,
{ . compatible = " nvidia,tegra210-vic " , . data = & vic_t210_config } ,
2017-09-05 11:43:06 +03:00
{ . compatible = " nvidia,tegra186-vic " , . data = & vic_t186_config } ,
2018-10-26 10:59:38 +02:00
{ . compatible = " nvidia,tegra194-vic " , . data = & vic_t194_config } ,
2016-12-14 13:16:13 +02:00
{ } ,
} ;
static int vic_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct host1x_syncpt * * syncpts ;
struct resource * regs ;
struct vic * vic ;
int err ;
2019-02-08 13:10:41 +01:00
/* inherit DMA mask from host1x parent */
err = dma_coerce_mask_and_coherent ( dev , * dev - > parent - > dma_mask ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to set DMA mask: %d \n " , err ) ;
return err ;
}
2016-12-14 13:16:13 +02:00
vic = devm_kzalloc ( dev , sizeof ( * vic ) , GFP_KERNEL ) ;
if ( ! vic )
return - ENOMEM ;
2017-08-21 18:03:27 +02:00
vic - > config = of_device_get_match_data ( dev ) ;
2016-12-14 13:16:13 +02:00
syncpts = devm_kzalloc ( dev , sizeof ( * syncpts ) , GFP_KERNEL ) ;
if ( ! syncpts )
return - ENOMEM ;
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! regs ) {
dev_err ( & pdev - > dev , " failed to get registers \n " ) ;
return - ENXIO ;
}
vic - > regs = devm_ioremap_resource ( dev , regs ) ;
if ( IS_ERR ( vic - > regs ) )
return PTR_ERR ( vic - > regs ) ;
vic - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( vic - > clk ) ) {
dev_err ( & pdev - > dev , " failed to get clock \n " ) ;
return PTR_ERR ( vic - > clk ) ;
}
2018-11-23 13:06:37 +01:00
if ( ! dev - > pm_domain ) {
vic - > rst = devm_reset_control_get ( dev , " vic " ) ;
if ( IS_ERR ( vic - > rst ) ) {
dev_err ( & pdev - > dev , " failed to get reset \n " ) ;
return PTR_ERR ( vic - > rst ) ;
}
}
2016-12-14 13:16:13 +02:00
vic - > falcon . dev = dev ;
vic - > falcon . regs = vic - > regs ;
err = falcon_init ( & vic - > falcon ) ;
if ( err < 0 )
return err ;
platform_set_drvdata ( pdev , vic ) ;
INIT_LIST_HEAD ( & vic - > client . base . list ) ;
vic - > client . base . ops = & vic_client_ops ;
vic - > client . base . dev = dev ;
vic - > client . base . class = HOST1X_CLASS_VIC ;
vic - > client . base . syncpts = syncpts ;
vic - > client . base . num_syncpts = 1 ;
vic - > dev = dev ;
INIT_LIST_HEAD ( & vic - > client . list ) ;
2018-05-16 17:08:04 +02:00
vic - > client . version = vic - > config - > version ;
2016-12-14 13:16:13 +02:00
vic - > client . ops = & vic_ops ;
err = host1x_client_register ( & vic - > client . base ) ;
if ( err < 0 ) {
dev_err ( dev , " failed to register host1x client: %d \n " , err ) ;
goto exit_falcon ;
}
pm_runtime_enable ( & pdev - > dev ) ;
if ( ! pm_runtime_enabled ( & pdev - > dev ) ) {
err = vic_runtime_resume ( & pdev - > dev ) ;
if ( err < 0 )
goto unregister_client ;
}
return 0 ;
unregister_client :
host1x_client_unregister ( & vic - > client . base ) ;
exit_falcon :
falcon_exit ( & vic - > falcon ) ;
return err ;
}
static int vic_remove ( struct platform_device * pdev )
{
struct vic * vic = platform_get_drvdata ( pdev ) ;
int err ;
err = host1x_client_unregister ( & vic - > client . base ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to unregister host1x client: %d \n " ,
err ) ;
return err ;
}
if ( pm_runtime_enabled ( & pdev - > dev ) )
pm_runtime_disable ( & pdev - > dev ) ;
else
vic_runtime_suspend ( & pdev - > dev ) ;
falcon_exit ( & vic - > falcon ) ;
return 0 ;
}
static const struct dev_pm_ops vic_pm_ops = {
SET_RUNTIME_PM_OPS ( vic_runtime_suspend , vic_runtime_resume , NULL )
} ;
struct platform_driver tegra_vic_driver = {
. driver = {
. name = " tegra-vic " ,
. of_match_table = vic_match ,
. pm = & vic_pm_ops
} ,
. probe = vic_probe ,
. remove = vic_remove ,
} ;
2017-07-11 10:39:04 +02:00
# if IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)
MODULE_FIRMWARE ( NVIDIA_TEGRA_124_VIC_FIRMWARE ) ;
# endif
# if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
MODULE_FIRMWARE ( NVIDIA_TEGRA_210_VIC_FIRMWARE ) ;
# endif
2017-09-05 11:43:06 +03:00
# if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
MODULE_FIRMWARE ( NVIDIA_TEGRA_186_VIC_FIRMWARE ) ;
# endif
2018-10-26 10:59:38 +02:00
# if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
MODULE_FIRMWARE ( NVIDIA_TEGRA_194_VIC_FIRMWARE ) ;
# endif