📄 radeon_monitor.c
字号:
#include "radeonfb.h"#include "../edid.h"#ifdef CONFIG_PPC_OF#include <asm/prom.h>#include <asm/pci-bridge.h>#endif /* CONFIG_PPC_OF */static struct fb_var_screeninfo radeonfb_default_var = { 640, 480, 640, 480, 0, 0, 8, 0, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, 0, -1, -1, 0, 39721, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED};static char *radeon_get_mon_name(int type){ char *pret = NULL; switch (type) { case MT_NONE: pret = "no"; break; case MT_CRT: pret = "CRT"; break; case MT_DFP: pret = "DFP"; break; case MT_LCD: pret = "LCD"; break; case MT_CTV: pret = "CTV"; break; case MT_STV: pret = "STV"; break; } return pret;}#ifdef CONFIG_PPC_OF/* * Try to find monitor informations & EDID data out of the Open Firmware * device-tree. This also contains some "hacks" to work around a few machine * models with broken OF probing by hard-coding known EDIDs for some Mac * laptops internal LVDS panel. (XXX: not done yet) */static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_EDID, int hdno){ static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", "EDID2", NULL }; u8 *pedid = NULL; u8 *pmt = NULL; u8 *tmp; int i, mt; RTRACE("analyzing OF properties...\n"); pmt = (u8 *)get_property(dp, "display-type", NULL); if (!pmt) return MT_NONE; RTRACE("display-type: %s\n", pmt); /* OF says "LCD" for DFP as well, we discriminate from the caller of this * function */ if (!strcmp(pmt, "LCD") || !strcmp(pmt, "DFP")) mt = MT_DFP; else if (!strcmp(pmt, "CRT")) mt = MT_CRT; else if (strcmp(pmt, "NONE")) { printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n", pmt); return MT_NONE; } for (i = 0; propnames[i] != NULL; ++i) { pedid = (u8 *)get_property(dp, propnames[i], NULL); if (pedid != NULL) break; } /* We didn't find the EDID in the leaf node, some cards will actually * put EDID1/EDID2 in the parent, look for these (typically M6 tipb). * single-head cards have hdno == -1 and skip this step */ if (pedid == NULL && dp->parent && (hdno != -1)) pedid = get_property(dp->parent, (hdno == 0) ? "EDID1" : "EDID2", NULL); if (pedid == NULL && dp->parent && (hdno == 0)) pedid = get_property(dp->parent, "EDID", NULL); if (pedid == NULL) return mt; tmp = (u8 *)kmalloc(EDID_LENGTH, GFP_KERNEL); if (!tmp) return mt; memcpy(tmp, pedid, EDID_LENGTH); *out_EDID = tmp; return mt;}static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_no, u8 **out_EDID){ struct device_node *dp; RTRACE("radeon_probe_OF_head\n"); dp = pci_device_to_OF_node(rinfo->pdev); while (dp == NULL) return MT_NONE; if (rinfo->has_CRTC2) { char *pname; int len, second = 0; dp = dp->child; do { if (!dp) return MT_NONE; pname = (char *)get_property(dp, "name", NULL); if (!pname) return MT_NONE; len = strlen(pname); RTRACE("head: %s (letter: %c, head_no: %d)\n", pname, pname[len-1], head_no); if (pname[len-1] == 'A' && head_no == 0) { int mt = radeon_parse_montype_prop(dp, out_EDID, 0); /* Maybe check for LVDS_GEN_CNTL here ? I need to check out * what OF does when booting with lid closed */ if (mt == MT_DFP && rinfo->is_mobility) mt = MT_LCD; return mt; } else if (pname[len-1] == 'B' && head_no == 1) return radeon_parse_montype_prop(dp, out_EDID, 1); second = 1; dp = dp->sibling; } while(!second); } else { if (head_no > 0) return MT_NONE; return radeon_parse_montype_prop(dp, out_EDID, -1); } return MT_NONE;}#endif /* CONFIG_PPC_OF */static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo){ unsigned long tmp, tmp0; char stmp[30]; int i; if (!rinfo->bios_seg) return 0; if (!(tmp = BIOS_IN16(rinfo->fp_bios_start + 0x40))) { printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n"); rinfo->panel_info.pwr_delay = 200; return 0; } for(i=0; i<24; i++) stmp[i] = BIOS_IN8(tmp+i+1); stmp[24] = 0; printk("radeonfb: panel ID string: %s\n", stmp); rinfo->panel_info.xres = BIOS_IN16(tmp + 25); rinfo->panel_info.yres = BIOS_IN16(tmp + 27); printk("radeonfb: detected LVDS panel size from BIOS: %dx%d\n", rinfo->panel_info.xres, rinfo->panel_info.yres); rinfo->panel_info.pwr_delay = BIOS_IN16(tmp + 44); RTRACE("BIOS provided panel power delay: %d\n", rinfo->panel_info.pwr_delay); if (rinfo->panel_info.pwr_delay > 2000 || rinfo->panel_info.pwr_delay <= 0) rinfo->panel_info.pwr_delay = 2000; /* * Some panels only work properly with some divider combinations */ rinfo->panel_info.ref_divider = BIOS_IN16(tmp + 46); rinfo->panel_info.post_divider = BIOS_IN8(tmp + 48); rinfo->panel_info.fbk_divider = BIOS_IN16(tmp + 49); if (rinfo->panel_info.ref_divider != 0 && rinfo->panel_info.fbk_divider > 3) { rinfo->panel_info.use_bios_dividers = 1; printk(KERN_INFO "radeondb: BIOS provided dividers will be used\n"); RTRACE("ref_divider = %x\n", rinfo->panel_info.ref_divider); RTRACE("post_divider = %x\n", rinfo->panel_info.post_divider); RTRACE("fbk_divider = %x\n", rinfo->panel_info.fbk_divider); } RTRACE("Scanning BIOS table ...\n"); for(i=0; i<32; i++) { tmp0 = BIOS_IN16(tmp+64+i*2); if (tmp0 == 0) break; RTRACE(" %d x %d\n", BIOS_IN16(tmp0), BIOS_IN16(tmp0+2)); if ((BIOS_IN16(tmp0) == rinfo->panel_info.xres) && (BIOS_IN16(tmp0+2) == rinfo->panel_info.yres)) { rinfo->panel_info.hblank = (BIOS_IN16(tmp0+17) - BIOS_IN16(tmp0+19)) * 8; rinfo->panel_info.hOver_plus = ((BIOS_IN16(tmp0+21) - BIOS_IN16(tmp0+19) -1) * 8) & 0x7fff; rinfo->panel_info.hSync_width = BIOS_IN8(tmp0+23) * 8; rinfo->panel_info.vblank = BIOS_IN16(tmp0+24) - BIOS_IN16(tmp0+26); rinfo->panel_info.vOver_plus = (BIOS_IN16(tmp0+28) & 0x7ff) - BIOS_IN16(tmp0+26); rinfo->panel_info.vSync_width = (BIOS_IN16(tmp0+28) & 0xf800) >> 11; rinfo->panel_info.clock = BIOS_IN16(tmp0+9); /* Assume high active syncs for now until ATI tells me more... maybe we * can probe register values here ? */ rinfo->panel_info.hAct_high = 1; rinfo->panel_info.vAct_high = 1; /* Mark panel infos valid */ rinfo->panel_info.valid = 1; RTRACE("Found panel in BIOS table:\n"); RTRACE(" hblank: %d\n", rinfo->panel_info.hblank); RTRACE(" hOver_plus: %d\n", rinfo->panel_info.hOver_plus); RTRACE(" hSync_width: %d\n", rinfo->panel_info.hSync_width); RTRACE(" vblank: %d\n", rinfo->panel_info.vblank); RTRACE(" vOver_plus: %d\n", rinfo->panel_info.vOver_plus); RTRACE(" vSync_width: %d\n", rinfo->panel_info.vSync_width); RTRACE(" clock: %d\n", rinfo->panel_info.clock); return 1; } } RTRACE("Didn't find panel in BIOS table !\n"); return 0;}/* Try to extract the connector informations from the BIOS. This * doesn't quite work yet, but it's output is still useful for * debugging */static void __devinit radeon_parse_connector_info(struct radeonfb_info *rinfo){ int offset, chips, connectors, tmp, i, conn, type; static char* __conn_type_table[16] = { "NONE", "Proprietary", "CRT", "DVI-I", "DVI-D", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown" }; if (!rinfo->bios_seg) return; offset = BIOS_IN16(rinfo->fp_bios_start + 0x50); if (offset == 0) { printk(KERN_WARNING "radeonfb: No connector info table detected\n"); return; } /* Don't do much more at this point but displaying the data if * DEBUG is enabled */ chips = BIOS_IN8(offset++) >> 4; RTRACE("%d chips in connector info\n", chips); for (i = 0; i < chips; i++) { tmp = BIOS_IN8(offset++); connectors = tmp & 0x0f; RTRACE(" - chip %d has %d connectors\n", tmp >> 4, connectors); for (conn = 0; ; conn++) { tmp = BIOS_IN16(offset); if (tmp == 0) break; offset += 2; type = (tmp >> 12) & 0x0f; RTRACE(" * connector %d of type %d (%s) : %04x\n", conn, type, __conn_type_table[type], tmp); } }}/* * Probe physical connection of a CRT. This code comes from XFree * as well and currently is only implemented for the CRT DAC, the * code for the TVDAC is commented out in XFree as "non working" */static int __devinit radeon_crt_is_connected(struct radeonfb_info *rinfo, int is_crt_dac){ int connected = 0; /* the monitor either wasn't connected or it is a non-DDC CRT. * try to probe it */ if (is_crt_dac) { unsigned long ulOrigVCLK_ECP_CNTL; unsigned long ulOrigDAC_CNTL; unsigned long ulOrigDAC_EXT_CNTL; unsigned long ulOrigCRTC_EXT_CNTL; unsigned long ulData; unsigned long ulMask; ulOrigVCLK_ECP_CNTL = INPLL(VCLK_ECP_CNTL); ulData = ulOrigVCLK_ECP_CNTL; ulData &= ~(PIXCLK_ALWAYS_ONb | PIXCLK_DAC_ALWAYS_ONb); ulMask = ~(PIXCLK_ALWAYS_ONb | PIXCLK_DAC_ALWAYS_ONb); OUTPLLP(VCLK_ECP_CNTL, ulData, ulMask); ulOrigCRTC_EXT_CNTL = INREG(CRTC_EXT_CNTL); ulData = ulOrigCRTC_EXT_CNTL; ulData |= CRTC_CRT_ON; OUTREG(CRTC_EXT_CNTL, ulData); ulOrigDAC_EXT_CNTL = INREG(DAC_EXT_CNTL); ulData = ulOrigDAC_EXT_CNTL; ulData &= ~DAC_FORCE_DATA_MASK; ulData |= (DAC_FORCE_BLANK_OFF_EN |DAC_FORCE_DATA_EN |DAC_FORCE_DATA_SEL_MASK); if ((rinfo->family == CHIP_FAMILY_RV250) || (rinfo->family == CHIP_FAMILY_RV280)) ulData |= (0x01b6 << DAC_FORCE_DATA_SHIFT); else ulData |= (0x01ac << DAC_FORCE_DATA_SHIFT); OUTREG(DAC_EXT_CNTL, ulData); ulOrigDAC_CNTL = INREG(DAC_CNTL); ulData = ulOrigDAC_CNTL; ulData |= DAC_CMP_EN; ulData &= ~(DAC_RANGE_CNTL_MASK | DAC_PDWN); ulData |= 0x2; OUTREG(DAC_CNTL, ulData); mdelay(1); ulData = INREG(DAC_CNTL); connected = (DAC_CMP_OUTPUT & ulData) ? 1 : 0; ulData = ulOrigVCLK_ECP_CNTL; ulMask = 0xFFFFFFFFL; OUTPLLP(VCLK_ECP_CNTL, ulData, ulMask); OUTREG(DAC_CNTL, ulOrigDAC_CNTL ); OUTREG(DAC_EXT_CNTL, ulOrigDAC_EXT_CNTL ); OUTREG(CRTC_EXT_CNTL, ulOrigCRTC_EXT_CNTL); } return connected ? MT_CRT : MT_NONE;}/* * Parse the "monitor_layout" string if any. This code is mostly * copied from XFree's radeon driver */static int __devinit radeon_parse_monitor_layout(struct radeonfb_info *rinfo, const char *monitor_layout){ char s1[5], s2[5]; int i = 0, second = 0; const char *s; if (!monitor_layout) return 0; s = monitor_layout; do { switch(*s) { case ',': s1[i] = '\0'; i = 0; second = 1; break; case ' ': case '\0': break; default: if (i > 4) break; if (second) s2[i] = *s; else s1[i] = *s; i++; } } while (*s++); if (second) s2[i] = 0; else { s1[i] = 0; s2[0] = 0; } if (strcmp(s1, "CRT") == 0) rinfo->mon1_type = MT_CRT; else if (strcmp(s1, "TMDS") == 0) rinfo->mon1_type = MT_DFP; else if (strcmp(s1, "LVDS") == 0) rinfo->mon1_type = MT_LCD; if (strcmp(s2, "CRT") == 0) rinfo->mon2_type = MT_CRT; else if (strcmp(s2, "TMDS") == 0) rinfo->mon2_type = MT_DFP; else if (strcmp(s2, "LVDS") == 0) rinfo->mon2_type = MT_LCD; return 1;}/* * Probe display on both primary and secondary card's connector (if any) * by various available techniques (i2c, OF device tree, BIOS, ...) and * try to retreive EDID. The algorithm here comes from XFree's radeon * driver */void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, const char *monitor_layout, int ignore_edid){#ifdef CONFIG_FB_RADEON_I2C int ddc_crt2_used = 0; #endif int tmp, i; radeon_parse_connector_info(rinfo); if (radeon_parse_monitor_layout(rinfo, monitor_layout)) { /* * If user specified a monitor_layout option, use it instead * of auto-detecting. Maybe we should only use this argument * on the first radeon card probed or provide a way to specify * a layout for each card ? */ RTRACE("Using specified monitor layout: %s", monitor_layout);#ifdef CONFIG_FB_RADEON_I2C if (!ignore_edid) { if (rinfo->mon1_type != MT_NONE) if (!radeon_probe_i2c_connector(rinfo, ddc_dvi, &rinfo->mon1_EDID)) { radeon_probe_i2c_connector(rinfo, ddc_crt2, &rinfo->mon1_EDID); ddc_crt2_used = 1; } if (rinfo->mon2_type != MT_NONE) if (!radeon_probe_i2c_connector(rinfo, ddc_vga, &rinfo->mon2_EDID) && !ddc_crt2_used) radeon_probe_i2c_connector(rinfo, ddc_crt2, &rinfo->mon2_EDID); }#endif /* CONFIG_FB_RADEON_I2C */ if (rinfo->mon1_type == MT_NONE) { if (rinfo->mon2_type != MT_NONE) { rinfo->mon1_type = rinfo->mon2_type; rinfo->mon1_EDID = rinfo->mon2_EDID; } else { rinfo->mon1_type = MT_CRT; printk(KERN_INFO "radeonfb: No valid monitor, assuming CRT on first port\n"); } rinfo->mon2_type = MT_NONE; rinfo->mon2_EDID = NULL; } } else { /* * Auto-detecting display type (well... trying to ...) */ RTRACE("Starting monitor auto detection...\n");#if DEBUG && defined(CONFIG_FB_RADEON_I2C) { u8 *EDIDs[4] = { NULL, NULL, NULL, NULL }; int mon_types[4] = {MT_NONE, MT_NONE, MT_NONE, MT_NONE}; int i; for (i = 0; i < 4; i++) mon_types[i] = radeon_probe_i2c_connector(rinfo, i+1, &EDIDs[i]); }#endif /* DEBUG */ /* * Old single head cards */ if (!rinfo->has_CRTC2) {#ifdef CONFIG_PPC_OF if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, &rinfo->mon1_EDID);#endif /* CONFIG_PPC_OF */#ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi, &rinfo->mon1_EDID); if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_vga, &rinfo->mon1_EDID);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -