2014-07-31 09:39:11 +02:00
/*
* Copyright ( C ) STMicroelectronics SA 2014
* Author : Benjamin Gaignard < benjamin . gaignard @ st . com > for STMicroelectronics .
* License terms : GNU General Public License ( GPL ) , version 2
*/
# 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_crtc_helper.h>
# include <drm/drm_gem_cma_helper.h>
# include <drm/drm_fb_cma_helper.h>
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 ) ;
plane - > fps_info . output = ( val > > i ) & 1 ;
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_debugfs_create ( struct dentry * root ,
struct drm_minor * minor ,
const char * name ,
const struct file_operations * fops )
{
struct drm_device * dev = minor - > dev ;
struct drm_info_node * node ;
struct dentry * ent ;
ent = debugfs_create_file ( name , S_IRUGO | S_IWUSR , root , dev , fops ) ;
if ( IS_ERR ( ent ) )
return PTR_ERR ( ent ) ;
node = kmalloc ( sizeof ( * node ) , GFP_KERNEL ) ;
if ( ! node ) {
debugfs_remove ( ent ) ;
return - ENOMEM ;
}
node - > minor = minor ;
node - > dent = ent ;
node - > info_ent = ( void * ) fops ;
mutex_lock ( & minor - > debugfs_lock ) ;
list_add ( & node - > list , & minor - > debugfs_list ) ;
mutex_unlock ( & minor - > debugfs_lock ) ;
return 0 ;
}
static int sti_drm_dbg_init ( struct drm_minor * minor )
{
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 ;
ret = sti_drm_debugfs_create ( minor - > debugfs_root , minor , " fps_show " ,
& sti_drm_fps_fops ) ;
if ( ret )
goto err ;
DRM_INFO ( " %s: debugfs installed \n " , DRIVER_NAME ) ;
return 0 ;
err :
DRM_ERROR ( " %s: cannot install debugfs \n " , DRIVER_NAME ) ;
return ret ;
}
void sti_drm_dbg_cleanup ( struct drm_minor * minor )
{
drm_debugfs_remove_files ( sti_drm_dbg_list ,
ARRAY_SIZE ( sti_drm_dbg_list ) , minor ) ;
drm_debugfs_remove_files ( ( struct drm_info_list * ) & sti_drm_fps_fops ,
1 , minor ) ;
}
2015-07-31 11:32:34 +02:00
static void sti_atomic_schedule ( struct sti_private * private ,
struct drm_atomic_state * state )
2015-03-19 13:35:16 +01:00
{
private - > commit . state = state ;
schedule_work ( & private - > commit . work ) ;
}
2015-07-31 11:32:34 +02:00
static void sti_atomic_complete ( struct sti_private * private ,
struct drm_atomic_state * state )
2015-03-19 13:35:16 +01:00
{
struct drm_device * drm = private - > drm_dev ;
/*
* Everything below can be run asynchronously without the need to grab
* any modeset locks at all under one condition : It must be guaranteed
* that the asynchronous work has either been cancelled ( if the driver
* supports it , which at least requires that the framebuffers get
* cleaned up with drm_atomic_helper_cleanup_planes ( ) ) or completed
* before the new state gets committed on the software side with
* drm_atomic_helper_swap_state ( ) .
*
* This scheme allows new atomic state updates to be prepared and
* checked in parallel to the asynchronous completion of the previous
* update . Which is important since compositors need to figure out the
* composition of the next frame right after having submitted the
* current layout .
*/
drm_atomic_helper_commit_modeset_disables ( drm , state ) ;
2015-09-08 12:02:07 +02:00
drm_atomic_helper_commit_planes ( drm , state , false ) ;
2015-03-19 13:35:16 +01:00
drm_atomic_helper_commit_modeset_enables ( drm , state ) ;
drm_atomic_helper_wait_for_vblanks ( drm , state ) ;
drm_atomic_helper_cleanup_planes ( drm , state ) ;
drm_atomic_state_free ( state ) ;
}
2015-07-31 11:32:34 +02:00
static void sti_atomic_work ( struct work_struct * work )
2015-03-19 13:35:16 +01:00
{
2015-07-31 11:32:34 +02:00
struct sti_private * private = container_of ( work ,
struct sti_private , commit . work ) ;
2015-03-19 13:35:16 +01:00
2015-07-31 11:32:34 +02:00
sti_atomic_complete ( private , private - > commit . state ) ;
2015-03-19 13:35:16 +01:00
}
2015-07-31 11:32:34 +02:00
static int sti_atomic_commit ( struct drm_device * drm ,
2016-04-26 16:11:41 +02:00
struct drm_atomic_state * state , bool nonblock )
2015-03-19 13:35:16 +01:00
{
2015-07-31 11:32:34 +02:00
struct sti_private * private = drm - > dev_private ;
2015-03-19 13:35:16 +01:00
int err ;
err = drm_atomic_helper_prepare_planes ( drm , state ) ;
if ( err )
return err ;
2016-04-26 16:11:41 +02:00
/* serialize outstanding nonblocking commits */
2015-03-19 13:35:16 +01:00
mutex_lock ( & private - > commit . lock ) ;
flush_work ( & private - > commit . work ) ;
/*
* This is the point of no return - everything below never fails except
* when the hw goes bonghits . Which means we can commit the new state on
* the software side now .
*/
2016-06-10 00:06:32 +02:00
drm_atomic_helper_swap_state ( state , true ) ;
2015-03-19 13:35:16 +01:00
2016-04-26 16:11:41 +02:00
if ( nonblock )
2015-07-31 11:32:34 +02:00
sti_atomic_schedule ( private , state ) ;
2015-03-19 13:35:16 +01:00
else
2015-07-31 11:32:34 +02:00
sti_atomic_complete ( private , state ) ;
2015-03-19 13:35:16 +01:00
mutex_unlock ( & private - > commit . lock ) ;
return 0 ;
}
2016-06-21 15:09:40 +02:00
static void sti_output_poll_changed ( struct drm_device * ddev )
{
struct sti_private * private = ddev - > dev_private ;
if ( ! ddev - > mode_config . num_connector )
return ;
if ( private - > fbdev ) {
drm_fbdev_cma_hotplug_event ( private - > fbdev ) ;
return ;
}
private - > fbdev = drm_fbdev_cma_init ( ddev , 32 ,
ddev - > mode_config . num_crtc ,
ddev - > mode_config . num_connector ) ;
if ( IS_ERR ( private - > fbdev ) )
private - > fbdev = NULL ;
}
2015-09-02 13:44:15 +03:00
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
2014-07-31 09:39:11 +02:00
. fb_create = drm_fb_cma_create ,
2016-06-21 15:09:40 +02:00
. output_poll_changed = sti_output_poll_changed ,
2015-03-19 13:35:16 +01:00
. atomic_check = drm_atomic_helper_check ,
2015-07-31 11:32:34 +02:00
. atomic_commit = sti_atomic_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 ;
2014-07-31 09:39:11 +02:00
}
2015-07-31 11:32:34 +02:00
static const struct file_operations sti_driver_fops = {
2014-07-31 09:39:11 +02:00
. owner = THIS_MODULE ,
. open = drm_open ,
. mmap = drm_gem_cma_mmap ,
. poll = drm_poll ,
. read = drm_read ,
. unlocked_ioctl = drm_ioctl ,
# ifdef CONFIG_COMPAT
. compat_ioctl = drm_compat_ioctl ,
# endif
. release = drm_release ,
} ;
2015-07-31 11:32:34 +02:00
static struct drm_driver sti_driver = {
2014-07-31 09:39:11 +02:00
. driver_features = DRIVER_HAVE_IRQ | 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 ,
. dumb_map_offset = drm_gem_cma_dumb_map_offset ,
. dumb_destroy = drm_gem_dumb_destroy ,
2015-07-31 11:32:34 +02:00
. fops = & sti_driver_fops ,
2014-07-31 09:39:11 +02:00
2015-09-30 16:46:48 +03:00
. get_vblank_counter = drm_vblank_no_hw_counter ,
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 ,
. debugfs_cleanup = sti_drm_dbg_cleanup ,
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 ;
mutex_init ( & private - > commit . lock ) ;
INIT_WORK ( & private - > commit . work , sti_atomic_work ) ;
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 ;
if ( private - > fbdev ) {
drm_fbdev_cma_fini ( private - > fbdev ) ;
private - > fbdev = NULL ;
}
drm_kms_helper_poll_fini ( ddev ) ;
drm_vblank_cleanup ( ddev ) ;
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 ) ;
if ( ! ddev )
return - ENOMEM ;
ddev - > platformdev = to_platform_device ( dev ) ;
ret = sti_init ( ddev ) ;
if ( ret )
goto err_drm_dev_unref ;
ret = component_bind_all ( ddev - > dev , ddev ) ;
if ( ret )
goto err_cleanup ;
ret = drm_dev_register ( ddev , 0 ) ;
if ( ret )
goto err_register ;
drm_mode_config_reset ( ddev ) ;
return 0 ;
err_register :
drm_mode_config_cleanup ( ddev ) ;
err_cleanup :
sti_cleanup ( ddev ) ;
err_drm_dev_unref :
drm_dev_unref ( ddev ) ;
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 ) ;
drm_dev_unref ( 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 ) ) ;
2015-07-17 12:06:11 +02:00
of_platform_populate ( node , NULL , NULL , dev ) ;
2014-07-31 09:39:11 +02:00
child_np = of_get_next_available_child ( node , NULL ) ;
while ( child_np ) {
component_match_add ( dev , & match , compare_of , child_np ) ;
of_node_put ( child_np ) ;
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 ) ;
2014-07-31 09:39:11 +02:00
of_platform_depopulate ( & pdev - > dev ) ;
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_vtac_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 " ) ;