Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / video / aty / radeon_pm.c
index 498dc4e..459fda6 100644 (file)
 #include <linux/agp_backend.h>
 
 #ifdef CONFIG_PPC_PMAC
-#include <asm/processor.h>
+#include <asm/machdep.h>
 #include <asm/prom.h>
 #include <asm/pmac_feature.h>
 #endif
 
+/* For detecting supported PC laptops */
+#ifdef CONFIG_X86
+#include <linux/dmi.h>
+#endif
+
 #include "ati_ids.h"
 
-void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
+#ifdef CONFIG_X86
+/* This array holds a list of supported PC laptops.
+ * Currently only few IBM models are tested.
+ * If you want to experiment, use dmidecode to find out
+ * vendor and product codes for Your laptop.
+ */
+static struct dmi_system_id __devinitdata radeonfb_dmi_table[] = {
+#include "radeon_pm_whitelist.h"
+};
+
+extern int radeon_force_sleep;
+#endif
+
+static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
 {
        u32 tmp;
 
@@ -62,9 +80,9 @@ void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
                 OUTPLL(pllSCLK_CNTL, tmp);
                return;
        }
-       /* RV350 (M10) */
+       /* RV350 (M10/M11) */
        if (rinfo->family == CHIP_FAMILY_RV350) {
-                /* for RV350/M10, no delays are required. */
+                /* for RV350/M10/M11, no delays are required. */
                 tmp = INPLL(pllSCLK_CNTL2);
                 tmp |= (SCLK_CNTL2__R300_FORCE_TCL |
                         SCLK_CNTL2__R300_FORCE_GA  |
@@ -229,7 +247,7 @@ void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
        radeon_msleep(16);
 }
 
-void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo)
+static void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo)
 {
        u32 tmp;
 
@@ -248,7 +266,7 @@ void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo)
                return;
        }
 
-       /* M10 */
+       /* M10/M11 */
        if (rinfo->family == CHIP_FAMILY_RV350) {
                tmp = INPLL(pllSCLK_CNTL2);
                tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL |
@@ -852,7 +870,14 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
        /* because both INPLL and OUTPLL take the same lock, that's why. */
        tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
        OUTPLL( pllMCLK_MISC, tmp);
-       
+
+       /* BUS_CNTL1__MOBILE_PLATORM_SEL setting is northbridge chipset
+        * and radeon chip dependent. Thus we only enable it on Mac for
+        * now (until we get more info on how to compute the correct
+        * value for various X86 bridges).
+        */
+
+#ifdef CONFIG_PPC_PMAC
        /* AGP PLL control */
        if (rinfo->family <= CHIP_FAMILY_RV280) {
                OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) |  BUS_CNTL1__AGPCLK_VALID);
@@ -864,6 +889,7 @@ static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
                OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
                OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
        }
+#endif
 
        OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL)
                                  & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
@@ -1155,7 +1181,7 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo)
        OUTREG( CRTC_GEN_CNTL,  (crtcGenCntl | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B) );
        OUTREG( CRTC2_GEN_CNTL, (crtcGenCntl2 | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B) );
   
-       /* This is the code for the Aluminium PowerBooks M10 */
+       /* This is the code for the Aluminium PowerBooks M10 / iBooks M11 */
        if (rinfo->family == CHIP_FAMILY_RV350) {
                u32 sdram_mode_reg = rinfo->save_regs[35];
                static u32 default_mrtable[] =
@@ -1321,8 +1347,6 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo)
        mdelay( 15);
 }
 
-#ifdef CONFIG_PPC_OF
-
 static void radeon_pm_reset_pad_ctlr_strength(struct radeonfb_info *rinfo)
 {
        u32 tmp, tmp2;
@@ -1373,7 +1397,9 @@ static void radeon_pm_start_mclk_sclk(struct radeonfb_info *rinfo)
        /* Reconfigure SPLL charge pump, VCO gain, duty cycle */
        tmp = INPLL(pllSPLL_CNTL);
        OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+       radeon_pll_errata_after_data(rinfo);
 
        /* Set SPLL feedback divider */
        tmp = INPLL(pllM_SPLL_REF_FB_DIV);
@@ -1406,7 +1432,9 @@ static void radeon_pm_start_mclk_sclk(struct radeonfb_info *rinfo)
        /* Reconfigure MPLL charge pump, VCO gain, duty cycle */
        tmp = INPLL(pllMPLL_CNTL);
        OUTREG8(CLOCK_CNTL_INDEX, pllMPLL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+       radeon_pll_errata_after_data(rinfo);
 
        /* Set MPLL feedback divider */
        tmp = INPLL(pllM_SPLL_REF_FB_DIV);
@@ -1525,7 +1553,9 @@ static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo)
        u32 tmp;
 
        OUTREG8(CLOCK_CNTL_INDEX, pllHTOTAL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA, 0);
+       radeon_pll_errata_after_data(rinfo);
 
        tmp = INPLL(pllVCLK_ECP_CNTL);
        OUTPLL(pllVCLK_ECP_CNTL, tmp | 0x80);
@@ -1541,11 +1571,9 @@ static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo)
         */
        tmp = INPLL(pllPPLL_CNTL);
        OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
