/drivers/video/aty/atyfb_base.c
C | 1631 lines | 1255 code | 199 blank | 177 comment | 169 complexity | def674e294f81689722f612f46ca2ea4 MD5 | raw file
- /*
- * ATI Frame Buffer Device Driver Core
- *
- * Copyright (C) 2004 Alex Kern <alex.kern@gmx.de>
- * Copyright (C) 1997-2001 Geert Uytterhoeven
- * Copyright (C) 1998 Bernd Harries
- * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
- *
- * This driver supports the following ATI graphics chips:
- * - ATI Mach64
- *
- * To do: add support for
- * - ATI Rage128 (from aty128fb.c)
- * - ATI Radeon (from radeonfb.c)
- *
- * This driver is partly based on the PowerMac console driver:
- *
- * Copyright (C) 1996 Paul Mackerras
- *
- * and on the PowerMac ATI/mach64 display driver:
- *
- * Copyright (C) 1997 Michael AK Tesch
- *
- * with work by Jon Howell
- * Harry AC Eaton
- * Anthony Tong <atong@uiuc.edu>
- *
- * Generic LCD support written by Daniel Mantione, ported from 2.4.20 by Alex Kern
- * Many Thanks to Ville Syrjälä for patches and fixing nasting 16 bit color bug.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive for
- * more details.
- *
- * Many thanks to Nitya from ATI devrel for support and patience !
- */
- /******************************************************************************
- TODO:
- - cursor support on all cards and all ramdacs.
- - cursor parameters controlable via ioctl()s.
- - guess PLL and MCLK based on the original PLL register values initialized
- by Open Firmware (if they are initialized). BIOS is done
- (Anyone with Mac to help with this?)
- ******************************************************************************/
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/string.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/vmalloc.h>
- #include <linux/delay.h>
- #include <linux/console.h>
- #include <linux/fb.h>
- #include <linux/init.h>
- #include <linux/pci.h>
- #include <linux/interrupt.h>
- #include <linux/spinlock.h>
- #include <linux/wait.h>
- #include <linux/backlight.h>
- #include <linux/reboot.h>
- #include <linux/dmi.h>
- #include <asm/io.h>
- #include <linux/uaccess.h>
- #include <video/mach64.h>
- #include "atyfb.h"
- #include "ati_ids.h"
- #ifdef __powerpc__
- #include <asm/machdep.h>
- #include <asm/prom.h>
- #include "../macmodes.h"
- #endif
- #ifdef __sparc__
- #include <asm/fbio.h>
- #include <asm/oplib.h>
- #include <asm/prom.h>
- #endif
- #ifdef CONFIG_ADB_PMU
- #include <linux/adb.h>
- #include <linux/pmu.h>
- #endif
- #ifdef CONFIG_BOOTX_TEXT
- #include <asm/btext.h>
- #endif
- #ifdef CONFIG_PMAC_BACKLIGHT
- #include <asm/backlight.h>
- #endif
- #ifdef CONFIG_MTRR
- #include <asm/mtrr.h>
- #endif
- /*
- * Debug flags.
- */
- #undef DEBUG
- /*#define DEBUG*/
- /* Make sure n * PAGE_SIZE is protected at end of Aperture for GUI-regs */
- /* - must be large enough to catch all GUI-Regs */
- /* - must be aligned to a PAGE boundary */
- #define GUI_RESERVE (1 * PAGE_SIZE)
- /* FIXME: remove the FAIL definition */
- #define FAIL(msg) do { \
- if (!(var->activate & FB_ACTIVATE_TEST)) \
- printk(KERN_CRIT "atyfb: " msg "\n"); \
- return -EINVAL; \
- } while (0)
- #define FAIL_MAX(msg, x, _max_) do { \
- if (x > _max_) { \
- if (!(var->activate & FB_ACTIVATE_TEST)) \
- printk(KERN_CRIT "atyfb: " msg " %x(%x)\n", x, _max_); \
- return -EINVAL; \
- } \
- } while (0)
- #ifdef DEBUG
- #define DPRINTK(fmt, args...) printk(KERN_DEBUG "atyfb: " fmt, ## args)
- #else
- #define DPRINTK(fmt, args...)
- #endif
- #define PRINTKI(fmt, args...) printk(KERN_INFO "atyfb: " fmt, ## args)
- #define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args)
- #if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \
- defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_FB_ATY_BACKLIGHT)
- static const u32 lt_lcd_regs[] = {
- CNFG_PANEL_LG,
- LCD_GEN_CNTL_LG,
- DSTN_CONTROL_LG,
- HFB_PITCH_ADDR_LG,
- HORZ_STRETCHING_LG,
- VERT_STRETCHING_LG,
- 0, /* EXT_VERT_STRETCH */
- LT_GIO_LG,
- POWER_MANAGEMENT_LG
- };
- void aty_st_lcd(int index, u32 val, const struct atyfb_par *par)
- {
- if (M64_HAS(LT_LCD_REGS)) {
- aty_st_le32(lt_lcd_regs[index], val, par);
- } else {
- unsigned long temp;
- /* write addr byte */
- temp = aty_ld_le32(LCD_INDEX, par);
- aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
- /* write the register value */
- aty_st_le32(LCD_DATA, val, par);
- }
- }
- u32 aty_ld_lcd(int index, const struct atyfb_par *par)
- {
- if (M64_HAS(LT_LCD_REGS)) {
- return aty_ld_le32(lt_lcd_regs[index], par);
- } else {
- unsigned long temp;
- /* write addr byte */
- temp = aty_ld_le32(LCD_INDEX, par);
- aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
- /* read the register value */
- return aty_ld_le32(LCD_DATA, par);
- }
- }
- #endif /* defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) */
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- /*
- * ATIReduceRatio --
- *
- * Reduce a fraction by factoring out the largest common divider of the
- * fraction's numerator and denominator.
- */
- static void ATIReduceRatio(int *Numerator, int *Denominator)
- {
- int Multiplier, Divider, Remainder;
- Multiplier = *Numerator;
- Divider = *Denominator;
- while ((Remainder = Multiplier % Divider)) {
- Multiplier = Divider;
- Divider = Remainder;
- }
- *Numerator /= Divider;
- *Denominator /= Divider;
- }
- #endif
- /*
- * The Hardware parameters for each card
- */
- struct pci_mmap_map {
- unsigned long voff;
- unsigned long poff;
- unsigned long size;
- unsigned long prot_flag;
- unsigned long prot_mask;
- };
- static struct fb_fix_screeninfo atyfb_fix __devinitdata = {
- .id = "ATY Mach64",
- .type = FB_TYPE_PACKED_PIXELS,
- .visual = FB_VISUAL_PSEUDOCOLOR,
- .xpanstep = 8,
- .ypanstep = 1,
- };
- /*
- * Frame buffer device API
- */
- static int atyfb_open(struct fb_info *info, int user);
- static int atyfb_release(struct fb_info *info, int user);
- static int atyfb_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info);
- static int atyfb_set_par(struct fb_info *info);
- static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
- u_int transp, struct fb_info *info);
- static int atyfb_pan_display(struct fb_var_screeninfo *var,
- struct fb_info *info);
- static int atyfb_blank(int blank, struct fb_info *info);
- static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg);
- #ifdef __sparc__
- static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
- #endif
- static int atyfb_sync(struct fb_info *info);
- /*
- * Internal routines
- */
- static int aty_init(struct fb_info *info);
- static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc);
- static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc);
- static int aty_var_to_crtc(const struct fb_info *info,
- const struct fb_var_screeninfo *var,
- struct crtc *crtc);
- static int aty_crtc_to_var(const struct crtc *crtc,
- struct fb_var_screeninfo *var);
- static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info);
- #ifdef CONFIG_PPC
- static int read_aty_sense(const struct atyfb_par *par);
- #endif
- static DEFINE_MUTEX(reboot_lock);
- static struct fb_info *reboot_info;
- /*
- * Interface used by the world
- */
- static struct fb_var_screeninfo default_var = {
- /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
- 640, 480, 640, 480, 0, 0, 8, 0,
- {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
- 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
- 0, FB_VMODE_NONINTERLACED
- };
- static struct fb_videomode defmode = {
- /* 640x480 @ 60 Hz, 31.5 kHz hsync */
- NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
- 0, FB_VMODE_NONINTERLACED
- };
- static struct fb_ops atyfb_ops = {
- .owner = THIS_MODULE,
- .fb_open = atyfb_open,
- .fb_release = atyfb_release,
- .fb_check_var = atyfb_check_var,
- .fb_set_par = atyfb_set_par,
- .fb_setcolreg = atyfb_setcolreg,
- .fb_pan_display = atyfb_pan_display,
- .fb_blank = atyfb_blank,
- .fb_ioctl = atyfb_ioctl,
- .fb_fillrect = atyfb_fillrect,
- .fb_copyarea = atyfb_copyarea,
- .fb_imageblit = atyfb_imageblit,
- #ifdef __sparc__
- .fb_mmap = atyfb_mmap,
- #endif
- .fb_sync = atyfb_sync,
- };
- static int noaccel;
- #ifdef CONFIG_MTRR
- static int nomtrr;
- #endif
- static int vram;
- static int pll;
- static int mclk;
- static int xclk;
- static int comp_sync __devinitdata = -1;
- static char *mode;
- #ifdef CONFIG_PMAC_BACKLIGHT
- static int backlight __devinitdata = 1;
- #else
- static int backlight __devinitdata = 0;
- #endif
- #ifdef CONFIG_PPC
- static int default_vmode __devinitdata = VMODE_CHOOSE;
- static int default_cmode __devinitdata = CMODE_CHOOSE;
- module_param_named(vmode, default_vmode, int, 0);
- MODULE_PARM_DESC(vmode, "int: video mode for mac");
- module_param_named(cmode, default_cmode, int, 0);
- MODULE_PARM_DESC(cmode, "int: color mode for mac");
- #endif
- #ifdef CONFIG_ATARI
- static unsigned int mach64_count __devinitdata = 0;
- static unsigned long phys_vmembase[FB_MAX] __devinitdata = { 0, };
- static unsigned long phys_size[FB_MAX] __devinitdata = { 0, };
- static unsigned long phys_guiregbase[FB_MAX] __devinitdata = { 0, };
- #endif
- /* top -> down is an evolution of mach64 chipset, any corrections? */
- #define ATI_CHIP_88800GX (M64F_GX)
- #define ATI_CHIP_88800CX (M64F_GX)
- #define ATI_CHIP_264CT (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
- #define ATI_CHIP_264ET (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
- #define ATI_CHIP_264VT (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_MAGIC_FIFO)
- #define ATI_CHIP_264GT (M64F_GT | M64F_INTEGRATED | M64F_MAGIC_FIFO | M64F_EXTRA_BRIGHT)
- #define ATI_CHIP_264VTB (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP)
- #define ATI_CHIP_264VT3 (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL)
- #define ATI_CHIP_264VT4 (M64F_VT | M64F_INTEGRATED | M64F_GTB_DSP)
- /* FIXME what is this chip? */
- #define ATI_CHIP_264LT (M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP)
- /* make sets shorter */
- #define ATI_MODERN_SET (M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | M64F_EXTRA_BRIGHT)
- #define ATI_CHIP_264GTB (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
- /*#define ATI_CHIP_264GTDVD ?*/
- #define ATI_CHIP_264LTG (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
- #define ATI_CHIP_264GT2C (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE)
- #define ATI_CHIP_264GTPRO (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
- #define ATI_CHIP_264LTPRO (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
- #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)
- #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)
- static struct {
- u16 pci_id;
- const char *name;
- int pll, mclk, xclk, ecp_max;
- u32 features;
- } aty_chips[] __devinitdata = {
- #ifdef CONFIG_FB_ATY_GX
- /* Mach64 GX */
- { PCI_CHIP_MACH64GX, "ATI888GX00 (Mach64 GX)", 135, 50, 50, 0, ATI_CHIP_88800GX },
- { PCI_CHIP_MACH64CX, "ATI888CX00 (Mach64 CX)", 135, 50, 50, 0, ATI_CHIP_88800CX },
- #endif /* CONFIG_FB_ATY_GX */
- #ifdef CONFIG_FB_ATY_CT
- { PCI_CHIP_MACH64CT, "ATI264CT (Mach64 CT)", 135, 60, 60, 0, ATI_CHIP_264CT },
- { PCI_CHIP_MACH64ET, "ATI264ET (Mach64 ET)", 135, 60, 60, 0, ATI_CHIP_264ET },
- /* FIXME what is this chip? */
- { PCI_CHIP_MACH64LT, "ATI264LT (Mach64 LT)", 135, 63, 63, 0, ATI_CHIP_264LT },
- { PCI_CHIP_MACH64VT, "ATI264VT (Mach64 VT)", 170, 67, 67, 80, ATI_CHIP_264VT },
- { PCI_CHIP_MACH64GT, "3D RAGE (Mach64 GT)", 135, 63, 63, 80, ATI_CHIP_264GT },
- { PCI_CHIP_MACH64VU, "ATI264VT3 (Mach64 VU)", 200, 67, 67, 80, ATI_CHIP_264VT3 },
- { PCI_CHIP_MACH64GU, "3D RAGE II+ (Mach64 GU)", 200, 67, 67, 100, ATI_CHIP_264GTB },
- { PCI_CHIP_MACH64LG, "3D RAGE LT (Mach64 LG)", 230, 63, 63, 100, ATI_CHIP_264LTG | M64F_LT_LCD_REGS | M64F_G3_PB_1024x768 },
- { PCI_CHIP_MACH64VV, "ATI264VT4 (Mach64 VV)", 230, 83, 83, 100, ATI_CHIP_264VT4 },
- { PCI_CHIP_MACH64GV, "3D RAGE IIC (Mach64 GV, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
- { PCI_CHIP_MACH64GW, "3D RAGE IIC (Mach64 GW, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
- { PCI_CHIP_MACH64GY, "3D RAGE IIC (Mach64 GY, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
- { PCI_CHIP_MACH64GZ, "3D RAGE IIC (Mach64 GZ, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
- { PCI_CHIP_MACH64GB, "3D RAGE PRO (Mach64 GB, BGA, AGP)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
- { PCI_CHIP_MACH64GD, "3D RAGE PRO (Mach64 GD, BGA, AGP 1x)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
- { PCI_CHIP_MACH64GI, "3D RAGE PRO (Mach64 GI, BGA, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO | M64F_MAGIC_VRAM_SIZE },
- { PCI_CHIP_MACH64GP, "3D RAGE PRO (Mach64 GP, PQFP, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
- { PCI_CHIP_MACH64GQ, "3D RAGE PRO (Mach64 GQ, PQFP, PCI, limited 3D)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
- { PCI_CHIP_MACH64LB, "3D RAGE LT PRO (Mach64 LB, AGP)", 236, 75, 100, 135, ATI_CHIP_264LTPRO },
- { PCI_CHIP_MACH64LD, "3D RAGE LT PRO (Mach64 LD, AGP)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
- { 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 },
- { PCI_CHIP_MACH64LP, "3D RAGE LT PRO (Mach64 LP, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1024x768 },
- { PCI_CHIP_MACH64LQ, "3D RAGE LT PRO (Mach64 LQ, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
- { PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
- { PCI_CHIP_MACH64GN, "3D RAGE XC (Mach64 GN, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
- { PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
- { PCI_CHIP_MACH64GL, "3D RAGE XC (Mach64 GL, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
- { PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL | M64F_SDRAM_MAGIC_PLL },
- { PCI_CHIP_MACH64GS, "3D RAGE XC (Mach64 GS, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL },
- { PCI_CHIP_MACH64LM, "3D RAGE Mobility P/M (Mach64 LM, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
- { PCI_CHIP_MACH64LN, "3D RAGE Mobility L (Mach64 LN, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
- { PCI_CHIP_MACH64LR, "3D RAGE Mobility P/M (Mach64 LR, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
- { PCI_CHIP_MACH64LS, "3D RAGE Mobility L (Mach64 LS, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
- #endif /* CONFIG_FB_ATY_CT */
- };
- static int __devinit correct_chipset(struct atyfb_par *par)
- {
- u8 rev;
- u16 type;
- u32 chip_id;
- const char *name;
- int i;
- for (i = ARRAY_SIZE(aty_chips) - 1; i >= 0; i--)
- if (par->pci_id == aty_chips[i].pci_id)
- break;
- if (i < 0)
- return -ENODEV;
- name = aty_chips[i].name;
- par->pll_limits.pll_max = aty_chips[i].pll;
- par->pll_limits.mclk = aty_chips[i].mclk;
- par->pll_limits.xclk = aty_chips[i].xclk;
- par->pll_limits.ecp_max = aty_chips[i].ecp_max;
- par->features = aty_chips[i].features;
- chip_id = aty_ld_le32(CNFG_CHIP_ID, par);
- type = chip_id & CFG_CHIP_TYPE;
- rev = (chip_id & CFG_CHIP_REV) >> 24;
- switch (par->pci_id) {
- #ifdef CONFIG_FB_ATY_GX
- case PCI_CHIP_MACH64GX:
- if (type != 0x00d7)
- return -ENODEV;
- break;
- case PCI_CHIP_MACH64CX:
- if (type != 0x0057)
- return -ENODEV;
- break;
- #endif
- #ifdef CONFIG_FB_ATY_CT
- case PCI_CHIP_MACH64VT:
- switch (rev & 0x07) {
- case 0x00:
- switch (rev & 0xc0) {
- case 0x00:
- name = "ATI264VT (A3) (Mach64 VT)";
- par->pll_limits.pll_max = 170;
- par->pll_limits.mclk = 67;
- par->pll_limits.xclk = 67;
- par->pll_limits.ecp_max = 80;
- par->features = ATI_CHIP_264VT;
- break;
- case 0x40:
- name = "ATI264VT2 (A4) (Mach64 VT)";
- par->pll_limits.pll_max = 200;
- par->pll_limits.mclk = 67;
- par->pll_limits.xclk = 67;
- par->pll_limits.ecp_max = 80;
- par->features = ATI_CHIP_264VT | M64F_MAGIC_POSTDIV;
- break;
- }
- break;
- case 0x01:
- name = "ATI264VT3 (B1) (Mach64 VT)";
- par->pll_limits.pll_max = 200;
- par->pll_limits.mclk = 67;
- par->pll_limits.xclk = 67;
- par->pll_limits.ecp_max = 80;
- par->features = ATI_CHIP_264VTB;
- break;
- case 0x02:
- name = "ATI264VT3 (B2) (Mach64 VT)";
- par->pll_limits.pll_max = 200;
- par->pll_limits.mclk = 67;
- par->pll_limits.xclk = 67;
- par->pll_limits.ecp_max = 80;
- par->features = ATI_CHIP_264VT3;
- break;
- }
- break;
- case PCI_CHIP_MACH64GT:
- switch (rev & 0x07) {
- case 0x01:
- name = "3D RAGE II (Mach64 GT)";
- par->pll_limits.pll_max = 170;
- par->pll_limits.mclk = 67;
- par->pll_limits.xclk = 67;
- par->pll_limits.ecp_max = 80;
- par->features = ATI_CHIP_264GTB;
- break;
- case 0x02:
- name = "3D RAGE II+ (Mach64 GT)";
- par->pll_limits.pll_max = 200;
- par->pll_limits.mclk = 67;
- par->pll_limits.xclk = 67;
- par->pll_limits.ecp_max = 100;
- par->features = ATI_CHIP_264GTB;
- break;
- }
- break;
- #endif
- }
- PRINTKI("%s [0x%04x rev 0x%02x]\n", name, type, rev);
- return 0;
- }
- static char ram_dram[] __devinitdata = "DRAM";
- static char ram_resv[] __devinitdata = "RESV";
- #ifdef CONFIG_FB_ATY_GX
- static char ram_vram[] __devinitdata = "VRAM";
- #endif /* CONFIG_FB_ATY_GX */
- #ifdef CONFIG_FB_ATY_CT
- static char ram_edo[] __devinitdata = "EDO";
- static char ram_sdram[] __devinitdata = "SDRAM (1:1)";
- static char ram_sgram[] __devinitdata = "SGRAM (1:1)";
- static char ram_sdram32[] __devinitdata = "SDRAM (2:1) (32-bit)";
- static char ram_wram[] __devinitdata = "WRAM";
- static char ram_off[] __devinitdata = "OFF";
- #endif /* CONFIG_FB_ATY_CT */
- #ifdef CONFIG_FB_ATY_GX
- static char *aty_gx_ram[8] __devinitdata = {
- ram_dram, ram_vram, ram_vram, ram_dram,
- ram_dram, ram_vram, ram_vram, ram_resv
- };
- #endif /* CONFIG_FB_ATY_GX */
- #ifdef CONFIG_FB_ATY_CT
- static char *aty_ct_ram[8] __devinitdata = {
- ram_off, ram_dram, ram_edo, ram_edo,
- ram_sdram, ram_sgram, ram_wram, ram_resv
- };
- static char *aty_xl_ram[8] __devinitdata = {
- ram_off, ram_dram, ram_edo, ram_edo,
- ram_sdram, ram_sgram, ram_sdram32, ram_resv
- };
- #endif /* CONFIG_FB_ATY_CT */
- static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var,
- struct atyfb_par *par)
- {
- u32 pixclock = var->pixclock;
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- u32 lcd_on_off;
- par->pll.ct.xres = 0;
- if (par->lcd_table != 0) {
- lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par);
- if (lcd_on_off & LCD_ON) {
- par->pll.ct.xres = var->xres;
- pixclock = par->lcd_pixclock;
- }
- }
- #endif
- return pixclock;
- }
- #if defined(CONFIG_PPC)
- /*
- * Apple monitor sense
- */
- static int __devinit read_aty_sense(const struct atyfb_par *par)
- {
- int sense, i;
- aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */
- __delay(200);
- aty_st_le32(GP_IO, 0, par); /* turn off outputs */
- __delay(2000);
- i = aty_ld_le32(GP_IO, par); /* get primary sense value */
- sense = ((i & 0x3000) >> 3) | (i & 0x100);
- /* drive each sense line low in turn and collect the other 2 */
- aty_st_le32(GP_IO, 0x20000000, par); /* drive A low */
- __delay(2000);
- i = aty_ld_le32(GP_IO, par);
- sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
- aty_st_le32(GP_IO, 0x20002000, par); /* drive A high again */
- __delay(200);
- aty_st_le32(GP_IO, 0x10000000, par); /* drive B low */
- __delay(2000);
- i = aty_ld_le32(GP_IO, par);
- sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
- aty_st_le32(GP_IO, 0x10001000, par); /* drive B high again */
- __delay(200);
- aty_st_le32(GP_IO, 0x01000000, par); /* drive C low */
- __delay(2000);
- sense |= (aty_ld_le32(GP_IO, par) & 0x3000) >> 12;
- aty_st_le32(GP_IO, 0, par); /* turn off outputs */
- return sense;
- }
- #endif /* defined(CONFIG_PPC) */
- /* ------------------------------------------------------------------------- */
- /*
- * CRTC programming
- */
- static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc)
- {
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- if (par->lcd_table != 0) {
- if (!M64_HAS(LT_LCD_REGS)) {
- crtc->lcd_index = aty_ld_le32(LCD_INDEX, par);
- aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
- }
- crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par);
- crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par);
- /* switch to non shadow registers */
- aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
- ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
- /* save stretching */
- crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
- crtc->vert_stretching = aty_ld_lcd(VERT_STRETCHING, par);
- if (!M64_HAS(LT_LCD_REGS))
- crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par);
- }
- #endif
- crtc->h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
- crtc->h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
- crtc->v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
- crtc->v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
- crtc->vline_crnt_vline = aty_ld_le32(CRTC_VLINE_CRNT_VLINE, par);
- crtc->off_pitch = aty_ld_le32(CRTC_OFF_PITCH, par);
- crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- if (par->lcd_table != 0) {
- /* switch to shadow registers */
- aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
- SHADOW_EN | SHADOW_RW_EN, par);
- crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
- crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
- crtc->shadow_v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
- crtc->shadow_v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
- aty_st_le32(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
- }
- #endif /* CONFIG_FB_ATY_GENERIC_LCD */
- }
- static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc)
- {
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- if (par->lcd_table != 0) {
- /* stop CRTC */
- aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl &
- ~(CRTC_EXT_DISP_EN | CRTC_EN), par);
- /* update non-shadow registers first */
- aty_st_lcd(CNFG_PANEL, crtc->lcd_config_panel, par);
- aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
- ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
- /* temporarily disable stretching */
- aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching &
- ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par);
- aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching &
- ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 |
- VERT_STRETCH_USE0 | VERT_STRETCH_EN), par);
- }
- #endif
- /* turn off CRT */
- aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~CRTC_EN, par);
- DPRINTK("setting up CRTC\n");
- DPRINTK("set primary CRT to %ix%i %c%c composite %c\n",
- ((((crtc->h_tot_disp >> 16) & 0xff) + 1) << 3),
- (((crtc->v_tot_disp >> 16) & 0x7ff) + 1),
- (crtc->h_sync_strt_wid & 0x200000) ? 'N' : 'P',
- (crtc->v_sync_strt_wid & 0x200000) ? 'N' : 'P',
- (crtc->gen_cntl & CRTC_CSYNC_EN) ? 'P' : 'N');
- DPRINTK("CRTC_H_TOTAL_DISP: %x\n", crtc->h_tot_disp);
- DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n", crtc->h_sync_strt_wid);
- DPRINTK("CRTC_V_TOTAL_DISP: %x\n", crtc->v_tot_disp);
- DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n", crtc->v_sync_strt_wid);
- DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch);
- DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline);
- DPRINTK("CRTC_GEN_CNTL: %x\n", crtc->gen_cntl);
- aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par);
- aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par);
- aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_tot_disp, par);
- aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid, par);
- aty_st_le32(CRTC_OFF_PITCH, crtc->off_pitch, par);
- aty_st_le32(CRTC_VLINE_CRNT_VLINE, crtc->vline_crnt_vline, par);
- aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl, par);
- #if 0
- FIXME
- if (par->accel_flags & FB_ACCELF_TEXT)
- aty_init_engine(par, info);
- #endif
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- /* after setting the CRTC registers we should set the LCD registers. */
- if (par->lcd_table != 0) {
- /* switch to shadow registers */
- aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
- SHADOW_EN | SHADOW_RW_EN, par);
- DPRINTK("set shadow CRT to %ix%i %c%c\n",
- ((((crtc->shadow_h_tot_disp >> 16) & 0xff) + 1) << 3),
- (((crtc->shadow_v_tot_disp >> 16) & 0x7ff) + 1),
- (crtc->shadow_h_sync_strt_wid & 0x200000) ? 'N' : 'P',
- (crtc->shadow_v_sync_strt_wid & 0x200000) ? 'N' : 'P');
- DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n",
- crtc->shadow_h_tot_disp);
- DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n",
- crtc->shadow_h_sync_strt_wid);
- DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n",
- crtc->shadow_v_tot_disp);
- DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n",
- crtc->shadow_v_sync_strt_wid);
- aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par);
- aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par);
- aty_st_le32(CRTC_V_TOTAL_DISP, crtc->shadow_v_tot_disp, par);
- aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->shadow_v_sync_strt_wid, par);
- /* restore CRTC selection & shadow state and enable stretching */
- DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl);
- DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching);
- DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching);
- if (!M64_HAS(LT_LCD_REGS))
- DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch);
- aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
- aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par);
- aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par);
- if (!M64_HAS(LT_LCD_REGS)) {
- aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par);
- aty_ld_le32(LCD_INDEX, par);
- aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
- }
- }
- #endif /* CONFIG_FB_ATY_GENERIC_LCD */
- }
- static u32 calc_line_length(struct atyfb_par *par, u32 vxres, u32 bpp)
- {
- u32 line_length = vxres * bpp / 8;
- if (par->ram_type == SGRAM ||
- (!M64_HAS(XL_MEM) && par->ram_type == WRAM))
- line_length = (line_length + 63) & ~63;
- return line_length;
- }
- static int aty_var_to_crtc(const struct fb_info *info,
- const struct fb_var_screeninfo *var,
- struct crtc *crtc)
- {
- struct atyfb_par *par = (struct atyfb_par *) info->par;
- u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
- u32 sync, vmode, vdisplay;
- u32 h_total, h_disp, h_sync_strt, h_sync_end, h_sync_dly, h_sync_wid, h_sync_pol;
- u32 v_total, v_disp, v_sync_strt, v_sync_end, v_sync_wid, v_sync_pol, c_sync;
- u32 pix_width, dp_pix_width, dp_chain_mask;
- u32 line_length;
- /* input */
- xres = (var->xres + 7) & ~7;
- yres = var->yres;
- vxres = (var->xres_virtual + 7) & ~7;
- vyres = var->yres_virtual;
- xoffset = (var->xoffset + 7) & ~7;
- yoffset = var->yoffset;
- bpp = var->bits_per_pixel;
- if (bpp == 16)
- bpp = (var->green.length == 5) ? 15 : 16;
- sync = var->sync;
- vmode = var->vmode;
- /* convert (and round up) and validate */
- if (vxres < xres + xoffset)
- vxres = xres + xoffset;
- h_disp = xres;
- if (vyres < yres + yoffset)
- vyres = yres + yoffset;
- v_disp = yres;
- if (bpp <= 8) {
- bpp = 8;
- pix_width = CRTC_PIX_WIDTH_8BPP;
- dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
- BYTE_ORDER_LSB_TO_MSB;
- dp_chain_mask = DP_CHAIN_8BPP;
- } else if (bpp <= 15) {
- bpp = 16;
- pix_width = CRTC_PIX_WIDTH_15BPP;
- dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP |
- BYTE_ORDER_LSB_TO_MSB;
- dp_chain_mask = DP_CHAIN_15BPP;
- } else if (bpp <= 16) {
- bpp = 16;
- pix_width = CRTC_PIX_WIDTH_16BPP;
- dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP |
- BYTE_ORDER_LSB_TO_MSB;
- dp_chain_mask = DP_CHAIN_16BPP;
- } else if (bpp <= 24 && M64_HAS(INTEGRATED)) {
- bpp = 24;
- pix_width = CRTC_PIX_WIDTH_24BPP;
- dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
- BYTE_ORDER_LSB_TO_MSB;
- dp_chain_mask = DP_CHAIN_24BPP;
- } else if (bpp <= 32) {
- bpp = 32;
- pix_width = CRTC_PIX_WIDTH_32BPP;
- dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP |
- BYTE_ORDER_LSB_TO_MSB;
- dp_chain_mask = DP_CHAIN_32BPP;
- } else
- FAIL("invalid bpp");
- line_length = calc_line_length(par, vxres, bpp);
- if (vyres * line_length > info->fix.smem_len)
- FAIL("not enough video RAM");
- h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
- v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
- if ((xres > 1600) || (yres > 1200)) {
- FAIL("MACH64 chips are designed for max 1600x1200\n"
- "select anoter resolution.");
- }
- h_sync_strt = h_disp + var->right_margin;
- h_sync_end = h_sync_strt + var->hsync_len;
- h_sync_dly = var->right_margin & 7;
- h_total = h_sync_end + h_sync_dly + var->left_margin;
- v_sync_strt = v_disp + var->lower_margin;
- v_sync_end = v_sync_strt + var->vsync_len;
- v_total = v_sync_end + var->upper_margin;
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- if (par->lcd_table != 0) {
- if (!M64_HAS(LT_LCD_REGS)) {
- u32 lcd_index = aty_ld_le32(LCD_INDEX, par);
- crtc->lcd_index = lcd_index &
- ~(LCD_INDEX_MASK | LCD_DISPLAY_DIS |
- LCD_SRC_SEL | CRTC2_DISPLAY_DIS);
- aty_st_le32(LCD_INDEX, lcd_index, par);
- }
- if (!M64_HAS(MOBIL_BUS))
- crtc->lcd_index |= CRTC2_DISPLAY_DIS;
- crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par) | 0x4000;
- crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par) & ~CRTC_RW_SELECT;
- crtc->lcd_gen_cntl &=
- ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | TVCLK_PM_EN |
- /*VCLK_DAC_PM_EN | USE_SHADOWED_VEND |*/
- USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN);
- crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT;
- if ((crtc->lcd_gen_cntl & LCD_ON) &&
- ((xres > par->lcd_width) || (yres > par->lcd_height))) {
- /*
- * We cannot display the mode on the LCD. If the CRT is
- * enabled we can turn off the LCD.
- * If the CRT is off, it isn't a good idea to switch it
- * on; we don't know if one is connected. So it's better
- * to fail then.
- */
- if (crtc->lcd_gen_cntl & CRT_ON) {
- if (!(var->activate & FB_ACTIVATE_TEST))
- PRINTKI("Disable LCD panel, because video mode does not fit.\n");
- crtc->lcd_gen_cntl &= ~LCD_ON;
- /*aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);*/
- } else {
- if (!(var->activate & FB_ACTIVATE_TEST))
- PRINTKE("Video mode exceeds size of LCD panel.\nConnect this computer to a conventional monitor if you really need this mode.\n");
- return -EINVAL;
- }
- }
- }
- if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) {
- int VScan = 1;
- /* bpp -> bytespp, 1,4 -> 0; 8 -> 2; 15,16 -> 1; 24 -> 6; 32 -> 5
- const u8 DFP_h_sync_dly_LT[] = { 0, 2, 1, 6, 5 };
- const u8 ADD_to_strt_wid_and_dly_LT_DAC[] = { 0, 5, 6, 9, 9, 12, 12 }; */
- vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED);
- /*
- * This is horror! When we simulate, say 640x480 on an 800x600
- * LCD monitor, the CRTC should be programmed 800x600 values for
- * the non visible part, but 640x480 for the visible part.
- * This code has been tested on a laptop with it's 1400x1050 LCD
- * monitor and a conventional monitor both switched on.
- * Tested modes: 1280x1024, 1152x864, 1024x768, 800x600,
- * works with little glitches also with DOUBLESCAN modes
- */
- if (yres < par->lcd_height) {
- VScan = par->lcd_height / yres;
- if (VScan > 1) {
- VScan = 2;
- vmode |= FB_VMODE_DOUBLE;
- }
- }
- h_sync_strt = h_disp + par->lcd_right_margin;
- h_sync_end = h_sync_strt + par->lcd_hsync_len;
- h_sync_dly = /*DFP_h_sync_dly[ ( bpp + 1 ) / 3 ]; */par->lcd_hsync_dly;
- h_total = h_disp + par->lcd_hblank_len;
- v_sync_strt = v_disp + par->lcd_lower_margin / VScan;
- v_sync_end = v_sync_strt + par->lcd_vsync_len / VScan;
- v_total = v_disp + par->lcd_vblank_len / VScan;
- }
- #endif /* CONFIG_FB_ATY_GENERIC_LCD */
- h_disp = (h_disp >> 3) - 1;
- h_sync_strt = (h_sync_strt >> 3) - 1;
- h_sync_end = (h_sync_end >> 3) - 1;
- h_total = (h_total >> 3) - 1;
- h_sync_wid = h_sync_end - h_sync_strt;
- FAIL_MAX("h_disp too large", h_disp, 0xff);
- FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff);
- /*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/
- if (h_sync_wid > 0x1f)
- h_sync_wid = 0x1f;
- FAIL_MAX("h_total too large", h_total, 0x1ff);
- if (vmode & FB_VMODE_DOUBLE) {
- v_disp <<= 1;
- v_sync_strt <<= 1;
- v_sync_end <<= 1;
- v_total <<= 1;
- }
- vdisplay = yres;
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON))
- vdisplay = par->lcd_height;
- #endif
- v_disp--;
- v_sync_strt--;
- v_sync_end--;
- v_total--;
- v_sync_wid = v_sync_end - v_sync_strt;
- FAIL_MAX("v_disp too large", v_disp, 0x7ff);
- FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff);
- /*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/
- if (v_sync_wid > 0x1f)
- v_sync_wid = 0x1f;
- FAIL_MAX("v_total too large", v_total, 0x7ff);
- c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0;
- /* output */
- crtc->vxres = vxres;
- crtc->vyres = vyres;
- crtc->xoffset = xoffset;
- crtc->yoffset = yoffset;
- crtc->bpp = bpp;
- crtc->off_pitch =
- ((yoffset * line_length + xoffset * bpp / 8) / 8) |
- ((line_length / bpp) << 22);
- crtc->vline_crnt_vline = 0;
- crtc->h_tot_disp = h_total | (h_disp << 16);
- crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly << 8) |
- ((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16) |
- (h_sync_pol << 21);
- crtc->v_tot_disp = v_total | (v_disp << 16);
- crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
- (v_sync_pol << 21);
- /* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */
- crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync;
- crtc->gen_cntl |= CRTC_VGA_LINEAR;
- /* Enable doublescan mode if requested */
- if (vmode & FB_VMODE_DOUBLE)
- crtc->gen_cntl |= CRTC_DBL_SCAN_EN;
- /* Enable interlaced mode if requested */
- if (vmode & FB_VMODE_INTERLACED)
- crtc->gen_cntl |= CRTC_INTERLACE_EN;
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- if (par->lcd_table != 0) {
- vdisplay = yres;
- if (vmode & FB_VMODE_DOUBLE)
- vdisplay <<= 1;
- crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH);
- crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 |
- /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/
- USE_SHADOWED_VEND |
- USE_SHADOWED_ROWCUR |
- SHADOW_EN | SHADOW_RW_EN);
- crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR/* | LOCK_8DOT*/;
- /* MOBILITY M1 tested, FIXME: LT */
- crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
- if (!M64_HAS(LT_LCD_REGS))
- crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) &
- ~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3);
- crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO |
- HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO |
- HORZ_STRETCH_MODE | HORZ_STRETCH_EN);
- if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) {
- do {
- /*
- * The horizontal blender misbehaves when
- * HDisplay is less than a certain threshold
- * (440 for a 1024-wide panel). It doesn't
- * stretch such modes enough. Use pixel
- * replication instead of blending to stretch
- * modes that can be made to exactly fit the
- * panel width. The undocumented "NoLCDBlend"
- * option allows the pixel-replicated mode to
- * be slightly wider or narrower than the
- * panel width. It also causes a mode that is
- * exactly half as wide as the panel to be
- * pixel-replicated, rather than blended.
- */
- int HDisplay = xres & ~7;
- int nStretch = par->lcd_width / HDisplay;
- int Remainder = par->lcd_width % HDisplay;
- if ((!Remainder && ((nStretch > 2))) ||
- (((HDisplay * 16) / par->lcd_width) < 7)) {
- static const char StretchLoops[] = { 10, 12, 13, 15, 16 };
- int horz_stretch_loop = -1, BestRemainder;
- int Numerator = HDisplay, Denominator = par->lcd_width;
- int Index = 5;
- ATIReduceRatio(&Numerator, &Denominator);
- BestRemainder = (Numerator * 16) / Denominator;
- while (--Index >= 0) {
- Remainder = ((Denominator - Numerator) * StretchLoops[Index]) %
- Denominator;
- if (Remainder < BestRemainder) {
- horz_stretch_loop = Index;
- if (!(BestRemainder = Remainder))
- break;
- }
- }
- if ((horz_stretch_loop >= 0) && !BestRemainder) {
- int horz_stretch_ratio = 0, Accumulator = 0;
- int reuse_previous = 1;
- Index = StretchLoops[horz_stretch_loop];
- while (--Index >= 0) {
- if (Accumulator > 0)
- horz_stretch_ratio |= reuse_previous;
- else
- Accumulator += Denominator;
- Accumulator -= Numerator;
- reuse_previous <<= 1;
- }
- crtc->horz_stretching |= (HORZ_STRETCH_EN |
- ((horz_stretch_loop & HORZ_STRETCH_LOOP) << 16) |
- (horz_stretch_ratio & HORZ_STRETCH_RATIO));
- break; /* Out of the do { ... } while (0) */
- }
- }
- crtc->horz_stretching |= (HORZ_STRETCH_MODE | HORZ_STRETCH_EN |
- (((HDisplay * (HORZ_STRETCH_BLEND + 1)) / par->lcd_width) & HORZ_STRETCH_BLEND));
- } while (0);
- }
- if (vdisplay < par->lcd_height && crtc->lcd_gen_cntl & LCD_ON) {
- crtc->vert_stretching = (VERT_STRETCH_USE0 | VERT_STRETCH_EN |
- (((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0));
- if (!M64_HAS(LT_LCD_REGS) &&
- xres <= (M64_HAS(MOBIL_BUS) ? 1024 : 800))
- crtc->ext_vert_stretch |= VERT_STRETCH_MODE;
- } else {
- /*
- * Don't use vertical blending if the mode is too wide
- * or not vertically stretched.
- */
- crtc->vert_stretching = 0;
- }
- /* copy to shadow crtc */
- crtc->shadow_h_tot_disp = crtc->h_tot_disp;
- crtc->shadow_h_sync_strt_wid = crtc->h_sync_strt_wid;
- crtc->shadow_v_tot_disp = crtc->v_tot_disp;
- crtc->shadow_v_sync_strt_wid = crtc->v_sync_strt_wid;
- }
- #endif /* CONFIG_FB_ATY_GENERIC_LCD */
- if (M64_HAS(MAGIC_FIFO)) {
- /* FIXME: display FIFO low watermark values */
- crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_FIFO_LWM);
- }
- crtc->dp_pix_width = dp_pix_width;
- crtc->dp_chain_mask = dp_chain_mask;
- return 0;
- }
- static int aty_crtc_to_var(const struct crtc *crtc,
- struct fb_var_screeninfo *var)
- {
- u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync;
- u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
- u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
- u32 pix_width;
- u32 double_scan, interlace;
- /* input */
- h_total = crtc->h_tot_disp & 0x1ff;
- h_disp = (crtc->h_tot_disp >> 16) & 0xff;
- h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | ((crtc->h_sync_strt_wid >> 4) & 0x100);
- h_sync_dly = (crtc->h_sync_strt_wid >> 8) & 0x7;
- h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x1f;
- h_sync_pol = (crtc->h_sync_strt_wid >> 21) & 0x1;
- v_total = crtc->v_tot_disp & 0x7ff;
- v_disp = (crtc->v_tot_disp >> 16) & 0x7ff;
- v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
- v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f;
- v_sync_pol = (crtc->v_sync_strt_wid >> 21) & 0x1;
- c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
- pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
- double_scan = crtc->gen_cntl & CRTC_DBL_SCAN_EN;
- interlace = crtc->gen_cntl & CRTC_INTERLACE_EN;
- /* convert */
- xres = (h_disp + 1) * 8;
- yres = v_disp + 1;
- left = (h_total - h_sync_strt - h_sync_wid) * 8 - h_sync_dly;
- right = (h_sync_strt - h_disp) * 8 + h_sync_dly;
- hslen = h_sync_wid * 8;
- upper = v_total - v_sync_strt - v_sync_wid;
- lower = v_sync_strt - v_disp;
- vslen = v_sync_wid;
- sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
- (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
- (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
- switch (pix_width) {
- #if 0
- case CRTC_PIX_WIDTH_4BPP:
- bpp = 4;
- var->red.offset = 0;
- var->red.length = 8;
- var->green.offset = 0;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- break;
- #endif
- case CRTC_PIX_WIDTH_8BPP:
- bpp = 8;
- var->red.offset = 0;
- var->red.length = 8;
- var->green.offset = 0;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- break;
- case CRTC_PIX_WIDTH_15BPP: /* RGB 555 */
- bpp = 16;
- var->red.offset = 10;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 5;
- var->blue.offset = 0;
- var->blue.length = 5;
- var->transp.offset = 0;
- var->transp.length = 0;
- break;
- case CRTC_PIX_WIDTH_16BPP: /* RGB 565 */
- bpp = 16;
- var->red.offset = 11;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 6;
- var->blue.offset = 0;
- var->blue.length = 5;
- var->transp.offset = 0;
- var->transp.length = 0;
- break;
- case CRTC_PIX_WIDTH_24BPP: /* RGB 888 */
- bpp = 24;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- break;
- case CRTC_PIX_WIDTH_32BPP: /* ARGB 8888 */
- bpp = 32;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 24;
- var->transp.length = 8;
- break;
- default:
- PRINTKE("Invalid pixel width\n");
- return -EINVAL;
- }
- /* output */
- var->xres = xres;
- var->yres = yres;
- var->xres_virtual = crtc->vxres;
- var->yres_virtual = crtc->vyres;
- var->bits_per_pixel = bpp;
- var->left_margin = left;
- var->right_margin = right;
- var->upper_margin = upper;
- var->lower_margin = lower;
- var->hsync_len = hslen;
- var->vsync_len = vslen;
- var->sync = sync;
- var->vmode = FB_VMODE_NONINTERLACED;
- /*
- * In double scan mode, the vertical parameters are doubled,
- * so we need to halve them to get the right values.
- * In interlaced mode the values are already correct,
- * so no correction is necessary.
- */
- if (interlace)
- var->vmode = FB_VMODE_INTERLACED;
- if (double_scan) {
- var->vmode = FB_VMODE_DOUBLE;
- var->yres >>= 1;
- var->upper_margin >>= 1;
- var->lower_margin >>= 1;
- var->vsync_len >>= 1;
- }
- return 0;
- }
- /* ------------------------------------------------------------------------- */
- static int atyfb_set_par(struct fb_info *info)
- {
- struct atyfb_par *par = (struct atyfb_par *) info->par;
- struct fb_var_screeninfo *var = &info->var;
- u32 tmp, pixclock;
- int err;
- #ifdef DEBUG
- struct fb_var_screeninfo debug;
- u32 pixclock_in_ps;
- #endif
- if (par->asleep)
- return 0;
- err = aty_var_to_crtc(info, var, &par->crtc);
- if (err)
- return err;
- pixclock = atyfb_get_pixclock(var, par);
- if (pixclock == 0) {
- PRINTKE("Invalid pixclock\n");
- return -EINVAL;
- } else {
- err = par->pll_ops->var_to_pll(info, pixclock,
- var->bits_per_pixel, &par->pll);
- if (err)
- return err;
- }
- par->accel_flags = var->accel_flags; /* hack */
- if (var->accel_flags) {
- info->fbops->fb_sync = atyfb_sync;
- info->flags &= ~FBINFO_HWACCEL_DISABLED;
- } else {
- info->fbops->fb_sync = NULL;
- info->flags |= FBINFO_HWACCEL_DISABLED;
- }
- if (par->blitter_may_be_busy)
- wait_for_idle(par);
- aty_set_crtc(par, &par->crtc);
- par->dac_ops->set_dac(info, &par->pll,
- var->bits_per_pixel, par->accel_flags);
- par->pll_ops->set_pll(info, &par->pll);
- #ifdef DEBUG
- if (par->pll_ops && par->pll_ops->pll_to_var)
- pixclock_in_ps = par->pll_ops->pll_to_var(info, &par->pll);
- else
- pixclock_in_ps = 0;
- if (0 == pixclock_in_ps) {
- PRINTKE("ALERT ops->pll_to_var get 0\n");
- pixclock_in_ps = pixclock;
- }
- memset(&debug, 0, sizeof(debug));
- if (!aty_crtc_to_var(&par->crtc, &debug)) {
- u32 hSync, vRefresh;
- u32 h_disp, h_sync_strt, h_sync_end, h_total;
- u32 v_disp, v_sync_strt, v_sync_end, v_total;
- h_disp = debug.xres;
- h_sync_strt = h_disp + debug.right_margin;
- h_sync_end = h_sync_strt + debug.hsync_len;
- h_total = h_sync_end + debug.left_margin;
- v_disp = debug.yres;
- v_sync_strt = v_disp + debug.lower_margin;
- v_sync_end = v_sync_strt + debug.vsync_len;
- v_total = v_sync_end + debug.upper_margin;
- hSync = 1000000000 / (pixclock_in_ps * h_total);
- vRefresh = (hSync * 1000) / v_total;
- if (par->crtc.gen_cntl & CRTC_INTERLACE_EN)
- vRefresh *= 2;
- if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
- vRefresh /= 2;
- DPRINTK("atyfb_set_par\n");
- DPRINTK(" Set Visible Mode to %ix%i-%i\n",
- var->xres, var->yres, var->bits_per_pixel);
- DPRINTK(" Virtual resolution %ix%i, "
- "pixclock_in_ps %i (calculated %i)\n",
- var->xres_virtual, var->yres_virtual,
- pixclock, pixclock_in_ps);
- DPRINTK(" Dot clock: %i MHz\n",
- 1000000 / pixclock_in_ps);
- DPRINTK(" Horizontal sync: %i kHz\n", hSync);
- DPRINTK(" Vertical refresh: %i Hz\n", vRefresh);
- DPRINTK(" x style: %i.%03i %i %i %i %i %i %i %i %i\n",
- 1000000 / pixclock_in_ps, 1000000 % pixclock_in_ps,
- h_disp, h_sync_strt, h_sync_end, h_total,
- v_disp, v_sync_strt, v_sync_end, v_total);
- DPRINTK(" fb style: %i %i %i %i %i %i %i %i %i\n",
- pixclock_in_ps,
- debug.left_margin, h_disp, debug.right_margin, debug.hsync_len,
- debug.upper_margin, v_disp, debug.lower_margin, debug.vsync_len);
- }
- #endif /* DEBUG */
- if (!M64_HAS(INTEGRATED)) {
- /* Don't forget MEM_CNTL */
- tmp = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff;
- switch (var->bits_per_pixel) {
- case 8:
- tmp |= 0x02000000;
- break;
- case 16:
- tmp |= 0x03000000;
- break;
- case 32:
- tmp |= 0x06000000;
- break;
- }
- aty_st_le32(MEM_CNTL, tmp, par);
- } else {
- tmp = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff;
- if (!M64_HAS(MAGIC_POSTDIV))
- tmp |= par->mem_refresh_rate << 20;
- switch (var->bits_per_pixel) {
- case 8:
- case 24:
- tmp |= 0x00000000;
- break;
- case 16:
- tmp |= 0x04000000;
- break;
- case 32:
- tmp |= 0x08000000;
- break;
- }
- if (M64_HAS(CT_BUS)) {
- aty_st_le32(DAC_CNTL, 0x87010184, par);
- aty_st_le32(BUS_CNTL, 0x680000f9, par);
- } else if (M64_HAS(VT_BUS)) {
- aty_st_le32(DAC_CNTL, 0x87010184, par);
- aty_st_le32(BUS_CNTL, 0x680000f9, par);
- } else if (M64_HAS(MOBIL_BUS)) {
- aty_st_le32(DAC_CNTL, 0x80010102, par);
- aty_st_le32(BUS_CNTL, 0x7b33a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
- } else {
- /* GT */
- aty_st_le32(DAC_CNTL, 0x86010102, par);
- aty_st_le32(BUS_CNTL, 0x7b23a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
- aty_st_le32(EXT_MEM_CNTL, aty_ld_le32(EXT_MEM_CNTL, par) | 0x5000001, par);
- }
- aty_st_le32(MEM_CNTL, tmp, par);
- }
- aty_st_8(DAC_MASK, 0xff, par);
- info->fix.line_length = calc_line_length(par, var->xres_virtual,
- var->bits_per_pixel);
- info->fix.visual = var->bits_per_pixel <= 8 ?
- FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
- /* Initialize the graphics engine */
- if (par->accel_flags & FB_ACCELF_TEXT)
- aty_init_engine(par, info);
- #ifdef CONFIG_BOOTX_TEXT
- btext_update_display(info->fix.smem_start,
- (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8,
- ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1,
- var->bits_per_pixel,
- par->crtc.vxres * var->bits_per_pixel / 8);
- #endif /* CONFIG_BOOTX_TEXT */
- #if 0
- /* switch to accelerator mode */
- if (!(par->crtc.gen_cntl & CRTC_EXT_DISP_EN))
- aty_st_le32(CRTC_GEN_CNTL, par->crtc.gen_cntl | CRTC_EXT_DISP_EN, par);
- #endif
- #ifdef DEBUG
- {
- /* dump non shadow CRTC, pll, LCD registers */
- int i; u32 base;
- /* CRTC registers */
- base = 0x2000;
- printk("debug atyfb: Mach64 non-shadow register values:");
- for (i = 0; i < 256; i = i+4) {
- if (i % 16 == 0)
- printk("\ndebug atyfb: 0x%04X: ", base + i);
- printk(" %08X", aty_ld_le32(i, par));
- }
- printk("\n\n");
- #ifdef CONFIG_FB_ATY_CT
- /* PLL registers */
- base = 0x00;
- printk("debug atyfb: Mach64 PLL register values:");
- for (i = 0; i < 64; i++) {
- if (i % 16 == 0)
- printk("\ndebug atyfb: 0x%02X: ", base + i);
- if (i % 4 == 0)
- printk(" ");
- printk("%02X", aty_ld_pll_ct(i, par));
- }
- printk("\n\n");
- #endif /* CONFIG_FB_ATY_CT */
- #ifdef CONFIG_FB_ATY_GENERIC_LCD
- if (par->lcd_table != 0) {
- /* LCD registers */
- base = 0x00;
- printk("debug atyfb: LCD register values:");
- if (M64_HAS(LT_LCD_REGS)) {
- for (i = 0; i <= POWER_MANAGEMENT; i++) {
- if (i == EXT_VERT_STRETCH)
- continue;
- printk("\ndebug atyfb: 0x%04X: ",
- lt_lcd_regs[i]);
- printk(" %08X", aty_ld_lcd(i, par));
- }
- } else {
- for (i = 0; i < 64; i++) {
- if (i % 4 == 0)
- printk("\ndebug atyfb: 0x%02X: ",
- base + i);
- printk(" %08X", aty_ld_lcd(i, par));
- }
- }
- printk("\n\n");
- }
- #endif /* CONFIG_FB_ATY_GENERIC_LCD */
- }
- #endif /* DEBUG */
- return 0;
- }
- static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
- {
- struct atyfb_par *par = (struct atyfb_par *) info->par;
- int err;
- struct crtc crtc;
- union aty_pll pll;
- u32 pixclock;
- memcpy(&pll, &par->pll, sizeof(pll));
- err = aty_var_to_crtc(info, var, &crtc);
- if (err)
- return err;
- pixclock = atyfb_get_pixclock(var, par);
- if (pixclock == 0) {
- if (!(var->activate & FB_ACTIVATE_TEST))
- PRINTKE("Invalid pixclock\n");
- return -EINVAL;
- } else {
- err = par->pll_ops->var_to_pll(info, pixclock,
- var->bits_per_pixel, &pll);
- if (err)
- return err;
- }
- if (var->accel_flags & FB_ACCELF_TEXT)
- info->var.accel_flags = FB_ACCELF_TEXT;
- else
- info->var.accel_flags = 0;
- aty_crtc_to_var(&crtc, var);
- var->pixclock = par->pll_ops->pll_to_var(info, &pll);
- return 0;
- }
- static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info)
- {
- u32 xoffset = info->var.xoffset;
- u32 yoffset = info->var.yoffset;
- u32 line_length = info->fix.line_length;
- u32 bpp = info->var.bits_per_pixel;
- par->crtc.off_pitch =
- ((yoffset * line_length + xoffset * bpp / 8) / 8) |
- ((line_length / bpp) << 22);
- }
- /*
- * Open/Release the frame buffer device
- */
- static int atyfb_open(struct fb_info *info, int user)
- {
- struct atyfb_par *par = (struct atyfb_par *) info->par;
- if (user) {
- par->open++;
- #ifdef __sparc__
- par->mmaped = 0;
- #endif
- }
- return 0;
- }
- static irqreturn_t aty_irq(int irq, void *dev_id)
- {
- struct atyfb_par *par = dev_id;
- int handled = 0;
- u32 int_cntl;
- spin_lock(&par->int_lock);
- int_cntl = aty_ld_le32(CRTC_INT_CNTL, par);
- if (int_cntl & CRTC_VBLANK_INT) {
- /* clear interrupt */
- aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) |
- CRTC_VBLANK_INT_AK, par);
- par->vblank.count++;
- if (par->vblank.pan_display) {
- par->vblank.pan_display = 0;
- aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
- }
- wake_up_interruptible(&par->vblank.wait);
- handled = 1;
- }
- spin_unlock(&par->int_lock);
- return IRQ_RETVAL(handled);
- }
- static int aty_enable_irq(struct atyfb_par *par, int reenable)
- {
- u32 int_cntl;
- if (!test_and_set_bit(0, &par->irq_flags)) {
- if (request_irq(par->irq, aty_irq, IRQF_SHARED, "atyfb", par)) {
- clear_bit(0, &par->irq_flags);
- return -EINVAL;
-