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