diff options
Diffstat (limited to 'drivers/video/aty/radeon_pm.c')
-rw-r--r-- | drivers/video/aty/radeon_pm.c | 119 |
1 files changed, 53 insertions, 66 deletions
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index c4ac2a032fcb..c6d7cc76516f 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c | |||
@@ -2507,11 +2507,28 @@ static void radeon_reinitialize_QW(struct radeonfb_info *rinfo) | |||
2507 | 2507 | ||
2508 | #endif /* CONFIG_PPC_OF */ | 2508 | #endif /* CONFIG_PPC_OF */ |
2509 | 2509 | ||
2510 | static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) | 2510 | static void radeonfb_whack_power_state(struct radeonfb_info *rinfo, pci_power_t state) |
2511 | { | 2511 | { |
2512 | u16 pwr_cmd; | 2512 | u16 pwr_cmd; |
2513 | |||
2514 | for (;;) { | ||
2515 | pci_read_config_word(rinfo->pdev, | ||
2516 | rinfo->pm_reg+PCI_PM_CTRL, | ||
2517 | &pwr_cmd); | ||
2518 | if (pwr_cmd & 2) | ||
2519 | break; | ||
2520 | pwr_cmd = (pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | 2; | ||
2521 | pci_write_config_word(rinfo->pdev, | ||
2522 | rinfo->pm_reg+PCI_PM_CTRL, | ||
2523 | pwr_cmd); | ||
2524 | msleep(500); | ||
2525 | } | ||
2526 | rinfo->pdev->current_state = state; | ||
2527 | } | ||
2528 | |||
2529 | static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) | ||
2530 | { | ||
2513 | u32 tmp; | 2531 | u32 tmp; |
2514 | int i; | ||
2515 | 2532 | ||
2516 | if (!rinfo->pm_reg) | 2533 | if (!rinfo->pm_reg) |
2517 | return; | 2534 | return; |
@@ -2557,32 +2574,19 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) | |||
2557 | } | 2574 | } |
2558 | } | 2575 | } |
2559 | 2576 | ||
2560 | for (i = 0; i < 64; ++i) | ||
2561 | pci_read_config_dword(rinfo->pdev, i * 4, | ||
2562 | &rinfo->cfg_save[i]); | ||
2563 | |||
2564 | /* Switch PCI power management to D2. */ | 2577 | /* Switch PCI power management to D2. */ |
2565 | pci_disable_device(rinfo->pdev); | 2578 | pci_disable_device(rinfo->pdev); |
2566 | for (;;) { | 2579 | pci_save_state(rinfo->pdev); |
2567 | pci_read_config_word( | 2580 | /* The chip seems to need us to whack the PM register |
2568 | rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, | 2581 | * repeatedly until it sticks. We do that -prior- to |
2569 | &pwr_cmd); | 2582 | * calling pci_set_power_state() |
2570 | if (pwr_cmd & 2) | 2583 | */ |
2571 | break; | 2584 | radeonfb_whack_power_state(rinfo, PCI_D2); |
2572 | pci_write_config_word( | 2585 | pci_set_power_state(rinfo->pdev, PCI_D2); |
2573 | rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, | ||
2574 | (pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | 2); | ||
2575 | mdelay(500); | ||
2576 | } | ||
2577 | } else { | 2586 | } else { |
2578 | printk(KERN_DEBUG "radeonfb (%s): switching to D0 state...\n", | 2587 | printk(KERN_DEBUG "radeonfb (%s): switching to D0 state...\n", |
2579 | pci_name(rinfo->pdev)); | 2588 | pci_name(rinfo->pdev)); |
2580 | 2589 | ||
2581 | /* Switch back PCI powermanagment to D0 */ | ||
2582 | mdelay(200); | ||
2583 | pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0); | ||
2584 | mdelay(500); | ||
2585 | |||
2586 | if (rinfo->family <= CHIP_FAMILY_RV250) { | 2590 | if (rinfo->family <= CHIP_FAMILY_RV250) { |
2587 | /* Reset the SDRAM controller */ | 2591 | /* Reset the SDRAM controller */ |
2588 | radeon_pm_full_reset_sdram(rinfo); | 2592 | radeon_pm_full_reset_sdram(rinfo); |
@@ -2598,37 +2602,10 @@ static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend) | |||
2598 | } | 2602 | } |
2599 | } | 2603 | } |
2600 | 2604 | ||
2601 | static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo) | ||
2602 | { | ||
2603 | int i; | ||
2604 | static u32 radeon_cfg_after_resume[64]; | ||
2605 | |||
2606 | for (i = 0; i < 64; ++i) | ||
2607 | pci_read_config_dword(rinfo->pdev, i * 4, | ||
2608 | &radeon_cfg_after_resume[i]); | ||
2609 | |||
2610 | if (radeon_cfg_after_resume[PCI_BASE_ADDRESS_0/4] | ||
2611 | == rinfo->cfg_save[PCI_BASE_ADDRESS_0/4]) | ||
2612 | return 0; /* assume everything is ok */ | ||
2613 | |||
2614 | for (i = PCI_BASE_ADDRESS_0/4; i < 64; ++i) { | ||
2615 | if (radeon_cfg_after_resume[i] != rinfo->cfg_save[i]) | ||
2616 | pci_write_config_dword(rinfo->pdev, i * 4, | ||
2617 | rinfo->cfg_save[i]); | ||
2618 | } | ||
2619 | pci_write_config_word(rinfo->pdev, PCI_CACHE_LINE_SIZE, | ||
2620 | rinfo->cfg_save[PCI_CACHE_LINE_SIZE/4]); | ||
2621 | pci_write_config_word(rinfo->pdev, PCI_COMMAND, | ||
2622 | rinfo->cfg_save[PCI_COMMAND/4]); | ||
2623 | return 1; | ||
2624 | } | ||
2625 | |||
2626 | |||
2627 | int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) | 2605 | int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) |
2628 | { | 2606 | { |
2629 | struct fb_info *info = pci_get_drvdata(pdev); | 2607 | struct fb_info *info = pci_get_drvdata(pdev); |
2630 | struct radeonfb_info *rinfo = info->par; | 2608 | struct radeonfb_info *rinfo = info->par; |
2631 | int i; | ||
2632 | 2609 | ||
2633 | if (mesg.event == pdev->dev.power.power_state.event) | 2610 | if (mesg.event == pdev->dev.power.power_state.event) |
2634 | return 0; | 2611 | return 0; |
@@ -2674,6 +2651,11 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) | |||
2674 | pmac_suspend_agp_for_card(pdev); | 2651 | pmac_suspend_agp_for_card(pdev); |
2675 | #endif /* CONFIG_PPC_PMAC */ | 2652 | #endif /* CONFIG_PPC_PMAC */ |
2676 | 2653 | ||
2654 | /* It's unclear whether or when the generic code will do that, so let's | ||
2655 | * do it ourselves. We save state before we do any power management | ||
2656 | */ | ||
2657 | pci_save_state(pdev); | ||
2658 | |||
2677 | /* If we support wakeup from poweroff, we save all regs we can including cfg | 2659 | /* If we support wakeup from poweroff, we save all regs we can including cfg |
2678 | * space | 2660 | * space |
2679 | */ | 2661 | */ |
@@ -2698,9 +2680,6 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) | |||
2698 | mdelay(20); | 2680 | mdelay(20); |
2699 | OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON)); | 2681 | OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON)); |
2700 | } | 2682 | } |
2701 | // FIXME: Use PCI layer | ||
2702 | for (i = 0; i < 64; ++i) | ||
2703 | pci_read_config_dword(pdev, i * 4, &rinfo->cfg_save[i]); | ||
2704 | pci_disable_device(pdev); | 2683 | pci_disable_device(pdev); |
2705 | } | 2684 | } |
2706 | /* If we support D2, we go to it (should be fixed later with a flag forcing | 2685 | /* If we support D2, we go to it (should be fixed later with a flag forcing |
@@ -2717,6 +2696,13 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) | |||
2717 | return 0; | 2696 | return 0; |
2718 | } | 2697 | } |
2719 | 2698 | ||
2699 | static int radeon_check_power_loss(struct radeonfb_info *rinfo) | ||
2700 | { | ||
2701 | return rinfo->save_regs[4] != INPLL(CLK_PIN_CNTL) || | ||
2702 | rinfo->save_regs[2] != INPLL(MCLK_CNTL) || | ||
2703 | rinfo->save_regs[3] != INPLL(SCLK_CNTL); | ||
2704 | } | ||
2705 | |||
2720 | int radeonfb_pci_resume(struct pci_dev *pdev) | 2706 | int radeonfb_pci_resume(struct pci_dev *pdev) |
2721 | { | 2707 | { |
2722 | struct fb_info *info = pci_get_drvdata(pdev); | 2708 | struct fb_info *info = pci_get_drvdata(pdev); |
@@ -2735,20 +2721,13 @@ int radeonfb_pci_resume(struct pci_dev *pdev) | |||
2735 | printk(KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n", | 2721 | printk(KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n", |
2736 | pci_name(pdev), pdev->dev.power.power_state.event); | 2722 | pci_name(pdev), pdev->dev.power.power_state.event); |
2737 | 2723 | ||
2738 | 2724 | /* PCI state will have been restored by the core, so | |
2739 | if (pci_enable_device(pdev)) { | 2725 | * we should be in D0 now with our config space fully |
2740 | rc = -ENODEV; | 2726 | * restored |
2741 | printk(KERN_ERR "radeonfb (%s): can't enable PCI device !\n", | 2727 | */ |
2742 | pci_name(pdev)); | ||
2743 | goto bail; | ||
2744 | } | ||
2745 | pci_set_master(pdev); | ||
2746 | |||
2747 | if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { | 2728 | if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { |
2748 | /* Wakeup chip. Check from config space if we were powered off | 2729 | /* Wakeup chip */ |
2749 | * (todo: additionally, check CLK_PIN_CNTL too) | 2730 | if ((rinfo->pm_mode & radeon_pm_off) && radeon_check_power_loss(rinfo)) { |
2750 | */ | ||
2751 | if ((rinfo->pm_mode & radeon_pm_off) && radeon_restore_pci_cfg(rinfo)) { | ||
2752 | if (rinfo->reinit_func != NULL) | 2731 | if (rinfo->reinit_func != NULL) |
2753 | rinfo->reinit_func(rinfo); | 2732 | rinfo->reinit_func(rinfo); |
2754 | else { | 2733 | else { |
@@ -2807,12 +2786,13 @@ int radeonfb_pci_resume(struct pci_dev *pdev) | |||
2807 | return rc; | 2786 | return rc; |
2808 | } | 2787 | } |
2809 | 2788 | ||
2810 | #ifdef CONFIG_PPC_OF | 2789 | #ifdef CONFIG_PPC_OF__disabled |
2811 | static void radeonfb_early_resume(void *data) | 2790 | static void radeonfb_early_resume(void *data) |
2812 | { | 2791 | { |
2813 | struct radeonfb_info *rinfo = data; | 2792 | struct radeonfb_info *rinfo = data; |
2814 | 2793 | ||
2815 | rinfo->no_schedule = 1; | 2794 | rinfo->no_schedule = 1; |
2795 | pci_restore_state(rinfo->pdev); | ||
2816 | radeonfb_pci_resume(rinfo->pdev); | 2796 | radeonfb_pci_resume(rinfo->pdev); |
2817 | rinfo->no_schedule = 0; | 2797 | rinfo->no_schedule = 0; |
2818 | } | 2798 | } |
@@ -2879,7 +2859,14 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis | |||
2879 | */ | 2859 | */ |
2880 | if (rinfo->pm_mode != radeon_pm_none) { | 2860 | if (rinfo->pm_mode != radeon_pm_none) { |
2881 | pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, rinfo->of_node, 0, 1); | 2861 | pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, rinfo->of_node, 0, 1); |
2862 | #if 0 /* Disable the early video resume hack for now as it's causing problems, among | ||
2863 | * others we now rely on the PCI core restoring the config space for us, which | ||
2864 | * isn't the case with that hack, and that code path causes various things to | ||
2865 | * be called with interrupts off while they shouldn't. I'm leaving the code in | ||
2866 | * as it can be useful for debugging purposes | ||
2867 | */ | ||
2882 | pmac_set_early_video_resume(radeonfb_early_resume, rinfo); | 2868 | pmac_set_early_video_resume(radeonfb_early_resume, rinfo); |
2869 | #endif | ||
2883 | } | 2870 | } |
2884 | 2871 | ||
2885 | #if 0 | 2872 | #if 0 |