From: "Antonino A. Daplas" <adaplas@hotpop.com> Alexander Kern <alex.kern@gmx.de> [PATCH] port Daniel Mantione 2.4 driver to 2.6 [PATCH] add more pci_id number [PATCH] add accelerated imgblit [PATCH] revert SDRAM_MAGIC_PLL to old behaviour [PATCH] do a "from BIOS" initialisation only by __i386__ Arnaud FONTAINE <arnaud.fontaine@free.fr> [PATCH atyfb] correction for 3D Rage Mobility L Geert Uytterhoeven <geert@linux-m68k.org> [PATCH atyfb] Atari Atyfb fixes [PATCH atyfb] Atyfb on Mach64 GX or Atari [PATCH 468] m68k sparse floating point James Simmons <jsimmons@infradead.org> [PATCH add] port to framebuffer_alloc api Nicolas Souchu <nsouch@free.fr> [PATCH] I do not found a copy, but it was incorporated too Ville Syrj�l� <syrjala@sci.fi> [PATCH] fix pan with doublescan [PATCH] another double scan fix [PATCH] disable linear aperture register access [PATCH] Memory type correction [PATCH] atyfb (2.6): Fix mmio_start [PATCH] atyfb (2.6): Fix mem_refresh_rate for Mobility [PATCH] atyfb (2.6): Add RGB565 support [PATCH] atyfb: Blank LCD by turning off backlight voltage [PATCH] atyfb: Rage LT LCD register access [PATCH] atyfb: vblank irq support [PATCH] atyfb: MTRR support Signed-off-by: Antonino Daplas <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@osdl.org> --- 25-sparc64-akpm/drivers/video/Kconfig | 19 25-sparc64-akpm/drivers/video/aty/ati_ids.h | 1 25-sparc64-akpm/drivers/video/aty/atyfb.h | 207 - 25-sparc64-akpm/drivers/video/aty/atyfb_base.c | 3624 ++++++++++++++-------- 25-sparc64-akpm/drivers/video/aty/mach64_accel.c | 212 + 25-sparc64-akpm/drivers/video/aty/mach64_ct.c | 731 +++- 25-sparc64-akpm/drivers/video/aty/mach64_cursor.c | 309 - 25-sparc64-akpm/drivers/video/aty/mach64_gx.c | 20 25-sparc64-akpm/drivers/video/aty/xlinit.c | 68 25-sparc64-akpm/include/video/mach64.h | 351 +- 10 files changed, 3704 insertions(+), 1838 deletions(-) diff -puN drivers/video/aty/ati_ids.h~fbdev-various-mach64-changes drivers/video/aty/ati_ids.h --- 25-sparc64/drivers/video/aty/ati_ids.h~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/ati_ids.h 2004-10-22 00:41:34.000000000 -0700 @@ -64,6 +64,7 @@ #define PCI_CHIP_MACH64LQ 0x4C51 #define PCI_CHIP_MACH64LR 0x4C52 #define PCI_CHIP_MACH64LS 0x4C53 +#define PCI_CHIP_MACH64LT 0x4C54 #define PCI_CHIP_RADEON_LW 0x4C57 #define PCI_CHIP_RADEON_LX 0x4C58 #define PCI_CHIP_RADEON_LY 0x4C59 diff -puN drivers/video/aty/atyfb_base.c~fbdev-various-mach64-changes drivers/video/aty/atyfb_base.c --- 25-sparc64/drivers/video/aty/atyfb_base.c~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/atyfb_base.c 2004-10-22 00:57:43.321141648 -0700 @@ -1,6 +1,7 @@ /* * 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) @@ -24,10 +25,13 @@ * 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 ! */ @@ -38,15 +42,16 @@ - 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 the BIOS or Open Firmware (if they are initialized). + by Open Firmware (if they are initialized). BIOS is done - (Anyone to help with this?) + (Anyone with Mac to help with this?) ******************************************************************************/ #include <linux/config.h> #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> @@ -54,19 +59,20 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/delay.h> -#include <linux/selection.h> #include <linux/console.h> #include <linux/fb.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/vt_kern.h> -#include <linux/kd.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/wait.h> #include <asm/io.h> #include <asm/uaccess.h> #include <video/mach64.h> #include "atyfb.h" +#include "ati_ids.h" #ifdef __powerpc__ #include <asm/prom.h> @@ -87,23 +93,102 @@ #ifdef CONFIG_PMAC_BACKLIGHT #include <asm/backlight.h> #endif - +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif /* * Debug flags. */ -#undef DEBUG +/*#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(x) do { printk(x "\n"); return -EINVAL; } while (0) +#define FAIL(msg) do { printk(KERN_CRIT "atyfb: " msg "\n"); return -EINVAL; } while (0) +#define FAIL_MAX(msg, x, _max_) do { if(x > _max_) { 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) +static const u32 lt_lcd_regs[] = { + CONFIG_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 */ @@ -138,22 +223,19 @@ static struct fb_fix_screeninfo atyfb_fi 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_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); + 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 inode *inode, struct file *file, u_int cmd, - u_long arg, struct fb_info *info); + u_long arg, struct fb_info *info); extern void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect); extern void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area); extern void atyfb_imageblit(struct fb_info *info, const struct fb_image *image); #ifdef __sparc__ -static int atyfb_mmap(struct fb_info *info, struct file *file, - struct vm_area_struct *vma); +static int atyfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma); #endif static int atyfb_sync(struct fb_info *info); @@ -166,15 +248,14 @@ static int aty_init(struct fb_info *info static int store_video_par(char *videopar, unsigned char m64_num); #endif -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); +static struct crtc saved_crtc; +static union aty_pll saved_pll; +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 @@ -184,10 +265,19 @@ static int read_aty_sense(const struct a * Interface used by the world */ -int atyfb_init(void); -#ifndef MODULE -int atyfb_setup(char *); -#endif +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, @@ -202,22 +292,22 @@ static struct fb_ops atyfb_ops = { .fb_fillrect = atyfb_fillrect, .fb_copyarea = atyfb_copyarea, .fb_imageblit = atyfb_imageblit, - .fb_cursor = soft_cursor, + .fb_cursor = atyfb_cursor, #ifdef __sparc__ .fb_mmap = atyfb_mmap, #endif .fb_sync = atyfb_sync, }; -static char curblink __initdata = 1; -static char noaccel __initdata = 0; -static u32 default_vram __initdata = 0; -static int default_pll __initdata = 0; -static int default_mclk __initdata = 0; - -#ifndef MODULE -static char *mode_option __initdata = NULL; -#endif +static int noaccel; +#ifdef CONFIG_MTRR +static int nomtrr; +#endif +static int vram; +static int pll; +static int mclk; +static int xclk; +static char *mode; #ifdef CONFIG_PPC static int default_vmode __initdata = VMODE_CHOOSE; @@ -231,177 +321,185 @@ static unsigned long phys_size[FB_MAX] _ static unsigned long phys_guiregbase[FB_MAX] __initdata = { 0, }; #endif -#ifdef CONFIG_FB_ATY_GX -static char m64n_gx[] __initdata = "mach64GX (ATI888GX00)"; -static char m64n_cx[] __initdata = "mach64CX (ATI888CX00)"; -#endif /* CONFIG_FB_ATY_GX */ -#ifdef CONFIG_FB_ATY_CT -static char m64n_ct[] __initdata = "mach64CT (ATI264CT)"; -static char m64n_et[] __initdata = "mach64ET (ATI264ET)"; -static char m64n_vta3[] __initdata = "mach64VTA3 (ATI264VT)"; -static char m64n_vta4[] __initdata = "mach64VTA4 (ATI264VT)"; -static char m64n_vtb[] __initdata = "mach64VTB (ATI264VTB)"; -static char m64n_vt4[] __initdata = "mach64VT4 (ATI264VT4)"; -static char m64n_gt[] __initdata = "3D RAGE (GT)"; -static char m64n_gtb[] __initdata = "3D RAGE II+ (GTB)"; -static char m64n_iic_p[] __initdata = "3D RAGE IIC (PCI)"; -static char m64n_iic_a[] __initdata = "3D RAGE IIC (AGP)"; -static char m64n_lt[] __initdata = "3D RAGE LT"; -static char m64n_ltg[] __initdata = "3D RAGE LT-G"; -static char m64n_gtc_ba[] __initdata = "3D RAGE PRO (BGA, AGP)"; -static char m64n_gtc_ba1[] __initdata = "3D RAGE PRO (BGA, AGP, 1x only)"; -static char m64n_gtc_bp[] __initdata = "3D RAGE PRO (BGA, PCI)"; -static char m64n_gtc_pp[] __initdata = "3D RAGE PRO (PQFP, PCI)"; -static char m64n_gtc_ppl[] __initdata = - "3D RAGE PRO (PQFP, PCI, limited 3D)"; -static char m64n_xl[] __initdata = "3D RAGE (XL)"; -static char m64n_ltp_a[] __initdata = "3D RAGE LT PRO (AGP)"; -static char m64n_ltp_p[] __initdata = "3D RAGE LT PRO (PCI)"; -static char m64n_mob_p[] __initdata = "3D RAGE Mobility (PCI)"; -static char m64n_mob_a[] __initdata = "3D RAGE Mobility (AGP)"; -#endif /* CONFIG_FB_ATY_CT */ +/* 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) + +#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) +#define ATI_CHIP_MOBILITY (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_MOBIL_BUS) static struct { - u16 pci_id, chip_type; - u8 rev_mask, rev_val; + u16 pci_id; const char *name; - int pll, mclk; + int pll, mclk, xclk; u32 features; } aty_chips[] __initdata = { #ifdef CONFIG_FB_ATY_GX /* Mach64 GX */ - { - 0x4758, 0x00d7, 0x00, 0x00, m64n_gx, 135, 50, M64F_GX}, { - 0x4358, 0x0057, 0x00, 0x00, m64n_cx, 135, 50, M64F_GX}, -#endif /* CONFIG_FB_ATY_GX */ + { PCI_CHIP_MACH64GX, "ATI888GX00 (Mach64 GX)", 135, 50, 50, ATI_CHIP_88800GX }, + { PCI_CHIP_MACH64CX, "ATI888CX00 (Mach64 CX)", 135, 50, 50, ATI_CHIP_88800CX }, +#endif /* CONFIG_FB_ATY_GX */ + #ifdef CONFIG_FB_ATY_CT - /* Mach64 CT */ - { - 0x4354, 0x4354, 0x00, 0x00, m64n_ct, 135, 60, - M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | - M64F_MAGIC_FIFO}, { - 0x4554, 0x4554, 0x00, 0x00, m64n_et, 135, 60, - M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | - M64F_MAGIC_FIFO}, - /* Mach64 VT */ - { - 0x5654, 0x5654, 0xc7, 0x00, m64n_vta3, 170, 67, - M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | - M64F_MAGIC_FIFO | M64F_FIFO_24}, { - 0x5654, 0x5654, 0xc7, 0x40, m64n_vta4, 200, 67, - M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | - M64F_MAGIC_FIFO | M64F_FIFO_24 | M64F_MAGIC_POSTDIV}, { - 0x5654, 0x5654, 0x00, 0x00, m64n_vtb, 200, 67, - M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | - M64F_GTB_DSP | M64F_FIFO_24}, { - 0x5655, 0x5655, 0x00, 0x00, m64n_vtb, 200, 67, - M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | - M64F_GTB_DSP | M64F_FIFO_24 | M64F_SDRAM_MAGIC_PLL}, { - 0x5656, 0x5656, 0x00, 0x00, m64n_vt4, 230, 83, - M64F_VT | M64F_INTEGRATED | M64F_GTB_DSP}, - /* Mach64 GT (3D RAGE) */ - { - 0x4754, 0x4754, 0x07, 0x00, m64n_gt, 135, 63, - M64F_GT | M64F_INTEGRATED | M64F_MAGIC_FIFO | - M64F_FIFO_24 | M64F_EXTRA_BRIGHT}, { - 0x4754, 0x4754, 0x07, 0x01, m64n_gt, 170, 67, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | - M64F_FIFO_24 | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x4754, 0x4754, 0x07, 0x02, m64n_gt, 200, 67, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | - M64F_FIFO_24 | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x4755, 0x4755, 0x00, 0x00, m64n_gtb, 200, 67, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | - M64F_FIFO_24 | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x4756, 0x4756, 0x00, 0x00, m64n_iic_p, 230, 83, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | - M64F_FIFO_24 | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x4757, 0x4757, 0x00, 0x00, m64n_iic_a, 230, 83, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | - M64F_FIFO_24 | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x475a, 0x475a, 0x00, 0x00, m64n_iic_a, 230, 83, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | - M64F_FIFO_24 | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, - /* Mach64 LT */ - { - 0x4c54, 0x4c54, 0x00, 0x00, m64n_lt, 135, 63, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP}, { - 0x4c47, 0x4c47, 0x00, 0x00, m64n_ltg, 230, 63, - M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | - M64F_SDRAM_MAGIC_PLL | M64F_EXTRA_BRIGHT | - M64F_LT_SLEEP | M64F_G3_PB_1024x768}, - /* Mach64 GTC (3D RAGE PRO) */ - { - 0x4742, 0x4742, 0x00, 0x00, m64n_gtc_ba, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x4744, 0x4744, 0x00, 0x00, m64n_gtc_ba1, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x4749, 0x4749, 0x00, 0x00, m64n_gtc_bp, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT | M64F_MAGIC_VRAM_SIZE}, { - 0x4750, 0x4750, 0x00, 0x00, m64n_gtc_pp, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, { - 0x4751, 0x4751, 0x00, 0x00, m64n_gtc_ppl, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT}, - /* 3D RAGE XL */ - { - 0x4752, 0x4752, 0x00, 0x00, m64n_xl, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | - M64F_EXTRA_BRIGHT | M64F_XL_DLL}, - /* Mach64 LT PRO */ - { - 0x4c42, 0x4c42, 0x00, 0x00, m64n_ltp_a, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP}, { - 0x4c44, 0x4c44, 0x00, 0x00, m64n_ltp_p, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP}, { - 0x4c49, 0x4c49, 0x00, 0x00, m64n_ltp_p, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_EXTRA_BRIGHT | - M64F_G3_PB_1_1 | M64F_G3_PB_1024x768}, { - 0x4c50, 0x4c50, 0x00, 0x00, m64n_ltp_p, 230, 100, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP}, - /* 3D RAGE Mobility */ - { - 0x4c4d, 0x4c4d, 0x00, 0x00, m64n_mob_p, 230, 50, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_MOBIL_BUS}, { - 0x4c4e, 0x4c4e, 0x00, 0x00, m64n_mob_a, 230, 50, - M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | - M64F_GTB_DSP | M64F_MOBIL_BUS}, -#endif /* CONFIG_FB_ATY_CT */ + { PCI_CHIP_MACH64CT, "ATI264CT (Mach64 CT)", 135, 60, 60, ATI_CHIP_264CT }, + { PCI_CHIP_MACH64ET, "ATI264ET (Mach64 ET)", 135, 60, 60, ATI_CHIP_264ET }, + { PCI_CHIP_MACH64VT, "ATI264VT? (Mach64 VT)", 170, 67, 67, ATI_CHIP_264VT }, + { PCI_CHIP_MACH64GT, "3D RAGE (Mach64 GT)", 135, 63, 63, ATI_CHIP_264GT }, + /* FIXME { ...ATI_264GU, maybe ATI_CHIP_264GTDVD }, */ + { PCI_CHIP_MACH64GU, "3D RAGE II+ (Mach64 GTB)", 200, 67, 67, ATI_CHIP_264GTB }, + { PCI_CHIP_MACH64VU, "ATI264VTB (Mach64 VU)", 200, 67, 67, ATI_CHIP_264VT3 }, + + { PCI_CHIP_MACH64LT, "3D RAGE LT (Mach64 LT)", 135, 63, 63, ATI_CHIP_264LT }, + /* FIXME chipset maybe ATI_CHIP_264LTPRO ? */ + { PCI_CHIP_MACH64LG, "3D RAGE LT-G (Mach64 LG)", 230, 63, 63, ATI_CHIP_264LTG | M64F_LT_LCD_REGS | M64F_G3_PB_1024x768 }, + + { PCI_CHIP_MACH64VV, "ATI264VT4 (Mach64 VV)", 230, 83, 83, ATI_CHIP_264VT4 }, + + { PCI_CHIP_MACH64GV, "3D RAGE IIC (Mach64 GV, PCI)", 230, 83, 83, ATI_CHIP_264GT2C }, + { PCI_CHIP_MACH64GW, "3D RAGE IIC (Mach64 GW, AGP)", 230, 83, 83, ATI_CHIP_264GT2C }, + { PCI_CHIP_MACH64GY, "3D RAGE IIC (Mach64 GY, PCI)", 230, 83, 83, ATI_CHIP_264GT2C }, + { PCI_CHIP_MACH64GZ, "3D RAGE IIC (Mach64 GZ, AGP)", 230, 83, 83, ATI_CHIP_264GT2C }, + + { PCI_CHIP_MACH64GB, "3D RAGE PRO (Mach64 GB, BGA, AGP)", 230, 100, 100, ATI_CHIP_264GTPRO }, + { PCI_CHIP_MACH64GD, "3D RAGE PRO (Mach64 GD, BGA, AGP 1x)", 230, 100, 100, ATI_CHIP_264GTPRO }, + { PCI_CHIP_MACH64GI, "3D RAGE PRO (Mach64 GI, BGA, PCI)", 230, 100, 100, ATI_CHIP_264GTPRO | M64F_MAGIC_VRAM_SIZE }, + { PCI_CHIP_MACH64GP, "3D RAGE PRO (Mach64 GP, PQFP, PCI)", 230, 100, 100, ATI_CHIP_264GTPRO }, + { PCI_CHIP_MACH64GQ, "3D RAGE PRO (Mach64 GQ, PQFP, PCI, limited 3D)", 230, 100, 100, ATI_CHIP_264GTPRO }, + + { PCI_CHIP_MACH64LB, "3D RAGE LT PRO (Mach64 LB, AGP)", 236, 75, 100, ATI_CHIP_264LTPRO }, + { PCI_CHIP_MACH64LD, "3D RAGE LT PRO (Mach64 LD, AGP)", 230, 100, 100, ATI_CHIP_264LTPRO }, + { PCI_CHIP_MACH64LI, "3D RAGE LT PRO (Mach64 LI, PCI)", 230, 100, 100, 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, ATI_CHIP_264LTPRO }, + { PCI_CHIP_MACH64LQ, "3D RAGE LT PRO (Mach64 LQ, PCI)", 230, 100, 100, ATI_CHIP_264LTPRO }, + + { PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP)", 230, 83, 63, ATI_CHIP_264XL }, + { PCI_CHIP_MACH64GN, "3D RAGE XL (Mach64 GN, AGP)", 230, 83, 63, ATI_CHIP_264XL }, + { PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66/BGA)", 230, 83, 63, ATI_CHIP_264XL }, + { PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33MHz)", 230, 83, 63, ATI_CHIP_264XL }, + { PCI_CHIP_MACH64GL, "3D RAGE XL (Mach64 GL, PCI)", 230, 83, 63, ATI_CHIP_264XL }, + { PCI_CHIP_MACH64GS, "3D RAGE XL (Mach64 GS, PCI)", 230, 83, 63, ATI_CHIP_264XL }, + + { PCI_CHIP_MACH64LM, "3D RAGE Mobility P/M (Mach64 LM, AGP 2x)", 230, 83, 125, ATI_CHIP_MOBILITY }, + { PCI_CHIP_MACH64LN, "3D RAGE Mobility L (Mach64 LN, AGP 2x)", 230, 83, 125, ATI_CHIP_MOBILITY }, + { PCI_CHIP_MACH64LR, "3D RAGE Mobility P/M (Mach64 LR, PCI)", 230, 83, 125, ATI_CHIP_MOBILITY }, + { PCI_CHIP_MACH64LS, "3D RAGE Mobility L (Mach64 LS, PCI)", 230, 83, 125, ATI_CHIP_MOBILITY }, +#endif /* CONFIG_FB_ATY_CT */ }; +/* can not fail */ +static int __devinit correct_chipset(struct atyfb_par *par) +{ + u8 rev; + u16 type; + u32 chip_id; + const char *name; + int i; + + for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--) + if (par->pci_id == aty_chips[i].pci_id) + break; + + 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->features = aty_chips[i].features; + + chip_id = aty_ld_le32(CONFIG_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: + rev &= 0xc7; + if(rev == 0x00) { + name = "ATI264VTA3 (Mach64 VT)"; + par->pll_limits.pll_max = 170; + par->pll_limits.mclk = 67; + par->pll_limits.xclk = 67; + par->features = ATI_CHIP_264VT; + } else if(rev == 0x40) { + name = "ATI264VTA4 (Mach64 VT)"; + par->pll_limits.pll_max = 200; + par->pll_limits.mclk = 67; + par->pll_limits.xclk = 67; + par->features = ATI_CHIP_264VT | M64F_MAGIC_POSTDIV; + } else { + name = "ATI264VTB (Mach64 VT)"; + par->pll_limits.pll_max = 200; + par->pll_limits.mclk = 67; + par->pll_limits.xclk = 67; + par->features = ATI_CHIP_264VTB; + } + break; + case PCI_CHIP_MACH64GT: + rev &= 0x07; + if(rev == 0x01) { + par->pll_limits.pll_max = 170; + par->pll_limits.mclk = 67; + par->pll_limits.xclk = 67; + par->features = ATI_CHIP_264GTB; + } else if(rev == 0x02) { + par->pll_limits.pll_max = 200; + par->pll_limits.mclk = 67; + par->pll_limits.xclk = 67; + par->features = ATI_CHIP_264GTB; + } + break; +#endif + } + + PRINTKI("%s [0x%04x rev 0x%02x]\n", name, type, rev); + return 0; +} + static char ram_dram[] __initdata = "DRAM"; +static char ram_resv[] __initdata = "RESV"; #ifdef CONFIG_FB_ATY_GX static char ram_vram[] __initdata = "VRAM"; #endif /* CONFIG_FB_ATY_GX */ #ifdef CONFIG_FB_ATY_CT static char ram_edo[] __initdata = "EDO"; -static char ram_sdram[] __initdata = "SDRAM"; -static char ram_sgram[] __initdata = "SGRAM"; -static char ram_wram[] __initdata = "WRAM"; +static char ram_sdram[] __initdata = "SDRAM (1:1)"; +static char ram_sgram[] __initdata = "SGRAM (1:1)"; +static char ram_sdram32[] __initdata = "SDRAM (2:1) (32-bit)"; static char ram_off[] __initdata = "OFF"; #endif /* CONFIG_FB_ATY_CT */ -static char ram_resv[] __initdata = "RESV"; + static u32 pseudo_palette[17]; @@ -410,109 +508,226 @@ static char *aty_gx_ram[8] __initdata = ram_dram, ram_vram, ram_vram, ram_dram, ram_dram, ram_vram, ram_vram, ram_resv }; -#endif /* CONFIG_FB_ATY_GX */ +#endif /* CONFIG_FB_ATY_GX */ #ifdef CONFIG_FB_ATY_CT static char *aty_ct_ram[8] __initdata = { ram_off, ram_dram, ram_edo, ram_edo, - ram_sdram, ram_sgram, ram_wram, ram_resv + ram_sdram, ram_sgram, ram_sdram32, ram_resv }; -#endif /* CONFIG_FB_ATY_CT */ +#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 - */ +/* + * Apple monitor sense + */ static int __init read_aty_sense(const struct atyfb_par *par) { int sense, i; - aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */ + aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */ __delay(200); - aty_st_le32(GP_IO, 0, par); /* turn off outputs */ + aty_st_le32(GP_IO, 0, par); /* turn off outputs */ __delay(2000); - i = aty_ld_le32(GP_IO, par); /* get primary sense value */ + 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 */ + 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 */ + aty_st_le32(GP_IO, 0x20002000, par); /* drive A high again */ __delay(200); - aty_st_le32(GP_IO, 0x10000000, par); /* drive B low */ + 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 */ + aty_st_le32(GP_IO, 0x10001000, par); /* drive B high again */ __delay(200); - aty_st_le32(GP_IO, 0x01000000, par); /* drive C low */ + 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 */ + aty_st_le32(GP_IO, 0, par); /* turn off outputs */ return sense; } -#endif /* defined(CONFIG_PPC) */ - -#if defined(CONFIG_PMAC_PBOOK) || defined(CONFIG_PMAC_BACKLIGHT) -static void aty_st_lcd(int index, u32 val, const struct atyfb_par *par) -{ - 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); -} - -static u32 aty_ld_lcd(int index, const struct atyfb_par *par) -{ - 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 /* CONFIG_PMAC_PBOOK || CONFIG_PMAC_BACKLIGHT */ +#endif /* defined(CONFIG_PPC) */ /* ------------------------------------------------------------------------- */ - /* - * CRTC programming - */ +/* + * CRTC programming + */ -static void aty_set_crtc(const struct atyfb_par *par, - const struct crtc *crtc) +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(CONFIG_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(CONFIG_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"); + PRINTKI("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_VLINE_CRNT_VLINE, 0, 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); + + PRINTKI("set secondary 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 int aty_var_to_crtc(const struct fb_info *info, - const struct fb_var_screeninfo *var, - struct crtc *crtc) + 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 left, right, upper, lower, hslen, vslen, sync, vmode; - 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 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; /* input */ @@ -523,53 +738,19 @@ static int aty_var_to_crtc(const struct xoffset = var->xoffset; yoffset = var->yoffset; bpp = var->bits_per_pixel; - left = var->left_margin; - right = var->right_margin; - upper = var->upper_margin; - lower = var->lower_margin; - hslen = var->hsync_len; - vslen = var->vsync_len; + if (bpp == 16) + bpp = (var->green.length == 5) ? 15 : 16; sync = var->sync; vmode = var->vmode; /* convert (and round up) and validate */ - xres = (xres + 7) & ~7; - xoffset = (xoffset + 7) & ~7; - vxres = (vxres + 7) & ~7; if (vxres < xres + xoffset) vxres = xres + xoffset; - h_disp = xres / 8 - 1; - if (h_disp > 0xff) - FAIL("h_disp too large"); - h_sync_strt = h_disp + (right / 8); - if (h_sync_strt > 0x1ff) - FAIL("h_sync_start too large"); - h_sync_dly = right & 7; - h_sync_wid = (hslen + 7) / 8; - if (h_sync_wid > 0x1f) - FAIL("h_sync_wid too large"); - h_total = h_sync_strt + h_sync_wid + (h_sync_dly + left + 7) / 8; - if (h_total > 0x1ff) - FAIL("h_total too large"); - h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1; + h_disp = xres; if (vyres < yres + yoffset) vyres = yres + yoffset; - v_disp = yres - 1; - if (v_disp > 0x7ff) - FAIL("v_disp too large"); - v_sync_strt = v_disp + lower; - if (v_sync_strt > 0x7ff) - FAIL("v_sync_strt too large"); - v_sync_wid = vslen; - if (v_sync_wid > 0x1f) - FAIL("v_sync_wid too large"); - v_total = v_sync_strt + v_sync_wid + upper; - if (v_total > 0x7ff) - FAIL("v_total too large"); - v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1; - - c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0; + v_disp = yres; if (bpp <= 8) { bpp = 8; @@ -577,54 +758,325 @@ static int aty_var_to_crtc(const struct dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP | BYTE_ORDER_LSB_TO_MSB; - dp_chain_mask = 0x8080; - } else if (bpp <= 16) { + 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 = 0x4210; + 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 = 0x8080; + 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 = 0x8080; + dp_chain_mask = DP_CHAIN_32BPP; } else FAIL("invalid bpp"); if (vxres * vyres * bpp / 8 > info->fix.smem_len) FAIL("not enough video RAM"); - if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) - FAIL("invalid vmode"); + 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(CONFIG_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) { + 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 { + FAIL("Video mode exceeds size of lcd panel.\nConnect this computer to a conventional monitor if you really need this mode."); + } + } + } + + 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 + + if(vdisplay < 400) { + h_sync_pol = 1; + v_sync_pol = 0; + } else if(vdisplay < 480) { + h_sync_pol = 0; + v_sync_pol = 1; + } else if(vdisplay < 768) { + h_sync_pol = 0; + v_sync_pol = 0; + } else { + h_sync_pol = 1; + v_sync_pol = 1; + } + + 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->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->off_pitch = - ((yoffset * vxres + xoffset) * bpp / 64) | (vxres << 19); - crtc->gen_cntl = - pix_width | c_sync | CRTC_EXT_DISP_EN | CRTC_ENABLE; + crtc->xoffset = xoffset; + crtc->yoffset = yoffset; + crtc->bpp = bpp; + crtc->off_pitch = ((yoffset*vxres+xoffset)*bpp/64) | (vxres<<19); + 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; + if(vmode & FB_VMODE_INTERLACED) { + vdisplay >>= 1; + + /* The prefered mode for the lcd is not interlaced, so disable it if + it was enabled. For doublescan there is no problem, because we can + compensate for it in the hardware stretching (we stretch half as much) + */ + vmode &= ~FB_VMODE_INTERLACED; + /*crtc->gen_cntl &= ~CRTC_INTERLACE_EN;*/ + } + 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) { + do { + /* + * The horizontal blender misbehaves when HDisplay is less than a + * 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->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)) { /* Not VTB/GTB */ /* FIXME: magic FIFO values */ - crtc->gen_cntl |= - aty_ld_le32(CRTC_GEN_CNTL, par) & 0x000e0000; + crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC2_PIX_WIDTH); } crtc->dp_pix_width = dp_pix_width; crtc->dp_chain_mask = dp_chain_mask; @@ -632,21 +1084,19 @@ static int aty_var_to_crtc(const struct return 0; } - -static int aty_crtc_to_var(const struct crtc *crtc, - struct fb_var_screeninfo *var) +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_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; @@ -657,6 +1107,8 @@ static int aty_crtc_to_var(const struct 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; @@ -707,7 +1159,6 @@ static int aty_crtc_to_var(const struct var->transp.offset = 0; var->transp.length = 0; break; -#if 0 case CRTC_PIX_WIDTH_16BPP: /* RGB 565 */ bpp = 16; var->red.offset = 11; @@ -719,7 +1170,6 @@ static int aty_crtc_to_var(const struct var->transp.offset = 0; var->transp.length = 0; break; -#endif case CRTC_PIX_WIDTH_24BPP: /* RGB 888 */ bpp = 24; var->red.offset = 16; @@ -760,6 +1210,21 @@ static int aty_crtc_to_var(const struct 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 + half 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; } @@ -770,58 +1235,116 @@ static int atyfb_set_par(struct fb_info { struct atyfb_par *par = (struct atyfb_par *) info->par; struct fb_var_screeninfo *var = &info->var; - u8 tmp; - u32 i; + u32 tmp, pixclock; int err; +#ifdef DEBUG + struct fb_var_screeninfo debug; + u32 pixclock_in_ps; +#endif + if (par->asleep) + return 0; - if ((err = aty_var_to_crtc(info, var, &par->crtc)) || - (err = par->pll_ops->var_to_pll(info, var->pixclock, - var->bits_per_pixel, &par->pll))) + if ((err = aty_var_to_crtc(info, var, &par->crtc))) return err; - par->accel_flags = var->accel_flags; /* hack */ + pixclock = atyfb_get_pixclock(var, par); + + if (pixclock == 0) { + FAIL("Invalid pixclock"); + } else { + if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &par->pll))) + return err; + } + + par->accel_flags = var->accel_flags; /* hack */ if (par->blitter_may_be_busy) wait_for_idle(par); - tmp = aty_ld_8(CRTC_GEN_CNTL + 3, par); - aty_set_crtc(par, &par->crtc); - aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par); - /* better call aty_StrobeClock ?? */ - aty_st_8(CLOCK_CNTL + par->clk_wr_offset, CLOCK_STROBE, par); - par->dac_ops->set_dac(info, &par->pll, var->bits_per_pixel, - par->accel_flags); + 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 */ - i = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff; + tmp = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff; switch (var->bits_per_pixel) { case 8: - i |= 0x02000000; + tmp |= 0x02000000; break; case 16: - i |= 0x03000000; + tmp |= 0x03000000; break; case 32: - i |= 0x06000000; + tmp |= 0x06000000; break; } - aty_st_le32(MEM_CNTL, i, par); + aty_st_le32(MEM_CNTL, tmp, par); } else { - i = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff; + tmp = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff; if (!M64_HAS(MAGIC_POSTDIV)) - i |= par->mem_refresh_rate << 20; + tmp |= par->mem_refresh_rate << 20; switch (var->bits_per_pixel) { case 8: case 24: - i |= 0x00000000; + tmp |= 0x00000000; break; case 16: - i |= 0x04000000; + tmp |= 0x04000000; break; case 32: - i |= 0x08000000; + tmp |= 0x08000000; break; } if (M64_HAS(CT_BUS)) { @@ -832,16 +1355,14 @@ static int atyfb_set_par(struct fb_info 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); + 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); - aty_st_le32(EXT_MEM_CNTL, - aty_ld_le32(EXT_MEM_CNTL, - par) | 0x5000001, 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, i, par); + aty_st_le32(MEM_CNTL, tmp, par); } aty_st_8(DAC_MASK, 0xff, par); @@ -855,29 +1376,98 @@ static int atyfb_set_par(struct fb_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 */ + (((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) +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; - int err; + u32 pixclock; - if ((err = aty_var_to_crtc(info, var, &crtc)) || - (err = par->pll_ops->var_to_pll(info, var->pixclock, - var->bits_per_pixel, &pll))) + memcpy(&pll, &(par->pll), sizeof(pll)); + + if((err = aty_var_to_crtc(info, var, &crtc))) return err; -#if 0 /* fbmon is not done. uncomment for 2.5.x -brad */ - if (!fbmon_valid_timings(var->pixclock, htotal, vtotal, info)) + pixclock = atyfb_get_pixclock(var, par); + + if (pixclock == 0) { + FAIL("Invalid pixclock"); + } else { + if((err = par->pll_ops->var_to_pll(info, pixclock, var->bits_per_pixel, &pll))) + return err; + } + + if (var->accel_flags & FB_ACCELF_TEXT) + info->var.accel_flags = FB_ACCELF_TEXT; + else + info->var.accel_flags = 0; + +#if 0 /* fbmon is not done. uncomment for 2.5.x -brad */ + if (!fbmon_valid_timings(pixclock, htotal, vtotal, info)) return -EINVAL; #endif aty_crtc_to_var(&crtc, var); @@ -885,17 +1475,14 @@ static int atyfb_check_var(struct fb_var return 0; } -static void set_off_pitch(struct atyfb_par *par, - const struct fb_info *info) +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 vxres = par->crtc.vxres; u32 bpp = info->var.bits_per_pixel; - par->crtc.off_pitch = - ((yoffset * vxres + xoffset) * bpp / 64) | (vxres << 19); - aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par); + par->crtc.off_pitch = ((yoffset * vxres + xoffset) * bpp / 64) | (vxres << 19); } @@ -905,43 +1492,111 @@ static void set_off_pitch(struct atyfb_p static int atyfb_open(struct fb_info *info, int user) { -#ifdef __sparc__ struct atyfb_par *par = (struct atyfb_par *) info->par; if (user) { par->open++; +#ifdef __sparc__ par->mmaped = 0; - } #endif + } return (0); } -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 irqreturn_t aty_irq(int irq, void *dev_id, struct pt_regs *fp) +{ + struct atyfb_par *par = dev_id; + int handled = 0; + u32 int_cntl; -static int atyfb_release(struct fb_info *info, int user) + 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) { -#ifdef __sparc__ - struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 int_cntl; - if (user) { - par->open--; - mdelay(1); - wait_for_idle(par); - if (!par->open) { - int was_mmaped = par->mmaped; + if (!test_and_set_bit(0, &par->irq_flags)) { + if (request_irq(par->irq, aty_irq, SA_SHIRQ, "atyfb", par)) { + clear_bit(0, &par->irq_flags); + return -EINVAL; + } + spin_lock_irq(&par->int_lock); + int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; + /* clear interrupt */ + aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_AK, par); + /* enable interrupt */ + aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par); + spin_unlock_irq(&par->int_lock); + } else if (reenable) { + spin_lock_irq(&par->int_lock); + int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; + if (!(int_cntl & CRTC_VBLANK_INT_EN)) { + printk("atyfb: someone disabled IRQ [%08x]\n", int_cntl); + /* re-enable interrupt */ + aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par ); + } + spin_unlock_irq(&par->int_lock); + } - par->mmaped = 0; + return 0; +} - if (was_mmaped) { - struct fb_var_screeninfo var; +static int aty_disable_irq(struct atyfb_par *par) +{ + u32 int_cntl; - /* Now reset the default display config, we have no + if (test_and_clear_bit(0, &par->irq_flags)) { + if (par->vblank.pan_display) { + par->vblank.pan_display = 0; + aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par); + } + spin_lock_irq(&par->int_lock); + int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK; + /* disable interrupt */ + aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par ); + spin_unlock_irq(&par->int_lock); + free_irq(par->irq, par); + } + + return 0; +} + +static int atyfb_release(struct fb_info *info, int user) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + if (user) { + par->open--; + mdelay(1); + wait_for_idle(par); + if (!par->open) { +#ifdef __sparc__ + int was_mmaped = par->mmaped; + + par->mmaped = 0; + + if (was_mmaped) { + struct fb_var_screeninfo var; + + /* Now reset the default display config, we have no * idea what the program(s) which mmap'd the chip did * to the configuration, nor whether it restored it * correctly. @@ -952,21 +1607,16 @@ static int atyfb_release(struct fb_info else var.accel_flags |= FB_ACCELF_TEXT; if (var.yres == var.yres_virtual) { - u32 vram = - (info->fix.smem_len - - (PAGE_SIZE << 2)); - var.yres_virtual = - ((vram * 8) / - var.bits_per_pixel) / - var.xres_virtual; + u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); + var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual; if (var.yres_virtual < var.yres) - var.yres_virtual = - var.yres; + var.yres_virtual = var.yres; } } - } - } #endif + aty_disable_irq(par); + } + } return (0); } @@ -976,25 +1626,67 @@ static int atyfb_release(struct fb_info * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag */ -static int atyfb_pan_display(struct fb_var_screeninfo *var, - struct fb_info *info) +static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct atyfb_par *par = (struct atyfb_par *) info->par; u32 xres, yres, xoffset, yoffset; xres = (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8; yres = ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1; + if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) + yres >>= 1; xoffset = (var->xoffset + 7) & ~7; yoffset = var->yoffset; - if (xoffset + xres > par->crtc.vxres - || yoffset + yres > par->crtc.vyres) + if (xoffset + xres > par->crtc.vxres || yoffset + yres > par->crtc.vyres) return -EINVAL; info->var.xoffset = xoffset; info->var.yoffset = yoffset; + if (par->asleep) + return 0; + set_off_pitch(par, info); + if ((var->activate & FB_ACTIVATE_VBL) && !aty_enable_irq(par, 0)) { + par->vblank.pan_display = 1; + } else { + par->vblank.pan_display = 0; + aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par); + } + + return 0; +} + +static int aty_waitforvblank(struct atyfb_par *par, u32 crtc) +{ + struct aty_interrupt *vbl; + unsigned int count; + int ret; + + switch (crtc) { + case 0: + vbl = &par->vblank; + break; + default: + return -ENODEV; + } + + ret = aty_enable_irq(par, 0); + if (ret) + return ret; + + count = vbl->count; + ret = wait_event_interruptible_timeout(vbl->wait, count != vbl->count, HZ/10); + if (ret < 0) { + return ret; + } + if (ret == 0) { + aty_enable_irq(par, 1); + return -ETIMEDOUT; + } + return 0; } + #ifdef DEBUG #define ATYIO_CLKR 0x41545900 /* ATY\00 */ #define ATYIO_CLKW 0x41545901 /* ATY\01 */ @@ -1004,6 +1696,8 @@ struct atyclk { u8 pll_ref_div; u8 mclk_fb_div; u8 mclk_post_div; /* 1,2,3,4,8 */ + u8 mclk_fb_mult; /* 2 or 4 */ + u8 xclk_post_div; /* 1,2,3,4,8 */ u8 vclk_fb_div; u8 vclk_post_div; /* 1,2,3,4,6,8,12 */ u32 dsp_xclks_per_row; /* 0-16383 */ @@ -1017,12 +1711,14 @@ struct atyclk { #define ATYIO_FEATW 0x41545903 /* ATY\03 */ #endif +#ifndef FBIO_WAITFORVSYNC +#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) +#endif + static int atyfb_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg, struct fb_info *info) + u_long arg, struct fb_info *info) { -#if defined(__sparc__) || (defined(DEBUG) && defined(CONFIG_FB_ATY_CT)) struct atyfb_par *par = (struct atyfb_par *) info->par; -#endif /* __sparc__ || DEBUG */ #ifdef __sparc__ struct fbtype fbtyp; #endif @@ -1036,31 +1732,43 @@ static int atyfb_ioctl(struct inode *ino fbtyp.fb_depth = info->var.bits_per_pixel; fbtyp.fb_cmsize = info->cmap.len; fbtyp.fb_size = info->fix.smem_len; - if (copy_to_user - ((struct fbtype __user *) arg, &fbtyp, sizeof(fbtyp))) + if (copy_to_user((struct fbtype __user *) arg, &fbtyp, sizeof(fbtyp))) return -EFAULT; break; -#endif /* __sparc__ */ +#endif /* __sparc__ */ + + case FBIO_WAITFORVSYNC: + { + u32 crtc; + + if (get_user(crtc, (__u32 *) arg)) + return -EFAULT; + + return aty_waitforvblank(par, crtc); + } + break; + #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT) case ATYIO_CLKR: if (M64_HAS(INTEGRATED)) { struct atyclk clk; - union aty_pll *pll = par->pll; + union aty_pll *pll = &(par->pll); u32 dsp_config = pll->ct.dsp_config; u32 dsp_on_off = pll->ct.dsp_on_off; clk.ref_clk_per = par->ref_clk_per; clk.pll_ref_div = pll->ct.pll_ref_div; clk.mclk_fb_div = pll->ct.mclk_fb_div; clk.mclk_post_div = pll->ct.mclk_post_div_real; + clk.mclk_fb_mult = pll->ct.mclk_fb_mult; + clk.xclk_post_div = pll->ct.xclk_post_div_real; clk.vclk_fb_div = pll->ct.vclk_fb_div; clk.vclk_post_div = pll->ct.vclk_post_div_real; clk.dsp_xclks_per_row = dsp_config & 0x3fff; clk.dsp_loop_latency = (dsp_config >> 16) & 0xf; clk.dsp_precision = (dsp_config >> 20) & 7; - clk.dsp_on = dsp_on_off & 0x7ff; - clk.dsp_off = (dsp_on_off >> 16) & 0x7ff; - if (copy_to_user - ((struct atyclk *) arg, &clk, sizeof(clk))) + clk.dsp_off = dsp_on_off & 0x7ff; + clk.dsp_on = (dsp_on_off >> 16) & 0x7ff; + if (copy_to_user((struct atyclk *) arg, &clk, sizeof(clk))) return -EFAULT; } else return -EINVAL; @@ -1068,27 +1776,21 @@ static int atyfb_ioctl(struct inode *ino case ATYIO_CLKW: if (M64_HAS(INTEGRATED)) { struct atyclk clk; - union aty_pll *pll = par->pll; - if (copy_from_user - (&clk, (struct atyclk *) arg, sizeof(clk))) + union aty_pll *pll = &(par->pll); + if (copy_from_user(&clk, (struct atyclk *) arg, sizeof(clk))) return -EFAULT; par->ref_clk_per = clk.ref_clk_per; pll->ct.pll_ref_div = clk.pll_ref_div; pll->ct.mclk_fb_div = clk.mclk_fb_div; pll->ct.mclk_post_div_real = clk.mclk_post_div; + pll->ct.mclk_fb_mult = clk.mclk_fb_mult; + pll->ct.xclk_post_div_real = clk.xclk_post_div; pll->ct.vclk_fb_div = clk.vclk_fb_div; pll->ct.vclk_post_div_real = clk.vclk_post_div; - pll->ct.dsp_config = - (clk. - dsp_xclks_per_row & 0x3fff) | ((clk. - dsp_loop_latency - & 0xf) << 16) - | ((clk.dsp_precision & 7) << 20); - pll->ct.dsp_on_off = - (clk. - dsp_on & 0x7ff) | ((clk. - dsp_off & 0x7ff) << 16); - aty_calc_pll_ct(info, &pll->ct); + pll->ct.dsp_config = (clk.dsp_xclks_per_row & 0x3fff) | + ((clk.dsp_loop_latency & 0xf)<<16)| ((clk.dsp_precision & 7)<<20); + pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) | ((clk.dsp_on & 0x7ff)<<16); + /*aty_calc_pll_ct(info, &pll->ct);*/ aty_set_pll_ct(info, pll); } else return -EINVAL; @@ -1101,7 +1803,7 @@ static int atyfb_ioctl(struct inode *ino if (put_user(par->features, (u32 *) arg)) return -EFAULT; break; -#endif /* DEBUG && CONFIG_FB_ATY_CT */ +#endif /* DEBUG && CONFIG_FB_ATY_CT */ default: return -EINVAL; } @@ -1118,8 +1820,7 @@ static int atyfb_sync(struct fb_info *in } #ifdef __sparc__ -static int atyfb_mmap(struct fb_info *info, struct file *file, - struct vm_area_struct *vma) +static int atyfb_mmap(struct fb_info *info, struct file *file, struct vm_area_struct *vma) { struct atyfb_par *par = (struct atyfb_par *) info->par; unsigned int size, page, map_size = 0; @@ -1243,83 +1944,22 @@ static void atyfb_palette(int enter) info->var.yoffset = atyfb_save.yoffset; set_off_pitch(par, info); } + aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par); break; } } } -#endif /* __sparc__ */ - +#endif /* __sparc__ */ -#ifdef CONFIG_PMAC_PBOOK -static struct fb_info *first_display = NULL; +#if defined(CONFIG_PM) && defined(CONFIG_PCI) /* Power management routines. Those are used for PowerBook sleep. - * - * It appears that Rage LT and Rage LT Pro have different power - * management registers. There's is some confusion about which - * chipID is a Rage LT or LT pro :( */ -static int aty_power_mgmt_LT(int sleep, struct atyfb_par *par) -{ - unsigned int pm; - int timeout; - - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG; - aty_st_le32(POWER_MANAGEMENT_LG, pm, par); - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - - timeout = 200000; - if (sleep) { - /* Sleep */ - pm &= ~PWR_MGT_ON; - aty_st_le32(POWER_MANAGEMENT_LG, pm, par); - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - udelay(10); - pm &= ~(PWR_BLON | AUTO_PWR_UP); - pm |= SUSPEND_NOW; - aty_st_le32(POWER_MANAGEMENT_LG, pm, par); - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - udelay(10); - pm |= PWR_MGT_ON; - aty_st_le32(POWER_MANAGEMENT_LG, pm, par); - do { - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - udelay(10); - if ((--timeout) == 0) - break; - } while ((pm & PWR_MGT_STATUS_MASK) != - PWR_MGT_STATUS_SUSPEND); - } else { - /* Wakeup */ - pm &= ~PWR_MGT_ON; - aty_st_le32(POWER_MANAGEMENT_LG, pm, par); - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - udelay(10); - pm |= (PWR_BLON | AUTO_PWR_UP); - pm &= ~SUSPEND_NOW; - aty_st_le32(POWER_MANAGEMENT_LG, pm, par); - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - udelay(10); - pm |= PWR_MGT_ON; - aty_st_le32(POWER_MANAGEMENT_LG, pm, par); - do { - pm = aty_ld_le32(POWER_MANAGEMENT_LG, par); - udelay(10); - if ((--timeout) == 0) - break; - } while ((pm & PWR_MGT_STATUS_MASK) != 0); - } - mdelay(500); - - return timeout ? PBOOK_SLEEP_OK : PBOOK_SLEEP_REFUSE; -} - -static int aty_power_mgmt_LTPro(int sleep, struct atyfb_par *par) +static int aty_power_mgmt(int sleep, struct atyfb_par *par) { - unsigned int pm; + u32 pm; int timeout; pm = aty_ld_lcd(POWER_MANAGEMENT, par); @@ -1327,7 +1967,7 @@ static int aty_power_mgmt_LTPro(int slee aty_st_lcd(POWER_MANAGEMENT, pm, par); pm = aty_ld_lcd(POWER_MANAGEMENT, par); - timeout = 200; + timeout = 2000; if (sleep) { /* Sleep */ pm &= ~PWR_MGT_ON; @@ -1346,8 +1986,7 @@ static int aty_power_mgmt_LTPro(int slee mdelay(1); if ((--timeout) == 0) break; - } while ((pm & PWR_MGT_STATUS_MASK) != - PWR_MGT_STATUS_SUSPEND); + } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND); } else { /* Wakeup */ pm &= ~PWR_MGT_ON; @@ -1368,88 +2007,92 @@ static int aty_power_mgmt_LTPro(int slee break; } while ((pm & PWR_MGT_STATUS_MASK) != 0); } + mdelay(500); - return timeout ? PBOOK_SLEEP_OK : PBOOK_SLEEP_REFUSE; + return timeout ? -1 : 0; } -static int aty_power_mgmt(int sleep, struct atyfb_par *par) +static int atyfb_pci_suspend(struct pci_dev *pdev, u32 state) { - return M64_HAS(LT_SLEEP) ? aty_power_mgmt_LT(sleep, par) - : aty_power_mgmt_LTPro(sleep, par); + struct fb_info *info = pci_get_drvdata(pdev); + struct atyfb_par *par = (struct atyfb_par *) info->par; + +#ifdef CONFIG_PPC_PMAC + /* HACK ALERT ! Once I find a proper way to say to each driver + * individually what will happen with it's PCI slot, I'll change + * that. On laptops, the AGP slot is just unclocked, so D2 is + * expected, while on desktops, the card is powered off + */ + if (state >= 3) + state = 2; +#endif /* CONFIG_PPC_PMAC */ + + if (state != 2 || state == pdev->dev.power_state) + return 0; + + acquire_console_sem(); + + fb_set_suspend(info, 1); + + /* Idle & reset engine */ + wait_for_idle(par); + aty_reset_engine(par); + + /* Blank display and LCD */ + atyfb_blank(VESA_POWERDOWN + 1, info); + + par->asleep = 1; + par->lock_blank = 1; + + /* Set chip to "suspend" mode */ + if (aty_power_mgmt(1, par)) { + par->asleep = 0; + par->lock_blank = 0; + atyfb_blank(0, info); + fb_set_suspend(info, 0); + release_console_sem(); + return -EIO; + } + + release_console_sem(); + + pdev->dev.power_state = state; + + return 0; } -/* - * Save the contents of the frame buffer when we go to sleep, - * and restore it when we wake up again. - */ -static int aty_sleep_notify(struct pmu_sleep_notifier *self, int when) +static int atyfb_pci_resume(struct pci_dev *pdev) { - struct fb_info *info; - struct atyfb_par *par; - int result; + struct fb_info *info = pci_get_drvdata(pdev); + struct atyfb_par *par = (struct atyfb_par *) info->par; - result = PBOOK_SLEEP_OK; + if (pdev->dev.power_state == 0) + return 0; - for (info = first_display; info != NULL; info = par->next) { - int nb; + acquire_console_sem(); - par = (struct atyfb_par *) info->par; - nb = info->var.yres * info->fix.line_length; - - switch (when) { - case PBOOK_SLEEP_REQUEST: - par->save_framebuffer = vmalloc(nb); - if (par->save_framebuffer == NULL) - return PBOOK_SLEEP_REFUSE; - break; - case PBOOK_SLEEP_REJECT: - if (par->save_framebuffer) { - vfree(par->save_framebuffer); - par->save_framebuffer = NULL; - } - break; - case PBOOK_SLEEP_NOW: - if (par->blitter_may_be_busy) - wait_for_idle(par); - /* Stop accel engine (stop bus mastering) */ - if (par->accel_flags & FB_ACCELF_TEXT) - aty_reset_engine(par); - - /* Backup fb content */ - if (par->save_framebuffer) - memcpy_fromio(par->save_framebuffer, - (void *) info->screen_base, nb); - - /* Blank display and LCD */ - atyfb_blank(VESA_POWERDOWN + 1, info); - - /* Set chip to "suspend" mode */ - result = aty_power_mgmt(1, par); - break; - case PBOOK_WAKE: - /* Wakeup chip */ - result = aty_power_mgmt(0, par); - - /* Restore fb content */ - if (par->save_framebuffer) { - memcpy_toio((void *) info->screen_base, - par->save_framebuffer, nb); - vfree(par->save_framebuffer); - par->save_framebuffer = NULL; - } - /* Restore display */ - atyfb_set_par(info); - atyfb_blank(0, info); - break; - } - } - return result; + if (pdev->dev.power_state == 2) + aty_power_mgmt(0, par); + par->asleep = 0; + + /* Restore display */ + atyfb_set_par(info); + + /* Refresh */ + fb_set_suspend(info, 0); + + /* Unblank */ + par->lock_blank = 0; + atyfb_blank(0, info); + + release_console_sem(); + + pdev->dev.power_state = 0; + + return 0; } -static struct pmu_sleep_notifier aty_sleep_notifier = { - aty_sleep_notify, SLEEP_LEVEL_VIDEO, -}; -#endif /* CONFIG_PMAC_PBOOK */ +#endif /* defined(CONFIG_PM) && defined(CONFIG_PCI) */ #ifdef CONFIG_PMAC_BACKLIGHT @@ -1489,9 +2132,34 @@ static struct backlight_controller aty_b aty_set_backlight_enable, aty_set_backlight_level }; -#endif /* CONFIG_PMAC_BACKLIGHT */ +#endif /* CONFIG_PMAC_BACKLIGHT */ +static void __init aty_calc_mem_refresh(struct atyfb_par *par, int xclk) +{ + const int ragepro_tbl[] = { + 44, 50, 55, 66, 75, 80, 100 + }; + const int ragexl_tbl[] = { + 50, 66, 75, 83, 90, 95, 100, 105, + 110, 115, 120, 125, 133, 143, 166 + }; + const int *refresh_tbl; + int i, size; + + if (IS_XL(par->pci_id) || IS_MOBILITY(par->pci_id)) { + refresh_tbl = ragexl_tbl; + size = sizeof(ragexl_tbl)/sizeof(int); + } else { + refresh_tbl = ragepro_tbl; + size = sizeof(ragepro_tbl)/sizeof(int); + } + for (i=0; i < size; i++) { + if (xclk < refresh_tbl[i]) + break; + } + par->mem_refresh_rate = i; +} /* * Initialisation @@ -1502,37 +2170,36 @@ static struct fb_info *fb_list = NULL; static int __init aty_init(struct fb_info *info, const char *name) { struct atyfb_par *par = (struct atyfb_par *) info->par; - const char *chipname = NULL, *ramname = NULL, *xtal; - int j, pll, mclk, gtb_memsize; + const char *ramname = NULL, *xtal; + int gtb_memsize; struct fb_var_screeninfo var; - u32 chip_id, i; - u16 type; - u8 rev; + u8 pll_ref_div; + u32 i; #if defined(CONFIG_PPC) int sense; #endif - u8 pll_ref_div; + + init_waitqueue_head(&par->vblank.wait); + spin_lock_init(&par->int_lock); par->aty_cmap_regs = (struct aty_cmap_regs *) (par->ati_regbase + 0xc0); - chip_id = aty_ld_le32(CONFIG_CHIP_ID, par); - type = chip_id & CFG_CHIP_TYPE; - rev = (chip_id & CFG_CHIP_REV) >> 24; - for (j = 0; j < (sizeof(aty_chips) / sizeof(*aty_chips)); j++) - if (type == aty_chips[j].chip_type && - (rev & aty_chips[j].rev_mask) == - aty_chips[j].rev_val) { - chipname = aty_chips[j].name; - pll = aty_chips[j].pll; - mclk = aty_chips[j].mclk; - par->features = aty_chips[j].features; - goto found; - } - printk("atyfb: Unknown mach64 0x%04x rev 0x%04x\n", type, rev); - return 0; - found: - printk("atyfb: %s [0x%04x rev 0x%02x] ", chipname, type, rev); + if (pll) + par->pll_limits.pll_max = pll; + if (mclk) + par->pll_limits.mclk = mclk; + if (xclk) + par->pll_limits.xclk = xclk; + + aty_calc_mem_refresh(par, par->pll_limits.xclk); + par->pll_per = 1000000/par->pll_limits.pll_max; + par->mclk_per = 1000000/par->pll_limits.mclk; + par->xclk_per = 1000000/par->pll_limits.xclk; + + par->ref_clk_per = 1000000000000ULL / 14318180; + xtal = "14.31818"; + #ifdef CONFIG_FB_ATY_GX if (!M64_HAS(INTEGRATED)) { u32 stat0; @@ -1549,9 +2216,7 @@ static int __init aty_init(struct fb_inf if (dac_type == 0x07) dac_subtype = DAC_ATT20C408; else - dac_subtype = - (aty_ld_8(SCRATCH_REG1 + 1, par) & 0xF0) | - dac_type; + dac_subtype = (aty_ld_8(SCRATCH_REG1 + 1, par) & 0xF0) | dac_type; #else dac_type = DAC_IBMRGB514; dac_subtype = DAC_IBMRGB514; @@ -1570,8 +2235,7 @@ static int __init aty_init(struct fb_inf par->dac_ops = &aty_dac_att21c498; break; default: - printk - (" atyfb_set_par: DAC type not implemented yet!\n"); + PRINTKI("aty_init: DAC type not implemented yet!\n"); par->dac_ops = &aty_dac_unsupported; break; } @@ -1592,33 +2256,30 @@ static int __init aty_init(struct fb_inf par->pll_ops = &aty_pll_ibm514; break; default: - printk - (" atyfb_set_par: CLK type not implemented yet!"); + PRINTKI("aty_init: CLK type not implemented yet!"); par->pll_ops = &aty_pll_unsupported; break; } } -#endif /* CONFIG_FB_ATY_GX */ +#endif /* CONFIG_FB_ATY_GX */ #ifdef CONFIG_FB_ATY_CT if (M64_HAS(INTEGRATED)) { + par->dac_ops = &aty_dac_ct; + par->pll_ops = &aty_pll_ct; par->bus_type = PCI; par->ram_type = (aty_ld_le32(CONFIG_STAT0, par) & 0x07); ramname = aty_ct_ram[par->ram_type]; - par->dac_ops = &aty_dac_ct; - par->pll_ops = &aty_pll_ct; + /* for many chips, the mclk is 67 MHz for SDRAM, 63 MHz otherwise */ - if (mclk == 67 && par->ram_type < SDRAM) - mclk = 63; + if (par->pll_limits.mclk == 67 && par->ram_type < SDRAM) + par->pll_limits.mclk = 63; } -#endif /* CONFIG_FB_ATY_CT */ - par->ref_clk_per = 1000000000000ULL / 14318180; - xtal = "14.31818"; if (M64_HAS(GTB_DSP) - && (pll_ref_div = aty_ld_pll(PLL_REF_DIV, par))) { + && (pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par))) { int diff1, diff2; - diff1 = 510 * 14 / pll_ref_div - pll; - diff2 = 510 * 29 / pll_ref_div - pll; + diff1 = 510 * 14 / pll_ref_div - par->pll_limits.pll_max; + diff2 = 510 * 29 / pll_ref_div - par->pll_limits.pll_max; if (diff1 < 0) diff1 = -diff1; if (diff2 < 0) @@ -1628,6 +2289,12 @@ static int __init aty_init(struct fb_inf xtal = "29.498928"; } } +#endif /* CONFIG_FB_ATY_CT */ + + /* save previous video mode */ + aty_get_crtc(par, &saved_crtc); + if(par->pll_ops->get_pll) + par->pll_ops->get_pll(info, &saved_pll); i = aty_ld_le32(MEM_CNTL, par); gtb_memsize = M64_HAS(GTB_DSP); @@ -1682,8 +2349,8 @@ static int __init aty_init(struct fb_inf info->fix.smem_len += 0x400000; } - if (default_vram) { - info->fix.smem_len = default_vram * 1024; + if (vram) { + info->fix.smem_len = vram * 1024; i = i & ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS); if (info->fix.smem_len <= 0x80000) i |= MEM_SIZE_512K; @@ -1701,7 +2368,7 @@ static int __init aty_init(struct fb_inf } /* - * Reg Block 0 (CT-compatible block) is at mmio_start + * Reg Block 0 (CT-compatible block) is at mmio_start * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400 */ if (M64_HAS(GX)) { @@ -1711,79 +2378,77 @@ static int __init aty_init(struct fb_inf info->fix.mmio_len = 0x400; info->fix.accel = FB_ACCEL_ATI_MACH64CT; } else if (M64_HAS(VT)) { - info->fix.mmio_start = -0x400; + info->fix.mmio_start -= 0x400; info->fix.mmio_len = 0x800; info->fix.accel = FB_ACCEL_ATI_MACH64VT; - } else { /* if (M64_HAS(GT)) */ - - info->fix.mmio_start = -0x400; + } else {/* GT */ + info->fix.mmio_start -= 0x400; info->fix.mmio_len = 0x800; info->fix.accel = FB_ACCEL_ATI_MACH64GT; } - if (default_pll) - pll = default_pll; - if (default_mclk) - mclk = default_mclk; - - printk("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK\n", - info->fix.smem_len == - 0x80000 ? 512 : (info->fix.smem_len >> 20), - info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, - xtal, pll, mclk); - - if (mclk < 44) - par->mem_refresh_rate = 0; /* 000 = 10 Mhz - 43 Mhz */ - else if (mclk < 50) - par->mem_refresh_rate = 1; /* 001 = 44 Mhz - 49 Mhz */ - else if (mclk < 55) - par->mem_refresh_rate = 2; /* 010 = 50 Mhz - 54 Mhz */ - else if (mclk < 66) - par->mem_refresh_rate = 3; /* 011 = 55 Mhz - 65 Mhz */ - else if (mclk < 75) - par->mem_refresh_rate = 4; /* 100 = 66 Mhz - 74 Mhz */ - else if (mclk < 80) - par->mem_refresh_rate = 5; /* 101 = 75 Mhz - 79 Mhz */ - else if (mclk < 100) - par->mem_refresh_rate = 6; /* 110 = 80 Mhz - 100 Mhz */ - else - par->mem_refresh_rate = 7; /* 111 = 100 Mhz and above */ - par->pll_per = 1000000 / pll; - par->mclk_per = 1000000 / mclk; + PRINTKI("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK, %d MHz XCLK\n", + info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len >> 20), + info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal, par->pll_limits.pll_max, + par->pll_limits.mclk, par->pll_limits.xclk); -#ifdef DEBUG +#if defined(DEBUG) && defined(CONFIG_ATY_CT) if (M64_HAS(INTEGRATED)) { int i; - printk - ("BUS_CNTL DAC_CNTL MEM_CNTL EXT_MEM_CNTL CRTC_GEN_CNTL " - "DSP_CONFIG DSP_ON_OFF\n" - "%08x %08x %08x %08x %08x %08x %08x\n" - "PLL", aty_ld_le32(BUS_CNTL, par), - aty_ld_le32(DAC_CNTL, par), aty_ld_le32(MEM_CNTL, - par), - aty_ld_le32(EXT_MEM_CNTL, par), - aty_ld_le32(CRTC_GEN_CNTL, par), - aty_ld_le32(DSP_CONFIG, par), aty_ld_le32(DSP_ON_OFF, - par)); - for (i = 0; i < 16; i++) - printk(" %02x", aty_ld_pll(i, par)); + printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL EXT_MEM_CNTL CRTC_GEN_CNTL " + "DSP_CONFIG DSP_ON_OFF CLOCK_CNTL\n" + "debug atyfb: %08x %08x %08x %08x %08x %08x %08x %08x\n" + "debug atyfb: PLL", + aty_ld_le32(BUS_CNTL, par), aty_ld_le32(DAC_CNTL, par), + aty_ld_le32(MEM_CNTL, par), aty_ld_le32(EXT_MEM_CNTL, par), + aty_ld_le32(CRTC_GEN_CNTL, par), aty_ld_le32(DSP_CONFIG, par), + aty_ld_le32(DSP_ON_OFF, par), aty_ld_le32(CLOCK_CNTL, par)); + for (i = 0; i < 40; i++) + printk(" %02x", aty_ld_pll_ct(i, par)); printk("\n"); } #endif + if(par->pll_ops->init_pll) + par->pll_ops->init_pll(info, &par->pll); /* * Last page of 8 MB (4 MB on ISA) aperture is MMIO * FIXME: we should use the auxiliary aperture instead so we can access * the full 8 MB of video RAM on 8 MB boards */ - if (info->fix.smem_len == 0x800000 || - (par->bus_type == ISA - && info->fix.smem_len == 0x400000)) + + if (!par->aux_start && + (info->fix.smem_len == 0x800000 || (par->bus_type == ISA && info->fix.smem_len == 0x400000))) info->fix.smem_len -= GUI_RESERVE; + /* + * Disable register access through the linear aperture + * if the auxiliary aperture is used so we can access + * the full 8 MB of video RAM on 8 MB boards. + */ + if (par->aux_start) + aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par); + +#ifdef CONFIG_MTRR + par->mtrr_aper = -1; + par->mtrr_reg = -1; + if (!nomtrr) { + /* Cover the whole resource. */ + par->mtrr_aper = mtrr_add(par->res_start, par->res_size, MTRR_TYPE_WRCOMB, 1); + if (par->mtrr_aper >= 0 && !par->aux_start) { + /* Make a hole for mmio. */ + par->mtrr_reg = mtrr_add(par->res_start + 0x800000 - GUI_RESERVE, + GUI_RESERVE, MTRR_TYPE_UNCACHABLE, 1); + if (par->mtrr_reg < 0) { + mtrr_del(par->mtrr_aper, 0, 0); + par->mtrr_aper = -1; + } + } + } +#endif + /* Clear the video memory */ - fb_memset((void *) info->screen_base, 0, - info->fix.smem_len); + fb_memset((void *) info->screen_base, 0, info->fix.smem_len); info->fbops = &atyfb_ops; info->pseudo_palette = pseudo_palette; @@ -1792,18 +2457,12 @@ static int __init aty_init(struct fb_inf #ifdef CONFIG_PMAC_BACKLIGHT if (M64_HAS(G3_PB_1_1) && machine_is_compatible("PowerBook1,1")) { /* these bits let the 101 powerbook wake up from sleep -- paulus */ - aty_st_lcd(POWER_MANAGEMENT, - aty_ld_lcd(POWER_MANAGEMENT, par) + aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) | (USE_F32KHZ | TRISTATE_MEM_EN), par); - } - if (M64_HAS(MOBIL_BUS)) - register_backlight_controller(&aty_backlight_controller, - info, "ati"); -#endif /* CONFIG_PMAC_BACKLIGHT */ + } else if (M64_HAS(MOBIL_BUS)) + register_backlight_controller(&aty_backlight_controller, info, "ati"); +#endif /* CONFIG_PMAC_BACKLIGHT */ -#ifdef MODULE - var = default_var; -#else /* !MODULE */ memset(&var, 0, sizeof(var)); #ifdef CONFIG_PPC if (_machine == _MACH_Pmac) { @@ -1811,9 +2470,8 @@ static int __init aty_init(struct fb_inf * FIXME: The NVRAM stuff should be put in a Mac-specific file, as it * applies to all Mac video cards */ - if (mode_option) { - if (!mac_find_mode - (&var, info, mode_option, 8)) + if (mode) { + if (!mac_find_mode(&var, info, mode, 8)) var = default_var; } else { if (default_vmode == VMODE_CHOOSE) { @@ -1829,484 +2487,948 @@ static int __init aty_init(struct fb_inf else default_vmode = VMODE_640_480_67; sense = read_aty_sense(par); - printk(KERN_INFO - "atyfb: monitor sense=%x, mode %d\n", - sense, - mac_map_monitor_sense(sense)); + PRINTKI("monitor sense=%x, mode %d\n", + sense, mac_map_monitor_sense(sense)); } - if (default_vmode <= 0 - || default_vmode > VMODE_MAX) + if (default_vmode <= 0 || default_vmode > VMODE_MAX) default_vmode = VMODE_640_480_60; - if (default_cmode < CMODE_8 - || default_cmode > CMODE_32) + if (default_cmode < CMODE_8 || default_cmode > CMODE_32) default_cmode = CMODE_8; - if (mac_vmode_to_var - (default_vmode, default_cmode, &var)) + if (mac_vmode_to_var(default_vmode, default_cmode, &var)) var = default_var; } } else - if (!fb_find_mode - (&var, info, mode_option, NULL, 0, NULL, 8)) - var = default_var; -#else /* !CONFIG_PPC */ -#ifdef __sparc__ - if (mode_option) { - if (!fb_find_mode - (&var, info, mode_option, NULL, 0, NULL, 8)) - var = default_var; - } else +#endif /* !CONFIG_PPC */ + if (!fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8)) var = default_var; -#else - if (!fb_find_mode - (&var, info, mode_option, NULL, 0, NULL, 8)) - var = default_var; -#endif /* !__sparc__ */ -#endif /* !CONFIG_PPC */ -#endif /* !MODULE */ + if (noaccel) var.accel_flags &= ~FB_ACCELF_TEXT; else var.accel_flags |= FB_ACCELF_TEXT; if (var.yres == var.yres_virtual) { - u32 vram = (info->fix.smem_len - (PAGE_SIZE << 2)); - var.yres_virtual = - ((vram * 8) / var.bits_per_pixel) / var.xres_virtual; + u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2)); + var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual; if (var.yres_virtual < var.yres) var.yres_virtual = var.yres; } if (atyfb_check_var(&var, info)) { - printk("atyfb: can't set default video mode\n"); - return 0; + PRINTKE("can't set default video mode\n"); + goto aty_init_exit; } + #ifdef __sparc__ atyfb_save_palette(par, 0); #endif #ifdef CONFIG_FB_ATY_CT - if (curblink && M64_HAS(INTEGRATED)) - par->cursor = aty_init_cursor(info); -#endif /* CONFIG_FB_ATY_CT */ + if (!noaccel && M64_HAS(INTEGRATED)) + aty_init_cursor(info); +#endif /* CONFIG_FB_ATY_CT */ info->var = var; fb_alloc_cmap(&info->cmap, 256, 0); if (register_framebuffer(info) < 0) - return 0; + goto aty_init_exit; fb_list = info; - printk("fb%d: %s frame buffer device on %s\n", + PRINTKI("fb%d: %s frame buffer device on %s\n", info->node, info->fix.id, name); - return 1; -} - -int __init atyfb_do_init(void) -{ -#if defined(CONFIG_PCI) - unsigned long addr, res_start, res_size; - struct atyfb_par *default_par; - struct pci_dev *pdev = NULL; - struct fb_info *info; - int i; -#ifdef __sparc__ - extern void (*prom_palette) (int); - extern int con_is_present(void); - struct pcidev_cookie *pcp; - char prop[128]; - int node, len, j; - u32 mem, chip_id; -#else - u16 tmp; -#endif + return 0; -#ifdef __sparc__ - /* Do not attach when we have a serial console. */ - if (!con_is_present()) - return -ENXIO; +aty_init_exit: + /* restore video mode */ + aty_set_crtc(par, &saved_crtc); + par->pll_ops->set_pll(info, &saved_pll); + +#ifdef CONFIG_MTRR + if (par->mtrr_reg >= 0) { + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; + } + if (par->mtrr_aper >= 0) { + mtrr_del(par->mtrr_aper, 0, 0); + par->mtrr_aper = -1; + } #endif + return -1; +} - while ((pdev = - pci_find_device(PCI_VENDOR_ID_ATI, PCI_ANY_ID, pdev))) { - if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { - struct resource *rp; - - for (i = - sizeof(aty_chips) / sizeof(*aty_chips) - 1; - i >= 0; i--) - if (pdev->device == aty_chips[i].pci_id) - break; - if (i < 0) - continue; - - rp = &pdev->resource[0]; - if (rp->flags & IORESOURCE_IO) - rp = &pdev->resource[1]; - addr = rp->start; - if (!addr) - continue; - - res_start = rp->start; - res_size = rp->end - rp->start + 1; - if (!request_mem_region - (res_start, res_size, "atyfb")) - continue; - - info = - kmalloc(sizeof(struct fb_info), GFP_ATOMIC); - if (!info) { - printk - ("atyfb_init: can't alloc fb_info\n"); - return -ENXIO; - } - memset(info, 0, sizeof(struct fb_info)); - - default_par = - kmalloc(sizeof(struct atyfb_par), GFP_ATOMIC); - if (!default_par) { - printk - ("atyfb_init: can't alloc atyfb_par\n"); - kfree(info); - return -ENXIO; - } - memset(default_par, 0, sizeof(struct atyfb_par)); +#ifdef CONFIG_ATARI +static int __init store_video_par(char *video_str, unsigned char m64_num) +{ + char *p; + unsigned long vmembase, size, guiregbase; - info->fix = atyfb_fix; - info->par = default_par; - info->device = &pdev->dev; -#ifdef __sparc__ - /* - * Map memory-mapped registers. - */ - default_par->ati_regbase = addr + 0x7ffc00UL; - info->fix.mmio_start = addr + 0x7ffc00UL; + PRINTKI("store_video_par() '%s' \n", video_str); - /* - * Map in big-endian aperture. - */ - info->screen_base = (char *) (addr + 0x800000UL); - info->fix.smem_start = addr + 0x800000UL; + if (!(p = strsep(&video_str, ";")) || !*p) + goto mach64_invalid; + vmembase = simple_strtoul(p, NULL, 0); + if (!(p = strsep(&video_str, ";")) || !*p) + goto mach64_invalid; + size = simple_strtoul(p, NULL, 0); + if (!(p = strsep(&video_str, ";")) || !*p) + goto mach64_invalid; + guiregbase = simple_strtoul(p, NULL, 0); - /* - * Figure mmap addresses from PCI config space. - * Split Framebuffer in big- and little-endian halfs. - */ - for (i = 0; i < 6 && pdev->resource[i].start; i++) - /* nothing */ ; - j = i + 4; - - default_par->mmap_map = - kmalloc(j * sizeof(*default_par->mmap_map), - GFP_ATOMIC); - if (!default_par->mmap_map) { - printk - ("atyfb_init: can't alloc mmap_map\n"); - kfree(default_par); - kfree(info); - release_mem_region(res_start, res_size); - return -ENXIO; - } - memset(default_par->mmap_map, 0, - j * sizeof(*default_par->mmap_map)); + phys_vmembase[m64_num] = vmembase; + phys_size[m64_num] = size; + phys_guiregbase[m64_num] = guiregbase; + PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size, + guiregbase); + return 0; - for (i = 0, j = 2; - i < 6 && pdev->resource[i].start; i++) { - struct resource *rp = &pdev->resource[i]; - int io, breg = - PCI_BASE_ADDRESS_0 + (i << 2); - unsigned long base; - u32 size, pbase; + mach64_invalid: + phys_vmembase[m64_num] = 0; + return -1; +} +#endif /* CONFIG_ATARI */ - base = rp->start; + /* + * Blank the display. + */ - io = (rp->flags & IORESOURCE_IO); +static int atyfb_blank(int blank, struct fb_info *info) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u8 gen_cntl; - size = rp->end - base + 1; + if (par->lock_blank || par->asleep) + return 0; - pci_read_config_dword(pdev, breg, &pbase); +#ifdef CONFIG_PMAC_BACKLIGHT + if ((_machine == _MACH_Pmac) && blank) + set_backlight_enable(0); +#elif defined(CONFIG_FB_ATY_GENERIC_LCD) + if (par->lcd_table && blank && + (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { + u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par); + pm &= ~PWR_BLON; + aty_st_lcd(POWER_MANAGEMENT, pm, par); + } +#endif - if (io) - size &= ~1; + gen_cntl = aty_ld_8(CRTC_GEN_CNTL, par); + if (blank > 0) + switch (blank - 1) { + case VESA_NO_BLANKING: + gen_cntl |= 0x40; + break; + case VESA_VSYNC_SUSPEND: + gen_cntl |= 0x8; + break; + case VESA_HSYNC_SUSPEND: + gen_cntl |= 0x4; + break; + case VESA_POWERDOWN: + gen_cntl |= 0x4c; + break; + } else + gen_cntl &= ~(0x4c); + aty_st_8(CRTC_GEN_CNTL, gen_cntl, par); - /* - * Map the framebuffer a second time, this time without - * the braindead _PAGE_IE setting. This is used by the - * fixed Xserver, but we need to maintain the old mapping - * to stay compatible with older ones... - */ - if (base == addr) { - default_par->mmap_map[j].voff = - (pbase + - 0x10000000) & PAGE_MASK; - default_par->mmap_map[j].poff = - base & PAGE_MASK; - default_par->mmap_map[j].size = - (size + - ~PAGE_MASK) & PAGE_MASK; - default_par->mmap_map[j].prot_mask = - _PAGE_CACHE; - default_par->mmap_map[j].prot_flag = - _PAGE_E; - j++; - } +#ifdef CONFIG_PMAC_BACKLIGHT + if ((_machine == _MACH_Pmac) && !blank) + set_backlight_enable(1); +#elif defined(CONFIG_FB_ATY_GENERIC_LCD) + if (par->lcd_table && !blank && + (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { + u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par); + pm |= PWR_BLON; + aty_st_lcd(POWER_MANAGEMENT, pm, par); + } +#endif - /* - * Here comes the old framebuffer mapping with _PAGE_IE - * set for the big endian half of the framebuffer... - */ - if (base == addr) { - default_par->mmap_map[j].voff = - (pbase + 0x800000) & PAGE_MASK; - default_par->mmap_map[j].poff = - (base + 0x800000) & PAGE_MASK; - default_par->mmap_map[j].size = 0x800000; - default_par->mmap_map[j].prot_mask = - _PAGE_CACHE; - default_par->mmap_map[j].prot_flag = - _PAGE_E | _PAGE_IE; - size -= 0x800000; - j++; - } + return 0; +} - default_par->mmap_map[j].voff = pbase & PAGE_MASK; - default_par->mmap_map[j].poff = base & PAGE_MASK; - default_par->mmap_map[j].size = - (size + ~PAGE_MASK) & PAGE_MASK; - default_par->mmap_map[j].prot_mask = _PAGE_CACHE; - default_par->mmap_map[j].prot_flag = _PAGE_E; - j++; - } +static void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue, + const struct atyfb_par *par) +{ +#ifdef CONFIG_ATARI + out_8(&par->aty_cmap_regs->windex, regno); + out_8(&par->aty_cmap_regs->lut, red); + out_8(&par->aty_cmap_regs->lut, green); + out_8(&par->aty_cmap_regs->lut, blue); +#else + writeb(regno, &par->aty_cmap_regs->windex); + writeb(red, &par->aty_cmap_regs->lut); + writeb(green, &par->aty_cmap_regs->lut); + writeb(blue, &par->aty_cmap_regs->lut); +#endif +} - if (pdev->device != XL_CHIP_ID) { - /* - * Fix PROMs idea of MEM_CNTL settings... - */ - mem = aty_ld_le32(MEM_CNTL, default_par); - chip_id = aty_ld_le32(CONFIG_CHIP_ID, default_par); - if (((chip_id & CFG_CHIP_TYPE) == VT_CHIP_ID) - && !((chip_id >> 24) & 1)) { - switch (mem & 0x0f) { - case 3: - mem = (mem & ~(0x0f)) | 2; - break; - case 7: - mem = (mem & ~(0x0f)) | 3; - break; - case 9: - mem = (mem & ~(0x0f)) | 4; - break; - case 11: - mem = (mem & ~(0x0f)) | 5; - break; - default: - break; - } - if ((aty_ld_le32(CONFIG_STAT0, default_par) & 7) >= SDRAM) - mem &= ~(0x00700000); - } - mem &= ~(0xcf80e000); /* Turn off all undocumented bits. */ - aty_st_le32(MEM_CNTL, mem, default_par); - } + /* + * Set a single color register. The values supplied are already + * rounded down to the hardware's capabilities (according to the + * entries in the var structure). Return != 0 for invalid regno. + * !! 4 & 8 = PSEUDO, > 8 = DIRECTCOLOR + */ - /* - * If this is the console device, we will set default video - * settings to what the PROM left us with. - */ - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "aliases"); - if (node) { - len = - prom_getproperty(node, "screen", prop, - sizeof(prop)); - if (len > 0) { - prop[len] = '\0'; - node = prom_finddevice(prop); - } else { - node = 0; - } +static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + int i, depth; + u32 *pal = info->pseudo_palette; + + depth = info->var.bits_per_pixel; + if (depth == 16) + depth = (info->var.green.length == 5) ? 15 : 16; + + if (par->asleep) + return 0; + + if (regno > 255 || + (depth == 16 && regno > 63) || + (depth == 15 && regno > 31)) + return 1; + + red >>= 8; + green >>= 8; + blue >>= 8; + + par->palette[regno].red = red; + par->palette[regno].green = green; + par->palette[regno].blue = blue; + + if (regno < 16) { + switch (depth) { + case 15: + pal[regno] = (regno << 10) | (regno << 5) | regno; + break; + case 16: + pal[regno] = (regno << 11) | (regno << 5) | regno; + break; + case 24: + pal[regno] = (regno << 16) | (regno << 8) | regno; + break; + case 32: + i = (regno << 8) | regno; + pal[regno] = (i << 16) | i; + break; + } + } + + i = aty_ld_8(DAC_CNTL, par) & 0xfc; + if (M64_HAS(EXTRA_BRIGHT)) + i |= 0x2; /* DAC_CNTL | 0x2 turns off the extra brightness for gt */ + aty_st_8(DAC_CNTL, i, par); + aty_st_8(DAC_MASK, 0xff, par); + + if (M64_HAS(INTEGRATED)) { + if (depth == 16) { + if (regno < 32) + aty_st_pal(regno << 3, red, + par->palette[regno<<1].green, + blue, par); + red = par->palette[regno>>1].red; + blue = par->palette[regno>>1].blue; + regno <<= 2; + } else if (depth == 15) { + regno <<= 3; + for(i = 0; i < 8; i++) { + aty_st_pal(regno + i, red, green, blue, par); } + } + } + aty_st_pal(regno, red, green, blue, par); - pcp = pdev->sysdata; - if (node == pcp->prom_node) { + return 0; +} - struct fb_var_screeninfo *var = - &default_var; - unsigned int N, P, Q, M, T, R; - u32 v_total, h_total; - struct crtc crtc; - u8 pll_regs[16]; - u8 clock_cntl; - - crtc.vxres = - prom_getintdefault(node, "width", - 1024); - crtc.vyres = - prom_getintdefault(node, "height", - 768); - var->bits_per_pixel = - prom_getintdefault(node, "depth", 8); - var->xoffset = var->yoffset = 0; - crtc.h_tot_disp = - aty_ld_le32(CRTC_H_TOTAL_DISP, default_par); - crtc.h_sync_strt_wid = - aty_ld_le32(CRTC_H_SYNC_STRT_WID, - default_par); - crtc.v_tot_disp = - aty_ld_le32(CRTC_V_TOTAL_DISP, default_par); - crtc.v_sync_strt_wid = - aty_ld_le32(CRTC_V_SYNC_STRT_WID, - default_par); - crtc.gen_cntl = - aty_ld_le32(CRTC_GEN_CNTL, default_par); - aty_crtc_to_var(&crtc, var); - - h_total = var->xres + var->right_margin + - var->hsync_len + var->left_margin; - v_total = var->yres + var->lower_margin + - var->vsync_len + var->upper_margin; +#ifdef CONFIG_PCI - /* - * Read the PLL to figure actual Refresh Rate. - */ - clock_cntl = aty_ld_8(CLOCK_CNTL, default_par); - /* printk("atyfb: CLOCK_CNTL: %02x\n", clock_cntl); */ - for (i = 0; i < 16; i++) - pll_regs[i] = aty_ld_pll(i, default_par); +#ifdef __sparc__ - /* - * PLL Reference Divider M: - */ - M = pll_regs[2]; +static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, struct fb_indo *info, unsigned long addr) +{ + extern void (*prom_palette) (int); + extern int con_is_present(void); - /* - * PLL Feedback Divider N (Dependant on CLOCK_CNTL): - */ - N = pll_regs[7 + (clock_cntl & 3)]; + struct atyfb_par *par = info->par; + struct pcidev_cookie *pcp; + char prop[128]; + int node, len, i, j, ret; + u32 mem, chip_id; - /* - * PLL Post Divider P (Dependant on CLOCK_CNTL): - */ - P = 1 << (pll_regs[6] >> - ((clock_cntl & 3) << 1)); + /* Do not attach when we have a serial console. */ + if (!con_is_present()) + return -ENXIO; - /* - * PLL Divider Q: - */ - Q = N / P; + /* + * Map memory-mapped registers. + */ + par->ati_regbase = addr + 0x7ffc00UL; + info->fix.mmio_start = addr + 0x7ffc00UL; - /* - * Target Frequency: - * - * T * M - * Q = ------- - * 2 * R - * - * where R is XTALIN (= 14318 or 29498 kHz). - */ - if (pdev->device == XL_CHIP_ID) - R = 29498; - else - R = 14318; + /* + * Map in big-endian aperture. + */ + info->screen_base = (char *) (addr + 0x800000UL); + info->fix.smem_start = addr + 0x800000UL; - T = 2 * Q * R / M; + /* + * Figure mmap addresses from PCI config space. + * Split Framebuffer in big- and little-endian halfs. + */ + for (i = 0; i < 6 && pdev->resource[i].start; i++) + /* nothing */ ; + j = i + 4; - default_var.pixclock = 1000000000 / T; - } -#else /* __sparc__ */ + par->mmap_map = kmalloc(j * sizeof(*par->mmap_map), GFP_ATOMIC); + if (!_par->mmap_map) { + PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n"); + return -ENOMEM; + } + memset(par->mmap_map, 0, j * sizeof(*par->mmap_map)); - info->fix.mmio_start = 0x7ff000 + addr; - default_par->ati_regbase = (unsigned long) - ioremap(info->fix.mmio_start, 0x1000); + for (i = 0, j = 2; i < 6 && pdev->resource[i].start; i++) { + struct resource *rp = &pdev->resource[i]; + int io, breg = PCI_BASE_ADDRESS_0 + (i << 2); + unsigned long base; + u32 size, pbase; - if (!default_par->ati_regbase) { -#ifdef __sparc__ - kfree(default_par->mmap_map); -#endif - kfree(default_par); - kfree(info); - release_mem_region(res_start, res_size); - return -ENOMEM; + base = rp->start; + + io = (rp->flags & IORESOURCE_IO); + + size = rp->end - base + 1; + + pci_read_config_dword(pdev, breg, &pbase); + + if (io) + size &= ~1; + + /* + * Map the framebuffer a second time, this time without + * the braindead _PAGE_IE setting. This is used by the + * fixed Xserver, but we need to maintain the old mapping + * to stay compatible with older ones... + */ + if (base == addr) { + par->mmap_map[j].voff = (pbase + 0x10000000) & PAGE_MASK; + par->mmap_map[j].poff = base & PAGE_MASK; + par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK; + par->mmap_map[j].prot_mask = _PAGE_CACHE; + par->mmap_map[j].prot_flag = _PAGE_E; + j++; + } + + /* + * Here comes the old framebuffer mapping with _PAGE_IE + * set for the big endian half of the framebuffer... + */ + if (base == addr) { + par->mmap_map[j].voff = (pbase + 0x800000) & PAGE_MASK; + par->mmap_map[j].poff = (base + 0x800000) & PAGE_MASK; + par->mmap_map[j].size = 0x800000; + par->mmap_map[j].prot_mask = _PAGE_CACHE; + par->mmap_map[j].prot_flag = _PAGE_E | _PAGE_IE; + size -= 0x800000; + j++; + } + + par->mmap_map[j].voff = pbase & PAGE_MASK; + par->mmap_map[j].poff = base & PAGE_MASK; + par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK; + par->mmap_map[j].prot_mask = _PAGE_CACHE; + par->mmap_map[j].prot_flag = _PAGE_E; + j++; + } + + if((ret = correct_chipset(par))) + return ret; + + if (IS_XL(pdev->device)) { + /* + * Fix PROMs idea of MEM_CNTL settings... + */ + mem = aty_ld_le32(MEM_CNTL, par); + chip_id = aty_ld_le32(CONFIG_CHIP_ID, par); + if (((chip_id & CFG_CHIP_TYPE) == VT_CHIP_ID) && !((chip_id >> 24) & 1)) { + switch (mem & 0x0f) { + case 3: + mem = (mem & ~(0x0f)) | 2; + break; + case 7: + mem = (mem & ~(0x0f)) | 3; + break; + case 9: + mem = (mem & ~(0x0f)) | 4; + break; + case 11: + mem = (mem & ~(0x0f)) | 5; + break; + default: + break; } + if ((aty_ld_le32(CONFIG_STAT0, par) & 7) >= SDRAM) + mem &= ~(0x00700000); + } + mem &= ~(0xcf80e000); /* Turn off all undocumented bits. */ + aty_st_le32(MEM_CNTL, mem, par); + } - info->fix.mmio_start += 0xc00; - default_par->ati_regbase += 0xc00; + /* + * If this is the console device, we will set default video + * settings to what the PROM left us with. + */ + node = prom_getchild(prom_root_node); + node = prom_searchsiblings(node, "aliases"); + if (node) { + len = prom_getproperty(node, "screen", prop, sizeof(prop)); + if (len > 0) { + prop[len] = '\0'; + node = prom_finddevice(prop); + } else + node = 0; + } - /* - * Enable memory-space accesses using config-space - * command register. - */ - pci_read_config_word(pdev, PCI_COMMAND, &tmp); - if (!(tmp & PCI_COMMAND_MEMORY)) { - tmp |= PCI_COMMAND_MEMORY; - pci_write_config_word(pdev, PCI_COMMAND, - tmp); + pcp = pdev->sysdata; + if (node == pcp->prom_node) { + struct fb_var_screeninfo *var = &default_var; + unsigned int N, P, Q, M, T, R; + u32 v_total, h_total; + struct crtc crtc; + u8 pll_regs[16]; + u8 clock_cntl; + + crtc.vxres = prom_getintdefault(node, "width", 1024); + crtc.vyres = prom_getintdefault(node, "height", 768); + var->bits_per_pixel = prom_getintdefault(node, "depth", 8); + var->xoffset = var->yoffset = 0; + 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.gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par); + aty_crtc_to_var(&crtc, var); + + h_total = var->xres + var->right_margin + var->hsync_len + var->left_margin; + v_total = var->yres + var->lower_margin + var->vsync_len + var->upper_margin; + + /* + * Read the PLL to figure actual Refresh Rate. + */ + clock_cntl = aty_ld_8(CLOCK_CNTL, par); + /* DPRINTK("CLOCK_CNTL %02x\n", clock_cntl); */ + for (i = 0; i < 16; i++) + pll_regs[i] = aty_ld_pll_ct(i, par); + + /* + * PLL Reference Divider M: + */ + M = pll_regs[2]; + + /* + * PLL Feedback Divider N (Dependant on CLOCK_CNTL): + */ + N = pll_regs[7 + (clock_cntl & 3)]; + + /* + * PLL Post Divider P (Dependant on CLOCK_CNTL): + */ + P = 1 << (pll_regs[6] >> ((clock_cntl & 3) << 1)); + + /* + * PLL Divider Q: + */ + Q = N / P; + + /* + * Target Frequency: + * + * T * M + * Q = ------- + * 2 * R + * + * where R is XTALIN (= 14318 or 29498 kHz). + */ + if (IS_XL(pdev->device)) + R = 29498; + else + R = 14318; + + T = 2 * Q * R / M; + + default_var.pixclock = 1000000000 / T; + } + + return 0; +} + +#else /* __sparc__ */ + +#ifdef __i386__ +#ifdef CONFIG_FB_ATY_GENERIC_LCD +void aty_init_lcd(struct atyfb_par *par, u32 bios_base) +{ + u32 driv_inf_tab, sig; + u16 lcd_ofs; + + /* To support an LCD panel, we should know it's dimensions and + * it's desired pixel clock. + * There are two ways to do it: + * - Check the startup video mode and calculate the panel + * size from it. This is unreliable. + * - Read it from the driver information table in the video BIOS. + */ + /* Address of driver information table is at offset 0x78. */ + driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78)); + + /* Check for the driver information table signature. */ + sig = (*(u32 *)driv_inf_tab); + if ((sig == 0x54504c24) || /* Rage LT pro */ + (sig == 0x544d5224) || /* Rage mobility */ + (sig == 0x54435824) || /* Rage XC */ + (sig == 0x544c5824)) { /* Rage XL */ + PRINTKI("BIOS contains driver information table.\n"); + lcd_ofs = (*(u16 *)(driv_inf_tab + 10)); + par->lcd_table = 0; + if (lcd_ofs != 0) { + par->lcd_table = bios_base + lcd_ofs; + } + } + + if (par->lcd_table != 0) { + char model[24]; + char strbuf[16]; + char refresh_rates_buf[100]; + int id, tech, f, i, m, default_refresh_rate; + char *txtcolour; + char *txtmonitor; + char *txtdual; + char *txtformat; + u16 width, height, panel_type, refresh_rates; + u16 *lcdmodeptr; + u32 format; + u8 lcd_refresh_rates[16] = {50,56,60,67,70,72,75,76,85,90,100,120,140,150,160,200}; + /* The most important information is the panel size at + * offset 25 and 27, but there's some other nice information + * which we print to the screen. + */ + id = *(u8 *)par->lcd_table; + strncpy(model,(char *)par->lcd_table+1,24); + model[23]=0; + + width = par->lcd_width = *(u16 *)(par->lcd_table+25); + height = par->lcd_height = *(u16 *)(par->lcd_table+27); + panel_type = *(u16 *)(par->lcd_table+29); + if (panel_type & 1) + txtcolour = "colour"; + else + txtcolour = "monochrome"; + if (panel_type & 2) + txtdual = "dual (split) "; + else + txtdual = ""; + tech = (panel_type>>2) & 63; + switch (tech) { + case 0: + txtmonitor = "passive matrix"; + break; + case 1: + txtmonitor = "active matrix"; + break; + case 2: + txtmonitor = "active addressed STN"; + break; + case 3: + txtmonitor = "EL"; + break; + case 4: + txtmonitor = "plasma"; + break; + default: + txtmonitor = "unknown"; + } + format = *(u32 *)(par->lcd_table+57); + if (tech == 0 || tech == 2) { + switch (format & 7) { + case 0: + txtformat = "12 bit interface"; + break; + case 1: + txtformat = "16 bit interface"; + break; + case 2: + txtformat = "24 bit interface"; + break; + default: + txtformat = "unkown format"; + } + } else { + switch (format & 7) { + case 0: + txtformat = "8 colours"; + break; + case 1: + txtformat = "512 colours"; + break; + case 2: + txtformat = "4096 colours"; + break; + case 4: + txtformat = "262144 colours (LT mode)"; + break; + case 5: + txtformat = "16777216 colours"; + break; + case 6: + txtformat = "262144 colours (FDPI-2 mode)"; + break; + default: + txtformat = "unkown format"; + } + } + PRINTKI("%s%s %s monitor detected: %s\n", + txtdual ,txtcolour, txtmonitor, model); + PRINTKI(" id=%d, %dx%d pixels, %s\n", + id, width, height, txtformat); + refresh_rates_buf[0] = 0; + refresh_rates = *(u16 *)(par->lcd_table+62); + m = 1; + f = 0; + for (i=0;i<16;i++) { + if (refresh_rates & m) { + if (f == 0) { + sprintf(strbuf, "%d", lcd_refresh_rates[i]); + f++; + } else { + sprintf(strbuf, ",%d", lcd_refresh_rates[i]); + } + strcat(refresh_rates_buf,strbuf); } + m = m << 1; + } + default_refresh_rate = (*(u8 *)(par->lcd_table+61) & 0xf0) >> 4; + PRINTKI(" supports refresh rates [%s], default %d Hz\n", + refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]); + par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate]; + /* We now need to determine the crtc parameters for the + * lcd monitor. This is tricky, because they are not stored + * individually in the BIOS. Instead, the BIOS contains a + * table of display modes that work for this monitor. + * + * The idea is that we search for a mode of the same dimensions + * as the dimensions of the lcd monitor. Say our lcd monitor + * is 800x600 pixels, we search for a 800x600 monitor. + * The CRTC parameters we find here are the ones that we need + * to use to simulate other resolutions on the lcd screen. + */ + lcdmodeptr = (u16 *)(par->lcd_table + 64); + while (*lcdmodeptr != 0) { + u32 modeptr; + u16 mwidth, mheight, lcd_hsync_start, lcd_vsync_start; + modeptr = bios_base + *lcdmodeptr; + + mwidth = *((u16 *)(modeptr+0)); + mheight = *((u16 *)(modeptr+2)); + + if (mwidth == width && mheight == height) { + par->lcd_pixclock = 100000000 / *((u16 *)(modeptr+9)); + par->lcd_htotal = *((u16 *)(modeptr+17)) & 511; + par->lcd_hdisp = *((u16 *)(modeptr+19)) & 511; + lcd_hsync_start = *((u16 *)(modeptr+21)) & 511; + par->lcd_hsync_dly = (*((u16 *)(modeptr+21)) >> 9) & 7; + par->lcd_hsync_len = *((u8 *)(modeptr+23)) & 63; + + par->lcd_vtotal = *((u16 *)(modeptr+24)) & 2047; + par->lcd_vdisp = *((u16 *)(modeptr+26)) & 2047; + lcd_vsync_start = *((u16 *)(modeptr+28)) & 2047; + par->lcd_vsync_len = (*((u16 *)(modeptr+28)) >> 11) & 31; + + par->lcd_htotal = (par->lcd_htotal + 1) * 8; + par->lcd_hdisp = (par->lcd_hdisp + 1) * 8; + lcd_hsync_start = (lcd_hsync_start + 1) * 8; + par->lcd_hsync_len = par->lcd_hsync_len * 8; + + par->lcd_vtotal++; + par->lcd_vdisp++; + lcd_vsync_start++; + + par->lcd_right_margin = lcd_hsync_start - par->lcd_hdisp; + par->lcd_lower_margin = lcd_vsync_start - par->lcd_vdisp; + par->lcd_hblank_len = par->lcd_htotal - par->lcd_hdisp; + par->lcd_vblank_len = par->lcd_vtotal - par->lcd_vdisp; + break; + } + + lcdmodeptr++; + } + if (*lcdmodeptr == 0) { + PRINTKE("LCD monitor CRTC parameters not found!!!\n"); + /* To do: Switch to CRT if possible. */ + } else { + PRINTKI(" LCD CRTC parameters: %d.%d %d %d %d %d %d %d %d %d\n", + 1000000 / par->lcd_pixclock, 1000000 % par->lcd_pixclock, + par->lcd_hdisp, + par->lcd_hdisp + par->lcd_right_margin, + par->lcd_hdisp + par->lcd_right_margin + + par->lcd_hsync_dly + par->lcd_hsync_len, + par->lcd_htotal, + par->lcd_vdisp, + par->lcd_vdisp + par->lcd_lower_margin, + par->lcd_vdisp + par->lcd_lower_margin + par->lcd_vsync_len, + par->lcd_vtotal); + PRINTKI(" : %d %d %d %d %d %d %d %d %d\n", + par->lcd_pixclock, + par->lcd_hblank_len - (par->lcd_right_margin + + par->lcd_hsync_dly + par->lcd_hsync_len), + par->lcd_hdisp, + par->lcd_right_margin, + par->lcd_hsync_len, + par->lcd_vblank_len - (par->lcd_lower_margin + par->lcd_vsync_len), + par->lcd_vdisp, + par->lcd_lower_margin, + par->lcd_vsync_len); + } + } +} +#endif /* CONFIG_FB_ATY_GENERIC_LCD */ + +static int __devinit init_from_bios(struct atyfb_par *par) +{ + u32 bios_base, rom_addr; + int ret; + + rom_addr = 0xc0000 + ((aty_ld_le32(SCRATCH_REG1, par) & 0x7f) << 11); + bios_base = (unsigned long)ioremap(rom_addr, 0x10000); + + /* The BIOS starts with 0xaa55. */ + if (*((u16 *)bios_base) == 0xaa55) { + + u8 *bios_ptr; + u16 rom_table_offset, freq_table_offset; + PLL_BLOCK_MACH64 pll_block; + + PRINTKI("Mach64 BIOS is located at %x, mapped at %x.\n", rom_addr, bios_base); + + /* check for frequncy table */ + bios_ptr = (u8*)bios_base; + rom_table_offset = (u16)(bios_ptr[0x48] | (bios_ptr[0x49] << 8)); + freq_table_offset = bios_ptr[rom_table_offset + 16] | (bios_ptr[rom_table_offset + 17] << 8); + memcpy(&pll_block, bios_ptr + freq_table_offset, sizeof(PLL_BLOCK_MACH64)); + + PRINTKI("BIOS frequency table:\n"); + PRINTKI("PCLK_min_freq %d, PCLK_max_freq %d, ref_freq %d, ref_divider %d\n", + pll_block.PCLK_min_freq, pll_block.PCLK_max_freq, + pll_block.ref_freq, pll_block.ref_divider); + PRINTKI("MCLK_pwd %d, MCLK_max_freq %d, XCLK_max_freq %d, SCLK_freq %d\n", + pll_block.MCLK_pwd, pll_block.MCLK_max_freq, + pll_block.XCLK_max_freq, pll_block.SCLK_freq); + + par->pll_limits.pll_min = pll_block.PCLK_min_freq/100; + par->pll_limits.pll_max = pll_block.PCLK_max_freq/100; + par->pll_limits.ref_clk = pll_block.ref_freq/100; + par->pll_limits.ref_div = pll_block.ref_divider; + par->pll_limits.sclk = pll_block.SCLK_freq/100; + par->pll_limits.mclk = pll_block.MCLK_max_freq/100; + par->pll_limits.mclk_pm = pll_block.MCLK_pwd/100; + par->pll_limits.xclk = pll_block.XCLK_max_freq/100; +#ifdef CONFIG_FB_ATY_GENERIC_LCD + aty_init_lcd(par, bios_base); +#endif + ret = 0; + } else { + PRINTKE("no BIOS frequency table found, use parameters\n"); + ret = -ENXIO; + } + iounmap((void*)bios_base); + + return ret; +} +#endif /* __i386__ */ + +static int __devinit atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info, unsigned long addr) +{ + struct atyfb_par *par = info->par; + u16 tmp; + unsigned long raddr; + struct resource *rrp; + int ret = 0; + + raddr = addr + 0x7ff000UL; + rrp = &pdev->resource[2]; + if ((rrp->flags & IORESOURCE_MEM) && request_mem_region(rrp->start, rrp->end - rrp->start + 1, "atyfb")) { + par->aux_start = rrp->start; + par->aux_size = rrp->end - rrp->start + 1; + raddr = rrp->start; + PRINTKI("using auxiliary register aperture\n"); + } + + info->fix.mmio_start = raddr; + par->ati_regbase = ioremap(info->fix.mmio_start, 0x1000); + if (par->ati_regbase == 0) + return -ENOMEM; + + info->fix.mmio_start += par->aux_start ? 0x400 : 0xc00; + par->ati_regbase += par->aux_start ? 0x400 : 0xc00; + + /* + * Enable memory-space accesses using config-space + * command register. + */ + pci_read_config_word(pdev, PCI_COMMAND, &tmp); + if (!(tmp & PCI_COMMAND_MEMORY)) { + tmp |= PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, tmp); + } #ifdef __BIG_ENDIAN - /* Use the big-endian aperture */ - addr += 0x800000; + /* Use the big-endian aperture */ + addr += 0x800000; #endif - /* Map in frame buffer */ - info->fix.smem_start = addr; - info->screen_base = - (char *) ioremap(addr, 0x800000); + /* Map in frame buffer */ + info->fix.smem_start = addr; + info->screen_base = ioremap(addr, 0x800000); + if (info->screen_base == NULL) { + ret = -ENOMEM; + goto atyfb_setup_generic_fail; + } + + if((ret = correct_chipset(par))) + goto atyfb_setup_generic_fail; +#ifdef __i386__ + if((ret = init_from_bios(par))) + goto atyfb_setup_generic_fail; +#endif + if (!(aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_EXT_DISP_EN)) + par->clk_wr_offset = (inb(R_GENMO) & 0x0CU) >> 2; + else + par->clk_wr_offset = aty_ld_8(CLOCK_CNTL, par) & 0x03U; + + /* according to ATI, we should use clock 3 for acelerated mode */ + par->clk_wr_offset = 3; + + return 0; + +atyfb_setup_generic_fail: + iounmap(par->ati_regbase); + par->ati_regbase = 0; + return ret; +} + +#endif /* !__sparc__ */ + +static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + unsigned long addr, res_start, res_size; + struct fb_info *info; + struct resource *rp; + struct atyfb_par *par; + int i, rc = -ENOMEM; + + for (i = sizeof(aty_chips) / sizeof(*aty_chips) - 1; i >= 0; i--) + if (pdev->device == aty_chips[i].pci_id) + break; + + if (i < 0) + return -ENODEV; + + /* Enable device in PCI config */ + if (pci_enable_device(pdev)) { + PRINTKE("Cannot enable PCI device\n"); + return -ENXIO; + } + + /* Find which resource to use */ + rp = &pdev->resource[0]; + if (rp->flags & IORESOURCE_IO) + rp = &pdev->resource[1]; + addr = rp->start; + if (!addr) + return -ENXIO; - if (!info->screen_base) { + /* Reserve space */ + res_start = rp->start; + res_size = rp->end - rp->start + 1; + if (!request_mem_region (res_start, res_size, "atyfb")) + return -EBUSY; + + /* Allocate framebuffer */ + info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev); + if (!info) { + PRINTKE("atyfb_pci_probe() can't alloc fb_info\n"); + return -ENOMEM; + } + par = info->par; + info->fix = atyfb_fix; + info->device = &pdev->dev; + par->pci_id = aty_chips[i].pci_id; + par->res_start = res_start; + par->res_size = res_size; + par->irq = pdev->irq; + + /* Setup "info" structure */ #ifdef __sparc__ - kfree(default_par->mmap_map); + rc = atyfb_setup_sparc(pdev, info, addr); +#else + rc = atyfb_setup_generic(pdev, info, addr); #endif - kfree(default_par); - kfree(info); - release_mem_region(res_start, res_size); - return -ENXIO; - } -#endif /* __sparc__ */ + if (rc) + goto err_release_mem; + + pci_set_drvdata(pdev, info); + + /* Init chip & register framebuffer */ + if (aty_init(info, "PCI")) + goto err_release_io; - if (!aty_init(info, "PCI")) { -#ifdef __sparc__ - if (default_par->mmap_map) - kfree(default_par->mmap_map); -#endif - kfree(default_par); - kfree(info); - release_mem_region(res_start, res_size); - return -ENXIO; - } #ifdef __sparc__ - if (!prom_palette) - prom_palette = atyfb_palette; + if (!prom_palette) + prom_palette = atyfb_palette; - /* - * Add /dev/fb mmap values. - */ - default_par->mmap_map[0].voff = 0x8000000000000000UL; - default_par->mmap_map[0].poff = - (unsigned long) info->screen_base & PAGE_MASK; - default_par->mmap_map[0].size = - info->fix.smem_len; - default_par->mmap_map[0].prot_mask = _PAGE_CACHE; - default_par->mmap_map[0].prot_flag = _PAGE_E; - default_par->mmap_map[1].voff = - default_par->mmap_map[0].voff + - info->fix.smem_len; - default_par->mmap_map[1].poff = - default_par->ati_regbase & PAGE_MASK; - default_par->mmap_map[1].size = PAGE_SIZE; - default_par->mmap_map[1].prot_mask = _PAGE_CACHE; - default_par->mmap_map[1].prot_flag = _PAGE_E; -#endif /* __sparc__ */ - -#ifdef CONFIG_PMAC_PBOOK - if (first_display == NULL) - pmu_register_sleep_notifier(&aty_sleep_notifier); - default_par->next = first_display; + /* + * Add /dev/fb mmap values. + */ + par->mmap_map[0].voff = 0x8000000000000000UL; + par->mmap_map[0].poff = (unsigned long) info->screen_base & PAGE_MASK; + par->mmap_map[0].size = info->fix.smem_len; + par->mmap_map[0].prot_mask = _PAGE_CACHE; + par->mmap_map[0].prot_flag = _PAGE_E; + par->mmap_map[1].voff = par->mmap_map[0].voff + info->fix.smem_len; + par->mmap_map[1].poff = par->ati_regbase & PAGE_MASK; + par->mmap_map[1].size = PAGE_SIZE; + par->mmap_map[1].prot_mask = _PAGE_CACHE; + par->mmap_map[1].prot_flag = _PAGE_E; +#endif /* __sparc__ */ + + return 0; + +err_release_io: +#ifdef __sparc__ + if (par->mmap_map) + kfree(par->mmap_map); +#else + if (par->ati_regbase) + iounmap(par->ati_regbase); + if (info->screen_base) + iounmap(info->screen_base); #endif - } - } +err_release_mem: + if(par->aux_start) + release_mem_region(par->aux_start, par->aux_size); -#elif defined(CONFIG_ATARI) - struct atyfb_par *default_par; + release_mem_region(par->res_start, par->res_size); + framebuffer_release(info); + + return rc; +} + +#endif /* CONFIG_PCI */ + +#ifdef CONFIG_ATARI + +static int __devinit atyfb_atari_probe(void) +{ + struct aty_par *par; struct fb_info *info; int m64_num; u32 clock_r; @@ -2314,85 +3436,135 @@ int __init atyfb_do_init(void) for (m64_num = 0; m64_num < mach64_count; m64_num++) { if (!phys_vmembase[m64_num] || !phys_size[m64_num] || !phys_guiregbase[m64_num]) { - printk - (" phys_*[%d] parameters not set => returning early. \n", - m64_num); + PRINTKI("phys_*[%d] parameters not set => returning early. \n", m64_num); continue; } - info = kmalloc(sizeof(struct fb_info), GFP_ATOMIC); + info = framebuffer_alloc(sizeof(struct atyfb_par), NULL); if (!info) { - printk("atyfb_init: can't alloc fb_info\n"); + PRINTKE("atyfb_atari_probe() can't alloc fb_info\n"); return -ENOMEM; } - memset(info, 0, sizeof(struct fb_info)); - - default_par = kmalloc(sizeof(struct atyfb_par), GFP_ATOMIC); - if (!default_par) { - printk - ("atyfb_init: can't alloc atyfb_par\n"); - kfree(info); - return -ENXIO; - } - memset(default_par, 0, sizeof(struct atyfb_par)); + par = info->par; info->fix = atyfb_fix; - info->par = default_par; + + par->irq = (unsigned int) -1; /* something invalid */ /* * Map the video memory (physical address given) to somewhere in the * kernel address space. */ - info->screen_base = ioremap(phys_vmembase[m64_num], - phys_size[m64_num]); - info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */ - default_par->ati_regbase = (unsigned long)ioremap(phys_guiregbase[m64_num], - 0x10000) + 0xFC00ul; - info->fix.mmio_start = default_par->ati_regbase; /* Fake! */ + info->screen_base = ioremap(phys_vmembase[m64_num], phys_size[m64_num]); + info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */ + par->ati_regbase = ioremap(phys_guiregbase[m64_num], 0x10000) + 0xFC00ul; + info->fix.mmio_start = par->ati_regbase; /* Fake! */ - aty_st_le32(CLOCK_CNTL, 0x12345678, default_par); - clock_r = aty_ld_le32(CLOCK_CNTL, default_par); + aty_st_le32(CLOCK_CNTL, 0x12345678, par); + clock_r = aty_ld_le32(CLOCK_CNTL, par); switch (clock_r & 0x003F) { case 0x12: - default_par->clk_wr_offset = 3; /* */ + par->clk_wr_offset = 3; /* */ break; case 0x34: - default_par->clk_wr_offset = 2; /* Medusa ST-IO ISA Adapter etc. */ + par->clk_wr_offset = 2; /* Medusa ST-IO ISA Adapter etc. */ break; case 0x16: - default_par->clk_wr_offset = 1; /* */ + par->clk_wr_offset = 1; /* */ break; case 0x38: - default_par->clk_wr_offset = 0; /* Panther 1 ISA Adapter (Gerald) */ + par->clk_wr_offset = 0; /* Panther 1 ISA Adapter (Gerald) */ break; } - if (!aty_init(info, "ISA bus")) { - kfree(default_par); - kfree(info); + if (aty_init(info, "ISA bus")) { + framebuffer_release(info); /* This is insufficient! kernel_map has added two large chunks!! */ return -ENXIO; } } -#endif /* CONFIG_ATARI */ - return 0; } -int __init atyfb_init(void) +#endif /* CONFIG_ATARI */ + +static void __devexit atyfb_remove(struct fb_info *info) { -#ifndef MODULE - char *option = NULL; + struct atyfb_par *par = (struct atyfb_par *) info->par; - if (fb_get_options("atyfb", &option)) - return -ENODEV; - atyfb_setup(option); + /* restore video mode */ + aty_set_crtc(par, &saved_crtc); + par->pll_ops->set_pll(info, &saved_pll); + + unregister_framebuffer(info); + +#ifdef CONFIG_MTRR + if (par->mtrr_reg >= 0) { + mtrr_del(par->mtrr_reg, 0, 0); + par->mtrr_reg = -1; + } + if (par->mtrr_aper >= 0) { + mtrr_del(par->mtrr_aper, 0, 0); + par->mtrr_aper = -1; + } #endif - return atyfb_do_init(); +#ifndef __sparc__ + if (par->ati_regbase) + iounmap(par->ati_regbase); + if (info->screen_base) + iounmap(info->screen_base); +#ifdef __BIG_ENDIAN + if (info->sprite.addr) + iounmap(info->sprite.addr); +#endif +#endif +#ifdef __sparc__ + if (par->mmap_map) + kfree(par->mmap_map); +#endif + if (par->aux_start) + release_mem_region(par->aux_start, par->aux_size); + + if (par->res_start) + release_mem_region(par->res_start, par->res_size); + + framebuffer_release(info); } +#ifdef CONFIG_PCI + +static void __devexit atyfb_pci_remove(struct pci_dev *pdev) +{ + struct fb_info *info = pci_get_drvdata(pdev); + + atyfb_remove(info); +} + +/* + * This driver uses its own matching table. That will be more difficult + * to fix, so for now, we just match against any ATI ID and let the + * probe() function find out what's up. That also mean we don't have + * a module ID table though. + */ +static struct pci_device_id atyfb_pci_tbl[] = { + { PCI_VENDOR_ID_ATI, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0 }, + { 0, } +}; + +static struct pci_driver atyfb_driver = { + .name = "atyfb", + .id_table = atyfb_pci_tbl, + .probe = atyfb_pci_probe, + .remove = __devexit_p(atyfb_pci_remove), +#ifdef CONFIG_PM + .suspend = atyfb_pci_suspend, + .resume = atyfb_pci_resume, +#endif /* CONFIG_PM */ +}; + +#endif /* CONFIG_PCI */ -#ifndef MODULE int __init atyfb_setup(char *options) { char *this_opt; @@ -2401,19 +3573,20 @@ int __init atyfb_setup(char *options) return 0; while ((this_opt = strsep(&options, ",")) != NULL) { - if (!strncmp(this_opt, "noblink", 7)) { - curblink = 0; - } else if (!strncmp(this_opt, "noaccel", 7)) { + if (!strncmp(this_opt, "noaccel", 7)) { noaccel = 1; +#ifdef CONFIG_MTRR + } else if (!strncmp(this_opt, "nomtrr", 6)) { + nomtrr = 1; +#endif } else if (!strncmp(this_opt, "vram:", 5)) - default_vram = - simple_strtoul(this_opt + 5, NULL, 0); + vram = simple_strtoul(this_opt + 5, NULL, 0); else if (!strncmp(this_opt, "pll:", 4)) - default_pll = - simple_strtoul(this_opt + 4, NULL, 0); + pll = simple_strtoul(this_opt + 4, NULL, 0); else if (!strncmp(this_opt, "mclk:", 5)) - default_mclk = - simple_strtoul(this_opt + 5, NULL, 0); + mclk = simple_strtoul(this_opt + 5, NULL, 0); + else if (!strncmp(this_opt, "xclk:", 5)) + xclk = simple_strtoul(this_opt+5, NULL, 0); #ifdef CONFIG_PPC else if (!strncmp(this_opt, "vmode:", 6)) { unsigned int vmode = @@ -2456,182 +3629,63 @@ int __init atyfb_setup(char *options) } #endif else - mode_option = this_opt; + mode = this_opt; } return 0; } -module_init(atyfb_init); -#endif /* !MODULE */ - -#ifdef CONFIG_ATARI -static int __init store_video_par(char *video_str, unsigned char m64_num) -{ - char *p; - unsigned long vmembase, size, guiregbase; - - printk("store_video_par() '%s' \n", video_str); - - if (!(p = strsep(&video_str, ";")) || !*p) - goto mach64_invalid; - vmembase = simple_strtoul(p, NULL, 0); - if (!(p = strsep(&video_str, ";")) || !*p) - goto mach64_invalid; - size = simple_strtoul(p, NULL, 0); - if (!(p = strsep(&video_str, ";")) || !*p) - goto mach64_invalid; - guiregbase = simple_strtoul(p, NULL, 0); - - phys_vmembase[m64_num] = vmembase; - phys_size[m64_num] = size; - phys_guiregbase[m64_num] = guiregbase; - printk(" stored them all: $%08lX $%08lX $%08lX \n", vmembase, size, - guiregbase); - return 0; - - mach64_invalid: - phys_vmembase[m64_num] = 0; - return -1; -} -#endif /* CONFIG_ATARI */ -/* -#ifdef CONFIG_FB_ATY_CT - * Erase HW Cursor * - if (par->cursor && (info->currcon >= 0)) - atyfb_cursor(&fb_display[par->currcon], CM_ERASE, - par->cursor->pos.x, par->cursor->pos.y); -#endif * CONFIG_FB_ATY_CT * - -#ifdef CONFIG_FB_ATY_CT - * Install hw cursor * - if (par->cursor) { - aty_set_cursor_color(info); - aty_set_cursor_shape(info); - } -#endif * CONFIG_FB_ATY_CT */ - - /* - * Blank the display. - */ - -static int atyfb_blank(int blank, struct fb_info *info) +int __init atyfb_init(void) { - struct atyfb_par *par = (struct atyfb_par *) info->par; - u8 gen_cntl; - -#ifdef CONFIG_PMAC_BACKLIGHT - if ((_machine == _MACH_Pmac) && blank) - set_backlight_enable(0); -#endif /* CONFIG_PMAC_BACKLIGHT */ - - gen_cntl = aty_ld_8(CRTC_GEN_CNTL, par); - if (blank > 0) - switch (blank - 1) { - case VESA_NO_BLANKING: - gen_cntl |= 0x40; - break; - case VESA_VSYNC_SUSPEND: - gen_cntl |= 0x8; - break; - case VESA_HSYNC_SUSPEND: - gen_cntl |= 0x4; - break; - case VESA_POWERDOWN: - gen_cntl |= 0x4c; - break; - } else - gen_cntl &= ~(0x4c); - aty_st_8(CRTC_GEN_CNTL, gen_cntl, par); - -#ifdef CONFIG_PMAC_BACKLIGHT - if ((_machine == _MACH_Pmac) && !blank) - set_backlight_enable(1); -#endif /* CONFIG_PMAC_BACKLIGHT */ - return 0; -} - - /* - * Set a single color register. The values supplied are already - * rounded down to the hardware's capabilities (according to the - * entries in the var structure). Return != 0 for invalid regno. - */ +#ifndef MODULE + char *option = NULL; -static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, - u_int transp, struct fb_info *info) -{ - struct atyfb_par *par = (struct atyfb_par *) info->par; - int i, scale; - u32 *pal = info->pseudo_palette; + if (fb_get_options("atyfb", &option)) + return -ENODEV; + atyfb_setup(option); +#endif - if (regno > 255) - return 1; - red >>= 8; - green >>= 8; - blue >>= 8; - i = aty_ld_8(DAC_CNTL, par) & 0xfc; - if (M64_HAS(EXTRA_BRIGHT)) - i |= 0x2; /*DAC_CNTL|0x2 turns off the extra brightness for gt */ - aty_st_8(DAC_CNTL, i, par); - aty_st_8(DAC_MASK, 0xff, par); - scale = (M64_HAS(INTEGRATED) && info->var.bits_per_pixel == 16) ? 3 : 0; +#ifdef CONFIG_PCI + pci_module_init(&atyfb_driver); +#endif #ifdef CONFIG_ATARI - out_8(&par->aty_cmap_regs->windex, regno << scale); - out_8(&par->aty_cmap_regs->lut, red); - out_8(&par->aty_cmap_regs->lut, green); - out_8(&par->aty_cmap_regs->lut, blue); -#else - writeb(regno << scale, &par->aty_cmap_regs->windex); - writeb(red, &par->aty_cmap_regs->lut); - writeb(green, &par->aty_cmap_regs->lut); - writeb(blue, &par->aty_cmap_regs->lut); + atyfb_atari_probe(); #endif - if (regno < 16) - switch (info->var.bits_per_pixel) { - case 16: - pal[regno] = (regno << 10) | (regno << 5) | regno; - break; - case 24: - pal[regno] = (regno << 16) | (regno << 8) | regno; - break; - case 32: - i = (regno << 8) | regno; - pal[regno] = (i << 16) | i; - break; - } - return 0; + return 0; } -#ifdef MODULE -int __init init_module(void) +void __exit atyfb_exit(void) { - atyfb_init(); - return fb_list ? 0 : -ENXIO; -} - -void cleanup_module(void) -{ - struct fb_info *info = fb_list; - struct atyfb_par *par = (struct atyfb_par *) info->par; - unregister_framebuffer(info); - -#ifndef __sparc__ - if (par->ati_regbase) - iounmap((void *) par->ati_regbase); - if (info->screen_base) - iounmap((void *) info->screen_base); -#ifdef __BIG_ENDIAN - if (par->cursor && par->cursor->ram) - iounmap(par->cursor->ram); -#endif -#endif - if (par->cursor) - kfree(par->cursor); -#ifdef __sparc__ - if (par->mmap_map) - kfree(par->mmap_map); +#ifdef CONFIG_PCI + pci_unregister_driver(&atyfb_driver); #endif - kfree(info); } +module_init(atyfb_init); +#ifdef MODULE +module_exit(atyfb_exit); #endif + +MODULE_DESCRIPTION("FBDev driver for ATI Mach64 cards"); MODULE_LICENSE("GPL"); +module_param(noaccel, bool, 0); +MODULE_PARM_DESC(noaccel, "bool: disable acceleration"); +module_param(vram, int, 0); +MODULE_PARM_DESC(vram, "int: override size of video ram"); +module_param(pll, int, 0); +MODULE_PARM_DESC(pll, "int: override video clock"); +module_param(mclk, int, 0); +MODULE_PARM_DESC(mclk, "int: override memory clock"); +module_param(xclk, int, 0); +MODULE_PARM_DESC(xclk, "int: override accelerated engine clock"); +#ifdef CONFIG_PPC +module_param(vmode, int, 0); +MODULE_PARM_DESC(vmode, "int: video mode for mac"); +module_param(cmode, int, 0); +MODULE_PARM_DESC(cmode, "int: color mode for mac"); +#endif +module_param(mode, charp, 0); +MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" "); +#ifdef CONFIG_MTRR +module_param(nomtrr, bool, 0); +MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers"); +#endif diff -puN drivers/video/aty/atyfb.h~fbdev-various-mach64-changes drivers/video/aty/atyfb.h --- 25-sparc64/drivers/video/aty/atyfb.h~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/atyfb.h 2004-10-22 00:41:34.000000000 -0700 @@ -3,6 +3,8 @@ */ #include <linux/config.h> +#include <linux/spinlock.h> +#include <linux/wait.h> /* * Elements of the hardware specific atyfb_par structure */ @@ -10,16 +12,60 @@ struct crtc { u32 vxres; u32 vyres; + u32 xoffset; + u32 yoffset; + u32 bpp; u32 h_tot_disp; u32 h_sync_strt_wid; u32 v_tot_disp; u32 v_sync_strt_wid; + u32 vline_crnt_vline; u32 off_pitch; u32 gen_cntl; u32 dp_pix_width; /* acceleration */ u32 dp_chain_mask; /* acceleration */ +#ifdef CONFIG_FB_ATY_GENERIC_LCD + u32 horz_stretching; + u32 vert_stretching; + u32 ext_vert_stretch; + u32 shadow_h_tot_disp; + u32 shadow_h_sync_strt_wid; + u32 shadow_v_tot_disp; + u32 shadow_v_sync_strt_wid; + u32 lcd_gen_cntl; + u32 lcd_config_panel; + u32 lcd_index; +#endif +}; + +struct aty_interrupt { + wait_queue_head_t wait; + unsigned int count; + int pan_display; +}; + +struct pll_info { + int pll_max; + int pll_min; + int sclk, mclk, mclk_pm, xclk; + int ref_div; + int ref_clk; }; +typedef struct { + u16 unknown1; + u16 PCLK_min_freq; + u16 PCLK_max_freq; + u16 unknown2; + u16 ref_freq; + u16 ref_divider; + u16 unknown3; + u16 MCLK_pwd; + u16 MCLK_max_freq; + u16 XCLK_max_freq; + u16 SCLK_freq; +} __attribute__ ((packed)) PLL_BLOCK_MACH64; + struct pll_514 { u8 m; u8 n; @@ -36,16 +82,39 @@ struct pll_ct { u8 pll_ref_div; u8 pll_gen_cntl; u8 mclk_fb_div; + u8 mclk_fb_mult; /* 2 ro 4 */ +/* u8 sclk_fb_div;*/ u8 pll_vclk_cntl; u8 vclk_post_div; u8 vclk_fb_div; u8 pll_ext_cntl; - u32 dsp_config; /* Mach64 GTB DSP */ - u32 dsp_on_off; /* Mach64 GTB DSP */ +/* u8 ext_vpll_cntl; + u8 spll_cntl2;*/ + u32 dsp_config; /* Mach64 GTB DSP */ + u32 dsp_on_off; /* Mach64 GTB DSP */ + u32 dsp_loop_latency; + u32 fifo_size; + u32 xclkpagefaultdelay; + u32 xclkmaxrasdelay; + u8 xclk_ref_div; + u8 xclk_post_div; u8 mclk_post_div_real; + u8 xclk_post_div_real; u8 vclk_post_div_real; + u8 features; +#ifdef CONFIG_FB_ATY_GENERIC_LCD + u32 xres; /* use for LCD stretching/scaling */ +#endif }; +/* + for pll_ct.features +*/ +#define DONT_USE_SPLL 0x1 +#define DONT_USE_XDLL 0x2 +#define USE_CPUCLK 0x4 +#define POWERDOWN_PLL 0x8 + union aty_pll { struct pll_ct ct; struct pll_514 ibm514; @@ -56,42 +125,68 @@ union aty_pll { * The hardware parameters for each card */ -struct aty_cursor { - u8 bits[8][64]; - u8 mask[8][64]; - u8 *ram; -}; - struct atyfb_par { struct aty_cmap_regs *aty_cmap_regs; + struct { u8 red, green, blue; } palette[256]; const struct aty_dac_ops *dac_ops; const struct aty_pll_ops *pll_ops; - struct aty_cursor *cursor; - unsigned long ati_regbase; - unsigned long clk_wr_offset; + void *ati_regbase; + unsigned long clk_wr_offset; /* meaning overloaded, clock id by CT */ struct crtc crtc; union aty_pll pll; + struct pll_info pll_limits; u32 features; u32 ref_clk_per; u32 pll_per; u32 mclk_per; + u32 xclk_per; u8 bus_type; u8 ram_type; u8 mem_refresh_rate; - u8 blitter_may_be_busy; + u16 pci_id; u32 accel_flags; + int blitter_may_be_busy; + int asleep; + int lock_blank; + unsigned long res_start; + unsigned long res_size; #ifdef __sparc__ struct pci_mmap_map *mmap_map; u8 mmaped; +#endif int open; +#ifdef CONFIG_FB_ATY_GENERIC_LCD + unsigned long bios_base_phys; + unsigned long bios_base; + unsigned long lcd_table; + u16 lcd_width; + u16 lcd_height; + u32 lcd_pixclock; + u16 lcd_refreshrate; + u16 lcd_htotal; + u16 lcd_hdisp; + u16 lcd_hsync_dly; + u16 lcd_hsync_len; + u16 lcd_vtotal; + u16 lcd_vdisp; + u16 lcd_vsync_len; + u16 lcd_right_margin; + u16 lcd_lower_margin; + u16 lcd_hblank_len; + u16 lcd_vblank_len; #endif -#ifdef CONFIG_PMAC_PBOOK - struct fb_info *next; - unsigned char *save_framebuffer; - unsigned long save_pll[64]; + unsigned long aux_start; /* auxiliary aperture */ + unsigned long aux_size; + struct aty_interrupt vblank; + unsigned long irq_flags; + unsigned int irq; + spinlock_t int_lock; +#ifdef CONFIG_MTRR + int mtrr_aper; + int mtrr_reg; #endif }; - + /* * ATI Mach64 features */ @@ -101,7 +196,7 @@ struct atyfb_par { #define M64F_RESET_3D 0x00000001 #define M64F_MAGIC_FIFO 0x00000002 #define M64F_GTB_DSP 0x00000004 -#define M64F_FIFO_24 0x00000008 +#define M64F_FIFO_32 0x00000008 #define M64F_SDRAM_MAGIC_PLL 0x00000010 #define M64F_MAGIC_POSTDIV 0x00000020 #define M64F_INTEGRATED 0x00000040 @@ -116,9 +211,10 @@ struct atyfb_par { #define M64F_G3_PB_1_1 0x00008000 #define M64F_G3_PB_1024x768 0x00010000 #define M64F_EXTRA_BRIGHT 0x00020000 -#define M64F_LT_SLEEP 0x00040000 +#define M64F_LT_LCD_REGS 0x00040000 #define M64F_XL_DLL 0x00080000 - +#define M64F_MFB_FORCE_4 0x00100000 +#define M64F_HW_TRIPLE 0x00200000 /* * Register access @@ -137,8 +233,7 @@ static inline u32 aty_ld_le32(int regind #endif } -static inline void aty_st_le32(int regindex, u32 val, - const struct atyfb_par *par) +static inline void aty_st_le32(int regindex, u32 val, const struct atyfb_par *par) { /* Hack for bloc 1, should be cleanly optimized by compiler */ if (regindex >= 0x400) @@ -163,8 +258,7 @@ static inline u8 aty_ld_8(int regindex, #endif } -static inline void aty_st_8(int regindex, u8 val, - const struct atyfb_par *par) +static inline void aty_st_8(int regindex, u8 val, const struct atyfb_par *par) { /* Hack for bloc 1, should be cleanly optimized by compiler */ if (regindex >= 0x400) @@ -177,17 +271,10 @@ static inline void aty_st_8(int regindex #endif } -static inline u8 aty_ld_pll(int offset, const struct atyfb_par *par) -{ - u8 res; - - /* write addr byte */ - aty_st_8(CLOCK_CNTL + 1, (offset << 2), par); - /* read the register value */ - res = aty_ld_8(CLOCK_CNTL + 2, par); - return res; -} - +#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) +extern void aty_st_lcd(int index, u32 val, const struct atyfb_par *par); +extern u32 aty_ld_lcd(int index, const struct atyfb_par *par); +#endif /* * DAC operations @@ -195,14 +282,14 @@ static inline u8 aty_ld_pll(int offset, struct aty_dac_ops { int (*set_dac) (const struct fb_info * info, - const union aty_pll * pll, u32 bpp, u32 accel); + const union aty_pll * pll, u32 bpp, u32 accel); }; -extern const struct aty_dac_ops aty_dac_ibm514; /* IBM RGB514 */ -extern const struct aty_dac_ops aty_dac_ati68860b; /* ATI 68860-B */ -extern const struct aty_dac_ops aty_dac_att21c498; /* AT&T 21C498 */ -extern const struct aty_dac_ops aty_dac_unsupported; /* unsupported */ -extern const struct aty_dac_ops aty_dac_ct; /* Integrated */ +extern const struct aty_dac_ops aty_dac_ibm514; /* IBM RGB514 */ +extern const struct aty_dac_ops aty_dac_ati68860b; /* ATI 68860-B */ +extern const struct aty_dac_ops aty_dac_att21c498; /* AT&T 21C498 */ +extern const struct aty_dac_ops aty_dac_unsupported; /* unsupported */ +extern const struct aty_dac_ops aty_dac_ct; /* Integrated */ /* @@ -210,37 +297,32 @@ extern const struct aty_dac_ops aty_dac_ */ struct aty_pll_ops { - int (*var_to_pll) (const struct fb_info * info, u32 vclk_per, - u8 bpp, union aty_pll * pll); - u32(*pll_to_var) (const struct fb_info * info, - const union aty_pll * pll); - void (*set_pll) (const struct fb_info * info, - const union aty_pll * pll); + int (*var_to_pll) (const struct fb_info * info, u32 vclk_per, u32 bpp, union aty_pll * pll); + u32 (*pll_to_var) (const struct fb_info * info, const union aty_pll * pll); + void (*set_pll) (const struct fb_info * info, const union aty_pll * pll); + void (*get_pll) (const struct fb_info *info, union aty_pll * pll); + int (*init_pll) (const struct fb_info * info, union aty_pll * pll); }; -extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */ -extern const struct aty_pll_ops aty_pll_stg1703; /* STG 1703 */ -extern const struct aty_pll_ops aty_pll_ch8398; /* Chrontel 8398 */ -extern const struct aty_pll_ops aty_pll_att20c408; /* AT&T 20C408 */ -extern const struct aty_pll_ops aty_pll_ibm514; /* IBM RGB514 */ -extern const struct aty_pll_ops aty_pll_unsupported; /* unsupported */ -extern const struct aty_pll_ops aty_pll_ct; /* Integrated */ +extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */ +extern const struct aty_pll_ops aty_pll_stg1703; /* STG 1703 */ +extern const struct aty_pll_ops aty_pll_ch8398; /* Chrontel 8398 */ +extern const struct aty_pll_ops aty_pll_att20c408; /* AT&T 20C408 */ +extern const struct aty_pll_ops aty_pll_ibm514; /* IBM RGB514 */ +extern const struct aty_pll_ops aty_pll_unsupported; /* unsupported */ +extern const struct aty_pll_ops aty_pll_ct; /* Integrated */ -extern void aty_set_pll_ct(const struct fb_info *info, - const union aty_pll *pll); -extern void aty_calc_pll_ct(const struct fb_info *info, - struct pll_ct *pll); +extern void aty_set_pll_ct(const struct fb_info *info, const union aty_pll *pll); +extern u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par); /* * Hardware cursor support */ -extern struct aty_cursor *aty_init_cursor(struct fb_info *info); +extern int aty_init_cursor(struct fb_info *info); extern int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor); -extern void aty_set_cursor_color(struct fb_info *info); -extern void aty_set_cursor_shape(struct fb_info *info); /* * Hardware acceleration @@ -260,6 +342,5 @@ static inline void wait_for_idle(struct } extern void aty_reset_engine(const struct atyfb_par *par); -extern void aty_init_engine(struct atyfb_par *par, - struct fb_info *info); +extern void aty_init_engine(struct atyfb_par *par, struct fb_info *info); diff -puN drivers/video/aty/mach64_accel.c~fbdev-various-mach64-changes drivers/video/aty/mach64_accel.c --- 25-sparc64/drivers/video/aty/mach64_accel.c~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/mach64_accel.c 2004-10-22 00:41:34.000000000 -0700 @@ -13,22 +13,41 @@ * Generic Mach64 routines */ +/* this is for DMA GUI engine! work in progress */ +typedef struct { + u32 frame_buf_offset; + u32 system_mem_addr; + u32 command; + u32 reserved; +} BM_DESCRIPTOR_ENTRY; + +#define LAST_DESCRIPTOR (1 << 31) +#define SYSTEM_TO_FRAME_BUFFER 0 + +static u32 rotation24bpp(u32 dx, u32 direction) +{ + u32 rotation; + if (direction & DST_X_LEFT_TO_RIGHT) { + rotation = (dx / 4) % 6; + } else { + rotation = ((dx + 2) / 4) % 6; + } + + return ((rotation << 8) | DST_24_ROTATION_ENABLE); +} + void aty_reset_engine(const struct atyfb_par *par) { /* reset engine */ aty_st_le32(GEN_TEST_CNTL, - aty_ld_le32(GEN_TEST_CNTL, par) & ~GUI_ENGINE_ENABLE, - par); + aty_ld_le32(GEN_TEST_CNTL, par) & ~GUI_ENGINE_ENABLE, par); /* enable engine */ aty_st_le32(GEN_TEST_CNTL, - aty_ld_le32(GEN_TEST_CNTL, par) | GUI_ENGINE_ENABLE, - par); + aty_ld_le32(GEN_TEST_CNTL, par) | GUI_ENGINE_ENABLE, par); /* ensure engine is not locked up by clearing any FIFO or */ /* HOST errors */ aty_st_le32(BUS_CNTL, - aty_ld_le32(BUS_CNTL, - par) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK, - par); + aty_ld_le32(BUS_CNTL, par) | BUS_HOST_ERR_ACK | BUS_FIFO_ERR_ACK, par); } static void reset_GTC_3D_engine(const struct atyfb_par *par) @@ -51,7 +70,7 @@ void aty_init_engine(struct atyfb_par *p if (info->var.bits_per_pixel == 24) { /* In 24 bpp, the engine is in 8 bpp - this requires that all */ /* horizontal coordinates and widths must be adjusted */ - pitch_value = pitch_value * 3; + pitch_value *= 3; } /* On GTC (RagePro), we need to reset the 3D engine before */ @@ -146,7 +165,7 @@ void aty_init_engine(struct atyfb_par *p aty_st_le32(DP_CHAIN_MASK, par->crtc.dp_chain_mask, par); wait_for_fifo(5, par); - aty_st_le32(SCALE_3D_CNTL, 0, par); + aty_st_le32(SCALE_3D_CNTL, 0, par); aty_st_le32(Z_CNTL, 0, par); aty_st_le32(CRTC_INT_CNTL, aty_ld_le32(CRTC_INT_CNTL, par) & ~0x20, par); @@ -174,9 +193,10 @@ void atyfb_copyarea(struct fb_info *info { struct atyfb_par *par = (struct atyfb_par *) info->par; u32 dy = area->dy, sy = area->sy, direction = DST_LAST_PEL; - u32 sx = area->sx, dx = area->dx, width = area->width; - u32 pitch_value; + u32 sx = area->sx, dx = area->dx, width = area->width, rotation = 0; + if (par->asleep) + return; if (!area->width || !area->height) return; if (!par->accel_flags) { @@ -186,11 +206,9 @@ void atyfb_copyarea(struct fb_info *info return; } - pitch_value = info->var.xres_virtual; if (info->var.bits_per_pixel == 24) { /* In 24 bpp, the engine is in 8 bpp - this requires that all */ /* horizontal coordinates and widths must be adjusted */ - pitch_value *= 3; sx *= 3; dx *= 3; width *= 3; @@ -208,19 +226,25 @@ void atyfb_copyarea(struct fb_info *info } else direction |= DST_X_LEFT_TO_RIGHT; + if (info->var.bits_per_pixel == 24) { + rotation = rotation24bpp(dx, direction); + } + wait_for_fifo(4, par); aty_st_le32(DP_SRC, FRGD_SRC_BLIT, par); aty_st_le32(SRC_Y_X, (sx << 16) | sy, par); aty_st_le32(SRC_HEIGHT1_WIDTH1, (width << 16) | area->height, par); - aty_st_le32(DST_CNTL, direction, par); + aty_st_le32(DST_CNTL, direction | rotation, par); draw_rect(dx, dy, width, area->height, par); } void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct atyfb_par *par = (struct atyfb_par *) info->par; - u32 color = rect->color, dx = rect->dx, width = rect->width; + u32 color = rect->color, dx = rect->dx, width = rect->width, rotation = 0; + if (par->asleep) + return; if (!rect->width || !rect->height) return; if (!par->accel_flags) { @@ -238,6 +262,7 @@ void atyfb_fillrect(struct fb_info *info /* horizontal coordinates and widths must be adjusted */ dx *= 3; width *= 3; + rotation = rotation24bpp(dx, DST_X_LEFT_TO_RIGHT); } wait_for_fifo(3, par); @@ -247,15 +272,162 @@ void atyfb_fillrect(struct fb_info *info par); aty_st_le32(DST_CNTL, DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM | - DST_X_LEFT_TO_RIGHT, par); + DST_X_LEFT_TO_RIGHT | rotation, par); draw_rect(dx, rect->dy, width, rect->height, par); } void atyfb_imageblit(struct fb_info *info, const struct fb_image *image) { struct atyfb_par *par = (struct atyfb_par *) info->par; - - if (par->blitter_may_be_busy) - wait_for_idle(par); - cfb_imageblit(info, image); + u32 src_bytes, dx = image->dx, dy = image->dy, width = image->width; + u32 pix_width_save, pix_width, host_cntl, rotation = 0, src, mix; + + if (par->asleep) + return; + if (!image->width || !image->height) + return; + if (!par->accel_flags || + (image->depth != 1 && info->var.bits_per_pixel != image->depth)) { + if (par->blitter_may_be_busy) + wait_for_idle(par); + + cfb_imageblit(info, image); + return; + } + + wait_for_idle(par); + pix_width = pix_width_save = aty_ld_le32(DP_PIX_WIDTH, par); + host_cntl = aty_ld_le32(HOST_CNTL, par) | HOST_BYTE_ALIGN; + + switch (image->depth) { + case 1: + pix_width &= ~(BYTE_ORDER_MASK | HOST_MASK); + pix_width |= (BYTE_ORDER_MSB_TO_LSB | HOST_1BPP); + break; + case 4: + pix_width &= ~(BYTE_ORDER_MASK | HOST_MASK); + pix_width |= (BYTE_ORDER_MSB_TO_LSB | HOST_4BPP); + break; + case 8: + pix_width &= ~HOST_MASK; + pix_width |= HOST_8BPP; + break; + case 15: + pix_width &= ~HOST_MASK; + pix_width |= HOST_15BPP; + break; + case 16: + pix_width &= ~HOST_MASK; + pix_width |= HOST_16BPP; + break; + case 24: + pix_width &= ~HOST_MASK; + pix_width |= HOST_24BPP; + break; + case 32: + pix_width &= ~HOST_MASK; + pix_width |= HOST_32BPP; + break; + } + + if (info->var.bits_per_pixel == 24) { + /* In 24 bpp, the engine is in 8 bpp - this requires that all */ + /* horizontal coordinates and widths must be adjusted */ + dx *= 3; + width *= 3; + + rotation = rotation24bpp(dx, DST_X_LEFT_TO_RIGHT); + + pix_width &= ~DST_MASK; + pix_width |= DST_8BPP; + + /* + * since Rage 3D IIc we have DP_HOST_TRIPLE_EN bit + * this hwaccelerated triple has an issue with not aligned data + */ + if (M64_HAS(HW_TRIPLE) && image->width % 8 == 0) + pix_width |= DP_HOST_TRIPLE_EN; + } + + if (image->depth == 1) { + u32 fg, bg; + if (info->fix.visual == FB_VISUAL_TRUECOLOR || + info->fix.visual == FB_VISUAL_DIRECTCOLOR) { + fg = ((u32*)(info->pseudo_palette))[image->fg_color]; + bg = ((u32*)(info->pseudo_palette))[image->bg_color]; + } else { + fg = image->fg_color; + bg = image->bg_color; + } + + wait_for_fifo(2, par); + aty_st_le32(DP_BKGD_CLR, bg, par); + aty_st_le32(DP_FRGD_CLR, fg, par); + src = MONO_SRC_HOST | FRGD_SRC_FRGD_CLR | BKGD_SRC_BKGD_CLR; + mix = FRGD_MIX_S | BKGD_MIX_S; + } else { + src = MONO_SRC_ONE | FRGD_SRC_HOST; + mix = FRGD_MIX_D_XOR_S | BKGD_MIX_D; + } + + wait_for_fifo(6, par); + aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF, par); + aty_st_le32(DP_PIX_WIDTH, pix_width, par); + aty_st_le32(DP_MIX, mix, par); + aty_st_le32(DP_SRC, src, par); + aty_st_le32(HOST_CNTL, host_cntl, par); + aty_st_le32(DST_CNTL, DST_Y_TOP_TO_BOTTOM | DST_X_LEFT_TO_RIGHT | rotation, par); + + draw_rect(dx, dy, width, image->height, par); + src_bytes = (((image->width * image->depth) + 7) / 8) * image->height; + + /* manual triple each pixel */ + if (info->var.bits_per_pixel == 24 && !(pix_width & DP_HOST_TRIPLE_EN)) { + int inbit, outbit, mult24, byte_id_in_dword, width; + u8 *pbitmapin = (u8*)image->data, *pbitmapout; + u32 hostdword; + + for (width = image->width, inbit = 7, mult24 = 0; src_bytes; ) { + for (hostdword = 0, pbitmapout = (u8*)&hostdword, byte_id_in_dword = 0; + byte_id_in_dword < 4 && src_bytes; + byte_id_in_dword++, pbitmapout++) { + for (outbit = 7; outbit >= 0; outbit--) { + *pbitmapout |= (((*pbitmapin >> inbit) & 1) << outbit); + mult24++; + /* next bit */ + if (mult24 == 3) { + mult24 = 0; + inbit--; + width--; + } + + /* next byte */ + if (inbit < 0 || width == 0) { + src_bytes--; + pbitmapin++; + inbit = 7; + + if (width == 0) { + width = image->width; + outbit = 0; + } + } + } + } + wait_for_fifo(1, par); + aty_st_le32(HOST_DATA0, hostdword, par); + } + } else { + u32 *pbitmap, dwords = (src_bytes + 3) / 4; + for (pbitmap = (u32*)(image->data); dwords; dwords--, pbitmap++) { + wait_for_fifo(1, par); + aty_st_le32(HOST_DATA0, le32_to_cpup(pbitmap), par); + } + } + + wait_for_idle(par); + + /* restore pix_width */ + wait_for_fifo(1, par); + aty_st_le32(DP_PIX_WIDTH, pix_width_save, par); } diff -puN drivers/video/aty/mach64_ct.c~fbdev-various-mach64-changes drivers/video/aty/mach64_ct.c --- 25-sparc64/drivers/video/aty/mach64_ct.c~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/mach64_ct.c 2004-10-22 00:41:35.000000000 -0700 @@ -4,214 +4,236 @@ */ #include <linux/fb.h> - +#include <linux/delay.h> #include <asm/io.h> - #include <video/mach64.h> #include "atyfb.h" +#undef DEBUG -/* FIXME: remove the FAIL definition */ -#define FAIL(x) do { printk(x "\n"); return -EINVAL; } while (0) - -static void aty_st_pll(int offset, u8 val, const struct atyfb_par *par); -static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per, - struct pll_ct *pll); -static int aty_dsp_gt(const struct fb_info *info, u8 bpp, - struct pll_ct *pll); -static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, - u8 bpp, union aty_pll *pll); -static u32 aty_pll_ct_to_var(const struct fb_info *info, - const union aty_pll *pll); +static int aty_valid_pll_ct (const struct fb_info *info, u32 vclk_per, struct pll_ct *pll); +static int aty_dsp_gt (const struct fb_info *info, u32 bpp, struct pll_ct *pll); +static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, u32 bpp, union aty_pll *pll); +static u32 aty_pll_to_var_ct(const struct fb_info *info, const union aty_pll *pll); +u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par) +{ + u8 res; + /* write addr byte */ + aty_st_8(CLOCK_CNTL_ADDR, (offset << 2) & PLL_ADDR, par); + /* read the register value */ + res = aty_ld_8(CLOCK_CNTL_DATA, par); + return res; +} -static void aty_st_pll(int offset, u8 val, const struct atyfb_par *par) +void aty_st_pll_ct(int offset, u8 val, const struct atyfb_par *par) { /* write addr byte */ - aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN, par); + aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) | PLL_WR_EN, par); /* write the register value */ - aty_st_8(CLOCK_CNTL + 2, val, par); - aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN, par); + aty_st_8(CLOCK_CNTL_DATA, val & PLL_DATA, par); + aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) & ~PLL_WR_EN, par); } +/* + * by Daniel Mantione + * <daniel.mantione@freepascal.org> + * + * + * ATI Mach64 CT clock synthesis description. + * + * All clocks on the Mach64 can be calculated using the same principle: + * + * XTALIN * x * FB_DIV + * CLK = ---------------------- + * PLL_REF_DIV * POST_DIV + * + * XTALIN is a fixed speed clock. Common speeds are 14.31 MHz and 29.50 MHz. + * PLL_REF_DIV can be set by the user, but is the same for all clocks. + * FB_DIV can be set by the user for each clock individually, it should be set + * between 128 and 255, the chip will generate a bad clock signal for too low + * values. + * x depends on the type of clock; usually it is 2, but for the MCLK it can also + * be set to 4. + * POST_DIV can be set by the user for each clock individually, Possible values + * are 1,2,4,8 and for some clocks other values are available too. + * CLK is of course the clock speed that is generated. + * + * The Mach64 has these clocks: + * + * MCLK The clock rate of the chip + * XCLK The clock rate of the on-chip memory + * VCLK0 First pixel clock of first CRT controller + * VCLK1 Second pixel clock of first CRT controller + * VCLK2 Third pixel clock of first CRT controller + * VCLK3 Fourth pixel clock of first CRT controller + * VCLK Selected pixel clock, one of VCLK0, VCLK1, VCLK2, VCLK3 + * V2CLK Pixel clock of the second CRT controller. + * SCLK Multi-purpose clock + * + * - MCLK and XCLK use the same FB_DIV + * - VCLK0 .. VCLK3 use the same FB_DIV + * - V2CLK is needed when the second CRTC is used (can be used for dualhead); + * i.e. CRT monitor connected to laptop has different resolution than built + * in LCD monitor. + * - SCLK is not available on all cards; it is know to exist on the Rage LT-PRO, + * Rage XL and Rage Mobility. It is know not to exist on the Mach64 VT. + * - V2CLK is not available on all cards, most likely only the Rage LT-PRO, + * the Rage XL and the Rage Mobility + * + * SCLK can be used to: + * - Clock the chip instead of MCLK + * - Replace XTALIN with a user defined frequency + * - Generate the pixel clock for the LCD monitor (instead of VCLK) + */ + + /* + * It can be quite hard to calculate XCLK and MCLK if they don't run at the + * same frequency. Luckily, until now all cards that need asynchrone clock + * speeds seem to have SCLK. + * So this driver uses SCLK to clock the chip and XCLK to clock the memory. + */ /* ------------------------------------------------------------------------- */ - /* - * PLL programming (Mach64 CT family) - */ +/* + * PLL programming (Mach64 CT family) + * + * + * This procedure sets the display fifo. The display fifo is a buffer that + * contains data read from the video memory that waits to be processed by + * the CRT controller. + * + * On the more modern Mach64 variants, the chip doesn't calculate the + * interval after which the display fifo has to be reloaded from memory + * automatically, the driver has to do it instead. + */ -static int aty_dsp_gt(const struct fb_info *info, u8 bpp, - struct pll_ct *pll) +#define Maximum_DSP_PRECISION 7 +static u8 postdividers[] = {1,2,4,8,3}; + +static int aty_dsp_gt(const struct fb_info *info, u32 bpp, struct pll_ct *pll) { - struct atyfb_par *par = (struct atyfb_par *) info->par; - u32 dsp_xclks_per_row, dsp_loop_latency, dsp_precision, dsp_off, - dsp_on; - u32 xclks_per_row, fifo_off, fifo_on, y, fifo_size, page_size; - - /* xclocks_per_row<<11 */ - xclks_per_row = - (pll->mclk_fb_div * pll->vclk_post_div_real * 64 << 11) / - (pll->vclk_fb_div * pll->mclk_post_div_real * bpp); - if (xclks_per_row < (1 << 11)) - FAIL("Dotclock to high"); - if (M64_HAS(FIFO_24)) { - fifo_size = 24; - dsp_loop_latency = 0; - } else { - fifo_size = 32; - dsp_loop_latency = 2; + u32 dsp_off, dsp_on, dsp_xclks; + u32 multiplier, divider, ras_multiplier, ras_divider, tmp; + u8 vshift, xshift; + s8 dsp_precision; + + multiplier = ((u32)pll->mclk_fb_div) * pll->vclk_post_div_real; + divider = ((u32)pll->vclk_fb_div) * pll->xclk_ref_div; + + ras_multiplier = pll->xclkmaxrasdelay; + ras_divider = 1; + + if (bpp>=8) + divider = divider * (bpp >> 2); + + vshift = (6 - 2) - pll->xclk_post_div; /* FIFO is 64 bits wide in accelerator mode ... */ + + if (bpp == 0) + vshift--; /* ... but only 32 bits in VGA mode. */ + +#ifdef CONFIG_FB_ATY_GENERIC_LCD + if (pll->xres != 0) { + struct atyfb_par *par = (struct atyfb_par *) info->par; + + multiplier = multiplier * par->lcd_width; + divider = divider * pll->xres & ~7; + + ras_multiplier = ras_multiplier * par->lcd_width; + ras_divider = ras_divider * pll->xres & ~7; } - dsp_precision = 0; - y = (xclks_per_row * fifo_size) >> 11; - while (y) { - y >>= 1; - dsp_precision++; - } - dsp_precision -= 5; - /* fifo_off<<6 */ - fifo_off = ((xclks_per_row * (fifo_size - 1)) >> 5) + (3 << 6); - - if (info->fix.smem_len > 1 * 1024 * 1024) { - if (par->ram_type >= SDRAM) { - /* >1 MB SDRAM */ - dsp_loop_latency += 8; - page_size = 8; - } else { - /* >1 MB DRAM */ - dsp_loop_latency += 6; - page_size = 9; - } - } else { - if (par->ram_type >= SDRAM) { - /* <2 MB SDRAM */ - dsp_loop_latency += 9; - page_size = 10; - } else { - /* <2 MB DRAM */ - dsp_loop_latency += 8; - page_size = 10; - } +#endif + /* If we don't do this, 32 bits for multiplier & divider won't be + enough in certain situations! */ + while (((multiplier | divider) & 1) == 0) { + multiplier = multiplier >> 1; + divider = divider >> 1; } - /* fifo_on<<6 */ - if (xclks_per_row >= (page_size << 11)) - fifo_on = - ((2 * page_size + 1) << 6) + (xclks_per_row >> 5); - else - fifo_on = (3 * page_size + 2) << 6; - dsp_xclks_per_row = xclks_per_row >> dsp_precision; - dsp_on = fifo_on >> dsp_precision; - dsp_off = fifo_off >> dsp_precision; - - pll->dsp_config = (dsp_xclks_per_row & 0x3fff) | - ((dsp_loop_latency & 0xf) << 16) | ((dsp_precision & 7) << 20); - pll->dsp_on_off = (dsp_on & 0x7ff) | ((dsp_off & 0x7ff) << 16); - return 0; -} + /* Determine DSP precision first */ + tmp = ((multiplier * pll->fifo_size) << vshift) / divider; -static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per, - struct pll_ct *pll) -{ - struct atyfb_par *par = (struct atyfb_par *) info->par; - u32 q, x; /* x is a workaround for sparc64-linux-gcc */ - x = x; /* x is a workaround for sparc64-linux-gcc */ + for (dsp_precision = -5; tmp; dsp_precision++) + tmp >>= 1; + if (dsp_precision < 0) + dsp_precision = 0; + else if (dsp_precision > Maximum_DSP_PRECISION) + dsp_precision = Maximum_DSP_PRECISION; + + xshift = 6 - dsp_precision; + vshift += xshift; + + /* Move on to dsp_off */ + dsp_off = ((multiplier * (pll->fifo_size - 1)) << vshift) / divider - + (1 << (vshift - xshift)); + +/* if (bpp == 0) + dsp_on = ((multiplier * 20 << vshift) + divider) / divider; + else */ + { + dsp_on = ((multiplier << vshift) + divider) / divider; + tmp = ((ras_multiplier << xshift) + ras_divider) / ras_divider; + if (dsp_on < tmp) + dsp_on = tmp; + dsp_on = dsp_on + (tmp * 2) + (pll->xclkpagefaultdelay << xshift); + } - pll->pll_ref_div = par->pll_per * 2 * 255 / par->ref_clk_per; + /* Calculate rounding factor and apply it to dsp_on */ + tmp = ((1 << (Maximum_DSP_PRECISION - dsp_precision)) - 1) >> 1; + dsp_on = ((dsp_on + tmp) / (tmp + 1)) * (tmp + 1); + + if (dsp_on >= ((dsp_off / (tmp + 1)) * (tmp + 1))) { + dsp_on = dsp_off - (multiplier << vshift) / divider; + dsp_on = (dsp_on / (tmp + 1)) * (tmp + 1); + } - /* FIXME: use the VTB/GTB /3 post divider if it's better suited */ - q = par->ref_clk_per * pll->pll_ref_div * 4 / par->mclk_per; /* actually 8*q */ - if (q < 16 * 8 || q > 255 * 8) - FAIL("mclk out of range"); - else if (q < 32 * 8) - pll->mclk_post_div_real = 8; - else if (q < 64 * 8) - pll->mclk_post_div_real = 4; - else if (q < 128 * 8) - pll->mclk_post_div_real = 2; - else - pll->mclk_post_div_real = 1; - pll->mclk_fb_div = q * pll->mclk_post_div_real / 8; + /* Last but not least: dsp_xclks */ + dsp_xclks = ((multiplier << (vshift + 5)) + divider) / divider; - /* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */ - q = par->ref_clk_per * pll->pll_ref_div * 4 / vclk_per; /* actually 8*q */ - if (q < 16 * 8 || q > 255 * 8) - FAIL("vclk out of range"); - else if (q < 32 * 8) - pll->vclk_post_div_real = 8; - else if (q < 64 * 8) - pll->vclk_post_div_real = 4; - else if (q < 128 * 8) - pll->vclk_post_div_real = 2; - else - pll->vclk_post_div_real = 1; - pll->vclk_fb_div = q * pll->vclk_post_div_real / 8; + /* Get register values. */ + pll->dsp_on_off = (dsp_on << 16) + dsp_off; + pll->dsp_config = (dsp_precision << 20) | (pll->dsp_loop_latency << 16) | dsp_xclks; +#ifdef DEBUG + printk("atyfb(%s): dsp_config 0x%08x, dsp_on_off 0x%08x\n", + __FUNCTION__, pll->dsp_config, pll->dsp_on_off); +#endif return 0; } -void aty_calc_pll_ct(const struct fb_info *info, struct pll_ct *pll) +static int aty_valid_pll_ct(const struct fb_info *info, u32 vclk_per, struct pll_ct *pll) { + u32 q; struct atyfb_par *par = (struct atyfb_par *) info->par; - u8 mpostdiv = 0; - u8 vpostdiv = 0; - - if (M64_HAS(SDRAM_MAGIC_PLL) && (par->ram_type >= SDRAM)) - pll->pll_gen_cntl = 0x04; - else - pll->pll_gen_cntl = 0x84; - - switch (pll->mclk_post_div_real) { - case 1: - mpostdiv = 0; - break; - case 2: - mpostdiv = 1; - break; - case 3: - mpostdiv = 4; - break; - case 4: - mpostdiv = 2; - break; - case 8: - mpostdiv = 3; - break; - } - pll->pll_gen_cntl |= mpostdiv << 4; /* mclk */ - - if (M64_HAS(MAGIC_POSTDIV)) - pll->pll_ext_cntl = 0; - else - pll->pll_ext_cntl = mpostdiv; /* xclk == mclk */ +#ifdef DEBUG + int pllvclk; +#endif - switch (pll->vclk_post_div_real) { - case 2: - vpostdiv = 1; - break; - case 3: - pll->pll_ext_cntl |= 0x10; - case 1: - vpostdiv = 0; - break; - case 6: - pll->pll_ext_cntl |= 0x10; - case 4: - vpostdiv = 2; - break; - case 12: - pll->pll_ext_cntl |= 0x10; - case 8: - vpostdiv = 3; - break; + /* FIXME: use the VTB/GTB /{3,6,12} post dividers if they're better suited */ + q = par->ref_clk_per * pll->pll_ref_div * 4 / vclk_per; + if (q < 16*8 || q > 255*8) { + printk(KERN_CRIT "atyfb: vclk out of range\n"); + return -EINVAL; + } else { + pll->vclk_post_div = (q < 128*8); + pll->vclk_post_div += (q < 64*8); + pll->vclk_post_div += (q < 32*8); } - - pll->pll_vclk_cntl = 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */ - pll->vclk_post_div = vpostdiv; + pll->vclk_post_div_real = postdividers[pll->vclk_post_div]; + // pll->vclk_post_div <<= 6; + pll->vclk_fb_div = q * pll->vclk_post_div_real / 8; +#ifdef DEBUG + pllvclk = (1000000 * 2 * pll->vclk_fb_div) / + (par->ref_clk_per * pll->pll_ref_div); + printk("atyfb(%s): pllvclk=%d MHz, vclk=%d MHz\n", + __FUNCTION__, pllvclk, pllvclk / pll->vclk_post_div_real); +#endif + pll->pll_vclk_cntl = 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */ + return 0; } -static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, - u8 bpp, union aty_pll *pll) +static int aty_var_to_pll_ct(const struct fb_info *info, u32 vclk_per, u32 bpp, union aty_pll *pll) { struct atyfb_par *par = (struct atyfb_par *) info->par; int err; @@ -220,47 +242,358 @@ static int aty_var_to_pll_ct(const struc return err; if (M64_HAS(GTB_DSP) && (err = aty_dsp_gt(info, bpp, &pll->ct))) return err; - aty_calc_pll_ct(info, &pll->ct); + /*aty_calc_pll_ct(info, &pll->ct);*/ return 0; } -static u32 aty_pll_ct_to_var(const struct fb_info *info, - const union aty_pll *pll) +static u32 aty_pll_to_var_ct(const struct fb_info *info, const union aty_pll *pll) { struct atyfb_par *par = (struct atyfb_par *) info->par; - - u32 ref_clk_per = par->ref_clk_per; - u8 pll_ref_div = pll->ct.pll_ref_div; - u8 vclk_fb_div = pll->ct.vclk_fb_div; - u8 vclk_post_div = pll->ct.vclk_post_div_real; - - return ref_clk_per * pll_ref_div * vclk_post_div / vclk_fb_div / 2; + u32 ret; + ret = par->ref_clk_per * pll->ct.pll_ref_div * pll->ct.vclk_post_div_real / pll->ct.vclk_fb_div / 2; +#ifdef CONFIG_FB_ATY_GENERIC_LCD + if(pll->ct.xres > 0) { + ret *= par->lcd_width; + ret /= pll->ct.xres; + } +#endif +#ifdef DEBUG + printk("atyfb(%s): calculated 0x%08X(%i)\n", __FUNCTION__, ret, ret); +#endif + return ret; } -void aty_set_pll_ct(const struct fb_info *info, - const union aty_pll *pll) +void aty_set_pll_ct(const struct fb_info *info, const union aty_pll *pll) { struct atyfb_par *par = (struct atyfb_par *) info->par; + u32 crtc_gen_cntl, lcd_gen_cntrl = 0; + u8 tmp, tmp2; +#ifdef DEBUG + printk("atyfb(%s): about to program:\n" + "pll_ext_cntl=0x%02x pll_gen_cntl=0x%02x pll_vclk_cntl=0x%02x\n", + __FUNCTION__, + pll->ct.pll_ext_cntl, pll->ct.pll_gen_cntl, pll->ct.pll_vclk_cntl); + + printk("atyfb(%s): setting clock %lu for FeedBackDivider %i, ReferenceDivider %i, PostDivider %i(%i)\n", + __FUNCTION__, + par->clk_wr_offset, pll->ct.vclk_fb_div, + pll->ct.pll_ref_div, pll->ct.vclk_post_div, pll->ct.vclk_post_div_real); +#endif +#ifdef CONFIG_FB_ATY_GENERIC_LCD + if (par->lcd_table != 0) { + /* turn off LCD */ + lcd_gen_cntrl = aty_ld_lcd(LCD_GEN_CNTL, par); + aty_st_lcd(LCD_GEN_CNTL, lcd_gen_cntrl & ~LCD_ON, par); + } +#endif + aty_st_8(CLOCK_CNTL, par->clk_wr_offset | CLOCK_STROBE, par); - aty_st_pll(PLL_REF_DIV, pll->ct.pll_ref_div, par); - aty_st_pll(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par); - aty_st_pll(MCLK_FB_DIV, pll->ct.mclk_fb_div, par); - aty_st_pll(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par); - aty_st_pll(VCLK_POST_DIV, pll->ct.vclk_post_div, par); - aty_st_pll(VCLK0_FB_DIV, pll->ct.vclk_fb_div, par); - aty_st_pll(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par); + /* Temporarily switch to accelerator mode */ + crtc_gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par); + if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN)) + aty_st_le32(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN, par); + + /* Reset VCLK generator */ + aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par); + + /* Set post-divider */ + tmp2 = par->clk_wr_offset << 1; + tmp = aty_ld_pll_ct(VCLK_POST_DIV, par); + tmp &= ~(0x03U << tmp2); + tmp |= ((pll->ct.vclk_post_div & 0x03U) << tmp2); + aty_st_pll_ct(VCLK_POST_DIV, tmp, par); + + /* Set extended post-divider */ + tmp = aty_ld_pll_ct(PLL_EXT_CNTL, par); + tmp &= ~(0x10U << par->clk_wr_offset); + tmp &= 0xF0U; + tmp |= pll->ct.pll_ext_cntl; + aty_st_pll_ct(PLL_EXT_CNTL, tmp, par); + + /* Set feedback divider */ + tmp = VCLK0_FB_DIV + par->clk_wr_offset; + aty_st_pll_ct(tmp, (pll->ct.vclk_fb_div & 0xFFU), par); + + aty_st_pll_ct(PLL_GEN_CNTL, (pll->ct.pll_gen_cntl & (~(PLL_OVERRIDE | PLL_MCLK_RST))) | OSC_EN, par); + + /* End VCLK generator reset */ + aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl & ~(PLL_VCLK_RST), par); + mdelay(5); + + aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par); + aty_st_pll_ct(PLL_VCLK_CNTL, pll->ct.pll_vclk_cntl, par); + mdelay(1); + + /* Restore mode register */ + if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN)) + aty_st_le32(CRTC_GEN_CNTL, crtc_gen_cntl, par); if (M64_HAS(GTB_DSP)) { + u8 dll_cntl; + if (M64_HAS(XL_DLL)) - aty_st_pll(DLL_CNTL, 0x80, par); + dll_cntl = 0x80; else if (par->ram_type >= SDRAM) - aty_st_pll(DLL_CNTL, 0xa6, par); + dll_cntl = 0xa6; else - aty_st_pll(DLL_CNTL, 0xa0, par); - aty_st_pll(VFC_CNTL, 0x1b, par); + dll_cntl = 0xa0; + aty_st_pll_ct(DLL_CNTL, dll_cntl, par); + aty_st_pll_ct(VFC_CNTL, 0x1b, par); aty_st_le32(DSP_CONFIG, pll->ct.dsp_config, par); aty_st_le32(DSP_ON_OFF, pll->ct.dsp_on_off, par); + + mdelay(10); + aty_st_pll_ct(DLL_CNTL, dll_cntl, par); + mdelay(10); + aty_st_pll_ct(DLL_CNTL, dll_cntl | 0x40, par); + mdelay(10); + aty_st_pll_ct(DLL_CNTL, dll_cntl & ~0x40, par); } +#ifdef CONFIG_FB_ATY_GENERIC_LCD + if (par->lcd_table != 0) { + /* restore LCD */ + aty_st_lcd(LCD_GEN_CNTL, lcd_gen_cntrl, par); + } +#endif +} + +void __init aty_get_pll_ct(const struct fb_info *info, union aty_pll *pll) +{ + struct atyfb_par *par = (struct atyfb_par *) info->par; + u8 tmp, clock; + + clock = aty_ld_8(CLOCK_CNTL, par) & 0x03U; + tmp = clock << 1; + pll->ct.vclk_post_div = (aty_ld_pll_ct(VCLK_POST_DIV, par) >> tmp) & 0x03U; + + pll->ct.pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par) & 0x0FU; + pll->ct.vclk_fb_div = aty_ld_pll_ct(VCLK0_FB_DIV + clock, par) & 0xFFU; + pll->ct.pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par); + pll->ct.mclk_fb_div = aty_ld_pll_ct(MCLK_FB_DIV, par); + + pll->ct.pll_gen_cntl = aty_ld_pll_ct(PLL_GEN_CNTL, par); + pll->ct.pll_vclk_cntl = aty_ld_pll_ct(PLL_VCLK_CNTL, par); + + if (M64_HAS(GTB_DSP)) { + pll->ct.dsp_config = aty_ld_le32(DSP_CONFIG, par); + pll->ct.dsp_on_off = aty_ld_le32(DSP_ON_OFF, par); + } +} + +int __init aty_init_pll_ct(const struct fb_info *info, union aty_pll *pll) { + struct atyfb_par *par = (struct atyfb_par *) info->par; + u8 mpost_div, xpost_div, sclk_post_div_real, sclk_fb_div, spll_cntl2; + u32 q, i, memcntl, trp; + u32 dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off; +#ifdef DEBUG + int pllmclk, pllsclk; +#endif + pll->ct.pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par); + pll->ct.xclk_post_div = pll->ct.pll_ext_cntl & 0x07; + pll->ct.xclk_ref_div = 1; + switch (pll->ct.xclk_post_div) { + case 0: case 1: case 2: case 3: + break; + + case 4: + pll->ct.xclk_ref_div = 3; + pll->ct.xclk_post_div = 0; + break; + + default: + printk(KERN_CRIT "atyfb: Unsupported xclk source: %d.\n", pll->ct.xclk_post_div); + return -EINVAL; + } + pll->ct.mclk_fb_mult = 2; + if(pll->ct.pll_ext_cntl & PLL_MFB_TIMES_4_2B) { + pll->ct.mclk_fb_mult = 4; + pll->ct.xclk_post_div -= 1; + } + +#ifdef DEBUG + printk("atyfb(%s): mclk_fb_mult=%d, xclk_post_div=%d\n", + __FUNCTION__, pll->ct.mclk_fb_mult, pll->ct.xclk_post_div); +#endif + + memcntl = aty_ld_le32(MEM_CNTL, par); + trp = (memcntl & 0x300) >> 8; + + pll->ct.xclkpagefaultdelay = ((memcntl & 0xc00) >> 10) + ((memcntl & 0x1000) >> 12) + trp + 2; + pll->ct.xclkmaxrasdelay = ((memcntl & 0x70000) >> 16) + trp + 2; + + if (M64_HAS(FIFO_32)) { + pll->ct.fifo_size = 32; + } else { + pll->ct.fifo_size = 24; + pll->ct.xclkpagefaultdelay += 2; + pll->ct.xclkmaxrasdelay += 3; + } + + switch (par->ram_type) { + case DRAM: + if (info->fix.smem_len<=ONE_MB) { + pll->ct.dsp_loop_latency = 10; + } else { + pll->ct.dsp_loop_latency = 8; + pll->ct.xclkpagefaultdelay += 2; + } + break; + case EDO: + case PSEUDO_EDO: + if (info->fix.smem_len<=ONE_MB) { + pll->ct.dsp_loop_latency = 9; + } else { + pll->ct.dsp_loop_latency = 8; + pll->ct.xclkpagefaultdelay += 1; + } + break; + case SDRAM: + if (info->fix.smem_len<=ONE_MB) { + pll->ct.dsp_loop_latency = 11; + } else { + pll->ct.dsp_loop_latency = 10; + pll->ct.xclkpagefaultdelay += 1; + } + break; + case SGRAM: + pll->ct.dsp_loop_latency = 8; + pll->ct.xclkpagefaultdelay += 3; + break; + default: + pll->ct.dsp_loop_latency = 11; + pll->ct.xclkpagefaultdelay += 3; + break; + } + + if (pll->ct.xclkmaxrasdelay <= pll->ct.xclkpagefaultdelay) + pll->ct.xclkmaxrasdelay = pll->ct.xclkpagefaultdelay + 1; + + /* Allow BIOS to override */ + dsp_config = aty_ld_le32(DSP_CONFIG, par); + dsp_on_off = aty_ld_le32(DSP_ON_OFF, par); + vga_dsp_config = aty_ld_le32(VGA_DSP_CONFIG, par); + vga_dsp_on_off = aty_ld_le32(VGA_DSP_ON_OFF, par); + + if (dsp_config) + pll->ct.dsp_loop_latency = (dsp_config & DSP_LOOP_LATENCY) >> 16; +#if 0 + FIXME: is it relevant for us? + if ((!dsp_on_off && !M64_HAS(RESET_3D)) || + ((dsp_on_off == vga_dsp_on_off) && + (!dsp_config || !((dsp_config ^ vga_dsp_config) & DSP_XCLKS_PER_QW)))) { + vga_dsp_on_off &= VGA_DSP_OFF; + vga_dsp_config &= VGA_DSP_XCLKS_PER_QW; + if (ATIDivide(vga_dsp_on_off, vga_dsp_config, 5, 1) > 24) + pll->ct.fifo_size = 32; + else + pll->ct.fifo_size = 24; + } +#endif + /* Exit if the user does not want us to tamper with the clock + rates of her chip. */ + if (par->mclk_per == 0) { + u8 mclk_fb_div, pll_ext_cntl; + pll->ct.pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par); + pll_ext_cntl = aty_ld_pll_ct(PLL_EXT_CNTL, par); + pll->ct.xclk_post_div_real = postdividers[pll_ext_cntl & 0x07]; + mclk_fb_div = aty_ld_pll_ct(MCLK_FB_DIV, par); + if (pll_ext_cntl & PLL_MFB_TIMES_4_2B) + mclk_fb_div <<= 1; + pll->ct.mclk_fb_div = mclk_fb_div; + return 0; + } + + pll->ct.pll_ref_div = par->pll_per * 2 * 255 / par->ref_clk_per; + + /* FIXME: use the VTB/GTB /3 post divider if it's better suited */ + q = par->ref_clk_per * pll->ct.pll_ref_div * 8 / + (pll->ct.mclk_fb_mult * par->xclk_per); + + if (q < 16*8 || q > 255*8) { + printk(KERN_CRIT "atxfb: xclk out of range\n"); + return -EINVAL; + } else { + xpost_div = (q < 128*8); + xpost_div += (q < 64*8); + xpost_div += (q < 32*8); + } + pll->ct.xclk_post_div_real = postdividers[xpost_div]; + pll->ct.mclk_fb_div = q * pll->ct.xclk_post_div_real / 8; + +#ifdef DEBUG + pllmclk = (1000000 * pll->ct.mclk_fb_mult * pll->ct.mclk_fb_div) / + (par->ref_clk_per * pll->ct.pll_ref_div); + printk("atyfb(%s): pllmclk=%d MHz, xclk=%d MHz\n", + __FUNCTION__, pllmclk, pllmclk / pll->ct.xclk_post_div_real); +#endif + + if (M64_HAS(SDRAM_MAGIC_PLL) && (par->ram_type >= SDRAM)) + pll->ct.pll_gen_cntl = OSC_EN; + else + pll->ct.pll_gen_cntl = OSC_EN | DLL_PWDN /* | FORCE_DCLK_TRI_STATE */; + + if (M64_HAS(MAGIC_POSTDIV)) + pll->ct.pll_ext_cntl = 0; + else + pll->ct.pll_ext_cntl = xpost_div; + + if (pll->ct.mclk_fb_mult == 4) + pll->ct.pll_ext_cntl |= PLL_MFB_TIMES_4_2B; + + if (par->mclk_per == par->xclk_per) { + pll->ct.pll_gen_cntl |= (xpost_div << 4); /* mclk == xclk */ + } else { + /* + * The chip clock is not equal to the memory clock. + * Therefore we will use sclk to clock the chip. + */ + pll->ct.pll_gen_cntl |= (6 << 4); /* mclk == sclk */ + + q = par->ref_clk_per * pll->ct.pll_ref_div * 4 / par->mclk_per; + if (q < 16*8 || q > 255*8) { + printk(KERN_CRIT "atyfb: mclk out of range\n"); + return -EINVAL; + } else { + mpost_div = (q < 128*8); + mpost_div += (q < 64*8); + mpost_div += (q < 32*8); + } + sclk_post_div_real = postdividers[mpost_div]; + sclk_fb_div = q * sclk_post_div_real / 8; + spll_cntl2 = mpost_div << 4; +#ifdef DEBUG + pllsclk = (1000000 * 2 * sclk_fb_div) / + (par->ref_clk_per * pll->ct.pll_ref_div); + printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n", + __FUNCTION__, pllsclk, pllsclk / sclk_post_div_real); +#endif + /* + * This disables the sclk, crashes the computer as reported: + * aty_st_pll_ct(SPLL_CNTL2, 3, info); + * + * So it seems the sclk must be enabled before it is used; + * so PLL_GEN_CNTL must be programmed *after* the sclk. + */ + aty_st_pll_ct(SCLK_FB_DIV, sclk_fb_div, par); + aty_st_pll_ct(SPLL_CNTL2, spll_cntl2, par); + /* + * The sclk has been started. However, I believe the first clock + * ticks it generates are not very stable. Hope this primitive loop + * helps for Rage Mobilities that sometimes crash when + * we switch to sclk. (Daniel Mantione, 13-05-2003) + */ + for (i=0;i<=0x1ffff;i++); + } + + aty_st_pll_ct(PLL_REF_DIV, pll->ct.pll_ref_div, par); + aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par); + aty_st_pll_ct(MCLK_FB_DIV, pll->ct.mclk_fb_div, par); + aty_st_pll_ct(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par); + /* Disable the extra precision pixel clock controls since we do not use them. */ + aty_st_pll_ct(EXT_VPLL_CNTL, aty_ld_pll_ct(EXT_VPLL_CNTL, par) & + ~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC), par); + + return 0; } static int dummy(void) @@ -274,6 +607,8 @@ const struct aty_dac_ops aty_dac_ct = { const struct aty_pll_ops aty_pll_ct = { .var_to_pll = aty_var_to_pll_ct, - .pll_to_var = aty_pll_ct_to_var, + .pll_to_var = aty_pll_to_var_ct, .set_pll = aty_set_pll_ct, + .get_pll = aty_get_pll_ct, + .init_pll = aty_init_pll_ct }; diff -puN drivers/video/aty/mach64_cursor.c~fbdev-various-mach64-changes drivers/video/aty/mach64_cursor.c --- 25-sparc64/drivers/video/aty/mach64_cursor.c~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/mach64_cursor.c 2004-10-22 00:57:46.722624544 -0700 @@ -1,12 +1,11 @@ - /* * ATI Mach64 CT/VT/GT/LT Cursor Support */ #include <linux/slab.h> -#include <linux/console.h> #include <linux/fb.h> #include <linux/init.h> +#include <linux/string.h> #include <asm/io.h> #include <asm/uaccess.h> @@ -19,13 +18,49 @@ #include <video/mach64.h> #include "atyfb.h" +/* + * The hardware cursor definition requires 2 bits per pixel. The + * Cursor size reguardless of the visible cursor size is 64 pixels + * by 64 lines. The total memory required to define the cursor is + * 16 bytes / line for 64 lines or 1024 bytes of data. The data + * must be in a contigiuos format. The 2 bit cursor code values are + * as follows: + * + * 00 - pixel colour = CURSOR_CLR_0 + * 01 - pixel colour = CURSOR_CLR_1 + * 10 - pixel colour = transparent (current display pixel) + * 11 - pixel colour = 1's complement of current display pixel + * + * Cursor Offset 64 pixels Actual Displayed Area + * \_________________________/ + * | | | | + * |<--------------->| | | + * | CURS_HORZ_OFFSET| | | + * | |_______| | 64 Lines + * | ^ | | + * | | | | + * | CURS_VERT_OFFSET| | + * | | | | + * |____________________|____| | + * + * + * The Screen position of the top left corner of the displayed + * cursor is specificed by CURS_HORZ_VERT_POSN. Care must be taken + * when the cursor hot spot is not the top left corner and the + * physical cursor position becomes negative. It will be be displayed + * if either the horizontal or vertical cursor position is negative + * + * If x becomes negative the cursor manager must adjust the CURS_HORZ_OFFSET + * to a larger number and saturate CUR_HORZ_POSN to zero. + * + * if Y becomes negative, CUR_VERT_OFFSET must be adjusted to a larger number, + * CUR_OFFSET must be adjusted to a point to the appropraite line in the cursor + * definitation and CUR_VERT_POSN must be saturated to zero. + */ + /* * Hardware Cursor support. */ - -static const u8 cursor_pixel_map[2] = { 0, 15 }; -static const u8 cursor_color_map[2] = { 0, 0xff }; - static const u8 cursor_bits_lookup[16] = { 0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54, 0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55 @@ -36,90 +71,39 @@ static const u8 cursor_mask_lookup[16] = 0xa8, 0x28, 0x88, 0x08, 0xa0, 0x20, 0x80, 0x00 }; -void aty_set_cursor_color(struct fb_info *info) -{ - struct atyfb_par *par = (struct atyfb_par *) info->par; - struct aty_cursor *c = par->cursor; - const u8 *pixel = cursor_pixel_map; /* ++Geert: Why?? */ - const u8 *red = cursor_color_map; - const u8 *green = cursor_color_map; - const u8 *blue = cursor_color_map; - u32 fg_color, bg_color; - - if (!c) - return; - -#ifdef __sparc__ - if (par->mmaped) - return; -#endif - fg_color = (u32) red[0] << 24; - fg_color |= (u32) green[0] << 16; - fg_color |= (u32) blue[0] << 8; - fg_color |= (u32) pixel[0]; - - bg_color = (u32) red[1] << 24; - bg_color |= (u32) green[1] << 16; - bg_color |= (u32) blue[1] << 8; - bg_color |= (u32) pixel[1]; - - wait_for_fifo(2, par); - aty_st_le32(CUR_CLR0, fg_color, par); - aty_st_le32(CUR_CLR1, bg_color, par); -} - -void aty_set_cursor_shape(struct fb_info *info) +int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor) { struct atyfb_par *par = (struct atyfb_par *) info->par; - struct fb_cursor *cursor = &info->cursor; - struct aty_cursor *c = par->cursor; - u8 *ram, m, b; + u16 xoff, yoff; int x, y; - if (!c) - return; #ifdef __sparc__ if (par->mmaped) - return; + return -EPERM; #endif + if (par->asleep) + return -EPERM; - ram = c->ram; - for (y = 0; y < cursor->image.height; y++) { - for (x = 0; x < cursor->image.width >> 2; x++) { - m = c->mask[x][y]; - b = c->bits[x][y]; - fb_writeb(cursor_mask_lookup[m >> 4] | - cursor_bits_lookup[(b & m) >> 4], ram++); - fb_writeb(cursor_mask_lookup[m & 0x0f] | - cursor_bits_lookup[(b & m) & 0x0f], - ram++); - } - for (; x < 8; x++) { - fb_writeb(0xaa, ram++); - fb_writeb(0xaa, ram++); - } + /* Hide cursor */ + wait_for_fifo(1, par); + aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par) & ~HWCURSOR_ENABLE, par); + + /* Set size */ + if (cursor->set & FB_CUR_SETSIZE) { + info->cursor.image.height = cursor->image.height; + info->cursor.image.width = cursor->image.width; } - fb_memset(ram, 0xaa, (64 - cursor->image.height) * 16); -} -static void aty_set_cursor(struct fb_info *info) -{ - struct atyfb_par *par = (struct atyfb_par *) info->par; - struct fb_cursor *cursor = &info->cursor; - struct aty_cursor *c = par->cursor; - u16 xoff, yoff; - int x, y; + /* Set hot spot */ + if (cursor->set & FB_CUR_SETHOT) + info->cursor.hot = cursor->hot; + + /* set position */ + if (cursor->set & FB_CUR_SETPOS) { + info->cursor.image.dx = cursor->image.dx; + info->cursor.image.dy = cursor->image.dy; - if (!c) - return; - -#ifdef __sparc__ - if (par->mmaped) - return; -#endif - - if (cursor->enable) { - x = cursor->image.dx - cursor->hot.x - info->var.xoffset; + x = info->cursor.image.dx - info->cursor.hot.x - info->var.xoffset; if (x < 0) { xoff = -x; x = 0; @@ -127,7 +111,7 @@ static void aty_set_cursor(struct fb_inf xoff = 0; } - y = cursor->image.dy - cursor->hot.y - info->var.yoffset; + y = info->cursor.image.dy - info->cursor.hot.y - info->var.yoffset; if (y < 0) { yoff = -y; y = 0; @@ -135,107 +119,120 @@ static void aty_set_cursor(struct fb_inf yoff = 0; } + /* + * In doublescan mode, the cursor location also needs to be + * doubled. + */ + if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) + y<<=1; wait_for_fifo(4, par); - aty_st_le32(CUR_OFFSET, (info->fix.smem_len >> 3) + (yoff << 1), - par); + aty_st_le32(CUR_OFFSET, (info->fix.smem_len >> 3) + (yoff << 1), par); aty_st_le32(CUR_HORZ_VERT_OFF, - ((u32) (64 - cursor->image.height + yoff) << 16) | xoff, + ((u32) (64 - info->cursor.image.height + yoff) << 16) | xoff, par); aty_st_le32(CUR_HORZ_VERT_POSN, ((u32) y << 16) | x, par); - aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par) - | HWCURSOR_ENABLE, par); - } else { - wait_for_fifo(1, par); - aty_st_le32(GEN_TEST_CNTL, - aty_ld_le32(GEN_TEST_CNTL, - par) & ~HWCURSOR_ENABLE, par); } - if (par->blitter_may_be_busy) - wait_for_idle(par); -} - -int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor) -{ - struct atyfb_par *par = (struct atyfb_par *) info->par; - struct aty_cursor *c = par->cursor; - if (!c) - return -1; - -#ifdef __sparc__ - if (par->mmaped) - return 0; -#endif + /* Set color map */ + if (cursor->set & FB_CUR_SETCMAP) { + u32 fg_idx, bg_idx, fg, bg; + + info->cursor.image.fg_color = cursor->image.fg_color; + info->cursor.image.bg_color = cursor->image.bg_color; + fg_idx = info->cursor.image.fg_color; + bg_idx = info->cursor.image.bg_color; + + fg = (info->cmap.red[fg_idx] << 24) | + (info->cmap.green[fg_idx] << 16) | + (info->cmap.blue[fg_idx] << 8) | 15; + + bg = (info->cmap.red[bg_idx] << 24) | + (info->cmap.green[bg_idx] << 16) | + (info->cmap.blue[bg_idx] << 8); + + wait_for_fifo(2, par); + aty_st_le32(CUR_CLR0, bg, par); + aty_st_le32(CUR_CLR1, fg, par); + } - aty_set_cursor(info); - cursor->image.dx = info->cursor.image.dx; - cursor->image.dy = info->cursor.image.dy; + if (cursor->set & FB_CUR_SETSHAPE) { + u8 *src = (u8 *)cursor->image.data; + u8 *msk = (u8 *)info->cursor.mask; + u8 *dst = (u8 *)info->sprite.addr; + unsigned int width = (info->cursor.image.width + 7) >> 3; + unsigned int height = info->cursor.image.height; + unsigned int align = info->sprite.scan_align; + + unsigned int i, j, offset; + u8 m, b; + + // Clear cursor image with 1010101010... + fb_memset(dst, 0xaa, 1024); + + offset = align - width*2; + + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + b = *src++; + m = *msk++; + switch (info->cursor.rop) { + case ROP_XOR: + // Upper 4 bits of mask data + fb_writeb(cursor_mask_lookup[m >> 4 ] | + cursor_bits_lookup[(b ^ m) >> 4], dst++); + // Lower 4 bits of mask + fb_writeb(cursor_mask_lookup[m & 0x0f ] | + cursor_bits_lookup[(b ^ m) & 0x0f], dst++); + break; + case ROP_COPY: + // Upper 4 bits of mask data + fb_writeb(cursor_mask_lookup[m >> 4 ] | + cursor_bits_lookup[(b & m) >> 4], dst++); + // Lower 4 bits of mask + fb_writeb(cursor_mask_lookup[m & 0x0f ] | + cursor_bits_lookup[(b & m) & 0x0f], dst++); + break; + } + } + dst += offset; + } + } - aty_set_cursor(info); + if (cursor->enable) { + wait_for_fifo(1, par); + aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par) + | HWCURSOR_ENABLE, par); + } return 0; } -struct aty_cursor *__init aty_init_cursor(struct fb_info *info) +int __init aty_init_cursor(struct fb_info *info) { - struct aty_cursor *cursor; unsigned long addr; - cursor = kmalloc(sizeof(struct aty_cursor), GFP_ATOMIC); - if (!cursor) - return NULL; - memset(cursor, 0, sizeof(*cursor)); - info->fix.smem_len -= PAGE_SIZE; #ifdef __sparc__ addr = (unsigned long) info->screen_base - 0x800000 + info->fix.smem_len; - cursor->ram = (u8 *) addr; + info->sprite.addr = (u8 *) addr; #else #ifdef __BIG_ENDIAN addr = info->fix.smem_start - 0x800000 + info->fix.smem_len; - cursor->ram = (u8 *) ioremap(addr, 1024); + info->sprite.addr = (u8 *) ioremap(addr, 1024); #else addr = (unsigned long) info->screen_base + info->fix.smem_len; - cursor->ram = (u8 *) addr; + info->sprite.addr = (u8 *) addr; #endif #endif - if (!cursor->ram) { - kfree(cursor); - return NULL; - } - return cursor; -} + if (!info->sprite.addr) + return -ENXIO; + info->sprite.size = PAGE_SIZE; + info->sprite.scan_align = 16; /* Scratch pad 64 bytes wide */ + info->sprite.buf_align = 16; /* and 64 lines tall. */ + info->sprite.flags = FB_PIXMAP_IO; -int atyfb_set_font(struct fb_info *info, int width, int height) -{ - struct atyfb_par *par = (struct atyfb_par *) info->par; - struct fb_cursor *cursor = &info->cursor; - struct aty_cursor *c = par->cursor; - int i, j; - - if (c) { - if (!width || !height) { - width = 8; - height = 16; - } - - cursor->hot.x = 0; - cursor->hot.y = 0; - cursor->image.width = width; - cursor->image.height = height; - - memset(c->bits, 0xff, sizeof(c->bits)); - memset(c->mask, 0, sizeof(c->mask)); - - for (i = 0, j = width; j >= 0; j -= 8, i++) { - c->mask[i][height - 2] = - (j >= 8) ? 0xff : (0xff << (8 - j)); - c->mask[i][height - 1] = - (j >= 8) ? 0xff : (0xff << (8 - j)); - } + info->fbops->fb_cursor = atyfb_cursor; - aty_set_cursor_color(info); - aty_set_cursor_shape(info); - } - return 1; + return 0; } + diff -puN drivers/video/aty/mach64_gx.c~fbdev-various-mach64-changes drivers/video/aty/mach64_gx.c --- 25-sparc64/drivers/video/aty/mach64_gx.c~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/mach64_gx.c 2004-10-22 00:41:35.000000000 -0700 @@ -121,7 +121,7 @@ static int aty_set_dac_514(const struct } static int aty_var_to_pll_514(const struct fb_info *info, u32 vclk_per, - u8 bpp, union aty_pll *pll) + u32 bpp, union aty_pll *pll) { /* * FIXME: use real calculations instead of using fixed values from the old @@ -253,9 +253,9 @@ static int aty_set_dac_ATI68860_B(const temp = aty_ld_8(DAC_CNTL, par); aty_st_8(DAC_CNTL, temp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par); - if (info->fix.smem_len < MEM_SIZE_1M) + if (info->fix.smem_len < ONE_MB) mask = 0x04; - else if (info->fix.smem_len == MEM_SIZE_1M) + else if (info->fix.smem_len == ONE_MB) mask = 0x08; else mask = 0x0C; @@ -339,8 +339,8 @@ const struct aty_dac_ops aty_dac_att21c4 * ATI 18818 / ICS 2595 Clock Chip */ -static int aty_var_to_pll_18818(const struct fb_info *info, - u32 vclk_per, u8 bpp, union aty_pll *pll) +static int aty_var_to_pll_18818(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) { u32 MHz100; /* in 0.01 MHz */ u32 program_bits; @@ -495,8 +495,8 @@ const struct aty_pll_ops aty_pll_ati1881 * STG 1703 Clock Chip */ -static int aty_var_to_pll_1703(const struct fb_info *info, - u32 vclk_per, u8 bpp, union aty_pll *pll) +static int aty_var_to_pll_1703(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) { u32 mhz100; /* in 0.01 MHz */ u32 program_bits; @@ -611,8 +611,8 @@ const struct aty_pll_ops aty_pll_stg1703 * Chrontel 8398 Clock Chip */ -static int aty_var_to_pll_8398(const struct fb_info *info, - u32 vclk_per, u8 bpp, union aty_pll *pll) +static int aty_var_to_pll_8398(const struct fb_info *info, u32 vclk_per, + u32 bpp, union aty_pll *pll) { u32 tempA, tempB, fOut, longMHz100, diff, preDiff; @@ -736,7 +736,7 @@ const struct aty_pll_ops aty_pll_ch8398 */ static int aty_var_to_pll_408(const struct fb_info *info, u32 vclk_per, - u8 bpp, union aty_pll *pll) + u32 bpp, union aty_pll *pll) { u32 mhz100; /* in 0.01 MHz */ u32 program_bits; diff -puN drivers/video/aty/xlinit.c~fbdev-various-mach64-changes drivers/video/aty/xlinit.c --- 25-sparc64/drivers/video/aty/xlinit.c~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/aty/xlinit.c 2004-10-22 00:41:35.000000000 -0700 @@ -148,15 +148,15 @@ static void reset_sdram(struct atyfb_par static void init_dll(struct atyfb_par *par) { // enable DLL - aty_st_pll(PLL_GEN_CNTL, - aty_ld_pll(PLL_GEN_CNTL, par) & 0x7f, + aty_st_pll_ct(PLL_GEN_CNTL, + aty_ld_pll_ct(PLL_GEN_CNTL, par) & 0x7f, par); // reset DLL - aty_st_pll(DLL_CNTL, 0x82, par); - aty_st_pll(DLL_CNTL, 0xE2, par); + aty_st_pll_ct(DLL_CNTL, 0x82, par); + aty_st_pll_ct(DLL_CNTL, 0xE2, par); mdelay(5); - aty_st_pll(DLL_CNTL, 0x82, par); + aty_st_pll_ct(DLL_CNTL, 0x82, par); mdelay(6); } @@ -164,8 +164,8 @@ static void reset_clocks(struct atyfb_pa int hsync_enb) { reset_gui(par); - aty_st_pll(MCLK_FB_DIV, pll->mclk_fb_div, par); - aty_st_pll(SCLK_FB_DIV, pll->sclk_fb_div, par); + aty_st_pll_ct(MCLK_FB_DIV, pll->mclk_fb_div, par); + aty_st_pll_ct(SCLK_FB_DIV, pll->sclk_fb_div, par); mdelay(15); init_dll(par); @@ -177,9 +177,9 @@ static void reset_clocks(struct atyfb_pa aty_st_8(CRTC_GEN_CNTL+3, hsync_enb ? 0x00 : 0x04, par); - aty_st_pll(SPLL_CNTL2, pll->spll_cntl2, par); - aty_st_pll(PLL_GEN_CNTL, pll->pll_gen_cntl, par); - aty_st_pll(PLL_VCLK_CNTL, pll->pll_vclk_cntl, par); + aty_st_pll_ct(SPLL_CNTL2, pll->spll_cntl2, par); + aty_st_pll_ct(PLL_GEN_CNTL, pll->pll_gen_cntl, par); + aty_st_pll_ct(PLL_VCLK_CNTL, pll->pll_vclk_cntl, par); } int atyfb_xl_init(struct fb_info *info) @@ -216,31 +216,31 @@ int atyfb_xl_init(struct fb_info *info) if ((err = aty_pll_ct.var_to_pll(info, 39726, 8, &pll))) return err; - aty_st_pll(LVDS_CNTL0, 0x00, par); - aty_st_pll(DLL2_CNTL, card->dll2_cntl, par); - aty_st_pll(V2PLL_CNTL, 0x10, par); - aty_st_pll(MPLL_CNTL, MPLL_GAIN, par); - aty_st_pll(VPLL_CNTL, VPLL_GAIN, par); - aty_st_pll(PLL_VCLK_CNTL, 0x00, par); - aty_st_pll(VFC_CNTL, 0x1B, par); - aty_st_pll(PLL_REF_DIV, pll.ct.pll_ref_div, par); - aty_st_pll(PLL_EXT_CNTL, pll.ct.pll_ext_cntl, par); - aty_st_pll(SPLL_CNTL2, 0x03, par); - aty_st_pll(PLL_GEN_CNTL, 0x44, par); + aty_st_pll_ct(LVDS_CNTL0, 0x00, par); + aty_st_pll_ct(DLL2_CNTL, card->dll2_cntl, par); + aty_st_pll_ct(V2PLL_CNTL, 0x10, par); + aty_st_pll_ct(MPLL_CNTL, MPLL_GAIN, par); + aty_st_pll_ct(VPLL_CNTL, VPLL_GAIN, par); + aty_st_pll_ct(PLL_VCLK_CNTL, 0x00, par); + aty_st_pll_ct(VFC_CNTL, 0x1B, par); + aty_st_pll_ct(PLL_REF_DIV, pll.ct.pll_ref_div, par); + aty_st_pll_ct(PLL_EXT_CNTL, pll.ct.pll_ext_cntl, par); + aty_st_pll_ct(SPLL_CNTL2, 0x03, par); + aty_st_pll_ct(PLL_GEN_CNTL, 0x44, par); reset_clocks(par, &pll.ct, 0); mdelay(10); - aty_st_pll(VCLK_POST_DIV, 0x03, par); - aty_st_pll(VCLK0_FB_DIV, 0xDA, par); - aty_st_pll(VCLK_POST_DIV, 0x0F, par); - aty_st_pll(VCLK1_FB_DIV, 0xF5, par); - aty_st_pll(VCLK_POST_DIV, 0x3F, par); - aty_st_pll(PLL_EXT_CNTL, 0x40 | pll.ct.pll_ext_cntl, par); - aty_st_pll(VCLK2_FB_DIV, 0x00, par); - aty_st_pll(VCLK_POST_DIV, 0xFF, par); - aty_st_pll(PLL_EXT_CNTL, 0xC0 | pll.ct.pll_ext_cntl, par); - aty_st_pll(VCLK3_FB_DIV, 0x00, par); + aty_st_pll_ct(VCLK_POST_DIV, 0x03, par); + aty_st_pll_ct(VCLK0_FB_DIV, 0xDA, par); + aty_st_pll_ct(VCLK_POST_DIV, 0x0F, par); + aty_st_pll_ct(VCLK1_FB_DIV, 0xF5, par); + aty_st_pll_ct(VCLK_POST_DIV, 0x3F, par); + aty_st_pll_ct(PLL_EXT_CNTL, 0x40 | pll.ct.pll_ext_cntl, par); + aty_st_pll_ct(VCLK2_FB_DIV, 0x00, par); + aty_st_pll_ct(VCLK_POST_DIV, 0xFF, par); + aty_st_pll_ct(PLL_EXT_CNTL, 0xC0 | pll.ct.pll_ext_cntl, par); + aty_st_pll_ct(VCLK3_FB_DIV, 0x00, par); aty_st_8(BUS_CNTL, 0x01, par); aty_st_le32(BUS_CNTL, card->bus_cntl | 0x08000000, par); @@ -295,7 +295,7 @@ int atyfb_xl_init(struct fb_info *info) aty_st_8(CRTC_GEN_CNTL+3, 0x04, par); mdelay(10); - aty_st_pll(PLL_YCLK_CNTL, 0x25, par); + aty_st_pll_ct(PLL_YCLK_CNTL, 0x25, par); aty_st_le16(CUSTOM_MACRO_CNTL, 0x0179, par); aty_st_le16(CUSTOM_MACRO_CNTL+2, 0x005E, par); @@ -309,9 +309,9 @@ int atyfb_xl_init(struct fb_info *info) aty_st_8(CONFIG_STAT0, 0xA0 | card->mem_type, par); - aty_st_pll(PLL_YCLK_CNTL, 0x01, par); + aty_st_pll_ct(PLL_YCLK_CNTL, 0x01, par); mdelay(15); - aty_st_pll(PLL_YCLK_CNTL, card->pll_yclk_cntl, par); + aty_st_pll_ct(PLL_YCLK_CNTL, card->pll_yclk_cntl, par); mdelay(1); reset_clocks(par, &pll.ct, 0); diff -puN drivers/video/Kconfig~fbdev-various-mach64-changes drivers/video/Kconfig --- 25-sparc64/drivers/video/Kconfig~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/drivers/video/Kconfig 2004-10-22 00:41:35.000000000 -0700 @@ -750,6 +750,19 @@ config FB_ATY_CT framebuffer device. The ATI product support page for these boards is at <http://support.ati.com/products/pc/mach64/>. +config FB_ATY_GENERIC_LCD + bool "Mach64 generic LCD support (EXPERIMENTAL)" + depends on FB_ATY_CT + help + Say Y if you have a laptop with an ATI Rage LT PRO, Rage Mobility, + Rage XC, or Rage XL chipset. + +config FB_ATY_XL_INIT + bool "Rage XL No-BIOS Init support" + depends on FB_ATY_CT + help + Say Y here to support booting a Rage XL without BIOS support. + config FB_ATY_GX bool "Mach64 GX support" if PCI depends on FB_ATY @@ -761,12 +774,6 @@ config FB_ATY_GX is at <http://support.ati.com/products/pc/mach64/graphics_xpression.html>. -config FB_ATY_XL_INIT - bool " Rage XL No-BIOS Init support" if FB_ATY_CT - depends on FB_ATY - help - Say Y here to support booting a Rage XL without BIOS support. - config FB_SIS tristate "SiS acceleration" depends on FB && PCI diff -puN include/video/mach64.h~fbdev-various-mach64-changes include/video/mach64.h --- 25-sparc64/include/video/mach64.h~fbdev-various-mach64-changes 2004-10-22 00:41:34.000000000 -0700 +++ 25-sparc64-akpm/include/video/mach64.h 2004-10-22 00:41:35.000000000 -0700 @@ -68,6 +68,8 @@ #define I2C_CNTL_0 0x003C /* Dword offset 0_0F */ +#define DSTN_CONTROL_LG 0x003C /* Dword offset 0_0F (LG) */ + /* Overscan */ #define OVR_CLR 0x0040 /* Dword offset 0_10 */ #define OVR2_CLR 0x0040 /* Dword offset 0_10 */ @@ -101,7 +103,7 @@ #define CUR_HORZ_VERT_OFF 0x0070 /* Dword offset 0_1C */ #define CUR2_HORZ_VERT_OFF 0x0070 /* Dword offset 0_1C */ -#define CONFIG_PANEL_LG 0x0074 /* Dword offset 0_1D */ +#define CONFIG_PANEL_LG 0x0074 /* Dword offset 0_1D (LG) */ /* General I/O Control */ #define GP_IO 0x0078 /* Dword offset 0_1E */ @@ -116,7 +118,31 @@ #define SCRATCH_REG3 0x008C /* Dword offset 0_23 */ /* Clock Control */ -#define CLOCK_CNTL 0x0090 /* Dword offset 0_24 */ +#define CLOCK_CNTL 0x0090 /* Dword offset 0_24 */ +/* CLOCK_CNTL register constants CT LAYOUT */ +#define CLOCK_SEL 0x0f +#define CLOCK_SEL_INTERNAL 0x03 +#define CLOCK_SEL_EXTERNAL 0x0c +#define CLOCK_DIV 0x30 +#define CLOCK_DIV1 0x00 +#define CLOCK_DIV2 0x10 +#define CLOCK_DIV4 0x20 +#define CLOCK_STROBE 0x40 +/* ? 0x80 */ +/* CLOCK_CNTL register constants GX LAYOUT */ +#define CLOCK_BIT 0x04 /* For ICS2595 */ +#define CLOCK_PULSE 0x08 /* For ICS2595 */ +/*#define CLOCK_STROBE 0x40 dito as CT */ +#define CLOCK_DATA 0x80 + +/* For internal PLL(CT) start */ +#define CLOCK_CNTL_ADDR CLOCK_CNTL + 1 +#define PLL_WR_EN 0x02 +#define PLL_ADDR 0xfc +#define CLOCK_CNTL_DATA CLOCK_CNTL + 2 +#define PLL_DATA 0xff +/* For internal PLL(CT) end */ + #define CLOCK_SEL_CNTL 0x0090 /* Dword offset 0_24 */ /* Configuration */ @@ -129,6 +155,8 @@ #define LCD_INDEX 0x00A4 /* Dword offset 0_29 */ #define LCD_DATA 0x00A8 /* Dword offset 0_2A */ +#define HFB_PITCH_ADDR_LG 0x00A8 /* Dword offset 0_2A (LG) */ + /* Memory Control */ #define EXT_MEM_CNTL 0x00AC /* Dword offset 0_2B */ #define MEM_CNTL 0x00B0 /* Dword offset 0_2C */ @@ -137,6 +165,8 @@ #define I2C_CNTL_1 0x00BC /* Dword offset 0_2F */ +#define LT_GIO_LG 0x00BC /* Dword offset 0_2F (LG) */ + /* DAC Control */ #define DAC_REGS 0x00C0 /* Dword offset 0_30 */ #define DAC_W_INDEX 0x00C0 /* Dword offset 0_30 */ @@ -147,14 +177,16 @@ #define EXT_DAC_REGS 0x00C8 /* Dword offset 0_32 */ +#define HORZ_STRETCHING_LG 0x00C8 /* Dword offset 0_32 (LG) */ +#define VERT_STRETCHING_LG 0x00CC /* Dword offset 0_33 (LG) */ + /* Test and Debug */ #define GEN_TEST_CNTL 0x00D0 /* Dword offset 0_34 */ /* Custom Macros */ #define CUSTOM_MACRO_CNTL 0x00D4 /* Dword offset 0_35 */ -#define LCD_GEN_CNTL_LG 0x00D4 /* Dword offset 0_35 */ - +#define LCD_GEN_CNTL_LG 0x00D4 /* Dword offset 0_35 (LG) */ #define POWER_MANAGEMENT_LG 0x00D8 /* Dword offset 0_36 (LG) */ /* Configuration */ @@ -558,7 +590,7 @@ #define CRTC_CSYNC_EN 0x00000010 #define CRTC_PIX_BY_2_EN 0x00000020 /* unused on RAGE */ #define CRTC_DISPLAY_DIS 0x00000040 -#define CRTC_VGA_XOVERSCAN 0x00000040 +#define CRTC_VGA_XOVERSCAN 0x00000080 #define CRTC_PIX_WIDTH_MASK 0x00000700 #define CRTC_PIX_WIDTH_4BPP 0x00000100 @@ -572,25 +604,95 @@ #define CRTC_PIX_ORDER_MSN_LSN 0x00000000 #define CRTC_PIX_ORDER_LSN_MSN 0x00000800 +#define CRTC_VSYNC_INT_EN 0x00001000ul /* XC/XL */ +#define CRTC_VSYNC_INT 0x00002000ul /* XC/XL */ +#define CRTC_FIFO_OVERFILL 0x0000c000ul /* VT/GT */ +#define CRTC2_VSYNC_INT_EN 0x00004000ul /* XC/XL */ +#define CRTC2_VSYNC_INT 0x00008000ul /* XC/XL */ + #define CRTC_FIFO_LWM 0x000f0000 +#define CRTC_HVSYNC_IO_DRIVE 0x00010000 /* XC/XL */ +#define CRTC2_PIX_WIDTH 0x000e0000 /* LTPro */ -#define VGA_128KAP_PAGING 0x00100000 -#define VFC_SYNC_TRISTATE 0x00200000 +#define CRTC_VGA_128KAP_PAGING 0x00100000 +#define CRTC_VFC_SYNC_TRISTATE 0x00200000 /* VTB/GTB/LT */ +#define CRTC2_EN 0x00200000 /* LTPro */ #define CRTC_LOCK_REGS 0x00400000 #define CRTC_SYNC_TRISTATE 0x00800000 #define CRTC_EXT_DISP_EN 0x01000000 -#define CRTC_ENABLE 0x02000000 -#define CRTC_DISP_REQ_ENB 0x04000000 -#define VGA_ATI_LINEAR 0x08000000 +#define CRTC_EN 0x02000000 +#define CRTC_DISP_REQ_EN 0x04000000 +#define CRTC_VGA_LINEAR 0x08000000 #define CRTC_VSYNC_FALL_EDGE 0x10000000 -#define VGA_TEXT_132 0x20000000 -#define VGA_XCRT_CNT_EN 0x40000000 -#define VGA_CUR_B_TEST 0x80000000 +#define CRTC_VGA_TEXT_132 0x20000000 +#define CRTC_CNT_EN 0x40000000 +#define CRTC_CUR_B_TEST 0x80000000 #define CRTC_CRNT_VLINE 0x07f00000 -#define CRTC_VBLANK 0x00000001 +#define CRTC_PRESERVED_MASK 0x0001f000 + +#define CRTC_VBLANK 0x00000001 +#define CRTC_VBLANK_INT_EN 0x00000002 +#define CRTC_VBLANK_INT 0x00000004 +#define CRTC_VBLANK_INT_AK CRTC_VBLANK_INT +#define CRTC_VLINE_INT_EN 0x00000008 +#define CRTC_VLINE_INT 0x00000010 +#define CRTC_VLINE_INT_AK CRTC_VLINE_INT +#define CRTC_VLINE_SYNC 0x00000020 +#define CRTC_FRAME 0x00000040 +#define SNAPSHOT_INT_EN 0x00000080 +#define SNAPSHOT_INT 0x00000100 +#define SNAPSHOT_INT_AK SNAPSHOT_INT +#define I2C_INT_EN 0x00000200 +#define I2C_INT 0x00000400 +#define I2C_INT_AK I2C_INT +#define CRTC2_VBLANK 0x00000800 +#define CRTC2_VBLANK_INT_EN 0x00001000 +#define CRTC2_VBLANK_INT 0x00002000 +#define CRTC2_VBLANK_INT_AK CRTC2_VBLANK_INT +#define CRTC2_VLINE_INT_EN 0x00004000 +#define CRTC2_VLINE_INT 0x00008000 +#define CRTC2_VLINE_INT_AK CRTC2_VLINE_INT +#define CAPBUF0_INT_EN 0x00010000 +#define CAPBUF0_INT 0x00020000 +#define CAPBUF0_INT_AK CAPBUF0_INT +#define CAPBUF1_INT_EN 0x00040000 +#define CAPBUF1_INT 0x00080000 +#define CAPBUF1_INT_AK CAPBUF1_INT +#define OVERLAY_EOF_INT_EN 0x00100000 +#define OVERLAY_EOF_INT 0x00200000 +#define OVERLAY_EOF_INT_AK OVERLAY_EOF_INT +#define ONESHOT_CAP_INT_EN 0x00400000 +#define ONESHOT_CAP_INT 0x00800000 +#define ONESHOT_CAP_INT_AK ONESHOT_CAP_INT +#define BUSMASTER_EOL_INT_EN 0x01000000 +#define BUSMASTER_EOL_INT 0x02000000 +#define BUSMASTER_EOL_INT_AK BUSMASTER_EOL_INT +#define GP_INT_EN 0x04000000 +#define GP_INT 0x08000000 +#define GP_INT_AK GP_INT +#define CRTC2_VLINE_SYNC 0x10000000 +#define SNAPSHOT2_INT_EN 0x20000000 +#define SNAPSHOT2_INT 0x40000000 +#define SNAPSHOT2_INT_AK SNAPSHOT2_INT +#define VBLANK_BIT2_INT 0x80000000 +#define VBLANK_BIT2_INT_AK VBLANK_BIT2_INT + +#define CRTC_INT_EN_MASK (CRTC_VBLANK_INT_EN | \ + CRTC_VLINE_INT_EN | \ + SNAPSHOT_INT_EN | \ + I2C_INT_EN | \ + CRTC2_VBLANK_INT_EN | \ + CRTC2_VLINE_INT_EN | \ + CAPBUF0_INT_EN | \ + CAPBUF1_INT_EN | \ + OVERLAY_EOF_INT_EN | \ + ONESHOT_CAP_INT_EN | \ + BUSMASTER_EOL_INT_EN | \ + GP_INT_EN | \ + SNAPSHOT2_INT_EN) /* DAC control values */ @@ -606,6 +708,24 @@ #define DAC_BLANK_ADJ_1 0x00000800 #define DAC_BLANK_ADJ_2 0x00001000 +/* DAC control values (my source XL/XC Register reference) */ +#define DAC_OUTPUT_MASK 0x00000001 /* 0 - PAL, 1 - NTSC */ +#define DAC_MISTERY_BIT 0x00000002 /* PS2 ? RS343 ?, EXTRA_BRIGHT for GT */ +#define DAC_BLANKING 0x00000004 +#define DAC_CMP_DISABLE 0x00000008 +#define DAC1_CLK_SEL 0x00000010 +#define PALETTE_ACCESS_CNTL 0x00000020 +#define PALETTE2_SNOOP_EN 0x00000040 +#define DAC_CMP_OUTPUT 0x00000080 /* read only */ +/* #define DAC_8BIT_EN is ok */ +#define CRT_SENSE 0x00000800 /* read only */ +#define CRT_DETECTION_ON 0x00001000 +#define DAC_VGA_ADR_EN 0x00002000 +#define DAC_FEA_CON_EN 0x00004000 +#define DAC_PDWN 0x00008000 +#define DAC_TYPE_MASK 0x00070000 /* read only */ + + /* Mix control values */ @@ -635,6 +755,7 @@ /* Mach64 engine bit constants - these are typically ORed together */ /* BUS_CNTL register constants */ +#define BUS_APER_REG_DIS 0x00000010 #define BUS_FIFO_ERR_ACK 0x00200000 #define BUS_HOST_ERR_ACK 0x00800000 @@ -652,29 +773,48 @@ /* DSP_ON_OFF register constants */ #define DSP_OFF 0x000007ff #define DSP_ON 0x07ff0000 +#define VGA_DSP_OFF DSP_OFF +#define VGA_DSP_ON DSP_ON +#define VGA_DSP_XCLKS_PER_QW DSP_XCLKS_PER_QW -/* CLOCK_CNTL register constants */ -#define CLOCK_SEL 0x0f -#define CLOCK_DIV 0x30 -#define CLOCK_DIV1 0x00 -#define CLOCK_DIV2 0x10 -#define CLOCK_DIV4 0x20 -#define CLOCK_STROBE 0x40 -#define PLL_WR_EN 0x02 - -/* PLL register indices */ +/* PLL register indices and fields */ #define MPLL_CNTL 0x00 +#define PLL_PC_GAIN 0x07 +#define PLL_VC_GAIN 0x18 +#define PLL_DUTY_CYC 0xE0 #define VPLL_CNTL 0x01 #define PLL_REF_DIV 0x02 #define PLL_GEN_CNTL 0x03 +#define PLL_OVERRIDE 0x01 /* PLL_SLEEP */ +#define PLL_MCLK_RST 0x02 /* PLL_MRESET */ +#define OSC_EN 0x04 +#define EXT_CLK_EN 0x08 +#define FORCE_DCLK_TRI_STATE 0x08 /* VT4 -> */ +#define MCLK_SRC_SEL 0x70 +#define EXT_CLK_CNTL 0x80 +#define DLL_PWDN 0x80 /* VT4 -> */ #define MCLK_FB_DIV 0x04 #define PLL_VCLK_CNTL 0x05 +#define PLL_VCLK_SRC_SEL 0x03 +#define PLL_VCLK_RST 0x04 +#define PLL_VCLK_INVERT 0x08 #define VCLK_POST_DIV 0x06 +#define VCLK0_POST 0x03 +#define VCLK1_POST 0x0C +#define VCLK2_POST 0x30 +#define VCLK3_POST 0xC0 #define VCLK0_FB_DIV 0x07 #define VCLK1_FB_DIV 0x08 #define VCLK2_FB_DIV 0x09 #define VCLK3_FB_DIV 0x0A #define PLL_EXT_CNTL 0x0B +#define PLL_XCLK_MCLK_RATIO 0x03 +#define PLL_XCLK_SRC_SEL 0x07 +#define PLL_MFB_TIMES_4_2B 0x08 +#define PLL_VCLK0_XDIV 0x10 +#define PLL_VCLK1_XDIV 0x20 +#define PLL_VCLK2_XDIV 0x40 +#define PLL_VCLK3_XDIV 0x80 #define DLL_CNTL 0x0C #define DLL1_CNTL 0x0C #define VFC_CNTL 0x0D @@ -690,6 +830,9 @@ #define SPLL_CNTL2 0x17 #define APLL_STRAPS 0x18 #define EXT_VPLL_CNTL 0x19 +#define EXT_VPLL_EN 0x04 +#define EXT_VPLL_VGA_EN 0x08 +#define EXT_VPLL_INSYNC 0x10 #define EXT_VPLL_REF_DIV 0x1A #define EXT_VPLL_FB_DIV 0x1B #define EXT_VPLL_MSB 0x1C @@ -708,24 +851,6 @@ #define PLL_YCLK_CNTL 0x29 #define PM_DYN_CLK_CNTL 0x2A -/* Fields in PLL registers */ -#define PLL_PC_GAIN 0x07 -#define PLL_VC_GAIN 0x18 -#define PLL_DUTY_CYC 0xE0 -#define PLL_OVERRIDE 0x01 -#define PLL_MCLK_RST 0x02 -#define OSC_EN 0x04 -#define EXT_CLK_EN 0x08 -#define MCLK_SRC_SEL 0x70 -#define EXT_CLK_CNTL 0x80 -#define VCLK_SRC_SEL 0x03 -#define PLL_VCLK_RST 0x04 -#define VCLK_INVERT 0x08 -#define VCLK0_POST 0x03 -#define VCLK1_POST 0x0C -#define VCLK2_POST 0x30 -#define VCLK3_POST 0xC0 - /* CONFIG_CNTL register constants */ #define APERTURE_4M_ENABLE 1 #define APERTURE_8M_ENABLE 2 @@ -811,6 +936,7 @@ #define MEM_BNDRY_1M 0x00030000 #define MEM_BNDRY_EN 0x00040000 +#define ONE_MB 0x100000 /* ATI PCI constants */ #define PCI_ATI_VENDOR_ID 0x1002 @@ -849,7 +975,19 @@ #define LI_CHIP_ID 0x4c49 /* RAGE LT PRO */ #define LP_CHIP_ID 0x4c50 /* RAGE LT PRO */ #define LT_CHIP_ID 0x4c54 /* RAGE LT */ -#define XL_CHIP_ID 0x4752 /* RAGE (XL) */ + +/* mach64CT family / (Rage XL) class */ +#define GR_CHIP_ID 0x4752 /* RAGE XL, BGA, PCI33 */ +#define GS_CHIP_ID 0x4753 /* RAGE XL, PQFP, PCI33 */ +#define GM_CHIP_ID 0x474d /* RAGE XL, BGA, AGP 1x,2x */ +#define GN_CHIP_ID 0x474e /* RAGE XL, PQFP,AGP 1x,2x */ +#define GO_CHIP_ID 0x474f /* RAGE XL, BGA, PCI66 */ +#define GL_CHIP_ID 0x474c /* RAGE XL, PQFP, PCI66 */ + +#define IS_XL(id) ((id)==GR_CHIP_ID || (id)==GS_CHIP_ID || \ + (id)==GM_CHIP_ID || (id)==GN_CHIP_ID || \ + (id)==GO_CHIP_ID || (id)==GL_CHIP_ID) + #define GT_CHIP_ID 0x4754 /* RAGE (GT) */ #define GU_CHIP_ID 0x4755 /* RAGE II/II+ (GTB) */ #define GV_CHIP_ID 0x4756 /* RAGE IIC, PCI */ @@ -860,10 +998,14 @@ #define GI_CHIP_ID 0x4749 /* RAGE PRO, BGA, PCI33 only */ #define GP_CHIP_ID 0x4750 /* RAGE PRO, PQFP, PCI33, full 3D */ #define GQ_CHIP_ID 0x4751 /* RAGE PRO, PQFP, PCI33, limited 3D */ -#define LM_CHIP_ID 0x4c4d /* RAGE Mobility PCI */ -#define LN_CHIP_ID 0x4c4e /* RAGE Mobility AGP */ +#define LM_CHIP_ID 0x4c4d /* RAGE Mobility AGP, full function */ +#define LN_CHIP_ID 0x4c4e /* RAGE Mobility AGP */ +#define LR_CHIP_ID 0x4c52 /* RAGE Mobility PCI, full function */ +#define LS_CHIP_ID 0x4c53 /* RAGE Mobility PCI */ +#define IS_MOBILITY(id) ((id)==LM_CHIP_ID || (id)==LN_CHIP_ID || \ + (id)==LR_CHIP_ID || (id)==LS_CHIP_ID) /* Mach64 major ASIC revisions */ #define MACH64_ASIC_NEC_VT_A3 0x08 #define MACH64_ASIC_NEC_VT_A4 0x48 @@ -889,7 +1031,7 @@ #define MACH64_UNKNOWN 0 #define MACH64_GX 1 #define MACH64_CX 2 -#define MACH64_CT 3 +#define MACH64_CT 3Restore #define MACH64_ET 4 #define MACH64_VT 5 #define MACH64_GT 6 @@ -934,26 +1076,34 @@ #define DP_CHAIN_32BPP 0x8080 /* DP_PIX_WIDTH register constants */ -#define DST_1BPP 0 -#define DST_4BPP 1 -#define DST_8BPP 2 -#define DST_15BPP 3 -#define DST_16BPP 4 -#define DST_32BPP 6 -#define SRC_1BPP 0 +#define DST_1BPP 0x0 +#define DST_4BPP 0x1 +#define DST_8BPP 0x2 +#define DST_15BPP 0x3 +#define DST_16BPP 0x4 +#define DST_24BPP 0x5 +#define DST_32BPP 0x6 +#define DST_MASK 0xF +#define SRC_1BPP 0x000 #define SRC_4BPP 0x100 #define SRC_8BPP 0x200 #define SRC_15BPP 0x300 #define SRC_16BPP 0x400 +#define SRC_24BPP 0x500 #define SRC_32BPP 0x600 -#define HOST_1BPP 0 +#define SRC_MASK 0xF00 +#define DP_HOST_TRIPLE_EN 0x2000 +#define HOST_1BPP 0x00000 #define HOST_4BPP 0x10000 #define HOST_8BPP 0x20000 #define HOST_15BPP 0x30000 #define HOST_16BPP 0x40000 +#define HOST_24BPP 0x50000 #define HOST_32BPP 0x60000 +#define HOST_MASK 0xF0000 #define BYTE_ORDER_MSB_TO_LSB 0 #define BYTE_ORDER_LSB_TO_MSB 0x1000000 +#define BYTE_ORDER_MASK 0x1000000 /* DP_MIX register constants */ #define BKGD_MIX_NOT_D 0 @@ -1027,12 +1177,12 @@ #define CONTEXT_CMD_DISABLE 0x80000000 /* GUI_STAT register constants */ -#define ENGINE_IDLE 0 -#define ENGINE_BUSY 1 -#define SCISSOR_LEFT_FLAG 0x10 -#define SCISSOR_RIGHT_FLAG 0x20 -#define SCISSOR_TOP_FLAG 0x40 -#define SCISSOR_BOTTOM_FLAG 0x80 +#define ENGINE_IDLE 0 +#define ENGINE_BUSY 1 +#define SCISSOR_LEFT_FLAG 0x10 +#define SCISSOR_RIGHT_FLAG 0x20 +#define SCISSOR_TOP_FLAG 0x40 +#define SCISSOR_BOTTOM_FLAG 0x80 /* ATI VGA Extended Regsiters */ #define sioATIEXT 0x1ce @@ -1043,6 +1193,7 @@ #define ATI36 0xb6 /* VGA Graphics Controller Registers */ +#define R_GENMO 0x3cc #define VGAGRA 0x3ce #define GRA06 0x06 @@ -1103,7 +1254,7 @@ /* LCD register indices */ #define CONFIG_PANEL 0x00 -#define LCD_GEN_CTRL 0x01 +#define LCD_GEN_CNTL 0x01 #define DSTN_CONTROL 0x02 #define HFB_PITCH_ADDR 0x03 #define HORZ_STRETCHING 0x04 @@ -1148,11 +1299,79 @@ #define APC_LUT_MN 0x39 #define APC_LUT_OP 0x3A +/* Values in LCD_GEN_CTRL */ +#define CRT_ON 0x00000001ul +#define LCD_ON 0x00000002ul +#define HORZ_DIVBY2_EN 0x00000004ul +#define DONT_DS_ICON 0x00000008ul +#define LOCK_8DOT 0x00000010ul +#define ICON_ENABLE 0x00000020ul +#define DONT_SHADOW_VPAR 0x00000040ul +#define V2CLK_PM_EN 0x00000080ul +#define RST_FM 0x00000100ul +#define DISABLE_PCLK_RESET 0x00000200ul /* XC/XL */ +#define DIS_HOR_CRT_DIVBY2 0x00000400ul +#define SCLK_SEL 0x00000800ul +#define SCLK_DELAY 0x0000f000ul +#define TVCLK_PM_EN 0x00010000ul +#define VCLK_DAC_PM_EN 0x00020000ul +#define VCLK_LCD_OFF 0x00040000ul +#define SELECT_WAIT_4MS 0x00080000ul +#define XTALIN_PM_EN 0x00080000ul /* XC/XL */ +#define V2CLK_DAC_PM_EN 0x00100000ul +#define LVDS_EN 0x00200000ul +#define LVDS_PLL_EN 0x00400000ul +#define LVDS_PLL_RESET 0x00800000ul +#define LVDS_RESERVED_BITS 0x07000000ul +#define CRTC_RW_SELECT 0x08000000ul /* LTPro */ +#define USE_SHADOWED_VEND 0x10000000ul +#define USE_SHADOWED_ROWCUR 0x20000000ul +#define SHADOW_EN 0x40000000ul +#define SHADOW_RW_EN 0x80000000ul + +#define LCD_SET_PRIMARY_MASK 0x07FFFBFBul + +/* Values in HORZ_STRETCHING */ +#define HORZ_STRETCH_BLEND 0x00000ffful +#define HORZ_STRETCH_RATIO 0x0000fffful +#define HORZ_STRETCH_LOOP 0x00070000ul +#define HORZ_STRETCH_LOOP09 0x00000000ul +#define HORZ_STRETCH_LOOP11 0x00010000ul +#define HORZ_STRETCH_LOOP12 0x00020000ul +#define HORZ_STRETCH_LOOP14 0x00030000ul +#define HORZ_STRETCH_LOOP15 0x00040000ul +/* ? 0x00050000ul */ +/* ? 0x00060000ul */ +/* ? 0x00070000ul */ +/* ? 0x00080000ul */ +#define HORZ_PANEL_SIZE 0x0ff00000ul /* XC/XL */ +/* ? 0x10000000ul */ +#define AUTO_HORZ_RATIO 0x20000000ul /* XC/XL */ +#define HORZ_STRETCH_MODE 0x40000000ul +#define HORZ_STRETCH_EN 0x80000000ul + +/* Values in VERT_STRETCHING */ +#define VERT_STRETCH_RATIO0 0x000003fful +#define VERT_STRETCH_RATIO1 0x000ffc00ul +#define VERT_STRETCH_RATIO2 0x3ff00000ul +#define VERT_STRETCH_USE0 0x40000000ul +#define VERT_STRETCH_EN 0x80000000ul + +/* Values in EXT_VERT_STRETCH */ +#define VERT_STRETCH_RATIO3 0x000003fful +#define FORCE_DAC_DATA 0x000000fful +#define FORCE_DAC_DATA_SEL 0x00000300ul +#define VERT_STRETCH_MODE 0x00000400ul +#define VERT_PANEL_SIZE 0x003ff800ul +#define AUTO_VERT_RATIO 0x00400000ul +#define USE_AUTO_FP_POS 0x00800000ul +#define USE_AUTO_LCD_VSYNC 0x01000000ul +/* ? 0xfe000000ul */ /* Values in LCD_MISC_CNTL */ -#define BIAS_MOD_LEVEL_MASK 0x0000ff00 -#define BIAS_MOD_LEVEL_SHIFT 8 -#define BLMOD_EN 0x00010000 -#define BIASMOD_EN 0x00020000 +#define BIAS_MOD_LEVEL_MASK 0x0000ff00 +#define BIAS_MOD_LEVEL_SHIFT 8 +#define BLMOD_EN 0x00010000 +#define BIASMOD_EN 0x00020000 -#endif /* REGMACH64_H */ +#endif /* REGMACH64_H */ _