1*f7018c21STomi Valkeinen /* 2*f7018c21STomi Valkeinen * ATI Frame Buffer Device Driver Core 3*f7018c21STomi Valkeinen * 4*f7018c21STomi Valkeinen * Copyright (C) 2004 Alex Kern <alex.kern@gmx.de> 5*f7018c21STomi Valkeinen * Copyright (C) 1997-2001 Geert Uytterhoeven 6*f7018c21STomi Valkeinen * Copyright (C) 1998 Bernd Harries 7*f7018c21STomi Valkeinen * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) 8*f7018c21STomi Valkeinen * 9*f7018c21STomi Valkeinen * This driver supports the following ATI graphics chips: 10*f7018c21STomi Valkeinen * - ATI Mach64 11*f7018c21STomi Valkeinen * 12*f7018c21STomi Valkeinen * To do: add support for 13*f7018c21STomi Valkeinen * - ATI Rage128 (from aty128fb.c) 14*f7018c21STomi Valkeinen * - ATI Radeon (from radeonfb.c) 15*f7018c21STomi Valkeinen * 16*f7018c21STomi Valkeinen * This driver is partly based on the PowerMac console driver: 17*f7018c21STomi Valkeinen * 18*f7018c21STomi Valkeinen * Copyright (C) 1996 Paul Mackerras 19*f7018c21STomi Valkeinen * 20*f7018c21STomi Valkeinen * and on the PowerMac ATI/mach64 display driver: 21*f7018c21STomi Valkeinen * 22*f7018c21STomi Valkeinen * Copyright (C) 1997 Michael AK Tesch 23*f7018c21STomi Valkeinen * 24*f7018c21STomi Valkeinen * with work by Jon Howell 25*f7018c21STomi Valkeinen * Harry AC Eaton 26*f7018c21STomi Valkeinen * Anthony Tong <atong@uiuc.edu> 27*f7018c21STomi Valkeinen * 28*f7018c21STomi Valkeinen * Generic LCD support written by Daniel Mantione, ported from 2.4.20 by Alex Kern 29*f7018c21STomi Valkeinen * Many Thanks to Ville Syrjälä for patches and fixing nasting 16 bit color bug. 30*f7018c21STomi Valkeinen * 31*f7018c21STomi Valkeinen * This file is subject to the terms and conditions of the GNU General Public 32*f7018c21STomi Valkeinen * License. See the file COPYING in the main directory of this archive for 33*f7018c21STomi Valkeinen * more details. 34*f7018c21STomi Valkeinen * 35*f7018c21STomi Valkeinen * Many thanks to Nitya from ATI devrel for support and patience ! 36*f7018c21STomi Valkeinen */ 37*f7018c21STomi Valkeinen 38*f7018c21STomi Valkeinen /****************************************************************************** 39*f7018c21STomi Valkeinen 40*f7018c21STomi Valkeinen TODO: 41*f7018c21STomi Valkeinen 42*f7018c21STomi Valkeinen - cursor support on all cards and all ramdacs. 43*f7018c21STomi Valkeinen - cursor parameters controlable via ioctl()s. 44*f7018c21STomi Valkeinen - guess PLL and MCLK based on the original PLL register values initialized 45*f7018c21STomi Valkeinen by Open Firmware (if they are initialized). BIOS is done 46*f7018c21STomi Valkeinen 47*f7018c21STomi Valkeinen (Anyone with Mac to help with this?) 48*f7018c21STomi Valkeinen 49*f7018c21STomi Valkeinen ******************************************************************************/ 50*f7018c21STomi Valkeinen 51*f7018c21STomi Valkeinen 52*f7018c21STomi Valkeinen #include <linux/module.h> 53*f7018c21STomi Valkeinen #include <linux/moduleparam.h> 54*f7018c21STomi Valkeinen #include <linux/kernel.h> 55*f7018c21STomi Valkeinen #include <linux/errno.h> 56*f7018c21STomi Valkeinen #include <linux/string.h> 57*f7018c21STomi Valkeinen #include <linux/mm.h> 58*f7018c21STomi Valkeinen #include <linux/slab.h> 59*f7018c21STomi Valkeinen #include <linux/vmalloc.h> 60*f7018c21STomi Valkeinen #include <linux/delay.h> 61*f7018c21STomi Valkeinen #include <linux/compiler.h> 62*f7018c21STomi Valkeinen #include <linux/console.h> 63*f7018c21STomi Valkeinen #include <linux/fb.h> 64*f7018c21STomi Valkeinen #include <linux/init.h> 65*f7018c21STomi Valkeinen #include <linux/pci.h> 66*f7018c21STomi Valkeinen #include <linux/interrupt.h> 67*f7018c21STomi Valkeinen #include <linux/spinlock.h> 68*f7018c21STomi Valkeinen #include <linux/wait.h> 69*f7018c21STomi Valkeinen #include <linux/backlight.h> 70*f7018c21STomi Valkeinen #include <linux/reboot.h> 71*f7018c21STomi Valkeinen #include <linux/dmi.h> 72*f7018c21STomi Valkeinen 73*f7018c21STomi Valkeinen #include <asm/io.h> 74*f7018c21STomi Valkeinen #include <linux/uaccess.h> 75*f7018c21STomi Valkeinen 76*f7018c21STomi Valkeinen #include <video/mach64.h> 77*f7018c21STomi Valkeinen #include "atyfb.h" 78*f7018c21STomi Valkeinen #include "ati_ids.h" 79*f7018c21STomi Valkeinen 80*f7018c21STomi Valkeinen #ifdef __powerpc__ 81*f7018c21STomi Valkeinen #include <asm/machdep.h> 82*f7018c21STomi Valkeinen #include <asm/prom.h> 83*f7018c21STomi Valkeinen #include "../macmodes.h" 84*f7018c21STomi Valkeinen #endif 85*f7018c21STomi Valkeinen #ifdef __sparc__ 86*f7018c21STomi Valkeinen #include <asm/fbio.h> 87*f7018c21STomi Valkeinen #include <asm/oplib.h> 88*f7018c21STomi Valkeinen #include <asm/prom.h> 89*f7018c21STomi Valkeinen #endif 90*f7018c21STomi Valkeinen 91*f7018c21STomi Valkeinen #ifdef CONFIG_ADB_PMU 92*f7018c21STomi Valkeinen #include <linux/adb.h> 93*f7018c21STomi Valkeinen #include <linux/pmu.h> 94*f7018c21STomi Valkeinen #endif 95*f7018c21STomi Valkeinen #ifdef CONFIG_BOOTX_TEXT 96*f7018c21STomi Valkeinen #include <asm/btext.h> 97*f7018c21STomi Valkeinen #endif 98*f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT 99*f7018c21STomi Valkeinen #include <asm/backlight.h> 100*f7018c21STomi Valkeinen #endif 101*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 102*f7018c21STomi Valkeinen #include <asm/mtrr.h> 103*f7018c21STomi Valkeinen #endif 104*f7018c21STomi Valkeinen 105*f7018c21STomi Valkeinen /* 106*f7018c21STomi Valkeinen * Debug flags. 107*f7018c21STomi Valkeinen */ 108*f7018c21STomi Valkeinen #undef DEBUG 109*f7018c21STomi Valkeinen /*#define DEBUG*/ 110*f7018c21STomi Valkeinen 111*f7018c21STomi Valkeinen /* Make sure n * PAGE_SIZE is protected at end of Aperture for GUI-regs */ 112*f7018c21STomi Valkeinen /* - must be large enough to catch all GUI-Regs */ 113*f7018c21STomi Valkeinen /* - must be aligned to a PAGE boundary */ 114*f7018c21STomi Valkeinen #define GUI_RESERVE (1 * PAGE_SIZE) 115*f7018c21STomi Valkeinen 116*f7018c21STomi Valkeinen /* FIXME: remove the FAIL definition */ 117*f7018c21STomi Valkeinen #define FAIL(msg) do { \ 118*f7018c21STomi Valkeinen if (!(var->activate & FB_ACTIVATE_TEST)) \ 119*f7018c21STomi Valkeinen printk(KERN_CRIT "atyfb: " msg "\n"); \ 120*f7018c21STomi Valkeinen return -EINVAL; \ 121*f7018c21STomi Valkeinen } while (0) 122*f7018c21STomi Valkeinen #define FAIL_MAX(msg, x, _max_) do { \ 123*f7018c21STomi Valkeinen if (x > _max_) { \ 124*f7018c21STomi Valkeinen if (!(var->activate & FB_ACTIVATE_TEST)) \ 125*f7018c21STomi Valkeinen printk(KERN_CRIT "atyfb: " msg " %x(%x)\n", x, _max_); \ 126*f7018c21STomi Valkeinen return -EINVAL; \ 127*f7018c21STomi Valkeinen } \ 128*f7018c21STomi Valkeinen } while (0) 129*f7018c21STomi Valkeinen #ifdef DEBUG 130*f7018c21STomi Valkeinen #define DPRINTK(fmt, args...) printk(KERN_DEBUG "atyfb: " fmt, ## args) 131*f7018c21STomi Valkeinen #else 132*f7018c21STomi Valkeinen #define DPRINTK(fmt, args...) 133*f7018c21STomi Valkeinen #endif 134*f7018c21STomi Valkeinen 135*f7018c21STomi Valkeinen #define PRINTKI(fmt, args...) printk(KERN_INFO "atyfb: " fmt, ## args) 136*f7018c21STomi Valkeinen #define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args) 137*f7018c21STomi Valkeinen 138*f7018c21STomi Valkeinen #if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \ 139*f7018c21STomi Valkeinen defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_FB_ATY_BACKLIGHT) 140*f7018c21STomi Valkeinen static const u32 lt_lcd_regs[] = { 141*f7018c21STomi Valkeinen CNFG_PANEL_LG, 142*f7018c21STomi Valkeinen LCD_GEN_CNTL_LG, 143*f7018c21STomi Valkeinen DSTN_CONTROL_LG, 144*f7018c21STomi Valkeinen HFB_PITCH_ADDR_LG, 145*f7018c21STomi Valkeinen HORZ_STRETCHING_LG, 146*f7018c21STomi Valkeinen VERT_STRETCHING_LG, 147*f7018c21STomi Valkeinen 0, /* EXT_VERT_STRETCH */ 148*f7018c21STomi Valkeinen LT_GIO_LG, 149*f7018c21STomi Valkeinen POWER_MANAGEMENT_LG 150*f7018c21STomi Valkeinen }; 151*f7018c21STomi Valkeinen 152*f7018c21STomi Valkeinen void aty_st_lcd(int index, u32 val, const struct atyfb_par *par) 153*f7018c21STomi Valkeinen { 154*f7018c21STomi Valkeinen if (M64_HAS(LT_LCD_REGS)) { 155*f7018c21STomi Valkeinen aty_st_le32(lt_lcd_regs[index], val, par); 156*f7018c21STomi Valkeinen } else { 157*f7018c21STomi Valkeinen unsigned long temp; 158*f7018c21STomi Valkeinen 159*f7018c21STomi Valkeinen /* write addr byte */ 160*f7018c21STomi Valkeinen temp = aty_ld_le32(LCD_INDEX, par); 161*f7018c21STomi Valkeinen aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par); 162*f7018c21STomi Valkeinen /* write the register value */ 163*f7018c21STomi Valkeinen aty_st_le32(LCD_DATA, val, par); 164*f7018c21STomi Valkeinen } 165*f7018c21STomi Valkeinen } 166*f7018c21STomi Valkeinen 167*f7018c21STomi Valkeinen u32 aty_ld_lcd(int index, const struct atyfb_par *par) 168*f7018c21STomi Valkeinen { 169*f7018c21STomi Valkeinen if (M64_HAS(LT_LCD_REGS)) { 170*f7018c21STomi Valkeinen return aty_ld_le32(lt_lcd_regs[index], par); 171*f7018c21STomi Valkeinen } else { 172*f7018c21STomi Valkeinen unsigned long temp; 173*f7018c21STomi Valkeinen 174*f7018c21STomi Valkeinen /* write addr byte */ 175*f7018c21STomi Valkeinen temp = aty_ld_le32(LCD_INDEX, par); 176*f7018c21STomi Valkeinen aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par); 177*f7018c21STomi Valkeinen /* read the register value */ 178*f7018c21STomi Valkeinen return aty_ld_le32(LCD_DATA, par); 179*f7018c21STomi Valkeinen } 180*f7018c21STomi Valkeinen } 181*f7018c21STomi Valkeinen #endif /* defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) */ 182*f7018c21STomi Valkeinen 183*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 184*f7018c21STomi Valkeinen /* 185*f7018c21STomi Valkeinen * ATIReduceRatio -- 186*f7018c21STomi Valkeinen * 187*f7018c21STomi Valkeinen * Reduce a fraction by factoring out the largest common divider of the 188*f7018c21STomi Valkeinen * fraction's numerator and denominator. 189*f7018c21STomi Valkeinen */ 190*f7018c21STomi Valkeinen static void ATIReduceRatio(int *Numerator, int *Denominator) 191*f7018c21STomi Valkeinen { 192*f7018c21STomi Valkeinen int Multiplier, Divider, Remainder; 193*f7018c21STomi Valkeinen 194*f7018c21STomi Valkeinen Multiplier = *Numerator; 195*f7018c21STomi Valkeinen Divider = *Denominator; 196*f7018c21STomi Valkeinen 197*f7018c21STomi Valkeinen while ((Remainder = Multiplier % Divider)) { 198*f7018c21STomi Valkeinen Multiplier = Divider; 199*f7018c21STomi Valkeinen Divider = Remainder; 200*f7018c21STomi Valkeinen } 201*f7018c21STomi Valkeinen 202*f7018c21STomi Valkeinen *Numerator /= Divider; 203*f7018c21STomi Valkeinen *Denominator /= Divider; 204*f7018c21STomi Valkeinen } 205*f7018c21STomi Valkeinen #endif 206*f7018c21STomi Valkeinen /* 207*f7018c21STomi Valkeinen * The Hardware parameters for each card 208*f7018c21STomi Valkeinen */ 209*f7018c21STomi Valkeinen 210*f7018c21STomi Valkeinen struct pci_mmap_map { 211*f7018c21STomi Valkeinen unsigned long voff; 212*f7018c21STomi Valkeinen unsigned long poff; 213*f7018c21STomi Valkeinen unsigned long size; 214*f7018c21STomi Valkeinen unsigned long prot_flag; 215*f7018c21STomi Valkeinen unsigned long prot_mask; 216*f7018c21STomi Valkeinen }; 217*f7018c21STomi Valkeinen 218*f7018c21STomi Valkeinen static struct fb_fix_screeninfo atyfb_fix = { 219*f7018c21STomi Valkeinen .id = "ATY Mach64", 220*f7018c21STomi Valkeinen .type = FB_TYPE_PACKED_PIXELS, 221*f7018c21STomi Valkeinen .visual = FB_VISUAL_PSEUDOCOLOR, 222*f7018c21STomi Valkeinen .xpanstep = 8, 223*f7018c21STomi Valkeinen .ypanstep = 1, 224*f7018c21STomi Valkeinen }; 225*f7018c21STomi Valkeinen 226*f7018c21STomi Valkeinen /* 227*f7018c21STomi Valkeinen * Frame buffer device API 228*f7018c21STomi Valkeinen */ 229*f7018c21STomi Valkeinen 230*f7018c21STomi Valkeinen static int atyfb_open(struct fb_info *info, int user); 231*f7018c21STomi Valkeinen static int atyfb_release(struct fb_info *info, int user); 232*f7018c21STomi Valkeinen static int atyfb_check_var(struct fb_var_screeninfo *var, 233*f7018c21STomi Valkeinen struct fb_info *info); 234*f7018c21STomi Valkeinen static int atyfb_set_par(struct fb_info *info); 235*f7018c21STomi Valkeinen static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 236*f7018c21STomi Valkeinen u_int transp, struct fb_info *info); 237*f7018c21STomi Valkeinen static int atyfb_pan_display(struct fb_var_screeninfo *var, 238*f7018c21STomi Valkeinen struct fb_info *info); 239*f7018c21STomi Valkeinen static int atyfb_blank(int blank, struct fb_info *info); 240*f7018c21STomi Valkeinen static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg); 241*f7018c21STomi Valkeinen #ifdef __sparc__ 242*f7018c21STomi Valkeinen static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma); 243*f7018c21STomi Valkeinen #endif 244*f7018c21STomi Valkeinen static int atyfb_sync(struct fb_info *info); 245*f7018c21STomi Valkeinen 246*f7018c21STomi Valkeinen /* 247*f7018c21STomi Valkeinen * Internal routines 248*f7018c21STomi Valkeinen */ 249*f7018c21STomi Valkeinen 250*f7018c21STomi Valkeinen static int aty_init(struct fb_info *info); 251*f7018c21STomi Valkeinen 252*f7018c21STomi Valkeinen static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc); 253*f7018c21STomi Valkeinen 254*f7018c21STomi Valkeinen static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); 255*f7018c21STomi Valkeinen static int aty_var_to_crtc(const struct fb_info *info, 256*f7018c21STomi Valkeinen const struct fb_var_screeninfo *var, 257*f7018c21STomi Valkeinen struct crtc *crtc); 258*f7018c21STomi Valkeinen static int aty_crtc_to_var(const struct crtc *crtc, 259*f7018c21STomi Valkeinen struct fb_var_screeninfo *var); 260*f7018c21STomi Valkeinen static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info); 261*f7018c21STomi Valkeinen #ifdef CONFIG_PPC 262*f7018c21STomi Valkeinen static int read_aty_sense(const struct atyfb_par *par); 263*f7018c21STomi Valkeinen #endif 264*f7018c21STomi Valkeinen 265*f7018c21STomi Valkeinen static DEFINE_MUTEX(reboot_lock); 266*f7018c21STomi Valkeinen static struct fb_info *reboot_info; 267*f7018c21STomi Valkeinen 268*f7018c21STomi Valkeinen /* 269*f7018c21STomi Valkeinen * Interface used by the world 270*f7018c21STomi Valkeinen */ 271*f7018c21STomi Valkeinen 272*f7018c21STomi Valkeinen static struct fb_var_screeninfo default_var = { 273*f7018c21STomi Valkeinen /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ 274*f7018c21STomi Valkeinen 640, 480, 640, 480, 0, 0, 8, 0, 275*f7018c21STomi Valkeinen {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 276*f7018c21STomi Valkeinen 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2, 277*f7018c21STomi Valkeinen 0, FB_VMODE_NONINTERLACED 278*f7018c21STomi Valkeinen }; 279*f7018c21STomi Valkeinen 280*f7018c21STomi Valkeinen static struct fb_videomode defmode = { 281*f7018c21STomi Valkeinen /* 640x480 @ 60 Hz, 31.5 kHz hsync */ 282*f7018c21STomi Valkeinen NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2, 283*f7018c21STomi Valkeinen 0, FB_VMODE_NONINTERLACED 284*f7018c21STomi Valkeinen }; 285*f7018c21STomi Valkeinen 286*f7018c21STomi Valkeinen static struct fb_ops atyfb_ops = { 287*f7018c21STomi Valkeinen .owner = THIS_MODULE, 288*f7018c21STomi Valkeinen .fb_open = atyfb_open, 289*f7018c21STomi Valkeinen .fb_release = atyfb_release, 290*f7018c21STomi Valkeinen .fb_check_var = atyfb_check_var, 291*f7018c21STomi Valkeinen .fb_set_par = atyfb_set_par, 292*f7018c21STomi Valkeinen .fb_setcolreg = atyfb_setcolreg, 293*f7018c21STomi Valkeinen .fb_pan_display = atyfb_pan_display, 294*f7018c21STomi Valkeinen .fb_blank = atyfb_blank, 295*f7018c21STomi Valkeinen .fb_ioctl = atyfb_ioctl, 296*f7018c21STomi Valkeinen .fb_fillrect = atyfb_fillrect, 297*f7018c21STomi Valkeinen .fb_copyarea = atyfb_copyarea, 298*f7018c21STomi Valkeinen .fb_imageblit = atyfb_imageblit, 299*f7018c21STomi Valkeinen #ifdef __sparc__ 300*f7018c21STomi Valkeinen .fb_mmap = atyfb_mmap, 301*f7018c21STomi Valkeinen #endif 302*f7018c21STomi Valkeinen .fb_sync = atyfb_sync, 303*f7018c21STomi Valkeinen }; 304*f7018c21STomi Valkeinen 305*f7018c21STomi Valkeinen static bool noaccel; 306*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 307*f7018c21STomi Valkeinen static bool nomtrr; 308*f7018c21STomi Valkeinen #endif 309*f7018c21STomi Valkeinen static int vram; 310*f7018c21STomi Valkeinen static int pll; 311*f7018c21STomi Valkeinen static int mclk; 312*f7018c21STomi Valkeinen static int xclk; 313*f7018c21STomi Valkeinen static int comp_sync = -1; 314*f7018c21STomi Valkeinen static char *mode; 315*f7018c21STomi Valkeinen 316*f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT 317*f7018c21STomi Valkeinen static int backlight = 1; 318*f7018c21STomi Valkeinen #else 319*f7018c21STomi Valkeinen static int backlight = 0; 320*f7018c21STomi Valkeinen #endif 321*f7018c21STomi Valkeinen 322*f7018c21STomi Valkeinen #ifdef CONFIG_PPC 323*f7018c21STomi Valkeinen static int default_vmode = VMODE_CHOOSE; 324*f7018c21STomi Valkeinen static int default_cmode = CMODE_CHOOSE; 325*f7018c21STomi Valkeinen 326*f7018c21STomi Valkeinen module_param_named(vmode, default_vmode, int, 0); 327*f7018c21STomi Valkeinen MODULE_PARM_DESC(vmode, "int: video mode for mac"); 328*f7018c21STomi Valkeinen module_param_named(cmode, default_cmode, int, 0); 329*f7018c21STomi Valkeinen MODULE_PARM_DESC(cmode, "int: color mode for mac"); 330*f7018c21STomi Valkeinen #endif 331*f7018c21STomi Valkeinen 332*f7018c21STomi Valkeinen #ifdef CONFIG_ATARI 333*f7018c21STomi Valkeinen static unsigned int mach64_count = 0; 334*f7018c21STomi Valkeinen static unsigned long phys_vmembase[FB_MAX] = { 0, }; 335*f7018c21STomi Valkeinen static unsigned long phys_size[FB_MAX] = { 0, }; 336*f7018c21STomi Valkeinen static unsigned long phys_guiregbase[FB_MAX] = { 0, }; 337*f7018c21STomi Valkeinen #endif 338*f7018c21STomi Valkeinen 339*f7018c21STomi Valkeinen /* top -> down is an evolution of mach64 chipset, any corrections? */ 340*f7018c21STomi Valkeinen #define ATI_CHIP_88800GX (M64F_GX) 341*f7018c21STomi Valkeinen #define ATI_CHIP_88800CX (M64F_GX) 342*f7018c21STomi Valkeinen 343*f7018c21STomi Valkeinen #define ATI_CHIP_264CT (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO) 344*f7018c21STomi Valkeinen #define ATI_CHIP_264ET (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO) 345*f7018c21STomi Valkeinen 346*f7018c21STomi Valkeinen #define ATI_CHIP_264VT (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_MAGIC_FIFO) 347*f7018c21STomi Valkeinen #define ATI_CHIP_264GT (M64F_GT | M64F_INTEGRATED | M64F_MAGIC_FIFO | M64F_EXTRA_BRIGHT) 348*f7018c21STomi Valkeinen 349*f7018c21STomi Valkeinen #define ATI_CHIP_264VTB (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP) 350*f7018c21STomi Valkeinen #define ATI_CHIP_264VT3 (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL) 351*f7018c21STomi Valkeinen #define ATI_CHIP_264VT4 (M64F_VT | M64F_INTEGRATED | M64F_GTB_DSP) 352*f7018c21STomi Valkeinen 353*f7018c21STomi Valkeinen /* FIXME what is this chip? */ 354*f7018c21STomi Valkeinen #define ATI_CHIP_264LT (M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP) 355*f7018c21STomi Valkeinen 356*f7018c21STomi Valkeinen /* make sets shorter */ 357*f7018c21STomi Valkeinen #define ATI_MODERN_SET (M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | M64F_EXTRA_BRIGHT) 358*f7018c21STomi Valkeinen 359*f7018c21STomi Valkeinen #define ATI_CHIP_264GTB (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL) 360*f7018c21STomi Valkeinen /*#define ATI_CHIP_264GTDVD ?*/ 361*f7018c21STomi Valkeinen #define ATI_CHIP_264LTG (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL) 362*f7018c21STomi Valkeinen 363*f7018c21STomi Valkeinen #define ATI_CHIP_264GT2C (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE) 364*f7018c21STomi Valkeinen #define ATI_CHIP_264GTPRO (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D) 365*f7018c21STomi Valkeinen #define ATI_CHIP_264LTPRO (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D) 366*f7018c21STomi Valkeinen 367*f7018c21STomi Valkeinen #define ATI_CHIP_264XL (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM) 368*f7018c21STomi Valkeinen #define ATI_CHIP_MOBILITY (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM | M64F_MOBIL_BUS) 369*f7018c21STomi Valkeinen 370*f7018c21STomi Valkeinen static struct { 371*f7018c21STomi Valkeinen u16 pci_id; 372*f7018c21STomi Valkeinen const char *name; 373*f7018c21STomi Valkeinen int pll, mclk, xclk, ecp_max; 374*f7018c21STomi Valkeinen u32 features; 375*f7018c21STomi Valkeinen } aty_chips[] = { 376*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX 377*f7018c21STomi Valkeinen /* Mach64 GX */ 378*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GX, "ATI888GX00 (Mach64 GX)", 135, 50, 50, 0, ATI_CHIP_88800GX }, 379*f7018c21STomi Valkeinen { PCI_CHIP_MACH64CX, "ATI888CX00 (Mach64 CX)", 135, 50, 50, 0, ATI_CHIP_88800CX }, 380*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */ 381*f7018c21STomi Valkeinen 382*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 383*f7018c21STomi Valkeinen { PCI_CHIP_MACH64CT, "ATI264CT (Mach64 CT)", 135, 60, 60, 0, ATI_CHIP_264CT }, 384*f7018c21STomi Valkeinen { PCI_CHIP_MACH64ET, "ATI264ET (Mach64 ET)", 135, 60, 60, 0, ATI_CHIP_264ET }, 385*f7018c21STomi Valkeinen 386*f7018c21STomi Valkeinen /* FIXME what is this chip? */ 387*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LT, "ATI264LT (Mach64 LT)", 135, 63, 63, 0, ATI_CHIP_264LT }, 388*f7018c21STomi Valkeinen 389*f7018c21STomi Valkeinen { PCI_CHIP_MACH64VT, "ATI264VT (Mach64 VT)", 170, 67, 67, 80, ATI_CHIP_264VT }, 390*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GT, "3D RAGE (Mach64 GT)", 135, 63, 63, 80, ATI_CHIP_264GT }, 391*f7018c21STomi Valkeinen 392*f7018c21STomi Valkeinen { PCI_CHIP_MACH64VU, "ATI264VT3 (Mach64 VU)", 200, 67, 67, 80, ATI_CHIP_264VT3 }, 393*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GU, "3D RAGE II+ (Mach64 GU)", 200, 67, 67, 100, ATI_CHIP_264GTB }, 394*f7018c21STomi Valkeinen 395*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LG, "3D RAGE LT (Mach64 LG)", 230, 63, 63, 100, ATI_CHIP_264LTG | M64F_LT_LCD_REGS | M64F_G3_PB_1024x768 }, 396*f7018c21STomi Valkeinen 397*f7018c21STomi Valkeinen { PCI_CHIP_MACH64VV, "ATI264VT4 (Mach64 VV)", 230, 83, 83, 100, ATI_CHIP_264VT4 }, 398*f7018c21STomi Valkeinen 399*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GV, "3D RAGE IIC (Mach64 GV, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C }, 400*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GW, "3D RAGE IIC (Mach64 GW, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C }, 401*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GY, "3D RAGE IIC (Mach64 GY, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C }, 402*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GZ, "3D RAGE IIC (Mach64 GZ, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C }, 403*f7018c21STomi Valkeinen 404*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GB, "3D RAGE PRO (Mach64 GB, BGA, AGP)", 230, 100, 100, 125, ATI_CHIP_264GTPRO }, 405*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GD, "3D RAGE PRO (Mach64 GD, BGA, AGP 1x)", 230, 100, 100, 125, ATI_CHIP_264GTPRO }, 406*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GI, "3D RAGE PRO (Mach64 GI, BGA, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO | M64F_MAGIC_VRAM_SIZE }, 407*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GP, "3D RAGE PRO (Mach64 GP, PQFP, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO }, 408*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GQ, "3D RAGE PRO (Mach64 GQ, PQFP, PCI, limited 3D)", 230, 100, 100, 125, ATI_CHIP_264GTPRO }, 409*f7018c21STomi Valkeinen 410*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LB, "3D RAGE LT PRO (Mach64 LB, AGP)", 236, 75, 100, 135, ATI_CHIP_264LTPRO }, 411*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LD, "3D RAGE LT PRO (Mach64 LD, AGP)", 230, 100, 100, 135, ATI_CHIP_264LTPRO }, 412*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LI, "3D RAGE LT PRO (Mach64 LI, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1_1 | M64F_G3_PB_1024x768 }, 413*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LP, "3D RAGE LT PRO (Mach64 LP, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1024x768 }, 414*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LQ, "3D RAGE LT PRO (Mach64 LQ, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO }, 415*f7018c21STomi Valkeinen 416*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL }, 417*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GN, "3D RAGE XC (Mach64 GN, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL }, 418*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL }, 419*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GL, "3D RAGE XC (Mach64 GL, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL }, 420*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL | M64F_SDRAM_MAGIC_PLL }, 421*f7018c21STomi Valkeinen { PCI_CHIP_MACH64GS, "3D RAGE XC (Mach64 GS, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL }, 422*f7018c21STomi Valkeinen 423*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LM, "3D RAGE Mobility P/M (Mach64 LM, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY }, 424*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LN, "3D RAGE Mobility L (Mach64 LN, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY }, 425*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LR, "3D RAGE Mobility P/M (Mach64 LR, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY }, 426*f7018c21STomi Valkeinen { PCI_CHIP_MACH64LS, "3D RAGE Mobility L (Mach64 LS, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY }, 427*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */ 428*f7018c21STomi Valkeinen }; 429*f7018c21STomi Valkeinen 430*f7018c21STomi Valkeinen static int correct_chipset(struct atyfb_par *par) 431*f7018c21STomi Valkeinen { 432*f7018c21STomi Valkeinen u8 rev; 433*f7018c21STomi Valkeinen u16 type; 434*f7018c21STomi Valkeinen u32 chip_id; 435*f7018c21STomi Valkeinen const char *name; 436*f7018c21STomi Valkeinen int i; 437*f7018c21STomi Valkeinen 438*f7018c21STomi Valkeinen for (i = (int)ARRAY_SIZE(aty_chips) - 1; i >= 0; i--) 439*f7018c21STomi Valkeinen if (par->pci_id == aty_chips[i].pci_id) 440*f7018c21STomi Valkeinen break; 441*f7018c21STomi Valkeinen 442*f7018c21STomi Valkeinen if (i < 0) 443*f7018c21STomi Valkeinen return -ENODEV; 444*f7018c21STomi Valkeinen 445*f7018c21STomi Valkeinen name = aty_chips[i].name; 446*f7018c21STomi Valkeinen par->pll_limits.pll_max = aty_chips[i].pll; 447*f7018c21STomi Valkeinen par->pll_limits.mclk = aty_chips[i].mclk; 448*f7018c21STomi Valkeinen par->pll_limits.xclk = aty_chips[i].xclk; 449*f7018c21STomi Valkeinen par->pll_limits.ecp_max = aty_chips[i].ecp_max; 450*f7018c21STomi Valkeinen par->features = aty_chips[i].features; 451*f7018c21STomi Valkeinen 452*f7018c21STomi Valkeinen chip_id = aty_ld_le32(CNFG_CHIP_ID, par); 453*f7018c21STomi Valkeinen type = chip_id & CFG_CHIP_TYPE; 454*f7018c21STomi Valkeinen rev = (chip_id & CFG_CHIP_REV) >> 24; 455*f7018c21STomi Valkeinen 456*f7018c21STomi Valkeinen switch (par->pci_id) { 457*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX 458*f7018c21STomi Valkeinen case PCI_CHIP_MACH64GX: 459*f7018c21STomi Valkeinen if (type != 0x00d7) 460*f7018c21STomi Valkeinen return -ENODEV; 461*f7018c21STomi Valkeinen break; 462*f7018c21STomi Valkeinen case PCI_CHIP_MACH64CX: 463*f7018c21STomi Valkeinen if (type != 0x0057) 464*f7018c21STomi Valkeinen return -ENODEV; 465*f7018c21STomi Valkeinen break; 466*f7018c21STomi Valkeinen #endif 467*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 468*f7018c21STomi Valkeinen case PCI_CHIP_MACH64VT: 469*f7018c21STomi Valkeinen switch (rev & 0x07) { 470*f7018c21STomi Valkeinen case 0x00: 471*f7018c21STomi Valkeinen switch (rev & 0xc0) { 472*f7018c21STomi Valkeinen case 0x00: 473*f7018c21STomi Valkeinen name = "ATI264VT (A3) (Mach64 VT)"; 474*f7018c21STomi Valkeinen par->pll_limits.pll_max = 170; 475*f7018c21STomi Valkeinen par->pll_limits.mclk = 67; 476*f7018c21STomi Valkeinen par->pll_limits.xclk = 67; 477*f7018c21STomi Valkeinen par->pll_limits.ecp_max = 80; 478*f7018c21STomi Valkeinen par->features = ATI_CHIP_264VT; 479*f7018c21STomi Valkeinen break; 480*f7018c21STomi Valkeinen case 0x40: 481*f7018c21STomi Valkeinen name = "ATI264VT2 (A4) (Mach64 VT)"; 482*f7018c21STomi Valkeinen par->pll_limits.pll_max = 200; 483*f7018c21STomi Valkeinen par->pll_limits.mclk = 67; 484*f7018c21STomi Valkeinen par->pll_limits.xclk = 67; 485*f7018c21STomi Valkeinen par->pll_limits.ecp_max = 80; 486*f7018c21STomi Valkeinen par->features = ATI_CHIP_264VT | M64F_MAGIC_POSTDIV; 487*f7018c21STomi Valkeinen break; 488*f7018c21STomi Valkeinen } 489*f7018c21STomi Valkeinen break; 490*f7018c21STomi Valkeinen case 0x01: 491*f7018c21STomi Valkeinen name = "ATI264VT3 (B1) (Mach64 VT)"; 492*f7018c21STomi Valkeinen par->pll_limits.pll_max = 200; 493*f7018c21STomi Valkeinen par->pll_limits.mclk = 67; 494*f7018c21STomi Valkeinen par->pll_limits.xclk = 67; 495*f7018c21STomi Valkeinen par->pll_limits.ecp_max = 80; 496*f7018c21STomi Valkeinen par->features = ATI_CHIP_264VTB; 497*f7018c21STomi Valkeinen break; 498*f7018c21STomi Valkeinen case 0x02: 499*f7018c21STomi Valkeinen name = "ATI264VT3 (B2) (Mach64 VT)"; 500*f7018c21STomi Valkeinen par->pll_limits.pll_max = 200; 501*f7018c21STomi Valkeinen par->pll_limits.mclk = 67; 502*f7018c21STomi Valkeinen par->pll_limits.xclk = 67; 503*f7018c21STomi Valkeinen par->pll_limits.ecp_max = 80; 504*f7018c21STomi Valkeinen par->features = ATI_CHIP_264VT3; 505*f7018c21STomi Valkeinen break; 506*f7018c21STomi Valkeinen } 507*f7018c21STomi Valkeinen break; 508*f7018c21STomi Valkeinen case PCI_CHIP_MACH64GT: 509*f7018c21STomi Valkeinen switch (rev & 0x07) { 510*f7018c21STomi Valkeinen case 0x01: 511*f7018c21STomi Valkeinen name = "3D RAGE II (Mach64 GT)"; 512*f7018c21STomi Valkeinen par->pll_limits.pll_max = 170; 513*f7018c21STomi Valkeinen par->pll_limits.mclk = 67; 514*f7018c21STomi Valkeinen par->pll_limits.xclk = 67; 515*f7018c21STomi Valkeinen par->pll_limits.ecp_max = 80; 516*f7018c21STomi Valkeinen par->features = ATI_CHIP_264GTB; 517*f7018c21STomi Valkeinen break; 518*f7018c21STomi Valkeinen case 0x02: 519*f7018c21STomi Valkeinen name = "3D RAGE II+ (Mach64 GT)"; 520*f7018c21STomi Valkeinen par->pll_limits.pll_max = 200; 521*f7018c21STomi Valkeinen par->pll_limits.mclk = 67; 522*f7018c21STomi Valkeinen par->pll_limits.xclk = 67; 523*f7018c21STomi Valkeinen par->pll_limits.ecp_max = 100; 524*f7018c21STomi Valkeinen par->features = ATI_CHIP_264GTB; 525*f7018c21STomi Valkeinen break; 526*f7018c21STomi Valkeinen } 527*f7018c21STomi Valkeinen break; 528*f7018c21STomi Valkeinen #endif 529*f7018c21STomi Valkeinen } 530*f7018c21STomi Valkeinen 531*f7018c21STomi Valkeinen PRINTKI("%s [0x%04x rev 0x%02x]\n", name, type, rev); 532*f7018c21STomi Valkeinen return 0; 533*f7018c21STomi Valkeinen } 534*f7018c21STomi Valkeinen 535*f7018c21STomi Valkeinen static char ram_dram[] __maybe_unused = "DRAM"; 536*f7018c21STomi Valkeinen static char ram_resv[] __maybe_unused = "RESV"; 537*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX 538*f7018c21STomi Valkeinen static char ram_vram[] = "VRAM"; 539*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */ 540*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 541*f7018c21STomi Valkeinen static char ram_edo[] = "EDO"; 542*f7018c21STomi Valkeinen static char ram_sdram[] = "SDRAM (1:1)"; 543*f7018c21STomi Valkeinen static char ram_sgram[] = "SGRAM (1:1)"; 544*f7018c21STomi Valkeinen static char ram_sdram32[] = "SDRAM (2:1) (32-bit)"; 545*f7018c21STomi Valkeinen static char ram_wram[] = "WRAM"; 546*f7018c21STomi Valkeinen static char ram_off[] = "OFF"; 547*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */ 548*f7018c21STomi Valkeinen 549*f7018c21STomi Valkeinen 550*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX 551*f7018c21STomi Valkeinen static char *aty_gx_ram[8] = { 552*f7018c21STomi Valkeinen ram_dram, ram_vram, ram_vram, ram_dram, 553*f7018c21STomi Valkeinen ram_dram, ram_vram, ram_vram, ram_resv 554*f7018c21STomi Valkeinen }; 555*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */ 556*f7018c21STomi Valkeinen 557*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 558*f7018c21STomi Valkeinen static char *aty_ct_ram[8] = { 559*f7018c21STomi Valkeinen ram_off, ram_dram, ram_edo, ram_edo, 560*f7018c21STomi Valkeinen ram_sdram, ram_sgram, ram_wram, ram_resv 561*f7018c21STomi Valkeinen }; 562*f7018c21STomi Valkeinen static char *aty_xl_ram[8] = { 563*f7018c21STomi Valkeinen ram_off, ram_dram, ram_edo, ram_edo, 564*f7018c21STomi Valkeinen ram_sdram, ram_sgram, ram_sdram32, ram_resv 565*f7018c21STomi Valkeinen }; 566*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */ 567*f7018c21STomi Valkeinen 568*f7018c21STomi Valkeinen static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, 569*f7018c21STomi Valkeinen struct atyfb_par *par) 570*f7018c21STomi Valkeinen { 571*f7018c21STomi Valkeinen u32 pixclock = var->pixclock; 572*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 573*f7018c21STomi Valkeinen u32 lcd_on_off; 574*f7018c21STomi Valkeinen par->pll.ct.xres = 0; 575*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 576*f7018c21STomi Valkeinen lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par); 577*f7018c21STomi Valkeinen if (lcd_on_off & LCD_ON) { 578*f7018c21STomi Valkeinen par->pll.ct.xres = var->xres; 579*f7018c21STomi Valkeinen pixclock = par->lcd_pixclock; 580*f7018c21STomi Valkeinen } 581*f7018c21STomi Valkeinen } 582*f7018c21STomi Valkeinen #endif 583*f7018c21STomi Valkeinen return pixclock; 584*f7018c21STomi Valkeinen } 585*f7018c21STomi Valkeinen 586*f7018c21STomi Valkeinen #if defined(CONFIG_PPC) 587*f7018c21STomi Valkeinen 588*f7018c21STomi Valkeinen /* 589*f7018c21STomi Valkeinen * Apple monitor sense 590*f7018c21STomi Valkeinen */ 591*f7018c21STomi Valkeinen 592*f7018c21STomi Valkeinen static int read_aty_sense(const struct atyfb_par *par) 593*f7018c21STomi Valkeinen { 594*f7018c21STomi Valkeinen int sense, i; 595*f7018c21STomi Valkeinen 596*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */ 597*f7018c21STomi Valkeinen __delay(200); 598*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0, par); /* turn off outputs */ 599*f7018c21STomi Valkeinen __delay(2000); 600*f7018c21STomi Valkeinen i = aty_ld_le32(GP_IO, par); /* get primary sense value */ 601*f7018c21STomi Valkeinen sense = ((i & 0x3000) >> 3) | (i & 0x100); 602*f7018c21STomi Valkeinen 603*f7018c21STomi Valkeinen /* drive each sense line low in turn and collect the other 2 */ 604*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0x20000000, par); /* drive A low */ 605*f7018c21STomi Valkeinen __delay(2000); 606*f7018c21STomi Valkeinen i = aty_ld_le32(GP_IO, par); 607*f7018c21STomi Valkeinen sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4); 608*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0x20002000, par); /* drive A high again */ 609*f7018c21STomi Valkeinen __delay(200); 610*f7018c21STomi Valkeinen 611*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0x10000000, par); /* drive B low */ 612*f7018c21STomi Valkeinen __delay(2000); 613*f7018c21STomi Valkeinen i = aty_ld_le32(GP_IO, par); 614*f7018c21STomi Valkeinen sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6); 615*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0x10001000, par); /* drive B high again */ 616*f7018c21STomi Valkeinen __delay(200); 617*f7018c21STomi Valkeinen 618*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0x01000000, par); /* drive C low */ 619*f7018c21STomi Valkeinen __delay(2000); 620*f7018c21STomi Valkeinen sense |= (aty_ld_le32(GP_IO, par) & 0x3000) >> 12; 621*f7018c21STomi Valkeinen aty_st_le32(GP_IO, 0, par); /* turn off outputs */ 622*f7018c21STomi Valkeinen return sense; 623*f7018c21STomi Valkeinen } 624*f7018c21STomi Valkeinen 625*f7018c21STomi Valkeinen #endif /* defined(CONFIG_PPC) */ 626*f7018c21STomi Valkeinen 627*f7018c21STomi Valkeinen /* ------------------------------------------------------------------------- */ 628*f7018c21STomi Valkeinen 629*f7018c21STomi Valkeinen /* 630*f7018c21STomi Valkeinen * CRTC programming 631*f7018c21STomi Valkeinen */ 632*f7018c21STomi Valkeinen 633*f7018c21STomi Valkeinen static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc) 634*f7018c21STomi Valkeinen { 635*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 636*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 637*f7018c21STomi Valkeinen if (!M64_HAS(LT_LCD_REGS)) { 638*f7018c21STomi Valkeinen crtc->lcd_index = aty_ld_le32(LCD_INDEX, par); 639*f7018c21STomi Valkeinen aty_st_le32(LCD_INDEX, crtc->lcd_index, par); 640*f7018c21STomi Valkeinen } 641*f7018c21STomi Valkeinen crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par); 642*f7018c21STomi Valkeinen crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par); 643*f7018c21STomi Valkeinen 644*f7018c21STomi Valkeinen 645*f7018c21STomi Valkeinen /* switch to non shadow registers */ 646*f7018c21STomi Valkeinen aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl & 647*f7018c21STomi Valkeinen ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); 648*f7018c21STomi Valkeinen 649*f7018c21STomi Valkeinen /* save stretching */ 650*f7018c21STomi Valkeinen crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par); 651*f7018c21STomi Valkeinen crtc->vert_stretching = aty_ld_lcd(VERT_STRETCHING, par); 652*f7018c21STomi Valkeinen if (!M64_HAS(LT_LCD_REGS)) 653*f7018c21STomi Valkeinen crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par); 654*f7018c21STomi Valkeinen } 655*f7018c21STomi Valkeinen #endif 656*f7018c21STomi Valkeinen crtc->h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par); 657*f7018c21STomi Valkeinen crtc->h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par); 658*f7018c21STomi Valkeinen crtc->v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par); 659*f7018c21STomi Valkeinen crtc->v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par); 660*f7018c21STomi Valkeinen crtc->vline_crnt_vline = aty_ld_le32(CRTC_VLINE_CRNT_VLINE, par); 661*f7018c21STomi Valkeinen crtc->off_pitch = aty_ld_le32(CRTC_OFF_PITCH, par); 662*f7018c21STomi Valkeinen crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par); 663*f7018c21STomi Valkeinen 664*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 665*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 666*f7018c21STomi Valkeinen /* switch to shadow registers */ 667*f7018c21STomi Valkeinen aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) | 668*f7018c21STomi Valkeinen SHADOW_EN | SHADOW_RW_EN, par); 669*f7018c21STomi Valkeinen 670*f7018c21STomi Valkeinen crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par); 671*f7018c21STomi Valkeinen crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par); 672*f7018c21STomi Valkeinen crtc->shadow_v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par); 673*f7018c21STomi Valkeinen crtc->shadow_v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par); 674*f7018c21STomi Valkeinen 675*f7018c21STomi Valkeinen aty_st_le32(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par); 676*f7018c21STomi Valkeinen } 677*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */ 678*f7018c21STomi Valkeinen } 679*f7018c21STomi Valkeinen 680*f7018c21STomi Valkeinen static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc) 681*f7018c21STomi Valkeinen { 682*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 683*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 684*f7018c21STomi Valkeinen /* stop CRTC */ 685*f7018c21STomi Valkeinen aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & 686*f7018c21STomi Valkeinen ~(CRTC_EXT_DISP_EN | CRTC_EN), par); 687*f7018c21STomi Valkeinen 688*f7018c21STomi Valkeinen /* update non-shadow registers first */ 689*f7018c21STomi Valkeinen aty_st_lcd(CNFG_PANEL, crtc->lcd_config_panel, par); 690*f7018c21STomi Valkeinen aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl & 691*f7018c21STomi Valkeinen ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par); 692*f7018c21STomi Valkeinen 693*f7018c21STomi Valkeinen /* temporarily disable stretching */ 694*f7018c21STomi Valkeinen aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching & 695*f7018c21STomi Valkeinen ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par); 696*f7018c21STomi Valkeinen aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching & 697*f7018c21STomi Valkeinen ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 | 698*f7018c21STomi Valkeinen VERT_STRETCH_USE0 | VERT_STRETCH_EN), par); 699*f7018c21STomi Valkeinen } 700*f7018c21STomi Valkeinen #endif 701*f7018c21STomi Valkeinen /* turn off CRT */ 702*f7018c21STomi Valkeinen aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~CRTC_EN, par); 703*f7018c21STomi Valkeinen 704*f7018c21STomi Valkeinen DPRINTK("setting up CRTC\n"); 705*f7018c21STomi Valkeinen DPRINTK("set primary CRT to %ix%i %c%c composite %c\n", 706*f7018c21STomi Valkeinen ((((crtc->h_tot_disp >> 16) & 0xff) + 1) << 3), 707*f7018c21STomi Valkeinen (((crtc->v_tot_disp >> 16) & 0x7ff) + 1), 708*f7018c21STomi Valkeinen (crtc->h_sync_strt_wid & 0x200000) ? 'N' : 'P', 709*f7018c21STomi Valkeinen (crtc->v_sync_strt_wid & 0x200000) ? 'N' : 'P', 710*f7018c21STomi Valkeinen (crtc->gen_cntl & CRTC_CSYNC_EN) ? 'P' : 'N'); 711*f7018c21STomi Valkeinen 712*f7018c21STomi Valkeinen DPRINTK("CRTC_H_TOTAL_DISP: %x\n", crtc->h_tot_disp); 713*f7018c21STomi Valkeinen DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n", crtc->h_sync_strt_wid); 714*f7018c21STomi Valkeinen DPRINTK("CRTC_V_TOTAL_DISP: %x\n", crtc->v_tot_disp); 715*f7018c21STomi Valkeinen DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n", crtc->v_sync_strt_wid); 716*f7018c21STomi Valkeinen DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch); 717*f7018c21STomi Valkeinen DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline); 718*f7018c21STomi Valkeinen DPRINTK("CRTC_GEN_CNTL: %x\n", crtc->gen_cntl); 719*f7018c21STomi Valkeinen 720*f7018c21STomi Valkeinen aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par); 721*f7018c21STomi Valkeinen aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par); 722*f7018c21STomi Valkeinen aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_tot_disp, par); 723*f7018c21STomi Valkeinen aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid, par); 724*f7018c21STomi Valkeinen aty_st_le32(CRTC_OFF_PITCH, crtc->off_pitch, par); 725*f7018c21STomi Valkeinen aty_st_le32(CRTC_VLINE_CRNT_VLINE, crtc->vline_crnt_vline, par); 726*f7018c21STomi Valkeinen 727*f7018c21STomi Valkeinen aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl, par); 728*f7018c21STomi Valkeinen #if 0 729*f7018c21STomi Valkeinen FIXME 730*f7018c21STomi Valkeinen if (par->accel_flags & FB_ACCELF_TEXT) 731*f7018c21STomi Valkeinen aty_init_engine(par, info); 732*f7018c21STomi Valkeinen #endif 733*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 734*f7018c21STomi Valkeinen /* after setting the CRTC registers we should set the LCD registers. */ 735*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 736*f7018c21STomi Valkeinen /* switch to shadow registers */ 737*f7018c21STomi Valkeinen aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) | 738*f7018c21STomi Valkeinen SHADOW_EN | SHADOW_RW_EN, par); 739*f7018c21STomi Valkeinen 740*f7018c21STomi Valkeinen DPRINTK("set shadow CRT to %ix%i %c%c\n", 741*f7018c21STomi Valkeinen ((((crtc->shadow_h_tot_disp >> 16) & 0xff) + 1) << 3), 742*f7018c21STomi Valkeinen (((crtc->shadow_v_tot_disp >> 16) & 0x7ff) + 1), 743*f7018c21STomi Valkeinen (crtc->shadow_h_sync_strt_wid & 0x200000) ? 'N' : 'P', 744*f7018c21STomi Valkeinen (crtc->shadow_v_sync_strt_wid & 0x200000) ? 'N' : 'P'); 745*f7018c21STomi Valkeinen 746*f7018c21STomi Valkeinen DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n", 747*f7018c21STomi Valkeinen crtc->shadow_h_tot_disp); 748*f7018c21STomi Valkeinen DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n", 749*f7018c21STomi Valkeinen crtc->shadow_h_sync_strt_wid); 750*f7018c21STomi Valkeinen DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n", 751*f7018c21STomi Valkeinen crtc->shadow_v_tot_disp); 752*f7018c21STomi Valkeinen DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n", 753*f7018c21STomi Valkeinen crtc->shadow_v_sync_strt_wid); 754*f7018c21STomi Valkeinen 755*f7018c21STomi Valkeinen aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par); 756*f7018c21STomi Valkeinen aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par); 757*f7018c21STomi Valkeinen aty_st_le32(CRTC_V_TOTAL_DISP, crtc->shadow_v_tot_disp, par); 758*f7018c21STomi Valkeinen aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->shadow_v_sync_strt_wid, par); 759*f7018c21STomi Valkeinen 760*f7018c21STomi Valkeinen /* restore CRTC selection & shadow state and enable stretching */ 761*f7018c21STomi Valkeinen DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl); 762*f7018c21STomi Valkeinen DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching); 763*f7018c21STomi Valkeinen DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching); 764*f7018c21STomi Valkeinen if (!M64_HAS(LT_LCD_REGS)) 765*f7018c21STomi Valkeinen DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch); 766*f7018c21STomi Valkeinen 767*f7018c21STomi Valkeinen aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par); 768*f7018c21STomi Valkeinen aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par); 769*f7018c21STomi Valkeinen aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par); 770*f7018c21STomi Valkeinen if (!M64_HAS(LT_LCD_REGS)) { 771*f7018c21STomi Valkeinen aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par); 772*f7018c21STomi Valkeinen aty_ld_le32(LCD_INDEX, par); 773*f7018c21STomi Valkeinen aty_st_le32(LCD_INDEX, crtc->lcd_index, par); 774*f7018c21STomi Valkeinen } 775*f7018c21STomi Valkeinen } 776*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */ 777*f7018c21STomi Valkeinen } 778*f7018c21STomi Valkeinen 779*f7018c21STomi Valkeinen static u32 calc_line_length(struct atyfb_par *par, u32 vxres, u32 bpp) 780*f7018c21STomi Valkeinen { 781*f7018c21STomi Valkeinen u32 line_length = vxres * bpp / 8; 782*f7018c21STomi Valkeinen 783*f7018c21STomi Valkeinen if (par->ram_type == SGRAM || 784*f7018c21STomi Valkeinen (!M64_HAS(XL_MEM) && par->ram_type == WRAM)) 785*f7018c21STomi Valkeinen line_length = (line_length + 63) & ~63; 786*f7018c21STomi Valkeinen 787*f7018c21STomi Valkeinen return line_length; 788*f7018c21STomi Valkeinen } 789*f7018c21STomi Valkeinen 790*f7018c21STomi Valkeinen static int aty_var_to_crtc(const struct fb_info *info, 791*f7018c21STomi Valkeinen const struct fb_var_screeninfo *var, 792*f7018c21STomi Valkeinen struct crtc *crtc) 793*f7018c21STomi Valkeinen { 794*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 795*f7018c21STomi Valkeinen u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp; 796*f7018c21STomi Valkeinen u32 sync, vmode, vdisplay; 797*f7018c21STomi Valkeinen u32 h_total, h_disp, h_sync_strt, h_sync_end, h_sync_dly, h_sync_wid, h_sync_pol; 798*f7018c21STomi Valkeinen u32 v_total, v_disp, v_sync_strt, v_sync_end, v_sync_wid, v_sync_pol, c_sync; 799*f7018c21STomi Valkeinen u32 pix_width, dp_pix_width, dp_chain_mask; 800*f7018c21STomi Valkeinen u32 line_length; 801*f7018c21STomi Valkeinen 802*f7018c21STomi Valkeinen /* input */ 803*f7018c21STomi Valkeinen xres = (var->xres + 7) & ~7; 804*f7018c21STomi Valkeinen yres = var->yres; 805*f7018c21STomi Valkeinen vxres = (var->xres_virtual + 7) & ~7; 806*f7018c21STomi Valkeinen vyres = var->yres_virtual; 807*f7018c21STomi Valkeinen xoffset = (var->xoffset + 7) & ~7; 808*f7018c21STomi Valkeinen yoffset = var->yoffset; 809*f7018c21STomi Valkeinen bpp = var->bits_per_pixel; 810*f7018c21STomi Valkeinen if (bpp == 16) 811*f7018c21STomi Valkeinen bpp = (var->green.length == 5) ? 15 : 16; 812*f7018c21STomi Valkeinen sync = var->sync; 813*f7018c21STomi Valkeinen vmode = var->vmode; 814*f7018c21STomi Valkeinen 815*f7018c21STomi Valkeinen /* convert (and round up) and validate */ 816*f7018c21STomi Valkeinen if (vxres < xres + xoffset) 817*f7018c21STomi Valkeinen vxres = xres + xoffset; 818*f7018c21STomi Valkeinen h_disp = xres; 819*f7018c21STomi Valkeinen 820*f7018c21STomi Valkeinen if (vyres < yres + yoffset) 821*f7018c21STomi Valkeinen vyres = yres + yoffset; 822*f7018c21STomi Valkeinen v_disp = yres; 823*f7018c21STomi Valkeinen 824*f7018c21STomi Valkeinen if (bpp <= 8) { 825*f7018c21STomi Valkeinen bpp = 8; 826*f7018c21STomi Valkeinen pix_width = CRTC_PIX_WIDTH_8BPP; 827*f7018c21STomi Valkeinen dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP | 828*f7018c21STomi Valkeinen BYTE_ORDER_LSB_TO_MSB; 829*f7018c21STomi Valkeinen dp_chain_mask = DP_CHAIN_8BPP; 830*f7018c21STomi Valkeinen } else if (bpp <= 15) { 831*f7018c21STomi Valkeinen bpp = 16; 832*f7018c21STomi Valkeinen pix_width = CRTC_PIX_WIDTH_15BPP; 833*f7018c21STomi Valkeinen dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP | 834*f7018c21STomi Valkeinen BYTE_ORDER_LSB_TO_MSB; 835*f7018c21STomi Valkeinen dp_chain_mask = DP_CHAIN_15BPP; 836*f7018c21STomi Valkeinen } else if (bpp <= 16) { 837*f7018c21STomi Valkeinen bpp = 16; 838*f7018c21STomi Valkeinen pix_width = CRTC_PIX_WIDTH_16BPP; 839*f7018c21STomi Valkeinen dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP | 840*f7018c21STomi Valkeinen BYTE_ORDER_LSB_TO_MSB; 841*f7018c21STomi Valkeinen dp_chain_mask = DP_CHAIN_16BPP; 842*f7018c21STomi Valkeinen } else if (bpp <= 24 && M64_HAS(INTEGRATED)) { 843*f7018c21STomi Valkeinen bpp = 24; 844*f7018c21STomi Valkeinen pix_width = CRTC_PIX_WIDTH_24BPP; 845*f7018c21STomi Valkeinen dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP | 846*f7018c21STomi Valkeinen BYTE_ORDER_LSB_TO_MSB; 847*f7018c21STomi Valkeinen dp_chain_mask = DP_CHAIN_24BPP; 848*f7018c21STomi Valkeinen } else if (bpp <= 32) { 849*f7018c21STomi Valkeinen bpp = 32; 850*f7018c21STomi Valkeinen pix_width = CRTC_PIX_WIDTH_32BPP; 851*f7018c21STomi Valkeinen dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP | 852*f7018c21STomi Valkeinen BYTE_ORDER_LSB_TO_MSB; 853*f7018c21STomi Valkeinen dp_chain_mask = DP_CHAIN_32BPP; 854*f7018c21STomi Valkeinen } else 855*f7018c21STomi Valkeinen FAIL("invalid bpp"); 856*f7018c21STomi Valkeinen 857*f7018c21STomi Valkeinen line_length = calc_line_length(par, vxres, bpp); 858*f7018c21STomi Valkeinen 859*f7018c21STomi Valkeinen if (vyres * line_length > info->fix.smem_len) 860*f7018c21STomi Valkeinen FAIL("not enough video RAM"); 861*f7018c21STomi Valkeinen 862*f7018c21STomi Valkeinen h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1; 863*f7018c21STomi Valkeinen v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1; 864*f7018c21STomi Valkeinen 865*f7018c21STomi Valkeinen if ((xres > 1920) || (yres > 1200)) { 866*f7018c21STomi Valkeinen FAIL("MACH64 chips are designed for max 1920x1200\n" 867*f7018c21STomi Valkeinen "select another resolution."); 868*f7018c21STomi Valkeinen } 869*f7018c21STomi Valkeinen h_sync_strt = h_disp + var->right_margin; 870*f7018c21STomi Valkeinen h_sync_end = h_sync_strt + var->hsync_len; 871*f7018c21STomi Valkeinen h_sync_dly = var->right_margin & 7; 872*f7018c21STomi Valkeinen h_total = h_sync_end + h_sync_dly + var->left_margin; 873*f7018c21STomi Valkeinen 874*f7018c21STomi Valkeinen v_sync_strt = v_disp + var->lower_margin; 875*f7018c21STomi Valkeinen v_sync_end = v_sync_strt + var->vsync_len; 876*f7018c21STomi Valkeinen v_total = v_sync_end + var->upper_margin; 877*f7018c21STomi Valkeinen 878*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 879*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 880*f7018c21STomi Valkeinen if (!M64_HAS(LT_LCD_REGS)) { 881*f7018c21STomi Valkeinen u32 lcd_index = aty_ld_le32(LCD_INDEX, par); 882*f7018c21STomi Valkeinen crtc->lcd_index = lcd_index & 883*f7018c21STomi Valkeinen ~(LCD_INDEX_MASK | LCD_DISPLAY_DIS | 884*f7018c21STomi Valkeinen LCD_SRC_SEL | CRTC2_DISPLAY_DIS); 885*f7018c21STomi Valkeinen aty_st_le32(LCD_INDEX, lcd_index, par); 886*f7018c21STomi Valkeinen } 887*f7018c21STomi Valkeinen 888*f7018c21STomi Valkeinen if (!M64_HAS(MOBIL_BUS)) 889*f7018c21STomi Valkeinen crtc->lcd_index |= CRTC2_DISPLAY_DIS; 890*f7018c21STomi Valkeinen 891*f7018c21STomi Valkeinen crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par) | 0x4000; 892*f7018c21STomi Valkeinen crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par) & ~CRTC_RW_SELECT; 893*f7018c21STomi Valkeinen 894*f7018c21STomi Valkeinen crtc->lcd_gen_cntl &= 895*f7018c21STomi Valkeinen ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | TVCLK_PM_EN | 896*f7018c21STomi Valkeinen /*VCLK_DAC_PM_EN | USE_SHADOWED_VEND |*/ 897*f7018c21STomi Valkeinen USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN); 898*f7018c21STomi Valkeinen crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT; 899*f7018c21STomi Valkeinen 900*f7018c21STomi Valkeinen if ((crtc->lcd_gen_cntl & LCD_ON) && 901*f7018c21STomi Valkeinen ((xres > par->lcd_width) || (yres > par->lcd_height))) { 902*f7018c21STomi Valkeinen /* 903*f7018c21STomi Valkeinen * We cannot display the mode on the LCD. If the CRT is 904*f7018c21STomi Valkeinen * enabled we can turn off the LCD. 905*f7018c21STomi Valkeinen * If the CRT is off, it isn't a good idea to switch it 906*f7018c21STomi Valkeinen * on; we don't know if one is connected. So it's better 907*f7018c21STomi Valkeinen * to fail then. 908*f7018c21STomi Valkeinen */ 909*f7018c21STomi Valkeinen if (crtc->lcd_gen_cntl & CRT_ON) { 910*f7018c21STomi Valkeinen if (!(var->activate & FB_ACTIVATE_TEST)) 911*f7018c21STomi Valkeinen PRINTKI("Disable LCD panel, because video mode does not fit.\n"); 912*f7018c21STomi Valkeinen crtc->lcd_gen_cntl &= ~LCD_ON; 913*f7018c21STomi Valkeinen /*aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);*/ 914*f7018c21STomi Valkeinen } else { 915*f7018c21STomi Valkeinen if (!(var->activate & FB_ACTIVATE_TEST)) 916*f7018c21STomi Valkeinen PRINTKE("Video mode exceeds size of LCD panel.\nConnect this computer to a conventional monitor if you really need this mode.\n"); 917*f7018c21STomi Valkeinen return -EINVAL; 918*f7018c21STomi Valkeinen } 919*f7018c21STomi Valkeinen } 920*f7018c21STomi Valkeinen } 921*f7018c21STomi Valkeinen 922*f7018c21STomi Valkeinen if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) { 923*f7018c21STomi Valkeinen int VScan = 1; 924*f7018c21STomi Valkeinen /* bpp -> bytespp, 1,4 -> 0; 8 -> 2; 15,16 -> 1; 24 -> 6; 32 -> 5 925*f7018c21STomi Valkeinen const u8 DFP_h_sync_dly_LT[] = { 0, 2, 1, 6, 5 }; 926*f7018c21STomi Valkeinen const u8 ADD_to_strt_wid_and_dly_LT_DAC[] = { 0, 5, 6, 9, 9, 12, 12 }; */ 927*f7018c21STomi Valkeinen 928*f7018c21STomi Valkeinen vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED); 929*f7018c21STomi Valkeinen 930*f7018c21STomi Valkeinen /* 931*f7018c21STomi Valkeinen * This is horror! When we simulate, say 640x480 on an 800x600 932*f7018c21STomi Valkeinen * LCD monitor, the CRTC should be programmed 800x600 values for 933*f7018c21STomi Valkeinen * the non visible part, but 640x480 for the visible part. 934*f7018c21STomi Valkeinen * This code has been tested on a laptop with it's 1400x1050 LCD 935*f7018c21STomi Valkeinen * monitor and a conventional monitor both switched on. 936*f7018c21STomi Valkeinen * Tested modes: 1280x1024, 1152x864, 1024x768, 800x600, 937*f7018c21STomi Valkeinen * works with little glitches also with DOUBLESCAN modes 938*f7018c21STomi Valkeinen */ 939*f7018c21STomi Valkeinen if (yres < par->lcd_height) { 940*f7018c21STomi Valkeinen VScan = par->lcd_height / yres; 941*f7018c21STomi Valkeinen if (VScan > 1) { 942*f7018c21STomi Valkeinen VScan = 2; 943*f7018c21STomi Valkeinen vmode |= FB_VMODE_DOUBLE; 944*f7018c21STomi Valkeinen } 945*f7018c21STomi Valkeinen } 946*f7018c21STomi Valkeinen 947*f7018c21STomi Valkeinen h_sync_strt = h_disp + par->lcd_right_margin; 948*f7018c21STomi Valkeinen h_sync_end = h_sync_strt + par->lcd_hsync_len; 949*f7018c21STomi Valkeinen h_sync_dly = /*DFP_h_sync_dly[ ( bpp + 1 ) / 3 ]; */par->lcd_hsync_dly; 950*f7018c21STomi Valkeinen h_total = h_disp + par->lcd_hblank_len; 951*f7018c21STomi Valkeinen 952*f7018c21STomi Valkeinen v_sync_strt = v_disp + par->lcd_lower_margin / VScan; 953*f7018c21STomi Valkeinen v_sync_end = v_sync_strt + par->lcd_vsync_len / VScan; 954*f7018c21STomi Valkeinen v_total = v_disp + par->lcd_vblank_len / VScan; 955*f7018c21STomi Valkeinen } 956*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */ 957*f7018c21STomi Valkeinen 958*f7018c21STomi Valkeinen h_disp = (h_disp >> 3) - 1; 959*f7018c21STomi Valkeinen h_sync_strt = (h_sync_strt >> 3) - 1; 960*f7018c21STomi Valkeinen h_sync_end = (h_sync_end >> 3) - 1; 961*f7018c21STomi Valkeinen h_total = (h_total >> 3) - 1; 962*f7018c21STomi Valkeinen h_sync_wid = h_sync_end - h_sync_strt; 963*f7018c21STomi Valkeinen 964*f7018c21STomi Valkeinen FAIL_MAX("h_disp too large", h_disp, 0xff); 965*f7018c21STomi Valkeinen FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff); 966*f7018c21STomi Valkeinen /*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/ 967*f7018c21STomi Valkeinen if (h_sync_wid > 0x1f) 968*f7018c21STomi Valkeinen h_sync_wid = 0x1f; 969*f7018c21STomi Valkeinen FAIL_MAX("h_total too large", h_total, 0x1ff); 970*f7018c21STomi Valkeinen 971*f7018c21STomi Valkeinen if (vmode & FB_VMODE_DOUBLE) { 972*f7018c21STomi Valkeinen v_disp <<= 1; 973*f7018c21STomi Valkeinen v_sync_strt <<= 1; 974*f7018c21STomi Valkeinen v_sync_end <<= 1; 975*f7018c21STomi Valkeinen v_total <<= 1; 976*f7018c21STomi Valkeinen } 977*f7018c21STomi Valkeinen 978*f7018c21STomi Valkeinen vdisplay = yres; 979*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 980*f7018c21STomi Valkeinen if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) 981*f7018c21STomi Valkeinen vdisplay = par->lcd_height; 982*f7018c21STomi Valkeinen #endif 983*f7018c21STomi Valkeinen 984*f7018c21STomi Valkeinen v_disp--; 985*f7018c21STomi Valkeinen v_sync_strt--; 986*f7018c21STomi Valkeinen v_sync_end--; 987*f7018c21STomi Valkeinen v_total--; 988*f7018c21STomi Valkeinen v_sync_wid = v_sync_end - v_sync_strt; 989*f7018c21STomi Valkeinen 990*f7018c21STomi Valkeinen FAIL_MAX("v_disp too large", v_disp, 0x7ff); 991*f7018c21STomi Valkeinen FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff); 992*f7018c21STomi Valkeinen /*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/ 993*f7018c21STomi Valkeinen if (v_sync_wid > 0x1f) 994*f7018c21STomi Valkeinen v_sync_wid = 0x1f; 995*f7018c21STomi Valkeinen FAIL_MAX("v_total too large", v_total, 0x7ff); 996*f7018c21STomi Valkeinen 997*f7018c21STomi Valkeinen c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0; 998*f7018c21STomi Valkeinen 999*f7018c21STomi Valkeinen /* output */ 1000*f7018c21STomi Valkeinen crtc->vxres = vxres; 1001*f7018c21STomi Valkeinen crtc->vyres = vyres; 1002*f7018c21STomi Valkeinen crtc->xoffset = xoffset; 1003*f7018c21STomi Valkeinen crtc->yoffset = yoffset; 1004*f7018c21STomi Valkeinen crtc->bpp = bpp; 1005*f7018c21STomi Valkeinen crtc->off_pitch = 1006*f7018c21STomi Valkeinen ((yoffset * line_length + xoffset * bpp / 8) / 8) | 1007*f7018c21STomi Valkeinen ((line_length / bpp) << 22); 1008*f7018c21STomi Valkeinen crtc->vline_crnt_vline = 0; 1009*f7018c21STomi Valkeinen 1010*f7018c21STomi Valkeinen crtc->h_tot_disp = h_total | (h_disp << 16); 1011*f7018c21STomi Valkeinen crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly << 8) | 1012*f7018c21STomi Valkeinen ((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16) | 1013*f7018c21STomi Valkeinen (h_sync_pol << 21); 1014*f7018c21STomi Valkeinen crtc->v_tot_disp = v_total | (v_disp << 16); 1015*f7018c21STomi Valkeinen crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) | 1016*f7018c21STomi Valkeinen (v_sync_pol << 21); 1017*f7018c21STomi Valkeinen 1018*f7018c21STomi Valkeinen /* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */ 1019*f7018c21STomi Valkeinen crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync; 1020*f7018c21STomi Valkeinen crtc->gen_cntl |= CRTC_VGA_LINEAR; 1021*f7018c21STomi Valkeinen 1022*f7018c21STomi Valkeinen /* Enable doublescan mode if requested */ 1023*f7018c21STomi Valkeinen if (vmode & FB_VMODE_DOUBLE) 1024*f7018c21STomi Valkeinen crtc->gen_cntl |= CRTC_DBL_SCAN_EN; 1025*f7018c21STomi Valkeinen /* Enable interlaced mode if requested */ 1026*f7018c21STomi Valkeinen if (vmode & FB_VMODE_INTERLACED) 1027*f7018c21STomi Valkeinen crtc->gen_cntl |= CRTC_INTERLACE_EN; 1028*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 1029*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 1030*f7018c21STomi Valkeinen vdisplay = yres; 1031*f7018c21STomi Valkeinen if (vmode & FB_VMODE_DOUBLE) 1032*f7018c21STomi Valkeinen vdisplay <<= 1; 1033*f7018c21STomi Valkeinen crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH); 1034*f7018c21STomi Valkeinen crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | 1035*f7018c21STomi Valkeinen /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/ 1036*f7018c21STomi Valkeinen USE_SHADOWED_VEND | 1037*f7018c21STomi Valkeinen USE_SHADOWED_ROWCUR | 1038*f7018c21STomi Valkeinen SHADOW_EN | SHADOW_RW_EN); 1039*f7018c21STomi Valkeinen crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR/* | LOCK_8DOT*/; 1040*f7018c21STomi Valkeinen 1041*f7018c21STomi Valkeinen /* MOBILITY M1 tested, FIXME: LT */ 1042*f7018c21STomi Valkeinen crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par); 1043*f7018c21STomi Valkeinen if (!M64_HAS(LT_LCD_REGS)) 1044*f7018c21STomi Valkeinen crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) & 1045*f7018c21STomi Valkeinen ~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3); 1046*f7018c21STomi Valkeinen 1047*f7018c21STomi Valkeinen crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO | 1048*f7018c21STomi Valkeinen HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO | 1049*f7018c21STomi Valkeinen HORZ_STRETCH_MODE | HORZ_STRETCH_EN); 1050*f7018c21STomi Valkeinen if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) { 1051*f7018c21STomi Valkeinen do { 1052*f7018c21STomi Valkeinen /* 1053*f7018c21STomi Valkeinen * The horizontal blender misbehaves when 1054*f7018c21STomi Valkeinen * HDisplay is less than a certain threshold 1055*f7018c21STomi Valkeinen * (440 for a 1024-wide panel). It doesn't 1056*f7018c21STomi Valkeinen * stretch such modes enough. Use pixel 1057*f7018c21STomi Valkeinen * replication instead of blending to stretch 1058*f7018c21STomi Valkeinen * modes that can be made to exactly fit the 1059*f7018c21STomi Valkeinen * panel width. The undocumented "NoLCDBlend" 1060*f7018c21STomi Valkeinen * option allows the pixel-replicated mode to 1061*f7018c21STomi Valkeinen * be slightly wider or narrower than the 1062*f7018c21STomi Valkeinen * panel width. It also causes a mode that is 1063*f7018c21STomi Valkeinen * exactly half as wide as the panel to be 1064*f7018c21STomi Valkeinen * pixel-replicated, rather than blended. 1065*f7018c21STomi Valkeinen */ 1066*f7018c21STomi Valkeinen int HDisplay = xres & ~7; 1067*f7018c21STomi Valkeinen int nStretch = par->lcd_width / HDisplay; 1068*f7018c21STomi Valkeinen int Remainder = par->lcd_width % HDisplay; 1069*f7018c21STomi Valkeinen 1070*f7018c21STomi Valkeinen if ((!Remainder && ((nStretch > 2))) || 1071*f7018c21STomi Valkeinen (((HDisplay * 16) / par->lcd_width) < 7)) { 1072*f7018c21STomi Valkeinen static const char StretchLoops[] = { 10, 12, 13, 15, 16 }; 1073*f7018c21STomi Valkeinen int horz_stretch_loop = -1, BestRemainder; 1074*f7018c21STomi Valkeinen int Numerator = HDisplay, Denominator = par->lcd_width; 1075*f7018c21STomi Valkeinen int Index = 5; 1076*f7018c21STomi Valkeinen ATIReduceRatio(&Numerator, &Denominator); 1077*f7018c21STomi Valkeinen 1078*f7018c21STomi Valkeinen BestRemainder = (Numerator * 16) / Denominator; 1079*f7018c21STomi Valkeinen while (--Index >= 0) { 1080*f7018c21STomi Valkeinen Remainder = ((Denominator - Numerator) * StretchLoops[Index]) % 1081*f7018c21STomi Valkeinen Denominator; 1082*f7018c21STomi Valkeinen if (Remainder < BestRemainder) { 1083*f7018c21STomi Valkeinen horz_stretch_loop = Index; 1084*f7018c21STomi Valkeinen if (!(BestRemainder = Remainder)) 1085*f7018c21STomi Valkeinen break; 1086*f7018c21STomi Valkeinen } 1087*f7018c21STomi Valkeinen } 1088*f7018c21STomi Valkeinen 1089*f7018c21STomi Valkeinen if ((horz_stretch_loop >= 0) && !BestRemainder) { 1090*f7018c21STomi Valkeinen int horz_stretch_ratio = 0, Accumulator = 0; 1091*f7018c21STomi Valkeinen int reuse_previous = 1; 1092*f7018c21STomi Valkeinen 1093*f7018c21STomi Valkeinen Index = StretchLoops[horz_stretch_loop]; 1094*f7018c21STomi Valkeinen 1095*f7018c21STomi Valkeinen while (--Index >= 0) { 1096*f7018c21STomi Valkeinen if (Accumulator > 0) 1097*f7018c21STomi Valkeinen horz_stretch_ratio |= reuse_previous; 1098*f7018c21STomi Valkeinen else 1099*f7018c21STomi Valkeinen Accumulator += Denominator; 1100*f7018c21STomi Valkeinen Accumulator -= Numerator; 1101*f7018c21STomi Valkeinen reuse_previous <<= 1; 1102*f7018c21STomi Valkeinen } 1103*f7018c21STomi Valkeinen 1104*f7018c21STomi Valkeinen crtc->horz_stretching |= (HORZ_STRETCH_EN | 1105*f7018c21STomi Valkeinen ((horz_stretch_loop & HORZ_STRETCH_LOOP) << 16) | 1106*f7018c21STomi Valkeinen (horz_stretch_ratio & HORZ_STRETCH_RATIO)); 1107*f7018c21STomi Valkeinen break; /* Out of the do { ... } while (0) */ 1108*f7018c21STomi Valkeinen } 1109*f7018c21STomi Valkeinen } 1110*f7018c21STomi Valkeinen 1111*f7018c21STomi Valkeinen crtc->horz_stretching |= (HORZ_STRETCH_MODE | HORZ_STRETCH_EN | 1112*f7018c21STomi Valkeinen (((HDisplay * (HORZ_STRETCH_BLEND + 1)) / par->lcd_width) & HORZ_STRETCH_BLEND)); 1113*f7018c21STomi Valkeinen } while (0); 1114*f7018c21STomi Valkeinen } 1115*f7018c21STomi Valkeinen 1116*f7018c21STomi Valkeinen if (vdisplay < par->lcd_height && crtc->lcd_gen_cntl & LCD_ON) { 1117*f7018c21STomi Valkeinen crtc->vert_stretching = (VERT_STRETCH_USE0 | VERT_STRETCH_EN | 1118*f7018c21STomi Valkeinen (((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0)); 1119*f7018c21STomi Valkeinen 1120*f7018c21STomi Valkeinen if (!M64_HAS(LT_LCD_REGS) && 1121*f7018c21STomi Valkeinen xres <= (M64_HAS(MOBIL_BUS) ? 1024 : 800)) 1122*f7018c21STomi Valkeinen crtc->ext_vert_stretch |= VERT_STRETCH_MODE; 1123*f7018c21STomi Valkeinen } else { 1124*f7018c21STomi Valkeinen /* 1125*f7018c21STomi Valkeinen * Don't use vertical blending if the mode is too wide 1126*f7018c21STomi Valkeinen * or not vertically stretched. 1127*f7018c21STomi Valkeinen */ 1128*f7018c21STomi Valkeinen crtc->vert_stretching = 0; 1129*f7018c21STomi Valkeinen } 1130*f7018c21STomi Valkeinen /* copy to shadow crtc */ 1131*f7018c21STomi Valkeinen crtc->shadow_h_tot_disp = crtc->h_tot_disp; 1132*f7018c21STomi Valkeinen crtc->shadow_h_sync_strt_wid = crtc->h_sync_strt_wid; 1133*f7018c21STomi Valkeinen crtc->shadow_v_tot_disp = crtc->v_tot_disp; 1134*f7018c21STomi Valkeinen crtc->shadow_v_sync_strt_wid = crtc->v_sync_strt_wid; 1135*f7018c21STomi Valkeinen } 1136*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */ 1137*f7018c21STomi Valkeinen 1138*f7018c21STomi Valkeinen if (M64_HAS(MAGIC_FIFO)) { 1139*f7018c21STomi Valkeinen /* FIXME: display FIFO low watermark values */ 1140*f7018c21STomi Valkeinen crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_FIFO_LWM); 1141*f7018c21STomi Valkeinen } 1142*f7018c21STomi Valkeinen crtc->dp_pix_width = dp_pix_width; 1143*f7018c21STomi Valkeinen crtc->dp_chain_mask = dp_chain_mask; 1144*f7018c21STomi Valkeinen 1145*f7018c21STomi Valkeinen return 0; 1146*f7018c21STomi Valkeinen } 1147*f7018c21STomi Valkeinen 1148*f7018c21STomi Valkeinen static int aty_crtc_to_var(const struct crtc *crtc, 1149*f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 1150*f7018c21STomi Valkeinen { 1151*f7018c21STomi Valkeinen u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync; 1152*f7018c21STomi Valkeinen u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol; 1153*f7018c21STomi Valkeinen u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync; 1154*f7018c21STomi Valkeinen u32 pix_width; 1155*f7018c21STomi Valkeinen u32 double_scan, interlace; 1156*f7018c21STomi Valkeinen 1157*f7018c21STomi Valkeinen /* input */ 1158*f7018c21STomi Valkeinen h_total = crtc->h_tot_disp & 0x1ff; 1159*f7018c21STomi Valkeinen h_disp = (crtc->h_tot_disp >> 16) & 0xff; 1160*f7018c21STomi Valkeinen h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | ((crtc->h_sync_strt_wid >> 4) & 0x100); 1161*f7018c21STomi Valkeinen h_sync_dly = (crtc->h_sync_strt_wid >> 8) & 0x7; 1162*f7018c21STomi Valkeinen h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x1f; 1163*f7018c21STomi Valkeinen h_sync_pol = (crtc->h_sync_strt_wid >> 21) & 0x1; 1164*f7018c21STomi Valkeinen v_total = crtc->v_tot_disp & 0x7ff; 1165*f7018c21STomi Valkeinen v_disp = (crtc->v_tot_disp >> 16) & 0x7ff; 1166*f7018c21STomi Valkeinen v_sync_strt = crtc->v_sync_strt_wid & 0x7ff; 1167*f7018c21STomi Valkeinen v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f; 1168*f7018c21STomi Valkeinen v_sync_pol = (crtc->v_sync_strt_wid >> 21) & 0x1; 1169*f7018c21STomi Valkeinen c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0; 1170*f7018c21STomi Valkeinen pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK; 1171*f7018c21STomi Valkeinen double_scan = crtc->gen_cntl & CRTC_DBL_SCAN_EN; 1172*f7018c21STomi Valkeinen interlace = crtc->gen_cntl & CRTC_INTERLACE_EN; 1173*f7018c21STomi Valkeinen 1174*f7018c21STomi Valkeinen /* convert */ 1175*f7018c21STomi Valkeinen xres = (h_disp + 1) * 8; 1176*f7018c21STomi Valkeinen yres = v_disp + 1; 1177*f7018c21STomi Valkeinen left = (h_total - h_sync_strt - h_sync_wid) * 8 - h_sync_dly; 1178*f7018c21STomi Valkeinen right = (h_sync_strt - h_disp) * 8 + h_sync_dly; 1179*f7018c21STomi Valkeinen hslen = h_sync_wid * 8; 1180*f7018c21STomi Valkeinen upper = v_total - v_sync_strt - v_sync_wid; 1181*f7018c21STomi Valkeinen lower = v_sync_strt - v_disp; 1182*f7018c21STomi Valkeinen vslen = v_sync_wid; 1183*f7018c21STomi Valkeinen sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) | 1184*f7018c21STomi Valkeinen (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) | 1185*f7018c21STomi Valkeinen (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0); 1186*f7018c21STomi Valkeinen 1187*f7018c21STomi Valkeinen switch (pix_width) { 1188*f7018c21STomi Valkeinen #if 0 1189*f7018c21STomi Valkeinen case CRTC_PIX_WIDTH_4BPP: 1190*f7018c21STomi Valkeinen bpp = 4; 1191*f7018c21STomi Valkeinen var->red.offset = 0; 1192*f7018c21STomi Valkeinen var->red.length = 8; 1193*f7018c21STomi Valkeinen var->green.offset = 0; 1194*f7018c21STomi Valkeinen var->green.length = 8; 1195*f7018c21STomi Valkeinen var->blue.offset = 0; 1196*f7018c21STomi Valkeinen var->blue.length = 8; 1197*f7018c21STomi Valkeinen var->transp.offset = 0; 1198*f7018c21STomi Valkeinen var->transp.length = 0; 1199*f7018c21STomi Valkeinen break; 1200*f7018c21STomi Valkeinen #endif 1201*f7018c21STomi Valkeinen case CRTC_PIX_WIDTH_8BPP: 1202*f7018c21STomi Valkeinen bpp = 8; 1203*f7018c21STomi Valkeinen var->red.offset = 0; 1204*f7018c21STomi Valkeinen var->red.length = 8; 1205*f7018c21STomi Valkeinen var->green.offset = 0; 1206*f7018c21STomi Valkeinen var->green.length = 8; 1207*f7018c21STomi Valkeinen var->blue.offset = 0; 1208*f7018c21STomi Valkeinen var->blue.length = 8; 1209*f7018c21STomi Valkeinen var->transp.offset = 0; 1210*f7018c21STomi Valkeinen var->transp.length = 0; 1211*f7018c21STomi Valkeinen break; 1212*f7018c21STomi Valkeinen case CRTC_PIX_WIDTH_15BPP: /* RGB 555 */ 1213*f7018c21STomi Valkeinen bpp = 16; 1214*f7018c21STomi Valkeinen var->red.offset = 10; 1215*f7018c21STomi Valkeinen var->red.length = 5; 1216*f7018c21STomi Valkeinen var->green.offset = 5; 1217*f7018c21STomi Valkeinen var->green.length = 5; 1218*f7018c21STomi Valkeinen var->blue.offset = 0; 1219*f7018c21STomi Valkeinen var->blue.length = 5; 1220*f7018c21STomi Valkeinen var->transp.offset = 0; 1221*f7018c21STomi Valkeinen var->transp.length = 0; 1222*f7018c21STomi Valkeinen break; 1223*f7018c21STomi Valkeinen case CRTC_PIX_WIDTH_16BPP: /* RGB 565 */ 1224*f7018c21STomi Valkeinen bpp = 16; 1225*f7018c21STomi Valkeinen var->red.offset = 11; 1226*f7018c21STomi Valkeinen var->red.length = 5; 1227*f7018c21STomi Valkeinen var->green.offset = 5; 1228*f7018c21STomi Valkeinen var->green.length = 6; 1229*f7018c21STomi Valkeinen var->blue.offset = 0; 1230*f7018c21STomi Valkeinen var->blue.length = 5; 1231*f7018c21STomi Valkeinen var->transp.offset = 0; 1232*f7018c21STomi Valkeinen var->transp.length = 0; 1233*f7018c21STomi Valkeinen break; 1234*f7018c21STomi Valkeinen case CRTC_PIX_WIDTH_24BPP: /* RGB 888 */ 1235*f7018c21STomi Valkeinen bpp = 24; 1236*f7018c21STomi Valkeinen var->red.offset = 16; 1237*f7018c21STomi Valkeinen var->red.length = 8; 1238*f7018c21STomi Valkeinen var->green.offset = 8; 1239*f7018c21STomi Valkeinen var->green.length = 8; 1240*f7018c21STomi Valkeinen var->blue.offset = 0; 1241*f7018c21STomi Valkeinen var->blue.length = 8; 1242*f7018c21STomi Valkeinen var->transp.offset = 0; 1243*f7018c21STomi Valkeinen var->transp.length = 0; 1244*f7018c21STomi Valkeinen break; 1245*f7018c21STomi Valkeinen case CRTC_PIX_WIDTH_32BPP: /* ARGB 8888 */ 1246*f7018c21STomi Valkeinen bpp = 32; 1247*f7018c21STomi Valkeinen var->red.offset = 16; 1248*f7018c21STomi Valkeinen var->red.length = 8; 1249*f7018c21STomi Valkeinen var->green.offset = 8; 1250*f7018c21STomi Valkeinen var->green.length = 8; 1251*f7018c21STomi Valkeinen var->blue.offset = 0; 1252*f7018c21STomi Valkeinen var->blue.length = 8; 1253*f7018c21STomi Valkeinen var->transp.offset = 24; 1254*f7018c21STomi Valkeinen var->transp.length = 8; 1255*f7018c21STomi Valkeinen break; 1256*f7018c21STomi Valkeinen default: 1257*f7018c21STomi Valkeinen PRINTKE("Invalid pixel width\n"); 1258*f7018c21STomi Valkeinen return -EINVAL; 1259*f7018c21STomi Valkeinen } 1260*f7018c21STomi Valkeinen 1261*f7018c21STomi Valkeinen /* output */ 1262*f7018c21STomi Valkeinen var->xres = xres; 1263*f7018c21STomi Valkeinen var->yres = yres; 1264*f7018c21STomi Valkeinen var->xres_virtual = crtc->vxres; 1265*f7018c21STomi Valkeinen var->yres_virtual = crtc->vyres; 1266*f7018c21STomi Valkeinen var->bits_per_pixel = bpp; 1267*f7018c21STomi Valkeinen var->left_margin = left; 1268*f7018c21STomi Valkeinen var->right_margin = right; 1269*f7018c21STomi Valkeinen var->upper_margin = upper; 1270*f7018c21STomi Valkeinen var->lower_margin = lower; 1271*f7018c21STomi Valkeinen var->hsync_len = hslen; 1272*f7018c21STomi Valkeinen var->vsync_len = vslen; 1273*f7018c21STomi Valkeinen var->sync = sync; 1274*f7018c21STomi Valkeinen var->vmode = FB_VMODE_NONINTERLACED; 1275*f7018c21STomi Valkeinen /* 1276*f7018c21STomi Valkeinen * In double scan mode, the vertical parameters are doubled, 1277*f7018c21STomi Valkeinen * so we need to halve them to get the right values. 1278*f7018c21STomi Valkeinen * In interlaced mode the values are already correct, 1279*f7018c21STomi Valkeinen * so no correction is necessary. 1280*f7018c21STomi Valkeinen */ 1281*f7018c21STomi Valkeinen if (interlace) 1282*f7018c21STomi Valkeinen var->vmode = FB_VMODE_INTERLACED; 1283*f7018c21STomi Valkeinen 1284*f7018c21STomi Valkeinen if (double_scan) { 1285*f7018c21STomi Valkeinen var->vmode = FB_VMODE_DOUBLE; 1286*f7018c21STomi Valkeinen var->yres >>= 1; 1287*f7018c21STomi Valkeinen var->upper_margin >>= 1; 1288*f7018c21STomi Valkeinen var->lower_margin >>= 1; 1289*f7018c21STomi Valkeinen var->vsync_len >>= 1; 1290*f7018c21STomi Valkeinen } 1291*f7018c21STomi Valkeinen 1292*f7018c21STomi Valkeinen return 0; 1293*f7018c21STomi Valkeinen } 1294*f7018c21STomi Valkeinen 1295*f7018c21STomi Valkeinen /* ------------------------------------------------------------------------- */ 1296*f7018c21STomi Valkeinen 1297*f7018c21STomi Valkeinen static int atyfb_set_par(struct fb_info *info) 1298*f7018c21STomi Valkeinen { 1299*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1300*f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &info->var; 1301*f7018c21STomi Valkeinen u32 tmp, pixclock; 1302*f7018c21STomi Valkeinen int err; 1303*f7018c21STomi Valkeinen #ifdef DEBUG 1304*f7018c21STomi Valkeinen struct fb_var_screeninfo debug; 1305*f7018c21STomi Valkeinen u32 pixclock_in_ps; 1306*f7018c21STomi Valkeinen #endif 1307*f7018c21STomi Valkeinen if (par->asleep) 1308*f7018c21STomi Valkeinen return 0; 1309*f7018c21STomi Valkeinen 1310*f7018c21STomi Valkeinen err = aty_var_to_crtc(info, var, &par->crtc); 1311*f7018c21STomi Valkeinen if (err) 1312*f7018c21STomi Valkeinen return err; 1313*f7018c21STomi Valkeinen 1314*f7018c21STomi Valkeinen pixclock = atyfb_get_pixclock(var, par); 1315*f7018c21STomi Valkeinen 1316*f7018c21STomi Valkeinen if (pixclock == 0) { 1317*f7018c21STomi Valkeinen PRINTKE("Invalid pixclock\n"); 1318*f7018c21STomi Valkeinen return -EINVAL; 1319*f7018c21STomi Valkeinen } else { 1320*f7018c21STomi Valkeinen err = par->pll_ops->var_to_pll(info, pixclock, 1321*f7018c21STomi Valkeinen var->bits_per_pixel, &par->pll); 1322*f7018c21STomi Valkeinen if (err) 1323*f7018c21STomi Valkeinen return err; 1324*f7018c21STomi Valkeinen } 1325*f7018c21STomi Valkeinen 1326*f7018c21STomi Valkeinen par->accel_flags = var->accel_flags; /* hack */ 1327*f7018c21STomi Valkeinen 1328*f7018c21STomi Valkeinen if (var->accel_flags) { 1329*f7018c21STomi Valkeinen info->fbops->fb_sync = atyfb_sync; 1330*f7018c21STomi Valkeinen info->flags &= ~FBINFO_HWACCEL_DISABLED; 1331*f7018c21STomi Valkeinen } else { 1332*f7018c21STomi Valkeinen info->fbops->fb_sync = NULL; 1333*f7018c21STomi Valkeinen info->flags |= FBINFO_HWACCEL_DISABLED; 1334*f7018c21STomi Valkeinen } 1335*f7018c21STomi Valkeinen 1336*f7018c21STomi Valkeinen if (par->blitter_may_be_busy) 1337*f7018c21STomi Valkeinen wait_for_idle(par); 1338*f7018c21STomi Valkeinen 1339*f7018c21STomi Valkeinen aty_set_crtc(par, &par->crtc); 1340*f7018c21STomi Valkeinen par->dac_ops->set_dac(info, &par->pll, 1341*f7018c21STomi Valkeinen var->bits_per_pixel, par->accel_flags); 1342*f7018c21STomi Valkeinen par->pll_ops->set_pll(info, &par->pll); 1343*f7018c21STomi Valkeinen 1344*f7018c21STomi Valkeinen #ifdef DEBUG 1345*f7018c21STomi Valkeinen if (par->pll_ops && par->pll_ops->pll_to_var) 1346*f7018c21STomi Valkeinen pixclock_in_ps = par->pll_ops->pll_to_var(info, &par->pll); 1347*f7018c21STomi Valkeinen else 1348*f7018c21STomi Valkeinen pixclock_in_ps = 0; 1349*f7018c21STomi Valkeinen 1350*f7018c21STomi Valkeinen if (0 == pixclock_in_ps) { 1351*f7018c21STomi Valkeinen PRINTKE("ALERT ops->pll_to_var get 0\n"); 1352*f7018c21STomi Valkeinen pixclock_in_ps = pixclock; 1353*f7018c21STomi Valkeinen } 1354*f7018c21STomi Valkeinen 1355*f7018c21STomi Valkeinen memset(&debug, 0, sizeof(debug)); 1356*f7018c21STomi Valkeinen if (!aty_crtc_to_var(&par->crtc, &debug)) { 1357*f7018c21STomi Valkeinen u32 hSync, vRefresh; 1358*f7018c21STomi Valkeinen u32 h_disp, h_sync_strt, h_sync_end, h_total; 1359*f7018c21STomi Valkeinen u32 v_disp, v_sync_strt, v_sync_end, v_total; 1360*f7018c21STomi Valkeinen 1361*f7018c21STomi Valkeinen h_disp = debug.xres; 1362*f7018c21STomi Valkeinen h_sync_strt = h_disp + debug.right_margin; 1363*f7018c21STomi Valkeinen h_sync_end = h_sync_strt + debug.hsync_len; 1364*f7018c21STomi Valkeinen h_total = h_sync_end + debug.left_margin; 1365*f7018c21STomi Valkeinen v_disp = debug.yres; 1366*f7018c21STomi Valkeinen v_sync_strt = v_disp + debug.lower_margin; 1367*f7018c21STomi Valkeinen v_sync_end = v_sync_strt + debug.vsync_len; 1368*f7018c21STomi Valkeinen v_total = v_sync_end + debug.upper_margin; 1369*f7018c21STomi Valkeinen 1370*f7018c21STomi Valkeinen hSync = 1000000000 / (pixclock_in_ps * h_total); 1371*f7018c21STomi Valkeinen vRefresh = (hSync * 1000) / v_total; 1372*f7018c21STomi Valkeinen if (par->crtc.gen_cntl & CRTC_INTERLACE_EN) 1373*f7018c21STomi Valkeinen vRefresh *= 2; 1374*f7018c21STomi Valkeinen if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) 1375*f7018c21STomi Valkeinen vRefresh /= 2; 1376*f7018c21STomi Valkeinen 1377*f7018c21STomi Valkeinen DPRINTK("atyfb_set_par\n"); 1378*f7018c21STomi Valkeinen DPRINTK(" Set Visible Mode to %ix%i-%i\n", 1379*f7018c21STomi Valkeinen var->xres, var->yres, var->bits_per_pixel); 1380*f7018c21STomi Valkeinen DPRINTK(" Virtual resolution %ix%i, " 1381*f7018c21STomi Valkeinen "pixclock_in_ps %i (calculated %i)\n", 1382*f7018c21STomi Valkeinen var->xres_virtual, var->yres_virtual, 1383*f7018c21STomi Valkeinen pixclock, pixclock_in_ps); 1384*f7018c21STomi Valkeinen DPRINTK(" Dot clock: %i MHz\n", 1385*f7018c21STomi Valkeinen 1000000 / pixclock_in_ps); 1386*f7018c21STomi Valkeinen DPRINTK(" Horizontal sync: %i kHz\n", hSync); 1387*f7018c21STomi Valkeinen DPRINTK(" Vertical refresh: %i Hz\n", vRefresh); 1388*f7018c21STomi Valkeinen DPRINTK(" x style: %i.%03i %i %i %i %i %i %i %i %i\n", 1389*f7018c21STomi Valkeinen 1000000 / pixclock_in_ps, 1000000 % pixclock_in_ps, 1390*f7018c21STomi Valkeinen h_disp, h_sync_strt, h_sync_end, h_total, 1391*f7018c21STomi Valkeinen v_disp, v_sync_strt, v_sync_end, v_total); 1392*f7018c21STomi Valkeinen DPRINTK(" fb style: %i %i %i %i %i %i %i %i %i\n", 1393*f7018c21STomi Valkeinen pixclock_in_ps, 1394*f7018c21STomi Valkeinen debug.left_margin, h_disp, debug.right_margin, debug.hsync_len, 1395*f7018c21STomi Valkeinen debug.upper_margin, v_disp, debug.lower_margin, debug.vsync_len); 1396*f7018c21STomi Valkeinen } 1397*f7018c21STomi Valkeinen #endif /* DEBUG */ 1398*f7018c21STomi Valkeinen 1399*f7018c21STomi Valkeinen if (!M64_HAS(INTEGRATED)) { 1400*f7018c21STomi Valkeinen /* Don't forget MEM_CNTL */ 1401*f7018c21STomi Valkeinen tmp = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff; 1402*f7018c21STomi Valkeinen switch (var->bits_per_pixel) { 1403*f7018c21STomi Valkeinen case 8: 1404*f7018c21STomi Valkeinen tmp |= 0x02000000; 1405*f7018c21STomi Valkeinen break; 1406*f7018c21STomi Valkeinen case 16: 1407*f7018c21STomi Valkeinen tmp |= 0x03000000; 1408*f7018c21STomi Valkeinen break; 1409*f7018c21STomi Valkeinen case 32: 1410*f7018c21STomi Valkeinen tmp |= 0x06000000; 1411*f7018c21STomi Valkeinen break; 1412*f7018c21STomi Valkeinen } 1413*f7018c21STomi Valkeinen aty_st_le32(MEM_CNTL, tmp, par); 1414*f7018c21STomi Valkeinen } else { 1415*f7018c21STomi Valkeinen tmp = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff; 1416*f7018c21STomi Valkeinen if (!M64_HAS(MAGIC_POSTDIV)) 1417*f7018c21STomi Valkeinen tmp |= par->mem_refresh_rate << 20; 1418*f7018c21STomi Valkeinen switch (var->bits_per_pixel) { 1419*f7018c21STomi Valkeinen case 8: 1420*f7018c21STomi Valkeinen case 24: 1421*f7018c21STomi Valkeinen tmp |= 0x00000000; 1422*f7018c21STomi Valkeinen break; 1423*f7018c21STomi Valkeinen case 16: 1424*f7018c21STomi Valkeinen tmp |= 0x04000000; 1425*f7018c21STomi Valkeinen break; 1426*f7018c21STomi Valkeinen case 32: 1427*f7018c21STomi Valkeinen tmp |= 0x08000000; 1428*f7018c21STomi Valkeinen break; 1429*f7018c21STomi Valkeinen } 1430*f7018c21STomi Valkeinen if (M64_HAS(CT_BUS)) { 1431*f7018c21STomi Valkeinen aty_st_le32(DAC_CNTL, 0x87010184, par); 1432*f7018c21STomi Valkeinen aty_st_le32(BUS_CNTL, 0x680000f9, par); 1433*f7018c21STomi Valkeinen } else if (M64_HAS(VT_BUS)) { 1434*f7018c21STomi Valkeinen aty_st_le32(DAC_CNTL, 0x87010184, par); 1435*f7018c21STomi Valkeinen aty_st_le32(BUS_CNTL, 0x680000f9, par); 1436*f7018c21STomi Valkeinen } else if (M64_HAS(MOBIL_BUS)) { 1437*f7018c21STomi Valkeinen aty_st_le32(DAC_CNTL, 0x80010102, par); 1438*f7018c21STomi Valkeinen aty_st_le32(BUS_CNTL, 0x7b33a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par); 1439*f7018c21STomi Valkeinen } else { 1440*f7018c21STomi Valkeinen /* GT */ 1441*f7018c21STomi Valkeinen aty_st_le32(DAC_CNTL, 0x86010102, par); 1442*f7018c21STomi Valkeinen aty_st_le32(BUS_CNTL, 0x7b23a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par); 1443*f7018c21STomi Valkeinen aty_st_le32(EXT_MEM_CNTL, aty_ld_le32(EXT_MEM_CNTL, par) | 0x5000001, par); 1444*f7018c21STomi Valkeinen } 1445*f7018c21STomi Valkeinen aty_st_le32(MEM_CNTL, tmp, par); 1446*f7018c21STomi Valkeinen } 1447*f7018c21STomi Valkeinen aty_st_8(DAC_MASK, 0xff, par); 1448*f7018c21STomi Valkeinen 1449*f7018c21STomi Valkeinen info->fix.line_length = calc_line_length(par, var->xres_virtual, 1450*f7018c21STomi Valkeinen var->bits_per_pixel); 1451*f7018c21STomi Valkeinen 1452*f7018c21STomi Valkeinen info->fix.visual = var->bits_per_pixel <= 8 ? 1453*f7018c21STomi Valkeinen FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; 1454*f7018c21STomi Valkeinen 1455*f7018c21STomi Valkeinen /* Initialize the graphics engine */ 1456*f7018c21STomi Valkeinen if (par->accel_flags & FB_ACCELF_TEXT) 1457*f7018c21STomi Valkeinen aty_init_engine(par, info); 1458*f7018c21STomi Valkeinen 1459*f7018c21STomi Valkeinen #ifdef CONFIG_BOOTX_TEXT 1460*f7018c21STomi Valkeinen btext_update_display(info->fix.smem_start, 1461*f7018c21STomi Valkeinen (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8, 1462*f7018c21STomi Valkeinen ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1, 1463*f7018c21STomi Valkeinen var->bits_per_pixel, 1464*f7018c21STomi Valkeinen par->crtc.vxres * var->bits_per_pixel / 8); 1465*f7018c21STomi Valkeinen #endif /* CONFIG_BOOTX_TEXT */ 1466*f7018c21STomi Valkeinen #if 0 1467*f7018c21STomi Valkeinen /* switch to accelerator mode */ 1468*f7018c21STomi Valkeinen if (!(par->crtc.gen_cntl & CRTC_EXT_DISP_EN)) 1469*f7018c21STomi Valkeinen aty_st_le32(CRTC_GEN_CNTL, par->crtc.gen_cntl | CRTC_EXT_DISP_EN, par); 1470*f7018c21STomi Valkeinen #endif 1471*f7018c21STomi Valkeinen #ifdef DEBUG 1472*f7018c21STomi Valkeinen { 1473*f7018c21STomi Valkeinen /* dump non shadow CRTC, pll, LCD registers */ 1474*f7018c21STomi Valkeinen int i; u32 base; 1475*f7018c21STomi Valkeinen 1476*f7018c21STomi Valkeinen /* CRTC registers */ 1477*f7018c21STomi Valkeinen base = 0x2000; 1478*f7018c21STomi Valkeinen printk("debug atyfb: Mach64 non-shadow register values:"); 1479*f7018c21STomi Valkeinen for (i = 0; i < 256; i = i+4) { 1480*f7018c21STomi Valkeinen if (i % 16 == 0) 1481*f7018c21STomi Valkeinen printk("\ndebug atyfb: 0x%04X: ", base + i); 1482*f7018c21STomi Valkeinen printk(" %08X", aty_ld_le32(i, par)); 1483*f7018c21STomi Valkeinen } 1484*f7018c21STomi Valkeinen printk("\n\n"); 1485*f7018c21STomi Valkeinen 1486*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 1487*f7018c21STomi Valkeinen /* PLL registers */ 1488*f7018c21STomi Valkeinen base = 0x00; 1489*f7018c21STomi Valkeinen printk("debug atyfb: Mach64 PLL register values:"); 1490*f7018c21STomi Valkeinen for (i = 0; i < 64; i++) { 1491*f7018c21STomi Valkeinen if (i % 16 == 0) 1492*f7018c21STomi Valkeinen printk("\ndebug atyfb: 0x%02X: ", base + i); 1493*f7018c21STomi Valkeinen if (i % 4 == 0) 1494*f7018c21STomi Valkeinen printk(" "); 1495*f7018c21STomi Valkeinen printk("%02X", aty_ld_pll_ct(i, par)); 1496*f7018c21STomi Valkeinen } 1497*f7018c21STomi Valkeinen printk("\n\n"); 1498*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */ 1499*f7018c21STomi Valkeinen 1500*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 1501*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 1502*f7018c21STomi Valkeinen /* LCD registers */ 1503*f7018c21STomi Valkeinen base = 0x00; 1504*f7018c21STomi Valkeinen printk("debug atyfb: LCD register values:"); 1505*f7018c21STomi Valkeinen if (M64_HAS(LT_LCD_REGS)) { 1506*f7018c21STomi Valkeinen for (i = 0; i <= POWER_MANAGEMENT; i++) { 1507*f7018c21STomi Valkeinen if (i == EXT_VERT_STRETCH) 1508*f7018c21STomi Valkeinen continue; 1509*f7018c21STomi Valkeinen printk("\ndebug atyfb: 0x%04X: ", 1510*f7018c21STomi Valkeinen lt_lcd_regs[i]); 1511*f7018c21STomi Valkeinen printk(" %08X", aty_ld_lcd(i, par)); 1512*f7018c21STomi Valkeinen } 1513*f7018c21STomi Valkeinen } else { 1514*f7018c21STomi Valkeinen for (i = 0; i < 64; i++) { 1515*f7018c21STomi Valkeinen if (i % 4 == 0) 1516*f7018c21STomi Valkeinen printk("\ndebug atyfb: 0x%02X: ", 1517*f7018c21STomi Valkeinen base + i); 1518*f7018c21STomi Valkeinen printk(" %08X", aty_ld_lcd(i, par)); 1519*f7018c21STomi Valkeinen } 1520*f7018c21STomi Valkeinen } 1521*f7018c21STomi Valkeinen printk("\n\n"); 1522*f7018c21STomi Valkeinen } 1523*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */ 1524*f7018c21STomi Valkeinen } 1525*f7018c21STomi Valkeinen #endif /* DEBUG */ 1526*f7018c21STomi Valkeinen return 0; 1527*f7018c21STomi Valkeinen } 1528*f7018c21STomi Valkeinen 1529*f7018c21STomi Valkeinen static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 1530*f7018c21STomi Valkeinen { 1531*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1532*f7018c21STomi Valkeinen int err; 1533*f7018c21STomi Valkeinen struct crtc crtc; 1534*f7018c21STomi Valkeinen union aty_pll pll; 1535*f7018c21STomi Valkeinen u32 pixclock; 1536*f7018c21STomi Valkeinen 1537*f7018c21STomi Valkeinen memcpy(&pll, &par->pll, sizeof(pll)); 1538*f7018c21STomi Valkeinen 1539*f7018c21STomi Valkeinen err = aty_var_to_crtc(info, var, &crtc); 1540*f7018c21STomi Valkeinen if (err) 1541*f7018c21STomi Valkeinen return err; 1542*f7018c21STomi Valkeinen 1543*f7018c21STomi Valkeinen pixclock = atyfb_get_pixclock(var, par); 1544*f7018c21STomi Valkeinen 1545*f7018c21STomi Valkeinen if (pixclock == 0) { 1546*f7018c21STomi Valkeinen if (!(var->activate & FB_ACTIVATE_TEST)) 1547*f7018c21STomi Valkeinen PRINTKE("Invalid pixclock\n"); 1548*f7018c21STomi Valkeinen return -EINVAL; 1549*f7018c21STomi Valkeinen } else { 1550*f7018c21STomi Valkeinen err = par->pll_ops->var_to_pll(info, pixclock, 1551*f7018c21STomi Valkeinen var->bits_per_pixel, &pll); 1552*f7018c21STomi Valkeinen if (err) 1553*f7018c21STomi Valkeinen return err; 1554*f7018c21STomi Valkeinen } 1555*f7018c21STomi Valkeinen 1556*f7018c21STomi Valkeinen if (var->accel_flags & FB_ACCELF_TEXT) 1557*f7018c21STomi Valkeinen info->var.accel_flags = FB_ACCELF_TEXT; 1558*f7018c21STomi Valkeinen else 1559*f7018c21STomi Valkeinen info->var.accel_flags = 0; 1560*f7018c21STomi Valkeinen 1561*f7018c21STomi Valkeinen aty_crtc_to_var(&crtc, var); 1562*f7018c21STomi Valkeinen var->pixclock = par->pll_ops->pll_to_var(info, &pll); 1563*f7018c21STomi Valkeinen return 0; 1564*f7018c21STomi Valkeinen } 1565*f7018c21STomi Valkeinen 1566*f7018c21STomi Valkeinen static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info) 1567*f7018c21STomi Valkeinen { 1568*f7018c21STomi Valkeinen u32 xoffset = info->var.xoffset; 1569*f7018c21STomi Valkeinen u32 yoffset = info->var.yoffset; 1570*f7018c21STomi Valkeinen u32 line_length = info->fix.line_length; 1571*f7018c21STomi Valkeinen u32 bpp = info->var.bits_per_pixel; 1572*f7018c21STomi Valkeinen 1573*f7018c21STomi Valkeinen par->crtc.off_pitch = 1574*f7018c21STomi Valkeinen ((yoffset * line_length + xoffset * bpp / 8) / 8) | 1575*f7018c21STomi Valkeinen ((line_length / bpp) << 22); 1576*f7018c21STomi Valkeinen } 1577*f7018c21STomi Valkeinen 1578*f7018c21STomi Valkeinen 1579*f7018c21STomi Valkeinen /* 1580*f7018c21STomi Valkeinen * Open/Release the frame buffer device 1581*f7018c21STomi Valkeinen */ 1582*f7018c21STomi Valkeinen 1583*f7018c21STomi Valkeinen static int atyfb_open(struct fb_info *info, int user) 1584*f7018c21STomi Valkeinen { 1585*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1586*f7018c21STomi Valkeinen 1587*f7018c21STomi Valkeinen if (user) { 1588*f7018c21STomi Valkeinen par->open++; 1589*f7018c21STomi Valkeinen #ifdef __sparc__ 1590*f7018c21STomi Valkeinen par->mmaped = 0; 1591*f7018c21STomi Valkeinen #endif 1592*f7018c21STomi Valkeinen } 1593*f7018c21STomi Valkeinen return 0; 1594*f7018c21STomi Valkeinen } 1595*f7018c21STomi Valkeinen 1596*f7018c21STomi Valkeinen static irqreturn_t aty_irq(int irq, void *dev_id) 1597*f7018c21STomi Valkeinen { 1598*f7018c21STomi Valkeinen struct atyfb_par *par = dev_id; 1599*f7018c21STomi Valkeinen int handled = 0; 1600*f7018c21STomi Valkeinen u32 int_cntl; 1601*f7018c21STomi Valkeinen 1602*f7018c21STomi Valkeinen spin_lock(&par->int_lock); 1603*f7018c21STomi Valkeinen 1604*f7018c21STomi Valkeinen int_cntl = aty_ld_le32(CRTC_INT_CNTL, par); 1605*f7018c21STomi Valkeinen 1606*f7018c21STomi Valkeinen if (int_cntl & CRTC_VBLANK_INT) { 1607*f7018c21STomi Valkeinen /* clear interrupt */ 1608*f7018c21STomi Valkeinen aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) | 1609*f7018c21STomi Valkeinen CRTC_VBLANK_INT_AK, par); 1610*f7018c21STomi Valkeinen par->vblank.count++; 1611*f7018c21STomi Valkeinen if (par->vblank.pan_display) { 1612*f7018c21STomi Valkeinen par->vblank.pan_display = 0; 1613*f7018c21STomi Valkeinen aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par); 1614*f7018c21STomi Valkeinen } 1615*f7018c21STomi Valkeinen wake_up_interruptible(&par->vblank.wait); 1616*f7018c21STomi Valkeinen handled = 1; 1617*f7018c21STomi Valkeinen } 1618*f7018c21STomi Valkeinen 1619*f7018c21STomi Valkeinen spin_unlock(&par->int_lock); 1620*f7018c21STomi Valkeinen 1621*f7018c21STomi Valkeinen return IRQ_RETVAL(handled); 1622*f7018c21STomi Valkeinen } 1623*f7018c21STomi Valkeinen 1624*f7018c21STomi Valkeinen static int aty_enable_irq(struct atyfb_par *par, int reenable) 1625*f7018c21STomi Valkeinen { 1626*f7018c21STomi Valkeinen u32 int_cntl; 1627*f7018c21STomi Valkeinen 1628*f7018c21STomi Valkeinen if (!test_and_set_bit(0, &par->irq_flags)) { 1629*f7018c21STomi Valkeinen if (request_irq(par->irq, aty_irq, IRQF_SHARED, "atyfb", par)) { 1630*f7018c21STomi Valkeinen clear_bit(0, &par->irq_flags); 1631*f7018c21STomi Valkeinen return -EINVAL; 1632*f7018c21STomi Valkeinen } 1633*f7018c21STomi Valkeinen spin_lock_irq(&par->int_lock); 1634*f7018c21STomi Valkeinen int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; 1635*f7018c21STomi Valkeinen /* clear interrupt */ 1636*f7018c21STomi Valkeinen aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_AK, par); 1637*f7018c21STomi Valkeinen /* enable interrupt */ 1638*f7018c21STomi Valkeinen aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par); 1639*f7018c21STomi Valkeinen spin_unlock_irq(&par->int_lock); 1640*f7018c21STomi Valkeinen } else if (reenable) { 1641*f7018c21STomi Valkeinen spin_lock_irq(&par->int_lock); 1642*f7018c21STomi Valkeinen int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; 1643*f7018c21STomi Valkeinen if (!(int_cntl & CRTC_VBLANK_INT_EN)) { 1644*f7018c21STomi Valkeinen printk("atyfb: someone disabled IRQ [%08x]\n", 1645*f7018c21STomi Valkeinen int_cntl); 1646*f7018c21STomi Valkeinen /* re-enable interrupt */ 1647*f7018c21STomi Valkeinen aty_st_le32(CRTC_INT_CNTL, int_cntl | 1648*f7018c21STomi Valkeinen CRTC_VBLANK_INT_EN, par); 1649*f7018c21STomi Valkeinen } 1650*f7018c21STomi Valkeinen spin_unlock_irq(&par->int_lock); 1651*f7018c21STomi Valkeinen } 1652*f7018c21STomi Valkeinen 1653*f7018c21STomi Valkeinen return 0; 1654*f7018c21STomi Valkeinen } 1655*f7018c21STomi Valkeinen 1656*f7018c21STomi Valkeinen static int aty_disable_irq(struct atyfb_par *par) 1657*f7018c21STomi Valkeinen { 1658*f7018c21STomi Valkeinen u32 int_cntl; 1659*f7018c21STomi Valkeinen 1660*f7018c21STomi Valkeinen if (test_and_clear_bit(0, &par->irq_flags)) { 1661*f7018c21STomi Valkeinen if (par->vblank.pan_display) { 1662*f7018c21STomi Valkeinen par->vblank.pan_display = 0; 1663*f7018c21STomi Valkeinen aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par); 1664*f7018c21STomi Valkeinen } 1665*f7018c21STomi Valkeinen spin_lock_irq(&par->int_lock); 1666*f7018c21STomi Valkeinen int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; 1667*f7018c21STomi Valkeinen /* disable interrupt */ 1668*f7018c21STomi Valkeinen aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par); 1669*f7018c21STomi Valkeinen spin_unlock_irq(&par->int_lock); 1670*f7018c21STomi Valkeinen free_irq(par->irq, par); 1671*f7018c21STomi Valkeinen } 1672*f7018c21STomi Valkeinen 1673*f7018c21STomi Valkeinen return 0; 1674*f7018c21STomi Valkeinen } 1675*f7018c21STomi Valkeinen 1676*f7018c21STomi Valkeinen static int atyfb_release(struct fb_info *info, int user) 1677*f7018c21STomi Valkeinen { 1678*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1679*f7018c21STomi Valkeinen #ifdef __sparc__ 1680*f7018c21STomi Valkeinen int was_mmaped; 1681*f7018c21STomi Valkeinen #endif 1682*f7018c21STomi Valkeinen 1683*f7018c21STomi Valkeinen if (!user) 1684*f7018c21STomi Valkeinen return 0; 1685*f7018c21STomi Valkeinen 1686*f7018c21STomi Valkeinen par->open--; 1687*f7018c21STomi Valkeinen mdelay(1); 1688*f7018c21STomi Valkeinen wait_for_idle(par); 1689*f7018c21STomi Valkeinen 1690*f7018c21STomi Valkeinen if (par->open) 1691*f7018c21STomi Valkeinen return 0; 1692*f7018c21STomi Valkeinen 1693*f7018c21STomi Valkeinen #ifdef __sparc__ 1694*f7018c21STomi Valkeinen was_mmaped = par->mmaped; 1695*f7018c21STomi Valkeinen 1696*f7018c21STomi Valkeinen par->mmaped = 0; 1697*f7018c21STomi Valkeinen 1698*f7018c21STomi Valkeinen if (was_mmaped) { 1699*f7018c21STomi Valkeinen struct fb_var_screeninfo var; 1700*f7018c21STomi Valkeinen 1701*f7018c21STomi Valkeinen /* 1702*f7018c21STomi Valkeinen * Now reset the default display config, we have 1703*f7018c21STomi Valkeinen * no idea what the program(s) which mmap'd the 1704*f7018c21STomi Valkeinen * chip did to the configuration, nor whether it 1705*f7018c21STomi Valkeinen * restored it correctly. 1706*f7018c21STomi Valkeinen */ 1707*f7018c21STomi Valkeinen var = default_var; 1708*f7018c21STomi Valkeinen if (noaccel) 1709*f7018c21STomi Valkeinen var.accel_flags &= ~FB_ACCELF_TEXT; 1710*f7018c21STomi Valkeinen else 1711*f7018c21STomi Valkeinen var.accel_flags |= FB_ACCELF_TEXT; 1712*f7018c21STomi Valkeinen if (var.yres == var.yres_virtual) { 1713*f7018c21STomi Valkeinen u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); 1714*f7018c21STomi Valkeinen var.yres_virtual = 1715*f7018c21STomi Valkeinen ((videoram * 8) / var.bits_per_pixel) / 1716*f7018c21STomi Valkeinen var.xres_virtual; 1717*f7018c21STomi Valkeinen if (var.yres_virtual < var.yres) 1718*f7018c21STomi Valkeinen var.yres_virtual = var.yres; 1719*f7018c21STomi Valkeinen } 1720*f7018c21STomi Valkeinen } 1721*f7018c21STomi Valkeinen #endif 1722*f7018c21STomi Valkeinen aty_disable_irq(par); 1723*f7018c21STomi Valkeinen 1724*f7018c21STomi Valkeinen return 0; 1725*f7018c21STomi Valkeinen } 1726*f7018c21STomi Valkeinen 1727*f7018c21STomi Valkeinen /* 1728*f7018c21STomi Valkeinen * Pan or Wrap the Display 1729*f7018c21STomi Valkeinen * 1730*f7018c21STomi Valkeinen * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag 1731*f7018c21STomi Valkeinen */ 1732*f7018c21STomi Valkeinen 1733*f7018c21STomi Valkeinen static int atyfb_pan_display(struct fb_var_screeninfo *var, 1734*f7018c21STomi Valkeinen struct fb_info *info) 1735*f7018c21STomi Valkeinen { 1736*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1737*f7018c21STomi Valkeinen u32 xres, yres, xoffset, yoffset; 1738*f7018c21STomi Valkeinen 1739*f7018c21STomi Valkeinen xres = (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8; 1740*f7018c21STomi Valkeinen yres = ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1; 1741*f7018c21STomi Valkeinen if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) 1742*f7018c21STomi Valkeinen yres >>= 1; 1743*f7018c21STomi Valkeinen xoffset = (var->xoffset + 7) & ~7; 1744*f7018c21STomi Valkeinen yoffset = var->yoffset; 1745*f7018c21STomi Valkeinen if (xoffset + xres > par->crtc.vxres || 1746*f7018c21STomi Valkeinen yoffset + yres > par->crtc.vyres) 1747*f7018c21STomi Valkeinen return -EINVAL; 1748*f7018c21STomi Valkeinen info->var.xoffset = xoffset; 1749*f7018c21STomi Valkeinen info->var.yoffset = yoffset; 1750*f7018c21STomi Valkeinen if (par->asleep) 1751*f7018c21STomi Valkeinen return 0; 1752*f7018c21STomi Valkeinen 1753*f7018c21STomi Valkeinen set_off_pitch(par, info); 1754*f7018c21STomi Valkeinen if ((var->activate & FB_ACTIVATE_VBL) && !aty_enable_irq(par, 0)) { 1755*f7018c21STomi Valkeinen par->vblank.pan_display = 1; 1756*f7018c21STomi Valkeinen } else { 1757*f7018c21STomi Valkeinen par->vblank.pan_display = 0; 1758*f7018c21STomi Valkeinen aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par); 1759*f7018c21STomi Valkeinen } 1760*f7018c21STomi Valkeinen 1761*f7018c21STomi Valkeinen return 0; 1762*f7018c21STomi Valkeinen } 1763*f7018c21STomi Valkeinen 1764*f7018c21STomi Valkeinen static int aty_waitforvblank(struct atyfb_par *par, u32 crtc) 1765*f7018c21STomi Valkeinen { 1766*f7018c21STomi Valkeinen struct aty_interrupt *vbl; 1767*f7018c21STomi Valkeinen unsigned int count; 1768*f7018c21STomi Valkeinen int ret; 1769*f7018c21STomi Valkeinen 1770*f7018c21STomi Valkeinen switch (crtc) { 1771*f7018c21STomi Valkeinen case 0: 1772*f7018c21STomi Valkeinen vbl = &par->vblank; 1773*f7018c21STomi Valkeinen break; 1774*f7018c21STomi Valkeinen default: 1775*f7018c21STomi Valkeinen return -ENODEV; 1776*f7018c21STomi Valkeinen } 1777*f7018c21STomi Valkeinen 1778*f7018c21STomi Valkeinen ret = aty_enable_irq(par, 0); 1779*f7018c21STomi Valkeinen if (ret) 1780*f7018c21STomi Valkeinen return ret; 1781*f7018c21STomi Valkeinen 1782*f7018c21STomi Valkeinen count = vbl->count; 1783*f7018c21STomi Valkeinen ret = wait_event_interruptible_timeout(vbl->wait, 1784*f7018c21STomi Valkeinen count != vbl->count, HZ/10); 1785*f7018c21STomi Valkeinen if (ret < 0) 1786*f7018c21STomi Valkeinen return ret; 1787*f7018c21STomi Valkeinen if (ret == 0) { 1788*f7018c21STomi Valkeinen aty_enable_irq(par, 1); 1789*f7018c21STomi Valkeinen return -ETIMEDOUT; 1790*f7018c21STomi Valkeinen } 1791*f7018c21STomi Valkeinen 1792*f7018c21STomi Valkeinen return 0; 1793*f7018c21STomi Valkeinen } 1794*f7018c21STomi Valkeinen 1795*f7018c21STomi Valkeinen 1796*f7018c21STomi Valkeinen #ifdef DEBUG 1797*f7018c21STomi Valkeinen #define ATYIO_CLKR 0x41545900 /* ATY\00 */ 1798*f7018c21STomi Valkeinen #define ATYIO_CLKW 0x41545901 /* ATY\01 */ 1799*f7018c21STomi Valkeinen 1800*f7018c21STomi Valkeinen struct atyclk { 1801*f7018c21STomi Valkeinen u32 ref_clk_per; 1802*f7018c21STomi Valkeinen u8 pll_ref_div; 1803*f7018c21STomi Valkeinen u8 mclk_fb_div; 1804*f7018c21STomi Valkeinen u8 mclk_post_div; /* 1,2,3,4,8 */ 1805*f7018c21STomi Valkeinen u8 mclk_fb_mult; /* 2 or 4 */ 1806*f7018c21STomi Valkeinen u8 xclk_post_div; /* 1,2,3,4,8 */ 1807*f7018c21STomi Valkeinen u8 vclk_fb_div; 1808*f7018c21STomi Valkeinen u8 vclk_post_div; /* 1,2,3,4,6,8,12 */ 1809*f7018c21STomi Valkeinen u32 dsp_xclks_per_row; /* 0-16383 */ 1810*f7018c21STomi Valkeinen u32 dsp_loop_latency; /* 0-15 */ 1811*f7018c21STomi Valkeinen u32 dsp_precision; /* 0-7 */ 1812*f7018c21STomi Valkeinen u32 dsp_on; /* 0-2047 */ 1813*f7018c21STomi Valkeinen u32 dsp_off; /* 0-2047 */ 1814*f7018c21STomi Valkeinen }; 1815*f7018c21STomi Valkeinen 1816*f7018c21STomi Valkeinen #define ATYIO_FEATR 0x41545902 /* ATY\02 */ 1817*f7018c21STomi Valkeinen #define ATYIO_FEATW 0x41545903 /* ATY\03 */ 1818*f7018c21STomi Valkeinen #endif 1819*f7018c21STomi Valkeinen 1820*f7018c21STomi Valkeinen static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg) 1821*f7018c21STomi Valkeinen { 1822*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1823*f7018c21STomi Valkeinen #ifdef __sparc__ 1824*f7018c21STomi Valkeinen struct fbtype fbtyp; 1825*f7018c21STomi Valkeinen #endif 1826*f7018c21STomi Valkeinen 1827*f7018c21STomi Valkeinen switch (cmd) { 1828*f7018c21STomi Valkeinen #ifdef __sparc__ 1829*f7018c21STomi Valkeinen case FBIOGTYPE: 1830*f7018c21STomi Valkeinen fbtyp.fb_type = FBTYPE_PCI_GENERIC; 1831*f7018c21STomi Valkeinen fbtyp.fb_width = par->crtc.vxres; 1832*f7018c21STomi Valkeinen fbtyp.fb_height = par->crtc.vyres; 1833*f7018c21STomi Valkeinen fbtyp.fb_depth = info->var.bits_per_pixel; 1834*f7018c21STomi Valkeinen fbtyp.fb_cmsize = info->cmap.len; 1835*f7018c21STomi Valkeinen fbtyp.fb_size = info->fix.smem_len; 1836*f7018c21STomi Valkeinen if (copy_to_user((struct fbtype __user *) arg, &fbtyp, 1837*f7018c21STomi Valkeinen sizeof(fbtyp))) 1838*f7018c21STomi Valkeinen return -EFAULT; 1839*f7018c21STomi Valkeinen break; 1840*f7018c21STomi Valkeinen #endif /* __sparc__ */ 1841*f7018c21STomi Valkeinen 1842*f7018c21STomi Valkeinen case FBIO_WAITFORVSYNC: 1843*f7018c21STomi Valkeinen { 1844*f7018c21STomi Valkeinen u32 crtc; 1845*f7018c21STomi Valkeinen 1846*f7018c21STomi Valkeinen if (get_user(crtc, (__u32 __user *) arg)) 1847*f7018c21STomi Valkeinen return -EFAULT; 1848*f7018c21STomi Valkeinen 1849*f7018c21STomi Valkeinen return aty_waitforvblank(par, crtc); 1850*f7018c21STomi Valkeinen } 1851*f7018c21STomi Valkeinen 1852*f7018c21STomi Valkeinen #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT) 1853*f7018c21STomi Valkeinen case ATYIO_CLKR: 1854*f7018c21STomi Valkeinen if (M64_HAS(INTEGRATED)) { 1855*f7018c21STomi Valkeinen struct atyclk clk; 1856*f7018c21STomi Valkeinen union aty_pll *pll = &par->pll; 1857*f7018c21STomi Valkeinen u32 dsp_config = pll->ct.dsp_config; 1858*f7018c21STomi Valkeinen u32 dsp_on_off = pll->ct.dsp_on_off; 1859*f7018c21STomi Valkeinen clk.ref_clk_per = par->ref_clk_per; 1860*f7018c21STomi Valkeinen clk.pll_ref_div = pll->ct.pll_ref_div; 1861*f7018c21STomi Valkeinen clk.mclk_fb_div = pll->ct.mclk_fb_div; 1862*f7018c21STomi Valkeinen clk.mclk_post_div = pll->ct.mclk_post_div_real; 1863*f7018c21STomi Valkeinen clk.mclk_fb_mult = pll->ct.mclk_fb_mult; 1864*f7018c21STomi Valkeinen clk.xclk_post_div = pll->ct.xclk_post_div_real; 1865*f7018c21STomi Valkeinen clk.vclk_fb_div = pll->ct.vclk_fb_div; 1866*f7018c21STomi Valkeinen clk.vclk_post_div = pll->ct.vclk_post_div_real; 1867*f7018c21STomi Valkeinen clk.dsp_xclks_per_row = dsp_config & 0x3fff; 1868*f7018c21STomi Valkeinen clk.dsp_loop_latency = (dsp_config >> 16) & 0xf; 1869*f7018c21STomi Valkeinen clk.dsp_precision = (dsp_config >> 20) & 7; 1870*f7018c21STomi Valkeinen clk.dsp_off = dsp_on_off & 0x7ff; 1871*f7018c21STomi Valkeinen clk.dsp_on = (dsp_on_off >> 16) & 0x7ff; 1872*f7018c21STomi Valkeinen if (copy_to_user((struct atyclk __user *) arg, &clk, 1873*f7018c21STomi Valkeinen sizeof(clk))) 1874*f7018c21STomi Valkeinen return -EFAULT; 1875*f7018c21STomi Valkeinen } else 1876*f7018c21STomi Valkeinen return -EINVAL; 1877*f7018c21STomi Valkeinen break; 1878*f7018c21STomi Valkeinen case ATYIO_CLKW: 1879*f7018c21STomi Valkeinen if (M64_HAS(INTEGRATED)) { 1880*f7018c21STomi Valkeinen struct atyclk clk; 1881*f7018c21STomi Valkeinen union aty_pll *pll = &par->pll; 1882*f7018c21STomi Valkeinen if (copy_from_user(&clk, (struct atyclk __user *) arg, 1883*f7018c21STomi Valkeinen sizeof(clk))) 1884*f7018c21STomi Valkeinen return -EFAULT; 1885*f7018c21STomi Valkeinen par->ref_clk_per = clk.ref_clk_per; 1886*f7018c21STomi Valkeinen pll->ct.pll_ref_div = clk.pll_ref_div; 1887*f7018c21STomi Valkeinen pll->ct.mclk_fb_div = clk.mclk_fb_div; 1888*f7018c21STomi Valkeinen pll->ct.mclk_post_div_real = clk.mclk_post_div; 1889*f7018c21STomi Valkeinen pll->ct.mclk_fb_mult = clk.mclk_fb_mult; 1890*f7018c21STomi Valkeinen pll->ct.xclk_post_div_real = clk.xclk_post_div; 1891*f7018c21STomi Valkeinen pll->ct.vclk_fb_div = clk.vclk_fb_div; 1892*f7018c21STomi Valkeinen pll->ct.vclk_post_div_real = clk.vclk_post_div; 1893*f7018c21STomi Valkeinen pll->ct.dsp_config = (clk.dsp_xclks_per_row & 0x3fff) | 1894*f7018c21STomi Valkeinen ((clk.dsp_loop_latency & 0xf) << 16) | 1895*f7018c21STomi Valkeinen ((clk.dsp_precision & 7) << 20); 1896*f7018c21STomi Valkeinen pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) | 1897*f7018c21STomi Valkeinen ((clk.dsp_on & 0x7ff) << 16); 1898*f7018c21STomi Valkeinen /*aty_calc_pll_ct(info, &pll->ct);*/ 1899*f7018c21STomi Valkeinen aty_set_pll_ct(info, pll); 1900*f7018c21STomi Valkeinen } else 1901*f7018c21STomi Valkeinen return -EINVAL; 1902*f7018c21STomi Valkeinen break; 1903*f7018c21STomi Valkeinen case ATYIO_FEATR: 1904*f7018c21STomi Valkeinen if (get_user(par->features, (u32 __user *) arg)) 1905*f7018c21STomi Valkeinen return -EFAULT; 1906*f7018c21STomi Valkeinen break; 1907*f7018c21STomi Valkeinen case ATYIO_FEATW: 1908*f7018c21STomi Valkeinen if (put_user(par->features, (u32 __user *) arg)) 1909*f7018c21STomi Valkeinen return -EFAULT; 1910*f7018c21STomi Valkeinen break; 1911*f7018c21STomi Valkeinen #endif /* DEBUG && CONFIG_FB_ATY_CT */ 1912*f7018c21STomi Valkeinen default: 1913*f7018c21STomi Valkeinen return -EINVAL; 1914*f7018c21STomi Valkeinen } 1915*f7018c21STomi Valkeinen return 0; 1916*f7018c21STomi Valkeinen } 1917*f7018c21STomi Valkeinen 1918*f7018c21STomi Valkeinen static int atyfb_sync(struct fb_info *info) 1919*f7018c21STomi Valkeinen { 1920*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1921*f7018c21STomi Valkeinen 1922*f7018c21STomi Valkeinen if (par->blitter_may_be_busy) 1923*f7018c21STomi Valkeinen wait_for_idle(par); 1924*f7018c21STomi Valkeinen return 0; 1925*f7018c21STomi Valkeinen } 1926*f7018c21STomi Valkeinen 1927*f7018c21STomi Valkeinen #ifdef __sparc__ 1928*f7018c21STomi Valkeinen static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma) 1929*f7018c21STomi Valkeinen { 1930*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 1931*f7018c21STomi Valkeinen unsigned int size, page, map_size = 0; 1932*f7018c21STomi Valkeinen unsigned long map_offset = 0; 1933*f7018c21STomi Valkeinen unsigned long off; 1934*f7018c21STomi Valkeinen int i; 1935*f7018c21STomi Valkeinen 1936*f7018c21STomi Valkeinen if (!par->mmap_map) 1937*f7018c21STomi Valkeinen return -ENXIO; 1938*f7018c21STomi Valkeinen 1939*f7018c21STomi Valkeinen if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) 1940*f7018c21STomi Valkeinen return -EINVAL; 1941*f7018c21STomi Valkeinen 1942*f7018c21STomi Valkeinen off = vma->vm_pgoff << PAGE_SHIFT; 1943*f7018c21STomi Valkeinen size = vma->vm_end - vma->vm_start; 1944*f7018c21STomi Valkeinen 1945*f7018c21STomi Valkeinen /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */ 1946*f7018c21STomi Valkeinen 1947*f7018c21STomi Valkeinen if (((vma->vm_pgoff == 0) && (size == info->fix.smem_len)) || 1948*f7018c21STomi Valkeinen ((off == info->fix.smem_len) && (size == PAGE_SIZE))) 1949*f7018c21STomi Valkeinen off += 0x8000000000000000UL; 1950*f7018c21STomi Valkeinen 1951*f7018c21STomi Valkeinen vma->vm_pgoff = off >> PAGE_SHIFT; /* propagate off changes */ 1952*f7018c21STomi Valkeinen 1953*f7018c21STomi Valkeinen /* Each page, see which map applies */ 1954*f7018c21STomi Valkeinen for (page = 0; page < size;) { 1955*f7018c21STomi Valkeinen map_size = 0; 1956*f7018c21STomi Valkeinen for (i = 0; par->mmap_map[i].size; i++) { 1957*f7018c21STomi Valkeinen unsigned long start = par->mmap_map[i].voff; 1958*f7018c21STomi Valkeinen unsigned long end = start + par->mmap_map[i].size; 1959*f7018c21STomi Valkeinen unsigned long offset = off + page; 1960*f7018c21STomi Valkeinen 1961*f7018c21STomi Valkeinen if (start > offset) 1962*f7018c21STomi Valkeinen continue; 1963*f7018c21STomi Valkeinen if (offset >= end) 1964*f7018c21STomi Valkeinen continue; 1965*f7018c21STomi Valkeinen 1966*f7018c21STomi Valkeinen map_size = par->mmap_map[i].size - (offset - start); 1967*f7018c21STomi Valkeinen map_offset = par->mmap_map[i].poff + (offset - start); 1968*f7018c21STomi Valkeinen break; 1969*f7018c21STomi Valkeinen } 1970*f7018c21STomi Valkeinen if (!map_size) { 1971*f7018c21STomi Valkeinen page += PAGE_SIZE; 1972*f7018c21STomi Valkeinen continue; 1973*f7018c21STomi Valkeinen } 1974*f7018c21STomi Valkeinen if (page + map_size > size) 1975*f7018c21STomi Valkeinen map_size = size - page; 1976*f7018c21STomi Valkeinen 1977*f7018c21STomi Valkeinen pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask); 1978*f7018c21STomi Valkeinen pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag; 1979*f7018c21STomi Valkeinen 1980*f7018c21STomi Valkeinen if (remap_pfn_range(vma, vma->vm_start + page, 1981*f7018c21STomi Valkeinen map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot)) 1982*f7018c21STomi Valkeinen return -EAGAIN; 1983*f7018c21STomi Valkeinen 1984*f7018c21STomi Valkeinen page += map_size; 1985*f7018c21STomi Valkeinen } 1986*f7018c21STomi Valkeinen 1987*f7018c21STomi Valkeinen if (!map_size) 1988*f7018c21STomi Valkeinen return -EINVAL; 1989*f7018c21STomi Valkeinen 1990*f7018c21STomi Valkeinen if (!par->mmaped) 1991*f7018c21STomi Valkeinen par->mmaped = 1; 1992*f7018c21STomi Valkeinen return 0; 1993*f7018c21STomi Valkeinen } 1994*f7018c21STomi Valkeinen #endif /* __sparc__ */ 1995*f7018c21STomi Valkeinen 1996*f7018c21STomi Valkeinen 1997*f7018c21STomi Valkeinen 1998*f7018c21STomi Valkeinen #if defined(CONFIG_PM) && defined(CONFIG_PCI) 1999*f7018c21STomi Valkeinen 2000*f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC 2001*f7018c21STomi Valkeinen /* Power management routines. Those are used for PowerBook sleep. 2002*f7018c21STomi Valkeinen */ 2003*f7018c21STomi Valkeinen static int aty_power_mgmt(int sleep, struct atyfb_par *par) 2004*f7018c21STomi Valkeinen { 2005*f7018c21STomi Valkeinen u32 pm; 2006*f7018c21STomi Valkeinen int timeout; 2007*f7018c21STomi Valkeinen 2008*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2009*f7018c21STomi Valkeinen pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG; 2010*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2011*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2012*f7018c21STomi Valkeinen 2013*f7018c21STomi Valkeinen timeout = 2000; 2014*f7018c21STomi Valkeinen if (sleep) { 2015*f7018c21STomi Valkeinen /* Sleep */ 2016*f7018c21STomi Valkeinen pm &= ~PWR_MGT_ON; 2017*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2018*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2019*f7018c21STomi Valkeinen udelay(10); 2020*f7018c21STomi Valkeinen pm &= ~(PWR_BLON | AUTO_PWR_UP); 2021*f7018c21STomi Valkeinen pm |= SUSPEND_NOW; 2022*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2023*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2024*f7018c21STomi Valkeinen udelay(10); 2025*f7018c21STomi Valkeinen pm |= PWR_MGT_ON; 2026*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2027*f7018c21STomi Valkeinen do { 2028*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2029*f7018c21STomi Valkeinen mdelay(1); 2030*f7018c21STomi Valkeinen if ((--timeout) == 0) 2031*f7018c21STomi Valkeinen break; 2032*f7018c21STomi Valkeinen } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND); 2033*f7018c21STomi Valkeinen } else { 2034*f7018c21STomi Valkeinen /* Wakeup */ 2035*f7018c21STomi Valkeinen pm &= ~PWR_MGT_ON; 2036*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2037*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2038*f7018c21STomi Valkeinen udelay(10); 2039*f7018c21STomi Valkeinen pm &= ~SUSPEND_NOW; 2040*f7018c21STomi Valkeinen pm |= (PWR_BLON | AUTO_PWR_UP); 2041*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2042*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2043*f7018c21STomi Valkeinen udelay(10); 2044*f7018c21STomi Valkeinen pm |= PWR_MGT_ON; 2045*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2046*f7018c21STomi Valkeinen do { 2047*f7018c21STomi Valkeinen pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2048*f7018c21STomi Valkeinen mdelay(1); 2049*f7018c21STomi Valkeinen if ((--timeout) == 0) 2050*f7018c21STomi Valkeinen break; 2051*f7018c21STomi Valkeinen } while ((pm & PWR_MGT_STATUS_MASK) != 0); 2052*f7018c21STomi Valkeinen } 2053*f7018c21STomi Valkeinen mdelay(500); 2054*f7018c21STomi Valkeinen 2055*f7018c21STomi Valkeinen return timeout ? 0 : -EIO; 2056*f7018c21STomi Valkeinen } 2057*f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */ 2058*f7018c21STomi Valkeinen 2059*f7018c21STomi Valkeinen static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state) 2060*f7018c21STomi Valkeinen { 2061*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(pdev); 2062*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 2063*f7018c21STomi Valkeinen 2064*f7018c21STomi Valkeinen if (state.event == pdev->dev.power.power_state.event) 2065*f7018c21STomi Valkeinen return 0; 2066*f7018c21STomi Valkeinen 2067*f7018c21STomi Valkeinen console_lock(); 2068*f7018c21STomi Valkeinen 2069*f7018c21STomi Valkeinen fb_set_suspend(info, 1); 2070*f7018c21STomi Valkeinen 2071*f7018c21STomi Valkeinen /* Idle & reset engine */ 2072*f7018c21STomi Valkeinen wait_for_idle(par); 2073*f7018c21STomi Valkeinen aty_reset_engine(par); 2074*f7018c21STomi Valkeinen 2075*f7018c21STomi Valkeinen /* Blank display and LCD */ 2076*f7018c21STomi Valkeinen atyfb_blank(FB_BLANK_POWERDOWN, info); 2077*f7018c21STomi Valkeinen 2078*f7018c21STomi Valkeinen par->asleep = 1; 2079*f7018c21STomi Valkeinen par->lock_blank = 1; 2080*f7018c21STomi Valkeinen 2081*f7018c21STomi Valkeinen /* 2082*f7018c21STomi Valkeinen * Because we may change PCI D state ourselves, we need to 2083*f7018c21STomi Valkeinen * first save the config space content so the core can 2084*f7018c21STomi Valkeinen * restore it properly on resume. 2085*f7018c21STomi Valkeinen */ 2086*f7018c21STomi Valkeinen pci_save_state(pdev); 2087*f7018c21STomi Valkeinen 2088*f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC 2089*f7018c21STomi Valkeinen /* Set chip to "suspend" mode */ 2090*f7018c21STomi Valkeinen if (machine_is(powermac) && aty_power_mgmt(1, par)) { 2091*f7018c21STomi Valkeinen par->asleep = 0; 2092*f7018c21STomi Valkeinen par->lock_blank = 0; 2093*f7018c21STomi Valkeinen atyfb_blank(FB_BLANK_UNBLANK, info); 2094*f7018c21STomi Valkeinen fb_set_suspend(info, 0); 2095*f7018c21STomi Valkeinen console_unlock(); 2096*f7018c21STomi Valkeinen return -EIO; 2097*f7018c21STomi Valkeinen } 2098*f7018c21STomi Valkeinen #else 2099*f7018c21STomi Valkeinen pci_set_power_state(pdev, pci_choose_state(pdev, state)); 2100*f7018c21STomi Valkeinen #endif 2101*f7018c21STomi Valkeinen 2102*f7018c21STomi Valkeinen console_unlock(); 2103*f7018c21STomi Valkeinen 2104*f7018c21STomi Valkeinen pdev->dev.power.power_state = state; 2105*f7018c21STomi Valkeinen 2106*f7018c21STomi Valkeinen return 0; 2107*f7018c21STomi Valkeinen } 2108*f7018c21STomi Valkeinen 2109*f7018c21STomi Valkeinen static void aty_resume_chip(struct fb_info *info) 2110*f7018c21STomi Valkeinen { 2111*f7018c21STomi Valkeinen struct atyfb_par *par = info->par; 2112*f7018c21STomi Valkeinen 2113*f7018c21STomi Valkeinen aty_st_le32(MEM_CNTL, par->mem_cntl, par); 2114*f7018c21STomi Valkeinen 2115*f7018c21STomi Valkeinen if (par->pll_ops->resume_pll) 2116*f7018c21STomi Valkeinen par->pll_ops->resume_pll(info, &par->pll); 2117*f7018c21STomi Valkeinen 2118*f7018c21STomi Valkeinen if (par->aux_start) 2119*f7018c21STomi Valkeinen aty_st_le32(BUS_CNTL, 2120*f7018c21STomi Valkeinen aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par); 2121*f7018c21STomi Valkeinen } 2122*f7018c21STomi Valkeinen 2123*f7018c21STomi Valkeinen static int atyfb_pci_resume(struct pci_dev *pdev) 2124*f7018c21STomi Valkeinen { 2125*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(pdev); 2126*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 2127*f7018c21STomi Valkeinen 2128*f7018c21STomi Valkeinen if (pdev->dev.power.power_state.event == PM_EVENT_ON) 2129*f7018c21STomi Valkeinen return 0; 2130*f7018c21STomi Valkeinen 2131*f7018c21STomi Valkeinen console_lock(); 2132*f7018c21STomi Valkeinen 2133*f7018c21STomi Valkeinen /* 2134*f7018c21STomi Valkeinen * PCI state will have been restored by the core, so 2135*f7018c21STomi Valkeinen * we should be in D0 now with our config space fully 2136*f7018c21STomi Valkeinen * restored 2137*f7018c21STomi Valkeinen */ 2138*f7018c21STomi Valkeinen 2139*f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC 2140*f7018c21STomi Valkeinen if (machine_is(powermac) && 2141*f7018c21STomi Valkeinen pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) 2142*f7018c21STomi Valkeinen aty_power_mgmt(0, par); 2143*f7018c21STomi Valkeinen #endif 2144*f7018c21STomi Valkeinen 2145*f7018c21STomi Valkeinen aty_resume_chip(info); 2146*f7018c21STomi Valkeinen 2147*f7018c21STomi Valkeinen par->asleep = 0; 2148*f7018c21STomi Valkeinen 2149*f7018c21STomi Valkeinen /* Restore display */ 2150*f7018c21STomi Valkeinen atyfb_set_par(info); 2151*f7018c21STomi Valkeinen 2152*f7018c21STomi Valkeinen /* Refresh */ 2153*f7018c21STomi Valkeinen fb_set_suspend(info, 0); 2154*f7018c21STomi Valkeinen 2155*f7018c21STomi Valkeinen /* Unblank */ 2156*f7018c21STomi Valkeinen par->lock_blank = 0; 2157*f7018c21STomi Valkeinen atyfb_blank(FB_BLANK_UNBLANK, info); 2158*f7018c21STomi Valkeinen 2159*f7018c21STomi Valkeinen console_unlock(); 2160*f7018c21STomi Valkeinen 2161*f7018c21STomi Valkeinen pdev->dev.power.power_state = PMSG_ON; 2162*f7018c21STomi Valkeinen 2163*f7018c21STomi Valkeinen return 0; 2164*f7018c21STomi Valkeinen } 2165*f7018c21STomi Valkeinen 2166*f7018c21STomi Valkeinen #endif /* defined(CONFIG_PM) && defined(CONFIG_PCI) */ 2167*f7018c21STomi Valkeinen 2168*f7018c21STomi Valkeinen /* Backlight */ 2169*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_BACKLIGHT 2170*f7018c21STomi Valkeinen #define MAX_LEVEL 0xFF 2171*f7018c21STomi Valkeinen 2172*f7018c21STomi Valkeinen static int aty_bl_get_level_brightness(struct atyfb_par *par, int level) 2173*f7018c21STomi Valkeinen { 2174*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(par->pdev); 2175*f7018c21STomi Valkeinen int atylevel; 2176*f7018c21STomi Valkeinen 2177*f7018c21STomi Valkeinen /* Get and convert the value */ 2178*f7018c21STomi Valkeinen /* No locking of bl_curve since we read a single value */ 2179*f7018c21STomi Valkeinen atylevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL; 2180*f7018c21STomi Valkeinen 2181*f7018c21STomi Valkeinen if (atylevel < 0) 2182*f7018c21STomi Valkeinen atylevel = 0; 2183*f7018c21STomi Valkeinen else if (atylevel > MAX_LEVEL) 2184*f7018c21STomi Valkeinen atylevel = MAX_LEVEL; 2185*f7018c21STomi Valkeinen 2186*f7018c21STomi Valkeinen return atylevel; 2187*f7018c21STomi Valkeinen } 2188*f7018c21STomi Valkeinen 2189*f7018c21STomi Valkeinen static int aty_bl_update_status(struct backlight_device *bd) 2190*f7018c21STomi Valkeinen { 2191*f7018c21STomi Valkeinen struct atyfb_par *par = bl_get_data(bd); 2192*f7018c21STomi Valkeinen unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par); 2193*f7018c21STomi Valkeinen int level; 2194*f7018c21STomi Valkeinen 2195*f7018c21STomi Valkeinen if (bd->props.power != FB_BLANK_UNBLANK || 2196*f7018c21STomi Valkeinen bd->props.fb_blank != FB_BLANK_UNBLANK) 2197*f7018c21STomi Valkeinen level = 0; 2198*f7018c21STomi Valkeinen else 2199*f7018c21STomi Valkeinen level = bd->props.brightness; 2200*f7018c21STomi Valkeinen 2201*f7018c21STomi Valkeinen reg |= (BLMOD_EN | BIASMOD_EN); 2202*f7018c21STomi Valkeinen if (level > 0) { 2203*f7018c21STomi Valkeinen reg &= ~BIAS_MOD_LEVEL_MASK; 2204*f7018c21STomi Valkeinen reg |= (aty_bl_get_level_brightness(par, level) << BIAS_MOD_LEVEL_SHIFT); 2205*f7018c21STomi Valkeinen } else { 2206*f7018c21STomi Valkeinen reg &= ~BIAS_MOD_LEVEL_MASK; 2207*f7018c21STomi Valkeinen reg |= (aty_bl_get_level_brightness(par, 0) << BIAS_MOD_LEVEL_SHIFT); 2208*f7018c21STomi Valkeinen } 2209*f7018c21STomi Valkeinen aty_st_lcd(LCD_MISC_CNTL, reg, par); 2210*f7018c21STomi Valkeinen 2211*f7018c21STomi Valkeinen return 0; 2212*f7018c21STomi Valkeinen } 2213*f7018c21STomi Valkeinen 2214*f7018c21STomi Valkeinen static int aty_bl_get_brightness(struct backlight_device *bd) 2215*f7018c21STomi Valkeinen { 2216*f7018c21STomi Valkeinen return bd->props.brightness; 2217*f7018c21STomi Valkeinen } 2218*f7018c21STomi Valkeinen 2219*f7018c21STomi Valkeinen static const struct backlight_ops aty_bl_data = { 2220*f7018c21STomi Valkeinen .get_brightness = aty_bl_get_brightness, 2221*f7018c21STomi Valkeinen .update_status = aty_bl_update_status, 2222*f7018c21STomi Valkeinen }; 2223*f7018c21STomi Valkeinen 2224*f7018c21STomi Valkeinen static void aty_bl_init(struct atyfb_par *par) 2225*f7018c21STomi Valkeinen { 2226*f7018c21STomi Valkeinen struct backlight_properties props; 2227*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(par->pdev); 2228*f7018c21STomi Valkeinen struct backlight_device *bd; 2229*f7018c21STomi Valkeinen char name[12]; 2230*f7018c21STomi Valkeinen 2231*f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT 2232*f7018c21STomi Valkeinen if (!pmac_has_backlight_type("ati")) 2233*f7018c21STomi Valkeinen return; 2234*f7018c21STomi Valkeinen #endif 2235*f7018c21STomi Valkeinen 2236*f7018c21STomi Valkeinen snprintf(name, sizeof(name), "atybl%d", info->node); 2237*f7018c21STomi Valkeinen 2238*f7018c21STomi Valkeinen memset(&props, 0, sizeof(struct backlight_properties)); 2239*f7018c21STomi Valkeinen props.type = BACKLIGHT_RAW; 2240*f7018c21STomi Valkeinen props.max_brightness = FB_BACKLIGHT_LEVELS - 1; 2241*f7018c21STomi Valkeinen bd = backlight_device_register(name, info->dev, par, &aty_bl_data, 2242*f7018c21STomi Valkeinen &props); 2243*f7018c21STomi Valkeinen if (IS_ERR(bd)) { 2244*f7018c21STomi Valkeinen info->bl_dev = NULL; 2245*f7018c21STomi Valkeinen printk(KERN_WARNING "aty: Backlight registration failed\n"); 2246*f7018c21STomi Valkeinen goto error; 2247*f7018c21STomi Valkeinen } 2248*f7018c21STomi Valkeinen 2249*f7018c21STomi Valkeinen info->bl_dev = bd; 2250*f7018c21STomi Valkeinen fb_bl_default_curve(info, 0, 2251*f7018c21STomi Valkeinen 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL, 2252*f7018c21STomi Valkeinen 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL); 2253*f7018c21STomi Valkeinen 2254*f7018c21STomi Valkeinen bd->props.brightness = bd->props.max_brightness; 2255*f7018c21STomi Valkeinen bd->props.power = FB_BLANK_UNBLANK; 2256*f7018c21STomi Valkeinen backlight_update_status(bd); 2257*f7018c21STomi Valkeinen 2258*f7018c21STomi Valkeinen printk("aty: Backlight initialized (%s)\n", name); 2259*f7018c21STomi Valkeinen 2260*f7018c21STomi Valkeinen return; 2261*f7018c21STomi Valkeinen 2262*f7018c21STomi Valkeinen error: 2263*f7018c21STomi Valkeinen return; 2264*f7018c21STomi Valkeinen } 2265*f7018c21STomi Valkeinen 2266*f7018c21STomi Valkeinen #ifdef CONFIG_PCI 2267*f7018c21STomi Valkeinen static void aty_bl_exit(struct backlight_device *bd) 2268*f7018c21STomi Valkeinen { 2269*f7018c21STomi Valkeinen backlight_device_unregister(bd); 2270*f7018c21STomi Valkeinen printk("aty: Backlight unloaded\n"); 2271*f7018c21STomi Valkeinen } 2272*f7018c21STomi Valkeinen #endif /* CONFIG_PCI */ 2273*f7018c21STomi Valkeinen 2274*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_BACKLIGHT */ 2275*f7018c21STomi Valkeinen 2276*f7018c21STomi Valkeinen static void aty_calc_mem_refresh(struct atyfb_par *par, int xclk) 2277*f7018c21STomi Valkeinen { 2278*f7018c21STomi Valkeinen const int ragepro_tbl[] = { 2279*f7018c21STomi Valkeinen 44, 50, 55, 66, 75, 80, 100 2280*f7018c21STomi Valkeinen }; 2281*f7018c21STomi Valkeinen const int ragexl_tbl[] = { 2282*f7018c21STomi Valkeinen 50, 66, 75, 83, 90, 95, 100, 105, 2283*f7018c21STomi Valkeinen 110, 115, 120, 125, 133, 143, 166 2284*f7018c21STomi Valkeinen }; 2285*f7018c21STomi Valkeinen const int *refresh_tbl; 2286*f7018c21STomi Valkeinen int i, size; 2287*f7018c21STomi Valkeinen 2288*f7018c21STomi Valkeinen if (M64_HAS(XL_MEM)) { 2289*f7018c21STomi Valkeinen refresh_tbl = ragexl_tbl; 2290*f7018c21STomi Valkeinen size = ARRAY_SIZE(ragexl_tbl); 2291*f7018c21STomi Valkeinen } else { 2292*f7018c21STomi Valkeinen refresh_tbl = ragepro_tbl; 2293*f7018c21STomi Valkeinen size = ARRAY_SIZE(ragepro_tbl); 2294*f7018c21STomi Valkeinen } 2295*f7018c21STomi Valkeinen 2296*f7018c21STomi Valkeinen for (i = 0; i < size; i++) { 2297*f7018c21STomi Valkeinen if (xclk < refresh_tbl[i]) 2298*f7018c21STomi Valkeinen break; 2299*f7018c21STomi Valkeinen } 2300*f7018c21STomi Valkeinen par->mem_refresh_rate = i; 2301*f7018c21STomi Valkeinen } 2302*f7018c21STomi Valkeinen 2303*f7018c21STomi Valkeinen /* 2304*f7018c21STomi Valkeinen * Initialisation 2305*f7018c21STomi Valkeinen */ 2306*f7018c21STomi Valkeinen 2307*f7018c21STomi Valkeinen static struct fb_info *fb_list = NULL; 2308*f7018c21STomi Valkeinen 2309*f7018c21STomi Valkeinen #if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) 2310*f7018c21STomi Valkeinen static int atyfb_get_timings_from_lcd(struct atyfb_par *par, 2311*f7018c21STomi Valkeinen struct fb_var_screeninfo *var) 2312*f7018c21STomi Valkeinen { 2313*f7018c21STomi Valkeinen int ret = -EINVAL; 2314*f7018c21STomi Valkeinen 2315*f7018c21STomi Valkeinen if (par->lcd_table != 0 && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { 2316*f7018c21STomi Valkeinen *var = default_var; 2317*f7018c21STomi Valkeinen var->xres = var->xres_virtual = par->lcd_hdisp; 2318*f7018c21STomi Valkeinen var->right_margin = par->lcd_right_margin; 2319*f7018c21STomi Valkeinen var->left_margin = par->lcd_hblank_len - 2320*f7018c21STomi Valkeinen (par->lcd_right_margin + par->lcd_hsync_dly + 2321*f7018c21STomi Valkeinen par->lcd_hsync_len); 2322*f7018c21STomi Valkeinen var->hsync_len = par->lcd_hsync_len + par->lcd_hsync_dly; 2323*f7018c21STomi Valkeinen var->yres = var->yres_virtual = par->lcd_vdisp; 2324*f7018c21STomi Valkeinen var->lower_margin = par->lcd_lower_margin; 2325*f7018c21STomi Valkeinen var->upper_margin = par->lcd_vblank_len - 2326*f7018c21STomi Valkeinen (par->lcd_lower_margin + par->lcd_vsync_len); 2327*f7018c21STomi Valkeinen var->vsync_len = par->lcd_vsync_len; 2328*f7018c21STomi Valkeinen var->pixclock = par->lcd_pixclock; 2329*f7018c21STomi Valkeinen ret = 0; 2330*f7018c21STomi Valkeinen } 2331*f7018c21STomi Valkeinen 2332*f7018c21STomi Valkeinen return ret; 2333*f7018c21STomi Valkeinen } 2334*f7018c21STomi Valkeinen #endif /* defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) */ 2335*f7018c21STomi Valkeinen 2336*f7018c21STomi Valkeinen static int aty_init(struct fb_info *info) 2337*f7018c21STomi Valkeinen { 2338*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 2339*f7018c21STomi Valkeinen const char *ramname = NULL, *xtal; 2340*f7018c21STomi Valkeinen int gtb_memsize, has_var = 0; 2341*f7018c21STomi Valkeinen struct fb_var_screeninfo var; 2342*f7018c21STomi Valkeinen int ret; 2343*f7018c21STomi Valkeinen 2344*f7018c21STomi Valkeinen init_waitqueue_head(&par->vblank.wait); 2345*f7018c21STomi Valkeinen spin_lock_init(&par->int_lock); 2346*f7018c21STomi Valkeinen 2347*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX 2348*f7018c21STomi Valkeinen if (!M64_HAS(INTEGRATED)) { 2349*f7018c21STomi Valkeinen u32 stat0; 2350*f7018c21STomi Valkeinen u8 dac_type, dac_subtype, clk_type; 2351*f7018c21STomi Valkeinen stat0 = aty_ld_le32(CNFG_STAT0, par); 2352*f7018c21STomi Valkeinen par->bus_type = (stat0 >> 0) & 0x07; 2353*f7018c21STomi Valkeinen par->ram_type = (stat0 >> 3) & 0x07; 2354*f7018c21STomi Valkeinen ramname = aty_gx_ram[par->ram_type]; 2355*f7018c21STomi Valkeinen /* FIXME: clockchip/RAMDAC probing? */ 2356*f7018c21STomi Valkeinen dac_type = (aty_ld_le32(DAC_CNTL, par) >> 16) & 0x07; 2357*f7018c21STomi Valkeinen #ifdef CONFIG_ATARI 2358*f7018c21STomi Valkeinen clk_type = CLK_ATI18818_1; 2359*f7018c21STomi Valkeinen dac_type = (stat0 >> 9) & 0x07; 2360*f7018c21STomi Valkeinen if (dac_type == 0x07) 2361*f7018c21STomi Valkeinen dac_subtype = DAC_ATT20C408; 2362*f7018c21STomi Valkeinen else 2363*f7018c21STomi Valkeinen dac_subtype = (aty_ld_8(SCRATCH_REG1 + 1, par) & 0xF0) | dac_type; 2364*f7018c21STomi Valkeinen #else 2365*f7018c21STomi Valkeinen dac_type = DAC_IBMRGB514; 2366*f7018c21STomi Valkeinen dac_subtype = DAC_IBMRGB514; 2367*f7018c21STomi Valkeinen clk_type = CLK_IBMRGB514; 2368*f7018c21STomi Valkeinen #endif 2369*f7018c21STomi Valkeinen switch (dac_subtype) { 2370*f7018c21STomi Valkeinen case DAC_IBMRGB514: 2371*f7018c21STomi Valkeinen par->dac_ops = &aty_dac_ibm514; 2372*f7018c21STomi Valkeinen break; 2373*f7018c21STomi Valkeinen #ifdef CONFIG_ATARI 2374*f7018c21STomi Valkeinen case DAC_ATI68860_B: 2375*f7018c21STomi Valkeinen case DAC_ATI68860_C: 2376*f7018c21STomi Valkeinen par->dac_ops = &aty_dac_ati68860b; 2377*f7018c21STomi Valkeinen break; 2378*f7018c21STomi Valkeinen case DAC_ATT20C408: 2379*f7018c21STomi Valkeinen case DAC_ATT21C498: 2380*f7018c21STomi Valkeinen par->dac_ops = &aty_dac_att21c498; 2381*f7018c21STomi Valkeinen break; 2382*f7018c21STomi Valkeinen #endif 2383*f7018c21STomi Valkeinen default: 2384*f7018c21STomi Valkeinen PRINTKI("aty_init: DAC type not implemented yet!\n"); 2385*f7018c21STomi Valkeinen par->dac_ops = &aty_dac_unsupported; 2386*f7018c21STomi Valkeinen break; 2387*f7018c21STomi Valkeinen } 2388*f7018c21STomi Valkeinen switch (clk_type) { 2389*f7018c21STomi Valkeinen #ifdef CONFIG_ATARI 2390*f7018c21STomi Valkeinen case CLK_ATI18818_1: 2391*f7018c21STomi Valkeinen par->pll_ops = &aty_pll_ati18818_1; 2392*f7018c21STomi Valkeinen break; 2393*f7018c21STomi Valkeinen #else 2394*f7018c21STomi Valkeinen case CLK_IBMRGB514: 2395*f7018c21STomi Valkeinen par->pll_ops = &aty_pll_ibm514; 2396*f7018c21STomi Valkeinen break; 2397*f7018c21STomi Valkeinen #endif 2398*f7018c21STomi Valkeinen #if 0 /* dead code */ 2399*f7018c21STomi Valkeinen case CLK_STG1703: 2400*f7018c21STomi Valkeinen par->pll_ops = &aty_pll_stg1703; 2401*f7018c21STomi Valkeinen break; 2402*f7018c21STomi Valkeinen case CLK_CH8398: 2403*f7018c21STomi Valkeinen par->pll_ops = &aty_pll_ch8398; 2404*f7018c21STomi Valkeinen break; 2405*f7018c21STomi Valkeinen case CLK_ATT20C408: 2406*f7018c21STomi Valkeinen par->pll_ops = &aty_pll_att20c408; 2407*f7018c21STomi Valkeinen break; 2408*f7018c21STomi Valkeinen #endif 2409*f7018c21STomi Valkeinen default: 2410*f7018c21STomi Valkeinen PRINTKI("aty_init: CLK type not implemented yet!"); 2411*f7018c21STomi Valkeinen par->pll_ops = &aty_pll_unsupported; 2412*f7018c21STomi Valkeinen break; 2413*f7018c21STomi Valkeinen } 2414*f7018c21STomi Valkeinen } 2415*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */ 2416*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 2417*f7018c21STomi Valkeinen if (M64_HAS(INTEGRATED)) { 2418*f7018c21STomi Valkeinen par->dac_ops = &aty_dac_ct; 2419*f7018c21STomi Valkeinen par->pll_ops = &aty_pll_ct; 2420*f7018c21STomi Valkeinen par->bus_type = PCI; 2421*f7018c21STomi Valkeinen par->ram_type = (aty_ld_le32(CNFG_STAT0, par) & 0x07); 2422*f7018c21STomi Valkeinen if (M64_HAS(XL_MEM)) 2423*f7018c21STomi Valkeinen ramname = aty_xl_ram[par->ram_type]; 2424*f7018c21STomi Valkeinen else 2425*f7018c21STomi Valkeinen ramname = aty_ct_ram[par->ram_type]; 2426*f7018c21STomi Valkeinen /* for many chips, the mclk is 67 MHz for SDRAM, 63 MHz otherwise */ 2427*f7018c21STomi Valkeinen if (par->pll_limits.mclk == 67 && par->ram_type < SDRAM) 2428*f7018c21STomi Valkeinen par->pll_limits.mclk = 63; 2429*f7018c21STomi Valkeinen /* Mobility + 32bit memory interface need halved XCLK. */ 2430*f7018c21STomi Valkeinen if (M64_HAS(MOBIL_BUS) && par->ram_type == SDRAM32) 2431*f7018c21STomi Valkeinen par->pll_limits.xclk = (par->pll_limits.xclk + 1) >> 1; 2432*f7018c21STomi Valkeinen } 2433*f7018c21STomi Valkeinen #endif 2434*f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC 2435*f7018c21STomi Valkeinen /* 2436*f7018c21STomi Valkeinen * The Apple iBook1 uses non-standard memory frequencies. 2437*f7018c21STomi Valkeinen * We detect it and set the frequency manually. 2438*f7018c21STomi Valkeinen */ 2439*f7018c21STomi Valkeinen if (of_machine_is_compatible("PowerBook2,1")) { 2440*f7018c21STomi Valkeinen par->pll_limits.mclk = 70; 2441*f7018c21STomi Valkeinen par->pll_limits.xclk = 53; 2442*f7018c21STomi Valkeinen } 2443*f7018c21STomi Valkeinen #endif 2444*f7018c21STomi Valkeinen 2445*f7018c21STomi Valkeinen /* Allow command line to override clocks. */ 2446*f7018c21STomi Valkeinen if (pll) 2447*f7018c21STomi Valkeinen par->pll_limits.pll_max = pll; 2448*f7018c21STomi Valkeinen if (mclk) 2449*f7018c21STomi Valkeinen par->pll_limits.mclk = mclk; 2450*f7018c21STomi Valkeinen if (xclk) 2451*f7018c21STomi Valkeinen par->pll_limits.xclk = xclk; 2452*f7018c21STomi Valkeinen 2453*f7018c21STomi Valkeinen aty_calc_mem_refresh(par, par->pll_limits.xclk); 2454*f7018c21STomi Valkeinen par->pll_per = 1000000/par->pll_limits.pll_max; 2455*f7018c21STomi Valkeinen par->mclk_per = 1000000/par->pll_limits.mclk; 2456*f7018c21STomi Valkeinen par->xclk_per = 1000000/par->pll_limits.xclk; 2457*f7018c21STomi Valkeinen 2458*f7018c21STomi Valkeinen par->ref_clk_per = 1000000000000ULL / 14318180; 2459*f7018c21STomi Valkeinen xtal = "14.31818"; 2460*f7018c21STomi Valkeinen 2461*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 2462*f7018c21STomi Valkeinen if (M64_HAS(GTB_DSP)) { 2463*f7018c21STomi Valkeinen u8 pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par); 2464*f7018c21STomi Valkeinen 2465*f7018c21STomi Valkeinen if (pll_ref_div) { 2466*f7018c21STomi Valkeinen int diff1, diff2; 2467*f7018c21STomi Valkeinen diff1 = 510 * 14 / pll_ref_div - par->pll_limits.pll_max; 2468*f7018c21STomi Valkeinen diff2 = 510 * 29 / pll_ref_div - par->pll_limits.pll_max; 2469*f7018c21STomi Valkeinen if (diff1 < 0) 2470*f7018c21STomi Valkeinen diff1 = -diff1; 2471*f7018c21STomi Valkeinen if (diff2 < 0) 2472*f7018c21STomi Valkeinen diff2 = -diff2; 2473*f7018c21STomi Valkeinen if (diff2 < diff1) { 2474*f7018c21STomi Valkeinen par->ref_clk_per = 1000000000000ULL / 29498928; 2475*f7018c21STomi Valkeinen xtal = "29.498928"; 2476*f7018c21STomi Valkeinen } 2477*f7018c21STomi Valkeinen } 2478*f7018c21STomi Valkeinen } 2479*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */ 2480*f7018c21STomi Valkeinen 2481*f7018c21STomi Valkeinen /* save previous video mode */ 2482*f7018c21STomi Valkeinen aty_get_crtc(par, &par->saved_crtc); 2483*f7018c21STomi Valkeinen if (par->pll_ops->get_pll) 2484*f7018c21STomi Valkeinen par->pll_ops->get_pll(info, &par->saved_pll); 2485*f7018c21STomi Valkeinen 2486*f7018c21STomi Valkeinen par->mem_cntl = aty_ld_le32(MEM_CNTL, par); 2487*f7018c21STomi Valkeinen gtb_memsize = M64_HAS(GTB_DSP); 2488*f7018c21STomi Valkeinen if (gtb_memsize) 2489*f7018c21STomi Valkeinen /* 0xF used instead of MEM_SIZE_ALIAS */ 2490*f7018c21STomi Valkeinen switch (par->mem_cntl & 0xF) { 2491*f7018c21STomi Valkeinen case MEM_SIZE_512K: 2492*f7018c21STomi Valkeinen info->fix.smem_len = 0x80000; 2493*f7018c21STomi Valkeinen break; 2494*f7018c21STomi Valkeinen case MEM_SIZE_1M: 2495*f7018c21STomi Valkeinen info->fix.smem_len = 0x100000; 2496*f7018c21STomi Valkeinen break; 2497*f7018c21STomi Valkeinen case MEM_SIZE_2M_GTB: 2498*f7018c21STomi Valkeinen info->fix.smem_len = 0x200000; 2499*f7018c21STomi Valkeinen break; 2500*f7018c21STomi Valkeinen case MEM_SIZE_4M_GTB: 2501*f7018c21STomi Valkeinen info->fix.smem_len = 0x400000; 2502*f7018c21STomi Valkeinen break; 2503*f7018c21STomi Valkeinen case MEM_SIZE_6M_GTB: 2504*f7018c21STomi Valkeinen info->fix.smem_len = 0x600000; 2505*f7018c21STomi Valkeinen break; 2506*f7018c21STomi Valkeinen case MEM_SIZE_8M_GTB: 2507*f7018c21STomi Valkeinen info->fix.smem_len = 0x800000; 2508*f7018c21STomi Valkeinen break; 2509*f7018c21STomi Valkeinen default: 2510*f7018c21STomi Valkeinen info->fix.smem_len = 0x80000; 2511*f7018c21STomi Valkeinen } else 2512*f7018c21STomi Valkeinen switch (par->mem_cntl & MEM_SIZE_ALIAS) { 2513*f7018c21STomi Valkeinen case MEM_SIZE_512K: 2514*f7018c21STomi Valkeinen info->fix.smem_len = 0x80000; 2515*f7018c21STomi Valkeinen break; 2516*f7018c21STomi Valkeinen case MEM_SIZE_1M: 2517*f7018c21STomi Valkeinen info->fix.smem_len = 0x100000; 2518*f7018c21STomi Valkeinen break; 2519*f7018c21STomi Valkeinen case MEM_SIZE_2M: 2520*f7018c21STomi Valkeinen info->fix.smem_len = 0x200000; 2521*f7018c21STomi Valkeinen break; 2522*f7018c21STomi Valkeinen case MEM_SIZE_4M: 2523*f7018c21STomi Valkeinen info->fix.smem_len = 0x400000; 2524*f7018c21STomi Valkeinen break; 2525*f7018c21STomi Valkeinen case MEM_SIZE_6M: 2526*f7018c21STomi Valkeinen info->fix.smem_len = 0x600000; 2527*f7018c21STomi Valkeinen break; 2528*f7018c21STomi Valkeinen case MEM_SIZE_8M: 2529*f7018c21STomi Valkeinen info->fix.smem_len = 0x800000; 2530*f7018c21STomi Valkeinen break; 2531*f7018c21STomi Valkeinen default: 2532*f7018c21STomi Valkeinen info->fix.smem_len = 0x80000; 2533*f7018c21STomi Valkeinen } 2534*f7018c21STomi Valkeinen 2535*f7018c21STomi Valkeinen if (M64_HAS(MAGIC_VRAM_SIZE)) { 2536*f7018c21STomi Valkeinen if (aty_ld_le32(CNFG_STAT1, par) & 0x40000000) 2537*f7018c21STomi Valkeinen info->fix.smem_len += 0x400000; 2538*f7018c21STomi Valkeinen } 2539*f7018c21STomi Valkeinen 2540*f7018c21STomi Valkeinen if (vram) { 2541*f7018c21STomi Valkeinen info->fix.smem_len = vram * 1024; 2542*f7018c21STomi Valkeinen par->mem_cntl &= ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS); 2543*f7018c21STomi Valkeinen if (info->fix.smem_len <= 0x80000) 2544*f7018c21STomi Valkeinen par->mem_cntl |= MEM_SIZE_512K; 2545*f7018c21STomi Valkeinen else if (info->fix.smem_len <= 0x100000) 2546*f7018c21STomi Valkeinen par->mem_cntl |= MEM_SIZE_1M; 2547*f7018c21STomi Valkeinen else if (info->fix.smem_len <= 0x200000) 2548*f7018c21STomi Valkeinen par->mem_cntl |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M; 2549*f7018c21STomi Valkeinen else if (info->fix.smem_len <= 0x400000) 2550*f7018c21STomi Valkeinen par->mem_cntl |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M; 2551*f7018c21STomi Valkeinen else if (info->fix.smem_len <= 0x600000) 2552*f7018c21STomi Valkeinen par->mem_cntl |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M; 2553*f7018c21STomi Valkeinen else 2554*f7018c21STomi Valkeinen par->mem_cntl |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M; 2555*f7018c21STomi Valkeinen aty_st_le32(MEM_CNTL, par->mem_cntl, par); 2556*f7018c21STomi Valkeinen } 2557*f7018c21STomi Valkeinen 2558*f7018c21STomi Valkeinen /* 2559*f7018c21STomi Valkeinen * Reg Block 0 (CT-compatible block) is at mmio_start 2560*f7018c21STomi Valkeinen * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400 2561*f7018c21STomi Valkeinen */ 2562*f7018c21STomi Valkeinen if (M64_HAS(GX)) { 2563*f7018c21STomi Valkeinen info->fix.mmio_len = 0x400; 2564*f7018c21STomi Valkeinen info->fix.accel = FB_ACCEL_ATI_MACH64GX; 2565*f7018c21STomi Valkeinen } else if (M64_HAS(CT)) { 2566*f7018c21STomi Valkeinen info->fix.mmio_len = 0x400; 2567*f7018c21STomi Valkeinen info->fix.accel = FB_ACCEL_ATI_MACH64CT; 2568*f7018c21STomi Valkeinen } else if (M64_HAS(VT)) { 2569*f7018c21STomi Valkeinen info->fix.mmio_start -= 0x400; 2570*f7018c21STomi Valkeinen info->fix.mmio_len = 0x800; 2571*f7018c21STomi Valkeinen info->fix.accel = FB_ACCEL_ATI_MACH64VT; 2572*f7018c21STomi Valkeinen } else {/* GT */ 2573*f7018c21STomi Valkeinen info->fix.mmio_start -= 0x400; 2574*f7018c21STomi Valkeinen info->fix.mmio_len = 0x800; 2575*f7018c21STomi Valkeinen info->fix.accel = FB_ACCEL_ATI_MACH64GT; 2576*f7018c21STomi Valkeinen } 2577*f7018c21STomi Valkeinen 2578*f7018c21STomi Valkeinen PRINTKI("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK, %d MHz XCLK\n", 2579*f7018c21STomi Valkeinen info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len>>20), 2580*f7018c21STomi Valkeinen info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal, 2581*f7018c21STomi Valkeinen par->pll_limits.pll_max, par->pll_limits.mclk, 2582*f7018c21STomi Valkeinen par->pll_limits.xclk); 2583*f7018c21STomi Valkeinen 2584*f7018c21STomi Valkeinen #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT) 2585*f7018c21STomi Valkeinen if (M64_HAS(INTEGRATED)) { 2586*f7018c21STomi Valkeinen int i; 2587*f7018c21STomi Valkeinen printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL " 2588*f7018c21STomi Valkeinen "EXT_MEM_CNTL CRTC_GEN_CNTL DSP_CONFIG " 2589*f7018c21STomi Valkeinen "DSP_ON_OFF CLOCK_CNTL\n" 2590*f7018c21STomi Valkeinen "debug atyfb: %08x %08x %08x " 2591*f7018c21STomi Valkeinen "%08x %08x %08x " 2592*f7018c21STomi Valkeinen "%08x %08x\n" 2593*f7018c21STomi Valkeinen "debug atyfb: PLL", 2594*f7018c21STomi Valkeinen aty_ld_le32(BUS_CNTL, par), 2595*f7018c21STomi Valkeinen aty_ld_le32(DAC_CNTL, par), 2596*f7018c21STomi Valkeinen aty_ld_le32(MEM_CNTL, par), 2597*f7018c21STomi Valkeinen aty_ld_le32(EXT_MEM_CNTL, par), 2598*f7018c21STomi Valkeinen aty_ld_le32(CRTC_GEN_CNTL, par), 2599*f7018c21STomi Valkeinen aty_ld_le32(DSP_CONFIG, par), 2600*f7018c21STomi Valkeinen aty_ld_le32(DSP_ON_OFF, par), 2601*f7018c21STomi Valkeinen aty_ld_le32(CLOCK_CNTL, par)); 2602*f7018c21STomi Valkeinen for (i = 0; i < 40; i++) 2603*f7018c21STomi Valkeinen printk(" %02x", aty_ld_pll_ct(i, par)); 2604*f7018c21STomi Valkeinen printk("\n"); 2605*f7018c21STomi Valkeinen } 2606*f7018c21STomi Valkeinen #endif 2607*f7018c21STomi Valkeinen if (par->pll_ops->init_pll) 2608*f7018c21STomi Valkeinen par->pll_ops->init_pll(info, &par->pll); 2609*f7018c21STomi Valkeinen if (par->pll_ops->resume_pll) 2610*f7018c21STomi Valkeinen par->pll_ops->resume_pll(info, &par->pll); 2611*f7018c21STomi Valkeinen 2612*f7018c21STomi Valkeinen /* 2613*f7018c21STomi Valkeinen * Last page of 8 MB (4 MB on ISA) aperture is MMIO, 2614*f7018c21STomi Valkeinen * unless the auxiliary register aperture is used. 2615*f7018c21STomi Valkeinen */ 2616*f7018c21STomi Valkeinen if (!par->aux_start && 2617*f7018c21STomi Valkeinen (info->fix.smem_len == 0x800000 || 2618*f7018c21STomi Valkeinen (par->bus_type == ISA && info->fix.smem_len == 0x400000))) 2619*f7018c21STomi Valkeinen info->fix.smem_len -= GUI_RESERVE; 2620*f7018c21STomi Valkeinen 2621*f7018c21STomi Valkeinen /* 2622*f7018c21STomi Valkeinen * Disable register access through the linear aperture 2623*f7018c21STomi Valkeinen * if the auxiliary aperture is used so we can access 2624*f7018c21STomi Valkeinen * the full 8 MB of video RAM on 8 MB boards. 2625*f7018c21STomi Valkeinen */ 2626*f7018c21STomi Valkeinen if (par->aux_start) 2627*f7018c21STomi Valkeinen aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | 2628*f7018c21STomi Valkeinen BUS_APER_REG_DIS, par); 2629*f7018c21STomi Valkeinen 2630*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 2631*f7018c21STomi Valkeinen par->mtrr_aper = -1; 2632*f7018c21STomi Valkeinen par->mtrr_reg = -1; 2633*f7018c21STomi Valkeinen if (!nomtrr) { 2634*f7018c21STomi Valkeinen /* Cover the whole resource. */ 2635*f7018c21STomi Valkeinen par->mtrr_aper = mtrr_add(par->res_start, par->res_size, 2636*f7018c21STomi Valkeinen MTRR_TYPE_WRCOMB, 1); 2637*f7018c21STomi Valkeinen if (par->mtrr_aper >= 0 && !par->aux_start) { 2638*f7018c21STomi Valkeinen /* Make a hole for mmio. */ 2639*f7018c21STomi Valkeinen par->mtrr_reg = mtrr_add(par->res_start + 0x800000 - 2640*f7018c21STomi Valkeinen GUI_RESERVE, GUI_RESERVE, 2641*f7018c21STomi Valkeinen MTRR_TYPE_UNCACHABLE, 1); 2642*f7018c21STomi Valkeinen if (par->mtrr_reg < 0) { 2643*f7018c21STomi Valkeinen mtrr_del(par->mtrr_aper, 0, 0); 2644*f7018c21STomi Valkeinen par->mtrr_aper = -1; 2645*f7018c21STomi Valkeinen } 2646*f7018c21STomi Valkeinen } 2647*f7018c21STomi Valkeinen } 2648*f7018c21STomi Valkeinen #endif 2649*f7018c21STomi Valkeinen 2650*f7018c21STomi Valkeinen info->fbops = &atyfb_ops; 2651*f7018c21STomi Valkeinen info->pseudo_palette = par->pseudo_palette; 2652*f7018c21STomi Valkeinen info->flags = FBINFO_DEFAULT | 2653*f7018c21STomi Valkeinen FBINFO_HWACCEL_IMAGEBLIT | 2654*f7018c21STomi Valkeinen FBINFO_HWACCEL_FILLRECT | 2655*f7018c21STomi Valkeinen FBINFO_HWACCEL_COPYAREA | 2656*f7018c21STomi Valkeinen FBINFO_HWACCEL_YPAN | 2657*f7018c21STomi Valkeinen FBINFO_READS_FAST; 2658*f7018c21STomi Valkeinen 2659*f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT 2660*f7018c21STomi Valkeinen if (M64_HAS(G3_PB_1_1) && of_machine_is_compatible("PowerBook1,1")) { 2661*f7018c21STomi Valkeinen /* 2662*f7018c21STomi Valkeinen * these bits let the 101 powerbook 2663*f7018c21STomi Valkeinen * wake up from sleep -- paulus 2664*f7018c21STomi Valkeinen */ 2665*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) | 2666*f7018c21STomi Valkeinen USE_F32KHZ | TRISTATE_MEM_EN, par); 2667*f7018c21STomi Valkeinen } else 2668*f7018c21STomi Valkeinen #endif 2669*f7018c21STomi Valkeinen if (M64_HAS(MOBIL_BUS) && backlight) { 2670*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_BACKLIGHT 2671*f7018c21STomi Valkeinen aty_bl_init(par); 2672*f7018c21STomi Valkeinen #endif 2673*f7018c21STomi Valkeinen } 2674*f7018c21STomi Valkeinen 2675*f7018c21STomi Valkeinen memset(&var, 0, sizeof(var)); 2676*f7018c21STomi Valkeinen #ifdef CONFIG_PPC 2677*f7018c21STomi Valkeinen if (machine_is(powermac)) { 2678*f7018c21STomi Valkeinen /* 2679*f7018c21STomi Valkeinen * FIXME: The NVRAM stuff should be put in a Mac-specific file, 2680*f7018c21STomi Valkeinen * as it applies to all Mac video cards 2681*f7018c21STomi Valkeinen */ 2682*f7018c21STomi Valkeinen if (mode) { 2683*f7018c21STomi Valkeinen if (mac_find_mode(&var, info, mode, 8)) 2684*f7018c21STomi Valkeinen has_var = 1; 2685*f7018c21STomi Valkeinen } else { 2686*f7018c21STomi Valkeinen if (default_vmode == VMODE_CHOOSE) { 2687*f7018c21STomi Valkeinen int sense; 2688*f7018c21STomi Valkeinen if (M64_HAS(G3_PB_1024x768)) 2689*f7018c21STomi Valkeinen /* G3 PowerBook with 1024x768 LCD */ 2690*f7018c21STomi Valkeinen default_vmode = VMODE_1024_768_60; 2691*f7018c21STomi Valkeinen else if (of_machine_is_compatible("iMac")) 2692*f7018c21STomi Valkeinen default_vmode = VMODE_1024_768_75; 2693*f7018c21STomi Valkeinen else if (of_machine_is_compatible("PowerBook2,1")) 2694*f7018c21STomi Valkeinen /* iBook with 800x600 LCD */ 2695*f7018c21STomi Valkeinen default_vmode = VMODE_800_600_60; 2696*f7018c21STomi Valkeinen else 2697*f7018c21STomi Valkeinen default_vmode = VMODE_640_480_67; 2698*f7018c21STomi Valkeinen sense = read_aty_sense(par); 2699*f7018c21STomi Valkeinen PRINTKI("monitor sense=%x, mode %d\n", 2700*f7018c21STomi Valkeinen sense, mac_map_monitor_sense(sense)); 2701*f7018c21STomi Valkeinen } 2702*f7018c21STomi Valkeinen if (default_vmode <= 0 || default_vmode > VMODE_MAX) 2703*f7018c21STomi Valkeinen default_vmode = VMODE_640_480_60; 2704*f7018c21STomi Valkeinen if (default_cmode < CMODE_8 || default_cmode > CMODE_32) 2705*f7018c21STomi Valkeinen default_cmode = CMODE_8; 2706*f7018c21STomi Valkeinen if (!mac_vmode_to_var(default_vmode, default_cmode, 2707*f7018c21STomi Valkeinen &var)) 2708*f7018c21STomi Valkeinen has_var = 1; 2709*f7018c21STomi Valkeinen } 2710*f7018c21STomi Valkeinen } 2711*f7018c21STomi Valkeinen 2712*f7018c21STomi Valkeinen #endif /* !CONFIG_PPC */ 2713*f7018c21STomi Valkeinen 2714*f7018c21STomi Valkeinen #if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) 2715*f7018c21STomi Valkeinen if (!atyfb_get_timings_from_lcd(par, &var)) 2716*f7018c21STomi Valkeinen has_var = 1; 2717*f7018c21STomi Valkeinen #endif 2718*f7018c21STomi Valkeinen 2719*f7018c21STomi Valkeinen if (mode && fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8)) 2720*f7018c21STomi Valkeinen has_var = 1; 2721*f7018c21STomi Valkeinen 2722*f7018c21STomi Valkeinen if (!has_var) 2723*f7018c21STomi Valkeinen var = default_var; 2724*f7018c21STomi Valkeinen 2725*f7018c21STomi Valkeinen if (noaccel) 2726*f7018c21STomi Valkeinen var.accel_flags &= ~FB_ACCELF_TEXT; 2727*f7018c21STomi Valkeinen else 2728*f7018c21STomi Valkeinen var.accel_flags |= FB_ACCELF_TEXT; 2729*f7018c21STomi Valkeinen 2730*f7018c21STomi Valkeinen if (comp_sync != -1) { 2731*f7018c21STomi Valkeinen if (!comp_sync) 2732*f7018c21STomi Valkeinen var.sync &= ~FB_SYNC_COMP_HIGH_ACT; 2733*f7018c21STomi Valkeinen else 2734*f7018c21STomi Valkeinen var.sync |= FB_SYNC_COMP_HIGH_ACT; 2735*f7018c21STomi Valkeinen } 2736*f7018c21STomi Valkeinen 2737*f7018c21STomi Valkeinen if (var.yres == var.yres_virtual) { 2738*f7018c21STomi Valkeinen u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); 2739*f7018c21STomi Valkeinen var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual; 2740*f7018c21STomi Valkeinen if (var.yres_virtual < var.yres) 2741*f7018c21STomi Valkeinen var.yres_virtual = var.yres; 2742*f7018c21STomi Valkeinen } 2743*f7018c21STomi Valkeinen 2744*f7018c21STomi Valkeinen ret = atyfb_check_var(&var, info); 2745*f7018c21STomi Valkeinen if (ret) { 2746*f7018c21STomi Valkeinen PRINTKE("can't set default video mode\n"); 2747*f7018c21STomi Valkeinen goto aty_init_exit; 2748*f7018c21STomi Valkeinen } 2749*f7018c21STomi Valkeinen 2750*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 2751*f7018c21STomi Valkeinen if (!noaccel && M64_HAS(INTEGRATED)) 2752*f7018c21STomi Valkeinen aty_init_cursor(info); 2753*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */ 2754*f7018c21STomi Valkeinen info->var = var; 2755*f7018c21STomi Valkeinen 2756*f7018c21STomi Valkeinen ret = fb_alloc_cmap(&info->cmap, 256, 0); 2757*f7018c21STomi Valkeinen if (ret < 0) 2758*f7018c21STomi Valkeinen goto aty_init_exit; 2759*f7018c21STomi Valkeinen 2760*f7018c21STomi Valkeinen ret = register_framebuffer(info); 2761*f7018c21STomi Valkeinen if (ret < 0) { 2762*f7018c21STomi Valkeinen fb_dealloc_cmap(&info->cmap); 2763*f7018c21STomi Valkeinen goto aty_init_exit; 2764*f7018c21STomi Valkeinen } 2765*f7018c21STomi Valkeinen 2766*f7018c21STomi Valkeinen fb_list = info; 2767*f7018c21STomi Valkeinen 2768*f7018c21STomi Valkeinen PRINTKI("fb%d: %s frame buffer device on %s\n", 2769*f7018c21STomi Valkeinen info->node, info->fix.id, par->bus_type == ISA ? "ISA" : "PCI"); 2770*f7018c21STomi Valkeinen return 0; 2771*f7018c21STomi Valkeinen 2772*f7018c21STomi Valkeinen aty_init_exit: 2773*f7018c21STomi Valkeinen /* restore video mode */ 2774*f7018c21STomi Valkeinen aty_set_crtc(par, &par->saved_crtc); 2775*f7018c21STomi Valkeinen par->pll_ops->set_pll(info, &par->saved_pll); 2776*f7018c21STomi Valkeinen 2777*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 2778*f7018c21STomi Valkeinen if (par->mtrr_reg >= 0) { 2779*f7018c21STomi Valkeinen mtrr_del(par->mtrr_reg, 0, 0); 2780*f7018c21STomi Valkeinen par->mtrr_reg = -1; 2781*f7018c21STomi Valkeinen } 2782*f7018c21STomi Valkeinen if (par->mtrr_aper >= 0) { 2783*f7018c21STomi Valkeinen mtrr_del(par->mtrr_aper, 0, 0); 2784*f7018c21STomi Valkeinen par->mtrr_aper = -1; 2785*f7018c21STomi Valkeinen } 2786*f7018c21STomi Valkeinen #endif 2787*f7018c21STomi Valkeinen return ret; 2788*f7018c21STomi Valkeinen } 2789*f7018c21STomi Valkeinen 2790*f7018c21STomi Valkeinen #if defined(CONFIG_ATARI) && !defined(MODULE) 2791*f7018c21STomi Valkeinen static int store_video_par(char *video_str, unsigned char m64_num) 2792*f7018c21STomi Valkeinen { 2793*f7018c21STomi Valkeinen char *p; 2794*f7018c21STomi Valkeinen unsigned long vmembase, size, guiregbase; 2795*f7018c21STomi Valkeinen 2796*f7018c21STomi Valkeinen PRINTKI("store_video_par() '%s' \n", video_str); 2797*f7018c21STomi Valkeinen 2798*f7018c21STomi Valkeinen if (!(p = strsep(&video_str, ";")) || !*p) 2799*f7018c21STomi Valkeinen goto mach64_invalid; 2800*f7018c21STomi Valkeinen vmembase = simple_strtoul(p, NULL, 0); 2801*f7018c21STomi Valkeinen if (!(p = strsep(&video_str, ";")) || !*p) 2802*f7018c21STomi Valkeinen goto mach64_invalid; 2803*f7018c21STomi Valkeinen size = simple_strtoul(p, NULL, 0); 2804*f7018c21STomi Valkeinen if (!(p = strsep(&video_str, ";")) || !*p) 2805*f7018c21STomi Valkeinen goto mach64_invalid; 2806*f7018c21STomi Valkeinen guiregbase = simple_strtoul(p, NULL, 0); 2807*f7018c21STomi Valkeinen 2808*f7018c21STomi Valkeinen phys_vmembase[m64_num] = vmembase; 2809*f7018c21STomi Valkeinen phys_size[m64_num] = size; 2810*f7018c21STomi Valkeinen phys_guiregbase[m64_num] = guiregbase; 2811*f7018c21STomi Valkeinen PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size, 2812*f7018c21STomi Valkeinen guiregbase); 2813*f7018c21STomi Valkeinen return 0; 2814*f7018c21STomi Valkeinen 2815*f7018c21STomi Valkeinen mach64_invalid: 2816*f7018c21STomi Valkeinen phys_vmembase[m64_num] = 0; 2817*f7018c21STomi Valkeinen return -1; 2818*f7018c21STomi Valkeinen } 2819*f7018c21STomi Valkeinen #endif /* CONFIG_ATARI && !MODULE */ 2820*f7018c21STomi Valkeinen 2821*f7018c21STomi Valkeinen /* 2822*f7018c21STomi Valkeinen * Blank the display. 2823*f7018c21STomi Valkeinen */ 2824*f7018c21STomi Valkeinen 2825*f7018c21STomi Valkeinen static int atyfb_blank(int blank, struct fb_info *info) 2826*f7018c21STomi Valkeinen { 2827*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 2828*f7018c21STomi Valkeinen u32 gen_cntl; 2829*f7018c21STomi Valkeinen 2830*f7018c21STomi Valkeinen if (par->lock_blank || par->asleep) 2831*f7018c21STomi Valkeinen return 0; 2832*f7018c21STomi Valkeinen 2833*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 2834*f7018c21STomi Valkeinen if (par->lcd_table && blank > FB_BLANK_NORMAL && 2835*f7018c21STomi Valkeinen (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { 2836*f7018c21STomi Valkeinen u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2837*f7018c21STomi Valkeinen pm &= ~PWR_BLON; 2838*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2839*f7018c21STomi Valkeinen } 2840*f7018c21STomi Valkeinen #endif 2841*f7018c21STomi Valkeinen 2842*f7018c21STomi Valkeinen gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par); 2843*f7018c21STomi Valkeinen gen_cntl &= ~0x400004c; 2844*f7018c21STomi Valkeinen switch (blank) { 2845*f7018c21STomi Valkeinen case FB_BLANK_UNBLANK: 2846*f7018c21STomi Valkeinen break; 2847*f7018c21STomi Valkeinen case FB_BLANK_NORMAL: 2848*f7018c21STomi Valkeinen gen_cntl |= 0x4000040; 2849*f7018c21STomi Valkeinen break; 2850*f7018c21STomi Valkeinen case FB_BLANK_VSYNC_SUSPEND: 2851*f7018c21STomi Valkeinen gen_cntl |= 0x4000048; 2852*f7018c21STomi Valkeinen break; 2853*f7018c21STomi Valkeinen case FB_BLANK_HSYNC_SUSPEND: 2854*f7018c21STomi Valkeinen gen_cntl |= 0x4000044; 2855*f7018c21STomi Valkeinen break; 2856*f7018c21STomi Valkeinen case FB_BLANK_POWERDOWN: 2857*f7018c21STomi Valkeinen gen_cntl |= 0x400004c; 2858*f7018c21STomi Valkeinen break; 2859*f7018c21STomi Valkeinen } 2860*f7018c21STomi Valkeinen aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par); 2861*f7018c21STomi Valkeinen 2862*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 2863*f7018c21STomi Valkeinen if (par->lcd_table && blank <= FB_BLANK_NORMAL && 2864*f7018c21STomi Valkeinen (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { 2865*f7018c21STomi Valkeinen u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par); 2866*f7018c21STomi Valkeinen pm |= PWR_BLON; 2867*f7018c21STomi Valkeinen aty_st_lcd(POWER_MANAGEMENT, pm, par); 2868*f7018c21STomi Valkeinen } 2869*f7018c21STomi Valkeinen #endif 2870*f7018c21STomi Valkeinen 2871*f7018c21STomi Valkeinen return 0; 2872*f7018c21STomi Valkeinen } 2873*f7018c21STomi Valkeinen 2874*f7018c21STomi Valkeinen static void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue, 2875*f7018c21STomi Valkeinen const struct atyfb_par *par) 2876*f7018c21STomi Valkeinen { 2877*f7018c21STomi Valkeinen aty_st_8(DAC_W_INDEX, regno, par); 2878*f7018c21STomi Valkeinen aty_st_8(DAC_DATA, red, par); 2879*f7018c21STomi Valkeinen aty_st_8(DAC_DATA, green, par); 2880*f7018c21STomi Valkeinen aty_st_8(DAC_DATA, blue, par); 2881*f7018c21STomi Valkeinen } 2882*f7018c21STomi Valkeinen 2883*f7018c21STomi Valkeinen /* 2884*f7018c21STomi Valkeinen * Set a single color register. The values supplied are already 2885*f7018c21STomi Valkeinen * rounded down to the hardware's capabilities (according to the 2886*f7018c21STomi Valkeinen * entries in the var structure). Return != 0 for invalid regno. 2887*f7018c21STomi Valkeinen * !! 4 & 8 = PSEUDO, > 8 = DIRECTCOLOR 2888*f7018c21STomi Valkeinen */ 2889*f7018c21STomi Valkeinen 2890*f7018c21STomi Valkeinen static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 2891*f7018c21STomi Valkeinen u_int transp, struct fb_info *info) 2892*f7018c21STomi Valkeinen { 2893*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 2894*f7018c21STomi Valkeinen int i, depth; 2895*f7018c21STomi Valkeinen u32 *pal = info->pseudo_palette; 2896*f7018c21STomi Valkeinen 2897*f7018c21STomi Valkeinen depth = info->var.bits_per_pixel; 2898*f7018c21STomi Valkeinen if (depth == 16) 2899*f7018c21STomi Valkeinen depth = (info->var.green.length == 5) ? 15 : 16; 2900*f7018c21STomi Valkeinen 2901*f7018c21STomi Valkeinen if (par->asleep) 2902*f7018c21STomi Valkeinen return 0; 2903*f7018c21STomi Valkeinen 2904*f7018c21STomi Valkeinen if (regno > 255 || 2905*f7018c21STomi Valkeinen (depth == 16 && regno > 63) || 2906*f7018c21STomi Valkeinen (depth == 15 && regno > 31)) 2907*f7018c21STomi Valkeinen return 1; 2908*f7018c21STomi Valkeinen 2909*f7018c21STomi Valkeinen red >>= 8; 2910*f7018c21STomi Valkeinen green >>= 8; 2911*f7018c21STomi Valkeinen blue >>= 8; 2912*f7018c21STomi Valkeinen 2913*f7018c21STomi Valkeinen par->palette[regno].red = red; 2914*f7018c21STomi Valkeinen par->palette[regno].green = green; 2915*f7018c21STomi Valkeinen par->palette[regno].blue = blue; 2916*f7018c21STomi Valkeinen 2917*f7018c21STomi Valkeinen if (regno < 16) { 2918*f7018c21STomi Valkeinen switch (depth) { 2919*f7018c21STomi Valkeinen case 15: 2920*f7018c21STomi Valkeinen pal[regno] = (regno << 10) | (regno << 5) | regno; 2921*f7018c21STomi Valkeinen break; 2922*f7018c21STomi Valkeinen case 16: 2923*f7018c21STomi Valkeinen pal[regno] = (regno << 11) | (regno << 5) | regno; 2924*f7018c21STomi Valkeinen break; 2925*f7018c21STomi Valkeinen case 24: 2926*f7018c21STomi Valkeinen pal[regno] = (regno << 16) | (regno << 8) | regno; 2927*f7018c21STomi Valkeinen break; 2928*f7018c21STomi Valkeinen case 32: 2929*f7018c21STomi Valkeinen i = (regno << 8) | regno; 2930*f7018c21STomi Valkeinen pal[regno] = (i << 16) | i; 2931*f7018c21STomi Valkeinen break; 2932*f7018c21STomi Valkeinen } 2933*f7018c21STomi Valkeinen } 2934*f7018c21STomi Valkeinen 2935*f7018c21STomi Valkeinen i = aty_ld_8(DAC_CNTL, par) & 0xfc; 2936*f7018c21STomi Valkeinen if (M64_HAS(EXTRA_BRIGHT)) 2937*f7018c21STomi Valkeinen i |= 0x2; /* DAC_CNTL | 0x2 turns off the extra brightness for gt */ 2938*f7018c21STomi Valkeinen aty_st_8(DAC_CNTL, i, par); 2939*f7018c21STomi Valkeinen aty_st_8(DAC_MASK, 0xff, par); 2940*f7018c21STomi Valkeinen 2941*f7018c21STomi Valkeinen if (M64_HAS(INTEGRATED)) { 2942*f7018c21STomi Valkeinen if (depth == 16) { 2943*f7018c21STomi Valkeinen if (regno < 32) 2944*f7018c21STomi Valkeinen aty_st_pal(regno << 3, red, 2945*f7018c21STomi Valkeinen par->palette[regno << 1].green, 2946*f7018c21STomi Valkeinen blue, par); 2947*f7018c21STomi Valkeinen red = par->palette[regno >> 1].red; 2948*f7018c21STomi Valkeinen blue = par->palette[regno >> 1].blue; 2949*f7018c21STomi Valkeinen regno <<= 2; 2950*f7018c21STomi Valkeinen } else if (depth == 15) { 2951*f7018c21STomi Valkeinen regno <<= 3; 2952*f7018c21STomi Valkeinen for (i = 0; i < 8; i++) 2953*f7018c21STomi Valkeinen aty_st_pal(regno + i, red, green, blue, par); 2954*f7018c21STomi Valkeinen } 2955*f7018c21STomi Valkeinen } 2956*f7018c21STomi Valkeinen aty_st_pal(regno, red, green, blue, par); 2957*f7018c21STomi Valkeinen 2958*f7018c21STomi Valkeinen return 0; 2959*f7018c21STomi Valkeinen } 2960*f7018c21STomi Valkeinen 2961*f7018c21STomi Valkeinen #ifdef CONFIG_PCI 2962*f7018c21STomi Valkeinen 2963*f7018c21STomi Valkeinen #ifdef __sparc__ 2964*f7018c21STomi Valkeinen 2965*f7018c21STomi Valkeinen static int atyfb_setup_sparc(struct pci_dev *pdev, struct fb_info *info, 2966*f7018c21STomi Valkeinen unsigned long addr) 2967*f7018c21STomi Valkeinen { 2968*f7018c21STomi Valkeinen struct atyfb_par *par = info->par; 2969*f7018c21STomi Valkeinen struct device_node *dp; 2970*f7018c21STomi Valkeinen u32 mem, chip_id; 2971*f7018c21STomi Valkeinen int i, j, ret; 2972*f7018c21STomi Valkeinen 2973*f7018c21STomi Valkeinen /* 2974*f7018c21STomi Valkeinen * Map memory-mapped registers. 2975*f7018c21STomi Valkeinen */ 2976*f7018c21STomi Valkeinen par->ati_regbase = (void *)addr + 0x7ffc00UL; 2977*f7018c21STomi Valkeinen info->fix.mmio_start = addr + 0x7ffc00UL; 2978*f7018c21STomi Valkeinen 2979*f7018c21STomi Valkeinen /* 2980*f7018c21STomi Valkeinen * Map in big-endian aperture. 2981*f7018c21STomi Valkeinen */ 2982*f7018c21STomi Valkeinen info->screen_base = (char *) (addr + 0x800000UL); 2983*f7018c21STomi Valkeinen info->fix.smem_start = addr + 0x800000UL; 2984*f7018c21STomi Valkeinen 2985*f7018c21STomi Valkeinen /* 2986*f7018c21STomi Valkeinen * Figure mmap addresses from PCI config space. 2987*f7018c21STomi Valkeinen * Split Framebuffer in big- and little-endian halfs. 2988*f7018c21STomi Valkeinen */ 2989*f7018c21STomi Valkeinen for (i = 0; i < 6 && pdev->resource[i].start; i++) 2990*f7018c21STomi Valkeinen /* nothing */ ; 2991*f7018c21STomi Valkeinen j = i + 4; 2992*f7018c21STomi Valkeinen 2993*f7018c21STomi Valkeinen par->mmap_map = kcalloc(j, sizeof(*par->mmap_map), GFP_ATOMIC); 2994*f7018c21STomi Valkeinen if (!par->mmap_map) { 2995*f7018c21STomi Valkeinen PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n"); 2996*f7018c21STomi Valkeinen return -ENOMEM; 2997*f7018c21STomi Valkeinen } 2998*f7018c21STomi Valkeinen 2999*f7018c21STomi Valkeinen for (i = 0, j = 2; i < 6 && pdev->resource[i].start; i++) { 3000*f7018c21STomi Valkeinen struct resource *rp = &pdev->resource[i]; 3001*f7018c21STomi Valkeinen int io, breg = PCI_BASE_ADDRESS_0 + (i << 2); 3002*f7018c21STomi Valkeinen unsigned long base; 3003*f7018c21STomi Valkeinen u32 size, pbase; 3004*f7018c21STomi Valkeinen 3005*f7018c21STomi Valkeinen base = rp->start; 3006*f7018c21STomi Valkeinen 3007*f7018c21STomi Valkeinen io = (rp->flags & IORESOURCE_IO); 3008*f7018c21STomi Valkeinen 3009*f7018c21STomi Valkeinen size = rp->end - base + 1; 3010*f7018c21STomi Valkeinen 3011*f7018c21STomi Valkeinen pci_read_config_dword(pdev, breg, &pbase); 3012*f7018c21STomi Valkeinen 3013*f7018c21STomi Valkeinen if (io) 3014*f7018c21STomi Valkeinen size &= ~1; 3015*f7018c21STomi Valkeinen 3016*f7018c21STomi Valkeinen /* 3017*f7018c21STomi Valkeinen * Map the framebuffer a second time, this time without 3018*f7018c21STomi Valkeinen * the braindead _PAGE_IE setting. This is used by the 3019*f7018c21STomi Valkeinen * fixed Xserver, but we need to maintain the old mapping 3020*f7018c21STomi Valkeinen * to stay compatible with older ones... 3021*f7018c21STomi Valkeinen */ 3022*f7018c21STomi Valkeinen if (base == addr) { 3023*f7018c21STomi Valkeinen par->mmap_map[j].voff = (pbase + 0x10000000) & PAGE_MASK; 3024*f7018c21STomi Valkeinen par->mmap_map[j].poff = base & PAGE_MASK; 3025*f7018c21STomi Valkeinen par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK; 3026*f7018c21STomi Valkeinen par->mmap_map[j].prot_mask = _PAGE_CACHE; 3027*f7018c21STomi Valkeinen par->mmap_map[j].prot_flag = _PAGE_E; 3028*f7018c21STomi Valkeinen j++; 3029*f7018c21STomi Valkeinen } 3030*f7018c21STomi Valkeinen 3031*f7018c21STomi Valkeinen /* 3032*f7018c21STomi Valkeinen * Here comes the old framebuffer mapping with _PAGE_IE 3033*f7018c21STomi Valkeinen * set for the big endian half of the framebuffer... 3034*f7018c21STomi Valkeinen */ 3035*f7018c21STomi Valkeinen if (base == addr) { 3036*f7018c21STomi Valkeinen par->mmap_map[j].voff = (pbase + 0x800000) & PAGE_MASK; 3037*f7018c21STomi Valkeinen par->mmap_map[j].poff = (base + 0x800000) & PAGE_MASK; 3038*f7018c21STomi Valkeinen par->mmap_map[j].size = 0x800000; 3039*f7018c21STomi Valkeinen par->mmap_map[j].prot_mask = _PAGE_CACHE; 3040*f7018c21STomi Valkeinen par->mmap_map[j].prot_flag = _PAGE_E | _PAGE_IE; 3041*f7018c21STomi Valkeinen size -= 0x800000; 3042*f7018c21STomi Valkeinen j++; 3043*f7018c21STomi Valkeinen } 3044*f7018c21STomi Valkeinen 3045*f7018c21STomi Valkeinen par->mmap_map[j].voff = pbase & PAGE_MASK; 3046*f7018c21STomi Valkeinen par->mmap_map[j].poff = base & PAGE_MASK; 3047*f7018c21STomi Valkeinen par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK; 3048*f7018c21STomi Valkeinen par->mmap_map[j].prot_mask = _PAGE_CACHE; 3049*f7018c21STomi Valkeinen par->mmap_map[j].prot_flag = _PAGE_E; 3050*f7018c21STomi Valkeinen j++; 3051*f7018c21STomi Valkeinen } 3052*f7018c21STomi Valkeinen 3053*f7018c21STomi Valkeinen ret = correct_chipset(par); 3054*f7018c21STomi Valkeinen if (ret) 3055*f7018c21STomi Valkeinen return ret; 3056*f7018c21STomi Valkeinen 3057*f7018c21STomi Valkeinen if (IS_XL(pdev->device)) { 3058*f7018c21STomi Valkeinen /* 3059*f7018c21STomi Valkeinen * Fix PROMs idea of MEM_CNTL settings... 3060*f7018c21STomi Valkeinen */ 3061*f7018c21STomi Valkeinen mem = aty_ld_le32(MEM_CNTL, par); 3062*f7018c21STomi Valkeinen chip_id = aty_ld_le32(CNFG_CHIP_ID, par); 3063*f7018c21STomi Valkeinen if (((chip_id & CFG_CHIP_TYPE) == VT_CHIP_ID) && !((chip_id >> 24) & 1)) { 3064*f7018c21STomi Valkeinen switch (mem & 0x0f) { 3065*f7018c21STomi Valkeinen case 3: 3066*f7018c21STomi Valkeinen mem = (mem & ~(0x0f)) | 2; 3067*f7018c21STomi Valkeinen break; 3068*f7018c21STomi Valkeinen case 7: 3069*f7018c21STomi Valkeinen mem = (mem & ~(0x0f)) | 3; 3070*f7018c21STomi Valkeinen break; 3071*f7018c21STomi Valkeinen case 9: 3072*f7018c21STomi Valkeinen mem = (mem & ~(0x0f)) | 4; 3073*f7018c21STomi Valkeinen break; 3074*f7018c21STomi Valkeinen case 11: 3075*f7018c21STomi Valkeinen mem = (mem & ~(0x0f)) | 5; 3076*f7018c21STomi Valkeinen break; 3077*f7018c21STomi Valkeinen default: 3078*f7018c21STomi Valkeinen break; 3079*f7018c21STomi Valkeinen } 3080*f7018c21STomi Valkeinen if ((aty_ld_le32(CNFG_STAT0, par) & 7) >= SDRAM) 3081*f7018c21STomi Valkeinen mem &= ~(0x00700000); 3082*f7018c21STomi Valkeinen } 3083*f7018c21STomi Valkeinen mem &= ~(0xcf80e000); /* Turn off all undocumented bits. */ 3084*f7018c21STomi Valkeinen aty_st_le32(MEM_CNTL, mem, par); 3085*f7018c21STomi Valkeinen } 3086*f7018c21STomi Valkeinen 3087*f7018c21STomi Valkeinen dp = pci_device_to_OF_node(pdev); 3088*f7018c21STomi Valkeinen if (dp == of_console_device) { 3089*f7018c21STomi Valkeinen struct fb_var_screeninfo *var = &default_var; 3090*f7018c21STomi Valkeinen unsigned int N, P, Q, M, T, R; 3091*f7018c21STomi Valkeinen u32 v_total, h_total; 3092*f7018c21STomi Valkeinen struct crtc crtc; 3093*f7018c21STomi Valkeinen u8 pll_regs[16]; 3094*f7018c21STomi Valkeinen u8 clock_cntl; 3095*f7018c21STomi Valkeinen 3096*f7018c21STomi Valkeinen crtc.vxres = of_getintprop_default(dp, "width", 1024); 3097*f7018c21STomi Valkeinen crtc.vyres = of_getintprop_default(dp, "height", 768); 3098*f7018c21STomi Valkeinen var->bits_per_pixel = of_getintprop_default(dp, "depth", 8); 3099*f7018c21STomi Valkeinen var->xoffset = var->yoffset = 0; 3100*f7018c21STomi Valkeinen crtc.h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par); 3101*f7018c21STomi Valkeinen crtc.h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par); 3102*f7018c21STomi Valkeinen crtc.v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par); 3103*f7018c21STomi Valkeinen crtc.v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par); 3104*f7018c21STomi Valkeinen crtc.gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par); 3105*f7018c21STomi Valkeinen aty_crtc_to_var(&crtc, var); 3106*f7018c21STomi Valkeinen 3107*f7018c21STomi Valkeinen h_total = var->xres + var->right_margin + var->hsync_len + var->left_margin; 3108*f7018c21STomi Valkeinen v_total = var->yres + var->lower_margin + var->vsync_len + var->upper_margin; 3109*f7018c21STomi Valkeinen 3110*f7018c21STomi Valkeinen /* 3111*f7018c21STomi Valkeinen * Read the PLL to figure actual Refresh Rate. 3112*f7018c21STomi Valkeinen */ 3113*f7018c21STomi Valkeinen clock_cntl = aty_ld_8(CLOCK_CNTL, par); 3114*f7018c21STomi Valkeinen /* DPRINTK("CLOCK_CNTL %02x\n", clock_cntl); */ 3115*f7018c21STomi Valkeinen for (i = 0; i < 16; i++) 3116*f7018c21STomi Valkeinen pll_regs[i] = aty_ld_pll_ct(i, par); 3117*f7018c21STomi Valkeinen 3118*f7018c21STomi Valkeinen /* 3119*f7018c21STomi Valkeinen * PLL Reference Divider M: 3120*f7018c21STomi Valkeinen */ 3121*f7018c21STomi Valkeinen M = pll_regs[2]; 3122*f7018c21STomi Valkeinen 3123*f7018c21STomi Valkeinen /* 3124*f7018c21STomi Valkeinen * PLL Feedback Divider N (Dependent on CLOCK_CNTL): 3125*f7018c21STomi Valkeinen */ 3126*f7018c21STomi Valkeinen N = pll_regs[7 + (clock_cntl & 3)]; 3127*f7018c21STomi Valkeinen 3128*f7018c21STomi Valkeinen /* 3129*f7018c21STomi Valkeinen * PLL Post Divider P (Dependent on CLOCK_CNTL): 3130*f7018c21STomi Valkeinen */ 3131*f7018c21STomi Valkeinen P = 1 << (pll_regs[6] >> ((clock_cntl & 3) << 1)); 3132*f7018c21STomi Valkeinen 3133*f7018c21STomi Valkeinen /* 3134*f7018c21STomi Valkeinen * PLL Divider Q: 3135*f7018c21STomi Valkeinen */ 3136*f7018c21STomi Valkeinen Q = N / P; 3137*f7018c21STomi Valkeinen 3138*f7018c21STomi Valkeinen /* 3139*f7018c21STomi Valkeinen * Target Frequency: 3140*f7018c21STomi Valkeinen * 3141*f7018c21STomi Valkeinen * T * M 3142*f7018c21STomi Valkeinen * Q = ------- 3143*f7018c21STomi Valkeinen * 2 * R 3144*f7018c21STomi Valkeinen * 3145*f7018c21STomi Valkeinen * where R is XTALIN (= 14318 or 29498 kHz). 3146*f7018c21STomi Valkeinen */ 3147*f7018c21STomi Valkeinen if (IS_XL(pdev->device)) 3148*f7018c21STomi Valkeinen R = 29498; 3149*f7018c21STomi Valkeinen else 3150*f7018c21STomi Valkeinen R = 14318; 3151*f7018c21STomi Valkeinen 3152*f7018c21STomi Valkeinen T = 2 * Q * R / M; 3153*f7018c21STomi Valkeinen 3154*f7018c21STomi Valkeinen default_var.pixclock = 1000000000 / T; 3155*f7018c21STomi Valkeinen } 3156*f7018c21STomi Valkeinen 3157*f7018c21STomi Valkeinen return 0; 3158*f7018c21STomi Valkeinen } 3159*f7018c21STomi Valkeinen 3160*f7018c21STomi Valkeinen #else /* __sparc__ */ 3161*f7018c21STomi Valkeinen 3162*f7018c21STomi Valkeinen #ifdef __i386__ 3163*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 3164*f7018c21STomi Valkeinen static void aty_init_lcd(struct atyfb_par *par, u32 bios_base) 3165*f7018c21STomi Valkeinen { 3166*f7018c21STomi Valkeinen u32 driv_inf_tab, sig; 3167*f7018c21STomi Valkeinen u16 lcd_ofs; 3168*f7018c21STomi Valkeinen 3169*f7018c21STomi Valkeinen /* 3170*f7018c21STomi Valkeinen * To support an LCD panel, we should know it's dimensions and 3171*f7018c21STomi Valkeinen * it's desired pixel clock. 3172*f7018c21STomi Valkeinen * There are two ways to do it: 3173*f7018c21STomi Valkeinen * - Check the startup video mode and calculate the panel 3174*f7018c21STomi Valkeinen * size from it. This is unreliable. 3175*f7018c21STomi Valkeinen * - Read it from the driver information table in the video BIOS. 3176*f7018c21STomi Valkeinen */ 3177*f7018c21STomi Valkeinen /* Address of driver information table is at offset 0x78. */ 3178*f7018c21STomi Valkeinen driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78)); 3179*f7018c21STomi Valkeinen 3180*f7018c21STomi Valkeinen /* Check for the driver information table signature. */ 3181*f7018c21STomi Valkeinen sig = *(u32 *)driv_inf_tab; 3182*f7018c21STomi Valkeinen if ((sig == 0x54504c24) || /* Rage LT pro */ 3183*f7018c21STomi Valkeinen (sig == 0x544d5224) || /* Rage mobility */ 3184*f7018c21STomi Valkeinen (sig == 0x54435824) || /* Rage XC */ 3185*f7018c21STomi Valkeinen (sig == 0x544c5824)) { /* Rage XL */ 3186*f7018c21STomi Valkeinen PRINTKI("BIOS contains driver information table.\n"); 3187*f7018c21STomi Valkeinen lcd_ofs = *(u16 *)(driv_inf_tab + 10); 3188*f7018c21STomi Valkeinen par->lcd_table = 0; 3189*f7018c21STomi Valkeinen if (lcd_ofs != 0) 3190*f7018c21STomi Valkeinen par->lcd_table = bios_base + lcd_ofs; 3191*f7018c21STomi Valkeinen } 3192*f7018c21STomi Valkeinen 3193*f7018c21STomi Valkeinen if (par->lcd_table != 0) { 3194*f7018c21STomi Valkeinen char model[24]; 3195*f7018c21STomi Valkeinen char strbuf[16]; 3196*f7018c21STomi Valkeinen char refresh_rates_buf[100]; 3197*f7018c21STomi Valkeinen int id, tech, f, i, m, default_refresh_rate; 3198*f7018c21STomi Valkeinen char *txtcolour; 3199*f7018c21STomi Valkeinen char *txtmonitor; 3200*f7018c21STomi Valkeinen char *txtdual; 3201*f7018c21STomi Valkeinen char *txtformat; 3202*f7018c21STomi Valkeinen u16 width, height, panel_type, refresh_rates; 3203*f7018c21STomi Valkeinen u16 *lcdmodeptr; 3204*f7018c21STomi Valkeinen u32 format; 3205*f7018c21STomi Valkeinen u8 lcd_refresh_rates[16] = { 50, 56, 60, 67, 70, 72, 75, 76, 85, 3206*f7018c21STomi Valkeinen 90, 100, 120, 140, 150, 160, 200 }; 3207*f7018c21STomi Valkeinen /* 3208*f7018c21STomi Valkeinen * The most important information is the panel size at 3209*f7018c21STomi Valkeinen * offset 25 and 27, but there's some other nice information 3210*f7018c21STomi Valkeinen * which we print to the screen. 3211*f7018c21STomi Valkeinen */ 3212*f7018c21STomi Valkeinen id = *(u8 *)par->lcd_table; 3213*f7018c21STomi Valkeinen strncpy(model, (char *)par->lcd_table+1, 24); 3214*f7018c21STomi Valkeinen model[23] = 0; 3215*f7018c21STomi Valkeinen 3216*f7018c21STomi Valkeinen width = par->lcd_width = *(u16 *)(par->lcd_table+25); 3217*f7018c21STomi Valkeinen height = par->lcd_height = *(u16 *)(par->lcd_table+27); 3218*f7018c21STomi Valkeinen panel_type = *(u16 *)(par->lcd_table+29); 3219*f7018c21STomi Valkeinen if (panel_type & 1) 3220*f7018c21STomi Valkeinen txtcolour = "colour"; 3221*f7018c21STomi Valkeinen else 3222*f7018c21STomi Valkeinen txtcolour = "monochrome"; 3223*f7018c21STomi Valkeinen if (panel_type & 2) 3224*f7018c21STomi Valkeinen txtdual = "dual (split) "; 3225*f7018c21STomi Valkeinen else 3226*f7018c21STomi Valkeinen txtdual = ""; 3227*f7018c21STomi Valkeinen tech = (panel_type >> 2) & 63; 3228*f7018c21STomi Valkeinen switch (tech) { 3229*f7018c21STomi Valkeinen case 0: 3230*f7018c21STomi Valkeinen txtmonitor = "passive matrix"; 3231*f7018c21STomi Valkeinen break; 3232*f7018c21STomi Valkeinen case 1: 3233*f7018c21STomi Valkeinen txtmonitor = "active matrix"; 3234*f7018c21STomi Valkeinen break; 3235*f7018c21STomi Valkeinen case 2: 3236*f7018c21STomi Valkeinen txtmonitor = "active addressed STN"; 3237*f7018c21STomi Valkeinen break; 3238*f7018c21STomi Valkeinen case 3: 3239*f7018c21STomi Valkeinen txtmonitor = "EL"; 3240*f7018c21STomi Valkeinen break; 3241*f7018c21STomi Valkeinen case 4: 3242*f7018c21STomi Valkeinen txtmonitor = "plasma"; 3243*f7018c21STomi Valkeinen break; 3244*f7018c21STomi Valkeinen default: 3245*f7018c21STomi Valkeinen txtmonitor = "unknown"; 3246*f7018c21STomi Valkeinen } 3247*f7018c21STomi Valkeinen format = *(u32 *)(par->lcd_table+57); 3248*f7018c21STomi Valkeinen if (tech == 0 || tech == 2) { 3249*f7018c21STomi Valkeinen switch (format & 7) { 3250*f7018c21STomi Valkeinen case 0: 3251*f7018c21STomi Valkeinen txtformat = "12 bit interface"; 3252*f7018c21STomi Valkeinen break; 3253*f7018c21STomi Valkeinen case 1: 3254*f7018c21STomi Valkeinen txtformat = "16 bit interface"; 3255*f7018c21STomi Valkeinen break; 3256*f7018c21STomi Valkeinen case 2: 3257*f7018c21STomi Valkeinen txtformat = "24 bit interface"; 3258*f7018c21STomi Valkeinen break; 3259*f7018c21STomi Valkeinen default: 3260*f7018c21STomi Valkeinen txtformat = "unknown format"; 3261*f7018c21STomi Valkeinen } 3262*f7018c21STomi Valkeinen } else { 3263*f7018c21STomi Valkeinen switch (format & 7) { 3264*f7018c21STomi Valkeinen case 0: 3265*f7018c21STomi Valkeinen txtformat = "8 colours"; 3266*f7018c21STomi Valkeinen break; 3267*f7018c21STomi Valkeinen case 1: 3268*f7018c21STomi Valkeinen txtformat = "512 colours"; 3269*f7018c21STomi Valkeinen break; 3270*f7018c21STomi Valkeinen case 2: 3271*f7018c21STomi Valkeinen txtformat = "4096 colours"; 3272*f7018c21STomi Valkeinen break; 3273*f7018c21STomi Valkeinen case 4: 3274*f7018c21STomi Valkeinen txtformat = "262144 colours (LT mode)"; 3275*f7018c21STomi Valkeinen break; 3276*f7018c21STomi Valkeinen case 5: 3277*f7018c21STomi Valkeinen txtformat = "16777216 colours"; 3278*f7018c21STomi Valkeinen break; 3279*f7018c21STomi Valkeinen case 6: 3280*f7018c21STomi Valkeinen txtformat = "262144 colours (FDPI-2 mode)"; 3281*f7018c21STomi Valkeinen break; 3282*f7018c21STomi Valkeinen default: 3283*f7018c21STomi Valkeinen txtformat = "unknown format"; 3284*f7018c21STomi Valkeinen } 3285*f7018c21STomi Valkeinen } 3286*f7018c21STomi Valkeinen PRINTKI("%s%s %s monitor detected: %s\n", 3287*f7018c21STomi Valkeinen txtdual, txtcolour, txtmonitor, model); 3288*f7018c21STomi Valkeinen PRINTKI(" id=%d, %dx%d pixels, %s\n", 3289*f7018c21STomi Valkeinen id, width, height, txtformat); 3290*f7018c21STomi Valkeinen refresh_rates_buf[0] = 0; 3291*f7018c21STomi Valkeinen refresh_rates = *(u16 *)(par->lcd_table+62); 3292*f7018c21STomi Valkeinen m = 1; 3293*f7018c21STomi Valkeinen f = 0; 3294*f7018c21STomi Valkeinen for (i = 0; i < 16; i++) { 3295*f7018c21STomi Valkeinen if (refresh_rates & m) { 3296*f7018c21STomi Valkeinen if (f == 0) { 3297*f7018c21STomi Valkeinen sprintf(strbuf, "%d", 3298*f7018c21STomi Valkeinen lcd_refresh_rates[i]); 3299*f7018c21STomi Valkeinen f++; 3300*f7018c21STomi Valkeinen } else { 3301*f7018c21STomi Valkeinen sprintf(strbuf, ",%d", 3302*f7018c21STomi Valkeinen lcd_refresh_rates[i]); 3303*f7018c21STomi Valkeinen } 3304*f7018c21STomi Valkeinen strcat(refresh_rates_buf, strbuf); 3305*f7018c21STomi Valkeinen } 3306*f7018c21STomi Valkeinen m = m << 1; 3307*f7018c21STomi Valkeinen } 3308*f7018c21STomi Valkeinen default_refresh_rate = (*(u8 *)(par->lcd_table+61) & 0xf0) >> 4; 3309*f7018c21STomi Valkeinen PRINTKI(" supports refresh rates [%s], default %d Hz\n", 3310*f7018c21STomi Valkeinen refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]); 3311*f7018c21STomi Valkeinen par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate]; 3312*f7018c21STomi Valkeinen /* 3313*f7018c21STomi Valkeinen * We now need to determine the crtc parameters for the 3314*f7018c21STomi Valkeinen * LCD monitor. This is tricky, because they are not stored 3315*f7018c21STomi Valkeinen * individually in the BIOS. Instead, the BIOS contains a 3316*f7018c21STomi Valkeinen * table of display modes that work for this monitor. 3317*f7018c21STomi Valkeinen * 3318*f7018c21STomi Valkeinen * The idea is that we search for a mode of the same dimensions 3319*f7018c21STomi Valkeinen * as the dimensions of the LCD monitor. Say our LCD monitor 3320*f7018c21STomi Valkeinen * is 800x600 pixels, we search for a 800x600 monitor. 3321*f7018c21STomi Valkeinen * The CRTC parameters we find here are the ones that we need 3322*f7018c21STomi Valkeinen * to use to simulate other resolutions on the LCD screen. 3323*f7018c21STomi Valkeinen */ 3324*f7018c21STomi Valkeinen lcdmodeptr = (u16 *)(par->lcd_table + 64); 3325*f7018c21STomi Valkeinen while (*lcdmodeptr != 0) { 3326*f7018c21STomi Valkeinen u32 modeptr; 3327*f7018c21STomi Valkeinen u16 mwidth, mheight, lcd_hsync_start, lcd_vsync_start; 3328*f7018c21STomi Valkeinen modeptr = bios_base + *lcdmodeptr; 3329*f7018c21STomi Valkeinen 3330*f7018c21STomi Valkeinen mwidth = *((u16 *)(modeptr+0)); 3331*f7018c21STomi Valkeinen mheight = *((u16 *)(modeptr+2)); 3332*f7018c21STomi Valkeinen 3333*f7018c21STomi Valkeinen if (mwidth == width && mheight == height) { 3334*f7018c21STomi Valkeinen par->lcd_pixclock = 100000000 / *((u16 *)(modeptr+9)); 3335*f7018c21STomi Valkeinen par->lcd_htotal = *((u16 *)(modeptr+17)) & 511; 3336*f7018c21STomi Valkeinen par->lcd_hdisp = *((u16 *)(modeptr+19)) & 511; 3337*f7018c21STomi Valkeinen lcd_hsync_start = *((u16 *)(modeptr+21)) & 511; 3338*f7018c21STomi Valkeinen par->lcd_hsync_dly = (*((u16 *)(modeptr+21)) >> 9) & 7; 3339*f7018c21STomi Valkeinen par->lcd_hsync_len = *((u8 *)(modeptr+23)) & 63; 3340*f7018c21STomi Valkeinen 3341*f7018c21STomi Valkeinen par->lcd_vtotal = *((u16 *)(modeptr+24)) & 2047; 3342*f7018c21STomi Valkeinen par->lcd_vdisp = *((u16 *)(modeptr+26)) & 2047; 3343*f7018c21STomi Valkeinen lcd_vsync_start = *((u16 *)(modeptr+28)) & 2047; 3344*f7018c21STomi Valkeinen par->lcd_vsync_len = (*((u16 *)(modeptr+28)) >> 11) & 31; 3345*f7018c21STomi Valkeinen 3346*f7018c21STomi Valkeinen par->lcd_htotal = (par->lcd_htotal + 1) * 8; 3347*f7018c21STomi Valkeinen par->lcd_hdisp = (par->lcd_hdisp + 1) * 8; 3348*f7018c21STomi Valkeinen lcd_hsync_start = (lcd_hsync_start + 1) * 8; 3349*f7018c21STomi Valkeinen par->lcd_hsync_len = par->lcd_hsync_len * 8; 3350*f7018c21STomi Valkeinen 3351*f7018c21STomi Valkeinen par->lcd_vtotal++; 3352*f7018c21STomi Valkeinen par->lcd_vdisp++; 3353*f7018c21STomi Valkeinen lcd_vsync_start++; 3354*f7018c21STomi Valkeinen 3355*f7018c21STomi Valkeinen par->lcd_right_margin = lcd_hsync_start - par->lcd_hdisp; 3356*f7018c21STomi Valkeinen par->lcd_lower_margin = lcd_vsync_start - par->lcd_vdisp; 3357*f7018c21STomi Valkeinen par->lcd_hblank_len = par->lcd_htotal - par->lcd_hdisp; 3358*f7018c21STomi Valkeinen par->lcd_vblank_len = par->lcd_vtotal - par->lcd_vdisp; 3359*f7018c21STomi Valkeinen break; 3360*f7018c21STomi Valkeinen } 3361*f7018c21STomi Valkeinen 3362*f7018c21STomi Valkeinen lcdmodeptr++; 3363*f7018c21STomi Valkeinen } 3364*f7018c21STomi Valkeinen if (*lcdmodeptr == 0) { 3365*f7018c21STomi Valkeinen PRINTKE("LCD monitor CRTC parameters not found!!!\n"); 3366*f7018c21STomi Valkeinen /* To do: Switch to CRT if possible. */ 3367*f7018c21STomi Valkeinen } else { 3368*f7018c21STomi Valkeinen PRINTKI(" LCD CRTC parameters: %d.%d %d %d %d %d %d %d %d %d\n", 3369*f7018c21STomi Valkeinen 1000000 / par->lcd_pixclock, 1000000 % par->lcd_pixclock, 3370*f7018c21STomi Valkeinen par->lcd_hdisp, 3371*f7018c21STomi Valkeinen par->lcd_hdisp + par->lcd_right_margin, 3372*f7018c21STomi Valkeinen par->lcd_hdisp + par->lcd_right_margin 3373*f7018c21STomi Valkeinen + par->lcd_hsync_dly + par->lcd_hsync_len, 3374*f7018c21STomi Valkeinen par->lcd_htotal, 3375*f7018c21STomi Valkeinen par->lcd_vdisp, 3376*f7018c21STomi Valkeinen par->lcd_vdisp + par->lcd_lower_margin, 3377*f7018c21STomi Valkeinen par->lcd_vdisp + par->lcd_lower_margin + par->lcd_vsync_len, 3378*f7018c21STomi Valkeinen par->lcd_vtotal); 3379*f7018c21STomi Valkeinen PRINTKI(" : %d %d %d %d %d %d %d %d %d\n", 3380*f7018c21STomi Valkeinen par->lcd_pixclock, 3381*f7018c21STomi Valkeinen par->lcd_hblank_len - (par->lcd_right_margin + 3382*f7018c21STomi Valkeinen par->lcd_hsync_dly + par->lcd_hsync_len), 3383*f7018c21STomi Valkeinen par->lcd_hdisp, 3384*f7018c21STomi Valkeinen par->lcd_right_margin, 3385*f7018c21STomi Valkeinen par->lcd_hsync_len, 3386*f7018c21STomi Valkeinen par->lcd_vblank_len - (par->lcd_lower_margin + par->lcd_vsync_len), 3387*f7018c21STomi Valkeinen par->lcd_vdisp, 3388*f7018c21STomi Valkeinen par->lcd_lower_margin, 3389*f7018c21STomi Valkeinen par->lcd_vsync_len); 3390*f7018c21STomi Valkeinen } 3391*f7018c21STomi Valkeinen } 3392*f7018c21STomi Valkeinen } 3393*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */ 3394*f7018c21STomi Valkeinen 3395*f7018c21STomi Valkeinen static int init_from_bios(struct atyfb_par *par) 3396*f7018c21STomi Valkeinen { 3397*f7018c21STomi Valkeinen u32 bios_base, rom_addr; 3398*f7018c21STomi Valkeinen int ret; 3399*f7018c21STomi Valkeinen 3400*f7018c21STomi Valkeinen rom_addr = 0xc0000 + ((aty_ld_le32(SCRATCH_REG1, par) & 0x7f) << 11); 3401*f7018c21STomi Valkeinen bios_base = (unsigned long)ioremap(rom_addr, 0x10000); 3402*f7018c21STomi Valkeinen 3403*f7018c21STomi Valkeinen /* The BIOS starts with 0xaa55. */ 3404*f7018c21STomi Valkeinen if (*((u16 *)bios_base) == 0xaa55) { 3405*f7018c21STomi Valkeinen 3406*f7018c21STomi Valkeinen u8 *bios_ptr; 3407*f7018c21STomi Valkeinen u16 rom_table_offset, freq_table_offset; 3408*f7018c21STomi Valkeinen PLL_BLOCK_MACH64 pll_block; 3409*f7018c21STomi Valkeinen 3410*f7018c21STomi Valkeinen PRINTKI("Mach64 BIOS is located at %x, mapped at %x.\n", rom_addr, bios_base); 3411*f7018c21STomi Valkeinen 3412*f7018c21STomi Valkeinen /* check for frequncy table */ 3413*f7018c21STomi Valkeinen bios_ptr = (u8*)bios_base; 3414*f7018c21STomi Valkeinen rom_table_offset = (u16)(bios_ptr[0x48] | (bios_ptr[0x49] << 8)); 3415*f7018c21STomi Valkeinen freq_table_offset = bios_ptr[rom_table_offset + 16] | (bios_ptr[rom_table_offset + 17] << 8); 3416*f7018c21STomi Valkeinen memcpy(&pll_block, bios_ptr + freq_table_offset, sizeof(PLL_BLOCK_MACH64)); 3417*f7018c21STomi Valkeinen 3418*f7018c21STomi Valkeinen PRINTKI("BIOS frequency table:\n"); 3419*f7018c21STomi Valkeinen PRINTKI("PCLK_min_freq %d, PCLK_max_freq %d, ref_freq %d, ref_divider %d\n", 3420*f7018c21STomi Valkeinen pll_block.PCLK_min_freq, pll_block.PCLK_max_freq, 3421*f7018c21STomi Valkeinen pll_block.ref_freq, pll_block.ref_divider); 3422*f7018c21STomi Valkeinen PRINTKI("MCLK_pwd %d, MCLK_max_freq %d, XCLK_max_freq %d, SCLK_freq %d\n", 3423*f7018c21STomi Valkeinen pll_block.MCLK_pwd, pll_block.MCLK_max_freq, 3424*f7018c21STomi Valkeinen pll_block.XCLK_max_freq, pll_block.SCLK_freq); 3425*f7018c21STomi Valkeinen 3426*f7018c21STomi Valkeinen par->pll_limits.pll_min = pll_block.PCLK_min_freq/100; 3427*f7018c21STomi Valkeinen par->pll_limits.pll_max = pll_block.PCLK_max_freq/100; 3428*f7018c21STomi Valkeinen par->pll_limits.ref_clk = pll_block.ref_freq/100; 3429*f7018c21STomi Valkeinen par->pll_limits.ref_div = pll_block.ref_divider; 3430*f7018c21STomi Valkeinen par->pll_limits.sclk = pll_block.SCLK_freq/100; 3431*f7018c21STomi Valkeinen par->pll_limits.mclk = pll_block.MCLK_max_freq/100; 3432*f7018c21STomi Valkeinen par->pll_limits.mclk_pm = pll_block.MCLK_pwd/100; 3433*f7018c21STomi Valkeinen par->pll_limits.xclk = pll_block.XCLK_max_freq/100; 3434*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD 3435*f7018c21STomi Valkeinen aty_init_lcd(par, bios_base); 3436*f7018c21STomi Valkeinen #endif 3437*f7018c21STomi Valkeinen ret = 0; 3438*f7018c21STomi Valkeinen } else { 3439*f7018c21STomi Valkeinen PRINTKE("no BIOS frequency table found, use parameters\n"); 3440*f7018c21STomi Valkeinen ret = -ENXIO; 3441*f7018c21STomi Valkeinen } 3442*f7018c21STomi Valkeinen iounmap((void __iomem *)bios_base); 3443*f7018c21STomi Valkeinen 3444*f7018c21STomi Valkeinen return ret; 3445*f7018c21STomi Valkeinen } 3446*f7018c21STomi Valkeinen #endif /* __i386__ */ 3447*f7018c21STomi Valkeinen 3448*f7018c21STomi Valkeinen static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, 3449*f7018c21STomi Valkeinen unsigned long addr) 3450*f7018c21STomi Valkeinen { 3451*f7018c21STomi Valkeinen struct atyfb_par *par = info->par; 3452*f7018c21STomi Valkeinen u16 tmp; 3453*f7018c21STomi Valkeinen unsigned long raddr; 3454*f7018c21STomi Valkeinen struct resource *rrp; 3455*f7018c21STomi Valkeinen int ret = 0; 3456*f7018c21STomi Valkeinen 3457*f7018c21STomi Valkeinen raddr = addr + 0x7ff000UL; 3458*f7018c21STomi Valkeinen rrp = &pdev->resource[2]; 3459*f7018c21STomi Valkeinen if ((rrp->flags & IORESOURCE_MEM) && 3460*f7018c21STomi Valkeinen request_mem_region(rrp->start, resource_size(rrp), "atyfb")) { 3461*f7018c21STomi Valkeinen par->aux_start = rrp->start; 3462*f7018c21STomi Valkeinen par->aux_size = resource_size(rrp); 3463*f7018c21STomi Valkeinen raddr = rrp->start; 3464*f7018c21STomi Valkeinen PRINTKI("using auxiliary register aperture\n"); 3465*f7018c21STomi Valkeinen } 3466*f7018c21STomi Valkeinen 3467*f7018c21STomi Valkeinen info->fix.mmio_start = raddr; 3468*f7018c21STomi Valkeinen par->ati_regbase = ioremap(info->fix.mmio_start, 0x1000); 3469*f7018c21STomi Valkeinen if (par->ati_regbase == NULL) 3470*f7018c21STomi Valkeinen return -ENOMEM; 3471*f7018c21STomi Valkeinen 3472*f7018c21STomi Valkeinen info->fix.mmio_start += par->aux_start ? 0x400 : 0xc00; 3473*f7018c21STomi Valkeinen par->ati_regbase += par->aux_start ? 0x400 : 0xc00; 3474*f7018c21STomi Valkeinen 3475*f7018c21STomi Valkeinen /* 3476*f7018c21STomi Valkeinen * Enable memory-space accesses using config-space 3477*f7018c21STomi Valkeinen * command register. 3478*f7018c21STomi Valkeinen */ 3479*f7018c21STomi Valkeinen pci_read_config_word(pdev, PCI_COMMAND, &tmp); 3480*f7018c21STomi Valkeinen if (!(tmp & PCI_COMMAND_MEMORY)) { 3481*f7018c21STomi Valkeinen tmp |= PCI_COMMAND_MEMORY; 3482*f7018c21STomi Valkeinen pci_write_config_word(pdev, PCI_COMMAND, tmp); 3483*f7018c21STomi Valkeinen } 3484*f7018c21STomi Valkeinen #ifdef __BIG_ENDIAN 3485*f7018c21STomi Valkeinen /* Use the big-endian aperture */ 3486*f7018c21STomi Valkeinen addr += 0x800000; 3487*f7018c21STomi Valkeinen #endif 3488*f7018c21STomi Valkeinen 3489*f7018c21STomi Valkeinen /* Map in frame buffer */ 3490*f7018c21STomi Valkeinen info->fix.smem_start = addr; 3491*f7018c21STomi Valkeinen info->screen_base = ioremap(addr, 0x800000); 3492*f7018c21STomi Valkeinen if (info->screen_base == NULL) { 3493*f7018c21STomi Valkeinen ret = -ENOMEM; 3494*f7018c21STomi Valkeinen goto atyfb_setup_generic_fail; 3495*f7018c21STomi Valkeinen } 3496*f7018c21STomi Valkeinen 3497*f7018c21STomi Valkeinen ret = correct_chipset(par); 3498*f7018c21STomi Valkeinen if (ret) 3499*f7018c21STomi Valkeinen goto atyfb_setup_generic_fail; 3500*f7018c21STomi Valkeinen #ifdef __i386__ 3501*f7018c21STomi Valkeinen ret = init_from_bios(par); 3502*f7018c21STomi Valkeinen if (ret) 3503*f7018c21STomi Valkeinen goto atyfb_setup_generic_fail; 3504*f7018c21STomi Valkeinen #endif 3505*f7018c21STomi Valkeinen if (!(aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_EXT_DISP_EN)) 3506*f7018c21STomi Valkeinen par->clk_wr_offset = (inb(R_GENMO) & 0x0CU) >> 2; 3507*f7018c21STomi Valkeinen else 3508*f7018c21STomi Valkeinen par->clk_wr_offset = aty_ld_8(CLOCK_CNTL, par) & 0x03U; 3509*f7018c21STomi Valkeinen 3510*f7018c21STomi Valkeinen /* according to ATI, we should use clock 3 for acelerated mode */ 3511*f7018c21STomi Valkeinen par->clk_wr_offset = 3; 3512*f7018c21STomi Valkeinen 3513*f7018c21STomi Valkeinen return 0; 3514*f7018c21STomi Valkeinen 3515*f7018c21STomi Valkeinen atyfb_setup_generic_fail: 3516*f7018c21STomi Valkeinen iounmap(par->ati_regbase); 3517*f7018c21STomi Valkeinen par->ati_regbase = NULL; 3518*f7018c21STomi Valkeinen if (info->screen_base) { 3519*f7018c21STomi Valkeinen iounmap(info->screen_base); 3520*f7018c21STomi Valkeinen info->screen_base = NULL; 3521*f7018c21STomi Valkeinen } 3522*f7018c21STomi Valkeinen return ret; 3523*f7018c21STomi Valkeinen } 3524*f7018c21STomi Valkeinen 3525*f7018c21STomi Valkeinen #endif /* !__sparc__ */ 3526*f7018c21STomi Valkeinen 3527*f7018c21STomi Valkeinen static int atyfb_pci_probe(struct pci_dev *pdev, 3528*f7018c21STomi Valkeinen const struct pci_device_id *ent) 3529*f7018c21STomi Valkeinen { 3530*f7018c21STomi Valkeinen unsigned long addr, res_start, res_size; 3531*f7018c21STomi Valkeinen struct fb_info *info; 3532*f7018c21STomi Valkeinen struct resource *rp; 3533*f7018c21STomi Valkeinen struct atyfb_par *par; 3534*f7018c21STomi Valkeinen int rc = -ENOMEM; 3535*f7018c21STomi Valkeinen 3536*f7018c21STomi Valkeinen /* Enable device in PCI config */ 3537*f7018c21STomi Valkeinen if (pci_enable_device(pdev)) { 3538*f7018c21STomi Valkeinen PRINTKE("Cannot enable PCI device\n"); 3539*f7018c21STomi Valkeinen return -ENXIO; 3540*f7018c21STomi Valkeinen } 3541*f7018c21STomi Valkeinen 3542*f7018c21STomi Valkeinen /* Find which resource to use */ 3543*f7018c21STomi Valkeinen rp = &pdev->resource[0]; 3544*f7018c21STomi Valkeinen if (rp->flags & IORESOURCE_IO) 3545*f7018c21STomi Valkeinen rp = &pdev->resource[1]; 3546*f7018c21STomi Valkeinen addr = rp->start; 3547*f7018c21STomi Valkeinen if (!addr) 3548*f7018c21STomi Valkeinen return -ENXIO; 3549*f7018c21STomi Valkeinen 3550*f7018c21STomi Valkeinen /* Reserve space */ 3551*f7018c21STomi Valkeinen res_start = rp->start; 3552*f7018c21STomi Valkeinen res_size = resource_size(rp); 3553*f7018c21STomi Valkeinen if (!request_mem_region(res_start, res_size, "atyfb")) 3554*f7018c21STomi Valkeinen return -EBUSY; 3555*f7018c21STomi Valkeinen 3556*f7018c21STomi Valkeinen /* Allocate framebuffer */ 3557*f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev); 3558*f7018c21STomi Valkeinen if (!info) { 3559*f7018c21STomi Valkeinen PRINTKE("atyfb_pci_probe() can't alloc fb_info\n"); 3560*f7018c21STomi Valkeinen return -ENOMEM; 3561*f7018c21STomi Valkeinen } 3562*f7018c21STomi Valkeinen par = info->par; 3563*f7018c21STomi Valkeinen info->fix = atyfb_fix; 3564*f7018c21STomi Valkeinen info->device = &pdev->dev; 3565*f7018c21STomi Valkeinen par->pci_id = pdev->device; 3566*f7018c21STomi Valkeinen par->res_start = res_start; 3567*f7018c21STomi Valkeinen par->res_size = res_size; 3568*f7018c21STomi Valkeinen par->irq = pdev->irq; 3569*f7018c21STomi Valkeinen par->pdev = pdev; 3570*f7018c21STomi Valkeinen 3571*f7018c21STomi Valkeinen /* Setup "info" structure */ 3572*f7018c21STomi Valkeinen #ifdef __sparc__ 3573*f7018c21STomi Valkeinen rc = atyfb_setup_sparc(pdev, info, addr); 3574*f7018c21STomi Valkeinen #else 3575*f7018c21STomi Valkeinen rc = atyfb_setup_generic(pdev, info, addr); 3576*f7018c21STomi Valkeinen #endif 3577*f7018c21STomi Valkeinen if (rc) 3578*f7018c21STomi Valkeinen goto err_release_mem; 3579*f7018c21STomi Valkeinen 3580*f7018c21STomi Valkeinen pci_set_drvdata(pdev, info); 3581*f7018c21STomi Valkeinen 3582*f7018c21STomi Valkeinen /* Init chip & register framebuffer */ 3583*f7018c21STomi Valkeinen rc = aty_init(info); 3584*f7018c21STomi Valkeinen if (rc) 3585*f7018c21STomi Valkeinen goto err_release_io; 3586*f7018c21STomi Valkeinen 3587*f7018c21STomi Valkeinen #ifdef __sparc__ 3588*f7018c21STomi Valkeinen /* 3589*f7018c21STomi Valkeinen * Add /dev/fb mmap values. 3590*f7018c21STomi Valkeinen */ 3591*f7018c21STomi Valkeinen par->mmap_map[0].voff = 0x8000000000000000UL; 3592*f7018c21STomi Valkeinen par->mmap_map[0].poff = (unsigned long) info->screen_base & PAGE_MASK; 3593*f7018c21STomi Valkeinen par->mmap_map[0].size = info->fix.smem_len; 3594*f7018c21STomi Valkeinen par->mmap_map[0].prot_mask = _PAGE_CACHE; 3595*f7018c21STomi Valkeinen par->mmap_map[0].prot_flag = _PAGE_E; 3596*f7018c21STomi Valkeinen par->mmap_map[1].voff = par->mmap_map[0].voff + info->fix.smem_len; 3597*f7018c21STomi Valkeinen par->mmap_map[1].poff = (long)par->ati_regbase & PAGE_MASK; 3598*f7018c21STomi Valkeinen par->mmap_map[1].size = PAGE_SIZE; 3599*f7018c21STomi Valkeinen par->mmap_map[1].prot_mask = _PAGE_CACHE; 3600*f7018c21STomi Valkeinen par->mmap_map[1].prot_flag = _PAGE_E; 3601*f7018c21STomi Valkeinen #endif /* __sparc__ */ 3602*f7018c21STomi Valkeinen 3603*f7018c21STomi Valkeinen mutex_lock(&reboot_lock); 3604*f7018c21STomi Valkeinen if (!reboot_info) 3605*f7018c21STomi Valkeinen reboot_info = info; 3606*f7018c21STomi Valkeinen mutex_unlock(&reboot_lock); 3607*f7018c21STomi Valkeinen 3608*f7018c21STomi Valkeinen return 0; 3609*f7018c21STomi Valkeinen 3610*f7018c21STomi Valkeinen err_release_io: 3611*f7018c21STomi Valkeinen #ifdef __sparc__ 3612*f7018c21STomi Valkeinen kfree(par->mmap_map); 3613*f7018c21STomi Valkeinen #else 3614*f7018c21STomi Valkeinen if (par->ati_regbase) 3615*f7018c21STomi Valkeinen iounmap(par->ati_regbase); 3616*f7018c21STomi Valkeinen if (info->screen_base) 3617*f7018c21STomi Valkeinen iounmap(info->screen_base); 3618*f7018c21STomi Valkeinen #endif 3619*f7018c21STomi Valkeinen err_release_mem: 3620*f7018c21STomi Valkeinen if (par->aux_start) 3621*f7018c21STomi Valkeinen release_mem_region(par->aux_start, par->aux_size); 3622*f7018c21STomi Valkeinen 3623*f7018c21STomi Valkeinen release_mem_region(par->res_start, par->res_size); 3624*f7018c21STomi Valkeinen framebuffer_release(info); 3625*f7018c21STomi Valkeinen 3626*f7018c21STomi Valkeinen return rc; 3627*f7018c21STomi Valkeinen } 3628*f7018c21STomi Valkeinen 3629*f7018c21STomi Valkeinen #endif /* CONFIG_PCI */ 3630*f7018c21STomi Valkeinen 3631*f7018c21STomi Valkeinen #ifdef CONFIG_ATARI 3632*f7018c21STomi Valkeinen 3633*f7018c21STomi Valkeinen static int __init atyfb_atari_probe(void) 3634*f7018c21STomi Valkeinen { 3635*f7018c21STomi Valkeinen struct atyfb_par *par; 3636*f7018c21STomi Valkeinen struct fb_info *info; 3637*f7018c21STomi Valkeinen int m64_num; 3638*f7018c21STomi Valkeinen u32 clock_r; 3639*f7018c21STomi Valkeinen int num_found = 0; 3640*f7018c21STomi Valkeinen 3641*f7018c21STomi Valkeinen for (m64_num = 0; m64_num < mach64_count; m64_num++) { 3642*f7018c21STomi Valkeinen if (!phys_vmembase[m64_num] || !phys_size[m64_num] || 3643*f7018c21STomi Valkeinen !phys_guiregbase[m64_num]) { 3644*f7018c21STomi Valkeinen PRINTKI("phys_*[%d] parameters not set => " 3645*f7018c21STomi Valkeinen "returning early. \n", m64_num); 3646*f7018c21STomi Valkeinen continue; 3647*f7018c21STomi Valkeinen } 3648*f7018c21STomi Valkeinen 3649*f7018c21STomi Valkeinen info = framebuffer_alloc(sizeof(struct atyfb_par), NULL); 3650*f7018c21STomi Valkeinen if (!info) { 3651*f7018c21STomi Valkeinen PRINTKE("atyfb_atari_probe() can't alloc fb_info\n"); 3652*f7018c21STomi Valkeinen return -ENOMEM; 3653*f7018c21STomi Valkeinen } 3654*f7018c21STomi Valkeinen par = info->par; 3655*f7018c21STomi Valkeinen 3656*f7018c21STomi Valkeinen info->fix = atyfb_fix; 3657*f7018c21STomi Valkeinen 3658*f7018c21STomi Valkeinen par->irq = (unsigned int) -1; /* something invalid */ 3659*f7018c21STomi Valkeinen 3660*f7018c21STomi Valkeinen /* 3661*f7018c21STomi Valkeinen * Map the video memory (physical address given) 3662*f7018c21STomi Valkeinen * to somewhere in the kernel address space. 3663*f7018c21STomi Valkeinen */ 3664*f7018c21STomi Valkeinen info->screen_base = ioremap(phys_vmembase[m64_num], phys_size[m64_num]); 3665*f7018c21STomi Valkeinen info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */ 3666*f7018c21STomi Valkeinen par->ati_regbase = ioremap(phys_guiregbase[m64_num], 0x10000) + 3667*f7018c21STomi Valkeinen 0xFC00ul; 3668*f7018c21STomi Valkeinen info->fix.mmio_start = (unsigned long)par->ati_regbase; /* Fake! */ 3669*f7018c21STomi Valkeinen 3670*f7018c21STomi Valkeinen aty_st_le32(CLOCK_CNTL, 0x12345678, par); 3671*f7018c21STomi Valkeinen clock_r = aty_ld_le32(CLOCK_CNTL, par); 3672*f7018c21STomi Valkeinen 3673*f7018c21STomi Valkeinen switch (clock_r & 0x003F) { 3674*f7018c21STomi Valkeinen case 0x12: 3675*f7018c21STomi Valkeinen par->clk_wr_offset = 3; /* */ 3676*f7018c21STomi Valkeinen break; 3677*f7018c21STomi Valkeinen case 0x34: 3678*f7018c21STomi Valkeinen par->clk_wr_offset = 2; /* Medusa ST-IO ISA Adapter etc. */ 3679*f7018c21STomi Valkeinen break; 3680*f7018c21STomi Valkeinen case 0x16: 3681*f7018c21STomi Valkeinen par->clk_wr_offset = 1; /* */ 3682*f7018c21STomi Valkeinen break; 3683*f7018c21STomi Valkeinen case 0x38: 3684*f7018c21STomi Valkeinen par->clk_wr_offset = 0; /* Panther 1 ISA Adapter (Gerald) */ 3685*f7018c21STomi Valkeinen break; 3686*f7018c21STomi Valkeinen } 3687*f7018c21STomi Valkeinen 3688*f7018c21STomi Valkeinen /* Fake pci_id for correct_chipset() */ 3689*f7018c21STomi Valkeinen switch (aty_ld_le32(CNFG_CHIP_ID, par) & CFG_CHIP_TYPE) { 3690*f7018c21STomi Valkeinen case 0x00d7: 3691*f7018c21STomi Valkeinen par->pci_id = PCI_CHIP_MACH64GX; 3692*f7018c21STomi Valkeinen break; 3693*f7018c21STomi Valkeinen case 0x0057: 3694*f7018c21STomi Valkeinen par->pci_id = PCI_CHIP_MACH64CX; 3695*f7018c21STomi Valkeinen break; 3696*f7018c21STomi Valkeinen default: 3697*f7018c21STomi Valkeinen break; 3698*f7018c21STomi Valkeinen } 3699*f7018c21STomi Valkeinen 3700*f7018c21STomi Valkeinen if (correct_chipset(par) || aty_init(info)) { 3701*f7018c21STomi Valkeinen iounmap(info->screen_base); 3702*f7018c21STomi Valkeinen iounmap(par->ati_regbase); 3703*f7018c21STomi Valkeinen framebuffer_release(info); 3704*f7018c21STomi Valkeinen } else { 3705*f7018c21STomi Valkeinen num_found++; 3706*f7018c21STomi Valkeinen } 3707*f7018c21STomi Valkeinen } 3708*f7018c21STomi Valkeinen 3709*f7018c21STomi Valkeinen return num_found ? 0 : -ENXIO; 3710*f7018c21STomi Valkeinen } 3711*f7018c21STomi Valkeinen 3712*f7018c21STomi Valkeinen #endif /* CONFIG_ATARI */ 3713*f7018c21STomi Valkeinen 3714*f7018c21STomi Valkeinen #ifdef CONFIG_PCI 3715*f7018c21STomi Valkeinen 3716*f7018c21STomi Valkeinen static void atyfb_remove(struct fb_info *info) 3717*f7018c21STomi Valkeinen { 3718*f7018c21STomi Valkeinen struct atyfb_par *par = (struct atyfb_par *) info->par; 3719*f7018c21STomi Valkeinen 3720*f7018c21STomi Valkeinen /* restore video mode */ 3721*f7018c21STomi Valkeinen aty_set_crtc(par, &par->saved_crtc); 3722*f7018c21STomi Valkeinen par->pll_ops->set_pll(info, &par->saved_pll); 3723*f7018c21STomi Valkeinen 3724*f7018c21STomi Valkeinen unregister_framebuffer(info); 3725*f7018c21STomi Valkeinen 3726*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_BACKLIGHT 3727*f7018c21STomi Valkeinen if (M64_HAS(MOBIL_BUS)) 3728*f7018c21STomi Valkeinen aty_bl_exit(info->bl_dev); 3729*f7018c21STomi Valkeinen #endif 3730*f7018c21STomi Valkeinen 3731*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 3732*f7018c21STomi Valkeinen if (par->mtrr_reg >= 0) { 3733*f7018c21STomi Valkeinen mtrr_del(par->mtrr_reg, 0, 0); 3734*f7018c21STomi Valkeinen par->mtrr_reg = -1; 3735*f7018c21STomi Valkeinen } 3736*f7018c21STomi Valkeinen if (par->mtrr_aper >= 0) { 3737*f7018c21STomi Valkeinen mtrr_del(par->mtrr_aper, 0, 0); 3738*f7018c21STomi Valkeinen par->mtrr_aper = -1; 3739*f7018c21STomi Valkeinen } 3740*f7018c21STomi Valkeinen #endif 3741*f7018c21STomi Valkeinen #ifndef __sparc__ 3742*f7018c21STomi Valkeinen if (par->ati_regbase) 3743*f7018c21STomi Valkeinen iounmap(par->ati_regbase); 3744*f7018c21STomi Valkeinen if (info->screen_base) 3745*f7018c21STomi Valkeinen iounmap(info->screen_base); 3746*f7018c21STomi Valkeinen #ifdef __BIG_ENDIAN 3747*f7018c21STomi Valkeinen if (info->sprite.addr) 3748*f7018c21STomi Valkeinen iounmap(info->sprite.addr); 3749*f7018c21STomi Valkeinen #endif 3750*f7018c21STomi Valkeinen #endif 3751*f7018c21STomi Valkeinen #ifdef __sparc__ 3752*f7018c21STomi Valkeinen kfree(par->mmap_map); 3753*f7018c21STomi Valkeinen #endif 3754*f7018c21STomi Valkeinen if (par->aux_start) 3755*f7018c21STomi Valkeinen release_mem_region(par->aux_start, par->aux_size); 3756*f7018c21STomi Valkeinen 3757*f7018c21STomi Valkeinen if (par->res_start) 3758*f7018c21STomi Valkeinen release_mem_region(par->res_start, par->res_size); 3759*f7018c21STomi Valkeinen 3760*f7018c21STomi Valkeinen framebuffer_release(info); 3761*f7018c21STomi Valkeinen } 3762*f7018c21STomi Valkeinen 3763*f7018c21STomi Valkeinen 3764*f7018c21STomi Valkeinen static void atyfb_pci_remove(struct pci_dev *pdev) 3765*f7018c21STomi Valkeinen { 3766*f7018c21STomi Valkeinen struct fb_info *info = pci_get_drvdata(pdev); 3767*f7018c21STomi Valkeinen 3768*f7018c21STomi Valkeinen mutex_lock(&reboot_lock); 3769*f7018c21STomi Valkeinen if (reboot_info == info) 3770*f7018c21STomi Valkeinen reboot_info = NULL; 3771*f7018c21STomi Valkeinen mutex_unlock(&reboot_lock); 3772*f7018c21STomi Valkeinen 3773*f7018c21STomi Valkeinen atyfb_remove(info); 3774*f7018c21STomi Valkeinen } 3775*f7018c21STomi Valkeinen 3776*f7018c21STomi Valkeinen static struct pci_device_id atyfb_pci_tbl[] = { 3777*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX 3778*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GX) }, 3779*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CX) }, 3780*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */ 3781*f7018c21STomi Valkeinen 3782*f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT 3783*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CT) }, 3784*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64ET) }, 3785*f7018c21STomi Valkeinen 3786*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LT) }, 3787*f7018c21STomi Valkeinen 3788*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VT) }, 3789*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GT) }, 3790*f7018c21STomi Valkeinen 3791*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VU) }, 3792*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GU) }, 3793*f7018c21STomi Valkeinen 3794*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LG) }, 3795*f7018c21STomi Valkeinen 3796*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VV) }, 3797*f7018c21STomi Valkeinen 3798*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GV) }, 3799*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GW) }, 3800*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GY) }, 3801*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GZ) }, 3802*f7018c21STomi Valkeinen 3803*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GB) }, 3804*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GD) }, 3805*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GI) }, 3806*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GP) }, 3807*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GQ) }, 3808*f7018c21STomi Valkeinen 3809*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LB) }, 3810*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LD) }, 3811*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LI) }, 3812*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LP) }, 3813*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LQ) }, 3814*f7018c21STomi Valkeinen 3815*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GM) }, 3816*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GN) }, 3817*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GO) }, 3818*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GL) }, 3819*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GR) }, 3820*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GS) }, 3821*f7018c21STomi Valkeinen 3822*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LM) }, 3823*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LN) }, 3824*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LR) }, 3825*f7018c21STomi Valkeinen { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LS) }, 3826*f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */ 3827*f7018c21STomi Valkeinen { } 3828*f7018c21STomi Valkeinen }; 3829*f7018c21STomi Valkeinen 3830*f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, atyfb_pci_tbl); 3831*f7018c21STomi Valkeinen 3832*f7018c21STomi Valkeinen static struct pci_driver atyfb_driver = { 3833*f7018c21STomi Valkeinen .name = "atyfb", 3834*f7018c21STomi Valkeinen .id_table = atyfb_pci_tbl, 3835*f7018c21STomi Valkeinen .probe = atyfb_pci_probe, 3836*f7018c21STomi Valkeinen .remove = atyfb_pci_remove, 3837*f7018c21STomi Valkeinen #ifdef CONFIG_PM 3838*f7018c21STomi Valkeinen .suspend = atyfb_pci_suspend, 3839*f7018c21STomi Valkeinen .resume = atyfb_pci_resume, 3840*f7018c21STomi Valkeinen #endif /* CONFIG_PM */ 3841*f7018c21STomi Valkeinen }; 3842*f7018c21STomi Valkeinen 3843*f7018c21STomi Valkeinen #endif /* CONFIG_PCI */ 3844*f7018c21STomi Valkeinen 3845*f7018c21STomi Valkeinen #ifndef MODULE 3846*f7018c21STomi Valkeinen static int __init atyfb_setup(char *options) 3847*f7018c21STomi Valkeinen { 3848*f7018c21STomi Valkeinen char *this_opt; 3849*f7018c21STomi Valkeinen 3850*f7018c21STomi Valkeinen if (!options || !*options) 3851*f7018c21STomi Valkeinen return 0; 3852*f7018c21STomi Valkeinen 3853*f7018c21STomi Valkeinen while ((this_opt = strsep(&options, ",")) != NULL) { 3854*f7018c21STomi Valkeinen if (!strncmp(this_opt, "noaccel", 7)) { 3855*f7018c21STomi Valkeinen noaccel = 1; 3856*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 3857*f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "nomtrr", 6)) { 3858*f7018c21STomi Valkeinen nomtrr = 1; 3859*f7018c21STomi Valkeinen #endif 3860*f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "vram:", 5)) 3861*f7018c21STomi Valkeinen vram = simple_strtoul(this_opt + 5, NULL, 0); 3862*f7018c21STomi Valkeinen else if (!strncmp(this_opt, "pll:", 4)) 3863*f7018c21STomi Valkeinen pll = simple_strtoul(this_opt + 4, NULL, 0); 3864*f7018c21STomi Valkeinen else if (!strncmp(this_opt, "mclk:", 5)) 3865*f7018c21STomi Valkeinen mclk = simple_strtoul(this_opt + 5, NULL, 0); 3866*f7018c21STomi Valkeinen else if (!strncmp(this_opt, "xclk:", 5)) 3867*f7018c21STomi Valkeinen xclk = simple_strtoul(this_opt+5, NULL, 0); 3868*f7018c21STomi Valkeinen else if (!strncmp(this_opt, "comp_sync:", 10)) 3869*f7018c21STomi Valkeinen comp_sync = simple_strtoul(this_opt+10, NULL, 0); 3870*f7018c21STomi Valkeinen else if (!strncmp(this_opt, "backlight:", 10)) 3871*f7018c21STomi Valkeinen backlight = simple_strtoul(this_opt+10, NULL, 0); 3872*f7018c21STomi Valkeinen #ifdef CONFIG_PPC 3873*f7018c21STomi Valkeinen else if (!strncmp(this_opt, "vmode:", 6)) { 3874*f7018c21STomi Valkeinen unsigned int vmode = 3875*f7018c21STomi Valkeinen simple_strtoul(this_opt + 6, NULL, 0); 3876*f7018c21STomi Valkeinen if (vmode > 0 && vmode <= VMODE_MAX) 3877*f7018c21STomi Valkeinen default_vmode = vmode; 3878*f7018c21STomi Valkeinen } else if (!strncmp(this_opt, "cmode:", 6)) { 3879*f7018c21STomi Valkeinen unsigned int cmode = 3880*f7018c21STomi Valkeinen simple_strtoul(this_opt + 6, NULL, 0); 3881*f7018c21STomi Valkeinen switch (cmode) { 3882*f7018c21STomi Valkeinen case 0: 3883*f7018c21STomi Valkeinen case 8: 3884*f7018c21STomi Valkeinen default_cmode = CMODE_8; 3885*f7018c21STomi Valkeinen break; 3886*f7018c21STomi Valkeinen case 15: 3887*f7018c21STomi Valkeinen case 16: 3888*f7018c21STomi Valkeinen default_cmode = CMODE_16; 3889*f7018c21STomi Valkeinen break; 3890*f7018c21STomi Valkeinen case 24: 3891*f7018c21STomi Valkeinen case 32: 3892*f7018c21STomi Valkeinen default_cmode = CMODE_32; 3893*f7018c21STomi Valkeinen break; 3894*f7018c21STomi Valkeinen } 3895*f7018c21STomi Valkeinen } 3896*f7018c21STomi Valkeinen #endif 3897*f7018c21STomi Valkeinen #ifdef CONFIG_ATARI 3898*f7018c21STomi Valkeinen /* 3899*f7018c21STomi Valkeinen * Why do we need this silly Mach64 argument? 3900*f7018c21STomi Valkeinen * We are already here because of mach64= so its redundant. 3901*f7018c21STomi Valkeinen */ 3902*f7018c21STomi Valkeinen else if (MACH_IS_ATARI 3903*f7018c21STomi Valkeinen && (!strncmp(this_opt, "Mach64:", 7))) { 3904*f7018c21STomi Valkeinen static unsigned char m64_num; 3905*f7018c21STomi Valkeinen static char mach64_str[80]; 3906*f7018c21STomi Valkeinen strlcpy(mach64_str, this_opt + 7, sizeof(mach64_str)); 3907*f7018c21STomi Valkeinen if (!store_video_par(mach64_str, m64_num)) { 3908*f7018c21STomi Valkeinen m64_num++; 3909*f7018c21STomi Valkeinen mach64_count = m64_num; 3910*f7018c21STomi Valkeinen } 3911*f7018c21STomi Valkeinen } 3912*f7018c21STomi Valkeinen #endif 3913*f7018c21STomi Valkeinen else 3914*f7018c21STomi Valkeinen mode = this_opt; 3915*f7018c21STomi Valkeinen } 3916*f7018c21STomi Valkeinen return 0; 3917*f7018c21STomi Valkeinen } 3918*f7018c21STomi Valkeinen #endif /* MODULE */ 3919*f7018c21STomi Valkeinen 3920*f7018c21STomi Valkeinen static int atyfb_reboot_notify(struct notifier_block *nb, 3921*f7018c21STomi Valkeinen unsigned long code, void *unused) 3922*f7018c21STomi Valkeinen { 3923*f7018c21STomi Valkeinen struct atyfb_par *par; 3924*f7018c21STomi Valkeinen 3925*f7018c21STomi Valkeinen if (code != SYS_RESTART) 3926*f7018c21STomi Valkeinen return NOTIFY_DONE; 3927*f7018c21STomi Valkeinen 3928*f7018c21STomi Valkeinen mutex_lock(&reboot_lock); 3929*f7018c21STomi Valkeinen 3930*f7018c21STomi Valkeinen if (!reboot_info) 3931*f7018c21STomi Valkeinen goto out; 3932*f7018c21STomi Valkeinen 3933*f7018c21STomi Valkeinen if (!lock_fb_info(reboot_info)) 3934*f7018c21STomi Valkeinen goto out; 3935*f7018c21STomi Valkeinen 3936*f7018c21STomi Valkeinen par = reboot_info->par; 3937*f7018c21STomi Valkeinen 3938*f7018c21STomi Valkeinen /* 3939*f7018c21STomi Valkeinen * HP OmniBook 500's BIOS doesn't like the state of the 3940*f7018c21STomi Valkeinen * hardware after atyfb has been used. Restore the hardware 3941*f7018c21STomi Valkeinen * to the original state to allow successful reboots. 3942*f7018c21STomi Valkeinen */ 3943*f7018c21STomi Valkeinen aty_set_crtc(par, &par->saved_crtc); 3944*f7018c21STomi Valkeinen par->pll_ops->set_pll(reboot_info, &par->saved_pll); 3945*f7018c21STomi Valkeinen 3946*f7018c21STomi Valkeinen unlock_fb_info(reboot_info); 3947*f7018c21STomi Valkeinen out: 3948*f7018c21STomi Valkeinen mutex_unlock(&reboot_lock); 3949*f7018c21STomi Valkeinen 3950*f7018c21STomi Valkeinen return NOTIFY_DONE; 3951*f7018c21STomi Valkeinen } 3952*f7018c21STomi Valkeinen 3953*f7018c21STomi Valkeinen static struct notifier_block atyfb_reboot_notifier = { 3954*f7018c21STomi Valkeinen .notifier_call = atyfb_reboot_notify, 3955*f7018c21STomi Valkeinen }; 3956*f7018c21STomi Valkeinen 3957*f7018c21STomi Valkeinen static const struct dmi_system_id atyfb_reboot_ids[] = { 3958*f7018c21STomi Valkeinen { 3959*f7018c21STomi Valkeinen .ident = "HP OmniBook 500", 3960*f7018c21STomi Valkeinen .matches = { 3961*f7018c21STomi Valkeinen DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 3962*f7018c21STomi Valkeinen DMI_MATCH(DMI_PRODUCT_NAME, "HP OmniBook PC"), 3963*f7018c21STomi Valkeinen DMI_MATCH(DMI_PRODUCT_VERSION, "HP OmniBook 500 FA"), 3964*f7018c21STomi Valkeinen }, 3965*f7018c21STomi Valkeinen }, 3966*f7018c21STomi Valkeinen 3967*f7018c21STomi Valkeinen { } 3968*f7018c21STomi Valkeinen }; 3969*f7018c21STomi Valkeinen 3970*f7018c21STomi Valkeinen static int __init atyfb_init(void) 3971*f7018c21STomi Valkeinen { 3972*f7018c21STomi Valkeinen int err1 = 1, err2 = 1; 3973*f7018c21STomi Valkeinen #ifndef MODULE 3974*f7018c21STomi Valkeinen char *option = NULL; 3975*f7018c21STomi Valkeinen 3976*f7018c21STomi Valkeinen if (fb_get_options("atyfb", &option)) 3977*f7018c21STomi Valkeinen return -ENODEV; 3978*f7018c21STomi Valkeinen atyfb_setup(option); 3979*f7018c21STomi Valkeinen #endif 3980*f7018c21STomi Valkeinen 3981*f7018c21STomi Valkeinen #ifdef CONFIG_PCI 3982*f7018c21STomi Valkeinen err1 = pci_register_driver(&atyfb_driver); 3983*f7018c21STomi Valkeinen #endif 3984*f7018c21STomi Valkeinen #ifdef CONFIG_ATARI 3985*f7018c21STomi Valkeinen err2 = atyfb_atari_probe(); 3986*f7018c21STomi Valkeinen #endif 3987*f7018c21STomi Valkeinen 3988*f7018c21STomi Valkeinen if (err1 && err2) 3989*f7018c21STomi Valkeinen return -ENODEV; 3990*f7018c21STomi Valkeinen 3991*f7018c21STomi Valkeinen if (dmi_check_system(atyfb_reboot_ids)) 3992*f7018c21STomi Valkeinen register_reboot_notifier(&atyfb_reboot_notifier); 3993*f7018c21STomi Valkeinen 3994*f7018c21STomi Valkeinen return 0; 3995*f7018c21STomi Valkeinen } 3996*f7018c21STomi Valkeinen 3997*f7018c21STomi Valkeinen static void __exit atyfb_exit(void) 3998*f7018c21STomi Valkeinen { 3999*f7018c21STomi Valkeinen if (dmi_check_system(atyfb_reboot_ids)) 4000*f7018c21STomi Valkeinen unregister_reboot_notifier(&atyfb_reboot_notifier); 4001*f7018c21STomi Valkeinen 4002*f7018c21STomi Valkeinen #ifdef CONFIG_PCI 4003*f7018c21STomi Valkeinen pci_unregister_driver(&atyfb_driver); 4004*f7018c21STomi Valkeinen #endif 4005*f7018c21STomi Valkeinen } 4006*f7018c21STomi Valkeinen 4007*f7018c21STomi Valkeinen module_init(atyfb_init); 4008*f7018c21STomi Valkeinen module_exit(atyfb_exit); 4009*f7018c21STomi Valkeinen 4010*f7018c21STomi Valkeinen MODULE_DESCRIPTION("FBDev driver for ATI Mach64 cards"); 4011*f7018c21STomi Valkeinen MODULE_LICENSE("GPL"); 4012*f7018c21STomi Valkeinen module_param(noaccel, bool, 0); 4013*f7018c21STomi Valkeinen MODULE_PARM_DESC(noaccel, "bool: disable acceleration"); 4014*f7018c21STomi Valkeinen module_param(vram, int, 0); 4015*f7018c21STomi Valkeinen MODULE_PARM_DESC(vram, "int: override size of video ram"); 4016*f7018c21STomi Valkeinen module_param(pll, int, 0); 4017*f7018c21STomi Valkeinen MODULE_PARM_DESC(pll, "int: override video clock"); 4018*f7018c21STomi Valkeinen module_param(mclk, int, 0); 4019*f7018c21STomi Valkeinen MODULE_PARM_DESC(mclk, "int: override memory clock"); 4020*f7018c21STomi Valkeinen module_param(xclk, int, 0); 4021*f7018c21STomi Valkeinen MODULE_PARM_DESC(xclk, "int: override accelerated engine clock"); 4022*f7018c21STomi Valkeinen module_param(comp_sync, int, 0); 4023*f7018c21STomi Valkeinen MODULE_PARM_DESC(comp_sync, "Set composite sync signal to low (0) or high (1)"); 4024*f7018c21STomi Valkeinen module_param(mode, charp, 0); 4025*f7018c21STomi Valkeinen MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); 4026*f7018c21STomi Valkeinen #ifdef CONFIG_MTRR 4027*f7018c21STomi Valkeinen module_param(nomtrr, bool, 0); 4028*f7018c21STomi Valkeinen MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers"); 4029*f7018c21STomi Valkeinen #endif 4030