diff options
Diffstat (limited to 'ANDROID_3.4.5/drivers/video/fbmon.c')
-rw-r--r-- | ANDROID_3.4.5/drivers/video/fbmon.c | 1498 |
1 files changed, 0 insertions, 1498 deletions
diff --git a/ANDROID_3.4.5/drivers/video/fbmon.c b/ANDROID_3.4.5/drivers/video/fbmon.c deleted file mode 100644 index cef65574..00000000 --- a/ANDROID_3.4.5/drivers/video/fbmon.c +++ /dev/null @@ -1,1498 +0,0 @@ -/* - * 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> -#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(unsigned char *edid, int fix) -{ - int i; - unsigned char *b, csum = 0; - - switch (fix) { - case FBMON_FIX_HEADER: - printk("fbmon: trying a header reconstruct\n"); - memcpy(edid, edid_v1_header, 8); - break; - case FBMON_FIX_INPUT: - printk("fbmon: trying to fix input type\n"); - b = edid + EDID_STRUCT_DISPLAY; - b[0] &= ~0x80; - edid[127] += 0x80; - break; - case FBMON_FIX_TIMINGS: - printk("fbmon: trying to fix monitor timings\n"); - b = edid + DETAILED_TIMING_DESCRIPTIONS_START; - for (i = 0; i < 4; i++) { - if (!(edid_is_serial_block(b) || - edid_is_ascii_block(b) || - edid_is_monitor_block(b) || - edid_is_timing_block(b))) { - b[0] = 0x00; - b[1] = 0x00; - b[2] = 0x00; - b[3] = 0xfd; - b[4] = 0x00; - b[5] = 60; /* vfmin */ - b[6] = 60; /* vfmax */ - b[7] = 30; /* hfmin */ - b[8] = 75; /* hfmax */ - b[9] = 17; /* pixclock - 170 MHz*/ - b[10] = 0; /* GTF */ - break; - } - - b += DETAILED_TIMING_DESCRIPTION_SIZE; - } - - for (i = 0; i < EDID_LENGTH - 1; i++) - csum += edid[i]; - - edid[127] = 256 - csum; - break; - } -} - -static int edid_checksum(unsigned char *edid) -{ - unsigned char csum = 0, all_null = 0; - int i, err = 0, fix = check_edid(edid); - - if (fix) - fix_edid(edid, fix); - - for (i = 0; i < EDID_LENGTH; i++) { - csum += edid[i]; - all_null |= edid[i]; - } - - if (csum == 0x00 && all_null) { - /* checksum passed, everything's good */ - err = 1; - } - - return err; -} - -static int edid_check_header(unsigned char *edid) -{ - int i, err = 1, fix = check_edid(edid); - - if (fix) - fix_edid(edid, fix); - - for (i = 0; i < 8; i++) { - if (edid[i] != edid_v1_header[i]) - err = 0; - } - - return err; -} - -static void parse_vendor_block(unsigned char *block, struct fb_monspecs *specs) -{ - specs->manufacturer[0] = ((block[0] & 0x7c) >> 2) + '@'; - specs->manufacturer[1] = ((block[0] & 0x03) << 3) + - ((block[1] & 0xe0) >> 5) + '@'; - specs->manufacturer[2] = (block[1] & 0x1f) + '@'; - specs->manufacturer[3] = 0; - specs->model = block[2] + (block[3] << 8); - specs->serial = block[4] + (block[5] << 8) + - (block[6] << 16) + (block[7] << 24); - specs->year = block[9] + 1990; - specs->week = block[8]; - DPRINTK(" Manufacturer: %s\n", specs->manufacturer); - DPRINTK(" Model: %x\n", specs->model); - DPRINTK(" Serial#: %u\n", specs->serial); - DPRINTK(" Year: %u Week %u\n", specs->year, specs->week); -} - -static void get_dpms_capabilities(unsigned char flags, - struct fb_monspecs *specs) -{ - specs->dpms = 0; - if (flags & DPMS_ACTIVE_OFF) - specs->dpms |= FB_DPMS_ACTIVE_OFF; - if (flags & DPMS_SUSPEND) - specs->dpms |= FB_DPMS_SUSPEND; - if (flags & DPMS_STANDBY) - specs->dpms |= FB_DPMS_STANDBY; - DPRINTK(" DPMS: Active %s, Suspend %s, Standby %s\n", - (flags & DPMS_ACTIVE_OFF) ? "yes" : "no", - (flags & DPMS_SUSPEND) ? "yes" : "no", - (flags & DPMS_STANDBY) ? "yes" : "no"); -} - -static void get_chroma(unsigned char *block, struct fb_monspecs *specs) -{ - int tmp; - - DPRINTK(" Chroma\n"); - /* Chromaticity data */ - tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.redx = tmp/1024; - DPRINTK(" RedX: 0.%03d ", specs->chroma.redx); - - tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.redy = tmp/1024; - DPRINTK("RedY: 0.%03d\n", specs->chroma.redy); - - tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.greenx = tmp/1024; - DPRINTK(" GreenX: 0.%03d ", specs->chroma.greenx); - - tmp = (block[5] & 3) | (block[0xa] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.greeny = tmp/1024; - DPRINTK("GreenY: 0.%03d\n", specs->chroma.greeny); - - tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.bluex = tmp/1024; - DPRINTK(" BlueX: 0.%03d ", specs->chroma.bluex); - - tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.bluey = tmp/1024; - DPRINTK("BlueY: 0.%03d\n", specs->chroma.bluey); - - tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.whitex = tmp/1024; - DPRINTK(" WhiteX: 0.%03d ", specs->chroma.whitex); - - tmp = (block[6] & 3) | (block[0xe] << 2); - tmp *= 1000; - tmp += 512; - specs->chroma.whitey = tmp/1024; - DPRINTK("WhiteY: 0.%03d\n", specs->chroma.whitey); -} - -static void calc_mode_timings(int xres, int yres, int refresh, - struct fb_videomode *mode) -{ - struct fb_var_screeninfo *var; - - var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL); - - if (var) { - var->xres = xres; - var->yres = yres; - fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, - refresh, var, NULL); - mode->xres = xres; - mode->yres = yres; - mode->pixclock = var->pixclock; - mode->refresh = refresh; - mode->left_margin = var->left_margin; - mode->right_margin = var->right_margin; - mode->upper_margin = var->upper_margin; - mode->lower_margin = var->lower_margin; - mode->hsync_len = var->hsync_len; - mode->vsync_len = var->vsync_len; - mode->vmode = 0; - mode->sync = 0; - kfree(var); - } -} - -static int get_est_timing(unsigned char *block, struct fb_videomode *mode) -{ - int num = 0; - unsigned char c; - - c = block[0]; - if (c&0x80) { - calc_mode_timings(720, 400, 70, &mode[num]); - mode[num++].flag = FB_MODE_IS_CALCULATED; - DPRINTK(" 720x400@70Hz\n"); - } - if (c&0x40) { - calc_mode_timings(720, 400, 88, &mode[num]); - mode[num++].flag = FB_MODE_IS_CALCULATED; - DPRINTK(" 720x400@88Hz\n"); - } - if (c&0x20) { - mode[num++] = vesa_modes[3]; - DPRINTK(" 640x480@60Hz\n"); - } - if (c&0x10) { - calc_mode_timings(640, 480, 67, &mode[num]); - mode[num++].flag = FB_MODE_IS_CALCULATED; - DPRINTK(" 640x480@67Hz\n"); - } - if (c&0x08) { - mode[num++] = vesa_modes[4]; - DPRINTK(" 640x480@72Hz\n"); - } - if (c&0x04) { - mode[num++] = vesa_modes[5]; - DPRINTK(" 640x480@75Hz\n"); - } - if (c&0x02) { - mode[num++] = vesa_modes[7]; - DPRINTK(" 800x600@56Hz\n"); - } - if (c&0x01) { - mode[num++] = vesa_modes[8]; - DPRINTK(" 800x600@60Hz\n"); - } - - c = block[1]; - if (c&0x80) { - mode[num++] = vesa_modes[9]; - DPRINTK(" 800x600@72Hz\n"); - } - if (c&0x40) { - mode[num++] = vesa_modes[10]; - DPRINTK(" 800x600@75Hz\n"); - } - if (c&0x20) { - calc_mode_timings(832, 624, 75, &mode[num]); - mode[num++].flag = FB_MODE_IS_CALCULATED; - DPRINTK(" 832x624@75Hz\n"); - } - if (c&0x10) { - mode[num++] = vesa_modes[12]; - DPRINTK(" 1024x768@87Hz Interlaced\n"); - } - if (c&0x08) { - mode[num++] = vesa_modes[13]; - DPRINTK(" 1024x768@60Hz\n"); - } - if (c&0x04) { - mode[num++] = vesa_modes[14]; - DPRINTK(" 1024x768@70Hz\n"); - } - if (c&0x02) { - mode[num++] = vesa_modes[15]; - DPRINTK(" 1024x768@75Hz\n"); - } - if (c&0x01) { - mode[num++] = vesa_modes[21]; - DPRINTK(" 1280x1024@75Hz\n"); - } - c = block[2]; - if (c&0x80) { - mode[num++] = vesa_modes[17]; - DPRINTK(" 1152x870@75Hz\n"); - } - DPRINTK(" Manufacturer's mask: %x\n",c&0x7F); - return num; -} - -static int get_std_timing(unsigned char *block, struct fb_videomode *mode, - int ver, int rev) -{ - int xres, yres = 0, refresh, ratio, i; - - xres = (block[0] + 31) * 8; - if (xres <= 256) - return 0; - - ratio = (block[1] & 0xc0) >> 6; - switch (ratio) { - case 0: - /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ - if (ver < 1 || (ver == 1 && rev < 3)) - yres = xres; - else - yres = (xres * 10)/16; - break; - case 1: - yres = (xres * 3)/4; - break; - case 2: - yres = (xres * 4)/5; - break; - case 3: - yres = (xres * 9)/16; - break; - } - refresh = (block[1] & 0x3f) + 60; - - DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); - for (i = 0; i < VESA_MODEDB_SIZE; i++) { - if (vesa_modes[i].xres == xres && - vesa_modes[i].yres == yres && - vesa_modes[i].refresh == refresh) { - *mode = vesa_modes[i]; - mode->flag |= FB_MODE_IS_STANDARD; - return 1; - } - } - calc_mode_timings(xres, yres, refresh, mode); - return 1; -} - -static int get_dst_timing(unsigned char *block, - struct fb_videomode *mode, int ver, int rev) -{ - int j, num = 0; - - for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num], ver, rev); - - return num; -} - -static void get_detailed_timing(unsigned char *block, - struct fb_videomode *mode) -{ - mode->xres = H_ACTIVE; - mode->yres = V_ACTIVE; - mode->pixclock = PIXEL_CLOCK; - mode->pixclock /= 1000; - mode->pixclock = KHZ2PICOS(mode->pixclock); - mode->right_margin = H_SYNC_OFFSET; - mode->left_margin = (H_ACTIVE + H_BLANKING) - - (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); - mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - - V_SYNC_WIDTH; - mode->lower_margin = V_SYNC_OFFSET; - mode->hsync_len = H_SYNC_WIDTH; - mode->vsync_len = V_SYNC_WIDTH; - if (HSYNC_POSITIVE) - mode->sync |= FB_SYNC_HOR_HIGH_ACT; - if (VSYNC_POSITIVE) - mode->sync |= FB_SYNC_VERT_HIGH_ACT; - mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) * - (V_ACTIVE + V_BLANKING)); - if (INTERLACED) { - mode->yres *= 2; - mode->upper_margin *= 2; - mode->lower_margin *= 2; - mode->vsync_len *= 2; - mode->vmode |= FB_VMODE_INTERLACED; - } - mode->flag = FB_MODE_IS_DETAILED; - - DPRINTK(" %d MHz ", PIXEL_CLOCK/1000000); - DPRINTK("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, - H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING); - DPRINTK("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET, - V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING); - DPRINTK("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-", - (VSYNC_POSITIVE) ? "+" : "-"); -} - -/** - * fb_create_modedb - create video mode database - * @edid: EDID data - * @dbsize: database size - * - * RETURNS: struct fb_videomode, @dbsize contains length of database - * - * DESCRIPTION: - * This function builds a mode database using the contents of the EDID - * data - */ -static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) -{ - struct fb_videomode *mode, *m; - unsigned char *block; - int num = 0, i, first = 1; - int ver, rev; - - ver = edid[EDID_STRUCT_VERSION]; - rev = edid[EDID_STRUCT_REVISION]; - - mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); - if (mode == NULL) - return NULL; - - if (edid == NULL || !edid_checksum(edid) || - !edid_check_header(edid)) { - kfree(mode); - return NULL; - } - - *dbsize = 0; - - DPRINTK(" Detailed Timings\n"); - block = edid + DETAILED_TIMING_DESCRIPTIONS_START; - for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { - if (!(block[0] == 0x00 && block[1] == 0x00)) { - get_detailed_timing(block, &mode[num]); - if (first) { - mode[num].flag |= FB_MODE_IS_FIRST; - first = 0; - } - num++; - } - } - - DPRINTK(" Supported VESA Modes\n"); - block = edid + ESTABLISHED_TIMING_1; - num += get_est_timing(block, &mode[num]); - - DPRINTK(" Standard Timings\n"); - block = edid + STD_TIMING_DESCRIPTIONS_START; - for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num], ver, rev); - - block = edid + DETAILED_TIMING_DESCRIPTIONS_START; - for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { - if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa) - num += get_dst_timing(block + 5, &mode[num], ver, rev); - } - - /* Yikes, EDID data is totally useless */ - if (!num) { - kfree(mode); - return NULL; - } - - *dbsize = num; - m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL); - if (!m) - return mode; - memmove(m, mode, num * sizeof(struct fb_videomode)); - kfree(mode); - return m; -} - -/** - * fb_destroy_modedb - destroys mode database - * @modedb: mode database to destroy - * - * DESCRIPTION: - * Destroy mode database created by fb_create_modedb - */ -void fb_destroy_modedb(struct fb_videomode *modedb) -{ - kfree(modedb); -} - -static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) -{ - int i, retval = 1; - unsigned char *block; - - block = edid + DETAILED_TIMING_DESCRIPTIONS_START; - - DPRINTK(" Monitor Operating Limits: "); - - for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { - if (edid_is_limits_block(block)) { - specs->hfmin = H_MIN_RATE * 1000; - specs->hfmax = H_MAX_RATE * 1000; - specs->vfmin = V_MIN_RATE; - specs->vfmax = V_MAX_RATE; - specs->dclkmax = MAX_PIXEL_CLOCK * 1000000; - specs->gtf = (GTF_SUPPORT) ? 1 : 0; - retval = 0; - DPRINTK("From EDID\n"); - break; - } - } - - /* estimate monitor limits based on modes supported */ - if (retval) { - struct fb_videomode *modes, *mode; - int num_modes, hz, hscan, pixclock; - int vtotal, htotal; - - modes = fb_create_modedb(edid, &num_modes); - if (!modes) { - DPRINTK("None Available\n"); - return 1; - } - - retval = 0; - for (i = 0; i < num_modes; i++) { - mode = &modes[i]; - pixclock = PICOS2KHZ(modes[i].pixclock) * 1000; - htotal = mode->xres + mode->right_margin + mode->hsync_len - + mode->left_margin; - vtotal = mode->yres + mode->lower_margin + mode->vsync_len - + mode->upper_margin; - - if (mode->vmode & FB_VMODE_INTERLACED) - vtotal /= 2; - - if (mode->vmode & FB_VMODE_DOUBLE) - vtotal *= 2; - - hscan = (pixclock + htotal / 2) / htotal; - hscan = (hscan + 500) / 1000 * 1000; - hz = (hscan + vtotal / 2) / vtotal; - - if (specs->dclkmax == 0 || specs->dclkmax < pixclock) - specs->dclkmax = pixclock; - - if (specs->dclkmin == 0 || specs->dclkmin > pixclock) - specs->dclkmin = pixclock; - - if (specs->hfmax == 0 || specs->hfmax < hscan) - specs->hfmax = hscan; - - if (specs->hfmin == 0 || specs->hfmin > hscan) - specs->hfmin = hscan; - - if (specs->vfmax == 0 || specs->vfmax < hz) - specs->vfmax = hz; - - if (specs->vfmin == 0 || specs->vfmin > hz) - specs->vfmin = hz; - } - DPRINTK("Extrapolated\n"); - fb_destroy_modedb(modes); - } - DPRINTK(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n", - specs->hfmin/1000, specs->hfmax/1000, specs->vfmin, - specs->vfmax, specs->dclkmax/1000000); - return retval; -} - -static void get_monspecs(unsigned char *edid, struct fb_monspecs *specs) -{ - unsigned char c, *block; - - block = edid + EDID_STRUCT_DISPLAY; - - fb_get_monitor_limits(edid, specs); - - c = block[0] & 0x80; - specs->input = 0; - if (c) { - specs->input |= FB_DISP_DDI; - DPRINTK(" Digital Display Input"); - } else { - DPRINTK(" Analog Display Input: Input Voltage - "); - switch ((block[0] & 0x60) >> 5) { - case 0: - DPRINTK("0.700V/0.300V"); - specs->input |= FB_DISP_ANA_700_300; - break; - case 1: - DPRINTK("0.714V/0.286V"); - specs->input |= FB_DISP_ANA_714_286; - break; - case 2: - DPRINTK("1.000V/0.400V"); - specs->input |= FB_DISP_ANA_1000_400; - break; - case 3: - DPRINTK("0.700V/0.000V"); - specs->input |= FB_DISP_ANA_700_000; - break; - } - } - DPRINTK("\n Sync: "); - c = block[0] & 0x10; - if (c) - DPRINTK(" Configurable signal level\n"); - c = block[0] & 0x0f; - specs->signal = 0; - if (c & 0x10) { - DPRINTK("Blank to Blank "); - specs->signal |= FB_SIGNAL_BLANK_BLANK; - } - if (c & 0x08) { - DPRINTK("Separate "); - specs->signal |= FB_SIGNAL_SEPARATE; - } - if (c & 0x04) { - DPRINTK("Composite "); - specs->signal |= FB_SIGNAL_COMPOSITE; - } - if (c & 0x02) { - DPRINTK("Sync on Green "); - specs->signal |= FB_SIGNAL_SYNC_ON_GREEN; - } - if (c & 0x01) { - DPRINTK("Serration on "); - specs->signal |= FB_SIGNAL_SERRATION_ON; - } - DPRINTK("\n"); - specs->max_x = block[1]; - specs->max_y = block[2]; - DPRINTK(" Max H-size in cm: "); - if (specs->max_x) - DPRINTK("%d\n", specs->max_x); - else - DPRINTK("variable\n"); - DPRINTK(" Max V-size in cm: "); - if (specs->max_y) - DPRINTK("%d\n", specs->max_y); - else - DPRINTK("variable\n"); - - c = block[3]; - specs->gamma = c+100; - DPRINTK(" Gamma: "); - DPRINTK("%d.%d\n", specs->gamma/100, specs->gamma % 100); - - get_dpms_capabilities(block[4], specs); - - switch ((block[4] & 0x18) >> 3) { - case 0: - DPRINTK(" Monochrome/Grayscale\n"); - specs->input |= FB_DISP_MONO; - break; - case 1: - DPRINTK(" RGB Color Display\n"); - specs->input |= FB_DISP_RGB; - break; - case 2: - DPRINTK(" Non-RGB Multicolor Display\n"); - specs->input |= FB_DISP_MULTI; - break; - default: - DPRINTK(" Unknown\n"); - specs->input |= FB_DISP_UNKNOWN; - break; - } - - get_chroma(block, specs); - - specs->misc = 0; - c = block[4] & 0x7; - if (c & 0x04) { - DPRINTK(" Default color format is primary\n"); - specs->misc |= FB_MISC_PRIM_COLOR; - } - if (c & 0x02) { - DPRINTK(" First DETAILED Timing is preferred\n"); - specs->misc |= FB_MISC_1ST_DETAIL; - } - if (c & 0x01) { - printk(" Display is GTF capable\n"); - specs->gtf = 1; - } -} - -int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) -{ - int i; - unsigned char *block; - - if (edid == NULL || var == NULL) - return 1; - - if (!(edid_checksum(edid))) - return 1; - - if (!(edid_check_header(edid))) - return 1; - - block = edid + DETAILED_TIMING_DESCRIPTIONS_START; - - for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { - if (edid_is_timing_block(block)) { - var->xres = var->xres_virtual = H_ACTIVE; - var->yres = var->yres_virtual = V_ACTIVE; - var->height = var->width = 0; - var->right_margin = H_SYNC_OFFSET; - var->left_margin = (H_ACTIVE + H_BLANKING) - - (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); - var->upper_margin = V_BLANKING - V_SYNC_OFFSET - - V_SYNC_WIDTH; - var->lower_margin = V_SYNC_OFFSET; - var->hsync_len = H_SYNC_WIDTH; - var->vsync_len = V_SYNC_WIDTH; - var->pixclock = PIXEL_CLOCK; - var->pixclock /= 1000; - var->pixclock = KHZ2PICOS(var->pixclock); - - if (HSYNC_POSITIVE) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if (VSYNC_POSITIVE) - var->sync |= FB_SYNC_VERT_HIGH_ACT; - return 0; - } - } - return 1; -} - -void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) -{ - unsigned char *block; - int i, found = 0; - - if (edid == NULL) - return; - - if (!(edid_checksum(edid))) - return; - - if (!(edid_check_header(edid))) - return; - - memset(specs, 0, sizeof(struct fb_monspecs)); - - specs->version = edid[EDID_STRUCT_VERSION]; - specs->revision = edid[EDID_STRUCT_REVISION]; - - DPRINTK("========================================\n"); - DPRINTK("Display Information (EDID)\n"); - DPRINTK("========================================\n"); - DPRINTK(" EDID Version %d.%d\n", (int) specs->version, - (int) specs->revision); - - parse_vendor_block(edid + ID_MANUFACTURER_NAME, specs); - - block = edid + DETAILED_TIMING_DESCRIPTIONS_START; - for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { - if (edid_is_serial_block(block)) { - copy_string(block, specs->serial_no); - DPRINTK(" Serial Number: %s\n", specs->serial_no); - } else if (edid_is_ascii_block(block)) { - copy_string(block, specs->ascii); - DPRINTK(" ASCII Block: %s\n", specs->ascii); - } else if (edid_is_monitor_block(block)) { - copy_string(block, specs->monitor); - DPRINTK(" Monitor Name: %s\n", specs->monitor); - } - } - - DPRINTK(" Display Characteristics:\n"); - get_monspecs(edid, specs); - - specs->modedb = fb_create_modedb(edid, &specs->modedb_len); - - /* - * Workaround for buggy EDIDs that sets that the first - * detailed timing is preferred but has not detailed - * timing specified - */ - for (i = 0; i < specs->modedb_len; i++) { - if (specs->modedb[i].flag & FB_MODE_IS_DETAILED) { - found = 1; - break; - } - } - - if (!found) - specs->misc &= ~FB_MISC_1ST_DETAIL; - - DPRINTK("========================================\n"); -} - -/** - * fb_edid_add_monspecs() - add monitor video modes from E-EDID data - * @edid: 128 byte array with an E-EDID block - * @spacs: monitor specs to be extended - */ -void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) -{ - unsigned char *block; - struct fb_videomode *m; - int num = 0, i; - u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE]; - u8 pos = 4, svd_n = 0; - - if (!edid) - return; - - if (!edid_checksum(edid)) - return; - - if (edid[0] != 0x2 || - edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE) - return; - - DPRINTK(" Short Video Descriptors\n"); - - while (pos < edid[2]) { - u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7; - pr_debug("Data block %u of %u bytes\n", type, len); - if (type == 2) - for (i = pos; i < pos + len; i++) { - u8 idx = edid[pos + i] & 0x7f; - svd[svd_n++] = idx; - pr_debug("N%sative mode #%d\n", - edid[pos + i] & 0x80 ? "" : "on-n", idx); - } - pos += len + 1; - } - - block = edid + edid[2]; - - DPRINTK(" Extended Detailed Timings\n"); - - for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE; - i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) - if (PIXEL_CLOCK) - edt[num++] = block - edid; - - /* Yikes, EDID data is totally useless */ - if (!(num + svd_n)) - return; - - m = kzalloc((specs->modedb_len + num + svd_n) * - sizeof(struct fb_videomode), GFP_KERNEL); - - if (!m) - return; - - memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode)); - - for (i = specs->modedb_len; i < specs->modedb_len + num; i++) { - get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]); - if (i == specs->modedb_len) - m[i].flag |= FB_MODE_IS_FIRST; - pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh); - } - - for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) { - int idx = svd[i - specs->modedb_len - num]; - if (!idx || idx > 63) { - pr_warning("Reserved SVD code %d\n", idx); - } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) { - pr_warning("Unimplemented SVD code %d\n", idx); - } else { - memcpy(&m[i], cea_modes + idx, sizeof(m[i])); - pr_debug("Adding SVD #%d: %ux%u@%u\n", idx, - m[i].xres, m[i].yres, m[i].refresh); - } - } - - kfree(specs->modedb); - specs->modedb = m; - specs->modedb_len = specs->modedb_len + num + svd_n; -} - -/* - * VESA Generalized Timing Formula (GTF) - */ - -#define FLYBACK 550 -#define V_FRONTPORCH 1 -#define H_OFFSET 40 -#define H_SCALEFACTOR 20 -#define H_BLANKSCALE 128 -#define H_GRADIENT 600 -#define C_VAL 30 -#define M_VAL 300 - -struct __fb_timings { - u32 dclk; - u32 hfreq; - u32 vfreq; - u32 hactive; - u32 vactive; - u32 hblank; - u32 vblank; - u32 htotal; - u32 vtotal; -}; - -/** - * fb_get_vblank - get vertical blank time - * @hfreq: horizontal freq - * - * DESCRIPTION: - * vblank = right_margin + vsync_len + left_margin - * - * given: right_margin = 1 (V_FRONTPORCH) - * vsync_len = 3 - * flyback = 550 - * - * flyback * hfreq - * left_margin = --------------- - vsync_len - * 1000000 - */ -static u32 fb_get_vblank(u32 hfreq) -{ - u32 vblank; - - vblank = (hfreq * FLYBACK)/1000; - vblank = (vblank + 500)/1000; - return (vblank + V_FRONTPORCH); -} - -/** - * fb_get_hblank_by_freq - get horizontal blank time given hfreq - * @hfreq: horizontal freq - * @xres: horizontal resolution in pixels - * - * DESCRIPTION: - * - * xres * duty_cycle - * hblank = ------------------ - * 100 - duty_cycle - * - * duty cycle = percent of htotal assigned to inactive display - * duty cycle = C - (M/Hfreq) - * - * where: C = ((offset - scale factor) * blank_scale) - * -------------------------------------- + scale factor - * 256 - * M = blank_scale * gradient - * - */ -static u32 fb_get_hblank_by_hfreq(u32 hfreq, u32 xres) -{ - u32 c_val, m_val, duty_cycle, hblank; - - c_val = (((H_OFFSET - H_SCALEFACTOR) * H_BLANKSCALE)/256 + - H_SCALEFACTOR) * 1000; - m_val = (H_BLANKSCALE * H_GRADIENT)/256; - m_val = (m_val * 1000000)/hfreq; - duty_cycle = c_val - m_val; - hblank = (xres * duty_cycle)/(100000 - duty_cycle); - return (hblank); -} - -/** - * fb_get_hblank_by_dclk - get horizontal blank time given pixelclock - * @dclk: pixelclock in Hz - * @xres: horizontal resolution in pixels - * - * DESCRIPTION: - * - * xres * duty_cycle - * hblank = ------------------ - * 100 - duty_cycle - * - * duty cycle = percent of htotal assigned to inactive display - * duty cycle = C - (M * h_period) - * - * where: h_period = SQRT(100 - C + (0.4 * xres * M)/dclk) + C - 100 - * ----------------------------------------------- - * 2 * M - * M = 300; - * C = 30; - - */ -static u32 fb_get_hblank_by_dclk(u32 dclk, u32 xres) -{ - u32 duty_cycle, h_period, hblank; - - dclk /= 1000; - h_period = 100 - C_VAL; - h_period *= h_period; - h_period += (M_VAL * xres * 2 * 1000)/(5 * dclk); - h_period *= 10000; - - h_period = int_sqrt(h_period); - h_period -= (100 - C_VAL) * 100; - h_period *= 1000; - h_period /= 2 * M_VAL; - - duty_cycle = C_VAL * 1000 - (M_VAL * h_period)/100; - hblank = (xres * duty_cycle)/(100000 - duty_cycle) + 8; - hblank &= ~15; - return (hblank); -} - -/** - * fb_get_hfreq - estimate hsync - * @vfreq: vertical refresh rate - * @yres: vertical resolution - * - * DESCRIPTION: - * - * (yres + front_port) * vfreq * 1000000 - * hfreq = ------------------------------------- - * (1000000 - (vfreq * FLYBACK) - * - */ - -static u32 fb_get_hfreq(u32 vfreq, u32 yres) -{ - u32 divisor, hfreq; - - divisor = (1000000 - (vfreq * FLYBACK))/1000; - hfreq = (yres + V_FRONTPORCH) * vfreq * 1000; - return (hfreq/divisor); -} - -static void fb_timings_vfreq(struct __fb_timings *timings) -{ - timings->hfreq = fb_get_hfreq(timings->vfreq, timings->vactive); - timings->vblank = fb_get_vblank(timings->hfreq); - timings->vtotal = timings->vactive + timings->vblank; - timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, - timings->hactive); - timings->htotal = timings->hactive + timings->hblank; - timings->dclk = timings->htotal * timings->hfreq; -} - -static void fb_timings_hfreq(struct __fb_timings *timings) -{ - timings->vblank = fb_get_vblank(timings->hfreq); - timings->vtotal = timings->vactive + timings->vblank; - timings->vfreq = timings->hfreq/timings->vtotal; - timings->hblank = fb_get_hblank_by_hfreq(timings->hfreq, - timings->hactive); - timings->htotal = timings->hactive + timings->hblank; - timings->dclk = timings->htotal * timings->hfreq; -} - -static void fb_timings_dclk(struct __fb_timings *timings) -{ - timings->hblank = fb_get_hblank_by_dclk(timings->dclk, - timings->hactive); - timings->htotal = timings->hactive + timings->hblank; - timings->hfreq = timings->dclk/timings->htotal; - timings->vblank = fb_get_vblank(timings->hfreq); - timings->vtotal = timings->vactive + timings->vblank; - timings->vfreq = timings->hfreq/timings->vtotal; -} - -/* - * fb_get_mode - calculates video mode using VESA GTF - * @flags: if: 0 - maximize vertical refresh rate - * 1 - vrefresh-driven calculation; - * 2 - hscan-driven calculation; - * 3 - pixelclock-driven calculation; - * @val: depending on @flags, ignored, vrefresh, hsync or pixelclock - * @var: pointer to fb_var_screeninfo - * @info: pointer to fb_info - * - * DESCRIPTION: - * Calculates video mode based on monitor specs using VESA GTF. - * The GTF is best for VESA GTF compliant monitors but is - * specifically formulated to work for older monitors as well. - * - * If @flag==0, the function will attempt to maximize the - * refresh rate. Otherwise, it will calculate timings based on - * the flag and accompanying value. - * - * If FB_IGNOREMON bit is set in @flags, monitor specs will be - * ignored and @var will be filled with the calculated timings. - * - * All calculations are based on the VESA GTF Spreadsheet - * available at VESA's public ftp (http://www.vesa.org). - * - * NOTES: - * The timings generated by the GTF will be different from VESA - * DMT. It might be a good idea to keep a table of standard - * VESA modes as well. The GTF may also not work for some displays, - * such as, and especially, analog TV. - * - * REQUIRES: - * A valid info->monspecs, otherwise 'safe numbers' will be used. - */ -int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info) -{ - struct __fb_timings *timings; - u32 interlace = 1, dscan = 1; - u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0; - - - timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL); - - if (!timings) - return -ENOMEM; - - /* - * If monspecs are invalid, use values that are enough - * for 640x480@60 - */ - if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax || - !info->monspecs.dclkmax || - info->monspecs.hfmax < info->monspecs.hfmin || - info->monspecs.vfmax < info->monspecs.vfmin || - info->monspecs.dclkmax < info->monspecs.dclkmin) { - hfmin = 29000; hfmax = 30000; - vfmin = 60; vfmax = 60; - dclkmin = 0; dclkmax = 25000000; - } else { - hfmin = info->monspecs.hfmin; - hfmax = info->monspecs.hfmax; - vfmin = info->monspecs.vfmin; - vfmax = info->monspecs.vfmax; - dclkmin = info->monspecs.dclkmin; - dclkmax = info->monspecs.dclkmax; - } - - timings->hactive = var->xres; - timings->vactive = var->yres; - if (var->vmode & FB_VMODE_INTERLACED) { - timings->vactive /= 2; - interlace = 2; - } - if (var->vmode & FB_VMODE_DOUBLE) { - timings->vactive *= 2; - dscan = 2; - } - - switch (flags & ~FB_IGNOREMON) { - case FB_MAXTIMINGS: /* maximize refresh rate */ - timings->hfreq = hfmax; - fb_timings_hfreq(timings); - if (timings->vfreq > vfmax) { - timings->vfreq = vfmax; - fb_timings_vfreq(timings); - } - if (timings->dclk > dclkmax) { - timings->dclk = dclkmax; - fb_timings_dclk(timings); - } - break; - case FB_VSYNCTIMINGS: /* vrefresh driven */ - timings->vfreq = val; - fb_timings_vfreq(timings); - break; - case FB_HSYNCTIMINGS: /* hsync driven */ - timings->hfreq = val; - fb_timings_hfreq(timings); - break; - case FB_DCLKTIMINGS: /* pixelclock driven */ - timings->dclk = PICOS2KHZ(val) * 1000; - fb_timings_dclk(timings); - break; - default: - err = -EINVAL; - - } - - if (err || (!(flags & FB_IGNOREMON) && - (timings->vfreq < vfmin || timings->vfreq > vfmax || - timings->hfreq < hfmin || timings->hfreq > hfmax || - timings->dclk < dclkmin || timings->dclk > dclkmax))) { - err = -EINVAL; - } else { - var->pixclock = KHZ2PICOS(timings->dclk/1000); - var->hsync_len = (timings->htotal * 8)/100; - var->right_margin = (timings->hblank/2) - var->hsync_len; - var->left_margin = timings->hblank - var->right_margin - - var->hsync_len; - var->vsync_len = (3 * interlace)/dscan; - var->lower_margin = (1 * interlace)/dscan; - var->upper_margin = (timings->vblank * interlace)/dscan - - (var->vsync_len + var->lower_margin); - } - - kfree(timings); - return err; -} -#else -int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var) -{ - return 1; -} -void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) -{ - specs = NULL; -} -void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) -{ -} -void fb_destroy_modedb(struct fb_videomode *modedb) -{ -} -int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, - struct fb_info *info) -{ - return -EINVAL; -} -#endif /* CONFIG_FB_MODE_HELPERS */ - -/* - * fb_validate_mode - validates var against monitor capabilities - * @var: pointer to fb_var_screeninfo - * @info: pointer to fb_info - * - * DESCRIPTION: - * Validates video mode against monitor capabilities specified in - * info->monspecs. - * - * REQUIRES: - * A valid info->monspecs. - */ -int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info) -{ - u32 hfreq, vfreq, htotal, vtotal, pixclock; - u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax; - - /* - * If monspecs are invalid, use values that are enough - * for 640x480@60 - */ - if (!info->monspecs.hfmax || !info->monspecs.vfmax || - !info->monspecs.dclkmax || - info->monspecs.hfmax < info->monspecs.hfmin || - info->monspecs.vfmax < info->monspecs.vfmin || - info->monspecs.dclkmax < info->monspecs.dclkmin) { - hfmin = 29000; hfmax = 30000; - vfmin = 60; vfmax = 60; - dclkmin = 0; dclkmax = 25000000; - } else { - hfmin = info->monspecs.hfmin; - hfmax = info->monspecs.hfmax; - vfmin = info->monspecs.vfmin; - vfmax = info->monspecs.vfmax; - dclkmin = info->monspecs.dclkmin; - dclkmax = info->monspecs.dclkmax; - } - - if (!var->pixclock) - return -EINVAL; - pixclock = PICOS2KHZ(var->pixclock) * 1000; - - htotal = var->xres + var->right_margin + var->hsync_len + - var->left_margin; - vtotal = var->yres + var->lower_margin + var->vsync_len + - var->upper_margin; - - if (var->vmode & FB_VMODE_INTERLACED) - vtotal /= 2; - if (var->vmode & FB_VMODE_DOUBLE) - vtotal *= 2; - - hfreq = pixclock/htotal; - hfreq = (hfreq + 500) / 1000 * 1000; - - vfreq = hfreq/vtotal; - - return (vfreq < vfmin || vfreq > vfmax || - hfreq < hfmin || hfreq > hfmax || - pixclock < dclkmin || pixclock > dclkmax) ? - -EINVAL : 0; -} - -#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86) - -/* - * We need to ensure that the EDID block is only returned for - * the primary graphics adapter. - */ - -const unsigned char *fb_firmware_edid(struct device *device) -{ - struct pci_dev *dev = NULL; - struct resource *res = NULL; - unsigned char *edid = NULL; - - if (device) - dev = to_pci_dev(device); - - if (dev) - res = &dev->resource[PCI_ROM_RESOURCE]; - - if (res && res->flags & IORESOURCE_ROM_SHADOW) - edid = edid_info.dummy; - - return edid; -} -#else -const unsigned char *fb_firmware_edid(struct device *device) -{ - return NULL; -} -#endif -EXPORT_SYMBOL(fb_firmware_edid); - -EXPORT_SYMBOL(fb_parse_edid); -EXPORT_SYMBOL(fb_edid_to_monspecs); -EXPORT_SYMBOL(fb_edid_add_monspecs); -EXPORT_SYMBOL(fb_get_mode); -EXPORT_SYMBOL(fb_validate_mode); -EXPORT_SYMBOL(fb_destroy_modedb); |