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