summaryrefslogtreecommitdiffstats
path: root/drivers/video/atyfb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/atyfb.c')
-rw-r--r--drivers/video/atyfb.c294
1 files changed, 163 insertions, 131 deletions
diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c
index b6163def3..cf83c755e 100644
--- a/drivers/video/atyfb.c
+++ b/drivers/video/atyfb.c
@@ -1,4 +1,4 @@
-/* $Id: atyfb.c,v 1.126 1999/09/16 18:46:23 geert Exp $
+/* $Id: atyfb.c,v 1.136 2000/01/06 23:53:29 davem Exp $
* linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
*
* Copyright (C) 1997-1998 Geert Uytterhoeven
@@ -269,8 +269,8 @@ struct fb_info_aty {
} fbcon_cmap;
u8 blitter_may_be_busy;
#ifdef __sparc__
- u8 open;
u8 mmaped;
+ int open;
int vtconsole;
int consolecnt;
#endif
@@ -470,9 +470,6 @@ static int read_aty_sense(const struct fb_info_aty *info);
*/
int atyfb_init(void);
-#ifdef CONFIG_FB_OF
-void atyfb_of_init(struct device_node *dp);
-#endif
#ifndef MODULE
int atyfb_setup(char*);
#endif
@@ -2622,10 +2619,8 @@ static int atyfb_open(struct fb_info *info, int user)
struct fb_info_aty *fb = (struct fb_info_aty *)info;
if (user) {
- if (fb->open)
- return -EBUSY;
+ fb->open++;
fb->mmaped = 0;
- fb->open = 1;
fb->vtconsole = -1;
} else {
fb->consolecnt++;
@@ -2635,17 +2630,54 @@ static int atyfb_open(struct fb_info *info, int user)
return(0);
}
+struct fb_var_screeninfo default_var = {
+ /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
+ 640, 480, 640, 480, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
+ 0, FB_VMODE_NONINTERLACED
+};
+
static int atyfb_release(struct fb_info *info, int user)
{
#ifdef __sparc__
struct fb_info_aty *fb = (struct fb_info_aty *)info;
if (user) {
- if (fb->vtconsole != -1)
- vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
- fb->open = 0;
- fb->mmaped = 0;
- fb->vtconsole = -1;
+ fb->open--;
+ udelay(1000);
+ wait_for_idle(fb);
+ if (!fb->open) {
+ int was_mmaped = fb->mmaped;
+
+ fb->mmaped = 0;
+ if (fb->vtconsole != -1)
+ vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
+ fb->vtconsole = -1;
+
+ if (was_mmaped) {
+ struct fb_var_screeninfo var;
+
+ /* Now reset the default display config, we have no
+ * idea what the program(s) which mmap'd the chip did
+ * to the configuration, nor whether it restored it
+ * correctly.
+ */
+ var = default_var;
+ if (noaccel)
+ var.accel_flags &= ~FB_ACCELF_TEXT;
+ else
+ var.accel_flags |= FB_ACCELF_TEXT;
+ if (var.yres == var.yres_virtual) {
+ u32 vram = (fb->total_vram - (PAGE_SIZE << 2));
+ var.yres_virtual = ((vram * 8) / var.bits_per_pixel) /
+ var.xres_virtual;
+ if (var.yres_virtual < var.yres)
+ var.yres_virtual = var.yres;
+ }
+ atyfb_set_var(&var, -1, &fb->fb_info);
+ }
+ }
} else {
fb->consolecnt--;
}
@@ -2708,15 +2740,6 @@ static int encode_fix(struct fb_fix_screeninfo *fix,
}
-struct fb_var_screeninfo default_var = {
- /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
- 640, 480, 640, 480, 0, 0, 8, 0,
- {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
- 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
- 0, FB_VMODE_NONINTERLACED
-};
-
-
/*
* Get the Fixed Part of the Display
*/
@@ -3614,13 +3637,10 @@ static int __init aty_init(struct fb_info_aty *info, const char *name)
int __init atyfb_init(void)
{
-#if defined(CONFIG_FB_OF)
- /* We don't want to be called like this. */
- /* We rely on Open Firmware (offb) instead. */
-#elif defined(CONFIG_PCI)
- struct pci_dev *pdev;
+#if defined(CONFIG_PCI)
+ struct pci_dev *pdev = NULL;
struct fb_info_aty *info;
- unsigned long addr;
+ unsigned long addr, res_start, res_size;
#ifdef __sparc__
extern void (*prom_palette) (int);
extern int con_is_present(void);
@@ -3637,9 +3657,8 @@ int __init atyfb_init(void)
u16 tmp;
#endif
- for (pdev = pci_devices; pdev; pdev = pdev->next) {
- if (((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) &&
- (pdev->vendor == PCI_VENDOR_ID_ATI)) {
+ while ((pdev = pci_find_device(PCI_VENDOR_ID_ATI, PCI_ANY_ID, pdev))) {
+ if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
struct resource *rp;
info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC);
@@ -3656,6 +3675,11 @@ int __init atyfb_init(void)
if (!addr)
continue;
+ res_start = rp->start;
+ res_size = rp->end-rp->start+1;
+ if (!request_mem_region(res_start, res_size, "atyfb"))
+ continue;
+
#ifdef __sparc__
/*
* Map memory-mapped registers.
@@ -3681,6 +3705,7 @@ int __init atyfb_init(void)
if (!info->mmap_map) {
printk("atyfb_init: can't alloc mmap_map\n");
kfree(info);
+ release_mem_region(res_start, res_size);
return -ENXIO;
}
memset(info->mmap_map, 0, j * sizeof(*info->mmap_map));
@@ -3860,6 +3885,7 @@ int __init atyfb_init(void)
if(!info->ati_regbase) {
kfree(info);
+ release_mem_region(res_start, res_size);
return -ENOMEM;
}
@@ -3887,6 +3913,7 @@ int __init atyfb_init(void)
if(!info->frame_buffer) {
kfree(info);
+ release_mem_region(res_start, res_size);
return -ENXIO;
}
@@ -3896,6 +3923,7 @@ int __init atyfb_init(void)
if (info->mmap_map)
kfree(info->mmap_map);
kfree(info);
+ release_mem_region(res_start, res_size);
return -ENXIO;
}
@@ -3917,6 +3945,18 @@ int __init atyfb_init(void)
info->mmap_map[1].prot_mask = _PAGE_CACHE;
info->mmap_map[1].prot_flag = _PAGE_E;
#endif /* __sparc__ */
+
+#ifdef CONFIG_PMAC_PBOOK
+ if (first_display == NULL)
+ pmu_register_sleep_notifier(&aty_sleep_notifier);
+ info->next = first_display;
+ first_display = info;
+#endif
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+ if (!console_fb_info)
+ console_fb_info = &info->fb_info;
+#endif /* CONFIG_FB_COMPAT_XPMAC */
}
}
@@ -3977,106 +4017,6 @@ int __init atyfb_init(void)
return 0;
}
-#ifdef CONFIG_FB_OF
-void __init atyfb_of_init(struct device_node *dp)
-{
- unsigned long addr;
- u8 bus, devfn;
- u16 cmd;
- struct fb_info_aty *info;
- int i;
-
- if (device_is_compatible(dp, "ATY,264LTPro")) {
- /* XXX kludge for now */
- if (dp->name == 0 || strcmp(dp->name, "ATY,264LTProA") != 0
- || dp->parent == 0)
- return;
- dp = dp->parent;
- }
- switch (dp->n_addrs) {
- case 1:
- case 2:
- case 3:
- addr = dp->addrs[0].address;
- break;
- case 4:
- addr = dp->addrs[1].address;
- break;
- default:
- printk("Warning: got %d adresses for ATY:\n", dp->n_addrs);
- for (i = 0; i < dp->n_addrs; i++)
- printk(" %08x-%08x", dp->addrs[i].address,
- dp->addrs[i].address+dp->addrs[i].size-1);
- if (dp->n_addrs)
- printk("\n");
- return;
- }
-
- info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC);
- if (!info) {
- printk("atyfb_of_init: can't alloc fb_info_aty\n");
- return;
- }
- memset(info, 0, sizeof(struct fb_info_aty));
-
- info->ati_regbase_phys = 0x7ff000+addr;
- info->ati_regbase = (unsigned long)ioremap(info->ati_regbase_phys,
- 0x1000);
-
- if(! info->ati_regbase) {
- printk("atyfb_of_init: ioremap() returned NULL\n");
- kfree(info);
- return;
- }
-
- info->ati_regbase_phys += 0xc00;
- info->ati_regbase += 0xc00;
-
- /* enable memory-space accesses using config-space command register */
- if (pci_device_loc(dp, &bus, &devfn) == 0) {
- pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd);
- if (cmd != 0xffff) {
- cmd |= PCI_COMMAND_MEMORY;
- pcibios_write_config_word(bus, devfn, PCI_COMMAND, cmd);
- }
- }
-
-#ifdef __BIG_ENDIAN
- /* Use the big-endian aperture */
- addr += 0x800000;
-#endif
-
- /* Map in frame buffer */
- info->frame_buffer_phys = addr;
- info->frame_buffer = (unsigned long)ioremap(addr, 0x800000);
-
- if(! info->frame_buffer) {
- printk("atyfb_of_init: ioremap() returned NULL\n");
- kfree(info);
- return;
- }
-
- if (!aty_init(info, dp->full_name)) {
- kfree(info);
- return;
- }
-
-#ifdef CONFIG_PMAC_PBOOK
- if (first_display == NULL)
- pmu_register_sleep_notifier(&aty_sleep_notifier);
- info->next = first_display;
- first_display = info;
-#endif
-
-
-#ifdef CONFIG_FB_COMPAT_XPMAC
- if (!console_fb_info)
- console_fb_info = &info->fb_info;
-#endif /* CONFIG_FB_COMPAT_XPMAC */
-}
-#endif /* CONFIG_FB_OF */
-
-
#ifndef MODULE
int __init atyfb_setup(char *options)
{
@@ -4865,6 +4805,98 @@ aty_sleep_notify(struct pmu_sleep_notifier *self, int when)
}
#endif /* CONFIG_PMAC_PBOOK */
+#ifdef CONFIG_PMAC_PBOOK
+/*
+ * Save the contents of the frame buffer when we go to sleep,
+ * and restore it when we wake up again.
+ */
+int
+aty_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+ struct fb_info_aty *info;
+ unsigned int pm;
+
+ for (info = first_display; info != NULL; info = info->next) {
+ struct fb_fix_screeninfo fix;
+ int nb;
+
+ atyfb_get_fix(&fix, fg_console, (struct fb_info *)info);
+ nb = fb_display[fg_console].var.yres * fix.line_length;
+
+ switch (when) {
+ case PBOOK_SLEEP_NOW:
+ /* Stop accel engine (stop bus mastering) */
+ if (info->current_par.accel_flags & FB_ACCELF_TEXT)
+ reset_engine(info);
+#if 1
+ /* Backup fb content */
+ info->save_framebuffer = vmalloc(nb);
+ if (info->save_framebuffer)
+ memcpy(info->save_framebuffer,
+ (void *)info->frame_buffer, nb);
+#endif
+ /* Blank display and LCD */
+ atyfbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info);
+
+ /* Set chip to "suspend" mode. Note: There's an HW bug in the
+ chip which prevents proper resync on wakeup with automatic
+ power management, we handle suspend manually using the
+ following (weird) sequence described by ATI. Note2:
+ We could enable this for all Rage LT Pro chip ids */
+ if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID) || (Gx == LP_CHIP_ID)) {
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ pm &= ~PWR_MGT_ON;
+ aty_st_le32(POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ pm &= ~(PWR_BLON | AUTO_PWR_UP);
+ pm |= SUSPEND_NOW;
+ aty_st_le32(POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ pm |= PWR_MGT_ON;
+ aty_st_le32(POWER_MANAGEMENT, pm, info);
+ do {
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND);
+ mdelay(500);
+ }
+ break;
+ case PBOOK_WAKE:
+ /* Wakeup chip */
+ if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID) || (Gx == LP_CHIP_ID)) {
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ pm &= ~PWR_MGT_ON;
+ aty_st_le32(POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ pm |= (PWR_BLON | AUTO_PWR_UP);
+ pm &= ~SUSPEND_NOW;
+ aty_st_le32(POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ pm |= PWR_MGT_ON;
+ aty_st_le32(POWER_MANAGEMENT, pm, info);
+ do {
+ pm = aty_ld_le32(POWER_MANAGEMENT, info);
+ } while ((pm & PWR_MGT_STATUS_MASK) != 0);
+ mdelay(500);
+ }
+#if 1
+ /* Restore fb content */
+ if (info->save_framebuffer) {
+ memcpy((void *)info->frame_buffer,
+ info->save_framebuffer, nb);
+ vfree(info->save_framebuffer);
+ info->save_framebuffer = 0;
+ }
+#endif
+ /* Restore display */
+ atyfb_set_par(&info->current_par, info);
+ atyfbcon_blank(0, (struct fb_info *)info);
+ break;
+ }
+ }
+ return PBOOK_SLEEP_OK;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
#ifdef MODULE
int __init init_module(void)
{