All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
@ 2006-01-03 20:44 Stuffed Crust
  2006-01-03 23:29 ` Petr Vandrovec
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Stuffed Crust @ 2006-01-03 20:44 UTC (permalink / raw)
  To: linux-fbdev-devel


[-- Attachment #1.1: Type: text/plain, Size: 3128 bytes --]

Signed-off-by:  Solomon Peachy <pizza@shaftnet.org>

Newer Radeon graphics cards use a different table layout in their 
BIOSes, known as 'ATOM'.  

I've attached a patch (against 2.6.15) which implements the PLL and LVDS
parsing out of the ATOM BIOSes, using the current X.org sources as a
reference.  Notably absent is the code which detects the connectors and
TDMS information, but it's of lesser importance at the moment.

This code still has quirks, but I'm not sure if those are due to bugs in
my patch or incomplete/buggy support for the RV410.  So I'd appreciate
it if people could try this out, especially if they have a newer ATOM
BIOS in their radeon card, and let me know what happens. 

Finally, this patch also has PCI IDs added for my Radeon Mobility X700 
(M26) adapter. 

Below is the dump of my system with this driver:

radeonfb_pci_register BEGIN
ACPI: PCI Interrupt 0000:01:00.0[A] -> GSI 18 (level, low) -> IRQ 217
radeonfb (0000:01:00.0): Found 131072k of DDR 128 bits wide videoram
radeonfb (0000:01:00.0): mapped 16384k videoram
ATOM BIOS detected
radeonfb: Retreived PLL infos from BIOS
radeonfb: Reference=27.00 MHz (RefDiv=12) Memory=330.00 Mhz, System=358.00 MHz
radeonfb: PLL min 20000 max 50000
XXXX Write ATOM BIOS connector parse
Starting monitor auto detection...
radeonfb: I2C (port 1) ... not found
radeonfb: I2C (port 2) ... not found
radeonfb: I2C (port 3) ... not found
radeonfb: I2C (port 4) ... not found
radeonfb: I2C (port 2) ... not found
radeonfb: I2C (port 4) ... not found
Non-DDC laptop panel detected
radeonfb: I2C (port 3) ... not found
radeonfb: I2C (port 4) ... not found
radeonfb: Monitor 1 type LCD found
radeonfb: Monitor 2 type CRT found
radeonfb: detected LVDS panel size from BIOS: 1680x1050
BIOS provided panel power delay: 15369
Setting up default mode based on panel info
radeonfb: Dynamic Clock Power Management enabled
hStart = 1728, hEnd = 1760, hTotal = 1840
vStart = 1053, vEnd = 1059, vTotal = 1080
h_total_disp = 0xd100e5           hsync_strt_wid = 0x406ba
v_total_disp = 0x4190437          vsync_strt_wid = 0x6041c
pixclock = 8387
freq = 11923
freq = 11923, PLL min = 20000, PLL max = 50000
ref_div = 12, ref_clk = 2700, output_freq = 23846
ref_div = 12, ref_clk = 2700, output_freq = 23846
post div = 0x1
fb_div = 0x6a
ppll_div_3 = 0x1006a
Console: switching to colour frame buffer device 210x65
radeonfb (0000:01:00.0): ATI Radeon VS 
radeonfb_pci_register END

And at this point, I have yummy hires fbdev goodness.  

I also have a question about the panel_info.pwr_delay field --- Is this
is supposed to be the time we wait for the LCD to settle after a power
state change?  X.org seems to use 200ms everywhere, but the default in
radeonfb seems to be 2000ms, and my panel is reporting a whopping
15369ms.  I'm also forced to use the default pll.ref_div of 12, and I've
tweaked the code to make that default universal if the BIOS information
is clearly bogus.

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL 					 
Quidquid latine dictum sit, altum viditur.

[-- Attachment #1.2: radeon_atom_bios.diff --]
[-- Type: text/plain, Size: 15832 bytes --]

diff -ur /tmp/linux-2.6.15/drivers/video/aty/ati_ids.h ./aty/ati_ids.h
--- /tmp/linux-2.6.15/drivers/video/aty/ati_ids.h	2006-01-03 14:30:15.000000000 -0500
+++ ./aty/ati_ids.h	2006-01-03 15:12:01.000000000 -0500
@@ -185,6 +185,8 @@
 #define PCI_CHIP_R423_UQ                0x5551
 #define PCI_CHIP_R423_UR                0x5552
 #define PCI_CHIP_R423_UT                0x5554
+#define PCI_CHIP_RV410_5652             0x5652
+#define PCI_CHIP_RV410_VS               0x5653
 #define PCI_CHIP_MACH64VT		0x5654
 #define PCI_CHIP_MACH64VU		0x5655
 #define PCI_CHIP_MACH64VV		0x5656
diff -ur /tmp/linux-2.6.15/drivers/video/aty/radeon_base.c ./aty/radeon_base.c
--- /tmp/linux-2.6.15/drivers/video/aty/radeon_base.c	2006-01-03 14:30:15.000000000 -0500
+++ ./aty/radeon_base.c	2006-01-03 15:13:48.000000000 -0500
@@ -214,6 +214,8 @@
 	CHIP_DEF(PCI_CHIP_R420_JL,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JM,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JN,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_5652,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VS,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
 	CHIP_DEF(PCI_CHIP_R420_JP,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UH,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UI,	R420,	CHIP_HAS_CRTC2),
@@ -342,7 +344,7 @@
 	 * to phase out Open Firmware images.
 	 *
 	 * Currently, we only look at the first PCI data, we could iteratre and deal with
-	 * them all, and we should use fb_bios_start relative to start of image and not
+	 * them all, and we should use fp_bios_start relative to start of image and not
 	 * relative start of ROM, but so far, I never found a dual-image ATI card
 	 *
 	 * typedef struct {
@@ -655,8 +657,6 @@
 		rinfo->pll.ref_clk = 2700;
 		break;
 	}
-	rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
-
 
 #ifdef CONFIG_PPC_OF
 	/*
@@ -673,14 +673,31 @@
 	 * and if yes, retreive them
 	 */
 	if (!force_measure_pll && rinfo->bios_seg) {
-		u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+		u16 pll_info_block;
+		if (rinfo->is_atom_bios) {
+			pll_info_block = BIOS_IN16(rinfo->atom_data_start + 12);
+
+			rinfo->pll.sclk = BIOS_IN32(pll_info_block + 8);
+			rinfo->pll.mclk = BIOS_IN32(pll_info_block + 12);
+			rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 82);
+			rinfo->pll.ref_div = 0; /* Have to get it elsewhere */
+			rinfo->pll.ppll_min = BIOS_IN16(pll_info_block + 78);
+			rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 32);
+
+			if (rinfo->pll.sclk == 0)  rinfo->pll.sclk = 20000;
+			if (rinfo->pll.mclk == 0)  rinfo->pll.mclk = 20000;
 
-		rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
-		rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
-		rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
-		rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
-		rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
-		rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
+		} else {
+			pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+			
+			rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
+			rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
+			rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
+			rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
+			rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
+			rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
+			
+		}
 
 		printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
 		goto found;
@@ -701,6 +718,22 @@
        	printk(KERN_INFO "radeonfb: Used default PLL infos\n");
 
 found:
+
+	/* Check and fix-up the PLL divisor if necessary */
+	if (rinfo->pll.ref_div < 2) {
+		int tmp = INPLL(PPLL_REF_DIV);
+		if (rinfo->family == CHIP_FAMILY_RS300) {
+			rinfo->pll.ref_div = (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
+		} else {
+			rinfo->pll.ref_div = tmp & PPLL_REF_DIV_MASK;
+		}
+		
+		/* Sane default */
+		if (rinfo->pll.ref_div < 2) {		
+			rinfo->pll.ref_div = 12;
+		}
+	}		
+
 	/*
 	 * Some methods fail to retreive SCLK and MCLK values, we apply default
 	 * settings in this case (200Mhz). If that really happne often, we could
@@ -2412,6 +2445,7 @@
 	 * We probably need to make sure this is the primary display,
 	 * but that is difficult without some arch support.
 	 */
+
 #ifdef CONFIG_X86
 	if (rinfo->bios_seg == NULL)
 		radeon_find_mem_vbios(rinfo);
@@ -2423,6 +2457,28 @@
 	if (rinfo->bios_seg == NULL && rinfo->is_mobility)
 		radeon_map_ROM(rinfo, pdev);
 
+	/* Check BIOS Type */
+	{
+	  int tmp = rinfo->fp_bios_start + 4;
+	  if ((BIOS_IN8(tmp) == 'A' &&
+	       BIOS_IN8(tmp+1) == 'T' &&
+	       BIOS_IN8(tmp+2) == 'O' &&
+	       BIOS_IN8(tmp+3) == 'M') ||
+	      (BIOS_IN8(tmp) == 'M' &&
+	       BIOS_IN8(tmp+1) == 'O' &&
+	       BIOS_IN8(tmp+2) == 'T' &&
+	       BIOS_IN8(tmp+3) == 'A')) {
+		  rinfo->is_atom_bios = 1;
+	  } else {
+		  rinfo->is_atom_bios = 0;
+	  }
+	}
+	
+	if (rinfo->is_atom_bios) {
+		rinfo->atom_data_start = BIOS_IN16(rinfo->fp_bios_start + 32);
+		printk("ATOM BIOS detected\n");
+	}
+
 	/* Get informations about the board's PLL */
 	radeon_get_pllinfo(rinfo);
 
diff -ur /tmp/linux-2.6.15/drivers/video/aty/radeon_monitor.c ./aty/radeon_monitor.c
--- /tmp/linux-2.6.15/drivers/video/aty/radeon_monitor.c	2005-10-27 20:02:08.000000000 -0400
+++ ./aty/radeon_monitor.c	2006-01-03 12:15:55.000000000 -0500
@@ -169,76 +169,117 @@
 	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;
+	if (rinfo->is_atom_bios) {
+		tmp = BIOS_IN16(rinfo->atom_data_start + 16);
+		if (!tmp) {
+			printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
+			rinfo->panel_info.pwr_delay = 200;
+			return 0;
+		}
 
-	/*
-	 * 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);
+		rinfo->panel_info.xres = BIOS_IN16(tmp+6);
+		rinfo->panel_info.yres = BIOS_IN16(tmp+10);
+		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+40);
+		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;
+		
+		/* No special divider combinations? */
+		       
+		rinfo->panel_info.hblank = BIOS_IN16(tmp+8);
+		rinfo->panel_info.hOver_plus = BIOS_IN16(tmp+14);
+		rinfo->panel_info.hSync_width = BIOS_IN16(tmp+16);
+		rinfo->panel_info.vblank = BIOS_IN16(tmp+12);
+		rinfo->panel_info.vOver_plus = BIOS_IN16(tmp+18);
+		rinfo->panel_info.vSync_width = BIOS_IN16(tmp+20);
+		rinfo->panel_info.clock = BIOS_IN16(tmp+4);
+
+		/* 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;
+
+		return 1;
+	} else {
+		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;
 				
-			return 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;
@@ -261,29 +302,34 @@
 	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);
+	if (rinfo->is_atom_bios) {
+		printk(KERN_WARNING "XXXX Write ATOM BIOS connector parse\n");
+	} else {
+	  
+		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);
+			}
 		}
 	}
 }
@@ -519,6 +565,8 @@
 		/*
 		 * Check for cards with reversed DACs or TMDS controllers using BIOS
 		 */
+		// XXXX What about atom_bios types?
+
 		if (rinfo->bios_seg &&
 		    (tmp = BIOS_IN16(rinfo->fp_bios_start + 0x50))) {
 			for (i = 1; i < 4; i++) {
diff -ur /tmp/linux-2.6.15/drivers/video/aty/radeonfb.h ./aty/radeonfb.h
--- /tmp/linux-2.6.15/drivers/video/aty/radeonfb.h	2006-01-03 14:30:15.000000000 -0500
+++ ./aty/radeonfb.h	2006-01-03 11:30:51.000000000 -0500
@@ -298,6 +298,9 @@
 	void __iomem		*bios_seg;
 	int			fp_bios_start;
 
+	int                     is_atom_bios;
+	int                     atom_data_start;
+
 	u32			pseudo_palette[17];
 	struct { u8 red, green, blue, pad; }
 				palette[256];

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
  2006-01-03 20:44 [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support Stuffed Crust
@ 2006-01-03 23:29 ` Petr Vandrovec
  2006-01-05  9:59 ` Benjamin Herrenschmidt
  2006-02-14 21:28 ` Benjamin Herrenschmidt
  2 siblings, 0 replies; 16+ messages in thread
From: Petr Vandrovec @ 2006-01-03 23:29 UTC (permalink / raw)
  To: linux-fbdev-devel

Stuffed Crust wrote:
> Signed-off-by:  Solomon Peachy <pizza@shaftnet.org>
> 
> Newer Radeon graphics cards use a different table layout in their 
> BIOSes, known as 'ATOM'.  
> 
> I've attached a patch (against 2.6.15) which implements the PLL and LVDS
> parsing out of the ATOM BIOSes, using the current X.org sources as a
> reference.  Notably absent is the code which detects the connectors and
> TDMS information, but it's of lesser importance at the moment.
> 
> This code still has quirks, but I'm not sure if those are due to bugs in
> my patch or incomplete/buggy support for the RV410.  So I'd appreciate
> it if people could try this out, especially if they have a newer ATOM
> BIOS in their radeon card, and let me know what happens. 

Thanks.  It correctly detects panel size from BIOS, unfortunately it behaves 
like X.org driver (which is probably expected).  I captured video just in case 
it provides some clue to somebody 
(http://platan.vc.cvut.cz/ftp/private/radeon/video.avi).  Otherwise I'll try 
some trial & error session during weekend.

Jan  3 23:58:02 debian kernel: radeonfb_pci_register BEGIN
Jan  3 23:58:02 debian kernel: ACPI: PCI Interrupt 0000:01:00.0[A] -> GSI 18 
(level, low) -> IRQ 209
Jan  3 23:58:02 debian kernel: radeonfb (0000:01:00.0): Found 131072k of DDR 128 
bits wide videoram
Jan  3 23:58:02 debian kernel: radeonfb (0000:01:00.0): mapped 16384k videoram
Jan  3 23:58:02 debian kernel: ATOM BIOS detected
Jan  3 23:58:02 debian kernel: radeonfb: Retreived PLL infos from BIOS
Jan  3 23:58:02 debian kernel: radeonfb: Reference=27.00 MHz (RefDiv=14) 
Memory=280.00 Mhz, System=358.00 MHz
Jan  3 23:58:02 debian kernel: radeonfb: PLL min 20000 max 50000
Jan  3 23:58:02 debian kernel: XXXX Write ATOM BIOS connector parse
Jan  3 23:58:02 debian kernel: Starting monitor auto detection...
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 1) ... not found
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 2) ... not found
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 3) ... not found
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 4) ... not found
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 2) ... not found
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 4) ... not found
Jan  3 23:58:02 debian kernel: Non-DDC laptop panel detected
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 3) ... not found
Jan  3 23:58:02 debian kernel: radeonfb: I2C (port 4) ... not found
Jan  3 23:58:02 debian kernel: radeonfb: Monitor 1 type LCD found
Jan  3 23:58:02 debian kernel: radeonfb: Monitor 2 type CRT found
Jan  3 23:58:02 debian kernel: radeonfb: detected LVDS panel size from BIOS: 
1920x1200
Jan  3 23:58:02 debian kernel: BIOS provided panel power delay: 15369
Jan  3 23:58:02 debian kernel: Setting up default mode based on panel info
Jan  3 23:58:02 debian kernel: radeonfb: Dynamic Clock Power Management enabled
Jan  3 23:58:02 debian kernel: hStart = 2020, hEnd = 2052, hTotal = 2184
Jan  3 23:58:02 debian kernel: vStart = 1202, vEnd = 1208, vTotal = 1235
Jan  3 23:58:02 debian kernel: h_total_disp = 0xef0110^I   hsync_strt_wid = 0x407de
Jan  3 23:58:02 debian kernel: v_total_disp = 0x4af04d2^I   vsync_strt_wid = 0x604b1
Jan  3 23:58:02 debian kernel: pixclock = 6589
Jan  3 23:58:02 debian kernel: freq = 15176
Jan  3 23:58:02 debian kernel: freq = 15176, PLL min = 20000, PLL max = 50000
Jan  3 23:58:02 debian kernel: ref_div = 14, ref_clk = 2700, output_freq = 30352
Jan  3 23:58:02 debian kernel: ref_div = 14, ref_clk = 2700, output_freq = 30352
Jan  3 23:58:02 debian kernel: post div = 0x1
Jan  3 23:58:02 debian kernel: fb_div = 0x9d
Jan  3 23:58:02 debian kernel: ppll_div_3 = 0x1009d
Jan  3 23:58:02 debian kernel: Console: switching to colour frame buffer device 
240x75
Jan  3 23:58:02 debian kernel: radeonfb (0000:01:00.0): ATI Radeon VS
Jan  3 23:58:02 debian kernel: radeonfb_pci_register END

							Thanks,
									Petr



