diff options
Diffstat (limited to 'drivers/video/atyfb.c')
-rw-r--r-- | drivers/video/atyfb.c | 294 |
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) { |