/*
* ( C ) Copyright 2004
* Pierre Aubert , Staubli Faverges , < p . aubert @ staubli . com >
* Copyright 2011 Freescale Semiconductor , Inc .
*
* SPDX - License - Identifier : GPL - 2.0 +
*/
/************************************************************************
Get Parameters for the video mode :
The default video mode can be defined in CONFIG_SYS_DEFAULT_VIDEO_MODE .
If undefined , default video mode is set to 0x301
Parameters can be set via the variable " videomode " in the environment .
2 diferent ways are possible :
" videomode=301 " - 301 is a hexadecimal number describing the VESA
mode . Following modes are implemented :
Colors 640 x480 800 x600 1024 x768 1152 x864 1280 x1024
- - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
8 bits | 0x301 0x303 0x305 0x161 0x307
15 bits | 0x310 0x313 0x316 0x162 0x319
16 bits | 0x311 0x314 0x317 0x163 0x31A
24 bits | 0x312 0x315 0x318 ? 0x31B
- - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
" videomode=bootargs "
- the parameters are parsed from the bootargs .
The format is " NAME:VALUE,NAME:VALUE " etc .
Ex . :
" bootargs=video=ctfb:x:800,y:600,depth:16,pclk:25000 "
Parameters not included in the list will be taken from
the default mode , which is one of the following :
mode : 0 640 x480x24
mode : 1 800 x600x16
mode : 2 1024 x768x8
mode : 3 960 x720x24
mode : 4 1152 x864x16
mode : 5 1280 x1024x8
if " mode " is not provided within the parameter list ,
mode : 0 is assumed .
Following parameters are supported :
x xres = visible resolution horizontal
y yres = visible resolution vertical
pclk pixelclocks in pico sec
le left_marging time from sync to picture in pixelclocks
ri right_marging time from picture to sync in pixelclocks
up upper_margin time from sync to picture
lo lower_margin
hs hsync_len length of horizontal sync
vs vsync_len length of vertical sync
sync see FB_SYNC_ *
vmode see FB_VMODE_ *
depth Color depth in bits per pixel
All other parameters in the variable bootargs are ignored .
It is also possible to set the parameters direct in the
variable " videomode " , or in another variable i . e .
" myvideo " and setting the variable " videomode=myvideo " . .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <common.h>
# include <edid.h>
# include <errno.h>
# include <linux/ctype.h>
# include "videomodes.h"
const struct ctfb_vesa_modes vesa_modes [ VESA_MODES_COUNT ] = {
{ 0x301 , RES_MODE_640x480 , 8 } ,
{ 0x310 , RES_MODE_640x480 , 15 } ,
{ 0x311 , RES_MODE_640x480 , 16 } ,
{ 0x312 , RES_MODE_640x480 , 24 } ,
{ 0x303 , RES_MODE_800x600 , 8 } ,
{ 0x313 , RES_MODE_800x600 , 15 } ,
{ 0x314 , RES_MODE_800x600 , 16 } ,
{ 0x315 , RES_MODE_800x600 , 24 } ,
{ 0x305 , RES_MODE_1024x768 , 8 } ,
{ 0x316 , RES_MODE_1024x768 , 15 } ,
{ 0x317 , RES_MODE_1024x768 , 16 } ,
{ 0x318 , RES_MODE_1024x768 , 24 } ,
{ 0x161 , RES_MODE_1152x864 , 8 } ,
{ 0x162 , RES_MODE_1152x864 , 15 } ,
{ 0x163 , RES_MODE_1152x864 , 16 } ,
{ 0x307 , RES_MODE_1280x1024 , 8 } ,
{ 0x319 , RES_MODE_1280x1024 , 15 } ,
{ 0x31A , RES_MODE_1280x1024 , 16 } ,
{ 0x31B , RES_MODE_1280x1024 , 24 } ,
} ;
const struct ctfb_res_modes res_mode_init [ RES_MODES_COUNT ] = {
/* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */
# ifndef CONFIG_VIDEO_STD_TIMINGS
{ 640 , 480 , 60 , 39721 , 25180 , 40 , 24 , 32 , 11 , 96 , 2 , 0 , FB_VMODE_NONINTERLACED } ,
{ 800 , 600 , 60 , 27778 , 36000 , 64 , 24 , 22 , 1 , 72 , 2 , 0 , FB_VMODE_NONINTERLACED } ,
{ 1024 , 768 , 60 , 15384 , 65000 , 168 , 8 , 29 , 3 , 144 , 4 , 0 , FB_VMODE_NONINTERLACED } ,
{ 960 , 720 , 80 , 13100 , 76335 , 160 , 40 , 32 , 8 , 80 , 4 , 0 , FB_VMODE_NONINTERLACED } ,
{ 1152 , 864 , 60 , 12004 , 83300 , 200 , 64 , 32 , 16 , 80 , 4 , 0 , FB_VMODE_NONINTERLACED } ,
{ 1280 , 1024 , 60 , 9090 , 110000 , 200 , 48 , 26 , 1 , 184 , 3 , 0 , FB_VMODE_NONINTERLACED } ,
# else
{ 640 , 480 , 60 , 39683 , 25200 , 48 , 16 , 33 , 10 , 96 , 2 , 0 , FB_VMODE_NONINTERLACED } ,
{ 800 , 600 , 60 , 25000 , 40000 , 88 , 40 , 23 , 1 , 128 , 4 , FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT , FB_VMODE_NONINTERLACED } ,
{ 1024 , 768 , 60 , 15384 , 65000 , 160 , 24 , 29 , 3 , 136 , 6 , 0 , FB_VMODE_NONINTERLACED } ,
{ 960 , 720 , 75 , 13468 , 74250 , 176 , 72 , 27 , 1 , 112 , 2 , 0 , FB_VMODE_NONINTERLACED } ,
{ 1152 , 864 , 75 , 9259 , 108000 , 256 , 64 , 32 , 1 , 128 , 3 , FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT , FB_VMODE_NONINTERLACED } ,
{ 1280 , 1024 , 60 , 9259 , 108000 , 248 , 48 , 38 , 1 , 112 , 3 , FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT , FB_VMODE_NONINTERLACED } ,
# endif
{ 1280 , 720 , 60 , 13468 , 74250 , 220 , 110 , 20 , 5 , 40 , 5 , FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT , FB_VMODE_NONINTERLACED } ,
{ 1360 , 768 , 60 , 11696 , 85500 , 256 , 64 , 17 , 3 , 112 , 7 , 0 , FB_VMODE_NONINTERLACED } ,
{ 1920 , 1080 , 60 , 6734 , 148500 , 148 , 88 , 36 , 4 , 44 , 5 , FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT , FB_VMODE_NONINTERLACED } ,
{ 1920 , 1200 , 60 , 6494 , 154000 , 80 , 48 , 26 , 3 , 32 , 6 , FB_SYNC_HOR_HIGH_ACT , FB_VMODE_NONINTERLACED } ,
} ;
/************************************************************************
* Get Parameters for the video mode :
*/
/*********************************************************************
* returns the length to the next seperator
*/
static int
video_get_param_len ( const char * start , char sep )
{
int i = 0 ;
while ( ( * start ! = 0 ) & & ( * start ! = sep ) ) {
start + + ;
i + + ;
}
return i ;
}
static int
video_search_param ( char * start , char * param )
{
int len , totallen , i ;
char * p = start ;
len = strlen ( param ) ;
totallen = len + strlen ( start ) ;
for ( i = 0 ; i < totallen ; i + + ) {
if ( strncmp ( p + + , param , len ) = = 0 )
return ( i ) ;
}
return - 1 ;
}
/***************************************************************
* Get parameter via the environment as it is done for the
* linux kernel i . e :
* video = ctfb : x : 800 , xv : 1280 , y : 600 , yv : 1024 , depth : 16 , mode : 0 , pclk : 25000 ,
* le : 56 , ri : 48 , up : 26 , lo : 5 , hs : 152 , vs : 2 , sync : 0 , vmode : 0 , accel : 0
*
* penv is a pointer to the environment , containing the string , or the name of
* another environment variable . It could even be the term " bootargs "
*/
# define GET_OPTION(name,var) \
if ( strncmp ( p , name , strlen ( name ) ) = = 0 ) { \
val_s = p + strlen ( name ) ; \
var = simple_strtoul ( val_s , NULL , 10 ) ; \
}
int video_get_params ( struct ctfb_res_modes * pPar , char * penv )
{
char * p , * s , * val_s ;
int i = 0 ;
int bpp ;
int mode ;
/* first search for the environment containing the real param string */
s = penv ;
if ( ( p = getenv ( s ) ) ! = NULL )
s = p ;
/*
* in case of the bootargs line , we have to start
* after " video=ctfb: "
*/
i = video_search_param ( s , " video=ctfb: " ) ;
if ( i > = 0 ) {
s + = i ;
s + = strlen ( " video=ctfb: " ) ;
}
/* search for mode as a default value */
p = s ;
mode = 0 ; /* default */
while ( ( i = video_get_param_len ( p , ' , ' ) ) ! = 0 ) {
GET_OPTION ( " mode: " , mode )
p + = i ;
if ( * p ! = 0 )
p + + ; /* skip ',' */
}
if ( mode > = RES_MODES_COUNT )
mode = 0 ;
* pPar = res_mode_init [ mode ] ; /* copy default values */
bpp = 24 - ( ( mode % 3 ) * 8 ) ;
p = s ; /* restart */
while ( ( i = video_get_param_len ( p , ' , ' ) ) ! = 0 ) {
GET_OPTION ( " x: " , pPar - > xres )
GET_OPTION ( " y: " , pPar - > yres )
GET_OPTION ( " refresh: " , pPar - > refresh )
GET_OPTION ( " le: " , pPar - > left_margin )
GET_OPTION ( " ri: " , pPar - > right_margin )
GET_OPTION ( " up: " , pPar - > upper_margin )
GET_OPTION ( " lo: " , pPar - > lower_margin )
GET_OPTION ( " hs: " , pPar - > hsync_len )
GET_OPTION ( " vs: " , pPar - > vsync_len )
GET_OPTION ( " sync: " , pPar - > sync )
GET_OPTION ( " vmode: " , pPar - > vmode )
GET_OPTION ( " pclk: " , pPar - > pixclock )
GET_OPTION ( " pclk_khz: " , pPar - > pixclock_khz )
GET_OPTION ( " depth: " , bpp )
p + = i ;
if ( * p ! = 0 )
p + + ; /* skip ',' */
}
return bpp ;
}
/*
* Parse the ' video - mode ' environment variable
*
* Example : " video-mode=fslfb:1280x1024-32@60,monitor=dvi " . See
* doc / README . video for more information on how to set the variable .
*
* @ xres : returned value of X - resolution
* @ yres : returned value of Y - resolution
* @ depth : returned value of color depth
* @ freq : returned value of monitor frequency
* @ options : pointer to any remaining options , or NULL
*
* Returns 1 if valid values were found , 0 otherwise
*/
int video_get_video_mode ( unsigned int * xres , unsigned int * yres ,
unsigned int * depth , unsigned int * freq , const char * * options )
{
char * p = getenv ( " video-mode " ) ;
if ( ! p )
return 0 ;
/* Skip over the driver name, which we don't care about. */
p = strchr ( p , ' : ' ) ;
if ( ! p )
return 0 ;
/* Get the X-resolution*/
while ( * p & & ! isdigit ( * p ) )
p + + ;
* xres = simple_strtoul ( p , & p , 10 ) ;
if ( ! * xres )
return 0 ;
/* Get the Y-resolution */
while ( * p & & ! isdigit ( * p ) )
p + + ;
* yres = simple_strtoul ( p , & p , 10 ) ;
if ( ! * yres )
return 0 ;
/* Get the depth */
while ( * p & & ! isdigit ( * p ) )
p + + ;
* depth = simple_strtoul ( p , & p , 10 ) ;
if ( ! * depth )
return 0 ;
/* Get the frequency */
while ( * p & & ! isdigit ( * p ) )
p + + ;
* freq = simple_strtoul ( p , & p , 10 ) ;
if ( ! * freq )
return 0 ;
/* Find the extra options, if any */
p = strchr ( p , ' , ' ) ;
* options = p ? p + 1 : NULL ;
return 1 ;
}
/*
* Parse the ' video - mode ' environment variable using video_get_video_mode ( )
* and lookup the matching ctfb_res_modes in res_mode_init .
*
* @ default_mode : RES_MODE_ # # x # # define for the mode to store in mode_ret
* when ' video - mode ' is not set or does not contain a valid mode
* @ default_depth : depth to set when ' video - mode ' is not set
* @ mode_ret : pointer where the mode will be stored
* @ depth_ret : pointer where the depth will be stored
* @ options : pointer to any remaining options , or NULL
*/
void video_get_ctfb_res_modes ( int default_mode , unsigned int default_depth ,
const struct ctfb_res_modes * * mode_ret ,
unsigned int * depth_ret ,
const char * * options )
{
unsigned int i , xres , yres , depth , refresh ;
* mode_ret = & res_mode_init [ default_mode ] ;
* depth_ret = default_depth ;
* options = NULL ;
if ( ! video_get_video_mode ( & xres , & yres , & depth , & refresh , options ) )
return ;
for ( i = 0 ; i < RES_MODES_COUNT ; i + + ) {
if ( res_mode_init [ i ] . xres = = xres & &
res_mode_init [ i ] . yres = = yres & &
res_mode_init [ i ] . refresh = = refresh ) {
* mode_ret = & res_mode_init [ i ] ;
* depth_ret = depth ;
return ;
}
}
printf ( " video-mode %dx%d-%d@%d not available, falling back to %dx%d-%d@%d \n " ,
xres , yres , depth , refresh , ( * mode_ret ) - > xres ,
( * mode_ret ) - > yres , * depth_ret , ( * mode_ret ) - > refresh ) ;
}
/*
* Find the named string option within the ' , ' separated options string , and
* store its value in dest .
*
* @ options : ' , ' separated options string
* @ name : name of the option to look for
* @ dest : destination buffer to store the value of the option in
* @ dest_len : length of dest
* @ def : value to store in dest if the option is not present in options
*/
void video_get_option_string ( const char * options , const char * name ,
char * dest , int dest_len , const char * def )
{
const char * p = options ;
const int name_len = strlen ( name ) ;
int i , len ;
while ( p & & ( i = video_get_param_len ( p , ' , ' ) ) ! = 0 ) {
if ( strncmp ( p , name , name_len ) = = 0 & & p [ name_len ] = = ' = ' ) {
len = i - ( name_len + 1 ) ;
if ( len > = dest_len )
len = dest_len - 1 ;
memcpy ( dest , & p [ name_len + 1 ] , len ) ;
dest [ len ] = 0 ;
return ;
}
p + = i ;
if ( * p ! = 0 )
p + + ; /* skip ',' */
}
strcpy ( dest , def ) ;
}
/*
* Find the named integer option within the ' , ' separated options string , and
* return its value .
*
* @ options : ' , ' separated options string
* @ name : name of the option to look for
* @ def : value to return if the option is not present in options
*/
int video_get_option_int ( const char * options , const char * name , int def )
{
const char * p = options ;
const int name_len = strlen ( name ) ;
int i ;
while ( p & & ( i = video_get_param_len ( p , ' , ' ) ) ! = 0 ) {
if ( strncmp ( p , name , name_len ) = = 0 & & p [ name_len ] = = ' = ' )
return simple_strtoul ( & p [ name_len + 1 ] , NULL , 10 ) ;
p + = i ;
if ( * p ! = 0 )
p + + ; /* skip ',' */
}
return def ;
}
/**
* Convert an EDID detailed timing to a struct ctfb_res_modes
*
* @ param t The EDID detailed timing to be converted
* @ param mode Returns the converted timing
*
* @ return 0 on success , or a negative errno on error
*/
int video_edid_dtd_to_ctfb_res_modes ( struct edid_detailed_timing * t ,
struct ctfb_res_modes * mode )
{
int margin , h_total , v_total ;
/* Check all timings are non 0 */
if ( EDID_DETAILED_TIMING_PIXEL_CLOCK ( * t ) = = 0 | |
EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE ( * t ) = = 0 | |
EDID_DETAILED_TIMING_HORIZONTAL_BLANKING ( * t ) = = 0 | |
EDID_DETAILED_TIMING_VERTICAL_ACTIVE ( * t ) = = 0 | |
EDID_DETAILED_TIMING_VERTICAL_BLANKING ( * t ) = = 0 | |
EDID_DETAILED_TIMING_HSYNC_OFFSET ( * t ) = = 0 | |
EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH ( * t ) = = 0 | |
EDID_DETAILED_TIMING_VSYNC_OFFSET ( * t ) = = 0 | |
EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH ( * t ) = = 0 | |
/* 3d formats are not supported*/
EDID_DETAILED_TIMING_FLAG_STEREO ( * t ) ! = 0 )
return - EINVAL ;
mode - > xres = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE ( * t ) ;
mode - > yres = EDID_DETAILED_TIMING_VERTICAL_ACTIVE ( * t ) ;
h_total = mode - > xres + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING ( * t ) ;
v_total = mode - > yres + EDID_DETAILED_TIMING_VERTICAL_BLANKING ( * t ) ;
mode - > refresh = EDID_DETAILED_TIMING_PIXEL_CLOCK ( * t ) /
( h_total * v_total ) ;
mode - > pixclock_khz = EDID_DETAILED_TIMING_PIXEL_CLOCK ( * t ) / 1000 ;
mode - > pixclock = 1000000000L / mode - > pixclock_khz ;
mode - > right_margin = EDID_DETAILED_TIMING_HSYNC_OFFSET ( * t ) ;
mode - > hsync_len = EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH ( * t ) ;
margin = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING ( * t ) -
( mode - > right_margin + mode - > hsync_len ) ;
if ( margin < = 0 )
return - EINVAL ;
mode - > left_margin = margin ;
mode - > lower_margin = EDID_DETAILED_TIMING_VSYNC_OFFSET ( * t ) ;
mode - > vsync_len = EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH ( * t ) ;
margin = EDID_DETAILED_TIMING_VERTICAL_BLANKING ( * t ) -
( mode - > lower_margin + mode - > vsync_len ) ;
if ( margin < = 0 )
return - EINVAL ;
mode - > upper_margin = margin ;
mode - > sync = 0 ;
if ( EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY ( * t ) )
mode - > sync | = FB_SYNC_HOR_HIGH_ACT ;
if ( EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY ( * t ) )
mode - > sync | = FB_SYNC_VERT_HIGH_ACT ;
if ( EDID_DETAILED_TIMING_FLAG_INTERLACED ( * t ) )
mode - > vmode = FB_VMODE_INTERLACED ;
else
mode - > vmode = FB_VMODE_NONINTERLACED ;
return 0 ;
}