patch-2.2.17 linux/drivers/video/atyfb.c
Next file: linux/drivers/video/chipsfb.c
Previous file: linux/drivers/video/aty128fb.c
Back to the patch index
Back to the overall index
- Lines: 569
- Date:
Mon Sep 4 18:39:22 2000
- Orig file:
v2.2.16/drivers/video/atyfb.c
- Orig date:
Mon Sep 4 18:37:41 2000
diff -u --recursive --new-file v2.2.16/drivers/video/atyfb.c linux/drivers/video/atyfb.c
@@ -1,4 +1,4 @@
-/* $Id: atyfb.c,v 1.106.2.8 2000/04/28 04:40:09 davem Exp $
+/* $Id: atyfb.c,v 1.106.2.9 2000/06/23 12:06:38 davem Exp $
* linux/drivers/video/atyfb.c -- Frame buffer device for ATI Mach64
*
* Copyright (C) 1997-1998 Geert Uytterhoeven
@@ -20,6 +20,8 @@
* 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 !
*/
/******************************************************************************
@@ -87,7 +89,7 @@
/*
* Debug flags.
*/
-#undef DEBUG
+#define DEBUG
#define GUI_RESERVE 0x00001000
@@ -419,7 +421,7 @@
static int default_mclk __initdata = 0;
#if defined(CONFIG_PPC)
-static int default_vmode __initdata = VMODE_NVRAM;
+static int default_vmode __initdata = VMODE_CHOOSE;
static int default_cmode __initdata = CMODE_NVRAM;
#endif
@@ -452,7 +454,7 @@
/* mach64CT family / mach64GT (3D RAGE) class */
{ 0x4c42, 0x4c42, "3D RAGE LT PRO (AGP)" },
{ 0x4c44, 0x4c44, "3D RAGE LT PRO" },
- { 0x4c47, 0x4c47, "3D RAGE LT PRO" },
+ { 0x4c47, 0x4c47, "3D RAGE LT-G" },
{ 0x4c49, 0x4c49, "3D RAGE LT PRO" },
{ 0x4c50, 0x4c50, "3D RAGE LT PRO" },
{ 0x4c54, 0x4c54, "3D RAGE LT" },
@@ -466,6 +468,8 @@
{ 0x4749, 0x4749, "3D RAGE PRO (BGA, PCI)" },
{ 0x4750, 0x4750, "3D RAGE PRO (PQFP, PCI)" },
{ 0x4751, 0x4751, "3D RAGE PRO (PQFP, PCI, limited 3D)" },
+ { 0x4c4d, 0x4c4d, "3D RAGE Mobility (PCI)" },
+ { 0x4c4e, 0x4c4e, "3D RAGE Mobility (AGP)" },
};
static const char *aty_gx_ram[8] __initdata = {
@@ -477,15 +481,19 @@
};
-static inline u32 aty_ld_le32(volatile unsigned int regindex,
+static inline u32 aty_ld_le32(volatile int regindex,
const struct fb_info_aty *info)
{
unsigned long temp;
u32 val;
+ /* Hack for bloc 1, should be cleanly optimized by compiler */
+ if (regindex >= 0x400)
+ regindex -= 0x800;
+
#if defined(__powerpc__)
temp = info->ati_regbase;
- asm("lwbrx %0,%1,%2" : "=r"(val) : "b" (regindex), "r" (temp));
+ __asm__ __volatile__("lwbrx %0,%1,%2;eieio" : "=r"(val) : "b" (regindex), "r" (temp));
#elif defined(__sparc_v9__)
temp = info->ati_regbase + regindex;
asm("lduwa [%1] %2, %0" : "=r" (val) : "r" (temp), "i" (ASI_PL));
@@ -496,14 +504,18 @@
return val;
}
-static inline void aty_st_le32(volatile unsigned int regindex, u32 val,
+static inline void aty_st_le32(volatile int regindex, u32 val,
const struct fb_info_aty *info)
{
unsigned long temp;
+ /* Hack for bloc 1, should be cleanly optimized by compiler */
+ if (regindex >= 0x400)
+ regindex -= 0x800;
+
#if defined(__powerpc__)
temp = info->ati_regbase;
- asm("stwbrx %0,%1,%2" : : "r" (val), "b" (regindex), "r" (temp) :
+ __asm__ __volatile__("stwbrx %0,%1,%2;eieio" : : "r" (val), "b" (regindex), "r" (temp) :
"memory");
#elif defined(__sparc_v9__)
temp = info->ati_regbase + regindex;
@@ -514,18 +526,56 @@
#endif
}
-static inline u8 aty_ld_8(volatile unsigned int regindex,
+static inline u8 aty_ld_8(volatile int regindex,
const struct fb_info_aty *info)
{
- return *(volatile u8 *)(info->ati_regbase+regindex);
+ u8 val;
+
+ /* Hack for bloc 1, should be cleanly optimized by compiler */
+ if (regindex >= 0x400)
+ regindex -= 0x800;
+
+ val = *(volatile u8 *)(info->ati_regbase+regindex);
+#if defined(__powerpc__)
+ eieio();
+#endif
+ return val;
}
-static inline void aty_st_8(volatile unsigned int regindex, u8 val,
+static inline void aty_st_8(volatile int regindex, u8 val,
const struct fb_info_aty *info)
{
+ /* Hack for bloc 1, should be cleanly optimized by compiler */
+ if (regindex >= 0x400)
+ regindex -= 0x800;
+
*(volatile u8 *)(info->ati_regbase+regindex) = val;
+#if defined(__powerpc__)
+ eieio();
+#endif
+}
+
+static void aty_st_lcd(int index, u32 val, const struct fb_info_aty *info)
+{
+ unsigned long temp;
+
+ /* write addr byte */
+ temp = aty_ld_le32(LCD_INDEX, info);
+ aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, info);
+ /* write the register value */
+ aty_st_le32(LCD_DATA, val, info);
}
+static u32 aty_ld_lcd(int index, const struct fb_info_aty *info)
+{
+ unsigned long temp;
+
+ /* write addr byte */
+ temp = aty_ld_le32(LCD_INDEX, info);
+ aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, info);
+ /* read the register value */
+ return aty_ld_le32(LCD_DATA, info);
+}
/*
* Generic Mach64 routines
@@ -566,6 +616,16 @@
BUS_FIFO_ERR_ACK, info);
}
+static void reset_GTC_3D_engine(const struct fb_info_aty *info)
+{
+ aty_st_le32(SCALE_3D_CNTL, 0xc0, info);
+ mdelay(GTC_3D_RESET_DELAY);
+ aty_st_le32(SETUP_CNTL, 0x00, info);
+ mdelay(GTC_3D_RESET_DELAY);
+ aty_st_le32(SCALE_3D_CNTL, 0x00, info);
+ mdelay(GTC_3D_RESET_DELAY);
+}
+
static void init_engine(const struct atyfb_par *par, struct fb_info_aty *info)
{
u32 pitch_value;
@@ -579,8 +639,16 @@
pitch_value = pitch_value * 3;
}
+ /* On GTC (RagePro), we need to reset the 3D engine first */
+ if (Gx == LB_CHIP_ID || Gx == LD_CHIP_ID || Gx == LI_CHIP_ID ||
+ Gx == LP_CHIP_ID || Gx == GB_CHIP_ID || Gx == GD_CHIP_ID ||
+ Gx == GI_CHIP_ID || Gx == GP_CHIP_ID || Gx == GQ_CHIP_ID ||
+ Gx == LM_CHIP_ID || Gx == LN_CHIP_ID)
+ reset_GTC_3D_engine(info);
+
/* Reset engine, enable, and clear any engine errors */
reset_engine(info);
+
/* Ensure that vga page pointers are set to zero - the upper */
/* page pointers are set to 1 to handle overflows in the */
/* lower page */
@@ -683,9 +751,7 @@
aty_st_8(DAC_W_INDEX, offset & 0xff, info);
/* left addr byte */
aty_st_8(DAC_DATA, (offset >> 8) & 0xff, info);
- eieio();
aty_st_8(DAC_MASK, val, info);
- eieio();
aty_st_8(DAC_CNTL, 0, info);
}
@@ -693,10 +759,8 @@
{
/* write addr byte */
aty_st_8(CLOCK_CNTL + 1, (offset << 2) | PLL_WR_EN, info);
- eieio();
/* write the register value */
aty_st_8(CLOCK_CNTL + 2, val, info);
- eieio();
aty_st_8(CLOCK_CNTL + 1, (offset << 2) & ~PLL_WR_EN, info);
}
@@ -706,10 +770,8 @@
/* write addr byte */
aty_st_8(CLOCK_CNTL + 1, (offset << 2), info);
- eieio();
/* read the register value */
res = aty_ld_8(CLOCK_CNTL + 2, info);
- eieio();
return res;
}
@@ -1772,6 +1834,9 @@
} else if ((Gx == VT_CHIP_ID) || (Gx == VU_CHIP_ID)) {
aty_st_le32(DAC_CNTL, 0x87010184, info);
aty_st_le32(BUS_CNTL, 0x680000f9, info);
+ } else if ((Gx == LN_CHIP_ID) || (Gx == LM_CHIP_ID)) {
+ aty_st_le32(DAC_CNTL, 0x80010102, info);
+ aty_st_le32(BUS_CNTL, 0x7b33a040, info);
} else {
/* GT */
aty_st_le32(DAC_CNTL, 0x86010102, info);
@@ -1788,7 +1853,7 @@
init_engine(par, info);
#ifdef CONFIG_FB_COMPAT_XPMAC
- if (console_fb_info == &info->fb_info) {
+ if (!console_fb_info || console_fb_info == &info->fb_info) {
struct fb_var_screeninfo var;
int vmode, cmode;
display_info.height = ((par->crtc.v_tot_disp>>16) & 0x7ff)+1;
@@ -2406,7 +2471,6 @@
tmp |= 0x2;
aty_st_8(DAC_CNTL, tmp, info);
aty_st_8(DAC_MASK, 0xff, info);
- eieio();
scale = ((Gx != GX_CHIP_ID) && (Gx != CX_CHIP_ID) &&
(info->current_par.crtc.bpp == 16)) ? 3 : 0;
info->aty_cmap_regs->rindex = i << scale;
@@ -2549,11 +2613,19 @@
} else if (Gx == GB_CHIP_ID || Gx == GD_CHIP_ID ||
Gx == GI_CHIP_ID || Gx == GP_CHIP_ID ||
Gx == GQ_CHIP_ID || Gx == LB_CHIP_ID ||
- Gx == LD_CHIP_ID || Gx == LG_CHIP_ID ||
+ Gx == LD_CHIP_ID ||
Gx == LI_CHIP_ID || Gx == LP_CHIP_ID) {
/* RAGE PRO or LT PRO */
pll = 230;
mclk = 100;
+ } else if (Gx == LG_CHIP_ID) {
+ /* Rage LT */
+ pll = 230;
+ mclk = 63;
+ } else if ((Gx == LN_CHIP_ID) || (Gx == LM_CHIP_ID)) {
+ /* Rage mobility M1 */
+ pll = 230;
+ mclk = 50;
} else {
/* other RAGE */
pll = 135;
@@ -2706,24 +2778,43 @@
}
#if defined(CONFIG_PPC)
+ if (Gx == LI_CHIP_ID && machine_is_compatible("PowerBook1,1")) {
+ /* these bits let the 101 powerbook wake up from sleep -- paulus */
+ aty_st_lcd(LCD_POWER_MANAGEMENT, aty_ld_lcd(LCD_POWER_MANAGEMENT, info)
+ | (USE_F32KHZ | TRISTATE_MEM_EN), info);
+ }
+
if (default_vmode == VMODE_NVRAM) {
+#if 0 /* This is not really supported */
default_vmode = nvram_read_byte(NV_VMODE);
if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+#endif
default_vmode = VMODE_CHOOSE;
}
+ /*
+ * The default video mode is 1024x768 @ 75Hz, as that
+ * works on iMacs as well as the G3 powerbooks. - paulus
+ */
if (default_vmode == VMODE_CHOOSE) {
if (Gx == LG_CHIP_ID)
/* G3 PowerBook with 1024x768 LCD */
default_vmode = VMODE_1024_768_60;
- else {
- sense = read_aty_sense(info);
- default_vmode = mac_map_monitor_sense(sense);
- }
+ else if (Gx == LN_CHIP_ID)
+ /* iBook with 800x600 LCD */
+ default_vmode = VMODE_800_600_60;
+ else
+ default_vmode = VMODE_1024_768_75;
+ /* 'twould be nice to get this going - paulus */
+ sense = read_aty_sense(info);
+ printk(KERN_INFO "atyfb: monitor sense=%x, maps to mode %d\n",
+ sense, mac_map_monitor_sense(sense));
}
if (default_vmode <= 0 || default_vmode > VMODE_MAX)
- default_vmode = VMODE_640_480_60;
+ default_vmode = VMODE_1024_768_75;
+#if 0
if (default_cmode == CMODE_NVRAM)
default_cmode = nvram_read_byte(NV_CMODE);
+#endif
if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
default_cmode = CMODE_8;
if (mac_vmode_to_var(default_vmode, default_cmode, &var))
@@ -3476,11 +3567,10 @@
if (Gx == GT_CHIP_ID || Gx == GU_CHIP_ID || Gx == GV_CHIP_ID ||
Gx == GW_CHIP_ID || Gx == GZ_CHIP_ID || Gx == LG_CHIP_ID ||
Gx == GB_CHIP_ID || Gx == GD_CHIP_ID || Gx == GI_CHIP_ID ||
- Gx == GP_CHIP_ID || Gx == GQ_CHIP_ID)
+ Gx == GP_CHIP_ID || Gx == GQ_CHIP_ID || Gx == LI_CHIP_ID)
i |= 0x2; /*DAC_CNTL|0x2 turns off the extra brightness for gt*/
aty_st_8(DAC_CNTL, i, info);
aty_st_8(DAC_MASK, 0xff, info);
- eieio();
scale = ((Gx != GX_CHIP_ID) && (Gx != CX_CHIP_ID) &&
(info->current_par.crtc.bpp == 16)) ? 3 : 0;
info->aty_cmap_regs->windex = regno << scale;
@@ -3930,6 +4020,124 @@
#endif
#ifdef CONFIG_PMAC_PBOOK
+
+/* 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 fb_info_aty *info)
+{
+ unsigned int pm;
+ int timeout;
+
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+ pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG;
+ aty_st_le32(POWER_MANAGEMENT_LG, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+
+ timeout = 200000;
+ if (sleep) {
+ /* Sleep */
+ pm &= ~PWR_MGT_ON;
+ aty_st_le32(POWER_MANAGEMENT_LG, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+ udelay(10);
+ pm &= ~(PWR_BLON | AUTO_PWR_UP);
+ pm |= SUSPEND_NOW;
+ aty_st_le32(POWER_MANAGEMENT_LG, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+ udelay(10);
+ pm |= PWR_MGT_ON;
+ aty_st_le32(POWER_MANAGEMENT_LG, pm, info);
+ do {
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+ 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, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+ udelay(10);
+ pm |= (PWR_BLON | AUTO_PWR_UP);
+ pm &= ~SUSPEND_NOW;
+ aty_st_le32(POWER_MANAGEMENT_LG, pm, info);
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+ udelay(10);
+ pm |= PWR_MGT_ON;
+ aty_st_le32(POWER_MANAGEMENT_LG, pm, info);
+ do {
+ pm = aty_ld_le32(POWER_MANAGEMENT_LG, info);
+ 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 fb_info_aty *info)
+{
+ unsigned int pm;
+ int timeout;
+
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+ pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG;
+ aty_st_lcd(LCD_POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+
+ timeout = 200;
+ if (sleep) {
+ /* Sleep */
+ pm &= ~PWR_MGT_ON;
+ aty_st_lcd(LCD_POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+ udelay(10);
+ pm &= ~(PWR_BLON | AUTO_PWR_UP);
+ pm |= SUSPEND_NOW;
+ aty_st_lcd(LCD_POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+ udelay(10);
+ pm |= PWR_MGT_ON;
+ aty_st_lcd(LCD_POWER_MANAGEMENT, pm, info);
+ do {
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+ udelay(1000);
+ if ((--timeout) == 0)
+ break;
+ } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND);
+ } else {
+ /* Wakeup */
+ pm &= ~PWR_MGT_ON;
+ aty_st_lcd(LCD_POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+ udelay(10);
+ pm &= ~SUSPEND_NOW;
+ pm |= (PWR_BLON | AUTO_PWR_UP);
+ aty_st_lcd(LCD_POWER_MANAGEMENT, pm, info);
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+ udelay(10);
+ pm |= PWR_MGT_ON;
+ aty_st_lcd(LCD_POWER_MANAGEMENT, pm, info);
+ do {
+ pm = aty_ld_lcd(LCD_POWER_MANAGEMENT, info);
+ udelay(1000);
+ if ((--timeout) == 0)
+ break;
+ } while ((pm & PWR_MGT_STATUS_MASK) != 0);
+ }
+
+ return timeout ? PBOOK_SLEEP_OK : PBOOK_SLEEP_REFUSE;
+}
+
/*
* Save the contents of the frame buffer when we go to sleep,
* and restore it when we wake up again.
@@ -3938,7 +4146,9 @@
aty_sleep_notify(struct pmu_sleep_notifier *self, int when)
{
struct fb_info_aty *info;
- unsigned int pm;
+ int result;
+
+ result = PBOOK_SLEEP_OK;
for (info = first_display; info != NULL; info = info->next) {
struct fb_fix_screeninfo fix;
@@ -3950,6 +4160,8 @@
switch (when) {
case PBOOK_SLEEP_REQUEST:
info->save_framebuffer = vmalloc(nb);
+ if (info->save_framebuffer == NULL)
+ return PBOOK_SLEEP_REFUSE;
break;
case PBOOK_SLEEP_REJECT:
if (info->save_framebuffer) {
@@ -3966,80 +4178,38 @@
/* Backup fb content */
if (info->save_framebuffer)
- memcpy(info->save_framebuffer,
+ memcpy_fromio(info->save_framebuffer,
(void *)info->frame_buffer, nb);
/* Blank display and LCD */
atyfbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info);
- /* Set chip to "suspend" mode. Note: There's a HW bug
- in the chip which prevents proper resync on wakeup
- with automatic power management, we handle suspend
- manually using the following (weird) sequence
- described by ATI.
- Note2:
- We could enable this for all Rage LT Pro chip ids */
- if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID)
- || (Gx == LP_CHIP_ID)) {
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- pm &= ~PWR_MGT_ON;
- aty_st_le32(POWER_MANAGEMENT, pm, info);
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- mdelay(1);
- pm &= ~(PWR_BLON | AUTO_PWR_UP);
- pm |= SUSPEND_NOW;
- aty_st_le32(POWER_MANAGEMENT, pm, info);
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- mdelay(1);
- pm |= PWR_MGT_ON;
- aty_st_le32(POWER_MANAGEMENT, pm, info);
- do {
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- /* Fix a problem with revision 4c50 of the chip */
- if (Gx == LP_CHIP_ID)
- break;
- } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND);
- mdelay(500);
- }
+ /* Set chip to "suspend" mode */
+ if (Gx == LG_CHIP_ID)
+ result = aty_power_mgmt_LT(1, info);
+ else
+ result = aty_power_mgmt_LTPro(1, info);
break;
case PBOOK_WAKE:
/* Wakeup chip */
- if ((Gx == LG_CHIP_ID) || (Gx == LT_CHIP_ID) || (Gx == LP_CHIP_ID)) {
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- pm &= ~PWR_MGT_ON;
- aty_st_le32(POWER_MANAGEMENT, pm, info);
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- mdelay(1);
- pm |= (PWR_BLON | AUTO_PWR_UP);
- pm &= ~SUSPEND_NOW;
- aty_st_le32(POWER_MANAGEMENT, pm, info);
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- mdelay(1);
- pm |= PWR_MGT_ON;
- aty_st_le32(POWER_MANAGEMENT, pm, info);
- do {
- pm = aty_ld_le32(POWER_MANAGEMENT, info);
- /* Fix a problem with revision 4c50 of the chip */
- if (Gx == LP_CHIP_ID)
- break;
- } while ((pm & PWR_MGT_STATUS_MASK) != 0);
- mdelay(500);
- }
+ if (Gx == LG_CHIP_ID)
+ result = aty_power_mgmt_LT(0, info);
+ else
+ result = aty_power_mgmt_LTPro(0, info);
/* Restore fb content */
if (info->save_framebuffer) {
- memcpy((void *)info->frame_buffer,
+ memcpy_toio((void *)info->frame_buffer,
info->save_framebuffer, nb);
vfree(info->save_framebuffer);
info->save_framebuffer = 0;
}
-
- /* Restore display */
+ /* Restore display */
atyfb_set_par(&info->current_par, info);
atyfbcon_blank(0, (struct fb_info *)info);
break;
}
}
- return PBOOK_SLEEP_OK;
+ return result;
}
#endif /* CONFIG_PMAC_PBOOK */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)