2017-12-06 12:29:47 +01:00
// SPDX-License-Identifier: GPL-2.0
2014-07-31 09:39:11 +02:00
/*
* Copyright ( C ) STMicroelectronics SA 2014
* Author : Benjamin Gaignard < benjamin . gaignard @ st . com > for STMicroelectronics .
*/
# include <drm/drmP.h>
# include <linux/component.h>
# include <linux/debugfs.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of_platform.h>
2015-03-19 13:35:16 +01:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
2014-07-31 09:39:11 +02:00
# include <drm/drm_gem_cma_helper.h>
2017-08-13 15:31:59 +02:00
# include <drm/drm_gem_framebuffer_helper.h>
2017-11-15 15:19:51 +01:00
# include <drm/drm_fb_helper.h>
2014-07-31 09:39:11 +02:00
# include <drm/drm_fb_cma_helper.h>
2016-10-19 11:28:27 +01:00
# include <drm/drm_of.h>
2019-01-17 22:03:34 +01:00
# include <drm/drm_probe_helper.h>
2014-07-31 09:39:11 +02:00
2015-07-31 11:32:34 +02:00
# include "sti_crtc.h"
# include "sti_drv.h"
2016-02-08 11:34:31 +01:00
# include "sti_plane.h"
2014-07-31 09:39:11 +02:00
# define DRIVER_NAME "sti"
# define DRIVER_DESC "STMicroelectronics SoC DRM"
# define DRIVER_DATE "20140601"
# define DRIVER_MAJOR 1
# define DRIVER_MINOR 0
# define STI_MAX_FB_HEIGHT 4096
# define STI_MAX_FB_WIDTH 4096
2016-02-08 11:34:31 +01:00
static int sti_drm_fps_get ( void * data , u64 * val )
{
struct drm_device * drm_dev = data ;
struct drm_plane * p ;
unsigned int i = 0 ;
* val = 0 ;
list_for_each_entry ( p , & drm_dev - > mode_config . plane_list , head ) {
struct sti_plane * plane = to_sti_plane ( p ) ;
* val | = plane - > fps_info . output < < i ;
i + + ;
}
return 0 ;
}
static int sti_drm_fps_set ( void * data , u64 val )
{
struct drm_device * drm_dev = data ;
struct drm_plane * p ;
unsigned int i = 0 ;
list_for_each_entry ( p , & drm_dev - > mode_config . plane_list , head ) {
struct sti_plane * plane = to_sti_plane ( p ) ;
2017-02-02 09:58:39 +01:00
memset ( & plane - > fps_info , 0 , sizeof ( plane - > fps_info ) ) ;
2016-02-08 11:34:31 +01:00
plane - > fps_info . output = ( val > > i ) & 1 ;
2017-02-02 09:58:39 +01:00
2016-02-08 11:34:31 +01:00
i + + ;
}
return 0 ;
}
DEFINE_SIMPLE_ATTRIBUTE ( sti_drm_fps_fops ,
sti_drm_fps_get , sti_drm_fps_set , " %llu \n " ) ;
static int sti_drm_fps_dbg_show ( struct seq_file * s , void * data )
{
struct drm_info_node * node = s - > private ;
struct drm_device * dev = node - > minor - > dev ;
struct drm_plane * p ;
list_for_each_entry ( p , & dev - > mode_config . plane_list , head ) {
struct sti_plane * plane = to_sti_plane ( p ) ;
seq_printf ( s , " %s%s \n " ,
plane - > fps_info . fps_str ,
plane - > fps_info . fips_str ) ;
}
return 0 ;
}
static struct drm_info_list sti_drm_dbg_list [ ] = {
{ " fps_get " , sti_drm_fps_dbg_show , 0 } ,
} ;
static int sti_drm_dbg_init ( struct drm_minor * minor )
{
2017-01-26 23:56:15 +01:00
struct dentry * dentry ;
2016-02-08 11:34:31 +01:00
int ret ;
ret = drm_debugfs_create_files ( sti_drm_dbg_list ,
ARRAY_SIZE ( sti_drm_dbg_list ) ,
minor - > debugfs_root , minor ) ;
if ( ret )
goto err ;
2017-01-26 23:56:15 +01:00
dentry = debugfs_create_file ( " fps_show " , S_IRUGO | S_IWUSR ,
minor - > debugfs_root , minor - > dev ,
2016-02-08 11:34:31 +01:00
& sti_drm_fps_fops ) ;
2017-01-26 23:56:15 +01:00
if ( ! dentry ) {
ret = - ENOMEM ;
2016-02-08 11:34:31 +01:00
goto err ;
2017-01-26 23:56:15 +01:00
}
2016-02-08 11:34:31 +01:00
DRM_INFO ( " %s: debugfs installed \n " , DRIVER_NAME ) ;
return 0 ;
err :
DRM_ERROR ( " %s: cannot install debugfs \n " , DRIVER_NAME ) ;
return ret ;
}
2015-09-02 13:44:15 +03:00
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
2017-08-13 15:31:59 +02:00
. fb_create = drm_gem_fb_create ,
2018-03-21 12:20:27 +02:00
. atomic_check = drm_atomic_helper_check ,
2017-01-12 17:27:35 +01:00
. atomic_commit = drm_atomic_helper_commit ,
2014-07-31 09:39:11 +02:00
} ;
2015-07-31 11:32:34 +02:00
static void sti_mode_config_init ( struct drm_device * dev )
2014-07-31 09:39:11 +02:00
{
dev - > mode_config . min_width = 0 ;
dev - > mode_config . min_height = 0 ;
/*
* set max width and height as default value .
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb ( ) .
*/
2015-10-29 14:20:27 +01:00
dev - > mode_config . max_width = STI_MAX_FB_WIDTH ;
dev - > mode_config . max_height = STI_MAX_FB_HEIGHT ;
2014-07-31 09:39:11 +02:00
2015-07-31 11:32:34 +02:00
dev - > mode_config . funcs = & sti_mode_config_funcs ;
2018-03-21 12:20:27 +02:00
dev - > mode_config . normalize_zpos = true ;
2014-07-31 09:39:11 +02:00
}
2017-03-08 15:12:56 +01:00
DEFINE_DRM_GEM_CMA_FOPS ( sti_driver_fops ) ;
2014-07-31 09:39:11 +02:00
2015-07-31 11:32:34 +02:00
static struct drm_driver sti_driver = {
2016-08-16 15:06:08 +08:00
. driver_features = DRIVER_MODESET |
2016-01-07 14:30:41 +01:00
DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC ,
2016-05-30 19:53:11 +02:00
. gem_free_object_unlocked = drm_gem_cma_free_object ,
2014-07-31 09:39:11 +02:00
. gem_vm_ops = & drm_gem_cma_vm_ops ,
. dumb_create = drm_gem_cma_dumb_create ,
2015-07-31 11:32:34 +02:00
. fops = & sti_driver_fops ,
2014-07-31 09:39:11 +02:00
2015-07-31 11:32:34 +02:00
. enable_vblank = sti_crtc_enable_vblank ,
. disable_vblank = sti_crtc_disable_vblank ,
2014-07-31 09:39:11 +02:00
. prime_handle_to_fd = drm_gem_prime_handle_to_fd ,
. prime_fd_to_handle = drm_gem_prime_fd_to_handle ,
2016-02-26 11:19:06 +01:00
. gem_prime_export = drm_gem_prime_export ,
2014-07-31 09:39:11 +02:00
. gem_prime_import = drm_gem_prime_import ,
. gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table ,
. gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table ,
. gem_prime_vmap = drm_gem_cma_prime_vmap ,
. gem_prime_vunmap = drm_gem_cma_prime_vunmap ,
. gem_prime_mmap = drm_gem_cma_prime_mmap ,
2016-02-08 11:34:31 +01:00
. debugfs_init = sti_drm_dbg_init ,
2014-07-31 09:39:11 +02:00
. name = DRIVER_NAME ,
. desc = DRIVER_DESC ,
. date = DRIVER_DATE ,
. major = DRIVER_MAJOR ,
. minor = DRIVER_MINOR ,
} ;
static int compare_of ( struct device * dev , void * data )
{
return dev - > of_node = = data ;
}
2016-06-21 15:09:40 +02:00
static int sti_init ( struct drm_device * ddev )
{
struct sti_private * private ;
private = kzalloc ( sizeof ( * private ) , GFP_KERNEL ) ;
if ( ! private )
return - ENOMEM ;
ddev - > dev_private = ( void * ) private ;
dev_set_drvdata ( ddev - > dev , ddev ) ;
private - > drm_dev = ddev ;
drm_mode_config_init ( ddev ) ;
sti_mode_config_init ( ddev ) ;
drm_kms_helper_poll_init ( ddev ) ;
return 0 ;
}
static void sti_cleanup ( struct drm_device * ddev )
{
struct sti_private * private = ddev - > dev_private ;
drm_kms_helper_poll_fini ( ddev ) ;
2018-10-04 22:24:37 +02:00
drm_atomic_helper_shutdown ( ddev ) ;
drm_mode_config_cleanup ( ddev ) ;
2017-02-02 09:51:43 +01:00
component_unbind_all ( ddev - > dev , ddev ) ;
2016-06-21 15:09:40 +02:00
kfree ( private ) ;
ddev - > dev_private = NULL ;
}
2015-07-31 11:32:34 +02:00
static int sti_bind ( struct device * dev )
2014-07-31 09:39:11 +02:00
{
2016-06-21 15:09:40 +02:00
struct drm_device * ddev ;
int ret ;
ddev = drm_dev_alloc ( & sti_driver , dev ) ;
2016-09-21 16:59:19 +02:00
if ( IS_ERR ( ddev ) )
return PTR_ERR ( ddev ) ;
2016-06-21 15:09:40 +02:00
ret = sti_init ( ddev ) ;
if ( ret )
2018-07-12 17:26:39 +02:00
goto err_drm_dev_put ;
2016-06-21 15:09:40 +02:00
ret = component_bind_all ( ddev - > dev , ddev ) ;
if ( ret )
goto err_cleanup ;
ret = drm_dev_register ( ddev , 0 ) ;
if ( ret )
2018-10-04 22:24:37 +02:00
goto err_cleanup ;
2016-06-21 15:09:40 +02:00
drm_mode_config_reset ( ddev ) ;
2018-09-08 15:46:41 +02:00
drm_fbdev_generic_setup ( ddev , 32 ) ;
2017-01-04 13:06:51 +01:00
2016-06-21 15:09:40 +02:00
return 0 ;
err_cleanup :
sti_cleanup ( ddev ) ;
2018-07-12 17:26:39 +02:00
err_drm_dev_put :
drm_dev_put ( ddev ) ;
2016-06-21 15:09:40 +02:00
return ret ;
2014-07-31 09:39:11 +02:00
}
2015-07-31 11:32:34 +02:00
static void sti_unbind ( struct device * dev )
2014-07-31 09:39:11 +02:00
{
2016-06-21 15:09:40 +02:00
struct drm_device * ddev = dev_get_drvdata ( dev ) ;
drm_dev_unregister ( ddev ) ;
sti_cleanup ( ddev ) ;
2018-07-12 17:26:39 +02:00
drm_dev_put ( ddev ) ;
2014-07-31 09:39:11 +02:00
}
2015-07-31 11:32:34 +02:00
static const struct component_master_ops sti_ops = {
. bind = sti_bind ,
. unbind = sti_unbind ,
2014-07-31 09:39:11 +02:00
} ;
2015-07-31 11:32:34 +02:00
static int sti_platform_probe ( struct platform_device * pdev )
2014-07-31 09:39:11 +02:00
{
struct device * dev = & pdev - > dev ;
2015-07-17 12:06:11 +02:00
struct device_node * node = dev - > of_node ;
2014-07-31 09:39:11 +02:00
struct device_node * child_np ;
struct component_match * match = NULL ;
dma_set_coherent_mask ( dev , DMA_BIT_MASK ( 32 ) ) ;
2017-02-24 17:14:34 +01:00
devm_of_platform_populate ( dev ) ;
2015-07-17 12:06:11 +02:00
2014-07-31 09:39:11 +02:00
child_np = of_get_next_available_child ( node , NULL ) ;
while ( child_np ) {
2016-10-19 11:28:27 +01:00
drm_of_component_match_add ( dev , & match , compare_of ,
child_np ) ;
2014-07-31 09:39:11 +02:00
child_np = of_get_next_available_child ( node , child_np ) ;
}
2015-07-31 11:32:34 +02:00
return component_master_add_with_match ( dev , & sti_ops , match ) ;
2014-07-31 09:39:11 +02:00
}
2015-07-31 11:32:34 +02:00
static int sti_platform_remove ( struct platform_device * pdev )
2014-07-31 09:39:11 +02:00
{
2015-07-31 11:32:34 +02:00
component_master_del ( & pdev - > dev , & sti_ops ) ;
2015-07-17 12:06:11 +02:00
2014-07-31 09:39:11 +02:00
return 0 ;
}
2015-07-31 11:32:34 +02:00
static const struct of_device_id sti_dt_ids [ ] = {
2014-07-31 09:39:11 +02:00
{ . compatible = " st,sti-display-subsystem " , } ,
{ /* end node */ } ,
} ;
2015-07-31 11:32:34 +02:00
MODULE_DEVICE_TABLE ( of , sti_dt_ids ) ;
2014-07-31 09:39:11 +02:00
2015-07-31 11:32:34 +02:00
static struct platform_driver sti_platform_driver = {
. probe = sti_platform_probe ,
. remove = sti_platform_remove ,
2014-07-31 09:39:11 +02:00
. driver = {
. name = DRIVER_NAME ,
2015-07-31 11:32:34 +02:00
. of_match_table = sti_dt_ids ,
2014-07-31 09:39:11 +02:00
} ,
} ;
2015-09-24 19:02:40 +02:00
static struct platform_driver * const drivers [ ] = {
& sti_tvout_driver ,
& sti_hqvdp_driver ,
& sti_hdmi_driver ,
& sti_hda_driver ,
& sti_dvo_driver ,
& sti_vtg_driver ,
& sti_compositor_driver ,
& sti_platform_driver ,
} ;
static int sti_drm_init ( void )
{
return platform_register_drivers ( drivers , ARRAY_SIZE ( drivers ) ) ;
}
module_init ( sti_drm_init ) ;
static void sti_drm_exit ( void )
{
platform_unregister_drivers ( drivers , ARRAY_SIZE ( drivers ) ) ;
}
module_exit ( sti_drm_exit ) ;
2014-07-31 09:39:11 +02:00
MODULE_AUTHOR ( " Benjamin Gaignard <benjamin.gaignard@st.com> " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics SoC DRM driver " ) ;
MODULE_LICENSE ( " GPL " ) ;