lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 17 May 2007 21:59:59 +0200
From:	Luca Tettamanti <kronos.it@...il.com>
To:	Benjamin Herrenschmidt <benh@...nel.crashing.org>
Cc:	Daniel Drake <dsd@...too.org>,
	linux list <linux-kernel@...r.kernel.org>, Jimmy.Jazz@....net
Subject: Re: radeonfb and X800 cards

Il Thu, May 17, 2007 at 11:59:23AM +1000, Benjamin Herrenschmidt ha scritto: 
> On Wed, 2007-05-16 at 21:47 -0400, Daniel Drake wrote:
> > Hi,
> > 
> > Did anything happen to the patch titled "radeonfb: add support for newer 
> > cards"?
> > http://lwn.net/Articles/215965/
> > 
> > Jimmy at http://bugs.gentoo.org/174063 has extended upon this with some 
> > further fixes based on code the in X11 driver. The patches are on the 
> > bug report.
> > 
> > Ben, where can the most up-to-date radeonfb code be found?
> 
> upstream. I haven't released anything else so far. Does the patch still
> apply ?

This is what I'm using here, it's still the same old patch (Solomon's
plus my stuff). I think I can coordinate with Jimmy.Jazz@....net to
create an incremental patch to fix radeonfb on his hardware (provided
that this mega-patch is suitable as baseline that is).

---
  Applies to current git

 drivers/video/aty/ati_ids.h          |   32 
 drivers/video/aty/radeon_accel.c     |   19 
 drivers/video/aty/radeon_backlight.c |    4 
 drivers/video/aty/radeon_base.c      |  494 +++++++----
 drivers/video/aty/radeon_i2c.c       |   66 -
 drivers/video/aty/radeon_monitor.c   | 1339 +++++++++++++++++++++---------
 drivers/video/aty/radeonfb.h         |  163 ++-
 7 files changed, 1516 insertions(+), 601 deletions(-)

diff --git a/drivers/video/aty/ati_ids.h b/drivers/video/aty/ati_ids.h
index 90e7df2..7b701cc 100644
--- a/drivers/video/aty/ati_ids.h
+++ b/drivers/video/aty/ati_ids.h
@@ -31,6 +31,7 @@
 #define PCI_CHIP_RV360_AR               0x4152
 #define PCI_CHIP_RV350_AS               0x4153
 #define PCI_CHIP_RV350_AT               0x4154
+#define PCI_CHIP_RV350_AU 		0x4155
 #define PCI_CHIP_RV350_AV               0x4156
 #define PCI_CHIP_MACH32			0x4158
 #define PCI_CHIP_RS250_4237		0x4237
@@ -71,7 +72,13 @@
 #define PCI_CHIP_R420_JL                0x4A4C
 #define PCI_CHIP_R420_JM                0x4A4D
 #define PCI_CHIP_R420_JN                0x4A4E
+#define PCI_CHIP_R420_JO 		0x4A4F
 #define PCI_CHIP_R420_JP                0x4A50
+#define PCI_CHIP_R420_JT 		0x4A54
+#define PCI_CHIP_R480_KI 		0x4B49
+#define PCI_CHIP_R480_KJ 		0x4B4A
+#define PCI_CHIP_R480_KK                0x4B4B
+#define PCI_CHIP_R480_KL 		0x4B4C
 #define PCI_CHIP_MACH64LB		0x4C42
 #define PCI_CHIP_MACH64LD		0x4C44
 #define PCI_CHIP_RAGE128LE		0x4C45
@@ -182,9 +189,19 @@
 #define PCI_CHIP_R423_UI                0x5549
 #define PCI_CHIP_R423_UJ                0x554A
 #define PCI_CHIP_R423_UK                0x554B
+#define PCI_CHIP_R423_UL 		0x554C
+#define PCI_CHIP_R423_UM 		0x554D
+#define PCI_CHIP_R423_UN 		0x554E
+#define PCI_CHIP_R423_UO 		0x554F
+#define PCI_CHIP_R423_UP 	       	0x5550
 #define PCI_CHIP_R423_UQ                0x5551
 #define PCI_CHIP_R423_UR                0x5552
 #define PCI_CHIP_R423_UT                0x5554
+#define PCI_CHIP_RV410_VJ 		0x564A
+#define PCI_CHIP_RV410_VK 		0x564B
+#define PCI_CHIP_RV410_VO 		0x564F
+#define PCI_CHIP_RV410_VR 		0x5652
+#define PCI_CHIP_RV410_VS 		0x5653
 #define PCI_CHIP_MACH64VT		0x5654
 #define PCI_CHIP_MACH64VU		0x5655
 #define PCI_CHIP_MACH64VV		0x5656
@@ -206,7 +223,22 @@
 #define PCI_CHIP_RV280_5964		0x5964
 #define PCI_CHIP_RV280_5C61		0x5C61
 #define PCI_CHIP_RV280_5C63		0x5C63
+#define PCI_CHIP_R423_5D48		0x5D48
+#define PCI_CHIP_R423_5D49		0x5D49
+#define PCI_CHIP_R423_5D4A		0x5D4A
+#define PCI_CHIP_R480_5D4C		0x5D4C
+#define PCI_CHIP_R480_5D4D		0x5D4D
+#define PCI_CHIP_R480_5D4E		0x5D4E
+#define PCI_CHIP_R480_5D4F		0x5D4F
+#define PCI_CHIP_R480_5D50		0x5D50
+#define PCI_CHIP_R480_5D52		0x5D52
 #define PCI_CHIP_R423_5D57              0x5D57
+#define PCI_CHIP_RV410_5E48		0x5E48
+#define PCI_CHIP_RV410_5E4A		0x5E4A
+#define PCI_CHIP_RV410_5E4B		0x5E4B
+#define PCI_CHIP_RV410_5E4C		0x5E4C
+#define PCI_CHIP_RV410_5E4D		0x5E4D
+#define PCI_CHIP_RV410_5E4F		0x5E4F
 #define PCI_CHIP_RS350_7834             0x7834
 #define PCI_CHIP_RS350_7835             0x7835
 #define PCI_CHIP_RS480_5955             0x5955
diff --git a/drivers/video/aty/radeon_accel.c b/drivers/video/aty/radeon_accel.c
index 3ca27cb..529f158 100644
--- a/drivers/video/aty/radeon_accel.c
+++ b/drivers/video/aty/radeon_accel.c
@@ -203,9 +203,7 @@ void radeonfb_engine_reset(struct radeonfb_info *rinfo)
 	host_path_cntl = INREG(HOST_PATH_CNTL);
 	rbbm_soft_reset = INREG(RBBM_SOFT_RESET);
 
