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