2009-11-03 12:23:50 +03:00
/*
* linux / drivers / video / omap2 / dss / core . c
*
* Copyright ( C ) 2009 Nokia Corporation
* Author : Tomi Valkeinen < tomi . valkeinen @ nokia . com >
*
* Some code and ideas taken from drivers / video / omap / driver
* by Imre Deak .
*
* 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/>.
*/
# define DSS_SUBSYS_NAME "CORE"
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/seq_file.h>
# include <linux/debugfs.h>
# include <linux/io.h>
# include <linux/device.h>
2010-02-04 18:03:41 +03:00
# include <linux/regulator/consumer.h>
2009-11-03 12:23:50 +03:00
# include <plat/display.h>
# include "dss.h"
2010-09-15 17:50:00 +04:00
# include "dss_features.h"
2009-11-03 12:23:50 +03:00
static struct {
struct platform_device * pdev ;
2010-02-04 18:03:41 +03:00
struct regulator * vdds_dsi_reg ;
struct regulator * vdds_sdi_reg ;
2009-11-03 12:23:50 +03:00
} core ;
static char * def_disp_name ;
module_param_named ( def_disp , def_disp_name , charp , 0 ) ;
MODULE_PARM_DESC ( def_disp_name , " default display name " ) ;
# ifdef DEBUG
unsigned int dss_debug ;
module_param_named ( debug , dss_debug , bool , 0644 ) ;
# endif
2010-02-04 18:03:41 +03:00
/* REGULATORS */
struct regulator * dss_get_vdds_dsi ( void )
{
struct regulator * reg ;
if ( core . vdds_dsi_reg ! = NULL )
return core . vdds_dsi_reg ;
reg = regulator_get ( & core . pdev - > dev , " vdds_dsi " ) ;
if ( ! IS_ERR ( reg ) )
core . vdds_dsi_reg = reg ;
return reg ;
}
struct regulator * dss_get_vdds_sdi ( void )
{
struct regulator * reg ;
if ( core . vdds_sdi_reg ! = NULL )
return core . vdds_sdi_reg ;
reg = regulator_get ( & core . pdev - > dev , " vdds_sdi " ) ;
if ( ! IS_ERR ( reg ) )
core . vdds_sdi_reg = reg ;
return reg ;
}
2009-11-03 12:23:50 +03:00
# if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
static int dss_debug_show ( struct seq_file * s , void * unused )
{
void ( * func ) ( struct seq_file * ) = s - > private ;
func ( s ) ;
return 0 ;
}
static int dss_debug_open ( struct inode * inode , struct file * file )
{
return single_open ( file , dss_debug_show , inode - > i_private ) ;
}
static const struct file_operations dss_debug_fops = {
. open = dss_debug_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static struct dentry * dss_debugfs_dir ;
static int dss_initialize_debugfs ( void )
{
dss_debugfs_dir = debugfs_create_dir ( " omapdss " , NULL ) ;
if ( IS_ERR ( dss_debugfs_dir ) ) {
int err = PTR_ERR ( dss_debugfs_dir ) ;
dss_debugfs_dir = NULL ;
return err ;
}
debugfs_create_file ( " clk " , S_IRUGO , dss_debugfs_dir ,
& dss_debug_dump_clocks , & dss_debug_fops ) ;
2010-02-08 13:19:46 +03:00
# ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
2009-12-17 15:35:21 +03:00
debugfs_create_file ( " dispc_irq " , S_IRUGO , dss_debugfs_dir ,
& dispc_dump_irqs , & dss_debug_fops ) ;
2010-02-08 13:19:46 +03:00
# endif
2009-12-17 15:35:21 +03:00
2010-02-08 13:19:46 +03:00
# if defined(CONFIG_OMAP2_DSS_DSI) && defined(CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS)
2009-12-17 15:35:21 +03:00
debugfs_create_file ( " dsi_irq " , S_IRUGO , dss_debugfs_dir ,
& dsi_dump_irqs , & dss_debug_fops ) ;
# endif
2009-11-03 12:23:50 +03:00
debugfs_create_file ( " dss " , S_IRUGO , dss_debugfs_dir ,
& dss_dump_regs , & dss_debug_fops ) ;
debugfs_create_file ( " dispc " , S_IRUGO , dss_debugfs_dir ,
& dispc_dump_regs , & dss_debug_fops ) ;
# ifdef CONFIG_OMAP2_DSS_RFBI
debugfs_create_file ( " rfbi " , S_IRUGO , dss_debugfs_dir ,
& rfbi_dump_regs , & dss_debug_fops ) ;
# endif
# ifdef CONFIG_OMAP2_DSS_DSI
debugfs_create_file ( " dsi " , S_IRUGO , dss_debugfs_dir ,
& dsi_dump_regs , & dss_debug_fops ) ;
# endif
# ifdef CONFIG_OMAP2_DSS_VENC
debugfs_create_file ( " venc " , S_IRUGO , dss_debugfs_dir ,
& venc_dump_regs , & dss_debug_fops ) ;
# endif
return 0 ;
}
static void dss_uninitialize_debugfs ( void )
{
if ( dss_debugfs_dir )
debugfs_remove_recursive ( dss_debugfs_dir ) ;
}
2010-05-07 13:58:41 +04:00
# else /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
static inline int dss_initialize_debugfs ( void )
{
return 0 ;
}
static inline void dss_uninitialize_debugfs ( void )
{
}
2009-11-03 12:23:50 +03:00
# endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
/* PLATFORM DEVICE */
static int omap_dss_probe ( struct platform_device * pdev )
{
struct omap_dss_board_info * pdata = pdev - > dev . platform_data ;
int skip_init = 0 ;
int r ;
int i ;
core . pdev = pdev ;
2010-09-15 17:50:00 +04:00
dss_features_init ( ) ;
2009-11-03 12:23:50 +03:00
dss_init_overlay_managers ( pdev ) ;
dss_init_overlays ( pdev ) ;
2011-01-24 09:21:57 +03:00
r = dss_init_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
if ( r ) {
2011-01-24 09:21:57 +03:00
DSSERR ( " Failed to initialize DSS platform driver \n " ) ;
2010-05-07 13:58:42 +04:00
goto err_dss ;
2009-11-03 12:23:50 +03:00
}
2011-01-24 09:21:58 +03:00
/* keep clocks enabled to prevent context saves/restores during init */
2011-01-31 19:27:44 +03:00
dss_clk_enable ( DSS_CLK_ICK | DSS_CLK_FCK ) ;
2011-01-24 09:21:58 +03:00
2011-01-24 09:21:59 +03:00
r = rfbi_init_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
if ( r ) {
2011-01-24 09:21:59 +03:00
DSSERR ( " Failed to initialize rfbi platform driver \n " ) ;
2010-05-07 13:58:42 +04:00
goto err_rfbi ;
2009-11-03 12:23:50 +03:00
}
2010-02-04 18:03:41 +03:00
r = dpi_init ( pdev ) ;
2009-11-03 12:23:50 +03:00
if ( r ) {
DSSERR ( " Failed to initialize dpi \n " ) ;
2010-05-07 13:58:42 +04:00
goto err_dpi ;
2009-11-03 12:23:50 +03:00
}
2011-01-24 09:22:00 +03:00
r = dispc_init_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
if ( r ) {
2011-01-24 09:22:00 +03:00
DSSERR ( " Failed to initialize dispc platform driver \n " ) ;
2010-05-07 13:58:42 +04:00
goto err_dispc ;
2009-11-03 12:23:50 +03:00
}
2010-05-07 13:58:41 +04:00
2011-01-24 09:22:01 +03:00
r = venc_init_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
if ( r ) {
2011-01-24 09:22:01 +03:00
DSSERR ( " Failed to initialize venc platform driver \n " ) ;
2010-05-07 13:58:42 +04:00
goto err_venc ;
2009-11-03 12:23:50 +03:00
}
2010-05-07 13:58:41 +04:00
2011-01-24 09:21:57 +03:00
# ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
/* DISPC_CONTROL */
if ( omap_readl ( 0x48050440 ) & 1 ) /* LCD enabled? */
skip_init = 1 ;
# endif
2009-11-03 12:23:50 +03:00
if ( cpu_is_omap34xx ( ) ) {
r = sdi_init ( skip_init ) ;
if ( r ) {
DSSERR ( " Failed to initialize SDI \n " ) ;
2010-05-07 13:58:42 +04:00
goto err_sdi ;
2009-11-03 12:23:50 +03:00
}
2010-05-07 13:58:41 +04:00
2011-01-24 09:22:02 +03:00
r = dsi_init_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
if ( r ) {
2011-01-24 09:22:02 +03:00
DSSERR ( " Failed to initialize DSI platform driver \n " ) ;
2010-05-07 13:58:42 +04:00
goto err_dsi ;
2009-11-03 12:23:50 +03:00
}
}
r = dss_initialize_debugfs ( ) ;
if ( r )
2010-05-07 13:58:42 +04:00
goto err_debugfs ;
2009-11-03 12:23:50 +03:00
for ( i = 0 ; i < pdata - > num_devices ; + + i ) {
struct omap_dss_device * dssdev = pdata - > devices [ i ] ;
r = omap_dss_register_device ( dssdev ) ;
2010-05-07 13:58:42 +04:00
if ( r ) {
DSSERR ( " device %d %s register failed %d \n " , i ,
dssdev - > name ? : " unnamed " , r ) ;
while ( - - i > = 0 )
omap_dss_unregister_device ( pdata - > devices [ i ] ) ;
goto err_register ;
}
2009-11-03 12:23:50 +03:00
if ( def_disp_name & & strcmp ( def_disp_name , dssdev - > name ) = = 0 )
pdata - > default_device = dssdev ;
}
2011-01-31 19:27:44 +03:00
dss_clk_disable ( DSS_CLK_ICK | DSS_CLK_FCK ) ;
2009-11-03 12:23:50 +03:00
return 0 ;
2010-05-07 13:58:42 +04:00
err_register :
dss_uninitialize_debugfs ( ) ;
err_debugfs :
if ( cpu_is_omap34xx ( ) )
2011-01-24 09:22:02 +03:00
dsi_uninit_platform_driver ( ) ;
2010-05-07 13:58:42 +04:00
err_dsi :
if ( cpu_is_omap34xx ( ) )
sdi_exit ( ) ;
err_sdi :
2011-01-24 09:22:01 +03:00
venc_uninit_platform_driver ( ) ;
2010-05-07 13:58:42 +04:00
err_venc :
2011-01-24 09:22:00 +03:00
dispc_uninit_platform_driver ( ) ;
2010-05-07 13:58:42 +04:00
err_dispc :
dpi_exit ( ) ;
err_dpi :
2011-01-24 09:21:59 +03:00
rfbi_uninit_platform_driver ( ) ;
2010-05-07 13:58:42 +04:00
err_rfbi :
2011-01-24 09:21:57 +03:00
dss_uninit_platform_driver ( ) ;
2010-05-07 13:58:42 +04:00
err_dss :
2009-11-03 12:23:50 +03:00
return r ;
}
static int omap_dss_remove ( struct platform_device * pdev )
{
struct omap_dss_board_info * pdata = pdev - > dev . platform_data ;
int i ;
dss_uninitialize_debugfs ( ) ;
2011-01-24 09:22:01 +03:00
venc_uninit_platform_driver ( ) ;
2011-01-24 09:22:00 +03:00
dispc_uninit_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
dpi_exit ( ) ;
2011-01-24 09:21:59 +03:00
rfbi_uninit_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
if ( cpu_is_omap34xx ( ) ) {
2011-01-24 09:22:02 +03:00
dsi_uninit_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
sdi_exit ( ) ;
}
2011-01-24 09:21:57 +03:00
dss_uninit_platform_driver ( ) ;
2009-11-03 12:23:50 +03:00
dss_uninit_overlays ( pdev ) ;
dss_uninit_overlay_managers ( pdev ) ;
for ( i = 0 ; i < pdata - > num_devices ; + + i )
omap_dss_unregister_device ( pdata - > devices [ i ] ) ;
return 0 ;
}
static void omap_dss_shutdown ( struct platform_device * pdev )
{
DSSDBG ( " shutdown \n " ) ;
dss_disable_all_devices ( ) ;
}
static int omap_dss_suspend ( struct platform_device * pdev , pm_message_t state )
{
DSSDBG ( " suspend %d \n " , state . event ) ;
return dss_suspend_all_devices ( ) ;
}
static int omap_dss_resume ( struct platform_device * pdev )
{
DSSDBG ( " resume \n " ) ;
return dss_resume_all_devices ( ) ;
}
static struct platform_driver omap_dss_driver = {
. probe = omap_dss_probe ,
. remove = omap_dss_remove ,
. shutdown = omap_dss_shutdown ,
. suspend = omap_dss_suspend ,
. resume = omap_dss_resume ,
. driver = {
. name = " omapdss " ,
. owner = THIS_MODULE ,
} ,
} ;
/* BUS */
static int dss_bus_match ( struct device * dev , struct device_driver * driver )
{
struct omap_dss_device * dssdev = to_dss_device ( dev ) ;
DSSDBG ( " bus_match. dev %s/%s, drv %s \n " ,
dev_name ( dev ) , dssdev - > driver_name , driver - > name ) ;
return strcmp ( dssdev - > driver_name , driver - > name ) = = 0 ;
}
static ssize_t device_name_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct omap_dss_device * dssdev = to_dss_device ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
dssdev - > name ?
dssdev - > name : " " ) ;
}
static struct device_attribute default_dev_attrs [ ] = {
__ATTR ( name , S_IRUGO , device_name_show , NULL ) ,
__ATTR_NULL ,
} ;
static ssize_t driver_name_show ( struct device_driver * drv , char * buf )
{
struct omap_dss_driver * dssdrv = to_dss_driver ( drv ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " ,
dssdrv - > driver . name ?
dssdrv - > driver . name : " " ) ;
}
static struct driver_attribute default_drv_attrs [ ] = {
__ATTR ( name , S_IRUGO , driver_name_show , NULL ) ,
__ATTR_NULL ,
} ;
static struct bus_type dss_bus_type = {
. name = " omapdss " ,
. match = dss_bus_match ,
. dev_attrs = default_dev_attrs ,
. drv_attrs = default_drv_attrs ,
} ;
static void dss_bus_release ( struct device * dev )
{
DSSDBG ( " bus_release \n " ) ;
}
static struct device dss_bus = {
. release = dss_bus_release ,
} ;
struct bus_type * dss_get_bus ( void )
{
return & dss_bus_type ;
}
/* DRIVER */
static int dss_driver_probe ( struct device * dev )
{
int r ;
struct omap_dss_driver * dssdrv = to_dss_driver ( dev - > driver ) ;
struct omap_dss_device * dssdev = to_dss_device ( dev ) ;
struct omap_dss_board_info * pdata = core . pdev - > dev . platform_data ;
bool force ;
DSSDBG ( " driver_probe: dev %s/%s, drv %s \n " ,
dev_name ( dev ) , dssdev - > driver_name ,
dssdrv - > driver . name ) ;
dss_init_device ( core . pdev , dssdev ) ;
2010-02-17 14:36:48 +03:00
force = pdata - > default_device = = dssdev ;
dss_recheck_connections ( dssdev , force ) ;
2009-11-03 12:23:50 +03:00
r = dssdrv - > probe ( dssdev ) ;
if ( r ) {
DSSERR ( " driver probe failed: %d \n " , r ) ;
2010-02-17 12:50:07 +03:00
dss_uninit_device ( core . pdev , dssdev ) ;
2009-11-03 12:23:50 +03:00
return r ;
}
DSSDBG ( " probe done for device %s \n " , dev_name ( dev ) ) ;
dssdev - > driver = dssdrv ;
return 0 ;
}
static int dss_driver_remove ( struct device * dev )
{
struct omap_dss_driver * dssdrv = to_dss_driver ( dev - > driver ) ;
struct omap_dss_device * dssdev = to_dss_device ( dev ) ;
DSSDBG ( " driver_remove: dev %s/%s \n " , dev_name ( dev ) ,
dssdev - > driver_name ) ;
dssdrv - > remove ( dssdev ) ;
dss_uninit_device ( core . pdev , dssdev ) ;
dssdev - > driver = NULL ;
return 0 ;
}
int omap_dss_register_driver ( struct omap_dss_driver * dssdriver )
{
dssdriver - > driver . bus = & dss_bus_type ;
dssdriver - > driver . probe = dss_driver_probe ;
dssdriver - > driver . remove = dss_driver_remove ;
2010-01-11 14:54:33 +03:00
if ( dssdriver - > get_resolution = = NULL )
dssdriver - > get_resolution = omapdss_default_get_resolution ;
2010-01-11 15:33:40 +03:00
if ( dssdriver - > get_recommended_bpp = = NULL )
dssdriver - > get_recommended_bpp =
omapdss_default_get_recommended_bpp ;
2010-01-11 14:54:33 +03:00
2009-11-03 12:23:50 +03:00
return driver_register ( & dssdriver - > driver ) ;
}
EXPORT_SYMBOL ( omap_dss_register_driver ) ;
void omap_dss_unregister_driver ( struct omap_dss_driver * dssdriver )
{
driver_unregister ( & dssdriver - > driver ) ;
}
EXPORT_SYMBOL ( omap_dss_unregister_driver ) ;
/* DEVICE */
static void reset_device ( struct device * dev , int check )
{
u8 * dev_p = ( u8 * ) dev ;
u8 * dev_end = dev_p + sizeof ( * dev ) ;
void * saved_pdata ;
saved_pdata = dev - > platform_data ;
if ( check ) {
/*
* Check if there is any other setting than platform_data
* in struct device ; warn that these will be reset by our
* init .
*/
dev - > platform_data = NULL ;
while ( dev_p < dev_end ) {
if ( * dev_p ) {
WARN ( " %s: struct device fields will be "
" discarded \n " ,
__func__ ) ;
break ;
}
dev_p + + ;
}
}
memset ( dev , 0 , sizeof ( * dev ) ) ;
dev - > platform_data = saved_pdata ;
}
static void omap_dss_dev_release ( struct device * dev )
{
reset_device ( dev , 0 ) ;
}
int omap_dss_register_device ( struct omap_dss_device * dssdev )
{
static int dev_num ;
WARN_ON ( ! dssdev - > driver_name ) ;
reset_device ( & dssdev - > dev , 1 ) ;
dssdev - > dev . bus = & dss_bus_type ;
dssdev - > dev . parent = & dss_bus ;
dssdev - > dev . release = omap_dss_dev_release ;
dev_set_name ( & dssdev - > dev , " display%d " , dev_num + + ) ;
2010-02-17 14:36:48 +03:00
return device_register ( & dssdev - > dev ) ;
2009-11-03 12:23:50 +03:00
}
void omap_dss_unregister_device ( struct omap_dss_device * dssdev )
{
device_unregister ( & dssdev - > dev ) ;
}
/* BUS */
static int omap_dss_bus_register ( void )
{
int r ;
r = bus_register ( & dss_bus_type ) ;
if ( r ) {
DSSERR ( " bus register failed \n " ) ;
return r ;
}
dev_set_name ( & dss_bus , " omapdss " ) ;
r = device_register ( & dss_bus ) ;
if ( r ) {
DSSERR ( " bus driver register failed \n " ) ;
bus_unregister ( & dss_bus_type ) ;
return r ;
}
return 0 ;
}
/* INIT */
# ifdef CONFIG_OMAP2_DSS_MODULE
static void omap_dss_bus_unregister ( void )
{
device_unregister ( & dss_bus ) ;
bus_unregister ( & dss_bus_type ) ;
}
static int __init omap_dss_init ( void )
{
int r ;
r = omap_dss_bus_register ( ) ;
if ( r )
return r ;
r = platform_driver_register ( & omap_dss_driver ) ;
if ( r ) {
omap_dss_bus_unregister ( ) ;
return r ;
}
return 0 ;
}
static void __exit omap_dss_exit ( void )
{
2010-02-04 18:03:41 +03:00
if ( core . vdds_dsi_reg ! = NULL ) {
regulator_put ( core . vdds_dsi_reg ) ;
core . vdds_dsi_reg = NULL ;
}
if ( core . vdds_sdi_reg ! = NULL ) {
regulator_put ( core . vdds_sdi_reg ) ;
core . vdds_sdi_reg = NULL ;
}
2009-11-03 12:23:50 +03:00
platform_driver_unregister ( & omap_dss_driver ) ;
omap_dss_bus_unregister ( ) ;
}
module_init ( omap_dss_init ) ;
module_exit ( omap_dss_exit ) ;
# else
static int __init omap_dss_init ( void )
{
return omap_dss_bus_register ( ) ;
}
static int __init omap_dss_init2 ( void )
{
return platform_driver_register ( & omap_dss_driver ) ;
}
core_initcall ( omap_dss_init ) ;
device_initcall ( omap_dss_init2 ) ;
# endif
MODULE_AUTHOR ( " Tomi Valkeinen <tomi.valkeinen@nokia.com> " ) ;
MODULE_DESCRIPTION ( " OMAP2/3 Display Subsystem " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;