-	if (rinfo->family == CHIP_FAMILY_R300 ||
-	    rinfo->family == CHIP_FAMILY_R350 ||
-	    rinfo->family == CHIP_FAMILY_RV350) {
+	if (IS_R300_VARIANT(rinfo)) {
 		u32 tmp;
 
 		OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset |
@@ -241,9 +239,7 @@ void radeonfb_engine_reset(struct radeonfb_info *rinfo)
 	INREG(HOST_PATH_CNTL);
 	OUTREG(HOST_PATH_CNTL, host_path_cntl);
 
-	if (rinfo->family != CHIP_FAMILY_R300 ||
-	    rinfo->family != CHIP_FAMILY_R350 ||
-	    rinfo->family != CHIP_FAMILY_RV350)
+	if (IS_R300_VARIANT(rinfo))
 		OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset);
 
 	OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
@@ -254,16 +250,15 @@ void radeonfb_engine_init (struct radeonfb_info *rinfo)
 {
 	unsigned long temp;
 
-	/* disable 3D engine */
-	OUTREG(RB3D_CNTL, 0);
-
 	radeonfb_engine_reset(rinfo);
 
 	radeon_fifo_wait (1);
-	if ((rinfo->family != CHIP_FAMILY_R300) &&
-	    (rinfo->family != CHIP_FAMILY_R350) &&
-	    (rinfo->family != CHIP_FAMILY_RV350))
+	if (IS_R300_VARIANT(rinfo)) {
+		temp = INREG(RB2D_DSTCACHE_MODE);
+		OUTREG(RB2D_DSTCACHE_MODE, temp | (1<<17)); /* FIXME */
+	} else {
 		OUTREG(RB2D_DSTCACHE_MODE, 0);
+	}
 
 	radeon_fifo_wait (3);
 	/* We re-read MC_FB_LOCATION from card as it can have been
diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c
index 0be25fa..0c264f3 100644
--- a/drivers/video/aty/radeon_backlight.c
+++ b/drivers/video/aty/radeon_backlight.c
@@ -52,7 +52,7 @@ static int radeon_bl_update_status(struct backlight_device *bd)
 	u32 lvds_gen_cntl, tmpPixclksCntl;
 	int level;
 
-	if (rinfo->mon1_type != MT_LCD)
+	if (PRIMARY_MONITOR(rinfo) != MT_LCD)
 		return 0;
 
 	/* We turn off the LCD completely instead of just dimming the
@@ -138,7 +138,7 @@ void radeonfb_bl_init(struct radeonfb_info *rinfo)
 	struct radeon_bl_privdata *pdata;
 	char name[12];
 
-	if (rinfo->mon1_type != MT_LCD)
+	if (PRIMARY_MONITOR(rinfo) == MT_LCD)
 		return;
 
 #ifdef CONFIG_PMAC_BACKLIGHT
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
index 2ce0501..9616303 100644
--- a/drivers/video/aty/radeon_base.c
+++ b/drivers/video/aty/radeon_base.c
@@ -3,6 +3,7 @@
  *
  *	framebuffer driver for ATI Radeon chipset video boards
  *
+ *	Copyright 2006	Solomon Peachy <pizza@...ftnet.org>
  *	Copyright 2003	Ben. Herrenschmidt <benh@...nel.crashing.org>
  *	Copyright 2000	Ani Joshi <ajoshi@...nel.crashing.org>
  *
@@ -50,7 +51,7 @@
  */
 
 
-#define RADEON_VERSION	"0.2.0"
+#define RADEON_VERSION	"0.3.0"
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
@@ -112,7 +113,7 @@ static struct pci_device_id radeonfb_pci_table[] = {
 	/* Radeon IGP320M (U1) */
 	CHIP_DEF(PCI_CHIP_RS100_4336,	RS100,	CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
 	/* Radeon IGP320 (A3) */
-	CHIP_DEF(PCI_CHIP_RS100_4136,	RS100,	CHIP_HAS_CRTC2 | CHIP_IS_IGP), 
+	CHIP_DEF(PCI_CHIP_RS100_4136,	RS100,	CHIP_HAS_CRTC2 | CHIP_IS_IGP),
 	/* IGP330M/340M/350M (U2) */
 	CHIP_DEF(PCI_CHIP_RS200_4337,	RS200,	CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
 	/* IGP330/340/350 (A4) */
@@ -182,6 +183,7 @@ static struct pci_device_id radeonfb_pci_table[] = {
 	CHIP_DEF(PCI_CHIP_RV360_AR,	RV350,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_RV350_AS,	RV350,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_RV350_AT,	RV350,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV350_AU, 	RV350,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_RV350_AV,	RV350,	CHIP_HAS_CRTC2),
 	/* 9800/Pro/FileGL X2 */
 	CHIP_DEF(PCI_CHIP_R350_AH,	R350,	CHIP_HAS_CRTC2),
@@ -192,7 +194,7 @@ static struct pci_device_id radeonfb_pci_table[] = {
 	CHIP_DEF(PCI_CHIP_R350_NI,	R350,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R360_NJ,	R350,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R350_NK,	R350,	CHIP_HAS_CRTC2),
-	/* Newer stuff */
+	/* X300/X600 */
 	CHIP_DEF(PCI_CHIP_RV380_3E50,	RV380,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_RV380_3E54,	RV380,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_RV380_3150,	RV380,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
@@ -203,6 +205,19 @@ static struct pci_device_id radeonfb_pci_table[] = {
 	CHIP_DEF(PCI_CHIP_RV370_5B65,	RV380,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_RV370_5460,	RV380,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
 	CHIP_DEF(PCI_CHIP_RV370_5464,	RV380,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	/* X700 */
+	CHIP_DEF(PCI_CHIP_RV410_VJ, 	RV410,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VK, 	RV410,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VO, 	RV410,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VR, 	RV410,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_VS, 	RV410,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_RV410_5E48,	RV410,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV410_5E4A,	RV410,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV410_5E4B,	RV410,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV410_5E4C,	RV410,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV410_5E4D,	RV410,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_RV410_5E4F,	RV410,	CHIP_HAS_CRTC2),
+	/* X800/X850 */
 	CHIP_DEF(PCI_CHIP_R420_JH,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JI,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JJ,	R420,	CHIP_HAS_CRTC2),
@@ -210,7 +225,9 @@ static struct pci_device_id radeonfb_pci_table[] = {
 	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_R420_JO, 	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R420_JP,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R420_JT, 	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UH,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UI,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UJ,	R420,	CHIP_HAS_CRTC2),
@@ -219,6 +236,24 @@ static struct pci_device_id radeonfb_pci_table[] = {
 	CHIP_DEF(PCI_CHIP_R423_UR,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_UT,	R420,	CHIP_HAS_CRTC2),
 	CHIP_DEF(PCI_CHIP_R423_5D57,	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UP, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_5D49, 	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_R423_5D4A, 	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_R423_5D48, 	R420,	CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+	CHIP_DEF(PCI_CHIP_R423_UO, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UM, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UN, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R423_UL, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_5D4C, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_5D50, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_5D4E, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_5D4F, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_5D52, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_5D4D, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_KJ, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_KK,    R420,     CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_KI, 	R420,	CHIP_HAS_CRTC2),
+	CHIP_DEF(PCI_CHIP_R480_KL, 	R420,	CHIP_HAS_CRTC2),
 	/* Original Radeon/7200 */
 	CHIP_DEF(PCI_CHIP_RADEON_QD,	RADEON,	0),
 	CHIP_DEF(PCI_CHIP_RADEON_QE,	RADEON,	0),
@@ -239,7 +274,7 @@ typedef struct {
  * interfere with anything
  */
 static reg_val common_regs[] = {
-	{ OVR_CLR, 0 },	
+	{ OVR_CLR, 0 },
 	{ OVR_WID_LEFT_RIGHT, 0 },
 	{ OVR_WID_TOP_BOTTOM, 0 },
 	{ OV0_SCALE_CNTL, 0 },
@@ -254,13 +289,13 @@ static reg_val common_regs[] = {
 /*
  * globals
  */
-        
 static char *mode_option;
 static char *monitor_layout;
 static int noaccel = 0;
 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;
@@ -276,6 +311,12 @@ static int backlight = 1;
 static int backlight = 0;
 #endif
 
+#ifdef CONFIG_FB_RADEON_DEBUG
+int radeonfb_debug = 1;
+#else
+int radeonfb_debug = 0;
+#endif
+
 /*
  * prototypes
  */
@@ -331,7 +372,7 @@ static int __devinit radeon_map_ROM(struct radeonfb_info *rinfo, struct pci_dev
 	 * 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 {
@@ -417,7 +458,7 @@ static int  __devinit radeon_find_mem_vbios(struct radeonfb_info *rinfo)
  * 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;
 	const u32 *val;
@@ -440,7 +481,8 @@ static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo)
 	if (val && *val)
 		rinfo->pll.mclk = (*val) / 10;
 
-       	return 0;
+	RTRACE("Retrieved PLL infos from Open Firmware\n");
+	return 0;
 }
 #endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 
@@ -582,10 +624,87 @@ static int __devinit radeon_probe_pll_params(struct radeonfb_info *rinfo)
 	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);
+
+	RTRACE("Retrieved 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);
+
+	RTRACE("Retrieved PLL infos from ATOM BIOS\n");
+	return 0;
+}
+
+static void radeon_detect_bios_type(struct radeonfb_info *rinfo)
+{
+#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
+	rinfo->is_atom_bios = 0;
+	rinfo->radeon_get_pll_info = radeon_get_pll_info_openfirmware;
+	rinfo->radeon_get_lvds_info = radeon_get_lvds_info_openfirmware;
+	rinfo->radeon_get_tmds_info = NULL;
+	rinfo->radeon_get_conn_info = radeon_get_conn_info_openfirmware;
+#else
+	int tmp = rinfo->fp_bios_start + 4;
+	unsigned char sign[4];
+
+	sign[0] = BIOS_IN8(tmp);
+	sign[1] = BIOS_IN8(tmp + 1);
+	sign[2] = BIOS_IN8(tmp + 2);
+	sign[3] = BIOS_IN8(tmp + 3);
+
+	if (!memcmp(sign, "ATOM", 4) || !memcmp(sign, "MOTA", 4)) {
+		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 || defined(CONFIG_SPARC) */
+
+}
+
 /*
  * Retrieve 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
@@ -637,46 +756,30 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
 	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;
-
-
-#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
-	/*
-	 * Retrieve PLL infos from Open Firmware first
-	 */
-       	if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
-       		printk(KERN_INFO "radeonfb: Retrieved PLL infos from Open Firmware\n");
-		goto found;
-	}
-#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 
 	/*
-	 * Check out if we have an X86 which gave us some PLL informations
-	 * and if yes, retrieve 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: Retrieved 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) {
@@ -687,9 +790,25 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo)
 	/*
 	 * Fall back to already-set defaults...
 	 */
-       	printk(KERN_INFO "radeonfb: Used default PLL infos\n");
+	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 retrieve SCLK and MCLK values, we apply default
 	 * settings in this case (200Mhz). If that really happne often, we could
@@ -705,7 +824,7 @@ found:
 	       rinfo->pll.ref_div,
 	       rinfo->pll.mclk / 100, rinfo->pll.mclk % 100,
 	       rinfo->pll.sclk / 100, rinfo->pll.sclk % 100);
-	printk("radeonfb: PLL min %d max %d\n", rinfo->pll.ppll_min, rinfo->pll.ppll_max);
+	RTRACE("PLL min %d max %d\n", rinfo->pll.ppll_min, rinfo->pll.ppll_max);
 }
 
 static int radeonfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
@@ -846,7 +965,7 @@ static int radeonfb_pan_display (struct fb_var_screeninfo *var,
         if (rinfo->asleep)
         	return 0;
 
-	radeon_fifo_wait(2);
+	radeon_engine_idle();
         OUTREG(CRTC_OFFSET, ((var->yoffset * var->xres_virtual + var->xoffset)
 			     * var->bits_per_pixel / 8) & ~7);
         return 0;
@@ -927,9 +1046,10 @@ static int radeonfb_ioctl (struct fb_info *info, unsigned int cmd,
 
 int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch)
 {
-        u32 val;
+	u32 val;
 	u32 tmp_pix_clks;
 	int unblank = 0;
+	int i;
 
 	if (rinfo->lock_blank)
 		return 0;
@@ -937,9 +1057,9 @@ int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch)
 	radeon_engine_idle();
 
 	val = INREG(CRTC_EXT_CNTL);
-        val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
+	val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
                  CRTC_VSYNC_DIS);
-        switch (blank) {
+	switch (blank) {
 	case FB_BLANK_VSYNC_SUSPEND:
 		val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS);
 		break;
@@ -956,81 +1076,84 @@ int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch)
 	case FB_BLANK_UNBLANK:
 	default:
 		unblank = 1;
-        }
+	}
 	OUTREG(CRTC_EXT_CNTL, val);
 
+	for (i = 0 ; i < RADEON_MAX_CONNECTORS ; i++) {
+		if (rinfo->heads[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;
 	}
 
 	return 0;
@@ -1260,7 +1383,7 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg
 				~PPLL_DIV_SEL_MASK);
 			radeon_pll_errata_after_index(rinfo);
 			radeon_pll_errata_after_data(rinfo);
-            		return;
+			return;
 		}
 	}
 
@@ -1280,10 +1403,7 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg
 	radeon_pll_errata_after_data(rinfo);
 
 	/* Set PPLL ref. div */
-	if (rinfo->family == CHIP_FAMILY_R300 ||
-	    rinfo->family == CHIP_FAMILY_RS300 ||
-	    rinfo->family == CHIP_FAMILY_R350 ||
-	    rinfo->family == CHIP_FAMILY_RV350) {
+	if (IS_R300_VARIANT(rinfo) || (rinfo->family == CHIP_FAMILY_RS300)) {
 		if (mode->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) {
 			/* When restoring console mode, use saved PPLL_REF_DIV
 			 * setting.
@@ -1292,7 +1412,7 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg
 		} else {
 			/* R300 uses ref_div_acc field as real ref divider */
 			OUTPLLP(PPLL_REF_DIV,
-				(mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT), 
+				(mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT),
 				~R300_PPLL_REF_DIV_ACC_MASK);
 		}
 	} else
@@ -1380,6 +1500,7 @@ void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
 	OUTREG(CRTC_OFFSET_CNTL, 0);
 	OUTREG(CRTC_PITCH, mode->crtc_pitch);
 	OUTREG(SURFACE_CNTL, mode->surface_cntl);
+	OUTREG(DISP_MERGE_CNTL, 0xffff0000);
 
 	radeon_write_pll_regs(rinfo, mode);
 
@@ -1890,7 +2011,7 @@ static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo)
         info->fix.ywrapstep = 0;
         info->fix.type_aux = 0;
         info->fix.mmio_start = rinfo->mmio_base_phys;
-        info->fix.mmio_len = RADEON_REGSIZE;
+        info->fix.mmio_len = pci_resource_len(rinfo->pdev, 2);
 	info->fix.accel = FB_ACCEL_ATI_RADEON;
 
 	fb_alloc_cmap(&info->cmap, 256, 0);
@@ -1994,10 +2115,7 @@ static void radeon_identify_vram(struct radeonfb_info *rinfo)
 	u32 tmp;
 
 	/* framebuffer size */
-        if ((rinfo->family == CHIP_FAMILY_RS100) ||
-            (rinfo->family == CHIP_FAMILY_RS200) ||
-            (rinfo->family == CHIP_FAMILY_RS300) ||
-	    (rinfo->family == CHIP_FAMILY_RS480) ) {
+        if (rinfo->is_IGP) {
           u32 tom = INREG(NB_TOM);
           tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024);
 
@@ -2025,6 +2143,10 @@ static void radeon_identify_vram(struct radeonfb_info *rinfo)
 	/* mem size is bits [28:0], mask off the rest */
 	rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK;
 
+	/* Limit memory to 128 megs for now */
+	if (rinfo->video_ram > MAX_VRAM)
+		rinfo->video_ram = MAX_VRAM;
+
 	/*
 	 * Hack to get around some busted production M6's
 	 * reporting no ram
@@ -2106,10 +2228,10 @@ static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, si
 {
 	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;
+	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);
 }
 
 
@@ -2117,10 +2239,30 @@ static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, si
 {
 	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;
+	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[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->mon2_EDID);
+	return radeon_show_one_edid(buf, off, count, rinfo->connectors[rinfo->heads[3]].edid);
 }
 
 static struct bin_attribute edid1_attr = {
@@ -2143,6 +2285,25 @@ static struct bin_attribute edid2_attr = {
 	.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 __devinit radeonfb_pci_register (struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
@@ -2150,9 +2311,10 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 	struct fb_info *info;
 	struct radeonfb_info *rinfo;
 	int ret;
+	int i;
 
 	RTRACE("radeonfb_pci_register BEGIN\n");
-	
+
 	/* Enable device in PCI config */
 	ret = pci_enable_device(pdev);
 	if (ret < 0) {
@@ -2169,9 +2331,9 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 		goto err_disable;
 	}
 	rinfo = info->par;
-	rinfo->info = info;	
+	rinfo->info = info;
 	rinfo->pdev = pdev;
-	
+
 	spin_lock_init(&rinfo->reg_lock);
 	init_timer(&rinfo->lvds_timer);
 	rinfo->lvds_timer.function = radeon_lvds_timer_func;
@@ -2206,7 +2368,8 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 	}
 
 	/* map the regions */
-	rinfo->mmio_base = ioremap(rinfo->mmio_base_phys, RADEON_REGSIZE);
+	rinfo->mmio_base = ioremap(rinfo->mmio_base_phys,
+				   pci_resource_len(rinfo->pdev, 2));
 	if (!rinfo->mmio_base) {
 		printk(KERN_ERR "radeonfb (%s): cannot map MMIO\n",
 		       pci_name(rinfo->pdev));
@@ -2258,7 +2421,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 	rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, rinfo->video_ram);
 
 	do {
-		rinfo->fb_base = ioremap (rinfo->fb_base_phys,
+		rinfo->fb_base = ioremap(rinfo->fb_base_phys,
 					  rinfo->mapped_vram);
 	} while (   rinfo->fb_base == 0 &&
 		  ((rinfo->mapped_vram /=2) >= MIN_MAPPED_VRAM) );
@@ -2293,6 +2456,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 	 * 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);
@@ -2304,14 +2468,23 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 	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);
 
@@ -2322,10 +2495,15 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 	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
@@ -2371,10 +2549,12 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev,
 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);
@@ -2387,7 +2567,7 @@ err_release_pci2:
 err_release_pci0:
 	pci_release_region(pdev, 0);
 err_release_fb:
-        framebuffer_release(info);
+	framebuffer_release(info);
 err_disable:
 err_out:
 	return ret;
@@ -2399,16 +2579,21 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
 {
         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
@@ -2437,13 +2622,14 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
 	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        
+#endif
 	fb_dealloc_cmap(&info->cmap);
         framebuffer_release(info);
 }
@@ -2492,12 +2678,18 @@ static int __init radeonfb_setup (char *options)
 			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 if (!strncmp( this_opt, "default_dynclk:", 15)) {
+			default_dynclk = simple_strtoul((this_opt+15), NULL, 10);
 #if defined(CONFIG_PM) && defined(CONFIG_X86)
-	 	} else if (!strncmp(this_opt, "force_sleep", 11)) {
+		} else if (!strncmp(this_opt, "force_sleep", 11)) {
 			force_sleep = 1;
 		} else if (!strncmp(this_opt, "ignore_devlist", 14)) {
 			ignore_devlist = 1;
 #endif
+		} else if (!strncmp(this_opt, "debug", 5)) {
+			radeonfb_debug = 1;
 		} else
 			mode_option = this_opt;
 	}
@@ -2541,6 +2733,8 @@ module_param(force_dfp, bool, 0);
 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);
@@ -2559,3 +2753,5 @@ MODULE_PARM_DESC(force_sleep, "bool: force D2 sleep mode on all hardware");
 module_param(ignore_devlist, bool, 0);
 MODULE_PARM_DESC(ignore_devlist, "bool: ignore workarounds for bugs in specific laptops");
 #endif
+module_param(radeonfb_debug, int, 0);
+MODULE_PARM_DESC(radeonfb_debug, "Enable full debugging text");
diff --git a/drivers/video/aty/radeon_i2c.c b/drivers/video/aty/radeon_i2c.c
index 7db9de6..e4bc267 100644
--- a/drivers/video/aty/radeon_i2c.c
+++ b/drivers/video/aty/radeon_i2c.c
@@ -134,35 +134,57 @@ void radeon_delete_i2c_busses(struct radeonfb_info *rinfo)
 	rinfo->i2c[3].rinfo = NULL;
 }
 
-int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn,
-			       u8 **out_edid)
+
+int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, struct radeon_connector *conn)
 {
-	u32 reg = rinfo->i2c[conn-1].ddc_reg;
+	int mon_type = MT_NONE;
 	u8 *edid;
 
-	OUTREG(reg, INREG(reg) &
-			~(VGA_DDC_DATA_OUTPUT | VGA_DDC_CLK_OUTPUT));
+	if (!conn)
+		return 1;
+
+	if (rinfo->is_mobility && (conn->ddc_type == ddc_none) &&
+	    (INREG(LVDS_GEN_CNTL) & (LVDS_ON|LVDS_EN))) {
+			RTRACE("radeonfb: I2C (port %d) ... found LVDS panel\n", conn->ddc_type);
+			mon_type = MT_LCD;
+			edid = NULL;
+			goto done;
+	}
 
-	edid = fb_ddc_read(&rinfo->i2c[conn-1].adapter);
+	if (conn->ddc_type == ddc_none)
+		return 1;
+	edid = fb_ddc_read(&rinfo->i2c[conn->ddc_type].adapter);
 
-	if (out_edid)
-		*out_edid = edid;
 	if (!edid) {
-		RTRACE("radeonfb: I2C (port %d) ... not found\n", conn);
-		return MT_NONE;
+		/* what about the special case where we are a DFP/LVDS, but have a DDC connection
+		 * but no EDID? We should fall back to MT_LCD...?  XXXX
+		 */
+		RTRACE("radeonfb: I2C (port %d) ... not found\n", conn->ddc_type);
+		mon_type = MT_NONE;
+		goto done;
+	}
+	if ((edid[EDID_STRUCT_DISPLAY] & 0x80) && (conn->ddc_type == ddc_dvi)) {
+		RTRACE("radeonfb: I2C (port %d) ... found TMDS panel\n", conn->ddc_type);
+		mon_type = MT_DFP;
+		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 (rinfo->is_mobility &&
+	    (conn->conn_type == conn_lvds) &&
+	    (edid[EDID_STRUCT_DISPLAY] & 0x80) &&  // ie EDID valid and marks us as a DFP...
+	    (INREG(LVDS_GEN_CNTL) & (LVDS_ON|LVDS_EN))) {
+		RTRACE("radeonfb: I2C (port %d) ... found LVDS panel\n", conn->ddc_type);
+		mon_type = MT_LCD;
+		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);
 }
 
diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c
index 2030ed8..1b646d1 100644
--- a/drivers/video/aty/radeon_monitor.c
+++ b/drivers/video/aty/radeon_monitor.c
@@ -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}, {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,311 @@ static struct fb_var_screeninfo radeonfb_default_var = {
 	.vmode		= FB_VMODE_NONINTERLACED
 };
 
-static char *radeon_get_mon_name(int type)
+
+int __devinit radeon_get_lvds_info_atom(struct radeonfb_info *rinfo)
 {
-	char *pret = NULL;
+	unsigned long tmp;
 
-	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";
+	if (!rinfo->bios_seg)
+		return -ENODEV;
+
+	tmp = BIOS_IN16(rinfo->atom_data_start + 16);
+	if (!tmp) {
+		RTRACE("No LVDS panel info in ATOM 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 -ENODEV;
+
+	if (!(tmp = BIOS_IN16(rinfo->fp_bios_start + 0x40))) {
+		RTRACE("No LVDS panel info in Legacy BIOS\n");
+		rinfo->panel_info.pwr_delay = 200;
+		return -ENODEV;
+	}
+
+	for(i = 0; i < 24; i++)
+		stmp[i] = BIOS_IN8(tmp + i + 1);
+	stmp[24] = 0;
+	printk("radeonfb: panel ID string: %s\n", stmp);
+	rinfo->panel_info.xres = BIOS_IN16(tmp + 25);
+	rinfo->panel_info.yres = BIOS_IN16(tmp + 27);
+	printk("radeonfb: detected LVDS panel size from BIOS: %dx%d\n",
+	       rinfo->panel_info.xres, rinfo->panel_info.yres);
+
+	rinfo->panel_info.pwr_delay = BIOS_IN16(tmp + 44);
+	RTRACE("BIOS provided panel power delay: %d\n", rinfo->panel_info.pwr_delay);
+	if (rinfo->panel_info.pwr_delay > 2000 || rinfo->panel_info.pwr_delay <= 0)
+		rinfo->panel_info.pwr_delay = 2000;
+
+	/*
+	 * Some panels only work properly with some divider combinations
+	 */
+	rinfo->panel_info.ref_divider = BIOS_IN16(tmp + 46);
+	rinfo->panel_info.post_divider = BIOS_IN8(tmp + 48);
+	rinfo->panel_info.fbk_divider = BIOS_IN16(tmp + 49);
+	if (rinfo->panel_info.ref_divider != 0 &&
+	    rinfo->panel_info.fbk_divider > 3) {
+		rinfo->panel_info.use_bios_dividers = 1;
+		printk(KERN_INFO "radeondb: BIOS provided dividers will be used\n");
+		RTRACE("ref_divider = %x\n", rinfo->panel_info.ref_divider);
+		RTRACE("post_divider = %x\n", rinfo->panel_info.post_divider);
+		RTRACE("fbk_divider = %x\n", rinfo->panel_info.fbk_divider);
+	}
+
+	RTRACE("Scanning BIOS table ...\n");
+	for(i = 0; i < 32; i++) {
+		tmp0 = BIOS_IN16(tmp + 64 + i*2);
+		if (tmp0 == 0)
 			break;
+		RTRACE(" %d x %d\n", BIOS_IN16(tmp0), BIOS_IN16(tmp0 + 2));
+		if ((BIOS_IN16(tmp0) == rinfo->panel_info.xres) &&
+		    (BIOS_IN16(tmp0 + 2) == rinfo->panel_info.yres)) {
+			rinfo->panel_info.hblank = (BIOS_IN16(tmp0 + 17) - BIOS_IN16(tmp0 + 19)) * 8;
+			rinfo->panel_info.hOver_plus = ((BIOS_IN16(tmp0 + 21) -
+							 BIOS_IN16(tmp0 + 19) -1) * 8) & 0x7fff;
+			rinfo->panel_info.hSync_width = BIOS_IN8(tmp0 + 23) * 8;
+			rinfo->panel_info.vblank = BIOS_IN16(tmp0 + 24) - BIOS_IN16(tmp0 + 26);
+			rinfo->panel_info.vOver_plus = (BIOS_IN16(tmp0 + 28) & 0x7ff) - BIOS_IN16(tmp0 + 26);
+			rinfo->panel_info.vSync_width = (BIOS_IN16(tmp0 + 28) & 0xf800) >> 11;
+			rinfo->panel_info.clock = BIOS_IN16(tmp0 + 9);
+			/* Assume high active syncs for now until ATI tells me more... maybe we
+			 * can probe register values here ?
+			 */
+			rinfo->panel_info.hAct_high = 1;
+			rinfo->panel_info.vAct_high = 1;
+			/* Mark panel infos valid */
+			rinfo->panel_info.valid = 1;
+
+			RTRACE("Found panel in BIOS table:\n");
+			RTRACE("  hblank: %d\n", rinfo->panel_info.hblank);
+			RTRACE("  hOver_plus: %d\n", rinfo->panel_info.hOver_plus);
+			RTRACE("  hSync_width: %d\n", rinfo->panel_info.hSync_width);
+			RTRACE("  vblank: %d\n", rinfo->panel_info.vblank);
+			RTRACE("  vOver_plus: %d\n", rinfo->panel_info.vOver_plus);
+			RTRACE("  vSync_width: %d\n", rinfo->panel_info.vSync_width);
+			RTRACE("  clock: %d\n", rinfo->panel_info.clock);
+
+			return 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;
+	int family = rinfo->family;
+
+	/* Get default TMDS infos for this chip */
+	for (i = 0; i < 4; i++) {
+		rinfo->tmds_pll[i].value = default_tmds_pll[family][i].value;
+		rinfo->tmds_pll[i].freq = default_tmds_pll[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);
+	RTRACE("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;
 
-	return pret;
+	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));
+		RTRACE("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
+}
+
 #if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
 /*
  * Try to find monitor informations & EDID data out of the Open Firmware
@@ -59,33 +358,40 @@ static char *radeon_get_mon_name(int type)
  * 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 radeonfb_info *rinfo,
+					       struct device_node *dp,
+					       struct radeon_connector *conn,
 					       int hdno)
 {
-        static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID",
+	static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID",
 				     "EDID1", "EDID2",  NULL };
 	const u8 *pedid = NULL;
 	const u8 *pmt = NULL;
 	u8 *tmp;
-        int i, mt = MT_NONE;  
-	
+	int i;
+
 	RTRACE("analyzing OF properties...\n");
 	pmt = of_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) {
@@ -103,25 +409,36 @@ static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_
 	if (pedid == NULL && dp->parent && (hdno == 0))
 		pedid = of_get_property(dp->parent, "EDID", NULL);
 	if (pedid == NULL)
-		return mt;
+		return -1;
 
 	tmp = kmemdup(pedid, EDID_LENGTH, GFP_KERNEL);
-	if (!tmp)
-		return mt;
-	*out_EDID = tmp;
-	return mt;
+	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 device_node *dp;
+	struct radeon_connector *conn;
+	struct device_node *dp;
 
 	RTRACE("radeon_probe_OF_head\n");
 
-        dp = rinfo->of_node;
-        while (dp == NULL)
-		return MT_NONE;
+	conn = &rinfo->connectors[head_no];
+
+	dp = rinfo->of_node;
+	if (dp == NULL)
+		return -1;
 
 	if (rinfo->has_CRTC2) {
 		const char *pname;
@@ -130,116 +447,284 @@ static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_
 		dp = dp->child;
 		do {
 			if (!dp)
-				return MT_NONE;
+				return -1;
+
 			pname = of_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(rinfo, dp, conn, 0);
+			} else if (pname[len-1] == 'B' && head_no == 1) {
+				return radeon_parse_montype_prop(rinfo, 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);
+			return -1;
+		return radeon_parse_montype_prop(rinfo, dp, conn, -1);
 	}
-        return MT_NONE;
+	return -1;
 }
 #endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 
-
-static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
+/*
+ * 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)
 {
-	unsigned long tmp, tmp0;
-	char stmp[30];
 	int i;
 
-	if (!rinfo->bios_seg)
-		return 0;
-
-	if (!(tmp = BIOS_IN16(rinfo->fp_bios_start + 0x40))) {
-		printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
-		rinfo->panel_info.pwr_delay = 200;
-		return 0;
+	/* 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);
+			}
+		}
 
-	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);
+		/* 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;
+			}
+		}
+	}
 
-	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;
+	printk(KERN_INFO "radeonfb: No connector infos, using defaults...\n");
 
-	/*
-	 * Some panels only work properly with some divider combinations
+	/* Here, we use defaults that are common enough ... we hope
+	 * For a mobility chip, we assume LVDS is on primary
 	 */
-	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);
+	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;
+		}
 	}
-	RTRACE("Scanning BIOS table ...\n");
-	for(i=0; i<32; i++) {
-		tmp0 = BIOS_IN16(tmp+64+i*2);
-		if (tmp0 == 0)
-			break;
-		RTRACE(" %d x %d\n", BIOS_IN16(tmp0), BIOS_IN16(tmp0+2));
-		if ((BIOS_IN16(tmp0) == rinfo->panel_info.xres) &&
-		    (BIOS_IN16(tmp0+2) == rinfo->panel_info.yres)) {
-			rinfo->panel_info.hblank = (BIOS_IN16(tmp0+17) - BIOS_IN16(tmp0+19)) * 8;
-			rinfo->panel_info.hOver_plus = ((BIOS_IN16(tmp0+21) -
-							 BIOS_IN16(tmp0+19) -1) * 8) & 0x7fff;
-			rinfo->panel_info.hSync_width = BIOS_IN8(tmp0+23) * 8;
-			rinfo->panel_info.vblank = BIOS_IN16(tmp0+24) - BIOS_IN16(tmp0+26);
-			rinfo->panel_info.vOver_plus = (BIOS_IN16(tmp0+28) & 0x7ff) - BIOS_IN16(tmp0+26);
-			rinfo->panel_info.vSync_width = (BIOS_IN16(tmp0+28) & 0xf800) >> 11;
-			rinfo->panel_info.clock = BIOS_IN16(tmp0+9);
-			/* Assume high active syncs for now until ATI tells me more... maybe we
-			 * can probe register values here ?
-			 */
-			rinfo->panel_info.hAct_high = 1;
-			rinfo->panel_info.vAct_high = 1;
-			/* Mark panel infos valid */
-			rinfo->panel_info.valid = 1;
 
-			RTRACE("Found panel in BIOS table:\n");
-			RTRACE("  hblank: %d\n", rinfo->panel_info.hblank);
-			RTRACE("  hOver_plus: %d\n", rinfo->panel_info.hOver_plus);
-			RTRACE("  hSync_width: %d\n", rinfo->panel_info.hSync_width);
-			RTRACE("  vblank: %d\n", rinfo->panel_info.vblank);
-			RTRACE("  vOver_plus: %d\n", rinfo->panel_info.vOver_plus);
-			RTRACE("  vSync_width: %d\n", rinfo->panel_info.vSync_width);
-			RTRACE("  clock: %d\n", rinfo->panel_info.clock);
-				
-			return 1;
+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;
 		}
 	}
-	RTRACE("Didn't find panel in BIOS table !\n");
+
+	/* Single head chips all use primary DAC */
+	if (!rinfo->has_CRTC2)
+		rinfo->connectors[0].dac_type = dac_primary;
+
+	return;
+}
+
+#if defined CONFIG_PPC_OF || defined(CONFIG_SPARC)
+int __devinit radeon_get_conn_info_openfirmware(struct radeonfb_info *rinfo)
+{
+	int i;
+	int found = 0;
+
+	/* Only two heads for OF! */
+	for(i = 0 ; i < 2 ; i++) {
+		if (!radeon_probe_OF_head(rinfo, i))
+			found = 0;
+	}
+	return found;
+}
+#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
+
+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;
+	int shared;
+
+	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++) {
+		shared = 0;
+		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;
+
+			/* This port is shared. Update the values (if needed)
+			 * and probe next connector without creating a new
+			 * connector entry.
+			 */
+			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 == ddc_none)
+				rinfo->connectors[j].ddc_type = ddc_type;
+
+			shared = 1;
+		}
+		if (shared)
+			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 != conn_none))
+			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;
+		ids[idx] = port_id;
+
+		/* increment connector_found for primary connectors only */
+		if (idx < 2)
+			connector_found += (idx + 1);
+	}
+
+	if (connector_found == 0)
+		return -ENODEV;
 
 	return 0;
 }
