uboot/drivers/video/videomodes.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2004
   4 * Pierre Aubert, Staubli Faverges , <p.aubert@staubli.com>
   5 * Copyright 2011 Freescale Semiconductor, Inc.
   6 */
   7
   8/************************************************************************
   9  Get Parameters for the video mode:
  10  The default video mode can be defined in CONFIG_SYS_DEFAULT_VIDEO_MODE.
  11  If undefined, default video mode is set to 0x301
  12  Parameters can be set via the variable "videomode" in the environment.
  13  2 diferent ways are possible:
  14  "videomode=301"   - 301 is a hexadecimal number describing the VESA
  15                      mode. Following modes are implemented:
  16
  17                      Colors    640x480 800x600 1024x768 1152x864 1280x1024
  18                     --------+---------------------------------------------
  19                      8 bits |  0x301   0x303    0x305    0x161     0x307
  20                     15 bits |  0x310   0x313    0x316    0x162     0x319
  21                     16 bits |  0x311   0x314    0x317    0x163     0x31A
  22                     24 bits |  0x312   0x315    0x318      ?       0x31B
  23                     --------+---------------------------------------------
  24  "videomode=bootargs"
  25                   - the parameters are parsed from the bootargs.
  26                      The format is "NAME:VALUE,NAME:VALUE" etc.
  27                      Ex.:
  28                      "bootargs=video=ctfb:x:800,y:600,depth:16,pclk:25000"
  29                      Parameters not included in the list will be taken from
  30                      the default mode, which is one of the following:
  31                      mode:0  640x480x24
  32                      mode:1  800x600x16
  33                      mode:2  1024x768x8
  34                      mode:3  960x720x24
  35                      mode:4  1152x864x16
  36                      mode:5  1280x1024x8
  37
  38                      if "mode" is not provided within the parameter list,
  39                      mode:0 is assumed.
  40                      Following parameters are supported:
  41                      x       xres = visible resolution horizontal
  42                      y       yres = visible resolution vertical
  43                      pclk    pixelclocks in pico sec
  44                      le      left_marging time from sync to picture in pixelclocks
  45                      ri      right_marging time from picture to sync in pixelclocks
  46                      up      upper_margin time from sync to picture
  47                      lo      lower_margin
  48                      hs      hsync_len length of horizontal sync
  49                      vs      vsync_len length of vertical sync
  50                      sync    see FB_SYNC_*
  51                      vmode   see FB_VMODE_*
  52                      depth   Color depth in bits per pixel
  53                      All other parameters in the variable bootargs are ignored.
  54                      It is also possible to set the parameters direct in the
  55                      variable "videomode", or in another variable i.e.
  56                      "myvideo" and setting the variable "videomode=myvideo"..
  57****************************************************************************/
  58
  59#include <common.h>
  60#include <edid.h>
  61#include <env.h>
  62#include <errno.h>
  63#include <linux/ctype.h>
  64
  65#include "videomodes.h"
  66
  67const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = {
  68        {0x301, RES_MODE_640x480, 8},
  69        {0x310, RES_MODE_640x480, 15},
  70        {0x311, RES_MODE_640x480, 16},
  71        {0x312, RES_MODE_640x480, 24},
  72        {0x303, RES_MODE_800x600, 8},
  73        {0x313, RES_MODE_800x600, 15},
  74        {0x314, RES_MODE_800x600, 16},
  75        {0x315, RES_MODE_800x600, 24},
  76        {0x305, RES_MODE_1024x768, 8},
  77        {0x316, RES_MODE_1024x768, 15},
  78        {0x317, RES_MODE_1024x768, 16},
  79        {0x318, RES_MODE_1024x768, 24},
  80        {0x161, RES_MODE_1152x864, 8},
  81        {0x162, RES_MODE_1152x864, 15},
  82        {0x163, RES_MODE_1152x864, 16},
  83        {0x307, RES_MODE_1280x1024, 8},
  84        {0x319, RES_MODE_1280x1024, 15},
  85        {0x31A, RES_MODE_1280x1024, 16},
  86        {0x31B, RES_MODE_1280x1024, 24},
  87};
  88const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = {
  89        /*  x     y  hz  pixclk ps/kHz   le   ri  up  lo   hs vs  s  vmode */
  90#ifndef CONFIG_VIDEO_STD_TIMINGS
  91        { 640,  480, 60, 39721,  25180,  40,  24, 32, 11,  96, 2, 0, FB_VMODE_NONINTERLACED},
  92        { 800,  600, 60, 27778,  36000,  64,  24, 22,  1,  72, 2, 0, FB_VMODE_NONINTERLACED},
  93        {1024,  768, 60, 15384,  65000, 168,   8, 29,  3, 144, 4, 0, FB_VMODE_NONINTERLACED},
  94        { 960,  720, 80, 13100,  76335, 160,  40, 32,  8,  80, 4, 0, FB_VMODE_NONINTERLACED},
  95        {1152,  864, 60, 12004,  83300, 200,  64, 32, 16,  80, 4, 0, FB_VMODE_NONINTERLACED},
  96        {1280, 1024, 60,  9090, 110000, 200,  48, 26,  1, 184, 3, 0, FB_VMODE_NONINTERLACED},
  97#else
  98        { 640,  480, 60, 39683,  25200,  48,  16, 33, 10,  96, 2, 0, FB_VMODE_NONINTERLACED},
  99        { 800,  600, 60, 25000,  40000,  88,  40, 23,  1, 128, 4, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
 100        {1024,  768, 60, 15384,  65000, 160,  24, 29,  3, 136, 6, 0, FB_VMODE_NONINTERLACED},
 101        { 960,  720, 75, 13468,  74250, 176,  72, 27,  1, 112, 2, 0, FB_VMODE_NONINTERLACED},
 102        {1152,  864, 75,  9259, 108000, 256,  64, 32,  1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
 103        {1280, 1024, 60,  9259, 108000, 248,  48, 38,  1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
 104#endif
 105        {1280,  720, 60, 13468,  74250, 220, 110, 20,  5,  40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
 106        {1360,  768, 60, 11696,  85500, 256,  64, 17,  3, 112, 7, 0, FB_VMODE_NONINTERLACED},
 107        {1920, 1080, 60,  6734, 148500, 148,  88, 36,  4,  44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED},
 108        {1920, 1200, 60,  6494, 154000,  80,  48, 26,  3,  32, 6, FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED},
 109};
 110
 111/************************************************************************
 112 * Get Parameters for the video mode:
 113 */
 114/*********************************************************************
 115 * returns the length to the next seperator
 116 */
 117static int
 118video_get_param_len(const char *start, char sep)
 119{
 120        int i = 0;
 121        while ((*start != 0) && (*start != sep)) {
 122                start++;
 123                i++;
 124        }
 125        return i;
 126}
 127
 128static int
 129video_search_param (char *start, char *param)
 130{
 131        int len, totallen, i;
 132        char *p = start;
 133        len = strlen (param);
 134        totallen = len + strlen (start);
 135        for (i = 0; i < totallen; i++) {
 136                if (strncmp (p++, param, len) == 0)
 137                        return (i);
 138        }
 139        return -1;
 140}
 141
 142/***************************************************************
 143 * Get parameter via the environment as it is done for the
 144 * linux kernel i.e:
 145 * video=ctfb:x:800,xv:1280,y:600,yv:1024,depth:16,mode:0,pclk:25000,
 146 *       le:56,ri:48,up:26,lo:5,hs:152,vs:2,sync:0,vmode:0,accel:0
 147 *
 148 * penv is a pointer to the environment, containing the string, or the name of
 149 * another environment variable. It could even be the term "bootargs"
 150 */
 151
 152#define GET_OPTION(name,var)                            \
 153        if(strncmp(p,name,strlen(name))==0) {           \
 154                val_s=p+strlen(name);                   \
 155                var=simple_strtoul(val_s, NULL, 10);    \
 156        }
 157
 158int video_get_params (struct ctfb_res_modes *pPar, char *penv)
 159{
 160        char *p, *s, *val_s;
 161        int i = 0;
 162        int bpp;
 163        int mode;
 164
 165        /* first search for the environment containing the real param string */
 166        s = penv;
 167
 168        p = env_get(s);
 169        if (p)
 170                s = p;
 171
 172        /*
 173         * in case of the bootargs line, we have to start
 174         * after "video=ctfb:"
 175         */
 176        i = video_search_param (s, "video=ctfb:");
 177        if (i >= 0) {
 178                s += i;
 179                s += strlen ("video=ctfb:");
 180        }
 181        /* search for mode as a default value */
 182        p = s;
 183        mode = 0;               /* default */
 184
 185        while ((i = video_get_param_len (p, ',')) != 0) {
 186                GET_OPTION ("mode:", mode)
 187                        p += i;
 188                if (*p != 0)
 189                        p++;    /* skip ',' */
 190        }
 191
 192        if (mode >= RES_MODES_COUNT)
 193                mode = 0;
 194
 195        *pPar = res_mode_init[mode];    /* copy default values */
 196        bpp = 24 - ((mode % 3) * 8);
 197        p = s;                  /* restart */
 198
 199        while ((i = video_get_param_len (p, ',')) != 0) {
 200                GET_OPTION ("x:", pPar->xres)
 201                        GET_OPTION ("y:", pPar->yres)
 202                        GET_OPTION ("refresh:", pPar->refresh)
 203                        GET_OPTION ("le:", pPar->left_margin)
 204                        GET_OPTION ("ri:", pPar->right_margin)
 205                        GET_OPTION ("up:", pPar->upper_margin)
 206                        GET_OPTION ("lo:", pPar->lower_margin)
 207                        GET_OPTION ("hs:", pPar->hsync_len)
 208                        GET_OPTION ("vs:", pPar->vsync_len)
 209                        GET_OPTION ("sync:", pPar->sync)
 210                        GET_OPTION ("vmode:", pPar->vmode)
 211                        GET_OPTION ("pclk:", pPar->pixclock)
 212                        GET_OPTION ("pclk_khz:", pPar->pixclock_khz)
 213                        GET_OPTION ("depth:", bpp)
 214                        p += i;
 215                if (*p != 0)
 216                        p++;    /* skip ',' */
 217        }
 218        return bpp;
 219}
 220
 221/*
 222 * Parse the 'video-mode' environment variable
 223 *
 224 * Example: "video-mode=fslfb:1280x1024-32@60,monitor=dvi".  See
 225 * doc/README.video for more information on how to set the variable.
 226 *
 227 * @xres: returned value of X-resolution
 228 * @yres: returned value of Y-resolution
 229 * @depth: returned value of color depth
 230 * @freq: returned value of monitor frequency
 231 * @options: pointer to any remaining options, or NULL
 232 *
 233 * Returns 1 if valid values were found, 0 otherwise
 234 */
 235int video_get_video_mode(unsigned int *xres, unsigned int *yres,
 236        unsigned int *depth, unsigned int *freq, const char **options)
 237{
 238        char *p = env_get("video-mode");
 239        if (!p)
 240                return 0;
 241
 242        /* Skip over the driver name, which we don't care about. */
 243        p = strchr(p, ':');
 244        if (!p)
 245                return 0;
 246
 247        /* Get the X-resolution*/
 248        while (*p && !isdigit(*p))
 249                p++;
 250        *xres = simple_strtoul(p, &p, 10);
 251        if (!*xres)
 252                return 0;
 253
 254        /* Get the Y-resolution */
 255        while (*p && !isdigit(*p))
 256                p++;
 257        *yres = simple_strtoul(p, &p, 10);
 258        if (!*yres)
 259                return 0;
 260
 261        /* Get the depth */
 262        while (*p && !isdigit(*p))
 263                p++;
 264        *depth = simple_strtoul(p, &p, 10);
 265        if (!*depth)
 266                return 0;
 267
 268        /* Get the frequency */
 269        while (*p && !isdigit(*p))
 270                p++;
 271        *freq = simple_strtoul(p, &p, 10);
 272        if (!*freq)
 273                return 0;
 274
 275        /* Find the extra options, if any */
 276        p = strchr(p, ',');
 277        *options = p ? p + 1 : NULL;
 278
 279        return 1;
 280}
 281
 282/*
 283 * Parse the 'video-mode' environment variable using video_get_video_mode()
 284 * and lookup the matching ctfb_res_modes in res_mode_init.
 285 *
 286 * @default_mode: RES_MODE_##x## define for the mode to store in mode_ret
 287 *   when 'video-mode' is not set or does not contain a valid mode
 288 * @default_depth: depth to set when 'video-mode' is not set
 289 * @mode_ret: pointer where the mode will be stored
 290 * @depth_ret: pointer where the depth will be stored
 291 * @options: pointer to any remaining options, or NULL
 292 */
 293void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth,
 294                              const struct ctfb_res_modes **mode_ret,
 295                              unsigned int *depth_ret,
 296                              const char **options)
 297{
 298        unsigned int i, xres, yres, depth, refresh;
 299
 300        *mode_ret = &res_mode_init[default_mode];
 301        *depth_ret = default_depth;
 302        *options = NULL;
 303
 304        if (!video_get_video_mode(&xres, &yres, &depth, &refresh, options))
 305                return;
 306
 307        for (i = 0; i < RES_MODES_COUNT; i++) {
 308                if (res_mode_init[i].xres == xres &&
 309                    res_mode_init[i].yres == yres &&
 310                    res_mode_init[i].refresh == refresh) {
 311                        *mode_ret = &res_mode_init[i];
 312                        *depth_ret = depth;
 313                        return;
 314                }
 315        }
 316
 317        printf("video-mode %dx%d-%d@%d not available, falling back to %dx%d-%d@%d\n",
 318               xres, yres, depth, refresh, (*mode_ret)->xres,
 319               (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh);
 320}
 321
 322/*
 323 * Find the named string option within the ',' separated options string, and
 324 * store its value in dest.
 325 *
 326 * @options: ',' separated options string
 327 * @name: name of the option to look for
 328 * @dest: destination buffer to store the value of the option in
 329 * @dest_len: length of dest
 330 * @def: value to store in dest if the option is not present in options
 331 */
 332void video_get_option_string(const char *options, const char *name,
 333                             char *dest, int dest_len, const char *def)
 334{
 335        const char *p = options;
 336        const int name_len = strlen(name);
 337        int i, len;
 338
 339        while (p && (i = video_get_param_len(p, ',')) != 0) {
 340                if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') {
 341                        len = i - (name_len + 1);
 342                        if (len >= dest_len)
 343                                len = dest_len - 1;
 344                        memcpy(dest, &p[name_len + 1], len);
 345                        dest[len] = 0;
 346                        return;
 347                }
 348                p += i;
 349                if (*p != 0)
 350                        p++;    /* skip ',' */
 351        }
 352        strcpy(dest, def);
 353}
 354
 355/*
 356 * Find the named integer option within the ',' separated options string, and
 357 * return its value.
 358 *
 359 * @options: ',' separated options string
 360 * @name: name of the option to look for
 361 * @def: value to return if the option is not present in options
 362 */
 363int video_get_option_int(const char *options, const char *name, int def)
 364{
 365        const char *p = options;
 366        const int name_len = strlen(name);
 367        int i;
 368
 369        while (p && (i = video_get_param_len(p, ',')) != 0) {
 370                if (strncmp(p, name, name_len) == 0 && p[name_len] == '=')
 371                        return simple_strtoul(&p[name_len + 1], NULL, 10);
 372
 373                p += i;
 374                if (*p != 0)
 375                        p++;    /* skip ',' */
 376        }
 377        return def;
 378}
 379
 380/**
 381 * Convert an EDID detailed timing to a struct ctfb_res_modes
 382 *
 383 * @param t             The EDID detailed timing to be converted
 384 * @param mode          Returns the converted timing
 385 *
 386 * @return 0 on success, or a negative errno on error
 387 */
 388int video_edid_dtd_to_ctfb_res_modes(struct edid_detailed_timing *t,
 389                                     struct ctfb_res_modes *mode)
 390{
 391        int margin, h_total, v_total;
 392
 393        /* Check all timings are non 0 */
 394        if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) == 0 ||
 395            EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t) == 0 ||
 396            EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) == 0 ||
 397            EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t) == 0 ||
 398            EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) == 0 ||
 399            EDID_DETAILED_TIMING_HSYNC_OFFSET(*t) == 0 ||
 400            EDID_DETAILED_TIMING_VSYNC_OFFSET(*t) == 0 ||
 401            /* 3d formats are not supported */
 402            EDID_DETAILED_TIMING_FLAG_STEREO(*t) != 0)
 403                return -EINVAL;
 404
 405        mode->xres = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t);
 406        mode->yres = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t);
 407
 408        h_total = mode->xres + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t);
 409        v_total = mode->yres + EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t);
 410        mode->refresh = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) /
 411                        (h_total * v_total);
 412
 413        mode->pixclock_khz = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / 1000;
 414        mode->pixclock = 1000000000L / mode->pixclock_khz;
 415
 416        mode->right_margin = EDID_DETAILED_TIMING_HSYNC_OFFSET(*t);
 417        mode->hsync_len = EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t);
 418        margin = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) -
 419                        (mode->right_margin + mode->hsync_len);
 420        if (margin <= 0)
 421                return -EINVAL;
 422
 423        mode->left_margin = margin;
 424
 425        mode->lower_margin = EDID_DETAILED_TIMING_VSYNC_OFFSET(*t);
 426        mode->vsync_len = EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t);
 427        margin = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) -
 428                        (mode->lower_margin + mode->vsync_len);
 429        if (margin <= 0)
 430                return -EINVAL;
 431
 432        mode->upper_margin = margin;
 433
 434        mode->sync = 0;
 435        if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(*t))
 436                mode->sync |= FB_SYNC_HOR_HIGH_ACT;
 437        if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(*t))
 438                mode->sync |= FB_SYNC_VERT_HIGH_ACT;
 439
 440        if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t))
 441                mode->vmode = FB_VMODE_INTERLACED;
 442        else
 443                mode->vmode = FB_VMODE_NONINTERLACED;
 444
 445        return 0;
 446}
 447
 448void video_ctfb_mode_to_display_timing(const struct ctfb_res_modes *mode,
 449                                       struct display_timing *timing)
 450{
 451        timing->pixelclock.typ = mode->pixclock_khz * 1000;
 452
 453        timing->hactive.typ = mode->xres;
 454        timing->hfront_porch.typ = mode->right_margin;
 455        timing->hback_porch.typ = mode->left_margin;
 456        timing->hsync_len.typ = mode->hsync_len;
 457
 458        timing->vactive.typ = mode->yres;
 459        timing->vfront_porch.typ = mode->lower_margin;
 460        timing->vback_porch.typ = mode->upper_margin;
 461        timing->vsync_len.typ = mode->vsync_len;
 462
 463        timing->flags = 0;
 464
 465        if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
 466                timing->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
 467        else
 468                timing->flags |= DISPLAY_FLAGS_HSYNC_LOW;
 469        if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
 470                timing->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
 471        else
 472                timing->flags |= DISPLAY_FLAGS_VSYNC_LOW;
 473        if (mode->vmode == FB_VMODE_INTERLACED)
 474                timing->flags |= DISPLAY_FLAGS_INTERLACED;
 475}
 476