-
-       /* Not sure what was intended here ... */
-       tmp = INREG(CLOCK_CNTL_INDEX);
-       OUTREG(CLOCK_CNTL_INDEX, tmp);
+       radeon_pll_errata_after_data(rinfo);
 
        /* Restore our "reference" PPLL divider set by firmware
         * according to proper spread spectrum calculations
@@ -1570,6 +1598,8 @@ static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo)
 
        /* Switch pixel clock to firmware default div 0 */
        OUTREG8(CLOCK_CNTL_INDEX+1, 0);
+       radeon_pll_errata_after_index(rinfo);
+       radeon_pll_errata_after_data(rinfo);
 }
 
 static void radeon_pm_m10_reconfigure_mc(struct radeonfb_info *rinfo)
@@ -1830,6 +1860,8 @@ static void radeon_reinitialize_M10(struct radeonfb_info *rinfo)
        radeon_pm_m10_enable_lvds_spread_spectrum(rinfo);
 }
 
+#ifdef CONFIG_PPC_OF
+
 static void radeon_pm_m9p_reconfigure_mc(struct radeonfb_info *rinfo)
 {
        OUTREG(MC_CNTL, rinfo->save_regs[46]);
@@ -2074,7 +2106,7 @@ static void radeon_reinitialize_M9P(struct radeonfb_info *rinfo)
        OUTREG(0x2ec, 0x6332a3f0);
        mdelay(17);
 
-       OUTPLL(pllPPLL_REF_DIV, rinfo->pll.ref_div);;
+       OUTPLL(pllPPLL_REF_DIV, rinfo->pll.ref_div);
        OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]);
 
        mdelay(40);
@@ -2157,7 +2189,9 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo)
 
        tmp = INPLL(MPLL_CNTL);
        OUTREG8(CLOCK_CNTL_INDEX, MPLL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+       radeon_pll_errata_after_data(rinfo);
 
        tmp = INPLL(M_SPLL_REF_FB_DIV);
        OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x5900);
@@ -2178,7 +2212,9 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo)
 
        tmp = INPLL(SPLL_CNTL);
        OUTREG8(CLOCK_CNTL_INDEX, SPLL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+       radeon_pll_errata_after_data(rinfo);
 
        tmp = INPLL(M_SPLL_REF_FB_DIV);
        OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x780000);
@@ -2306,7 +2342,9 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo)
        OUTREG(CRTC_H_SYNC_STRT_WID, 0x008e0580);
        OUTREG(CRTC_H_TOTAL_DISP, 0x009f00d2);
        OUTREG8(CLOCK_CNTL_INDEX, HTOTAL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA, 0);
+       radeon_pll_errata_after_data(rinfo);
        OUTREG(CRTC_V_SYNC_STRT_WID, 0x00830403);
        OUTREG(CRTC_V_TOTAL_DISP, 0x03ff0429);
        OUTREG(FP_CRTC_H_TOTAL_DISP, 0x009f0033);
@@ -2328,10 +2366,15 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo)
        INPLL(PPLL_REF_DIV);
 
        OUTREG8(CLOCK_CNTL_INDEX, PPLL_CNTL + PLL_WR_EN);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG8(CLOCK_CNTL_DATA + 1, 0xbc);
+       radeon_pll_errata_after_data(rinfo);
 
        tmp = INREG(CLOCK_CNTL_INDEX);
+       radeon_pll_errata_after_index(rinfo);
        OUTREG(CLOCK_CNTL_INDEX, tmp & 0xff);
+       radeon_pll_errata_after_index(rinfo);
+       radeon_pll_errata_after_data(rinfo);
 
        OUTPLL(PPLL_DIV_0, 0x48090);
 