-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
  2006-01-03 20:44 [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support Stuffed Crust
  2006-01-03 23:29 ` Petr Vandrovec
@ 2006-01-05  9:59 ` Benjamin Herrenschmidt
  2006-01-05 20:13   ` Stuffed Crust
  2006-02-14 21:28 ` Benjamin Herrenschmidt
  2 siblings, 1 reply; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-05  9:59 UTC (permalink / raw)
  To: linux-fbdev-devel

On Tue, 2006-01-03 at 15:44 -0500, Stuffed Crust wrote:
> Signed-off-by:  Solomon Peachy <pizza@shaftnet.org>
> 
> Newer Radeon graphics cards use a different table layout in their 
> BIOSes, known as 'ATOM'.  
> 
> I've attached a patch (against 2.6.15) which implements the PLL and LVDS
> parsing out of the ATOM BIOSes, using the current X.org sources as a
> reference.  Notably absent is the code which detects the connectors and
> TDMS information, but it's of lesser importance at the moment.

Inresting. I started doing that (and updated the pre-ATOM BIOS code too)
but never finished... One thing you could do that would be useful is
move the various BIOS code to separate function and setup function
pointers that can be set based on the BIOS type instead of the endless
stream if (atom_bios) xxx else yyy. That way, it will be cleaner and
I'll be able to more easily add other variations like retreiving infos
from Open Firmware on ppc etc...

Ben.




-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
  2006-01-05  9:59 ` Benjamin Herrenschmidt
@ 2006-01-05 20:13   ` Stuffed Crust
  0 siblings, 0 replies; 16+ messages in thread
From: Stuffed Crust @ 2006-01-05 20:13 UTC (permalink / raw)
  To: linux-fbdev-devel

[-- Attachment #1: Type: text/plain, Size: 855 bytes --]

On Thu, Jan 05, 2006 at 08:59:15PM +1100, Benjamin Herrenschmidt wrote:
> Inresting. I started doing that (and updated the pre-ATOM BIOS code too)
> but never finished... One thing you could do that would be useful is
> move the various BIOS code to separate function and setup function
> pointers that can be set based on the BIOS type instead of the endless
> stream if (atom_bios) xxx else yyy. That way, it will be cleaner and
> I'll be able to more easily add other variations like retreiving infos
> from Open Firmware on ppc etc...

Will do.  I'll probably get to it during the weekend.  

Off the top of my head there's LVDS info, PLL, Connector, and TMDS 
detection/mangling to be split out; anything else?

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL 					 
Quidquid latine dictum sit, altum viditur.

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
  2006-01-03 20:44 [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support Stuffed Crust
  2006-01-03 23:29 ` Petr Vandrovec
  2006-01-05  9:59 ` Benjamin Herrenschmidt
@ 2006-02-14 21:28 ` Benjamin Herrenschmidt
  2006-02-22 22:19   ` Stuffed Crust
  2 siblings, 1 reply; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-02-14 21:28 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Solomon Peachy

On Tue, 2006-01-03 at 15:44 -0500, Stuffed Crust wrote:
> Signed-off-by:  Solomon Peachy <pizza@shaftnet.org>
> 
> Newer Radeon graphics cards use a different table layout in their 
> BIOSes, known as 'ATOM'.  
> 
> I've attached a patch (against 2.6.15) which implements the PLL and LVDS
> parsing out of the ATOM BIOSes, using the current X.org sources as a
> reference.  Notably absent is the code which detects the connectors and
> TDMS information, but it's of lesser importance at the moment.
> 
> This code still has quirks, but I'm not sure if those are due to bugs in
> my patch or incomplete/buggy support for the RV410.  So I'd appreciate
> it if people could try this out, especially if they have a newer ATOM
> BIOS in their radeon card, and let me know what happens. 
> 
> Finally, this patch also has PCI IDs added for my Radeon Mobility X700 
> (M26) adapter. 

Hi !

What's the status of this patch ? Do you have a new version ? People are
heving enough problems with recent cards to justify getting something in
soon...

Thanks,
Ben.




-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
for problems?  Stop!  Download the new AJAX search engine that makes
searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
  2006-02-14 21:28 ` Benjamin Herrenschmidt
@ 2006-02-22 22:19   ` Stuffed Crust
  2006-02-23  6:09     ` Stuffed Crust
  0 siblings, 1 reply; 16+ messages in thread
From: Stuffed Crust @ 2006-02-22 22:19 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linux-fbdev-devel


[-- Attachment #1.1: Type: text/plain, Size: 1077 bytes --]

On Wed, Feb 15, 2006 at 08:28:49AM +1100, Benjamin Herrenschmidt wrote:
> What's the status of this patch ? Do you have a new version ? People are
> heving enough problems with recent cards to justify getting something in
> soon...

Sorry about the delay, but as you mentioned earlier, you know how those 
higher-priority things are.  

I've attached my current WIP.  It has no additional functionality over
the original patch, but it's rearranged to be quite a bit cleaner, using 
function pointers for the atom/legacy bits.  

I haven't tested this particular patch out, but it does compile.

The reversed DAC/TDMS detection and connector parsing stuff is still 
unwritten for ATOM BIOSes.  I plan on digging into the X.Org sources 
later tonight.

Of course other stuff like OpenFirmware could be integrated better, but 
I'd rather leave that for people who can test things.

Let me know what you think.   It's against 2.6.15, FWIW.

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL
Quidquid latine dictum sit, altum viditur.

[-- Attachment #1.2: radeon-atom-bios2.diff --]
[-- Type: text/plain, Size: 12757 bytes --]

diff -aur aty-2.6.15/ati_ids.h aty/ati_ids.h
--- aty-2.6.15/ati_ids.h	2006-02-22 16:12:58.000000000 -0500
+++ aty/ati_ids.h	2006-02-22 16:15:54.000000000 -0500
@@ -185,6 +185,8 @@
 #define PCI_CHIP_R423_UQ                0x5551
 #define PCI_CHIP_R423_UR                0x5552
 #define PCI_CHIP_R423_UT                0x5554
+#define PCI_CHIP_RV410_5652             0x5652
+#define PCI_CHIP_RV410_VS               0x5653
 #define PCI_CHIP_MACH64VT		0x5654
 #define PCI_CHIP_MACH64VU		0x5655
 #define PCI_CHIP_MACH64VV		0x5656
diff -aur aty-2.6.15/radeon_base.c aty/radeon_base.c
--- aty-2.6.15/radeon_base.c	2006-02-22 16:12:58.000000000 -0500
+++ aty/radeon_base.c	2006-02-22 16:59:03.000000000 -0500
@@ -214,6 +214,8 @@
 	CHIP_DEF(PCI_CHIP_R420_JL,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JM,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JN,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_5652,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VS,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
 	CHIP_DEF(PCI_CHIP_R420_JP,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UH,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UI,	R420,	CHIP_HAS_CRTC2),
@@ -342,7 +344,7 @@
 	 * to phase out Open Firmware images.
 	 *
 	 * Currently, we only look at the first PCI data, we could iteratre and deal with
-	 * them all, and we should use fb_bios_start relative to start of image and not
+	 * them all, and we should use fp_bios_start relative to start of image and not
 	 * relative start of ROM, but so far, I never found a dual-image ATI card
 	 *
 	 * typedef struct {
@@ -593,6 +595,71 @@
 	return 0;
 }
 
+static void __devinit radeon_get_pllinfo_legacy(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+	
+	rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
+	rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
+	rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
+	rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
+	rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
+	rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
+}
+
+
+static void __devinit radeon_get_pllinfo_atom(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	pll_info_block = BIOS_IN16(rinfo->atom_data_start + 12);
+	
+	rinfo->pll.sclk = BIOS_IN32(pll_info_block + 8);
+	rinfo->pll.mclk = BIOS_IN32(pll_info_block + 12);
+	rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 82);
+	rinfo->pll.ref_div = 0; /* Have to get it elsewhere */
+	rinfo->pll.ppll_min = BIOS_IN16(pll_info_block + 78);
+	rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 32);
+	
+	if (rinfo->pll.sclk == 0)  rinfo->pll.sclk = 20000;
+	if (rinfo->pll.mclk == 0)  rinfo->pll.mclk = 20000;
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
+}
+
+static void radeon_detect_bios_type(struct radeonfb_info *rinfo)
+{
+	  int tmp = rinfo->fp_bios_start + 4;
+
+	  if ((BIOS_IN8(tmp) == 'A' &&
+	       BIOS_IN8(tmp+1) == 'T' &&
+	       BIOS_IN8(tmp+2) == 'O' &&
+	       BIOS_IN8(tmp+3) == 'M') ||
+	      (BIOS_IN8(tmp) == 'M' &&
+	       BIOS_IN8(tmp+1) == 'O' &&
+	       BIOS_IN8(tmp+2) == 'T' &&
+	       BIOS_IN8(tmp+3) == 'A')) {
+		  rinfo->is_atom_bios = 1;
+
+		  rinfo->atom_data_start = BIOS_IN16(rinfo->fp_bios_start + 32);
+		  rinfo->radeon_get_pll_info = radeon_get_pllinfo_atom;
+		  rinfo->radeon_get_panel_info = radeon_get_panel_info_atom;
+		  printk(KERN_WARNING "XXXX Write ATOM BIOS connector parse\n");
+		  rinfo->radeon_parse_connector_info = NULL;
+		  printk("ATOM BIOS detected\n");
+	  } else {
+		  rinfo->is_atom_bios = 0;
+		  rinfo->radeon_get_pll_info = radeon_get_pllinfo_legacy;
+		  rinfo->radeon_get_panel_info = radeon_get_panel_info_legacy;
+		  rinfo->radeon_parse_connector_info = radeon_parse_connector_info_legacy;
+		  printk("Legacy BIOS detected\n");
+	  }
+}
+
 /*
  * Retreive PLL infos by different means (BIOS, Open Firmware, register probing...)
  */
@@ -655,8 +722,6 @@
 		rinfo->pll.ref_clk = 2700;
 		break;
 	}
-	rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
-
 
 #ifdef CONFIG_PPC_OF
 	/*
@@ -673,17 +738,12 @@
 	 * and if yes, retreive them
 	 */
 	if (!force_measure_pll && rinfo->bios_seg) {
-		u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
-
-		rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
-		rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
-		rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
-		rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
-		rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
-		rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
-
-		printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
-		goto found;
+		if (rinfo->radeon_get_pll_info) {
+			rinfo->radeon_get_pll_info(rinfo);
+			goto found;
+		}
+			
+		printk(KERN_INFO "Unable to retrieve PLL info from BIOS\n");
 	}
 
 	/*
@@ -701,6 +761,22 @@
        	printk(KERN_INFO "radeonfb: Used default PLL infos\n");
 
 found:
+
+	/* Check and fix-up the PLL divisor if necessary */
+	if (rinfo->pll.ref_div < 2) {
+		int tmp = INPLL(PPLL_REF_DIV);
+		if (rinfo->family == CHIP_FAMILY_RS300) {
+			rinfo->pll.ref_div = (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
+		} else {
+			rinfo->pll.ref_div = tmp & PPLL_REF_DIV_MASK;
+		}
+		
+		/* Sane default */
+		if (rinfo->pll.ref_div < 2) {		
+			rinfo->pll.ref_div = 12;
+		}
+	}		
+
 	/*
 	 * Some methods fail to retreive SCLK and MCLK values, we apply default
 	 * settings in this case (200Mhz). If that really happne often, we could
@@ -2412,6 +2488,7 @@
 	 * We probably need to make sure this is the primary display,
 	 * but that is difficult without some arch support.
 	 */
+
 #ifdef CONFIG_X86
 	if (rinfo->bios_seg == NULL)
 		radeon_find_mem_vbios(rinfo);
@@ -2423,6 +2500,9 @@
 	if (rinfo->bios_seg == NULL && rinfo->is_mobility)
 		radeon_map_ROM(rinfo, pdev);
 
+	/* Check BIOS Type */
+	radeon_detect_bios_type(rinfo);
+	
 	/* Get informations about the board's PLL */
 	radeon_get_pllinfo(rinfo);
 
diff -aur aty-2.6.15/radeon_monitor.c aty/radeon_monitor.c
--- aty-2.6.15/radeon_monitor.c	2006-02-22 16:12:58.000000000 -0500
+++ aty/radeon_monitor.c	2006-02-22 16:56:59.000000000 -0500
@@ -160,7 +160,51 @@
 #endif /* CONFIG_PPC_OF */
 
 
-static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
+int __devinit radeon_get_panel_info_atom(struct radeonfb_info *rinfo)
+{
+	unsigned long tmp;
+
+	if (!rinfo->bios_seg)
+		return 0;
+
+	tmp = BIOS_IN16(rinfo->atom_data_start + 16);
+	if (!tmp) {
+		printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
+		rinfo->panel_info.pwr_delay = 200;
+		return 0;
+	}
+	
+	rinfo->panel_info.xres = BIOS_IN16(tmp+6);
+	rinfo->panel_info.yres = BIOS_IN16(tmp+10);
+	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+40);
+	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;
+	
+	/* No special divider combinations? */
+	
+	rinfo->panel_info.hblank = BIOS_IN16(tmp+8);
+	rinfo->panel_info.hOver_plus = BIOS_IN16(tmp+14);
+	rinfo->panel_info.hSync_width = BIOS_IN16(tmp+16);
+	rinfo->panel_info.vblank = BIOS_IN16(tmp+12);
+	rinfo->panel_info.vOver_plus = BIOS_IN16(tmp+18);
+	rinfo->panel_info.vSync_width = BIOS_IN16(tmp+20);
+	rinfo->panel_info.clock = BIOS_IN16(tmp+4);
+	
+	/* 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;
+	
+	return 1;
+}
+
+int __devinit radeon_get_panel_info_legacy(struct radeonfb_info *rinfo)
 {
 	unsigned long tmp, tmp0;
 	char stmp[30];
@@ -174,7 +218,7 @@
 		rinfo->panel_info.pwr_delay = 200;
 		return 0;
 	}
-
+	
 	for(i=0; i<24; i++)
 		stmp[i] = BIOS_IN8(tmp+i+1);
 	stmp[24] = 0;
@@ -182,13 +226,13 @@
 	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.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
 	 */
@@ -203,6 +247,7 @@
 		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);
@@ -226,7 +271,7 @@
 			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);
@@ -235,10 +280,11 @@
 			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;
@@ -248,7 +294,7 @@
  * 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)
+void __devinit radeon_parse_connector_info_legacy(struct radeonfb_info *rinfo)
 {
 	int offset, chips, connectors, tmp, i, conn, type;
 
@@ -266,7 +312,7 @@
 		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
 	 */
@@ -434,7 +480,9 @@
 #endif
 	int tmp, i;
 
-	radeon_parse_connector_info(rinfo);
+	if (rinfo->radeon_parse_connector_info) {
+		rinfo->radeon_parse_connector_info(rinfo);
+	}
 
 	if (radeon_parse_monitor_layout(rinfo, monitor_layout)) {
 
@@ -519,6 +567,8 @@
 		/*
 		 * Check for cards with reversed DACs or TMDS controllers using BIOS
 		 */
+		// XXXX What about atom_bios types?
+
 		if (rinfo->bios_seg &&
 		    (tmp = BIOS_IN16(rinfo->fp_bios_start + 0x50))) {
 			for (i = 1; i < 4; i++) {
@@ -731,6 +781,7 @@
 {
 	struct fb_info * info = rinfo->info;
 	int has_default_mode = 0;
+	int found = 0;
 
 	/*
 	 * Fill default var first
@@ -741,8 +792,12 @@
 	/*
 	 * First check out what BIOS has to say
 	 */
-	if (rinfo->mon1_type == MT_LCD)
-		radeon_get_panel_info_BIOS(rinfo);
+	if (rinfo->mon1_type == MT_LCD) {
+		if (rinfo->radeon_get_panel_info) {
+			rinfo->radeon_get_panel_info(rinfo);
+			// XXX Do we care about the return value?
+		}
+	}
 
 	/*
 	 * Parse EDID detailed timings and deduce panel infos if any. Right now
diff -aur aty-2.6.15/radeonfb.h aty/radeonfb.h
--- aty-2.6.15/radeonfb.h	2006-02-22 16:12:58.000000000 -0500
+++ aty/radeonfb.h	2006-02-22 16:57:56.000000000 -0500
@@ -298,6 +298,16 @@
 	void __iomem		*bios_seg;
 	int			fp_bios_start;
 
+	int                     is_atom_bios;
+	int                     atom_data_start;
+	
+	void                    (*radeon_get_pll_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_panel_info)(struct radeonfb_info *rinfo);
+	void                    (*radeon_parse_connector_info)(struct radeonfb_info *rinfo);
+
+
+
+
 	u32			pseudo_palette[17];
 	struct { u8 red, green, blue, pad; }
 				palette[256];
@@ -625,4 +635,10 @@
 extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
 			       int reg_only);
 
+/* Bios functions.  Fix this. */
+extern int __devinit radeon_get_panel_info_atom(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_panel_info_legacy(struct radeonfb_info *rinfo);
+void __devinit radeon_parse_connector_info_legacy(struct radeonfb_info *rinfo);
+
+
 #endif /* __RADEONFB_H__ */

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
  2006-02-22 22:19   ` Stuffed Crust
@ 2006-02-23  6:09     ` Stuffed Crust
  2006-02-23  6:50       ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 16+ messages in thread
From: Stuffed Crust @ 2006-02-23  6:09 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, linux-fbdev-devel


[-- Attachment #1.1: Type: text/plain, Size: 1922 bytes --]

On Wed, Feb 22, 2006 at 05:19:16PM -0500, Stuffed Crust wrote:
> On Wed, Feb 15, 2006 at 08:28:49AM +1100, Benjamin Herrenschmidt wrote:
> > What's the status of this patch ? Do you have a new version ? People are
> > heving enough problems with recent cards to justify getting something in
> > soon...
> 
> Sorry about the delay, but as you mentioned earlier, you know how those 
> higher-priority things are.  
> 
> I've attached my current WIP.  It has no additional functionality over
> the original patch, but it's rearranged to be quite a bit cleaner, using 
> function pointers for the atom/legacy bits.  
> 
> I haven't tested this particular patch out, but it does compile.
> 
> The reversed DAC/TDMS detection and connector parsing stuff is still 
> unwritten for ATOM BIOSes.  I plan on digging into the X.Org sources 
> later tonight.

And to follow up to myself, here is a third patch.  I've started 
incorporating bits and pieces from the "oooold" non-working rewrite 
that you sent me for inspiration.  Changes from last patch:

 * Naming cleanups
 * Merge the OpenFirmware PLL detection into the function pointer mess.
 * Merge the oooold patch's legacy bios connector parsing code, As the 
   original stuff didn't really do anything anyway, I don't see the harm.  
 * Merge the ooold patch's atom bios connector parsing code, but it still 
   doesn't work yet.  It's late and my eyes hurt.

The good news is that I'm typing this using this patch on my Radeon 
M26 (mmm.. 1680x1050!), but the bad news is that I don't know if the 
legacy BIOS code works, especially the connector parsing stuff.

Let me know if I'm going down the right path.  The RadeonFB driver is 
pretty complicated, so I'm trying to take baby steps as I figure more 
of it out.

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL
Quidquid latine dictum sit, altum viditur.

[-- Attachment #1.2: radeon-atom-3.diff --]
[-- Type: text/plain, Size: 26727 bytes --]

diff -aur aty-2.6.15/ati_ids.h aty/ati_ids.h
--- aty-2.6.15/ati_ids.h	2006-02-22 23:28:14.000000000 -0500
+++ aty/ati_ids.h	2006-02-22 23:28:38.000000000 -0500
@@ -185,6 +185,8 @@
 #define PCI_CHIP_R423_UQ                0x5551
 #define PCI_CHIP_R423_UR                0x5552
 #define PCI_CHIP_R423_UT                0x5554
+#define PCI_CHIP_RV410_5652             0x5652
+#define PCI_CHIP_RV410_VS               0x5653
 #define PCI_CHIP_MACH64VT		0x5654
 #define PCI_CHIP_MACH64VU		0x5655
 #define PCI_CHIP_MACH64VV		0x5656
Only in aty-2.6.15: radeon-atom-bios2.diff
Only in aty-2.6.15: radeon_atom_bios.diff
diff -aur aty-2.6.15/radeon_base.c aty/radeon_base.c
--- aty-2.6.15/radeon_base.c	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeon_base.c	2006-02-23 00:22:42.000000000 -0500
@@ -214,6 +214,8 @@
 	CHIP_DEF(PCI_CHIP_R420_JL,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JM,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JN,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_5652,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VS,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
 	CHIP_DEF(PCI_CHIP_R420_JP,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UH,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UI,	R420,	CHIP_HAS_CRTC2),
@@ -342,7 +344,7 @@
 	 * to phase out Open Firmware images.
 	 *
 	 * Currently, we only look at the first PCI data, we could iteratre and deal with
-	 * them all, and we should use fb_bios_start relative to start of image and not
+	 * them all, and we should use fp_bios_start relative to start of image and not
 	 * relative start of ROM, but so far, I never found a dual-image ATI card
 	 *
 	 * typedef struct {
@@ -428,7 +430,7 @@
  * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
  * tree. Hopefully, ATI OF driver is kind enough to fill these
  */
-static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo)
+static int __devinit radeon_get_pll_info_openfirmware (struct radeonfb_info *rinfo)
 {
 	struct device_node *dp = rinfo->of_node;
 	u32 *val;
@@ -451,6 +453,7 @@
 	if (val && *val)
 		rinfo->pll.mclk = (*val) / 10;
 
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
        	return 0;
 }
 #endif /* CONFIG_PPC_OF */
@@ -593,10 +596,87 @@
 	return 0;
 }
 
+static int __devinit radeon_get_pll_info_legacy(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	if (!rinfo->bios_seg)
+		return -EINVAL;
+
+	pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+	
+	rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
+	rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
+	rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
+	rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
+	rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
+	rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from Legacy BIOS\n");
+	return 0;
+}
+
+
+static int __devinit radeon_get_pll_info_atom(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	if (!rinfo->bios_seg)
+		return -EINVAL;
+
+	pll_info_block = BIOS_IN16(rinfo->atom_data_start + 12);
+	
+	rinfo->pll.sclk = BIOS_IN32(pll_info_block + 8);
+	rinfo->pll.mclk = BIOS_IN32(pll_info_block + 12);
+	rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 82);
+	rinfo->pll.ref_div = 0; /* Have to get it elsewhere */
+	rinfo->pll.ppll_min = BIOS_IN16(pll_info_block + 78);
+	rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 32);
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from ATOM BIOS\n");
+	return 0;
+}
+
+static void radeon_detect_bios_type(struct radeonfb_info *rinfo)
+{
+#ifdef CONFIG_PPC_OF
+	  rinfo->is_atom_bios = 0;
+	  rinfo->get_pll_info = radeon_get_pll_info_openfirmware;
+	  rinfo->get_panel_info = NULL;
+	  rinfo->parse_connector_info = NULL;
+#else
+	  int tmp = rinfo->fp_bios_start + 4;
+
+	  if ((BIOS_IN8(tmp) == 'A' &&
+	       BIOS_IN8(tmp+1) == 'T' &&
+	       BIOS_IN8(tmp+2) == 'O' &&
+	       BIOS_IN8(tmp+3) == 'M') ||
+	      (BIOS_IN8(tmp) == 'M' &&
+	       BIOS_IN8(tmp+1) == 'O' &&
+	       BIOS_IN8(tmp+2) == 'T' &&
+	       BIOS_IN8(tmp+3) == 'A')) {
+		  rinfo->is_atom_bios = 1;
+
+		  rinfo->atom_data_start = BIOS_IN16(rinfo->fp_bios_start + 32);
+		  rinfo->radeon_get_pll_info = radeon_get_pll_info_atom;
+		  rinfo->radeon_get_panel_info = radeon_get_panel_info_atom;
+		  rinfo->radeon_parse_connector_info = radeon_parse_connector_info_atom;
+		  printk("ATOM BIOS detected\n");
+	  } else {
+		  rinfo->is_atom_bios = 0;
+		  rinfo->radeon_get_pll_info = radeon_get_pll_info_legacy;
+		  rinfo->radeon_get_panel_info = radeon_get_panel_info_legacy;
+		  rinfo->radeon_parse_connector_info = radeon_parse_connector_info_legacy;
+		  printk("Legacy BIOS detected\n");
+	  }
+#endif  /* CONFIG_PPC_OF */
+
+}
+
 /*
  * Retreive PLL infos by different means (BIOS, Open Firmware, register probing...)
  */
-static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
+static void __devinit radeon_get_pll_info(struct radeonfb_info *rinfo)
 {
 	/*
 	 * In the case nothing works, these are defaults; they are mostly
@@ -655,39 +735,18 @@
 		rinfo->pll.ref_clk = 2700;
 		break;
 	}
-	rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
-
-
-#ifdef CONFIG_PPC_OF
-	/*
-	 * Retreive PLL infos from Open Firmware first
-	 */
-       	if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
-       		printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
-		goto found;
-	}
-#endif /* CONFIG_PPC_OF */
 
 	/*
-	 * Check out if we have an X86 which gave us some PLL informations
-	 * and if yes, retreive them
+	 * If we have a way to retrieve the PLL information, do so.
 	 */
-	if (!force_measure_pll && rinfo->bios_seg) {
-		u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
-
-		rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
-		rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
-		rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
-		rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
-		rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
-		rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
-
-		printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
-		goto found;
+	if (!force_measure_pll && rinfo->radeon_get_pll_info) {
+		if (!rinfo->radeon_get_pll_info(rinfo)) {
+			goto found;
+		}
 	}
 
 	/*
-	 * We didn't get PLL parameters from either OF or BIOS, we try to
+	 * If we don't get the PLL parameters handed to us, we try to
 	 * probe them
 	 */
 	if (radeon_probe_pll_params(rinfo) == 0) {
@@ -701,6 +760,22 @@
        	printk(KERN_INFO "radeonfb: Used default PLL infos\n");
 
 found:
+
+	/* Check and fix-up the PLL divisor if necessary */
+	if (rinfo->pll.ref_div < 2) {
+		int tmp = INPLL(PPLL_REF_DIV);
+		if (rinfo->family == CHIP_FAMILY_RS300) {
+			rinfo->pll.ref_div = (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
+		} else {
+			rinfo->pll.ref_div = tmp & PPLL_REF_DIV_MASK;
+		}
+		
+		/* Sane default */
+		if (rinfo->pll.ref_div < 2) {		
+			rinfo->pll.ref_div = 12;
+		}
+	}		
+
 	/*
 	 * Some methods fail to retreive SCLK and MCLK values, we apply default
 	 * settings in this case (200Mhz). If that really happne often, we could
@@ -2412,6 +2487,7 @@
 	 * We probably need to make sure this is the primary display,
 	 * but that is difficult without some arch support.
 	 */
+
 #ifdef CONFIG_X86
 	if (rinfo->bios_seg == NULL)
 		radeon_find_mem_vbios(rinfo);
@@ -2423,8 +2499,11 @@
 	if (rinfo->bios_seg == NULL && rinfo->is_mobility)
 		radeon_map_ROM(rinfo, pdev);
 
+	/* Check BIOS Type */
+	radeon_detect_bios_type(rinfo);
+	
 	/* Get informations about the board's PLL */
-	radeon_get_pllinfo(rinfo);
+	radeon_get_pll_info(rinfo);
 
 #ifdef CONFIG_FB_RADEON_I2C
 	/* Register I2C bus */
diff -aur aty-2.6.15/radeon_monitor.c aty/radeon_monitor.c
--- aty-2.6.15/radeon_monitor.c	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeon_monitor.c	2006-02-23 00:43:15.000000000 -0500
@@ -160,7 +160,51 @@
 #endif /* CONFIG_PPC_OF */
 
 
-static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
+int __devinit radeon_get_panel_info_atom(struct radeonfb_info *rinfo)
+{
+	unsigned long tmp;
+
+	if (!rinfo->bios_seg)
+		return 0;
+
+	tmp = BIOS_IN16(rinfo->atom_data_start + 16);
+	if (!tmp) {
+		printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
+		rinfo->panel_info.pwr_delay = 200;
+		return 0;
+	}
+	
+	rinfo->panel_info.xres = BIOS_IN16(tmp+6);
+	rinfo->panel_info.yres = BIOS_IN16(tmp+10);
+	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+40);
+	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;
+	
+	/* No special divider combinations? */
+	
+	rinfo->panel_info.hblank = BIOS_IN16(tmp+8);
+	rinfo->panel_info.hOver_plus = BIOS_IN16(tmp+14);
+	rinfo->panel_info.hSync_width = BIOS_IN16(tmp+16);
+	rinfo->panel_info.vblank = BIOS_IN16(tmp+12);
+	rinfo->panel_info.vOver_plus = BIOS_IN16(tmp+18);
+	rinfo->panel_info.vSync_width = BIOS_IN16(tmp+20);
+	rinfo->panel_info.clock = BIOS_IN16(tmp+4);
+	
+	/* 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;
+	
+	return 1;
+}
+
+int __devinit radeon_get_panel_info_legacy(struct radeonfb_info *rinfo)
 {
 	unsigned long tmp, tmp0;
 	char stmp[30];
@@ -174,7 +218,7 @@
 		rinfo->panel_info.pwr_delay = 200;
 		return 0;
 	}
-
+	
 	for(i=0; i<24; i++)
 		stmp[i] = BIOS_IN8(tmp+i+1);
 	stmp[24] = 0;
@@ -182,13 +226,13 @@
 	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.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
 	 */
@@ -203,6 +247,7 @@
 		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);
@@ -226,7 +271,7 @@
 			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);
@@ -235,57 +280,314 @@
 			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;
 }
 
+int __devinit radeon_parse_connector_info_atom(struct radeonfb_info *rinfo)
+{
+#if 0
+	int i, j, offset, valids;
+	int crtc = 0, ids[RADEON_MAX_CONNECTORS];
+	u16 portinfo;
+	int additional = 2;
+	int ddc_type, dac_type, conn_type, tmds_type, port_id;
+	
+	offset = BIOS_IN16(rinfo->atom_data_start + 22);
+	if (offset == 0)
+		return -ENODEV;
+
+	/* Again, I slightly modified X.org algorithm. I assign "primary" outputs
+	 * to entries 0 and 1, and anything else goes after 2.
+	 *
+	 * Also, I keep an array of all port IDs matching connectors[] array,
+	 * unlike X which limits itself to "crtc"'s
+	 */
+	for (i = 0; i < RADEON_MAX_CONNECTORS; i++)
+		ids[i] = -1;
+
+	valids = BIOS_IN16(offset + 4);
+	for (i = 0; i < 8; i++) {
+		if (!(valids & (1 << i)))
+			continue;
+		portinfo = BIOS_IN16(offset + 6 + i*2);
+
+		conn_type = (portinfo >> 4) & 0xf;
+		dac_type = (portinfo & 0xf) - 1;
+		// XXX ddc_type =
+		if (i == 3)
+			tmds_type = tmds_internal;
+		else if (i == 7)
+			tmds_type = tmds_external;
+		else
+			tmds_type = tmds_unknown;
+		port_id = (portinfo >> 8) & 0xf;
+
+		/* Ok, now we have the port ID, look for an existing port
+		 * already using this ID
+		 */
+		for (j = 0; j < RADEON_MAX_CONNECTORS; j++) {
+			if (port_id != ids[j])
+				continue;
+			/* Gotcha, just "update" values */
+			if (tmds_type != tmds_unknown)
+				rinfo->connectors[j].tmds_type = tmds_type;
+			if (rinfo->connectors[j].dac_type == dac_unknown)
+				rinfo->connectors[j].dac_type = dac_type;
+			goto next;
+		}
+		
+		if (crtc < 2) {
+			/* FIXME: ignore TV here */
+			if ((i==2) || (i==6)) continue;
+
+			if (crtc == 1) {
+			    /* sharing same port with id[0] */
+			    if (((portinfo>>8) & 0xf) == ids[0]) {
+				if (i == 3)
+				    pRADEONEnt->PortInfo[0].TMDSType = TMDS_INT;
+				else if (i == 7)
+				    pRADEONEnt->PortInfo[0].TMDSType = TMDS_EXT;
+
+				if (pRADEONEnt->PortInfo[0].DACType == DAC_UNKNOWN)
+				    pRADEONEnt->PortInfo[0].DACType = (portinfo & 0xf) - 1;
+				continue;
+			    }
+			}
+
+			id[crtc] = (portinfo>>8) & 0xf;
+			pRADEONEnt->PortInfo[crtc].DACType = (portinfo & 0xf) - 1;
+			pRADEONEnt->PortInfo[crtc].ConnectorType = (portinfo>>4) & 0xf;
+			if (i == 3)
+			    pRADEONEnt->PortInfo[crtc].TMDSType = TMDS_INT;
+			else if (i == 7)
+			    pRADEONEnt->PortInfo[crtc].TMDSType = TMDS_EXT;
+
+			if((tmp0 = RADEON_BIOS16 (info->MasterDataStart + 24)) && id[crtc]) {
+			    switch (RADEON_BIOS16 (tmp0 + 4 + 27 * id[crtc]) * 4)
+			    {
+			    case RADEON_GPIO_MONID:
+				pRADEONEnt->PortInfo[crtc].DDCType = DDC_MONID;
+				break;
+			    case RADEON_GPIO_DVI_DDC:
+				pRADEONEnt->PortInfo[crtc].DDCType = DDC_DVI;
+				break;
+			    case RADEON_GPIO_VGA_DDC:
+				pRADEONEnt->PortInfo[crtc].DDCType = DDC_VGA;
+				break;
+			    case RADEON_GPIO_CRT2_DDC:
+				pRADEONEnt->PortInfo[crtc].DDCType = DDC_CRT2;
+				break;
+			    default:
+				pRADEONEnt->PortInfo[crtc].DDCType = DDC_NONE;
+				break;
+			    }
+
+			} else {
+			    pRADEONEnt->PortInfo[crtc].DDCType = DDC_NONE;
+			}
+			crtc++;
+		    } else {
+			/* we have already had two CRTCs assigned. the rest may share the same
+			 * port with the existing connector, fill in them accordingly.
+			 */
+			for (j=0; j<2; j++) {
+				if (((portinfo>>8) & 0xf) == id[j]) {
+					if (i == 3)
+						pRADEONEnt->PortInfo[j].TMDSType = TMDS_INT;
+					else if (i == 7)
+						pRADEONEnt->PortInfo[j].TMDSType = TMDS_EXT;
+					
+					if (pRADEONEnt->PortInfo[j].DACType == DAC_UNKNOWN)
+						pRADEONEnt->PortInfo[j].DACType = (portinfo & 0xf) - 1;
+				}
+			}
+		}
+	}
+	
+
+	for (i=0; i<2; i++) {
+		xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Port%d: DDCType-%d, DACType-%d, TMDSType-%d, ConnectorType-%d\n",
+			   i, pRADEONEnt->PortInfo[i].DDCType, pRADEONEnt->PortInfo[i].DACType,
+			   pRADEONEnt->PortInfo[i].TMDSType, pRADEONEnt->PortInfo[i].ConnectorType);
+	}
+
+#else
+	return -1;
+#endif
+}
+
 /* 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 __devinit radeon_parse_connector_info_legacy(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"
+	int offset, i, entry, tmp;
+	int ddc_type, dac_type, conn_type, tmds_type;
+	int conn_index = 0;
+	int conn_add = 2;
+	int idx = 0;
+
+	/* Convert legacy to real connector types */
+	const enum radeon_conn_type legacy_conn_to_type[] = {
+		conn_none,
+		conn_proprietary,
+		conn_vga,
+		conn_dvi_i,
+		conn_dvi_d,
+		conn_ctv,
+		conn_stv,
+		conn_unsupported,
 	};
 
-	if (!rinfo->bios_seg)
-		return;
+	/* Some laptops only have one connector (VGA) listed in the connector
+	 * table, we need to add LVDS in as a non-DDC display.
+	 * Note, we can't assume the listed VGA will be filled in PortInfo[0],
+	 * when walking through connector table. connector_found has following
+	 * meaning:
+	 * 0 -- nothing found,
+	 * 1 -- only connectors[0] filled,
+	 * 2 -- only connectors[1] filled,
+	 * 3 -- both are filled.
+	 *
+	 * Note: I modified X.org algorithm to add additional entries if any
+	 * after the second table slot. Those entries do not affect the value
+	 * of connector_found. --BenH.
+	 */
+	int connector_found = 0;
 
 	offset = BIOS_IN16(rinfo->fp_bios_start + 0x50);
-	if (offset == 0) {
-		printk(KERN_WARNING "radeonfb: No connector info table detected\n");
-		return;
-	}
+	if (offset == 0)
+		return -ENODEV;
 
-	/* 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);
+	for (i = 1; i < 4; i++) {
+		entry = offset + i*2;
+
+		/* End of table */
+		if (!BIOS_IN8(entry) && i > 1)
+			break;
+
+		/* Read table entry, check connector type */
+		tmp = BIOS_IN16(entry);
+		conn_type = (tmp >> 12) & 0xf;
+		if (conn_type == legacy_conn_none)
+			continue;
+		ddc_type = (tmp >> 8) & 0xf;
+		dac_type = (tmp & 0x01) ? dac_tvdac : dac_primary;
+		tmds_type = (tmp & 0x10) ? tmds_external : tmds_internal;
+
+		/* same connector */
+		if (connector_found > 0) {
+			if (rinfo->connectors[conn_index].ddc_type == ddc_type)
+				continue;
 		}
+
+		/* sanity checks */
+		if (ddc_type > ddc_crt2)
+			ddc_type = ddc_none;
+		if (conn_type > legacy_conn_unsupported)
+			conn_type = legacy_conn_unsupported;
+		if (conn_type != legacy_conn_dvi_d &&
+		    conn_type != legacy_conn_dvi_i &&
+		    tmds_type == tmds_internal)
+			tmds_type= tmds_unknown;
+
+		/* convert connector type */
+		conn_type = legacy_conn_to_type[conn_type];
+
+		/* internal DDC_DVI port will get assigned to connector[0], or
+		 * if there is no DDC_DVI (like in some IGPs).
+		 */
+		conn_index = (ddc_type == ddc_dvi || conn_index == 1) ? 0 : 1;
+
+		/* if the port is a TV port, or both connectors are already
+		 * assigned, assign it after further in the table
+		 */
+		if (conn_type == conn_ctv || conn_type == conn_stv ||
+		    (rinfo->connectors[0].conn_type != conn_none &&
+		     rinfo->connectors[1].conn_type))
+			idx = conn_add++;
+		else
+			idx = conn_index;
+
+		/* if table full, exit */
+		if (idx >= RADEON_MAX_CONNECTORS) {
+			printk(KERN_WARNING "radeonfb: Connector table full !\n");
+			break;
+		}
+		rinfo->connectors[idx].conn_type	= conn_type;
+		rinfo->connectors[idx].ddc_type		= ddc_type;
+		rinfo->connectors[idx].dac_type		= dac_type;
+		rinfo->connectors[idx].tmds_type	= tmds_type;
+
+		/* increment connector_found for primary connectors only */
+		if (idx < 2)
+			connector_found += (idx + 1);
+	}
+
+	if (rinfo->is_mobility) {
+	    /* For the cases where only one VGA connector is found,
+	     * we assume LVDS is not listed in the connector table,
+	     * add it in here as the first port.
+	     *
+	     * TODO: Check what's up with laptops that have a DVI output
+	     * and no LVDS entry in the table. I suspect some thinkpads
+	     * may play trick with us here... We may want to check the
+	     * presence of a panel via LVDS_GEN_CNTL to be sure...
+	     */
+	    if ((connector_found < 3) &&
+		(rinfo->connectors[idx].conn_type == conn_vga)) {
+		    if (connector_found == 1) {
+			    memcpy(&rinfo->connectors[1],
+				   &rinfo->connectors[0],
+				   sizeof(struct radeon_connector));
+		    }
+		    /* Fixme: TV DAC is probably elsewhere ... */
+		    rinfo->connectors[0].dac_type = dac_tvdac;
+		    rinfo->connectors[0].tmds_type = tmds_unknown;
+		    rinfo->connectors[0].ddc_type = ddc_none;
+		    rinfo->connectors[0].conn_type = conn_proprietary;
+
+		    printk(KERN_WARNING "radeonfb: LVDS port is not in connector table, added in.\n");
+		    if (connector_found == 0)
+			    connector_found = 1;
+		    else
+			    connector_found = 3;
+	    }
+
+	    /* Check for LCD DDC info table */
+	    if ((offset = BIOS_IN16(rinfo->fp_bios_start + 0x42))) {
+		    if ((tmp = BIOS_IN16(offset + 0x15))) {
+			    if ((ddc_type = BIOS_IN8(tmp+2) & 0x07)) {
+				    rinfo->connectors[0].ddc_type = ddc_type;
+				    printk(KERN_WARNING "radeonfb: LCD DDC Info Table found, "
+						"forcing primary port to %d\n",
+						ddc_type);
+			    }
+		    }
+	    }
+	} else if (connector_found == 2) {
+		memcpy(&rinfo->connectors[0], &rinfo->connectors[1],
+			sizeof (struct radeon_connector));
+		rinfo->connectors[1].dac_type = dac_unknown;
+		rinfo->connectors[1].tmds_type = tmds_unknown;
+		rinfo->connectors[1].ddc_type = ddc_none;
+		rinfo->connectors[1].conn_type = conn_none;
+		connector_found = 1;
 	}
+
+	if (connector_found == 0)
+		return -ENODEV;
+
+	/* External TMDS Table, not used now */
+	return 0;
 }
 
 
@@ -434,7 +736,9 @@
 #endif
 	int tmp, i;
 
-	radeon_parse_connector_info(rinfo);
+	if (rinfo->radeon_parse_connector_info) {
+		rinfo->radeon_parse_connector_info(rinfo);
+	}
 
 	if (radeon_parse_monitor_layout(rinfo, monitor_layout)) {
 
@@ -519,7 +823,8 @@
 		/*
 		 * Check for cards with reversed DACs or TMDS controllers using BIOS
 		 */
-		if (rinfo->bios_seg &&
+
+		if (rinfo->bios_seg && !rinfo->is_atom_bios &&
 		    (tmp = BIOS_IN16(rinfo->fp_bios_start + 0x50))) {
 			for (i = 1; i < 4; i++) {
 				unsigned int tmp0;
@@ -741,8 +1046,12 @@
 	/*
 	 * First check out what BIOS has to say
 	 */
-	if (rinfo->mon1_type == MT_LCD)
-		radeon_get_panel_info_BIOS(rinfo);
+	if (rinfo->mon1_type == MT_LCD) {
+		if (rinfo->radeon_get_panel_info) {
+			rinfo->radeon_get_panel_info(rinfo);
+			// XXX Do we care about the return value?
+		}
+	}
 
 	/*
 	 * Parse EDID detailed timings and deduce panel infos if any. Right now
diff -aur aty-2.6.15/radeonfb.h aty/radeonfb.h
--- aty-2.6.15/radeonfb.h	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeonfb.h	2006-02-23 00:42:59.000000000 -0500
@@ -91,7 +91,7 @@
 /*
  * Monitor types
  */
-enum radeon_montype {
+enum radeon_mon_type {
 	MT_NONE = 0,
 	MT_CRT,		/* CRT */
 	MT_LCD,		/* LCD */
@@ -103,7 +103,7 @@
 /*
  * DDC i2c ports
  */
-enum ddc_type {
+enum radeon_ddc_type {
 	ddc_none,
 	ddc_monid,
 	ddc_dvi,
@@ -114,14 +114,69 @@
 /*
  * Connector types
  */
-enum conn_type {
-	conn_none,
-	conn_proprietary,
-	conn_crt,
-	conn_DVI_I,
-	conn_DVI_D,
+enum radeon_legacy_conn_type {
+	legacy_conn_none = 0,
+	legacy_conn_proprietary,
+	legacy_conn_crt,
+	legacy_conn_dvi_i,
+	legacy_conn_dvi_d,
+	legacy_conn_ctv,
+	legacy_conn_stv,
+	legacy_conn_unsupported,
 };
 
+enum radeon_conn_type {
+	conn_none = 0,
+	conn_vga,
+	conn_dvi_i,
+	conn_dvi_d,
+	conn_dvi_a,
+	conn_stv,
+	conn_ctv,
+	conn_lvds,
+	conn_digital,
+	conn_unsupported,
+	conn_proprietary
+};
+
+/*
+ * DAC types
+ */
+enum radeon_dac_type {
+	dac_unknown = -1,
+	dac_primary = 0,
+	dac_tvdac = 1,
+};
+
+/*
+ * TMDS types
+ */
+enum radeon_tmds_type {
+	tmds_unknown = -1,
+	tmds_internal = 0,
+	tmds_external = 1,
+};
+
+/*
+ * Each connector gets this structure associated with it,
+ * containing infos about the connector wiring and about
+ * whatever has been detected on it
+ */
+struct radeon_connector {
+	enum radeon_conn_type	conn_type;
+	enum radeon_ddc_type	ddc_type;
+	enum radeon_dac_type	dac_type;
+	enum radeon_tmds_type	tmds_type;
+	enum radeon_mon_type	mon_type;
+	u8			*edid;
+	struct fb_videomode	*modedb;
+	unsigned int		modedb_size;
+};
+
+/*
+ * Currently, the driver deals with at most 4 connectors
+ */
+#define RADEON_MAX_CONNECTORS	4
 
 /*
  * PLL infos
@@ -129,11 +184,19 @@
 struct pll_info {
 	int ppll_max;
 	int ppll_min;
-	int sclk, mclk;
+	int sclk;
+	int mclk;
 	int ref_div;
 	int ref_clk;
 };
 
+/*
+ * TMDS PLL infos
+ */
+struct radeon_tmds_pll_info {
+	long	freq;
+	u32	value;
+};
 
 /*
  * This structure contains the various registers manipulated by this
@@ -298,6 +361,18 @@
 	void __iomem		*bios_seg;
 	int			fp_bios_start;
 
+	int                     is_atom_bios;
+	int                     atom_data_start;
+	
+	int                     (*radeon_get_pll_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_panel_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_parse_connector_info)(struct radeonfb_info *rinfo);
+
+
+	/* Connector infos */
+	int				conn_count;
+	struct radeon_connector		connectors[RADEON_MAX_CONNECTORS];
+
 	u32			pseudo_palette[17];
 	struct { u8 red, green, blue, pad; }
 				palette[256];
@@ -625,4 +700,11 @@
 extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
 			       int reg_only);
 
+/* Bios functions.  Fix this. */
+extern int __devinit radeon_get_panel_info_atom(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_panel_info_legacy(struct radeonfb_info *rinfo);
+int __devinit radeon_parse_connector_info_legacy(struct radeonfb_info *rinfo);
+int __devinit radeon_parse_connector_info_atom(struct radeonfb_info *rinfo);
+
+
 #endif /* __RADEONFB_H__ */

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support
  2006-02-23  6:09     ` Stuffed Crust
@ 2006-02-23  6:50       ` Benjamin Herrenschmidt
  2006-02-23 22:36         ` [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4) Stuffed Crust
  0 siblings, 1 reply; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-02-23  6:50 UTC (permalink / raw)
  To: linux-fbdev-devel


> And to follow up to myself, here is a third patch.  I've started 
> incorporating bits and pieces from the "oooold" non-working rewrite 
> that you sent me for inspiration.  Changes from last patch:
> 
>  * Naming cleanups
>  * Merge the OpenFirmware PLL detection into the function pointer mess.
>  * Merge the oooold patch's legacy bios connector parsing code, As the 
>    original stuff didn't really do anything anyway, I don't see the harm.  
>  * Merge the ooold patch's atom bios connector parsing code, but it still 
>    doesn't work yet.  It's late and my eyes hurt.
> 
> The good news is that I'm typing this using this patch on my Radeon 
> M26 (mmm.. 1680x1050!), but the bad news is that I don't know if the 
> legacy BIOS code works, especially the connector parsing stuff.
> 
> Let me know if I'm going down the right path.  The RadeonFB driver is 
> pretty complicated, so I'm trying to take baby steps as I figure more 
> of it out.

From your comments, you sound on the right track. I haven't had time to
review the patches yet but I'll do asap, I'm very happy somebody is
tackling those issues :)

Ben.




-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4)
  2006-02-23  6:50       ` Benjamin Herrenschmidt
@ 2006-02-23 22:36         ` Stuffed Crust
  2006-02-23 23:15           ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 16+ messages in thread
From: Stuffed Crust @ 2006-02-23 22:36 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Benjamin Herrenschmidt


[-- Attachment #1.1: Type: text/plain, Size: 1383 bytes --]

On Thu, Feb 23, 2006 at 05:50:58PM +1100, Benjamin Herrenschmidt wrote:
> >From your comments, you sound on the right track. I haven't had time to
> review the patches yet but I'll do asap, I'm very happy somebody is
> tackling those issues :)

Fourth pass.  This patch supercedes the previous, and adds:

 * ATOM BIOS support for connector detection
 * TMDS parsing for both ATOM and Legacy BIOSes.  
 * Additional PCI IDs forall known M26 variants
 * Default PLL clocks for R420 family

I've tested this patch out on my laptop's ATOM X700/M26/RV410, and on a
dualhead Legacy 7500/RV200 add-in card.  All heads, tables, etc were
detected properly. 
 
It doesn't do anything with this new connector data other than 
to report it.  Integrating this stuff with the existing monitor detection
logic comes next, using the rinfo->connectors table instead of the 
rinfo->mon[12]_type stuff.   (at the very least, we shouldn't look for 
something plugged into a non-existant head...)

And of course, OpenFirmware integration, and doing something with the 
TMDS PLL information. 

I'm unsure how to best to proceed from here, so this patch is it until 
I get some more directed feedback.  

Signed-Off-By:  Solomon Peachy <pizza@shaftnet.org>

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL
Quidquid latine dictum sit, altum viditur.

[-- Attachment #1.2: radeon-atom-4.diff --]
[-- Type: text/plain, Size: 36752 bytes --]

diff -aur aty-2.6.15/ati_ids.h aty/ati_ids.h
--- aty-2.6.15/ati_ids.h	2006-02-22 23:28:14.000000000 -0500
+++ aty/ati_ids.h	2006-02-23 15:32:21.000000000 -0500
@@ -185,6 +185,10 @@
 #define PCI_CHIP_R423_UQ                0x5551
 #define PCI_CHIP_R423_UR                0x5552
 #define PCI_CHIP_R423_UT                0x5554
+#define PCI_CHIP_RV410_564B             0x564A
+#define PCI_CHIP_RV410_564A             0x564B
+#define PCI_CHIP_RV410_5652             0x5652
+#define PCI_CHIP_RV410_VS               0x5653
 #define PCI_CHIP_MACH64VT		0x5654
 #define PCI_CHIP_MACH64VU		0x5655
 #define PCI_CHIP_MACH64VV		0x5656
Only in aty-2.6.15: radeon-atom-bios2.diff
Only in aty-2.6.15: radeon_atom_bios.diff
diff -aur aty-2.6.15/radeon_base.c aty/radeon_base.c
--- aty-2.6.15/radeon_base.c	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeon_base.c	2006-02-23 15:41:35.000000000 -0500
@@ -214,6 +214,10 @@
 	CHIP_DEF(PCI_CHIP_R420_JL,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JM,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JN,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_564A,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_564B,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_5652,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VS,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
 	CHIP_DEF(PCI_CHIP_R420_JP,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UH,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UI,	R420,	CHIP_HAS_CRTC2),
@@ -342,7 +346,7 @@
 	 * to phase out Open Firmware images.
 	 *
 	 * Currently, we only look at the first PCI data, we could iteratre and deal with
-	 * them all, and we should use fb_bios_start relative to start of image and not
+	 * them all, and we should use fp_bios_start relative to start of image and not
 	 * relative start of ROM, but so far, I never found a dual-image ATI card
 	 *
 	 * typedef struct {
@@ -428,7 +432,7 @@
  * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
  * tree. Hopefully, ATI OF driver is kind enough to fill these
  */
-static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo)
+static int __devinit radeon_get_pll_info_openfirmware (struct radeonfb_info *rinfo)
 {
 	struct device_node *dp = rinfo->of_node;
 	u32 *val;
@@ -451,6 +455,7 @@
 	if (val && *val)
 		rinfo->pll.mclk = (*val) / 10;
 
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
        	return 0;
 }
 #endif /* CONFIG_PPC_OF */
@@ -593,10 +598,90 @@
 	return 0;
 }
 
+static int __devinit radeon_get_pll_info_legacy(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	if (!rinfo->bios_seg)
+		return -EINVAL;
+
+	pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+	
+	rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
+	rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
+	rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
+	rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
+	rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
+	rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from Legacy BIOS\n");
+	return 0;
+}
+
+
+static int __devinit radeon_get_pll_info_atom(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	if (!rinfo->bios_seg)
+		return -EINVAL;
+
+	pll_info_block = BIOS_IN16(rinfo->atom_data_start + 12);
+	
+	rinfo->pll.sclk = BIOS_IN32(pll_info_block + 8);
+	rinfo->pll.mclk = BIOS_IN32(pll_info_block + 12);
+	rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 82);
+	rinfo->pll.ref_div = 0; /* Have to get it elsewhere */
+	rinfo->pll.ppll_min = BIOS_IN16(pll_info_block + 78);
+	rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 32);
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from ATOM BIOS\n");
+	return 0;
+}
+
+static void radeon_detect_bios_type(struct radeonfb_info *rinfo)
+{
+#ifdef CONFIG_PPC_OF
+	  rinfo->is_atom_bios = 0;
+	  rinfo->get_pll_info = radeon_get_pll_info_openfirmware;
+	  rinfo->get_lvds_info = NULL;
+	  rinfo->get_conn_info = NULL;
+	  printk("radeonfb:  Using OpenFirmware tables\n");
+#else
+	  int tmp = rinfo->fp_bios_start + 4;
+
+	  if ((BIOS_IN8(tmp) == 'A' &&
+	       BIOS_IN8(tmp+1) == 'T' &&
+	       BIOS_IN8(tmp+2) == 'O' &&
+	       BIOS_IN8(tmp+3) == 'M') ||
+	      (BIOS_IN8(tmp) == 'M' &&
+	       BIOS_IN8(tmp+1) == 'O' &&
+	       BIOS_IN8(tmp+2) == 'T' &&
+	       BIOS_IN8(tmp+3) == 'A')) {
+		  rinfo->is_atom_bios = 1;
+
+		  rinfo->atom_data_start = BIOS_IN16(rinfo->fp_bios_start + 32);
+		  rinfo->radeon_get_pll_info = radeon_get_pll_info_atom;
+		  rinfo->radeon_get_lvds_info = radeon_get_lvds_info_atom;
+		  rinfo->radeon_get_conn_info = radeon_get_conn_info_atom;
+		  rinfo->radeon_get_tmds_info = radeon_get_tmds_info_atom;
+		  printk("radeonfb:  Using ATOM BIOS tables\n");
+	  } else {
+		  rinfo->is_atom_bios = 0;
+		  rinfo->radeon_get_pll_info = radeon_get_pll_info_legacy;
+		  rinfo->radeon_get_lvds_info = radeon_get_lvds_info_legacy;
+		  rinfo->radeon_get_conn_info = radeon_get_conn_info_legacy;
+		  rinfo->radeon_get_tmds_info = radeon_get_tmds_info_legacy;
+		  printk("radeonfb:  Using Legacy BIOS tables\n");
+	  }
+#endif  /* CONFIG_PPC_OF */
+
+}
+
 /*
  * Retreive PLL infos by different means (BIOS, Open Firmware, register probing...)
  */
