2013-01-09 01:04:28 +04:00
/*
* Copyright ( C ) 2012 Texas Instruments
* Author : Rob Clark < robdclark @ gmail . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* This program is distributed in the hope that 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/>.
*/
/* LCDC DRM driver, based on da8xx-fb */
2015-02-10 15:13:23 +03:00
# include <linux/component.h>
2014-07-29 10:27:58 +04:00
# include <linux/pinctrl/consumer.h>
# include <linux/suspend.h>
2015-12-30 18:40:24 +03:00
# include <drm/drm_atomic.h>
# include <drm/drm_atomic_helper.h>
2017-04-24 07:50:32 +03:00
# include <drm/drm_fb_helper.h>
2017-08-13 16:32:02 +03:00
# include <drm/drm_gem_framebuffer_helper.h>
2015-02-10 15:13:23 +03:00
2013-01-09 01:04:28 +04:00
# include "tilcdc_drv.h"
# include "tilcdc_regs.h"
# include "tilcdc_tfp410.h"
2012-12-19 03:34:16 +04:00
# include "tilcdc_panel.h"
2015-02-10 15:13:23 +03:00
# include "tilcdc_external.h"
2013-01-09 01:04:28 +04:00
static LIST_HEAD ( module_list ) ;
2016-08-11 19:09:43 +03:00
static const u32 tilcdc_rev1_formats [ ] = { DRM_FORMAT_RGB565 } ;
static const u32 tilcdc_straight_formats [ ] = { DRM_FORMAT_RGB565 ,
DRM_FORMAT_BGR888 ,
DRM_FORMAT_XBGR8888 } ;
static const u32 tilcdc_crossed_formats [ ] = { DRM_FORMAT_BGR565 ,
DRM_FORMAT_RGB888 ,
DRM_FORMAT_XRGB8888 } ;
static const u32 tilcdc_legacy_formats [ ] = { DRM_FORMAT_RGB565 ,
DRM_FORMAT_RGB888 ,
DRM_FORMAT_XRGB8888 } ;
2013-01-09 01:04:28 +04:00
void tilcdc_module_init ( struct tilcdc_module * mod , const char * name ,
const struct tilcdc_module_ops * funcs )
{
mod - > name = name ;
mod - > funcs = funcs ;
INIT_LIST_HEAD ( & mod - > list ) ;
list_add ( & mod - > list , & module_list ) ;
}
void tilcdc_module_cleanup ( struct tilcdc_module * mod )
{
list_del ( & mod - > list ) ;
}
static struct of_device_id tilcdc_of_match [ ] ;
static struct drm_framebuffer * tilcdc_fb_create ( struct drm_device * dev ,
2015-11-11 20:11:29 +03:00
struct drm_file * file_priv , const struct drm_mode_fb_cmd2 * mode_cmd )
2013-01-09 01:04:28 +04:00
{
2017-08-13 16:32:02 +03:00
return drm_gem_fb_create ( dev , file_priv , mode_cmd ) ;
2013-01-09 01:04:28 +04:00
}
2016-09-10 15:32:57 +03:00
static int tilcdc_atomic_check ( struct drm_device * dev ,
struct drm_atomic_state * state )
2015-12-30 18:40:24 +03:00
{
int ret ;
ret = drm_atomic_helper_check_modeset ( dev , state ) ;
if ( ret )
return ret ;
ret = drm_atomic_helper_check_planes ( dev , state ) ;
if ( ret )
return ret ;
/*
* tilcdc - > atomic_check can update - > mode_changed if pixel format
* changes , hence will we check modeset changes again .
*/
ret = drm_atomic_helper_check_modeset ( dev , state ) ;
if ( ret )
return ret ;
return ret ;
}
static int tilcdc_commit ( struct drm_device * dev ,
struct drm_atomic_state * state ,
bool async )
{
int ret ;
ret = drm_atomic_helper_prepare_planes ( dev , state ) ;
if ( ret )
return ret ;
2017-07-11 17:33:11 +03:00
ret = drm_atomic_helper_swap_state ( state , true ) ;
if ( ret ) {
drm_atomic_helper_cleanup_planes ( dev , state ) ;
return ret ;
}
2015-12-30 18:40:24 +03:00
/*
* 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 ( dev , state ) ;
2016-08-29 12:12:03 +03:00
drm_atomic_helper_commit_planes ( dev , state , 0 ) ;
2015-12-30 18:40:24 +03:00
drm_atomic_helper_commit_modeset_enables ( dev , state ) ;
drm_atomic_helper_wait_for_vblanks ( dev , state ) ;
drm_atomic_helper_cleanup_planes ( dev , state ) ;
return 0 ;
}
2013-01-09 01:04:28 +04:00
static const struct drm_mode_config_funcs mode_config_funcs = {
. fb_create = tilcdc_fb_create ,
2017-11-15 17:19:54 +03:00
. output_poll_changed = drm_fb_helper_output_poll_changed ,
2015-12-30 18:40:24 +03:00
. atomic_check = tilcdc_atomic_check ,
. atomic_commit = tilcdc_commit ,
2013-01-09 01:04:28 +04:00
} ;
2016-11-15 23:56:46 +03:00
static void modeset_init ( struct drm_device * dev )
2013-01-09 01:04:28 +04:00
{
struct tilcdc_drm_private * priv = dev - > dev_private ;
struct tilcdc_module * mod ;
list_for_each_entry ( mod , & module_list , list ) {
DBG ( " loading module: %s " , mod - > name ) ;
mod - > funcs - > modeset_init ( mod , dev ) ;
}
dev - > mode_config . min_width = 0 ;
dev - > mode_config . min_height = 0 ;
dev - > mode_config . max_width = tilcdc_crtc_max_width ( priv - > crtc ) ;
dev - > mode_config . max_height = 2048 ;
dev - > mode_config . funcs = & mode_config_funcs ;
}
# ifdef CONFIG_CPU_FREQ
static int cpufreq_transition ( struct notifier_block * nb ,
unsigned long val , void * data )
{
struct tilcdc_drm_private * priv = container_of ( nb ,
struct tilcdc_drm_private , freq_transition ) ;
2016-09-05 20:39:32 +03:00
2016-09-06 16:19:54 +03:00
if ( val = = CPUFREQ_POSTCHANGE )
tilcdc_crtc_update_clk ( priv - > crtc ) ;
2013-01-09 01:04:28 +04:00
return 0 ;
}
# endif
/*
* DRM operations :
*/
2016-10-17 17:53:33 +03:00
static void tilcdc_fini ( struct drm_device * dev )
2013-01-09 01:04:28 +04:00
{
struct tilcdc_drm_private * priv = dev - > dev_private ;
2016-10-18 23:23:27 +03:00
if ( priv - > crtc )
2016-10-25 12:27:31 +03:00
tilcdc_crtc_shutdown ( priv - > crtc ) ;
2016-10-17 17:53:33 +03:00
2016-10-18 23:23:27 +03:00
if ( priv - > is_registered )
drm_dev_unregister ( dev ) ;
2015-02-10 15:13:23 +03:00
2013-01-09 01:04:28 +04:00
drm_kms_helper_poll_fini ( dev ) ;
2016-10-18 23:23:27 +03:00
2017-11-15 17:19:54 +03:00
drm_fb_cma_fbdev_fini ( dev ) ;
2016-10-18 23:23:27 +03:00
2016-10-17 17:53:33 +03:00
drm_irq_uninstall ( dev ) ;
2013-01-09 01:04:28 +04:00
drm_mode_config_cleanup ( dev ) ;
2016-10-31 18:34:22 +03:00
tilcdc_remove_external_device ( dev ) ;
2013-01-09 01:04:28 +04:00
# ifdef CONFIG_CPU_FREQ
2016-10-18 23:23:27 +03:00
if ( priv - > freq_transition . notifier_call )
cpufreq_unregister_notifier ( & priv - > freq_transition ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
2013-01-09 01:04:28 +04:00
# endif
if ( priv - > clk )
clk_put ( priv - > clk ) ;
if ( priv - > mmio )
iounmap ( priv - > mmio ) ;
2016-10-18 23:23:27 +03:00
if ( priv - > wq ) {
flush_workqueue ( priv - > wq ) ;
destroy_workqueue ( priv - > wq ) ;
}
2013-01-09 01:04:28 +04:00
dev - > dev_private = NULL ;
pm_runtime_disable ( dev - > dev ) ;
2017-09-26 11:30:19 +03:00
drm_dev_put ( dev ) ;
2013-01-09 01:04:28 +04:00
}
2016-10-17 17:53:33 +03:00
static int tilcdc_init ( struct drm_driver * ddrv , struct device * dev )
2013-01-09 01:04:28 +04:00
{
2016-10-17 17:53:33 +03:00
struct drm_device * ddev ;
struct platform_device * pdev = to_platform_device ( dev ) ;
struct device_node * node = dev - > of_node ;
2013-01-09 01:04:28 +04:00
struct tilcdc_drm_private * priv ;
struct resource * res ;
2013-06-19 02:18:31 +04:00
u32 bpp = 0 ;
2013-01-09 01:04:28 +04:00
int ret ;
2016-10-17 17:53:33 +03:00
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
2018-02-06 23:51:15 +03:00
if ( ! priv )
2013-01-09 01:04:28 +04:00
return - ENOMEM ;
2016-10-17 17:53:33 +03:00
ddev = drm_dev_alloc ( ddrv , dev ) ;
if ( IS_ERR ( ddev ) )
return PTR_ERR ( ddev ) ;
ddev - > dev_private = priv ;
2016-10-18 23:23:27 +03:00
platform_set_drvdata ( pdev , ddev ) ;
drm_mode_config_init ( ddev ) ;
2013-01-09 01:04:28 +04:00
2015-02-10 15:13:23 +03:00
priv - > is_componentized =
2016-10-17 17:53:33 +03:00
tilcdc_get_external_components ( dev , NULL ) > 0 ;
2015-02-10 15:13:23 +03:00
2013-01-09 01:04:28 +04:00
priv - > wq = alloc_ordered_workqueue ( " tilcdc " , 0 ) ;
2014-09-02 16:51:15 +04:00
if ( ! priv - > wq ) {
ret = - ENOMEM ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2014-09-02 16:51:15 +04:00
}
2013-01-09 01:04:28 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
2016-10-17 17:53:33 +03:00
dev_err ( dev , " failed to get memory resource \n " ) ;
2013-01-09 01:04:28 +04:00
ret = - EINVAL ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
}
priv - > mmio = ioremap_nocache ( res - > start , resource_size ( res ) ) ;
if ( ! priv - > mmio ) {
2016-10-17 17:53:33 +03:00
dev_err ( dev , " failed to ioremap \n " ) ;
2013-01-09 01:04:28 +04:00
ret = - ENOMEM ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
}
2016-10-17 17:53:33 +03:00
priv - > clk = clk_get ( dev , " fck " ) ;
2013-01-09 01:04:28 +04:00
if ( IS_ERR ( priv - > clk ) ) {
2016-10-17 17:53:33 +03:00
dev_err ( dev , " failed to get functional clock \n " ) ;
2013-01-09 01:04:28 +04:00
ret = - ENODEV ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
}
# ifdef CONFIG_CPU_FREQ
priv - > freq_transition . notifier_call = cpufreq_transition ;
ret = cpufreq_register_notifier ( & priv - > freq_transition ,
CPUFREQ_TRANSITION_NOTIFIER ) ;
if ( ret ) {
2016-10-17 17:53:33 +03:00
dev_err ( dev , " failed to register cpufreq notifier \n " ) ;
2016-10-18 23:23:27 +03:00
priv - > freq_transition . notifier_call = NULL ;
goto init_failed ;
2013-01-09 01:04:28 +04:00
}
# endif
if ( of_property_read_u32 ( node , " max-bandwidth " , & priv - > max_bandwidth ) )
2013-06-21 22:52:23 +04:00
priv - > max_bandwidth = TILCDC_DEFAULT_MAX_BANDWIDTH ;
DBG ( " Maximum Bandwidth Value %d " , priv - > max_bandwidth ) ;
2016-11-28 14:37:23 +03:00
if ( of_property_read_u32 ( node , " max-width " , & priv - > max_width ) )
2013-06-21 22:52:23 +04:00
priv - > max_width = TILCDC_DEFAULT_MAX_WIDTH ;
DBG ( " Maximum Horizontal Pixel Width Value %dpixels " , priv - > max_width ) ;
2016-11-28 14:37:23 +03:00
if ( of_property_read_u32 ( node , " max-pixelclock " ,
2013-06-21 22:52:23 +04:00
& priv - > max_pixelclock ) )
priv - > max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK ;
DBG ( " Maximum Pixel Clock Value %dKHz " , priv - > max_pixelclock ) ;
2013-01-09 01:04:28 +04:00
2016-10-17 17:53:33 +03:00
pm_runtime_enable ( dev ) ;
2013-01-09 01:04:28 +04:00
/* Determine LCD IP Version */
2016-10-17 17:53:33 +03:00
pm_runtime_get_sync ( dev ) ;
switch ( tilcdc_read ( ddev , LCDC_PID_REG ) ) {
2013-01-09 01:04:28 +04:00
case 0x4c100102 :
priv - > rev = 1 ;
break ;
case 0x4f200800 :
case 0x4f201000 :
priv - > rev = 2 ;
break ;
default :
2016-10-17 17:53:33 +03:00
dev_warn ( dev , " Unknown PID Reg value 0x%08x, "
" defaulting to LCD revision 1 \n " ,
tilcdc_read ( ddev , LCDC_PID_REG ) ) ;
2013-01-09 01:04:28 +04:00
priv - > rev = 1 ;
break ;
}
2016-10-17 17:53:33 +03:00
pm_runtime_put_sync ( dev ) ;
2013-01-09 01:04:28 +04:00
2016-08-11 19:09:43 +03:00
if ( priv - > rev = = 1 ) {
DBG ( " Revision 1 LCDC supports only RGB565 format " ) ;
priv - > pixelformats = tilcdc_rev1_formats ;
priv - > num_pixelformats = ARRAY_SIZE ( tilcdc_rev1_formats ) ;
2016-08-13 21:08:20 +03:00
bpp = 16 ;
2016-08-11 19:09:43 +03:00
} else {
const char * str = " \0 " ;
of_property_read_string ( node , " blue-and-red-wiring " , & str ) ;
if ( 0 = = strcmp ( str , " crossed " ) ) {
DBG ( " Configured for crossed blue and red wires " ) ;
priv - > pixelformats = tilcdc_crossed_formats ;
priv - > num_pixelformats =
ARRAY_SIZE ( tilcdc_crossed_formats ) ;
2016-08-13 21:08:20 +03:00
bpp = 32 ; /* Choose bpp with RGB support for fbdef */
2016-08-11 19:09:43 +03:00
} else if ( 0 = = strcmp ( str , " straight " ) ) {
DBG ( " Configured for straight blue and red wires " ) ;
priv - > pixelformats = tilcdc_straight_formats ;
priv - > num_pixelformats =
ARRAY_SIZE ( tilcdc_straight_formats ) ;
2016-08-13 21:08:20 +03:00
bpp = 16 ; /* Choose bpp with RGB support for fbdef */
2016-08-11 19:09:43 +03:00
} else {
DBG ( " Blue and red wiring '%s' unknown, use legacy mode " ,
str ) ;
priv - > pixelformats = tilcdc_legacy_formats ;
priv - > num_pixelformats =
ARRAY_SIZE ( tilcdc_legacy_formats ) ;
2016-08-13 21:08:20 +03:00
bpp = 16 ; /* This is just a guess */
2016-08-11 19:09:43 +03:00
}
}
2016-11-15 23:56:46 +03:00
ret = tilcdc_crtc_create ( ddev ) ;
2013-01-09 01:04:28 +04:00
if ( ret < 0 ) {
2016-11-15 23:56:46 +03:00
dev_err ( dev , " failed to create crtc \n " ) ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
}
2016-11-15 23:56:46 +03:00
modeset_init ( ddev ) ;
2013-01-09 01:04:28 +04:00
2015-02-10 15:13:23 +03:00
if ( priv - > is_componentized ) {
2016-10-17 17:53:33 +03:00
ret = component_bind_all ( dev , ddev ) ;
2015-02-10 15:13:23 +03:00
if ( ret < 0 )
2016-10-18 23:23:27 +03:00
goto init_failed ;
2015-02-10 15:13:23 +03:00
2016-10-31 18:34:22 +03:00
ret = tilcdc_add_component_encoder ( ddev ) ;
2015-02-10 15:13:23 +03:00
if ( ret < 0 )
2016-10-18 23:23:27 +03:00
goto init_failed ;
2016-10-31 18:34:22 +03:00
} else {
ret = tilcdc_attach_external_device ( ddev ) ;
if ( ret )
goto init_failed ;
2015-02-10 15:13:23 +03:00
}
2016-10-31 18:34:22 +03:00
if ( ! priv - > external_connector & &
( ( priv - > num_encoders = = 0 ) | | ( priv - > num_connectors = = 0 ) ) ) {
2016-10-17 17:53:33 +03:00
dev_err ( dev , " no encoders/connectors found \n " ) ;
2015-02-10 15:13:23 +03:00
ret = - ENXIO ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2015-02-10 15:13:23 +03:00
}
2016-10-17 17:53:33 +03:00
ret = drm_vblank_init ( ddev , 1 ) ;
2013-01-09 01:04:28 +04:00
if ( ret < 0 ) {
2016-10-17 17:53:33 +03:00
dev_err ( dev , " failed to initialize vblank \n " ) ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
}
2016-10-17 17:53:33 +03:00
ret = drm_irq_install ( ddev , platform_get_irq ( pdev , 0 ) ) ;
2013-01-09 01:04:28 +04:00
if ( ret < 0 ) {
2016-10-17 17:53:33 +03:00
dev_err ( dev , " failed to install IRQ handler \n " ) ;
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
}
2016-10-17 17:53:33 +03:00
drm_mode_config_reset ( ddev ) ;
2015-12-29 18:27:32 +03:00
2017-11-15 17:19:54 +03:00
ret = drm_fb_cma_fbdev_init ( ddev , bpp , 0 ) ;
if ( ret )
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
2016-10-17 17:53:33 +03:00
drm_kms_helper_poll_init ( ddev ) ;
ret = drm_dev_register ( ddev , 0 ) ;
if ( ret )
2016-10-18 23:23:27 +03:00
goto init_failed ;
2013-01-09 01:04:28 +04:00
2016-10-18 23:23:27 +03:00
priv - > is_registered = true ;
2013-01-09 01:04:28 +04:00
return 0 ;
2016-10-18 23:23:27 +03:00
init_failed :
tilcdc_fini ( ddev ) ;
2016-02-23 13:44:27 +03:00
2013-01-09 01:04:28 +04:00
return ret ;
}
2013-12-11 14:34:42 +04:00
static irqreturn_t tilcdc_irq ( int irq , void * arg )
2013-01-09 01:04:28 +04:00
{
struct drm_device * dev = arg ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
return tilcdc_crtc_irq ( priv - > crtc ) ;
}
2016-06-16 11:28:23 +03:00
# if defined(CONFIG_DEBUG_FS)
2013-01-09 01:04:28 +04:00
static const struct {
const char * name ;
uint8_t rev ;
uint8_t save ;
uint32_t reg ;
2013-03-02 14:23:08 +04:00
} registers [ ] = {
2013-01-09 01:04:28 +04:00
# define REG(rev, save, reg) { #reg, rev, save, reg }
/* exists in revision 1: */
REG ( 1 , false , LCDC_PID_REG ) ,
REG ( 1 , true , LCDC_CTRL_REG ) ,
REG ( 1 , false , LCDC_STAT_REG ) ,
REG ( 1 , true , LCDC_RASTER_CTRL_REG ) ,
REG ( 1 , true , LCDC_RASTER_TIMING_0_REG ) ,
REG ( 1 , true , LCDC_RASTER_TIMING_1_REG ) ,
REG ( 1 , true , LCDC_RASTER_TIMING_2_REG ) ,
REG ( 1 , true , LCDC_DMA_CTRL_REG ) ,
REG ( 1 , true , LCDC_DMA_FB_BASE_ADDR_0_REG ) ,
REG ( 1 , true , LCDC_DMA_FB_CEILING_ADDR_0_REG ) ,
REG ( 1 , true , LCDC_DMA_FB_BASE_ADDR_1_REG ) ,
REG ( 1 , true , LCDC_DMA_FB_CEILING_ADDR_1_REG ) ,
/* new in revision 2: */
REG ( 2 , false , LCDC_RAW_STAT_REG ) ,
REG ( 2 , false , LCDC_MASKED_STAT_REG ) ,
2016-01-08 13:17:50 +03:00
REG ( 2 , true , LCDC_INT_ENABLE_SET_REG ) ,
2013-01-09 01:04:28 +04:00
REG ( 2 , false , LCDC_INT_ENABLE_CLR_REG ) ,
REG ( 2 , false , LCDC_END_OF_INT_IND_REG ) ,
REG ( 2 , true , LCDC_CLK_ENABLE_REG ) ,
# undef REG
} ;
2015-07-02 16:26:12 +03:00
2013-01-09 01:04:28 +04:00
# endif
# ifdef CONFIG_DEBUG_FS
static int tilcdc_regs_show ( struct seq_file * m , void * arg )
{
struct drm_info_node * node = ( struct drm_info_node * ) m - > private ;
struct drm_device * dev = node - > minor - > dev ;
struct tilcdc_drm_private * priv = dev - > dev_private ;
unsigned i ;
pm_runtime_get_sync ( dev - > dev ) ;
seq_printf ( m , " revision: %d \n " , priv - > rev ) ;
for ( i = 0 ; i < ARRAY_SIZE ( registers ) ; i + + )
if ( priv - > rev > = registers [ i ] . rev )
seq_printf ( m , " %s: \t %08x \n " , registers [ i ] . name ,
tilcdc_read ( dev , registers [ i ] . reg ) ) ;
pm_runtime_put_sync ( dev - > dev ) ;
return 0 ;
}
static int tilcdc_mm_show ( struct seq_file * m , void * arg )
{
struct drm_info_node * node = ( struct drm_info_node * ) m - > private ;
struct drm_device * dev = node - > minor - > dev ;
2016-12-29 14:09:24 +03:00
struct drm_printer p = drm_seq_file_printer ( m ) ;
drm_mm_print ( & dev - > vma_offset_manager - > vm_addr_space_mm , & p ) ;
return 0 ;
2013-01-09 01:04:28 +04:00
}
static struct drm_info_list tilcdc_debugfs_list [ ] = {
{ " regs " , tilcdc_regs_show , 0 } ,
{ " mm " , tilcdc_mm_show , 0 } ,
} ;
static int tilcdc_debugfs_init ( struct drm_minor * minor )
{
struct drm_device * dev = minor - > dev ;
struct tilcdc_module * mod ;
int ret ;
ret = drm_debugfs_create_files ( tilcdc_debugfs_list ,
ARRAY_SIZE ( tilcdc_debugfs_list ) ,
minor - > debugfs_root , minor ) ;
list_for_each_entry ( mod , & module_list , list )
if ( mod - > funcs - > debugfs_init )
mod - > funcs - > debugfs_init ( mod , minor ) ;
if ( ret ) {
dev_err ( dev - > dev , " could not install tilcdc_debugfs_list \n " ) ;
return ret ;
}
return ret ;
}
# endif
2017-03-08 17:12:56 +03:00
DEFINE_DRM_GEM_CMA_FOPS ( fops ) ;
2013-01-09 01:04:28 +04:00
static struct drm_driver tilcdc_driver = {
2015-06-23 14:31:17 +03:00
. driver_features = ( DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET |
2016-04-07 15:05:16 +03:00
DRIVER_PRIME | DRIVER_ATOMIC ) ,
2017-11-15 17:19:54 +03:00
. lastclose = drm_fb_helper_lastclose ,
2013-01-09 01:04:28 +04:00
. irq_handler = tilcdc_irq ,
2016-05-30 20:53:05 +03:00
. gem_free_object_unlocked = drm_gem_cma_free_object ,
2017-11-07 22:13:46 +03:00
. gem_print_info = drm_gem_cma_print_info ,
2013-01-09 01:04:28 +04:00
. gem_vm_ops = & drm_gem_cma_vm_ops ,
. dumb_create = drm_gem_cma_dumb_create ,
2015-06-23 14:31:17 +03:00
. prime_handle_to_fd = drm_gem_prime_handle_to_fd ,
. prime_fd_to_handle = drm_gem_prime_fd_to_handle ,
. gem_prime_import = drm_gem_prime_import ,
. gem_prime_export = drm_gem_prime_export ,
. 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 ,
2013-01-09 01:04:28 +04:00
# ifdef CONFIG_DEBUG_FS
. debugfs_init = tilcdc_debugfs_init ,
# endif
. fops = & fops ,
. name = " tilcdc " ,
. desc = " TI LCD Controller DRM " ,
. date = " 20121205 " ,
. major = 1 ,
. minor = 0 ,
} ;
/*
* Power management :
*/
# ifdef CONFIG_PM_SLEEP
static int tilcdc_pm_suspend ( struct device * dev )
{
struct drm_device * ddev = dev_get_drvdata ( dev ) ;
struct tilcdc_drm_private * priv = ddev - > dev_private ;
2016-06-16 11:28:23 +03:00
priv - > saved_state = drm_atomic_helper_suspend ( ddev ) ;
2013-01-09 01:04:28 +04:00
2014-09-19 05:42:57 +04:00
/* Select sleep pin state */
pinctrl_pm_select_sleep_state ( dev ) ;
2013-01-09 01:04:28 +04:00
return 0 ;
}
static int tilcdc_pm_resume ( struct device * dev )
{
struct drm_device * ddev = dev_get_drvdata ( dev ) ;
struct tilcdc_drm_private * priv = ddev - > dev_private ;
2016-06-16 11:28:23 +03:00
int ret = 0 ;
2013-01-09 01:04:28 +04:00
2014-07-29 10:27:58 +04:00
/* Select default pin state */
pinctrl_pm_select_default_state ( dev ) ;
2016-06-16 11:28:23 +03:00
if ( priv - > saved_state )
ret = drm_atomic_helper_resume ( ddev , priv - > saved_state ) ;
2013-01-09 01:04:28 +04:00
2016-06-16 11:28:23 +03:00
return ret ;
2013-01-09 01:04:28 +04:00
}
# endif
static const struct dev_pm_ops tilcdc_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( tilcdc_pm_suspend , tilcdc_pm_resume )
} ;
/*
* Platform driver :
*/
2015-02-10 15:13:23 +03:00
static int tilcdc_bind ( struct device * dev )
{
2016-10-17 17:53:33 +03:00
return tilcdc_init ( & tilcdc_driver , dev ) ;
2015-02-10 15:13:23 +03:00
}
static void tilcdc_unbind ( struct device * dev )
{
2016-06-23 11:07:16 +03:00
struct drm_device * ddev = dev_get_drvdata ( dev ) ;
/* Check if a subcomponent has already triggered the unloading. */
if ( ! ddev - > dev_private )
return ;
2016-10-17 17:53:33 +03:00
tilcdc_fini ( dev_get_drvdata ( dev ) ) ;
2015-02-10 15:13:23 +03:00
}
static const struct component_master_ops tilcdc_comp_ops = {
. bind = tilcdc_bind ,
. unbind = tilcdc_unbind ,
} ;
2013-01-09 01:04:28 +04:00
static int tilcdc_pdev_probe ( struct platform_device * pdev )
{
2015-02-10 15:13:23 +03:00
struct component_match * match = NULL ;
int ret ;
2013-01-09 01:04:28 +04:00
/* bail out early if no DT data: */
if ( ! pdev - > dev . of_node ) {
dev_err ( & pdev - > dev , " device-tree data is missing \n " ) ;
return - ENXIO ;
}
2015-02-10 15:13:23 +03:00
ret = tilcdc_get_external_components ( & pdev - > dev , & match ) ;
if ( ret < 0 )
return ret ;
else if ( ret = = 0 )
2016-10-17 17:53:33 +03:00
return tilcdc_init ( & tilcdc_driver , & pdev - > dev ) ;
2015-02-10 15:13:23 +03:00
else
return component_master_add_with_match ( & pdev - > dev ,
& tilcdc_comp_ops ,
match ) ;
2013-01-09 01:04:28 +04:00
}
static int tilcdc_pdev_remove ( struct platform_device * pdev )
{
2016-06-23 11:07:16 +03:00
int ret ;
2015-02-10 15:13:23 +03:00
2016-06-23 11:07:16 +03:00
ret = tilcdc_get_external_components ( & pdev - > dev , NULL ) ;
if ( ret < 0 )
return ret ;
else if ( ret = = 0 )
2016-10-17 17:53:33 +03:00
tilcdc_fini ( platform_get_drvdata ( pdev ) ) ;
2016-06-23 11:07:16 +03:00
else
component_master_del ( & pdev - > dev , & tilcdc_comp_ops ) ;
2013-01-09 01:04:28 +04:00
return 0 ;
}
static struct of_device_id tilcdc_of_match [ ] = {
{ . compatible = " ti,am33xx-tilcdc " , } ,
2016-10-03 18:45:19 +03:00
{ . compatible = " ti,da850-tilcdc " , } ,
2013-01-09 01:04:28 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tilcdc_of_match ) ;
static struct platform_driver tilcdc_platform_driver = {
. probe = tilcdc_pdev_probe ,
. remove = tilcdc_pdev_remove ,
. driver = {
. name = " tilcdc " ,
. pm = & tilcdc_pm_ops ,
. of_match_table = tilcdc_of_match ,
} ,
} ;
static int __init tilcdc_drm_init ( void )
{
DBG ( " init " ) ;
tilcdc_tfp410_init ( ) ;
2012-12-19 03:34:16 +04:00
tilcdc_panel_init ( ) ;
2013-01-09 01:04:28 +04:00
return platform_driver_register ( & tilcdc_platform_driver ) ;
}
static void __exit tilcdc_drm_fini ( void )
{
DBG ( " fini " ) ;
platform_driver_unregister ( & tilcdc_platform_driver ) ;
2014-06-17 18:17:08 +04:00
tilcdc_panel_fini ( ) ;
tilcdc_tfp410_fini ( ) ;
2013-01-09 01:04:28 +04:00
}
2014-06-17 18:17:11 +04:00
module_init ( tilcdc_drm_init ) ;
2013-01-09 01:04:28 +04:00
module_exit ( tilcdc_drm_fini ) ;
MODULE_AUTHOR ( " Rob Clark <robdclark@gmail.com " ) ;
MODULE_DESCRIPTION ( " TI LCD Controller DRM Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;