@@ -2400,7 +2443,8 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
         * including PCI config registers, clocks, AGP conf, ...)
         */
        if (suspend) {
-               printk(KERN_DEBUG "radeonfb: switching to D2 state...\n");
+               printk(KERN_DEBUG "radeonfb (%s): switching to D2 state...\n",
+                      pci_name(rinfo->pdev));
 
                /* Disable dynamic power management of clocks for the
                 * duration of the suspend/resume process
@@ -2453,7 +2497,8 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
                        mdelay(500);
                }
        } else {
-               printk(KERN_DEBUG "radeonfb: switching to D0 state...\n");
+               printk(KERN_DEBUG "radeonfb (%s): switching to D0 state...\n",
+                      pci_name(rinfo->pdev));
 
                /* Switch back PCI powermanagment to D0 */
                mdelay(200);
@@ -2501,31 +2546,25 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo)
 }
 
 
-static/*extern*/ int susdisking = 0;
-
-int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state)
+int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 {
         struct fb_info *info = pci_get_drvdata(pdev);
         struct radeonfb_info *rinfo = info->par;
        int i;
 
-       if (state == pdev->dev.power.power_state)
+       if (state.event == pdev->dev.power.power_state.event)
                return 0;
 
        printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n",
-              pci_name(pdev), state);
+              pci_name(pdev), state.event);
 
        /* For suspend-to-disk, we cheat here. We don't suspend anything and
         * let fbcon continue drawing until we are all set. That shouldn't
         * really cause any problem at this point, provided that the wakeup
         * code knows that any state in memory may not match the HW
         */
-       if (state != PM_SUSPEND_MEM)
-               goto done;
-       if (susdisking) {
-               printk("suspending to disk but state = %d\n", state);
+       if (state.event == PM_EVENT_FREEZE)
                goto done;
-       }
 
        acquire_console_sem();
 
@@ -2546,6 +2585,14 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state)
        rinfo->lock_blank = 1;
        del_timer_sync(&rinfo->lvds_timer);
 
+#ifdef CONFIG_PPC_PMAC
+       /* On powermac, we have hooks to properly suspend/resume AGP now,
+        * use them here. We'll ultimately need some generic support here,
+        * but the generic code isn't quite ready for that yet
+        */
+       pmac_suspend_agp_for_card(pdev);
+#endif /* CONFIG_PPC_PMAC */
+
        /* If we support wakeup from poweroff, we save all regs we can including cfg
         * space
         */
@@ -2569,12 +2616,11 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, u32 state)
                        OUTREG(LVDS_PLL_CNTL, (INREG(LVDS_PLL_CNTL) & ~30000) | 0x20000);
                        mdelay(20);
                        OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON));
-
-                       // FIXME: Use PCI layer
-                       for (i = 0; i < 64; ++i)
-                               pci_read_config_dword(rinfo->pdev, i * 4,
-                                                     &rinfo->cfg_save[i]);
                }
+               // FIXME: Use PCI layer
+               for (i = 0; i < 64; ++i)
+                       pci_read_config_dword(pdev, i * 4, &rinfo->cfg_save[i]);
+               pci_disable_device(pdev);
        }
        /* If we support D2, we go to it (should be fixed later with a flag forcing
         * D3 only for some laptops)
@@ -2596,7 +2642,7 @@ int radeonfb_pci_resume(struct pci_dev *pdev)
         struct radeonfb_info *rinfo = info->par;
        int rc = 0;
 
-       if (pdev->dev.power.power_state == 0)
+       if (pdev->dev.power.power_state.event == PM_EVENT_ON)
                return 0;
 
        if (rinfo->no_schedule) {
@@ -2606,7 +2652,7 @@ int radeonfb_pci_resume(struct pci_dev *pdev)
                acquire_console_sem();
 
        printk(KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n",
-              pci_name(pdev), pdev->dev.power.power_state);
+              pci_name(pdev), pdev->dev.power.power_state.event);
 
 
        if (pci_enable_device(pdev)) {
@@ -2617,7 +2663,7 @@ int radeonfb_pci_resume(struct pci_dev *pdev)
        }
        pci_set_master(pdev);
 
-       if (pdev->dev.power.power_state == PM_SUSPEND_MEM) {
+       if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
                /* Wakeup chip. Check from config space if we were powered off
                 * (todo: additionally, check CLK_PIN_CNTL too)
                 */
@@ -2657,13 +2703,22 @@ int radeonfb_pci_resume(struct pci_dev *pdev)
        rinfo->lock_blank = 0;
        radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 1);
 
+#ifdef CONFIG_PPC_PMAC
+       /* On powermac, we have hooks to properly suspend/resume AGP now,
+        * use them here. We'll ultimately need some generic support here,
+        * but the generic code isn't quite ready for that yet
+        */
+       pmac_resume_agp_for_card(pdev);
+#endif /* CONFIG_PPC_PMAC */
+
+
        /* Check status of dynclk */
        if (rinfo->dynclk == 1)
                radeon_pm_enable_dynamic_mode(rinfo);
        else if (rinfo->dynclk == 0)
                radeon_pm_disable_dynamic_mode(rinfo);
 
