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>
2016-10-19 11:28:27 +01:00
# include <drm/drm_of.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 ;
}
2016-10-10 17:50:56 +03:00
static int sti_atomic_check ( struct drm_device * dev ,
struct drm_atomic_state * state )
{
int ret ;
ret = drm_atomic_helper_check_modeset ( dev , state ) ;
if ( ret )
return ret ;
ret = drm_atomic_normalize_zpos ( dev , state ) ;
if ( ret )
return ret ;
ret = drm_atomic_helper_check_planes ( dev , state ) ;
if ( ret )
return ret ;
return ret ;
}
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 ;
2017-01-04 13:06:51 +01:00
drm_fbdev_cma_hotplug_event ( private - > fbdev ) ;
2016-06-21 15:09:40 +02:00
}
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 ,
2016-10-10 17:50:56 +03:00
. atomic_check = sti_atomic_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 ;
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 ,
. 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-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 ;
if ( private - > fbdev ) {
drm_fbdev_cma_fini ( private - > fbdev ) ;
private - > fbdev = NULL ;
}
drm_kms_helper_poll_fini ( 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 ;
2017-01-04 13:06:51 +01:00
struct sti_private * private ;
struct drm_fbdev_cma * fbdev ;
2016-06-21 15:09:40 +02:00
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 )
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 ) ;
2017-01-04 13:06:51 +01:00
private = ddev - > dev_private ;
if ( ddev - > mode_config . num_connector ) {
drm: Rely on mode_config data for fb_helper initialization
Instead of receiving the num_crts as a parameter, we can read it
directly from the mode_config structure. I audited the drivers that
invoke this helper and I believe all of them initialize the mode_config
struct accordingly, prior to calling the fb_helper.
I used the following coccinelle hack to make this transformation, except
for the function headers and comment updates. The first and second
rules are split because I couldn't find a way to remove the unused
temporary variables at the same time I removed the parameter.
// <smpl>
@r@
expression A,B,D,E;
identifier C;
@@
(
- drm_fb_helper_init(A,B,C,D)
+ drm_fb_helper_init(A,B,D)
|
- drm_fbdev_cma_init_with_funcs(A,B,C,D,E)
+ drm_fbdev_cma_init_with_funcs(A,B,D,E)
|
- drm_fbdev_cma_init(A,B,C,D)
+ drm_fbdev_cma_init(A,B,D)
)
@@
expression A,B,C,D,E;
@@
(
- drm_fb_helper_init(A,B,C,D)
+ drm_fb_helper_init(A,B,D)
|
- drm_fbdev_cma_init_with_funcs(A,B,C,D,E)
+ drm_fbdev_cma_init_with_funcs(A,B,D,E)
|
- drm_fbdev_cma_init(A,B,C,D)
+ drm_fbdev_cma_init(A,B,D)
)
@@
identifier r.C;
type T;
expression V;
@@
- T C;
<...
when != C
- C = V;
...>
// </smpl>
Changes since v1:
- Rebased on top of the tip of drm-misc-next.
- Remove mention to sti since a proper fix got merged.
Suggested-by: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
Reviewed-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: http://patchwork.freedesktop.org/patch/msgid/20170202162640.27261-1-krisman@collabora.co.uk
2017-02-02 14:26:40 -02:00
fbdev = drm_fbdev_cma_init ( ddev , 32 ,
2017-01-04 13:06:51 +01:00
ddev - > mode_config . num_connector ) ;
if ( IS_ERR ( fbdev ) ) {
DRM_DEBUG_DRIVER ( " Warning: fails to create fbdev \n " ) ;
fbdev = NULL ;
}
private - > fbdev = fbdev ;
}
2016-06-21 15:09:40 +02:00
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 ) ) ;
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 " ) ;