2011-01-24 06:21:54 +00:00
/*
* OMAP2plus display device setup / initialization .
*
* Copyright ( C ) 2010 Texas Instruments Incorporated - http : //www.ti.com/
* Senthilvadivu Guruswamy
* Sumit Semwal
*
* 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 " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2011-07-31 10:52:44 -04:00
# include <linux/string.h>
2011-01-24 06:21:54 +00:00
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <linux/err.h>
2011-05-11 14:05:07 +03:00
# include <video/omapdss.h>
2011-01-24 06:21:56 +00:00
# include <plat/omap_hwmod.h>
# include <plat/omap_device.h>
2011-05-23 15:50:47 +03:00
# include <plat/omap-pm.h>
2011-11-08 03:16:13 -07:00
# include <plat/common.h>
2011-01-24 06:21:54 +00:00
2012-01-02 14:02:37 +05:30
# include "mux.h"
2011-06-15 15:22:47 +03:00
# include "control.h"
2011-10-06 18:04:08 -06:00
# include "display.h"
# define DISPC_CONTROL 0x0040
# define DISPC_CONTROL2 0x0238
# define DISPC_IRQSTATUS 0x0018
# define DSS_SYSCONFIG 0x10
# define DSS_SYSSTATUS 0x14
# define DSS_CONTROL 0x40
# define DSS_SDI_CONTROL 0x44
# define DSS_PLL_CONTROL 0x48
# define LCD_EN_MASK (0x1 << 0)
# define DIGIT_EN_MASK (0x1 << 1)
# define FRAMEDONE_IRQ_SHIFT 0
# define EVSYNC_EVEN_IRQ_SHIFT 2
# define EVSYNC_ODD_IRQ_SHIFT 3
# define FRAMEDONE2_IRQ_SHIFT 22
# define FRAMEDONETV_IRQ_SHIFT 24
/*
* FRAMEDONE_IRQ_TIMEOUT : how long ( in milliseconds ) to wait during DISPC
* reset before deciding that something has gone wrong
*/
# define FRAMEDONE_IRQ_TIMEOUT 100
2011-06-15 15:22:47 +03:00
2011-01-24 06:21:54 +00:00
static struct platform_device omap_display_device = {
. name = " omapdss " ,
. id = - 1 ,
. dev = {
. platform_data = NULL ,
} ,
} ;
2011-04-18 09:32:13 +05:30
struct omap_dss_hwmod_data {
const char * oh_name ;
const char * dev_name ;
const int id ;
} ;
static const struct omap_dss_hwmod_data omap2_dss_hwmod_data [ ] __initdata = {
{ " dss_core " , " omapdss_dss " , - 1 } ,
{ " dss_dispc " , " omapdss_dispc " , - 1 } ,
{ " dss_rfbi " , " omapdss_rfbi " , - 1 } ,
{ " dss_venc " , " omapdss_venc " , - 1 } ,
} ;
static const struct omap_dss_hwmod_data omap3_dss_hwmod_data [ ] __initdata = {
{ " dss_core " , " omapdss_dss " , - 1 } ,
{ " dss_dispc " , " omapdss_dispc " , - 1 } ,
{ " dss_rfbi " , " omapdss_rfbi " , - 1 } ,
{ " dss_venc " , " omapdss_venc " , - 1 } ,
2011-08-03 14:00:57 +03:00
{ " dss_dsi1 " , " omapdss_dsi " , 0 } ,
2011-04-18 09:32:13 +05:30
} ;
static const struct omap_dss_hwmod_data omap4_dss_hwmod_data [ ] __initdata = {
{ " dss_core " , " omapdss_dss " , - 1 } ,
{ " dss_dispc " , " omapdss_dispc " , - 1 } ,
{ " dss_rfbi " , " omapdss_rfbi " , - 1 } ,
{ " dss_venc " , " omapdss_venc " , - 1 } ,
2011-08-03 14:00:57 +03:00
{ " dss_dsi1 " , " omapdss_dsi " , 0 } ,
{ " dss_dsi2 " , " omapdss_dsi " , 1 } ,
2011-04-18 09:32:13 +05:30
{ " dss_hdmi " , " omapdss_hdmi " , - 1 } ,
} ;
2012-01-02 14:02:37 +05:30
static void omap4_hdmi_mux_pads ( )
{
/* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
omap_mux_init_signal ( " hdmi_hpd " ,
OMAP_PIN_INPUT_PULLUP ) ;
omap_mux_init_signal ( " hdmi_cec " ,
OMAP_PIN_INPUT_PULLUP ) ;
/* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
omap_mux_init_signal ( " hdmi_ddc_scl " ,
OMAP_PIN_INPUT_PULLUP ) ;
omap_mux_init_signal ( " hdmi_ddc_sda " ,
OMAP_PIN_INPUT_PULLUP ) ;
}
2011-06-15 15:22:47 +03:00
static int omap4_dsi_mux_pads ( int dsi_id , unsigned lanes )
{
u32 enable_mask , enable_shift ;
u32 pipd_mask , pipd_shift ;
u32 reg ;
if ( dsi_id = = 0 ) {
enable_mask = OMAP4_DSI1_LANEENABLE_MASK ;
enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT ;
pipd_mask = OMAP4_DSI1_PIPD_MASK ;
pipd_shift = OMAP4_DSI1_PIPD_SHIFT ;
} else if ( dsi_id = = 1 ) {
enable_mask = OMAP4_DSI2_LANEENABLE_MASK ;
enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT ;
pipd_mask = OMAP4_DSI2_PIPD_MASK ;
pipd_shift = OMAP4_DSI2_PIPD_SHIFT ;
} else {
return - ENODEV ;
}
reg = omap4_ctrl_pad_readl ( OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY ) ;
reg & = ~ enable_mask ;
reg & = ~ pipd_mask ;
reg | = ( lanes < < enable_shift ) & enable_mask ;
reg | = ( lanes < < pipd_shift ) & pipd_mask ;
omap4_ctrl_pad_writel ( reg , OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY ) ;
return 0 ;
}
2012-01-02 14:02:37 +05:30
int omap_hdmi_init ( void )
{
if ( cpu_is_omap44xx ( ) )
omap4_hdmi_mux_pads ( ) ;
return 0 ;
}
2011-06-15 15:21:12 +03:00
static int omap_dsi_enable_pads ( int dsi_id , unsigned lane_mask )
{
2011-06-15 15:22:47 +03:00
if ( cpu_is_omap44xx ( ) )
return omap4_dsi_mux_pads ( dsi_id , lane_mask ) ;
2011-06-15 15:21:12 +03:00
return 0 ;
}
static void omap_dsi_disable_pads ( int dsi_id , unsigned lane_mask )
{
2011-06-15 15:22:47 +03:00
if ( cpu_is_omap44xx ( ) )
omap4_dsi_mux_pads ( dsi_id , 0 ) ;
2011-06-15 15:21:12 +03:00
}
2011-01-24 06:21:54 +00:00
int __init omap_display_init ( struct omap_dss_board_info * board_data )
{
int r = 0 ;
2011-01-24 06:21:56 +00:00
struct omap_hwmod * oh ;
2011-07-21 13:48:45 -07:00
struct platform_device * pdev ;
2011-04-18 09:32:13 +05:30
int i , oh_count ;
2011-01-24 06:21:56 +00:00
struct omap_display_platform_data pdata ;
2011-04-18 09:32:13 +05:30
const struct omap_dss_hwmod_data * curr_dss_hwmod ;
2011-01-24 06:21:56 +00:00
memset ( & pdata , 0 , sizeof ( pdata ) ) ;
2011-04-18 09:32:13 +05:30
if ( cpu_is_omap24xx ( ) ) {
curr_dss_hwmod = omap2_dss_hwmod_data ;
oh_count = ARRAY_SIZE ( omap2_dss_hwmod_data ) ;
} else if ( cpu_is_omap34xx ( ) ) {
curr_dss_hwmod = omap3_dss_hwmod_data ;
oh_count = ARRAY_SIZE ( omap3_dss_hwmod_data ) ;
} else {
curr_dss_hwmod = omap4_dss_hwmod_data ;
oh_count = ARRAY_SIZE ( omap4_dss_hwmod_data ) ;
}
2011-01-27 11:17:04 +00:00
2011-06-15 15:21:12 +03:00
if ( board_data - > dsi_enable_pads = = NULL )
board_data - > dsi_enable_pads = omap_dsi_enable_pads ;
if ( board_data - > dsi_disable_pads = = NULL )
board_data - > dsi_disable_pads = omap_dsi_disable_pads ;
2011-01-24 06:21:56 +00:00
pdata . board_data = board_data ;
2011-05-23 15:50:47 +03:00
pdata . board_data - > get_context_loss_count =
omap_pm_get_dev_context_loss_count ;
2011-01-24 06:21:56 +00:00
for ( i = 0 ; i < oh_count ; i + + ) {
2011-04-18 09:32:13 +05:30
oh = omap_hwmod_lookup ( curr_dss_hwmod [ i ] . oh_name ) ;
2011-01-24 06:21:56 +00:00
if ( ! oh ) {
2011-04-18 09:32:13 +05:30
pr_err ( " Could not look up %s \n " ,
curr_dss_hwmod [ i ] . oh_name ) ;
2011-01-24 06:21:56 +00:00
return - ENODEV ;
}
2011-03-01 02:42:13 -06:00
2011-07-21 13:48:45 -07:00
pdev = omap_device_build ( curr_dss_hwmod [ i ] . dev_name ,
2011-04-18 09:32:13 +05:30
curr_dss_hwmod [ i ] . id , oh , & pdata ,
2011-01-24 06:21:56 +00:00
sizeof ( struct omap_display_platform_data ) ,
2011-08-10 15:30:09 +02:00
NULL , 0 , 0 ) ;
2011-01-24 06:21:56 +00:00
2011-07-21 13:48:45 -07:00
if ( WARN ( ( IS_ERR ( pdev ) ) , " Could not build omap_device for %s \n " ,
2011-04-18 09:32:13 +05:30
curr_dss_hwmod [ i ] . oh_name ) )
2011-01-24 06:21:56 +00:00
return - ENODEV ;
}
2011-01-24 06:21:54 +00:00
omap_display_device . dev . platform_data = board_data ;
r = platform_device_register ( & omap_display_device ) ;
if ( r < 0 )
printk ( KERN_ERR " Unable to register OMAP-Display device \n " ) ;
return r ;
}
2011-11-08 03:16:13 -07:00
2011-10-06 18:04:08 -06:00
static void dispc_disable_outputs ( void )
{
u32 v , irq_mask = 0 ;
bool lcd_en , digit_en , lcd2_en = false ;
int i ;
struct omap_dss_dispc_dev_attr * da ;
struct omap_hwmod * oh ;
oh = omap_hwmod_lookup ( " dss_dispc " ) ;
if ( ! oh ) {
WARN ( 1 , " display: could not disable outputs during reset - could not find dss_dispc hwmod \n " ) ;
return ;
}
if ( ! oh - > dev_attr ) {
pr_err ( " display: could not disable outputs during reset due to missing dev_attr \n " ) ;
return ;
}
da = ( struct omap_dss_dispc_dev_attr * ) oh - > dev_attr ;
/* store value of LCDENABLE and DIGITENABLE bits */
v = omap_hwmod_read ( oh , DISPC_CONTROL ) ;
lcd_en = v & LCD_EN_MASK ;
digit_en = v & DIGIT_EN_MASK ;
/* store value of LCDENABLE for LCD2 */
if ( da - > manager_count > 2 ) {
v = omap_hwmod_read ( oh , DISPC_CONTROL2 ) ;
lcd2_en = v & LCD_EN_MASK ;
}
if ( ! ( lcd_en | digit_en | lcd2_en ) )
return ; /* no managers currently enabled */
/*
* If any manager was enabled , we need to disable it before
* DSS clocks are disabled or DISPC module is reset
*/
if ( lcd_en )
irq_mask | = 1 < < FRAMEDONE_IRQ_SHIFT ;
if ( digit_en ) {
if ( da - > has_framedonetv_irq ) {
irq_mask | = 1 < < FRAMEDONETV_IRQ_SHIFT ;
} else {
irq_mask | = 1 < < EVSYNC_EVEN_IRQ_SHIFT |
1 < < EVSYNC_ODD_IRQ_SHIFT ;
}
}
if ( lcd2_en )
irq_mask | = 1 < < FRAMEDONE2_IRQ_SHIFT ;
/*
* clear any previous FRAMEDONE , FRAMEDONETV ,
* EVSYNC_EVEN / ODD or FRAMEDONE2 interrupts
*/
omap_hwmod_write ( irq_mask , oh , DISPC_IRQSTATUS ) ;
/* disable LCD and TV managers */
v = omap_hwmod_read ( oh , DISPC_CONTROL ) ;
v & = ~ ( LCD_EN_MASK | DIGIT_EN_MASK ) ;
omap_hwmod_write ( v , oh , DISPC_CONTROL ) ;
/* disable LCD2 manager */
if ( da - > manager_count > 2 ) {
v = omap_hwmod_read ( oh , DISPC_CONTROL2 ) ;
v & = ~ LCD_EN_MASK ;
omap_hwmod_write ( v , oh , DISPC_CONTROL2 ) ;
}
i = 0 ;
while ( ( omap_hwmod_read ( oh , DISPC_IRQSTATUS ) & irq_mask ) ! =
irq_mask ) {
i + + ;
if ( i > FRAMEDONE_IRQ_TIMEOUT ) {
pr_err ( " didn't get FRAMEDONE1/2 or TV interrupt \n " ) ;
break ;
}
mdelay ( 1 ) ;
}
}
2011-11-08 03:16:13 -07:00
# define MAX_MODULE_SOFTRESET_WAIT 10000
int omap_dss_reset ( struct omap_hwmod * oh )
{
struct omap_hwmod_opt_clk * oc ;
int c = 0 ;
int i , r ;
if ( ! ( oh - > class - > sysc - > sysc_flags & SYSS_HAS_RESET_STATUS ) ) {
pr_err ( " dss_core: hwmod data doesn't contain reset data \n " ) ;
return - EINVAL ;
}
for ( i = oh - > opt_clks_cnt , oc = oh - > opt_clks ; i > 0 ; i - - , oc + + )
if ( oc - > _clk )
clk_enable ( oc - > _clk ) ;
2011-10-06 18:04:08 -06:00
dispc_disable_outputs ( ) ;
/* clear SDI registers */
if ( cpu_is_omap3430 ( ) ) {
omap_hwmod_write ( 0x0 , oh , DSS_SDI_CONTROL ) ;
omap_hwmod_write ( 0x0 , oh , DSS_PLL_CONTROL ) ;
}
/*
* clear DSS_CONTROL register to switch DSS clock sources to
* PRCM clock , if any
*/
omap_hwmod_write ( 0x0 , oh , DSS_CONTROL ) ;
2011-11-08 03:16:13 -07:00
omap_test_timeout ( ( omap_hwmod_read ( oh , oh - > class - > sysc - > syss_offs )
& SYSS_RESETDONE_MASK ) ,
MAX_MODULE_SOFTRESET_WAIT , c ) ;
if ( c = = MAX_MODULE_SOFTRESET_WAIT )
pr_warning ( " dss_core: waiting for reset to finish failed \n " ) ;
else
pr_debug ( " dss_core: softreset done \n " ) ;
for ( i = oh - > opt_clks_cnt , oc = oh - > opt_clks ; i > 0 ; i - - , oc + + )
if ( oc - > _clk )
clk_disable ( oc - > _clk ) ;
r = ( c = = MAX_MODULE_SOFTRESET_WAIT ) ? - ETIMEDOUT : 0 ;
return r ;
}