@@ -248,44 +733,167 @@ static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
  * 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;
+	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);
 	}
 
-	/* 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_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 +970,51 @@ static int __devinit radeon_crt_is_connected(struct radeonfb_info *rinfo, int is
     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
@@ -407,19 +1060,20 @@ static int __devinit radeon_parse_monitor_layout(struct radeonfb_info *rinfo,
 		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;
 }
@@ -433,12 +1087,7 @@ static int __devinit radeon_parse_monitor_layout(struct radeonfb_info *rinfo,
 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)) {
 
@@ -449,214 +1098,142 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
 		 * 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 {
 		/*
 		 * Auto-detecting display type (well... trying to ...)
 		 */
-		
-		RTRACE("Starting monitor auto detection...\n");
 
-#if DEBUG && defined(CONFIG_FB_RADEON_I2C)
-		{
-			u8 *EDIDs[4] = { NULL, NULL, NULL, NULL };
-			int mon_types[4] = {MT_NONE, MT_NONE, MT_NONE, MT_NONE};
-			int i;
+		RTRACE("Starting monitor auto detection...\n");
 
-			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) {
-#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
-			if (rinfo->mon1_type == MT_NONE)
-				rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
-									&rinfo->mon1_EDID);
-#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
 #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)
+			/* Probe each connector */
+			for(i = 0 ; i < RADEON_MAX_CONNECTORS ; i++) {
+				if (PRIMARY_MONITOR(rinfo) > MT_NONE)
+					/* only one head */
 					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");
+				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)
-		 */
-#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
-		if (rinfo->mon1_type == MT_NONE)
-			rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
-								&rinfo->mon1_EDID);
-#endif /* CONFIG_PPC_OF || CONFIG_SPARC */
+		/* 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;
-		}
-#endif /* CONFIG_FB_RADEON_I2C */
-		if (rinfo->mon1_type == 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");
+		/* Probe each connector in turn. */
+		for(i = 0; i < RADEON_MAX_CONNECTORS; i++) {
+			if (rinfo->connectors[i].mon_type > MT_NONE)
+				/* Don't probe "detected" stuff again */
+				continue;
+			if (!radeon_probe_i2c_connector(rinfo, &rinfo->connectors[i])) {
+				rinfo->heads[rinfo->num_heads] = i;
+				rinfo->connectors[i].head = rinfo->num_heads++;
+			}
 		}
-		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)
-		 */
-#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC)
-		if (rinfo->mon2_type == MT_NONE)
-			rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1,
-								&rinfo->mon2_EDID);
-#endif /* CONFIG_PPC_OF || defined(CONFIG_SPARC) */
-#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);
 
