2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2013-03-22 16:34:09 +02:00
/*
* Copyright ( c ) 2012 - 2013 , NVIDIA Corporation .
*/
# include <linux/clk.h>
2018-05-04 02:47:20 +03:00
# include <linux/iommu.h>
2019-08-04 11:41:30 +02:00
# include <linux/module.h>
2018-05-16 17:06:36 +02:00
# include <linux/of_device.h>
2013-03-22 16:34:09 +02:00
# include "drm.h"
# include "gem.h"
2013-10-07 09:55:57 +02:00
# include "gr2d.h"
2013-09-26 16:09:43 +02:00
2018-05-16 17:06:36 +02:00
struct gr2d_soc {
unsigned int version ;
} ;
2013-03-22 16:34:09 +02:00
struct gr2d {
2013-09-24 15:35:40 +02:00
struct tegra_drm_client client ;
2013-03-22 16:34:09 +02:00
struct host1x_channel * channel ;
2013-09-26 16:09:43 +02:00
struct clk * clk ;
2018-05-16 17:06:36 +02:00
const struct gr2d_soc * soc ;
2013-09-26 16:09:43 +02:00
DECLARE_BITMAP ( addr_regs , GR2D_NUM_REGS ) ;
2013-03-22 16:34:09 +02:00
} ;
2013-09-24 15:35:40 +02:00
static inline struct gr2d * to_gr2d ( struct tegra_drm_client * client )
2013-03-22 16:34:09 +02:00
{
return container_of ( client , struct gr2d , client ) ;
}
2013-10-14 14:43:22 +02:00
static int gr2d_init ( struct host1x_client * client )
2013-03-22 16:34:09 +02:00
{
2013-10-14 14:43:22 +02:00
struct tegra_drm_client * drm = host1x_to_drm_client ( client ) ;
2014-05-22 09:57:15 +02:00
struct drm_device * dev = dev_get_drvdata ( client - > parent ) ;
2013-10-14 15:21:55 +03:00
unsigned long flags = HOST1X_SYNCPT_HAS_BASE ;
2013-10-14 14:43:22 +02:00
struct gr2d * gr2d = to_gr2d ( drm ) ;
2018-05-04 02:47:20 +03:00
int err ;
2013-10-14 14:43:22 +02:00
2018-06-18 14:01:51 +02:00
gr2d - > channel = host1x_channel_request ( client ) ;
2013-10-14 14:43:22 +02:00
if ( ! gr2d - > channel )
return - ENOMEM ;
2017-08-30 12:48:31 +02:00
client - > syncpts [ 0 ] = host1x_syncpt_request ( client , flags ) ;
2013-10-14 14:43:22 +02:00
if ( ! client - > syncpts [ 0 ] ) {
2018-05-04 14:58:26 +02:00
err = - ENOMEM ;
dev_err ( client - > dev , " failed to request syncpoint: %d \n " , err ) ;
goto put ;
2013-10-14 14:43:22 +02:00
}
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 ) {
2018-05-04 15:02:24 +02:00
dev_err ( client - > dev , " failed to attach to domain: %d \n " , err ) ;
goto free ;
2018-05-04 02:47:20 +03:00
}
2018-05-04 15:02:24 +02:00
err = tegra_drm_register_client ( dev - > dev_private , drm ) ;
2018-05-04 14:58:26 +02:00
if ( err < 0 ) {
dev_err ( client - > dev , " failed to register client: %d \n " , err ) ;
goto detach ;
}
return 0 ;
detach :
2019-02-08 14:35:13 +01:00
host1x_client_iommu_detach ( client ) ;
2018-05-04 14:58:26 +02:00
free :
host1x_syncpt_free ( client - > syncpts [ 0 ] ) ;
put :
host1x_channel_put ( gr2d - > channel ) ;
return err ;
2013-03-22 16:34:09 +02:00
}
2013-10-14 14:43:22 +02:00
static int gr2d_exit ( struct host1x_client * client )
2013-03-22 16:34:09 +02:00
{
2013-10-14 14:43:22 +02:00
struct tegra_drm_client * drm = host1x_to_drm_client ( client ) ;
2014-05-22 09:57:15 +02:00
struct drm_device * dev = dev_get_drvdata ( client - > parent ) ;
2018-05-04 02:47:20 +03:00
struct tegra_drm * tegra = dev - > dev_private ;
2013-10-14 14:43:22 +02:00
struct gr2d * gr2d = to_gr2d ( drm ) ;
int err ;
2018-05-04 02:47:20 +03:00
err = tegra_drm_unregister_client ( tegra , drm ) ;
2013-10-14 14:43:22 +02:00
if ( err < 0 )
return err ;
2019-02-08 14:35:13 +01:00
host1x_client_iommu_detach ( client ) ;
2013-10-14 14:43:22 +02:00
host1x_syncpt_free ( client - > syncpts [ 0 ] ) ;
2017-06-15 02:18:42 +03:00
host1x_channel_put ( gr2d - > channel ) ;
2013-10-14 14:43:22 +02:00
2013-03-22 16:34:09 +02:00
return 0 ;
}
2013-09-24 15:35:40 +02:00
static const struct host1x_client_ops gr2d_client_ops = {
2013-10-14 14:43:22 +02:00
. init = gr2d_init ,
. exit = gr2d_exit ,
2013-09-24 15:35:40 +02:00
} ;
static int gr2d_open_channel ( struct tegra_drm_client * client ,
2013-09-26 16:08:22 +02:00
struct tegra_drm_context * context )
2013-03-22 16:34:09 +02:00
{
struct gr2d * gr2d = to_gr2d ( client ) ;
context - > channel = host1x_channel_get ( gr2d - > channel ) ;
if ( ! context - > channel )
return - ENOMEM ;
return 0 ;
}
2013-09-26 16:08:22 +02:00
static void gr2d_close_channel ( struct tegra_drm_context * context )
2013-03-22 16:34:09 +02:00
{
host1x_channel_put ( context - > channel ) ;
}
2013-09-26 16:09:43 +02:00
static int gr2d_is_addr_reg ( struct device * dev , u32 class , u32 offset )
{
struct gr2d * gr2d = dev_get_drvdata ( dev ) ;
switch ( class ) {
case HOST1X_CLASS_HOST1X :
if ( offset = = 0x2b )
return 1 ;
break ;
case HOST1X_CLASS_GR2D :
case HOST1X_CLASS_GR2D_SB :
if ( offset > = GR2D_NUM_REGS )
break ;
if ( test_bit ( offset , gr2d - > addr_regs ) )
return 1 ;
break ;
}
return 0 ;
}
2017-06-15 02:18:37 +03:00
static int gr2d_is_valid_class ( u32 class )
{
return ( class = = HOST1X_CLASS_GR2D | |
class = = HOST1X_CLASS_GR2D_SB ) ;
}
2013-09-24 15:35:40 +02:00
static const struct tegra_drm_client_ops gr2d_ops = {
2013-03-22 16:34:09 +02:00
. open_channel = gr2d_open_channel ,
. close_channel = gr2d_close_channel ,
2013-10-10 11:00:33 +02:00
. is_addr_reg = gr2d_is_addr_reg ,
2017-06-15 02:18:37 +03:00
. is_valid_class = gr2d_is_valid_class ,
2013-10-10 11:00:33 +02:00
. submit = tegra_drm_submit ,
2013-03-22 16:34:09 +02:00
} ;
2018-05-16 17:06:36 +02:00
static const struct gr2d_soc tegra20_gr2d_soc = {
. version = 0x20 ,
} ;
static const struct gr2d_soc tegra30_gr2d_soc = {
. version = 0x30 ,
} ;
2013-03-22 16:34:09 +02:00
static const struct of_device_id gr2d_match [ ] = {
2018-05-16 17:06:36 +02:00
{ . compatible = " nvidia,tegra30-gr2d " , . data = & tegra20_gr2d_soc } ,
{ . compatible = " nvidia,tegra20-gr2d " , . data = & tegra30_gr2d_soc } ,
2013-03-22 16:34:09 +02:00
{ } ,
} ;
2014-06-18 16:21:55 -06:00
MODULE_DEVICE_TABLE ( of , gr2d_match ) ;
2013-03-22 16:34:09 +02:00
2013-09-26 16:09:43 +02:00
static const u32 gr2d_addr_regs [ ] = {
2013-10-07 09:55:57 +02:00
GR2D_UA_BASE_ADDR ,
GR2D_VA_BASE_ADDR ,
GR2D_PAT_BASE_ADDR ,
GR2D_DSTA_BASE_ADDR ,
GR2D_DSTB_BASE_ADDR ,
GR2D_DSTC_BASE_ADDR ,
GR2D_SRCA_BASE_ADDR ,
GR2D_SRCB_BASE_ADDR ,
GR2D_SRC_BASE_ADDR_SB ,
GR2D_DSTA_BASE_ADDR_SB ,
GR2D_DSTB_BASE_ADDR_SB ,
GR2D_UA_BASE_ADDR_SB ,
GR2D_VA_BASE_ADDR_SB ,
2013-09-26 16:09:43 +02:00
} ;
2013-03-22 16:34:09 +02:00
static int gr2d_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct host1x_syncpt * * syncpts ;
2013-09-26 16:09:43 +02:00
struct gr2d * gr2d ;
unsigned int i ;
int err ;
2013-03-22 16:34:09 +02:00
gr2d = devm_kzalloc ( dev , sizeof ( * gr2d ) , GFP_KERNEL ) ;
if ( ! gr2d )
return - ENOMEM ;
2018-05-16 17:06:36 +02:00
gr2d - > soc = of_device_get_match_data ( dev ) ;
2013-03-22 16:34:09 +02:00
syncpts = devm_kzalloc ( dev , sizeof ( * syncpts ) , GFP_KERNEL ) ;
if ( ! syncpts )
return - ENOMEM ;
gr2d - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( gr2d - > clk ) ) {
dev_err ( dev , " cannot get clock \n " ) ;
return PTR_ERR ( gr2d - > clk ) ;
}
err = clk_prepare_enable ( gr2d - > clk ) ;
if ( err ) {
dev_err ( dev , " cannot turn on clock \n " ) ;
return err ;
}
2013-09-24 15:35:40 +02:00
INIT_LIST_HEAD ( & gr2d - > client . base . list ) ;
gr2d - > client . base . ops = & gr2d_client_ops ;
gr2d - > client . base . dev = dev ;
gr2d - > client . base . class = HOST1X_CLASS_GR2D ;
gr2d - > client . base . syncpts = syncpts ;
gr2d - > client . base . num_syncpts = 1 ;
2013-10-14 14:43:22 +02:00
INIT_LIST_HEAD ( & gr2d - > client . list ) ;
2018-05-16 17:06:36 +02:00
gr2d - > client . version = gr2d - > soc - > version ;
2013-09-24 15:35:40 +02:00
gr2d - > client . ops = & gr2d_ops ;
2013-03-22 16:34:09 +02:00
2013-10-14 14:43:22 +02:00
err = host1x_client_register ( & gr2d - > client . base ) ;
2013-03-22 16:34:09 +02:00
if ( err < 0 ) {
dev_err ( dev , " failed to register host1x client: %d \n " , err ) ;
2013-10-21 13:38:34 +08:00
clk_disable_unprepare ( gr2d - > clk ) ;
2013-03-22 16:34:09 +02:00
return err ;
}
2013-09-26 16:09:43 +02:00
/* initialize address register map */
for ( i = 0 ; i < ARRAY_SIZE ( gr2d_addr_regs ) ; i + + )
set_bit ( gr2d_addr_regs [ i ] , gr2d - > addr_regs ) ;
2013-03-22 16:34:09 +02:00
platform_set_drvdata ( pdev , gr2d ) ;
return 0 ;
}
2013-09-26 16:09:43 +02:00
static int gr2d_remove ( struct platform_device * pdev )
2013-03-22 16:34:09 +02:00
{
struct gr2d * gr2d = platform_get_drvdata ( pdev ) ;
int err ;
2013-10-14 14:43:22 +02:00
err = host1x_client_unregister ( & gr2d - > client . base ) ;
2013-03-22 16:34:09 +02:00
if ( err < 0 ) {
2013-09-26 16:09:43 +02:00
dev_err ( & pdev - > dev , " failed to unregister host1x client: %d \n " ,
err ) ;
2013-03-22 16:34:09 +02:00
return err ;
}
clk_disable_unprepare ( gr2d - > clk ) ;
return 0 ;
}
struct platform_driver tegra_gr2d_driver = {
. driver = {
2013-10-14 14:44:54 +02:00
. name = " tegra-gr2d " ,
2013-03-22 16:34:09 +02:00
. of_match_table = gr2d_match ,
2013-09-26 16:09:43 +02:00
} ,
. probe = gr2d_probe ,
. remove = gr2d_remove ,
2013-03-22 16:34:09 +02:00
} ;