/*
* linux/drivers/video/fbmon.c
*
* Copyright (C) 2002 James Simmons <jsimmons@users.sf.net>
*
* Credits:
*
* The EDID Parser is a conglomeration from the following sources:
*
* 1. SciTech SNAP Graphics Architecture
* Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved.
*
* 2. XFree86 4.3.0, interpret_edid.c
* Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
*
* 3. John Fremlin <vii@users.sourceforge.net> and
* Ani Joshi <ajoshi@unixbox.com>
*
* Generalized Timing Formula is derived from:
*
* GTF Spreadsheet by Andy Morrish (1/5/97)
* available at http://www.vesa.org
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
*/
#include <linux/fb.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <video/edid.h>
#include <video/of_videomode.h>
#include <video/videomode.h>
#ifdef CONFIG_PPC_OF
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
#include "../edid.h"
/*
* EDID parser
*/
#undef DEBUG /* define this for verbose EDID parsing output */
#ifdef DEBUG
#define DPRINTK(fmt, args...) printk(fmt,## args)
#else
#define DPRINTK(fmt, args...)
#endif
#define FBMON_FIX_HEADER 1
#define FBMON_FIX_INPUT 2
#define FBMON_FIX_TIMINGS 3
#ifdef CONFIG_FB_MODE_HELPERS
struct broken_edid {
u8 manufacturer[4];
u32 model;
u32 fix;
};
static const struct broken_edid brokendb[] = {
/* DEC FR-PCXAV-YZ */
{
.manufacturer = "DEC",
.model = 0x073a,
.fix = FBMON_FIX_HEADER,
},
/* ViewSonic PF775a */
{
.manufacturer = "VSC",
.model = 0x5a44,
.fix = FBMON_FIX_INPUT,
},
/* Sharp UXGA? */
{
.manufacturer = "SHP",
.model = 0x138e,
.fix = FBMON_FIX_TIMINGS,
},
};
static const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x00
};
static void copy_string(unsigned char *c, unsigned char *s)
{
int i;
c = c + 5;
for (i = 0; (i < 13 && *c != 0x0A); i++)
*(s++) = *(c++);
*s = 0;
while (i-- && (*--s == 0x20)) *s = 0;
}
static int edid_is_serial_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xff) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_ascii_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xfe) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_limits_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xfd) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_monitor_block(unsigned char *block)
{
if ((block[0] == 0x00) && (block[1] == 0x00) &&
(block[2] == 0x00) && (block[3] == 0xfc) &&
(block[4] == 0x00))
return 1;
else
return 0;
}
static int edid_is_timing_block(unsigned char *block)
{
if ((block[0] != 0x00) || (block[1] != 0x00) ||
(block[2] != 0x00) || (block[4] != 0x00))
return 1;
else
return 0;
}
static int check_edid(unsigned char *edid)
{
unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];
unsigned char *b;
u32 model;
int i, fix = 0, ret = 0;
manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@';
manufacturer[1] = ((block[0] & 0x03) << 3) +
((block[1] & 0xe0) >> 5) + '@';
manufacturer[2] = (block[1] & 0x1f) + '@';
manufacturer[3] = 0;
model = block[2] + (block[3] << 8);
for (i = 0; i < ARRAY_SIZE(brokendb); i++) {
if (!strncmp(manufacturer, brokendb[i].manufacturer, 4) &&
brokendb[i].model == model) {
fix = brokendb[i].fix;
break;
}
}
switch (fix) {
case FBMON_FIX_HEADER:
for (i = 0; i < 8; i++) {
if (edid[i] != edid_v1_header[i]) {
ret = fix;
break;
}
}
break;
case FBMON_FIX_INPUT:
b = edid + EDID_STRUCT_DISPLAY;
/* Only if display is GTF capable will
the input type be reset to analog */
if (b[4] & 0x01 && b[0] & 0x80)
ret = fix;
break;
case FBMON_FIX_TIMINGS:
b = edid + DETAILED_TIMING_DESCRIPTIONS_START;
ret = fix;
for (i = 0; i < 4; i++) {
if (edid_is_limits_block(b)) {
ret = 0;
break;
}
b += DETAILED_TIMING_DESCRIPTION_SIZE;
}
break;
}
if (ret)
printk("fbmon: The EDID Block of "
"Manufacturer: %s Model: 0x%x is known to "
"be broken,\n", manufacturer, model);
return ret;
}
static void fix_edid(unsigne
|