2016-12-14 13:16:13 +02:00
/*
* Copyright ( c ) 2015 , NVIDIA Corporation .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/clk.h>
# 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 ;
} ;
struct vic {
struct falcon falcon ;
bool booted ;
void __iomem * regs ;
struct tegra_drm_client client ;
struct host1x_channel * channel ;
struct iommu_domain * domain ;
struct device * dev ;
struct clk * clk ;
/* 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 ) ;
return clk_prepare_enable ( vic - > clk ) ;
}
static int vic_runtime_suspend ( struct device * dev )
{
struct vic * vic = dev_get_drvdata ( dev ) ;
clk_disable_unprepare ( vic - > clk ) ;
vic - > booted = false ;
return 0 ;
}
static int vic_boot ( struct vic * vic )
{
u32 fce_ucode_size , fce_bin_data_offset ;
void * hdr ;
int err = 0 ;
if ( vic - > booted )
return 0 ;
/* 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 ;
hdr = vic - > falcon . firmware . vaddr ;
fce_bin_data_offset = * ( u32 * ) ( hdr + VIC_UCODE_FCE_DATA_OFFSET ) ;
hdr = vic - > falcon . firmware . vaddr +
* ( 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 ,
( vic - > falcon . firmware . paddr + fce_bin_data_offset )
> > 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 void * vic_falcon_alloc ( struct falcon * falcon , size_t size ,
2017-11-08 13:22:17 +01:00
dma_addr_t * iova )
2016-12-14 13:16:13 +02:00
{
struct tegra_drm * tegra = falcon - > data ;
return tegra_drm_alloc ( tegra , size , iova ) ;
}
static void vic_falcon_free ( struct falcon * falcon , size_t size ,
dma_addr_t iova , void * va )
{
struct tegra_drm * tegra = falcon - > data ;
return tegra_drm_free ( tegra , size , va , iova ) ;
}
static const struct falcon_ops vic_falcon_ops = {
. alloc = vic_falcon_alloc ,
. free = vic_falcon_free
} ;
static int vic_init ( struct host1x_client * client )
{
struct tegra_drm_client * drm = host1x_to_drm_client ( client ) ;
2017-10-12 17:43:33 +02:00
struct iommu_group * group = iommu_group_get ( client - > dev ) ;
2016-12-14 13:16:13 +02:00
struct drm_device * dev = dev_get_drvdata ( client - > parent ) ;
struct tegra_drm * tegra = dev - > dev_private ;
struct vic * vic = to_vic ( drm ) ;
int err ;
2017-10-12 17:43:33 +02:00
if ( group & & tegra - > domain ) {
err = iommu_attach_group ( tegra - > domain , group ) ;
2016-12-14 13:16:13 +02:00
if ( err < 0 ) {
dev_err ( vic - > dev , " failed to attach to domain: %d \n " ,
err ) ;
return err ;
}
vic - > domain = tegra - > domain ;
}
if ( ! vic - > falcon . data ) {
vic - > falcon . data = tegra ;
err = falcon_load_firmware ( & vic - > falcon ) ;
if ( err < 0 )
2017-10-12 17:43:33 +02:00
goto detach ;
2016-12-14 13:16:13 +02:00
}
vic - > channel = host1x_channel_request ( client - > dev ) ;
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 ;
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 :
if ( group & & tegra - > domain )
iommu_detach_group ( tegra - > domain , group ) ;
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 ) ;
2017-10-12 17:43:33 +02:00
struct iommu_group * group = iommu_group_get ( client - > dev ) ;
2016-12-14 13:16:13 +02:00
struct drm_device * dev = dev_get_drvdata ( client - > parent ) ;
struct tegra_drm * tegra = dev - > dev_private ;
struct vic * vic = to_vic ( drm ) ;
int err ;
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 ) ;
2016-12-14 13:16:13 +02:00
if ( vic - > domain ) {
2017-10-12 17:43:33 +02:00
iommu_detach_group ( vic - > domain , group ) ;
2016-12-14 13:16:13 +02:00
vic - > domain = NULL ;
}
return 0 ;
}
static const struct host1x_client_ops vic_client_ops = {
. init = vic_init ,
. exit = vic_exit ,
} ;
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 ;
err = vic_boot ( vic ) ;
if ( err < 0 ) {
pm_runtime_put ( vic - > dev ) ;
return err ;
}
context - > channel = host1x_channel_get ( vic - > channel ) ;
if ( ! context - > channel ) {
pm_runtime_put ( vic - > dev ) ;
return - ENOMEM ;
}
return 0 ;
}
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 ,
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 ,
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 ,
} ;
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 } ,
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 ;
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 ) ;
}
vic - > falcon . dev = dev ;
vic - > falcon . regs = vic - > regs ;
vic - > falcon . ops = & vic_falcon_ops ;
err = falcon_init ( & vic - > falcon ) ;
if ( err < 0 )
return err ;
2017-08-21 18:03:27 +02:00
err = falcon_read_firmware ( & vic - > falcon , vic - > config - > firmware ) ;
2016-12-14 13:16:13 +02:00
if ( err < 0 )
goto exit_falcon ;
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 ) ;
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 ) ;
platform_set_drvdata ( pdev , NULL ) ;
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