-		/*
-		 * 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;
+		/* Mobility chips usually have LCDs... */
+		if ((PRIMARY_MONITOR(rinfo) <= MT_NONE) &&
+		    rinfo->is_mobility &&
+		    ((rinfo->bios_seg && (INREG(BIOS_4_SCRATCH) & 4)) ||
+		     (INREG(LVDS_GEN_CNTL) & (LVDS_EN|LVDS_ON)))) {
+			printk(KERN_INFO "radeonfb: Non-DDC laptop panel detected\n");
+			radeon_find_connector_for_mon(rinfo, MT_LCD);
+			if (rinfo->radeon_get_lvds_info) {
+				rinfo->radeon_get_lvds_info(rinfo);
+			}
 		}
 
-		/*
-		 * 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;
-			}
+		/* 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 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);
+
+		/* 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:
+
+	/* 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);
 	}
-
- 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");
 }
 
 
-/*
- * 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;
@@ -667,15 +1244,16 @@ static void radeon_fixup_panel_info(struct radeonfb_info *rinfo)
 		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 -ENODEV;
 }
-
+#endif /* CONFIG_PPC_OF */
 
 /*
  * Fill up panel infos from a mode definition, either returned by the EDID
@@ -688,18 +1266,18 @@ static void radeon_var_to_panel_info(struct radeonfb_info *rinfo, struct fb_var_
 	rinfo->panel_info.clock = 100000000 / var->pixclock;
 	rinfo->panel_info.hOver_plus = var->right_margin;
 	rinfo->panel_info.hSync_width = var->hsync_len;
-       	rinfo->panel_info.hblank = var->left_margin +
+	rinfo->panel_info.hblank = var->left_margin +
 		(var->right_margin + var->hsync_len);
 	rinfo->panel_info.vOver_plus = var->lower_margin;
 	rinfo->panel_info.vSync_width = var->vsync_len;
-       	rinfo->panel_info.vblank = var->upper_margin +
+	rinfo->panel_info.vblank = var->upper_margin +
 		(var->lower_margin + var->vsync_len);
 	rinfo->panel_info.hAct_high =
 		(var->sync & FB_SYNC_HOR_HIGH_ACT) != 0;
 	rinfo->panel_info.vAct_high =
 		(var->sync & FB_SYNC_VERT_HIGH_ACT) != 0;
 	rinfo->panel_info.valid = 1;
-	/* We use a default of 200ms for the panel power delay, 
+	/* We use a default of 200ms for the panel power delay,
 	 * I need to have a real schedule() instead of mdelay's in the panel code.
 	 * we might be possible to figure out a better power delay either from
 	 * MacOS OF tree or from the EDID block (proprietary extensions ?)
@@ -742,22 +1320,34 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
 	info->var = radeonfb_default_var;
 	INIT_LIST_HEAD(&info->modelist);
 
-	/*
-	 * First check out what BIOS has to say
-	 */
-	if (rinfo->mon1_type == MT_LCD)
-		radeon_get_panel_info_BIOS(rinfo);
+	/* If we're an LCD and don't have a valid setup... */
+	if ((PRIMARY_MONITOR(rinfo) == MT_LCD) &&
+	    !rinfo->panel_info.valid &&
+	    rinfo->radeon_get_lvds_info) {
+		rinfo->radeon_get_lvds_info(rinfo);
+	}
+
+#if 0
+	/* If we're a mobility and still haven't detected a screen..? */
+	if ((PRIMARY_MONITOR(rinfo) <= MT_NONE) &&
+	    rinfo->is_mobility &&
+	    rinfo->radeon_get_lvds_info) {
+		if (!rinfo->radeon_get_lvds_info(rinfo))
+			radeon_find_connector_for_mon(rinfo, MT_LCD);
+	}
+#endif
 
 	/*
 	 * 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);
@@ -765,15 +1355,10 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
 	}
 
 	/*
-	 * 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");
@@ -804,22 +1389,21 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
 	/*
 	 * 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;
 	}
 
-	
 	/*
 	 * Finally, if we don't have panel infos we need to figure some (or
 	 * 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];
@@ -833,25 +1417,26 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
 		}
 		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;
+		radeon_find_connector_for_mon(rinfo, MT_LCD);
 		radeon_var_to_panel_info(rinfo, &info->var);
 	}
 
- pickup_default:
+pickup_default:
 	/*
 	 * Apply passed-in mode option if any
 	 */
