fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / video / aty / radeon_monitor.c
index e93e94c..38c7dbf 100644 (file)
@@ -1,16 +1,26 @@
 #include "radeonfb.h"
 #include "../edid.h"
 
-#ifdef CONFIG_PPC_OF
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#endif /* CONFIG_PPC_OF */
-
 static struct fb_var_screeninfo radeonfb_default_var = {
-        640, 480, 640, 480, 0, 0, 8, 0,
-        {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
-        0, 0, -1, -1, 0, 39721, 40, 24, 32, 11, 96, 2,
-        0, FB_VMODE_NONINTERLACED
+       .xres           = 640,
+       .yres           = 480,
+       .xres_virtual   = 640,
+       .yres_virtual   = 480,
+       .bits_per_pixel = 8,
+       .red            = { .length = 8 },
+       .green          = { .length = 8 },
+       .blue           = { .length = 8 },
+       .activate       = FB_ACTIVATE_NOW,
+       .height         = -1,
+       .width          = -1,
+       .pixclock       = 39721,
+       .left_margin    = 40,
+       .right_margin   = 24,
+       .upper_margin   = 32,
+       .lower_margin   = 11,
+       .hsync_len      = 96,
+       .vsync_len      = 2,
+       .vmode          = FB_VMODE_NONINTERLACED
 };
 
 static char *radeon_get_mon_name(int type)
@@ -49,16 +59,18 @@ 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, int hdno)
+static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_EDID,
+                                              int hdno)
 {
-        static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", "EDID2",  NULL };
-       u8 *pedid = NULL;
-       u8 *pmt = NULL;
+        static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID",
+                                    "EDID1", "EDID2",  NULL };
+       const u8 *pedid = NULL;
+       const u8 *pmt = NULL;
        u8 *tmp;
-        int i, mt;  
+        int i, mt = MT_NONE;  
        
        RTRACE("analyzing OF properties...\n");
-       pmt = (u8 *)get_property(dp, "display-type", NULL);
+       pmt = get_property(dp, "display-type", NULL);
        if (!pmt)
                return MT_NONE;
        RTRACE("display-type: %s\n", pmt);
@@ -69,12 +81,15 @@ static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_
                mt = MT_DFP;
        else if (!strcmp(pmt, "CRT"))
                mt = MT_CRT;
-       else if (strcmp(pmt, "NONE")) {
-               printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n", pmt);
+       else {
+               if (strcmp(pmt, "NONE") != 0)
+                       printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n",
+                              pmt);
                return MT_NONE;
        }
+
        for (i = 0; propnames[i] != NULL; ++i) {
-               pedid = (u8 *)get_property(dp, propnames[i], NULL);
+               pedid = get_property(dp, propnames[i], NULL);
                if (pedid != NULL)
                        break;
        }
@@ -89,10 +104,9 @@ static int __devinit radeon_parse_montype_prop(struct device_node *dp, u8 **out_
        if (pedid == NULL)
                return mt;
 
-       tmp = (u8 *)kmalloc(EDID_LENGTH, GFP_KERNEL);
+       tmp = kmemdup(pedid, EDID_LENGTH, GFP_KERNEL);
        if (!tmp)
                return mt;
-       memcpy(tmp, pedid, EDID_LENGTH);
        *out_EDID = tmp;
        return mt;
 }
@@ -104,19 +118,19 @@ static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_
 
        RTRACE("radeon_probe_OF_head\n");
 
