2014-07-31 11:39:11 +04: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 15:35:16 +03:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
2014-07-31 11:39:11 +04:00
# include <drm/drm_crtc_helper.h>
# include <drm/drm_gem_cma_helper.h>
# include <drm/drm_fb_cma_helper.h>
2015-07-31 12:32:34 +03:00
# include "sti_crtc.h"
# include "sti_drv.h"
2014-07-31 11:39:11 +04: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
2015-07-31 12:32:34 +03:00
static void sti_atomic_schedule ( struct sti_private * private ,
struct drm_atomic_state * state )
2015-03-19 15:35:16 +03:00
{
private - > commit . state = state ;
schedule_work ( & private - > commit . work ) ;
}
2015-07-31 12:32:34 +03:00
static void sti_atomic_complete ( struct sti_private * private ,
struct drm_atomic_state * state )
2015-03-19 15:35:16 +03: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 13:02:07 +03:00
drm_atomic_helper_commit_planes ( drm , state , false ) ;
2015-03-19 15:35:16 +03: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 12:32:34 +03:00
static void sti_atomic_work ( struct work_struct * work )
2015-03-19 15:35:16 +03:00
{
2015-07-31 12:32:34 +03:00
struct sti_private * private = container_of ( work ,
struct sti_private , commit . work ) ;
2015-03-19 15:35:16 +03:00
2015-07-31 12:32:34 +03:00
sti_atomic_complete ( private , private - > commit . state ) ;
2015-03-19 15:35:16 +03:00
}
2015-07-31 12:32:34 +03:00
static int sti_atomic_commit ( struct drm_device * drm ,
struct drm_atomic_state * state , bool async )
2015-03-19 15:35:16 +03:00
{
2015-07-31 12:32:34 +03:00
struct sti_private * private = drm - > dev_private ;
2015-03-19 15:35:16 +03:00
int err ;
err = drm_atomic_helper_prepare_planes ( drm , state ) ;
if ( err )
return err ;
/* serialize outstanding asynchronous commits */
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 .
*/
drm_atomic_helper_swap_state ( drm , state ) ;
if ( async )
2015-07-31 12:32:34 +03:00
sti_atomic_schedule ( private , state ) ;
2015-03-19 15:35:16 +03:00
else
2015-07-31 12:32:34 +03:00
sti_atomic_complete ( private , state ) ;
2015-03-19 15:35:16 +03:00
mutex_unlock ( & private - > commit . lock ) ;
return 0 ;
}
2015-09-02 13:44:15 +03:00
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
2014-07-31 11:39:11 +04:00
. fb_create = drm_fb_cma_create ,
2015-03-19 15:35:16 +03:00
. atomic_check = drm_atomic_helper_check ,
2015-07-31 12:32:34 +03:00
. atomic_commit = sti_atomic_commit ,
2014-07-31 11:39:11 +04:00
} ;
2015-07-31 12:32:34 +03:00
static void sti_mode_config_init ( struct drm_device * dev )
2014-07-31 11:39:11 +04: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 16:20:27 +03:00
dev - > mode_config . max_width = STI_MAX_FB_WIDTH ;
dev - > mode_config . max_height = STI_MAX_FB_HEIGHT ;
2014-07-31 11:39:11 +04:00
2015-07-31 12:32:34 +03:00
dev - > mode_config . funcs = & sti_mode_config_funcs ;
2014-07-31 11:39:11 +04:00
}
2015-07-31 12:32:34 +03:00
static int sti_load ( struct drm_device * dev , unsigned long flags )
2014-07-31 11:39:11 +04:00
{
2015-07-31 12:32:34 +03:00
struct sti_private * private ;
2014-07-31 11:39:11 +04:00
int ret ;
2015-07-31 12:32:34 +03:00
private = kzalloc ( sizeof ( * private ) , GFP_KERNEL ) ;
2014-07-31 11:39:11 +04:00
if ( ! private ) {
DRM_ERROR ( " Failed to allocate private \n " ) ;
return - ENOMEM ;
}
dev - > dev_private = ( void * ) private ;
private - > drm_dev = dev ;
2015-03-19 15:35:16 +03:00
mutex_init ( & private - > commit . lock ) ;
2015-07-31 12:32:34 +03:00
INIT_WORK ( & private - > commit . work , sti_atomic_work ) ;
2015-03-19 15:35:16 +03:00
2014-07-31 11:39:11 +04:00
drm_mode_config_init ( dev ) ;
drm_kms_helper_poll_init ( dev ) ;
2015-07-31 12:32:34 +03:00
sti_mode_config_init ( dev ) ;
2014-07-31 11:39:11 +04:00
ret = component_bind_all ( dev - > dev , dev ) ;
2014-12-11 15:35:29 +03:00
if ( ret ) {
drm_kms_helper_poll_fini ( dev ) ;
drm_mode_config_cleanup ( dev ) ;
kfree ( private ) ;
2014-07-31 11:39:11 +04:00
return ret ;
2014-12-11 15:35:29 +03:00
}
2014-07-31 11:39:11 +04:00
2015-03-19 15:35:16 +03:00
drm_mode_config_reset ( dev ) ;
2014-07-31 11:39:11 +04:00
drm_fbdev_cma_init ( dev , 32 ,
2015-07-31 12:32:34 +03:00
dev - > mode_config . num_crtc ,
dev - > mode_config . num_connector ) ;
2015-10-27 11:10:58 +03:00
2014-07-31 11:39:11 +04:00
return 0 ;
}
2015-07-31 12:32:34 +03:00
static const struct file_operations sti_driver_fops = {
2014-07-31 11:39:11 +04: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 12:32:34 +03:00
static struct dma_buf * sti_gem_prime_export ( struct drm_device * dev ,
struct drm_gem_object * obj ,
int flags )
2014-07-31 11:39:11 +04:00
{
/* we want to be able to write in mmapped buffer */
flags | = O_RDWR ;
return drm_gem_prime_export ( dev , obj , flags ) ;
}
2015-07-31 12:32:34 +03:00
static struct drm_driver sti_driver = {
2014-07-31 11:39:11 +04:00
. driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
DRIVER_GEM | DRIVER_PRIME ,
2015-07-31 12:32:34 +03:00
. load = sti_load ,
2014-07-31 11:39:11 +04:00
. gem_free_object = drm_gem_cma_free_object ,
. 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 12:32:34 +03:00
. fops = & sti_driver_fops ,
2014-07-31 11:39:11 +04:00
2015-09-30 16:46:48 +03:00
. get_vblank_counter = drm_vblank_no_hw_counter ,
2015-07-31 12:32:34 +03:00
. enable_vblank = sti_crtc_enable_vblank ,
. disable_vblank = sti_crtc_disable_vblank ,
2014-07-31 11:39:11 +04:00
. prime_handle_to_fd = drm_gem_prime_handle_to_fd ,
. prime_fd_to_handle = drm_gem_prime_fd_to_handle ,
2015-07-31 12:32:34 +03:00
. gem_prime_export = sti_gem_prime_export ,
2014-07-31 11:39:11 +04: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 ,
. 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 ;
}
2015-07-31 12:32:34 +03:00
static int sti_bind ( struct device * dev )
2014-07-31 11:39:11 +04:00
{
2015-07-31 12:32:34 +03:00
return drm_platform_init ( & sti_driver , to_platform_device ( dev ) ) ;
2014-07-31 11:39:11 +04:00
}
2015-07-31 12:32:34 +03:00
static void sti_unbind ( struct device * dev )
2014-07-31 11:39:11 +04:00
{
drm_put_dev ( dev_get_drvdata ( dev ) ) ;
}
2015-07-31 12:32:34 +03:00
static const struct component_master_ops sti_ops = {
. bind = sti_bind ,
. unbind = sti_unbind ,
2014-07-31 11:39:11 +04:00
} ;
2015-07-31 12:32:34 +03:00
static int sti_platform_probe ( struct platform_device * pdev )
2014-07-31 11:39:11 +04:00
{
struct device * dev = & pdev - > dev ;
2015-07-17 13:06:11 +03:00
struct device_node * node = dev - > of_node ;
2014-07-31 11:39:11 +04:00
struct device_node * child_np ;
struct component_match * match = NULL ;
dma_set_coherent_mask ( dev , DMA_BIT_MASK ( 32 ) ) ;
2015-07-17 13:06:11 +03:00
of_platform_populate ( node , NULL , NULL , dev ) ;
2014-07-31 11:39:11 +04: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 12:32:34 +03:00
return component_master_add_with_match ( dev , & sti_ops , match ) ;
2014-07-31 11:39:11 +04:00
}
2015-07-31 12:32:34 +03:00
static int sti_platform_remove ( struct platform_device * pdev )
2014-07-31 11:39:11 +04:00
{
2015-07-31 12:32:34 +03:00
component_master_del ( & pdev - > dev , & sti_ops ) ;
2014-07-31 11:39:11 +04:00
of_platform_depopulate ( & pdev - > dev ) ;
2015-07-17 13:06:11 +03:00
2014-07-31 11:39:11 +04:00
return 0 ;
}
2015-07-31 12:32:34 +03:00
static const struct of_device_id sti_dt_ids [ ] = {
2014-07-31 11:39:11 +04:00
{ . compatible = " st,sti-display-subsystem " , } ,
{ /* end node */ } ,
} ;
2015-07-31 12:32:34 +03:00
MODULE_DEVICE_TABLE ( of , sti_dt_ids ) ;
2014-07-31 11:39:11 +04:00
2015-07-31 12:32:34 +03:00
static struct platform_driver sti_platform_driver = {
. probe = sti_platform_probe ,
. remove = sti_platform_remove ,
2014-07-31 11:39:11 +04:00
. driver = {
. name = DRIVER_NAME ,
2015-07-31 12:32:34 +03:00
. of_match_table = sti_dt_ids ,
2014-07-31 11:39:11 +04:00
} ,
} ;
2015-09-24 20:02:40 +03: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 11:39:11 +04:00
MODULE_AUTHOR ( " Benjamin Gaignard <benjamin.gaignard@st.com> " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics SoC DRM driver " ) ;
MODULE_LICENSE ( " GPL " ) ;