@ -6,6 +6,7 @@
# include <common.h>
# include <dm.h>
# include <fdtdec.h>
# include <panel.h>
# include <pwm.h>
# include <video.h>
# include <asm/system.h>
@ -21,28 +22,6 @@
DECLARE_GLOBAL_DATA_PTR ;
/* These are the stages we go throuh in enabling the LCD */
enum stage_t {
STAGE_START ,
STAGE_PANEL_VDD ,
STAGE_LVDS ,
STAGE_BACKLIGHT_VDD ,
STAGE_PWM ,
STAGE_BACKLIGHT_EN ,
STAGE_DONE ,
} ;
# define FDT_LCD_TIMINGS 4
enum {
FDT_LCD_TIMING_REF_TO_SYNC ,
FDT_LCD_TIMING_SYNC_WIDTH ,
FDT_LCD_TIMING_BACK_PORCH ,
FDT_LCD_TIMING_FRONT_PORCH ,
FDT_LCD_TIMING_COUNT ,
} ;
enum lcd_cache_t {
FDT_LCD_CACHE_OFF = 0 ,
FDT_LCD_CACHE_WRITE_THROUGH = 1 < < 0 ,
@ -54,37 +33,15 @@ enum lcd_cache_t {
/* Information about the display controller */
struct tegra_lcd_priv {
enum stage_t stage ; /* Current stage we are at */
unsigned long timer_next ; /* Time we can move onto next stage */
int width ; /* width in pixels */
int height ; /* height in pixels */
/*
* log2 of number of bpp , in general , unless it bpp is 24 in which
* case this field holds 24 also ! This is a U - Boot thing .
*/
int log2_bpp ;
enum video_log2_bpp log2_bpp ; /* colour depth */
struct display_timing timing ;
struct udevice * panel ;
struct disp_ctlr * disp ; /* Display controller to use */
fdt_addr_t frame_buffer ; /* Address of frame buffer */
unsigned pixel_clock ; /* Pixel clock in Hz */
uint horiz_timing [ FDT_LCD_TIMING_COUNT ] ; /* Horizontal timing */
uint vert_timing [ FDT_LCD_TIMING_COUNT ] ; /* Vertical timing */
struct udevice * pwm ;
int pwm_channel ; /* PWM channel to use for backlight */
enum lcd_cache_t cache_type ;
struct gpio_desc backlight_en ; /* GPIO for backlight enable */
struct gpio_desc lvds_shutdown ; /* GPIO for lvds shutdown */
struct gpio_desc backlight_vdd ; /* GPIO for backlight vdd */
struct gpio_desc panel_vdd ; /* GPIO for panel vdd */
/*
* Panel required timings
* Timing 1 : delay between panel_vdd - rise and data - rise
* Timing 2 : delay between data - rise and backlight_vdd - rise
* Timing 3 : delay between backlight_vdd and pwm - rise
* Timing 4 : delay between pwm - rise and backlight_en - rise
*/
uint panel_timings [ FDT_LCD_TIMINGS ] ;
} ;
enum {
@ -150,26 +107,23 @@ static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
writel ( val , & dc - > cmd . state_ctrl ) ;
}
static void write_pair ( struct tegra_lcd_priv * priv , int item , u32 * reg )
{
writel ( priv - > horiz_timing [ item ] |
( priv - > vert_timing [ item ] < < 16 ) , reg ) ;
}
static int update_display_mode ( struct dc_disp_reg * disp ,
struct tegra_lcd_priv * priv )
{
struct display_timing * dt = & priv - > timing ;
unsigned long val ;
unsigned long rate ;
unsigned long div ;
writel ( 0x0 , & disp - > disp_timing_opt ) ;
write_pair ( priv , FDT_LCD_TIMING_REF_TO_SYNC , & disp - > ref_to_sync ) ;
write_pair ( priv , FDT_LCD_TIMING_SYNC_WIDTH , & disp - > sync_width ) ;
write_pair ( priv , FDT_LCD_TIMING_BACK_PORCH , & disp - > back_porch ) ;
write_pair ( priv , FDT_LCD_TIMING_FRONT_PORCH , & disp - > front_porch ) ;
writel ( priv - > width | ( priv - > height < < 16 ) , & disp - > disp_active ) ;
writel ( 1 | 1 < < 16 , & disp - > ref_to_sync ) ;
writel ( dt - > hsync_len . typ | dt - > vsync_len . typ < < 16 , & disp - > sync_width ) ;
writel ( dt - > hback_porch . typ | dt - > vback_porch . typ < < 16 ,
& disp - > back_porch ) ;
writel ( ( dt - > hfront_porch . typ - 1 ) | ( dt - > vfront_porch . typ - 1 ) < < 16 ,
& disp - > front_porch ) ;
writel ( dt - > hactive . typ | ( dt - > vactive . typ < < 16 ) , & disp - > disp_active ) ;
val = DE_SELECT_ACTIVE < < DE_SELECT_SHIFT ;
val | = DE_CONTROL_NORMAL < < DE_CONTROL_SHIFT ;
@ -287,12 +241,11 @@ static int setup_window(struct disp_ctl_win *win,
win - > stride = priv - > width * ( 1 < < priv - > log2_bpp ) / 8 ;
debug ( " %s: depth = %d \n " , __func__ , priv - > log2_bpp ) ;
switch ( priv - > log2_bpp ) {
case 5 :
case 24 :
case VIDEO_BPP32 :
win - > fmt = COLOR_DEPTH_R8G8B8A8 ;
win - > bpp = 32 ;
break ;
case 4 :
case VIDEO_BPP16 :
win - > fmt = COLOR_DEPTH_B5G6R5 ;
win - > bpp = 16 ;
break ;
@ -305,18 +258,6 @@ static int setup_window(struct disp_ctl_win *win,
return 0 ;
}
static void debug_timing ( const char * name , unsigned int timing [ ] )
{
# ifdef DEBUG
int i ;
debug ( " %s timing: " , name ) ;
for ( i = 0 ; i < FDT_LCD_TIMING_COUNT ; i + + )
debug ( " %d " , timing [ i ] ) ;
debug ( " \n " ) ;
# endif
}
/**
* Register a new display based on device tree configuration .
*
@ -363,112 +304,6 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
return 0 ;
}
/**
* Handle the next stage of device init
*/
static int handle_stage ( const void * blob , struct tegra_lcd_priv * priv )
{
debug ( " %s: stage %d \n " , __func__ , priv - > stage ) ;
/* do the things for this stage */
switch ( priv - > stage ) {
case STAGE_START :
/*
* It is possible that the FDT has requested that the LCD be
* disabled . We currently don ' t support this . It would require
* changes to U - Boot LCD subsystem to have LCD support
* compiled in but not used . An easier option might be to
* still have a frame buffer , but leave the backlight off and
* remove all mention of lcd in the stdout environment
* variable .
*/
funcmux_select ( PERIPH_ID_DISP1 , FUNCMUX_DEFAULT ) ;
break ;
case STAGE_PANEL_VDD :
if ( dm_gpio_is_valid ( & priv - > panel_vdd ) )
dm_gpio_set_value ( & priv - > panel_vdd , 1 ) ;
break ;
case STAGE_LVDS :
if ( dm_gpio_is_valid ( & priv - > lvds_shutdown ) )
dm_gpio_set_value ( & priv - > lvds_shutdown , 1 ) ;
break ;
case STAGE_BACKLIGHT_VDD :
if ( dm_gpio_is_valid ( & priv - > backlight_vdd ) )
dm_gpio_set_value ( & priv - > backlight_vdd , 1 ) ;
break ;
case STAGE_PWM :
/* Enable PWM at 15/16 high, 32768 Hz with divider 1 */
pinmux_set_func ( PMUX_PINGRP_GPU , PMUX_FUNC_PWM ) ;
pinmux_tristate_disable ( PMUX_PINGRP_GPU ) ;
pwm_set_config ( priv - > pwm , priv - > pwm_channel , 0xdf , 0xff ) ;
pwm_set_enable ( priv - > pwm , priv - > pwm_channel , true ) ;
break ;
case STAGE_BACKLIGHT_EN :
if ( dm_gpio_is_valid ( & priv - > backlight_en ) )
dm_gpio_set_value ( & priv - > backlight_en , 1 ) ;
break ;
case STAGE_DONE :
break ;
}
/* set up timer for next stage */
priv - > timer_next = timer_get_us ( ) ;
if ( priv - > stage < FDT_LCD_TIMINGS )
priv - > timer_next + = priv - > panel_timings [ priv - > stage ] * 1000 ;
/* move to next stage */
priv - > stage + + ;
return 0 ;
}
/**
* Perform the next stage of the LCD init if it is time to do so .
*
* LCD init can be time - consuming because of the number of delays we need
* while waiting for the backlight power supply , etc . This function can
* be called at various times during U - Boot operation to advance the
* initialization of the LCD to the next stage if sufficient time has
* passed since the last stage . It keeps track of what stage it is up to
* and the time that it is permitted to move to the next stage .
*
* The final call should have wait = 1 to complete the init .
*
* @ param blob fdt blob containing LCD information
* @ param wait 1 to wait until all init is complete , and then return
* 0 to return immediately , potentially doing nothing if it is
* not yet time for the next init .
*/
static int tegra_lcd_check_next_stage ( const void * blob ,
struct tegra_lcd_priv * priv , int wait )
{
if ( priv - > stage = = STAGE_DONE )
return 0 ;
do {
/* wait if we need to */
debug ( " %s: stage %d \n " , __func__ , priv - > stage ) ;
if ( priv - > stage ! = STAGE_START ) {
int delay = priv - > timer_next - timer_get_us ( ) ;
if ( delay > 0 ) {
if ( wait )
udelay ( delay ) ;
else
return 0 ;
}
}
if ( handle_stage ( blob , priv ) )
return - 1 ;
} while ( wait & & priv - > stage ! = STAGE_DONE ) ;
if ( priv - > stage = = STAGE_DONE )
debug ( " %s: LCD init complete \n " , __func__ ) ;
return 0 ;
}
static int tegra_lcd_probe ( struct udevice * dev )
{
struct video_uc_platdata * plat = dev_get_uclass_platdata ( dev ) ;
@ -476,14 +311,23 @@ static int tegra_lcd_probe(struct udevice *dev)
struct tegra_lcd_priv * priv = dev_get_priv ( dev ) ;
const void * blob = gd - > fdt_blob ;
int type = DCACHE_OFF ;
int ret ;
/* Initialize the Tegra display controller */
funcmux_select ( PERIPH_ID_DISP1 , FUNCMUX_DEFAULT ) ;
if ( tegra_display_probe ( blob , priv , ( void * ) plat - > base ) ) {
printf ( " %s: Failed to probe display driver \n " , __func__ ) ;
return - 1 ;
}
tegra_lcd_check_next_stage ( blob , priv , 1 ) ;
pinmux_set_func ( PMUX_PINGRP_GPU , PMUX_FUNC_PWM ) ;
pinmux_tristate_disable ( PMUX_PINGRP_GPU ) ;
ret = panel_enable_backlight ( priv - > panel ) ;
if ( ret ) {
debug ( " %s: Cannot enable backlight, ret=%d \n " , __func__ , ret ) ;
return ret ;
}
/* Set up the LCD caching as requested */
if ( priv - > cache_type & FDT_LCD_CACHE_WRITE_THROUGH )
@ -507,13 +351,11 @@ static int tegra_lcd_probe(struct udevice *dev)
static int tegra_lcd_ofdata_to_platdata ( struct udevice * dev )
{
struct tegra_lcd_priv * priv = dev_get_priv ( dev ) ;
struct fdtdec_phandle_args args ;
const void * blob = gd - > fdt_blob ;
struct display_timing * timing ;
int node = dev - > of_offset ;
int front , back , ref ;
int panel_node ;
int rgb ;
int bpp , bit ;
int ret ;
priv - > disp = ( struct disp_ctlr * ) dev_get_addr ( dev ) ;
@ -523,96 +365,40 @@ static int tegra_lcd_ofdata_to_platdata(struct udevice *dev)
}
rgb = fdt_subnode_offset ( blob , node , " rgb " ) ;
panel_node = fdtdec_lookup_phandle ( blob , rgb , " nvidia,panel " ) ;
if ( panel_node < 0 ) {
debug ( " %s: Cannot find panel information \n " , __func__ ) ;
if ( rgb < 0 ) {
debug ( " %s: Cannot find rgb subnode for '%s' (ret=%d) \n " ,
__func__ , dev - > name , rgb ) ;
return - EINVAL ;
}
priv - > width = fdtdec_get_int ( blob , panel_node , " xres " , - 1 ) ;
priv - > height = fdtdec_get_int ( blob , panel_node , " yres " , - 1 ) ;
priv - > pixel_clock = fdtdec_get_int ( blob , panel_node , " clock " , 0 ) ;
if ( ! priv - > pixel_clock | | priv - > width = = - 1 | | priv - > height = = - 1 ) {
debug ( " %s: Pixel parameters missing \n " , __func__ ) ;
return - EINVAL ;
}
back = fdtdec_get_int ( blob , panel_node , " left-margin " , - 1 ) ;
front = fdtdec_get_int ( blob , panel_node , " right-margin " , - 1 ) ;
ref = fdtdec_get_int ( blob , panel_node , " hsync-len " , - 1 ) ;
if ( ( back | front | ref ) = = - 1 ) {
debug ( " %s: Horizontal parameters missing \n " , __func__ ) ;
return - EINVAL ;
}
/* Use a ref-to-sync of 1 always, and take this from the front porch */
priv - > horiz_timing [ FDT_LCD_TIMING_REF_TO_SYNC ] = 1 ;
priv - > horiz_timing [ FDT_LCD_TIMING_SYNC_WIDTH ] = ref ;
priv - > horiz_timing [ FDT_LCD_TIMING_BACK_PORCH ] = back ;
priv - > horiz_timing [ FDT_LCD_TIMING_FRONT_PORCH ] = front -
priv - > horiz_timing [ FDT_LCD_TIMING_REF_TO_SYNC ] ;
debug_timing ( " horiz " , priv - > horiz_timing ) ;
back = fdtdec_get_int ( blob , panel_node , " upper-margin " , - 1 ) ;
front = fdtdec_get_int ( blob , panel_node , " lower-margin " , - 1 ) ;
ref = fdtdec_get_int ( blob , panel_node , " vsync-len " , - 1 ) ;
if ( ( back | front | ref ) = = - 1 ) {
debug ( " %s: Vertical parameters missing \n " , __func__ ) ;
return - EINVAL ;
}
priv - > vert_timing [ FDT_LCD_TIMING_REF_TO_SYNC ] = 1 ;
priv - > vert_timing [ FDT_LCD_TIMING_SYNC_WIDTH ] = ref ;
priv - > vert_timing [ FDT_LCD_TIMING_BACK_PORCH ] = back ;
priv - > vert_timing [ FDT_LCD_TIMING_FRONT_PORCH ] = front -
priv - > vert_timing [ FDT_LCD_TIMING_REF_TO_SYNC ] ;
debug_timing ( " vert " , priv - > vert_timing ) ;
bpp = fdtdec_get_int ( blob , panel_node , " nvidia,bits-per-pixel " , - 1 ) ;
bit = ffs ( bpp ) - 1 ;
if ( bpp = = ( 1 < < bit ) )
priv - > log2_bpp = bit ;
else
priv - > log2_bpp = bpp ;
if ( bpp = = - 1 ) {
debug ( " %s: Pixel bpp parameters missing \n " , __func__ ) ;
ret = fdtdec_decode_display_timing ( blob , rgb , 0 , & priv - > timing ) ;
if ( ret ) {
debug ( " %s: Cannot read display timing for '%s' (ret=%d) \n " ,
__func__ , dev - > name , ret ) ;
return - EINVAL ;
}
timing = & priv - > timing ;
priv - > width = timing - > hactive . typ ;
priv - > height = timing - > vactive . typ ;
priv - > pixel_clock = timing - > pixelclock . typ ;
priv - > log2_bpp = VIDEO_BPP16 ;
if ( fdtdec_parse_phandle_with_args ( blob , panel_node , " nvidia,pwm " ,
" #pwm-cells " , 0 , 0 , & args ) ) {
debug ( " %s: Unable to decode PWM \n " , __func__ ) ;
/*
* Sadly the panel phandle is in an rgb subnode so we cannot use
* uclass_get_device_by_phandle ( ) .
*/
panel_node = fdtdec_lookup_phandle ( blob , rgb , " nvidia,panel " ) ;
if ( panel_node < 0 ) {
debug ( " %s: Cannot find panel information \n " , __func__ ) ;
return - EINVAL ;
}
ret = uclass_get_device_by_of_offset ( UCLASS_PWM , args . node , & priv - > pwm ) ;
ret = uclass_get_device_by_of_offset ( UCLASS_PANEL , panel_node ,
& priv - > panel ) ;
if ( ret ) {
debug ( " %s: Unable to find PWM \n " , __func__ ) ;
return - EINVAL ;
debug ( " %s: Cannot find panel for '%s' (ret=%d) \n " , __func__ ,
dev - > name , ret ) ;
return ret ;
}
priv - > pwm_channel = args . args [ 0 ] ;
priv - > cache_type = fdtdec_get_int ( blob , panel_node , " nvidia,cache-type " ,
FDT_LCD_CACHE_WRITE_BACK_FLUSH ) ;
/* These GPIOs are all optional */
gpio_request_by_name_nodev ( blob , panel_node ,
" nvidia,backlight-enable-gpios " , 0 ,
& priv - > backlight_en , GPIOD_IS_OUT ) ;
gpio_request_by_name_nodev ( blob , panel_node ,
" nvidia,lvds-shutdown-gpios " , 0 ,
& priv - > lvds_shutdown , GPIOD_IS_OUT ) ;
gpio_request_by_name_nodev ( blob , panel_node ,
" nvidia,backlight-vdd-gpios " , 0 ,
& priv - > backlight_vdd , GPIOD_IS_OUT ) ;
gpio_request_by_name_nodev ( blob , panel_node ,
" nvidia,panel-vdd-gpios " , 0 ,
& priv - > panel_vdd , GPIOD_IS_OUT ) ;
if ( fdtdec_get_int_array ( blob , panel_node , " nvidia,panel-timings " ,
priv - > panel_timings , FDT_LCD_TIMINGS ) )
return - EINVAL ;
return 0 ;
}