2013-03-22 16:34:09 +02:00
/*
* Copyright ( c ) 2012 - 2013 , NVIDIA Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/clk.h>
# 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
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 ;
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 ) ;
struct tegra_drm * tegra = 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 ) ;
gr2d - > channel = host1x_channel_request ( client - > dev ) ;
if ( ! gr2d - > channel )
return - ENOMEM ;
2013-10-14 15:21:55 +03:00
client - > syncpts [ 0 ] = host1x_syncpt_request ( client - > dev , flags ) ;
2013-10-14 14:43:22 +02:00
if ( ! client - > syncpts [ 0 ] ) {
host1x_channel_free ( gr2d - > channel ) ;
return - ENOMEM ;
}
return tegra_drm_register_client ( tegra , drm ) ;
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 ) ;
struct tegra_drm * tegra = dev_get_drvdata ( client - > parent ) ;
struct gr2d * gr2d = to_gr2d ( drm ) ;
int err ;
err = tegra_drm_unregister_client ( tegra , drm ) ;
if ( err < 0 )
return err ;
host1x_syncpt_free ( client - > syncpts [ 0 ] ) ;
host1x_channel_free ( gr2d - > channel ) ;
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 ;
}
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 ,
. submit = tegra_drm_submit ,
2013-03-22 16:34:09 +02:00
} ;
static const struct of_device_id gr2d_match [ ] = {
{ . compatible = " nvidia,tegra30-gr2d " } ,
{ . compatible = " nvidia,tegra20-gr2d " } ,
{ } ,
} ;
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 ;
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 ) ;
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
} ;