-        dp = pci_device_to_OF_node(rinfo->pdev);
+        dp = rinfo->of_node;
         while (dp == NULL)
                return MT_NONE;
 
        if (rinfo->has_CRTC2) {
-               char *pname;
+               const char *pname;
                int len, second = 0;
 
                dp = dp->child;
                do {
                        if (!dp)
                                return MT_NONE;
-                       pname = (char *)get_property(dp, "name", NULL);
+                       pname = get_property(dp, "name", NULL);
                        if (!pname)
                                return MT_NONE;
                        len = strlen(pname);
@@ -381,6 +395,10 @@ static int __devinit radeon_parse_monitor_layout(struct radeonfb_info *rinfo,
                                s1[i] = *s;
                        i++;
                }
+
+               if (i > 4)
+                       i = 4;
+
        } while (*s++);
        if (second)
                s2[i] = 0;
@@ -408,7 +426,7 @@ static int __devinit radeon_parse_monitor_layout(struct radeonfb_info *rinfo,
 /*
  * Probe display on both primary and secondary card's connector (if any)
  * by various available techniques (i2c, OF device tree, BIOS, ...) and
- * try to retreive EDID. The algorithm here comes from XFree's radeon
+ * try to retrieve EDID. The algorithm here comes from XFree's radeon
  * driver
  */
 void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
@@ -484,8 +502,9 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
 #endif /* CONFIG_PPC_OF */
 #ifdef CONFIG_FB_RADEON_I2C
                        if (rinfo->mon1_type == MT_NONE)
-                               rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi,
-                                                                     &rinfo->mon1_EDID);
+                               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,
@@ -527,7 +546,8 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
                 */
 #ifdef CONFIG_PPC_OF
                if (rinfo->mon1_type == MT_NONE)
-                       rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, &rinfo->mon1_EDID);
+                       rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
+                                                               &rinfo->mon1_EDID);
 #endif /* CONFIG_PPC_OF */
 #ifdef CONFIG_FB_RADEON_I2C
                if (rinfo->mon1_type == MT_NONE)
@@ -554,7 +574,8 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
                 */
 #ifdef CONFIG_PPC_OF
                if (rinfo->mon2_type == MT_NONE)
-                       rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1, &rinfo->mon2_EDID);
+                       rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1,
+                                                               &rinfo->mon2_EDID);
 #endif /* CONFIG_PPC_OF */
 #ifdef CONFIG_FB_RADEON_I2C
                if (rinfo->mon2_type == MT_NONE)
@@ -600,11 +621,9 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
                }
        }
        if (ignore_edid) {
-               if (rinfo->mon1_EDID)
-                       kfree(rinfo->mon1_EDID);
+               kfree(rinfo->mon1_EDID);
                rinfo->mon1_EDID = NULL;
-               if (rinfo->mon2_EDID)
-                       kfree(rinfo->mon2_EDID);
+               kfree(rinfo->mon2_EDID);
                rinfo->mon2_EDID = NULL;
        }
 
@@ -630,34 +649,28 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo,
  */
 static void radeon_fixup_panel_info(struct radeonfb_info *rinfo)
 {
+#ifdef CONFIG_PPC_OF
        /*
-        * A few iBook laptop panels seem to need a fixed PLL setting
-        *
-        * We should probably do this differently based on the panel
-        * type/model or eventually some other device-tree informations,
-        * but these tweaks below work enough for now. --BenH
+        * LCD Flat panels should use fixed dividers, we enfore that on
+        * PPC only for now...
         */
-#ifdef CONFIG_PPC_OF
-       /* iBook2's */
-       if (machine_is_compatible("PowerBook4,3")) {
-               rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
-               rinfo->panel_info.post_divider = 0x6;
-               rinfo->panel_info.fbk_divider = 0xad;
-               rinfo->panel_info.use_bios_dividers = 1;
-       }
-       /* Aluminium PowerBook 17" */
-       if (machine_is_compatible("PowerBook5,3")) {
-               rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
-               rinfo->panel_info.post_divider = 0x4;
-               rinfo->panel_info.fbk_divider = 0x80;
-               rinfo->panel_info.use_bios_dividers = 1;
-       }
-       /* iBook G4 */
-       if (machine_is_compatible("PowerBook6,3")) {
+       if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD
+           && rinfo->is_mobility) {
+               int ppll_div_sel;
+               u32 ppll_divn;
+               ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3;
+               radeon_pll_errata_after_index(rinfo);
+               ppll_divn = INPLL(PPLL_DIV_0 + ppll_div_sel);
                rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
-               rinfo->panel_info.post_divider = 0x6;
-               rinfo->panel_info.fbk_divider = 0xad;
+               rinfo->panel_info.fbk_divider = ppll_divn & 0x7ff;
+               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 "
+                      "from PPLL %d\n",
+                      rinfo->panel_info.fbk_divider |
+                      (rinfo->panel_info.post_divider << 16),
+                      ppll_div_sel);
        }
 #endif /* CONFIG_PPC_OF */
 }
