linux/drivers/video/fbmon.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/video/fbmon.c
   3 *
   4 * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
   5 *
   6 * Credits:
   7 *
   8 * The EDID Parser is a conglomeration from the following sources:
   9 *
  10 *   1. SciTech SNAP Graphics Architecture
  11 *      Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
  12 *
  13 *   2. XFree86 4.3.0, interpret_edid.c
  14 *      Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
  15 *
  16 *   3. John Fremlin <vii@users.sourceforge.net> and
  17 *      Ani Joshi <ajoshi@unixbox.com>
  18 *
  19 * Generalized Timing Formula is derived from:
  20 *
  21 *      GTF Spreadsheet by Andy Morrish (1/5/97)
  22 *      available at http://www.vesa.org
  23 *
  24 * This file is subject to the terms and conditions of the GNU General Public
  25 * License.  See the file COPYING in the main directory of this archive
  26 * for more details.
  27 *
  28 */
  29#include <linux/fb.h>
  30#include <linux/module.h>
  31#include <linux/pci.h>
  32#include <video/edid.h>
  33#ifdef CONFIG_PPC_OF
  34#include <asm/prom.h>
  35#include <asm/pci-bridge.h>
  36#endif
  37#include "edid.h"
  38
  39/*
  40 * EDID parser
  41 */
  42
  43#undef DEBUG  /* define this for verbose EDID parsing output */
  44
  45#ifdef DEBUG
  46#define DPRINTK(fmt, args...) printk(fmt,## args)
  47#else
  48#define DPRINTK(fmt, args...)
  49#endif
  50
  51#define FBMON_FIX_HEADER  1
  52#define FBMON_FIX_INPUT   2
  53#define FBMON_FIX_TIMINGS 3
  54
  55#ifdef CONFIG_FB_MODE_HELPERS
  56struct broken_edid {
  57        u8  manufacturer[4];
  58        u32 model;
  59        u32 fix;
  60};
  61
  62static const struct broken_edid brokendb[] = {
  63        /* DEC FR-PCXAV-YZ */
  64        {
  65                .manufacturer = "DEC",
  66                .model        = 0x073a,
  67                .fix          = FBMON_FIX_HEADER,
  68        },
  69        /* ViewSonic PF775a */
  70        {
  71                .manufacturer = "VSC",
  72                .model        = 0x5a44,
  73                .fix          = FBMON_FIX_INPUT,
  74        },
  75        /* Sharp UXGA? */
  76        {
  77                .manufacturer = "SHP",
  78                .model        = 0x138e,
  79                .fix          = FBMON_FIX_TIMINGS,
  80        },
  81};
  82
  83static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
  84        0xff, 0xff, 0xff, 0x00
  85};
  86
  87static void copy_string(unsigned char *c, unsigned char *s)
  88{
  89  int i;
  90  c = c + 5;
  91  for (i = 0; (i < 13 && *c != 0x0A); i++)
  92    *(s++) = *(c++);
  93  *s = 0;
  94  while (i-- && (*--s == 0x20)) *s = 0;
  95}
  96
  97static int edid_is_serial_block(unsigned char *block)
  98{
  99        if ((block[0] == 0x00) && (block[1] == 0x00) &&
 100            (block[2] == 0x00) && (block[3] == 0xff) &&
 101            (block[4] == 0x00))
 102                return 1;
 103        else
 104                return 0;
 105}
 106
 107static int edid_is_ascii_block(unsigned char *block)
 108{
 109        if ((block[0] == 0x00) && (block[1] == 0x00) &&
 110            (block[2] == 0x00) && (block[3] == 0xfe) &&
 111            (block[4] == 0x00))
 112                return 1;
 113        else
 114                return 0;
 115}
 116
 117static int edid_is_limits_block(unsigned char *block)
 118{
 119        if ((block[0] == 0x00) && (block[1] == 0x00) &&
 120            (block[2] == 0x00) && (block[3] == 0xfd) &&
 121            (block[4] == 0x00))
 122                return 1;
 123        else
 124                return 0;
 125}
 126
 127static int edid_is_monitor_block(unsigned char *block)
 128{
 129        if ((block[0] == 0x00) && (block[1] == 0x00) &&
 130            (block[2] == 0x00) && (block[3] == 0xfc) &&
 131            (block[4] == 0x00))
 132                return 1;
 133        else
 134                return 0;
 135}
 136
 137static int edid_is_timing_block(unsigned char *block)
 138{
 139        if ((block[0] != 0x00) || (block[1] != 0x00) ||
 140            (block[2] != 0x00) || (block[4] != 0x00))
 141                return 1;
 142        else
 143                return 0;
 144}
 145
 146static int check_edid(unsigned char *edid)
 147{
 148        unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
 149        unsigned char *b;
 150        u32 model;
 151        int i, fix = 0, ret = 0;
 152
 153        manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
 154        manufacturer[1] = ((block[0] & 0x03) << 3) +
 155                ((block[1] & 0xe0) >> 5) + '@';
 156        manufacturer[2] = (block[1] & 0x1f) + '@';
 157        manufacturer[3] = 0;
 158        model = block[2] + (block[3] << 8);
 159
 160        for (i = 0; i < ARRAY_SIZE(brokendb); i++) {
 161                if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) &&
 162                        brokendb[i].model == model) {
 163                        fix = brokendb[i].fix;
 164                        break;
 165                }
 166        }
 167
 168        switch (fix) {
 169        case FBMON_FIX_HEADER:
 170                for (i = 0; i < 8; i++) {
 171                        if (edid[i] != edid_v1_header[i]) {
 172                                ret = fix;
 173                                break;
 174                        }
 175                }
 176                break;
 177        case FBMON_FIX_INPUT:
 178                b = edid + EDID_STRUCT_DISPLAY;
 179                /* Only if display is GTF capable will
 180                   the input type be reset to analog */
 181                if (b[4] & 0x01 && b[0] & 0x80)
 182                        ret = fix;
 183                break;
 184        case FBMON_FIX_TIMINGS:
 185                b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 186                ret = fix;
 187
 188                for (i = 0; i < 4; i++) {
 189                        if (edid_is_limits_block(b)) {
 190                                ret = 0;
 191                                break;
 192                        }
 193
 194                        b += DETAILED_TIMING_DESCRIPTION_SIZE;
 195                }
 196
 197                break;
 198        }
 199
 200        if (ret)
 201                printk("fbmon: The EDID Block of "
 202                       "Manufacturer: %s Model: 0x%x is known to "
 203                       "be broken,\n",  manufacturer, model);
 204
 205        return ret;
 206}
 207
 208static void fix_edid(unsigned char *edid, int fix)
 209{
 210        int i;
 211        unsigned char *b, csum = 0;
 212
 213        switch (fix) {
 214        case FBMON_FIX_HEADER:
 215                printk("fbmon: trying a header reconstruct\n");
 216                memcpy(edid, edid_v1_header, 8);
 217                break;
 218        case FBMON_FIX_INPUT:
 219                printk("fbmon: trying to fix input type\n");
 220                b = edid + EDID_STRUCT_DISPLAY;
 221                b[0] &= ~0x80;
 222                edid[127] += 0x80;
 223                break;
 224        case FBMON_FIX_TIMINGS:
 225                printk("fbmon: trying to fix monitor timings\n");
 226                b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 227                for (i = 0; i < 4; i++) {
 228                        if (!(edid_is_serial_block(b) ||
 229                              edid_is_ascii_block(b) ||
 230                              edid_is_monitor_block(b) ||
 231                              edid_is_timing_block(b))) {
 232                                b[0] = 0x00;
 233                                b[1] = 0x00;
 234                                b[2] = 0x00;
 235                                b[3] = 0xfd;
 236                                b[4] = 0x00;
 237                                b[5] = 60;   /* vfmin */
 238                                b[6] = 60;   /* vfmax */
 239                                b[7] = 30;   /* hfmin */
 240                                b[8] = 75;   /* hfmax */
 241                                b[9] = 17;   /* pixclock - 170 MHz*/
 242                                b[10] = 0;   /* GTF */
 243                                break;
 244                        }
 245
 246                        b += DETAILED_TIMING_DESCRIPTION_SIZE;
 247                }
 248
 249                for (i = 0; i < EDID_LENGTH - 1; i++)
 250                        csum += edid[i];
 251
 252                edid[127] = 256 - csum;
 253                break;
 254        }
 255}
 256
 257static int edid_checksum(unsigned char *edid)
 258{
 259        unsigned char i, csum = 0, all_null = 0;
 260        int err = 0, fix = check_edid(edid);
 261
 262        if (fix)
 263                fix_edid(edid, fix);
 264
 265        for (i = 0; i < EDID_LENGTH; i++) {
 266                csum += edid[i];
 267                all_null |= edid[i];
 268        }
 269
 270        if (csum == 0x00 && all_null) {
 271                /* checksum passed, everything's good */
 272                err = 1;
 273        }
 274
 275        return err;
 276}
 277
 278static int edid_check_header(unsigned char *edid)
 279{
 280        int i, err = 1, fix = check_edid(edid);
 281
 282        if (fix)
 283                fix_edid(edid, fix);
 284
 285        for (i = 0; i < 8; i++) {
 286                if (edid[i] != edid_v1_header[i])
 287                        err = 0;
 288        }
 289
 290        return err;
 291}
 292
 293static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs)
 294{
 295        specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
 296        specs->manufacturer[1] = ((block[0] & 0x03) << 3) +
 297                ((block[1] & 0xe0) >> 5) + '@';
 298        specs->manufacturer[2] = (block[1] & 0x1f) + '@';
 299        specs->manufacturer[3] = 0;
 300        specs->model = block[2] + (block[3] << 8);
 301        specs->serial = block[4] + (block[5] << 8) +
 302               (block[6] << 16) + (block[7] << 24);
 303        specs->year = block[9] + 1990;
 304        specs->week = block[8];
 305        DPRINTK("   Manufacturer: %s\n", specs->manufacturer);
 306        DPRINTK("   Model: %x\n", specs->model);
 307        DPRINTK("   Serial#: %u\n", specs->serial);
 308        DPRINTK("   Year: %u Week %u\n", specs->year, specs->week);
 309}
 310
 311static void get_dpms_capabilities(unsigned char flags,
 312                                  struct fb_monspecs *specs)
 313{
 314        specs->dpms = 0;
 315        if (flags & DPMS_ACTIVE_OFF)
 316                specs->dpms |= FB_DPMS_ACTIVE_OFF;
 317        if (flags & DPMS_SUSPEND)
 318                specs->dpms |= FB_DPMS_SUSPEND;
 319        if (flags & DPMS_STANDBY)
 320                specs->dpms |= FB_DPMS_STANDBY;
 321        DPRINTK("      DPMS: Active %s, Suspend %s, Standby %s\n",
 322               (flags & DPMS_ACTIVE_OFF) ? "yes" : "no",
 323               (flags & DPMS_SUSPEND)    ? "yes" : "no",
 324               (flags & DPMS_STANDBY)    ? "yes" : "no");
 325}
 326
 327static void get_chroma(unsigned char *block, struct fb_monspecs *specs)
 328{
 329        int tmp;
 330
 331        DPRINTK("      Chroma\n");
 332        /* Chromaticity data */
 333        tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2);
 334        tmp *= 1000;
 335        tmp += 512;
 336        specs->chroma.redx = tmp/1024;
 337        DPRINTK("         RedX:     0.%03d ", specs->chroma.redx);
 338
 339        tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2);
 340        tmp *= 1000;
 341        tmp += 512;
 342        specs->chroma.redy = tmp/1024;
 343        DPRINTK("RedY:     0.%03d\n", specs->chroma.redy);
 344
 345        tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2);
 346        tmp *= 1000;
 347        tmp += 512;
 348        specs->chroma.greenx = tmp/1024;
 349        DPRINTK("         GreenX:   0.%03d ", specs->chroma.greenx);
 350
 351        tmp = (block[5] & 3) | (block[0xa] << 2);
 352        tmp *= 1000;
 353        tmp += 512;
 354        specs->chroma.greeny = tmp/1024;
 355        DPRINTK("GreenY:   0.%03d\n", specs->chroma.greeny);
 356
 357        tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2);
 358        tmp *= 1000;
 359        tmp += 512;
 360        specs->chroma.bluex = tmp/1024;
 361        DPRINTK("         BlueX:    0.%03d ", specs->chroma.bluex);
 362
 363        tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2);
 364        tmp *= 1000;
 365        tmp += 512;
 366        specs->chroma.bluey = tmp/1024;
 367        DPRINTK("BlueY:    0.%03d\n", specs->chroma.bluey);
 368
 369        tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2);
 370        tmp *= 1000;
 371        tmp += 512;
 372        specs->chroma.whitex = tmp/1024;
 373        DPRINTK("         WhiteX:   0.%03d ", specs->chroma.whitex);
 374
 375        tmp = (block[6] & 3) | (block[0xe] << 2);
 376        tmp *= 1000;
 377        tmp += 512;
 378        specs->chroma.whitey = tmp/1024;
 379        DPRINTK("WhiteY:   0.%03d\n", specs->chroma.whitey);
 380}
 381
 382static void calc_mode_timings(int xres, int yres, int refresh,
 383                              struct fb_videomode *mode)
 384{
 385        struct fb_var_screeninfo *var;
 386
 387        var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
 388
 389        if (var) {
 390                var->xres = xres;
 391                var->yres = yres;
 392                fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
 393                            refresh, var, NULL);
 394                mode->xres = xres;
 395                mode->yres = yres;
 396                mode->pixclock = var->pixclock;
 397                mode->refresh = refresh;
 398                mode->left_margin = var->left_margin;
 399                mode->right_margin = var->right_margin;
 400                mode->upper_margin = var->upper_margin;
 401                mode->lower_margin = var->lower_margin;
 402                mode->hsync_len = var->hsync_len;
 403                mode->vsync_len = var->vsync_len;
 404                mode->vmode = 0;
 405                mode->sync = 0;
 406                kfree(var);
 407        }
 408}
 409
 410static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
 411{
 412        int num = 0;
 413        unsigned char c;
 414
 415        c = block[0];
 416        if (c&0x80) {
 417                calc_mode_timings(720, 400, 70, &mode[num]);
 418                mode[num++].flag = FB_MODE_IS_CALCULATED;
 419                DPRINTK("      720x400@70Hz\n");
 420        }
 421        if (c&0x40) {
 422                calc_mode_timings(720, 400, 88, &mode[num]);
 423                mode[num++].flag = FB_MODE_IS_CALCULATED;
 424                DPRINTK("      720x400@88Hz\n");
 425        }
 426        if (c&0x20) {
 427                mode[num++] = vesa_modes[3];
 428                DPRINTK("      640x480@60Hz\n");
 429        }
 430        if (c&0x10) {
 431                calc_mode_timings(640, 480, 67, &mode[num]);
 432                mode[num++].flag = FB_MODE_IS_CALCULATED;
 433                DPRINTK("      640x480@67Hz\n");
 434        }
 435        if (c&0x08) {
 436                mode[num++] = vesa_modes[4];
 437                DPRINTK("      640x480@72Hz\n");
 438        }
 439        if (c&0x04) {
 440                mode[num++] = vesa_modes[5];
 441                DPRINTK("      640x480@75Hz\n");
 442        }
 443        if (c&0x02) {
 444                mode[num++] = vesa_modes[7];
 445                DPRINTK("      800x600@56Hz\n");
 446        }
 447        if (c&0x01) {
 448                mode[num++] = vesa_modes[8];
 449                DPRINTK("      800x600@60Hz\n");
 450        }
 451
 452        c = block[1];
 453        if (c&0x80) {
 454                mode[num++] = vesa_modes[9];
 455                DPRINTK("      800x600@72Hz\n");
 456        }
 457        if (c&0x40) {
 458                mode[num++] = vesa_modes[10];
 459                DPRINTK("      800x600@75Hz\n");
 460        }
 461        if (c&0x20) {
 462                calc_mode_timings(832, 624, 75, &mode[num]);
 463                mode[num++].flag = FB_MODE_IS_CALCULATED;
 464                DPRINTK("      832x624@75Hz\n");
 465        }
 466        if (c&0x10) {
 467                mode[num++] = vesa_modes[12];
 468                DPRINTK("      1024x768@87Hz Interlaced\n");
 469        }
 470        if (c&0x08) {
 471                mode[num++] = vesa_modes[13];
 472                DPRINTK("      1024x768@60Hz\n");
 473        }
 474        if (c&0x04) {
 475                mode[num++] = vesa_modes[14];
 476                DPRINTK("      1024x768@70Hz\n");
 477        }
 478        if (c&0x02) {
 479                mode[num++] = vesa_modes[15];
 480                DPRINTK("      1024x768@75Hz\n");
 481        }
 482        if (c&0x01) {
 483                mode[num++] = vesa_modes[21];
 484                DPRINTK("      1280x1024@75Hz\n");
 485        }
 486        c = block[2];
 487        if (c&0x80) {
 488                mode[num++] = vesa_modes[17];
 489                DPRINTK("      1152x870@75Hz\n");
 490        }
 491        DPRINTK("      Manufacturer's mask: %x\n",c&0x7F);
 492        return num;
 493}
 494
 495static int get_std_timing(unsigned char *block, struct fb_videomode *mode)
 496{
 497        int xres, yres = 0, refresh, ratio, i;
 498
 499        xres = (block[0] + 31) * 8;
 500        if (xres <= 256)
 501                return 0;
 502
 503        ratio = (block[1] & 0xc0) >> 6;
 504        switch (ratio) {
 505        case 0:
 506                yres = xres;
 507                break;
 508        case 1:
 509                yres = (xres * 3)/4;
 510                break;
 511        case 2:
 512                yres = (xres * 4)/5;
 513                break;
 514        case 3:
 515                yres = (xres * 9)/16;
 516                break;
 517        }
 518        refresh = (block[1] & 0x3f) + 60;
 519
 520        DPRINTK("      %dx%d@%dHz\n", xres, yres, refresh);
 521        for (i = 0; i < VESA_MODEDB_SIZE; i++) {
 522                if (vesa_modes[i].xres == xres &&
 523                    vesa_modes[i].yres == yres &&
 524                    vesa_modes[i].refresh == refresh) {
 525                        *mode = vesa_modes[i];
 526                        mode->flag |= FB_MODE_IS_STANDARD;
 527                        return 1;
 528                }
 529        }
 530        calc_mode_timings(xres, yres, refresh, mode);
 531        return 1;
 532}
 533
 534static int get_dst_timing(unsigned char *block,
 535                          struct fb_videomode *mode)
 536{
 537        int j, num = 0;
 538
 539        for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE)
 540                num += get_std_timing(block, &mode[num]);
 541
 542        return num;
 543}
 544
 545static void get_detailed_timing(unsigned char *block,
 546                                struct fb_videomode *mode)
 547{
 548        mode->xres = H_ACTIVE;
 549        mode->yres = V_ACTIVE;
 550        mode->pixclock = PIXEL_CLOCK;
 551        mode->pixclock /= 1000;
 552        mode->pixclock = KHZ2PICOS(mode->pixclock);
 553        mode->right_margin = H_SYNC_OFFSET;
 554        mode->left_margin = (H_ACTIVE + H_BLANKING) -
 555                (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
 556        mode->upper_margin = V_BLANKING - V_SYNC_OFFSET -
 557                V_SYNC_WIDTH;
 558        mode->lower_margin = V_SYNC_OFFSET;
 559        mode->hsync_len = H_SYNC_WIDTH;
 560        mode->vsync_len = V_SYNC_WIDTH;
 561        if (HSYNC_POSITIVE)
 562                mode->sync |= FB_SYNC_HOR_HIGH_ACT;
 563        if (VSYNC_POSITIVE)
 564                mode->sync |= FB_SYNC_VERT_HIGH_ACT;
 565        mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) *
 566                                     (V_ACTIVE + V_BLANKING));
 567        mode->vmode = 0;
 568        mode->flag = FB_MODE_IS_DETAILED;
 569
 570        DPRINTK("      %d MHz ",  PIXEL_CLOCK/1000000);
 571        DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET,
 572               H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
 573        DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET,
 574               V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
 575        DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-",
 576               (VSYNC_POSITIVE) ? "+" : "-");
 577}
 578
 579/**
 580 * fb_create_modedb - create video mode database
 581 * @edid: EDID data
 582 * @dbsize: database size
 583 *
 584 * RETURNS: struct fb_videomode, @dbsize contains length of database
 585 *
 586 * DESCRIPTION:
 587 * This function builds a mode database using the contents of the EDID
 588 * data
 589 */
 590static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize)
 591{
 592        struct fb_videomode *mode, *m;
 593        unsigned char *block;
 594        int num = 0, i, first = 1;
 595
 596        mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
 597        if (mode == NULL)
 598                return NULL;
 599
 600        if (edid == NULL || !edid_checksum(edid) ||
 601            !edid_check_header(edid)) {
 602                kfree(mode);
 603                return NULL;
 604        }
 605
 606        *dbsize = 0;
 607
 608        DPRINTK("   Detailed Timings\n");
 609        block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 610        for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
 611                if (!(block[0] == 0x00 && block[1] == 0x00)) {
 612                        get_detailed_timing(block, &mode[num]);
 613                        if (first) {
 614                                mode[num].flag |= FB_MODE_IS_FIRST;
 615                                first = 0;
 616                        }
 617                        num++;
 618                }
 619        }
 620
 621        DPRINTK("   Supported VESA Modes\n");
 622        block = edid + ESTABLISHED_TIMING_1;
 623        num += get_est_timing(block, &mode[num]);
 624
 625        DPRINTK("   Standard Timings\n");
 626        block = edid + STD_TIMING_DESCRIPTIONS_START;
 627        for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE)
 628                num += get_std_timing(block, &mode[num]);
 629
 630        block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 631        for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) {
 632                if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa)
 633                        num += get_dst_timing(block + 5, &mode[num]);
 634        }
 635
 636        /* Yikes, EDID data is totally useless */
 637        if (!num) {
 638                kfree(mode);
 639                return NULL;
 640        }
 641
 642        *dbsize = num;
 643        m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL);
 644        if (!m)
 645                return mode;
 646        memmove(m, mode, num * sizeof(struct fb_videomode));
 647        kfree(mode);
 648        return m;
 649}
 650
 651/**
 652 * fb_destroy_modedb - destroys mode database
 653 * @modedb: mode database to destroy
 654 *
 655 * DESCRIPTION:
 656 * Destroy mode database created by fb_create_modedb
 657 */
 658void fb_destroy_modedb(struct fb_videomode *modedb)
 659{
 660        kfree(modedb);
 661}
 662
 663static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
 664{
 665        int i, retval = 1;
 666        unsigned char *block;
 667
 668        block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 669
 670        DPRINTK("      Monitor Operating Limits: ");
 671
 672        for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
 673                if (edid_is_limits_block(block)) {
 674                        specs->hfmin = H_MIN_RATE * 1000;
 675                        specs->hfmax = H_MAX_RATE * 1000;
 676                        specs->vfmin = V_MIN_RATE;
 677                        specs->vfmax = V_MAX_RATE;
 678                        specs->dclkmax = MAX_PIXEL_CLOCK * 1000000;
 679                        specs->gtf = (GTF_SUPPORT) ? 1 : 0;
 680                        retval = 0;
 681                        DPRINTK("From EDID\n");
 682                        break;
 683                }
 684        }
 685
 686        /* estimate monitor limits based on modes supported */
 687        if (retval) {
 688                struct fb_videomode *modes, *mode;
 689                int num_modes, hz, hscan, pixclock;
 690                int vtotal, htotal;
 691
 692                modes = fb_create_modedb(edid, &num_modes);
 693                if (!modes) {
 694                        DPRINTK("None Available\n");
 695                        return 1;
 696                }
 697
 698                retval = 0;
 699                for (i = 0; i < num_modes; i++) {
 700                        mode = &modes[i];
 701                        pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
 702                        htotal = mode->xres + mode->right_margin + mode->hsync_len
 703                                + mode->left_margin;
 704                        vtotal = mode->yres + mode->lower_margin + mode->vsync_len
 705                                + mode->upper_margin;
 706
 707                        if (mode->vmode & FB_VMODE_INTERLACED)
 708                                vtotal /= 2;
 709
 710                        if (mode->vmode & FB_VMODE_DOUBLE)
 711                                vtotal *= 2;
 712
 713                        hscan = (pixclock + htotal / 2) / htotal;
 714                        hscan = (hscan + 500) / 1000 * 1000;
 715                        hz = (hscan + vtotal / 2) / vtotal;
 716
 717                        if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
 718                                specs->dclkmax = pixclock;
 719
 720                        if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
 721                                specs->dclkmin = pixclock;
 722
 723                        if (specs->hfmax == 0 || specs->hfmax < hscan)
 724                                specs->hfmax = hscan;
 725
 726                        if (specs->hfmin == 0 || specs->hfmin > hscan)
 727                                specs->hfmin = hscan;
 728
 729                        if (specs->vfmax == 0 || specs->vfmax < hz)
 730                                specs->vfmax = hz;
 731
 732                        if (specs->vfmin == 0 || specs->vfmin > hz)
 733                                specs->vfmin = hz;
 734                }
 735                DPRINTK("Extrapolated\n");
 736                fb_destroy_modedb(modes);
 737        }
 738        DPRINTK("           H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n",
 739                specs->hfmin/1000, specs->hfmax/1000, specs->vfmin,
 740                specs->vfmax, specs->dclkmax/1000000);
 741        return retval;
 742}
 743
 744static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs)
 745{
 746        unsigned char c, *block;
 747
 748        block = edid + EDID_STRUCT_DISPLAY;
 749
 750        fb_get_monitor_limits(edid, specs);
 751
 752        c = block[0] & 0x80;
 753        specs->input = 0;
 754        if (c) {
 755                specs->input |= FB_DISP_DDI;
 756                DPRINTK("      Digital Display Input");
 757        } else {
 758                DPRINTK("      Analog Display Input: Input Voltage - ");
 759                switch ((block[0] & 0x60) >> 5) {
 760                case 0:
 761                        DPRINTK("0.700V/0.300V");
 762                        specs->input |= FB_DISP_ANA_700_300;
 763                        break;
 764                case 1:
 765                        DPRINTK("0.714V/0.286V");
 766                        specs->input |= FB_DISP_ANA_714_286;
 767                        break;
 768                case 2:
 769                        DPRINTK("1.000V/0.400V");
 770                        specs->input |= FB_DISP_ANA_1000_400;
 771                        break;
 772                case 3:
 773                        DPRINTK("0.700V/0.000V");
 774                        specs->input |= FB_DISP_ANA_700_000;
 775                        break;
 776                }
 777        }
 778        DPRINTK("\n      Sync: ");
 779        c = block[0] & 0x10;
 780        if (c)
 781                DPRINTK("      Configurable signal level\n");
 782        c = block[0] & 0x0f;
 783        specs->signal = 0;
 784        if (c & 0x10) {
 785                DPRINTK("Blank to Blank ");
 786                specs->signal |= FB_SIGNAL_BLANK_BLANK;
 787        }
 788        if (c & 0x08) {
 789                DPRINTK("Separate ");
 790                specs->signal |= FB_SIGNAL_SEPARATE;
 791        }
 792        if (c & 0x04) {
 793                DPRINTK("Composite ");
 794                specs->signal |= FB_SIGNAL_COMPOSITE;
 795        }
 796        if (c & 0x02) {
 797                DPRINTK("Sync on Green ");
 798                specs->signal |= FB_SIGNAL_SYNC_ON_GREEN;
 799        }
 800        if (c & 0x01) {
 801                DPRINTK("Serration on ");
 802                specs->signal |= FB_SIGNAL_SERRATION_ON;
 803        }
 804        DPRINTK("\n");
 805        specs->max_x = block[1];
 806        specs->max_y = block[2];
 807        DPRINTK("      Max H-size in cm: ");
 808        if (specs->max_x)
 809                DPRINTK("%d\n", specs->max_x);
 810        else
 811                DPRINTK("variable\n");
 812        DPRINTK("      Max V-size in cm: ");
 813        if (specs->max_y)
 814                DPRINTK("%d\n", specs->max_y);
 815        else
 816                DPRINTK("variable\n");
 817
 818        c = block[3];
 819        specs->gamma = c+100;
 820        DPRINTK("      Gamma: ");
 821        DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100);
 822
 823        get_dpms_capabilities(block[4], specs);
 824
 825        switch ((block[4] & 0x18) >> 3) {
 826        case 0:
 827                DPRINTK("      Monochrome/Grayscale\n");
 828                specs->input |= FB_DISP_MONO;
 829                break;
 830        case 1:
 831                DPRINTK("      RGB Color Display\n");
 832                specs->input |= FB_DISP_RGB;
 833                break;
 834        case 2:
 835                DPRINTK("      Non-RGB Multicolor Display\n");
 836                specs->input |= FB_DISP_MULTI;
 837                break;
 838        default:
 839                DPRINTK("      Unknown\n");
 840                specs->input |= FB_DISP_UNKNOWN;
 841                break;
 842        }
 843
 844        get_chroma(block, specs);
 845
 846        specs->misc = 0;
 847        c = block[4] & 0x7;
 848        if (c & 0x04) {
 849                DPRINTK("      Default color format is primary\n");
 850                specs->misc |= FB_MISC_PRIM_COLOR;
 851        }
 852        if (c & 0x02) {
 853                DPRINTK("      First DETAILED Timing is preferred\n");
 854                specs->misc |= FB_MISC_1ST_DETAIL;
 855        }
 856        if (c & 0x01) {
 857                printk("      Display is GTF capable\n");
 858                specs->gtf = 1;
 859        }
 860}
 861
 862int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
 863{
 864        int i;
 865        unsigned char *block;
 866
 867        if (edid == NULL || var == NULL)
 868                return 1;
 869
 870        if (!(edid_checksum(edid)))
 871                return 1;
 872
 873        if (!(edid_check_header(edid)))
 874                return 1;
 875
 876        block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 877
 878        for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
 879                if (edid_is_timing_block(block)) {
 880                        var->xres = var->xres_virtual = H_ACTIVE;
 881                        var->yres = var->yres_virtual = V_ACTIVE;
 882                        var->height = var->width = -1;
 883                        var->right_margin = H_SYNC_OFFSET;
 884                        var->left_margin = (H_ACTIVE + H_BLANKING) -
 885                                (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
 886                        var->upper_margin = V_BLANKING - V_SYNC_OFFSET -
 887                                V_SYNC_WIDTH;
 888                        var->lower_margin = V_SYNC_OFFSET;
 889                        var->hsync_len = H_SYNC_WIDTH;
 890                        var->vsync_len = V_SYNC_WIDTH;
 891                        var->pixclock = PIXEL_CLOCK;
 892                        var->pixclock /= 1000;
 893                        var->pixclock = KHZ2PICOS(var->pixclock);
 894
 895                        if (HSYNC_POSITIVE)
 896                                var->sync |= FB_SYNC_HOR_HIGH_ACT;
 897                        if (VSYNC_POSITIVE)
 898                                var->sync |= FB_SYNC_VERT_HIGH_ACT;
 899                        return 0;
 900                }
 901        }
 902        return 1;
 903}
 904
 905void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
 906{
 907        unsigned char *block;
 908        int i, found = 0;
 909
 910        if (edid == NULL)
 911                return;
 912
 913        if (!(edid_checksum(edid)))
 914                return;
 915
 916        if (!(edid_check_header(edid)))
 917                return;
 918
 919        memset(specs, 0, sizeof(struct fb_monspecs));
 920
 921        specs->version = edid[EDID_STRUCT_VERSION];
 922        specs->revision = edid[EDID_STRUCT_REVISION];
 923
 924        DPRINTK("========================================\n");
 925        DPRINTK("Display Information (EDID)\n");
 926        DPRINTK("========================================\n");
 927        DPRINTK("   EDID Version %d.%d\n", (int) specs->version,
 928               (int) specs->revision);
 929
 930        parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs);
 931
 932        block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 933        for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
 934                if (edid_is_serial_block(block)) {
 935                        copy_string(block, specs->serial_no);
 936                        DPRINTK("   Serial Number: %s\n", specs->serial_no);
 937                } else if (edid_is_ascii_block(block)) {
 938                        copy_string(block, specs->ascii);
 939                        DPRINTK("   ASCII Block: %s\n", specs->ascii);
 940                } else if (edid_is_monitor_block(block)) {
 941                        copy_string(block, specs->monitor);
 942                        DPRINTK("   Monitor Name: %s\n", specs->monitor);
 943                }
 944        }
 945
 946        DPRINTK("   Display Characteristics:\n");
 947        get_monspecs(edid, specs);
 948
 949        specs->modedb = fb_create_modedb(edid, &specs->modedb_len);
 950
 951        /*
 952         * Workaround for buggy EDIDs that sets that the first
 953         * detailed timing is preferred but has not detailed
 954         * timing specified
 955         */
 956        for (i = 0; i < specs->modedb_len; i++) {
 957                if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) {
 958                        found = 1;
 959                        break;
 960                }
 961        }
 962
 963        if (!found)
 964                specs->misc &= ~FB_MISC_1ST_DETAIL;
 965
 966        DPRINTK("========================================\n");
 967}
 968
 969/*
 970 * VESA Generalized Timing Formula (GTF)
 971 */
 972
 973#define FLYBACK                     550
 974#define V_FRONTPORCH                1
 975#define H_OFFSET                    40
 976#define H_SCALEFACTOR               20
 977#define H_BLANKSCALE                128
 978#define H_GRADIENT                  600
 979#define C_VAL                       30
 980#define M_VAL                       300
 981
 982struct __fb_timings {
 983        u32 dclk;
 984        u32 hfreq;
 985        u32 vfreq;
 986        u32 hactive;
 987        u32 vactive;
 988        u32 hblank;
 989        u32 vblank;
 990        u32 htotal;
 991        u32 vtotal;
 992};
 993
 994/**
 995 * fb_get_vblank - get vertical blank time
 996 * @hfreq: horizontal freq
 997 *
 998 * DESCRIPTION:
 999 * vblank = right_margin + vsync_len + left_margin
1000 *
1001 *    given: right_margin = 1 (V_FRONTPORCH)
1002 *           vsync_len    = 3
1003 *           flyback      = 550
1004 *
1005 *                          flyback * hfreq
1006 *           left_margin  = --------------- - vsync_len
1007 *                           1000000
1008 */
1009static u32 fb_get_vblank(u32 hfreq)
1010{
1011        u32 vblank;
1012
1013        vblank = (hfreq * FLYBACK)/1000;
1014        vblank = (vblank + 500)/1000;
1015        return (vblank + V_FRONTPORCH);
1016}
1017
1018/**
1019 * fb_get_hblank_by_freq - get horizontal blank time given hfreq
1020 * @hfreq: horizontal freq
1021 * @xres: horizontal resolution in pixels
1022 *
1023 * DESCRIPTION:
1024 *
1025 *           xres * duty_cycle
1026 * hblank = ------------------
1027 *           100 - duty_cycle
1028 *
1029 * duty cycle = percent of htotal assigned to inactive display
1030 * duty cycle = C - (M/Hfreq)
1031 *
1032 * where: C = ((offset - scale factor) * blank_scale)
1033 *            -------------------------------------- + scale factor
1034 *                        256
1035 *        M = blank_scale * gradient
1036 *
1037 */
1038static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres)
1039{
1040        u32 c_val, m_val, duty_cycle, hblank;
1041
1042        c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 +
1043                 H_SCALEFACTOR) * 1000;
1044        m_val = (H_BLANKSCALE * H_GRADIENT)/256;
1045        m_val = (m_val * 1000000)/hfreq;
1046        duty_cycle = c_val - m_val;
1047        hblank = (xres * duty_cycle)/(100000 - duty_cycle);
1048        return (hblank);
1049}
1050
1051/**
1052 * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock
1053 * @dclk: pixelclock in Hz
1054 * @xres: horizontal resolution in pixels
1055 *
1056 * DESCRIPTION:
1057 *
1058 *           xres * duty_cycle
1059 * hblank = ------------------
1060 *           100 - duty_cycle
1061 *
1062 * duty cycle = percent of htotal assigned to inactive display
1063 * duty cycle = C - (M * h_period)
1064 *
1065 * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100
1066 *                   -----------------------------------------------
1067 *                                    2 * M
1068 *        M = 300;
1069 *        C = 30;
1070
1071 */
1072static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres)
1073{
1074        u32 duty_cycle, h_period, hblank;
1075
1076        dclk /= 1000;
1077        h_period = 100 - C_VAL;
1078        h_period *= h_period;
1079        h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk);
1080        h_period *= 10000;
1081
1082        h_period = int_sqrt(h_period);
1083        h_period -= (100 - C_VAL) * 100;
1084        h_period *= 1000;
1085        h_period /= 2 * M_VAL;
1086
1087        duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100;
1088        hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8;
1089        hblank &= ~15;
1090        return (hblank);
1091}
1092
1093/**
1094 * fb_get_hfreq - estimate hsync
1095 * @vfreq: vertical refresh rate
1096 * @yres: vertical resolution
1097 *
1098 * DESCRIPTION:
1099 *
1100 *          (yres + front_port) * vfreq * 1000000
1101 * hfreq = -------------------------------------
1102 *          (1000000 - (vfreq * FLYBACK)
1103 *
1104 */
1105
1106static u32 fb_get_hfreq(u32 vfreq, u32 yres)
1107{
1108        u32 divisor, hfreq;
1109
1110        divisor = (1000000 - (vfreq * FLYBACK))/1000;
1111        hfreq = (yres + V_FRONTPORCH) * vfreq  * 1000;
1112        return (hfreq/divisor);
1113}
1114
1115static void fb_timings_vfreq(struct __fb_timings *timings)
1116{
1117        timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive);
1118        timings->vblank = fb_get_vblank(timings->hfreq);
1119        timings->vtotal = timings->vactive + timings->vblank;
1120        timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
1121                                                 timings->hactive);
1122        timings->htotal = timings->hactive + timings->hblank;
1123        timings->dclk = timings->htotal * timings->hfreq;
1124}
1125
1126static void fb_timings_hfreq(struct __fb_timings *timings)
1127{
1128        timings->vblank = fb_get_vblank(timings->hfreq);
1129        timings->vtotal = timings->vactive + timings->vblank;
1130        timings->vfreq = timings->hfreq/timings->vtotal;
1131        timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq,
1132                                                 timings->hactive);
1133        timings->htotal = timings->hactive + timings->hblank;
1134        timings->dclk = timings->htotal * timings->hfreq;
1135}
1136
1137static void fb_timings_dclk(struct __fb_timings *timings)
1138{
1139        timings->hblank = fb_get_hblank_by_dclk(timings->dclk,
1140                                                timings->hactive);
1141        timings->htotal = timings->hactive + timings->hblank;
1142        timings->hfreq = timings->dclk/timings->htotal;
1143        timings->vblank = fb_get_vblank(timings->hfreq);
1144        timings->vtotal = timings->vactive + timings->vblank;
1145        timings->vfreq = timings->hfreq/timings->vtotal;
1146}
1147
1148/*
1149 * fb_get_mode - calculates video mode using VESA GTF
1150 * @flags: if: 0 - maximize vertical refresh rate
1151 *             1 - vrefresh-driven calculation;
1152 *             2 - hscan-driven calculation;
1153 *             3 - pixelclock-driven calculation;
1154 * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock
1155 * @var: pointer to fb_var_screeninfo
1156 * @info: pointer to fb_info
1157 *
1158 * DESCRIPTION:
1159 * Calculates video mode based on monitor specs using VESA GTF.
1160 * The GTF is best for VESA GTF compliant monitors but is
1161 * specifically formulated to work for older monitors as well.
1162 *
1163 * If @flag==0, the function will attempt to maximize the
1164 * refresh rate.  Otherwise, it will calculate timings based on
1165 * the flag and accompanying value.
1166 *
1167 * If FB_IGNOREMON bit is set in @flags, monitor specs will be
1168 * ignored and @var will be filled with the calculated timings.
1169 *
1170 * All calculations are based on the VESA GTF Spreadsheet
1171 * available at VESA's public ftp (http://www.vesa.org).
1172 *
1173 * NOTES:
1174 * The timings generated by the GTF will be different from VESA
1175 * DMT.  It might be a good idea to keep a table of standard
1176 * VESA modes as well.  The GTF may also not work for some displays,
1177 * such as, and especially, analog TV.
1178 *
1179 * REQUIRES:
1180 * A valid info->monspecs, otherwise 'safe numbers' will be used.
1181 */
1182int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info)
1183{
1184        struct __fb_timings *timings;
1185        u32 interlace = 1, dscan = 1;
1186        u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0;
1187
1188
1189        timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL);
1190
1191        if (!timings)
1192                return -ENOMEM;
1193
1194        /*
1195         * If monspecs are invalid, use values that are enough
1196         * for 640x480@60
1197         */
1198        if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax ||
1199            !info->monspecs.dclkmax ||
1200            info->monspecs.hfmax < info->monspecs.hfmin ||
1201            info->monspecs.vfmax < info->monspecs.vfmin ||
1202            info->monspecs.dclkmax < info->monspecs.dclkmin) {
1203                hfmin = 29000; hfmax = 30000;
1204                vfmin = 60; vfmax = 60;
1205                dclkmin = 0; dclkmax = 25000000;
1206        } else {
1207                hfmin = info->monspecs.hfmin;
1208                hfmax = info->monspecs.hfmax;
1209                vfmin = info->monspecs.vfmin;
1210                vfmax = info->monspecs.vfmax;
1211                dclkmin = info->monspecs.dclkmin;
1212                dclkmax = info->monspecs.dclkmax;
1213        }
1214
1215        timings->hactive = var->xres;
1216        timings->vactive = var->yres;
1217        if (var->vmode & FB_VMODE_INTERLACED) {
1218                timings->vactive /= 2;
1219                interlace = 2;
1220        }
1221        if (var->vmode & FB_VMODE_DOUBLE) {
1222                timings->vactive *= 2;
1223                dscan = 2;
1224        }
1225
1226        switch (flags & ~FB_IGNOREMON) {
1227        case FB_MAXTIMINGS: /* maximize refresh rate */
1228                timings->hfreq = hfmax;
1229                fb_timings_hfreq(timings);
1230                if (timings->vfreq > vfmax) {
1231                        timings->vfreq = vfmax;
1232                        fb_timings_vfreq(timings);
1233                }
1234                if (timings->dclk > dclkmax) {
1235                        timings->dclk = dclkmax;
1236                        fb_timings_dclk(timings);
1237                }
1238                break;
1239        case FB_VSYNCTIMINGS: /* vrefresh driven */
1240                timings->vfreq = val;
1241                fb_timings_vfreq(timings);
1242                break;
1243        case FB_HSYNCTIMINGS: /* hsync driven */
1244                timings->hfreq = val;
1245                fb_timings_hfreq(timings);
1246                break;
1247        case FB_DCLKTIMINGS: /* pixelclock driven */
1248                timings->dclk = PICOS2KHZ(val) * 1000;
1249                fb_timings_dclk(timings);
1250                break;
1251        default:
1252                err = -EINVAL;
1253
1254        }
1255
1256        if (err || (!(flags & FB_IGNOREMON) &&
1257            (timings->vfreq < vfmin || timings->vfreq > vfmax ||
1258             timings->hfreq < hfmin || timings->hfreq > hfmax ||
1259             timings->dclk < dclkmin || timings->dclk > dclkmax))) {
1260                err = -EINVAL;
1261        } else {
1262                var->pixclock = KHZ2PICOS(timings->dclk/1000);
1263                var->hsync_len = (timings->htotal * 8)/100;
1264                var->right_margin = (timings->hblank/2) - var->hsync_len;
1265                var->left_margin = timings->hblank - var->right_margin -
1266                        var->hsync_len;
1267                var->vsync_len = (3 * interlace)/dscan;
1268                var->lower_margin = (1 * interlace)/dscan;
1269                var->upper_margin = (timings->vblank * interlace)/dscan -
1270                        (var->vsync_len + var->lower_margin);
1271        }
1272
1273        kfree(timings);
1274        return err;
1275}
1276#else
1277int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
1278{
1279        return 1;
1280}
1281void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs)
1282{
1283        specs = NULL;
1284}
1285void fb_destroy_modedb(struct fb_videomode *modedb)
1286{
1287}
1288int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
1289                struct fb_info *info)
1290{
1291        return -EINVAL;
1292}
1293#endif /* CONFIG_FB_MODE_HELPERS */
1294
1295/*
1296 * fb_validate_mode - validates var against monitor capabilities
1297 * @var: pointer to fb_var_screeninfo
1298 * @info: pointer to fb_info
1299 *
1300 * DESCRIPTION:
1301 * Validates video mode against monitor capabilities specified in
1302 * info->monspecs.
1303 *
1304 * REQUIRES:
1305 * A valid info->monspecs.
1306 */
1307int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info)
1308{
1309        u32 hfreq, vfreq, htotal, vtotal, pixclock;
1310        u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax;
1311
1312        /*
1313         * If monspecs are invalid, use values that are enough
1314         * for 640x480@60
1315         */
1316        if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
1317            !info->monspecs.dclkmax ||
1318            info->monspecs.hfmax < info->monspecs.hfmin ||
1319            info->monspecs.vfmax < info->monspecs.vfmin ||
1320            info->monspecs.dclkmax < info->monspecs.dclkmin) {
1321                hfmin = 29000; hfmax = 30000;
1322                vfmin = 60; vfmax = 60;
1323                dclkmin = 0; dclkmax = 25000000;
1324        } else {
1325                hfmin = info->monspecs.hfmin;
1326                hfmax = info->monspecs.hfmax;
1327                vfmin = info->monspecs.vfmin;
1328                vfmax = info->monspecs.vfmax;
1329                dclkmin = info->monspecs.dclkmin;
1330                dclkmax = info->monspecs.dclkmax;
1331        }
1332
1333        if (!var->pixclock)
1334                return -EINVAL;
1335        pixclock = PICOS2KHZ(var->pixclock) * 1000;
1336
1337        htotal = var->xres + var->right_margin + var->hsync_len +
1338                var->left_margin;
1339        vtotal = var->yres + var->lower_margin + var->vsync_len +
1340                var->upper_margin;
1341
1342        if (var->vmode & FB_VMODE_INTERLACED)
1343                vtotal /= 2;
1344        if (var->vmode & FB_VMODE_DOUBLE)
1345                vtotal *= 2;
1346
1347        hfreq = pixclock/htotal;
1348        hfreq = (hfreq + 500) / 1000 * 1000;
1349
1350        vfreq = hfreq/vtotal;
1351
1352        return (vfreq < vfmin || vfreq > vfmax ||
1353                hfreq < hfmin || hfreq > hfmax ||
1354                pixclock < dclkmin || pixclock > dclkmax) ?
1355                -EINVAL : 0;
1356}
1357
1358#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86)
1359
1360/*
1361 * We need to ensure that the EDID block is only returned for
1362 * the primary graphics adapter.
1363 */
1364
1365const unsigned char *fb_firmware_edid(struct device *device)
1366{
1367        struct pci_dev *dev = NULL;
1368        struct resource *res = NULL;
1369        unsigned char *edid = NULL;
1370
1371        if (device)
1372                dev = to_pci_dev(device);
1373
1374        if (dev)
1375                res = &dev->resource[PCI_ROM_RESOURCE];
1376
1377        if (res && res->flags & IORESOURCE_ROM_SHADOW)
1378                edid = edid_info.dummy;
1379
1380        return edid;
1381}
1382#else
1383const unsigned char *fb_firmware_edid(struct device *device)
1384{
1385        return NULL;
1386}
1387#endif
1388EXPORT_SYMBOL(fb_firmware_edid);
1389
1390EXPORT_SYMBOL(fb_parse_edid);
1391EXPORT_SYMBOL(fb_edid_to_monspecs);
1392EXPORT_SYMBOL(fb_get_mode);
1393EXPORT_SYMBOL(fb_validate_mode);
1394EXPORT_SYMBOL(fb_destroy_modedb);
1395
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.