@@ -860,7 +1445,7 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
 				 info->monspecs.modedb,
 				 info->monspecs.modedb_len, NULL, 8) != 0)
 			has_default_mode = 1;
- 	}
+	}
 
 	/*
 	 * Still no mode, let's pick up a default from the db
@@ -947,14 +1532,14 @@ int  radeon_match_mode(struct radeonfb_info *rinfo,
 	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
@@ -967,7 +1552,7 @@ int  radeon_match_mode(struct radeonfb_info *rinfo,
 		 * 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;
@@ -979,7 +1564,7 @@ int  radeon_match_mode(struct radeonfb_info *rinfo,
 			int d;
 
 			if (db[i].yres < src->yres)
-				continue;	
+				continue;
 			if (db[i].xres < src->xres)
 				continue;
 			d = radeon_compare_modes(src, &db[i]);
diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h
index 7ebffcd..9d0b161 100644
--- a/drivers/video/aty/radeonfb.h
+++ b/drivers/video/aty/radeonfb.h
@@ -26,6 +26,8 @@
  * Most of the definitions here are adapted right from XFree86 *
  ***************************************************************/
 
+/* Sorry, we have to limit video ram to 128M */
+#define MAX_VRAM		(128*1024*1024)
 
 /*
  * Chip families. Must fit in the low 16 bits of a long word
@@ -47,7 +49,8 @@ enum radeon_family {
 	CHIP_FAMILY_R350,
 	CHIP_FAMILY_RV350,
 	CHIP_FAMILY_RV380,    /* RV370/RV380/M22/M24 */