@@ -693,40 +706,24 @@ static void radeon_var_to_panel_info(struct radeonfb_info *rinfo, struct fb_var_
        rinfo->panel_info.pwr_delay = 200;
 }
 
-static void radeon_var_to_videomode(struct fb_videomode *mode,
-                                   const struct fb_var_screeninfo *var)
-{
-    mode->xres = var->xres;
-    mode->yres = var->yres;
-    mode->pixclock = var->pixclock;
-    mode->left_margin = var->left_margin;
-    mode->right_margin = var->right_margin;
-    mode->upper_margin = var->upper_margin;
-    mode->lower_margin = var->lower_margin;
-    mode->hsync_len = var->hsync_len;
-    mode->vsync_len = var->vsync_len;
-    mode->sync = var->sync;
-    mode->vmode = var->vmode;
-}
-
 static void radeon_videomode_to_var(struct fb_var_screeninfo *var,
                                    const struct fb_videomode *mode)
 {
-    var->xres = mode->xres;
-    var->yres = mode->yres;
-    var->xres_virtual = mode->xres;
-    var->yres_virtual = mode->yres;
-    var->xoffset = 0;
-    var->yoffset = 0;
-    var->pixclock = mode->pixclock;
-    var->left_margin = mode->left_margin;
-    var->right_margin = mode->right_margin;
-    var->upper_margin = mode->upper_margin;
-    var->lower_margin = mode->lower_margin;
-    var->hsync_len = mode->hsync_len;
-    var->vsync_len = mode->vsync_len;
-    var->sync = mode->sync;
-    var->vmode = mode->vmode;
+       var->xres = mode->xres;
+       var->yres = mode->yres;
+       var->xres_virtual = mode->xres;
+       var->yres_virtual = mode->yres;
+       var->xoffset = 0;
+       var->yoffset = 0;
+       var->pixclock = mode->pixclock;
+       var->left_margin = mode->left_margin;
+       var->right_margin = mode->right_margin;
+       var->upper_margin = mode->upper_margin;
+       var->lower_margin = mode->lower_margin;
+       var->hsync_len = mode->hsync_len;
+       var->vsync_len = mode->vsync_len;
+       var->sync = mode->sync;
+       var->vmode = mode->vmode;
 }
 
 /*
@@ -735,12 +732,14 @@ static void radeon_videomode_to_var(struct fb_var_screeninfo *var,
  */
 void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_option)
 {
+       struct fb_info * info = rinfo->info;
        int has_default_mode = 0;
 
        /*
         * Fill default var first
         */
-       rinfo->info->var = radeonfb_default_var;
+       info->var = radeonfb_default_var;
+       INIT_LIST_HEAD(&info->modelist);
 
        /*
         * First check out what BIOS has to say
@@ -774,7 +773,7 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
         * those
         */
        if (rinfo->mon1_type != MT_CRT && rinfo->panel_info.valid) {
-               struct fb_var_screeninfo *var = &rinfo->info->var;
+               struct fb_var_screeninfo *var = &info->var;
 
                RTRACE("Setting up default mode based on panel info\n");
                var->xres = rinfo->panel_info.xres;
@@ -805,9 +804,12 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
         * Now build modedb from EDID
         */
        if (rinfo->mon1_EDID) {
-               rinfo->mon1_modedb = fb_create_modedb(rinfo->mon1_EDID,
-                                                     &rinfo->mon1_dbsize);
-               fb_get_monitor_limits(rinfo->mon1_EDID, &rinfo->info->monspecs);
+               fb_edid_to_monspecs(rinfo->mon1_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;
        }
 
        
@@ -838,31 +840,62 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
                modedb = rinfo->mon1_modedb;
                dbsize = rinfo->mon1_dbsize;
                snprintf(modename, 31, "%dx%d", rinfo->panel_info.xres, rinfo->panel_info.yres);
-               if (fb_find_mode(&rinfo->info->var, rinfo->info, modename,
+               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;
                        goto pickup_default;
                }
                has_default_mode = 1;
-               radeon_var_to_panel_info(rinfo, &rinfo->info->var);
+               radeon_var_to_panel_info(rinfo, &info->var);
        }
 
  pickup_default:
        /*
-        * Pick up a random default mode
+        * Apply passed-in mode option if any
         */
-       if (!has_default_mode || mode_option) {
-               struct fb_videomode default_mode;
-               if (has_default_mode)
-                       radeon_var_to_videomode(&default_mode, &rinfo->info->var);
-               else
-                       radeon_var_to_videomode(&default_mode, &radeonfb_default_var);
-               if (fb_find_mode(&rinfo->info->var, rinfo->info, mode_option,
-                                rinfo->mon1_modedb, rinfo->mon1_dbsize, &default_mode, 8) == 0)
-                       rinfo->info->var = radeonfb_default_var;
-       }
+       if (mode_option) {
+               if (fb_find_mode(&info->var, info, mode_option,
+                                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
+        */
+       if (!has_default_mode && info->monspecs.modedb != NULL) {
+               struct fb_monspecs *specs = &info->monspecs;
+               struct fb_videomode *modedb = NULL;
+
+               /* get preferred timing */
+               if (specs->misc & FB_MISC_1ST_DETAIL) {
+                       int i;
 
+                       for (i = 0; i < specs->modedb_len; i++) {
+                               if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
+                                       modedb = &specs->modedb[i];
+                                       break;
+                               }
+                       }
+               } else {
+                       /* otherwise, get first mode in database */
+                       modedb = &specs->modedb[0];
+               }
+               if (modedb != NULL) {
+                       info->var.bits_per_pixel = 8;
+                       radeon_videomode_to_var(&info->var, modedb);
+                       has_default_mode = 1;
+               }
+       }
+       if (1) {
+               struct fb_videomode mode;
+               /* Make sure that whatever mode got selected is actually in the
+                * modelist or the kernel may die
+                */
+               fb_var_to_videomode(&mode, &info->var);
+               fb_add_videomode(&mode, &info->modelist);
+       }
 }
 
 /*
@@ -871,7 +904,7 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
  */
 
 /*
- * This is used when looking for modes. We assign a "goodness" value
+ * This is used when looking for modes. We assign a "distance" value
  * to a mode in the modedb depending how "close" it is from what we
  * are looking for.
  * Currently, we don't compare that much, we could do better but
@@ -880,13 +913,11 @@ void __devinit radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_
 static int radeon_compare_modes(const struct fb_var_screeninfo *var,
                                const struct fb_videomode *mode)
 {
-       int goodness = 0;
+       int distance = 0;
 
-       if (var->yres == mode->yres)
-               goodness += 10;
-       if (var->xres == mode->xres)
-               goodness += 9;
-       return goodness;
+       distance = mode->yres - var->yres;
+       distance += (mode->xres - var->xres)/2;
+       return distance;
 }
 
 /*
@@ -908,7 +939,7 @@ int  radeon_match_mode(struct radeonfb_info *rinfo,
        const struct fb_videomode       *db = vesa_modes;
        int                             i, dbsize = 34;
        int                             has_rmx, native_db = 0;
-       int                             goodness = 0;
+       int                             distance = INT_MAX;
        const struct fb_videomode       *candidate = NULL;
 
        /* Start with a copy of the requested mode */
@@ -944,19 +975,19 @@ int  radeon_match_mode(struct radeonfb_info *rinfo,
        /* Now look for a mode in the database */
        while (db) {
                for (i = 0; i < dbsize; i++) {
-                       int g;
+                       int d;
 
                        if (db[i].yres < src->yres)
                                continue;       
                        if (db[i].xres < src->xres)
                                continue;
-                       g = radeon_compare_modes(src, &db[i]);
+                       d = radeon_compare_modes(src, &db[i]);
                        /* If the new mode is at least as good as the previous one,
                         * then it's our new candidate
                         */
-                       if (g >= goodness) {
+                       if (d < distance) {
                                candidate = &db[i];
-                               goodness = g;
+                               distance = d;
                        }
                }
                db = NULL;