162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* $Id: aty128fb.c,v 1.1.1.1.36.1 1999/12/11 09:03:05 Exp $ 362306a36Sopenharmony_ci * linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1999-2003, Brad Douglas <brad@neruo.com> 662306a36Sopenharmony_ci * Copyright (C) 1999, Anthony Tong <atong@uiuc.edu> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Ani Joshi / Jeff Garzik 962306a36Sopenharmony_ci * - Code cleanup 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Michel Danzer <michdaen@iiic.ethz.ch> 1262306a36Sopenharmony_ci * - 15/16 bit cleanup 1362306a36Sopenharmony_ci * - fix panning 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Benjamin Herrenschmidt 1662306a36Sopenharmony_ci * - pmac-specific PM stuff 1762306a36Sopenharmony_ci * - various fixes & cleanups 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Andreas Hundt <andi@convergence.de> 2062306a36Sopenharmony_ci * - FB_ACTIVATE fixes 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Paul Mackerras <paulus@samba.org> 2362306a36Sopenharmony_ci * - Convert to new framebuffer API, 2462306a36Sopenharmony_ci * fix colormap setting at 16 bits/pixel (565) 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Paul Mundt 2762306a36Sopenharmony_ci * - PCI hotplug 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Jon Smirl <jonsmirl@yahoo.com> 3062306a36Sopenharmony_ci * - PCI ID update 3162306a36Sopenharmony_ci * - replace ROM BIOS search 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Based off of Geert's atyfb.c and vfb.c. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * TODO: 3662306a36Sopenharmony_ci * - monitor sensing (DDC) 3762306a36Sopenharmony_ci * - virtual display 3862306a36Sopenharmony_ci * - other platform support (only ppc/x86 supported) 3962306a36Sopenharmony_ci * - hardware cursor support 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Please cc: your patches to brad@neruo.com. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * A special note of gratitude to ATI's devrel for providing documentation, 4662306a36Sopenharmony_ci * example code and hardware. Thanks Nitya. -atong and brad 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include <linux/aperture.h> 5162306a36Sopenharmony_ci#include <linux/module.h> 5262306a36Sopenharmony_ci#include <linux/moduleparam.h> 5362306a36Sopenharmony_ci#include <linux/kernel.h> 5462306a36Sopenharmony_ci#include <linux/errno.h> 5562306a36Sopenharmony_ci#include <linux/string.h> 5662306a36Sopenharmony_ci#include <linux/mm.h> 5762306a36Sopenharmony_ci#include <linux/vmalloc.h> 5862306a36Sopenharmony_ci#include <linux/delay.h> 5962306a36Sopenharmony_ci#include <linux/interrupt.h> 6062306a36Sopenharmony_ci#include <linux/uaccess.h> 6162306a36Sopenharmony_ci#include <linux/fb.h> 6262306a36Sopenharmony_ci#include <linux/init.h> 6362306a36Sopenharmony_ci#include <linux/pci.h> 6462306a36Sopenharmony_ci#include <linux/ioport.h> 6562306a36Sopenharmony_ci#include <linux/console.h> 6662306a36Sopenharmony_ci#include <linux/backlight.h> 6762306a36Sopenharmony_ci#include <asm/io.h> 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 7062306a36Sopenharmony_ci#include <asm/machdep.h> 7162306a36Sopenharmony_ci#include <asm/pmac_feature.h> 7262306a36Sopenharmony_ci#include "../macmodes.h" 7362306a36Sopenharmony_ci#endif 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 7662306a36Sopenharmony_ci#include <asm/backlight.h> 7762306a36Sopenharmony_ci#endif 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 8062306a36Sopenharmony_ci#include <asm/btext.h> 8162306a36Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#include <video/aty128.h> 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Debug flag */ 8662306a36Sopenharmony_ci#undef DEBUG 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#ifdef DEBUG 8962306a36Sopenharmony_ci#define DBG(fmt, args...) \ 9062306a36Sopenharmony_ci printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args); 9162306a36Sopenharmony_ci#else 9262306a36Sopenharmony_ci#define DBG(fmt, args...) 9362306a36Sopenharmony_ci#endif 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 9662306a36Sopenharmony_ci/* default mode */ 9762306a36Sopenharmony_cistatic const struct fb_var_screeninfo default_var = { 9862306a36Sopenharmony_ci /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ 9962306a36Sopenharmony_ci 640, 480, 640, 480, 0, 0, 8, 0, 10062306a36Sopenharmony_ci {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 10162306a36Sopenharmony_ci 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2, 10262306a36Sopenharmony_ci 0, FB_VMODE_NONINTERLACED 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#else /* CONFIG_PPC_PMAC */ 10662306a36Sopenharmony_ci/* default to 1024x768 at 75Hz on PPC - this will work 10762306a36Sopenharmony_ci * on the iMac, the usual 640x480 @ 60Hz doesn't. */ 10862306a36Sopenharmony_cistatic const struct fb_var_screeninfo default_var = { 10962306a36Sopenharmony_ci /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ 11062306a36Sopenharmony_ci 1024, 768, 1024, 768, 0, 0, 8, 0, 11162306a36Sopenharmony_ci {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 11262306a36Sopenharmony_ci 0, 0, -1, -1, 0, 12699, 160, 32, 28, 1, 96, 3, 11362306a36Sopenharmony_ci FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 11462306a36Sopenharmony_ci FB_VMODE_NONINTERLACED 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* default modedb mode */ 11962306a36Sopenharmony_ci/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */ 12062306a36Sopenharmony_cistatic const struct fb_videomode defaultmode = { 12162306a36Sopenharmony_ci .refresh = 60, 12262306a36Sopenharmony_ci .xres = 640, 12362306a36Sopenharmony_ci .yres = 480, 12462306a36Sopenharmony_ci .pixclock = 39722, 12562306a36Sopenharmony_ci .left_margin = 48, 12662306a36Sopenharmony_ci .right_margin = 16, 12762306a36Sopenharmony_ci .upper_margin = 33, 12862306a36Sopenharmony_ci .lower_margin = 10, 12962306a36Sopenharmony_ci .hsync_len = 96, 13062306a36Sopenharmony_ci .vsync_len = 2, 13162306a36Sopenharmony_ci .sync = 0, 13262306a36Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Chip generations */ 13662306a36Sopenharmony_cienum { 13762306a36Sopenharmony_ci rage_128, 13862306a36Sopenharmony_ci rage_128_pci, 13962306a36Sopenharmony_ci rage_128_pro, 14062306a36Sopenharmony_ci rage_128_pro_pci, 14162306a36Sopenharmony_ci rage_M3, 14262306a36Sopenharmony_ci rage_M3_pci, 14362306a36Sopenharmony_ci rage_M4, 14462306a36Sopenharmony_ci rage_128_ultra, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* Must match above enum */ 14862306a36Sopenharmony_cistatic char * const r128_family[] = { 14962306a36Sopenharmony_ci "AGP", 15062306a36Sopenharmony_ci "PCI", 15162306a36Sopenharmony_ci "PRO AGP", 15262306a36Sopenharmony_ci "PRO PCI", 15362306a36Sopenharmony_ci "M3 AGP", 15462306a36Sopenharmony_ci "M3 PCI", 15562306a36Sopenharmony_ci "M4 AGP", 15662306a36Sopenharmony_ci "Ultra AGP", 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * PCI driver prototypes 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic int aty128_probe(struct pci_dev *pdev, 16362306a36Sopenharmony_ci const struct pci_device_id *ent); 16462306a36Sopenharmony_cistatic void aty128_remove(struct pci_dev *pdev); 16562306a36Sopenharmony_cistatic int aty128_pci_suspend_late(struct device *dev, pm_message_t state); 16662306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_suspend(struct device *dev); 16762306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_hibernate(struct device *dev); 16862306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_freeze(struct device *dev); 16962306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_resume(struct device *dev); 17062306a36Sopenharmony_cistatic int aty128_do_resume(struct pci_dev *pdev); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic const struct dev_pm_ops aty128_pci_pm_ops = { 17362306a36Sopenharmony_ci .suspend = aty128_pci_suspend, 17462306a36Sopenharmony_ci .resume = aty128_pci_resume, 17562306a36Sopenharmony_ci .freeze = aty128_pci_freeze, 17662306a36Sopenharmony_ci .thaw = aty128_pci_resume, 17762306a36Sopenharmony_ci .poweroff = aty128_pci_hibernate, 17862306a36Sopenharmony_ci .restore = aty128_pci_resume, 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* supported Rage128 chipsets */ 18262306a36Sopenharmony_cistatic const struct pci_device_id aty128_pci_tbl[] = { 18362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE, 18462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci }, 18562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF, 18662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 }, 18762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF, 18862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, 18962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML, 19062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, 19162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA, 19262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 19362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB, 19462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 19562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC, 19662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 19762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD, 19862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, 19962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE, 20062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 20162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF, 20262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 20362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG, 20462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 20562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH, 20662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 20762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI, 20862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 20962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ, 21062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 21162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK, 21262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 21362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL, 21462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 21562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM, 21662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 21762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN, 21862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 21962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO, 22062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 22162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP, 22262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, 22362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ, 22462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 22562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR, 22662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, 22762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS, 22862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 22962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT, 23062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 23162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU, 23262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 23362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV, 23462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 23562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW, 23662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 23762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX, 23862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 23962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE, 24062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, 24162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF, 24262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 24362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG, 24462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 24562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK, 24662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, 24762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL, 24862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 24962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE, 25062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 25162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF, 25262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, 25362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG, 25462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 25562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH, 25662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 25762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK, 25862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 25962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL, 26062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 26162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM, 26262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 26362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN, 26462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 26562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF, 26662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 26762306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL, 26862306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 26962306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR, 27062306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 27162306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS, 27262306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 27362306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT, 27462306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 27562306a36Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU, 27662306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 27762306a36Sopenharmony_ci { 0, } 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, aty128_pci_tbl); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic struct pci_driver aty128fb_driver = { 28362306a36Sopenharmony_ci .name = "aty128fb", 28462306a36Sopenharmony_ci .id_table = aty128_pci_tbl, 28562306a36Sopenharmony_ci .probe = aty128_probe, 28662306a36Sopenharmony_ci .remove = aty128_remove, 28762306a36Sopenharmony_ci .driver.pm = &aty128_pci_pm_ops, 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* packed BIOS settings */ 29162306a36Sopenharmony_ci#ifndef CONFIG_PPC 29262306a36Sopenharmony_citypedef struct { 29362306a36Sopenharmony_ci u8 clock_chip_type; 29462306a36Sopenharmony_ci u8 struct_size; 29562306a36Sopenharmony_ci u8 accelerator_entry; 29662306a36Sopenharmony_ci u8 VGA_entry; 29762306a36Sopenharmony_ci u16 VGA_table_offset; 29862306a36Sopenharmony_ci u16 POST_table_offset; 29962306a36Sopenharmony_ci u16 XCLK; 30062306a36Sopenharmony_ci u16 MCLK; 30162306a36Sopenharmony_ci u8 num_PLL_blocks; 30262306a36Sopenharmony_ci u8 size_PLL_blocks; 30362306a36Sopenharmony_ci u16 PCLK_ref_freq; 30462306a36Sopenharmony_ci u16 PCLK_ref_divider; 30562306a36Sopenharmony_ci u32 PCLK_min_freq; 30662306a36Sopenharmony_ci u32 PCLK_max_freq; 30762306a36Sopenharmony_ci u16 MCLK_ref_freq; 30862306a36Sopenharmony_ci u16 MCLK_ref_divider; 30962306a36Sopenharmony_ci u32 MCLK_min_freq; 31062306a36Sopenharmony_ci u32 MCLK_max_freq; 31162306a36Sopenharmony_ci u16 XCLK_ref_freq; 31262306a36Sopenharmony_ci u16 XCLK_ref_divider; 31362306a36Sopenharmony_ci u32 XCLK_min_freq; 31462306a36Sopenharmony_ci u32 XCLK_max_freq; 31562306a36Sopenharmony_ci} __attribute__ ((packed)) PLL_BLOCK; 31662306a36Sopenharmony_ci#endif /* !CONFIG_PPC */ 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* onboard memory information */ 31962306a36Sopenharmony_cistruct aty128_meminfo { 32062306a36Sopenharmony_ci u8 ML; 32162306a36Sopenharmony_ci u8 MB; 32262306a36Sopenharmony_ci u8 Trcd; 32362306a36Sopenharmony_ci u8 Trp; 32462306a36Sopenharmony_ci u8 Twr; 32562306a36Sopenharmony_ci u8 CL; 32662306a36Sopenharmony_ci u8 Tr2w; 32762306a36Sopenharmony_ci u8 LoopLatency; 32862306a36Sopenharmony_ci u8 DspOn; 32962306a36Sopenharmony_ci u8 Rloop; 33062306a36Sopenharmony_ci const char *name; 33162306a36Sopenharmony_ci}; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* various memory configurations */ 33462306a36Sopenharmony_cistatic const struct aty128_meminfo sdr_128 = { 33562306a36Sopenharmony_ci .ML = 4, 33662306a36Sopenharmony_ci .MB = 4, 33762306a36Sopenharmony_ci .Trcd = 3, 33862306a36Sopenharmony_ci .Trp = 3, 33962306a36Sopenharmony_ci .Twr = 1, 34062306a36Sopenharmony_ci .CL = 3, 34162306a36Sopenharmony_ci .Tr2w = 1, 34262306a36Sopenharmony_ci .LoopLatency = 16, 34362306a36Sopenharmony_ci .DspOn = 30, 34462306a36Sopenharmony_ci .Rloop = 16, 34562306a36Sopenharmony_ci .name = "128-bit SDR SGRAM (1:1)", 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic const struct aty128_meminfo sdr_sgram = { 34962306a36Sopenharmony_ci .ML = 4, 35062306a36Sopenharmony_ci .MB = 4, 35162306a36Sopenharmony_ci .Trcd = 1, 35262306a36Sopenharmony_ci .Trp = 2, 35362306a36Sopenharmony_ci .Twr = 1, 35462306a36Sopenharmony_ci .CL = 2, 35562306a36Sopenharmony_ci .Tr2w = 1, 35662306a36Sopenharmony_ci .LoopLatency = 16, 35762306a36Sopenharmony_ci .DspOn = 24, 35862306a36Sopenharmony_ci .Rloop = 16, 35962306a36Sopenharmony_ci .name = "64-bit SDR SGRAM (2:1)", 36062306a36Sopenharmony_ci}; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic const struct aty128_meminfo ddr_sgram = { 36362306a36Sopenharmony_ci .ML = 4, 36462306a36Sopenharmony_ci .MB = 4, 36562306a36Sopenharmony_ci .Trcd = 3, 36662306a36Sopenharmony_ci .Trp = 3, 36762306a36Sopenharmony_ci .Twr = 2, 36862306a36Sopenharmony_ci .CL = 3, 36962306a36Sopenharmony_ci .Tr2w = 1, 37062306a36Sopenharmony_ci .LoopLatency = 16, 37162306a36Sopenharmony_ci .DspOn = 31, 37262306a36Sopenharmony_ci .Rloop = 16, 37362306a36Sopenharmony_ci .name = "64-bit DDR SGRAM", 37462306a36Sopenharmony_ci}; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic const struct fb_fix_screeninfo aty128fb_fix = { 37762306a36Sopenharmony_ci .id = "ATY Rage128", 37862306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 37962306a36Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 38062306a36Sopenharmony_ci .xpanstep = 8, 38162306a36Sopenharmony_ci .ypanstep = 1, 38262306a36Sopenharmony_ci .mmio_len = 0x2000, 38362306a36Sopenharmony_ci .accel = FB_ACCEL_ATI_RAGE128, 38462306a36Sopenharmony_ci}; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic char *mode_option = NULL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 38962306a36Sopenharmony_cistatic int default_vmode = VMODE_1024_768_60; 39062306a36Sopenharmony_cistatic int default_cmode = CMODE_8; 39162306a36Sopenharmony_ci#endif 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int default_crt_on = 0; 39462306a36Sopenharmony_cistatic int default_lcd_on = 1; 39562306a36Sopenharmony_cistatic bool mtrr = true; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 39862306a36Sopenharmony_cistatic int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT); 39962306a36Sopenharmony_ci#endif 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* PLL constants */ 40262306a36Sopenharmony_cistruct aty128_constants { 40362306a36Sopenharmony_ci u32 ref_clk; 40462306a36Sopenharmony_ci u32 ppll_min; 40562306a36Sopenharmony_ci u32 ppll_max; 40662306a36Sopenharmony_ci u32 ref_divider; 40762306a36Sopenharmony_ci u32 xclk; 40862306a36Sopenharmony_ci u32 fifo_width; 40962306a36Sopenharmony_ci u32 fifo_depth; 41062306a36Sopenharmony_ci}; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistruct aty128_crtc { 41362306a36Sopenharmony_ci u32 gen_cntl; 41462306a36Sopenharmony_ci u32 h_total, h_sync_strt_wid; 41562306a36Sopenharmony_ci u32 v_total, v_sync_strt_wid; 41662306a36Sopenharmony_ci u32 pitch; 41762306a36Sopenharmony_ci u32 offset, offset_cntl; 41862306a36Sopenharmony_ci u32 xoffset, yoffset; 41962306a36Sopenharmony_ci u32 vxres, vyres; 42062306a36Sopenharmony_ci u32 depth, bpp; 42162306a36Sopenharmony_ci}; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistruct aty128_pll { 42462306a36Sopenharmony_ci u32 post_divider; 42562306a36Sopenharmony_ci u32 feedback_divider; 42662306a36Sopenharmony_ci u32 vclk; 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistruct aty128_ddafifo { 43062306a36Sopenharmony_ci u32 dda_config; 43162306a36Sopenharmony_ci u32 dda_on_off; 43262306a36Sopenharmony_ci}; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* register values for a specific mode */ 43562306a36Sopenharmony_cistruct aty128fb_par { 43662306a36Sopenharmony_ci struct aty128_crtc crtc; 43762306a36Sopenharmony_ci struct aty128_pll pll; 43862306a36Sopenharmony_ci struct aty128_ddafifo fifo_reg; 43962306a36Sopenharmony_ci u32 accel_flags; 44062306a36Sopenharmony_ci struct aty128_constants constants; /* PLL and others */ 44162306a36Sopenharmony_ci void __iomem *regbase; /* remapped mmio */ 44262306a36Sopenharmony_ci u32 vram_size; /* onboard video ram */ 44362306a36Sopenharmony_ci int chip_gen; 44462306a36Sopenharmony_ci const struct aty128_meminfo *mem; /* onboard mem info */ 44562306a36Sopenharmony_ci int wc_cookie; 44662306a36Sopenharmony_ci int blitter_may_be_busy; 44762306a36Sopenharmony_ci int fifo_slots; /* free slots in FIFO (64 max) */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci int crt_on, lcd_on; 45062306a36Sopenharmony_ci struct pci_dev *pdev; 45162306a36Sopenharmony_ci struct fb_info *next; 45262306a36Sopenharmony_ci int asleep; 45362306a36Sopenharmony_ci int lock_blank; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci u8 red[32]; /* see aty128fb_setcolreg */ 45662306a36Sopenharmony_ci u8 green[64]; 45762306a36Sopenharmony_ci u8 blue[32]; 45862306a36Sopenharmony_ci u32 pseudo_palette[16]; /* used for TRUECOLOR */ 45962306a36Sopenharmony_ci}; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci#define round_div(n, d) ((n+(d/2))/d) 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int aty128fb_check_var(struct fb_var_screeninfo *var, 46562306a36Sopenharmony_ci struct fb_info *info); 46662306a36Sopenharmony_cistatic int aty128fb_set_par(struct fb_info *info); 46762306a36Sopenharmony_cistatic int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 46862306a36Sopenharmony_ci u_int transp, struct fb_info *info); 46962306a36Sopenharmony_cistatic int aty128fb_pan_display(struct fb_var_screeninfo *var, 47062306a36Sopenharmony_ci struct fb_info *fb); 47162306a36Sopenharmony_cistatic int aty128fb_blank(int blank, struct fb_info *fb); 47262306a36Sopenharmony_cistatic int aty128fb_ioctl(struct fb_info *info, u_int cmd, unsigned long arg); 47362306a36Sopenharmony_cistatic int aty128fb_sync(struct fb_info *info); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Internal routines 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic int aty128_encode_var(struct fb_var_screeninfo *var, 48062306a36Sopenharmony_ci const struct aty128fb_par *par); 48162306a36Sopenharmony_cistatic int aty128_decode_var(struct fb_var_screeninfo *var, 48262306a36Sopenharmony_ci struct aty128fb_par *par); 48362306a36Sopenharmony_cistatic void aty128_timings(struct aty128fb_par *par); 48462306a36Sopenharmony_cistatic void aty128_init_engine(struct aty128fb_par *par); 48562306a36Sopenharmony_cistatic void aty128_reset_engine(const struct aty128fb_par *par); 48662306a36Sopenharmony_cistatic void aty128_flush_pixel_cache(const struct aty128fb_par *par); 48762306a36Sopenharmony_cistatic void do_wait_for_fifo(u16 entries, struct aty128fb_par *par); 48862306a36Sopenharmony_cistatic void wait_for_fifo(u16 entries, struct aty128fb_par *par); 48962306a36Sopenharmony_cistatic void wait_for_idle(struct aty128fb_par *par); 49062306a36Sopenharmony_cistatic u32 depth_to_dst(u32 depth); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 49362306a36Sopenharmony_cistatic void aty128_bl_set_power(struct fb_info *info, int power); 49462306a36Sopenharmony_ci#endif 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci#define BIOS_IN8(v) (readb(bios + (v))) 49762306a36Sopenharmony_ci#define BIOS_IN16(v) (readb(bios + (v)) | \ 49862306a36Sopenharmony_ci (readb(bios + (v) + 1) << 8)) 49962306a36Sopenharmony_ci#define BIOS_IN32(v) (readb(bios + (v)) | \ 50062306a36Sopenharmony_ci (readb(bios + (v) + 1) << 8) | \ 50162306a36Sopenharmony_ci (readb(bios + (v) + 2) << 16) | \ 50262306a36Sopenharmony_ci (readb(bios + (v) + 3) << 24)) 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic const struct fb_ops aty128fb_ops = { 50662306a36Sopenharmony_ci .owner = THIS_MODULE, 50762306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 50862306a36Sopenharmony_ci .fb_check_var = aty128fb_check_var, 50962306a36Sopenharmony_ci .fb_set_par = aty128fb_set_par, 51062306a36Sopenharmony_ci .fb_setcolreg = aty128fb_setcolreg, 51162306a36Sopenharmony_ci .fb_pan_display = aty128fb_pan_display, 51262306a36Sopenharmony_ci .fb_blank = aty128fb_blank, 51362306a36Sopenharmony_ci .fb_ioctl = aty128fb_ioctl, 51462306a36Sopenharmony_ci .fb_sync = aty128fb_sync, 51562306a36Sopenharmony_ci}; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* 51862306a36Sopenharmony_ci * Functions to read from/write to the mmio registers 51962306a36Sopenharmony_ci * - endian conversions may possibly be avoided by 52062306a36Sopenharmony_ci * using the other register aperture. TODO. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_cistatic inline u32 _aty_ld_le32(volatile unsigned int regindex, 52362306a36Sopenharmony_ci const struct aty128fb_par *par) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci return readl (par->regbase + regindex); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic inline void _aty_st_le32(volatile unsigned int regindex, u32 val, 52962306a36Sopenharmony_ci const struct aty128fb_par *par) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci writel (val, par->regbase + regindex); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic inline u8 _aty_ld_8(unsigned int regindex, 53562306a36Sopenharmony_ci const struct aty128fb_par *par) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci return readb (par->regbase + regindex); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic inline void _aty_st_8(unsigned int regindex, u8 val, 54162306a36Sopenharmony_ci const struct aty128fb_par *par) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci writeb (val, par->regbase + regindex); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci#define aty_ld_le32(regindex) _aty_ld_le32(regindex, par) 54762306a36Sopenharmony_ci#define aty_st_le32(regindex, val) _aty_st_le32(regindex, val, par) 54862306a36Sopenharmony_ci#define aty_ld_8(regindex) _aty_ld_8(regindex, par) 54962306a36Sopenharmony_ci#define aty_st_8(regindex, val) _aty_st_8(regindex, val, par) 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * Functions to read from/write to the pll registers 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci#define aty_ld_pll(pll_index) _aty_ld_pll(pll_index, par) 55662306a36Sopenharmony_ci#define aty_st_pll(pll_index, val) _aty_st_pll(pll_index, val, par) 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic u32 _aty_ld_pll(unsigned int pll_index, 56062306a36Sopenharmony_ci const struct aty128fb_par *par) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F); 56362306a36Sopenharmony_ci return aty_ld_le32(CLOCK_CNTL_DATA); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic void _aty_st_pll(unsigned int pll_index, u32 val, 56862306a36Sopenharmony_ci const struct aty128fb_par *par) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN); 57162306a36Sopenharmony_ci aty_st_le32(CLOCK_CNTL_DATA, val); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci/* return true when the PLL has completed an atomic update */ 57662306a36Sopenharmony_cistatic int aty_pll_readupdate(const struct aty128fb_par *par) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void aty_pll_wait_readupdate(const struct aty128fb_par *par) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci unsigned long timeout = jiffies + HZ/100; // should be more than enough 58562306a36Sopenharmony_ci int reset = 1; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci while (time_before(jiffies, timeout)) 58862306a36Sopenharmony_ci if (aty_pll_readupdate(par)) { 58962306a36Sopenharmony_ci reset = 0; 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (reset) /* reset engine?? */ 59462306a36Sopenharmony_ci printk(KERN_DEBUG "aty128fb: PLL write timeout!\n"); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci/* tell PLL to update */ 59962306a36Sopenharmony_cistatic void aty_pll_writeupdate(const struct aty128fb_par *par) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci aty_pll_wait_readupdate(par); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci aty_st_pll(PPLL_REF_DIV, 60462306a36Sopenharmony_ci aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci/* write to the scratch register to test r/w functionality */ 60962306a36Sopenharmony_cistatic int register_test(const struct aty128fb_par *par) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci u32 val; 61262306a36Sopenharmony_ci int flag = 0; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci val = aty_ld_le32(BIOS_0_SCRATCH); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci aty_st_le32(BIOS_0_SCRATCH, 0x55555555); 61762306a36Sopenharmony_ci if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) { 61862306a36Sopenharmony_ci aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA) 62162306a36Sopenharmony_ci flag = 1; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci aty_st_le32(BIOS_0_SCRATCH, val); // restore value 62562306a36Sopenharmony_ci return flag; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* 63062306a36Sopenharmony_ci * Accelerator engine functions 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_cistatic void do_wait_for_fifo(u16 entries, struct aty128fb_par *par) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci int i; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci for (;;) { 63762306a36Sopenharmony_ci for (i = 0; i < 2000000; i++) { 63862306a36Sopenharmony_ci par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff; 63962306a36Sopenharmony_ci if (par->fifo_slots >= entries) 64062306a36Sopenharmony_ci return; 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci aty128_reset_engine(par); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void wait_for_idle(struct aty128fb_par *par) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci int i; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci do_wait_for_fifo(64, par); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for (;;) { 65462306a36Sopenharmony_ci for (i = 0; i < 2000000; i++) { 65562306a36Sopenharmony_ci if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) { 65662306a36Sopenharmony_ci aty128_flush_pixel_cache(par); 65762306a36Sopenharmony_ci par->blitter_may_be_busy = 0; 65862306a36Sopenharmony_ci return; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci aty128_reset_engine(par); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic void wait_for_fifo(u16 entries, struct aty128fb_par *par) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci if (par->fifo_slots < entries) 66962306a36Sopenharmony_ci do_wait_for_fifo(64, par); 67062306a36Sopenharmony_ci par->fifo_slots -= entries; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic void aty128_flush_pixel_cache(const struct aty128fb_par *par) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci int i; 67762306a36Sopenharmony_ci u32 tmp; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci tmp = aty_ld_le32(PC_NGUI_CTLSTAT); 68062306a36Sopenharmony_ci tmp &= ~(0x00ff); 68162306a36Sopenharmony_ci tmp |= 0x00ff; 68262306a36Sopenharmony_ci aty_st_le32(PC_NGUI_CTLSTAT, tmp); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci for (i = 0; i < 2000000; i++) 68562306a36Sopenharmony_ci if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY)) 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic void aty128_reset_engine(const struct aty128fb_par *par) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci u32 gen_reset_cntl, clock_cntl_index, mclk_cntl; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci aty128_flush_pixel_cache(par); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX); 69762306a36Sopenharmony_ci mclk_cntl = aty_ld_pll(MCLK_CNTL); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL); 70262306a36Sopenharmony_ci aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI); 70362306a36Sopenharmony_ci aty_ld_le32(GEN_RESET_CNTL); 70462306a36Sopenharmony_ci aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI)); 70562306a36Sopenharmony_ci aty_ld_le32(GEN_RESET_CNTL); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci aty_st_pll(MCLK_CNTL, mclk_cntl); 70862306a36Sopenharmony_ci aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index); 70962306a36Sopenharmony_ci aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* use old pio mode */ 71262306a36Sopenharmony_ci aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci DBG("engine reset"); 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic void aty128_init_engine(struct aty128fb_par *par) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci u32 pitch_value; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci wait_for_idle(par); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* 3D scaler not spoken here */ 72562306a36Sopenharmony_ci wait_for_fifo(1, par); 72662306a36Sopenharmony_ci aty_st_le32(SCALE_3D_CNTL, 0x00000000); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci aty128_reset_engine(par); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci pitch_value = par->crtc.pitch; 73162306a36Sopenharmony_ci if (par->crtc.bpp == 24) { 73262306a36Sopenharmony_ci pitch_value = pitch_value * 3; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci wait_for_fifo(4, par); 73662306a36Sopenharmony_ci /* setup engine offset registers */ 73762306a36Sopenharmony_ci aty_st_le32(DEFAULT_OFFSET, 0x00000000); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* setup engine pitch registers */ 74062306a36Sopenharmony_ci aty_st_le32(DEFAULT_PITCH, pitch_value); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* set the default scissor register to max dimensions */ 74362306a36Sopenharmony_ci aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* set the drawing controls registers */ 74662306a36Sopenharmony_ci aty_st_le32(DP_GUI_MASTER_CNTL, 74762306a36Sopenharmony_ci GMC_SRC_PITCH_OFFSET_DEFAULT | 74862306a36Sopenharmony_ci GMC_DST_PITCH_OFFSET_DEFAULT | 74962306a36Sopenharmony_ci GMC_SRC_CLIP_DEFAULT | 75062306a36Sopenharmony_ci GMC_DST_CLIP_DEFAULT | 75162306a36Sopenharmony_ci GMC_BRUSH_SOLIDCOLOR | 75262306a36Sopenharmony_ci (depth_to_dst(par->crtc.depth) << 8) | 75362306a36Sopenharmony_ci GMC_SRC_DSTCOLOR | 75462306a36Sopenharmony_ci GMC_BYTE_ORDER_MSB_TO_LSB | 75562306a36Sopenharmony_ci GMC_DP_CONVERSION_TEMP_6500 | 75662306a36Sopenharmony_ci ROP3_PATCOPY | 75762306a36Sopenharmony_ci GMC_DP_SRC_RECT | 75862306a36Sopenharmony_ci GMC_3D_FCN_EN_CLR | 75962306a36Sopenharmony_ci GMC_DST_CLR_CMP_FCN_CLEAR | 76062306a36Sopenharmony_ci GMC_AUX_CLIP_CLEAR | 76162306a36Sopenharmony_ci GMC_WRITE_MASK_SET); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci wait_for_fifo(8, par); 76462306a36Sopenharmony_ci /* clear the line drawing registers */ 76562306a36Sopenharmony_ci aty_st_le32(DST_BRES_ERR, 0); 76662306a36Sopenharmony_ci aty_st_le32(DST_BRES_INC, 0); 76762306a36Sopenharmony_ci aty_st_le32(DST_BRES_DEC, 0); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* set brush color registers */ 77062306a36Sopenharmony_ci aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */ 77162306a36Sopenharmony_ci aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */ 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* set source color registers */ 77462306a36Sopenharmony_ci aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF); /* white */ 77562306a36Sopenharmony_ci aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000); /* black */ 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* default write mask */ 77862306a36Sopenharmony_ci aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* Wait for all the writes to be completed before returning */ 78162306a36Sopenharmony_ci wait_for_idle(par); 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/* convert depth values to their register representation */ 78662306a36Sopenharmony_cistatic u32 depth_to_dst(u32 depth) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci if (depth <= 8) 78962306a36Sopenharmony_ci return DST_8BPP; 79062306a36Sopenharmony_ci else if (depth <= 15) 79162306a36Sopenharmony_ci return DST_15BPP; 79262306a36Sopenharmony_ci else if (depth == 16) 79362306a36Sopenharmony_ci return DST_16BPP; 79462306a36Sopenharmony_ci else if (depth <= 24) 79562306a36Sopenharmony_ci return DST_24BPP; 79662306a36Sopenharmony_ci else if (depth <= 32) 79762306a36Sopenharmony_ci return DST_32BPP; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return -EINVAL; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/* 80362306a36Sopenharmony_ci * PLL informations retreival 80462306a36Sopenharmony_ci */ 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci#ifndef __sparc__ 80862306a36Sopenharmony_cistatic void __iomem *aty128_map_ROM(const struct aty128fb_par *par, 80962306a36Sopenharmony_ci struct pci_dev *dev) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci u16 dptr; 81262306a36Sopenharmony_ci u8 rom_type; 81362306a36Sopenharmony_ci void __iomem *bios; 81462306a36Sopenharmony_ci size_t rom_size; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */ 81762306a36Sopenharmony_ci unsigned int temp; 81862306a36Sopenharmony_ci temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); 81962306a36Sopenharmony_ci temp &= 0x00ffffffu; 82062306a36Sopenharmony_ci temp |= 0x04 << 24; 82162306a36Sopenharmony_ci aty_st_le32(RAGE128_MPP_TB_CONFIG, temp); 82262306a36Sopenharmony_ci temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci bios = pci_map_rom(dev, &rom_size); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (!bios) { 82762306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: ROM failed to map\n"); 82862306a36Sopenharmony_ci return NULL; 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci /* Very simple test to make sure it appeared */ 83262306a36Sopenharmony_ci if (BIOS_IN16(0) != 0xaa55) { 83362306a36Sopenharmony_ci printk(KERN_DEBUG "aty128fb: Invalid ROM signature %x should " 83462306a36Sopenharmony_ci " be 0xaa55\n", BIOS_IN16(0)); 83562306a36Sopenharmony_ci goto failed; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Look for the PCI data to check the ROM type */ 83962306a36Sopenharmony_ci dptr = BIOS_IN16(0x18); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Check the PCI data signature. If it's wrong, we still assume a normal 84262306a36Sopenharmony_ci * x86 ROM for now, until I've verified this works everywhere. 84362306a36Sopenharmony_ci * The goal here is more to phase out Open Firmware images. 84462306a36Sopenharmony_ci * 84562306a36Sopenharmony_ci * Currently, we only look at the first PCI data, we could iteratre and 84662306a36Sopenharmony_ci * deal with them all, and we should use fb_bios_start relative to start 84762306a36Sopenharmony_ci * of image and not relative start of ROM, but so far, I never found a 84862306a36Sopenharmony_ci * dual-image ATI card. 84962306a36Sopenharmony_ci * 85062306a36Sopenharmony_ci * typedef struct { 85162306a36Sopenharmony_ci * u32 signature; + 0x00 85262306a36Sopenharmony_ci * u16 vendor; + 0x04 85362306a36Sopenharmony_ci * u16 device; + 0x06 85462306a36Sopenharmony_ci * u16 reserved_1; + 0x08 85562306a36Sopenharmony_ci * u16 dlen; + 0x0a 85662306a36Sopenharmony_ci * u8 drevision; + 0x0c 85762306a36Sopenharmony_ci * u8 class_hi; + 0x0d 85862306a36Sopenharmony_ci * u16 class_lo; + 0x0e 85962306a36Sopenharmony_ci * u16 ilen; + 0x10 86062306a36Sopenharmony_ci * u16 irevision; + 0x12 86162306a36Sopenharmony_ci * u8 type; + 0x14 86262306a36Sopenharmony_ci * u8 indicator; + 0x15 86362306a36Sopenharmony_ci * u16 reserved_2; + 0x16 86462306a36Sopenharmony_ci * } pci_data_t; 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ci if (BIOS_IN32(dptr) != (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) { 86762306a36Sopenharmony_ci printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n", 86862306a36Sopenharmony_ci BIOS_IN32(dptr)); 86962306a36Sopenharmony_ci goto anyway; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci rom_type = BIOS_IN8(dptr + 0x14); 87262306a36Sopenharmony_ci switch(rom_type) { 87362306a36Sopenharmony_ci case 0: 87462306a36Sopenharmony_ci printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n"); 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci case 1: 87762306a36Sopenharmony_ci printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n"); 87862306a36Sopenharmony_ci goto failed; 87962306a36Sopenharmony_ci case 2: 88062306a36Sopenharmony_ci printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n"); 88162306a36Sopenharmony_ci goto failed; 88262306a36Sopenharmony_ci default: 88362306a36Sopenharmony_ci printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", 88462306a36Sopenharmony_ci rom_type); 88562306a36Sopenharmony_ci goto failed; 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci anyway: 88862306a36Sopenharmony_ci return bios; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci failed: 89162306a36Sopenharmony_ci pci_unmap_rom(dev, bios); 89262306a36Sopenharmony_ci return NULL; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic void aty128_get_pllinfo(struct aty128fb_par *par, 89662306a36Sopenharmony_ci unsigned char __iomem *bios) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci unsigned int bios_hdr; 89962306a36Sopenharmony_ci unsigned int bios_pll; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci bios_hdr = BIOS_IN16(0x48); 90262306a36Sopenharmony_ci bios_pll = BIOS_IN16(bios_hdr + 0x30); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16); 90562306a36Sopenharmony_ci par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12); 90662306a36Sopenharmony_ci par->constants.xclk = BIOS_IN16(bios_pll + 0x08); 90762306a36Sopenharmony_ci par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10); 90862306a36Sopenharmony_ci par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n", 91162306a36Sopenharmony_ci par->constants.ppll_max, par->constants.ppll_min, 91262306a36Sopenharmony_ci par->constants.xclk, par->constants.ref_divider, 91362306a36Sopenharmony_ci par->constants.ref_clk); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci#ifdef CONFIG_X86 91862306a36Sopenharmony_cistatic void __iomem *aty128_find_mem_vbios(struct aty128fb_par *par) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci /* I simplified this code as we used to miss the signatures in 92162306a36Sopenharmony_ci * a lot of case. It's now closer to XFree, we just don't check 92262306a36Sopenharmony_ci * for signatures at all... Something better will have to be done 92362306a36Sopenharmony_ci * if we end up having conflicts 92462306a36Sopenharmony_ci */ 92562306a36Sopenharmony_ci u32 segstart; 92662306a36Sopenharmony_ci unsigned char __iomem *rom_base = NULL; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) { 92962306a36Sopenharmony_ci rom_base = ioremap(segstart, 0x10000); 93062306a36Sopenharmony_ci if (rom_base == NULL) 93162306a36Sopenharmony_ci return NULL; 93262306a36Sopenharmony_ci if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa) 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci iounmap(rom_base); 93562306a36Sopenharmony_ci rom_base = NULL; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci return rom_base; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci#endif 94062306a36Sopenharmony_ci#endif /* ndef(__sparc__) */ 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci/* fill in known card constants if pll_block is not available */ 94362306a36Sopenharmony_cistatic void aty128_timings(struct aty128fb_par *par) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci#ifdef CONFIG_PPC 94662306a36Sopenharmony_ci /* instead of a table lookup, assume OF has properly 94762306a36Sopenharmony_ci * setup the PLL registers and use their values 94862306a36Sopenharmony_ci * to set the XCLK values and reference divider values */ 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci u32 x_mpll_ref_fb_div; 95162306a36Sopenharmony_ci u32 xclk_cntl; 95262306a36Sopenharmony_ci u32 Nx, M; 95362306a36Sopenharmony_ci static const unsigned int PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 }; 95462306a36Sopenharmony_ci#endif 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (!par->constants.ref_clk) 95762306a36Sopenharmony_ci par->constants.ref_clk = 2950; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci#ifdef CONFIG_PPC 96062306a36Sopenharmony_ci x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV); 96162306a36Sopenharmony_ci xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7; 96262306a36Sopenharmony_ci Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8; 96362306a36Sopenharmony_ci M = x_mpll_ref_fb_div & 0x0000ff; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk), 96662306a36Sopenharmony_ci (M * PostDivSet[xclk_cntl])); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci par->constants.ref_divider = 96962306a36Sopenharmony_ci aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; 97062306a36Sopenharmony_ci#endif 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (!par->constants.ref_divider) { 97362306a36Sopenharmony_ci par->constants.ref_divider = 0x3b; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e); 97662306a36Sopenharmony_ci aty_pll_writeupdate(par); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider); 97962306a36Sopenharmony_ci aty_pll_writeupdate(par); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* from documentation */ 98262306a36Sopenharmony_ci if (!par->constants.ppll_min) 98362306a36Sopenharmony_ci par->constants.ppll_min = 12500; 98462306a36Sopenharmony_ci if (!par->constants.ppll_max) 98562306a36Sopenharmony_ci par->constants.ppll_max = 25000; /* 23000 on some cards? */ 98662306a36Sopenharmony_ci if (!par->constants.xclk) 98762306a36Sopenharmony_ci par->constants.xclk = 0x1d4d; /* same as mclk */ 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci par->constants.fifo_width = 128; 99062306a36Sopenharmony_ci par->constants.fifo_depth = 32; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci switch (aty_ld_le32(MEM_CNTL) & 0x3) { 99362306a36Sopenharmony_ci case 0: 99462306a36Sopenharmony_ci par->mem = &sdr_128; 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci case 1: 99762306a36Sopenharmony_ci par->mem = &sdr_sgram; 99862306a36Sopenharmony_ci break; 99962306a36Sopenharmony_ci case 2: 100062306a36Sopenharmony_ci par->mem = &ddr_sgram; 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci default: 100362306a36Sopenharmony_ci par->mem = &sdr_sgram; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci/* 101062306a36Sopenharmony_ci * CRTC programming 101162306a36Sopenharmony_ci */ 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci/* Program the CRTC registers */ 101462306a36Sopenharmony_cistatic void aty128_set_crtc(const struct aty128_crtc *crtc, 101562306a36Sopenharmony_ci const struct aty128fb_par *par) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl); 101862306a36Sopenharmony_ci aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total); 101962306a36Sopenharmony_ci aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid); 102062306a36Sopenharmony_ci aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total); 102162306a36Sopenharmony_ci aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid); 102262306a36Sopenharmony_ci aty_st_le32(CRTC_PITCH, crtc->pitch); 102362306a36Sopenharmony_ci aty_st_le32(CRTC_OFFSET, crtc->offset); 102462306a36Sopenharmony_ci aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl); 102562306a36Sopenharmony_ci /* Disable ATOMIC updating. Is this the right place? */ 102662306a36Sopenharmony_ci aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000)); 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic int aty128_var_to_crtc(const struct fb_var_screeninfo *var, 103162306a36Sopenharmony_ci struct aty128_crtc *crtc, 103262306a36Sopenharmony_ci const struct aty128fb_par *par) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst; 103562306a36Sopenharmony_ci u32 left, right, upper, lower, hslen, vslen, sync, vmode; 103662306a36Sopenharmony_ci u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol; 103762306a36Sopenharmony_ci u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; 103862306a36Sopenharmony_ci u32 depth, bytpp; 103962306a36Sopenharmony_ci u8 mode_bytpp[7] = { 0, 0, 1, 2, 2, 3, 4 }; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* input */ 104262306a36Sopenharmony_ci xres = var->xres; 104362306a36Sopenharmony_ci yres = var->yres; 104462306a36Sopenharmony_ci vxres = var->xres_virtual; 104562306a36Sopenharmony_ci vyres = var->yres_virtual; 104662306a36Sopenharmony_ci xoffset = var->xoffset; 104762306a36Sopenharmony_ci yoffset = var->yoffset; 104862306a36Sopenharmony_ci bpp = var->bits_per_pixel; 104962306a36Sopenharmony_ci left = var->left_margin; 105062306a36Sopenharmony_ci right = var->right_margin; 105162306a36Sopenharmony_ci upper = var->upper_margin; 105262306a36Sopenharmony_ci lower = var->lower_margin; 105362306a36Sopenharmony_ci hslen = var->hsync_len; 105462306a36Sopenharmony_ci vslen = var->vsync_len; 105562306a36Sopenharmony_ci sync = var->sync; 105662306a36Sopenharmony_ci vmode = var->vmode; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (bpp != 16) 105962306a36Sopenharmony_ci depth = bpp; 106062306a36Sopenharmony_ci else 106162306a36Sopenharmony_ci depth = (var->green.length == 6) ? 16 : 15; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* check for mode eligibility 106462306a36Sopenharmony_ci * accept only non interlaced modes */ 106562306a36Sopenharmony_ci if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) 106662306a36Sopenharmony_ci return -EINVAL; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* convert (and round up) and validate */ 106962306a36Sopenharmony_ci xres = (xres + 7) & ~7; 107062306a36Sopenharmony_ci xoffset = (xoffset + 7) & ~7; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (vxres < xres + xoffset) 107362306a36Sopenharmony_ci vxres = xres + xoffset; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (vyres < yres + yoffset) 107662306a36Sopenharmony_ci vyres = yres + yoffset; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci /* convert depth into ATI register depth */ 107962306a36Sopenharmony_ci dst = depth_to_dst(depth); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (dst == -EINVAL) { 108262306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n"); 108362306a36Sopenharmony_ci return -EINVAL; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* convert register depth to bytes per pixel */ 108762306a36Sopenharmony_ci bytpp = mode_bytpp[dst]; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* make sure there is enough video ram for the mode */ 109062306a36Sopenharmony_ci if ((u32)(vxres * vyres * bytpp) > par->vram_size) { 109162306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: Not enough memory for mode\n"); 109262306a36Sopenharmony_ci return -EINVAL; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci h_disp = (xres >> 3) - 1; 109662306a36Sopenharmony_ci h_total = (((xres + right + hslen + left) >> 3) - 1) & 0xFFFFL; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci v_disp = yres - 1; 109962306a36Sopenharmony_ci v_total = (yres + upper + vslen + lower - 1) & 0xFFFFL; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* check to make sure h_total and v_total are in range */ 110262306a36Sopenharmony_ci if (((h_total >> 3) - 1) > 0x1ff || (v_total - 1) > 0x7FF) { 110362306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: invalid width ranges\n"); 110462306a36Sopenharmony_ci return -EINVAL; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci h_sync_wid = (hslen + 7) >> 3; 110862306a36Sopenharmony_ci if (h_sync_wid == 0) 110962306a36Sopenharmony_ci h_sync_wid = 1; 111062306a36Sopenharmony_ci else if (h_sync_wid > 0x3f) /* 0x3f = max hwidth */ 111162306a36Sopenharmony_ci h_sync_wid = 0x3f; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci h_sync_strt = (h_disp << 3) + right; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci v_sync_wid = vslen; 111662306a36Sopenharmony_ci if (v_sync_wid == 0) 111762306a36Sopenharmony_ci v_sync_wid = 1; 111862306a36Sopenharmony_ci else if (v_sync_wid > 0x1f) /* 0x1f = max vwidth */ 111962306a36Sopenharmony_ci v_sync_wid = 0x1f; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci v_sync_strt = v_disp + lower; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1; 112462306a36Sopenharmony_ci v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci crtc->h_total = h_total | (h_disp << 16); 113162306a36Sopenharmony_ci crtc->v_total = v_total | (v_disp << 16); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci crtc->h_sync_strt_wid = h_sync_strt | (h_sync_wid << 16) | 113462306a36Sopenharmony_ci (h_sync_pol << 23); 113562306a36Sopenharmony_ci crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) | 113662306a36Sopenharmony_ci (v_sync_pol << 23); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci crtc->pitch = vxres >> 3; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci crtc->offset = 0; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) 114362306a36Sopenharmony_ci crtc->offset_cntl = 0x00010000; 114462306a36Sopenharmony_ci else 114562306a36Sopenharmony_ci crtc->offset_cntl = 0; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci crtc->vxres = vxres; 114862306a36Sopenharmony_ci crtc->vyres = vyres; 114962306a36Sopenharmony_ci crtc->xoffset = xoffset; 115062306a36Sopenharmony_ci crtc->yoffset = yoffset; 115162306a36Sopenharmony_ci crtc->depth = depth; 115262306a36Sopenharmony_ci crtc->bpp = bpp; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci return 0; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic int aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* fill in pixel info */ 116262306a36Sopenharmony_ci var->red.msb_right = 0; 116362306a36Sopenharmony_ci var->green.msb_right = 0; 116462306a36Sopenharmony_ci var->blue.offset = 0; 116562306a36Sopenharmony_ci var->blue.msb_right = 0; 116662306a36Sopenharmony_ci var->transp.offset = 0; 116762306a36Sopenharmony_ci var->transp.length = 0; 116862306a36Sopenharmony_ci var->transp.msb_right = 0; 116962306a36Sopenharmony_ci switch (pix_width) { 117062306a36Sopenharmony_ci case CRTC_PIX_WIDTH_8BPP: 117162306a36Sopenharmony_ci var->bits_per_pixel = 8; 117262306a36Sopenharmony_ci var->red.offset = 0; 117362306a36Sopenharmony_ci var->red.length = 8; 117462306a36Sopenharmony_ci var->green.offset = 0; 117562306a36Sopenharmony_ci var->green.length = 8; 117662306a36Sopenharmony_ci var->blue.length = 8; 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci case CRTC_PIX_WIDTH_15BPP: 117962306a36Sopenharmony_ci var->bits_per_pixel = 16; 118062306a36Sopenharmony_ci var->red.offset = 10; 118162306a36Sopenharmony_ci var->red.length = 5; 118262306a36Sopenharmony_ci var->green.offset = 5; 118362306a36Sopenharmony_ci var->green.length = 5; 118462306a36Sopenharmony_ci var->blue.length = 5; 118562306a36Sopenharmony_ci break; 118662306a36Sopenharmony_ci case CRTC_PIX_WIDTH_16BPP: 118762306a36Sopenharmony_ci var->bits_per_pixel = 16; 118862306a36Sopenharmony_ci var->red.offset = 11; 118962306a36Sopenharmony_ci var->red.length = 5; 119062306a36Sopenharmony_ci var->green.offset = 5; 119162306a36Sopenharmony_ci var->green.length = 6; 119262306a36Sopenharmony_ci var->blue.length = 5; 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci case CRTC_PIX_WIDTH_24BPP: 119562306a36Sopenharmony_ci var->bits_per_pixel = 24; 119662306a36Sopenharmony_ci var->red.offset = 16; 119762306a36Sopenharmony_ci var->red.length = 8; 119862306a36Sopenharmony_ci var->green.offset = 8; 119962306a36Sopenharmony_ci var->green.length = 8; 120062306a36Sopenharmony_ci var->blue.length = 8; 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case CRTC_PIX_WIDTH_32BPP: 120362306a36Sopenharmony_ci var->bits_per_pixel = 32; 120462306a36Sopenharmony_ci var->red.offset = 16; 120562306a36Sopenharmony_ci var->red.length = 8; 120662306a36Sopenharmony_ci var->green.offset = 8; 120762306a36Sopenharmony_ci var->green.length = 8; 120862306a36Sopenharmony_ci var->blue.length = 8; 120962306a36Sopenharmony_ci var->transp.offset = 24; 121062306a36Sopenharmony_ci var->transp.length = 8; 121162306a36Sopenharmony_ci break; 121262306a36Sopenharmony_ci default: 121362306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: Invalid pixel width\n"); 121462306a36Sopenharmony_ci return -EINVAL; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci return 0; 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic int aty128_crtc_to_var(const struct aty128_crtc *crtc, 122262306a36Sopenharmony_ci struct fb_var_screeninfo *var) 122362306a36Sopenharmony_ci{ 122462306a36Sopenharmony_ci u32 xres, yres, left, right, upper, lower, hslen, vslen, sync; 122562306a36Sopenharmony_ci u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol; 122662306a36Sopenharmony_ci u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; 122762306a36Sopenharmony_ci u32 pix_width; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* fun with masking */ 123062306a36Sopenharmony_ci h_total = crtc->h_total & 0x1ff; 123162306a36Sopenharmony_ci h_disp = (crtc->h_total >> 16) & 0xff; 123262306a36Sopenharmony_ci h_sync_strt = (crtc->h_sync_strt_wid >> 3) & 0x1ff; 123362306a36Sopenharmony_ci h_sync_dly = crtc->h_sync_strt_wid & 0x7; 123462306a36Sopenharmony_ci h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x3f; 123562306a36Sopenharmony_ci h_sync_pol = (crtc->h_sync_strt_wid >> 23) & 0x1; 123662306a36Sopenharmony_ci v_total = crtc->v_total & 0x7ff; 123762306a36Sopenharmony_ci v_disp = (crtc->v_total >> 16) & 0x7ff; 123862306a36Sopenharmony_ci v_sync_strt = crtc->v_sync_strt_wid & 0x7ff; 123962306a36Sopenharmony_ci v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f; 124062306a36Sopenharmony_ci v_sync_pol = (crtc->v_sync_strt_wid >> 23) & 0x1; 124162306a36Sopenharmony_ci c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0; 124262306a36Sopenharmony_ci pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci /* do conversions */ 124562306a36Sopenharmony_ci xres = (h_disp + 1) << 3; 124662306a36Sopenharmony_ci yres = v_disp + 1; 124762306a36Sopenharmony_ci left = ((h_total - h_sync_strt - h_sync_wid) << 3) - h_sync_dly; 124862306a36Sopenharmony_ci right = ((h_sync_strt - h_disp) << 3) + h_sync_dly; 124962306a36Sopenharmony_ci hslen = h_sync_wid << 3; 125062306a36Sopenharmony_ci upper = v_total - v_sync_strt - v_sync_wid; 125162306a36Sopenharmony_ci lower = v_sync_strt - v_disp; 125262306a36Sopenharmony_ci vslen = v_sync_wid; 125362306a36Sopenharmony_ci sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) | 125462306a36Sopenharmony_ci (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | 125562306a36Sopenharmony_ci (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci aty128_pix_width_to_var(pix_width, var); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci var->xres = xres; 126062306a36Sopenharmony_ci var->yres = yres; 126162306a36Sopenharmony_ci var->xres_virtual = crtc->vxres; 126262306a36Sopenharmony_ci var->yres_virtual = crtc->vyres; 126362306a36Sopenharmony_ci var->xoffset = crtc->xoffset; 126462306a36Sopenharmony_ci var->yoffset = crtc->yoffset; 126562306a36Sopenharmony_ci var->left_margin = left; 126662306a36Sopenharmony_ci var->right_margin = right; 126762306a36Sopenharmony_ci var->upper_margin = upper; 126862306a36Sopenharmony_ci var->lower_margin = lower; 126962306a36Sopenharmony_ci var->hsync_len = hslen; 127062306a36Sopenharmony_ci var->vsync_len = vslen; 127162306a36Sopenharmony_ci var->sync = sync; 127262306a36Sopenharmony_ci var->vmode = FB_VMODE_NONINTERLACED; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci return 0; 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic void aty128_set_crt_enable(struct aty128fb_par *par, int on) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci if (on) { 128062306a36Sopenharmony_ci aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | 128162306a36Sopenharmony_ci CRT_CRTC_ON); 128262306a36Sopenharmony_ci aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | 128362306a36Sopenharmony_ci DAC_PALETTE2_SNOOP_EN)); 128462306a36Sopenharmony_ci } else 128562306a36Sopenharmony_ci aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & 128662306a36Sopenharmony_ci ~CRT_CRTC_ON); 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic void aty128_set_lcd_enable(struct aty128fb_par *par, int on) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci u32 reg; 129262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 129362306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(par->pdev); 129462306a36Sopenharmony_ci#endif 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci if (on) { 129762306a36Sopenharmony_ci reg = aty_ld_le32(LVDS_GEN_CNTL); 129862306a36Sopenharmony_ci reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION; 129962306a36Sopenharmony_ci reg &= ~LVDS_DISPLAY_DIS; 130062306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 130162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 130262306a36Sopenharmony_ci aty128_bl_set_power(info, FB_BLANK_UNBLANK); 130362306a36Sopenharmony_ci#endif 130462306a36Sopenharmony_ci } else { 130562306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 130662306a36Sopenharmony_ci aty128_bl_set_power(info, FB_BLANK_POWERDOWN); 130762306a36Sopenharmony_ci#endif 130862306a36Sopenharmony_ci reg = aty_ld_le32(LVDS_GEN_CNTL); 130962306a36Sopenharmony_ci reg |= LVDS_DISPLAY_DIS; 131062306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 131162306a36Sopenharmony_ci mdelay(100); 131262306a36Sopenharmony_ci reg &= ~(LVDS_ON /*| LVDS_EN*/); 131362306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 131462306a36Sopenharmony_ci } 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic void aty128_set_pll(struct aty128_pll *pll, 131862306a36Sopenharmony_ci const struct aty128fb_par *par) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci u32 div3; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci /* register values for post dividers */ 132362306a36Sopenharmony_ci static const unsigned char post_conv[] = { 132462306a36Sopenharmony_ci 2, 0, 1, 4, 2, 2, 6, 2, 3, 2, 2, 2, 7 132562306a36Sopenharmony_ci }; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* select PPLL_DIV_3 */ 132862306a36Sopenharmony_ci aty_st_le32(CLOCK_CNTL_INDEX, aty_ld_le32(CLOCK_CNTL_INDEX) | (3 << 8)); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* reset PLL */ 133162306a36Sopenharmony_ci aty_st_pll(PPLL_CNTL, 133262306a36Sopenharmony_ci aty_ld_pll(PPLL_CNTL) | PPLL_RESET | PPLL_ATOMIC_UPDATE_EN); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* write the reference divider */ 133562306a36Sopenharmony_ci aty_pll_wait_readupdate(par); 133662306a36Sopenharmony_ci aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider & 0x3ff); 133762306a36Sopenharmony_ci aty_pll_writeupdate(par); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci div3 = aty_ld_pll(PPLL_DIV_3); 134062306a36Sopenharmony_ci div3 &= ~PPLL_FB3_DIV_MASK; 134162306a36Sopenharmony_ci div3 |= pll->feedback_divider; 134262306a36Sopenharmony_ci div3 &= ~PPLL_POST3_DIV_MASK; 134362306a36Sopenharmony_ci div3 |= post_conv[pll->post_divider] << 16; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci /* write feedback and post dividers */ 134662306a36Sopenharmony_ci aty_pll_wait_readupdate(par); 134762306a36Sopenharmony_ci aty_st_pll(PPLL_DIV_3, div3); 134862306a36Sopenharmony_ci aty_pll_writeupdate(par); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci aty_pll_wait_readupdate(par); 135162306a36Sopenharmony_ci aty_st_pll(HTOTAL_CNTL, 0); /* no horiz crtc adjustment */ 135262306a36Sopenharmony_ci aty_pll_writeupdate(par); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci /* clear the reset, just in case */ 135562306a36Sopenharmony_ci aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET); 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_cistatic int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll, 136062306a36Sopenharmony_ci const struct aty128fb_par *par) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci const struct aty128_constants c = par->constants; 136362306a36Sopenharmony_ci static const unsigned char post_dividers[] = { 1, 2, 4, 8, 3, 6, 12 }; 136462306a36Sopenharmony_ci u32 output_freq; 136562306a36Sopenharmony_ci u32 vclk; /* in .01 MHz */ 136662306a36Sopenharmony_ci int i = 0; 136762306a36Sopenharmony_ci u32 n, d; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci vclk = 100000000 / period_in_ps; /* convert units to 10 kHz */ 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci /* adjust pixel clock if necessary */ 137262306a36Sopenharmony_ci if (vclk > c.ppll_max) 137362306a36Sopenharmony_ci vclk = c.ppll_max; 137462306a36Sopenharmony_ci if (vclk * 12 < c.ppll_min) 137562306a36Sopenharmony_ci vclk = c.ppll_min/12; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* now, find an acceptable divider */ 137862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(post_dividers); i++) { 137962306a36Sopenharmony_ci output_freq = post_dividers[i] * vclk; 138062306a36Sopenharmony_ci if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) { 138162306a36Sopenharmony_ci pll->post_divider = post_dividers[i]; 138262306a36Sopenharmony_ci break; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (i == ARRAY_SIZE(post_dividers)) 138762306a36Sopenharmony_ci return -EINVAL; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* calculate feedback divider */ 139062306a36Sopenharmony_ci n = c.ref_divider * output_freq; 139162306a36Sopenharmony_ci d = c.ref_clk; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci pll->feedback_divider = round_div(n, d); 139462306a36Sopenharmony_ci pll->vclk = vclk; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci DBG("post %d feedback %d vlck %d output %d ref_divider %d " 139762306a36Sopenharmony_ci "vclk_per: %d\n", pll->post_divider, 139862306a36Sopenharmony_ci pll->feedback_divider, vclk, output_freq, 139962306a36Sopenharmony_ci c.ref_divider, period_in_ps); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci return 0; 140262306a36Sopenharmony_ci} 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_cistatic int aty128_pll_to_var(const struct aty128_pll *pll, 140662306a36Sopenharmony_ci struct fb_var_screeninfo *var) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci var->pixclock = 100000000 / pll->vclk; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci return 0; 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cistatic void aty128_set_fifo(const struct aty128_ddafifo *dsp, 141562306a36Sopenharmony_ci const struct aty128fb_par *par) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci aty_st_le32(DDA_CONFIG, dsp->dda_config); 141862306a36Sopenharmony_ci aty_st_le32(DDA_ON_OFF, dsp->dda_on_off); 141962306a36Sopenharmony_ci} 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic int aty128_ddafifo(struct aty128_ddafifo *dsp, 142362306a36Sopenharmony_ci const struct aty128_pll *pll, 142462306a36Sopenharmony_ci u32 depth, 142562306a36Sopenharmony_ci const struct aty128fb_par *par) 142662306a36Sopenharmony_ci{ 142762306a36Sopenharmony_ci const struct aty128_meminfo *m = par->mem; 142862306a36Sopenharmony_ci u32 xclk = par->constants.xclk; 142962306a36Sopenharmony_ci u32 fifo_width = par->constants.fifo_width; 143062306a36Sopenharmony_ci u32 fifo_depth = par->constants.fifo_depth; 143162306a36Sopenharmony_ci s32 x, b, p, ron, roff; 143262306a36Sopenharmony_ci u32 n, d, bpp; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci /* round up to multiple of 8 */ 143562306a36Sopenharmony_ci bpp = (depth+7) & ~7; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci n = xclk * fifo_width; 143862306a36Sopenharmony_ci d = pll->vclk * bpp; 143962306a36Sopenharmony_ci x = round_div(n, d); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci ron = 4 * m->MB + 144262306a36Sopenharmony_ci 3 * ((m->Trcd - 2 > 0) ? m->Trcd - 2 : 0) + 144362306a36Sopenharmony_ci 2 * m->Trp + 144462306a36Sopenharmony_ci m->Twr + 144562306a36Sopenharmony_ci m->CL + 144662306a36Sopenharmony_ci m->Tr2w + 144762306a36Sopenharmony_ci x; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci DBG("x %x\n", x); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci b = 0; 145262306a36Sopenharmony_ci while (x) { 145362306a36Sopenharmony_ci x >>= 1; 145462306a36Sopenharmony_ci b++; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci p = b + 1; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci ron <<= (11 - p); 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci n <<= (11 - p); 146162306a36Sopenharmony_ci x = round_div(n, d); 146262306a36Sopenharmony_ci roff = x * (fifo_depth - 4); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci if ((ron + m->Rloop) >= roff) { 146562306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: Mode out of range!\n"); 146662306a36Sopenharmony_ci return -EINVAL; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci DBG("p: %x rloop: %x x: %x ron: %x roff: %x\n", 147062306a36Sopenharmony_ci p, m->Rloop, x, ron, roff); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci dsp->dda_config = p << 16 | m->Rloop << 20 | x; 147362306a36Sopenharmony_ci dsp->dda_on_off = ron << 16 | roff; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci return 0; 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci/* 148062306a36Sopenharmony_ci * This actually sets the video mode. 148162306a36Sopenharmony_ci */ 148262306a36Sopenharmony_cistatic int aty128fb_set_par(struct fb_info *info) 148362306a36Sopenharmony_ci{ 148462306a36Sopenharmony_ci struct aty128fb_par *par = info->par; 148562306a36Sopenharmony_ci u32 config; 148662306a36Sopenharmony_ci int err; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if ((err = aty128_decode_var(&info->var, par)) != 0) 148962306a36Sopenharmony_ci return err; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci if (par->blitter_may_be_busy) 149262306a36Sopenharmony_ci wait_for_idle(par); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci /* clear all registers that may interfere with mode setting */ 149562306a36Sopenharmony_ci aty_st_le32(OVR_CLR, 0); 149662306a36Sopenharmony_ci aty_st_le32(OVR_WID_LEFT_RIGHT, 0); 149762306a36Sopenharmony_ci aty_st_le32(OVR_WID_TOP_BOTTOM, 0); 149862306a36Sopenharmony_ci aty_st_le32(OV0_SCALE_CNTL, 0); 149962306a36Sopenharmony_ci aty_st_le32(MPP_TB_CONFIG, 0); 150062306a36Sopenharmony_ci aty_st_le32(MPP_GP_CONFIG, 0); 150162306a36Sopenharmony_ci aty_st_le32(SUBPIC_CNTL, 0); 150262306a36Sopenharmony_ci aty_st_le32(VIPH_CONTROL, 0); 150362306a36Sopenharmony_ci aty_st_le32(I2C_CNTL_1, 0); /* turn off i2c */ 150462306a36Sopenharmony_ci aty_st_le32(GEN_INT_CNTL, 0); /* turn off interrupts */ 150562306a36Sopenharmony_ci aty_st_le32(CAP0_TRIG_CNTL, 0); 150662306a36Sopenharmony_ci aty_st_le32(CAP1_TRIG_CNTL, 0); 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci aty_st_8(CRTC_EXT_CNTL + 1, 4); /* turn video off */ 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci aty128_set_crtc(&par->crtc, par); 151162306a36Sopenharmony_ci aty128_set_pll(&par->pll, par); 151262306a36Sopenharmony_ci aty128_set_fifo(&par->fifo_reg, par); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci config = aty_ld_le32(CNFG_CNTL) & ~3; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci#if defined(__BIG_ENDIAN) 151762306a36Sopenharmony_ci if (par->crtc.bpp == 32) 151862306a36Sopenharmony_ci config |= 2; /* make aperture do 32 bit swapping */ 151962306a36Sopenharmony_ci else if (par->crtc.bpp == 16) 152062306a36Sopenharmony_ci config |= 1; /* make aperture do 16 bit swapping */ 152162306a36Sopenharmony_ci#endif 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci aty_st_le32(CNFG_CNTL, config); 152462306a36Sopenharmony_ci aty_st_8(CRTC_EXT_CNTL + 1, 0); /* turn the video back on */ 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci info->fix.line_length = (par->crtc.vxres * par->crtc.bpp) >> 3; 152762306a36Sopenharmony_ci info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR 152862306a36Sopenharmony_ci : FB_VISUAL_DIRECTCOLOR; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci if (par->chip_gen == rage_M3) { 153162306a36Sopenharmony_ci aty128_set_crt_enable(par, par->crt_on); 153262306a36Sopenharmony_ci aty128_set_lcd_enable(par, par->lcd_on); 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci if (par->accel_flags & FB_ACCELF_TEXT) 153562306a36Sopenharmony_ci aty128_init_engine(par); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 153862306a36Sopenharmony_ci btext_update_display(info->fix.smem_start, 153962306a36Sopenharmony_ci (((par->crtc.h_total>>16) & 0xff)+1)*8, 154062306a36Sopenharmony_ci ((par->crtc.v_total>>16) & 0x7ff)+1, 154162306a36Sopenharmony_ci par->crtc.bpp, 154262306a36Sopenharmony_ci par->crtc.vxres*par->crtc.bpp/8); 154362306a36Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci return 0; 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci/* 154962306a36Sopenharmony_ci * encode/decode the User Defined Part of the Display 155062306a36Sopenharmony_ci */ 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_cistatic int aty128_decode_var(struct fb_var_screeninfo *var, 155362306a36Sopenharmony_ci struct aty128fb_par *par) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci int err; 155662306a36Sopenharmony_ci struct aty128_crtc crtc; 155762306a36Sopenharmony_ci struct aty128_pll pll; 155862306a36Sopenharmony_ci struct aty128_ddafifo fifo_reg; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if ((err = aty128_var_to_crtc(var, &crtc, par))) 156162306a36Sopenharmony_ci return err; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if ((err = aty128_var_to_pll(var->pixclock, &pll, par))) 156462306a36Sopenharmony_ci return err; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if ((err = aty128_ddafifo(&fifo_reg, &pll, crtc.depth, par))) 156762306a36Sopenharmony_ci return err; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci par->crtc = crtc; 157062306a36Sopenharmony_ci par->pll = pll; 157162306a36Sopenharmony_ci par->fifo_reg = fifo_reg; 157262306a36Sopenharmony_ci par->accel_flags = var->accel_flags; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci return 0; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic int aty128_encode_var(struct fb_var_screeninfo *var, 157962306a36Sopenharmony_ci const struct aty128fb_par *par) 158062306a36Sopenharmony_ci{ 158162306a36Sopenharmony_ci int err; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci if ((err = aty128_crtc_to_var(&par->crtc, var))) 158462306a36Sopenharmony_ci return err; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci if ((err = aty128_pll_to_var(&par->pll, var))) 158762306a36Sopenharmony_ci return err; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci var->nonstd = 0; 159062306a36Sopenharmony_ci var->activate = 0; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci var->height = -1; 159362306a36Sopenharmony_ci var->width = -1; 159462306a36Sopenharmony_ci var->accel_flags = par->accel_flags; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci return 0; 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic int aty128fb_check_var(struct fb_var_screeninfo *var, 160162306a36Sopenharmony_ci struct fb_info *info) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci struct aty128fb_par par; 160462306a36Sopenharmony_ci int err; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci par = *(struct aty128fb_par *)info->par; 160762306a36Sopenharmony_ci if ((err = aty128_decode_var(var, &par)) != 0) 160862306a36Sopenharmony_ci return err; 160962306a36Sopenharmony_ci aty128_encode_var(var, &par); 161062306a36Sopenharmony_ci return 0; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci/* 161562306a36Sopenharmony_ci * Pan or Wrap the Display 161662306a36Sopenharmony_ci */ 161762306a36Sopenharmony_cistatic int aty128fb_pan_display(struct fb_var_screeninfo *var, 161862306a36Sopenharmony_ci struct fb_info *fb) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci struct aty128fb_par *par = fb->par; 162162306a36Sopenharmony_ci u32 xoffset, yoffset; 162262306a36Sopenharmony_ci u32 offset; 162362306a36Sopenharmony_ci u32 xres, yres; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci xres = (((par->crtc.h_total >> 16) & 0xff) + 1) << 3; 162662306a36Sopenharmony_ci yres = ((par->crtc.v_total >> 16) & 0x7ff) + 1; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci xoffset = (var->xoffset +7) & ~7; 162962306a36Sopenharmony_ci yoffset = var->yoffset; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci if (xoffset+xres > par->crtc.vxres || yoffset+yres > par->crtc.vyres) 163262306a36Sopenharmony_ci return -EINVAL; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci par->crtc.xoffset = xoffset; 163562306a36Sopenharmony_ci par->crtc.yoffset = yoffset; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3)) 163862306a36Sopenharmony_ci & ~7; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci if (par->crtc.bpp == 24) 164162306a36Sopenharmony_ci offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */ 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci aty_st_le32(CRTC_OFFSET, offset); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci return 0; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci/* 165062306a36Sopenharmony_ci * Helper function to store a single palette register 165162306a36Sopenharmony_ci */ 165262306a36Sopenharmony_cistatic void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue, 165362306a36Sopenharmony_ci struct aty128fb_par *par) 165462306a36Sopenharmony_ci{ 165562306a36Sopenharmony_ci if (par->chip_gen == rage_M3) { 165662306a36Sopenharmony_ci aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & 165762306a36Sopenharmony_ci ~DAC_PALETTE_ACCESS_CNTL); 165862306a36Sopenharmony_ci } 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci aty_st_8(PALETTE_INDEX, regno); 166162306a36Sopenharmony_ci aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue); 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic int aty128fb_sync(struct fb_info *info) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci struct aty128fb_par *par = info->par; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (par->blitter_may_be_busy) 166962306a36Sopenharmony_ci wait_for_idle(par); 167062306a36Sopenharmony_ci return 0; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci#ifndef MODULE 167462306a36Sopenharmony_cistatic int aty128fb_setup(char *options) 167562306a36Sopenharmony_ci{ 167662306a36Sopenharmony_ci char *this_opt; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (!options || !*options) 167962306a36Sopenharmony_ci return 0; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 168262306a36Sopenharmony_ci if (!strncmp(this_opt, "lcd:", 4)) { 168362306a36Sopenharmony_ci default_lcd_on = simple_strtoul(this_opt+4, NULL, 0); 168462306a36Sopenharmony_ci continue; 168562306a36Sopenharmony_ci } else if (!strncmp(this_opt, "crt:", 4)) { 168662306a36Sopenharmony_ci default_crt_on = simple_strtoul(this_opt+4, NULL, 0); 168762306a36Sopenharmony_ci continue; 168862306a36Sopenharmony_ci } else if (!strncmp(this_opt, "backlight:", 10)) { 168962306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 169062306a36Sopenharmony_ci backlight = simple_strtoul(this_opt+10, NULL, 0); 169162306a36Sopenharmony_ci#endif 169262306a36Sopenharmony_ci continue; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci if(!strncmp(this_opt, "nomtrr", 6)) { 169562306a36Sopenharmony_ci mtrr = false; 169662306a36Sopenharmony_ci continue; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 169962306a36Sopenharmony_ci /* vmode and cmode deprecated */ 170062306a36Sopenharmony_ci if (!strncmp(this_opt, "vmode:", 6)) { 170162306a36Sopenharmony_ci unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0); 170262306a36Sopenharmony_ci if (vmode > 0 && vmode <= VMODE_MAX) 170362306a36Sopenharmony_ci default_vmode = vmode; 170462306a36Sopenharmony_ci continue; 170562306a36Sopenharmony_ci } else if (!strncmp(this_opt, "cmode:", 6)) { 170662306a36Sopenharmony_ci unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0); 170762306a36Sopenharmony_ci switch (cmode) { 170862306a36Sopenharmony_ci case 0: 170962306a36Sopenharmony_ci case 8: 171062306a36Sopenharmony_ci default_cmode = CMODE_8; 171162306a36Sopenharmony_ci break; 171262306a36Sopenharmony_ci case 15: 171362306a36Sopenharmony_ci case 16: 171462306a36Sopenharmony_ci default_cmode = CMODE_16; 171562306a36Sopenharmony_ci break; 171662306a36Sopenharmony_ci case 24: 171762306a36Sopenharmony_ci case 32: 171862306a36Sopenharmony_ci default_cmode = CMODE_32; 171962306a36Sopenharmony_ci break; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci continue; 172262306a36Sopenharmony_ci } 172362306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 172462306a36Sopenharmony_ci mode_option = this_opt; 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci return 0; 172762306a36Sopenharmony_ci} 172862306a36Sopenharmony_ci#endif /* MODULE */ 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci/* Backlight */ 173162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 173262306a36Sopenharmony_ci#define MAX_LEVEL 0xFF 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_cistatic int aty128_bl_get_level_brightness(struct aty128fb_par *par, 173562306a36Sopenharmony_ci int level) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(par->pdev); 173862306a36Sopenharmony_ci int atylevel; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci /* Get and convert the value */ 174162306a36Sopenharmony_ci /* No locking of bl_curve since we read a single value */ 174262306a36Sopenharmony_ci atylevel = MAX_LEVEL - 174362306a36Sopenharmony_ci (info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL); 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci if (atylevel < 0) 174662306a36Sopenharmony_ci atylevel = 0; 174762306a36Sopenharmony_ci else if (atylevel > MAX_LEVEL) 174862306a36Sopenharmony_ci atylevel = MAX_LEVEL; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return atylevel; 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci/* We turn off the LCD completely instead of just dimming the backlight. 175462306a36Sopenharmony_ci * This provides greater power saving and the display is useless without 175562306a36Sopenharmony_ci * backlight anyway 175662306a36Sopenharmony_ci */ 175762306a36Sopenharmony_ci#define BACKLIGHT_LVDS_OFF 175862306a36Sopenharmony_ci/* That one prevents proper CRT output with LCD off */ 175962306a36Sopenharmony_ci#undef BACKLIGHT_DAC_OFF 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_cistatic int aty128_bl_update_status(struct backlight_device *bd) 176262306a36Sopenharmony_ci{ 176362306a36Sopenharmony_ci struct aty128fb_par *par = bl_get_data(bd); 176462306a36Sopenharmony_ci unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL); 176562306a36Sopenharmony_ci int level; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (!par->lcd_on) 176862306a36Sopenharmony_ci level = 0; 176962306a36Sopenharmony_ci else 177062306a36Sopenharmony_ci level = backlight_get_brightness(bd); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci reg |= LVDS_BL_MOD_EN | LVDS_BLON; 177362306a36Sopenharmony_ci if (level > 0) { 177462306a36Sopenharmony_ci reg |= LVDS_DIGION; 177562306a36Sopenharmony_ci if (!(reg & LVDS_ON)) { 177662306a36Sopenharmony_ci reg &= ~LVDS_BLON; 177762306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 177862306a36Sopenharmony_ci aty_ld_le32(LVDS_GEN_CNTL); 177962306a36Sopenharmony_ci mdelay(10); 178062306a36Sopenharmony_ci reg |= LVDS_BLON; 178162306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci reg &= ~LVDS_BL_MOD_LEVEL_MASK; 178462306a36Sopenharmony_ci reg |= (aty128_bl_get_level_brightness(par, level) << 178562306a36Sopenharmony_ci LVDS_BL_MOD_LEVEL_SHIFT); 178662306a36Sopenharmony_ci#ifdef BACKLIGHT_LVDS_OFF 178762306a36Sopenharmony_ci reg |= LVDS_ON | LVDS_EN; 178862306a36Sopenharmony_ci reg &= ~LVDS_DISPLAY_DIS; 178962306a36Sopenharmony_ci#endif 179062306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 179162306a36Sopenharmony_ci#ifdef BACKLIGHT_DAC_OFF 179262306a36Sopenharmony_ci aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN)); 179362306a36Sopenharmony_ci#endif 179462306a36Sopenharmony_ci } else { 179562306a36Sopenharmony_ci reg &= ~LVDS_BL_MOD_LEVEL_MASK; 179662306a36Sopenharmony_ci reg |= (aty128_bl_get_level_brightness(par, 0) << 179762306a36Sopenharmony_ci LVDS_BL_MOD_LEVEL_SHIFT); 179862306a36Sopenharmony_ci#ifdef BACKLIGHT_LVDS_OFF 179962306a36Sopenharmony_ci reg |= LVDS_DISPLAY_DIS; 180062306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 180162306a36Sopenharmony_ci aty_ld_le32(LVDS_GEN_CNTL); 180262306a36Sopenharmony_ci udelay(10); 180362306a36Sopenharmony_ci reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION); 180462306a36Sopenharmony_ci#endif 180562306a36Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 180662306a36Sopenharmony_ci#ifdef BACKLIGHT_DAC_OFF 180762306a36Sopenharmony_ci aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN); 180862306a36Sopenharmony_ci#endif 180962306a36Sopenharmony_ci } 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci return 0; 181262306a36Sopenharmony_ci} 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_cistatic const struct backlight_ops aty128_bl_data = { 181562306a36Sopenharmony_ci .update_status = aty128_bl_update_status, 181662306a36Sopenharmony_ci}; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_cistatic void aty128_bl_set_power(struct fb_info *info, int power) 181962306a36Sopenharmony_ci{ 182062306a36Sopenharmony_ci if (info->bl_dev) { 182162306a36Sopenharmony_ci info->bl_dev->props.power = power; 182262306a36Sopenharmony_ci backlight_update_status(info->bl_dev); 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci} 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_cistatic void aty128_bl_init(struct aty128fb_par *par) 182762306a36Sopenharmony_ci{ 182862306a36Sopenharmony_ci struct backlight_properties props; 182962306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(par->pdev); 183062306a36Sopenharmony_ci struct backlight_device *bd; 183162306a36Sopenharmony_ci char name[12]; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* Could be extended to Rage128Pro LVDS output too */ 183462306a36Sopenharmony_ci if (par->chip_gen != rage_M3) 183562306a36Sopenharmony_ci return; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 183862306a36Sopenharmony_ci if (!pmac_has_backlight_type("ati")) 183962306a36Sopenharmony_ci return; 184062306a36Sopenharmony_ci#endif 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci snprintf(name, sizeof(name), "aty128bl%d", info->node); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 184562306a36Sopenharmony_ci props.type = BACKLIGHT_RAW; 184662306a36Sopenharmony_ci props.max_brightness = FB_BACKLIGHT_LEVELS - 1; 184762306a36Sopenharmony_ci bd = backlight_device_register(name, info->device, par, &aty128_bl_data, 184862306a36Sopenharmony_ci &props); 184962306a36Sopenharmony_ci if (IS_ERR(bd)) { 185062306a36Sopenharmony_ci info->bl_dev = NULL; 185162306a36Sopenharmony_ci printk(KERN_WARNING "aty128: Backlight registration failed\n"); 185262306a36Sopenharmony_ci goto error; 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci info->bl_dev = bd; 185662306a36Sopenharmony_ci fb_bl_default_curve(info, 0, 185762306a36Sopenharmony_ci 63 * FB_BACKLIGHT_MAX / MAX_LEVEL, 185862306a36Sopenharmony_ci 219 * FB_BACKLIGHT_MAX / MAX_LEVEL); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci bd->props.brightness = bd->props.max_brightness; 186162306a36Sopenharmony_ci bd->props.power = FB_BLANK_UNBLANK; 186262306a36Sopenharmony_ci backlight_update_status(bd); 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci printk("aty128: Backlight initialized (%s)\n", name); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci return; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_cierror: 186962306a36Sopenharmony_ci return; 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic void aty128_bl_exit(struct backlight_device *bd) 187362306a36Sopenharmony_ci{ 187462306a36Sopenharmony_ci backlight_device_unregister(bd); 187562306a36Sopenharmony_ci printk("aty128: Backlight unloaded\n"); 187662306a36Sopenharmony_ci} 187762306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY128_BACKLIGHT */ 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci/* 188062306a36Sopenharmony_ci * Initialisation 188162306a36Sopenharmony_ci */ 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC__disabled 188462306a36Sopenharmony_cistatic void aty128_early_resume(void *data) 188562306a36Sopenharmony_ci{ 188662306a36Sopenharmony_ci struct aty128fb_par *par = data; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci if (!console_trylock()) 188962306a36Sopenharmony_ci return; 189062306a36Sopenharmony_ci pci_restore_state(par->pdev); 189162306a36Sopenharmony_ci aty128_do_resume(par->pdev); 189262306a36Sopenharmony_ci console_unlock(); 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic int aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 189962306a36Sopenharmony_ci struct aty128fb_par *par = info->par; 190062306a36Sopenharmony_ci struct fb_var_screeninfo var; 190162306a36Sopenharmony_ci char video_card[50]; 190262306a36Sopenharmony_ci u8 chip_rev; 190362306a36Sopenharmony_ci u32 dac; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci /* Get the chip revision */ 190662306a36Sopenharmony_ci chip_rev = (aty_ld_le32(CNFG_CNTL) >> 16) & 0x1F; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci strcpy(video_card, "Rage128 XX "); 190962306a36Sopenharmony_ci video_card[8] = ent->device >> 8; 191062306a36Sopenharmony_ci video_card[9] = ent->device & 0xFF; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* range check to make sure */ 191362306a36Sopenharmony_ci if (ent->driver_data < ARRAY_SIZE(r128_family)) 191462306a36Sopenharmony_ci strlcat(video_card, r128_family[ent->driver_data], 191562306a36Sopenharmony_ci sizeof(video_card)); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci if (par->vram_size % (1024 * 1024) == 0) 192062306a36Sopenharmony_ci printk("%dM %s\n", par->vram_size / (1024*1024), par->mem->name); 192162306a36Sopenharmony_ci else 192262306a36Sopenharmony_ci printk("%dk %s\n", par->vram_size / 1024, par->mem->name); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci par->chip_gen = ent->driver_data; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* fill in info */ 192762306a36Sopenharmony_ci info->fbops = &aty128fb_ops; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci par->lcd_on = default_lcd_on; 193062306a36Sopenharmony_ci par->crt_on = default_crt_on; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci var = default_var; 193362306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 193462306a36Sopenharmony_ci if (machine_is(powermac)) { 193562306a36Sopenharmony_ci /* Indicate sleep capability */ 193662306a36Sopenharmony_ci if (par->chip_gen == rage_M3) { 193762306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1); 193862306a36Sopenharmony_ci#if 0 /* Disable the early video resume hack for now as it's causing problems, 193962306a36Sopenharmony_ci * among others we now rely on the PCI core restoring the config space 194062306a36Sopenharmony_ci * for us, which isn't the case with that hack, and that code path causes 194162306a36Sopenharmony_ci * various things to be called with interrupts off while they shouldn't. 194262306a36Sopenharmony_ci * I'm leaving the code in as it can be useful for debugging purposes 194362306a36Sopenharmony_ci */ 194462306a36Sopenharmony_ci pmac_set_early_video_resume(aty128_early_resume, par); 194562306a36Sopenharmony_ci#endif 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci /* Find default mode */ 194962306a36Sopenharmony_ci if (mode_option) { 195062306a36Sopenharmony_ci if (!mac_find_mode(&var, info, mode_option, 8)) 195162306a36Sopenharmony_ci var = default_var; 195262306a36Sopenharmony_ci } else { 195362306a36Sopenharmony_ci if (default_vmode <= 0 || default_vmode > VMODE_MAX) 195462306a36Sopenharmony_ci default_vmode = VMODE_1024_768_60; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci /* iMacs need that resolution 195762306a36Sopenharmony_ci * PowerMac2,1 first r128 iMacs 195862306a36Sopenharmony_ci * PowerMac2,2 summer 2000 iMacs 195962306a36Sopenharmony_ci * PowerMac4,1 january 2001 iMacs "flower power" 196062306a36Sopenharmony_ci */ 196162306a36Sopenharmony_ci if (of_machine_is_compatible("PowerMac2,1") || 196262306a36Sopenharmony_ci of_machine_is_compatible("PowerMac2,2") || 196362306a36Sopenharmony_ci of_machine_is_compatible("PowerMac4,1")) 196462306a36Sopenharmony_ci default_vmode = VMODE_1024_768_75; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* iBook SE */ 196762306a36Sopenharmony_ci if (of_machine_is_compatible("PowerBook2,2")) 196862306a36Sopenharmony_ci default_vmode = VMODE_800_600_60; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci /* PowerBook Firewire (Pismo), iBook Dual USB */ 197162306a36Sopenharmony_ci if (of_machine_is_compatible("PowerBook3,1") || 197262306a36Sopenharmony_ci of_machine_is_compatible("PowerBook4,1")) 197362306a36Sopenharmony_ci default_vmode = VMODE_1024_768_60; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci /* PowerBook Titanium */ 197662306a36Sopenharmony_ci if (of_machine_is_compatible("PowerBook3,2")) 197762306a36Sopenharmony_ci default_vmode = VMODE_1152_768_60; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci if (default_cmode > 16) 198062306a36Sopenharmony_ci default_cmode = CMODE_32; 198162306a36Sopenharmony_ci else if (default_cmode > 8) 198262306a36Sopenharmony_ci default_cmode = CMODE_16; 198362306a36Sopenharmony_ci else 198462306a36Sopenharmony_ci default_cmode = CMODE_8; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci if (mac_vmode_to_var(default_vmode, default_cmode, &var)) 198762306a36Sopenharmony_ci var = default_var; 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci } else 199062306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 199162306a36Sopenharmony_ci { 199262306a36Sopenharmony_ci if (mode_option) 199362306a36Sopenharmony_ci if (fb_find_mode(&var, info, mode_option, NULL, 199462306a36Sopenharmony_ci 0, &defaultmode, 8) == 0) 199562306a36Sopenharmony_ci var = default_var; 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci var.accel_flags &= ~FB_ACCELF_TEXT; 199962306a36Sopenharmony_ci// var.accel_flags |= FB_ACCELF_TEXT;/* FIXME Will add accel later */ 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci if (aty128fb_check_var(&var, info)) { 200262306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: Cannot set default mode.\n"); 200362306a36Sopenharmony_ci return 0; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci /* setup the DAC the way we like it */ 200762306a36Sopenharmony_ci dac = aty_ld_le32(DAC_CNTL); 200862306a36Sopenharmony_ci dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL); 200962306a36Sopenharmony_ci dac |= DAC_MASK; 201062306a36Sopenharmony_ci if (par->chip_gen == rage_M3) 201162306a36Sopenharmony_ci dac |= DAC_PALETTE2_SNOOP_EN; 201262306a36Sopenharmony_ci aty_st_le32(DAC_CNTL, dac); 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci /* turn off bus mastering, just in case */ 201562306a36Sopenharmony_ci aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_MASTER_DIS); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci info->var = var; 201862306a36Sopenharmony_ci fb_alloc_cmap(&info->cmap, 256, 0); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci var.activate = FB_ACTIVATE_NOW; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci aty128_init_engine(par); 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci par->pdev = pdev; 202562306a36Sopenharmony_ci par->asleep = 0; 202662306a36Sopenharmony_ci par->lock_blank = 0; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci if (register_framebuffer(info) < 0) 202962306a36Sopenharmony_ci return 0; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 203262306a36Sopenharmony_ci if (backlight) 203362306a36Sopenharmony_ci aty128_bl_init(par); 203462306a36Sopenharmony_ci#endif 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci fb_info(info, "%s frame buffer device on %s\n", 203762306a36Sopenharmony_ci info->fix.id, video_card); 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci return 1; /* success! */ 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci#ifdef CONFIG_PCI 204362306a36Sopenharmony_ci/* register a card ++ajoshi */ 204462306a36Sopenharmony_cistatic int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci unsigned long fb_addr, reg_addr; 204762306a36Sopenharmony_ci struct aty128fb_par *par; 204862306a36Sopenharmony_ci struct fb_info *info; 204962306a36Sopenharmony_ci int err; 205062306a36Sopenharmony_ci#ifndef __sparc__ 205162306a36Sopenharmony_ci void __iomem *bios = NULL; 205262306a36Sopenharmony_ci#endif 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci err = aperture_remove_conflicting_pci_devices(pdev, "aty128fb"); 205562306a36Sopenharmony_ci if (err) 205662306a36Sopenharmony_ci return err; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* Enable device in PCI config */ 205962306a36Sopenharmony_ci if ((err = pci_enable_device(pdev))) { 206062306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n", 206162306a36Sopenharmony_ci err); 206262306a36Sopenharmony_ci return -ENODEV; 206362306a36Sopenharmony_ci } 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci fb_addr = pci_resource_start(pdev, 0); 206662306a36Sopenharmony_ci if (!request_mem_region(fb_addr, pci_resource_len(pdev, 0), 206762306a36Sopenharmony_ci "aty128fb FB")) { 206862306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: cannot reserve frame " 206962306a36Sopenharmony_ci "buffer memory\n"); 207062306a36Sopenharmony_ci return -ENODEV; 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci reg_addr = pci_resource_start(pdev, 2); 207462306a36Sopenharmony_ci if (!request_mem_region(reg_addr, pci_resource_len(pdev, 2), 207562306a36Sopenharmony_ci "aty128fb MMIO")) { 207662306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n"); 207762306a36Sopenharmony_ci goto err_free_fb; 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci /* We have the resources. Now virtualize them */ 208162306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev); 208262306a36Sopenharmony_ci if (!info) 208362306a36Sopenharmony_ci goto err_free_mmio; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci par = info->par; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci /* Virtualize mmio region */ 209062306a36Sopenharmony_ci info->fix.mmio_start = reg_addr; 209162306a36Sopenharmony_ci par->regbase = pci_ioremap_bar(pdev, 2); 209262306a36Sopenharmony_ci if (!par->regbase) 209362306a36Sopenharmony_ci goto err_free_info; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci /* Grab memory size from the card */ 209662306a36Sopenharmony_ci // How does this relate to the resource length from the PCI hardware? 209762306a36Sopenharmony_ci par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci /* Virtualize the framebuffer */ 210062306a36Sopenharmony_ci info->screen_base = ioremap_wc(fb_addr, par->vram_size); 210162306a36Sopenharmony_ci if (!info->screen_base) 210262306a36Sopenharmony_ci goto err_unmap_out; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* Set up info->fix */ 210562306a36Sopenharmony_ci info->fix = aty128fb_fix; 210662306a36Sopenharmony_ci info->fix.smem_start = fb_addr; 210762306a36Sopenharmony_ci info->fix.smem_len = par->vram_size; 210862306a36Sopenharmony_ci info->fix.mmio_start = reg_addr; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci /* If we can't test scratch registers, something is seriously wrong */ 211162306a36Sopenharmony_ci if (!register_test(par)) { 211262306a36Sopenharmony_ci printk(KERN_ERR "aty128fb: Can't write to video register!\n"); 211362306a36Sopenharmony_ci goto err_out; 211462306a36Sopenharmony_ci } 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci#ifndef __sparc__ 211762306a36Sopenharmony_ci bios = aty128_map_ROM(par, pdev); 211862306a36Sopenharmony_ci#ifdef CONFIG_X86 211962306a36Sopenharmony_ci if (bios == NULL) 212062306a36Sopenharmony_ci bios = aty128_find_mem_vbios(par); 212162306a36Sopenharmony_ci#endif 212262306a36Sopenharmony_ci if (bios == NULL) 212362306a36Sopenharmony_ci printk(KERN_INFO "aty128fb: BIOS not located, guessing timings.\n"); 212462306a36Sopenharmony_ci else { 212562306a36Sopenharmony_ci printk(KERN_INFO "aty128fb: Rage128 BIOS located\n"); 212662306a36Sopenharmony_ci aty128_get_pllinfo(par, bios); 212762306a36Sopenharmony_ci pci_unmap_rom(pdev, bios); 212862306a36Sopenharmony_ci } 212962306a36Sopenharmony_ci#endif /* __sparc__ */ 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci aty128_timings(par); 213262306a36Sopenharmony_ci pci_set_drvdata(pdev, info); 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci if (!aty128_init(pdev, ent)) 213562306a36Sopenharmony_ci goto err_out; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci if (mtrr) 213862306a36Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, 213962306a36Sopenharmony_ci par->vram_size); 214062306a36Sopenharmony_ci return 0; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_cierr_out: 214362306a36Sopenharmony_ci iounmap(info->screen_base); 214462306a36Sopenharmony_cierr_unmap_out: 214562306a36Sopenharmony_ci iounmap(par->regbase); 214662306a36Sopenharmony_cierr_free_info: 214762306a36Sopenharmony_ci framebuffer_release(info); 214862306a36Sopenharmony_cierr_free_mmio: 214962306a36Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 2), 215062306a36Sopenharmony_ci pci_resource_len(pdev, 2)); 215162306a36Sopenharmony_cierr_free_fb: 215262306a36Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 0), 215362306a36Sopenharmony_ci pci_resource_len(pdev, 0)); 215462306a36Sopenharmony_ci return -ENODEV; 215562306a36Sopenharmony_ci} 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_cistatic void aty128_remove(struct pci_dev *pdev) 215862306a36Sopenharmony_ci{ 215962306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 216062306a36Sopenharmony_ci struct aty128fb_par *par; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci if (!info) 216362306a36Sopenharmony_ci return; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci par = info->par; 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 216862306a36Sopenharmony_ci aty128_bl_exit(info->bl_dev); 216962306a36Sopenharmony_ci#endif 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci unregister_framebuffer(info); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 217462306a36Sopenharmony_ci iounmap(par->regbase); 217562306a36Sopenharmony_ci iounmap(info->screen_base); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 0), 217862306a36Sopenharmony_ci pci_resource_len(pdev, 0)); 217962306a36Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 2), 218062306a36Sopenharmony_ci pci_resource_len(pdev, 2)); 218162306a36Sopenharmony_ci framebuffer_release(info); 218262306a36Sopenharmony_ci} 218362306a36Sopenharmony_ci#endif /* CONFIG_PCI */ 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci /* 218862306a36Sopenharmony_ci * Blank the display. 218962306a36Sopenharmony_ci */ 219062306a36Sopenharmony_cistatic int aty128fb_blank(int blank, struct fb_info *fb) 219162306a36Sopenharmony_ci{ 219262306a36Sopenharmony_ci struct aty128fb_par *par = fb->par; 219362306a36Sopenharmony_ci u8 state; 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci if (par->lock_blank || par->asleep) 219662306a36Sopenharmony_ci return 0; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci switch (blank) { 219962306a36Sopenharmony_ci case FB_BLANK_NORMAL: 220062306a36Sopenharmony_ci state = 4; 220162306a36Sopenharmony_ci break; 220262306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 220362306a36Sopenharmony_ci state = 6; 220462306a36Sopenharmony_ci break; 220562306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 220662306a36Sopenharmony_ci state = 5; 220762306a36Sopenharmony_ci break; 220862306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 220962306a36Sopenharmony_ci state = 7; 221062306a36Sopenharmony_ci break; 221162306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 221262306a36Sopenharmony_ci default: 221362306a36Sopenharmony_ci state = 0; 221462306a36Sopenharmony_ci break; 221562306a36Sopenharmony_ci } 221662306a36Sopenharmony_ci aty_st_8(CRTC_EXT_CNTL+1, state); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci if (par->chip_gen == rage_M3) { 221962306a36Sopenharmony_ci aty128_set_crt_enable(par, par->crt_on && !blank); 222062306a36Sopenharmony_ci aty128_set_lcd_enable(par, par->lcd_on && !blank); 222162306a36Sopenharmony_ci } 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci return 0; 222462306a36Sopenharmony_ci} 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci/* 222762306a36Sopenharmony_ci * Set a single color register. The values supplied are already 222862306a36Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 222962306a36Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 223062306a36Sopenharmony_ci */ 223162306a36Sopenharmony_cistatic int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 223262306a36Sopenharmony_ci u_int transp, struct fb_info *info) 223362306a36Sopenharmony_ci{ 223462306a36Sopenharmony_ci struct aty128fb_par *par = info->par; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci if (regno > 255 223762306a36Sopenharmony_ci || (par->crtc.depth == 16 && regno > 63) 223862306a36Sopenharmony_ci || (par->crtc.depth == 15 && regno > 31)) 223962306a36Sopenharmony_ci return 1; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci red >>= 8; 224262306a36Sopenharmony_ci green >>= 8; 224362306a36Sopenharmony_ci blue >>= 8; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci if (regno < 16) { 224662306a36Sopenharmony_ci int i; 224762306a36Sopenharmony_ci u32 *pal = info->pseudo_palette; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci switch (par->crtc.depth) { 225062306a36Sopenharmony_ci case 15: 225162306a36Sopenharmony_ci pal[regno] = (regno << 10) | (regno << 5) | regno; 225262306a36Sopenharmony_ci break; 225362306a36Sopenharmony_ci case 16: 225462306a36Sopenharmony_ci pal[regno] = (regno << 11) | (regno << 6) | regno; 225562306a36Sopenharmony_ci break; 225662306a36Sopenharmony_ci case 24: 225762306a36Sopenharmony_ci pal[regno] = (regno << 16) | (regno << 8) | regno; 225862306a36Sopenharmony_ci break; 225962306a36Sopenharmony_ci case 32: 226062306a36Sopenharmony_ci i = (regno << 8) | regno; 226162306a36Sopenharmony_ci pal[regno] = (i << 16) | i; 226262306a36Sopenharmony_ci break; 226362306a36Sopenharmony_ci } 226462306a36Sopenharmony_ci } 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci if (par->crtc.depth == 16 && regno > 0) { 226762306a36Sopenharmony_ci /* 226862306a36Sopenharmony_ci * With the 5-6-5 split of bits for RGB at 16 bits/pixel, we 226962306a36Sopenharmony_ci * have 32 slots for R and B values but 64 slots for G values. 227062306a36Sopenharmony_ci * Thus the R and B values go in one slot but the G value 227162306a36Sopenharmony_ci * goes in a different slot, and we have to avoid disturbing 227262306a36Sopenharmony_ci * the other fields in the slots we touch. 227362306a36Sopenharmony_ci */ 227462306a36Sopenharmony_ci par->green[regno] = green; 227562306a36Sopenharmony_ci if (regno < 32) { 227662306a36Sopenharmony_ci par->red[regno] = red; 227762306a36Sopenharmony_ci par->blue[regno] = blue; 227862306a36Sopenharmony_ci aty128_st_pal(regno * 8, red, par->green[regno*2], 227962306a36Sopenharmony_ci blue, par); 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci red = par->red[regno/2]; 228262306a36Sopenharmony_ci blue = par->blue[regno/2]; 228362306a36Sopenharmony_ci regno <<= 2; 228462306a36Sopenharmony_ci } else if (par->crtc.bpp == 16) 228562306a36Sopenharmony_ci regno <<= 3; 228662306a36Sopenharmony_ci aty128_st_pal(regno, red, green, blue, par); 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci return 0; 228962306a36Sopenharmony_ci} 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci#define ATY_MIRROR_LCD_ON 0x00000001 229262306a36Sopenharmony_ci#define ATY_MIRROR_CRT_ON 0x00000002 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci/* out param: u32* backlight value: 0 to 15 */ 229562306a36Sopenharmony_ci#define FBIO_ATY128_GET_MIRROR _IOR('@', 1, __u32) 229662306a36Sopenharmony_ci/* in param: u32* backlight value: 0 to 15 */ 229762306a36Sopenharmony_ci#define FBIO_ATY128_SET_MIRROR _IOW('@', 2, __u32) 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_cistatic int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg) 230062306a36Sopenharmony_ci{ 230162306a36Sopenharmony_ci struct aty128fb_par *par = info->par; 230262306a36Sopenharmony_ci u32 value; 230362306a36Sopenharmony_ci int rc; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci switch (cmd) { 230662306a36Sopenharmony_ci case FBIO_ATY128_SET_MIRROR: 230762306a36Sopenharmony_ci if (par->chip_gen != rage_M3) 230862306a36Sopenharmony_ci return -EINVAL; 230962306a36Sopenharmony_ci rc = get_user(value, (__u32 __user *)arg); 231062306a36Sopenharmony_ci if (rc) 231162306a36Sopenharmony_ci return rc; 231262306a36Sopenharmony_ci par->lcd_on = (value & 0x01) != 0; 231362306a36Sopenharmony_ci par->crt_on = (value & 0x02) != 0; 231462306a36Sopenharmony_ci if (!par->crt_on && !par->lcd_on) 231562306a36Sopenharmony_ci par->lcd_on = 1; 231662306a36Sopenharmony_ci aty128_set_crt_enable(par, par->crt_on); 231762306a36Sopenharmony_ci aty128_set_lcd_enable(par, par->lcd_on); 231862306a36Sopenharmony_ci return 0; 231962306a36Sopenharmony_ci case FBIO_ATY128_GET_MIRROR: 232062306a36Sopenharmony_ci if (par->chip_gen != rage_M3) 232162306a36Sopenharmony_ci return -EINVAL; 232262306a36Sopenharmony_ci value = (par->crt_on << 1) | par->lcd_on; 232362306a36Sopenharmony_ci return put_user(value, (__u32 __user *)arg); 232462306a36Sopenharmony_ci } 232562306a36Sopenharmony_ci return -EINVAL; 232662306a36Sopenharmony_ci} 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_cistatic void aty128_set_suspend(struct aty128fb_par *par, int suspend) 232962306a36Sopenharmony_ci{ 233062306a36Sopenharmony_ci u32 pmgt; 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci if (!par->pdev->pm_cap) 233362306a36Sopenharmony_ci return; 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci /* Set the chip into the appropriate suspend mode (we use D2, 233662306a36Sopenharmony_ci * D3 would require a complete re-initialisation of the chip, 233762306a36Sopenharmony_ci * including PCI config registers, clocks, AGP configuration, ...) 233862306a36Sopenharmony_ci * 233962306a36Sopenharmony_ci * For resume, the core will have already brought us back to D0 234062306a36Sopenharmony_ci */ 234162306a36Sopenharmony_ci if (suspend) { 234262306a36Sopenharmony_ci /* Make sure CRTC2 is reset. Remove that the day we decide to 234362306a36Sopenharmony_ci * actually use CRTC2 and replace it with real code for disabling 234462306a36Sopenharmony_ci * the CRTC2 output during sleep 234562306a36Sopenharmony_ci */ 234662306a36Sopenharmony_ci aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) & 234762306a36Sopenharmony_ci ~(CRTC2_EN)); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci /* Set the power management mode to be PCI based */ 235062306a36Sopenharmony_ci /* Use this magic value for now */ 235162306a36Sopenharmony_ci pmgt = 0x0c005407; 235262306a36Sopenharmony_ci aty_st_pll(POWER_MANAGEMENT, pmgt); 235362306a36Sopenharmony_ci (void)aty_ld_pll(POWER_MANAGEMENT); 235462306a36Sopenharmony_ci aty_st_le32(BUS_CNTL1, 0x00000010); 235562306a36Sopenharmony_ci aty_st_le32(MEM_POWER_MISC, 0x0c830000); 235662306a36Sopenharmony_ci msleep(100); 235762306a36Sopenharmony_ci } 235862306a36Sopenharmony_ci} 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_cistatic int aty128_pci_suspend_late(struct device *dev, pm_message_t state) 236162306a36Sopenharmony_ci{ 236262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 236362306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 236462306a36Sopenharmony_ci struct aty128fb_par *par = info->par; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci /* We don't do anything but D2, for now we return 0, but 236762306a36Sopenharmony_ci * we may want to change that. How do we know if the BIOS 236862306a36Sopenharmony_ci * can properly take care of D3 ? Also, with swsusp, we 236962306a36Sopenharmony_ci * know we'll be rebooted, ... 237062306a36Sopenharmony_ci */ 237162306a36Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 237262306a36Sopenharmony_ci /* HACK ALERT ! Once I find a proper way to say to each driver 237362306a36Sopenharmony_ci * individually what will happen with it's PCI slot, I'll change 237462306a36Sopenharmony_ci * that. On laptops, the AGP slot is just unclocked, so D2 is 237562306a36Sopenharmony_ci * expected, while on desktops, the card is powered off 237662306a36Sopenharmony_ci */ 237762306a36Sopenharmony_ci return 0; 237862306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci if (state.event == pdev->dev.power.power_state.event) 238162306a36Sopenharmony_ci return 0; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci printk(KERN_DEBUG "aty128fb: suspending...\n"); 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci console_lock(); 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci fb_set_suspend(info, 1); 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci /* Make sure engine is reset */ 239062306a36Sopenharmony_ci wait_for_idle(par); 239162306a36Sopenharmony_ci aty128_reset_engine(par); 239262306a36Sopenharmony_ci wait_for_idle(par); 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci /* Blank display and LCD */ 239562306a36Sopenharmony_ci aty128fb_blank(FB_BLANK_POWERDOWN, info); 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci /* Sleep */ 239862306a36Sopenharmony_ci par->asleep = 1; 239962306a36Sopenharmony_ci par->lock_blank = 1; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 240262306a36Sopenharmony_ci /* On powermac, we have hooks to properly suspend/resume AGP now, 240362306a36Sopenharmony_ci * use them here. We'll ultimately need some generic support here, 240462306a36Sopenharmony_ci * but the generic code isn't quite ready for that yet 240562306a36Sopenharmony_ci */ 240662306a36Sopenharmony_ci pmac_suspend_agp_for_card(pdev); 240762306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci /* We need a way to make sure the fbdev layer will _not_ touch the 241062306a36Sopenharmony_ci * framebuffer before we put the chip to suspend state. On 2.4, I 241162306a36Sopenharmony_ci * used dummy fb ops, 2.5 need proper support for this at the 241262306a36Sopenharmony_ci * fbdev level 241362306a36Sopenharmony_ci */ 241462306a36Sopenharmony_ci if (state.event != PM_EVENT_ON) 241562306a36Sopenharmony_ci aty128_set_suspend(par, 1); 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci console_unlock(); 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci pdev->dev.power.power_state = state; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci return 0; 242262306a36Sopenharmony_ci} 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_suspend(struct device *dev) 242562306a36Sopenharmony_ci{ 242662306a36Sopenharmony_ci return aty128_pci_suspend_late(dev, PMSG_SUSPEND); 242762306a36Sopenharmony_ci} 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_hibernate(struct device *dev) 243062306a36Sopenharmony_ci{ 243162306a36Sopenharmony_ci return aty128_pci_suspend_late(dev, PMSG_HIBERNATE); 243262306a36Sopenharmony_ci} 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_freeze(struct device *dev) 243562306a36Sopenharmony_ci{ 243662306a36Sopenharmony_ci return aty128_pci_suspend_late(dev, PMSG_FREEZE); 243762306a36Sopenharmony_ci} 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_cistatic int aty128_do_resume(struct pci_dev *pdev) 244062306a36Sopenharmony_ci{ 244162306a36Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 244262306a36Sopenharmony_ci struct aty128fb_par *par = info->par; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci if (pdev->dev.power.power_state.event == PM_EVENT_ON) 244562306a36Sopenharmony_ci return 0; 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci /* PCI state will have been restored by the core, so 244862306a36Sopenharmony_ci * we should be in D0 now with our config space fully 244962306a36Sopenharmony_ci * restored 245062306a36Sopenharmony_ci */ 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci /* Wakeup chip */ 245362306a36Sopenharmony_ci aty128_set_suspend(par, 0); 245462306a36Sopenharmony_ci par->asleep = 0; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci /* Restore display & engine */ 245762306a36Sopenharmony_ci aty128_reset_engine(par); 245862306a36Sopenharmony_ci wait_for_idle(par); 245962306a36Sopenharmony_ci aty128fb_set_par(info); 246062306a36Sopenharmony_ci fb_pan_display(info, &info->var); 246162306a36Sopenharmony_ci fb_set_cmap(&info->cmap, info); 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci /* Refresh */ 246462306a36Sopenharmony_ci fb_set_suspend(info, 0); 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci /* Unblank */ 246762306a36Sopenharmony_ci par->lock_blank = 0; 246862306a36Sopenharmony_ci aty128fb_blank(0, info); 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 247162306a36Sopenharmony_ci /* On powermac, we have hooks to properly suspend/resume AGP now, 247262306a36Sopenharmony_ci * use them here. We'll ultimately need some generic support here, 247362306a36Sopenharmony_ci * but the generic code isn't quite ready for that yet 247462306a36Sopenharmony_ci */ 247562306a36Sopenharmony_ci pmac_resume_agp_for_card(pdev); 247662306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci pdev->dev.power.power_state = PMSG_ON; 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci printk(KERN_DEBUG "aty128fb: resumed !\n"); 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci return 0; 248362306a36Sopenharmony_ci} 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_resume(struct device *dev) 248662306a36Sopenharmony_ci{ 248762306a36Sopenharmony_ci int rc; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci console_lock(); 249062306a36Sopenharmony_ci rc = aty128_do_resume(to_pci_dev(dev)); 249162306a36Sopenharmony_ci console_unlock(); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci return rc; 249462306a36Sopenharmony_ci} 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_cistatic int aty128fb_init(void) 249862306a36Sopenharmony_ci{ 249962306a36Sopenharmony_ci#ifndef MODULE 250062306a36Sopenharmony_ci char *option = NULL; 250162306a36Sopenharmony_ci#endif 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci if (fb_modesetting_disabled("aty128fb")) 250462306a36Sopenharmony_ci return -ENODEV; 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci#ifndef MODULE 250762306a36Sopenharmony_ci if (fb_get_options("aty128fb", &option)) 250862306a36Sopenharmony_ci return -ENODEV; 250962306a36Sopenharmony_ci aty128fb_setup(option); 251062306a36Sopenharmony_ci#endif 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci return pci_register_driver(&aty128fb_driver); 251362306a36Sopenharmony_ci} 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_cistatic void __exit aty128fb_exit(void) 251662306a36Sopenharmony_ci{ 251762306a36Sopenharmony_ci pci_unregister_driver(&aty128fb_driver); 251862306a36Sopenharmony_ci} 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_cimodule_init(aty128fb_init); 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_cimodule_exit(aty128fb_exit); 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ciMODULE_AUTHOR("(c)1999-2003 Brad Douglas <brad@neruo.com>"); 252562306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards"); 252662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 252762306a36Sopenharmony_cimodule_param(mode_option, charp, 0); 252862306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); 252962306a36Sopenharmony_cimodule_param_named(nomtrr, mtrr, invbool, 0); 253062306a36Sopenharmony_ciMODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)"); 2531