-static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
+static void __devinit radeon_get_pll_info(struct radeonfb_info *rinfo)
 {
 	/*
 	 * In the case nothing works, these are defaults; they are mostly
@@ -648,46 +733,30 @@
 	case PCI_DEVICE_ID_ATI_RADEON_QF:
 	case PCI_DEVICE_ID_ATI_RADEON_QG:
 	default:
-		rinfo->pll.ppll_max = 35000;
-		rinfo->pll.ppll_min = 12000;
+		if (rinfo->family == CHIP_FAMILY_R420) {
+			rinfo->pll.ppll_max = 50000;
+			rinfo->pll.ppll_min = 20000;
+		} else {
+			rinfo->pll.ppll_max = 35000;
+			rinfo->pll.ppll_min = 12000;
+		}
 		rinfo->pll.mclk = 16600;
 		rinfo->pll.sclk = 16600;
 		rinfo->pll.ref_clk = 2700;
 		break;
 	}
-	rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
-
-
-#ifdef CONFIG_PPC_OF
-	/*
-	 * Retreive PLL infos from Open Firmware first
-	 */
-       	if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
-       		printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
-		goto found;
-	}
-#endif /* CONFIG_PPC_OF */
 
 	/*
-	 * Check out if we have an X86 which gave us some PLL informations
-	 * and if yes, retreive them
+	 * If we have a way to retrieve the PLL information, do so.
 	 */
