18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* $Id: aty128fb.c,v 1.1.1.1.36.1 1999/12/11 09:03:05 Exp $ 38c2ecf20Sopenharmony_ci * linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1999-2003, Brad Douglas <brad@neruo.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 1999, Anthony Tong <atong@uiuc.edu> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Ani Joshi / Jeff Garzik 98c2ecf20Sopenharmony_ci * - Code cleanup 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Michel Danzer <michdaen@iiic.ethz.ch> 128c2ecf20Sopenharmony_ci * - 15/16 bit cleanup 138c2ecf20Sopenharmony_ci * - fix panning 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Benjamin Herrenschmidt 168c2ecf20Sopenharmony_ci * - pmac-specific PM stuff 178c2ecf20Sopenharmony_ci * - various fixes & cleanups 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Andreas Hundt <andi@convergence.de> 208c2ecf20Sopenharmony_ci * - FB_ACTIVATE fixes 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Paul Mackerras <paulus@samba.org> 238c2ecf20Sopenharmony_ci * - Convert to new framebuffer API, 248c2ecf20Sopenharmony_ci * fix colormap setting at 16 bits/pixel (565) 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Paul Mundt 278c2ecf20Sopenharmony_ci * - PCI hotplug 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Jon Smirl <jonsmirl@yahoo.com> 308c2ecf20Sopenharmony_ci * - PCI ID update 318c2ecf20Sopenharmony_ci * - replace ROM BIOS search 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Based off of Geert's atyfb.c and vfb.c. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * TODO: 368c2ecf20Sopenharmony_ci * - monitor sensing (DDC) 378c2ecf20Sopenharmony_ci * - virtual display 388c2ecf20Sopenharmony_ci * - other platform support (only ppc/x86 supported) 398c2ecf20Sopenharmony_ci * - hardware cursor support 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Please cc: your patches to brad@neruo.com. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * A special note of gratitude to ATI's devrel for providing documentation, 468c2ecf20Sopenharmony_ci * example code and hardware. Thanks Nitya. -atong and brad 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#include <linux/module.h> 518c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 528c2ecf20Sopenharmony_ci#include <linux/kernel.h> 538c2ecf20Sopenharmony_ci#include <linux/errno.h> 548c2ecf20Sopenharmony_ci#include <linux/string.h> 558c2ecf20Sopenharmony_ci#include <linux/mm.h> 568c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 578c2ecf20Sopenharmony_ci#include <linux/delay.h> 588c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 598c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 608c2ecf20Sopenharmony_ci#include <linux/fb.h> 618c2ecf20Sopenharmony_ci#include <linux/init.h> 628c2ecf20Sopenharmony_ci#include <linux/pci.h> 638c2ecf20Sopenharmony_ci#include <linux/ioport.h> 648c2ecf20Sopenharmony_ci#include <linux/console.h> 658c2ecf20Sopenharmony_ci#include <linux/backlight.h> 668c2ecf20Sopenharmony_ci#include <asm/io.h> 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 698c2ecf20Sopenharmony_ci#include <asm/machdep.h> 708c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h> 718c2ecf20Sopenharmony_ci#include <asm/prom.h> 728c2ecf20Sopenharmony_ci#include "../macmodes.h" 738c2ecf20Sopenharmony_ci#endif 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 768c2ecf20Sopenharmony_ci#include <asm/backlight.h> 778c2ecf20Sopenharmony_ci#endif 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 808c2ecf20Sopenharmony_ci#include <asm/btext.h> 818c2ecf20Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#include <video/aty128.h> 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Debug flag */ 868c2ecf20Sopenharmony_ci#undef DEBUG 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#ifdef DEBUG 898c2ecf20Sopenharmony_ci#define DBG(fmt, args...) \ 908c2ecf20Sopenharmony_ci printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args); 918c2ecf20Sopenharmony_ci#else 928c2ecf20Sopenharmony_ci#define DBG(fmt, args...) 938c2ecf20Sopenharmony_ci#endif 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 968c2ecf20Sopenharmony_ci/* default mode */ 978c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo default_var = { 988c2ecf20Sopenharmony_ci /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ 998c2ecf20Sopenharmony_ci 640, 480, 640, 480, 0, 0, 8, 0, 1008c2ecf20Sopenharmony_ci {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 1018c2ecf20Sopenharmony_ci 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2, 1028c2ecf20Sopenharmony_ci 0, FB_VMODE_NONINTERLACED 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#else /* CONFIG_PPC_PMAC */ 1068c2ecf20Sopenharmony_ci/* default to 1024x768 at 75Hz on PPC - this will work 1078c2ecf20Sopenharmony_ci * on the iMac, the usual 640x480 @ 60Hz doesn't. */ 1088c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo default_var = { 1098c2ecf20Sopenharmony_ci /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ 1108c2ecf20Sopenharmony_ci 1024, 768, 1024, 768, 0, 0, 8, 0, 1118c2ecf20Sopenharmony_ci {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 1128c2ecf20Sopenharmony_ci 0, 0, -1, -1, 0, 12699, 160, 32, 28, 1, 96, 3, 1138c2ecf20Sopenharmony_ci FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 1148c2ecf20Sopenharmony_ci FB_VMODE_NONINTERLACED 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* default modedb mode */ 1198c2ecf20Sopenharmony_ci/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */ 1208c2ecf20Sopenharmony_cistatic const struct fb_videomode defaultmode = { 1218c2ecf20Sopenharmony_ci .refresh = 60, 1228c2ecf20Sopenharmony_ci .xres = 640, 1238c2ecf20Sopenharmony_ci .yres = 480, 1248c2ecf20Sopenharmony_ci .pixclock = 39722, 1258c2ecf20Sopenharmony_ci .left_margin = 48, 1268c2ecf20Sopenharmony_ci .right_margin = 16, 1278c2ecf20Sopenharmony_ci .upper_margin = 33, 1288c2ecf20Sopenharmony_ci .lower_margin = 10, 1298c2ecf20Sopenharmony_ci .hsync_len = 96, 1308c2ecf20Sopenharmony_ci .vsync_len = 2, 1318c2ecf20Sopenharmony_ci .sync = 0, 1328c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* Chip generations */ 1368c2ecf20Sopenharmony_cienum { 1378c2ecf20Sopenharmony_ci rage_128, 1388c2ecf20Sopenharmony_ci rage_128_pci, 1398c2ecf20Sopenharmony_ci rage_128_pro, 1408c2ecf20Sopenharmony_ci rage_128_pro_pci, 1418c2ecf20Sopenharmony_ci rage_M3, 1428c2ecf20Sopenharmony_ci rage_M3_pci, 1438c2ecf20Sopenharmony_ci rage_M4, 1448c2ecf20Sopenharmony_ci rage_128_ultra, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* Must match above enum */ 1488c2ecf20Sopenharmony_cistatic char * const r128_family[] = { 1498c2ecf20Sopenharmony_ci "AGP", 1508c2ecf20Sopenharmony_ci "PCI", 1518c2ecf20Sopenharmony_ci "PRO AGP", 1528c2ecf20Sopenharmony_ci "PRO PCI", 1538c2ecf20Sopenharmony_ci "M3 AGP", 1548c2ecf20Sopenharmony_ci "M3 PCI", 1558c2ecf20Sopenharmony_ci "M4 AGP", 1568c2ecf20Sopenharmony_ci "Ultra AGP", 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* 1608c2ecf20Sopenharmony_ci * PCI driver prototypes 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_cistatic int aty128_probe(struct pci_dev *pdev, 1638c2ecf20Sopenharmony_ci const struct pci_device_id *ent); 1648c2ecf20Sopenharmony_cistatic void aty128_remove(struct pci_dev *pdev); 1658c2ecf20Sopenharmony_cistatic int aty128_pci_suspend_late(struct device *dev, pm_message_t state); 1668c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_suspend(struct device *dev); 1678c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_hibernate(struct device *dev); 1688c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_freeze(struct device *dev); 1698c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_resume(struct device *dev); 1708c2ecf20Sopenharmony_cistatic int aty128_do_resume(struct pci_dev *pdev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct dev_pm_ops aty128_pci_pm_ops = { 1738c2ecf20Sopenharmony_ci .suspend = aty128_pci_suspend, 1748c2ecf20Sopenharmony_ci .resume = aty128_pci_resume, 1758c2ecf20Sopenharmony_ci .freeze = aty128_pci_freeze, 1768c2ecf20Sopenharmony_ci .thaw = aty128_pci_resume, 1778c2ecf20Sopenharmony_ci .poweroff = aty128_pci_hibernate, 1788c2ecf20Sopenharmony_ci .restore = aty128_pci_resume, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* supported Rage128 chipsets */ 1828c2ecf20Sopenharmony_cistatic const struct pci_device_id aty128_pci_tbl[] = { 1838c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE, 1848c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci }, 1858c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF, 1868c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 }, 1878c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF, 1888c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, 1898c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML, 1908c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 }, 1918c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA, 1928c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 1938c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB, 1948c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 1958c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC, 1968c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 1978c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD, 1988c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, 1998c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE, 2008c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2018c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF, 2028c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2038c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG, 2048c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2058c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH, 2068c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2078c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI, 2088c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2098c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ, 2108c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2118c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK, 2128c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2138c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL, 2148c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2158c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM, 2168c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2178c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN, 2188c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2198c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO, 2208c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2218c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP, 2228c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, 2238c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ, 2248c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2258c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR, 2268c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci }, 2278c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS, 2288c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2298c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT, 2308c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2318c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU, 2328c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2338c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV, 2348c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2358c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW, 2368c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2378c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX, 2388c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro }, 2398c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE, 2408c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, 2418c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF, 2428c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2438c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG, 2448c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2458c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK, 2468c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, 2478c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL, 2488c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2498c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE, 2508c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2518c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF, 2528c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci }, 2538c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG, 2548c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2558c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH, 2568c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2578c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK, 2588c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2598c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL, 2608c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2618c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM, 2628c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2638c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN, 2648c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 }, 2658c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF, 2668c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 2678c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL, 2688c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 2698c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR, 2708c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 2718c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS, 2728c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 2738c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT, 2748c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 2758c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU, 2768c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra }, 2778c2ecf20Sopenharmony_ci { 0, } 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, aty128_pci_tbl); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic struct pci_driver aty128fb_driver = { 2838c2ecf20Sopenharmony_ci .name = "aty128fb", 2848c2ecf20Sopenharmony_ci .id_table = aty128_pci_tbl, 2858c2ecf20Sopenharmony_ci .probe = aty128_probe, 2868c2ecf20Sopenharmony_ci .remove = aty128_remove, 2878c2ecf20Sopenharmony_ci .driver.pm = &aty128_pci_pm_ops, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* packed BIOS settings */ 2918c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC 2928c2ecf20Sopenharmony_citypedef struct { 2938c2ecf20Sopenharmony_ci u8 clock_chip_type; 2948c2ecf20Sopenharmony_ci u8 struct_size; 2958c2ecf20Sopenharmony_ci u8 accelerator_entry; 2968c2ecf20Sopenharmony_ci u8 VGA_entry; 2978c2ecf20Sopenharmony_ci u16 VGA_table_offset; 2988c2ecf20Sopenharmony_ci u16 POST_table_offset; 2998c2ecf20Sopenharmony_ci u16 XCLK; 3008c2ecf20Sopenharmony_ci u16 MCLK; 3018c2ecf20Sopenharmony_ci u8 num_PLL_blocks; 3028c2ecf20Sopenharmony_ci u8 size_PLL_blocks; 3038c2ecf20Sopenharmony_ci u16 PCLK_ref_freq; 3048c2ecf20Sopenharmony_ci u16 PCLK_ref_divider; 3058c2ecf20Sopenharmony_ci u32 PCLK_min_freq; 3068c2ecf20Sopenharmony_ci u32 PCLK_max_freq; 3078c2ecf20Sopenharmony_ci u16 MCLK_ref_freq; 3088c2ecf20Sopenharmony_ci u16 MCLK_ref_divider; 3098c2ecf20Sopenharmony_ci u32 MCLK_min_freq; 3108c2ecf20Sopenharmony_ci u32 MCLK_max_freq; 3118c2ecf20Sopenharmony_ci u16 XCLK_ref_freq; 3128c2ecf20Sopenharmony_ci u16 XCLK_ref_divider; 3138c2ecf20Sopenharmony_ci u32 XCLK_min_freq; 3148c2ecf20Sopenharmony_ci u32 XCLK_max_freq; 3158c2ecf20Sopenharmony_ci} __attribute__ ((packed)) PLL_BLOCK; 3168c2ecf20Sopenharmony_ci#endif /* !CONFIG_PPC */ 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* onboard memory information */ 3198c2ecf20Sopenharmony_cistruct aty128_meminfo { 3208c2ecf20Sopenharmony_ci u8 ML; 3218c2ecf20Sopenharmony_ci u8 MB; 3228c2ecf20Sopenharmony_ci u8 Trcd; 3238c2ecf20Sopenharmony_ci u8 Trp; 3248c2ecf20Sopenharmony_ci u8 Twr; 3258c2ecf20Sopenharmony_ci u8 CL; 3268c2ecf20Sopenharmony_ci u8 Tr2w; 3278c2ecf20Sopenharmony_ci u8 LoopLatency; 3288c2ecf20Sopenharmony_ci u8 DspOn; 3298c2ecf20Sopenharmony_ci u8 Rloop; 3308c2ecf20Sopenharmony_ci const char *name; 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* various memory configurations */ 3348c2ecf20Sopenharmony_cistatic const struct aty128_meminfo sdr_128 = { 3358c2ecf20Sopenharmony_ci .ML = 4, 3368c2ecf20Sopenharmony_ci .MB = 4, 3378c2ecf20Sopenharmony_ci .Trcd = 3, 3388c2ecf20Sopenharmony_ci .Trp = 3, 3398c2ecf20Sopenharmony_ci .Twr = 1, 3408c2ecf20Sopenharmony_ci .CL = 3, 3418c2ecf20Sopenharmony_ci .Tr2w = 1, 3428c2ecf20Sopenharmony_ci .LoopLatency = 16, 3438c2ecf20Sopenharmony_ci .DspOn = 30, 3448c2ecf20Sopenharmony_ci .Rloop = 16, 3458c2ecf20Sopenharmony_ci .name = "128-bit SDR SGRAM (1:1)", 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic const struct aty128_meminfo sdr_sgram = { 3498c2ecf20Sopenharmony_ci .ML = 4, 3508c2ecf20Sopenharmony_ci .MB = 4, 3518c2ecf20Sopenharmony_ci .Trcd = 1, 3528c2ecf20Sopenharmony_ci .Trp = 2, 3538c2ecf20Sopenharmony_ci .Twr = 1, 3548c2ecf20Sopenharmony_ci .CL = 2, 3558c2ecf20Sopenharmony_ci .Tr2w = 1, 3568c2ecf20Sopenharmony_ci .LoopLatency = 16, 3578c2ecf20Sopenharmony_ci .DspOn = 24, 3588c2ecf20Sopenharmony_ci .Rloop = 16, 3598c2ecf20Sopenharmony_ci .name = "64-bit SDR SGRAM (2:1)", 3608c2ecf20Sopenharmony_ci}; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic const struct aty128_meminfo ddr_sgram = { 3638c2ecf20Sopenharmony_ci .ML = 4, 3648c2ecf20Sopenharmony_ci .MB = 4, 3658c2ecf20Sopenharmony_ci .Trcd = 3, 3668c2ecf20Sopenharmony_ci .Trp = 3, 3678c2ecf20Sopenharmony_ci .Twr = 2, 3688c2ecf20Sopenharmony_ci .CL = 3, 3698c2ecf20Sopenharmony_ci .Tr2w = 1, 3708c2ecf20Sopenharmony_ci .LoopLatency = 16, 3718c2ecf20Sopenharmony_ci .DspOn = 31, 3728c2ecf20Sopenharmony_ci .Rloop = 16, 3738c2ecf20Sopenharmony_ci .name = "64-bit DDR SGRAM", 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo aty128fb_fix = { 3778c2ecf20Sopenharmony_ci .id = "ATY Rage128", 3788c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 3798c2ecf20Sopenharmony_ci .visual = FB_VISUAL_PSEUDOCOLOR, 3808c2ecf20Sopenharmony_ci .xpanstep = 8, 3818c2ecf20Sopenharmony_ci .ypanstep = 1, 3828c2ecf20Sopenharmony_ci .mmio_len = 0x2000, 3838c2ecf20Sopenharmony_ci .accel = FB_ACCEL_ATI_RAGE128, 3848c2ecf20Sopenharmony_ci}; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic char *mode_option = NULL; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 3898c2ecf20Sopenharmony_cistatic int default_vmode = VMODE_1024_768_60; 3908c2ecf20Sopenharmony_cistatic int default_cmode = CMODE_8; 3918c2ecf20Sopenharmony_ci#endif 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int default_crt_on = 0; 3948c2ecf20Sopenharmony_cistatic int default_lcd_on = 1; 3958c2ecf20Sopenharmony_cistatic bool mtrr = true; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 3988c2ecf20Sopenharmony_cistatic int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT); 3998c2ecf20Sopenharmony_ci#endif 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/* PLL constants */ 4028c2ecf20Sopenharmony_cistruct aty128_constants { 4038c2ecf20Sopenharmony_ci u32 ref_clk; 4048c2ecf20Sopenharmony_ci u32 ppll_min; 4058c2ecf20Sopenharmony_ci u32 ppll_max; 4068c2ecf20Sopenharmony_ci u32 ref_divider; 4078c2ecf20Sopenharmony_ci u32 xclk; 4088c2ecf20Sopenharmony_ci u32 fifo_width; 4098c2ecf20Sopenharmony_ci u32 fifo_depth; 4108c2ecf20Sopenharmony_ci}; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistruct aty128_crtc { 4138c2ecf20Sopenharmony_ci u32 gen_cntl; 4148c2ecf20Sopenharmony_ci u32 h_total, h_sync_strt_wid; 4158c2ecf20Sopenharmony_ci u32 v_total, v_sync_strt_wid; 4168c2ecf20Sopenharmony_ci u32 pitch; 4178c2ecf20Sopenharmony_ci u32 offset, offset_cntl; 4188c2ecf20Sopenharmony_ci u32 xoffset, yoffset; 4198c2ecf20Sopenharmony_ci u32 vxres, vyres; 4208c2ecf20Sopenharmony_ci u32 depth, bpp; 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistruct aty128_pll { 4248c2ecf20Sopenharmony_ci u32 post_divider; 4258c2ecf20Sopenharmony_ci u32 feedback_divider; 4268c2ecf20Sopenharmony_ci u32 vclk; 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistruct aty128_ddafifo { 4308c2ecf20Sopenharmony_ci u32 dda_config; 4318c2ecf20Sopenharmony_ci u32 dda_on_off; 4328c2ecf20Sopenharmony_ci}; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/* register values for a specific mode */ 4358c2ecf20Sopenharmony_cistruct aty128fb_par { 4368c2ecf20Sopenharmony_ci struct aty128_crtc crtc; 4378c2ecf20Sopenharmony_ci struct aty128_pll pll; 4388c2ecf20Sopenharmony_ci struct aty128_ddafifo fifo_reg; 4398c2ecf20Sopenharmony_ci u32 accel_flags; 4408c2ecf20Sopenharmony_ci struct aty128_constants constants; /* PLL and others */ 4418c2ecf20Sopenharmony_ci void __iomem *regbase; /* remapped mmio */ 4428c2ecf20Sopenharmony_ci u32 vram_size; /* onboard video ram */ 4438c2ecf20Sopenharmony_ci int chip_gen; 4448c2ecf20Sopenharmony_ci const struct aty128_meminfo *mem; /* onboard mem info */ 4458c2ecf20Sopenharmony_ci int wc_cookie; 4468c2ecf20Sopenharmony_ci int blitter_may_be_busy; 4478c2ecf20Sopenharmony_ci int fifo_slots; /* free slots in FIFO (64 max) */ 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci int crt_on, lcd_on; 4508c2ecf20Sopenharmony_ci struct pci_dev *pdev; 4518c2ecf20Sopenharmony_ci struct fb_info *next; 4528c2ecf20Sopenharmony_ci int asleep; 4538c2ecf20Sopenharmony_ci int lock_blank; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci u8 red[32]; /* see aty128fb_setcolreg */ 4568c2ecf20Sopenharmony_ci u8 green[64]; 4578c2ecf20Sopenharmony_ci u8 blue[32]; 4588c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; /* used for TRUECOLOR */ 4598c2ecf20Sopenharmony_ci}; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#define round_div(n, d) ((n+(d/2))/d) 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic int aty128fb_check_var(struct fb_var_screeninfo *var, 4658c2ecf20Sopenharmony_ci struct fb_info *info); 4668c2ecf20Sopenharmony_cistatic int aty128fb_set_par(struct fb_info *info); 4678c2ecf20Sopenharmony_cistatic int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 4688c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info); 4698c2ecf20Sopenharmony_cistatic int aty128fb_pan_display(struct fb_var_screeninfo *var, 4708c2ecf20Sopenharmony_ci struct fb_info *fb); 4718c2ecf20Sopenharmony_cistatic int aty128fb_blank(int blank, struct fb_info *fb); 4728c2ecf20Sopenharmony_cistatic int aty128fb_ioctl(struct fb_info *info, u_int cmd, unsigned long arg); 4738c2ecf20Sopenharmony_cistatic int aty128fb_sync(struct fb_info *info); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci /* 4768c2ecf20Sopenharmony_ci * Internal routines 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int aty128_encode_var(struct fb_var_screeninfo *var, 4808c2ecf20Sopenharmony_ci const struct aty128fb_par *par); 4818c2ecf20Sopenharmony_cistatic int aty128_decode_var(struct fb_var_screeninfo *var, 4828c2ecf20Sopenharmony_ci struct aty128fb_par *par); 4838c2ecf20Sopenharmony_cistatic void aty128_timings(struct aty128fb_par *par); 4848c2ecf20Sopenharmony_cistatic void aty128_init_engine(struct aty128fb_par *par); 4858c2ecf20Sopenharmony_cistatic void aty128_reset_engine(const struct aty128fb_par *par); 4868c2ecf20Sopenharmony_cistatic void aty128_flush_pixel_cache(const struct aty128fb_par *par); 4878c2ecf20Sopenharmony_cistatic void do_wait_for_fifo(u16 entries, struct aty128fb_par *par); 4888c2ecf20Sopenharmony_cistatic void wait_for_fifo(u16 entries, struct aty128fb_par *par); 4898c2ecf20Sopenharmony_cistatic void wait_for_idle(struct aty128fb_par *par); 4908c2ecf20Sopenharmony_cistatic u32 depth_to_dst(u32 depth); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 4938c2ecf20Sopenharmony_cistatic void aty128_bl_set_power(struct fb_info *info, int power); 4948c2ecf20Sopenharmony_ci#endif 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci#define BIOS_IN8(v) (readb(bios + (v))) 4978c2ecf20Sopenharmony_ci#define BIOS_IN16(v) (readb(bios + (v)) | \ 4988c2ecf20Sopenharmony_ci (readb(bios + (v) + 1) << 8)) 4998c2ecf20Sopenharmony_ci#define BIOS_IN32(v) (readb(bios + (v)) | \ 5008c2ecf20Sopenharmony_ci (readb(bios + (v) + 1) << 8) | \ 5018c2ecf20Sopenharmony_ci (readb(bios + (v) + 2) << 16) | \ 5028c2ecf20Sopenharmony_ci (readb(bios + (v) + 3) << 24)) 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic const struct fb_ops aty128fb_ops = { 5068c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 5078c2ecf20Sopenharmony_ci .fb_check_var = aty128fb_check_var, 5088c2ecf20Sopenharmony_ci .fb_set_par = aty128fb_set_par, 5098c2ecf20Sopenharmony_ci .fb_setcolreg = aty128fb_setcolreg, 5108c2ecf20Sopenharmony_ci .fb_pan_display = aty128fb_pan_display, 5118c2ecf20Sopenharmony_ci .fb_blank = aty128fb_blank, 5128c2ecf20Sopenharmony_ci .fb_ioctl = aty128fb_ioctl, 5138c2ecf20Sopenharmony_ci .fb_sync = aty128fb_sync, 5148c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 5158c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 5168c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 5178c2ecf20Sopenharmony_ci}; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* 5208c2ecf20Sopenharmony_ci * Functions to read from/write to the mmio registers 5218c2ecf20Sopenharmony_ci * - endian conversions may possibly be avoided by 5228c2ecf20Sopenharmony_ci * using the other register aperture. TODO. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_cistatic inline u32 _aty_ld_le32(volatile unsigned int regindex, 5258c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci return readl (par->regbase + regindex); 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic inline void _aty_st_le32(volatile unsigned int regindex, u32 val, 5318c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci writel (val, par->regbase + regindex); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic inline u8 _aty_ld_8(unsigned int regindex, 5378c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci return readb (par->regbase + regindex); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic inline void _aty_st_8(unsigned int regindex, u8 val, 5438c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci writeb (val, par->regbase + regindex); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci#define aty_ld_le32(regindex) _aty_ld_le32(regindex, par) 5498c2ecf20Sopenharmony_ci#define aty_st_le32(regindex, val) _aty_st_le32(regindex, val, par) 5508c2ecf20Sopenharmony_ci#define aty_ld_8(regindex) _aty_ld_8(regindex, par) 5518c2ecf20Sopenharmony_ci#define aty_st_8(regindex, val) _aty_st_8(regindex, val, par) 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* 5548c2ecf20Sopenharmony_ci * Functions to read from/write to the pll registers 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci#define aty_ld_pll(pll_index) _aty_ld_pll(pll_index, par) 5588c2ecf20Sopenharmony_ci#define aty_st_pll(pll_index, val) _aty_st_pll(pll_index, val, par) 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic u32 _aty_ld_pll(unsigned int pll_index, 5628c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F); 5658c2ecf20Sopenharmony_ci return aty_ld_le32(CLOCK_CNTL_DATA); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void _aty_st_pll(unsigned int pll_index, u32 val, 5708c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN); 5738c2ecf20Sopenharmony_ci aty_st_le32(CLOCK_CNTL_DATA, val); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/* return true when the PLL has completed an atomic update */ 5788c2ecf20Sopenharmony_cistatic int aty_pll_readupdate(const struct aty128fb_par *par) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void aty_pll_wait_readupdate(const struct aty128fb_par *par) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + HZ/100; // should be more than enough 5878c2ecf20Sopenharmony_ci int reset = 1; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) 5908c2ecf20Sopenharmony_ci if (aty_pll_readupdate(par)) { 5918c2ecf20Sopenharmony_ci reset = 0; 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (reset) /* reset engine?? */ 5968c2ecf20Sopenharmony_ci printk(KERN_DEBUG "aty128fb: PLL write timeout!\n"); 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci/* tell PLL to update */ 6018c2ecf20Sopenharmony_cistatic void aty_pll_writeupdate(const struct aty128fb_par *par) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci aty_pll_wait_readupdate(par); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci aty_st_pll(PPLL_REF_DIV, 6068c2ecf20Sopenharmony_ci aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci/* write to the scratch register to test r/w functionality */ 6118c2ecf20Sopenharmony_cistatic int register_test(const struct aty128fb_par *par) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci u32 val; 6148c2ecf20Sopenharmony_ci int flag = 0; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci val = aty_ld_le32(BIOS_0_SCRATCH); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci aty_st_le32(BIOS_0_SCRATCH, 0x55555555); 6198c2ecf20Sopenharmony_ci if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) { 6208c2ecf20Sopenharmony_ci aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA) 6238c2ecf20Sopenharmony_ci flag = 1; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci aty_st_le32(BIOS_0_SCRATCH, val); // restore value 6278c2ecf20Sopenharmony_ci return flag; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/* 6328c2ecf20Sopenharmony_ci * Accelerator engine functions 6338c2ecf20Sopenharmony_ci */ 6348c2ecf20Sopenharmony_cistatic void do_wait_for_fifo(u16 entries, struct aty128fb_par *par) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci int i; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci for (;;) { 6398c2ecf20Sopenharmony_ci for (i = 0; i < 2000000; i++) { 6408c2ecf20Sopenharmony_ci par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff; 6418c2ecf20Sopenharmony_ci if (par->fifo_slots >= entries) 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci aty128_reset_engine(par); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic void wait_for_idle(struct aty128fb_par *par) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci int i; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci do_wait_for_fifo(64, par); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci for (;;) { 6568c2ecf20Sopenharmony_ci for (i = 0; i < 2000000; i++) { 6578c2ecf20Sopenharmony_ci if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) { 6588c2ecf20Sopenharmony_ci aty128_flush_pixel_cache(par); 6598c2ecf20Sopenharmony_ci par->blitter_may_be_busy = 0; 6608c2ecf20Sopenharmony_ci return; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci aty128_reset_engine(par); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void wait_for_fifo(u16 entries, struct aty128fb_par *par) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci if (par->fifo_slots < entries) 6718c2ecf20Sopenharmony_ci do_wait_for_fifo(64, par); 6728c2ecf20Sopenharmony_ci par->fifo_slots -= entries; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic void aty128_flush_pixel_cache(const struct aty128fb_par *par) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci int i; 6798c2ecf20Sopenharmony_ci u32 tmp; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci tmp = aty_ld_le32(PC_NGUI_CTLSTAT); 6828c2ecf20Sopenharmony_ci tmp &= ~(0x00ff); 6838c2ecf20Sopenharmony_ci tmp |= 0x00ff; 6848c2ecf20Sopenharmony_ci aty_st_le32(PC_NGUI_CTLSTAT, tmp); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci for (i = 0; i < 2000000; i++) 6878c2ecf20Sopenharmony_ci if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY)) 6888c2ecf20Sopenharmony_ci break; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic void aty128_reset_engine(const struct aty128fb_par *par) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci u32 gen_reset_cntl, clock_cntl_index, mclk_cntl; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci aty128_flush_pixel_cache(par); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX); 6998c2ecf20Sopenharmony_ci mclk_cntl = aty_ld_pll(MCLK_CNTL); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL); 7048c2ecf20Sopenharmony_ci aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI); 7058c2ecf20Sopenharmony_ci aty_ld_le32(GEN_RESET_CNTL); 7068c2ecf20Sopenharmony_ci aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI)); 7078c2ecf20Sopenharmony_ci aty_ld_le32(GEN_RESET_CNTL); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci aty_st_pll(MCLK_CNTL, mclk_cntl); 7108c2ecf20Sopenharmony_ci aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index); 7118c2ecf20Sopenharmony_ci aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* use old pio mode */ 7148c2ecf20Sopenharmony_ci aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci DBG("engine reset"); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic void aty128_init_engine(struct aty128fb_par *par) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci u32 pitch_value; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci wait_for_idle(par); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* 3D scaler not spoken here */ 7278c2ecf20Sopenharmony_ci wait_for_fifo(1, par); 7288c2ecf20Sopenharmony_ci aty_st_le32(SCALE_3D_CNTL, 0x00000000); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci aty128_reset_engine(par); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci pitch_value = par->crtc.pitch; 7338c2ecf20Sopenharmony_ci if (par->crtc.bpp == 24) { 7348c2ecf20Sopenharmony_ci pitch_value = pitch_value * 3; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci wait_for_fifo(4, par); 7388c2ecf20Sopenharmony_ci /* setup engine offset registers */ 7398c2ecf20Sopenharmony_ci aty_st_le32(DEFAULT_OFFSET, 0x00000000); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* setup engine pitch registers */ 7428c2ecf20Sopenharmony_ci aty_st_le32(DEFAULT_PITCH, pitch_value); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* set the default scissor register to max dimensions */ 7458c2ecf20Sopenharmony_ci aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* set the drawing controls registers */ 7488c2ecf20Sopenharmony_ci aty_st_le32(DP_GUI_MASTER_CNTL, 7498c2ecf20Sopenharmony_ci GMC_SRC_PITCH_OFFSET_DEFAULT | 7508c2ecf20Sopenharmony_ci GMC_DST_PITCH_OFFSET_DEFAULT | 7518c2ecf20Sopenharmony_ci GMC_SRC_CLIP_DEFAULT | 7528c2ecf20Sopenharmony_ci GMC_DST_CLIP_DEFAULT | 7538c2ecf20Sopenharmony_ci GMC_BRUSH_SOLIDCOLOR | 7548c2ecf20Sopenharmony_ci (depth_to_dst(par->crtc.depth) << 8) | 7558c2ecf20Sopenharmony_ci GMC_SRC_DSTCOLOR | 7568c2ecf20Sopenharmony_ci GMC_BYTE_ORDER_MSB_TO_LSB | 7578c2ecf20Sopenharmony_ci GMC_DP_CONVERSION_TEMP_6500 | 7588c2ecf20Sopenharmony_ci ROP3_PATCOPY | 7598c2ecf20Sopenharmony_ci GMC_DP_SRC_RECT | 7608c2ecf20Sopenharmony_ci GMC_3D_FCN_EN_CLR | 7618c2ecf20Sopenharmony_ci GMC_DST_CLR_CMP_FCN_CLEAR | 7628c2ecf20Sopenharmony_ci GMC_AUX_CLIP_CLEAR | 7638c2ecf20Sopenharmony_ci GMC_WRITE_MASK_SET); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci wait_for_fifo(8, par); 7668c2ecf20Sopenharmony_ci /* clear the line drawing registers */ 7678c2ecf20Sopenharmony_ci aty_st_le32(DST_BRES_ERR, 0); 7688c2ecf20Sopenharmony_ci aty_st_le32(DST_BRES_INC, 0); 7698c2ecf20Sopenharmony_ci aty_st_le32(DST_BRES_DEC, 0); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* set brush color registers */ 7728c2ecf20Sopenharmony_ci aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */ 7738c2ecf20Sopenharmony_ci aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */ 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci /* set source color registers */ 7768c2ecf20Sopenharmony_ci aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF); /* white */ 7778c2ecf20Sopenharmony_ci aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000); /* black */ 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* default write mask */ 7808c2ecf20Sopenharmony_ci aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* Wait for all the writes to be completed before returning */ 7838c2ecf20Sopenharmony_ci wait_for_idle(par); 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci/* convert depth values to their register representation */ 7888c2ecf20Sopenharmony_cistatic u32 depth_to_dst(u32 depth) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci if (depth <= 8) 7918c2ecf20Sopenharmony_ci return DST_8BPP; 7928c2ecf20Sopenharmony_ci else if (depth <= 15) 7938c2ecf20Sopenharmony_ci return DST_15BPP; 7948c2ecf20Sopenharmony_ci else if (depth == 16) 7958c2ecf20Sopenharmony_ci return DST_16BPP; 7968c2ecf20Sopenharmony_ci else if (depth <= 24) 7978c2ecf20Sopenharmony_ci return DST_24BPP; 7988c2ecf20Sopenharmony_ci else if (depth <= 32) 7998c2ecf20Sopenharmony_ci return DST_32BPP; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return -EINVAL; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci/* 8058c2ecf20Sopenharmony_ci * PLL informations retreival 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci#ifndef __sparc__ 8108c2ecf20Sopenharmony_cistatic void __iomem *aty128_map_ROM(const struct aty128fb_par *par, 8118c2ecf20Sopenharmony_ci struct pci_dev *dev) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci u16 dptr; 8148c2ecf20Sopenharmony_ci u8 rom_type; 8158c2ecf20Sopenharmony_ci void __iomem *bios; 8168c2ecf20Sopenharmony_ci size_t rom_size; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */ 8198c2ecf20Sopenharmony_ci unsigned int temp; 8208c2ecf20Sopenharmony_ci temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); 8218c2ecf20Sopenharmony_ci temp &= 0x00ffffffu; 8228c2ecf20Sopenharmony_ci temp |= 0x04 << 24; 8238c2ecf20Sopenharmony_ci aty_st_le32(RAGE128_MPP_TB_CONFIG, temp); 8248c2ecf20Sopenharmony_ci temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci bios = pci_map_rom(dev, &rom_size); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (!bios) { 8298c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: ROM failed to map\n"); 8308c2ecf20Sopenharmony_ci return NULL; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* Very simple test to make sure it appeared */ 8348c2ecf20Sopenharmony_ci if (BIOS_IN16(0) != 0xaa55) { 8358c2ecf20Sopenharmony_ci printk(KERN_DEBUG "aty128fb: Invalid ROM signature %x should " 8368c2ecf20Sopenharmony_ci " be 0xaa55\n", BIOS_IN16(0)); 8378c2ecf20Sopenharmony_ci goto failed; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* Look for the PCI data to check the ROM type */ 8418c2ecf20Sopenharmony_ci dptr = BIOS_IN16(0x18); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* Check the PCI data signature. If it's wrong, we still assume a normal 8448c2ecf20Sopenharmony_ci * x86 ROM for now, until I've verified this works everywhere. 8458c2ecf20Sopenharmony_ci * The goal here is more to phase out Open Firmware images. 8468c2ecf20Sopenharmony_ci * 8478c2ecf20Sopenharmony_ci * Currently, we only look at the first PCI data, we could iteratre and 8488c2ecf20Sopenharmony_ci * deal with them all, and we should use fb_bios_start relative to start 8498c2ecf20Sopenharmony_ci * of image and not relative start of ROM, but so far, I never found a 8508c2ecf20Sopenharmony_ci * dual-image ATI card. 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * typedef struct { 8538c2ecf20Sopenharmony_ci * u32 signature; + 0x00 8548c2ecf20Sopenharmony_ci * u16 vendor; + 0x04 8558c2ecf20Sopenharmony_ci * u16 device; + 0x06 8568c2ecf20Sopenharmony_ci * u16 reserved_1; + 0x08 8578c2ecf20Sopenharmony_ci * u16 dlen; + 0x0a 8588c2ecf20Sopenharmony_ci * u8 drevision; + 0x0c 8598c2ecf20Sopenharmony_ci * u8 class_hi; + 0x0d 8608c2ecf20Sopenharmony_ci * u16 class_lo; + 0x0e 8618c2ecf20Sopenharmony_ci * u16 ilen; + 0x10 8628c2ecf20Sopenharmony_ci * u16 irevision; + 0x12 8638c2ecf20Sopenharmony_ci * u8 type; + 0x14 8648c2ecf20Sopenharmony_ci * u8 indicator; + 0x15 8658c2ecf20Sopenharmony_ci * u16 reserved_2; + 0x16 8668c2ecf20Sopenharmony_ci * } pci_data_t; 8678c2ecf20Sopenharmony_ci */ 8688c2ecf20Sopenharmony_ci if (BIOS_IN32(dptr) != (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) { 8698c2ecf20Sopenharmony_ci printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n", 8708c2ecf20Sopenharmony_ci BIOS_IN32(dptr)); 8718c2ecf20Sopenharmony_ci goto anyway; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci rom_type = BIOS_IN8(dptr + 0x14); 8748c2ecf20Sopenharmony_ci switch(rom_type) { 8758c2ecf20Sopenharmony_ci case 0: 8768c2ecf20Sopenharmony_ci printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n"); 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci case 1: 8798c2ecf20Sopenharmony_ci printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n"); 8808c2ecf20Sopenharmony_ci goto failed; 8818c2ecf20Sopenharmony_ci case 2: 8828c2ecf20Sopenharmony_ci printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n"); 8838c2ecf20Sopenharmony_ci goto failed; 8848c2ecf20Sopenharmony_ci default: 8858c2ecf20Sopenharmony_ci printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", 8868c2ecf20Sopenharmony_ci rom_type); 8878c2ecf20Sopenharmony_ci goto failed; 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci anyway: 8908c2ecf20Sopenharmony_ci return bios; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci failed: 8938c2ecf20Sopenharmony_ci pci_unmap_rom(dev, bios); 8948c2ecf20Sopenharmony_ci return NULL; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic void aty128_get_pllinfo(struct aty128fb_par *par, 8988c2ecf20Sopenharmony_ci unsigned char __iomem *bios) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci unsigned int bios_hdr; 9018c2ecf20Sopenharmony_ci unsigned int bios_pll; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci bios_hdr = BIOS_IN16(0x48); 9048c2ecf20Sopenharmony_ci bios_pll = BIOS_IN16(bios_hdr + 0x30); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16); 9078c2ecf20Sopenharmony_ci par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12); 9088c2ecf20Sopenharmony_ci par->constants.xclk = BIOS_IN16(bios_pll + 0x08); 9098c2ecf20Sopenharmony_ci par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10); 9108c2ecf20Sopenharmony_ci par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n", 9138c2ecf20Sopenharmony_ci par->constants.ppll_max, par->constants.ppll_min, 9148c2ecf20Sopenharmony_ci par->constants.xclk, par->constants.ref_divider, 9158c2ecf20Sopenharmony_ci par->constants.ref_clk); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 9208c2ecf20Sopenharmony_cistatic void __iomem *aty128_find_mem_vbios(struct aty128fb_par *par) 9218c2ecf20Sopenharmony_ci{ 9228c2ecf20Sopenharmony_ci /* I simplified this code as we used to miss the signatures in 9238c2ecf20Sopenharmony_ci * a lot of case. It's now closer to XFree, we just don't check 9248c2ecf20Sopenharmony_ci * for signatures at all... Something better will have to be done 9258c2ecf20Sopenharmony_ci * if we end up having conflicts 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_ci u32 segstart; 9288c2ecf20Sopenharmony_ci unsigned char __iomem *rom_base = NULL; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) { 9318c2ecf20Sopenharmony_ci rom_base = ioremap(segstart, 0x10000); 9328c2ecf20Sopenharmony_ci if (rom_base == NULL) 9338c2ecf20Sopenharmony_ci return NULL; 9348c2ecf20Sopenharmony_ci if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa) 9358c2ecf20Sopenharmony_ci break; 9368c2ecf20Sopenharmony_ci iounmap(rom_base); 9378c2ecf20Sopenharmony_ci rom_base = NULL; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci return rom_base; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci#endif 9428c2ecf20Sopenharmony_ci#endif /* ndef(__sparc__) */ 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci/* fill in known card constants if pll_block is not available */ 9458c2ecf20Sopenharmony_cistatic void aty128_timings(struct aty128fb_par *par) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 9488c2ecf20Sopenharmony_ci /* instead of a table lookup, assume OF has properly 9498c2ecf20Sopenharmony_ci * setup the PLL registers and use their values 9508c2ecf20Sopenharmony_ci * to set the XCLK values and reference divider values */ 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci u32 x_mpll_ref_fb_div; 9538c2ecf20Sopenharmony_ci u32 xclk_cntl; 9548c2ecf20Sopenharmony_ci u32 Nx, M; 9558c2ecf20Sopenharmony_ci unsigned PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 }; 9568c2ecf20Sopenharmony_ci#endif 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (!par->constants.ref_clk) 9598c2ecf20Sopenharmony_ci par->constants.ref_clk = 2950; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC 9628c2ecf20Sopenharmony_ci x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV); 9638c2ecf20Sopenharmony_ci xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7; 9648c2ecf20Sopenharmony_ci Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8; 9658c2ecf20Sopenharmony_ci M = x_mpll_ref_fb_div & 0x0000ff; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk), 9688c2ecf20Sopenharmony_ci (M * PostDivSet[xclk_cntl])); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci par->constants.ref_divider = 9718c2ecf20Sopenharmony_ci aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; 9728c2ecf20Sopenharmony_ci#endif 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (!par->constants.ref_divider) { 9758c2ecf20Sopenharmony_ci par->constants.ref_divider = 0x3b; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e); 9788c2ecf20Sopenharmony_ci aty_pll_writeupdate(par); 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider); 9818c2ecf20Sopenharmony_ci aty_pll_writeupdate(par); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* from documentation */ 9848c2ecf20Sopenharmony_ci if (!par->constants.ppll_min) 9858c2ecf20Sopenharmony_ci par->constants.ppll_min = 12500; 9868c2ecf20Sopenharmony_ci if (!par->constants.ppll_max) 9878c2ecf20Sopenharmony_ci par->constants.ppll_max = 25000; /* 23000 on some cards? */ 9888c2ecf20Sopenharmony_ci if (!par->constants.xclk) 9898c2ecf20Sopenharmony_ci par->constants.xclk = 0x1d4d; /* same as mclk */ 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci par->constants.fifo_width = 128; 9928c2ecf20Sopenharmony_ci par->constants.fifo_depth = 32; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci switch (aty_ld_le32(MEM_CNTL) & 0x3) { 9958c2ecf20Sopenharmony_ci case 0: 9968c2ecf20Sopenharmony_ci par->mem = &sdr_128; 9978c2ecf20Sopenharmony_ci break; 9988c2ecf20Sopenharmony_ci case 1: 9998c2ecf20Sopenharmony_ci par->mem = &sdr_sgram; 10008c2ecf20Sopenharmony_ci break; 10018c2ecf20Sopenharmony_ci case 2: 10028c2ecf20Sopenharmony_ci par->mem = &ddr_sgram; 10038c2ecf20Sopenharmony_ci break; 10048c2ecf20Sopenharmony_ci default: 10058c2ecf20Sopenharmony_ci par->mem = &sdr_sgram; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci/* 10128c2ecf20Sopenharmony_ci * CRTC programming 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci/* Program the CRTC registers */ 10168c2ecf20Sopenharmony_cistatic void aty128_set_crtc(const struct aty128_crtc *crtc, 10178c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl); 10208c2ecf20Sopenharmony_ci aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total); 10218c2ecf20Sopenharmony_ci aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid); 10228c2ecf20Sopenharmony_ci aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total); 10238c2ecf20Sopenharmony_ci aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid); 10248c2ecf20Sopenharmony_ci aty_st_le32(CRTC_PITCH, crtc->pitch); 10258c2ecf20Sopenharmony_ci aty_st_le32(CRTC_OFFSET, crtc->offset); 10268c2ecf20Sopenharmony_ci aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl); 10278c2ecf20Sopenharmony_ci /* Disable ATOMIC updating. Is this the right place? */ 10288c2ecf20Sopenharmony_ci aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000)); 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic int aty128_var_to_crtc(const struct fb_var_screeninfo *var, 10338c2ecf20Sopenharmony_ci struct aty128_crtc *crtc, 10348c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 10358c2ecf20Sopenharmony_ci{ 10368c2ecf20Sopenharmony_ci u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst; 10378c2ecf20Sopenharmony_ci u32 left, right, upper, lower, hslen, vslen, sync, vmode; 10388c2ecf20Sopenharmony_ci u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol; 10398c2ecf20Sopenharmony_ci u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; 10408c2ecf20Sopenharmony_ci u32 depth, bytpp; 10418c2ecf20Sopenharmony_ci u8 mode_bytpp[7] = { 0, 0, 1, 2, 2, 3, 4 }; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci /* input */ 10448c2ecf20Sopenharmony_ci xres = var->xres; 10458c2ecf20Sopenharmony_ci yres = var->yres; 10468c2ecf20Sopenharmony_ci vxres = var->xres_virtual; 10478c2ecf20Sopenharmony_ci vyres = var->yres_virtual; 10488c2ecf20Sopenharmony_ci xoffset = var->xoffset; 10498c2ecf20Sopenharmony_ci yoffset = var->yoffset; 10508c2ecf20Sopenharmony_ci bpp = var->bits_per_pixel; 10518c2ecf20Sopenharmony_ci left = var->left_margin; 10528c2ecf20Sopenharmony_ci right = var->right_margin; 10538c2ecf20Sopenharmony_ci upper = var->upper_margin; 10548c2ecf20Sopenharmony_ci lower = var->lower_margin; 10558c2ecf20Sopenharmony_ci hslen = var->hsync_len; 10568c2ecf20Sopenharmony_ci vslen = var->vsync_len; 10578c2ecf20Sopenharmony_ci sync = var->sync; 10588c2ecf20Sopenharmony_ci vmode = var->vmode; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (bpp != 16) 10618c2ecf20Sopenharmony_ci depth = bpp; 10628c2ecf20Sopenharmony_ci else 10638c2ecf20Sopenharmony_ci depth = (var->green.length == 6) ? 16 : 15; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* check for mode eligibility 10668c2ecf20Sopenharmony_ci * accept only non interlaced modes */ 10678c2ecf20Sopenharmony_ci if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) 10688c2ecf20Sopenharmony_ci return -EINVAL; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* convert (and round up) and validate */ 10718c2ecf20Sopenharmony_ci xres = (xres + 7) & ~7; 10728c2ecf20Sopenharmony_ci xoffset = (xoffset + 7) & ~7; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (vxres < xres + xoffset) 10758c2ecf20Sopenharmony_ci vxres = xres + xoffset; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (vyres < yres + yoffset) 10788c2ecf20Sopenharmony_ci vyres = yres + yoffset; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* convert depth into ATI register depth */ 10818c2ecf20Sopenharmony_ci dst = depth_to_dst(depth); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (dst == -EINVAL) { 10848c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n"); 10858c2ecf20Sopenharmony_ci return -EINVAL; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* convert register depth to bytes per pixel */ 10898c2ecf20Sopenharmony_ci bytpp = mode_bytpp[dst]; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* make sure there is enough video ram for the mode */ 10928c2ecf20Sopenharmony_ci if ((u32)(vxres * vyres * bytpp) > par->vram_size) { 10938c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: Not enough memory for mode\n"); 10948c2ecf20Sopenharmony_ci return -EINVAL; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci h_disp = (xres >> 3) - 1; 10988c2ecf20Sopenharmony_ci h_total = (((xres + right + hslen + left) >> 3) - 1) & 0xFFFFL; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci v_disp = yres - 1; 11018c2ecf20Sopenharmony_ci v_total = (yres + upper + vslen + lower - 1) & 0xFFFFL; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* check to make sure h_total and v_total are in range */ 11048c2ecf20Sopenharmony_ci if (((h_total >> 3) - 1) > 0x1ff || (v_total - 1) > 0x7FF) { 11058c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: invalid width ranges\n"); 11068c2ecf20Sopenharmony_ci return -EINVAL; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci h_sync_wid = (hslen + 7) >> 3; 11108c2ecf20Sopenharmony_ci if (h_sync_wid == 0) 11118c2ecf20Sopenharmony_ci h_sync_wid = 1; 11128c2ecf20Sopenharmony_ci else if (h_sync_wid > 0x3f) /* 0x3f = max hwidth */ 11138c2ecf20Sopenharmony_ci h_sync_wid = 0x3f; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci h_sync_strt = (h_disp << 3) + right; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci v_sync_wid = vslen; 11188c2ecf20Sopenharmony_ci if (v_sync_wid == 0) 11198c2ecf20Sopenharmony_ci v_sync_wid = 1; 11208c2ecf20Sopenharmony_ci else if (v_sync_wid > 0x1f) /* 0x1f = max vwidth */ 11218c2ecf20Sopenharmony_ci v_sync_wid = 0x1f; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci v_sync_strt = v_disp + lower; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1; 11268c2ecf20Sopenharmony_ci v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci crtc->h_total = h_total | (h_disp << 16); 11338c2ecf20Sopenharmony_ci crtc->v_total = v_total | (v_disp << 16); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci crtc->h_sync_strt_wid = h_sync_strt | (h_sync_wid << 16) | 11368c2ecf20Sopenharmony_ci (h_sync_pol << 23); 11378c2ecf20Sopenharmony_ci crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) | 11388c2ecf20Sopenharmony_ci (v_sync_pol << 23); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci crtc->pitch = vxres >> 3; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci crtc->offset = 0; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) 11458c2ecf20Sopenharmony_ci crtc->offset_cntl = 0x00010000; 11468c2ecf20Sopenharmony_ci else 11478c2ecf20Sopenharmony_ci crtc->offset_cntl = 0; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci crtc->vxres = vxres; 11508c2ecf20Sopenharmony_ci crtc->vyres = vyres; 11518c2ecf20Sopenharmony_ci crtc->xoffset = xoffset; 11528c2ecf20Sopenharmony_ci crtc->yoffset = yoffset; 11538c2ecf20Sopenharmony_ci crtc->depth = depth; 11548c2ecf20Sopenharmony_ci crtc->bpp = bpp; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci return 0; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic int aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* fill in pixel info */ 11648c2ecf20Sopenharmony_ci var->red.msb_right = 0; 11658c2ecf20Sopenharmony_ci var->green.msb_right = 0; 11668c2ecf20Sopenharmony_ci var->blue.offset = 0; 11678c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 11688c2ecf20Sopenharmony_ci var->transp.offset = 0; 11698c2ecf20Sopenharmony_ci var->transp.length = 0; 11708c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 11718c2ecf20Sopenharmony_ci switch (pix_width) { 11728c2ecf20Sopenharmony_ci case CRTC_PIX_WIDTH_8BPP: 11738c2ecf20Sopenharmony_ci var->bits_per_pixel = 8; 11748c2ecf20Sopenharmony_ci var->red.offset = 0; 11758c2ecf20Sopenharmony_ci var->red.length = 8; 11768c2ecf20Sopenharmony_ci var->green.offset = 0; 11778c2ecf20Sopenharmony_ci var->green.length = 8; 11788c2ecf20Sopenharmony_ci var->blue.length = 8; 11798c2ecf20Sopenharmony_ci break; 11808c2ecf20Sopenharmony_ci case CRTC_PIX_WIDTH_15BPP: 11818c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 11828c2ecf20Sopenharmony_ci var->red.offset = 10; 11838c2ecf20Sopenharmony_ci var->red.length = 5; 11848c2ecf20Sopenharmony_ci var->green.offset = 5; 11858c2ecf20Sopenharmony_ci var->green.length = 5; 11868c2ecf20Sopenharmony_ci var->blue.length = 5; 11878c2ecf20Sopenharmony_ci break; 11888c2ecf20Sopenharmony_ci case CRTC_PIX_WIDTH_16BPP: 11898c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 11908c2ecf20Sopenharmony_ci var->red.offset = 11; 11918c2ecf20Sopenharmony_ci var->red.length = 5; 11928c2ecf20Sopenharmony_ci var->green.offset = 5; 11938c2ecf20Sopenharmony_ci var->green.length = 6; 11948c2ecf20Sopenharmony_ci var->blue.length = 5; 11958c2ecf20Sopenharmony_ci break; 11968c2ecf20Sopenharmony_ci case CRTC_PIX_WIDTH_24BPP: 11978c2ecf20Sopenharmony_ci var->bits_per_pixel = 24; 11988c2ecf20Sopenharmony_ci var->red.offset = 16; 11998c2ecf20Sopenharmony_ci var->red.length = 8; 12008c2ecf20Sopenharmony_ci var->green.offset = 8; 12018c2ecf20Sopenharmony_ci var->green.length = 8; 12028c2ecf20Sopenharmony_ci var->blue.length = 8; 12038c2ecf20Sopenharmony_ci break; 12048c2ecf20Sopenharmony_ci case CRTC_PIX_WIDTH_32BPP: 12058c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 12068c2ecf20Sopenharmony_ci var->red.offset = 16; 12078c2ecf20Sopenharmony_ci var->red.length = 8; 12088c2ecf20Sopenharmony_ci var->green.offset = 8; 12098c2ecf20Sopenharmony_ci var->green.length = 8; 12108c2ecf20Sopenharmony_ci var->blue.length = 8; 12118c2ecf20Sopenharmony_ci var->transp.offset = 24; 12128c2ecf20Sopenharmony_ci var->transp.length = 8; 12138c2ecf20Sopenharmony_ci break; 12148c2ecf20Sopenharmony_ci default: 12158c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: Invalid pixel width\n"); 12168c2ecf20Sopenharmony_ci return -EINVAL; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci return 0; 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_cistatic int aty128_crtc_to_var(const struct aty128_crtc *crtc, 12248c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci u32 xres, yres, left, right, upper, lower, hslen, vslen, sync; 12278c2ecf20Sopenharmony_ci u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol; 12288c2ecf20Sopenharmony_ci u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; 12298c2ecf20Sopenharmony_ci u32 pix_width; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci /* fun with masking */ 12328c2ecf20Sopenharmony_ci h_total = crtc->h_total & 0x1ff; 12338c2ecf20Sopenharmony_ci h_disp = (crtc->h_total >> 16) & 0xff; 12348c2ecf20Sopenharmony_ci h_sync_strt = (crtc->h_sync_strt_wid >> 3) & 0x1ff; 12358c2ecf20Sopenharmony_ci h_sync_dly = crtc->h_sync_strt_wid & 0x7; 12368c2ecf20Sopenharmony_ci h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x3f; 12378c2ecf20Sopenharmony_ci h_sync_pol = (crtc->h_sync_strt_wid >> 23) & 0x1; 12388c2ecf20Sopenharmony_ci v_total = crtc->v_total & 0x7ff; 12398c2ecf20Sopenharmony_ci v_disp = (crtc->v_total >> 16) & 0x7ff; 12408c2ecf20Sopenharmony_ci v_sync_strt = crtc->v_sync_strt_wid & 0x7ff; 12418c2ecf20Sopenharmony_ci v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f; 12428c2ecf20Sopenharmony_ci v_sync_pol = (crtc->v_sync_strt_wid >> 23) & 0x1; 12438c2ecf20Sopenharmony_ci c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0; 12448c2ecf20Sopenharmony_ci pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* do conversions */ 12478c2ecf20Sopenharmony_ci xres = (h_disp + 1) << 3; 12488c2ecf20Sopenharmony_ci yres = v_disp + 1; 12498c2ecf20Sopenharmony_ci left = ((h_total - h_sync_strt - h_sync_wid) << 3) - h_sync_dly; 12508c2ecf20Sopenharmony_ci right = ((h_sync_strt - h_disp) << 3) + h_sync_dly; 12518c2ecf20Sopenharmony_ci hslen = h_sync_wid << 3; 12528c2ecf20Sopenharmony_ci upper = v_total - v_sync_strt - v_sync_wid; 12538c2ecf20Sopenharmony_ci lower = v_sync_strt - v_disp; 12548c2ecf20Sopenharmony_ci vslen = v_sync_wid; 12558c2ecf20Sopenharmony_ci sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) | 12568c2ecf20Sopenharmony_ci (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | 12578c2ecf20Sopenharmony_ci (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci aty128_pix_width_to_var(pix_width, var); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci var->xres = xres; 12628c2ecf20Sopenharmony_ci var->yres = yres; 12638c2ecf20Sopenharmony_ci var->xres_virtual = crtc->vxres; 12648c2ecf20Sopenharmony_ci var->yres_virtual = crtc->vyres; 12658c2ecf20Sopenharmony_ci var->xoffset = crtc->xoffset; 12668c2ecf20Sopenharmony_ci var->yoffset = crtc->yoffset; 12678c2ecf20Sopenharmony_ci var->left_margin = left; 12688c2ecf20Sopenharmony_ci var->right_margin = right; 12698c2ecf20Sopenharmony_ci var->upper_margin = upper; 12708c2ecf20Sopenharmony_ci var->lower_margin = lower; 12718c2ecf20Sopenharmony_ci var->hsync_len = hslen; 12728c2ecf20Sopenharmony_ci var->vsync_len = vslen; 12738c2ecf20Sopenharmony_ci var->sync = sync; 12748c2ecf20Sopenharmony_ci var->vmode = FB_VMODE_NONINTERLACED; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci return 0; 12778c2ecf20Sopenharmony_ci} 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_cistatic void aty128_set_crt_enable(struct aty128fb_par *par, int on) 12808c2ecf20Sopenharmony_ci{ 12818c2ecf20Sopenharmony_ci if (on) { 12828c2ecf20Sopenharmony_ci aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | 12838c2ecf20Sopenharmony_ci CRT_CRTC_ON); 12848c2ecf20Sopenharmony_ci aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | 12858c2ecf20Sopenharmony_ci DAC_PALETTE2_SNOOP_EN)); 12868c2ecf20Sopenharmony_ci } else 12878c2ecf20Sopenharmony_ci aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & 12888c2ecf20Sopenharmony_ci ~CRT_CRTC_ON); 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic void aty128_set_lcd_enable(struct aty128fb_par *par, int on) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci u32 reg; 12948c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 12958c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(par->pdev); 12968c2ecf20Sopenharmony_ci#endif 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (on) { 12998c2ecf20Sopenharmony_ci reg = aty_ld_le32(LVDS_GEN_CNTL); 13008c2ecf20Sopenharmony_ci reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION; 13018c2ecf20Sopenharmony_ci reg &= ~LVDS_DISPLAY_DIS; 13028c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 13038c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 13048c2ecf20Sopenharmony_ci aty128_bl_set_power(info, FB_BLANK_UNBLANK); 13058c2ecf20Sopenharmony_ci#endif 13068c2ecf20Sopenharmony_ci } else { 13078c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 13088c2ecf20Sopenharmony_ci aty128_bl_set_power(info, FB_BLANK_POWERDOWN); 13098c2ecf20Sopenharmony_ci#endif 13108c2ecf20Sopenharmony_ci reg = aty_ld_le32(LVDS_GEN_CNTL); 13118c2ecf20Sopenharmony_ci reg |= LVDS_DISPLAY_DIS; 13128c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 13138c2ecf20Sopenharmony_ci mdelay(100); 13148c2ecf20Sopenharmony_ci reg &= ~(LVDS_ON /*| LVDS_EN*/); 13158c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci} 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_cistatic void aty128_set_pll(struct aty128_pll *pll, 13208c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 13218c2ecf20Sopenharmony_ci{ 13228c2ecf20Sopenharmony_ci u32 div3; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci unsigned char post_conv[] = /* register values for post dividers */ 13258c2ecf20Sopenharmony_ci { 2, 0, 1, 4, 2, 2, 6, 2, 3, 2, 2, 2, 7 }; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* select PPLL_DIV_3 */ 13288c2ecf20Sopenharmony_ci aty_st_le32(CLOCK_CNTL_INDEX, aty_ld_le32(CLOCK_CNTL_INDEX) | (3 << 8)); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci /* reset PLL */ 13318c2ecf20Sopenharmony_ci aty_st_pll(PPLL_CNTL, 13328c2ecf20Sopenharmony_ci aty_ld_pll(PPLL_CNTL) | PPLL_RESET | PPLL_ATOMIC_UPDATE_EN); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci /* write the reference divider */ 13358c2ecf20Sopenharmony_ci aty_pll_wait_readupdate(par); 13368c2ecf20Sopenharmony_ci aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider & 0x3ff); 13378c2ecf20Sopenharmony_ci aty_pll_writeupdate(par); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci div3 = aty_ld_pll(PPLL_DIV_3); 13408c2ecf20Sopenharmony_ci div3 &= ~PPLL_FB3_DIV_MASK; 13418c2ecf20Sopenharmony_ci div3 |= pll->feedback_divider; 13428c2ecf20Sopenharmony_ci div3 &= ~PPLL_POST3_DIV_MASK; 13438c2ecf20Sopenharmony_ci div3 |= post_conv[pll->post_divider] << 16; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* write feedback and post dividers */ 13468c2ecf20Sopenharmony_ci aty_pll_wait_readupdate(par); 13478c2ecf20Sopenharmony_ci aty_st_pll(PPLL_DIV_3, div3); 13488c2ecf20Sopenharmony_ci aty_pll_writeupdate(par); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci aty_pll_wait_readupdate(par); 13518c2ecf20Sopenharmony_ci aty_st_pll(HTOTAL_CNTL, 0); /* no horiz crtc adjustment */ 13528c2ecf20Sopenharmony_ci aty_pll_writeupdate(par); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* clear the reset, just in case */ 13558c2ecf20Sopenharmony_ci aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET); 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll, 13608c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci const struct aty128_constants c = par->constants; 13638c2ecf20Sopenharmony_ci unsigned char post_dividers[] = {1,2,4,8,3,6,12}; 13648c2ecf20Sopenharmony_ci u32 output_freq; 13658c2ecf20Sopenharmony_ci u32 vclk; /* in .01 MHz */ 13668c2ecf20Sopenharmony_ci int i = 0; 13678c2ecf20Sopenharmony_ci u32 n, d; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci vclk = 100000000 / period_in_ps; /* convert units to 10 kHz */ 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* adjust pixel clock if necessary */ 13728c2ecf20Sopenharmony_ci if (vclk > c.ppll_max) 13738c2ecf20Sopenharmony_ci vclk = c.ppll_max; 13748c2ecf20Sopenharmony_ci if (vclk * 12 < c.ppll_min) 13758c2ecf20Sopenharmony_ci vclk = c.ppll_min/12; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci /* now, find an acceptable divider */ 13788c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(post_dividers); i++) { 13798c2ecf20Sopenharmony_ci output_freq = post_dividers[i] * vclk; 13808c2ecf20Sopenharmony_ci if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) { 13818c2ecf20Sopenharmony_ci pll->post_divider = post_dividers[i]; 13828c2ecf20Sopenharmony_ci break; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(post_dividers)) 13878c2ecf20Sopenharmony_ci return -EINVAL; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci /* calculate feedback divider */ 13908c2ecf20Sopenharmony_ci n = c.ref_divider * output_freq; 13918c2ecf20Sopenharmony_ci d = c.ref_clk; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci pll->feedback_divider = round_div(n, d); 13948c2ecf20Sopenharmony_ci pll->vclk = vclk; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci DBG("post %d feedback %d vlck %d output %d ref_divider %d " 13978c2ecf20Sopenharmony_ci "vclk_per: %d\n", pll->post_divider, 13988c2ecf20Sopenharmony_ci pll->feedback_divider, vclk, output_freq, 13998c2ecf20Sopenharmony_ci c.ref_divider, period_in_ps); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci return 0; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_cistatic int aty128_pll_to_var(const struct aty128_pll *pll, 14068c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var) 14078c2ecf20Sopenharmony_ci{ 14088c2ecf20Sopenharmony_ci var->pixclock = 100000000 / pll->vclk; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci return 0; 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_cistatic void aty128_set_fifo(const struct aty128_ddafifo *dsp, 14158c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci aty_st_le32(DDA_CONFIG, dsp->dda_config); 14188c2ecf20Sopenharmony_ci aty_st_le32(DDA_ON_OFF, dsp->dda_on_off); 14198c2ecf20Sopenharmony_ci} 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic int aty128_ddafifo(struct aty128_ddafifo *dsp, 14238c2ecf20Sopenharmony_ci const struct aty128_pll *pll, 14248c2ecf20Sopenharmony_ci u32 depth, 14258c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci const struct aty128_meminfo *m = par->mem; 14288c2ecf20Sopenharmony_ci u32 xclk = par->constants.xclk; 14298c2ecf20Sopenharmony_ci u32 fifo_width = par->constants.fifo_width; 14308c2ecf20Sopenharmony_ci u32 fifo_depth = par->constants.fifo_depth; 14318c2ecf20Sopenharmony_ci s32 x, b, p, ron, roff; 14328c2ecf20Sopenharmony_ci u32 n, d, bpp; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* round up to multiple of 8 */ 14358c2ecf20Sopenharmony_ci bpp = (depth+7) & ~7; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci n = xclk * fifo_width; 14388c2ecf20Sopenharmony_ci d = pll->vclk * bpp; 14398c2ecf20Sopenharmony_ci x = round_div(n, d); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci ron = 4 * m->MB + 14428c2ecf20Sopenharmony_ci 3 * ((m->Trcd - 2 > 0) ? m->Trcd - 2 : 0) + 14438c2ecf20Sopenharmony_ci 2 * m->Trp + 14448c2ecf20Sopenharmony_ci m->Twr + 14458c2ecf20Sopenharmony_ci m->CL + 14468c2ecf20Sopenharmony_ci m->Tr2w + 14478c2ecf20Sopenharmony_ci x; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci DBG("x %x\n", x); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci b = 0; 14528c2ecf20Sopenharmony_ci while (x) { 14538c2ecf20Sopenharmony_ci x >>= 1; 14548c2ecf20Sopenharmony_ci b++; 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci p = b + 1; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci ron <<= (11 - p); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci n <<= (11 - p); 14618c2ecf20Sopenharmony_ci x = round_div(n, d); 14628c2ecf20Sopenharmony_ci roff = x * (fifo_depth - 4); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if ((ron + m->Rloop) >= roff) { 14658c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: Mode out of range!\n"); 14668c2ecf20Sopenharmony_ci return -EINVAL; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci DBG("p: %x rloop: %x x: %x ron: %x roff: %x\n", 14708c2ecf20Sopenharmony_ci p, m->Rloop, x, ron, roff); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci dsp->dda_config = p << 16 | m->Rloop << 20 | x; 14738c2ecf20Sopenharmony_ci dsp->dda_on_off = ron << 16 | roff; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci/* 14808c2ecf20Sopenharmony_ci * This actually sets the video mode. 14818c2ecf20Sopenharmony_ci */ 14828c2ecf20Sopenharmony_cistatic int aty128fb_set_par(struct fb_info *info) 14838c2ecf20Sopenharmony_ci{ 14848c2ecf20Sopenharmony_ci struct aty128fb_par *par = info->par; 14858c2ecf20Sopenharmony_ci u32 config; 14868c2ecf20Sopenharmony_ci int err; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if ((err = aty128_decode_var(&info->var, par)) != 0) 14898c2ecf20Sopenharmony_ci return err; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (par->blitter_may_be_busy) 14928c2ecf20Sopenharmony_ci wait_for_idle(par); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* clear all registers that may interfere with mode setting */ 14958c2ecf20Sopenharmony_ci aty_st_le32(OVR_CLR, 0); 14968c2ecf20Sopenharmony_ci aty_st_le32(OVR_WID_LEFT_RIGHT, 0); 14978c2ecf20Sopenharmony_ci aty_st_le32(OVR_WID_TOP_BOTTOM, 0); 14988c2ecf20Sopenharmony_ci aty_st_le32(OV0_SCALE_CNTL, 0); 14998c2ecf20Sopenharmony_ci aty_st_le32(MPP_TB_CONFIG, 0); 15008c2ecf20Sopenharmony_ci aty_st_le32(MPP_GP_CONFIG, 0); 15018c2ecf20Sopenharmony_ci aty_st_le32(SUBPIC_CNTL, 0); 15028c2ecf20Sopenharmony_ci aty_st_le32(VIPH_CONTROL, 0); 15038c2ecf20Sopenharmony_ci aty_st_le32(I2C_CNTL_1, 0); /* turn off i2c */ 15048c2ecf20Sopenharmony_ci aty_st_le32(GEN_INT_CNTL, 0); /* turn off interrupts */ 15058c2ecf20Sopenharmony_ci aty_st_le32(CAP0_TRIG_CNTL, 0); 15068c2ecf20Sopenharmony_ci aty_st_le32(CAP1_TRIG_CNTL, 0); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci aty_st_8(CRTC_EXT_CNTL + 1, 4); /* turn video off */ 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci aty128_set_crtc(&par->crtc, par); 15118c2ecf20Sopenharmony_ci aty128_set_pll(&par->pll, par); 15128c2ecf20Sopenharmony_ci aty128_set_fifo(&par->fifo_reg, par); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci config = aty_ld_le32(CNFG_CNTL) & ~3; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci#if defined(__BIG_ENDIAN) 15178c2ecf20Sopenharmony_ci if (par->crtc.bpp == 32) 15188c2ecf20Sopenharmony_ci config |= 2; /* make aperture do 32 bit swapping */ 15198c2ecf20Sopenharmony_ci else if (par->crtc.bpp == 16) 15208c2ecf20Sopenharmony_ci config |= 1; /* make aperture do 16 bit swapping */ 15218c2ecf20Sopenharmony_ci#endif 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci aty_st_le32(CNFG_CNTL, config); 15248c2ecf20Sopenharmony_ci aty_st_8(CRTC_EXT_CNTL + 1, 0); /* turn the video back on */ 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci info->fix.line_length = (par->crtc.vxres * par->crtc.bpp) >> 3; 15278c2ecf20Sopenharmony_ci info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR 15288c2ecf20Sopenharmony_ci : FB_VISUAL_DIRECTCOLOR; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (par->chip_gen == rage_M3) { 15318c2ecf20Sopenharmony_ci aty128_set_crt_enable(par, par->crt_on); 15328c2ecf20Sopenharmony_ci aty128_set_lcd_enable(par, par->lcd_on); 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci if (par->accel_flags & FB_ACCELF_TEXT) 15358c2ecf20Sopenharmony_ci aty128_init_engine(par); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT 15388c2ecf20Sopenharmony_ci btext_update_display(info->fix.smem_start, 15398c2ecf20Sopenharmony_ci (((par->crtc.h_total>>16) & 0xff)+1)*8, 15408c2ecf20Sopenharmony_ci ((par->crtc.v_total>>16) & 0x7ff)+1, 15418c2ecf20Sopenharmony_ci par->crtc.bpp, 15428c2ecf20Sopenharmony_ci par->crtc.vxres*par->crtc.bpp/8); 15438c2ecf20Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */ 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci return 0; 15468c2ecf20Sopenharmony_ci} 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci/* 15498c2ecf20Sopenharmony_ci * encode/decode the User Defined Part of the Display 15508c2ecf20Sopenharmony_ci */ 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_cistatic int aty128_decode_var(struct fb_var_screeninfo *var, 15538c2ecf20Sopenharmony_ci struct aty128fb_par *par) 15548c2ecf20Sopenharmony_ci{ 15558c2ecf20Sopenharmony_ci int err; 15568c2ecf20Sopenharmony_ci struct aty128_crtc crtc; 15578c2ecf20Sopenharmony_ci struct aty128_pll pll; 15588c2ecf20Sopenharmony_ci struct aty128_ddafifo fifo_reg; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if ((err = aty128_var_to_crtc(var, &crtc, par))) 15618c2ecf20Sopenharmony_ci return err; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci if ((err = aty128_var_to_pll(var->pixclock, &pll, par))) 15648c2ecf20Sopenharmony_ci return err; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if ((err = aty128_ddafifo(&fifo_reg, &pll, crtc.depth, par))) 15678c2ecf20Sopenharmony_ci return err; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci par->crtc = crtc; 15708c2ecf20Sopenharmony_ci par->pll = pll; 15718c2ecf20Sopenharmony_ci par->fifo_reg = fifo_reg; 15728c2ecf20Sopenharmony_ci par->accel_flags = var->accel_flags; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci return 0; 15758c2ecf20Sopenharmony_ci} 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_cistatic int aty128_encode_var(struct fb_var_screeninfo *var, 15798c2ecf20Sopenharmony_ci const struct aty128fb_par *par) 15808c2ecf20Sopenharmony_ci{ 15818c2ecf20Sopenharmony_ci int err; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci if ((err = aty128_crtc_to_var(&par->crtc, var))) 15848c2ecf20Sopenharmony_ci return err; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci if ((err = aty128_pll_to_var(&par->pll, var))) 15878c2ecf20Sopenharmony_ci return err; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci var->nonstd = 0; 15908c2ecf20Sopenharmony_ci var->activate = 0; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci var->height = -1; 15938c2ecf20Sopenharmony_ci var->width = -1; 15948c2ecf20Sopenharmony_ci var->accel_flags = par->accel_flags; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci return 0; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_cistatic int aty128fb_check_var(struct fb_var_screeninfo *var, 16018c2ecf20Sopenharmony_ci struct fb_info *info) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct aty128fb_par par; 16048c2ecf20Sopenharmony_ci int err; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci par = *(struct aty128fb_par *)info->par; 16078c2ecf20Sopenharmony_ci if ((err = aty128_decode_var(var, &par)) != 0) 16088c2ecf20Sopenharmony_ci return err; 16098c2ecf20Sopenharmony_ci aty128_encode_var(var, &par); 16108c2ecf20Sopenharmony_ci return 0; 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci/* 16158c2ecf20Sopenharmony_ci * Pan or Wrap the Display 16168c2ecf20Sopenharmony_ci */ 16178c2ecf20Sopenharmony_cistatic int aty128fb_pan_display(struct fb_var_screeninfo *var, 16188c2ecf20Sopenharmony_ci struct fb_info *fb) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci struct aty128fb_par *par = fb->par; 16218c2ecf20Sopenharmony_ci u32 xoffset, yoffset; 16228c2ecf20Sopenharmony_ci u32 offset; 16238c2ecf20Sopenharmony_ci u32 xres, yres; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci xres = (((par->crtc.h_total >> 16) & 0xff) + 1) << 3; 16268c2ecf20Sopenharmony_ci yres = ((par->crtc.v_total >> 16) & 0x7ff) + 1; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci xoffset = (var->xoffset +7) & ~7; 16298c2ecf20Sopenharmony_ci yoffset = var->yoffset; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (xoffset+xres > par->crtc.vxres || yoffset+yres > par->crtc.vyres) 16328c2ecf20Sopenharmony_ci return -EINVAL; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci par->crtc.xoffset = xoffset; 16358c2ecf20Sopenharmony_ci par->crtc.yoffset = yoffset; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3)) 16388c2ecf20Sopenharmony_ci & ~7; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (par->crtc.bpp == 24) 16418c2ecf20Sopenharmony_ci offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */ 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci aty_st_le32(CRTC_OFFSET, offset); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci return 0; 16468c2ecf20Sopenharmony_ci} 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci/* 16508c2ecf20Sopenharmony_ci * Helper function to store a single palette register 16518c2ecf20Sopenharmony_ci */ 16528c2ecf20Sopenharmony_cistatic void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue, 16538c2ecf20Sopenharmony_ci struct aty128fb_par *par) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci if (par->chip_gen == rage_M3) { 16568c2ecf20Sopenharmony_ci aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & 16578c2ecf20Sopenharmony_ci ~DAC_PALETTE_ACCESS_CNTL); 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci aty_st_8(PALETTE_INDEX, regno); 16618c2ecf20Sopenharmony_ci aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue); 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_cistatic int aty128fb_sync(struct fb_info *info) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct aty128fb_par *par = info->par; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if (par->blitter_may_be_busy) 16698c2ecf20Sopenharmony_ci wait_for_idle(par); 16708c2ecf20Sopenharmony_ci return 0; 16718c2ecf20Sopenharmony_ci} 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci#ifndef MODULE 16748c2ecf20Sopenharmony_cistatic int aty128fb_setup(char *options) 16758c2ecf20Sopenharmony_ci{ 16768c2ecf20Sopenharmony_ci char *this_opt; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (!options || !*options) 16798c2ecf20Sopenharmony_ci return 0; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 16828c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "lcd:", 4)) { 16838c2ecf20Sopenharmony_ci default_lcd_on = simple_strtoul(this_opt+4, NULL, 0); 16848c2ecf20Sopenharmony_ci continue; 16858c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "crt:", 4)) { 16868c2ecf20Sopenharmony_ci default_crt_on = simple_strtoul(this_opt+4, NULL, 0); 16878c2ecf20Sopenharmony_ci continue; 16888c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "backlight:", 10)) { 16898c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 16908c2ecf20Sopenharmony_ci backlight = simple_strtoul(this_opt+10, NULL, 0); 16918c2ecf20Sopenharmony_ci#endif 16928c2ecf20Sopenharmony_ci continue; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci if(!strncmp(this_opt, "nomtrr", 6)) { 16958c2ecf20Sopenharmony_ci mtrr = false; 16968c2ecf20Sopenharmony_ci continue; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 16998c2ecf20Sopenharmony_ci /* vmode and cmode deprecated */ 17008c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "vmode:", 6)) { 17018c2ecf20Sopenharmony_ci unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0); 17028c2ecf20Sopenharmony_ci if (vmode > 0 && vmode <= VMODE_MAX) 17038c2ecf20Sopenharmony_ci default_vmode = vmode; 17048c2ecf20Sopenharmony_ci continue; 17058c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "cmode:", 6)) { 17068c2ecf20Sopenharmony_ci unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0); 17078c2ecf20Sopenharmony_ci switch (cmode) { 17088c2ecf20Sopenharmony_ci case 0: 17098c2ecf20Sopenharmony_ci case 8: 17108c2ecf20Sopenharmony_ci default_cmode = CMODE_8; 17118c2ecf20Sopenharmony_ci break; 17128c2ecf20Sopenharmony_ci case 15: 17138c2ecf20Sopenharmony_ci case 16: 17148c2ecf20Sopenharmony_ci default_cmode = CMODE_16; 17158c2ecf20Sopenharmony_ci break; 17168c2ecf20Sopenharmony_ci case 24: 17178c2ecf20Sopenharmony_ci case 32: 17188c2ecf20Sopenharmony_ci default_cmode = CMODE_32; 17198c2ecf20Sopenharmony_ci break; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci continue; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 17248c2ecf20Sopenharmony_ci mode_option = this_opt; 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci return 0; 17278c2ecf20Sopenharmony_ci} 17288c2ecf20Sopenharmony_ci#endif /* MODULE */ 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci/* Backlight */ 17318c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 17328c2ecf20Sopenharmony_ci#define MAX_LEVEL 0xFF 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_cistatic int aty128_bl_get_level_brightness(struct aty128fb_par *par, 17358c2ecf20Sopenharmony_ci int level) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(par->pdev); 17388c2ecf20Sopenharmony_ci int atylevel; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci /* Get and convert the value */ 17418c2ecf20Sopenharmony_ci /* No locking of bl_curve since we read a single value */ 17428c2ecf20Sopenharmony_ci atylevel = MAX_LEVEL - 17438c2ecf20Sopenharmony_ci (info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (atylevel < 0) 17468c2ecf20Sopenharmony_ci atylevel = 0; 17478c2ecf20Sopenharmony_ci else if (atylevel > MAX_LEVEL) 17488c2ecf20Sopenharmony_ci atylevel = MAX_LEVEL; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci return atylevel; 17518c2ecf20Sopenharmony_ci} 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci/* We turn off the LCD completely instead of just dimming the backlight. 17548c2ecf20Sopenharmony_ci * This provides greater power saving and the display is useless without 17558c2ecf20Sopenharmony_ci * backlight anyway 17568c2ecf20Sopenharmony_ci */ 17578c2ecf20Sopenharmony_ci#define BACKLIGHT_LVDS_OFF 17588c2ecf20Sopenharmony_ci/* That one prevents proper CRT output with LCD off */ 17598c2ecf20Sopenharmony_ci#undef BACKLIGHT_DAC_OFF 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_cistatic int aty128_bl_update_status(struct backlight_device *bd) 17628c2ecf20Sopenharmony_ci{ 17638c2ecf20Sopenharmony_ci struct aty128fb_par *par = bl_get_data(bd); 17648c2ecf20Sopenharmony_ci unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL); 17658c2ecf20Sopenharmony_ci int level; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci if (bd->props.power != FB_BLANK_UNBLANK || 17688c2ecf20Sopenharmony_ci bd->props.fb_blank != FB_BLANK_UNBLANK || 17698c2ecf20Sopenharmony_ci !par->lcd_on) 17708c2ecf20Sopenharmony_ci level = 0; 17718c2ecf20Sopenharmony_ci else 17728c2ecf20Sopenharmony_ci level = bd->props.brightness; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci reg |= LVDS_BL_MOD_EN | LVDS_BLON; 17758c2ecf20Sopenharmony_ci if (level > 0) { 17768c2ecf20Sopenharmony_ci reg |= LVDS_DIGION; 17778c2ecf20Sopenharmony_ci if (!(reg & LVDS_ON)) { 17788c2ecf20Sopenharmony_ci reg &= ~LVDS_BLON; 17798c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 17808c2ecf20Sopenharmony_ci aty_ld_le32(LVDS_GEN_CNTL); 17818c2ecf20Sopenharmony_ci mdelay(10); 17828c2ecf20Sopenharmony_ci reg |= LVDS_BLON; 17838c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci reg &= ~LVDS_BL_MOD_LEVEL_MASK; 17868c2ecf20Sopenharmony_ci reg |= (aty128_bl_get_level_brightness(par, level) << 17878c2ecf20Sopenharmony_ci LVDS_BL_MOD_LEVEL_SHIFT); 17888c2ecf20Sopenharmony_ci#ifdef BACKLIGHT_LVDS_OFF 17898c2ecf20Sopenharmony_ci reg |= LVDS_ON | LVDS_EN; 17908c2ecf20Sopenharmony_ci reg &= ~LVDS_DISPLAY_DIS; 17918c2ecf20Sopenharmony_ci#endif 17928c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 17938c2ecf20Sopenharmony_ci#ifdef BACKLIGHT_DAC_OFF 17948c2ecf20Sopenharmony_ci aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN)); 17958c2ecf20Sopenharmony_ci#endif 17968c2ecf20Sopenharmony_ci } else { 17978c2ecf20Sopenharmony_ci reg &= ~LVDS_BL_MOD_LEVEL_MASK; 17988c2ecf20Sopenharmony_ci reg |= (aty128_bl_get_level_brightness(par, 0) << 17998c2ecf20Sopenharmony_ci LVDS_BL_MOD_LEVEL_SHIFT); 18008c2ecf20Sopenharmony_ci#ifdef BACKLIGHT_LVDS_OFF 18018c2ecf20Sopenharmony_ci reg |= LVDS_DISPLAY_DIS; 18028c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 18038c2ecf20Sopenharmony_ci aty_ld_le32(LVDS_GEN_CNTL); 18048c2ecf20Sopenharmony_ci udelay(10); 18058c2ecf20Sopenharmony_ci reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION); 18068c2ecf20Sopenharmony_ci#endif 18078c2ecf20Sopenharmony_ci aty_st_le32(LVDS_GEN_CNTL, reg); 18088c2ecf20Sopenharmony_ci#ifdef BACKLIGHT_DAC_OFF 18098c2ecf20Sopenharmony_ci aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN); 18108c2ecf20Sopenharmony_ci#endif 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci return 0; 18148c2ecf20Sopenharmony_ci} 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_cistatic const struct backlight_ops aty128_bl_data = { 18178c2ecf20Sopenharmony_ci .update_status = aty128_bl_update_status, 18188c2ecf20Sopenharmony_ci}; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_cistatic void aty128_bl_set_power(struct fb_info *info, int power) 18218c2ecf20Sopenharmony_ci{ 18228c2ecf20Sopenharmony_ci if (info->bl_dev) { 18238c2ecf20Sopenharmony_ci info->bl_dev->props.power = power; 18248c2ecf20Sopenharmony_ci backlight_update_status(info->bl_dev); 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_cistatic void aty128_bl_init(struct aty128fb_par *par) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci struct backlight_properties props; 18318c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(par->pdev); 18328c2ecf20Sopenharmony_ci struct backlight_device *bd; 18338c2ecf20Sopenharmony_ci char name[12]; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci /* Could be extended to Rage128Pro LVDS output too */ 18368c2ecf20Sopenharmony_ci if (par->chip_gen != rage_M3) 18378c2ecf20Sopenharmony_ci return; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT 18408c2ecf20Sopenharmony_ci if (!pmac_has_backlight_type("ati")) 18418c2ecf20Sopenharmony_ci return; 18428c2ecf20Sopenharmony_ci#endif 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "aty128bl%d", info->node); 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 18478c2ecf20Sopenharmony_ci props.type = BACKLIGHT_RAW; 18488c2ecf20Sopenharmony_ci props.max_brightness = FB_BACKLIGHT_LEVELS - 1; 18498c2ecf20Sopenharmony_ci bd = backlight_device_register(name, info->dev, par, &aty128_bl_data, 18508c2ecf20Sopenharmony_ci &props); 18518c2ecf20Sopenharmony_ci if (IS_ERR(bd)) { 18528c2ecf20Sopenharmony_ci info->bl_dev = NULL; 18538c2ecf20Sopenharmony_ci printk(KERN_WARNING "aty128: Backlight registration failed\n"); 18548c2ecf20Sopenharmony_ci goto error; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci info->bl_dev = bd; 18588c2ecf20Sopenharmony_ci fb_bl_default_curve(info, 0, 18598c2ecf20Sopenharmony_ci 63 * FB_BACKLIGHT_MAX / MAX_LEVEL, 18608c2ecf20Sopenharmony_ci 219 * FB_BACKLIGHT_MAX / MAX_LEVEL); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci bd->props.brightness = bd->props.max_brightness; 18638c2ecf20Sopenharmony_ci bd->props.power = FB_BLANK_UNBLANK; 18648c2ecf20Sopenharmony_ci backlight_update_status(bd); 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci printk("aty128: Backlight initialized (%s)\n", name); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci return; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cierror: 18718c2ecf20Sopenharmony_ci return; 18728c2ecf20Sopenharmony_ci} 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_cistatic void aty128_bl_exit(struct backlight_device *bd) 18758c2ecf20Sopenharmony_ci{ 18768c2ecf20Sopenharmony_ci backlight_device_unregister(bd); 18778c2ecf20Sopenharmony_ci printk("aty128: Backlight unloaded\n"); 18788c2ecf20Sopenharmony_ci} 18798c2ecf20Sopenharmony_ci#endif /* CONFIG_FB_ATY128_BACKLIGHT */ 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci/* 18828c2ecf20Sopenharmony_ci * Initialisation 18838c2ecf20Sopenharmony_ci */ 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC__disabled 18868c2ecf20Sopenharmony_cistatic void aty128_early_resume(void *data) 18878c2ecf20Sopenharmony_ci{ 18888c2ecf20Sopenharmony_ci struct aty128fb_par *par = data; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (!console_trylock()) 18918c2ecf20Sopenharmony_ci return; 18928c2ecf20Sopenharmony_ci pci_restore_state(par->pdev); 18938c2ecf20Sopenharmony_ci aty128_do_resume(par->pdev); 18948c2ecf20Sopenharmony_ci console_unlock(); 18958c2ecf20Sopenharmony_ci} 18968c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_cistatic int aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 19018c2ecf20Sopenharmony_ci struct aty128fb_par *par = info->par; 19028c2ecf20Sopenharmony_ci struct fb_var_screeninfo var; 19038c2ecf20Sopenharmony_ci char video_card[50]; 19048c2ecf20Sopenharmony_ci u8 chip_rev; 19058c2ecf20Sopenharmony_ci u32 dac; 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci /* Get the chip revision */ 19088c2ecf20Sopenharmony_ci chip_rev = (aty_ld_le32(CNFG_CNTL) >> 16) & 0x1F; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci strcpy(video_card, "Rage128 XX "); 19118c2ecf20Sopenharmony_ci video_card[8] = ent->device >> 8; 19128c2ecf20Sopenharmony_ci video_card[9] = ent->device & 0xFF; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci /* range check to make sure */ 19158c2ecf20Sopenharmony_ci if (ent->driver_data < ARRAY_SIZE(r128_family)) 19168c2ecf20Sopenharmony_ci strlcat(video_card, r128_family[ent->driver_data], 19178c2ecf20Sopenharmony_ci sizeof(video_card)); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci if (par->vram_size % (1024 * 1024) == 0) 19228c2ecf20Sopenharmony_ci printk("%dM %s\n", par->vram_size / (1024*1024), par->mem->name); 19238c2ecf20Sopenharmony_ci else 19248c2ecf20Sopenharmony_ci printk("%dk %s\n", par->vram_size / 1024, par->mem->name); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci par->chip_gen = ent->driver_data; 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci /* fill in info */ 19298c2ecf20Sopenharmony_ci info->fbops = &aty128fb_ops; 19308c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci par->lcd_on = default_lcd_on; 19338c2ecf20Sopenharmony_ci par->crt_on = default_crt_on; 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci var = default_var; 19368c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 19378c2ecf20Sopenharmony_ci if (machine_is(powermac)) { 19388c2ecf20Sopenharmony_ci /* Indicate sleep capability */ 19398c2ecf20Sopenharmony_ci if (par->chip_gen == rage_M3) { 19408c2ecf20Sopenharmony_ci pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1); 19418c2ecf20Sopenharmony_ci#if 0 /* Disable the early video resume hack for now as it's causing problems, 19428c2ecf20Sopenharmony_ci * among others we now rely on the PCI core restoring the config space 19438c2ecf20Sopenharmony_ci * for us, which isn't the case with that hack, and that code path causes 19448c2ecf20Sopenharmony_ci * various things to be called with interrupts off while they shouldn't. 19458c2ecf20Sopenharmony_ci * I'm leaving the code in as it can be useful for debugging purposes 19468c2ecf20Sopenharmony_ci */ 19478c2ecf20Sopenharmony_ci pmac_set_early_video_resume(aty128_early_resume, par); 19488c2ecf20Sopenharmony_ci#endif 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci /* Find default mode */ 19528c2ecf20Sopenharmony_ci if (mode_option) { 19538c2ecf20Sopenharmony_ci if (!mac_find_mode(&var, info, mode_option, 8)) 19548c2ecf20Sopenharmony_ci var = default_var; 19558c2ecf20Sopenharmony_ci } else { 19568c2ecf20Sopenharmony_ci if (default_vmode <= 0 || default_vmode > VMODE_MAX) 19578c2ecf20Sopenharmony_ci default_vmode = VMODE_1024_768_60; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci /* iMacs need that resolution 19608c2ecf20Sopenharmony_ci * PowerMac2,1 first r128 iMacs 19618c2ecf20Sopenharmony_ci * PowerMac2,2 summer 2000 iMacs 19628c2ecf20Sopenharmony_ci * PowerMac4,1 january 2001 iMacs "flower power" 19638c2ecf20Sopenharmony_ci */ 19648c2ecf20Sopenharmony_ci if (of_machine_is_compatible("PowerMac2,1") || 19658c2ecf20Sopenharmony_ci of_machine_is_compatible("PowerMac2,2") || 19668c2ecf20Sopenharmony_ci of_machine_is_compatible("PowerMac4,1")) 19678c2ecf20Sopenharmony_ci default_vmode = VMODE_1024_768_75; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci /* iBook SE */ 19708c2ecf20Sopenharmony_ci if (of_machine_is_compatible("PowerBook2,2")) 19718c2ecf20Sopenharmony_ci default_vmode = VMODE_800_600_60; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci /* PowerBook Firewire (Pismo), iBook Dual USB */ 19748c2ecf20Sopenharmony_ci if (of_machine_is_compatible("PowerBook3,1") || 19758c2ecf20Sopenharmony_ci of_machine_is_compatible("PowerBook4,1")) 19768c2ecf20Sopenharmony_ci default_vmode = VMODE_1024_768_60; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* PowerBook Titanium */ 19798c2ecf20Sopenharmony_ci if (of_machine_is_compatible("PowerBook3,2")) 19808c2ecf20Sopenharmony_ci default_vmode = VMODE_1152_768_60; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci if (default_cmode > 16) 19838c2ecf20Sopenharmony_ci default_cmode = CMODE_32; 19848c2ecf20Sopenharmony_ci else if (default_cmode > 8) 19858c2ecf20Sopenharmony_ci default_cmode = CMODE_16; 19868c2ecf20Sopenharmony_ci else 19878c2ecf20Sopenharmony_ci default_cmode = CMODE_8; 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci if (mac_vmode_to_var(default_vmode, default_cmode, &var)) 19908c2ecf20Sopenharmony_ci var = default_var; 19918c2ecf20Sopenharmony_ci } 19928c2ecf20Sopenharmony_ci } else 19938c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 19948c2ecf20Sopenharmony_ci { 19958c2ecf20Sopenharmony_ci if (mode_option) 19968c2ecf20Sopenharmony_ci if (fb_find_mode(&var, info, mode_option, NULL, 19978c2ecf20Sopenharmony_ci 0, &defaultmode, 8) == 0) 19988c2ecf20Sopenharmony_ci var = default_var; 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci var.accel_flags &= ~FB_ACCELF_TEXT; 20028c2ecf20Sopenharmony_ci// var.accel_flags |= FB_ACCELF_TEXT;/* FIXME Will add accel later */ 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci if (aty128fb_check_var(&var, info)) { 20058c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: Cannot set default mode.\n"); 20068c2ecf20Sopenharmony_ci return 0; 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci /* setup the DAC the way we like it */ 20108c2ecf20Sopenharmony_ci dac = aty_ld_le32(DAC_CNTL); 20118c2ecf20Sopenharmony_ci dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL); 20128c2ecf20Sopenharmony_ci dac |= DAC_MASK; 20138c2ecf20Sopenharmony_ci if (par->chip_gen == rage_M3) 20148c2ecf20Sopenharmony_ci dac |= DAC_PALETTE2_SNOOP_EN; 20158c2ecf20Sopenharmony_ci aty_st_le32(DAC_CNTL, dac); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci /* turn off bus mastering, just in case */ 20188c2ecf20Sopenharmony_ci aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_MASTER_DIS); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci info->var = var; 20218c2ecf20Sopenharmony_ci fb_alloc_cmap(&info->cmap, 256, 0); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci var.activate = FB_ACTIVATE_NOW; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci aty128_init_engine(par); 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci par->pdev = pdev; 20288c2ecf20Sopenharmony_ci par->asleep = 0; 20298c2ecf20Sopenharmony_ci par->lock_blank = 0; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 20328c2ecf20Sopenharmony_ci if (backlight) 20338c2ecf20Sopenharmony_ci aty128_bl_init(par); 20348c2ecf20Sopenharmony_ci#endif 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci if (register_framebuffer(info) < 0) 20378c2ecf20Sopenharmony_ci return 0; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci fb_info(info, "%s frame buffer device on %s\n", 20408c2ecf20Sopenharmony_ci info->fix.id, video_card); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci return 1; /* success! */ 20438c2ecf20Sopenharmony_ci} 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 20468c2ecf20Sopenharmony_ci/* register a card ++ajoshi */ 20478c2ecf20Sopenharmony_cistatic int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 20488c2ecf20Sopenharmony_ci{ 20498c2ecf20Sopenharmony_ci unsigned long fb_addr, reg_addr; 20508c2ecf20Sopenharmony_ci struct aty128fb_par *par; 20518c2ecf20Sopenharmony_ci struct fb_info *info; 20528c2ecf20Sopenharmony_ci int err; 20538c2ecf20Sopenharmony_ci#ifndef __sparc__ 20548c2ecf20Sopenharmony_ci void __iomem *bios = NULL; 20558c2ecf20Sopenharmony_ci#endif 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci /* Enable device in PCI config */ 20588c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pdev))) { 20598c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n", 20608c2ecf20Sopenharmony_ci err); 20618c2ecf20Sopenharmony_ci return -ENODEV; 20628c2ecf20Sopenharmony_ci } 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci fb_addr = pci_resource_start(pdev, 0); 20658c2ecf20Sopenharmony_ci if (!request_mem_region(fb_addr, pci_resource_len(pdev, 0), 20668c2ecf20Sopenharmony_ci "aty128fb FB")) { 20678c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: cannot reserve frame " 20688c2ecf20Sopenharmony_ci "buffer memory\n"); 20698c2ecf20Sopenharmony_ci return -ENODEV; 20708c2ecf20Sopenharmony_ci } 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci reg_addr = pci_resource_start(pdev, 2); 20738c2ecf20Sopenharmony_ci if (!request_mem_region(reg_addr, pci_resource_len(pdev, 2), 20748c2ecf20Sopenharmony_ci "aty128fb MMIO")) { 20758c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n"); 20768c2ecf20Sopenharmony_ci goto err_free_fb; 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci /* We have the resources. Now virtualize them */ 20808c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev); 20818c2ecf20Sopenharmony_ci if (!info) 20828c2ecf20Sopenharmony_ci goto err_free_mmio; 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci par = info->par; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci /* Virtualize mmio region */ 20898c2ecf20Sopenharmony_ci info->fix.mmio_start = reg_addr; 20908c2ecf20Sopenharmony_ci par->regbase = pci_ioremap_bar(pdev, 2); 20918c2ecf20Sopenharmony_ci if (!par->regbase) 20928c2ecf20Sopenharmony_ci goto err_free_info; 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci /* Grab memory size from the card */ 20958c2ecf20Sopenharmony_ci // How does this relate to the resource length from the PCI hardware? 20968c2ecf20Sopenharmony_ci par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci /* Virtualize the framebuffer */ 20998c2ecf20Sopenharmony_ci info->screen_base = ioremap_wc(fb_addr, par->vram_size); 21008c2ecf20Sopenharmony_ci if (!info->screen_base) 21018c2ecf20Sopenharmony_ci goto err_unmap_out; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci /* Set up info->fix */ 21048c2ecf20Sopenharmony_ci info->fix = aty128fb_fix; 21058c2ecf20Sopenharmony_ci info->fix.smem_start = fb_addr; 21068c2ecf20Sopenharmony_ci info->fix.smem_len = par->vram_size; 21078c2ecf20Sopenharmony_ci info->fix.mmio_start = reg_addr; 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci /* If we can't test scratch registers, something is seriously wrong */ 21108c2ecf20Sopenharmony_ci if (!register_test(par)) { 21118c2ecf20Sopenharmony_ci printk(KERN_ERR "aty128fb: Can't write to video register!\n"); 21128c2ecf20Sopenharmony_ci goto err_out; 21138c2ecf20Sopenharmony_ci } 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci#ifndef __sparc__ 21168c2ecf20Sopenharmony_ci bios = aty128_map_ROM(par, pdev); 21178c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 21188c2ecf20Sopenharmony_ci if (bios == NULL) 21198c2ecf20Sopenharmony_ci bios = aty128_find_mem_vbios(par); 21208c2ecf20Sopenharmony_ci#endif 21218c2ecf20Sopenharmony_ci if (bios == NULL) 21228c2ecf20Sopenharmony_ci printk(KERN_INFO "aty128fb: BIOS not located, guessing timings.\n"); 21238c2ecf20Sopenharmony_ci else { 21248c2ecf20Sopenharmony_ci printk(KERN_INFO "aty128fb: Rage128 BIOS located\n"); 21258c2ecf20Sopenharmony_ci aty128_get_pllinfo(par, bios); 21268c2ecf20Sopenharmony_ci pci_unmap_rom(pdev, bios); 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci#endif /* __sparc__ */ 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci aty128_timings(par); 21318c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, info); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci if (!aty128_init(pdev, ent)) 21348c2ecf20Sopenharmony_ci goto err_out; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci if (mtrr) 21378c2ecf20Sopenharmony_ci par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, 21388c2ecf20Sopenharmony_ci par->vram_size); 21398c2ecf20Sopenharmony_ci return 0; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_cierr_out: 21428c2ecf20Sopenharmony_ci iounmap(info->screen_base); 21438c2ecf20Sopenharmony_cierr_unmap_out: 21448c2ecf20Sopenharmony_ci iounmap(par->regbase); 21458c2ecf20Sopenharmony_cierr_free_info: 21468c2ecf20Sopenharmony_ci framebuffer_release(info); 21478c2ecf20Sopenharmony_cierr_free_mmio: 21488c2ecf20Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 2), 21498c2ecf20Sopenharmony_ci pci_resource_len(pdev, 2)); 21508c2ecf20Sopenharmony_cierr_free_fb: 21518c2ecf20Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 0), 21528c2ecf20Sopenharmony_ci pci_resource_len(pdev, 0)); 21538c2ecf20Sopenharmony_ci return -ENODEV; 21548c2ecf20Sopenharmony_ci} 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_cistatic void aty128_remove(struct pci_dev *pdev) 21578c2ecf20Sopenharmony_ci{ 21588c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 21598c2ecf20Sopenharmony_ci struct aty128fb_par *par; 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci if (!info) 21628c2ecf20Sopenharmony_ci return; 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci par = info->par; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci unregister_framebuffer(info); 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT 21698c2ecf20Sopenharmony_ci aty128_bl_exit(info->bl_dev); 21708c2ecf20Sopenharmony_ci#endif 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci arch_phys_wc_del(par->wc_cookie); 21738c2ecf20Sopenharmony_ci iounmap(par->regbase); 21748c2ecf20Sopenharmony_ci iounmap(info->screen_base); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 0), 21778c2ecf20Sopenharmony_ci pci_resource_len(pdev, 0)); 21788c2ecf20Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 2), 21798c2ecf20Sopenharmony_ci pci_resource_len(pdev, 2)); 21808c2ecf20Sopenharmony_ci framebuffer_release(info); 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI */ 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci /* 21878c2ecf20Sopenharmony_ci * Blank the display. 21888c2ecf20Sopenharmony_ci */ 21898c2ecf20Sopenharmony_cistatic int aty128fb_blank(int blank, struct fb_info *fb) 21908c2ecf20Sopenharmony_ci{ 21918c2ecf20Sopenharmony_ci struct aty128fb_par *par = fb->par; 21928c2ecf20Sopenharmony_ci u8 state; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci if (par->lock_blank || par->asleep) 21958c2ecf20Sopenharmony_ci return 0; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci switch (blank) { 21988c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 21998c2ecf20Sopenharmony_ci state = 4; 22008c2ecf20Sopenharmony_ci break; 22018c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 22028c2ecf20Sopenharmony_ci state = 6; 22038c2ecf20Sopenharmony_ci break; 22048c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 22058c2ecf20Sopenharmony_ci state = 5; 22068c2ecf20Sopenharmony_ci break; 22078c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 22088c2ecf20Sopenharmony_ci state = 7; 22098c2ecf20Sopenharmony_ci break; 22108c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 22118c2ecf20Sopenharmony_ci default: 22128c2ecf20Sopenharmony_ci state = 0; 22138c2ecf20Sopenharmony_ci break; 22148c2ecf20Sopenharmony_ci } 22158c2ecf20Sopenharmony_ci aty_st_8(CRTC_EXT_CNTL+1, state); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (par->chip_gen == rage_M3) { 22188c2ecf20Sopenharmony_ci aty128_set_crt_enable(par, par->crt_on && !blank); 22198c2ecf20Sopenharmony_ci aty128_set_lcd_enable(par, par->lcd_on && !blank); 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci return 0; 22238c2ecf20Sopenharmony_ci} 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci/* 22268c2ecf20Sopenharmony_ci * Set a single color register. The values supplied are already 22278c2ecf20Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 22288c2ecf20Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 22298c2ecf20Sopenharmony_ci */ 22308c2ecf20Sopenharmony_cistatic int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 22318c2ecf20Sopenharmony_ci u_int transp, struct fb_info *info) 22328c2ecf20Sopenharmony_ci{ 22338c2ecf20Sopenharmony_ci struct aty128fb_par *par = info->par; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci if (regno > 255 22368c2ecf20Sopenharmony_ci || (par->crtc.depth == 16 && regno > 63) 22378c2ecf20Sopenharmony_ci || (par->crtc.depth == 15 && regno > 31)) 22388c2ecf20Sopenharmony_ci return 1; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci red >>= 8; 22418c2ecf20Sopenharmony_ci green >>= 8; 22428c2ecf20Sopenharmony_ci blue >>= 8; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci if (regno < 16) { 22458c2ecf20Sopenharmony_ci int i; 22468c2ecf20Sopenharmony_ci u32 *pal = info->pseudo_palette; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci switch (par->crtc.depth) { 22498c2ecf20Sopenharmony_ci case 15: 22508c2ecf20Sopenharmony_ci pal[regno] = (regno << 10) | (regno << 5) | regno; 22518c2ecf20Sopenharmony_ci break; 22528c2ecf20Sopenharmony_ci case 16: 22538c2ecf20Sopenharmony_ci pal[regno] = (regno << 11) | (regno << 6) | regno; 22548c2ecf20Sopenharmony_ci break; 22558c2ecf20Sopenharmony_ci case 24: 22568c2ecf20Sopenharmony_ci pal[regno] = (regno << 16) | (regno << 8) | regno; 22578c2ecf20Sopenharmony_ci break; 22588c2ecf20Sopenharmony_ci case 32: 22598c2ecf20Sopenharmony_ci i = (regno << 8) | regno; 22608c2ecf20Sopenharmony_ci pal[regno] = (i << 16) | i; 22618c2ecf20Sopenharmony_ci break; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci } 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci if (par->crtc.depth == 16 && regno > 0) { 22668c2ecf20Sopenharmony_ci /* 22678c2ecf20Sopenharmony_ci * With the 5-6-5 split of bits for RGB at 16 bits/pixel, we 22688c2ecf20Sopenharmony_ci * have 32 slots for R and B values but 64 slots for G values. 22698c2ecf20Sopenharmony_ci * Thus the R and B values go in one slot but the G value 22708c2ecf20Sopenharmony_ci * goes in a different slot, and we have to avoid disturbing 22718c2ecf20Sopenharmony_ci * the other fields in the slots we touch. 22728c2ecf20Sopenharmony_ci */ 22738c2ecf20Sopenharmony_ci par->green[regno] = green; 22748c2ecf20Sopenharmony_ci if (regno < 32) { 22758c2ecf20Sopenharmony_ci par->red[regno] = red; 22768c2ecf20Sopenharmony_ci par->blue[regno] = blue; 22778c2ecf20Sopenharmony_ci aty128_st_pal(regno * 8, red, par->green[regno*2], 22788c2ecf20Sopenharmony_ci blue, par); 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci red = par->red[regno/2]; 22818c2ecf20Sopenharmony_ci blue = par->blue[regno/2]; 22828c2ecf20Sopenharmony_ci regno <<= 2; 22838c2ecf20Sopenharmony_ci } else if (par->crtc.bpp == 16) 22848c2ecf20Sopenharmony_ci regno <<= 3; 22858c2ecf20Sopenharmony_ci aty128_st_pal(regno, red, green, blue, par); 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci return 0; 22888c2ecf20Sopenharmony_ci} 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci#define ATY_MIRROR_LCD_ON 0x00000001 22918c2ecf20Sopenharmony_ci#define ATY_MIRROR_CRT_ON 0x00000002 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci/* out param: u32* backlight value: 0 to 15 */ 22948c2ecf20Sopenharmony_ci#define FBIO_ATY128_GET_MIRROR _IOR('@', 1, __u32) 22958c2ecf20Sopenharmony_ci/* in param: u32* backlight value: 0 to 15 */ 22968c2ecf20Sopenharmony_ci#define FBIO_ATY128_SET_MIRROR _IOW('@', 2, __u32) 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_cistatic int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg) 22998c2ecf20Sopenharmony_ci{ 23008c2ecf20Sopenharmony_ci struct aty128fb_par *par = info->par; 23018c2ecf20Sopenharmony_ci u32 value; 23028c2ecf20Sopenharmony_ci int rc; 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci switch (cmd) { 23058c2ecf20Sopenharmony_ci case FBIO_ATY128_SET_MIRROR: 23068c2ecf20Sopenharmony_ci if (par->chip_gen != rage_M3) 23078c2ecf20Sopenharmony_ci return -EINVAL; 23088c2ecf20Sopenharmony_ci rc = get_user(value, (__u32 __user *)arg); 23098c2ecf20Sopenharmony_ci if (rc) 23108c2ecf20Sopenharmony_ci return rc; 23118c2ecf20Sopenharmony_ci par->lcd_on = (value & 0x01) != 0; 23128c2ecf20Sopenharmony_ci par->crt_on = (value & 0x02) != 0; 23138c2ecf20Sopenharmony_ci if (!par->crt_on && !par->lcd_on) 23148c2ecf20Sopenharmony_ci par->lcd_on = 1; 23158c2ecf20Sopenharmony_ci aty128_set_crt_enable(par, par->crt_on); 23168c2ecf20Sopenharmony_ci aty128_set_lcd_enable(par, par->lcd_on); 23178c2ecf20Sopenharmony_ci return 0; 23188c2ecf20Sopenharmony_ci case FBIO_ATY128_GET_MIRROR: 23198c2ecf20Sopenharmony_ci if (par->chip_gen != rage_M3) 23208c2ecf20Sopenharmony_ci return -EINVAL; 23218c2ecf20Sopenharmony_ci value = (par->crt_on << 1) | par->lcd_on; 23228c2ecf20Sopenharmony_ci return put_user(value, (__u32 __user *)arg); 23238c2ecf20Sopenharmony_ci } 23248c2ecf20Sopenharmony_ci return -EINVAL; 23258c2ecf20Sopenharmony_ci} 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_cistatic void aty128_set_suspend(struct aty128fb_par *par, int suspend) 23288c2ecf20Sopenharmony_ci{ 23298c2ecf20Sopenharmony_ci u32 pmgt; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (!par->pdev->pm_cap) 23328c2ecf20Sopenharmony_ci return; 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci /* Set the chip into the appropriate suspend mode (we use D2, 23358c2ecf20Sopenharmony_ci * D3 would require a complete re-initialisation of the chip, 23368c2ecf20Sopenharmony_ci * including PCI config registers, clocks, AGP configuration, ...) 23378c2ecf20Sopenharmony_ci * 23388c2ecf20Sopenharmony_ci * For resume, the core will have already brought us back to D0 23398c2ecf20Sopenharmony_ci */ 23408c2ecf20Sopenharmony_ci if (suspend) { 23418c2ecf20Sopenharmony_ci /* Make sure CRTC2 is reset. Remove that the day we decide to 23428c2ecf20Sopenharmony_ci * actually use CRTC2 and replace it with real code for disabling 23438c2ecf20Sopenharmony_ci * the CRTC2 output during sleep 23448c2ecf20Sopenharmony_ci */ 23458c2ecf20Sopenharmony_ci aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) & 23468c2ecf20Sopenharmony_ci ~(CRTC2_EN)); 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci /* Set the power management mode to be PCI based */ 23498c2ecf20Sopenharmony_ci /* Use this magic value for now */ 23508c2ecf20Sopenharmony_ci pmgt = 0x0c005407; 23518c2ecf20Sopenharmony_ci aty_st_pll(POWER_MANAGEMENT, pmgt); 23528c2ecf20Sopenharmony_ci (void)aty_ld_pll(POWER_MANAGEMENT); 23538c2ecf20Sopenharmony_ci aty_st_le32(BUS_CNTL1, 0x00000010); 23548c2ecf20Sopenharmony_ci aty_st_le32(MEM_POWER_MISC, 0x0c830000); 23558c2ecf20Sopenharmony_ci msleep(100); 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci} 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_cistatic int aty128_pci_suspend_late(struct device *dev, pm_message_t state) 23608c2ecf20Sopenharmony_ci{ 23618c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 23628c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 23638c2ecf20Sopenharmony_ci struct aty128fb_par *par = info->par; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci /* We don't do anything but D2, for now we return 0, but 23668c2ecf20Sopenharmony_ci * we may want to change that. How do we know if the BIOS 23678c2ecf20Sopenharmony_ci * can properly take care of D3 ? Also, with swsusp, we 23688c2ecf20Sopenharmony_ci * know we'll be rebooted, ... 23698c2ecf20Sopenharmony_ci */ 23708c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC_PMAC 23718c2ecf20Sopenharmony_ci /* HACK ALERT ! Once I find a proper way to say to each driver 23728c2ecf20Sopenharmony_ci * individually what will happen with it's PCI slot, I'll change 23738c2ecf20Sopenharmony_ci * that. On laptops, the AGP slot is just unclocked, so D2 is 23748c2ecf20Sopenharmony_ci * expected, while on desktops, the card is powered off 23758c2ecf20Sopenharmony_ci */ 23768c2ecf20Sopenharmony_ci return 0; 23778c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci if (state.event == pdev->dev.power.power_state.event) 23808c2ecf20Sopenharmony_ci return 0; 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci printk(KERN_DEBUG "aty128fb: suspending...\n"); 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci console_lock(); 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci fb_set_suspend(info, 1); 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci /* Make sure engine is reset */ 23898c2ecf20Sopenharmony_ci wait_for_idle(par); 23908c2ecf20Sopenharmony_ci aty128_reset_engine(par); 23918c2ecf20Sopenharmony_ci wait_for_idle(par); 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci /* Blank display and LCD */ 23948c2ecf20Sopenharmony_ci aty128fb_blank(FB_BLANK_POWERDOWN, info); 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci /* Sleep */ 23978c2ecf20Sopenharmony_ci par->asleep = 1; 23988c2ecf20Sopenharmony_ci par->lock_blank = 1; 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 24018c2ecf20Sopenharmony_ci /* On powermac, we have hooks to properly suspend/resume AGP now, 24028c2ecf20Sopenharmony_ci * use them here. We'll ultimately need some generic support here, 24038c2ecf20Sopenharmony_ci * but the generic code isn't quite ready for that yet 24048c2ecf20Sopenharmony_ci */ 24058c2ecf20Sopenharmony_ci pmac_suspend_agp_for_card(pdev); 24068c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci /* We need a way to make sure the fbdev layer will _not_ touch the 24098c2ecf20Sopenharmony_ci * framebuffer before we put the chip to suspend state. On 2.4, I 24108c2ecf20Sopenharmony_ci * used dummy fb ops, 2.5 need proper support for this at the 24118c2ecf20Sopenharmony_ci * fbdev level 24128c2ecf20Sopenharmony_ci */ 24138c2ecf20Sopenharmony_ci if (state.event != PM_EVENT_ON) 24148c2ecf20Sopenharmony_ci aty128_set_suspend(par, 1); 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci console_unlock(); 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci pdev->dev.power.power_state = state; 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci return 0; 24218c2ecf20Sopenharmony_ci} 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_suspend(struct device *dev) 24248c2ecf20Sopenharmony_ci{ 24258c2ecf20Sopenharmony_ci return aty128_pci_suspend_late(dev, PMSG_SUSPEND); 24268c2ecf20Sopenharmony_ci} 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_hibernate(struct device *dev) 24298c2ecf20Sopenharmony_ci{ 24308c2ecf20Sopenharmony_ci return aty128_pci_suspend_late(dev, PMSG_HIBERNATE); 24318c2ecf20Sopenharmony_ci} 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_freeze(struct device *dev) 24348c2ecf20Sopenharmony_ci{ 24358c2ecf20Sopenharmony_ci return aty128_pci_suspend_late(dev, PMSG_FREEZE); 24368c2ecf20Sopenharmony_ci} 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_cistatic int aty128_do_resume(struct pci_dev *pdev) 24398c2ecf20Sopenharmony_ci{ 24408c2ecf20Sopenharmony_ci struct fb_info *info = pci_get_drvdata(pdev); 24418c2ecf20Sopenharmony_ci struct aty128fb_par *par = info->par; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci if (pdev->dev.power.power_state.event == PM_EVENT_ON) 24448c2ecf20Sopenharmony_ci return 0; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci /* PCI state will have been restored by the core, so 24478c2ecf20Sopenharmony_ci * we should be in D0 now with our config space fully 24488c2ecf20Sopenharmony_ci * restored 24498c2ecf20Sopenharmony_ci */ 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci /* Wakeup chip */ 24528c2ecf20Sopenharmony_ci aty128_set_suspend(par, 0); 24538c2ecf20Sopenharmony_ci par->asleep = 0; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci /* Restore display & engine */ 24568c2ecf20Sopenharmony_ci aty128_reset_engine(par); 24578c2ecf20Sopenharmony_ci wait_for_idle(par); 24588c2ecf20Sopenharmony_ci aty128fb_set_par(info); 24598c2ecf20Sopenharmony_ci fb_pan_display(info, &info->var); 24608c2ecf20Sopenharmony_ci fb_set_cmap(&info->cmap, info); 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_ci /* Refresh */ 24638c2ecf20Sopenharmony_ci fb_set_suspend(info, 0); 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci /* Unblank */ 24668c2ecf20Sopenharmony_ci par->lock_blank = 0; 24678c2ecf20Sopenharmony_ci aty128fb_blank(0, info); 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_PMAC 24708c2ecf20Sopenharmony_ci /* On powermac, we have hooks to properly suspend/resume AGP now, 24718c2ecf20Sopenharmony_ci * use them here. We'll ultimately need some generic support here, 24728c2ecf20Sopenharmony_ci * but the generic code isn't quite ready for that yet 24738c2ecf20Sopenharmony_ci */ 24748c2ecf20Sopenharmony_ci pmac_resume_agp_for_card(pdev); 24758c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */ 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci pdev->dev.power.power_state = PMSG_ON; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci printk(KERN_DEBUG "aty128fb: resumed !\n"); 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci return 0; 24828c2ecf20Sopenharmony_ci} 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_cistatic int __maybe_unused aty128_pci_resume(struct device *dev) 24858c2ecf20Sopenharmony_ci{ 24868c2ecf20Sopenharmony_ci int rc; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci console_lock(); 24898c2ecf20Sopenharmony_ci rc = aty128_do_resume(to_pci_dev(dev)); 24908c2ecf20Sopenharmony_ci console_unlock(); 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci return rc; 24938c2ecf20Sopenharmony_ci} 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_cistatic int aty128fb_init(void) 24978c2ecf20Sopenharmony_ci{ 24988c2ecf20Sopenharmony_ci#ifndef MODULE 24998c2ecf20Sopenharmony_ci char *option = NULL; 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci if (fb_get_options("aty128fb", &option)) 25028c2ecf20Sopenharmony_ci return -ENODEV; 25038c2ecf20Sopenharmony_ci aty128fb_setup(option); 25048c2ecf20Sopenharmony_ci#endif 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci return pci_register_driver(&aty128fb_driver); 25078c2ecf20Sopenharmony_ci} 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_cistatic void __exit aty128fb_exit(void) 25108c2ecf20Sopenharmony_ci{ 25118c2ecf20Sopenharmony_ci pci_unregister_driver(&aty128fb_driver); 25128c2ecf20Sopenharmony_ci} 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_cimodule_init(aty128fb_init); 25158c2ecf20Sopenharmony_ci 25168c2ecf20Sopenharmony_cimodule_exit(aty128fb_exit); 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_ciMODULE_AUTHOR("(c)1999-2003 Brad Douglas <brad@neruo.com>"); 25198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards"); 25208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 25218c2ecf20Sopenharmony_cimodule_param(mode_option, charp, 0); 25228c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); 25238c2ecf20Sopenharmony_cimodule_param_named(nomtrr, mtrr, invbool, 0); 25248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)"); 2525