-	CHIP_FAMILY_R420,     /* R420/R423/M18 */
+	CHIP_FAMILY_RV410,    /* RV410/M26 */
+	CHIP_FAMILY_R420,     /* R420/R423/R480/M18 */
 	CHIP_FAMILY_RS480,
 	CHIP_FAMILY_LAST,
 };
@@ -65,6 +68,7 @@ enum radeon_family {
 				((rinfo)->family == CHIP_FAMILY_RV350) || \
 				((rinfo)->family == CHIP_FAMILY_R350)  || \
 				((rinfo)->family == CHIP_FAMILY_RV380) || \
+				((rinfo)->family == CHIP_FAMILY_RV410) || \
 				((rinfo)->family == CHIP_FAMILY_R420)  || \
 		                ((rinfo)->family == CHIP_FAMILY_RS480) )
 
@@ -88,11 +92,50 @@ enum radeon_errata {
 	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 */
@@ -102,27 +145,45 @@ enum radeon_montype {
 };
 
 /*
- * 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
@@ -130,11 +191,19 @@ enum conn_type {
 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
@@ -301,6 +370,20 @@ struct radeonfb_info {
 	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];
@@ -319,15 +402,8 @@ struct radeonfb_info {
 	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;
 
@@ -359,24 +435,19 @@ struct radeonfb_info {
 };
 
 
-#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
  */
-#ifdef CONFIG_FB_RADEON_DEBUG
-#define DEBUG		1
-#else
-#define DEBUG		0
-#endif
-
-#if DEBUG
-#define RTRACE		printk
-#else
-#define RTRACE		if(0) printk
-#endif
-
+extern int radeonfb_debug;
+#define RTRACE		if(radeonfb_debug) printk
 
 /*
  * IO macros
@@ -599,7 +670,7 @@ static inline void _radeon_engine_idle(struct radeonfb_info *rinfo)
 /* 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);
@@ -637,4 +708,18 @@ static inline void radeonfb_bl_init(struct radeonfb_info *rinfo) {}
 static inline void radeonfb_bl_exit(struct radeonfb_info *rinfo) {}
 #endif
 
+/* 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__ */



Luca
-- 
"New processes are created by other processes, just like new
 humans. New humans are created by other humans, of course,
 not by processes." -- Unix System Administration Handbook
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