summaryrefslogtreecommitdiff
path: root/drivers/char/agp/uninorth-agp.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2005-04-17 02:24:19 +0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-17 02:24:19 +0400
commit0c541b4406a68e74d94ddb667c69d9e03bce8681 (patch)
tree869506b6c3f7c00ac13f2aa80c35fb5e229cc329 /drivers/char/agp/uninorth-agp.c
parent7a648b9ec09f32606fe0f27fb9d095311cf968ca (diff)
downloadlinux-0c541b4406a68e74d94ddb667c69d9e03bce8681.tar.xz
[PATCH] ppc32: Fix AGP and sleep again
My previous patch that added sleep support for uninorth-agp and some AGP "off" stuff in radeonfb and aty128fb is breaking some configs. More specifically, it has problems with rage128 setups since the DRI code for these in X doesn't properly re-enable AGP on wakeup or console switch (unlike the radeon DRM). This patch fixes the problem for pmac once for all by using a different approach. The AGP driver "registers" special suspend/resume callbacks with some arch code that the fbdev's can later on call to suspend and resume AGP, making sure it's resumed back in the same state it was when suspended. This is platform specific for now. It would be too complicated to try to do a generic implementation of this at this point due to all sort of weird things going on with AGP on other architectures. We'll re-work that whole problem cleanly once we finally merge fbdev's and DRI. In the meantime, please apply this patch which brings back some r128 based laptops into working condition as far as system sleep is concerned. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/agp/uninorth-agp.c')
-rw-r--r--drivers/char/agp/uninorth-agp.c52
1 files changed, 44 insertions, 8 deletions
diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c
index 0f248239b4ba..a673971f2a90 100644
--- a/drivers/char/agp/uninorth-agp.c
+++ b/drivers/char/agp/uninorth-agp.c
@@ -10,6 +10,7 @@
#include <asm/uninorth.h>
#include <asm/pci-bridge.h>
#include <asm/prom.h>
+#include <asm/pmac_feature.h>
#include "agp.h"
/*
@@ -26,6 +27,7 @@
static int uninorth_rev;
static int is_u3;
+
static int uninorth_fetch_size(void)
{
int i;
@@ -264,7 +266,8 @@ static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode)
&scratch);
} while ((scratch & PCI_AGP_COMMAND_AGP) == 0 && ++timeout < 1000);
if ((scratch & PCI_AGP_COMMAND_AGP) == 0)
- printk(KERN_ERR PFX "failed to write UniNorth AGP command reg\n");
+ printk(KERN_ERR PFX "failed to write UniNorth AGP"
+ " command register\n");
if (uninorth_rev >= 0x30) {
/* This is an AGP V3 */
@@ -278,13 +281,24 @@ static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode)
}
#ifdef CONFIG_PM
-static int agp_uninorth_suspend(struct pci_dev *pdev, pm_message_t state)
+/*
+ * These Power Management routines are _not_ called by the normal PCI PM layer,
+ * but directly by the video driver through function pointers in the device
+ * tree.
+ */
+static int agp_uninorth_suspend(struct pci_dev *pdev)
{
+ struct agp_bridge_data *bridge;
u32 cmd;
u8 agp;
struct pci_dev *device = NULL;
- if (state != PMSG_SUSPEND)
+ bridge = agp_find_bridge(pdev);
+ if (bridge == NULL)
+ return -ENODEV;
+
+ /* Only one suspend supported */
+ if (bridge->dev_private_data)
return 0;
/* turn off AGP on the video chip, if it was enabled */
@@ -315,6 +329,7 @@ static int agp_uninorth_suspend(struct pci_dev *pdev, pm_message_t state)
/* turn off AGP on the bridge */
agp = pci_find_capability(pdev, PCI_CAP_ID_AGP);
pci_read_config_dword(pdev, agp + PCI_AGP_COMMAND, &cmd);
+ bridge->dev_private_data = (void *)cmd;
if (cmd & PCI_AGP_COMMAND_AGP) {
printk("uninorth-agp: disabling AGP on bridge %s\n",
pci_name(pdev));
@@ -329,9 +344,23 @@ static int agp_uninorth_suspend(struct pci_dev *pdev, pm_message_t state)
static int agp_uninorth_resume(struct pci_dev *pdev)
{
+ struct agp_bridge_data *bridge;
+ u32 command;
+
+ bridge = agp_find_bridge(pdev);
+ if (bridge == NULL)
+ return -ENODEV;
+
+ command = (u32)bridge->dev_private_data;
+ bridge->dev_private_data = NULL;
+ if (!(command & PCI_AGP_COMMAND_AGP))
+ return 0;
+
+ uninorth_agp_enable(bridge, command);
+
return 0;
}
-#endif
+#endif /* CONFIG_PM */
static int uninorth_create_gatt_table(struct agp_bridge_data *bridge)
{
@@ -575,6 +604,12 @@ static int __devinit agp_uninorth_probe(struct pci_dev *pdev,
of_node_put(uninorth_node);
}
+#ifdef CONFIG_PM
+ /* Inform platform of our suspend/resume caps */
+ pmac_register_agp_pm(pdev, agp_uninorth_suspend, agp_uninorth_resume);
+#endif
+
+ /* Allocate & setup our driver */
bridge = agp_alloc_bridge();
if (!bridge)
return -ENOMEM;
@@ -599,6 +634,11 @@ static void __devexit agp_uninorth_remove(struct pci_dev *pdev)
{
struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
+#ifdef CONFIG_PM
+ /* Inform platform of our suspend/resume caps */
+ pmac_register_agp_pm(pdev, NULL, NULL);
+#endif
+
agp_remove_bridge(bridge);
agp_put_bridge(bridge);
}
@@ -622,10 +662,6 @@ static struct pci_driver agp_uninorth_pci_driver = {
.id_table = agp_uninorth_pci_table,
.probe = agp_uninorth_probe,
.remove = agp_uninorth_remove,
-#ifdef CONFIG_PM
- .suspend = agp_uninorth_suspend,
- .resume = agp_uninorth_resume,
-#endif
};
static int __init agp_uninorth_init(void)