-	if (!force_measure_pll && rinfo->bios_seg) {
-		u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
-
-		rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
-		rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
-		rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
-		rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
-		rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
-		rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
-
-		printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
-		goto found;
+	if (!force_measure_pll && rinfo->radeon_get_pll_info) {
+		if (!rinfo->radeon_get_pll_info(rinfo)) {
+			goto found;
+		}
 	}
 
 	/*
-	 * We didn't get PLL parameters from either OF or BIOS, we try to
+	 * If we don't get the PLL parameters handed to us, we try to
 	 * probe them
 	 */
 	if (radeon_probe_pll_params(rinfo) == 0) {
@@ -701,6 +770,22 @@
        	printk(KERN_INFO "radeonfb: Used default PLL infos\n");
 
 found:
+
+	/* Check and fix-up the PLL divisor if necessary */
+	if (rinfo->pll.ref_div < 2) {
+		int tmp = INPLL(PPLL_REF_DIV);
+		if (rinfo->family == CHIP_FAMILY_RS300) {
+			rinfo->pll.ref_div = (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
+		} else {
+			rinfo->pll.ref_div = tmp & PPLL_REF_DIV_MASK;
+		}
+		
+		/* Sane default */
+		if (rinfo->pll.ref_div < 2) {		
+			rinfo->pll.ref_div = 12;
+		}
+	}		
+
 	/*
 	 * Some methods fail to retreive SCLK and MCLK values, we apply default
 	 * settings in this case (200Mhz). If that really happne often, we could
@@ -2412,6 +2497,7 @@
 	 * We probably need to make sure this is the primary display,
 	 * but that is difficult without some arch support.
 	 */
+
 #ifdef CONFIG_X86
 	if (rinfo->bios_seg == NULL)
 		radeon_find_mem_vbios(rinfo);
@@ -2423,8 +2509,17 @@
 	if (rinfo->bios_seg == NULL && rinfo->is_mobility)
 		radeon_map_ROM(rinfo, pdev);
 
+	/* Check BIOS Type */
+	radeon_detect_bios_type(rinfo);
+	
 	/* Get informations about the board's PLL */
-	radeon_get_pllinfo(rinfo);
+	radeon_get_pll_info(rinfo);
+
+	/* Get informations about internal TMDS controller if any */
+	radeon_get_tmds_info(rinfo);
+
+	/* Get infos about connectors */
+	radeon_get_conn_info(rinfo);
 
 #ifdef CONFIG_FB_RADEON_I2C
 	/* Register I2C bus */
diff -aur aty-2.6.15/radeon_monitor.c aty/radeon_monitor.c
--- aty-2.6.15/radeon_monitor.c	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeon_monitor.c	2006-02-23 15:06:41.000000000 -0500
@@ -1,6 +1,29 @@
 #include "radeonfb.h"
 #include "../edid.h"
 
+/*
+ * TMDS PLL configuration table, taken from X.org
+ */
+static const struct radeon_tmds_pll_info default_tmds_pll[CHIP_FAMILY_LAST][4] =
+{
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_UNKNOW*/
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_LEGACY*/
+    {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RADEON*/
+    {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV100*/
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_RS100*/
+    {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV200*/
+    {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RS200*/
+    {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_R200*/
+    {{15500, 0x81b}, {0xffffffff, 0x83f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV250*/
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_RS300*/
+    {{13000, 0x400f4}, {15000, 0x400f7}, {0xffffffff, 0x400f7/*0x40111*/}, {0, 0}},	/*CHIP_FAMILY_RV280*/
+    {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},		/*CHIP_FAMILY_R300*/
+    {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},		/*CHIP_FAMILY_R350*/
+    {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV350*/
+    {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV380*/
+    {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},		/*CHIP_FAMILY_R420*/
+};
+
 static struct fb_var_screeninfo radeonfb_default_var = {
 	.xres		= 640,
 	.yres		= 480,
@@ -160,21 +183,65 @@
 #endif /* CONFIG_PPC_OF */
 
 
-static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
+int __devinit radeon_get_lvds_info_atom(struct radeonfb_info *rinfo)
+{
+	unsigned long tmp;
+
+	if (!rinfo->bios_seg)
+		return -ENODEV;
+
+	tmp = BIOS_IN16(rinfo->atom_data_start + 16);
+	if (!tmp) {
+		printk(KERN_ERR "radeonfb: No LVDS panel info in BIOS\n");
+		rinfo->panel_info.pwr_delay = 200;
+		return -ENODEV;
+	}
+	
+	rinfo->panel_info.xres = BIOS_IN16(tmp+6);
+	rinfo->panel_info.yres = BIOS_IN16(tmp+10);
+	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+40);
+	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;
+	
+	/* No special divider combinations? */
+	
+	rinfo->panel_info.hblank = BIOS_IN16(tmp+8);
+	rinfo->panel_info.hOver_plus = BIOS_IN16(tmp+14);
+	rinfo->panel_info.hSync_width = BIOS_IN16(tmp+16);
+	rinfo->panel_info.vblank = BIOS_IN16(tmp+12);
+	rinfo->panel_info.vOver_plus = BIOS_IN16(tmp+18);
+	rinfo->panel_info.vSync_width = BIOS_IN16(tmp+20);
+	rinfo->panel_info.clock = BIOS_IN16(tmp+4);
+	
+	/* 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;
+	
+	return 0;
+}
+
+int __devinit radeon_get_lvds_info_legacy(struct radeonfb_info *rinfo)
 {
 	unsigned long tmp, tmp0;
 	char stmp[30];
 	int i;
 
 	if (!rinfo->bios_seg)
-		return 0;
+		return -ENODEV;
 
 	if (!(tmp = BIOS_IN16(rinfo->fp_bios_start + 0x40))) {
-		printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
+		printk(KERN_ERR "radeonfb: No LVDS panel info in BIOS\n");
 		rinfo->panel_info.pwr_delay = 200;
-		return 0;
+		return -ENODEV;
 	}
-
+	
 	for(i=0; i<24; i++)
 		stmp[i] = BIOS_IN8(tmp+i+1);
 	stmp[24] = 0;
@@ -182,13 +249,13 @@
 	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.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
 	 */
@@ -203,6 +270,7 @@
 		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);
@@ -226,7 +294,7 @@
 			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);
@@ -235,57 +303,507 @@
 			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;
+			
+			return 0;
 		}
 	}
+
 	RTRACE("Didn't find panel in BIOS table !\n");
 
+	return -ENODEV;
+}
+
+/*
+ * Get informations about TMDS controllers and their setup at
+ * different operating frequencies
+ */
+void __devinit radeon_get_tmds_info(struct radeonfb_info *rinfo)
+{
+	int i;
+
+	/* Get default TMDS infos for this chip */
+	for (i=0; i<4; i++) {
+		rinfo->tmds_pll[i].value =
+			default_tmds_pll[rinfo->family][i].value;
+		rinfo->tmds_pll[i].freq =
+			default_tmds_pll[rinfo->family][i].freq;
+	}
+
+	/* Get whatever the firmware provides */
+	if (rinfo->radeon_get_tmds_info) {
+		rinfo->radeon_get_tmds_info(rinfo);
+		// XXX Do we care about the return value?
+	}
+}
+
+int __devinit radeon_get_tmds_info_legacy(struct radeonfb_info *rinfo)
+{
+	int offset, i, n, rev;
+
+	offset = BIOS_IN16(rinfo->fp_bios_start + 0x34);
+	if (offset == 0)
+		return -ENODEV;
+
+	rev = BIOS_IN8(offset);
+	printk(KERN_INFO "DFP table revision: %d\n", rev);
+
+	switch(rev) {
+	case 3:
+		n = BIOS_IN8(offset + 5) + 1;
+		if (n > 4)
+			n = 4;
+		for (i = 0; i < n; i++) {
+			/* Looks bogus ... but that's what is in X.org */
+			rinfo->tmds_pll[i].value =
+				BIOS_IN32(offset+i*10+0x08);
+			rinfo->tmds_pll[i].freq =
+				BIOS_IN16(offset+i*10+0x10);
+		}
+		return 0;
+
+	/* revision 4 has some problem as it appears in RV280,
+	 * comment it off for now, use default instead
+	 */
+#if 0
+	case 4:
+		stride = 0;
+		n = BIOS_IN8(offset  5) + 1;
+		if (n > 4)
+			n = 4;
+		for (i = 0; i < n; i++) {
+			rinfo->tmds_pll[i].value =
+				BIOS_IN32(tmp+stride+0x08);
+			rinfo->tmds_pll[i].freq =
+				BIOS_IN16(tmp+stride+0x10);
+			if (i == 0)
+				stride += 10;
+			else
+				stride += 6;
+		}
+		return 0;
+#endif
+	}
+	return -ENODEV;
+}
+
+int __devinit radeon_get_tmds_info_atom(struct radeonfb_info *rinfo)
+{
+	int offset, i, maxfreq;
+
+	offset = BIOS_IN16(rinfo->atom_data_start + 18);
+	if (offset == 0)
+		return -ENODEV;
+
+	maxfreq = BIOS_IN16(offset + 4);
+
+	for (i = 0; i < 4; i++) {
+		rinfo->tmds_pll[i].freq = BIOS_IN16(offset+i*6+6);
+		/* This assumes each field in TMDS_PLL has 6 bit as
+		 * in R300/R420
+		 */
+		rinfo->tmds_pll[i].value =
+			((BIOS_IN8(offset+i*6+8) & 0x3f) |
+			 ((BIOS_IN8(offset+i*6+10) & 0x3f)<<6) |
+			 ((BIOS_IN8(offset+i*6+9) & 0xf)<<12) |
+			 ((BIOS_IN8(offset+i*6+11) & 0xf)<<16));
+		printk(KERN_INFO "TMDS PLL from BIOS: %ld %x\n",
+			   rinfo->tmds_pll[i].freq, rinfo->tmds_pll[i].value);
+
+		if (maxfreq == rinfo->tmds_pll[i].freq) {
+		    rinfo->tmds_pll[i].freq = 0xffffffff;
+		    break;
+		}
+	}
 	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
+
+/*
+ * Get informations about the various connectors on this card. This is
+ * the most prone to fail function as various firmwares tend to say
+ * crap or not give any info at all. The Open Firmware version is just
+ * a table of known cards for now for example. We'll probably need some
+ * additional module params to force different settings in case of
+ * misdetection here.
+ *
+ * This doesn _not_ try actual probing of whatever is plugged on those
+ * various connectors. This will be done later. We do store whatever
+ * probing info the firmware gives us though
  */
-static void __devinit radeon_parse_connector_info(struct radeonfb_info *rinfo)
+void __devinit radeon_get_conn_info(struct radeonfb_info *rinfo)
 {
-	int offset, chips, connectors, tmp, i, conn, type;
+	int i;
 
-	static char* __conn_type_table[16] = {
-		"NONE", "Proprietary", "CRT", "DVI-I", "DVI-D", "Unknown", "Unknown",
-		"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown",
-		"Unknown", "Unknown", "Unknown"
+	const char *conn_type_name[] = {
+		"NONE", "VGA", "DVI-I", "DVI-D", "DVI-A", "S-Video",
+		"Composite Video", "Internal Panel", "Digital",
+		"Unsupported", "Proprietary"
+	};
+	const char *mon_type_name[] = {
+		"None", "CRT", "LVDS Flat panel",
+		"DVI Flat panel", "Composite TV", "S-Video TV"
 	};
 
-	if (!rinfo->bios_seg)
-		return;
+	/* Clear table */
+	for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
+		rinfo->connectors[i].conn_type	= conn_none;
+		rinfo->connectors[i].ddc_type	= ddc_none;
+		rinfo->connectors[i].dac_type	= dac_unknown;
+		rinfo->connectors[i].tmds_type	= tmds_unknown;
+		rinfo->connectors[i].mon_type	= MT_UNKNOWN;
+	}
 
-	offset = BIOS_IN16(rinfo->fp_bios_start + 0x50);
-	if (offset == 0) {
-		printk(KERN_WARNING "radeonfb: No connector info table detected\n");
-		return;
+	/* Try to obtain infos from firmware */
+	if (rinfo->radeon_get_conn_info) {
+		if (!rinfo->radeon_get_conn_info(rinfo)) {
+			goto found;
+		}
+	}
+
+	printk(KERN_INFO "radeonfb: No connector infos, using defaults...\n");
+
+	/* Here, we use defaults that are common enough ... we hope
+	 * For a mobility chip, we assume LVDS is on primary
+	 */
+	if (rinfo->is_mobility) {
+		rinfo->connectors[0].conn_type	= conn_lvds;
+		rinfo->connectors[0].ddc_type	= ddc_dvi;
+		rinfo->connectors[0].dac_type	= dac_primary;
+		rinfo->connectors[0].tmds_type	= tmds_unknown;
+		rinfo->connectors[0].mon_type	= MT_UNKNOWN;
+
+		rinfo->connectors[1].conn_type	= conn_dvi_d;
+		rinfo->connectors[1].ddc_type	= ddc_vga;
+		rinfo->connectors[1].dac_type	= dac_primary;
+		rinfo->connectors[1].tmds_type	= tmds_internal;
+		rinfo->connectors[1].mon_type	= MT_UNKNOWN;
+
+		rinfo->connectors[2].conn_type	= conn_stv;
+		rinfo->connectors[2].ddc_type	= ddc_none;
+		rinfo->connectors[2].dac_type	= dac_tvdac;
+		rinfo->connectors[2].tmds_type	= tmds_unknown;
+		rinfo->connectors[2].mon_type	= MT_UNKNOWN;
+	} else {
+		rinfo->connectors[0].conn_type	= conn_dvi_d;
+		rinfo->connectors[0].ddc_type	= ddc_dvi;
+		rinfo->connectors[0].dac_type	= dac_tvdac;
+		rinfo->connectors[0].tmds_type	= tmds_internal;
+		rinfo->connectors[0].mon_type	= MT_UNKNOWN;
+
+		rinfo->connectors[1].conn_type	= conn_vga;
+		rinfo->connectors[1].ddc_type	= ddc_vga;
+		rinfo->connectors[1].dac_type	= dac_primary;
+		rinfo->connectors[1].tmds_type	= tmds_external;
+		rinfo->connectors[1].mon_type	= MT_UNKNOWN;
+	}
+
+ found:
+	/* Now, we do additional fixups */
+
+	/* RS300 has only one DAC, force TV-DAC on VGA port */
+	if (rinfo->family == CHIP_FAMILY_RS300) {
+		for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
+			if (rinfo->connectors[i].conn_type == conn_vga)
+				rinfo->connectors[i].dac_type = dac_tvdac;
+			else if (rinfo->connectors[i].dac_type != dac_unknown)
+				rinfo->connectors[i].dac_type = dac_primary;
+		}
 	}
 
-	/* Don't do much more at this point but displaying the data if
-	 * DEBUG is enabled
+	/* Single head chips all use primary DAC */
+	if (!rinfo->has_CRTC2)
+		rinfo->connectors[0].dac_type = dac_primary;
+
+	for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
+		if (rinfo->connectors[i].conn_type == conn_none)
+			continue;
+		printk(KERN_INFO " * Connector %d is %s. Monitor: %s\n", i,
+		       conn_type_name[rinfo->connectors[i].conn_type],		       
+			rinfo->connectors[i].mon_type == MT_UNKNOWN  ?
+				"Not Probed" : 
+				mon_type_name[rinfo->connectors[i].mon_type]);
+		printk(KERN_INFO "   ddc port: %d, dac: %d, tmds: %d\n",
+		       rinfo->connectors[i].ddc_type,
+		       rinfo->connectors[i].dac_type,
+		       rinfo->connectors[i].tmds_type);
+	}
+	return;
+ }
+
+
+int __devinit radeon_get_conn_info_atom(struct radeonfb_info *rinfo)
+{
+	int i, j, offset, valids;
+	int ids[RADEON_MAX_CONNECTORS];
+	u16 portinfo, tmp0;
+	int conn_index = 0;
+	int conn_add = 2;
+	int idx = 0;
+	int ddc_type, dac_type, conn_type, tmds_type, port_id;
+	int connector_found = 0;
+	
+	offset = BIOS_IN16(rinfo->atom_data_start + 22);
+	if (offset == 0)
+		return -ENODEV;
+
+	/* Again, I slightly modified X.org algorithm. I assign "primary" outputs
+	 * to entries 0 and 1, and anything else goes after 2.
+	 *
+	 * Also, I keep an array of all port IDs matching connectors[] array,
+	 * unlike X which limits itself to "crtc"'s
 	 */
-	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)
+	for (i = 0; i < RADEON_MAX_CONNECTORS; i++)
+		ids[i] = -1;
+
+	valids = BIOS_IN16(offset + 4);
+	for (i = 0; i < 8; i++) {
+		if (!(valids & (1 << i)))
+			continue;
+		portinfo = BIOS_IN16(offset + 6 + i*2);
+
+		conn_type = (portinfo >> 4) & 0xf;
+		dac_type = (portinfo & 0xf) - 1;
+		port_id = (portinfo >> 8) & 0xf;
+		ddc_type = ddc_none;
+
+		if ((tmp0 = BIOS_IN16(rinfo->atom_data_start + 24))) {
+			switch(BIOS_IN16(tmp0 + 4 + (27 * port_id)) * 4) {
+			case GPIO_MONID:
+				ddc_type = ddc_monid;
+				break;
+			case GPIO_DVI_DDC:
+				ddc_type = ddc_dvi;
+				break;
+			case GPIO_VGA_DDC:
+				ddc_type = ddc_vga;
 				break;
-			offset += 2;
-			type = (tmp >> 12) & 0x0f;
-			RTRACE("  * connector %d of type %d (%s) : %04x\n",
-			       conn, type, __conn_type_table[type], tmp);
+			case GPIO_CRT2_DDC:
+				ddc_type = ddc_crt2;
+				break;
+			default:
+				ddc_type = ddc_none;
+				break;
+			}
+		}
+
+		if (i == 3)
+			tmds_type = tmds_internal;
+		else if (i == 7)
+			tmds_type = tmds_external;
+		else
+			tmds_type = tmds_unknown;
+
+		RTRACE("index %d port %d conn %d dac %d ddc %d tmds %d\n", i, port_id, conn_type, dac_type, ddc_type, tmds_type);
+		
+		/* Ok, now we have the port ID, look for an existing port
+		 * already using this ID
+		 */
+		for (j = 0; j < RADEON_MAX_CONNECTORS; j++) {
+			if (port_id != ids[j])
+				continue;
+			/* Gotcha, just "update" values */
+			if (tmds_type != tmds_unknown)
+				rinfo->connectors[j].tmds_type = tmds_type;
+			if (rinfo->connectors[j].dac_type == dac_unknown)
+				rinfo->connectors[j].dac_type = dac_type;
+			if (rinfo->connectors[j].ddc_type == dac_unknown)
+				rinfo->connectors[j].ddc_type = dac_type;
+			continue;
+		}
+
+		conn_index = (ddc_type == ddc_dvi || conn_index == 1) ? 0 : 1;
+
+		/* if the port is a TV port, or both connectors are already
+		 * assigned, assign it after further in the table
+		 */
+		if (conn_type == conn_ctv || conn_type == conn_stv ||
+		    (rinfo->connectors[0].conn_type != conn_none &&
+		     rinfo->connectors[1].conn_type))
+			idx = conn_add++;
+		else
+			idx = conn_index;
+
+		rinfo->connectors[idx].tmds_type = tmds_type;
+		rinfo->connectors[idx].dac_type = dac_type;
+		rinfo->connectors[idx].ddc_type = ddc_type;
+		rinfo->connectors[idx].conn_type = conn_type;
+
+		/* increment connector_found for primary connectors only */
+		if (idx < 2)
+			connector_found += (idx + 1);
+	}
+
+	if (connector_found == 0)
+		return -ENODEV;
+
+	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
+ */
+int __devinit radeon_get_conn_info_legacy(struct radeonfb_info *rinfo)
+{
+	int offset, i, entry, tmp;
+	int ddc_type, dac_type, conn_type, tmds_type;
+	int conn_index = 0;
+	int conn_add = 2;
+	int idx = 0;
+
+	/* Convert legacy to real connector types */
+	const enum radeon_conn_type legacy_conn_to_type[] = {
+		conn_none,
+		conn_proprietary,
+		conn_vga,
+		conn_dvi_i,
+		conn_dvi_d,
+		conn_ctv,
+		conn_stv,
+		conn_unsupported,
+	};
+
+	/* Some laptops only have one connector (VGA) listed in the connector
+	 * table, we need to add LVDS in as a non-DDC display.
+	 * Note, we can't assume the listed VGA will be filled in PortInfo[0],
+	 * when walking through connector table. connector_found has following
+	 * meaning:
+	 * 0 -- nothing found,
+	 * 1 -- only connectors[0] filled,
+	 * 2 -- only connectors[1] filled,
+	 * 3 -- both are filled.
+	 *
+	 * Note: I modified X.org algorithm to add additional entries if any
+	 * after the second table slot. Those entries do not affect the value
+	 * of connector_found. --BenH.
+	 */
+	int connector_found = 0;
+
+	offset = BIOS_IN16(rinfo->fp_bios_start + 0x50);
+	if (offset == 0)
+		return -ENODEV;
+
+	for (i = 1; i < 4; i++) {
+		entry = offset + i*2;
+
+		/* End of table */
+		if (!BIOS_IN8(entry) && i > 1)
+			break;
+
+		/* Read table entry, check connector type */
+		tmp = BIOS_IN16(entry);
+		conn_type = (tmp >> 12) & 0xf;
+		if (conn_type == legacy_conn_none)
+			continue;
+		ddc_type = (tmp >> 8) & 0xf;
+		dac_type = (tmp & 0x01) ? dac_tvdac : dac_primary;
+		tmds_type = (tmp & 0x10) ? tmds_external : tmds_internal;
+
+		/* same connector */
+		if (connector_found > 0) {
+			if (rinfo->connectors[conn_index].ddc_type == ddc_type)
+				continue;
+		}
+
+		/* sanity checks */
+		if (ddc_type > ddc_crt2)
+			ddc_type = ddc_none;
+		if (conn_type > legacy_conn_unsupported)
+			conn_type = legacy_conn_unsupported;
+		if (conn_type != legacy_conn_dvi_d &&
+		    conn_type != legacy_conn_dvi_i &&
+		    tmds_type == tmds_internal)
+			tmds_type= tmds_unknown;
+
+		/* convert connector type */
+		conn_type = legacy_conn_to_type[conn_type];
+
+		/* internal DDC_DVI port will get assigned to connector[0], or
+		 * if there is no DDC_DVI (like in some IGPs).
+		 */
+		conn_index = (ddc_type == ddc_dvi || conn_index == 1) ? 0 : 1;
+
+		/* if the port is a TV port, or both connectors are already
+		 * assigned, assign it after further in the table
+		 */
+		if (conn_type == conn_ctv || conn_type == conn_stv ||
+		    (rinfo->connectors[0].conn_type != conn_none &&
+		     rinfo->connectors[1].conn_type))
+			idx = conn_add++;
+		else
+			idx = conn_index;
+
+		/* if table full, exit */
+		if (idx >= RADEON_MAX_CONNECTORS) {
+			printk(KERN_WARNING "radeonfb: Connector table full !\n");
+			break;
 		}
+		rinfo->connectors[idx].conn_type	= conn_type;
+		rinfo->connectors[idx].ddc_type		= ddc_type;
+		rinfo->connectors[idx].dac_type		= dac_type;
+		rinfo->connectors[idx].tmds_type	= tmds_type;
+
+		/* increment connector_found for primary connectors only */
+		if (idx < 2)
+			connector_found += (idx + 1);
+	}
+
+	if (rinfo->is_mobility) {
+	    /* For the cases where only one VGA connector is found,
+	     * we assume LVDS is not listed in the connector table,
+	     * add it in here as the first port.
+	     *
+	     * TODO: Check what's up with laptops that have a DVI output
+	     * and no LVDS entry in the table. I suspect some thinkpads
+	     * may play trick with us here... We may want to check the
+	     * presence of a panel via LVDS_GEN_CNTL to be sure...
+	     */
+	    if ((connector_found < 3) &&
+		(rinfo->connectors[idx].conn_type == conn_vga)) {
+		    if (connector_found == 1) {
+			    memcpy(&rinfo->connectors[1],
+				   &rinfo->connectors[0],
+				   sizeof(struct radeon_connector));
+		    }
+		    /* Fixme: TV DAC is probably elsewhere ... */
+		    rinfo->connectors[0].dac_type = dac_tvdac;
+		    rinfo->connectors[0].tmds_type = tmds_unknown;
+		    rinfo->connectors[0].ddc_type = ddc_none;
+		    rinfo->connectors[0].conn_type = conn_proprietary;
+
+		    printk(KERN_WARNING "radeonfb: LVDS port is not in connector table, added in.\n");
+		    if (connector_found == 0)
+			    connector_found = 1;
+		    else
+			    connector_found = 3;
+	    }
+
+	    /* Check for LCD DDC info table */
+	    if ((offset = BIOS_IN16(rinfo->fp_bios_start + 0x42))) {
+		    if ((tmp = BIOS_IN16(offset + 0x15))) {
+			    if ((ddc_type = BIOS_IN8(tmp+2) & 0x07)) {
+				    rinfo->connectors[0].ddc_type = ddc_type;
+				    printk(KERN_WARNING "radeonfb: LCD DDC Info Table found, "
+						"forcing primary port to %d\n",
+						ddc_type);
+			    }
+		    }
+	    }
+	} else if (connector_found == 2) {
+		memcpy(&rinfo->connectors[0], &rinfo->connectors[1],
+			sizeof (struct radeon_connector));
+		rinfo->connectors[1].dac_type = dac_unknown;
+		rinfo->connectors[1].tmds_type = tmds_unknown;
+		rinfo->connectors[1].ddc_type = ddc_none;
+		rinfo->connectors[1].conn_type = conn_none;
+		connector_found = 1;
 	}
+
+	if (connector_found == 0)
+		return -ENODEV;
+
+	/* External TMDS Table, not used now */
+	return 0;
 }
 
 
@@ -434,8 +952,6 @@
 #endif
 	int tmp, i;
 
-	radeon_parse_connector_info(rinfo);
-
 	if (radeon_parse_monitor_layout(rinfo, monitor_layout)) {
 
 		/*
@@ -445,7 +961,7 @@
 		 * a layout for each card ?
 		 */
 
-		RTRACE("Using specified monitor layout: %s", monitor_layout);
+		RTRACE("Using specified monitor layout: %s\n", monitor_layout);
 #ifdef CONFIG_FB_RADEON_I2C
 		if (!ignore_edid) {
 			if (rinfo->mon1_type != MT_NONE)
@@ -519,7 +1035,8 @@
 		/*
 		 * Check for cards with reversed DACs or TMDS controllers using BIOS
 		 */
-		if (rinfo->bios_seg &&
+
+		if (rinfo->bios_seg && !rinfo->is_atom_bios &&
 		    (tmp = BIOS_IN16(rinfo->fp_bios_start + 0x50))) {
 			for (i = 1; i < 4; i++) {
 				unsigned int tmp0;
@@ -663,7 +1180,7 @@
 		rinfo->panel_info.post_divider = (ppll_divn >> 16) & 0x7;
 		rinfo->panel_info.use_bios_dividers = 1;
 
-		printk(KERN_DEBUG "radeonfb: Using Firmware dividers 0x%08x "
+		printk(KERN_INFO "Using Firmware dividers 0x%08x "
 		       "from PPLL %d\n",
 		       rinfo->panel_info.fbk_divider |
 		       (rinfo->panel_info.post_divider << 16),
@@ -741,8 +1258,12 @@
 	/*
 	 * First check out what BIOS has to say
 	 */
-	if (rinfo->mon1_type == MT_LCD)
-		radeon_get_panel_info_BIOS(rinfo);
+	if (rinfo->mon1_type == MT_LCD) {
+		if (rinfo->radeon_get_lvds_info) {
+			rinfo->radeon_get_lvds_info(rinfo);
+			// XXX Do we care about the return value?
+		}
+	}
 
 	/*
 	 * Parse EDID detailed timings and deduce panel infos if any. Right now
diff -aur aty-2.6.15/radeonfb.h aty/radeonfb.h
--- aty-2.6.15/radeonfb.h	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeonfb.h	2006-02-23 15:00:24.000000000 -0500
@@ -91,7 +91,8 @@
 /*
  * Monitor types
  */
-enum radeon_montype {
+enum radeon_mon_type {
+	MT_UNKNOWN = -1,
 	MT_NONE = 0,
 	MT_CRT,		/* CRT */
 	MT_LCD,		/* LCD */
@@ -103,8 +104,8 @@
 /*
  * DDC i2c ports
  */
-enum ddc_type {
-	ddc_none,
+enum radeon_ddc_type {
+	ddc_none = 0,
 	ddc_monid,
 	ddc_dvi,
 	ddc_vga,
@@ -114,14 +115,69 @@
 /*
  * Connector types
  */
-enum conn_type {
-	conn_none,
-	conn_proprietary,
-	conn_crt,
-	conn_DVI_I,
-	conn_DVI_D,
+enum radeon_legacy_conn_type {
+	legacy_conn_none = 0,
+	legacy_conn_proprietary,
+	legacy_conn_crt,
+	legacy_conn_dvi_i,
+	legacy_conn_dvi_d,
+	legacy_conn_ctv,
+	legacy_conn_stv,
+	legacy_conn_unsupported,
 };
 
+enum radeon_conn_type {
+	conn_none = 0,
+	conn_vga,
+	conn_dvi_i,
+	conn_dvi_d,
+	conn_dvi_a,
+	conn_stv,
+	conn_ctv,
+	conn_lvds,
+	conn_digital,
+	conn_unsupported,
+	conn_proprietary
+};
+
+/*
+ * DAC types
+ */
+enum radeon_dac_type {
+	dac_unknown = -1,
+	dac_primary = 0,
+	dac_tvdac = 1,
+};
+
+/*
+ * TMDS types
+ */
+enum radeon_tmds_type {
+	tmds_unknown = -1,
+	tmds_internal = 0,
+	tmds_external = 1,
+};
+
+/*
+ * Each connector gets this structure associated with it,
+ * containing infos about the connector wiring and about
+ * whatever has been detected on it
+ */
+struct radeon_connector {
+	enum radeon_conn_type	conn_type;
+	enum radeon_ddc_type	ddc_type;
+	enum radeon_dac_type	dac_type;
+	enum radeon_tmds_type	tmds_type;
+	enum radeon_mon_type	mon_type;
+	u8			*edid;
+	struct fb_videomode	*modedb;
+	unsigned int		modedb_size;
+};
+
+/*
+ * Currently, the driver deals with at most 4 connectors
+ */
+#define RADEON_MAX_CONNECTORS	4
 
 /*
  * PLL infos
@@ -129,11 +185,19 @@
 struct pll_info {
 	int ppll_max;
 	int ppll_min;
-	int sclk, mclk;
+	int sclk;
+	int mclk;
 	int ref_div;
 	int ref_clk;
 };
 
+/*
+ * TMDS PLL infos
+ */
+struct radeon_tmds_pll_info {
+	long	freq;
+	u32	value;
+};
 
 /*
  * This structure contains the various registers manipulated by this
@@ -298,6 +362,18 @@
 	void __iomem		*bios_seg;
 	int			fp_bios_start;
 
+	int                     is_atom_bios;
+	int                     atom_data_start;
+	
+	/* BIOS Functions */
+	int                     (*radeon_get_pll_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_lvds_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_conn_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_tmds_info)(struct radeonfb_info *rinfo);
+
+	/* Connector infos */
+	struct radeon_connector		connectors[RADEON_MAX_CONNECTORS];
+
 	u32			pseudo_palette[17];
 	struct { u8 red, green, blue, pad; }
 				palette[256];
@@ -319,6 +395,8 @@
 	int			reversed_DAC;
 	int			reversed_TMDS;
 	struct panel_info	panel_info;
+	struct radeon_tmds_pll_info	tmds_pll[4];
+
 	int			mon1_type;
 	u8			*mon1_EDID;
 	struct fb_videomode	*mon1_modedb;
@@ -625,4 +703,14 @@
 extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
 			       int reg_only);
 
+/* Bios functions.  Fix this. */
+extern void __devinit radeon_get_conn_info(struct radeonfb_info *rinfo);
+extern void __devinit radeon_get_tmds_info(struct radeonfb_info *rinfo);
+
+extern int __devinit radeon_get_lvds_info_atom(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_lvds_info_legacy(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_conn_info_atom(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_conn_info_legacy(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_tmds_info_legacy(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_tmds_info_atom(struct radeonfb_info *rinfo);
 #endif /* __RADEONFB_H__ */
Only in aty-2.6.15: radeonfb.ko

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4)
  2006-02-23 22:36         ` [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4) Stuffed Crust
@ 2006-02-23 23:15           ` Benjamin Herrenschmidt
  2006-03-01  3:33             ` Stuffed Crust
  0 siblings, 1 reply; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-02-23 23:15 UTC (permalink / raw)
  To: Stuffed Crust; +Cc: linux-fbdev-devel


> And of course, OpenFirmware integration, and doing something with the 
> TMDS PLL information. 

Yeah. Also, I need to create some kind of table of known OpenFirmware
cards and their associated connector maping since unfortunately, the
firmware driver doesn't provide the information... But I'll do that
afterward.

> I'm unsure how to best to proceed from here, so this patch is it until 
> I get some more directed feedback.  

I'll review properly next week.

Ben.




-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4)
  2006-02-23 23:15           ` Benjamin Herrenschmidt
@ 2006-03-01  3:33             ` Stuffed Crust
  2006-03-01  3:46               ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 16+ messages in thread
From: Stuffed Crust @ 2006-03-01  3:33 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linux-fbdev-devel

[-- Attachment #1: Type: text/plain, Size: 1440 bytes --]

On Fri, Feb 24, 2006 at 10:15:21AM +1100, Benjamin Herrenschmidt wrote:
> > I'm unsure how to best to proceed from here, so this patch is it until 
> > I get some more directed feedback.  
> 
> I'll review properly next week.

I've since completely gutted the radeon_probe_screens code, and now it 
takes advantage of (and relies heavily on) the connector table stuffs.  
It's far, far cleaner now, yay!   

I basically have two chunks of remaining code to write:

1) The OpenFirmware stuff, which basically requires some re-jiggering of 
   arguments passed in and return codes.  
2) User-specified layout code isn't smart enough to try the secondary 
   connectors if the primary connectors don't have anything connected.
   (This could be lessened by extending the syntax to explicitly state 
    CRT,CRT2,DFP,DFP2,etc..)

Then there's the big open question of how we should allow the user
to override the connector table, in case we mis-detect or don't have a
table to begin with.  If we have DDC enabled we can build a 
connector table by probing all DDC ports, using the existing logic. 

Thoughts?

(This work builds on the radeon-atom-4 patch, and is definately in the 
 "heavily experimental" stage.  I'll post a patch once I have time to 
 clean it up and fix the first two points)

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL
Quidquid latine dictum sit, altum viditur.

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4)
  2006-03-01  3:33             ` Stuffed Crust
@ 2006-03-01  3:46               ` Benjamin Herrenschmidt
  2006-03-01 16:56                 ` Gabor Gombas
  2006-03-01 20:36                 ` Stuffed Crust
  0 siblings, 2 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-03-01  3:46 UTC (permalink / raw)
  To: Stuffed Crust; +Cc: linux-fbdev-devel


> I basically have two chunks of remaining code to write:
> 
> 1) The OpenFirmware stuff, which basically requires some re-jiggering of 
>    arguments passed in and return codes.  

Or just write a small howto and I will do the job :) I've been very busy
lately mostly due to my newborn child, thus I didn't help more than
that, but I'm really interested in your work. I also want to bring along
a bunch of fixes like the memory mapping fixes I've been doing on X.org
radeon driver recently.

> 2) User-specified layout code isn't smart enough to try the secondary 
>    connectors if the primary connectors don't have anything connected.
>    (This could be lessened by extending the syntax to explicitly state 
>     CRT,CRT2,DFP,DFP2,etc..)

Agreed. (And X too btw ...)

In fact, we need a way to provide the driver with the full connector
mapping I think, in case it can't be obtained from the firmware.

> Then there's the big open question of how we should allow the user
> to override the connector table, in case we mis-detect or don't have a
> table to begin with.  If we have DDC enabled we can build a 
> connector table by probing all DDC ports, using the existing logic. 

module option is the easy/cheap way but sucks in some ways... sysfs per
device instance is nice but a bit "too late" unless we have a way for
the driver to re-probe... which is a good thing to have anyway since we
might finally deal with screen hotplug :)

In fact, a mix of both might do the trick... module/kernel argument for
a default override and sysfs for "live" override ?

I would however not bother too much at the moment with that. Let's get
the rest working first

> Thoughts?
> 
> (This work builds on the radeon-atom-4 patch, and is definately in the 
>  "heavily experimental" stage.  I'll post a patch once I have time to 
>  clean it up and fix the first two points)

Thanks.

I'll look at your stuff in more detail asap and will then let it simmer
in -mm if I'm happy enough for at least a kernel version...

Ben.




-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4)
  2006-03-01  3:46               ` Benjamin Herrenschmidt
@ 2006-03-01 16:56                 ` Gabor Gombas
  2006-03-01 20:36                 ` Stuffed Crust
  1 sibling, 0 replies; 16+ messages in thread
From: Gabor Gombas @ 2006-03-01 16:56 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Stuffed Crust

On Wed, Mar 01, 2006 at 02:46:29PM +1100, Benjamin Herrenschmidt wrote:

> In fact, a mix of both might do the trick... module/kernel argument for
> a default override and sysfs for "live" override ?

Nowadays you can use sysfs to both display and modify module parameters,
so making use of that would be the best.

Gabor

-- 
     ---------------------------------------------------------
     MTA SZTAKI Computer and Automation Research Institute
                Hungarian Academy of Sciences
     ---------------------------------------------------------


-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: Re: [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4)
  2006-03-01  3:46               ` Benjamin Herrenschmidt
  2006-03-01 16:56                 ` Gabor Gombas
@ 2006-03-01 20:36                 ` Stuffed Crust
  2006-03-01 21:34                   ` [patch] [radeonfb] Radeon M26 and ATOM bios support (take 5) Stuffed Crust
  1 sibling, 1 reply; 16+ messages in thread
From: Stuffed Crust @ 2006-03-01 20:36 UTC (permalink / raw)
  To: linux-fbdev-devel

[-- Attachment #1: Type: text/plain, Size: 1887 bytes --]

On Wed, Mar 01, 2006 at 02:46:29PM +1100, Benjamin Herrenschmidt wrote:
> Or just write a small howto and I will do the job :) I've been very busy
> lately mostly due to my newborn child, thus I didn't help more than

Congratulations (and my condolances :P) on the kid!

> that, but I'm really interested in your work. I also want to bring along
> a bunch of fixes like the memory mapping fixes I've been doing on X.org
> radeon driver recently.

I often get lockups in a radeonfb console, could that be related 
to the mmap stuff you've been fixing?

> In fact, we need a way to provide the driver with the full connector
> mapping I think, in case it can't be obtained from the firmware.

I have a crude "bootstrap the connector table via ddc probing" but it 
has problems with secondary heads as well, and obviously without ddc it 
won't work anyway.  Either the user provices a connector table or we 
pray the defaults, one way or another, are sane.  :)

Of course, once a non-detectable layout is known, we can add it into an
appropriate driver table.

> module option is the easy/cheap way but sucks in some ways... sysfs per
> device instance is nice but a bit "too late" unless we have a way for
> the driver to re-probe... which is a good thing to have anyway since we
> might finally deal with screen hotplug :)

MM, that would be nice.. but I don't want to think about the console
interactions required for display reconfiguration.  That said, the new 
code is capable of re-detecting at any time, but I'm sure the radeonfb 
core has issues.

> I'll look at your stuff in more detail asap and will then let it simmer
> in -mm if I'm happy enough for at least a kernel version...

I'm about to mail out v5 of the patch.

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL
Quidquid latine dictum sit, altum viditur.

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [patch] [radeonfb] Radeon M26 and ATOM bios support (take 5)
  2006-03-01 20:36                 ` Stuffed Crust
@ 2006-03-01 21:34                   ` Stuffed Crust
  2006-03-01 21:47                     ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 16+ messages in thread
From: Stuffed Crust @ 2006-03-01 21:34 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Benjamin Herrenschmidt


[-- Attachment #1.1: Type: text/plain, Size: 2794 bytes --]

On Wed, Mar 01, 2006 at 03:36:11PM -0500, Stuffed Crust wrote:
> I'm about to mail out v5 of the patch.

Signed-Off-By:  Solomon Peachy <pizza@shaftnet.org>

The short set of changes in this patch (against 2.6.15.4)

 * ATOM BIOS support for newer Radeon cards
 * Clean method of detecting and handling disparate BIOS types
 * Radeon RV410/M26/M26GL (aka Mobility X700/FireGL5000) card IDs
 * Default PLL clocks for R420 and variants
 * Handle bogus PLL divider with sane default.
 * All new connector/head detection code that uses bios/firmware 
   defaults whenever possible
 
I can split it apart into smaller logical chunks if desired, but I don't 
think anyone's in a hurry to merge this upstream just yet.  :)  So, 
anyone who has a radeon console and is feeling brave, please give this a 
whirl.  If you are specifying a monitor layout, try it without the 
layout string.  

This is the last of the work I can do on this thing, as I don't have the
hardware (or bug reports/feedback) to develop it further.

Version 5 of the Radeon-Atom patch is attached.  Changes since v4:

 * Completely reworks the old monitor detection code to use the
   connector table.  Cleaned out a ton of cruft.
 * Connector info and LVDS info are pulled on OpenFirmware systems,
   (untested -- I don't have appropriate hardware)
 * Add a module argument to disable BIOS connector tables.
 * Bootstraping the connector table via ddc probing.
 * If user specifies a specific layout, code isn't smart enough to try
   secondary heads if the primary head has nothing attached.  The 
   OpenFirmware code may have similar problems.

4th version changes since v3:

 * ATOM BIOS support for connector detection
 * TMDS parsing for both ATOM and Legacy BIOSes.
 * Additional PCI IDs forall known M26 variants
 * Default PLL clocks for R420 family

3rd version changes since v2:

 * Naming cleanups
 * Merge the OpenFirmware PLL detection into the function pointer mess.
 * Merge the oooold patch's legacy bios connector parsing code, As the
   original stuff didn't really do anything anyway, I don't see the harm.
 * Merge the ooold patch's atom bios connector parsing code, but it still
   doesn't work yet.  It's late and my eyes hurt.

2nd version changes since v1:

 * It has no additional functionality over the original patch, but it's 
   rearranged to be quite a bit cleaner, using function pointers for the 
   atom/legacy bits.

Original Patch:

 * Added PCI IDs for Radeon M26 (aka Mobility X700)
 * Detect ATOM BIOSes, pull PLL/LVDS details from ATOM BIOSes
 * Default PLL divider to 12, and catch obviously bogus results

 - Solomon
-- 
Solomon Peachy        				 ICQ: 1318344
Melbourne, FL
Quidquid latine dictum sit, altum viditur.

[-- Attachment #1.2: radeon-atom-5.diff --]
[-- Type: text/plain, Size: 77179 bytes --]

Only in aty: .radeon_accel.o.cmd
Only in aty: .radeon_base.o.cmd
Only in aty: .radeon_i2c.o.cmd
Only in aty: .radeon_monitor.o.cmd
Only in aty: .radeon_pm.o.cmd
Only in aty: .radeonfb.ko.cmd
Only in aty: .radeonfb.mod.o.cmd
Only in aty: .radeonfb.o.cmd
Only in aty: .tmp_versions
diff -aur aty-2.6.15/ati_ids.h aty/ati_ids.h
--- aty-2.6.15/ati_ids.h	2006-02-22 23:28:14.000000000 -0500
+++ aty/ati_ids.h	2006-02-23 15:32:21.000000000 -0500
@@ -185,6 +185,10 @@
 #define PCI_CHIP_R423_UQ                0x5551
 #define PCI_CHIP_R423_UR                0x5552
 #define PCI_CHIP_R423_UT                0x5554
+#define PCI_CHIP_RV410_564B             0x564A
+#define PCI_CHIP_RV410_564A             0x564B
+#define PCI_CHIP_RV410_5652             0x5652
+#define PCI_CHIP_RV410_VS               0x5653
 #define PCI_CHIP_MACH64VT		0x5654
 #define PCI_CHIP_MACH64VU		0x5655
 #define PCI_CHIP_MACH64VV		0x5656
Only in aty: radeon_accel.o
diff -aur aty-2.6.15/radeon_base.c aty/radeon_base.c
--- aty-2.6.15/radeon_base.c	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeon_base.c	2006-03-01 14:20:01.000000000 -0500
@@ -3,6 +3,7 @@
  *
  *	framebuffer driver for ATI Radeon chipset video boards
  *
+ *	Copyright 2006	Solomon Peachy <pizza@shaftnet.org>
  *	Copyright 2003	Ben. Herrenschmidt <benh@kernel.crashing.org>
  *	Copyright 2000	Ani Joshi <ajoshi@kernel.crashing.org>
  *
@@ -50,7 +51,7 @@
  */
 
 
-#define RADEON_VERSION	"0.2.0"
+#define RADEON_VERSION	"0.3.0"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -214,6 +215,10 @@
 	CHIP_DEF(PCI_CHIP_R420_JL,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JM,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JN,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_564A,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_564B,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_5652,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VS,	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
 	CHIP_DEF(PCI_CHIP_R420_JP,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UH,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UI,	R420,	CHIP_HAS_CRTC2),
@@ -265,6 +270,7 @@
 static int default_dynclk = -2;
 static int nomodeset = 0;
 static int ignore_edid = 0;
+static int ignore_conntable = 0;
 static int mirror = 0;
 static int panel_yres = 0;
 static int force_dfp = 0;
@@ -342,7 +348,7 @@
 	 * to phase out Open Firmware images.
 	 *
 	 * Currently, we only look at the first PCI data, we could iteratre and deal with
-	 * them all, and we should use fb_bios_start relative to start of image and not
+	 * them all, and we should use fp_bios_start relative to start of image and not
 	 * relative start of ROM, but so far, I never found a dual-image ATI card
 	 *
 	 * typedef struct {
@@ -428,7 +434,7 @@
  * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
  * tree. Hopefully, ATI OF driver is kind enough to fill these
  */
-static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo)
+static int __devinit radeon_get_pll_info_openfirmware (struct radeonfb_info *rinfo)
 {
 	struct device_node *dp = rinfo->of_node;
 	u32 *val;
@@ -451,6 +457,7 @@
 	if (val && *val)
 		rinfo->pll.mclk = (*val) / 10;
 
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
        	return 0;
 }
 #endif /* CONFIG_PPC_OF */
@@ -593,10 +600,88 @@
 	return 0;
 }
 
+static int __devinit radeon_get_pll_info_legacy(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	if (!rinfo->bios_seg)
+		return -EINVAL;
+
+	pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+	
+	rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
+	rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
+	rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
+	rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
+	rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
+	rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from Legacy BIOS\n");
+	return 0;
+}
+
+
+static int __devinit radeon_get_pll_info_atom(struct radeonfb_info *rinfo)
+{
+	u16 pll_info_block;
+
+	if (!rinfo->bios_seg)
+		return -EINVAL;
+
+	pll_info_block = BIOS_IN16(rinfo->atom_data_start + 12);
+	
+	rinfo->pll.sclk = BIOS_IN32(pll_info_block + 8);
+	rinfo->pll.mclk = BIOS_IN32(pll_info_block + 12);
+	rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 82);
+	rinfo->pll.ref_div = 0; /* Have to get it elsewhere */
+	rinfo->pll.ppll_min = BIOS_IN16(pll_info_block + 78);
+	rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 32);
+
+	printk(KERN_INFO "radeonfb: Retreived PLL infos from ATOM BIOS\n");
+	return 0;
+}
+
+static void radeon_detect_bios_type(struct radeonfb_info *rinfo)
+{
+#ifdef CONFIG_PPC_OF
+	  rinfo->is_atom_bios = 0;
+	  rinfo->get_pll_info = radeon_get_pll_info_openfirmware;
+	  rinfo->get_lvds_info = radeon_get_lvds_info_openfirmware;
+	  rinfo->radeon_get_tmds_info = NULL;
+	  rinfo->get_conn_info = radeon_get_conn_info_openfirmware;
+#else
+	  int tmp = rinfo->fp_bios_start + 4;
+
+	  if ((BIOS_IN8(tmp) == 'A' &&
+	       BIOS_IN8(tmp+1) == 'T' &&
+	       BIOS_IN8(tmp+2) == 'O' &&
+	       BIOS_IN8(tmp+3) == 'M') ||
+	      (BIOS_IN8(tmp) == 'M' &&
+	       BIOS_IN8(tmp+1) == 'O' &&
+	       BIOS_IN8(tmp+2) == 'T' &&
+	       BIOS_IN8(tmp+3) == 'A')) {
+		  rinfo->is_atom_bios = 1;
+
+		  rinfo->atom_data_start = BIOS_IN16(rinfo->fp_bios_start + 32);
+		  rinfo->radeon_get_pll_info = radeon_get_pll_info_atom;
+		  rinfo->radeon_get_lvds_info = radeon_get_lvds_info_atom;
+		  rinfo->radeon_get_conn_info = radeon_get_conn_info_atom;
+		  rinfo->radeon_get_tmds_info = radeon_get_tmds_info_atom;
+	  } else {
+		  rinfo->is_atom_bios = 0;
+		  rinfo->radeon_get_pll_info = radeon_get_pll_info_legacy;
+		  rinfo->radeon_get_lvds_info = radeon_get_lvds_info_legacy;
+		  rinfo->radeon_get_conn_info = radeon_get_conn_info_legacy;
+		  rinfo->radeon_get_tmds_info = radeon_get_tmds_info_legacy;
+	  }
+#endif  /* CONFIG_PPC_OF */
+
+}
+
 /*
  * Retreive PLL infos by different means (BIOS, Open Firmware, register probing...)
  */
-static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
+static void __devinit radeon_get_pll_info(struct radeonfb_info *rinfo)
 {
 	/*
 	 * In the case nothing works, these are defaults; they are mostly
@@ -648,46 +733,30 @@
 	case PCI_DEVICE_ID_ATI_RADEON_QF:
 	case PCI_DEVICE_ID_ATI_RADEON_QG:
 	default:
-		rinfo->pll.ppll_max = 35000;
-		rinfo->pll.ppll_min = 12000;
+		if (rinfo->family == CHIP_FAMILY_R420) {
+			rinfo->pll.ppll_max = 50000;
+			rinfo->pll.ppll_min = 20000;
+		} else {
+			rinfo->pll.ppll_max = 35000;
+			rinfo->pll.ppll_min = 12000;
+		}
 		rinfo->pll.mclk = 16600;
 		rinfo->pll.sclk = 16600;
 		rinfo->pll.ref_clk = 2700;
 		break;
 	}
-	rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
-
-
-#ifdef CONFIG_PPC_OF
-	/*
-	 * Retreive PLL infos from Open Firmware first
-	 */
-       	if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
-       		printk(KERN_INFO "radeonfb: Retreived PLL infos from Open Firmware\n");
-		goto found;
-	}
-#endif /* CONFIG_PPC_OF */
 
 	/*
-	 * Check out if we have an X86 which gave us some PLL informations
-	 * and if yes, retreive them
+	 * If we have a way to retrieve the PLL information, do so.
 	 */
-	if (!force_measure_pll && rinfo->bios_seg) {
-		u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
-
-		rinfo->pll.sclk		= BIOS_IN16(pll_info_block + 0x08);
-		rinfo->pll.mclk		= BIOS_IN16(pll_info_block + 0x0a);
-		rinfo->pll.ref_clk	= BIOS_IN16(pll_info_block + 0x0e);
-		rinfo->pll.ref_div	= BIOS_IN16(pll_info_block + 0x10);
-		rinfo->pll.ppll_min	= BIOS_IN32(pll_info_block + 0x12);
-		rinfo->pll.ppll_max	= BIOS_IN32(pll_info_block + 0x16);
-
-		printk(KERN_INFO "radeonfb: Retreived PLL infos from BIOS\n");
-		goto found;
+	if (!force_measure_pll && rinfo->radeon_get_pll_info) {
+		if (!rinfo->radeon_get_pll_info(rinfo)) {
+			goto found;
+		}
 	}
 
 	/*
-	 * We didn't get PLL parameters from either OF or BIOS, we try to
+	 * If we don't get the PLL parameters handed to us, we try to
 	 * probe them
 	 */
 	if (radeon_probe_pll_params(rinfo) == 0) {
@@ -701,6 +770,22 @@
        	printk(KERN_INFO "radeonfb: Used default PLL infos\n");
 
 found:
+
+	/* Check and fix-up the PLL divisor if necessary */
+	if (rinfo->pll.ref_div < 2) {
+		int tmp = INPLL(PPLL_REF_DIV);
+		if (rinfo->family == CHIP_FAMILY_RS300) {
+			rinfo->pll.ref_div = (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
+		} else {
+			rinfo->pll.ref_div = tmp & PPLL_REF_DIV_MASK;
+		}
+		
+		/* Sane default */
+		if (rinfo->pll.ref_div < 2) {		
+			rinfo->pll.ref_div = 12;
+		}
+	}		
+
 	/*
 	 * Some methods fail to retreive SCLK and MCLK values, we apply default
 	 * settings in this case (200Mhz). If that really happne often, we could
@@ -941,6 +1026,7 @@
         u32 val;
 	u32 tmp_pix_clks;
 	int unblank = 0;
+	int i;
 
 	if (rinfo->lock_blank)
 		return 0;
@@ -970,78 +1056,80 @@
         }
 	OUTREG(CRTC_EXT_CNTL, val);
 
+	for (i = 0 ; i < RADEON_MAX_CONNECTORS ; i++) {
+		if (i == -1) continue;
 
-	switch (rinfo->mon1_type) {
-	case MT_DFP:
-		if (unblank)
-			OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN),
-				~(FP_FPON | FP_TMDS_EN));
-		else {
-			if (mode_switch || blank == FB_BLANK_NORMAL)
-				break;
-			OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN));
-		}
+		switch (rinfo->connectors[rinfo->heads[i]].mon_type) {
+		case MT_DFP:
+			if (unblank)
+				OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN),
+					~(FP_FPON | FP_TMDS_EN));
+			else {
+				if (mode_switch || blank == FB_BLANK_NORMAL)
+					break;
+				OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN));
+			}
 		break;
-	case MT_LCD:
-		del_timer_sync(&rinfo->lvds_timer);
-		val = INREG(LVDS_GEN_CNTL);
-		if (unblank) {
-			u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON
-				| LVDS_EN | (rinfo->init_state.lvds_gen_cntl
-					     & (LVDS_DIGON | LVDS_BL_MOD_EN));
-			if ((val ^ target_val) == LVDS_DISPLAY_DIS)
-				OUTREG(LVDS_GEN_CNTL, target_val);
-			else if ((val ^ target_val) != 0) {
-				OUTREG(LVDS_GEN_CNTL, target_val
-				       & ~(LVDS_ON | LVDS_BL_MOD_EN));
-				rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
-				rinfo->init_state.lvds_gen_cntl |=
-					target_val & LVDS_STATE_MASK;
-				if (mode_switch) {
-					radeon_msleep(rinfo->panel_info.pwr_delay);
+		case MT_LCD:
+			del_timer_sync(&rinfo->lvds_timer);
+			val = INREG(LVDS_GEN_CNTL);
+			if (unblank) {
+				u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON
+					| LVDS_EN | (rinfo->init_state.lvds_gen_cntl
+						     & (LVDS_DIGON | LVDS_BL_MOD_EN));
+				if ((val ^ target_val) == LVDS_DISPLAY_DIS)
 					OUTREG(LVDS_GEN_CNTL, target_val);
+				else if ((val ^ target_val) != 0) {
+					OUTREG(LVDS_GEN_CNTL, target_val
+					       & ~(LVDS_ON | LVDS_BL_MOD_EN));
+					rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+					rinfo->init_state.lvds_gen_cntl |=
+						target_val & LVDS_STATE_MASK;
+					if (mode_switch) {
+						radeon_msleep(rinfo->panel_info.pwr_delay);
+						OUTREG(LVDS_GEN_CNTL, target_val);
+					} else {
+						rinfo->pending_lvds_gen_cntl = target_val;
+						mod_timer(&rinfo->lvds_timer,
+							  jiffies +
+							  msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+					}
 				}
-				else {
-					rinfo->pending_lvds_gen_cntl = target_val;
-					mod_timer(&rinfo->lvds_timer,
-					   jiffies +
-					   msecs_to_jiffies(rinfo->panel_info.pwr_delay));
-				}
+			} else {
+				val |= LVDS_DISPLAY_DIS;
+				OUTREG(LVDS_GEN_CNTL, val);
+				
+				/* We don't do a full switch-off on a simple mode switch */
+				if (mode_switch || blank == FB_BLANK_NORMAL)
+					break;
+				
+				/* Asic bug, when turning off LVDS_ON, we have to make sure
+				 * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
+				 */
+				tmp_pix_clks = INPLL(PIXCLKS_CNTL);
+				if (rinfo->is_mobility || rinfo->is_IGP)
+					OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
+				val &= ~(LVDS_BL_MOD_EN);
+				OUTREG(LVDS_GEN_CNTL, val);
+				udelay(100);
+				val &= ~(LVDS_ON | LVDS_EN);
+				OUTREG(LVDS_GEN_CNTL, val);
+				val &= ~LVDS_DIGON;
+				rinfo->pending_lvds_gen_cntl = val;
+				mod_timer(&rinfo->lvds_timer,
+					  jiffies +
+					  msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+				rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+				rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK;
+				if (rinfo->is_mobility || rinfo->is_IGP)
+					OUTPLL(PIXCLKS_CNTL, tmp_pix_clks);
 			}
-		} else {
-			val |= LVDS_DISPLAY_DIS;
-			OUTREG(LVDS_GEN_CNTL, val);
-
-			/* We don't do a full switch-off on a simple mode switch */
-			if (mode_switch || blank == FB_BLANK_NORMAL)
-				break;
-
-			/* Asic bug, when turning off LVDS_ON, we have to make sure
-			 * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
-			 */
-			tmp_pix_clks = INPLL(PIXCLKS_CNTL);
-			if (rinfo->is_mobility || rinfo->is_IGP)
-				OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
-			val &= ~(LVDS_BL_MOD_EN);
-			OUTREG(LVDS_GEN_CNTL, val);
-			udelay(100);
-			val &= ~(LVDS_ON | LVDS_EN);
-			OUTREG(LVDS_GEN_CNTL, val);
-			val &= ~LVDS_DIGON;
-			rinfo->pending_lvds_gen_cntl = val;
-			mod_timer(&rinfo->lvds_timer,
-				  jiffies +
-				  msecs_to_jiffies(rinfo->panel_info.pwr_delay));
-			rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
-			rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK;
-			if (rinfo->is_mobility || rinfo->is_IGP)
-				OUTPLL(PIXCLKS_CNTL, tmp_pix_clks);
+			break;
+		case MT_CRT:
+			// todo: powerdown DAC
+		default:
+			break;
 		}
-		break;
-	case MT_CRT:
-		// todo: powerdown DAC
-	default:
-		break;
 	}
 
 	/* let fbcon do a soft blank for us */
@@ -1942,7 +2030,7 @@
 	u32 lvds_gen_cntl, tmpPixclksCntl;
 	int* conv_table;
 
-	if (rinfo->mon1_type != MT_LCD)
+	if (PRIMARY_MONITOR(rinfo) != MT_LCD)
 		return 0;
 
 	/* Pardon me for that hack... maybe some day we can figure
@@ -2230,7 +2318,7 @@
         struct fb_info *info = pci_get_drvdata(pdev);
         struct radeonfb_info *rinfo = info->par;
 
-	return radeon_show_one_edid(buf, off, count, rinfo->mon1_EDID);
+	return radeon_show_one_edid(buf, off, count, rinfo->connectors[rinfo->heads[0]].edid);
 }
 
 
@@ -2241,7 +2329,27 @@
         struct fb_info *info = pci_get_drvdata(pdev);
         struct radeonfb_info *rinfo = info->par;
 
-	return radeon_show_one_edid(buf, off, count, rinfo->mon2_EDID);
+	return radeon_show_one_edid(buf, off, count, rinfo->connectors[rinfo->heads[1]].edid);
+}
+
+static ssize_t radeon_show_edid3(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pci_dev *pdev = to_pci_dev(dev);
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+
+	return radeon_show_one_edid(buf, off, count, rinfo->connectors[rinfo->heads[2]].edid);
+}
+
+static ssize_t radeon_show_edid4(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct pci_dev *pdev = to_pci_dev(dev);
+        struct fb_info *info = pci_get_drvdata(pdev);
+        struct radeonfb_info *rinfo = info->par;
+
+	return radeon_show_one_edid(buf, off, count, rinfo->connectors[rinfo->heads[3]].edid);
 }
 
 static struct bin_attribute edid1_attr = {
@@ -2264,6 +2372,25 @@
 	.read	= radeon_show_edid2,
 };
 
+static struct bin_attribute edid3_attr = {
+	.attr   = {
+		.name	= "edid3",
+		.owner	= THIS_MODULE,
+		.mode	= 0444,
+	},
+	.size	= EDID_LENGTH,
+	.read	= radeon_show_edid3,
+};
+
+static struct bin_attribute edid4_attr = {
+	.attr   = {
+		.name	= "edid4",
+		.owner	= THIS_MODULE,
+		.mode	= 0444,
+	},
+	.size	= EDID_LENGTH,
+	.read	= radeon_show_edid4,
+};
 
 static int radeonfb_pci_register (struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
@@ -2271,6 +2398,7 @@
 	struct fb_info *info;
 	struct radeonfb_info *rinfo;
 	int ret;
+	int i;
 
 	RTRACE("radeonfb_pci_register BEGIN\n");
 	
@@ -2412,6 +2540,7 @@
 	 * We probably need to make sure this is the primary display,
 	 * but that is difficult without some arch support.
 	 */
+
 #ifdef CONFIG_X86
 	if (rinfo->bios_seg == NULL)
 		radeon_find_mem_vbios(rinfo);
@@ -2423,14 +2552,23 @@
 	if (rinfo->bios_seg == NULL && rinfo->is_mobility)
 		radeon_map_ROM(rinfo, pdev);
 
+	/* Check BIOS Type */
+	radeon_detect_bios_type(rinfo);
+	
 	/* Get informations about the board's PLL */
-	radeon_get_pllinfo(rinfo);
+	radeon_get_pll_info(rinfo);
+
+	/* Get informations about internal TMDS controller if any */
+	radeon_get_tmds_info(rinfo);
 
 #ifdef CONFIG_FB_RADEON_I2C
 	/* Register I2C bus */
 	radeon_create_i2c_busses(rinfo);
 #endif
 
+	/* Get infos about connectors -- need I2C here! */
+	radeon_get_conn_info(rinfo, ignore_conntable);
+
 	/* set all the vital stuff */
 	radeon_set_fbinfo (rinfo);
 
@@ -2441,10 +2579,15 @@
 	radeon_check_modes(rinfo, mode_option);
 
 	/* Register some sysfs stuff (should be done better) */
-	if (rinfo->mon1_EDID)
+	
+	if ((rinfo->heads[0] != -1) && rinfo->connectors[rinfo->heads[0]].edid)
 		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
-	if (rinfo->mon2_EDID)
+	if ((rinfo->heads[1] != -1) && rinfo->connectors[rinfo->heads[1]].edid)
 		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
+	if ((rinfo->heads[2] != -1) && rinfo->connectors[rinfo->heads[2]].edid)
+		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid3_attr);
+	if ((rinfo->heads[3] != -1) && rinfo->connectors[rinfo->heads[3]].edid)
+		sysfs_create_bin_file(&rinfo->pdev->dev.kobj, &edid4_attr);
 
 	/* save current mode regs before we switch into the new one
 	 * so we can restore this upon __exit
@@ -2478,7 +2621,7 @@
 #endif
 
 #ifdef CONFIG_PMAC_BACKLIGHT
-	if (rinfo->mon1_type == MT_LCD) {
+	if (PRIMARY_MONITOR(rinfo) == MT_LCD) {
 		register_backlight_controller(&radeon_backlight_controller,
 					      rinfo, "ati");
 		register_backlight_controller(&radeon_backlight_controller,
@@ -2496,10 +2639,12 @@
 err_unmap_fb:
 	iounmap(rinfo->fb_base);
 err_unmap_rom:
-	kfree(rinfo->mon1_EDID);
-	kfree(rinfo->mon2_EDID);
-	if (rinfo->mon1_modedb)
-		fb_destroy_modedb(rinfo->mon1_modedb);
+	for (i = 0 ; i < RADEON_MAX_CONNECTORS ; i++) {
+		kfree(rinfo->connectors[i].edid);
+		if (rinfo->connectors[i].modedb)
+			fb_destroy_modedb(rinfo->connectors[i].modedb);
+	}
+
 	fb_dealloc_cmap(&info->cmap);
 #ifdef CONFIG_FB_RADEON_I2C
 	radeon_delete_i2c_busses(rinfo);
@@ -2525,16 +2670,21 @@
 {
         struct fb_info *info = pci_get_drvdata(pdev);
         struct radeonfb_info *rinfo = info->par;
- 
+	int i;
+
         if (!rinfo)
                 return;
  
 	radeonfb_pm_exit(rinfo);
 
-	if (rinfo->mon1_EDID)
+	if ((rinfo->heads[0] != -1) && rinfo->connectors[rinfo->heads[0]].edid)
 		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
-	if (rinfo->mon2_EDID)
+	if ((rinfo->heads[1] != -1) && rinfo->connectors[rinfo->heads[1]].edid)
 		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
+	if ((rinfo->heads[2] != -1) && rinfo->connectors[rinfo->heads[2]].edid)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid3_attr);
+	if ((rinfo->heads[3] != -1) && rinfo->connectors[rinfo->heads[3]].edid)
+		sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid4_attr);
 
 #if 0
 	/* restore original state
@@ -2561,10 +2711,11 @@
 	pci_release_region(pdev, 2);
 	pci_release_region(pdev, 0);
 
-	kfree(rinfo->mon1_EDID);
-	kfree(rinfo->mon2_EDID);
-	if (rinfo->mon1_modedb)
-		fb_destroy_modedb(rinfo->mon1_modedb);
+	for (i = 0 ; i < RADEON_MAX_CONNECTORS ; i++) {
+		kfree(rinfo->connectors[i].edid);
+		if (rinfo->connectors[i].modedb)
+			fb_destroy_modedb(rinfo->connectors[i].modedb);
+	}
 #ifdef CONFIG_FB_RADEON_I2C
 	radeon_delete_i2c_busses(rinfo);
 #endif        
@@ -2615,6 +2766,8 @@
 			force_measure_pll = 1;
 		} else if (!strncmp(this_opt, "ignore_edid", 11)) {
 			ignore_edid = 1;
+		} else if (!strncmp(this_opt, "ignore_conntable", 16)) {
+			ignore_conntable = 1;
 		} else
 			mode_option = this_opt;
 	}
@@ -2658,6 +2811,8 @@
 MODULE_PARM_DESC(force_dfp, "bool: force display to dfp");
 module_param(ignore_edid, bool, 0);
 MODULE_PARM_DESC(ignore_edid, "bool: Ignore EDID data when doing DDC probe");
+module_param(ignore_conntable, bool, 0);
+MODULE_PARM_DESC(ignore_conntable, "bool: Ignore BIOS Connector table");
 module_param(monitor_layout, charp, 0);
 MODULE_PARM_DESC(monitor_layout, "Specify monitor mapping (like XFree86)");
 module_param(force_measure_pll, bool, 0);
Only in aty: radeon_base.o
diff -aur aty-2.6.15/radeon_i2c.c aty/radeon_i2c.c
--- aty-2.6.15/radeon_i2c.c	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeon_i2c.c	2006-03-01 13:48:40.000000000 -0500
@@ -170,13 +170,27 @@
 	return NULL;
 }
 
-
-int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 **out_edid)
+/* Returns 1 if probe unsuccessful. */
+int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, struct radeon_connector *conn)
 {
-	u32 reg = rinfo->i2c[conn-1].ddc_reg;
+	u32 reg;
 	u8 *edid = NULL;
+	int mon_type = MT_NONE;
+
 	int i, j;
 
+	if (!conn)
+		return 1;
+
+	if (rinfo->is_mobility && (conn->ddc_type == ddc_none) &&
+		(INREG(LVDS_GEN_CNTL) & LVDS_ON)) {
+			RTRACE("radeonfb: I2C (port %d) ... found LVDS panel\n", conn->ddc_type);
+			mon_type = MT_LCD;
+			goto done;
+	}
+
+	reg = rinfo->i2c[conn->ddc_type].ddc_reg;
+
 	OUTREG(reg, INREG(reg) & 
 			~(VGA_DDC_DATA_OUTPUT | VGA_DDC_CLK_OUTPUT));
 
@@ -212,7 +226,7 @@
 		msleep(15);
 
 		/* Do the real work */
-		edid = radeon_do_probe_i2c_edid(&rinfo->i2c[conn-1]);
+		edid = radeon_do_probe_i2c_edid(&rinfo->i2c[conn->ddc_type]);
 
 		OUTREG(reg, INREG(reg) | 
 				(VGA_DDC_DATA_OUT_EN | VGA_DDC_CLK_OUT_EN));
@@ -236,30 +250,32 @@
 		if (edid)
 			break;
 	}
+
 	/* Release the DDC lines when done or the Apple Cinema HD display
 	 * will switch off
 	 */
 	OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN | VGA_DDC_DATA_OUT_EN));
 	(void)INREG(reg);
 
-	if (out_edid)
-		*out_edid = edid;
 	if (!edid) {
-		RTRACE("radeonfb: I2C (port %d) ... not found\n", conn);
-		return MT_NONE;
+		RTRACE("radeonfb: I2C (port %d) ... not found\n", conn->ddc_type);
+		mon_type = MT_NONE;
+		goto done;
 	}
-	if (edid[0x14] & 0x80) {
-		/* Fix detection using BIOS tables */
-		if (rinfo->is_mobility /*&& conn == ddc_dvi*/ &&
-		    (INREG(LVDS_GEN_CNTL) & LVDS_ON)) {
-			RTRACE("radeonfb: I2C (port %d) ... found LVDS panel\n", conn);
-			return MT_LCD;
-		} else {
-			RTRACE("radeonfb: I2C (port %d) ... found TMDS panel\n", conn);
-			return MT_DFP;
-		}
+
+	if ((edid[0x14] & 0x80) && (conn->ddc_type == ddc_dvi)) {
+		RTRACE("radeonfb: I2C (port %d) ... found TMDS panel\n", conn->ddc_type);
+		mon_type = MT_DFP;
+		goto done;
 	}
-       	RTRACE("radeonfb: I2C (port %d) ... found CRT display\n", conn);
-	return MT_CRT;
+
+       	RTRACE("radeonfb: I2C (port %d) ... found CRT display\n", conn->ddc_type);
+	mon_type = MT_CRT;
+
+ done:
+	conn->edid = edid;
+	conn->mon_type = mon_type;
+
+	return (mon_type == MT_NONE);
 }
 
Only in aty: radeon_i2c.o
diff -aur aty-2.6.15/radeon_monitor.c aty/radeon_monitor.c
--- aty-2.6.15/radeon_monitor.c	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeon_monitor.c	2006-03-01 15:12:35.000000000 -0500
@@ -1,6 +1,29 @@
 #include "radeonfb.h"
 #include "../edid.h"
 
+/*
+ * TMDS PLL configuration table, taken from X.org
+ */
+static const struct radeon_tmds_pll_info default_tmds_pll[CHIP_FAMILY_LAST][4] =
+{
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_UNKNOW*/
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_LEGACY*/
+    {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RADEON*/
+    {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV100*/
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_RS100*/
+    {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV200*/
+    {{12000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RS200*/
+    {{15000, 0xa1b}, {0xffffffff, 0xa3f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_R200*/
+    {{15500, 0x81b}, {0xffffffff, 0x83f}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV250*/
+    {{0, 0}, {0, 0}, {0, 0}, {0, 0}},				/*CHIP_FAMILY_RS300*/
+    {{13000, 0x400f4}, {15000, 0x400f7}, {0xffffffff, 0x400f7/*0x40111*/}, {0, 0}},	/*CHIP_FAMILY_RV280*/
+    {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},		/*CHIP_FAMILY_R300*/
+    {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},		/*CHIP_FAMILY_R350*/
+    {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV350*/
+    {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}},	/*CHIP_FAMILY_RV380*/
+    {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}},		/*CHIP_FAMILY_R420*/
+};
+
 static struct fb_var_screeninfo radeonfb_default_var = {
 	.xres		= 640,
 	.yres		= 480,
@@ -23,35 +46,6 @@
 	.vmode		= 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
@@ -59,7 +53,8 @@
  * 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,
+static int __devinit radeon_parse_montype_prop(struct device_node *dp,
+					       struct radeon_connector *conn, 
 					       int hdno)
 {
         static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID",
@@ -67,25 +62,30 @@
 	u8 *pedid = NULL;
 	u8 *pmt = NULL;
 	u8 *tmp;
-        int i, mt = MT_NONE;  
+        int i;  
 	
 	RTRACE("analyzing OF properties...\n");
 	pmt = (u8 *)get_property(dp, "display-type", NULL);
 	if (!pmt)
-		return MT_NONE;
+		return 1;
 	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, "LCD") || !strcmp(pmt, "DFP")) {
+		/* OF says "LCD" for DFP as well.*/
+		if (rinfo->is_mobility) {
+			conn->mon_type = MT_LCD;
+			/* Maybe check for LVDS_GEN_CNTL here ? I need to check out
+			 * what OF does when booting with lid closed
+			 */
+		} else{
+			conn->mon_type = MT_DFP;
+		}
+	} else if (!strcmp(pmt, "CRT")) {
+		conn->mon_type = MT_CRT;
+	} else {
 		if (strcmp(pmt, "NONE") != 0)
 			printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n",
 			       pmt);
-		return MT_NONE;
+		return 1;
 	}
 
 	for (i = 0; propnames[i] != NULL; ++i) {
@@ -102,26 +102,41 @@
 	if (pedid == NULL && dp->parent && (hdno == 0))
 		pedid = get_property(dp->parent, "EDID", NULL);
 	if (pedid == NULL)
-		return mt;
+		return 1;
 
 	tmp = (u8 *)kmalloc(EDID_LENGTH, GFP_KERNEL);
-	if (!tmp)
-		return mt;
-	memcpy(tmp, pedid, EDID_LENGTH);
-	*out_EDID = tmp;
-	return mt;
+	if (tmp) {
+		memcpy(tmp, pedid, EDID_LENGTH);
+	}
+
+	conn->edid = tmp;
+
+	{
+		int found_tmds = 0;
+		int found_crt = 0;
+		int ddc_type = ddc_none;
+		// XXX what about reversed DAC/TMDS??
+		radeon_fill_conn(conn, conn->mon_type, ddc_type, &found_crt, &found_tmds);
+	}
+
+	return 0;
 }
 
-static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_no,
-					  u8 **out_EDID)
+/* return a 1 on error */
+static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_no)
+
 {
+	struct radeon_connector *conn;
         struct device_node *dp;
+	u8 *out_EDID;
 
 	RTRACE("radeon_probe_OF_head\n");
 
+	conn = rinfo->connectors[head_no];
+
         dp = rinfo->of_node;
-        while (dp == NULL)
-		return MT_NONE;
+        if (dp == NULL) 
+		return 1;
 
 	if (rinfo->has_CRTC2) {
 		char *pname;
@@ -129,52 +144,94 @@
 
 		dp = dp->child;
 		do {
-			if (!dp)
-				return MT_NONE;
+			if (!dp) 
+				return 1;
+
 			pname = (char *)get_property(dp, "name", NULL);
-			if (!pname)
-				return MT_NONE;
+			if (!pname) 
+				return 1;
+
 			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);
+				return radeon_parse_montype_prop(dp, conn, 0);
+			} else if (pname[len-1] == 'B' && head_no == 1) {
+				return radeon_parse_montype_prop(dp, conn, 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);
+		if (head_no > 0) {
+			return 1;
+		}
+		return radeon_parse_montype_prop(dp, conn, -1);
 	}
-        return MT_NONE;
+	return 1;
 }
 #endif /* CONFIG_PPC_OF */
 
 
-static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
+int __devinit radeon_get_lvds_info_atom(struct radeonfb_info *rinfo)
+{
+	unsigned long tmp;
+
+	if (!rinfo->bios_seg)
+		return -ENODEV;
+
+	tmp = BIOS_IN16(rinfo->atom_data_start + 16);
+	if (!tmp) {
+		printk(KERN_ERR "radeonfb: No LVDS panel info in BIOS\n");
+		rinfo->panel_info.pwr_delay = 200;
+		return -ENODEV;
+	}
+	
+	rinfo->panel_info.xres = BIOS_IN16(tmp+6);
+	rinfo->panel_info.yres = BIOS_IN16(tmp+10);
+	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+40);
+	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;
+	
+	/* No special divider combinations? */
+	
+	rinfo->panel_info.hblank = BIOS_IN16(tmp+8);
+	rinfo->panel_info.hOver_plus = BIOS_IN16(tmp+14);
+	rinfo->panel_info.hSync_width = BIOS_IN16(tmp+16);
+	rinfo->panel_info.vblank = BIOS_IN16(tmp+12);
+	rinfo->panel_info.vOver_plus = BIOS_IN16(tmp+18);
+	rinfo->panel_info.vSync_width = BIOS_IN16(tmp+20);
+	rinfo->panel_info.clock = BIOS_IN16(tmp+4);
+	
+	/* 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;
+	
+	return 0;
+}
+
+int __devinit radeon_get_lvds_info_legacy(struct radeonfb_info *rinfo)
 {
 	unsigned long tmp, tmp0;
 	char stmp[30];
 	int i;
 
 	if (!rinfo->bios_seg)
-		return 0;
+		return -ENODEV;
 
 	if (!(tmp = BIOS_IN16(rinfo->fp_bios_start + 0x40))) {
-		printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
+		printk(KERN_ERR "radeonfb: No LVDS panel info in BIOS\n");
 		rinfo->panel_info.pwr_delay = 200;
-		return 0;
+		return -ENODEV;
 	}
-
+	
 	for(i=0; i<24; i++)
 		stmp[i] = BIOS_IN8(tmp+i+1);
 	stmp[24] = 0;
@@ -182,13 +239,13 @@
 	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.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
 	 */
@@ -203,6 +260,7 @@
 		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);
@@ -226,7 +284,7 @@
 			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);
@@ -235,12 +293,427 @@
 			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;
+			
+			return 0;
 		}
 	}
+
 	RTRACE("Didn't find panel in BIOS table !\n");
 
+	return -ENODEV;
+}
+
+/*
+ * Get informations about TMDS controllers and their setup at
+ * different operating frequencies
+ */
+void __devinit radeon_get_tmds_info(struct radeonfb_info *rinfo)
+{
+	int i;
+
+	/* Get default TMDS infos for this chip */
+	for (i=0; i<4; i++) {
+		rinfo->tmds_pll[i].value =
+			default_tmds_pll[rinfo->family][i].value;
+		rinfo->tmds_pll[i].freq =
+			default_tmds_pll[rinfo->family][i].freq;
+	}
+
+	/* Get whatever the firmware provides */
+	if (rinfo->radeon_get_tmds_info) {
+		rinfo->radeon_get_tmds_info(rinfo);
+		// XXX Do we care about the return value?
+	}
+}
+
+int __devinit radeon_get_tmds_info_legacy(struct radeonfb_info *rinfo)
+{
+	int offset, i, n, rev;
+
+	offset = BIOS_IN16(rinfo->fp_bios_start + 0x34);
+	if (offset == 0)
+		return -ENODEV;
+
+	rev = BIOS_IN8(offset);
+	printk(KERN_INFO "DFP table revision: %d\n", rev);
+
+	switch(rev) {
+	case 3:
+		n = BIOS_IN8(offset + 5) + 1;
+		if (n > 4)
+			n = 4;
+		for (i = 0; i < n; i++) {
+			/* Looks bogus ... but that's what is in X.org */
+			rinfo->tmds_pll[i].value =
+				BIOS_IN32(offset+i*10+0x08);
+			rinfo->tmds_pll[i].freq =
+				BIOS_IN16(offset+i*10+0x10);
+		}
+		return 0;
+
+	/* revision 4 has some problem as it appears in RV280,
+	 * comment it off for now, use default instead
+	 */
+#if 0
+	case 4:
+		stride = 0;
+		n = BIOS_IN8(offset  5) + 1;
+		if (n > 4)
+			n = 4;
+		for (i = 0; i < n; i++) {
+			rinfo->tmds_pll[i].value =
+				BIOS_IN32(tmp+stride+0x08);
+			rinfo->tmds_pll[i].freq =
+				BIOS_IN16(tmp+stride+0x10);
+			if (i == 0)
+				stride += 10;
+			else
+				stride += 6;
+		}
+		return 0;
+#endif
+	}
+	return -ENODEV;
+}
+
+int __devinit radeon_get_tmds_info_atom(struct radeonfb_info *rinfo)
+{
+	int offset, i, maxfreq;
+
+	offset = BIOS_IN16(rinfo->atom_data_start + 18);
+	if (offset == 0)
+		return -ENODEV;
+
+	maxfreq = BIOS_IN16(offset + 4);
+
+	for (i = 0; i < 4; i++) {
+		rinfo->tmds_pll[i].freq = BIOS_IN16(offset+i*6+6);
+		/* This assumes each field in TMDS_PLL has 6 bit as
+		 * in R300/R420
+		 */
+		rinfo->tmds_pll[i].value =
+			((BIOS_IN8(offset+i*6+8) & 0x3f) |
+			 ((BIOS_IN8(offset+i*6+10) & 0x3f)<<6) |
+			 ((BIOS_IN8(offset+i*6+9) & 0xf)<<12) |
+			 ((BIOS_IN8(offset+i*6+11) & 0xf)<<16));
+		printk(KERN_INFO "TMDS PLL from BIOS: %ld %x\n",
+			   rinfo->tmds_pll[i].freq, rinfo->tmds_pll[i].value);
+
+		if (maxfreq == rinfo->tmds_pll[i].freq) {
+		    rinfo->tmds_pll[i].freq = 0xffffffff;
+		    break;
+		}
+	}
+	return 0;
+}
+
+
+static const char *conn_type_name[] = {
+	"NONE", "VGA", "DVI-I", "DVI-D", "DVI-A", "S-Video",
+	"Composite Video", "Internal Panel", "Digital",
+	"Unsupported", "Proprietary"
+};
+
+static const char *mon_type_name[] = {
+	"None", "CRT", "LVDS Flat panel",
+	"DVI Flat panel", "Composite TV", "S-Video TV"
+};
+
+static void __devinit radeon_fill_conn(struct radeon_connector *conn, int mon_type, int ddc_type, int *found_tmds, int *found_crt) 
+{
+	conn->mon_type = mon_type;
+	conn->ddc_type = ddc_type;
+
+	// XXX what about reversed DAC/TMDS??
+
+	switch(mon_type) {
+	case MT_CRT:
+		conn->conn_type = conn_vga;
+		conn->tmds_type = tmds_unknown;
+		conn->dac_type = (*found_crt) ? dac_tvdac: dac_primary;
+		if (ddc_type == ddc_none) conn->ddc_type = (*found_crt) ? ddc_crt2 : ddc_vga;
+		*found_crt = 1;
+		break;
+	case MT_DFP:
+		conn->conn_type = conn_dvi_i;
+		conn->tmds_type = (*found_tmds) ? tmds_external: tmds_internal;
+		conn->dac_type = dac_unknown;
+		if (ddc_type == ddc_none) conn->ddc_type = ddc_dvi;
+		*found_tmds = 1;
+		break;
+	case MT_LCD:
+		conn->conn_type = conn_lvds;
+		conn->tmds_type = tmds_unknown;
+		conn->dac_type = dac_unknown;
+		if (ddc_type == ddc_none) conn->ddc_type = ddc_none; //heh
+		break;
+	case MT_CTV:
+		conn->conn_type = conn_ctv;
+		conn->tmds_type = tmds_unknown;
+		conn->dac_type = dac_tvdac;
+		if (ddc_type == ddc_none) conn->ddc_type = ddc_vga; // XXX ddc_crt2?
+		break;
+	case MT_STV:
+		conn->conn_type = conn_stv;
+		conn->tmds_type = tmds_unknown;
+		conn->dac_type = dac_tvdac;
+		if (ddc_type == ddc_none) conn->ddc_type = ddc_vga; // XXX ddc_crt2?
+		break;
+	case MT_UNKNOWN:
+	case MT_NONE:
+		conn->conn_type = conn_none;
+		conn->tmds_type = tmds_unknown;
+		conn->mon_type = MT_NONE;
+		conn->ddc_type = ddc_none;
+		conn->dac_type = dac_unknown;
+		break;
+	default:
+		break;
+	}
+	// leaves conn_digital, conn_unsupported, conn_propritetary
+}
+
+/*
+ * Get informations about the various connectors on this card. This is
+ * the most prone to fail function as various firmwares tend to say
+ * crap or not give any info at all. The Open Firmware version is just
+ * a table of known cards for now for example. We'll probably need some
+ * additional module params to force different settings in case of
+ * misdetection here.
+ *
+ * This doesn _not_ try actual probing of whatever is plugged on those
+ * various connectors. This will be done later. We do store whatever
+ * probing info the firmware gives us though
+ */
+void __devinit radeon_get_conn_info(struct radeonfb_info *rinfo, int ignore_conntable)
+{
+	int i;
+
+	/* Clear table */
+	for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
+		rinfo->connectors[i].conn_type	= conn_none;
+		rinfo->connectors[i].ddc_type	= ddc_none;
+		rinfo->connectors[i].dac_type	= dac_unknown;
+		rinfo->connectors[i].tmds_type	= tmds_unknown;
+		rinfo->connectors[i].mon_type	= MT_UNKNOWN;
+		rinfo->connectors[i].head       = -1;
+		rinfo->heads[i] = -1;
+	}
+	rinfo->num_heads = 0;
+
+	if (ignore_conntable) {	
+#if defined(CONFIG_FB_RADEON_I2C)
+		struct radeon_connector conn;
+		int idx = 0;
+		int found_tmds = 0;
+		int found_crt = 0;
+
+		// XXX what about reversed DAC/TMDS??
+
+		for (i = 0; i < 4; i++) {
+			conn.ddc_type = i;
+			if (!radeon_probe_i2c_connector(rinfo, &conn)) {
+
+				radeon_fill_conn(&rinfo->connectors[idx++], conn.mon_type, conn.ddc_type, &found_tmds, &found_crt); 
+			}
+		}
+
+		/* If we failed to probe something.. */
+		if (idx) 
+			goto found;
+#endif /* CONFIG_FB_RADEON_I2C */
+	} else {
+		/* Try to obtain infos from firmware */
+		if (rinfo->radeon_get_conn_info) {
+			if (!rinfo->radeon_get_conn_info(rinfo)) {
+				goto found;
+			}
+		}
+	}
+
+	printk(KERN_INFO "radeonfb: No connector infos, using defaults...\n");
+
+	/* Here, we use defaults that are common enough ... we hope
+	 * For a mobility chip, we assume LVDS is on primary
+	 */
+	if (rinfo->is_mobility) {
+		rinfo->connectors[0].conn_type	= conn_lvds;
+		rinfo->connectors[0].ddc_type	= ddc_dvi;
+		rinfo->connectors[0].dac_type	= dac_primary;
+		rinfo->connectors[0].tmds_type	= tmds_unknown;
+		rinfo->connectors[0].mon_type	= MT_UNKNOWN;
+
+		rinfo->connectors[1].conn_type	= conn_dvi_d;
+		rinfo->connectors[1].ddc_type	= ddc_vga;
+		rinfo->connectors[1].dac_type	= dac_primary;
+		rinfo->connectors[1].tmds_type	= tmds_internal;
+		rinfo->connectors[1].mon_type	= MT_UNKNOWN;
+
+		rinfo->connectors[2].conn_type	= conn_stv;
+		rinfo->connectors[2].ddc_type	= ddc_none;
+		rinfo->connectors[2].dac_type	= dac_tvdac;
+		rinfo->connectors[2].tmds_type	= tmds_unknown;
+		rinfo->connectors[2].mon_type	= MT_UNKNOWN;
+	} else {
+		rinfo->connectors[0].conn_type	= conn_dvi_d;
+		rinfo->connectors[0].ddc_type	= ddc_dvi;
+		rinfo->connectors[0].dac_type	= dac_tvdac;
+		rinfo->connectors[0].tmds_type	= tmds_internal;
+		rinfo->connectors[0].mon_type	= MT_UNKNOWN;
+
+		rinfo->connectors[1].conn_type	= conn_vga;
+		rinfo->connectors[1].ddc_type	= ddc_vga;
+		rinfo->connectors[1].dac_type	= dac_primary;
+		rinfo->connectors[1].tmds_type	= tmds_unknown;
+		rinfo->connectors[1].mon_type	= MT_UNKNOWN;
+		
+		if (rinfo->has_CRTC2) {
+			rinfo->connectors[1].conn_type	= conn_vga;
+			rinfo->connectors[1].ddc_type	= ddc_crt2;
+			rinfo->connectors[1].dac_type	= dac_tvdac;
+			rinfo->connectors[1].tmds_type	= tmds_unknown;
+			rinfo->connectors[1].mon_type	= MT_UNKNOWN;
+		}
+	}
+
+ found:
+	/* Now, we do additional fixups */
+
+	/* RS300 has only one DAC, force TV-DAC on VGA port */
+	if (rinfo->family == CHIP_FAMILY_RS300) {
+		for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
+			if (rinfo->connectors[i].conn_type == conn_vga)
+				rinfo->connectors[i].dac_type = dac_tvdac;
+			else if (rinfo->connectors[i].dac_type != dac_unknown)
+				rinfo->connectors[i].dac_type = dac_primary;
+		}
+	}
+
+	/* Single head chips all use primary DAC */
+	if (!rinfo->has_CRTC2)
+		rinfo->connectors[0].dac_type = dac_primary;
+
+	return;
+ }
+
+#ifdef CONFIG_PPC_OF
+int __devinit radeon_get_conn_info_openfirmware(struct radeonfb_info *rinfo)
+{
+	int i;
+	int not_found = 1;
+
+	for(i = 0 ; < 2 ; i++) {  /* Only two heads for OF! */
+		if (!radeon_probe_OF_head(rinfo, i)) found = 0;
+	}
+	return found;
+}
+#endif /* CONFIG_PPC_OF */
+
+int __devinit radeon_get_conn_info_atom(struct radeonfb_info *rinfo)
+{
+	int i, j, offset, valids;
+	int ids[RADEON_MAX_CONNECTORS];
+	u16 portinfo, tmp0;
+	int conn_index = 0;
+	int conn_add = 2;
+	int idx = 0;
+	int ddc_type, dac_type, conn_type, tmds_type, port_id;
+	int connector_found = 0;
+	
+	offset = BIOS_IN16(rinfo->atom_data_start + 22);
+	if (offset == 0)
+		return -ENODEV;
+
+	/* Again, I slightly modified X.org algorithm. I assign "primary" outputs
+	 * to entries 0 and 1, and anything else goes after 2.
+	 *
+	 * Also, I keep an array of all port IDs matching connectors[] array,
+	 * unlike X which limits itself to "crtc"'s
+	 */
+	for (i = 0; i < RADEON_MAX_CONNECTORS; i++)
+		ids[i] = -1;
+
+	valids = BIOS_IN16(offset + 4);
+	for (i = 0; i < 8; i++) {
+		if (!(valids & (1 << i)))
+			continue;
+		portinfo = BIOS_IN16(offset + 6 + i*2);
+
+		conn_type = (portinfo >> 4) & 0xf;
+		dac_type = (portinfo & 0xf) - 1;
+		port_id = (portinfo >> 8) & 0xf;
+		ddc_type = ddc_none;
+
+		if ((tmp0 = BIOS_IN16(rinfo->atom_data_start + 24))) {
+			switch(BIOS_IN16(tmp0 + 4 + (27 * port_id)) * 4) {
+			case GPIO_MONID:
+				ddc_type = ddc_monid;
+				break;
+			case GPIO_DVI_DDC:
+				ddc_type = ddc_dvi;
+				break;
+			case GPIO_VGA_DDC:
+				ddc_type = ddc_vga;
+				break;
+			case GPIO_CRT2_DDC:
+				ddc_type = ddc_crt2;
+				break;
+			default:
+				ddc_type = ddc_none;
+				break;
+			}
+		}
+
+		if (i == 3)
+			tmds_type = tmds_internal;
+		else if (i == 7)
+			tmds_type = tmds_external;
+		else
+			tmds_type = tmds_unknown;
+
+		RTRACE("index %d port %d conn %d dac %d ddc %d tmds %d\n", i, port_id, conn_type, dac_type, ddc_type, tmds_type);
+		
+		/* Ok, now we have the port ID, look for an existing port
+		 * already using this ID
+		 */
+		for (j = 0; j < RADEON_MAX_CONNECTORS; j++) {
+			if (port_id != ids[j])
+				continue;
+			/* Gotcha, just "update" values */
+			if (tmds_type != tmds_unknown)
+				rinfo->connectors[j].tmds_type = tmds_type;
+			if (rinfo->connectors[j].dac_type == dac_unknown)
+				rinfo->connectors[j].dac_type = dac_type;
+			if (rinfo->connectors[j].ddc_type == dac_unknown)
+				rinfo->connectors[j].ddc_type = dac_type;
+			continue;
+		}
+
+		conn_index = (ddc_type == ddc_dvi || conn_index == 1) ? 0 : 1;
+
+		/* if the port is a TV port, or both connectors are already
+		 * assigned, assign it after further in the table
+		 */
+		if (conn_type == conn_ctv || conn_type == conn_stv ||
+		    (rinfo->connectors[0].conn_type != conn_none &&
+		     rinfo->connectors[1].conn_type))
+			idx = conn_add++;
+		else
+			idx = conn_index;
+
+		rinfo->connectors[idx].tmds_type = tmds_type;
+		rinfo->connectors[idx].dac_type = dac_type;
+		rinfo->connectors[idx].ddc_type = ddc_type;
+		rinfo->connectors[idx].conn_type = conn_type;
+
+		/* increment connector_found for primary connectors only */
+		if (idx < 2)
+			connector_found += (idx + 1);
+	}
+
+	if (connector_found == 0)
+		return -ENODEV;
+
 	return 0;
 }
 
@@ -248,44 +721,167 @@
  * 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 __devinit radeon_get_conn_info_legacy(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"
+	int offset, i, entry, tmp;
+	int ddc_type, dac_type, conn_type, tmds_type;
+	int conn_index = 0;
+	int conn_add = 2;
+	int idx = 0;
+
+	/* Convert legacy to real connector types */
+	const enum radeon_conn_type legacy_conn_to_type[] = {
+		conn_none,
+		conn_proprietary,
+		conn_vga,
+		conn_dvi_i,
+		conn_dvi_d,
+		conn_ctv,
+		conn_stv,
+		conn_unsupported,
 	};
 
-	if (!rinfo->bios_seg)
-		return;
+	/* Some laptops only have one connector (VGA) listed in the connector
+	 * table, we need to add LVDS in as a non-DDC display.
+	 * Note, we can't assume the listed VGA will be filled in PortInfo[0],
+	 * when walking through connector table. connector_found has following
+	 * meaning:
+	 * 0 -- nothing found,
+	 * 1 -- only connectors[0] filled,
+	 * 2 -- only connectors[1] filled,
+	 * 3 -- both are filled.
+	 *
+	 * Note: I modified X.org algorithm to add additional entries if any
+	 * after the second table slot. Those entries do not affect the value
+	 * of connector_found. --BenH.
+	 */
+	int connector_found = 0;
 
 	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);
+	if (offset == 0)
+		return -ENODEV;
+
+	for (i = 1; i < 4; i++) {
+		entry = offset + i*2;
+
+		/* End of table */
+		if (!BIOS_IN8(entry) && i > 1)
+			break;
+
+		/* Read table entry, check connector type */
+		tmp = BIOS_IN16(entry);
+		conn_type = (tmp >> 12) & 0xf;
+		if (conn_type == legacy_conn_none)
+			continue;
+		ddc_type = (tmp >> 8) & 0xf;
+		dac_type = (tmp & 0x01) ? dac_tvdac : dac_primary;
+		tmds_type = (tmp & 0x10) ? tmds_external : tmds_internal;
+
+		/* same connector */
+		if (connector_found > 0) {
+			if (rinfo->connectors[conn_index].ddc_type == ddc_type)
+				continue;
+		}
+
+		/* sanity checks */
+		if (ddc_type > ddc_crt2)
+			ddc_type = ddc_none;
+		if (conn_type > legacy_conn_unsupported)
+			conn_type = legacy_conn_unsupported;
+		if (conn_type != legacy_conn_dvi_d &&
+		    conn_type != legacy_conn_dvi_i &&
+		    tmds_type == tmds_internal)
+			tmds_type= tmds_unknown;
+
+		/* convert connector type */
+		conn_type = legacy_conn_to_type[conn_type];
+
+		/* internal DDC_DVI port will get assigned to connector[0], or
+		 * if there is no DDC_DVI (like in some IGPs).
+		 */
+		conn_index = (ddc_type == ddc_dvi || conn_index == 1) ? 0 : 1;
+
+		/* if the port is a TV port, or both connectors are already
+		 * assigned, assign it after further in the table
+		 */
+		if (conn_type == conn_ctv || conn_type == conn_stv ||
+		    (rinfo->connectors[0].conn_type != conn_none &&
+		     rinfo->connectors[1].conn_type))
+			idx = conn_add++;
+		else
+			idx = conn_index;
+
+		/* if table full, exit */
+		if (idx >= RADEON_MAX_CONNECTORS) {
+			printk(KERN_WARNING "radeonfb: Connector table full !\n");
+			break;
 		}
+		rinfo->connectors[idx].conn_type	= conn_type;
+		rinfo->connectors[idx].ddc_type		= ddc_type;
+		rinfo->connectors[idx].dac_type		= dac_type;
+		rinfo->connectors[idx].tmds_type	= tmds_type;
+
+		/* increment connector_found for primary connectors only */
+		if (idx < 2)
+			connector_found += (idx + 1);
+	}
+
+	if (rinfo->is_mobility) {
+	    /* For the cases where only one VGA connector is found,
+	     * we assume LVDS is not listed in the connector table,
+	     * add it in here as the first port.
+	     *
+	     * TODO: Check what's up with laptops that have a DVI output
+	     * and no LVDS entry in the table. I suspect some thinkpads
+	     * may play trick with us here... We may want to check the
+	     * presence of a panel via LVDS_GEN_CNTL to be sure...
+	     */
+	    if ((connector_found < 3) &&
+		(rinfo->connectors[idx].conn_type == conn_vga)) {
+		    if (connector_found == 1) {
+			    memcpy(&rinfo->connectors[1],
+				   &rinfo->connectors[0],
+				   sizeof(struct radeon_connector));
+		    }
+		    /* Fixme: TV DAC is probably elsewhere ... */
+		    rinfo->connectors[0].dac_type = dac_tvdac;
+		    rinfo->connectors[0].tmds_type = tmds_unknown;
+		    rinfo->connectors[0].ddc_type = ddc_none;
+		    rinfo->connectors[0].conn_type = conn_proprietary;
+
+		    printk(KERN_WARNING "radeonfb: LVDS port is not in connector table, added in.\n");
+		    if (connector_found == 0)
+			    connector_found = 1;
+		    else
+			    connector_found = 3;
+	    }
+
+	    /* Check for LCD DDC info table */
+	    if ((offset = BIOS_IN16(rinfo->fp_bios_start + 0x42))) {
+		    if ((tmp = BIOS_IN16(offset + 0x15))) {
+			    if ((ddc_type = BIOS_IN8(tmp+2) & 0x07)) {
+				    rinfo->connectors[0].ddc_type = ddc_type;
+				    printk(KERN_WARNING "radeonfb: LCD DDC Info Table found, "
+						"forcing primary port to %d\n",
+						ddc_type);
+			    }
+		    }
+	    }
+	} else if (connector_found == 2) {
+		memcpy(&rinfo->connectors[0], &rinfo->connectors[1],
+			sizeof (struct radeon_connector));
+		rinfo->connectors[1].dac_type = dac_unknown;
+		rinfo->connectors[1].tmds_type = tmds_unknown;
+		rinfo->connectors[1].ddc_type = ddc_none;
+		rinfo->connectors[1].conn_type = conn_none;
+		connector_found = 1;
 	}
+
+	if (connector_found == 0)
+		return -ENODEV;
+
+	/* External TMDS Table, not used now */
+	return 0;
 }
 
 
@@ -362,6 +958,50 @@
     return connected ? MT_CRT : MT_NONE;
 }
 
+/* Find if the desired connector and monitor are compatible */
+static int __devinit radeon_conn_monitor_compatible(int mon_type, int conn_type) 
+{
+	switch(mon_type) {
+	case MT_CRT:
+		return ((conn_type == conn_vga) || (conn_type == conn_dvi_a));
+	case MT_DFP:
+		return ((conn_type == conn_dvi_i) || (conn_type == conn_dvi_d));
+	case MT_LCD:
+		return (conn_type == conn_lvds);
+	case MT_CTV:
+		return (conn_type == conn_ctv);
+	case MT_STV:
+		return (conn_type == conn_stv);
+	case MT_UNKNOWN:
+	case MT_NONE:
+	default:
+		return 0;
+	}
+	// leaves conn_digital, conn_unsupported, conn_propritetary
+}
+
+/* Find a suitable connector for this display type */
+static int __devinit radeon_find_connector_for_mon(struct radeonfb_info *rinfo, int mon_type)
+{
+	int i;
+	
+	if (mon_type <= MT_NONE) return 0;
+	
+	for (i = 0; i < RADEON_MAX_CONNECTORS ; i++) {
+		if (radeon_conn_monitor_compatible(mon_type, rinfo->connectors[i].conn_type) && 
+		    (rinfo->connectors[i].mon_type <= MT_NONE)) {
+			rinfo->connectors[i].mon_type = mon_type;
+			rinfo->connectors[i].head = rinfo->num_heads;
+			rinfo->heads[rinfo->num_heads] = i;
+			rinfo->num_heads++;
+			return 0;
+		}
+	}
+
+	printk(KERN_INFO "radeonfb: couldn't find a connector for monitor %d\n", mon_type);
+	return -1;
+}
+
 /*
  * Parse the "monitor_layout" string if any. This code is mostly
  * copied from XFree's radeon driver
@@ -403,19 +1043,20 @@
 		s1[i] = 0;
 		s2[0] = 0;
 	}
+
 	if (strcmp(s1, "CRT") == 0)
-		rinfo->mon1_type = MT_CRT;
+		radeon_find_connector_for_mon(rinfo, MT_CRT);
 	else if (strcmp(s1, "TMDS") == 0)
-		rinfo->mon1_type = MT_DFP;
+		radeon_find_connector_for_mon(rinfo, MT_DFP);
 	else if (strcmp(s1, "LVDS") == 0)
-		rinfo->mon1_type = MT_LCD;
+		radeon_find_connector_for_mon(rinfo, MT_LCD);
 
 	if (strcmp(s2, "CRT") == 0)
-		rinfo->mon2_type = MT_CRT;
+		radeon_find_connector_for_mon(rinfo, MT_CRT);
 	else if (strcmp(s2, "TMDS") == 0)
-		rinfo->mon2_type = MT_DFP;
+		radeon_find_connector_for_mon(rinfo, MT_DFP);
 	else if (strcmp(s2, "LVDS") == 0)
-		rinfo->mon2_type = MT_LCD;
+		radeon_find_connector_for_mon(rinfo, MT_LCD);
 
 	return 1;
 }
@@ -429,12 +1070,7 @@
 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);
+	int i;
 
 	if (radeon_parse_monitor_layout(rinfo, monitor_layout)) {
 
@@ -445,30 +1081,33 @@
 		 * a layout for each card ?
 		 */
 
-		RTRACE("Using specified monitor layout: %s", monitor_layout);
+		RTRACE("Using specified monitor layout: %s\n", 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;
+			int mon_type;
+
+			/* If the DDC detection fails, 
+			   we still want to use the user's specified layout! */
+			mon_type = PRIMARY_MONITOR(rinfo);
+
+			if (PRIMARY_MONITOR(rinfo) > MT_NONE)
+				if (radeon_probe_i2c_connector(rinfo, &PRIMARY_HEAD(rinfo)))
+					PRIMARY_MONITOR(rinfo) = mon_type;
+			if (SECONDARY_HEAD_PRESENT(rinfo)) {
+				mon_type = SECONDARY_MONITOR(rinfo);
+				if (SECONDARY_MONITOR(rinfo) > MT_NONE) {
+					if (radeon_probe_i2c_connector(rinfo, &SECONDARY_HEAD(rinfo))) {
+						rinfo->connectors[rinfo->heads[1]].mon_type = mon_type;
+					}
 				}
-			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;
+		
+		/* If the user specified a bogus monitor layout... */
+		if (PRIMARY_MONITOR(rinfo) <= MT_NONE) {
+			radeon_find_connector_for_mon(rinfo, MT_CRT);
+			printk(KERN_INFO "radeonfb: No valid monitor, assuming CRT on first port\n");
 		}
 	} else {
 		/*
@@ -477,182 +1116,100 @@
 		
 		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);
-			if (rinfo->mon1_type == MT_NONE)
-				rinfo->mon1_type =
-					radeon_probe_i2c_connector(rinfo, ddc_crt2,
-								   &rinfo->mon1_EDID);	
-#endif /* CONFIG_FB_RADEON_I2C */
-			if (rinfo->mon1_type == MT_NONE)
-				rinfo->mon1_type = MT_CRT;
-			goto bail;
-		}
-
-		/*
-		 * Check for cards with reversed DACs or TMDS controllers using BIOS
-		 */
-		if (rinfo->bios_seg &&
-		    (tmp = BIOS_IN16(rinfo->fp_bios_start + 0x50))) {
-			for (i = 1; i < 4; i++) {
-				unsigned int tmp0;
-
-				if (!BIOS_IN8(tmp + i*2) && i > 1)
-					break;
-				tmp0 = BIOS_IN16(tmp + i*2);
-				if ((!(tmp0 & 0x01)) && (((tmp0 >> 8) & 0x0f) == ddc_dvi)) {
-					rinfo->reversed_DAC = 1;
-					printk(KERN_INFO "radeonfb: Reversed DACs detected\n");
-				}
-				if ((((tmp0 >> 8) & 0x0f) == ddc_dvi) && ((tmp0 >> 4) & 0x01)) {
-					rinfo->reversed_TMDS = 1;
-					printk(KERN_INFO "radeonfb: Reversed TMDS detected\n");
+			/* Probe each connector */
+			for(i = 0 ; i < RADEON_MAX_CONNECTORS ; i++) {
+				if (PRIMARY_MONITOR(rinfo) > MT_NONE) break;  /* only one head */
+				if (!radeon_probe_i2c_connector(rinfo, &rinfo->connectors[i])) {
+					rinfo->heads[rinfo->num_heads] = i;
+					rinfo->connectors[i].head = rinfo->num_heads++;
 				}
 			}
+#endif /* CONFIG_FB_RADEON_I2C */
+			if (PRIMARY_MONITOR(rinfo) <= MT_NONE) {
+				radeon_find_connector_for_mon(rinfo, MT_CRT);
+			}
+			goto bail;
 		}
 
-		/*
-		 * Probe primary head (DVI or laptop internal panel)
-		 */
-#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 */
+		/* Probe heads */
 #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_crt2,
-								      &rinfo->mon1_EDID);
-			if (rinfo->mon1_type != MT_NONE)
-				ddc_crt2_used = 1;
+		/* Probe each connector in turn. */
+		for(i = 0 ; i < RADEON_MAX_CONNECTORS ; i++) {
+			if (rinfo->connectors[i].mon_type > MT_NONE) continue; /* Don't probe "detected" stuff again */
+			if (!radeon_probe_i2c_connector(rinfo, &rinfo->connectors[i])) {
+				rinfo->heads[rinfo->num_heads] = i;
+				rinfo->connectors[i].head = rinfo->num_heads++;
+			}
 		}
+	
 #endif /* CONFIG_FB_RADEON_I2C */
-		if (rinfo->mon1_type == MT_NONE && rinfo->is_mobility &&
+
+		/* mobility chips are usually LCDs */
+		if ((PRIMARY_MONITOR(rinfo) <= MT_NONE) && rinfo->is_mobility &&
 		    ((rinfo->bios_seg && (INREG(BIOS_4_SCRATCH) & 4))
 		     || (INREG(LVDS_GEN_CNTL) & LVDS_ON))) {
-			rinfo->mon1_type = MT_LCD;
-			printk("Non-DDC laptop panel detected\n");
+			radeon_find_connector_for_mon(rinfo, MT_LCD);
+			printk(KERN_INFO "Non-DDC laptop panel detected\n");
 		}
-		if (rinfo->mon1_type == MT_NONE)
-			rinfo->mon1_type = radeon_crt_is_connected(rinfo, rinfo->reversed_DAC);
 
-		/*
-		 * Probe secondary head (mostly VGA, can be DVI)
-		 */
-#ifdef CONFIG_PPC_OF
-		if (rinfo->mon2_type == MT_NONE)
-			rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1,
-								&rinfo->mon2_EDID);
-#endif /* CONFIG_PPC_OF */
-#ifdef CONFIG_FB_RADEON_I2C
-		if (rinfo->mon2_type == MT_NONE)
-			rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_vga,
-								      &rinfo->mon2_EDID);
-		if (rinfo->mon2_type == MT_NONE && !ddc_crt2_used)
-			rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_crt2,
-								      &rinfo->mon2_EDID);
-#endif /* CONFIG_FB_RADEON_I2C */
-		if (rinfo->mon2_type == MT_NONE)
-			rinfo->mon2_type = radeon_crt_is_connected(rinfo, !rinfo->reversed_DAC);
+		/* Probe for monitors on the primary and secondary crtc heads */
+		if (PRIMARY_MONITOR(rinfo) <= MT_NONE) {
+			radeon_find_connector_for_mon(rinfo, radeon_crt_is_connected(rinfo, 1));
+		}
 
-		/*
-		 * If we only detected port 2, we swap them, if none detected,
-		 * assume CRT (maybe fallback to old BIOS_SCRATCH stuff ? or look
-		 * at FP registers ?)
-		 */
-		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;
-			rinfo->mon2_type = MT_NONE;
-			rinfo->mon2_EDID = NULL;
+		/* If we still haven't found anything, just force it to be on the CRT.. */
+		if (PRIMARY_MONITOR(rinfo) <= MT_NONE) {
+			radeon_find_connector_for_mon(rinfo, MT_CRT);
 		}
 
-		/*
-		 * Deal with reversed TMDS
-		 */
-		if (rinfo->reversed_TMDS) {
-			/* Always keep internal TMDS as primary head */
-			if (rinfo->mon1_type == MT_DFP || rinfo->mon2_type == MT_DFP) {
-				int tmp_type = rinfo->mon1_type;
-				u8 *tmp_EDID = rinfo->mon1_EDID;
-				rinfo->mon1_type = rinfo->mon2_type;
-				rinfo->mon1_EDID = rinfo->mon2_EDID;
-				rinfo->mon2_type = tmp_type;
-				rinfo->mon2_EDID = tmp_EDID;
-				if (rinfo->mon1_type == MT_CRT || rinfo->mon2_type == MT_CRT)
-					rinfo->reversed_DAC ^= 1;
-			}
+		/* Always keep internal TMDS as primary head */
+		if (SECONDARY_HEAD_PRESENT(rinfo) && (SECONDARY_HEAD(rinfo).tmds_type == tmds_internal) && (SECONDARY_MONITOR(rinfo) == MT_DFP)) {
+			int head = rinfo->heads[0];
+			rinfo->heads[0] = rinfo->heads[1];
+			rinfo->heads[1] = head;
 		}
 	}
-	if (ignore_edid) {
-		kfree(rinfo->mon1_EDID);
-		rinfo->mon1_EDID = NULL;
-		kfree(rinfo->mon2_EDID);
-		rinfo->mon2_EDID = NULL;
-	}
+bail:
 
- bail:
-	printk(KERN_INFO "radeonfb: Monitor 1 type %s found\n",
-	       radeon_get_mon_name(rinfo->mon1_type));
-	if (rinfo->mon1_EDID)
-		printk(KERN_INFO "radeonfb: EDID probed\n");
-	if (!rinfo->has_CRTC2)
-		return;
-	printk(KERN_INFO "radeonfb: Monitor 2 type %s found\n",
-	       radeon_get_mon_name(rinfo->mon2_type));
-	if (rinfo->mon2_EDID)
-		printk(KERN_INFO "radeonfb: EDID probed\n");
+	/* Dump out the heads we've found so far */
+	for (i = 0; i < RADEON_MAX_CONNECTORS; i++) {
+		
+		if (rinfo->connectors[i].conn_type == conn_none)
+			continue;
+		printk(KERN_INFO " * Connector %d is %s. Head %d, Monitor: %s ", i+1,
+		       conn_type_name[rinfo->connectors[i].conn_type],
+		       rinfo->connectors[i].head,
+		       rinfo->connectors[i].mon_type == MT_UNKNOWN  ?
+		         "Not Probed Yet" : 
+		         mon_type_name[rinfo->connectors[i].mon_type]);
+		if (rinfo->connectors[i].edid) {
+			printk("EDID probed\n");
+		} else {
+			printk("\n");
+		}
+		printk(KERN_INFO "   ddc port: %d, dac: %d, tmds: %d\n",
+		       rinfo->connectors[i].ddc_type,
+		       rinfo->connectors[i].dac_type,
+		       rinfo->connectors[i].tmds_type);
+	}
 }
 
 
-/*
- * This functions applyes any arch/model/machine specific fixups
- * to the panel info. It may eventually alter EDID block as
- * well or whatever is specific to a given model and not probed
- * properly by the default code
- */
-static void radeon_fixup_panel_info(struct radeonfb_info *rinfo)
-{
 #ifdef CONFIG_PPC_OF
+int __devinit radeon_get_lvds_info_openfirmware(struct radeonfb_info *rinfo)
+{
+
 	/*
 	 * LCD Flat panels should use fixed dividers, we enfore that on
 	 * PPC only for now...
 	 */
-	if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD
-	    && rinfo->is_mobility) {
+	If (!rinfo->panel_info.use_bios_dividers && (PRIMARY_MONITOR(rinfo) == MT_LCD) &&
+	    rinfo->is_mobility) {
 		int ppll_div_sel;
 		u32 ppll_divn;
 		ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3;
@@ -663,15 +1220,16 @@
 		rinfo->panel_info.post_divider = (ppll_divn >> 16) & 0x7;
 		rinfo->panel_info.use_bios_dividers = 1;
 
-		printk(KERN_DEBUG "radeonfb: Using Firmware dividers 0x%08x "
+		printk(KERN_INFO "Using Firmware dividers 0x%08x "
 		       "from PPLL %d\n",
 		       rinfo->panel_info.fbk_divider |
 		       (rinfo->panel_info.post_divider << 16),
 		       ppll_div_sel);
+		return 0;
 	}
-#endif /* CONFIG_PPC_OF */
+	return 1;
 }
-
+#endif /* CONFIG_PPC_OF */
 
 /*
  * Fill up panel infos from a mode definition, either returned by the EDID
@@ -741,19 +1299,24 @@
 	/*
 	 * First check out what BIOS has to say
 	 */
-	if (rinfo->mon1_type == MT_LCD)
-		radeon_get_panel_info_BIOS(rinfo);
+	if (PRIMARY_MONITOR(rinfo) == MT_LCD) {
+		if (rinfo->radeon_get_lvds_info) {
+			rinfo->radeon_get_lvds_info(rinfo);
+			// XXX Do we care about the return value?
+		}
+	}
 
 	/*
 	 * Parse EDID detailed timings and deduce panel infos if any. Right now
 	 * we only deal with first entry returned by parse_EDID, we may do better
 	 * some day...
 	 */
-	if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type != MT_CRT
-	    && rinfo->mon1_EDID) {
+	if (!rinfo->panel_info.use_bios_dividers && 
+	    (PRIMARY_MONITOR(rinfo) != MT_CRT) &&
+	    PRIMARY_HEAD(rinfo).edid) {
 		struct fb_var_screeninfo var;
 		RTRACE("Parsing EDID data for panel info\n");
-		if (fb_parse_edid(rinfo->mon1_EDID, &var) == 0) {
+		if (fb_parse_edid(PRIMARY_HEAD(rinfo).edid, &var) == 0) {
 			if (var.xres >= rinfo->panel_info.xres &&
 			    var.yres >= rinfo->panel_info.yres)
 				radeon_var_to_panel_info(rinfo, &var);
@@ -761,15 +1324,10 @@
 	}
 
 	/*
-	 * Do any additional platform/arch fixups to the panel infos
-	 */
-	radeon_fixup_panel_info(rinfo);
-
-	/*
 	 * If we have some valid panel infos, we setup the default mode based on
 	 * those
 	 */
-	if (rinfo->mon1_type != MT_CRT && rinfo->panel_info.valid) {
+	if ((PRIMARY_MONITOR(rinfo) != MT_CRT) && rinfo->panel_info.valid) {
 		struct fb_var_screeninfo *var = &info->var;
 
 		RTRACE("Setting up default mode based on panel info\n");
@@ -800,13 +1358,13 @@
 	/*
 	 * Now build modedb from EDID
 	 */
-	if (rinfo->mon1_EDID) {
-		fb_edid_to_monspecs(rinfo->mon1_EDID, &info->monspecs);
+	if (PRIMARY_HEAD(rinfo).edid) {
+		fb_edid_to_monspecs(PRIMARY_HEAD(rinfo).edid, &info->monspecs);
 		fb_videomode_to_modelist(info->monspecs.modedb,
 					 info->monspecs.modedb_len,
 					 &info->modelist);
-		rinfo->mon1_modedb = info->monspecs.modedb;
-		rinfo->mon1_dbsize = info->monspecs.modedb_len;
+		PRIMARY_HEAD(rinfo).modedb = info->monspecs.modedb;
+		PRIMARY_HEAD(rinfo).modedb_size = info->monspecs.modedb_len;
 	}
 
 	
@@ -815,7 +1373,7 @@
 	 * we try to read it from card), we try to pick a default mode
 	 * and create some panel infos. Whatever...
 	 */
-	if (rinfo->mon1_type != MT_CRT && !rinfo->panel_info.valid) {
+	if ((PRIMARY_MONITOR(rinfo) != MT_CRT) && !rinfo->panel_info.valid) {
 		struct fb_videomode	*modedb;
 		int			dbsize;
 		char			modename[32];
@@ -829,18 +1387,18 @@
 		}
 		if (rinfo->panel_info.xres == 0 || rinfo->panel_info.yres == 0) {
 			printk(KERN_WARNING "radeonfb: Can't find panel size, going back to CRT\n");
-			rinfo->mon1_type = MT_CRT;
+			radeon_find_connector_for_mon(rinfo, MT_CRT);
 			goto pickup_default;
 		}
 		printk(KERN_WARNING "radeonfb: Assuming panel size %dx%d\n",
 		       rinfo->panel_info.xres, rinfo->panel_info.yres);
-		modedb = rinfo->mon1_modedb;
-		dbsize = rinfo->mon1_dbsize;
+		modedb = PRIMARY_HEAD(rinfo).modedb;
+		dbsize = PRIMARY_HEAD(rinfo).modedb_size;
 		snprintf(modename, 31, "%dx%d", rinfo->panel_info.xres, rinfo->panel_info.yres);
 		if (fb_find_mode(&info->var, info, modename,
 				 modedb, dbsize, NULL, 8) == 0) {
 			printk(KERN_WARNING "radeonfb: Can't find mode for panel size, going back to CRT\n");
-			rinfo->mon1_type = MT_CRT;
+			radeon_find_connector_for_mon(rinfo, MT_CRT);
 			goto pickup_default;
 		}
 		has_default_mode = 1;
@@ -943,14 +1501,14 @@
 	memcpy(dest, src, sizeof(struct fb_var_screeninfo));
 
 	/* Check if we have a modedb built from EDID */
-	if (rinfo->mon1_modedb) {
-		db = rinfo->mon1_modedb;
-		dbsize = rinfo->mon1_dbsize;
+	if (PRIMARY_HEAD(rinfo).modedb) {
+		db = PRIMARY_HEAD(rinfo).modedb;
+		dbsize = PRIMARY_HEAD(rinfo).modedb_size;
 		native_db = 1;
 	}
 
 	/* Check if we have a scaler allowing any fancy mode */
-	has_rmx = rinfo->mon1_type == MT_LCD || rinfo->mon1_type == MT_DFP;
+	has_rmx = (PRIMARY_MONITOR(rinfo) == MT_LCD) || (PRIMARY_MONITOR(rinfo) == MT_DFP);
 
 	/* If we have a scaler and are passed FB_ACTIVATE_TEST or
 	 * FB_ACTIVATE_NOW, just do basic checking and return if the
@@ -963,7 +1521,7 @@
 		 * 640x480-60, but I assume userland knows what it's doing here
 		 * (though I may be proven wrong...)
 		 */
-		if (has_rmx == 0 && rinfo->mon1_modedb)
+		if (has_rmx == 0 && PRIMARY_HEAD(rinfo).modedb)
 			if (fb_validate_mode((struct fb_var_screeninfo *)src, rinfo->info))
 				return -EINVAL;
 		return 0;
Only in aty: radeon_monitor.o
Only in aty: radeon_pm.o
diff -aur aty-2.6.15/radeonfb.h aty/radeonfb.h
--- aty-2.6.15/radeonfb.h	2006-02-22 23:28:14.000000000 -0500
+++ aty/radeonfb.h	2006-03-01 12:24:57.000000000 -0500
@@ -87,11 +87,50 @@
 	CHIP_ERRATA_PLL_DELAY		= 0x00000004,
 };
 
+/*
+ * DDC i2c ports
+ */
+enum radeon_ddc_type {
+	ddc_none = -1,
+	ddc_monid = 0,
+	ddc_dvi,
+	ddc_vga,
+	ddc_crt2,
+};
+
+/*
+ * Connector types
+ */
+enum radeon_legacy_conn_type {
+	legacy_conn_none = 0,
+	legacy_conn_proprietary,
+	legacy_conn_crt,
+	legacy_conn_dvi_i,
+	legacy_conn_dvi_d,
+	legacy_conn_ctv,
+	legacy_conn_stv,
+	legacy_conn_unsupported,
+};
+
+enum radeon_conn_type {
+	conn_none = 0,
+	conn_vga,
+	conn_dvi_i,
+	conn_dvi_d,
+	conn_dvi_a,
+	conn_stv,
+	conn_ctv,
+	conn_lvds,
+	conn_digital,
+	conn_unsupported,
+	conn_proprietary,
+};
 
 /*
  * Monitor types
  */
-enum radeon_montype {
+enum radeon_mon_type {
+	MT_UNKNOWN = -1,
 	MT_NONE = 0,
 	MT_CRT,		/* CRT */
 	MT_LCD,		/* LCD */
@@ -101,27 +140,45 @@
 };
 
 /*
- * DDC i2c ports
+ * DAC types
  */
-enum ddc_type {
-	ddc_none,
-	ddc_monid,
-	ddc_dvi,
-	ddc_vga,
-	ddc_crt2,
+enum radeon_dac_type {
+	dac_unknown = -1,
+	dac_primary = 0,
+	dac_tvdac = 1,
 };
 
 /*
- * Connector types
+ * TMDS types
  */
-enum conn_type {
-	conn_none,
-	conn_proprietary,
-	conn_crt,
-	conn_DVI_I,
-	conn_DVI_D,
+enum radeon_tmds_type {
+	tmds_unknown = -1,
+	tmds_internal = 0,
+	tmds_external = 1,
 };
 
+/*
+ * Each connector gets this structure associated with it,
+ * containing infos about the connector wiring and about
+ * whatever has been detected on it
+ */
+struct radeon_connector {
+	enum radeon_conn_type	conn_type;
+	enum radeon_ddc_type	ddc_type;
+	enum radeon_dac_type	dac_type;
+	enum radeon_tmds_type	tmds_type;
+	enum radeon_mon_type	mon_type;
+	u8			*edid;
+	struct fb_videomode	*modedb;
+	unsigned int		modedb_size;
+
+	int head;
+};
+
+/*
+ * Currently, the driver deals with at most 4 connectors
+ */
+#define RADEON_MAX_CONNECTORS	4
 
 /*
  * PLL infos
@@ -129,11 +186,19 @@
 struct pll_info {
 	int ppll_max;
 	int ppll_min;
-	int sclk, mclk;
+	int sclk;
+	int mclk;
 	int ref_div;
 	int ref_clk;
 };
 
+/*
+ * TMDS PLL infos
+ */
+struct radeon_tmds_pll_info {
+	long	freq;
+	u32	value;
+};
 
 /*
  * This structure contains the various registers manipulated by this
@@ -298,6 +363,20 @@
 	void __iomem		*bios_seg;
 	int			fp_bios_start;
 
+	int                     is_atom_bios;
+	int                     atom_data_start;
+	
+	/* BIOS Functions */
+	int                     (*radeon_get_pll_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_lvds_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_conn_info)(struct radeonfb_info *rinfo);
+	int                     (*radeon_get_tmds_info)(struct radeonfb_info *rinfo);
+
+	/* Connector infos */
+	struct radeon_connector		connectors[RADEON_MAX_CONNECTORS];
+	int                     heads[RADEON_MAX_CONNECTORS];  // index into connectors.
+	int                     num_heads;  // number of heads.
+
 	u32			pseudo_palette[17];
 	struct { u8 red, green, blue, pad; }
 				palette[256];
@@ -316,15 +395,8 @@
 	int			has_CRTC2;
 	int			is_mobility;
 	int			is_IGP;
-	int			reversed_DAC;
-	int			reversed_TMDS;
 	struct panel_info	panel_info;
-	int			mon1_type;
-	u8			*mon1_EDID;
-	struct fb_videomode	*mon1_modedb;
-	int			mon1_dbsize;
-	int			mon2_type;
-	u8		        *mon2_EDID;
+	struct radeon_tmds_pll_info	tmds_pll[4];
 
 	u32			dp_gui_master_cntl;
 
@@ -356,8 +428,13 @@
 };
 
 
-#define PRIMARY_MONITOR(rinfo)	(rinfo->mon1_type)
+#define PRIMARY_HEAD(rinfo)      (rinfo->connectors[rinfo->heads[0]])
+#define SECONDARY_HEAD(rinfo)    (rinfo->connectors[rinfo->heads[1]])
+
+#define SECONDARY_HEAD_PRESENT(rinfo) (rinfo->heads[1] != -1)
 
+#define PRIMARY_MONITOR(rinfo)	 (rinfo->connectors[rinfo->heads[0]].mon_type)
+#define SECONDARY_MONITOR(rinfo) ((SECONDARY_HEAD_PRESENT(rinfo) ? (rinfo->connectors[rinfo->heads[1]].mon_type) : MT_NONE))
 
 /*
  * Debugging stuffs
@@ -596,7 +673,7 @@
 /* I2C Functions */
 extern void radeon_create_i2c_busses(struct radeonfb_info *rinfo);
 extern void radeon_delete_i2c_busses(struct radeonfb_info *rinfo);
-extern int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 **out_edid);
+extern int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, struct radeon_connector *conn);
 
 /* PM Functions */
 extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state);
@@ -625,4 +702,18 @@
 extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
 			       int reg_only);
 
+/* Bios functions.  Fix this. */
+extern void __devinit radeon_get_conn_info(struct radeonfb_info *rinfo, int ignore_conntable);
+extern void __devinit radeon_get_tmds_info(struct radeonfb_info *rinfo);
+
+extern int __devinit radeon_get_lvds_info_atom(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_lvds_info_legacy(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_conn_info_atom(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_conn_info_legacy(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_tmds_info_legacy(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_tmds_info_atom(struct radeonfb_info *rinfo);
+#ifdef CONFIG_PPC_OF
+extern int __devinit radeon_get_lvds_info_openfirmware(struct radeonfb_info *rinfo);
+extern int __devinit radeon_get_conn_info_openfirmware(struct radeonfb_info *rinfo);
+#endif
 #endif /* __RADEONFB_H__ */
Only in aty: radeonfb.ko
Only in aty: radeonfb.mod.c
Only in aty: radeonfb.mod.o
Only in aty: radeonfb.o

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [patch] [radeonfb] Radeon M26 and ATOM bios support (take 5)
  2006-03-01 21:34                   ` [patch] [radeonfb] Radeon M26 and ATOM bios support (take 5) Stuffed Crust
@ 2006-03-01 21:47                     ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 16+ messages in thread
From: Benjamin Herrenschmidt @ 2006-03-01 21:47 UTC (permalink / raw)
  To: linux-fbdev-devel; +Cc: Solomon Peachy

On Wed, 2006-03-01 at 16:34 -0500, Stuffed Crust wrote:
> On Wed, Mar 01, 2006 at 03:36:11PM -0500, Stuffed Crust wrote:
> > I'm about to mail out v5 of the patch.
> 
> Signed-Off-By:  Solomon Peachy <pizza@shaftnet.org>
> 
> The short set of changes in this patch (against 2.6.15.4)
> 
>  * ATOM BIOS support for newer Radeon cards
>  * Clean method of detecting and handling disparate BIOS types
>  * Radeon RV410/M26/M26GL (aka Mobility X700/FireGL5000) card IDs
>  * Default PLL clocks for R420 and variants
>  * Handle bogus PLL divider with sane default.
>  * All new connector/head detection code that uses bios/firmware 
>    defaults whenever possible

Ok, good. I need some time to review properly. There are a few things I
want to change, for example, some of the OF stuff should be decided at
runtime, not compile time (what set of callbacks to use) since some OF
machines do use x86 cards, etc... but I can fix all of these things.

Thanks for the great work.

Ben.




-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2006-03-01 21:47 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-01-03 20:44 [patch] [radeonfb] Radeon Mobility X700 (M26) and ATOM bios support Stuffed Crust
2006-01-03 23:29 ` Petr Vandrovec
2006-01-05  9:59 ` Benjamin Herrenschmidt
2006-01-05 20:13   ` Stuffed Crust
2006-02-14 21:28 ` Benjamin Herrenschmidt
2006-02-22 22:19   ` Stuffed Crust
2006-02-23  6:09     ` Stuffed Crust
2006-02-23  6:50       ` Benjamin Herrenschmidt
2006-02-23 22:36         ` [patch] [radeonfb] Radeon M26 and ATOM bios support (take 4) Stuffed Crust
2006-02-23 23:15           ` Benjamin Herrenschmidt
2006-03-01  3:33             ` Stuffed Crust
2006-03-01  3:46               ` Benjamin Herrenschmidt
2006-03-01 16:56                 ` Gabor Gombas
2006-03-01 20:36                 ` Stuffed Crust
2006-03-01 21:34                   ` [patch] [radeonfb] Radeon M26 and ATOM bios support (take 5) Stuffed Crust
2006-03-01 21:47                     ` Benjamin Herrenschmidt

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.