fbmon.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,261 行 · 第 1/3 页

C
1,261
字号
/* * 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/tty.h>#include <linux/fb.h>#include <linux/module.h>#ifdef CONFIG_PPC_OF#include <linux/pci.h>#include <asm/prom.h>#include <asm/pci-bridge.h>#endif#include <video/edid.h>#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#ifdef CONFIG_FB_MODE_HELPERSstruct broken_edid {	u8  manufacturer[4];	u32 model;	u32 fix;};static struct broken_edid brokendb[] = {	/* DEC FR-PCXAV-YZ */	{ .manufacturer = "DEC",	  .model        = 0x073a,	  .fix          = FBMON_FIX_HEADER,	},};const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0x00};const unsigned char edid_v1_descriptor_flag[] = { 0x00, 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 void fix_broken_edid(unsigned char *edid){	unsigned char *block = edid + ID_MANUFACTURER_NAME, manufacturer[4];	u32 model, i;	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) {			switch (brokendb[i].fix) {			case FBMON_FIX_HEADER:				printk("fbmon: The EDID header of "				       "Manufacturer: %s Model: 0x%x is "				       "known to be broken,\n"				       "fbmon: trying a header "				       "reconstruct\n", manufacturer, model);				memcpy(edid, edid_v1_header, 8);				break;			}		}	}}static int edid_checksum(unsigned char *edid){	unsigned char i, csum = 0, all_null = 0;	for (i = 0; i < EDID_LENGTH; i++) {		csum += edid[i];		all_null |= edid[i];	}	if (csum == 0x00 && all_null) {		/* checksum passed, everything's good */		return 1;	}	fix_broken_edid(edid);	csum = all_null = 0;	for (i = 0; i < EDID_LENGTH; i++) {		csum += edid[i];		all_null |= edid[i];	}	if (csum != 0x00 || !all_null) {		printk("EDID checksum failed, aborting\n");		return 0;	}	return 1;}static int edid_check_header(unsigned char *edid){	int i, fix = 0;	for (i = 0; i < 8; i++) {		if (edid[i] != edid_v1_header[i])			fix = 1;	}	if (!fix)		return 1;	fix_broken_edid(edid);	for (i = 0; i < 8; i++) {		if (edid[i] != edid_v1_header[i])			return 0;	}	return 1;}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 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 void calc_mode_timings(int xres, int yres, int refresh, struct fb_videomode *mode){	struct fb_var_screeninfo var;	struct fb_info info;		var.xres = xres;	var.yres = yres;	fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 		    refresh, &var, &info);	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;}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 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:		yres = xres;		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);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?