-       pdev->dev.power.power_state = 0;
+       pdev->dev.power.power_state = PMSG_ON;
 
  bail:
        release_console_sem();
@@ -2699,22 +2754,34 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
                printk("radeonfb: Dynamic Clock Power Management disabled\n");
        }
 
+#if defined(CONFIG_PM)
        /* Check if we can power manage on suspend/resume. We can do
         * D2 on M6, M7 and M9, and we can resume from D3 cold a few other
         * "Mac" cards, but that's all. We need more infos about what the
         * BIOS does tho. Right now, all this PM stuff is pmac-only for that
         * reason. --BenH
         */
-#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF)
-       if (_machine == _MACH_Pmac && rinfo->of_node) {
+       /* Special case for Samsung P35 laptops
+        */
+       if ((rinfo->pdev->vendor == PCI_VENDOR_ID_ATI) &&
+           (rinfo->pdev->device == PCI_CHIP_RV350_NP) &&
+           (rinfo->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG) &&
+           (rinfo->pdev->subsystem_device == 0xc00c)) {
+               rinfo->reinit_func = radeon_reinitialize_M10;
+               rinfo->pm_mode |= radeon_pm_off;
+       }
+#if defined(CONFIG_PPC_PMAC)
+       if (machine_is(powermac) && rinfo->of_node) {
                if (rinfo->is_mobility && rinfo->pm_reg &&
                    rinfo->family <= CHIP_FAMILY_RV250)
                        rinfo->pm_mode |= radeon_pm_d2;
 
                /* We can restart Jasper (M10 chip in albooks), BlueStone (7500 chip
-                * in some desktop G4s), and Via (M9+ chip on iBook G4)
+                * in some desktop G4s), Via (M9+ chip on iBook G4) and
+                * Snowy (M11 chip on iBook G4 manufactured after July 2005)
                 */
-               if (!strcmp(rinfo->of_node->name, "ATY,JasperParent")) {
+               if (!strcmp(rinfo->of_node->name, "ATY,JasperParent") ||
+                   !strcmp(rinfo->of_node->name, "ATY,SnowyParent")) {
                        rinfo->reinit_func = radeon_reinitialize_M10;
                        rinfo->pm_mode |= radeon_pm_off;
                }
@@ -2727,8 +2794,6 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
                if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) {
                        rinfo->reinit_func = radeon_reinitialize_M9P;
                        rinfo->pm_mode |= radeon_pm_off;
-                       /* Workaround not used for now */
-                       rinfo->m9p_workaround = 1;
                }
 
                /* If any of the above is set, we assume the machine can sleep/resume.
@@ -2749,12 +2814,36 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
                OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000);
 #endif
        }
-#endif /* defined(CONFIG_PM) && defined(CONFIG_PPC_OF) */
+#endif /* defined(CONFIG_PPC_PMAC) */
+#endif /* defined(CONFIG_PM) */
+
+/* The PM code also works on some PC laptops.
+ * Only a few models are actually tested so Your mileage may vary.
+ * We can do D2 on at least M7 and M9 on some IBM ThinkPad T41 models.
+ */
+#if defined(CONFIG_PM) && defined(CONFIG_X86)
+       if (radeon_force_sleep || dmi_check_system(radeonfb_dmi_table)) {
+               if (radeon_force_sleep)
+                       printk("radeonfb: forcefully enabling sleep mode\n");
+               else
+                       printk("radeonfb: enabling sleep mode\n");
+
+               if (rinfo->is_mobility && rinfo->pm_reg &&
+                   rinfo->family <= CHIP_FAMILY_RV250)
+                       rinfo->pm_mode |= radeon_pm_d2;
+
+               /* Power down TV DAC, that saves a significant amount of power,
+                * we'll have something better once we actually have some TVOut
+                * support
+                */
+               OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000);
+       }
+#endif /* defined(CONFIG_PM) && defined(CONFIG_X86) */
 }
 
 void radeonfb_pm_exit(struct radeonfb_info *rinfo)
 {
-#if defined(CONFIG_PM) && defined(CONFIG_PPC_OF)
+#if defined(CONFIG_PM) && defined(CONFIG_PPC_PMAC)
        if (rinfo->pm_mode != radeon_pm_none)
                pmac_set_early_video_resume(NULL, NULL);
 #endif