summaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/aty
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 01:02:30 +0000
commit76cb841cb886eef6b3bee341a2266c76578724ad (patch)
treef5892e5ba6cc11949952a6ce4ecbe6d516d6ce58 /drivers/video/fbdev/aty
parentInitial commit. (diff)
downloadlinux-76cb841cb886eef6b3bee341a2266c76578724ad.tar.xz
linux-76cb841cb886eef6b3bee341a2266c76578724ad.zip
Adding upstream version 4.19.249.upstream/4.19.249
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/video/fbdev/aty')
-rw-r--r--drivers/video/fbdev/aty/Makefile16
-rw-r--r--drivers/video/fbdev/aty/ati_ids.h215
-rw-r--r--drivers/video/fbdev/aty/aty128fb.c2607
-rw-r--r--drivers/video/fbdev/aty/atyfb.h368
-rw-r--r--drivers/video/fbdev/aty/atyfb_base.c4012
-rw-r--r--drivers/video/fbdev/aty/mach64_accel.c427
-rw-r--r--drivers/video/fbdev/aty/mach64_ct.c650
-rw-r--r--drivers/video/fbdev/aty/mach64_cursor.c226
-rw-r--r--drivers/video/fbdev/aty/mach64_gx.c911
-rw-r--r--drivers/video/fbdev/aty/radeon_accel.c329
-rw-r--r--drivers/video/fbdev/aty/radeon_backlight.c215
-rw-r--r--drivers/video/fbdev/aty/radeon_base.c2694
-rw-r--r--drivers/video/fbdev/aty/radeon_i2c.c168
-rw-r--r--drivers/video/fbdev/aty/radeon_monitor.c1053
-rw-r--r--drivers/video/fbdev/aty/radeon_pm.c2910
-rw-r--r--drivers/video/fbdev/aty/radeonfb.h521
16 files changed, 17322 insertions, 0 deletions
diff --git a/drivers/video/fbdev/aty/Makefile b/drivers/video/fbdev/aty/Makefile
new file mode 100644
index 000000000..069465c82
--- /dev/null
+++ b/drivers/video/fbdev/aty/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_FB_ATY) += atyfb.o
+obj-$(CONFIG_FB_ATY128) += aty128fb.o
+obj-$(CONFIG_FB_RADEON) += radeonfb.o
+
+atyfb-y := atyfb_base.o mach64_accel.o mach64_cursor.o
+atyfb-$(CONFIG_FB_ATY_GX) += mach64_gx.o
+atyfb-$(CONFIG_FB_ATY_CT) += mach64_ct.o
+
+atyfb-objs := $(atyfb-y)
+
+radeonfb-y := radeon_base.o radeon_pm.o radeon_monitor.o radeon_accel.o
+radeonfb-$(CONFIG_FB_RADEON_I2C) += radeon_i2c.o
+radeonfb-$(CONFIG_FB_RADEON_BACKLIGHT) += radeon_backlight.o
+radeonfb-objs := $(radeonfb-y)
+
diff --git a/drivers/video/fbdev/aty/ati_ids.h b/drivers/video/fbdev/aty/ati_ids.h
new file mode 100644
index 000000000..2b589d687
--- /dev/null
+++ b/drivers/video/fbdev/aty/ati_ids.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ATI PCI IDs from XFree86, kept here to make sync'ing with
+ * XFree much simpler. Currently, this list is only used by
+ * radeonfb
+ */
+
+#define PCI_CHIP_RV380_3150 0x3150
+#define PCI_CHIP_RV380_3151 0x3151
+#define PCI_CHIP_RV380_3152 0x3152
+#define PCI_CHIP_RV380_3153 0x3153
+#define PCI_CHIP_RV380_3154 0x3154
+#define PCI_CHIP_RV380_3156 0x3156
+#define PCI_CHIP_RV380_3E50 0x3E50
+#define PCI_CHIP_RV380_3E51 0x3E51
+#define PCI_CHIP_RV380_3E52 0x3E52
+#define PCI_CHIP_RV380_3E53 0x3E53
+#define PCI_CHIP_RV380_3E54 0x3E54
+#define PCI_CHIP_RV380_3E56 0x3E56
+#define PCI_CHIP_RS100_4136 0x4136
+#define PCI_CHIP_RS200_4137 0x4137
+#define PCI_CHIP_R300_AD 0x4144
+#define PCI_CHIP_R300_AE 0x4145
+#define PCI_CHIP_R300_AF 0x4146
+#define PCI_CHIP_R300_AG 0x4147
+#define PCI_CHIP_R350_AH 0x4148
+#define PCI_CHIP_R350_AI 0x4149
+#define PCI_CHIP_R350_AJ 0x414A
+#define PCI_CHIP_R350_AK 0x414B
+#define PCI_CHIP_RV350_AP 0x4150
+#define PCI_CHIP_RV350_AQ 0x4151
+#define PCI_CHIP_RV360_AR 0x4152
+#define PCI_CHIP_RV350_AS 0x4153
+#define PCI_CHIP_RV350_AT 0x4154
+#define PCI_CHIP_RV350_AV 0x4156
+#define PCI_CHIP_MACH32 0x4158
+#define PCI_CHIP_RS250_4237 0x4237
+#define PCI_CHIP_R200_BB 0x4242
+#define PCI_CHIP_R200_BC 0x4243
+#define PCI_CHIP_RS100_4336 0x4336
+#define PCI_CHIP_RS200_4337 0x4337
+#define PCI_CHIP_MACH64CT 0x4354
+#define PCI_CHIP_MACH64CX 0x4358
+#define PCI_CHIP_RS250_4437 0x4437
+#define PCI_CHIP_MACH64ET 0x4554
+#define PCI_CHIP_MACH64GB 0x4742
+#define PCI_CHIP_MACH64GD 0x4744
+#define PCI_CHIP_MACH64GI 0x4749
+#define PCI_CHIP_MACH64GL 0x474C
+#define PCI_CHIP_MACH64GM 0x474D
+#define PCI_CHIP_MACH64GN 0x474E
+#define PCI_CHIP_MACH64GO 0x474F
+#define PCI_CHIP_MACH64GP 0x4750
+#define PCI_CHIP_MACH64GQ 0x4751
+#define PCI_CHIP_MACH64GR 0x4752
+#define PCI_CHIP_MACH64GS 0x4753
+#define PCI_CHIP_MACH64GT 0x4754
+#define PCI_CHIP_MACH64GU 0x4755
+#define PCI_CHIP_MACH64GV 0x4756
+#define PCI_CHIP_MACH64GW 0x4757
+#define PCI_CHIP_MACH64GX 0x4758
+#define PCI_CHIP_MACH64GY 0x4759
+#define PCI_CHIP_MACH64GZ 0x475A
+#define PCI_CHIP_RV250_Id 0x4964
+#define PCI_CHIP_RV250_Ie 0x4965
+#define PCI_CHIP_RV250_If 0x4966
+#define PCI_CHIP_RV250_Ig 0x4967
+#define PCI_CHIP_R420_JH 0x4A48
+#define PCI_CHIP_R420_JI 0x4A49
+#define PCI_CHIP_R420_JJ 0x4A4A
+#define PCI_CHIP_R420_JK 0x4A4B
+#define PCI_CHIP_R420_JL 0x4A4C
+#define PCI_CHIP_R420_JM 0x4A4D
+#define PCI_CHIP_R420_JN 0x4A4E
+#define PCI_CHIP_R420_JP 0x4A50
+#define PCI_CHIP_MACH64LB 0x4C42
+#define PCI_CHIP_MACH64LD 0x4C44
+#define PCI_CHIP_RAGE128LE 0x4C45
+#define PCI_CHIP_RAGE128LF 0x4C46
+#define PCI_CHIP_MACH64LG 0x4C47
+#define PCI_CHIP_MACH64LI 0x4C49
+#define PCI_CHIP_MACH64LM 0x4C4D
+#define PCI_CHIP_MACH64LN 0x4C4E
+#define PCI_CHIP_MACH64LP 0x4C50
+#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
+#define PCI_CHIP_RADEON_LZ 0x4C5A
+#define PCI_CHIP_RV250_Ld 0x4C64
+#define PCI_CHIP_RV250_Le 0x4C65
+#define PCI_CHIP_RV250_Lf 0x4C66
+#define PCI_CHIP_RV250_Lg 0x4C67
+#define PCI_CHIP_RV250_Ln 0x4C6E
+#define PCI_CHIP_RAGE128MF 0x4D46
+#define PCI_CHIP_RAGE128ML 0x4D4C
+#define PCI_CHIP_R300_ND 0x4E44
+#define PCI_CHIP_R300_NE 0x4E45
+#define PCI_CHIP_R300_NF 0x4E46
+#define PCI_CHIP_R300_NG 0x4E47
+#define PCI_CHIP_R350_NH 0x4E48
+#define PCI_CHIP_R350_NI 0x4E49
+#define PCI_CHIP_R360_NJ 0x4E4A
+#define PCI_CHIP_R350_NK 0x4E4B
+#define PCI_CHIP_RV350_NP 0x4E50
+#define PCI_CHIP_RV350_NQ 0x4E51
+#define PCI_CHIP_RV350_NR 0x4E52
+#define PCI_CHIP_RV350_NS 0x4E53
+#define PCI_CHIP_RV350_NT 0x4E54
+#define PCI_CHIP_RV350_NV 0x4E56
+#define PCI_CHIP_RAGE128PA 0x5041
+#define PCI_CHIP_RAGE128PB 0x5042
+#define PCI_CHIP_RAGE128PC 0x5043
+#define PCI_CHIP_RAGE128PD 0x5044
+#define PCI_CHIP_RAGE128PE 0x5045
+#define PCI_CHIP_RAGE128PF 0x5046
+#define PCI_CHIP_RAGE128PG 0x5047
+#define PCI_CHIP_RAGE128PH 0x5048
+#define PCI_CHIP_RAGE128PI 0x5049
+#define PCI_CHIP_RAGE128PJ 0x504A
+#define PCI_CHIP_RAGE128PK 0x504B
+#define PCI_CHIP_RAGE128PL 0x504C
+#define PCI_CHIP_RAGE128PM 0x504D
+#define PCI_CHIP_RAGE128PN 0x504E
+#define PCI_CHIP_RAGE128PO 0x504F
+#define PCI_CHIP_RAGE128PP 0x5050
+#define PCI_CHIP_RAGE128PQ 0x5051
+#define PCI_CHIP_RAGE128PR 0x5052
+#define PCI_CHIP_RAGE128PS 0x5053
+#define PCI_CHIP_RAGE128PT 0x5054
+#define PCI_CHIP_RAGE128PU 0x5055
+#define PCI_CHIP_RAGE128PV 0x5056
+#define PCI_CHIP_RAGE128PW 0x5057
+#define PCI_CHIP_RAGE128PX 0x5058
+#define PCI_CHIP_RADEON_QD 0x5144
+#define PCI_CHIP_RADEON_QE 0x5145
+#define PCI_CHIP_RADEON_QF 0x5146
+#define PCI_CHIP_RADEON_QG 0x5147
+#define PCI_CHIP_R200_QH 0x5148
+#define PCI_CHIP_R200_QI 0x5149
+#define PCI_CHIP_R200_QJ 0x514A
+#define PCI_CHIP_R200_QK 0x514B
+#define PCI_CHIP_R200_QL 0x514C
+#define PCI_CHIP_R200_QM 0x514D
+#define PCI_CHIP_R200_QN 0x514E
+#define PCI_CHIP_R200_QO 0x514F
+#define PCI_CHIP_RV200_QW 0x5157
+#define PCI_CHIP_RV200_QX 0x5158
+#define PCI_CHIP_RV100_QY 0x5159
+#define PCI_CHIP_RV100_QZ 0x515A
+#define PCI_CHIP_RN50 0x515E
+#define PCI_CHIP_RAGE128RE 0x5245
+#define PCI_CHIP_RAGE128RF 0x5246
+#define PCI_CHIP_RAGE128RG 0x5247
+#define PCI_CHIP_RAGE128RK 0x524B
+#define PCI_CHIP_RAGE128RL 0x524C
+#define PCI_CHIP_RAGE128SE 0x5345
+#define PCI_CHIP_RAGE128SF 0x5346
+#define PCI_CHIP_RAGE128SG 0x5347
+#define PCI_CHIP_RAGE128SH 0x5348
+#define PCI_CHIP_RAGE128SK 0x534B
+#define PCI_CHIP_RAGE128SL 0x534C
+#define PCI_CHIP_RAGE128SM 0x534D
+#define PCI_CHIP_RAGE128SN 0x534E
+#define PCI_CHIP_RAGE128TF 0x5446
+#define PCI_CHIP_RAGE128TL 0x544C
+#define PCI_CHIP_RAGE128TR 0x5452
+#define PCI_CHIP_RAGE128TS 0x5453
+#define PCI_CHIP_RAGE128TT 0x5454
+#define PCI_CHIP_RAGE128TU 0x5455
+#define PCI_CHIP_RV370_5460 0x5460
+#define PCI_CHIP_RV370_5461 0x5461
+#define PCI_CHIP_RV370_5462 0x5462
+#define PCI_CHIP_RV370_5463 0x5463
+#define PCI_CHIP_RV370_5464 0x5464
+#define PCI_CHIP_RV370_5465 0x5465
+#define PCI_CHIP_RV370_5466 0x5466
+#define PCI_CHIP_RV370_5467 0x5467
+#define PCI_CHIP_R423_UH 0x5548
+#define PCI_CHIP_R423_UI 0x5549
+#define PCI_CHIP_R423_UJ 0x554A
+#define PCI_CHIP_R423_UK 0x554B
+#define PCI_CHIP_R423_UQ 0x5551
+#define PCI_CHIP_R423_UR 0x5552
+#define PCI_CHIP_R423_UT 0x5554
+#define PCI_CHIP_MACH64VT 0x5654
+#define PCI_CHIP_MACH64VU 0x5655
+#define PCI_CHIP_MACH64VV 0x5656
+#define PCI_CHIP_RC410_5A62 0x5A62
+#define PCI_CHIP_RS300_5834 0x5834
+#define PCI_CHIP_RS300_5835 0x5835
+#define PCI_CHIP_RS300_5836 0x5836
+#define PCI_CHIP_RS300_5837 0x5837
+#define PCI_CHIP_RS480_5955 0x5955
+#define PCI_CHIP_RV280_5960 0x5960
+#define PCI_CHIP_RV280_5961 0x5961
+#define PCI_CHIP_RV280_5962 0x5962
+#define PCI_CHIP_RV280_5964 0x5964
+#define PCI_CHIP_RS482_5975 0x5975
+#define PCI_CHIP_RV370_5B60 0x5B60
+#define PCI_CHIP_RV370_5B61 0x5B61
+#define PCI_CHIP_RV370_5B62 0x5B62
+#define PCI_CHIP_RV370_5B63 0x5B63
+#define PCI_CHIP_RV370_5B64 0x5B64
+#define PCI_CHIP_RV370_5B65 0x5B65
+#define PCI_CHIP_RV370_5B66 0x5B66
+#define PCI_CHIP_RV370_5B67 0x5B67
+#define PCI_CHIP_RV280_5C61 0x5C61
+#define PCI_CHIP_RV280_5C63 0x5C63
+#define PCI_CHIP_R423_5D57 0x5D57
+#define PCI_CHIP_RS350_7834 0x7834
+#define PCI_CHIP_RS350_7835 0x7835
diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c
new file mode 100644
index 000000000..6cc46867f
--- /dev/null
+++ b/drivers/video/fbdev/aty/aty128fb.c
@@ -0,0 +1,2607 @@
+/* $Id: aty128fb.c,v 1.1.1.1.36.1 1999/12/11 09:03:05 Exp $
+ * linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128
+ *
+ * Copyright (C) 1999-2003, Brad Douglas <brad@neruo.com>
+ * Copyright (C) 1999, Anthony Tong <atong@uiuc.edu>
+ *
+ * Ani Joshi / Jeff Garzik
+ * - Code cleanup
+ *
+ * Michel Danzer <michdaen@iiic.ethz.ch>
+ * - 15/16 bit cleanup
+ * - fix panning
+ *
+ * Benjamin Herrenschmidt
+ * - pmac-specific PM stuff
+ * - various fixes & cleanups
+ *
+ * Andreas Hundt <andi@convergence.de>
+ * - FB_ACTIVATE fixes
+ *
+ * Paul Mackerras <paulus@samba.org>
+ * - Convert to new framebuffer API,
+ * fix colormap setting at 16 bits/pixel (565)
+ *
+ * Paul Mundt
+ * - PCI hotplug
+ *
+ * Jon Smirl <jonsmirl@yahoo.com>
+ * - PCI ID update
+ * - replace ROM BIOS search
+ *
+ * Based off of Geert's atyfb.c and vfb.c.
+ *
+ * TODO:
+ * - monitor sensing (DDC)
+ * - virtual display
+ * - other platform support (only ppc/x86 supported)
+ * - hardware cursor support
+ *
+ * Please cc: your patches to brad@neruo.com.
+ */
+
+/*
+ * A special note of gratitude to ATI's devrel for providing documentation,
+ * example code and hardware. Thanks Nitya. -atong and brad
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/console.h>
+#include <linux/backlight.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_PPC_PMAC
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/prom.h>
+#include "../macmodes.h"
+#endif
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif /* CONFIG_BOOTX_TEXT */
+
+#include <video/aty128.h>
+
+/* Debug flag */
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt, args...) \
+ printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
+#else
+#define DBG(fmt, args...)
+#endif
+
+#ifndef CONFIG_PPC_PMAC
+/* default mode */
+static const 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
+};
+
+#else /* CONFIG_PPC_PMAC */
+/* default to 1024x768 at 75Hz on PPC - this will work
+ * on the iMac, the usual 640x480 @ 60Hz doesn't. */
+static const struct fb_var_screeninfo default_var = {
+ /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
+ 1024, 768, 1024, 768, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 12699, 160, 32, 28, 1, 96, 3,
+ FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ FB_VMODE_NONINTERLACED
+};
+#endif /* CONFIG_PPC_PMAC */
+
+/* default modedb mode */
+/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
+static const struct fb_videomode defaultmode = {
+ .refresh = 60,
+ .xres = 640,
+ .yres = 480,
+ .pixclock = 39722,
+ .left_margin = 48,
+ .right_margin = 16,
+ .upper_margin = 33,
+ .lower_margin = 10,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED
+};
+
+/* Chip generations */
+enum {
+ rage_128,
+ rage_128_pci,
+ rage_128_pro,
+ rage_128_pro_pci,
+ rage_M3,
+ rage_M3_pci,
+ rage_M4,
+ rage_128_ultra,
+};
+
+/* Must match above enum */
+static char * const r128_family[] = {
+ "AGP",
+ "PCI",
+ "PRO AGP",
+ "PRO PCI",
+ "M3 AGP",
+ "M3 PCI",
+ "M4 AGP",
+ "Ultra AGP",
+};
+
+/*
+ * PCI driver prototypes
+ */
+static int aty128_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static void aty128_remove(struct pci_dev *pdev);
+static int aty128_pci_suspend(struct pci_dev *pdev, pm_message_t state);
+static int aty128_pci_resume(struct pci_dev *pdev);
+static int aty128_do_resume(struct pci_dev *pdev);
+
+/* supported Rage128 chipsets */
+static const struct pci_device_id aty128_pci_tbl[] = {
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, aty128_pci_tbl);
+
+static struct pci_driver aty128fb_driver = {
+ .name = "aty128fb",
+ .id_table = aty128_pci_tbl,
+ .probe = aty128_probe,
+ .remove = aty128_remove,
+ .suspend = aty128_pci_suspend,
+ .resume = aty128_pci_resume,
+};
+
+/* packed BIOS settings */
+#ifndef CONFIG_PPC
+typedef struct {
+ u8 clock_chip_type;
+ u8 struct_size;
+ u8 accelerator_entry;
+ u8 VGA_entry;
+ u16 VGA_table_offset;
+ u16 POST_table_offset;
+ u16 XCLK;
+ u16 MCLK;
+ u8 num_PLL_blocks;
+ u8 size_PLL_blocks;
+ u16 PCLK_ref_freq;
+ u16 PCLK_ref_divider;
+ u32 PCLK_min_freq;
+ u32 PCLK_max_freq;
+ u16 MCLK_ref_freq;
+ u16 MCLK_ref_divider;
+ u32 MCLK_min_freq;
+ u32 MCLK_max_freq;
+ u16 XCLK_ref_freq;
+ u16 XCLK_ref_divider;
+ u32 XCLK_min_freq;
+ u32 XCLK_max_freq;
+} __attribute__ ((packed)) PLL_BLOCK;
+#endif /* !CONFIG_PPC */
+
+/* onboard memory information */
+struct aty128_meminfo {
+ u8 ML;
+ u8 MB;
+ u8 Trcd;
+ u8 Trp;
+ u8 Twr;
+ u8 CL;
+ u8 Tr2w;
+ u8 LoopLatency;
+ u8 DspOn;
+ u8 Rloop;
+ const char *name;
+};
+
+/* various memory configurations */
+static const struct aty128_meminfo sdr_128 = {
+ .ML = 4,
+ .MB = 4,
+ .Trcd = 3,
+ .Trp = 3,
+ .Twr = 1,
+ .CL = 3,
+ .Tr2w = 1,
+ .LoopLatency = 16,
+ .DspOn = 30,
+ .Rloop = 16,
+ .name = "128-bit SDR SGRAM (1:1)",
+};
+
+static const struct aty128_meminfo sdr_64 = {
+ .ML = 4,
+ .MB = 8,
+ .Trcd = 3,
+ .Trp = 3,
+ .Twr = 1,
+ .CL = 3,
+ .Tr2w = 1,
+ .LoopLatency = 17,
+ .DspOn = 46,
+ .Rloop = 17,
+ .name = "64-bit SDR SGRAM (1:1)",
+};
+
+static const struct aty128_meminfo sdr_sgram = {
+ .ML = 4,
+ .MB = 4,
+ .Trcd = 1,
+ .Trp = 2,
+ .Twr = 1,
+ .CL = 2,
+ .Tr2w = 1,
+ .LoopLatency = 16,
+ .DspOn = 24,
+ .Rloop = 16,
+ .name = "64-bit SDR SGRAM (2:1)",
+};
+
+static const struct aty128_meminfo ddr_sgram = {
+ .ML = 4,
+ .MB = 4,
+ .Trcd = 3,
+ .Trp = 3,
+ .Twr = 2,
+ .CL = 3,
+ .Tr2w = 1,
+ .LoopLatency = 16,
+ .DspOn = 31,
+ .Rloop = 16,
+ .name = "64-bit DDR SGRAM",
+};
+
+static const struct fb_fix_screeninfo aty128fb_fix = {
+ .id = "ATY Rage128",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_PSEUDOCOLOR,
+ .xpanstep = 8,
+ .ypanstep = 1,
+ .mmio_len = 0x2000,
+ .accel = FB_ACCEL_ATI_RAGE128,
+};
+
+static char *mode_option = NULL;
+
+#ifdef CONFIG_PPC_PMAC
+static int default_vmode = VMODE_1024_768_60;
+static int default_cmode = CMODE_8;
+#endif
+
+static int default_crt_on = 0;
+static int default_lcd_on = 1;
+static bool mtrr = true;
+
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int backlight = 1;
+#else
+static int backlight = 0;
+#endif
+#endif
+
+/* PLL constants */
+struct aty128_constants {
+ u32 ref_clk;
+ u32 ppll_min;
+ u32 ppll_max;
+ u32 ref_divider;
+ u32 xclk;
+ u32 fifo_width;
+ u32 fifo_depth;
+};
+
+struct aty128_crtc {
+ u32 gen_cntl;
+ u32 h_total, h_sync_strt_wid;
+ u32 v_total, v_sync_strt_wid;
+ u32 pitch;
+ u32 offset, offset_cntl;
+ u32 xoffset, yoffset;
+ u32 vxres, vyres;
+ u32 depth, bpp;
+};
+
+struct aty128_pll {
+ u32 post_divider;
+ u32 feedback_divider;
+ u32 vclk;
+};
+
+struct aty128_ddafifo {
+ u32 dda_config;
+ u32 dda_on_off;
+};
+
+/* register values for a specific mode */
+struct aty128fb_par {
+ struct aty128_crtc crtc;
+ struct aty128_pll pll;
+ struct aty128_ddafifo fifo_reg;
+ u32 accel_flags;
+ struct aty128_constants constants; /* PLL and others */
+ void __iomem *regbase; /* remapped mmio */
+ u32 vram_size; /* onboard video ram */
+ int chip_gen;
+ const struct aty128_meminfo *mem; /* onboard mem info */
+ int wc_cookie;
+ int blitter_may_be_busy;
+ int fifo_slots; /* free slots in FIFO (64 max) */
+
+ int crt_on, lcd_on;
+ struct pci_dev *pdev;
+ struct fb_info *next;
+ int asleep;
+ int lock_blank;
+
+ u8 red[32]; /* see aty128fb_setcolreg */
+ u8 green[64];
+ u8 blue[32];
+ u32 pseudo_palette[16]; /* used for TRUECOLOR */
+};
+
+
+#define round_div(n, d) ((n+(d/2))/d)
+
+static int aty128fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int aty128fb_set_par(struct fb_info *info);
+static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info);
+static int aty128fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fb);
+static int aty128fb_blank(int blank, struct fb_info *fb);
+static int aty128fb_ioctl(struct fb_info *info, u_int cmd, unsigned long arg);
+static int aty128fb_sync(struct fb_info *info);
+
+ /*
+ * Internal routines
+ */
+
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+ const struct aty128fb_par *par);
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+ struct aty128fb_par *par);
+#if 0
+static void aty128_get_pllinfo(struct aty128fb_par *par, void __iomem *bios);
+static void __iomem *aty128_map_ROM(struct pci_dev *pdev,
+ const struct aty128fb_par *par);
+#endif
+static void aty128_timings(struct aty128fb_par *par);
+static void aty128_init_engine(struct aty128fb_par *par);
+static void aty128_reset_engine(const struct aty128fb_par *par);
+static void aty128_flush_pixel_cache(const struct aty128fb_par *par);
+static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par);
+static void wait_for_fifo(u16 entries, struct aty128fb_par *par);
+static void wait_for_idle(struct aty128fb_par *par);
+static u32 depth_to_dst(u32 depth);
+
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+static void aty128_bl_set_power(struct fb_info *info, int power);
+#endif
+
+#define BIOS_IN8(v) (readb(bios + (v)))
+#define BIOS_IN16(v) (readb(bios + (v)) | \
+ (readb(bios + (v) + 1) << 8))
+#define BIOS_IN32(v) (readb(bios + (v)) | \
+ (readb(bios + (v) + 1) << 8) | \
+ (readb(bios + (v) + 2) << 16) | \
+ (readb(bios + (v) + 3) << 24))
+
+
+static struct fb_ops aty128fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = aty128fb_check_var,
+ .fb_set_par = aty128fb_set_par,
+ .fb_setcolreg = aty128fb_setcolreg,
+ .fb_pan_display = aty128fb_pan_display,
+ .fb_blank = aty128fb_blank,
+ .fb_ioctl = aty128fb_ioctl,
+ .fb_sync = aty128fb_sync,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+ /*
+ * Functions to read from/write to the mmio registers
+ * - endian conversions may possibly be avoided by
+ * using the other register aperture. TODO.
+ */
+static inline u32 _aty_ld_le32(volatile unsigned int regindex,
+ const struct aty128fb_par *par)
+{
+ return readl (par->regbase + regindex);
+}
+
+static inline void _aty_st_le32(volatile unsigned int regindex, u32 val,
+ const struct aty128fb_par *par)
+{
+ writel (val, par->regbase + regindex);
+}
+
+static inline u8 _aty_ld_8(unsigned int regindex,
+ const struct aty128fb_par *par)
+{
+ return readb (par->regbase + regindex);
+}
+
+static inline void _aty_st_8(unsigned int regindex, u8 val,
+ const struct aty128fb_par *par)
+{
+ writeb (val, par->regbase + regindex);
+}
+
+#define aty_ld_le32(regindex) _aty_ld_le32(regindex, par)
+#define aty_st_le32(regindex, val) _aty_st_le32(regindex, val, par)
+#define aty_ld_8(regindex) _aty_ld_8(regindex, par)
+#define aty_st_8(regindex, val) _aty_st_8(regindex, val, par)
+
+ /*
+ * Functions to read from/write to the pll registers
+ */
+
+#define aty_ld_pll(pll_index) _aty_ld_pll(pll_index, par)
+#define aty_st_pll(pll_index, val) _aty_st_pll(pll_index, val, par)
+
+
+static u32 _aty_ld_pll(unsigned int pll_index,
+ const struct aty128fb_par *par)
+{
+ aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F);
+ return aty_ld_le32(CLOCK_CNTL_DATA);
+}
+
+
+static void _aty_st_pll(unsigned int pll_index, u32 val,
+ const struct aty128fb_par *par)
+{
+ aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN);
+ aty_st_le32(CLOCK_CNTL_DATA, val);
+}
+
+
+/* return true when the PLL has completed an atomic update */
+static int aty_pll_readupdate(const struct aty128fb_par *par)
+{
+ return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R);
+}
+
+
+static void aty_pll_wait_readupdate(const struct aty128fb_par *par)
+{
+ unsigned long timeout = jiffies + HZ/100; // should be more than enough
+ int reset = 1;
+
+ while (time_before(jiffies, timeout))
+ if (aty_pll_readupdate(par)) {
+ reset = 0;
+ break;
+ }
+
+ if (reset) /* reset engine?? */
+ printk(KERN_DEBUG "aty128fb: PLL write timeout!\n");
+}
+
+
+/* tell PLL to update */
+static void aty_pll_writeupdate(const struct aty128fb_par *par)
+{
+ aty_pll_wait_readupdate(par);
+
+ aty_st_pll(PPLL_REF_DIV,
+ aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W);
+}
+
+
+/* write to the scratch register to test r/w functionality */
+static int register_test(const struct aty128fb_par *par)
+{
+ u32 val;
+ int flag = 0;
+
+ val = aty_ld_le32(BIOS_0_SCRATCH);
+
+ aty_st_le32(BIOS_0_SCRATCH, 0x55555555);
+ if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) {
+ aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA);
+
+ if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA)
+ flag = 1;
+ }
+
+ aty_st_le32(BIOS_0_SCRATCH, val); // restore value
+ return flag;
+}
+
+
+/*
+ * Accelerator engine functions
+ */
+static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par)
+{
+ int i;
+
+ for (;;) {
+ for (i = 0; i < 2000000; i++) {
+ par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff;
+ if (par->fifo_slots >= entries)
+ return;
+ }
+ aty128_reset_engine(par);
+ }
+}
+
+
+static void wait_for_idle(struct aty128fb_par *par)
+{
+ int i;
+
+ do_wait_for_fifo(64, par);
+
+ for (;;) {
+ for (i = 0; i < 2000000; i++) {
+ if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) {
+ aty128_flush_pixel_cache(par);
+ par->blitter_may_be_busy = 0;
+ return;
+ }
+ }
+ aty128_reset_engine(par);
+ }
+}
+
+
+static void wait_for_fifo(u16 entries, struct aty128fb_par *par)
+{
+ if (par->fifo_slots < entries)
+ do_wait_for_fifo(64, par);
+ par->fifo_slots -= entries;
+}
+
+
+static void aty128_flush_pixel_cache(const struct aty128fb_par *par)
+{
+ int i;
+ u32 tmp;
+
+ tmp = aty_ld_le32(PC_NGUI_CTLSTAT);
+ tmp &= ~(0x00ff);
+ tmp |= 0x00ff;
+ aty_st_le32(PC_NGUI_CTLSTAT, tmp);
+
+ for (i = 0; i < 2000000; i++)
+ if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY))
+ break;
+}
+
+
+static void aty128_reset_engine(const struct aty128fb_par *par)
+{
+ u32 gen_reset_cntl, clock_cntl_index, mclk_cntl;
+
+ aty128_flush_pixel_cache(par);
+
+ clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX);
+ mclk_cntl = aty_ld_pll(MCLK_CNTL);
+
+ aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000);
+
+ gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL);
+ aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI);
+ aty_ld_le32(GEN_RESET_CNTL);
+ aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI));
+ aty_ld_le32(GEN_RESET_CNTL);
+
+ aty_st_pll(MCLK_CNTL, mclk_cntl);
+ aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index);
+ aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl);
+
+ /* use old pio mode */
+ aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4);
+
+ DBG("engine reset");
+}
+
+
+static void aty128_init_engine(struct aty128fb_par *par)
+{
+ u32 pitch_value;
+
+ wait_for_idle(par);
+
+ /* 3D scaler not spoken here */
+ wait_for_fifo(1, par);
+ aty_st_le32(SCALE_3D_CNTL, 0x00000000);
+
+ aty128_reset_engine(par);
+
+ pitch_value = par->crtc.pitch;
+ if (par->crtc.bpp == 24) {
+ pitch_value = pitch_value * 3;
+ }
+
+ wait_for_fifo(4, par);
+ /* setup engine offset registers */
+ aty_st_le32(DEFAULT_OFFSET, 0x00000000);
+
+ /* setup engine pitch registers */
+ aty_st_le32(DEFAULT_PITCH, pitch_value);
+
+ /* set the default scissor register to max dimensions */
+ aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF);
+
+ /* set the drawing controls registers */
+ aty_st_le32(DP_GUI_MASTER_CNTL,
+ GMC_SRC_PITCH_OFFSET_DEFAULT |
+ GMC_DST_PITCH_OFFSET_DEFAULT |
+ GMC_SRC_CLIP_DEFAULT |
+ GMC_DST_CLIP_DEFAULT |
+ GMC_BRUSH_SOLIDCOLOR |
+ (depth_to_dst(par->crtc.depth) << 8) |
+ GMC_SRC_DSTCOLOR |
+ GMC_BYTE_ORDER_MSB_TO_LSB |
+ GMC_DP_CONVERSION_TEMP_6500 |
+ ROP3_PATCOPY |
+ GMC_DP_SRC_RECT |
+ GMC_3D_FCN_EN_CLR |
+ GMC_DST_CLR_CMP_FCN_CLEAR |
+ GMC_AUX_CLIP_CLEAR |
+ GMC_WRITE_MASK_SET);
+
+ wait_for_fifo(8, par);
+ /* clear the line drawing registers */
+ aty_st_le32(DST_BRES_ERR, 0);
+ aty_st_le32(DST_BRES_INC, 0);
+ aty_st_le32(DST_BRES_DEC, 0);
+
+ /* set brush color registers */
+ aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */
+ aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */
+
+ /* set source color registers */
+ aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF); /* white */
+ aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000); /* black */
+
+ /* default write mask */
+ aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF);
+
+ /* Wait for all the writes to be completed before returning */
+ wait_for_idle(par);
+}
+
+
+/* convert depth values to their register representation */
+static u32 depth_to_dst(u32 depth)
+{
+ if (depth <= 8)
+ return DST_8BPP;
+ else if (depth <= 15)
+ return DST_15BPP;
+ else if (depth == 16)
+ return DST_16BPP;
+ else if (depth <= 24)
+ return DST_24BPP;
+ else if (depth <= 32)
+ return DST_32BPP;
+
+ return -EINVAL;
+}
+
+/*
+ * PLL informations retreival
+ */
+
+
+#ifndef __sparc__
+static void __iomem *aty128_map_ROM(const struct aty128fb_par *par,
+ struct pci_dev *dev)
+{
+ u16 dptr;
+ u8 rom_type;
+ void __iomem *bios;
+ size_t rom_size;
+
+ /* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */
+ unsigned int temp;
+ temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
+ temp &= 0x00ffffffu;
+ temp |= 0x04 << 24;
+ aty_st_le32(RAGE128_MPP_TB_CONFIG, temp);
+ temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
+
+ bios = pci_map_rom(dev, &rom_size);
+
+ if (!bios) {
+ printk(KERN_ERR "aty128fb: ROM failed to map\n");
+ return NULL;
+ }
+
+ /* Very simple test to make sure it appeared */
+ if (BIOS_IN16(0) != 0xaa55) {
+ printk(KERN_DEBUG "aty128fb: Invalid ROM signature %x should "
+ " be 0xaa55\n", BIOS_IN16(0));
+ goto failed;
+ }
+
+ /* Look for the PCI data to check the ROM type */
+ dptr = BIOS_IN16(0x18);
+
+ /* Check the PCI data signature. If it's wrong, we still assume a normal
+ * x86 ROM for now, until I've verified this works everywhere.
+ * The goal here is more to phase out Open Firmware images.
+ *
+ * Currently, we only look at the first PCI data, we could iteratre and
+ * deal with them all, and we should use fb_bios_start relative to start
+ * of image and not relative start of ROM, but so far, I never found a
+ * dual-image ATI card.
+ *
+ * typedef struct {
+ * u32 signature; + 0x00
+ * u16 vendor; + 0x04
+ * u16 device; + 0x06
+ * u16 reserved_1; + 0x08
+ * u16 dlen; + 0x0a
+ * u8 drevision; + 0x0c
+ * u8 class_hi; + 0x0d
+ * u16 class_lo; + 0x0e
+ * u16 ilen; + 0x10
+ * u16 irevision; + 0x12
+ * u8 type; + 0x14
+ * u8 indicator; + 0x15
+ * u16 reserved_2; + 0x16
+ * } pci_data_t;
+ */
+ if (BIOS_IN32(dptr) != (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) {
+ printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n",
+ BIOS_IN32(dptr));
+ goto anyway;
+ }
+ rom_type = BIOS_IN8(dptr + 0x14);
+ switch(rom_type) {
+ case 0:
+ printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n");
+ break;
+ case 1:
+ printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n");
+ goto failed;
+ case 2:
+ printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n");
+ goto failed;
+ default:
+ printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n",
+ rom_type);
+ goto failed;
+ }
+ anyway:
+ return bios;
+
+ failed:
+ pci_unmap_rom(dev, bios);
+ return NULL;
+}
+
+static void aty128_get_pllinfo(struct aty128fb_par *par,
+ unsigned char __iomem *bios)
+{
+ unsigned int bios_hdr;
+ unsigned int bios_pll;
+
+ bios_hdr = BIOS_IN16(0x48);
+ bios_pll = BIOS_IN16(bios_hdr + 0x30);
+
+ par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16);
+ par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12);
+ par->constants.xclk = BIOS_IN16(bios_pll + 0x08);
+ par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10);
+ par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e);
+
+ DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n",
+ par->constants.ppll_max, par->constants.ppll_min,
+ par->constants.xclk, par->constants.ref_divider,
+ par->constants.ref_clk);
+
+}
+
+#ifdef CONFIG_X86
+static void __iomem *aty128_find_mem_vbios(struct aty128fb_par *par)
+{
+ /* I simplified this code as we used to miss the signatures in
+ * a lot of case. It's now closer to XFree, we just don't check
+ * for signatures at all... Something better will have to be done
+ * if we end up having conflicts
+ */
+ u32 segstart;
+ unsigned char __iomem *rom_base = NULL;
+
+ for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
+ rom_base = ioremap(segstart, 0x10000);
+ if (rom_base == NULL)
+ return NULL;
+ if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa)
+ break;
+ iounmap(rom_base);
+ rom_base = NULL;
+ }
+ return rom_base;
+}
+#endif
+#endif /* ndef(__sparc__) */
+
+/* fill in known card constants if pll_block is not available */
+static void aty128_timings(struct aty128fb_par *par)
+{
+#ifdef CONFIG_PPC
+ /* instead of a table lookup, assume OF has properly
+ * setup the PLL registers and use their values
+ * to set the XCLK values and reference divider values */
+
+ u32 x_mpll_ref_fb_div;
+ u32 xclk_cntl;
+ u32 Nx, M;
+ unsigned PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 };
+#endif
+
+ if (!par->constants.ref_clk)
+ par->constants.ref_clk = 2950;
+
+#ifdef CONFIG_PPC
+ x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV);
+ xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7;
+ Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8;
+ M = x_mpll_ref_fb_div & 0x0000ff;
+
+ par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk),
+ (M * PostDivSet[xclk_cntl]));
+
+ par->constants.ref_divider =
+ aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
+#endif
+
+ if (!par->constants.ref_divider) {
+ par->constants.ref_divider = 0x3b;
+
+ aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e);
+ aty_pll_writeupdate(par);
+ }
+ aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider);
+ aty_pll_writeupdate(par);
+
+ /* from documentation */
+ if (!par->constants.ppll_min)
+ par->constants.ppll_min = 12500;
+ if (!par->constants.ppll_max)
+ par->constants.ppll_max = 25000; /* 23000 on some cards? */
+ if (!par->constants.xclk)
+ par->constants.xclk = 0x1d4d; /* same as mclk */
+
+ par->constants.fifo_width = 128;
+ par->constants.fifo_depth = 32;
+
+ switch (aty_ld_le32(MEM_CNTL) & 0x3) {
+ case 0:
+ par->mem = &sdr_128;
+ break;
+ case 1:
+ par->mem = &sdr_sgram;
+ break;
+ case 2:
+ par->mem = &ddr_sgram;
+ break;
+ default:
+ par->mem = &sdr_sgram;
+ }
+}
+
+
+
+/*
+ * CRTC programming
+ */
+
+/* Program the CRTC registers */
+static void aty128_set_crtc(const struct aty128_crtc *crtc,
+ const struct aty128fb_par *par)
+{
+ aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl);
+ aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total);
+ aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid);
+ aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total);
+ aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid);
+ aty_st_le32(CRTC_PITCH, crtc->pitch);
+ aty_st_le32(CRTC_OFFSET, crtc->offset);
+ aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
+ /* Disable ATOMIC updating. Is this the right place? */
+ aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000));
+}
+
+
+static int aty128_var_to_crtc(const struct fb_var_screeninfo *var,
+ struct aty128_crtc *crtc,
+ const struct aty128fb_par *par)
+{
+ u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst;
+ u32 left, right, upper, lower, hslen, vslen, sync, vmode;
+ u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol;
+ u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
+ u32 depth, bytpp;
+ u8 mode_bytpp[7] = { 0, 0, 1, 2, 2, 3, 4 };
+
+ /* input */
+ xres = var->xres;
+ yres = var->yres;
+ vxres = var->xres_virtual;
+ vyres = var->yres_virtual;
+ 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;
+ sync = var->sync;
+ vmode = var->vmode;
+
+ if (bpp != 16)
+ depth = bpp;
+ else
+ depth = (var->green.length == 6) ? 16 : 15;
+
+ /* check for mode eligibility
+ * accept only non interlaced modes */
+ if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+
+ /* convert (and round up) and validate */
+ xres = (xres + 7) & ~7;
+ xoffset = (xoffset + 7) & ~7;
+
+ if (vxres < xres + xoffset)
+ vxres = xres + xoffset;
+
+ if (vyres < yres + yoffset)
+ vyres = yres + yoffset;
+
+ /* convert depth into ATI register depth */
+ dst = depth_to_dst(depth);
+
+ if (dst == -EINVAL) {
+ printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n");
+ return -EINVAL;
+ }
+
+ /* convert register depth to bytes per pixel */
+ bytpp = mode_bytpp[dst];
+
+ /* make sure there is enough video ram for the mode */
+ if ((u32)(vxres * vyres * bytpp) > par->vram_size) {
+ printk(KERN_ERR "aty128fb: Not enough memory for mode\n");
+ return -EINVAL;
+ }
+
+ h_disp = (xres >> 3) - 1;
+ h_total = (((xres + right + hslen + left) >> 3) - 1) & 0xFFFFL;
+
+ v_disp = yres - 1;
+ v_total = (yres + upper + vslen + lower - 1) & 0xFFFFL;
+
+ /* check to make sure h_total and v_total are in range */
+ if (((h_total >> 3) - 1) > 0x1ff || (v_total - 1) > 0x7FF) {
+ printk(KERN_ERR "aty128fb: invalid width ranges\n");
+ return -EINVAL;
+ }
+
+ h_sync_wid = (hslen + 7) >> 3;
+ if (h_sync_wid == 0)
+ h_sync_wid = 1;
+ else if (h_sync_wid > 0x3f) /* 0x3f = max hwidth */
+ h_sync_wid = 0x3f;
+
+ h_sync_strt = (h_disp << 3) + right;
+
+ v_sync_wid = vslen;
+ if (v_sync_wid == 0)
+ v_sync_wid = 1;
+ else if (v_sync_wid > 0x1f) /* 0x1f = max vwidth */
+ v_sync_wid = 0x1f;
+
+ v_sync_strt = v_disp + lower;
+
+ h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+ v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+ c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
+
+ crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8);
+
+ crtc->h_total = h_total | (h_disp << 16);
+ crtc->v_total = v_total | (v_disp << 16);
+
+ crtc->h_sync_strt_wid = h_sync_strt | (h_sync_wid << 16) |
+ (h_sync_pol << 23);
+ crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
+ (v_sync_pol << 23);
+
+ crtc->pitch = vxres >> 3;
+
+ crtc->offset = 0;
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
+ crtc->offset_cntl = 0x00010000;
+ else
+ crtc->offset_cntl = 0;
+
+ crtc->vxres = vxres;
+ crtc->vyres = vyres;
+ crtc->xoffset = xoffset;
+ crtc->yoffset = yoffset;
+ crtc->depth = depth;
+ crtc->bpp = bpp;
+
+ return 0;
+}
+
+
+static int aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var)
+{
+
+ /* fill in pixel info */
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.offset = 0;
+ var->blue.msb_right = 0;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ var->transp.msb_right = 0;
+ switch (pix_width) {
+ case CRTC_PIX_WIDTH_8BPP:
+ var->bits_per_pixel = 8;
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.length = 8;
+ break;
+ case CRTC_PIX_WIDTH_15BPP:
+ var->bits_per_pixel = 16;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ break;
+ case CRTC_PIX_WIDTH_16BPP:
+ var->bits_per_pixel = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.length = 5;
+ break;
+ case CRTC_PIX_WIDTH_24BPP:
+ var->bits_per_pixel = 24;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ break;
+ case CRTC_PIX_WIDTH_32BPP:
+ var->bits_per_pixel = 32;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ break;
+ default:
+ printk(KERN_ERR "aty128fb: Invalid pixel width\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int aty128_crtc_to_var(const struct aty128_crtc *crtc,
+ struct fb_var_screeninfo *var)
+{
+ u32 xres, yres, 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;
+
+ /* fun with masking */
+ h_total = crtc->h_total & 0x1ff;
+ h_disp = (crtc->h_total >> 16) & 0xff;
+ h_sync_strt = (crtc->h_sync_strt_wid >> 3) & 0x1ff;
+ h_sync_dly = crtc->h_sync_strt_wid & 0x7;
+ h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x3f;
+ h_sync_pol = (crtc->h_sync_strt_wid >> 23) & 0x1;
+ v_total = crtc->v_total & 0x7ff;
+ v_disp = (crtc->v_total >> 16) & 0x7ff;
+ v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
+ v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f;
+ v_sync_pol = (crtc->v_sync_strt_wid >> 23) & 0x1;
+ c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+ pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+ /* do conversions */
+ xres = (h_disp + 1) << 3;
+ yres = v_disp + 1;
+ left = ((h_total - h_sync_strt - h_sync_wid) << 3) - h_sync_dly;
+ right = ((h_sync_strt - h_disp) << 3) + h_sync_dly;
+ hslen = h_sync_wid << 3;
+ upper = v_total - v_sync_strt - v_sync_wid;
+ lower = v_sync_strt - v_disp;
+ vslen = v_sync_wid;
+ sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+ (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+ (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+
+ aty128_pix_width_to_var(pix_width, var);
+
+ var->xres = xres;
+ var->yres = yres;
+ var->xres_virtual = crtc->vxres;
+ var->yres_virtual = crtc->vyres;
+ var->xoffset = crtc->xoffset;
+ var->yoffset = crtc->yoffset;
+ var->left_margin = left;
+ var->right_margin = right;
+ var->upper_margin = upper;
+ var->lower_margin = lower;
+ var->hsync_len = hslen;
+ var->vsync_len = vslen;
+ var->sync = sync;
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ return 0;
+}
+
+static void aty128_set_crt_enable(struct aty128fb_par *par, int on)
+{
+ if (on) {
+ aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) |
+ CRT_CRTC_ON);
+ aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) |
+ DAC_PALETTE2_SNOOP_EN));
+ } else
+ aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) &
+ ~CRT_CRTC_ON);
+}
+
+static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
+{
+ u32 reg;
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ struct fb_info *info = pci_get_drvdata(par->pdev);
+#endif
+
+ if (on) {
+ reg = aty_ld_le32(LVDS_GEN_CNTL);
+ reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION;
+ reg &= ~LVDS_DISPLAY_DIS;
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ aty128_bl_set_power(info, FB_BLANK_UNBLANK);
+#endif
+ } else {
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ aty128_bl_set_power(info, FB_BLANK_POWERDOWN);
+#endif
+ reg = aty_ld_le32(LVDS_GEN_CNTL);
+ reg |= LVDS_DISPLAY_DIS;
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+ mdelay(100);
+ reg &= ~(LVDS_ON /*| LVDS_EN*/);
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+ }
+}
+
+static void aty128_set_pll(struct aty128_pll *pll,
+ const struct aty128fb_par *par)
+{
+ u32 div3;
+
+ unsigned char post_conv[] = /* register values for post dividers */
+ { 2, 0, 1, 4, 2, 2, 6, 2, 3, 2, 2, 2, 7 };
+
+ /* select PPLL_DIV_3 */
+ aty_st_le32(CLOCK_CNTL_INDEX, aty_ld_le32(CLOCK_CNTL_INDEX) | (3 << 8));
+
+ /* reset PLL */
+ aty_st_pll(PPLL_CNTL,
+ aty_ld_pll(PPLL_CNTL) | PPLL_RESET | PPLL_ATOMIC_UPDATE_EN);
+
+ /* write the reference divider */
+ aty_pll_wait_readupdate(par);
+ aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider & 0x3ff);
+ aty_pll_writeupdate(par);
+
+ div3 = aty_ld_pll(PPLL_DIV_3);
+ div3 &= ~PPLL_FB3_DIV_MASK;
+ div3 |= pll->feedback_divider;
+ div3 &= ~PPLL_POST3_DIV_MASK;
+ div3 |= post_conv[pll->post_divider] << 16;
+
+ /* write feedback and post dividers */
+ aty_pll_wait_readupdate(par);
+ aty_st_pll(PPLL_DIV_3, div3);
+ aty_pll_writeupdate(par);
+
+ aty_pll_wait_readupdate(par);
+ aty_st_pll(HTOTAL_CNTL, 0); /* no horiz crtc adjustment */
+ aty_pll_writeupdate(par);
+
+ /* clear the reset, just in case */
+ aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET);
+}
+
+
+static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
+ const struct aty128fb_par *par)
+{
+ const struct aty128_constants c = par->constants;
+ unsigned char post_dividers[] = {1,2,4,8,3,6,12};
+ u32 output_freq;
+ u32 vclk; /* in .01 MHz */
+ int i = 0;
+ u32 n, d;
+
+ vclk = 100000000 / period_in_ps; /* convert units to 10 kHz */
+
+ /* adjust pixel clock if necessary */
+ if (vclk > c.ppll_max)
+ vclk = c.ppll_max;
+ if (vclk * 12 < c.ppll_min)
+ vclk = c.ppll_min/12;
+
+ /* now, find an acceptable divider */
+ for (i = 0; i < ARRAY_SIZE(post_dividers); i++) {
+ output_freq = post_dividers[i] * vclk;
+ if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) {
+ pll->post_divider = post_dividers[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(post_dividers))
+ return -EINVAL;
+
+ /* calculate feedback divider */
+ n = c.ref_divider * output_freq;
+ d = c.ref_clk;
+
+ pll->feedback_divider = round_div(n, d);
+ pll->vclk = vclk;
+
+ DBG("post %d feedback %d vlck %d output %d ref_divider %d "
+ "vclk_per: %d\n", pll->post_divider,
+ pll->feedback_divider, vclk, output_freq,
+ c.ref_divider, period_in_ps);
+
+ return 0;
+}
+
+
+static int aty128_pll_to_var(const struct aty128_pll *pll,
+ struct fb_var_screeninfo *var)
+{
+ var->pixclock = 100000000 / pll->vclk;
+
+ return 0;
+}
+
+
+static void aty128_set_fifo(const struct aty128_ddafifo *dsp,
+ const struct aty128fb_par *par)
+{
+ aty_st_le32(DDA_CONFIG, dsp->dda_config);
+ aty_st_le32(DDA_ON_OFF, dsp->dda_on_off);
+}
+
+
+static int aty128_ddafifo(struct aty128_ddafifo *dsp,
+ const struct aty128_pll *pll,
+ u32 depth,
+ const struct aty128fb_par *par)
+{
+ const struct aty128_meminfo *m = par->mem;
+ u32 xclk = par->constants.xclk;
+ u32 fifo_width = par->constants.fifo_width;
+ u32 fifo_depth = par->constants.fifo_depth;
+ s32 x, b, p, ron, roff;
+ u32 n, d, bpp;
+
+ /* round up to multiple of 8 */
+ bpp = (depth+7) & ~7;
+
+ n = xclk * fifo_width;
+ d = pll->vclk * bpp;
+ x = round_div(n, d);
+
+ ron = 4 * m->MB +
+ 3 * ((m->Trcd - 2 > 0) ? m->Trcd - 2 : 0) +
+ 2 * m->Trp +
+ m->Twr +
+ m->CL +
+ m->Tr2w +
+ x;
+
+ DBG("x %x\n", x);
+
+ b = 0;
+ while (x) {
+ x >>= 1;
+ b++;
+ }
+ p = b + 1;
+
+ ron <<= (11 - p);
+
+ n <<= (11 - p);
+ x = round_div(n, d);
+ roff = x * (fifo_depth - 4);
+
+ if ((ron + m->Rloop) >= roff) {
+ printk(KERN_ERR "aty128fb: Mode out of range!\n");
+ return -EINVAL;
+ }
+
+ DBG("p: %x rloop: %x x: %x ron: %x roff: %x\n",
+ p, m->Rloop, x, ron, roff);
+
+ dsp->dda_config = p << 16 | m->Rloop << 20 | x;
+ dsp->dda_on_off = ron << 16 | roff;
+
+ return 0;
+}
+
+
+/*
+ * This actually sets the video mode.
+ */
+static int aty128fb_set_par(struct fb_info *info)
+{
+ struct aty128fb_par *par = info->par;
+ u32 config;
+ int err;
+
+ if ((err = aty128_decode_var(&info->var, par)) != 0)
+ return err;
+
+ if (par->blitter_may_be_busy)
+ wait_for_idle(par);
+
+ /* clear all registers that may interfere with mode setting */
+ aty_st_le32(OVR_CLR, 0);
+ aty_st_le32(OVR_WID_LEFT_RIGHT, 0);
+ aty_st_le32(OVR_WID_TOP_BOTTOM, 0);
+ aty_st_le32(OV0_SCALE_CNTL, 0);
+ aty_st_le32(MPP_TB_CONFIG, 0);
+ aty_st_le32(MPP_GP_CONFIG, 0);
+ aty_st_le32(SUBPIC_CNTL, 0);
+ aty_st_le32(VIPH_CONTROL, 0);
+ aty_st_le32(I2C_CNTL_1, 0); /* turn off i2c */
+ aty_st_le32(GEN_INT_CNTL, 0); /* turn off interrupts */
+ aty_st_le32(CAP0_TRIG_CNTL, 0);
+ aty_st_le32(CAP1_TRIG_CNTL, 0);
+
+ aty_st_8(CRTC_EXT_CNTL + 1, 4); /* turn video off */
+
+ aty128_set_crtc(&par->crtc, par);
+ aty128_set_pll(&par->pll, par);
+ aty128_set_fifo(&par->fifo_reg, par);
+
+ config = aty_ld_le32(CNFG_CNTL) & ~3;
+
+#if defined(__BIG_ENDIAN)
+ if (par->crtc.bpp == 32)
+ config |= 2; /* make aperture do 32 bit swapping */
+ else if (par->crtc.bpp == 16)
+ config |= 1; /* make aperture do 16 bit swapping */
+#endif
+
+ aty_st_le32(CNFG_CNTL, config);
+ aty_st_8(CRTC_EXT_CNTL + 1, 0); /* turn the video back on */
+
+ info->fix.line_length = (par->crtc.vxres * par->crtc.bpp) >> 3;
+ info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR
+ : FB_VISUAL_DIRECTCOLOR;
+
+ if (par->chip_gen == rage_M3) {
+ aty128_set_crt_enable(par, par->crt_on);
+ aty128_set_lcd_enable(par, par->lcd_on);
+ }
+ if (par->accel_flags & FB_ACCELF_TEXT)
+ aty128_init_engine(par);
+
+#ifdef CONFIG_BOOTX_TEXT
+ btext_update_display(info->fix.smem_start,
+ (((par->crtc.h_total>>16) & 0xff)+1)*8,
+ ((par->crtc.v_total>>16) & 0x7ff)+1,
+ par->crtc.bpp,
+ par->crtc.vxres*par->crtc.bpp/8);
+#endif /* CONFIG_BOOTX_TEXT */
+
+ return 0;
+}
+
+/*
+ * encode/decode the User Defined Part of the Display
+ */
+
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+ struct aty128fb_par *par)
+{
+ int err;
+ struct aty128_crtc crtc;
+ struct aty128_pll pll;
+ struct aty128_ddafifo fifo_reg;
+
+ if ((err = aty128_var_to_crtc(var, &crtc, par)))
+ return err;
+
+ if ((err = aty128_var_to_pll(var->pixclock, &pll, par)))
+ return err;
+
+ if ((err = aty128_ddafifo(&fifo_reg, &pll, crtc.depth, par)))
+ return err;
+
+ par->crtc = crtc;
+ par->pll = pll;
+ par->fifo_reg = fifo_reg;
+ par->accel_flags = var->accel_flags;
+
+ return 0;
+}
+
+
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+ const struct aty128fb_par *par)
+{
+ int err;
+
+ if ((err = aty128_crtc_to_var(&par->crtc, var)))
+ return err;
+
+ if ((err = aty128_pll_to_var(&par->pll, var)))
+ return err;
+
+ var->nonstd = 0;
+ var->activate = 0;
+
+ var->height = -1;
+ var->width = -1;
+ var->accel_flags = par->accel_flags;
+
+ return 0;
+}
+
+
+static int aty128fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct aty128fb_par par;
+ int err;
+
+ par = *(struct aty128fb_par *)info->par;
+ if ((err = aty128_decode_var(var, &par)) != 0)
+ return err;
+ aty128_encode_var(var, &par);
+ return 0;
+}
+
+
+/*
+ * Pan or Wrap the Display
+ */
+static int aty128fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
+{
+ struct aty128fb_par *par = fb->par;
+ u32 xoffset, yoffset;
+ u32 offset;
+ u32 xres, yres;
+
+ xres = (((par->crtc.h_total >> 16) & 0xff) + 1) << 3;
+ yres = ((par->crtc.v_total >> 16) & 0x7ff) + 1;
+
+ xoffset = (var->xoffset +7) & ~7;
+ yoffset = var->yoffset;
+
+ if (xoffset+xres > par->crtc.vxres || yoffset+yres > par->crtc.vyres)
+ return -EINVAL;
+
+ par->crtc.xoffset = xoffset;
+ par->crtc.yoffset = yoffset;
+
+ offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3))
+ & ~7;
+
+ if (par->crtc.bpp == 24)
+ offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
+
+ aty_st_le32(CRTC_OFFSET, offset);
+
+ return 0;
+}
+
+
+/*
+ * Helper function to store a single palette register
+ */
+static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
+ struct aty128fb_par *par)
+{
+ if (par->chip_gen == rage_M3) {
+#if 0
+ /* Note: For now, on M3, we set palette on both heads, which may
+ * be useless. Can someone with a M3 check this ?
+ *
+ * This code would still be useful if using the second CRTC to
+ * do mirroring
+ */
+
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) |
+ DAC_PALETTE_ACCESS_CNTL);
+ aty_st_8(PALETTE_INDEX, regno);
+ aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+#endif
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) &
+ ~DAC_PALETTE_ACCESS_CNTL);
+ }
+
+ aty_st_8(PALETTE_INDEX, regno);
+ aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+}
+
+static int aty128fb_sync(struct fb_info *info)
+{
+ struct aty128fb_par *par = info->par;
+
+ if (par->blitter_may_be_busy)
+ wait_for_idle(par);
+ return 0;
+}
+
+#ifndef MODULE
+static int aty128fb_setup(char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!strncmp(this_opt, "lcd:", 4)) {
+ default_lcd_on = simple_strtoul(this_opt+4, NULL, 0);
+ continue;
+ } else if (!strncmp(this_opt, "crt:", 4)) {
+ default_crt_on = simple_strtoul(this_opt+4, NULL, 0);
+ continue;
+ } else if (!strncmp(this_opt, "backlight:", 10)) {
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ backlight = simple_strtoul(this_opt+10, NULL, 0);
+#endif
+ continue;
+ }
+ if(!strncmp(this_opt, "nomtrr", 6)) {
+ mtrr = false;
+ continue;
+ }
+#ifdef CONFIG_PPC_PMAC
+ /* vmode and cmode deprecated */
+ if (!strncmp(this_opt, "vmode:", 6)) {
+ unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
+ if (vmode > 0 && vmode <= VMODE_MAX)
+ default_vmode = vmode;
+ continue;
+ } else if (!strncmp(this_opt, "cmode:", 6)) {
+ unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0);
+ switch (cmode) {
+ case 0:
+ case 8:
+ default_cmode = CMODE_8;
+ break;
+ case 15:
+ case 16:
+ default_cmode = CMODE_16;
+ break;
+ case 24:
+ case 32:
+ default_cmode = CMODE_32;
+ break;
+ }
+ continue;
+ }
+#endif /* CONFIG_PPC_PMAC */
+ mode_option = this_opt;
+ }
+ return 0;
+}
+#endif /* MODULE */
+
+/* Backlight */
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+#define MAX_LEVEL 0xFF
+
+static int aty128_bl_get_level_brightness(struct aty128fb_par *par,
+ int level)
+{
+ struct fb_info *info = pci_get_drvdata(par->pdev);
+ int atylevel;
+
+ /* Get and convert the value */
+ /* No locking of bl_curve since we read a single value */
+ atylevel = MAX_LEVEL -
+ (info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL);
+
+ if (atylevel < 0)
+ atylevel = 0;
+ else if (atylevel > MAX_LEVEL)
+ atylevel = MAX_LEVEL;
+
+ return atylevel;
+}
+
+/* We turn off the LCD completely instead of just dimming the backlight.
+ * This provides greater power saving and the display is useless without
+ * backlight anyway
+ */
+#define BACKLIGHT_LVDS_OFF
+/* That one prevents proper CRT output with LCD off */
+#undef BACKLIGHT_DAC_OFF
+
+static int aty128_bl_update_status(struct backlight_device *bd)
+{
+ struct aty128fb_par *par = bl_get_data(bd);
+ unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
+ int level;
+
+ if (bd->props.power != FB_BLANK_UNBLANK ||
+ bd->props.fb_blank != FB_BLANK_UNBLANK ||
+ !par->lcd_on)
+ level = 0;
+ else
+ level = bd->props.brightness;
+
+ reg |= LVDS_BL_MOD_EN | LVDS_BLON;
+ if (level > 0) {
+ reg |= LVDS_DIGION;
+ if (!(reg & LVDS_ON)) {
+ reg &= ~LVDS_BLON;
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+ aty_ld_le32(LVDS_GEN_CNTL);
+ mdelay(10);
+ reg |= LVDS_BLON;
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+ }
+ reg &= ~LVDS_BL_MOD_LEVEL_MASK;
+ reg |= (aty128_bl_get_level_brightness(par, level) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+ reg |= LVDS_ON | LVDS_EN;
+ reg &= ~LVDS_DISPLAY_DIS;
+#endif
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
+#endif
+ } else {
+ reg &= ~LVDS_BL_MOD_LEVEL_MASK;
+ reg |= (aty128_bl_get_level_brightness(par, 0) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+ reg |= LVDS_DISPLAY_DIS;
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+ aty_ld_le32(LVDS_GEN_CNTL);
+ udelay(10);
+ reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
+#endif
+ aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
+#endif
+ }
+
+ return 0;
+}
+
+static const struct backlight_ops aty128_bl_data = {
+ .update_status = aty128_bl_update_status,
+};
+
+static void aty128_bl_set_power(struct fb_info *info, int power)
+{
+ if (info->bl_dev) {
+ info->bl_dev->props.power = power;
+ backlight_update_status(info->bl_dev);
+ }
+}
+
+static void aty128_bl_init(struct aty128fb_par *par)
+{
+ struct backlight_properties props;
+ struct fb_info *info = pci_get_drvdata(par->pdev);
+ struct backlight_device *bd;
+ char name[12];
+
+ /* Could be extended to Rage128Pro LVDS output too */
+ if (par->chip_gen != rage_M3)
+ return;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+ if (!pmac_has_backlight_type("ati"))
+ return;
+#endif
+
+ snprintf(name, sizeof(name), "aty128bl%d", info->node);
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
+ bd = backlight_device_register(name, info->dev, par, &aty128_bl_data,
+ &props);
+ if (IS_ERR(bd)) {
+ info->bl_dev = NULL;
+ printk(KERN_WARNING "aty128: Backlight registration failed\n");
+ goto error;
+ }
+
+ info->bl_dev = bd;
+ fb_bl_default_curve(info, 0,
+ 63 * FB_BACKLIGHT_MAX / MAX_LEVEL,
+ 219 * FB_BACKLIGHT_MAX / MAX_LEVEL);
+
+ bd->props.brightness = bd->props.max_brightness;
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+
+ printk("aty128: Backlight initialized (%s)\n", name);
+
+ return;
+
+error:
+ return;
+}
+
+static void aty128_bl_exit(struct backlight_device *bd)
+{
+ backlight_device_unregister(bd);
+ printk("aty128: Backlight unloaded\n");
+}
+#endif /* CONFIG_FB_ATY128_BACKLIGHT */
+
+/*
+ * Initialisation
+ */
+
+#ifdef CONFIG_PPC_PMAC__disabled
+static void aty128_early_resume(void *data)
+{
+ struct aty128fb_par *par = data;
+
+ if (!console_trylock())
+ return;
+ pci_restore_state(par->pdev);
+ aty128_do_resume(par->pdev);
+ console_unlock();
+}
+#endif /* CONFIG_PPC_PMAC */
+
+static int aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct aty128fb_par *par = info->par;
+ struct fb_var_screeninfo var;
+ char video_card[50];
+ u8 chip_rev;
+ u32 dac;
+
+ /* Get the chip revision */
+ chip_rev = (aty_ld_le32(CNFG_CNTL) >> 16) & 0x1F;
+
+ strcpy(video_card, "Rage128 XX ");
+ video_card[8] = ent->device >> 8;
+ video_card[9] = ent->device & 0xFF;
+
+ /* range check to make sure */
+ if (ent->driver_data < ARRAY_SIZE(r128_family))
+ strlcat(video_card, r128_family[ent->driver_data],
+ sizeof(video_card));
+
+ printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
+
+ if (par->vram_size % (1024 * 1024) == 0)
+ printk("%dM %s\n", par->vram_size / (1024*1024), par->mem->name);
+ else
+ printk("%dk %s\n", par->vram_size / 1024, par->mem->name);
+
+ par->chip_gen = ent->driver_data;
+
+ /* fill in info */
+ info->fbops = &aty128fb_ops;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ par->lcd_on = default_lcd_on;
+ par->crt_on = default_crt_on;
+
+ var = default_var;
+#ifdef CONFIG_PPC_PMAC
+ if (machine_is(powermac)) {
+ /* Indicate sleep capability */
+ if (par->chip_gen == rage_M3) {
+ pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
+#if 0 /* Disable the early video resume hack for now as it's causing problems,
+ * among others we now rely on the PCI core restoring the config space
+ * for us, which isn't the case with that hack, and that code path causes
+ * various things to be called with interrupts off while they shouldn't.
+ * I'm leaving the code in as it can be useful for debugging purposes
+ */
+ pmac_set_early_video_resume(aty128_early_resume, par);
+#endif
+ }
+
+ /* Find default mode */
+ if (mode_option) {
+ if (!mac_find_mode(&var, info, mode_option, 8))
+ var = default_var;
+ } else {
+ if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+ default_vmode = VMODE_1024_768_60;
+
+ /* iMacs need that resolution
+ * PowerMac2,1 first r128 iMacs
+ * PowerMac2,2 summer 2000 iMacs
+ * PowerMac4,1 january 2001 iMacs "flower power"
+ */
+ if (of_machine_is_compatible("PowerMac2,1") ||
+ of_machine_is_compatible("PowerMac2,2") ||
+ of_machine_is_compatible("PowerMac4,1"))
+ default_vmode = VMODE_1024_768_75;
+
+ /* iBook SE */
+ if (of_machine_is_compatible("PowerBook2,2"))
+ default_vmode = VMODE_800_600_60;
+
+ /* PowerBook Firewire (Pismo), iBook Dual USB */
+ if (of_machine_is_compatible("PowerBook3,1") ||
+ of_machine_is_compatible("PowerBook4,1"))
+ default_vmode = VMODE_1024_768_60;
+
+ /* PowerBook Titanium */
+ if (of_machine_is_compatible("PowerBook3,2"))
+ default_vmode = VMODE_1152_768_60;
+
+ if (default_cmode > 16)
+ default_cmode = CMODE_32;
+ else if (default_cmode > 8)
+ default_cmode = CMODE_16;
+ else
+ default_cmode = CMODE_8;
+
+ if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+ var = default_var;
+ }
+ } else
+#endif /* CONFIG_PPC_PMAC */
+ {
+ if (mode_option)
+ if (fb_find_mode(&var, info, mode_option, NULL,
+ 0, &defaultmode, 8) == 0)
+ var = default_var;
+ }
+
+ var.accel_flags &= ~FB_ACCELF_TEXT;
+// var.accel_flags |= FB_ACCELF_TEXT;/* FIXME Will add accel later */
+
+ if (aty128fb_check_var(&var, info)) {
+ printk(KERN_ERR "aty128fb: Cannot set default mode.\n");
+ return 0;
+ }
+
+ /* setup the DAC the way we like it */
+ dac = aty_ld_le32(DAC_CNTL);
+ dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL);
+ dac |= DAC_MASK;
+ if (par->chip_gen == rage_M3)
+ dac |= DAC_PALETTE2_SNOOP_EN;
+ aty_st_le32(DAC_CNTL, dac);
+
+ /* turn off bus mastering, just in case */
+ aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_MASTER_DIS);
+
+ info->var = var;
+ fb_alloc_cmap(&info->cmap, 256, 0);
+
+ var.activate = FB_ACTIVATE_NOW;
+
+ aty128_init_engine(par);
+
+ par->pdev = pdev;
+ par->asleep = 0;
+ par->lock_blank = 0;
+
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ if (backlight)
+ aty128_bl_init(par);
+#endif
+
+ if (register_framebuffer(info) < 0)
+ return 0;
+
+ fb_info(info, "%s frame buffer device on %s\n",
+ info->fix.id, video_card);
+
+ return 1; /* success! */
+}
+
+#ifdef CONFIG_PCI
+/* register a card ++ajoshi */
+static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ unsigned long fb_addr, reg_addr;
+ struct aty128fb_par *par;
+ struct fb_info *info;
+ int err;
+#ifndef __sparc__
+ void __iomem *bios = NULL;
+#endif
+
+ /* Enable device in PCI config */
+ if ((err = pci_enable_device(pdev))) {
+ printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n",
+ err);
+ return -ENODEV;
+ }
+
+ fb_addr = pci_resource_start(pdev, 0);
+ if (!request_mem_region(fb_addr, pci_resource_len(pdev, 0),
+ "aty128fb FB")) {
+ printk(KERN_ERR "aty128fb: cannot reserve frame "
+ "buffer memory\n");
+ return -ENODEV;
+ }
+
+ reg_addr = pci_resource_start(pdev, 2);
+ if (!request_mem_region(reg_addr, pci_resource_len(pdev, 2),
+ "aty128fb MMIO")) {
+ printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n");
+ goto err_free_fb;
+ }
+
+ /* We have the resources. Now virtualize them */
+ info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev);
+ if (info == NULL) {
+ printk(KERN_ERR "aty128fb: can't alloc fb_info_aty128\n");
+ goto err_free_mmio;
+ }
+ par = info->par;
+
+ info->pseudo_palette = par->pseudo_palette;
+
+ /* Virtualize mmio region */
+ info->fix.mmio_start = reg_addr;
+ par->regbase = pci_ioremap_bar(pdev, 2);
+ if (!par->regbase)
+ goto err_free_info;
+
+ /* Grab memory size from the card */
+ // How does this relate to the resource length from the PCI hardware?
+ par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF;
+
+ /* Virtualize the framebuffer */
+ info->screen_base = ioremap_wc(fb_addr, par->vram_size);
+ if (!info->screen_base)
+ goto err_unmap_out;
+
+ /* Set up info->fix */
+ info->fix = aty128fb_fix;
+ info->fix.smem_start = fb_addr;
+ info->fix.smem_len = par->vram_size;
+ info->fix.mmio_start = reg_addr;
+
+ /* If we can't test scratch registers, something is seriously wrong */
+ if (!register_test(par)) {
+ printk(KERN_ERR "aty128fb: Can't write to video register!\n");
+ goto err_out;
+ }
+
+#ifndef __sparc__
+ bios = aty128_map_ROM(par, pdev);
+#ifdef CONFIG_X86
+ if (bios == NULL)
+ bios = aty128_find_mem_vbios(par);
+#endif
+ if (bios == NULL)
+ printk(KERN_INFO "aty128fb: BIOS not located, guessing timings.\n");
+ else {
+ printk(KERN_INFO "aty128fb: Rage128 BIOS located\n");
+ aty128_get_pllinfo(par, bios);
+ pci_unmap_rom(pdev, bios);
+ }
+#endif /* __sparc__ */
+
+ aty128_timings(par);
+ pci_set_drvdata(pdev, info);
+
+ if (!aty128_init(pdev, ent))
+ goto err_out;
+
+ if (mtrr)
+ par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
+ par->vram_size);
+ return 0;
+
+err_out:
+ iounmap(info->screen_base);
+err_unmap_out:
+ iounmap(par->regbase);
+err_free_info:
+ framebuffer_release(info);
+err_free_mmio:
+ release_mem_region(pci_resource_start(pdev, 2),
+ pci_resource_len(pdev, 2));
+err_free_fb:
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ return -ENODEV;
+}
+
+static void aty128_remove(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct aty128fb_par *par;
+
+ if (!info)
+ return;
+
+ par = info->par;
+
+ unregister_framebuffer(info);
+
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ aty128_bl_exit(info->bl_dev);
+#endif
+
+ arch_phys_wc_del(par->wc_cookie);
+ iounmap(par->regbase);
+ iounmap(info->screen_base);
+
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ release_mem_region(pci_resource_start(pdev, 2),
+ pci_resource_len(pdev, 2));
+ framebuffer_release(info);
+}
+#endif /* CONFIG_PCI */
+
+
+
+ /*
+ * Blank the display.
+ */
+static int aty128fb_blank(int blank, struct fb_info *fb)
+{
+ struct aty128fb_par *par = fb->par;
+ u8 state;
+
+ if (par->lock_blank || par->asleep)
+ return 0;
+
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ state = 4;
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ state = 6;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ state = 5;
+ break;
+ case FB_BLANK_POWERDOWN:
+ state = 7;
+ break;
+ case FB_BLANK_UNBLANK:
+ default:
+ state = 0;
+ break;
+ }
+ aty_st_8(CRTC_EXT_CNTL+1, state);
+
+ if (par->chip_gen == rage_M3) {
+ aty128_set_crt_enable(par, par->crt_on && !blank);
+ aty128_set_lcd_enable(par, par->lcd_on && !blank);
+ }
+
+ 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.
+ */
+static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ struct aty128fb_par *par = info->par;
+
+ if (regno > 255
+ || (par->crtc.depth == 16 && regno > 63)
+ || (par->crtc.depth == 15 && regno > 31))
+ return 1;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+
+ if (regno < 16) {
+ int i;
+ u32 *pal = info->pseudo_palette;
+
+ switch (par->crtc.depth) {
+ case 15:
+ pal[regno] = (regno << 10) | (regno << 5) | regno;
+ break;
+ case 16:
+ pal[regno] = (regno << 11) | (regno << 6) | regno;
+ break;
+ case 24:
+ pal[regno] = (regno << 16) | (regno << 8) | regno;
+ break;
+ case 32:
+ i = (regno << 8) | regno;
+ pal[regno] = (i << 16) | i;
+ break;
+ }
+ }
+
+ if (par->crtc.depth == 16 && regno > 0) {
+ /*
+ * With the 5-6-5 split of bits for RGB at 16 bits/pixel, we
+ * have 32 slots for R and B values but 64 slots for G values.
+ * Thus the R and B values go in one slot but the G value
+ * goes in a different slot, and we have to avoid disturbing
+ * the other fields in the slots we touch.
+ */
+ par->green[regno] = green;
+ if (regno < 32) {
+ par->red[regno] = red;
+ par->blue[regno] = blue;
+ aty128_st_pal(regno * 8, red, par->green[regno*2],
+ blue, par);
+ }
+ red = par->red[regno/2];
+ blue = par->blue[regno/2];
+ regno <<= 2;
+ } else if (par->crtc.bpp == 16)
+ regno <<= 3;
+ aty128_st_pal(regno, red, green, blue, par);
+
+ return 0;
+}
+
+#define ATY_MIRROR_LCD_ON 0x00000001
+#define ATY_MIRROR_CRT_ON 0x00000002
+
+/* out param: u32* backlight value: 0 to 15 */
+#define FBIO_ATY128_GET_MIRROR _IOR('@', 1, __u32)
+/* in param: u32* backlight value: 0 to 15 */
+#define FBIO_ATY128_SET_MIRROR _IOW('@', 2, __u32)
+
+static int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
+{
+ struct aty128fb_par *par = info->par;
+ u32 value;
+ int rc;
+
+ switch (cmd) {
+ case FBIO_ATY128_SET_MIRROR:
+ if (par->chip_gen != rage_M3)
+ return -EINVAL;
+ rc = get_user(value, (__u32 __user *)arg);
+ if (rc)
+ return rc;
+ par->lcd_on = (value & 0x01) != 0;
+ par->crt_on = (value & 0x02) != 0;
+ if (!par->crt_on && !par->lcd_on)
+ par->lcd_on = 1;
+ aty128_set_crt_enable(par, par->crt_on);
+ aty128_set_lcd_enable(par, par->lcd_on);
+ return 0;
+ case FBIO_ATY128_GET_MIRROR:
+ if (par->chip_gen != rage_M3)
+ return -EINVAL;
+ value = (par->crt_on << 1) | par->lcd_on;
+ return put_user(value, (__u32 __user *)arg);
+ }
+ return -EINVAL;
+}
+
+#if 0
+ /*
+ * Accelerated functions
+ */
+
+static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
+ u_int width, u_int height,
+ struct fb_info_aty128 *par)
+{
+ u32 save_dp_datatype, save_dp_cntl, dstval;
+
+ if (!width || !height)
+ return;
+
+ dstval = depth_to_dst(par->current_par.crtc.depth);
+ if (dstval == DST_24BPP) {
+ srcx *= 3;
+ dstx *= 3;
+ width *= 3;
+ } else if (dstval == -EINVAL) {
+ printk("aty128fb: invalid depth or RGBA\n");
+ return;
+ }
+
+ wait_for_fifo(2, par);
+ save_dp_datatype = aty_ld_le32(DP_DATATYPE);
+ save_dp_cntl = aty_ld_le32(DP_CNTL);
+
+ wait_for_fifo(6, par);
+ aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
+ aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
+ aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
+ aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
+
+ aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
+ aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
+
+ par->blitter_may_be_busy = 1;
+
+ wait_for_fifo(2, par);
+ aty_st_le32(DP_DATATYPE, save_dp_datatype);
+ aty_st_le32(DP_CNTL, save_dp_cntl);
+}
+
+
+ /*
+ * Text mode accelerated functions
+ */
+
+static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy,
+ int dx, int height, int width)
+{
+ sx *= fontwidth(p);
+ sy *= fontheight(p);
+ dx *= fontwidth(p);
+ dy *= fontheight(p);
+ width *= fontwidth(p);
+ height *= fontheight(p);
+
+ aty128_rectcopy(sx, sy, dx, dy, width, height,
+ (struct fb_info_aty128 *)p->fb_info);
+}
+#endif /* 0 */
+
+static void aty128_set_suspend(struct aty128fb_par *par, int suspend)
+{
+ u32 pmgt;
+ struct pci_dev *pdev = par->pdev;
+
+ if (!par->pdev->pm_cap)
+ return;
+
+ /* Set the chip into the appropriate suspend mode (we use D2,
+ * D3 would require a complete re-initialisation of the chip,
+ * including PCI config registers, clocks, AGP configuration, ...)
+ *
+ * For resume, the core will have already brought us back to D0
+ */
+ if (suspend) {
+ /* Make sure CRTC2 is reset. Remove that the day we decide to
+ * actually use CRTC2 and replace it with real code for disabling
+ * the CRTC2 output during sleep
+ */
+ aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) &
+ ~(CRTC2_EN));
+
+ /* Set the power management mode to be PCI based */
+ /* Use this magic value for now */
+ pmgt = 0x0c005407;
+ aty_st_pll(POWER_MANAGEMENT, pmgt);
+ (void)aty_ld_pll(POWER_MANAGEMENT);
+ aty_st_le32(BUS_CNTL1, 0x00000010);
+ aty_st_le32(MEM_POWER_MISC, 0x0c830000);
+ msleep(100);
+
+ /* Switch PCI power management to D2 */
+ pci_set_power_state(pdev, PCI_D2);
+ }
+}
+
+static int aty128_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct aty128fb_par *par = info->par;
+
+ /* Because we may change PCI D state ourselves, we need to
+ * first save the config space content so the core can
+ * restore it properly on resume.
+ */
+ pci_save_state(pdev);
+
+ /* We don't do anything but D2, for now we return 0, but
+ * we may want to change that. How do we know if the BIOS
+ * can properly take care of D3 ? Also, with swsusp, we
+ * know we'll be rebooted, ...
+ */
+#ifndef 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
+ */
+ return 0;
+#endif /* CONFIG_PPC_PMAC */
+
+ if (state.event == pdev->dev.power.power_state.event)
+ return 0;
+
+ printk(KERN_DEBUG "aty128fb: suspending...\n");
+
+ console_lock();
+
+ fb_set_suspend(info, 1);
+
+ /* Make sure engine is reset */
+ wait_for_idle(par);
+ aty128_reset_engine(par);
+ wait_for_idle(par);
+
+ /* Blank display and LCD */
+ aty128fb_blank(FB_BLANK_POWERDOWN, info);
+
+ /* Sleep */
+ par->asleep = 1;
+ par->lock_blank = 1;
+
+#ifdef CONFIG_PPC_PMAC
+ /* On powermac, we have hooks to properly suspend/resume AGP now,
+ * use them here. We'll ultimately need some generic support here,
+ * but the generic code isn't quite ready for that yet
+ */
+ pmac_suspend_agp_for_card(pdev);
+#endif /* CONFIG_PPC_PMAC */
+
+ /* We need a way to make sure the fbdev layer will _not_ touch the
+ * framebuffer before we put the chip to suspend state. On 2.4, I
+ * used dummy fb ops, 2.5 need proper support for this at the
+ * fbdev level
+ */
+ if (state.event != PM_EVENT_ON)
+ aty128_set_suspend(par, 1);
+
+ console_unlock();
+
+ pdev->dev.power.power_state = state;
+
+ return 0;
+}
+
+static int aty128_do_resume(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct aty128fb_par *par = info->par;
+
+ if (pdev->dev.power.power_state.event == PM_EVENT_ON)
+ return 0;
+
+ /* PCI state will have been restored by the core, so
+ * we should be in D0 now with our config space fully
+ * restored
+ */
+
+ /* Wakeup chip */
+ aty128_set_suspend(par, 0);
+ par->asleep = 0;
+
+ /* Restore display & engine */
+ aty128_reset_engine(par);
+ wait_for_idle(par);
+ aty128fb_set_par(info);
+ fb_pan_display(info, &info->var);
+ fb_set_cmap(&info->cmap, info);
+
+ /* Refresh */
+ fb_set_suspend(info, 0);
+
+ /* Unblank */
+ par->lock_blank = 0;
+ aty128fb_blank(0, info);
+
+#ifdef CONFIG_PPC_PMAC
+ /* On powermac, we have hooks to properly suspend/resume AGP now,
+ * use them here. We'll ultimately need some generic support here,
+ * but the generic code isn't quite ready for that yet
+ */
+ pmac_resume_agp_for_card(pdev);
+#endif /* CONFIG_PPC_PMAC */
+
+ pdev->dev.power.power_state = PMSG_ON;
+
+ printk(KERN_DEBUG "aty128fb: resumed !\n");
+
+ return 0;
+}
+
+static int aty128_pci_resume(struct pci_dev *pdev)
+{
+ int rc;
+
+ console_lock();
+ rc = aty128_do_resume(pdev);
+ console_unlock();
+
+ return rc;
+}
+
+
+static int aty128fb_init(void)
+{
+#ifndef MODULE
+ char *option = NULL;
+
+ if (fb_get_options("aty128fb", &option))
+ return -ENODEV;
+ aty128fb_setup(option);
+#endif
+
+ return pci_register_driver(&aty128fb_driver);
+}
+
+static void __exit aty128fb_exit(void)
+{
+ pci_unregister_driver(&aty128fb_driver);
+}
+
+module_init(aty128fb_init);
+
+module_exit(aty128fb_exit);
+
+MODULE_AUTHOR("(c)1999-2003 Brad Douglas <brad@neruo.com>");
+MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
+MODULE_LICENSE("GPL");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
+module_param_named(nomtrr, mtrr, invbool, 0);
+MODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)");
diff --git a/drivers/video/fbdev/aty/atyfb.h b/drivers/video/fbdev/aty/atyfb.h
new file mode 100644
index 000000000..d09bab3bf
--- /dev/null
+++ b/drivers/video/fbdev/aty/atyfb.h
@@ -0,0 +1,368 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ATI Frame Buffer Device Driver Core Definitions
+ */
+
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+ /*
+ * Elements of the hardware specific atyfb_par structure
+ */
+
+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;
+ int ecp_max;
+};
+
+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;
+};
+
+struct pll_18818 {
+ u32 program_bits;
+ u32 locationAddr;
+ u32 period_in_ps;
+ u32 post_divider;
+};
+
+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;
+ 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;
+ struct pll_18818 ics2595;
+};
+
+ /*
+ * The hardware parameters for each card
+ */
+
+struct atyfb_par {
+ u32 pseudo_palette[16];
+ struct { u8 red, green, blue; } palette[256];
+ const struct aty_dac_ops *dac_ops;
+ const struct aty_pll_ops *pll_ops;
+ void __iomem *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;
+ u16 pci_id;
+ u32 accel_flags;
+ int blitter_may_be_busy;
+ int asleep;
+ int lock_blank;
+ unsigned long res_start;
+ unsigned long res_size;
+ struct pci_dev *pdev;
+#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
+ 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;
+ int wc_cookie;
+ u32 mem_cntl;
+ struct crtc saved_crtc;
+ union aty_pll saved_pll;
+};
+
+ /*
+ * ATI Mach64 features
+ */
+
+#define M64_HAS(feature) ((par)->features & (M64F_##feature))
+
+#define M64F_RESET_3D 0x00000001
+#define M64F_MAGIC_FIFO 0x00000002
+#define M64F_GTB_DSP 0x00000004
+#define M64F_FIFO_32 0x00000008
+#define M64F_SDRAM_MAGIC_PLL 0x00000010
+#define M64F_MAGIC_POSTDIV 0x00000020
+#define M64F_INTEGRATED 0x00000040
+#define M64F_CT_BUS 0x00000080
+#define M64F_VT_BUS 0x00000100
+#define M64F_MOBIL_BUS 0x00000200
+#define M64F_GX 0x00000400
+#define M64F_CT 0x00000800
+#define M64F_VT 0x00001000
+#define M64F_GT 0x00002000
+#define M64F_MAGIC_VRAM_SIZE 0x00004000
+#define M64F_G3_PB_1_1 0x00008000
+#define M64F_G3_PB_1024x768 0x00010000
+#define M64F_EXTRA_BRIGHT 0x00020000
+#define M64F_LT_LCD_REGS 0x00040000
+#define M64F_XL_DLL 0x00080000
+#define M64F_MFB_FORCE_4 0x00100000
+#define M64F_HW_TRIPLE 0x00200000
+#define M64F_XL_MEM 0x00400000
+ /*
+ * Register access
+ */
+
+static inline u32 aty_ld_le32(int regindex, const struct atyfb_par *par)
+{
+ /* Hack for bloc 1, should be cleanly optimized by compiler */
+ if (regindex >= 0x400)
+ regindex -= 0x800;
+
+#ifdef CONFIG_ATARI
+ return in_le32(par->ati_regbase + regindex);
+#else
+ return readl(par->ati_regbase + regindex);
+#endif
+}
+
+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)
+ regindex -= 0x800;
+
+#ifdef CONFIG_ATARI
+ out_le32(par->ati_regbase + regindex, val);
+#else
+ writel(val, par->ati_regbase + regindex);
+#endif
+}
+
+static inline void aty_st_le16(int regindex, u16 val,
+ const struct atyfb_par *par)
+{
+ /* Hack for bloc 1, should be cleanly optimized by compiler */
+ if (regindex >= 0x400)
+ regindex -= 0x800;
+#ifdef CONFIG_ATARI
+ out_le16(par->ati_regbase + regindex, val);
+#else
+ writel(val, par->ati_regbase + regindex);
+#endif
+}
+
+static inline u8 aty_ld_8(int regindex, const struct atyfb_par *par)
+{
+ /* Hack for bloc 1, should be cleanly optimized by compiler */
+ if (regindex >= 0x400)
+ regindex -= 0x800;
+#ifdef CONFIG_ATARI
+ return in_8(par->ati_regbase + regindex);
+#else
+ return readb(par->ati_regbase + regindex);
+#endif
+}
+
+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)
+ regindex -= 0x800;
+
+#ifdef CONFIG_ATARI
+ out_8(par->ati_regbase + regindex, val);
+#else
+ writeb(val, par->ati_regbase + regindex);
+#endif
+}
+
+#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \
+defined (CONFIG_FB_ATY_GENERIC_LCD) || defined (CONFIG_FB_ATY_BACKLIGHT)
+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
+ */
+
+struct aty_dac_ops {
+ int (*set_dac) (const struct fb_info * info,
+ 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 */
+
+
+ /*
+ * Clock operations
+ */
+
+struct aty_pll_ops {
+ 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);
+ void (*resume_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 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);
+
+extern const u8 aty_postdividers[8];
+
+
+ /*
+ * Hardware cursor support
+ */
+
+extern int aty_init_cursor(struct fb_info *info);
+
+ /*
+ * Hardware acceleration
+ */
+
+static inline void wait_for_fifo(u16 entries, const struct atyfb_par *par)
+{
+ while ((aty_ld_le32(FIFO_STAT, par) & 0xffff) >
+ ((u32) (0x8000 >> entries)));
+}
+
+static inline void wait_for_idle(struct atyfb_par *par)
+{
+ wait_for_fifo(16, par);
+ while ((aty_ld_le32(GUI_STAT, par) & 1) != 0);
+ par->blitter_may_be_busy = 0;
+}
+
+extern void aty_reset_engine(const struct atyfb_par *par);
+extern void aty_init_engine(struct atyfb_par *par, struct fb_info *info);
+
+void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
+void atyfb_imageblit(struct fb_info *info, const struct fb_image *image);
+
diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c
new file mode 100644
index 000000000..05111e90f
--- /dev/null
+++ b/drivers/video/fbdev/aty/atyfb_base.c
@@ -0,0 +1,4012 @@
+/*
+ * ATI Frame Buffer Device Driver Core
+ *
+ * Copyright (C) 2004 Alex Kern <alex.kern@gmx.de>
+ * Copyright (C) 1997-2001 Geert Uytterhoeven
+ * Copyright (C) 1998 Bernd Harries
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ *
+ * This driver supports the following ATI graphics chips:
+ * - ATI Mach64
+ *
+ * To do: add support for
+ * - ATI Rage128 (from aty128fb.c)
+ * - ATI Radeon (from radeonfb.c)
+ *
+ * This driver is partly based on the PowerMac console driver:
+ *
+ * Copyright (C) 1996 Paul Mackerras
+ *
+ * and on the PowerMac ATI/mach64 display driver:
+ *
+ * Copyright (C) 1997 Michael AK Tesch
+ *
+ * with work by Jon Howell
+ * Harry AC Eaton
+ * Anthony Tong <atong@uiuc.edu>
+ *
+ * Generic LCD support written by Daniel Mantione, ported from 2.4.20 by Alex Kern
+ * Many Thanks to Ville Syrjälä for patches and fixing nasting 16 bit color bug.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * Many thanks to Nitya from ATI devrel for support and patience !
+ */
+
+/******************************************************************************
+
+ TODO:
+
+ - cursor support on all cards and all ramdacs.
+ - cursor parameters controlable via ioctl()s.
+ - guess PLL and MCLK based on the original PLL register values initialized
+ by Open Firmware (if they are initialized). BIOS is done
+
+ (Anyone with Mac to help with this?)
+
+******************************************************************************/
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/compiler.h>
+#include <linux/console.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/backlight.h>
+#include <linux/reboot.h>
+#include <linux/dmi.h>
+
+#include <asm/io.h>
+#include <linux/uaccess.h>
+
+#include <video/mach64.h>
+#include "atyfb.h"
+#include "ati_ids.h"
+
+#ifdef __powerpc__
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include "../macmodes.h"
+#endif
+#ifdef __sparc__
+#include <asm/fbio.h>
+#include <asm/oplib.h>
+#include <asm/prom.h>
+#endif
+
+#ifdef CONFIG_ADB_PMU
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#endif
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+/*
+ * Debug flags.
+ */
+#undef DEBUG
+/*#define DEBUG*/
+
+/* Make sure n * PAGE_SIZE is protected at end of Aperture for GUI-regs */
+/* - must be large enough to catch all GUI-Regs */
+/* - must be aligned to a PAGE boundary */
+#define GUI_RESERVE (1 * PAGE_SIZE)
+
+/* FIXME: remove the FAIL definition */
+#define FAIL(msg) do { \
+ if (!(var->activate & FB_ACTIVATE_TEST)) \
+ printk(KERN_CRIT "atyfb: " msg "\n"); \
+ return -EINVAL; \
+} while (0)
+#define FAIL_MAX(msg, x, _max_) do { \
+ if (x > _max_) { \
+ if (!(var->activate & FB_ACTIVATE_TEST)) \
+ printk(KERN_CRIT "atyfb: " msg " %x(%x)\n", x, _max_); \
+ return -EINVAL; \
+ } \
+} while (0)
+#ifdef DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "atyfb: " fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define PRINTKI(fmt, args...) printk(KERN_INFO "atyfb: " fmt, ## args)
+#define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args)
+
+#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \
+defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_FB_ATY_BACKLIGHT)
+static const u32 lt_lcd_regs[] = {
+ CNFG_PANEL_LG,
+ LCD_GEN_CNTL_LG,
+ DSTN_CONTROL_LG,
+ HFB_PITCH_ADDR_LG,
+ HORZ_STRETCHING_LG,
+ VERT_STRETCHING_LG,
+ 0, /* EXT_VERT_STRETCH */
+ LT_GIO_LG,
+ POWER_MANAGEMENT_LG
+};
+
+void aty_st_lcd(int index, u32 val, const struct atyfb_par *par)
+{
+ if (M64_HAS(LT_LCD_REGS)) {
+ aty_st_le32(lt_lcd_regs[index], val, par);
+ } else {
+ unsigned long temp;
+
+ /* write addr byte */
+ temp = aty_ld_le32(LCD_INDEX, par);
+ aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
+ /* write the register value */
+ aty_st_le32(LCD_DATA, val, par);
+ }
+}
+
+u32 aty_ld_lcd(int index, const struct atyfb_par *par)
+{
+ if (M64_HAS(LT_LCD_REGS)) {
+ return aty_ld_le32(lt_lcd_regs[index], par);
+ } else {
+ unsigned long temp;
+
+ /* write addr byte */
+ temp = aty_ld_le32(LCD_INDEX, par);
+ aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
+ /* read the register value */
+ return aty_ld_le32(LCD_DATA, par);
+ }
+}
+#endif /* defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) */
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+/*
+ * ATIReduceRatio --
+ *
+ * Reduce a fraction by factoring out the largest common divider of the
+ * fraction's numerator and denominator.
+ */
+static void ATIReduceRatio(int *Numerator, int *Denominator)
+{
+ int Multiplier, Divider, Remainder;
+
+ Multiplier = *Numerator;
+ Divider = *Denominator;
+
+ while ((Remainder = Multiplier % Divider)) {
+ Multiplier = Divider;
+ Divider = Remainder;
+ }
+
+ *Numerator /= Divider;
+ *Denominator /= Divider;
+}
+#endif
+/*
+ * The Hardware parameters for each card
+ */
+
+struct pci_mmap_map {
+ unsigned long voff;
+ unsigned long poff;
+ unsigned long size;
+ unsigned long prot_flag;
+ unsigned long prot_mask;
+};
+
+static const struct fb_fix_screeninfo atyfb_fix = {
+ .id = "ATY Mach64",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_PSEUDOCOLOR,
+ .xpanstep = 8,
+ .ypanstep = 1,
+};
+
+/*
+ * Frame buffer device API
+ */
+
+static int atyfb_open(struct fb_info *info, int user);
+static int atyfb_release(struct fb_info *info, int user);
+static int atyfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int atyfb_set_par(struct fb_info *info);
+static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info);
+static int atyfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int atyfb_blank(int blank, struct fb_info *info);
+static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg);
+#ifdef __sparc__
+static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
+#endif
+static int atyfb_sync(struct fb_info *info);
+
+/*
+ * Internal routines
+ */
+
+static int aty_init(struct fb_info *info);
+
+static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc);
+
+static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc);
+static int aty_var_to_crtc(const struct fb_info *info,
+ const struct fb_var_screeninfo *var,
+ struct crtc *crtc);
+static int aty_crtc_to_var(const struct crtc *crtc,
+ struct fb_var_screeninfo *var);
+static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info);
+#ifdef CONFIG_PPC
+static int read_aty_sense(const struct atyfb_par *par);
+#endif
+
+static DEFINE_MUTEX(reboot_lock);
+static struct fb_info *reboot_info;
+
+/*
+ * Interface used by the world
+ */
+
+static struct fb_var_screeninfo default_var = {
+ /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
+ 640, 480, 640, 480, 0, 0, 8, 0,
+ {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+ 0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
+ 0, FB_VMODE_NONINTERLACED
+};
+
+static const struct fb_videomode defmode = {
+ /* 640x480 @ 60 Hz, 31.5 kHz hsync */
+ NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
+ 0, FB_VMODE_NONINTERLACED
+};
+
+static struct fb_ops atyfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = atyfb_open,
+ .fb_release = atyfb_release,
+ .fb_check_var = atyfb_check_var,
+ .fb_set_par = atyfb_set_par,
+ .fb_setcolreg = atyfb_setcolreg,
+ .fb_pan_display = atyfb_pan_display,
+ .fb_blank = atyfb_blank,
+ .fb_ioctl = atyfb_ioctl,
+ .fb_fillrect = atyfb_fillrect,
+ .fb_copyarea = atyfb_copyarea,
+ .fb_imageblit = atyfb_imageblit,
+#ifdef __sparc__
+ .fb_mmap = atyfb_mmap,
+#endif
+ .fb_sync = atyfb_sync,
+};
+
+static bool noaccel;
+static bool nomtrr;
+static int vram;
+static int pll;
+static int mclk;
+static int xclk;
+static int comp_sync = -1;
+static char *mode;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int backlight = 1;
+#else
+static int backlight = 0;
+#endif
+
+#ifdef CONFIG_PPC
+static int default_vmode = VMODE_CHOOSE;
+static int default_cmode = CMODE_CHOOSE;
+
+module_param_named(vmode, default_vmode, int, 0);
+MODULE_PARM_DESC(vmode, "int: video mode for mac");
+module_param_named(cmode, default_cmode, int, 0);
+MODULE_PARM_DESC(cmode, "int: color mode for mac");
+#endif
+
+#ifdef CONFIG_ATARI
+static unsigned int mach64_count = 0;
+static unsigned long phys_vmembase[FB_MAX] = { 0, };
+static unsigned long phys_size[FB_MAX] = { 0, };
+static unsigned long phys_guiregbase[FB_MAX] = { 0, };
+#endif
+
+/* top -> down is an evolution of mach64 chipset, any corrections? */
+#define ATI_CHIP_88800GX (M64F_GX)
+#define ATI_CHIP_88800CX (M64F_GX)
+
+#define ATI_CHIP_264CT (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
+#define ATI_CHIP_264ET (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
+
+#define ATI_CHIP_264VT (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_MAGIC_FIFO)
+#define ATI_CHIP_264GT (M64F_GT | M64F_INTEGRATED | M64F_MAGIC_FIFO | M64F_EXTRA_BRIGHT)
+
+#define ATI_CHIP_264VTB (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP)
+#define ATI_CHIP_264VT3 (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL)
+#define ATI_CHIP_264VT4 (M64F_VT | M64F_INTEGRATED | M64F_GTB_DSP)
+
+/* FIXME what is this chip? */
+#define ATI_CHIP_264LT (M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP)
+
+/* make sets shorter */
+#define ATI_MODERN_SET (M64F_GT | M64F_INTEGRATED | M64F_GTB_DSP | M64F_EXTRA_BRIGHT)
+
+#define ATI_CHIP_264GTB (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
+/*#define ATI_CHIP_264GTDVD ?*/
+#define ATI_CHIP_264LTG (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
+
+#define ATI_CHIP_264GT2C (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE)
+#define ATI_CHIP_264GTPRO (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
+#define ATI_CHIP_264LTPRO (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
+
+#define ATI_CHIP_264XL (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM)
+#define ATI_CHIP_MOBILITY (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM | M64F_MOBIL_BUS)
+
+static struct {
+ u16 pci_id;
+ const char *name;
+ int pll, mclk, xclk, ecp_max;
+ u32 features;
+} aty_chips[] = {
+#ifdef CONFIG_FB_ATY_GX
+ /* Mach64 GX */
+ { PCI_CHIP_MACH64GX, "ATI888GX00 (Mach64 GX)", 135, 50, 50, 0, ATI_CHIP_88800GX },
+ { PCI_CHIP_MACH64CX, "ATI888CX00 (Mach64 CX)", 135, 50, 50, 0, ATI_CHIP_88800CX },
+#endif /* CONFIG_FB_ATY_GX */
+
+#ifdef CONFIG_FB_ATY_CT
+ { PCI_CHIP_MACH64CT, "ATI264CT (Mach64 CT)", 135, 60, 60, 0, ATI_CHIP_264CT },
+ { PCI_CHIP_MACH64ET, "ATI264ET (Mach64 ET)", 135, 60, 60, 0, ATI_CHIP_264ET },
+
+ /* FIXME what is this chip? */
+ { PCI_CHIP_MACH64LT, "ATI264LT (Mach64 LT)", 135, 63, 63, 0, ATI_CHIP_264LT },
+
+ { PCI_CHIP_MACH64VT, "ATI264VT (Mach64 VT)", 170, 67, 67, 80, ATI_CHIP_264VT },
+ { PCI_CHIP_MACH64GT, "3D RAGE (Mach64 GT)", 135, 63, 63, 80, ATI_CHIP_264GT },
+
+ { PCI_CHIP_MACH64VU, "ATI264VT3 (Mach64 VU)", 200, 67, 67, 80, ATI_CHIP_264VT3 },
+ { PCI_CHIP_MACH64GU, "3D RAGE II+ (Mach64 GU)", 200, 67, 67, 100, ATI_CHIP_264GTB },
+
+ { PCI_CHIP_MACH64LG, "3D RAGE LT (Mach64 LG)", 230, 63, 63, 100, ATI_CHIP_264LTG | M64F_LT_LCD_REGS | M64F_G3_PB_1024x768 },
+
+ { PCI_CHIP_MACH64VV, "ATI264VT4 (Mach64 VV)", 230, 83, 83, 100, ATI_CHIP_264VT4 },
+
+ { PCI_CHIP_MACH64GV, "3D RAGE IIC (Mach64 GV, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
+ { PCI_CHIP_MACH64GW, "3D RAGE IIC (Mach64 GW, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
+ { PCI_CHIP_MACH64GY, "3D RAGE IIC (Mach64 GY, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
+ { PCI_CHIP_MACH64GZ, "3D RAGE IIC (Mach64 GZ, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
+
+ { PCI_CHIP_MACH64GB, "3D RAGE PRO (Mach64 GB, BGA, AGP)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
+ { PCI_CHIP_MACH64GD, "3D RAGE PRO (Mach64 GD, BGA, AGP 1x)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
+ { PCI_CHIP_MACH64GI, "3D RAGE PRO (Mach64 GI, BGA, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO | M64F_MAGIC_VRAM_SIZE },
+ { PCI_CHIP_MACH64GP, "3D RAGE PRO (Mach64 GP, PQFP, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
+ { PCI_CHIP_MACH64GQ, "3D RAGE PRO (Mach64 GQ, PQFP, PCI, limited 3D)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
+
+ { PCI_CHIP_MACH64LB, "3D RAGE LT PRO (Mach64 LB, AGP)", 236, 75, 100, 135, ATI_CHIP_264LTPRO },
+ { PCI_CHIP_MACH64LD, "3D RAGE LT PRO (Mach64 LD, AGP)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
+ { PCI_CHIP_MACH64LI, "3D RAGE LT PRO (Mach64 LI, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1_1 | M64F_G3_PB_1024x768 },
+ { PCI_CHIP_MACH64LP, "3D RAGE LT PRO (Mach64 LP, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1024x768 },
+ { PCI_CHIP_MACH64LQ, "3D RAGE LT PRO (Mach64 LQ, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
+
+ { PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
+ { PCI_CHIP_MACH64GN, "3D RAGE XC (Mach64 GN, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
+ { PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
+ { PCI_CHIP_MACH64GL, "3D RAGE XC (Mach64 GL, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
+ { PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL | M64F_SDRAM_MAGIC_PLL },
+ { PCI_CHIP_MACH64GS, "3D RAGE XC (Mach64 GS, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL },
+
+ { PCI_CHIP_MACH64LM, "3D RAGE Mobility P/M (Mach64 LM, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
+ { PCI_CHIP_MACH64LN, "3D RAGE Mobility L (Mach64 LN, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
+ { PCI_CHIP_MACH64LR, "3D RAGE Mobility P/M (Mach64 LR, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
+ { PCI_CHIP_MACH64LS, "3D RAGE Mobility L (Mach64 LS, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
+#endif /* CONFIG_FB_ATY_CT */
+};
+
+/*
+ * Last page of 8 MB (4 MB on ISA) aperture is MMIO,
+ * unless the auxiliary register aperture is used.
+ */
+static void aty_fudge_framebuffer_len(struct fb_info *info)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ if (!par->aux_start &&
+ (info->fix.smem_len == 0x800000 ||
+ (par->bus_type == ISA && info->fix.smem_len == 0x400000)))
+ info->fix.smem_len -= GUI_RESERVE;
+}
+
+static int correct_chipset(struct atyfb_par *par)
+{
+ u8 rev;
+ u16 type;
+ u32 chip_id;
+ const char *name;
+ int i;
+
+ for (i = (int)ARRAY_SIZE(aty_chips) - 1; i >= 0; i--)
+ if (par->pci_id == aty_chips[i].pci_id)
+ break;
+
+ if (i < 0)
+ return -ENODEV;
+
+ name = aty_chips[i].name;
+ par->pll_limits.pll_max = aty_chips[i].pll;
+ par->pll_limits.mclk = aty_chips[i].mclk;
+ par->pll_limits.xclk = aty_chips[i].xclk;
+ par->pll_limits.ecp_max = aty_chips[i].ecp_max;
+ par->features = aty_chips[i].features;
+
+ chip_id = aty_ld_le32(CNFG_CHIP_ID, par);
+ type = chip_id & CFG_CHIP_TYPE;
+ rev = (chip_id & CFG_CHIP_REV) >> 24;
+
+ switch (par->pci_id) {
+#ifdef CONFIG_FB_ATY_GX
+ case PCI_CHIP_MACH64GX:
+ if (type != 0x00d7)
+ return -ENODEV;
+ break;
+ case PCI_CHIP_MACH64CX:
+ if (type != 0x0057)
+ return -ENODEV;
+ break;
+#endif
+#ifdef CONFIG_FB_ATY_CT
+ case PCI_CHIP_MACH64VT:
+ switch (rev & 0x07) {
+ case 0x00:
+ switch (rev & 0xc0) {
+ case 0x00:
+ name = "ATI264VT (A3) (Mach64 VT)";
+ par->pll_limits.pll_max = 170;
+ par->pll_limits.mclk = 67;
+ par->pll_limits.xclk = 67;
+ par->pll_limits.ecp_max = 80;
+ par->features = ATI_CHIP_264VT;
+ break;
+ case 0x40:
+ name = "ATI264VT2 (A4) (Mach64 VT)";
+ par->pll_limits.pll_max = 200;
+ par->pll_limits.mclk = 67;
+ par->pll_limits.xclk = 67;
+ par->pll_limits.ecp_max = 80;
+ par->features = ATI_CHIP_264VT | M64F_MAGIC_POSTDIV;
+ break;
+ }
+ break;
+ case 0x01:
+ name = "ATI264VT3 (B1) (Mach64 VT)";
+ par->pll_limits.pll_max = 200;
+ par->pll_limits.mclk = 67;
+ par->pll_limits.xclk = 67;
+ par->pll_limits.ecp_max = 80;
+ par->features = ATI_CHIP_264VTB;
+ break;
+ case 0x02:
+ name = "ATI264VT3 (B2) (Mach64 VT)";
+ par->pll_limits.pll_max = 200;
+ par->pll_limits.mclk = 67;
+ par->pll_limits.xclk = 67;
+ par->pll_limits.ecp_max = 80;
+ par->features = ATI_CHIP_264VT3;
+ break;
+ }
+ break;
+ case PCI_CHIP_MACH64GT:
+ switch (rev & 0x07) {
+ case 0x01:
+ name = "3D RAGE II (Mach64 GT)";
+ par->pll_limits.pll_max = 170;
+ par->pll_limits.mclk = 67;
+ par->pll_limits.xclk = 67;
+ par->pll_limits.ecp_max = 80;
+ par->features = ATI_CHIP_264GTB;
+ break;
+ case 0x02:
+ name = "3D RAGE II+ (Mach64 GT)";
+ par->pll_limits.pll_max = 200;
+ par->pll_limits.mclk = 67;
+ par->pll_limits.xclk = 67;
+ par->pll_limits.ecp_max = 100;
+ par->features = ATI_CHIP_264GTB;
+ break;
+ }
+ break;
+#endif
+ }
+
+ PRINTKI("%s [0x%04x rev 0x%02x]\n", name, type, rev);
+ return 0;
+}
+
+static char ram_dram[] __maybe_unused = "DRAM";
+static char ram_resv[] __maybe_unused = "RESV";
+#ifdef CONFIG_FB_ATY_GX
+static char ram_vram[] = "VRAM";
+#endif /* CONFIG_FB_ATY_GX */
+#ifdef CONFIG_FB_ATY_CT
+static char ram_edo[] = "EDO";
+static char ram_sdram[] = "SDRAM (1:1)";
+static char ram_sgram[] = "SGRAM (1:1)";
+static char ram_sdram32[] = "SDRAM (2:1) (32-bit)";
+static char ram_wram[] = "WRAM";
+static char ram_off[] = "OFF";
+#endif /* CONFIG_FB_ATY_CT */
+
+
+#ifdef CONFIG_FB_ATY_GX
+static char *aty_gx_ram[8] = {
+ ram_dram, ram_vram, ram_vram, ram_dram,
+ ram_dram, ram_vram, ram_vram, ram_resv
+};
+#endif /* CONFIG_FB_ATY_GX */
+
+#ifdef CONFIG_FB_ATY_CT
+static char *aty_ct_ram[8] = {
+ ram_off, ram_dram, ram_edo, ram_edo,
+ ram_sdram, ram_sgram, ram_wram, ram_resv
+};
+static char *aty_xl_ram[8] = {
+ ram_off, ram_dram, ram_edo, ram_edo,
+ ram_sdram, ram_sgram, ram_sdram32, ram_resv
+};
+#endif /* CONFIG_FB_ATY_CT */
+
+static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var,
+ struct atyfb_par *par)
+{
+ u32 pixclock = var->pixclock;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ u32 lcd_on_off;
+ par->pll.ct.xres = 0;
+ if (par->lcd_table != 0) {
+ lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par);
+ if (lcd_on_off & LCD_ON) {
+ par->pll.ct.xres = var->xres;
+ pixclock = par->lcd_pixclock;
+ }
+ }
+#endif
+ return pixclock;
+}
+
+#if defined(CONFIG_PPC)
+
+/*
+ * Apple monitor sense
+ */
+
+static int read_aty_sense(const struct atyfb_par *par)
+{
+ int sense, i;
+
+ aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */
+ __delay(200);
+ aty_st_le32(GP_IO, 0, par); /* turn off outputs */
+ __delay(2000);
+ i = aty_ld_le32(GP_IO, par); /* get primary sense value */
+ sense = ((i & 0x3000) >> 3) | (i & 0x100);
+
+ /* drive each sense line low in turn and collect the other 2 */
+ aty_st_le32(GP_IO, 0x20000000, par); /* drive A low */
+ __delay(2000);
+ i = aty_ld_le32(GP_IO, par);
+ sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
+ aty_st_le32(GP_IO, 0x20002000, par); /* drive A high again */
+ __delay(200);
+
+ aty_st_le32(GP_IO, 0x10000000, par); /* drive B low */
+ __delay(2000);
+ i = aty_ld_le32(GP_IO, par);
+ sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
+ aty_st_le32(GP_IO, 0x10001000, par); /* drive B high again */
+ __delay(200);
+
+ aty_st_le32(GP_IO, 0x01000000, par); /* drive C low */
+ __delay(2000);
+ sense |= (aty_ld_le32(GP_IO, par) & 0x3000) >> 12;
+ aty_st_le32(GP_IO, 0, par); /* turn off outputs */
+ return sense;
+}
+
+#endif /* defined(CONFIG_PPC) */
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * CRTC programming
+ */
+
+static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc)
+{
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table != 0) {
+ if (!M64_HAS(LT_LCD_REGS)) {
+ crtc->lcd_index = aty_ld_le32(LCD_INDEX, par);
+ aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
+ }
+ crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par);
+ crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par);
+
+
+ /* switch to non shadow registers */
+ aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
+ ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
+
+ /* save stretching */
+ crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
+ crtc->vert_stretching = aty_ld_lcd(VERT_STRETCHING, par);
+ if (!M64_HAS(LT_LCD_REGS))
+ crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par);
+ }
+#endif
+ crtc->h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
+ crtc->h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
+ crtc->v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
+ crtc->v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
+ crtc->vline_crnt_vline = aty_ld_le32(CRTC_VLINE_CRNT_VLINE, par);
+ crtc->off_pitch = aty_ld_le32(CRTC_OFF_PITCH, par);
+ crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table != 0) {
+ /* switch to shadow registers */
+ aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
+ SHADOW_EN | SHADOW_RW_EN, par);
+
+ crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
+ crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
+ crtc->shadow_v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
+ crtc->shadow_v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
+
+ aty_st_le32(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
+ }
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+}
+
+static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc)
+{
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table != 0) {
+ /* stop CRTC */
+ aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl &
+ ~(CRTC_EXT_DISP_EN | CRTC_EN), par);
+
+ /* update non-shadow registers first */
+ aty_st_lcd(CNFG_PANEL, crtc->lcd_config_panel, par);
+ aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
+ ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
+
+ /* temporarily disable stretching */
+ aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching &
+ ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par);
+ aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching &
+ ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 |
+ VERT_STRETCH_USE0 | VERT_STRETCH_EN), par);
+ }
+#endif
+ /* turn off CRT */
+ aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~CRTC_EN, par);
+
+ DPRINTK("setting up CRTC\n");
+ DPRINTK("set primary CRT to %ix%i %c%c composite %c\n",
+ ((((crtc->h_tot_disp >> 16) & 0xff) + 1) << 3),
+ (((crtc->v_tot_disp >> 16) & 0x7ff) + 1),
+ (crtc->h_sync_strt_wid & 0x200000) ? 'N' : 'P',
+ (crtc->v_sync_strt_wid & 0x200000) ? 'N' : 'P',
+ (crtc->gen_cntl & CRTC_CSYNC_EN) ? 'P' : 'N');
+
+ DPRINTK("CRTC_H_TOTAL_DISP: %x\n", crtc->h_tot_disp);
+ DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n", crtc->h_sync_strt_wid);
+ DPRINTK("CRTC_V_TOTAL_DISP: %x\n", crtc->v_tot_disp);
+ DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n", crtc->v_sync_strt_wid);
+ DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch);
+ DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline);
+ DPRINTK("CRTC_GEN_CNTL: %x\n", crtc->gen_cntl);
+
+ aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par);
+ aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par);
+ aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_tot_disp, par);
+ aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid, par);
+ aty_st_le32(CRTC_OFF_PITCH, crtc->off_pitch, par);
+ aty_st_le32(CRTC_VLINE_CRNT_VLINE, crtc->vline_crnt_vline, par);
+
+ aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl, par);
+#if 0
+ FIXME
+ if (par->accel_flags & FB_ACCELF_TEXT)
+ aty_init_engine(par, info);
+#endif
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ /* after setting the CRTC registers we should set the LCD registers. */
+ if (par->lcd_table != 0) {
+ /* switch to shadow registers */
+ aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
+ SHADOW_EN | SHADOW_RW_EN, par);
+
+ DPRINTK("set shadow CRT to %ix%i %c%c\n",
+ ((((crtc->shadow_h_tot_disp >> 16) & 0xff) + 1) << 3),
+ (((crtc->shadow_v_tot_disp >> 16) & 0x7ff) + 1),
+ (crtc->shadow_h_sync_strt_wid & 0x200000) ? 'N' : 'P',
+ (crtc->shadow_v_sync_strt_wid & 0x200000) ? 'N' : 'P');
+
+ DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n",
+ crtc->shadow_h_tot_disp);
+ DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n",
+ crtc->shadow_h_sync_strt_wid);
+ DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n",
+ crtc->shadow_v_tot_disp);
+ DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n",
+ crtc->shadow_v_sync_strt_wid);
+
+ aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par);
+ aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par);
+ aty_st_le32(CRTC_V_TOTAL_DISP, crtc->shadow_v_tot_disp, par);
+ aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->shadow_v_sync_strt_wid, par);
+
+ /* restore CRTC selection & shadow state and enable stretching */
+ DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl);
+ DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching);
+ DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching);
+ if (!M64_HAS(LT_LCD_REGS))
+ DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch);
+
+ aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
+ aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par);
+ aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par);
+ if (!M64_HAS(LT_LCD_REGS)) {
+ aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par);
+ aty_ld_le32(LCD_INDEX, par);
+ aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
+ }
+ }
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+}
+
+static u32 calc_line_length(struct atyfb_par *par, u32 vxres, u32 bpp)
+{
+ u32 line_length = vxres * bpp / 8;
+
+ if (par->ram_type == SGRAM ||
+ (!M64_HAS(XL_MEM) && par->ram_type == WRAM))
+ line_length = (line_length + 63) & ~63;
+
+ return line_length;
+}
+
+static int aty_var_to_crtc(const struct fb_info *info,
+ const struct fb_var_screeninfo *var,
+ struct crtc *crtc)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
+ u32 sync, vmode;
+ u32 h_total, h_disp, h_sync_strt, h_sync_end, h_sync_dly, h_sync_wid, h_sync_pol;
+ u32 v_total, v_disp, v_sync_strt, v_sync_end, v_sync_wid, v_sync_pol, c_sync;
+ u32 pix_width, dp_pix_width, dp_chain_mask;
+ u32 line_length;
+
+ /* input */
+ xres = (var->xres + 7) & ~7;
+ yres = var->yres;
+ vxres = (var->xres_virtual + 7) & ~7;
+ vyres = var->yres_virtual;
+ xoffset = (var->xoffset + 7) & ~7;
+ yoffset = var->yoffset;
+ bpp = var->bits_per_pixel;
+ if (bpp == 16)
+ bpp = (var->green.length == 5) ? 15 : 16;
+ sync = var->sync;
+ vmode = var->vmode;
+
+ /* convert (and round up) and validate */
+ if (vxres < xres + xoffset)
+ vxres = xres + xoffset;
+ h_disp = xres;
+
+ if (vyres < yres + yoffset)
+ vyres = yres + yoffset;
+ v_disp = yres;
+
+ if (bpp <= 8) {
+ bpp = 8;
+ pix_width = CRTC_PIX_WIDTH_8BPP;
+ dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
+ BYTE_ORDER_LSB_TO_MSB;
+ dp_chain_mask = DP_CHAIN_8BPP;
+ } else if (bpp <= 15) {
+ bpp = 16;
+ pix_width = CRTC_PIX_WIDTH_15BPP;
+ dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP |
+ BYTE_ORDER_LSB_TO_MSB;
+ dp_chain_mask = DP_CHAIN_15BPP;
+ } else if (bpp <= 16) {
+ bpp = 16;
+ pix_width = CRTC_PIX_WIDTH_16BPP;
+ dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP |
+ BYTE_ORDER_LSB_TO_MSB;
+ dp_chain_mask = DP_CHAIN_16BPP;
+ } else if (bpp <= 24 && M64_HAS(INTEGRATED)) {
+ bpp = 24;
+ pix_width = CRTC_PIX_WIDTH_24BPP;
+ dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
+ BYTE_ORDER_LSB_TO_MSB;
+ dp_chain_mask = DP_CHAIN_24BPP;
+ } else if (bpp <= 32) {
+ bpp = 32;
+ pix_width = CRTC_PIX_WIDTH_32BPP;
+ dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP |
+ BYTE_ORDER_LSB_TO_MSB;
+ dp_chain_mask = DP_CHAIN_32BPP;
+ } else
+ FAIL("invalid bpp");
+
+ line_length = calc_line_length(par, vxres, bpp);
+
+ if (vyres * line_length > info->fix.smem_len)
+ FAIL("not enough video RAM");
+
+ h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+ v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+ if ((xres > 1920) || (yres > 1200)) {
+ FAIL("MACH64 chips are designed for max 1920x1200\n"
+ "select another resolution.");
+ }
+ h_sync_strt = h_disp + var->right_margin;
+ h_sync_end = h_sync_strt + var->hsync_len;
+ h_sync_dly = var->right_margin & 7;
+ h_total = h_sync_end + h_sync_dly + var->left_margin;
+
+ v_sync_strt = v_disp + var->lower_margin;
+ v_sync_end = v_sync_strt + var->vsync_len;
+ v_total = v_sync_end + var->upper_margin;
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table != 0) {
+ if (!M64_HAS(LT_LCD_REGS)) {
+ u32 lcd_index = aty_ld_le32(LCD_INDEX, par);
+ crtc->lcd_index = lcd_index &
+ ~(LCD_INDEX_MASK | LCD_DISPLAY_DIS |
+ LCD_SRC_SEL | CRTC2_DISPLAY_DIS);
+ aty_st_le32(LCD_INDEX, lcd_index, par);
+ }
+
+ if (!M64_HAS(MOBIL_BUS))
+ crtc->lcd_index |= CRTC2_DISPLAY_DIS;
+
+ crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par) | 0x4000;
+ crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par) & ~CRTC_RW_SELECT;
+
+ crtc->lcd_gen_cntl &=
+ ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | TVCLK_PM_EN |
+ /*VCLK_DAC_PM_EN | USE_SHADOWED_VEND |*/
+ USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN);
+ crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT;
+
+ if ((crtc->lcd_gen_cntl & LCD_ON) &&
+ ((xres > par->lcd_width) || (yres > par->lcd_height))) {
+ /*
+ * We cannot display the mode on the LCD. If the CRT is
+ * enabled we can turn off the LCD.
+ * If the CRT is off, it isn't a good idea to switch it
+ * on; we don't know if one is connected. So it's better
+ * to fail then.
+ */
+ if (crtc->lcd_gen_cntl & CRT_ON) {
+ if (!(var->activate & FB_ACTIVATE_TEST))
+ PRINTKI("Disable LCD panel, because video mode does not fit.\n");
+ crtc->lcd_gen_cntl &= ~LCD_ON;
+ /*aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);*/
+ } else {
+ if (!(var->activate & FB_ACTIVATE_TEST))
+ PRINTKE("Video mode exceeds size of LCD panel.\nConnect this computer to a conventional monitor if you really need this mode.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) {
+ int VScan = 1;
+ /* bpp -> bytespp, 1,4 -> 0; 8 -> 2; 15,16 -> 1; 24 -> 6; 32 -> 5
+ const u8 DFP_h_sync_dly_LT[] = { 0, 2, 1, 6, 5 };
+ const u8 ADD_to_strt_wid_and_dly_LT_DAC[] = { 0, 5, 6, 9, 9, 12, 12 }; */
+
+ vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED);
+
+ /*
+ * This is horror! When we simulate, say 640x480 on an 800x600
+ * LCD monitor, the CRTC should be programmed 800x600 values for
+ * the non visible part, but 640x480 for the visible part.
+ * This code has been tested on a laptop with it's 1400x1050 LCD
+ * monitor and a conventional monitor both switched on.
+ * Tested modes: 1280x1024, 1152x864, 1024x768, 800x600,
+ * works with little glitches also with DOUBLESCAN modes
+ */
+ if (yres < par->lcd_height) {
+ VScan = par->lcd_height / yres;
+ if (VScan > 1) {
+ VScan = 2;
+ vmode |= FB_VMODE_DOUBLE;
+ }
+ }
+
+ h_sync_strt = h_disp + par->lcd_right_margin;
+ h_sync_end = h_sync_strt + par->lcd_hsync_len;
+ h_sync_dly = /*DFP_h_sync_dly[ ( bpp + 1 ) / 3 ]; */par->lcd_hsync_dly;
+ h_total = h_disp + par->lcd_hblank_len;
+
+ v_sync_strt = v_disp + par->lcd_lower_margin / VScan;
+ v_sync_end = v_sync_strt + par->lcd_vsync_len / VScan;
+ v_total = v_disp + par->lcd_vblank_len / VScan;
+ }
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+
+ h_disp = (h_disp >> 3) - 1;
+ h_sync_strt = (h_sync_strt >> 3) - 1;
+ h_sync_end = (h_sync_end >> 3) - 1;
+ h_total = (h_total >> 3) - 1;
+ h_sync_wid = h_sync_end - h_sync_strt;
+
+ FAIL_MAX("h_disp too large", h_disp, 0xff);
+ FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff);
+ /*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/
+ if (h_sync_wid > 0x1f)
+ h_sync_wid = 0x1f;
+ FAIL_MAX("h_total too large", h_total, 0x1ff);
+
+ if (vmode & FB_VMODE_DOUBLE) {
+ v_disp <<= 1;
+ v_sync_strt <<= 1;
+ v_sync_end <<= 1;
+ v_total <<= 1;
+ }
+
+ v_disp--;
+ v_sync_strt--;
+ v_sync_end--;
+ v_total--;
+ v_sync_wid = v_sync_end - v_sync_strt;
+
+ FAIL_MAX("v_disp too large", v_disp, 0x7ff);
+ FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff);
+ /*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/
+ if (v_sync_wid > 0x1f)
+ v_sync_wid = 0x1f;
+ FAIL_MAX("v_total too large", v_total, 0x7ff);
+
+ c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0;
+
+ /* output */
+ crtc->vxres = vxres;
+ crtc->vyres = vyres;
+ crtc->xoffset = xoffset;
+ crtc->yoffset = yoffset;
+ crtc->bpp = bpp;
+ crtc->off_pitch =
+ ((yoffset * line_length + xoffset * bpp / 8) / 8) |
+ ((line_length / bpp) << 22);
+ crtc->vline_crnt_vline = 0;
+
+ crtc->h_tot_disp = h_total | (h_disp << 16);
+ crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly << 8) |
+ ((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16) |
+ (h_sync_pol << 21);
+ crtc->v_tot_disp = v_total | (v_disp << 16);
+ crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
+ (v_sync_pol << 21);
+
+ /* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */
+ crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync;
+ crtc->gen_cntl |= CRTC_VGA_LINEAR;
+
+ /* Enable doublescan mode if requested */
+ if (vmode & FB_VMODE_DOUBLE)
+ crtc->gen_cntl |= CRTC_DBL_SCAN_EN;
+ /* Enable interlaced mode if requested */
+ if (vmode & FB_VMODE_INTERLACED)
+ crtc->gen_cntl |= CRTC_INTERLACE_EN;
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table != 0) {
+ u32 vdisplay = yres;
+ if (vmode & FB_VMODE_DOUBLE)
+ vdisplay <<= 1;
+ crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH);
+ crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 |
+ /*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/
+ USE_SHADOWED_VEND |
+ USE_SHADOWED_ROWCUR |
+ SHADOW_EN | SHADOW_RW_EN);
+ crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR/* | LOCK_8DOT*/;
+
+ /* MOBILITY M1 tested, FIXME: LT */
+ crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
+ if (!M64_HAS(LT_LCD_REGS))
+ crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) &
+ ~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3);
+
+ crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO |
+ HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO |
+ HORZ_STRETCH_MODE | HORZ_STRETCH_EN);
+ if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) {
+ do {
+ /*
+ * The horizontal blender misbehaves when
+ * HDisplay is less than a certain threshold
+ * (440 for a 1024-wide panel). It doesn't
+ * stretch such modes enough. Use pixel
+ * replication instead of blending to stretch
+ * modes that can be made to exactly fit the
+ * panel width. The undocumented "NoLCDBlend"
+ * option allows the pixel-replicated mode to
+ * be slightly wider or narrower than the
+ * panel width. It also causes a mode that is
+ * exactly half as wide as the panel to be
+ * pixel-replicated, rather than blended.
+ */
+ int HDisplay = xres & ~7;
+ int nStretch = par->lcd_width / HDisplay;
+ int Remainder = par->lcd_width % HDisplay;
+
+ if ((!Remainder && ((nStretch > 2))) ||
+ (((HDisplay * 16) / par->lcd_width) < 7)) {
+ static const char StretchLoops[] = { 10, 12, 13, 15, 16 };
+ int horz_stretch_loop = -1, BestRemainder;
+ int Numerator = HDisplay, Denominator = par->lcd_width;
+ int Index = 5;
+ ATIReduceRatio(&Numerator, &Denominator);
+
+ BestRemainder = (Numerator * 16) / Denominator;
+ while (--Index >= 0) {
+ Remainder = ((Denominator - Numerator) * StretchLoops[Index]) %
+ Denominator;
+ if (Remainder < BestRemainder) {
+ horz_stretch_loop = Index;
+ if (!(BestRemainder = Remainder))
+ break;
+ }
+ }
+
+ if ((horz_stretch_loop >= 0) && !BestRemainder) {
+ int horz_stretch_ratio = 0, Accumulator = 0;
+ int reuse_previous = 1;
+
+ Index = StretchLoops[horz_stretch_loop];
+
+ while (--Index >= 0) {
+ if (Accumulator > 0)
+ horz_stretch_ratio |= reuse_previous;
+ else
+ Accumulator += Denominator;
+ Accumulator -= Numerator;
+ reuse_previous <<= 1;
+ }
+
+ crtc->horz_stretching |= (HORZ_STRETCH_EN |
+ ((horz_stretch_loop & HORZ_STRETCH_LOOP) << 16) |
+ (horz_stretch_ratio & HORZ_STRETCH_RATIO));
+ break; /* Out of the do { ... } while (0) */
+ }
+ }
+
+ crtc->horz_stretching |= (HORZ_STRETCH_MODE | HORZ_STRETCH_EN |
+ (((HDisplay * (HORZ_STRETCH_BLEND + 1)) / par->lcd_width) & HORZ_STRETCH_BLEND));
+ } while (0);
+ }
+
+ if (vdisplay < par->lcd_height && crtc->lcd_gen_cntl & LCD_ON) {
+ crtc->vert_stretching = (VERT_STRETCH_USE0 | VERT_STRETCH_EN |
+ (((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0));
+
+ if (!M64_HAS(LT_LCD_REGS) &&
+ xres <= (M64_HAS(MOBIL_BUS) ? 1024 : 800))
+ crtc->ext_vert_stretch |= VERT_STRETCH_MODE;
+ } else {
+ /*
+ * Don't use vertical blending if the mode is too wide
+ * or not vertically stretched.
+ */
+ crtc->vert_stretching = 0;
+ }
+ /* copy to shadow crtc */
+ crtc->shadow_h_tot_disp = crtc->h_tot_disp;
+ crtc->shadow_h_sync_strt_wid = crtc->h_sync_strt_wid;
+ crtc->shadow_v_tot_disp = crtc->v_tot_disp;
+ crtc->shadow_v_sync_strt_wid = crtc->v_sync_strt_wid;
+ }
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+
+ if (M64_HAS(MAGIC_FIFO)) {
+ /* FIXME: display FIFO low watermark values */
+ crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_FIFO_LWM);
+ }
+ crtc->dp_pix_width = dp_pix_width;
+ crtc->dp_chain_mask = dp_chain_mask;
+
+ return 0;
+}
+
+static int aty_crtc_to_var(const struct crtc *crtc,
+ struct fb_var_screeninfo *var)
+{
+ u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync;
+ u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
+ u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
+ u32 pix_width;
+ u32 double_scan, interlace;
+
+ /* input */
+ h_total = crtc->h_tot_disp & 0x1ff;
+ h_disp = (crtc->h_tot_disp >> 16) & 0xff;
+ h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | ((crtc->h_sync_strt_wid >> 4) & 0x100);
+ h_sync_dly = (crtc->h_sync_strt_wid >> 8) & 0x7;
+ h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x1f;
+ h_sync_pol = (crtc->h_sync_strt_wid >> 21) & 0x1;
+ v_total = crtc->v_tot_disp & 0x7ff;
+ v_disp = (crtc->v_tot_disp >> 16) & 0x7ff;
+ v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
+ v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f;
+ v_sync_pol = (crtc->v_sync_strt_wid >> 21) & 0x1;
+ c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+ pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+ double_scan = crtc->gen_cntl & CRTC_DBL_SCAN_EN;
+ interlace = crtc->gen_cntl & CRTC_INTERLACE_EN;
+
+ /* convert */
+ xres = (h_disp + 1) * 8;
+ yres = v_disp + 1;
+ left = (h_total - h_sync_strt - h_sync_wid) * 8 - h_sync_dly;
+ right = (h_sync_strt - h_disp) * 8 + h_sync_dly;
+ hslen = h_sync_wid * 8;
+ upper = v_total - v_sync_strt - v_sync_wid;
+ lower = v_sync_strt - v_disp;
+ vslen = v_sync_wid;
+ sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+ (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+ (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+
+ switch (pix_width) {
+#if 0
+ case CRTC_PIX_WIDTH_4BPP:
+ bpp = 4;
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+#endif
+ case CRTC_PIX_WIDTH_8BPP:
+ bpp = 8;
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case CRTC_PIX_WIDTH_15BPP: /* RGB 555 */
+ bpp = 16;
+ var->red.offset = 10;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case CRTC_PIX_WIDTH_16BPP: /* RGB 565 */
+ bpp = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case CRTC_PIX_WIDTH_24BPP: /* RGB 888 */
+ bpp = 24;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case CRTC_PIX_WIDTH_32BPP: /* ARGB 8888 */
+ bpp = 32;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ break;
+ default:
+ PRINTKE("Invalid pixel width\n");
+ return -EINVAL;
+ }
+
+ /* output */
+ var->xres = xres;
+ var->yres = yres;
+ var->xres_virtual = crtc->vxres;
+ var->yres_virtual = crtc->vyres;
+ var->bits_per_pixel = bpp;
+ var->left_margin = left;
+ var->right_margin = right;
+ var->upper_margin = upper;
+ var->lower_margin = lower;
+ var->hsync_len = hslen;
+ var->vsync_len = vslen;
+ var->sync = sync;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ /*
+ * In double scan mode, the vertical parameters are doubled,
+ * so we need to halve them to get the right values.
+ * In interlaced mode the values are already correct,
+ * so no correction is necessary.
+ */
+ if (interlace)
+ var->vmode = FB_VMODE_INTERLACED;
+
+ if (double_scan) {
+ var->vmode = FB_VMODE_DOUBLE;
+ var->yres >>= 1;
+ var->upper_margin >>= 1;
+ var->lower_margin >>= 1;
+ var->vsync_len >>= 1;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int atyfb_set_par(struct fb_info *info)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ u32 tmp, pixclock;
+ int err;
+#ifdef DEBUG
+ struct fb_var_screeninfo debug;
+ u32 pixclock_in_ps;
+#endif
+ if (par->asleep)
+ return 0;
+
+ err = aty_var_to_crtc(info, var, &par->crtc);
+ if (err)
+ return err;
+
+ pixclock = atyfb_get_pixclock(var, par);
+
+ if (pixclock == 0) {
+ PRINTKE("Invalid pixclock\n");
+ return -EINVAL;
+ } else {
+ err = par->pll_ops->var_to_pll(info, pixclock,
+ var->bits_per_pixel, &par->pll);
+ if (err)
+ return err;
+ }
+
+ par->accel_flags = var->accel_flags; /* hack */
+
+ if (var->accel_flags) {
+ info->fbops->fb_sync = atyfb_sync;
+ info->flags &= ~FBINFO_HWACCEL_DISABLED;
+ } else {
+ info->fbops->fb_sync = NULL;
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+ }
+
+ if (par->blitter_may_be_busy)
+ wait_for_idle(par);
+
+ aty_set_crtc(par, &par->crtc);
+ par->dac_ops->set_dac(info, &par->pll,
+ var->bits_per_pixel, par->accel_flags);
+ par->pll_ops->set_pll(info, &par->pll);
+
+#ifdef DEBUG
+ if (par->pll_ops && par->pll_ops->pll_to_var)
+ pixclock_in_ps = par->pll_ops->pll_to_var(info, &par->pll);
+ else
+ pixclock_in_ps = 0;
+
+ if (0 == pixclock_in_ps) {
+ PRINTKE("ALERT ops->pll_to_var get 0\n");
+ pixclock_in_ps = pixclock;
+ }
+
+ memset(&debug, 0, sizeof(debug));
+ if (!aty_crtc_to_var(&par->crtc, &debug)) {
+ u32 hSync, vRefresh;
+ u32 h_disp, h_sync_strt, h_sync_end, h_total;
+ u32 v_disp, v_sync_strt, v_sync_end, v_total;
+
+ h_disp = debug.xres;
+ h_sync_strt = h_disp + debug.right_margin;
+ h_sync_end = h_sync_strt + debug.hsync_len;
+ h_total = h_sync_end + debug.left_margin;
+ v_disp = debug.yres;
+ v_sync_strt = v_disp + debug.lower_margin;
+ v_sync_end = v_sync_strt + debug.vsync_len;
+ v_total = v_sync_end + debug.upper_margin;
+
+ hSync = 1000000000 / (pixclock_in_ps * h_total);
+ vRefresh = (hSync * 1000) / v_total;
+ if (par->crtc.gen_cntl & CRTC_INTERLACE_EN)
+ vRefresh *= 2;
+ if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
+ vRefresh /= 2;
+
+ DPRINTK("atyfb_set_par\n");
+ DPRINTK(" Set Visible Mode to %ix%i-%i\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ DPRINTK(" Virtual resolution %ix%i, "
+ "pixclock_in_ps %i (calculated %i)\n",
+ var->xres_virtual, var->yres_virtual,
+ pixclock, pixclock_in_ps);
+ DPRINTK(" Dot clock: %i MHz\n",
+ 1000000 / pixclock_in_ps);
+ DPRINTK(" Horizontal sync: %i kHz\n", hSync);
+ DPRINTK(" Vertical refresh: %i Hz\n", vRefresh);
+ DPRINTK(" x style: %i.%03i %i %i %i %i %i %i %i %i\n",
+ 1000000 / pixclock_in_ps, 1000000 % pixclock_in_ps,
+ h_disp, h_sync_strt, h_sync_end, h_total,
+ v_disp, v_sync_strt, v_sync_end, v_total);
+ DPRINTK(" fb style: %i %i %i %i %i %i %i %i %i\n",
+ pixclock_in_ps,
+ debug.left_margin, h_disp, debug.right_margin, debug.hsync_len,
+ debug.upper_margin, v_disp, debug.lower_margin, debug.vsync_len);
+ }
+#endif /* DEBUG */
+
+ if (!M64_HAS(INTEGRATED)) {
+ /* Don't forget MEM_CNTL */
+ tmp = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff;
+ switch (var->bits_per_pixel) {
+ case 8:
+ tmp |= 0x02000000;
+ break;
+ case 16:
+ tmp |= 0x03000000;
+ break;
+ case 32:
+ tmp |= 0x06000000;
+ break;
+ }
+ aty_st_le32(MEM_CNTL, tmp, par);
+ } else {
+ tmp = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff;
+ if (!M64_HAS(MAGIC_POSTDIV))
+ tmp |= par->mem_refresh_rate << 20;
+ switch (var->bits_per_pixel) {
+ case 8:
+ case 24:
+ tmp |= 0x00000000;
+ break;
+ case 16:
+ tmp |= 0x04000000;
+ break;
+ case 32:
+ tmp |= 0x08000000;
+ break;
+ }
+ if (M64_HAS(CT_BUS)) {
+ aty_st_le32(DAC_CNTL, 0x87010184, par);
+ aty_st_le32(BUS_CNTL, 0x680000f9, par);
+ } else if (M64_HAS(VT_BUS)) {
+ aty_st_le32(DAC_CNTL, 0x87010184, par);
+ aty_st_le32(BUS_CNTL, 0x680000f9, par);
+ } else if (M64_HAS(MOBIL_BUS)) {
+ aty_st_le32(DAC_CNTL, 0x80010102, par);
+ aty_st_le32(BUS_CNTL, 0x7b33a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
+ } else {
+ /* GT */
+ aty_st_le32(DAC_CNTL, 0x86010102, par);
+ aty_st_le32(BUS_CNTL, 0x7b23a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
+ aty_st_le32(EXT_MEM_CNTL, aty_ld_le32(EXT_MEM_CNTL, par) | 0x5000001, par);
+ }
+ aty_st_le32(MEM_CNTL, tmp, par);
+ }
+ aty_st_8(DAC_MASK, 0xff, par);
+
+ info->fix.line_length = calc_line_length(par, var->xres_virtual,
+ var->bits_per_pixel);
+
+ info->fix.visual = var->bits_per_pixel <= 8 ?
+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
+
+ /* Initialize the graphics engine */
+ if (par->accel_flags & FB_ACCELF_TEXT)
+ aty_init_engine(par, info);
+
+#ifdef CONFIG_BOOTX_TEXT
+ btext_update_display(info->fix.smem_start,
+ (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8,
+ ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1,
+ var->bits_per_pixel,
+ par->crtc.vxres * var->bits_per_pixel / 8);
+#endif /* CONFIG_BOOTX_TEXT */
+#if 0
+ /* switch to accelerator mode */
+ if (!(par->crtc.gen_cntl & CRTC_EXT_DISP_EN))
+ aty_st_le32(CRTC_GEN_CNTL, par->crtc.gen_cntl | CRTC_EXT_DISP_EN, par);
+#endif
+#ifdef DEBUG
+{
+ /* dump non shadow CRTC, pll, LCD registers */
+ int i; u32 base;
+
+ /* CRTC registers */
+ base = 0x2000;
+ printk("debug atyfb: Mach64 non-shadow register values:");
+ for (i = 0; i < 256; i = i+4) {
+ if (i % 16 == 0)
+ printk("\ndebug atyfb: 0x%04X: ", base + i);
+ printk(" %08X", aty_ld_le32(i, par));
+ }
+ printk("\n\n");
+
+#ifdef CONFIG_FB_ATY_CT
+ /* PLL registers */
+ base = 0x00;
+ printk("debug atyfb: Mach64 PLL register values:");
+ for (i = 0; i < 64; i++) {
+ if (i % 16 == 0)
+ printk("\ndebug atyfb: 0x%02X: ", base + i);
+ if (i % 4 == 0)
+ printk(" ");
+ printk("%02X", aty_ld_pll_ct(i, par));
+ }
+ printk("\n\n");
+#endif /* CONFIG_FB_ATY_CT */
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table != 0) {
+ /* LCD registers */
+ base = 0x00;
+ printk("debug atyfb: LCD register values:");
+ if (M64_HAS(LT_LCD_REGS)) {
+ for (i = 0; i <= POWER_MANAGEMENT; i++) {
+ if (i == EXT_VERT_STRETCH)
+ continue;
+ printk("\ndebug atyfb: 0x%04X: ",
+ lt_lcd_regs[i]);
+ printk(" %08X", aty_ld_lcd(i, par));
+ }
+ } else {
+ for (i = 0; i < 64; i++) {
+ if (i % 4 == 0)
+ printk("\ndebug atyfb: 0x%02X: ",
+ base + i);
+ printk(" %08X", aty_ld_lcd(i, par));
+ }
+ }
+ printk("\n\n");
+ }
+#endif /* CONFIG_FB_ATY_GENERIC_LCD */
+}
+#endif /* DEBUG */
+ return 0;
+}
+
+static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ int err;
+ struct crtc crtc;
+ union aty_pll pll;
+ u32 pixclock;
+
+ memcpy(&pll, &par->pll, sizeof(pll));
+
+ err = aty_var_to_crtc(info, var, &crtc);
+ if (err)
+ return err;
+
+ pixclock = atyfb_get_pixclock(var, par);
+
+ if (pixclock == 0) {
+ if (!(var->activate & FB_ACTIVATE_TEST))
+ PRINTKE("Invalid pixclock\n");
+ return -EINVAL;
+ } else {
+ err = par->pll_ops->var_to_pll(info, pixclock,
+ var->bits_per_pixel, &pll);
+ if (err)
+ return err;
+ }
+
+ if (var->accel_flags & FB_ACCELF_TEXT)
+ info->var.accel_flags = FB_ACCELF_TEXT;
+ else
+ info->var.accel_flags = 0;
+
+ aty_crtc_to_var(&crtc, var);
+ var->pixclock = par->pll_ops->pll_to_var(info, &pll);
+ return 0;
+}
+
+static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info)
+{
+ u32 xoffset = info->var.xoffset;
+ u32 yoffset = info->var.yoffset;
+ u32 line_length = info->fix.line_length;
+ u32 bpp = info->var.bits_per_pixel;
+
+ par->crtc.off_pitch =
+ ((yoffset * line_length + xoffset * bpp / 8) / 8) |
+ ((line_length / bpp) << 22);
+}
+
+
+/*
+ * Open/Release the frame buffer device
+ */
+
+static int atyfb_open(struct fb_info *info, int user)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ if (user) {
+ par->open++;
+#ifdef __sparc__
+ par->mmaped = 0;
+#endif
+ }
+ return 0;
+}
+
+static irqreturn_t aty_irq(int irq, void *dev_id)
+{
+ struct atyfb_par *par = dev_id;
+ int handled = 0;
+ u32 int_cntl;
+
+ spin_lock(&par->int_lock);
+
+ int_cntl = aty_ld_le32(CRTC_INT_CNTL, par);
+
+ if (int_cntl & CRTC_VBLANK_INT) {
+ /* clear interrupt */
+ aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) |
+ CRTC_VBLANK_INT_AK, par);
+ par->vblank.count++;
+ if (par->vblank.pan_display) {
+ par->vblank.pan_display = 0;
+ aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
+ }
+ wake_up_interruptible(&par->vblank.wait);
+ handled = 1;
+ }
+
+ spin_unlock(&par->int_lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int aty_enable_irq(struct atyfb_par *par, int reenable)
+{
+ u32 int_cntl;
+
+ if (!test_and_set_bit(0, &par->irq_flags)) {
+ if (request_irq(par->irq, aty_irq, IRQF_SHARED, "atyfb", par)) {
+ clear_bit(0, &par->irq_flags);
+ return -EINVAL;
+ }
+ 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);
+ }
+
+ return 0;
+}
+
+static int aty_disable_irq(struct atyfb_par *par)
+{
+ u32 int_cntl;
+
+ 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;
+#ifdef __sparc__
+ int was_mmaped;
+#endif
+
+ if (!user)
+ return 0;
+
+ par->open--;
+ mdelay(1);
+ wait_for_idle(par);
+
+ if (par->open)
+ return 0;
+
+#ifdef __sparc__
+ 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.
+ */
+ var = default_var;
+ if (noaccel)
+ var.accel_flags &= ~FB_ACCELF_TEXT;
+ else
+ var.accel_flags |= FB_ACCELF_TEXT;
+ if (var.yres == var.yres_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;
+ }
+ }
+#endif
+ aty_disable_irq(par);
+
+ return 0;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * 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)
+{
+ 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)
+ 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 */
+
+struct atyclk {
+ u32 ref_clk_per;
+ 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 */
+ u32 dsp_loop_latency; /* 0-15 */
+ u32 dsp_precision; /* 0-7 */
+ u32 dsp_on; /* 0-2047 */
+ u32 dsp_off; /* 0-2047 */
+};
+
+#define ATYIO_FEATR 0x41545902 /* ATY\02 */
+#define ATYIO_FEATW 0x41545903 /* ATY\03 */
+#endif
+
+static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+#ifdef __sparc__
+ struct fbtype fbtyp;
+#endif
+
+ switch (cmd) {
+#ifdef __sparc__
+ case FBIOGTYPE:
+ fbtyp.fb_type = FBTYPE_PCI_GENERIC;
+ fbtyp.fb_width = par->crtc.vxres;
+ fbtyp.fb_height = par->crtc.vyres;
+ 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)))
+ return -EFAULT;
+ break;
+#endif /* __sparc__ */
+
+ case FBIO_WAITFORVSYNC:
+ {
+ u32 crtc;
+
+ if (get_user(crtc, (__u32 __user *) arg))
+ return -EFAULT;
+
+ return aty_waitforvblank(par, crtc);
+ }
+
+#if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
+ case ATYIO_CLKR:
+ if (M64_HAS(INTEGRATED)) {
+ struct atyclk clk = { 0 };
+ 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_off = dsp_on_off & 0x7ff;
+ clk.dsp_on = (dsp_on_off >> 16) & 0x7ff;
+ if (copy_to_user((struct atyclk __user *) arg, &clk,
+ sizeof(clk)))
+ return -EFAULT;
+ } else
+ return -EINVAL;
+ break;
+ case ATYIO_CLKW:
+ if (M64_HAS(INTEGRATED)) {
+ struct atyclk clk;
+ union aty_pll *pll = &par->pll;
+ if (copy_from_user(&clk, (struct atyclk __user *) 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_off & 0x7ff) |
+ ((clk.dsp_on & 0x7ff) << 16);
+ /*aty_calc_pll_ct(info, &pll->ct);*/
+ aty_set_pll_ct(info, pll);
+ } else
+ return -EINVAL;
+ break;
+ case ATYIO_FEATR:
+ if (get_user(par->features, (u32 __user *) arg))
+ return -EFAULT;
+ break;
+ case ATYIO_FEATW:
+ if (put_user(par->features, (u32 __user *) arg))
+ return -EFAULT;
+ break;
+#endif /* DEBUG && CONFIG_FB_ATY_CT */
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int atyfb_sync(struct fb_info *info)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ if (par->blitter_may_be_busy)
+ wait_for_idle(par);
+ return 0;
+}
+
+#ifdef __sparc__
+static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ unsigned int size, page, map_size = 0;
+ unsigned long map_offset = 0;
+ unsigned long off;
+ int i;
+
+ if (!par->mmap_map)
+ return -ENXIO;
+
+ if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+ return -EINVAL;
+
+ off = vma->vm_pgoff << PAGE_SHIFT;
+ size = vma->vm_end - vma->vm_start;
+
+ /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
+
+ if (((vma->vm_pgoff == 0) && (size == info->fix.smem_len)) ||
+ ((off == info->fix.smem_len) && (size == PAGE_SIZE)))
+ off += 0x8000000000000000UL;
+
+ vma->vm_pgoff = off >> PAGE_SHIFT; /* propagate off changes */
+
+ /* Each page, see which map applies */
+ for (page = 0; page < size;) {
+ map_size = 0;
+ for (i = 0; par->mmap_map[i].size; i++) {
+ unsigned long start = par->mmap_map[i].voff;
+ unsigned long end = start + par->mmap_map[i].size;
+ unsigned long offset = off + page;
+
+ if (start > offset)
+ continue;
+ if (offset >= end)
+ continue;
+
+ map_size = par->mmap_map[i].size - (offset - start);
+ map_offset = par->mmap_map[i].poff + (offset - start);
+ break;
+ }
+ if (!map_size) {
+ page += PAGE_SIZE;
+ continue;
+ }
+ if (page + map_size > size)
+ map_size = size - page;
+
+ pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask);
+ pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag;
+
+ if (remap_pfn_range(vma, vma->vm_start + page,
+ map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot))
+ return -EAGAIN;
+
+ page += map_size;
+ }
+
+ if (!map_size)
+ return -EINVAL;
+
+ if (!par->mmaped)
+ par->mmaped = 1;
+ return 0;
+}
+#endif /* __sparc__ */
+
+
+
+#if defined(CONFIG_PM) && defined(CONFIG_PCI)
+
+#ifdef CONFIG_PPC_PMAC
+/* Power management routines. Those are used for PowerBook sleep.
+ */
+static int aty_power_mgmt(int sleep, struct atyfb_par *par)
+{
+ u32 pm;
+ int timeout;
+
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+ pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG;
+ aty_st_lcd(POWER_MANAGEMENT, pm, par);
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+
+ timeout = 2000;
+ if (sleep) {
+ /* Sleep */
+ pm &= ~PWR_MGT_ON;
+ aty_st_lcd(POWER_MANAGEMENT, pm, par);
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+ udelay(10);
+ pm &= ~(PWR_BLON | AUTO_PWR_UP);
+ pm |= SUSPEND_NOW;
+ aty_st_lcd(POWER_MANAGEMENT, pm, par);
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+ udelay(10);
+ pm |= PWR_MGT_ON;
+ aty_st_lcd(POWER_MANAGEMENT, pm, par);
+ do {
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+ mdelay(1);
+ if ((--timeout) == 0)
+ break;
+ } while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND);
+ } else {
+ /* Wakeup */
+ pm &= ~PWR_MGT_ON;
+ aty_st_lcd(POWER_MANAGEMENT, pm, par);
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+ udelay(10);
+ pm &= ~SUSPEND_NOW;
+ pm |= (PWR_BLON | AUTO_PWR_UP);
+ aty_st_lcd(POWER_MANAGEMENT, pm, par);
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+ udelay(10);
+ pm |= PWR_MGT_ON;
+ aty_st_lcd(POWER_MANAGEMENT, pm, par);
+ do {
+ pm = aty_ld_lcd(POWER_MANAGEMENT, par);
+ mdelay(1);
+ if ((--timeout) == 0)
+ break;
+ } while ((pm & PWR_MGT_STATUS_MASK) != 0);
+ }
+ mdelay(500);
+
+ return timeout ? 0 : -EIO;
+}
+#endif /* CONFIG_PPC_PMAC */
+
+static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ if (state.event == pdev->dev.power.power_state.event)
+ return 0;
+
+ console_lock();
+
+ fb_set_suspend(info, 1);
+
+ /* Idle & reset engine */
+ wait_for_idle(par);
+ aty_reset_engine(par);
+
+ /* Blank display and LCD */
+ atyfb_blank(FB_BLANK_POWERDOWN, info);
+
+ par->asleep = 1;
+ par->lock_blank = 1;
+
+ /*
+ * Because we may change PCI D state ourselves, we need to
+ * first save the config space content so the core can
+ * restore it properly on resume.
+ */
+ pci_save_state(pdev);
+
+#ifdef CONFIG_PPC_PMAC
+ /* Set chip to "suspend" mode */
+ if (machine_is(powermac) && aty_power_mgmt(1, par)) {
+ par->asleep = 0;
+ par->lock_blank = 0;
+ atyfb_blank(FB_BLANK_UNBLANK, info);
+ fb_set_suspend(info, 0);
+ console_unlock();
+ return -EIO;
+ }
+#else
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+#endif
+
+ console_unlock();
+
+ pdev->dev.power.power_state = state;
+
+ return 0;
+}
+
+static void aty_resume_chip(struct fb_info *info)
+{
+ struct atyfb_par *par = info->par;
+
+ aty_st_le32(MEM_CNTL, par->mem_cntl, par);
+
+ if (par->pll_ops->resume_pll)
+ par->pll_ops->resume_pll(info, &par->pll);
+
+ if (par->aux_start)
+ aty_st_le32(BUS_CNTL,
+ aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par);
+}
+
+static int atyfb_pci_resume(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ if (pdev->dev.power.power_state.event == PM_EVENT_ON)
+ return 0;
+
+ console_lock();
+
+ /*
+ * PCI state will have been restored by the core, so
+ * we should be in D0 now with our config space fully
+ * restored
+ */
+
+#ifdef CONFIG_PPC_PMAC
+ if (machine_is(powermac) &&
+ pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
+ aty_power_mgmt(0, par);
+#endif
+
+ aty_resume_chip(info);
+
+ par->asleep = 0;
+
+ /* Restore display */
+ atyfb_set_par(info);
+
+ /* Refresh */
+ fb_set_suspend(info, 0);
+
+ /* Unblank */
+ par->lock_blank = 0;
+ atyfb_blank(FB_BLANK_UNBLANK, info);
+
+ console_unlock();
+
+ pdev->dev.power.power_state = PMSG_ON;
+
+ return 0;
+}
+
+#endif /* defined(CONFIG_PM) && defined(CONFIG_PCI) */
+
+/* Backlight */
+#ifdef CONFIG_FB_ATY_BACKLIGHT
+#define MAX_LEVEL 0xFF
+
+static int aty_bl_get_level_brightness(struct atyfb_par *par, int level)
+{
+ struct fb_info *info = pci_get_drvdata(par->pdev);
+ int atylevel;
+
+ /* Get and convert the value */
+ /* No locking of bl_curve since we read a single value */
+ atylevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL;
+
+ if (atylevel < 0)
+ atylevel = 0;
+ else if (atylevel > MAX_LEVEL)
+ atylevel = MAX_LEVEL;
+
+ return atylevel;
+}
+
+static int aty_bl_update_status(struct backlight_device *bd)
+{
+ struct atyfb_par *par = bl_get_data(bd);
+ unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par);
+ int level;
+
+ if (bd->props.power != FB_BLANK_UNBLANK ||
+ bd->props.fb_blank != FB_BLANK_UNBLANK)
+ level = 0;
+ else
+ level = bd->props.brightness;
+
+ reg |= (BLMOD_EN | BIASMOD_EN);
+ if (level > 0) {
+ reg &= ~BIAS_MOD_LEVEL_MASK;
+ reg |= (aty_bl_get_level_brightness(par, level) << BIAS_MOD_LEVEL_SHIFT);
+ } else {
+ reg &= ~BIAS_MOD_LEVEL_MASK;
+ reg |= (aty_bl_get_level_brightness(par, 0) << BIAS_MOD_LEVEL_SHIFT);
+ }
+ aty_st_lcd(LCD_MISC_CNTL, reg, par);
+
+ return 0;
+}
+
+static const struct backlight_ops aty_bl_data = {
+ .update_status = aty_bl_update_status,
+};
+
+static void aty_bl_init(struct atyfb_par *par)
+{
+ struct backlight_properties props;
+ struct fb_info *info = pci_get_drvdata(par->pdev);
+ struct backlight_device *bd;
+ char name[12];
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+ if (!pmac_has_backlight_type("ati"))
+ return;
+#endif
+
+ snprintf(name, sizeof(name), "atybl%d", info->node);
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
+ bd = backlight_device_register(name, info->dev, par, &aty_bl_data,
+ &props);
+ if (IS_ERR(bd)) {
+ info->bl_dev = NULL;
+ printk(KERN_WARNING "aty: Backlight registration failed\n");
+ goto error;
+ }
+
+ info->bl_dev = bd;
+ fb_bl_default_curve(info, 0,
+ 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL,
+ 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL);
+
+ bd->props.brightness = bd->props.max_brightness;
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+
+ printk("aty: Backlight initialized (%s)\n", name);
+
+ return;
+
+error:
+ return;
+}
+
+#ifdef CONFIG_PCI
+static void aty_bl_exit(struct backlight_device *bd)
+{
+ backlight_device_unregister(bd);
+ printk("aty: Backlight unloaded\n");
+}
+#endif /* CONFIG_PCI */
+
+#endif /* CONFIG_FB_ATY_BACKLIGHT */
+
+static void aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
+{
+ static const int ragepro_tbl[] = {
+ 44, 50, 55, 66, 75, 80, 100
+ };
+ static 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 (M64_HAS(XL_MEM)) {
+ refresh_tbl = ragexl_tbl;
+ size = ARRAY_SIZE(ragexl_tbl);
+ } else {
+ refresh_tbl = ragepro_tbl;
+ size = ARRAY_SIZE(ragepro_tbl);
+ }
+
+ for (i = 0; i < size; i++) {
+ if (xclk < refresh_tbl[i])
+ break;
+ }
+ par->mem_refresh_rate = i;
+}
+
+/*
+ * Initialisation
+ */
+
+static struct fb_info *fb_list = NULL;
+
+#if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
+static int atyfb_get_timings_from_lcd(struct atyfb_par *par,
+ struct fb_var_screeninfo *var)
+{
+ int ret = -EINVAL;
+
+ if (par->lcd_table != 0 && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
+ *var = default_var;
+ var->xres = var->xres_virtual = par->lcd_hdisp;
+ var->right_margin = par->lcd_right_margin;
+ var->left_margin = par->lcd_hblank_len -
+ (par->lcd_right_margin + par->lcd_hsync_dly +
+ par->lcd_hsync_len);
+ var->hsync_len = par->lcd_hsync_len + par->lcd_hsync_dly;
+ var->yres = var->yres_virtual = par->lcd_vdisp;
+ var->lower_margin = par->lcd_lower_margin;
+ var->upper_margin = par->lcd_vblank_len -
+ (par->lcd_lower_margin + par->lcd_vsync_len);
+ var->vsync_len = par->lcd_vsync_len;
+ var->pixclock = par->lcd_pixclock;
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif /* defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) */
+
+static int aty_init(struct fb_info *info)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ const char *ramname = NULL, *xtal;
+ int gtb_memsize, has_var = 0;
+ struct fb_var_screeninfo var;
+ int ret;
+
+ init_waitqueue_head(&par->vblank.wait);
+ spin_lock_init(&par->int_lock);
+
+#ifdef CONFIG_FB_ATY_GX
+ if (!M64_HAS(INTEGRATED)) {
+ u32 stat0;
+ u8 dac_type, dac_subtype, clk_type;
+ stat0 = aty_ld_le32(CNFG_STAT0, par);
+ par->bus_type = (stat0 >> 0) & 0x07;
+ par->ram_type = (stat0 >> 3) & 0x07;
+ ramname = aty_gx_ram[par->ram_type];
+ /* FIXME: clockchip/RAMDAC probing? */
+ dac_type = (aty_ld_le32(DAC_CNTL, par) >> 16) & 0x07;
+#ifdef CONFIG_ATARI
+ clk_type = CLK_ATI18818_1;
+ dac_type = (stat0 >> 9) & 0x07;
+ if (dac_type == 0x07)
+ dac_subtype = DAC_ATT20C408;
+ else
+ dac_subtype = (aty_ld_8(SCRATCH_REG1 + 1, par) & 0xF0) | dac_type;
+#else
+ dac_type = DAC_IBMRGB514;
+ dac_subtype = DAC_IBMRGB514;
+ clk_type = CLK_IBMRGB514;
+#endif
+ switch (dac_subtype) {
+ case DAC_IBMRGB514:
+ par->dac_ops = &aty_dac_ibm514;
+ break;
+#ifdef CONFIG_ATARI
+ case DAC_ATI68860_B:
+ case DAC_ATI68860_C:
+ par->dac_ops = &aty_dac_ati68860b;
+ break;
+ case DAC_ATT20C408:
+ case DAC_ATT21C498:
+ par->dac_ops = &aty_dac_att21c498;
+ break;
+#endif
+ default:
+ PRINTKI("aty_init: DAC type not implemented yet!\n");
+ par->dac_ops = &aty_dac_unsupported;
+ break;
+ }
+ switch (clk_type) {
+#ifdef CONFIG_ATARI
+ case CLK_ATI18818_1:
+ par->pll_ops = &aty_pll_ati18818_1;
+ break;
+#else
+ case CLK_IBMRGB514:
+ par->pll_ops = &aty_pll_ibm514;
+ break;
+#endif
+#if 0 /* dead code */
+ case CLK_STG1703:
+ par->pll_ops = &aty_pll_stg1703;
+ break;
+ case CLK_CH8398:
+ par->pll_ops = &aty_pll_ch8398;
+ break;
+ case CLK_ATT20C408:
+ par->pll_ops = &aty_pll_att20c408;
+ break;
+#endif
+ default:
+ PRINTKI("aty_init: CLK type not implemented yet!");
+ par->pll_ops = &aty_pll_unsupported;
+ break;
+ }
+ }
+#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(CNFG_STAT0, par) & 0x07);
+ if (M64_HAS(XL_MEM))
+ ramname = aty_xl_ram[par->ram_type];
+ else
+ ramname = aty_ct_ram[par->ram_type];
+ /* for many chips, the mclk is 67 MHz for SDRAM, 63 MHz otherwise */
+ if (par->pll_limits.mclk == 67 && par->ram_type < SDRAM)
+ par->pll_limits.mclk = 63;
+ /* Mobility + 32bit memory interface need halved XCLK. */
+ if (M64_HAS(MOBIL_BUS) && par->ram_type == SDRAM32)
+ par->pll_limits.xclk = (par->pll_limits.xclk + 1) >> 1;
+ }
+#endif
+#ifdef CONFIG_PPC_PMAC
+ /*
+ * The Apple iBook1 uses non-standard memory frequencies.
+ * We detect it and set the frequency manually.
+ */
+ if (of_machine_is_compatible("PowerBook2,1")) {
+ par->pll_limits.mclk = 70;
+ par->pll_limits.xclk = 53;
+ }
+#endif
+
+ /* Allow command line to override clocks. */
+ 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_CT
+ if (M64_HAS(GTB_DSP)) {
+ u8 pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par);
+
+ if (pll_ref_div) {
+ int diff1, diff2;
+ 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)
+ diff2 = -diff2;
+ if (diff2 < diff1) {
+ par->ref_clk_per = 1000000000000ULL / 29498928;
+ xtal = "29.498928";
+ }
+ }
+ }
+#endif /* CONFIG_FB_ATY_CT */
+
+ /* save previous video mode */
+ aty_get_crtc(par, &par->saved_crtc);
+ if (par->pll_ops->get_pll)
+ par->pll_ops->get_pll(info, &par->saved_pll);
+
+ par->mem_cntl = aty_ld_le32(MEM_CNTL, par);
+ gtb_memsize = M64_HAS(GTB_DSP);
+ if (gtb_memsize)
+ /* 0xF used instead of MEM_SIZE_ALIAS */
+ switch (par->mem_cntl & 0xF) {
+ case MEM_SIZE_512K:
+ info->fix.smem_len = 0x80000;
+ break;
+ case MEM_SIZE_1M:
+ info->fix.smem_len = 0x100000;
+ break;
+ case MEM_SIZE_2M_GTB:
+ info->fix.smem_len = 0x200000;
+ break;
+ case MEM_SIZE_4M_GTB:
+ info->fix.smem_len = 0x400000;
+ break;
+ case MEM_SIZE_6M_GTB:
+ info->fix.smem_len = 0x600000;
+ break;
+ case MEM_SIZE_8M_GTB:
+ info->fix.smem_len = 0x800000;
+ break;
+ default:
+ info->fix.smem_len = 0x80000;
+ } else
+ switch (par->mem_cntl & MEM_SIZE_ALIAS) {
+ case MEM_SIZE_512K:
+ info->fix.smem_len = 0x80000;
+ break;
+ case MEM_SIZE_1M:
+ info->fix.smem_len = 0x100000;
+ break;
+ case MEM_SIZE_2M:
+ info->fix.smem_len = 0x200000;
+ break;
+ case MEM_SIZE_4M:
+ info->fix.smem_len = 0x400000;
+ break;
+ case MEM_SIZE_6M:
+ info->fix.smem_len = 0x600000;
+ break;
+ case MEM_SIZE_8M:
+ info->fix.smem_len = 0x800000;
+ break;
+ default:
+ info->fix.smem_len = 0x80000;
+ }
+
+ if (M64_HAS(MAGIC_VRAM_SIZE)) {
+ if (aty_ld_le32(CNFG_STAT1, par) & 0x40000000)
+ info->fix.smem_len += 0x400000;
+ }
+
+ if (vram) {
+ info->fix.smem_len = vram * 1024;
+ par->mem_cntl &= ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS);
+ if (info->fix.smem_len <= 0x80000)
+ par->mem_cntl |= MEM_SIZE_512K;
+ else if (info->fix.smem_len <= 0x100000)
+ par->mem_cntl |= MEM_SIZE_1M;
+ else if (info->fix.smem_len <= 0x200000)
+ par->mem_cntl |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M;
+ else if (info->fix.smem_len <= 0x400000)
+ par->mem_cntl |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M;
+ else if (info->fix.smem_len <= 0x600000)
+ par->mem_cntl |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M;
+ else
+ par->mem_cntl |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M;
+ aty_st_le32(MEM_CNTL, par->mem_cntl, par);
+ }
+
+ /*
+ * Reg Block 0 (CT-compatible block) is at mmio_start
+ * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400
+ */
+ if (M64_HAS(GX)) {
+ info->fix.mmio_len = 0x400;
+ info->fix.accel = FB_ACCEL_ATI_MACH64GX;
+ } else if (M64_HAS(CT)) {
+ 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_len = 0x800;
+ info->fix.accel = FB_ACCEL_ATI_MACH64VT;
+ } else {/* GT */
+ info->fix.mmio_start -= 0x400;
+ info->fix.mmio_len = 0x800;
+ info->fix.accel = FB_ACCEL_ATI_MACH64GT;
+ }
+
+ 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);
+
+#if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
+ if (M64_HAS(INTEGRATED)) {
+ int i;
+ 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);
+ if (par->pll_ops->resume_pll)
+ par->pll_ops->resume_pll(info, &par->pll);
+
+ aty_fudge_framebuffer_len(info);
+
+ /*
+ * 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);
+
+ if (!nomtrr)
+ /*
+ * Only the ioremap_wc()'d area will get WC here
+ * since ioremap_uc() was used on the entire PCI BAR.
+ */
+ par->wc_cookie = arch_phys_wc_add(par->res_start,
+ par->res_size);
+
+ info->fbops = &atyfb_ops;
+ info->pseudo_palette = par->pseudo_palette;
+ info->flags = FBINFO_DEFAULT |
+ FBINFO_HWACCEL_IMAGEBLIT |
+ FBINFO_HWACCEL_FILLRECT |
+ FBINFO_HWACCEL_COPYAREA |
+ FBINFO_HWACCEL_YPAN |
+ FBINFO_READS_FAST;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+ if (M64_HAS(G3_PB_1_1) && of_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) |
+ USE_F32KHZ | TRISTATE_MEM_EN, par);
+ } else
+#endif
+ if (M64_HAS(MOBIL_BUS) && backlight) {
+#ifdef CONFIG_FB_ATY_BACKLIGHT
+ aty_bl_init(par);
+#endif
+ }
+
+ memset(&var, 0, sizeof(var));
+#ifdef CONFIG_PPC
+ if (machine_is(powermac)) {
+ /*
+ * FIXME: The NVRAM stuff should be put in a Mac-specific file,
+ * as it applies to all Mac video cards
+ */
+ if (mode) {
+ if (mac_find_mode(&var, info, mode, 8))
+ has_var = 1;
+ } else {
+ if (default_vmode == VMODE_CHOOSE) {
+ int sense;
+ if (M64_HAS(G3_PB_1024x768))
+ /* G3 PowerBook with 1024x768 LCD */
+ default_vmode = VMODE_1024_768_60;
+ else if (of_machine_is_compatible("iMac"))
+ default_vmode = VMODE_1024_768_75;
+ else if (of_machine_is_compatible("PowerBook2,1"))
+ /* iBook with 800x600 LCD */
+ default_vmode = VMODE_800_600_60;
+ else
+ default_vmode = VMODE_640_480_67;
+ sense = read_aty_sense(par);
+ PRINTKI("monitor sense=%x, mode %d\n",
+ sense, mac_map_monitor_sense(sense));
+ }
+ if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+ default_vmode = VMODE_640_480_60;
+ if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
+ default_cmode = CMODE_8;
+ if (!mac_vmode_to_var(default_vmode, default_cmode,
+ &var))
+ has_var = 1;
+ }
+ }
+
+#endif /* !CONFIG_PPC */
+
+#if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
+ if (!atyfb_get_timings_from_lcd(par, &var))
+ has_var = 1;
+#endif
+
+ if (mode && fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8))
+ has_var = 1;
+
+ if (!has_var)
+ var = default_var;
+
+ if (noaccel)
+ var.accel_flags &= ~FB_ACCELF_TEXT;
+ else
+ var.accel_flags |= FB_ACCELF_TEXT;
+
+ if (comp_sync != -1) {
+ if (!comp_sync)
+ var.sync &= ~FB_SYNC_COMP_HIGH_ACT;
+ else
+ var.sync |= FB_SYNC_COMP_HIGH_ACT;
+ }
+
+ if (var.yres == var.yres_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;
+ }
+
+ ret = atyfb_check_var(&var, info);
+ if (ret) {
+ PRINTKE("can't set default video mode\n");
+ goto aty_init_exit;
+ }
+
+#ifdef CONFIG_FB_ATY_CT
+ if (!noaccel && M64_HAS(INTEGRATED))
+ aty_init_cursor(info);
+#endif /* CONFIG_FB_ATY_CT */
+ info->var = var;
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret < 0)
+ goto aty_init_exit;
+
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ fb_dealloc_cmap(&info->cmap);
+ goto aty_init_exit;
+ }
+
+ fb_list = info;
+
+ PRINTKI("fb%d: %s frame buffer device on %s\n",
+ info->node, info->fix.id, par->bus_type == ISA ? "ISA" : "PCI");
+ return 0;
+
+aty_init_exit:
+ /* restore video mode */
+ aty_set_crtc(par, &par->saved_crtc);
+ par->pll_ops->set_pll(info, &par->saved_pll);
+ arch_phys_wc_del(par->wc_cookie);
+
+ return ret;
+}
+
+#if defined(CONFIG_ATARI) && !defined(MODULE)
+static int store_video_par(char *video_str, unsigned char m64_num)
+{
+ char *p;
+ unsigned long vmembase, size, guiregbase;
+
+ PRINTKI("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;
+ PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size,
+ guiregbase);
+ return 0;
+
+ mach64_invalid:
+ phys_vmembase[m64_num] = 0;
+ return -1;
+}
+#endif /* CONFIG_ATARI && !MODULE */
+
+/*
+ * Blank the display.
+ */
+
+static int atyfb_blank(int blank, struct fb_info *info)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 gen_cntl;
+
+ if (par->lock_blank || par->asleep)
+ return 0;
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table && blank > FB_BLANK_NORMAL &&
+ (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
+
+ gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
+ gen_cntl &= ~0x400004c;
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ break;
+ case FB_BLANK_NORMAL:
+ gen_cntl |= 0x4000040;
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ gen_cntl |= 0x4000048;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ gen_cntl |= 0x4000044;
+ break;
+ case FB_BLANK_POWERDOWN:
+ gen_cntl |= 0x400004c;
+ break;
+ }
+ aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par);
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ if (par->lcd_table && blank <= FB_BLANK_NORMAL &&
+ (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
+
+ return 0;
+}
+
+static void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue,
+ const struct atyfb_par *par)
+{
+ aty_st_8(DAC_W_INDEX, regno, par);
+ aty_st_8(DAC_DATA, red, par);
+ aty_st_8(DAC_DATA, green, par);
+ aty_st_8(DAC_DATA, blue, 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
+ */
+
+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);
+
+ return 0;
+}
+
+#ifdef CONFIG_PCI
+
+#ifdef __sparc__
+
+static int atyfb_setup_sparc(struct pci_dev *pdev, struct fb_info *info,
+ unsigned long addr)
+{
+ struct atyfb_par *par = info->par;
+ struct device_node *dp;
+ u32 mem, chip_id;
+ int i, j, ret;
+
+ /*
+ * Map memory-mapped registers.
+ */
+ par->ati_regbase = (void *)addr + 0x7ffc00UL;
+ info->fix.mmio_start = addr + 0x7ffc00UL;
+
+ /*
+ * Map in big-endian aperture.
+ */
+ info->screen_base = (char *) (addr + 0x800000UL);
+ info->fix.smem_start = addr + 0x800000UL;
+
+ /*
+ * 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;
+
+ par->mmap_map = kcalloc(j, sizeof(*par->mmap_map), GFP_ATOMIC);
+ if (!par->mmap_map) {
+ PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n");
+ return -ENOMEM;
+ }
+
+ 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;
+
+ 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++;
+ }
+
+ ret = correct_chipset(par);
+ if (ret)
+ 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(CNFG_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(CNFG_STAT0, par) & 7) >= SDRAM)
+ mem &= ~(0x00700000);
+ }
+ mem &= ~(0xcf80e000); /* Turn off all undocumented bits. */
+ aty_st_le32(MEM_CNTL, mem, par);
+ }
+
+ dp = pci_device_to_OF_node(pdev);
+ if (dp == of_console_device) {
+ 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 = of_getintprop_default(dp, "width", 1024);
+ crtc.vyres = of_getintprop_default(dp, "height", 768);
+ var->bits_per_pixel = of_getintprop_default(dp, "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[PLL_REF_DIV];
+
+ /*
+ * PLL Feedback Divider N (Dependent on CLOCK_CNTL):
+ */
+ N = pll_regs[VCLK0_FB_DIV + (clock_cntl & 3)];
+
+ /*
+ * PLL Post Divider P (Dependent on CLOCK_CNTL):
+ */
+ P = aty_postdividers[((pll_regs[VCLK_POST_DIV] >> ((clock_cntl & 3) << 1)) & 3) |
+ ((pll_regs[PLL_EXT_CNTL] >> (2 + (clock_cntl & 3))) & 4)];
+
+ /*
+ * 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
+static 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 = "unknown 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 = "unknown 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 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 __iomem *)bios_base);
+
+ return ret;
+}
+#endif /* __i386__ */
+
+static int 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, resource_size(rrp), "atyfb")) {
+ par->aux_start = rrp->start;
+ par->aux_size = resource_size(rrp);
+ raddr = rrp->start;
+ PRINTKI("using auxiliary register aperture\n");
+ }
+
+ info->fix.mmio_start = raddr;
+ /*
+ * By using strong UC we force the MTRR to never have an
+ * effect on the MMIO region on both non-PAT and PAT systems.
+ */
+ par->ati_regbase = ioremap_uc(info->fix.mmio_start, 0x1000);
+ if (par->ati_regbase == NULL)
+ 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;
+#endif
+
+ /* Map in frame buffer */
+ info->fix.smem_start = addr;
+
+ /*
+ * The framebuffer is not always 8 MiB, that's just the size of the
+ * PCI BAR. We temporarily abuse smem_len here to store the size
+ * of the BAR. aty_init() will later correct it to match the actual
+ * framebuffer size.
+ *
+ * On devices that don't have the auxiliary register aperture, the
+ * registers are housed at the top end of the framebuffer PCI BAR.
+ * aty_fudge_framebuffer_len() is used to reduce smem_len to not
+ * overlap with the registers.
+ */
+ info->fix.smem_len = 0x800000;
+
+ aty_fudge_framebuffer_len(info);
+
+ info->screen_base = ioremap_wc(info->fix.smem_start,
+ info->fix.smem_len);
+ if (info->screen_base == NULL) {
+ ret = -ENOMEM;
+ goto atyfb_setup_generic_fail;
+ }
+
+ ret = correct_chipset(par);
+ if (ret)
+ goto atyfb_setup_generic_fail;
+#ifdef __i386__
+ ret = init_from_bios(par);
+ if (ret)
+ 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 = NULL;
+ if (info->screen_base) {
+ iounmap(info->screen_base);
+ info->screen_base = NULL;
+ }
+ return ret;
+}
+
+#endif /* !__sparc__ */
+
+static int 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 rc = -ENOMEM;
+
+ /* 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;
+
+ /* Reserve space */
+ res_start = rp->start;
+ res_size = resource_size(rp);
+ 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;
+ par->bus_type = PCI;
+ info->fix = atyfb_fix;
+ info->device = &pdev->dev;
+ par->pci_id = pdev->device;
+ par->res_start = res_start;
+ par->res_size = res_size;
+ par->irq = pdev->irq;
+ par->pdev = pdev;
+
+ /* Setup "info" structure */
+#ifdef __sparc__
+ rc = atyfb_setup_sparc(pdev, info, addr);
+#else
+ rc = atyfb_setup_generic(pdev, info, addr);
+#endif
+ if (rc)
+ goto err_release_mem;
+
+ pci_set_drvdata(pdev, info);
+
+ /* Init chip & register framebuffer */
+ rc = aty_init(info);
+ if (rc)
+ goto err_release_io;
+
+#ifdef __sparc__
+ /*
+ * 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 = (long)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__ */
+
+ mutex_lock(&reboot_lock);
+ if (!reboot_info)
+ reboot_info = info;
+ mutex_unlock(&reboot_lock);
+
+ return 0;
+
+err_release_io:
+#ifdef __sparc__
+ 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);
+
+ release_mem_region(par->res_start, par->res_size);
+ framebuffer_release(info);
+
+ return rc;
+}
+
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_ATARI
+
+static int __init atyfb_atari_probe(void)
+{
+ struct atyfb_par *par;
+ struct fb_info *info;
+ int m64_num;
+ u32 clock_r;
+ int num_found = 0;
+
+ for (m64_num = 0; m64_num < mach64_count; m64_num++) {
+ if (!phys_vmembase[m64_num] || !phys_size[m64_num] ||
+ !phys_guiregbase[m64_num]) {
+ PRINTKI("phys_*[%d] parameters not set => "
+ "returning early. \n", m64_num);
+ continue;
+ }
+
+ info = framebuffer_alloc(sizeof(struct atyfb_par), NULL);
+ if (!info) {
+ PRINTKE("atyfb_atari_probe() can't alloc fb_info\n");
+ return -ENOMEM;
+ }
+ par = info->par;
+
+ info->fix = atyfb_fix;
+
+ 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_wc(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 = (unsigned long)par->ati_regbase; /* Fake! */
+
+ aty_st_le32(CLOCK_CNTL, 0x12345678, par);
+ clock_r = aty_ld_le32(CLOCK_CNTL, par);
+
+ switch (clock_r & 0x003F) {
+ case 0x12:
+ par->clk_wr_offset = 3; /* */
+ break;
+ case 0x34:
+ par->clk_wr_offset = 2; /* Medusa ST-IO ISA Adapter etc. */
+ break;
+ case 0x16:
+ par->clk_wr_offset = 1; /* */
+ break;
+ case 0x38:
+ par->clk_wr_offset = 0; /* Panther 1 ISA Adapter (Gerald) */
+ break;
+ }
+
+ /* Fake pci_id for correct_chipset() */
+ switch (aty_ld_le32(CNFG_CHIP_ID, par) & CFG_CHIP_TYPE) {
+ case 0x00d7:
+ par->pci_id = PCI_CHIP_MACH64GX;
+ break;
+ case 0x0057:
+ par->pci_id = PCI_CHIP_MACH64CX;
+ break;
+ default:
+ break;
+ }
+
+ if (correct_chipset(par) || aty_init(info)) {
+ iounmap(info->screen_base);
+ iounmap(par->ati_regbase);
+ framebuffer_release(info);
+ } else {
+ num_found++;
+ }
+ }
+
+ return num_found ? 0 : -ENXIO;
+}
+
+#endif /* CONFIG_ATARI */
+
+#ifdef CONFIG_PCI
+
+static void atyfb_remove(struct fb_info *info)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ /* restore video mode */
+ aty_set_crtc(par, &par->saved_crtc);
+ par->pll_ops->set_pll(info, &par->saved_pll);
+
+ unregister_framebuffer(info);
+
+#ifdef CONFIG_FB_ATY_BACKLIGHT
+ if (M64_HAS(MOBIL_BUS))
+ aty_bl_exit(info->bl_dev);
+#endif
+ arch_phys_wc_del(par->wc_cookie);
+
+#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__
+ 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);
+}
+
+
+static void atyfb_pci_remove(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+
+ mutex_lock(&reboot_lock);
+ if (reboot_info == info)
+ reboot_info = NULL;
+ mutex_unlock(&reboot_lock);
+
+ atyfb_remove(info);
+}
+
+static const struct pci_device_id atyfb_pci_tbl[] = {
+#ifdef CONFIG_FB_ATY_GX
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GX) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CX) },
+#endif /* CONFIG_FB_ATY_GX */
+
+#ifdef CONFIG_FB_ATY_CT
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CT) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64ET) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LT) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VT) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GT) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VU) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GU) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LG) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VV) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GV) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GW) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GY) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GZ) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GB) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GD) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GI) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GP) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GQ) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LB) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LD) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LI) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LP) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LQ) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GM) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GN) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GO) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GL) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GR) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GS) },
+
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LM) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LN) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LR) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LS) },
+#endif /* CONFIG_FB_ATY_CT */
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, atyfb_pci_tbl);
+
+static struct pci_driver atyfb_driver = {
+ .name = "atyfb",
+ .id_table = atyfb_pci_tbl,
+ .probe = atyfb_pci_probe,
+ .remove = atyfb_pci_remove,
+#ifdef CONFIG_PM
+ .suspend = atyfb_pci_suspend,
+ .resume = atyfb_pci_resume,
+#endif /* CONFIG_PM */
+};
+
+#endif /* CONFIG_PCI */
+
+#ifndef MODULE
+static int __init atyfb_setup(char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!strncmp(this_opt, "noaccel", 7)) {
+ noaccel = 1;
+ } else if (!strncmp(this_opt, "nomtrr", 6)) {
+ nomtrr = 1;
+ } else if (!strncmp(this_opt, "vram:", 5))
+ vram = simple_strtoul(this_opt + 5, NULL, 0);
+ else if (!strncmp(this_opt, "pll:", 4))
+ pll = simple_strtoul(this_opt + 4, NULL, 0);
+ else if (!strncmp(this_opt, "mclk:", 5))
+ mclk = simple_strtoul(this_opt + 5, NULL, 0);
+ else if (!strncmp(this_opt, "xclk:", 5))
+ xclk = simple_strtoul(this_opt+5, NULL, 0);
+ else if (!strncmp(this_opt, "comp_sync:", 10))
+ comp_sync = simple_strtoul(this_opt+10, NULL, 0);
+ else if (!strncmp(this_opt, "backlight:", 10))
+ backlight = simple_strtoul(this_opt+10, NULL, 0);
+#ifdef CONFIG_PPC
+ else if (!strncmp(this_opt, "vmode:", 6)) {
+ unsigned int vmode =
+ simple_strtoul(this_opt + 6, NULL, 0);
+ if (vmode > 0 && vmode <= VMODE_MAX)
+ default_vmode = vmode;
+ } else if (!strncmp(this_opt, "cmode:", 6)) {
+ unsigned int cmode =
+ simple_strtoul(this_opt + 6, NULL, 0);
+ switch (cmode) {
+ case 0:
+ case 8:
+ default_cmode = CMODE_8;
+ break;
+ case 15:
+ case 16:
+ default_cmode = CMODE_16;
+ break;
+ case 24:
+ case 32:
+ default_cmode = CMODE_32;
+ break;
+ }
+ }
+#endif
+#ifdef CONFIG_ATARI
+ /*
+ * Why do we need this silly Mach64 argument?
+ * We are already here because of mach64= so its redundant.
+ */
+ else if (MACH_IS_ATARI
+ && (!strncmp(this_opt, "Mach64:", 7))) {
+ static unsigned char m64_num;
+ static char mach64_str[80];
+ strlcpy(mach64_str, this_opt + 7, sizeof(mach64_str));
+ if (!store_video_par(mach64_str, m64_num)) {
+ m64_num++;
+ mach64_count = m64_num;
+ }
+ }
+#endif
+ else
+ mode = this_opt;
+ }
+ return 0;
+}
+#endif /* MODULE */
+
+static int atyfb_reboot_notify(struct notifier_block *nb,
+ unsigned long code, void *unused)
+{
+ struct atyfb_par *par;
+
+ if (code != SYS_RESTART)
+ return NOTIFY_DONE;
+
+ mutex_lock(&reboot_lock);
+
+ if (!reboot_info)
+ goto out;
+
+ if (!lock_fb_info(reboot_info))
+ goto out;
+
+ par = reboot_info->par;
+
+ /*
+ * HP OmniBook 500's BIOS doesn't like the state of the
+ * hardware after atyfb has been used. Restore the hardware
+ * to the original state to allow successful reboots.
+ */
+ aty_set_crtc(par, &par->saved_crtc);
+ par->pll_ops->set_pll(reboot_info, &par->saved_pll);
+
+ unlock_fb_info(reboot_info);
+ out:
+ mutex_unlock(&reboot_lock);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block atyfb_reboot_notifier = {
+ .notifier_call = atyfb_reboot_notify,
+};
+
+static const struct dmi_system_id atyfb_reboot_ids[] __initconst = {
+ {
+ .ident = "HP OmniBook 500",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP OmniBook PC"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "HP OmniBook 500 FA"),
+ },
+ },
+
+ { }
+};
+static bool registered_notifier = false;
+
+static int __init atyfb_init(void)
+{
+ int err1 = 1, err2 = 1;
+#ifndef MODULE
+ char *option = NULL;
+
+ if (fb_get_options("atyfb", &option))
+ return -ENODEV;
+ atyfb_setup(option);
+#endif
+
+#ifdef CONFIG_PCI
+ err1 = pci_register_driver(&atyfb_driver);
+#endif
+#ifdef CONFIG_ATARI
+ err2 = atyfb_atari_probe();
+#endif
+
+ if (err1 && err2)
+ return -ENODEV;
+
+ if (dmi_check_system(atyfb_reboot_ids)) {
+ register_reboot_notifier(&atyfb_reboot_notifier);
+ registered_notifier = true;
+ }
+
+ return 0;
+}
+
+static void __exit atyfb_exit(void)
+{
+ if (registered_notifier)
+ unregister_reboot_notifier(&atyfb_reboot_notifier);
+
+#ifdef CONFIG_PCI
+ pci_unregister_driver(&atyfb_driver);
+#endif
+}
+
+module_init(atyfb_init);
+module_exit(atyfb_exit);
+
+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");
+module_param(comp_sync, int, 0);
+MODULE_PARM_DESC(comp_sync, "Set composite sync signal to low (0) or high (1)");
+module_param(mode, charp, 0);
+MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
+module_param(nomtrr, bool, 0);
+MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
diff --git a/drivers/video/fbdev/aty/mach64_accel.c b/drivers/video/fbdev/aty/mach64_accel.c
new file mode 100644
index 000000000..3ad46255f
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_accel.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * ATI Mach64 Hardware Acceleration
+ */
+
+#include <linux/delay.h>
+#include <asm/unaligned.h>
+#include <linux/fb.h>
+#include <video/mach64.h>
+#include "atyfb.h"
+
+ /*
+ * 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 | HWCURSOR_ENABLE), par);
+ /* enable engine */
+ aty_st_le32(GEN_TEST_CNTL,
+ 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);
+}
+
+static void reset_GTC_3D_engine(const struct atyfb_par *par)
+{
+ aty_st_le32(SCALE_3D_CNTL, 0xc0, par);
+ mdelay(GTC_3D_RESET_DELAY);
+ aty_st_le32(SETUP_CNTL, 0x00, par);
+ mdelay(GTC_3D_RESET_DELAY);
+ aty_st_le32(SCALE_3D_CNTL, 0x00, par);
+ mdelay(GTC_3D_RESET_DELAY);
+}
+
+void aty_init_engine(struct atyfb_par *par, struct fb_info *info)
+{
+ u32 pitch_value;
+ u32 vxres;
+
+ /* determine modal information from global mode structure */
+ pitch_value = info->fix.line_length / (info->var.bits_per_pixel / 8);
+ vxres = 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;
+ vxres *= 3;
+ }
+
+ /* On GTC (RagePro), we need to reset the 3D engine before */
+ if (M64_HAS(RESET_3D))
+ reset_GTC_3D_engine(par);
+
+ /* Reset engine, enable, and clear any engine errors */
+ aty_reset_engine(par);
+ /* Ensure that vga page pointers are set to zero - the upper */
+ /* page pointers are set to 1 to handle overflows in the */
+ /* lower page */
+ aty_st_le32(MEM_VGA_WP_SEL, 0x00010000, par);
+ aty_st_le32(MEM_VGA_RP_SEL, 0x00010000, par);
+
+ /* ---- Setup standard engine context ---- */
+
+ /* All GUI registers here are FIFOed - therefore, wait for */
+ /* the appropriate number of empty FIFO entries */
+ wait_for_fifo(14, par);
+
+ /* enable all registers to be loaded for context loads */
+ aty_st_le32(CONTEXT_MASK, 0xFFFFFFFF, par);
+
+ /* set destination pitch to modal pitch, set offset to zero */
+ aty_st_le32(DST_OFF_PITCH, (pitch_value / 8) << 22, par);
+
+ /* zero these registers (set them to a known state) */
+ aty_st_le32(DST_Y_X, 0, par);
+ aty_st_le32(DST_HEIGHT, 0, par);
+ aty_st_le32(DST_BRES_ERR, 0, par);
+ aty_st_le32(DST_BRES_INC, 0, par);
+ aty_st_le32(DST_BRES_DEC, 0, par);
+
+ /* set destination drawing attributes */
+ aty_st_le32(DST_CNTL, DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
+ DST_X_LEFT_TO_RIGHT, par);
+
+ /* set source pitch to modal pitch, set offset to zero */
+ aty_st_le32(SRC_OFF_PITCH, (pitch_value / 8) << 22, par);
+
+ /* set these registers to a known state */
+ aty_st_le32(SRC_Y_X, 0, par);
+ aty_st_le32(SRC_HEIGHT1_WIDTH1, 1, par);
+ aty_st_le32(SRC_Y_X_START, 0, par);
+ aty_st_le32(SRC_HEIGHT2_WIDTH2, 1, par);
+
+ /* set source pixel retrieving attributes */
+ aty_st_le32(SRC_CNTL, SRC_LINE_X_LEFT_TO_RIGHT, par);
+
+ /* set host attributes */
+ wait_for_fifo(13, par);
+ aty_st_le32(HOST_CNTL, HOST_BYTE_ALIGN, par);
+
+ /* set pattern attributes */
+ aty_st_le32(PAT_REG0, 0, par);
+ aty_st_le32(PAT_REG1, 0, par);
+ aty_st_le32(PAT_CNTL, 0, par);
+
+ /* set scissors to modal size */
+ aty_st_le32(SC_LEFT, 0, par);
+ aty_st_le32(SC_TOP, 0, par);
+ aty_st_le32(SC_BOTTOM, par->crtc.vyres - 1, par);
+ aty_st_le32(SC_RIGHT, vxres - 1, par);
+
+ /* set background color to minimum value (usually BLACK) */
+ aty_st_le32(DP_BKGD_CLR, 0, par);
+
+ /* set foreground color to maximum value (usually WHITE) */
+ aty_st_le32(DP_FRGD_CLR, 0xFFFFFFFF, par);
+
+ /* set write mask to effect all pixel bits */
+ aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF, par);
+
+ /* set foreground mix to overpaint and background mix to */
+ /* no-effect */
+ aty_st_le32(DP_MIX, FRGD_MIX_S | BKGD_MIX_D, par);
+
+ /* set primary source pixel channel to foreground color */
+ /* register */
+ aty_st_le32(DP_SRC, FRGD_SRC_FRGD_CLR, par);
+
+ /* set compare functionality to false (no-effect on */
+ /* destination) */
+ wait_for_fifo(3, par);
+ aty_st_le32(CLR_CMP_CLR, 0, par);
+ aty_st_le32(CLR_CMP_MASK, 0xFFFFFFFF, par);
+ aty_st_le32(CLR_CMP_CNTL, 0, par);
+
+ /* set pixel depth */
+ wait_for_fifo(2, par);
+ aty_st_le32(DP_PIX_WIDTH, par->crtc.dp_pix_width, par);
+ 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(Z_CNTL, 0, par);
+ aty_st_le32(CRTC_INT_CNTL, aty_ld_le32(CRTC_INT_CNTL, par) & ~0x20,
+ par);
+ aty_st_le32(GUI_TRAJ_CNTL, 0x100023, par);
+
+ /* insure engine is idle before leaving */
+ wait_for_idle(par);
+}
+
+ /*
+ * Accelerated functions
+ */
+
+static inline void draw_rect(s16 x, s16 y, u16 width, u16 height,
+ struct atyfb_par *par)
+{
+ /* perform rectangle fill */
+ wait_for_fifo(2, par);
+ aty_st_le32(DST_Y_X, (x << 16) | y, par);
+ aty_st_le32(DST_HEIGHT_WIDTH, (width << 16) | height, par);
+ par->blitter_may_be_busy = 1;
+}
+
+void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ 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, rotation = 0;
+
+ if (par->asleep)
+ return;
+ if (!area->width || !area->height)
+ return;
+ if (!par->accel_flags) {
+ cfb_copyarea(info, area);
+ return;
+ }
+
+ 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 */
+ sx *= 3;
+ dx *= 3;
+ width *= 3;
+ }
+
+ if (area->sy < area->dy) {
+ dy += area->height - 1;
+ sy += area->height - 1;
+ } else
+ direction |= DST_Y_TOP_TO_BOTTOM;
+
+ if (sx < dx) {
+ dx += width - 1;
+ sx += width - 1;
+ } else
+ direction |= DST_X_LEFT_TO_RIGHT;
+
+ if (info->var.bits_per_pixel == 24) {
+ rotation = rotation24bpp(dx, direction);
+ }
+
+ wait_for_fifo(5, par);
+ aty_st_le32(DP_PIX_WIDTH, par->crtc.dp_pix_width, 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 | 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, dx = rect->dx, width = rect->width, rotation = 0;
+
+ if (par->asleep)
+ return;
+ if (!rect->width || !rect->height)
+ return;
+ if (!par->accel_flags) {
+ cfb_fillrect(info, rect);
+ return;
+ }
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR)
+ color = ((u32 *)(info->pseudo_palette))[rect->color];
+ else
+ color = rect->color;
+
+ 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);
+ }
+
+ wait_for_fifo(4, par);
+ aty_st_le32(DP_PIX_WIDTH, par->crtc.dp_pix_width, par);
+ aty_st_le32(DP_FRGD_CLR, color, par);
+ aty_st_le32(DP_SRC,
+ BKGD_SRC_BKGD_CLR | FRGD_SRC_FRGD_CLR | MONO_SRC_ONE,
+ par);
+ aty_st_le32(DST_CNTL,
+ DST_LAST_PEL | DST_Y_TOP_TO_BOTTOM |
+ 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;
+ u32 src_bytes, dx = image->dx, dy = image->dy, width = image->width;
+ u32 pix_width, 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)) {
+ cfb_imageblit(info, image);
+ return;
+ }
+
+ pix_width = par->crtc.dp_pix_width;
+
+ 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 (image->depth == 1 && 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(5, 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_BYTE_ALIGN, 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 (image->depth == 1 && 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, le32_to_cpu(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, get_unaligned_le32(pbitmap), par);
+ }
+ }
+}
diff --git a/drivers/video/fbdev/aty/mach64_ct.c b/drivers/video/fbdev/aty/mach64_ct.c
new file mode 100644
index 000000000..f87cc81f4
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_ct.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * ATI Mach64 CT/VT/GT/LT Support
+ */
+
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <video/mach64.h>
+#include "atyfb.h"
+#ifdef CONFIG_PPC
+#include <asm/machdep.h>
+#endif
+
+#undef DEBUG
+
+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_ct(int offset, u8 val, const struct atyfb_par *par)
+{
+ /* write addr byte */
+ aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) | PLL_WR_EN, par);
+ /* write the register value */
+ 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)
+ *
+ *
+ * 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.
+ */
+
+#define Maximum_DSP_PRECISION 7
+const u8 aty_postdividers[8] = {1,2,4,8,3,5,6,12};
+
+static int aty_dsp_gt(const struct fb_info *info, u32 bpp, struct pll_ct *pll)
+{
+ 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;
+ }
+#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;
+ }
+
+ /* Determine DSP precision first */
+ tmp = ((multiplier * pll->fifo_size) << vshift) / divider;
+
+ 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);
+ }
+
+ /* 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);
+ }
+
+ /* Last but not least: dsp_xclks */
+ dsp_xclks = ((multiplier << (vshift + 5)) + divider) / divider;
+
+ /* 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",
+ __func__, pll->dsp_config, pll->dsp_on_off);
+#endif
+ return 0;
+}
+
+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;
+ int pllvclk;
+
+ /* 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->vclk_post_div_real = aty_postdividers[pll->vclk_post_div];
+ // pll->vclk_post_div <<= 6;
+ pll->vclk_fb_div = q * pll->vclk_post_div_real / 8;
+ pllvclk = (1000000 * 2 * pll->vclk_fb_div) /
+ (par->ref_clk_per * pll->pll_ref_div);
+#ifdef DEBUG
+ printk("atyfb(%s): pllvclk=%d MHz, vclk=%d MHz\n",
+ __func__, pllvclk, pllvclk / pll->vclk_post_div_real);
+#endif
+ pll->pll_vclk_cntl = 0x03; /* VCLK = PLL_VCLK/VCLKx_POST */
+
+ /* Set ECP (scaler/overlay clock) divider */
+ if (par->pll_limits.ecp_max) {
+ int ecp = pllvclk / pll->vclk_post_div_real;
+ int ecp_div = 0;
+
+ while (ecp > par->pll_limits.ecp_max && ecp_div < 2) {
+ ecp >>= 1;
+ ecp_div++;
+ }
+ pll->pll_vclk_cntl |= ecp_div << 4;
+ }
+
+ return 0;
+}
+
+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;
+
+ if ((err = aty_valid_pll_ct(info, vclk_per, &pll->ct)))
+ return err;
+ if (M64_HAS(GTB_DSP) && (err = aty_dsp_gt(info, bpp, &pll->ct)))
+ return err;
+ /*aty_calc_pll_ct(info, &pll->ct);*/
+ return 0;
+}
+
+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 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", __func__, ret, ret);
+#endif
+ return ret;
+}
+
+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;
+ u8 tmp, tmp2;
+
+ lcd_gen_cntrl = 0;
+#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",
+ __func__,
+ 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",
+ __func__,
+ 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);
+
+ /* 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))
+ dll_cntl = 0x80;
+ else if (par->ram_type >= SDRAM)
+ dll_cntl = 0xa6;
+ else
+ 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
+}
+
+static void 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);
+ }
+}
+
+static int 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;
+ u32 q, 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",
+ __func__, 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 = aty_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 = aty_postdividers[xpost_div];
+ pll->ct.mclk_fb_div = q * pll->ct.xclk_post_div_real / 8;
+
+#ifdef CONFIG_PPC
+ if (machine_is(powermac)) {
+ /* Override PLL_EXT_CNTL & 0x07. */
+ pll->ct.xclk_post_div = xpost_div;
+ pll->ct.xclk_ref_div = 1;
+ }
+#endif
+
+#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",
+ __func__, 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 = aty_postdividers[mpost_div];
+ pll->ct.sclk_fb_div = q * sclk_post_div_real / 8;
+ pll->ct.spll_cntl2 = mpost_div << 4;
+#ifdef DEBUG
+ pllsclk = (1000000 * 2 * pll->ct.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",
+ __func__, pllsclk, pllsclk / sclk_post_div_real);
+#endif
+ }
+
+ /* Disable the extra precision pixel clock controls since we do not use them. */
+ pll->ct.ext_vpll_cntl = aty_ld_pll_ct(EXT_VPLL_CNTL, par);
+ pll->ct.ext_vpll_cntl &= ~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC);
+
+ return 0;
+}
+
+static void aty_resume_pll_ct(const struct fb_info *info,
+ union aty_pll *pll)
+{
+ struct atyfb_par *par = info->par;
+
+ if (par->mclk_per != par->xclk_per) {
+ /*
+ * 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, pll->ct.sclk_fb_div, par);
+ aty_st_pll_ct(SPLL_CNTL2, pll->ct.spll_cntl2, par);
+ /*
+ * SCLK has been started. Wait for the PLL to lock. 5 ms
+ * should be enough according to mach64 programmer's guide.
+ */
+ mdelay(5);
+ }
+
+ 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);
+ aty_st_pll_ct(EXT_VPLL_CNTL, pll->ct.ext_vpll_cntl, par);
+}
+
+static int dummy(void)
+{
+ return 0;
+}
+
+const struct aty_dac_ops aty_dac_ct = {
+ .set_dac = (void *) dummy,
+};
+
+const struct aty_pll_ops aty_pll_ct = {
+ .var_to_pll = aty_var_to_pll_ct,
+ .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,
+ .resume_pll = aty_resume_pll_ct,
+};
diff --git a/drivers/video/fbdev/aty/mach64_cursor.c b/drivers/video/fbdev/aty/mach64_cursor.c
new file mode 100644
index 000000000..4cde25eab
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_cursor.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ATI Mach64 CT/VT/GT/LT Cursor Support
+ */
+
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include "../core/fb_draw.h"
+
+#include <asm/io.h>
+
+#ifdef __sparc__
+#include <asm/fbio.h>
+#endif
+
+#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 appropriate line in the cursor
+ * definitation and CUR_VERT_POSN must be saturated to zero.
+ */
+
+ /*
+ * Hardware Cursor support.
+ */
+static const u8 cursor_bits_lookup[16] = {
+ 0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
+ 0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
+};
+
+static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u16 xoff, yoff;
+ int x, y, h;
+
+#ifdef __sparc__
+ if (par->mmaped)
+ return -EPERM;
+#endif
+ if (par->asleep)
+ return -EPERM;
+
+ wait_for_fifo(1, par);
+ if (cursor->enable)
+ aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par)
+ | HWCURSOR_ENABLE, par);
+ else
+ aty_st_le32(GEN_TEST_CNTL, aty_ld_le32(GEN_TEST_CNTL, par)
+ & ~HWCURSOR_ENABLE, par);
+
+ /* set position */
+ if (cursor->set & FB_CUR_SETPOS) {
+ x = cursor->image.dx - cursor->hot.x - info->var.xoffset;
+ if (x < 0) {
+ xoff = -x;
+ x = 0;
+ } else {
+ xoff = 0;
+ }
+
+ y = cursor->image.dy - cursor->hot.y - info->var.yoffset;
+ if (y < 0) {
+ yoff = -y;
+ y = 0;
+ } else {
+ yoff = 0;
+ }
+
+ h = cursor->image.height;
+
+ /*
+ * In doublescan mode, the cursor location
+ * and heigh also needs to be doubled.
+ */
+ if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN) {
+ y<<=1;
+ h<<=1;
+ }
+ wait_for_fifo(3, par);
+ aty_st_le32(CUR_OFFSET, (info->fix.smem_len >> 3) + (yoff << 1), par);
+ aty_st_le32(CUR_HORZ_VERT_OFF,
+ ((u32) (64 - h + yoff) << 16) | xoff, par);
+ aty_st_le32(CUR_HORZ_VERT_POSN, ((u32) y << 16) | x, par);
+ }
+
+ /* Set color map */
+ if (cursor->set & FB_CUR_SETCMAP) {
+ u32 fg_idx, bg_idx, fg, bg;
+
+ fg_idx = cursor->image.fg_color;
+ bg_idx = cursor->image.bg_color;
+
+ fg = ((info->cmap.red[fg_idx] & 0xff) << 24) |
+ ((info->cmap.green[fg_idx] & 0xff) << 16) |
+ ((info->cmap.blue[fg_idx] & 0xff) << 8) | 0xff;
+
+ bg = ((info->cmap.red[bg_idx] & 0xff) << 24) |
+ ((info->cmap.green[bg_idx] & 0xff) << 16) |
+ ((info->cmap.blue[bg_idx] & 0xff) << 8);
+
+ wait_for_fifo(2, par);
+ aty_st_le32(CUR_CLR0, bg, par);
+ aty_st_le32(CUR_CLR1, fg, par);
+ }
+
+ if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
+ u8 *src = (u8 *)cursor->image.data;
+ u8 *msk = (u8 *)cursor->mask;
+ u8 __iomem *dst = (u8 __iomem *)info->sprite.addr;
+ unsigned int width = (cursor->image.width + 7) >> 3;
+ unsigned int height = 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++) {
+ u16 l = 0xaaaa;
+ b = *src++;
+ m = *msk++;
+ switch (cursor->rop) {
+ case ROP_XOR:
+ // Upper 4 bits of mask data
+ l = cursor_bits_lookup[(b ^ m) >> 4] |
+ // Lower 4 bits of mask
+ (cursor_bits_lookup[(b ^ m) & 0x0f] << 8);
+ break;
+ case ROP_COPY:
+ // Upper 4 bits of mask data
+ l = cursor_bits_lookup[(b & m) >> 4] |
+ // Lower 4 bits of mask
+ (cursor_bits_lookup[(b & m) & 0x0f] << 8);
+ break;
+ }
+ /*
+ * If cursor size is not a multiple of 8 characters
+ * we must pad it with transparent pattern (0xaaaa).
+ */
+ if ((j + 1) * 8 > cursor->image.width) {
+ l = comp(l, 0xaaaa,
+ (1 << ((cursor->image.width & 7) * 2)) - 1);
+ }
+ fb_writeb(l & 0xff, dst++);
+ fb_writeb(l >> 8, dst++);
+ }
+ dst += offset;
+ }
+ }
+
+ return 0;
+}
+
+int aty_init_cursor(struct fb_info *info)
+{
+ unsigned long addr;
+
+ info->fix.smem_len -= PAGE_SIZE;
+
+#ifdef __sparc__
+ addr = (unsigned long) info->screen_base - 0x800000 + info->fix.smem_len;
+ info->sprite.addr = (u8 *) addr;
+#else
+#ifdef __BIG_ENDIAN
+ addr = info->fix.smem_start - 0x800000 + info->fix.smem_len;
+ info->sprite.addr = (u8 *) ioremap(addr, 1024);
+#else
+ addr = (unsigned long) info->screen_base + info->fix.smem_len;
+ info->sprite.addr = (u8 *) addr;
+#endif
+#endif
+ 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;
+
+ info->fbops->fb_cursor = atyfb_cursor;
+
+ return 0;
+}
+
diff --git a/drivers/video/fbdev/aty/mach64_gx.c b/drivers/video/fbdev/aty/mach64_gx.c
new file mode 100644
index 000000000..27cb65fa2
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_gx.c
@@ -0,0 +1,911 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * ATI Mach64 GX Support
+ */
+
+#include <linux/delay.h>
+#include <linux/fb.h>
+
+#include <asm/io.h>
+
+#include <video/mach64.h>
+#include "atyfb.h"
+
+/* Definitions for the ICS 2595 == ATI 18818_1 Clockchip */
+
+#define REF_FREQ_2595 1432 /* 14.33 MHz (exact 14.31818) */
+#define REF_DIV_2595 46 /* really 43 on ICS 2595 !!! */
+ /* ohne Prescaler */
+#define MAX_FREQ_2595 15938 /* 159.38 MHz (really 170.486) */
+#define MIN_FREQ_2595 8000 /* 80.00 MHz ( 85.565) */
+ /* mit Prescaler 2, 4, 8 */
+#define ABS_MIN_FREQ_2595 1000 /* 10.00 MHz (really 10.697) */
+#define N_ADJ_2595 257
+
+#define STOP_BITS_2595 0x1800
+
+
+#define MIN_N_408 2
+
+#define MIN_N_1703 6
+
+#define MIN_M 2
+#define MAX_M 30
+#define MIN_N 35
+#define MAX_N 255-8
+
+
+ /*
+ * Support Functions
+ */
+
+static void aty_dac_waste4(const struct atyfb_par *par)
+{
+ (void) aty_ld_8(DAC_REGS, par);
+
+ (void) aty_ld_8(DAC_REGS + 2, par);
+ (void) aty_ld_8(DAC_REGS + 2, par);
+ (void) aty_ld_8(DAC_REGS + 2, par);
+ (void) aty_ld_8(DAC_REGS + 2, par);
+}
+
+static void aty_StrobeClock(const struct atyfb_par *par)
+{
+ u8 tmp;
+
+ udelay(26);
+
+ tmp = aty_ld_8(CLOCK_CNTL, par);
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset, tmp | CLOCK_STROBE, par);
+ return;
+}
+
+
+ /*
+ * IBM RGB514 DAC and Clock Chip
+ */
+
+static void aty_st_514(int offset, u8 val, const struct atyfb_par *par)
+{
+ aty_st_8(DAC_CNTL, 1, par);
+ /* right addr byte */
+ aty_st_8(DAC_W_INDEX, offset & 0xff, par);
+ /* left addr byte */
+ aty_st_8(DAC_DATA, (offset >> 8) & 0xff, par);
+ aty_st_8(DAC_MASK, val, par);
+ aty_st_8(DAC_CNTL, 0, par);
+}
+
+static int aty_set_dac_514(const struct fb_info *info,
+ const union aty_pll *pll, u32 bpp, u32 accel)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ static struct {
+ u8 pixel_dly;
+ u8 misc2_cntl;
+ u8 pixel_rep;
+ u8 pixel_cntl_index;
+ u8 pixel_cntl_v1;
+ } tab[3] = {
+ {
+ 0, 0x41, 0x03, 0x71, 0x45}, /* 8 bpp */
+ {
+ 0, 0x45, 0x04, 0x0c, 0x01}, /* 555 */
+ {
+ 0, 0x45, 0x06, 0x0e, 0x00}, /* XRGB */
+ };
+ int i;
+
+ switch (bpp) {
+ case 8:
+ default:
+ i = 0;
+ break;
+ case 16:
+ i = 1;
+ break;
+ case 32:
+ i = 2;
+ break;
+ }
+ aty_st_514(0x90, 0x00, par); /* VRAM Mask Low */
+ aty_st_514(0x04, tab[i].pixel_dly, par); /* Horizontal Sync Control */
+ aty_st_514(0x05, 0x00, par); /* Power Management */
+ aty_st_514(0x02, 0x01, par); /* Misc Clock Control */
+ aty_st_514(0x71, tab[i].misc2_cntl, par); /* Misc Control 2 */
+ aty_st_514(0x0a, tab[i].pixel_rep, par); /* Pixel Format */
+ aty_st_514(tab[i].pixel_cntl_index, tab[i].pixel_cntl_v1, par);
+ /* Misc Control 2 / 16 BPP Control / 32 BPP Control */
+ return 0;
+}
+
+static int aty_var_to_pll_514(const struct fb_info *info, u32 vclk_per,
+ u32 bpp, union aty_pll *pll)
+{
+ /*
+ * FIXME: use real calculations instead of using fixed values from the old
+ * driver
+ */
+ static struct {
+ u32 limit; /* pixlock rounding limit (arbitrary) */
+ u8 m; /* (df<<6) | vco_div_count */
+ u8 n; /* ref_div_count */
+ } RGB514_clocks[7] = {
+ {
+ 8000, (3 << 6) | 20, 9}, /* 7395 ps / 135.2273 MHz */
+ {
+ 10000, (1 << 6) | 19, 3}, /* 9977 ps / 100.2273 MHz */
+ {
+ 13000, (1 << 6) | 2, 3}, /* 12509 ps / 79.9432 MHz */
+ {
+ 14000, (2 << 6) | 8, 7}, /* 13394 ps / 74.6591 MHz */
+ {
+ 16000, (1 << 6) | 44, 6}, /* 15378 ps / 65.0284 MHz */
+ {
+ 25000, (1 << 6) | 15, 5}, /* 17460 ps / 57.2727 MHz */
+ {
+ 50000, (0 << 6) | 53, 7}, /* 33145 ps / 30.1705 MHz */
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(RGB514_clocks); i++)
+ if (vclk_per <= RGB514_clocks[i].limit) {
+ pll->ibm514.m = RGB514_clocks[i].m;
+ pll->ibm514.n = RGB514_clocks[i].n;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static u32 aty_pll_514_to_var(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u8 df, vco_div_count, ref_div_count;
+
+ df = pll->ibm514.m >> 6;
+ vco_div_count = pll->ibm514.m & 0x3f;
+ ref_div_count = pll->ibm514.n;
+
+ return ((par->ref_clk_per * ref_div_count) << (3 - df))/
+ (vco_div_count + 65);
+}
+
+static void aty_set_pll_514(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ aty_st_514(0x06, 0x02, par); /* DAC Operation */
+ aty_st_514(0x10, 0x01, par); /* PLL Control 1 */
+ aty_st_514(0x70, 0x01, par); /* Misc Control 1 */
+ aty_st_514(0x8f, 0x1f, par); /* PLL Ref. Divider Input */
+ aty_st_514(0x03, 0x00, par); /* Sync Control */
+ aty_st_514(0x05, 0x00, par); /* Power Management */
+ aty_st_514(0x20, pll->ibm514.m, par); /* F0 / M0 */
+ aty_st_514(0x21, pll->ibm514.n, par); /* F1 / N0 */
+}
+
+const struct aty_dac_ops aty_dac_ibm514 = {
+ .set_dac = aty_set_dac_514,
+};
+
+const struct aty_pll_ops aty_pll_ibm514 = {
+ .var_to_pll = aty_var_to_pll_514,
+ .pll_to_var = aty_pll_514_to_var,
+ .set_pll = aty_set_pll_514,
+};
+
+
+ /*
+ * ATI 68860-B DAC
+ */
+
+static int aty_set_dac_ATI68860_B(const struct fb_info *info,
+ const union aty_pll *pll, u32 bpp,
+ u32 accel)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 gModeReg, devSetupRegA, temp, mask;
+
+ gModeReg = 0;
+ devSetupRegA = 0;
+
+ switch (bpp) {
+ case 8:
+ gModeReg = 0x83;
+ devSetupRegA =
+ 0x60 | 0x00 /*(info->mach64DAC8Bit ? 0x00 : 0x01) */ ;
+ break;
+ case 15:
+ gModeReg = 0xA0;
+ devSetupRegA = 0x60;
+ break;
+ case 16:
+ gModeReg = 0xA1;
+ devSetupRegA = 0x60;
+ break;
+ case 24:
+ gModeReg = 0xC0;
+ devSetupRegA = 0x60;
+ break;
+ case 32:
+ gModeReg = 0xE3;
+ devSetupRegA = 0x60;
+ break;
+ }
+
+ if (!accel) {
+ gModeReg = 0x80;
+ devSetupRegA = 0x61;
+ }
+
+ temp = aty_ld_8(DAC_CNTL, par);
+ aty_st_8(DAC_CNTL, (temp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3,
+ par);
+
+ aty_st_8(DAC_REGS + 2, 0x1D, par);
+ aty_st_8(DAC_REGS + 3, gModeReg, par);
+ aty_st_8(DAC_REGS, 0x02, par);
+
+ 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 < ONE_MB)
+ mask = 0x04;
+ else if (info->fix.smem_len == ONE_MB)
+ mask = 0x08;
+ else
+ mask = 0x0C;
+
+ /* The following assumes that the BIOS has correctly set R7 of the
+ * Device Setup Register A at boot time.
+ */
+#define A860_DELAY_L 0x80
+
+ temp = aty_ld_8(DAC_REGS, par);
+ aty_st_8(DAC_REGS, (devSetupRegA | mask) | (temp & A860_DELAY_L),
+ par);
+ temp = aty_ld_8(DAC_CNTL, par);
+ aty_st_8(DAC_CNTL, (temp & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3)),
+ par);
+
+ aty_st_le32(BUS_CNTL, 0x890e20f1, par);
+ aty_st_le32(DAC_CNTL, 0x47052100, par);
+ return 0;
+}
+
+const struct aty_dac_ops aty_dac_ati68860b = {
+ .set_dac = aty_set_dac_ATI68860_B,
+};
+
+
+ /*
+ * AT&T 21C498 DAC
+ */
+
+static int aty_set_dac_ATT21C498(const struct fb_info *info,
+ const union aty_pll *pll, u32 bpp,
+ u32 accel)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 dotClock;
+ int muxmode = 0;
+ int DACMask = 0;
+
+ dotClock = 100000000 / pll->ics2595.period_in_ps;
+
+ switch (bpp) {
+ case 8:
+ if (dotClock > 8000) {
+ DACMask = 0x24;
+ muxmode = 1;
+ } else
+ DACMask = 0x04;
+ break;
+ case 15:
+ DACMask = 0x16;
+ break;
+ case 16:
+ DACMask = 0x36;
+ break;
+ case 24:
+ DACMask = 0xE6;
+ break;
+ case 32:
+ DACMask = 0xE6;
+ break;
+ }
+
+ if (1 /* info->mach64DAC8Bit */ )
+ DACMask |= 0x02;
+
+ aty_dac_waste4(par);
+ aty_st_8(DAC_REGS + 2, DACMask, par);
+
+ aty_st_le32(BUS_CNTL, 0x890e20f1, par);
+ aty_st_le32(DAC_CNTL, 0x00072000, par);
+ return muxmode;
+}
+
+const struct aty_dac_ops aty_dac_att21c498 = {
+ .set_dac = aty_set_dac_ATT21C498,
+};
+
+
+ /*
+ * ATI 18818 / ICS 2595 Clock Chip
+ */
+
+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;
+ u32 post_divider;
+
+ /* Calculate the programming word */
+ MHz100 = 100000000 / vclk_per;
+
+ program_bits = -1;
+ post_divider = 1;
+
+ if (MHz100 > MAX_FREQ_2595) {
+ MHz100 = MAX_FREQ_2595;
+ return -EINVAL;
+ } else if (MHz100 < ABS_MIN_FREQ_2595) {
+ program_bits = 0; /* MHz100 = 257 */
+ return -EINVAL;
+ } else {
+ while (MHz100 < MIN_FREQ_2595) {
+ MHz100 *= 2;
+ post_divider *= 2;
+ }
+ }
+ MHz100 *= 1000;
+ MHz100 = (REF_DIV_2595 * MHz100) / REF_FREQ_2595;
+
+ MHz100 += 500; /* + 0.5 round */
+ MHz100 /= 1000;
+
+ if (program_bits == -1) {
+ program_bits = MHz100 - N_ADJ_2595;
+ switch (post_divider) {
+ case 1:
+ program_bits |= 0x0600;
+ break;
+ case 2:
+ program_bits |= 0x0400;
+ break;
+ case 4:
+ program_bits |= 0x0200;
+ break;
+ case 8:
+ default:
+ break;
+ }
+ }
+
+ program_bits |= STOP_BITS_2595;
+
+ pll->ics2595.program_bits = program_bits;
+ pll->ics2595.locationAddr = 0;
+ pll->ics2595.post_divider = post_divider;
+ pll->ics2595.period_in_ps = vclk_per;
+
+ return 0;
+}
+
+static u32 aty_pll_18818_to_var(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ return (pll->ics2595.period_in_ps); /* default for now */
+}
+
+static void aty_ICS2595_put1bit(u8 data, const struct atyfb_par *par)
+{
+ u8 tmp;
+
+ data &= 0x01;
+ tmp = aty_ld_8(CLOCK_CNTL, par);
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
+ (tmp & ~0x04) | (data << 2), par);
+
+ tmp = aty_ld_8(CLOCK_CNTL, par);
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (0 << 3),
+ par);
+
+ aty_StrobeClock(par);
+
+ tmp = aty_ld_8(CLOCK_CNTL, par);
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset, (tmp & ~0x08) | (1 << 3),
+ par);
+
+ aty_StrobeClock(par);
+ return;
+}
+
+static void aty_set_pll18818(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 program_bits;
+ u32 locationAddr;
+
+ u32 i;
+
+ u8 old_clock_cntl;
+ u8 old_crtc_ext_disp;
+
+ old_clock_cntl = aty_ld_8(CLOCK_CNTL, par);
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par);
+
+ old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+ aty_st_8(CRTC_GEN_CNTL + 3,
+ old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+ mdelay(15); /* delay for 50 (15) ms */
+
+ program_bits = pll->ics2595.program_bits;
+ locationAddr = pll->ics2595.locationAddr;
+
+ /* Program the clock chip */
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 0, par); /* Strobe = 0 */
+ aty_StrobeClock(par);
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset, 1, par); /* Strobe = 0 */
+ aty_StrobeClock(par);
+
+ aty_ICS2595_put1bit(1, par); /* Send start bits */
+ aty_ICS2595_put1bit(0, par); /* Start bit */
+ aty_ICS2595_put1bit(0, par); /* Read / ~Write */
+
+ for (i = 0; i < 5; i++) { /* Location 0..4 */
+ aty_ICS2595_put1bit(locationAddr & 1, par);
+ locationAddr >>= 1;
+ }
+
+ for (i = 0; i < 8 + 1 + 2 + 2; i++) {
+ aty_ICS2595_put1bit(program_bits & 1, par);
+ program_bits >>= 1;
+ }
+
+ mdelay(1); /* delay for 1 ms */
+
+ (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
+ aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
+ old_clock_cntl | CLOCK_STROBE, par);
+
+ mdelay(50); /* delay for 50 (15) ms */
+ aty_st_8(CLOCK_CNTL + par->clk_wr_offset,
+ ((pll->ics2595.locationAddr & 0x0F) | CLOCK_STROBE), par);
+ return;
+}
+
+const struct aty_pll_ops aty_pll_ati18818_1 = {
+ .var_to_pll = aty_var_to_pll_18818,
+ .pll_to_var = aty_pll_18818_to_var,
+ .set_pll = aty_set_pll18818,
+};
+
+
+ /*
+ * STG 1703 Clock Chip
+ */
+
+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;
+ /* u32 post_divider; */
+ u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
+ u32 temp, tempB;
+ u16 remainder, preRemainder;
+ short divider = 0, tempA;
+
+ /* Calculate the programming word */
+ mhz100 = 100000000 / vclk_per;
+ mach64MinFreq = MIN_FREQ_2595;
+ mach64MaxFreq = MAX_FREQ_2595;
+ mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */
+
+ /* Calculate program word */
+ if (mhz100 == 0)
+ program_bits = 0xE0;
+ else {
+ if (mhz100 < mach64MinFreq)
+ mhz100 = mach64MinFreq;
+ if (mhz100 > mach64MaxFreq)
+ mhz100 = mach64MaxFreq;
+
+ divider = 0;
+ while (mhz100 < (mach64MinFreq << 3)) {
+ mhz100 <<= 1;
+ divider += 0x20;
+ }
+
+ temp = (unsigned int) (mhz100);
+ temp = (unsigned int) (temp * (MIN_N_1703 + 2));
+ temp -= (short) (mach64RefFreq << 1);
+
+ tempA = MIN_N_1703;
+ preRemainder = 0xffff;
+
+ do {
+ tempB = temp;
+ remainder = tempB % mach64RefFreq;
+ tempB = tempB / mach64RefFreq;
+
+ if ((tempB & 0xffff) <= 127
+ && (remainder <= preRemainder)) {
+ preRemainder = remainder;
+ divider &= ~0x1f;
+ divider |= tempA;
+ divider =
+ (divider & 0x00ff) +
+ ((tempB & 0xff) << 8);
+ }
+
+ temp += mhz100;
+ tempA++;
+ } while (tempA <= (MIN_N_1703 << 1));
+
+ program_bits = divider;
+ }
+
+ pll->ics2595.program_bits = program_bits;
+ pll->ics2595.locationAddr = 0;
+ pll->ics2595.post_divider = divider; /* fuer nix */
+ pll->ics2595.period_in_ps = vclk_per;
+
+ return 0;
+}
+
+static u32 aty_pll_1703_to_var(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ return (pll->ics2595.period_in_ps); /* default for now */
+}
+
+static void aty_set_pll_1703(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 program_bits;
+ u32 locationAddr;
+
+ char old_crtc_ext_disp;
+
+ old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+ aty_st_8(CRTC_GEN_CNTL + 3,
+ old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+ program_bits = pll->ics2595.program_bits;
+ locationAddr = pll->ics2595.locationAddr;
+
+ /* Program clock */
+ aty_dac_waste4(par);
+
+ (void) aty_ld_8(DAC_REGS + 2, par);
+ aty_st_8(DAC_REGS + 2, (locationAddr << 1) + 0x20, par);
+ aty_st_8(DAC_REGS + 2, 0, par);
+ aty_st_8(DAC_REGS + 2, (program_bits & 0xFF00) >> 8, par);
+ aty_st_8(DAC_REGS + 2, (program_bits & 0xFF), par);
+
+ (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
+ aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+ return;
+}
+
+const struct aty_pll_ops aty_pll_stg1703 = {
+ .var_to_pll = aty_var_to_pll_1703,
+ .pll_to_var = aty_pll_1703_to_var,
+ .set_pll = aty_set_pll_1703,
+};
+
+
+ /*
+ * Chrontel 8398 Clock Chip
+ */
+
+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;
+
+ u32 mhz100; /* in 0.01 MHz */
+ u32 program_bits;
+ /* u32 post_divider; */
+ u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
+ u16 m, n, k = 0, save_m, save_n, twoToKth;
+
+ /* Calculate the programming word */
+ mhz100 = 100000000 / vclk_per;
+ mach64MinFreq = MIN_FREQ_2595;
+ mach64MaxFreq = MAX_FREQ_2595;
+ mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */
+
+ save_m = 0;
+ save_n = 0;
+
+ /* Calculate program word */
+ if (mhz100 == 0)
+ program_bits = 0xE0;
+ else {
+ if (mhz100 < mach64MinFreq)
+ mhz100 = mach64MinFreq;
+ if (mhz100 > mach64MaxFreq)
+ mhz100 = mach64MaxFreq;
+
+ longMHz100 = mhz100 * 256 / 100; /* 8 bit scale this */
+
+ while (mhz100 < (mach64MinFreq << 3)) {
+ mhz100 <<= 1;
+ k++;
+ }
+
+ twoToKth = 1 << k;
+ diff = 0;
+ preDiff = 0xFFFFFFFF;
+
+ for (m = MIN_M; m <= MAX_M; m++) {
+ for (n = MIN_N; n <= MAX_N; n++) {
+ tempA = 938356; /* 14.31818 * 65536 */
+ tempA *= (n + 8); /* 43..256 */
+ tempB = twoToKth * 256;
+ tempB *= (m + 2); /* 4..32 */
+ fOut = tempA / tempB; /* 8 bit scale */
+
+ if (longMHz100 > fOut)
+ diff = longMHz100 - fOut;
+ else
+ diff = fOut - longMHz100;
+
+ if (diff < preDiff) {
+ save_m = m;
+ save_n = n;
+ preDiff = diff;
+ }
+ }
+ }
+
+ program_bits = (k << 6) + (save_m) + (save_n << 8);
+ }
+
+ pll->ics2595.program_bits = program_bits;
+ pll->ics2595.locationAddr = 0;
+ pll->ics2595.post_divider = 0;
+ pll->ics2595.period_in_ps = vclk_per;
+
+ return 0;
+}
+
+static u32 aty_pll_8398_to_var(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ return (pll->ics2595.period_in_ps); /* default for now */
+}
+
+static void aty_set_pll_8398(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 program_bits;
+ u32 locationAddr;
+
+ char old_crtc_ext_disp;
+ char tmp;
+
+ old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+ aty_st_8(CRTC_GEN_CNTL + 3,
+ old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+ program_bits = pll->ics2595.program_bits;
+ locationAddr = pll->ics2595.locationAddr;
+
+ /* Program clock */
+ tmp = aty_ld_8(DAC_CNTL, par);
+ aty_st_8(DAC_CNTL, tmp | DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3, par);
+
+ aty_st_8(DAC_REGS, locationAddr, par);
+ aty_st_8(DAC_REGS + 1, (program_bits & 0xff00) >> 8, par);
+ aty_st_8(DAC_REGS + 1, (program_bits & 0xff), par);
+
+ tmp = aty_ld_8(DAC_CNTL, par);
+ aty_st_8(DAC_CNTL, (tmp & ~DAC_EXT_SEL_RS2) | DAC_EXT_SEL_RS3,
+ par);
+
+ (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
+ aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+
+ return;
+}
+
+const struct aty_pll_ops aty_pll_ch8398 = {
+ .var_to_pll = aty_var_to_pll_8398,
+ .pll_to_var = aty_pll_8398_to_var,
+ .set_pll = aty_set_pll_8398,
+};
+
+
+ /*
+ * AT&T 20C408 Clock Chip
+ */
+
+static int aty_var_to_pll_408(const struct fb_info *info, u32 vclk_per,
+ u32 bpp, union aty_pll *pll)
+{
+ u32 mhz100; /* in 0.01 MHz */
+ u32 program_bits;
+ /* u32 post_divider; */
+ u32 mach64MinFreq, mach64MaxFreq, mach64RefFreq;
+ u32 temp, tempB;
+ u16 remainder, preRemainder;
+ short divider = 0, tempA;
+
+ /* Calculate the programming word */
+ mhz100 = 100000000 / vclk_per;
+ mach64MinFreq = MIN_FREQ_2595;
+ mach64MaxFreq = MAX_FREQ_2595;
+ mach64RefFreq = REF_FREQ_2595; /* 14.32 MHz */
+
+ /* Calculate program word */
+ if (mhz100 == 0)
+ program_bits = 0xFF;
+ else {
+ if (mhz100 < mach64MinFreq)
+ mhz100 = mach64MinFreq;
+ if (mhz100 > mach64MaxFreq)
+ mhz100 = mach64MaxFreq;
+
+ while (mhz100 < (mach64MinFreq << 3)) {
+ mhz100 <<= 1;
+ divider += 0x40;
+ }
+
+ temp = (unsigned int) mhz100;
+ temp = (unsigned int) (temp * (MIN_N_408 + 2));
+ temp -= ((short) (mach64RefFreq << 1));
+
+ tempA = MIN_N_408;
+ preRemainder = 0xFFFF;
+
+ do {
+ tempB = temp;
+ remainder = tempB % mach64RefFreq;
+ tempB = tempB / mach64RefFreq;
+ if (((tempB & 0xFFFF) <= 255)
+ && (remainder <= preRemainder)) {
+ preRemainder = remainder;
+ divider &= ~0x3f;
+ divider |= tempA;
+ divider =
+ (divider & 0x00FF) +
+ ((tempB & 0xFF) << 8);
+ }
+ temp += mhz100;
+ tempA++;
+ } while (tempA <= 32);
+
+ program_bits = divider;
+ }
+
+ pll->ics2595.program_bits = program_bits;
+ pll->ics2595.locationAddr = 0;
+ pll->ics2595.post_divider = divider; /* fuer nix */
+ pll->ics2595.period_in_ps = vclk_per;
+
+ return 0;
+}
+
+static u32 aty_pll_408_to_var(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ return (pll->ics2595.period_in_ps); /* default for now */
+}
+
+static void aty_set_pll_408(const struct fb_info *info,
+ const union aty_pll *pll)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+ u32 program_bits;
+ u32 locationAddr;
+
+ u8 tmpA, tmpB, tmpC;
+ char old_crtc_ext_disp;
+
+ old_crtc_ext_disp = aty_ld_8(CRTC_GEN_CNTL + 3, par);
+ aty_st_8(CRTC_GEN_CNTL + 3,
+ old_crtc_ext_disp | (CRTC_EXT_DISP_EN >> 24), par);
+
+ program_bits = pll->ics2595.program_bits;
+ locationAddr = pll->ics2595.locationAddr;
+
+ /* Program clock */
+ aty_dac_waste4(par);
+ tmpB = aty_ld_8(DAC_REGS + 2, par) | 1;
+ aty_dac_waste4(par);
+ aty_st_8(DAC_REGS + 2, tmpB, par);
+
+ tmpA = tmpB;
+ tmpC = tmpA;
+ tmpA |= 8;
+ tmpB = 1;
+
+ aty_st_8(DAC_REGS, tmpB, par);
+ aty_st_8(DAC_REGS + 2, tmpA, par);
+
+ udelay(400); /* delay for 400 us */
+
+ locationAddr = (locationAddr << 2) + 0x40;
+ tmpB = locationAddr;
+ tmpA = program_bits >> 8;
+
+ aty_st_8(DAC_REGS, tmpB, par);
+ aty_st_8(DAC_REGS + 2, tmpA, par);
+
+ tmpB = locationAddr + 1;
+ tmpA = (u8) program_bits;
+
+ aty_st_8(DAC_REGS, tmpB, par);
+ aty_st_8(DAC_REGS + 2, tmpA, par);
+
+ tmpB = locationAddr + 2;
+ tmpA = 0x77;
+
+ aty_st_8(DAC_REGS, tmpB, par);
+ aty_st_8(DAC_REGS + 2, tmpA, par);
+
+ udelay(400); /* delay for 400 us */
+ tmpA = tmpC & (~(1 | 8));
+ tmpB = 1;
+
+ aty_st_8(DAC_REGS, tmpB, par);
+ aty_st_8(DAC_REGS + 2, tmpA, par);
+
+ (void) aty_ld_8(DAC_REGS, par); /* Clear DAC Counter */
+ aty_st_8(CRTC_GEN_CNTL + 3, old_crtc_ext_disp, par);
+ return;
+}
+
+const struct aty_pll_ops aty_pll_att20c408 = {
+ .var_to_pll = aty_var_to_pll_408,
+ .pll_to_var = aty_pll_408_to_var,
+ .set_pll = aty_set_pll_408,
+};
+
+
+ /*
+ * Unsupported DAC and Clock Chip
+ */
+
+static int aty_set_dac_unsupported(const struct fb_info *info,
+ const union aty_pll *pll, u32 bpp,
+ u32 accel)
+{
+ struct atyfb_par *par = (struct atyfb_par *) info->par;
+
+ aty_st_le32(BUS_CNTL, 0x890e20f1, par);
+ aty_st_le32(DAC_CNTL, 0x47052100, par);
+ /* new in 2.2.3p1 from Geert. ???????? */
+ aty_st_le32(BUS_CNTL, 0x590e10ff, par);
+ aty_st_le32(DAC_CNTL, 0x47012100, par);
+ return 0;
+}
+
+static int dummy(void)
+{
+ return 0;
+}
+
+const struct aty_dac_ops aty_dac_unsupported = {
+ .set_dac = aty_set_dac_unsupported,
+};
+
+const struct aty_pll_ops aty_pll_unsupported = {
+ .var_to_pll = (void *) dummy,
+ .pll_to_var = (void *) dummy,
+ .set_pll = (void *) dummy,
+};
diff --git a/drivers/video/fbdev/aty/radeon_accel.c b/drivers/video/fbdev/aty/radeon_accel.c
new file mode 100644
index 000000000..bb147d8bf
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_accel.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "radeonfb.h"
+
+/* the accelerated functions here are patterned after the
+ * "ACCEL_MMIO" ifdef branches in XFree86
+ * --dte
+ */
+
+static void radeon_fixup_offset(struct radeonfb_info *rinfo)
+{
+ u32 local_base;
+
+ /* *** Ugly workaround *** */
+ /*
+ * On some platforms, the video memory is mapped at 0 in radeon chip space
+ * (like PPCs) by the firmware. X will always move it up so that it's seen
+ * by the chip to be at the same address as the PCI BAR.
+ * That means that when switching back from X, there is a mismatch between
+ * the offsets programmed into the engine. This means that potentially,
+ * accel operations done before radeonfb has a chance to re-init the engine
+ * will have incorrect offsets, and potentially trash system memory !
+ *
+ * The correct fix is for fbcon to never call any accel op before the engine
+ * has properly been re-initialized (by a call to set_var), but this is a
+ * complex fix. This workaround in the meantime, called before every accel
+ * operation, makes sure the offsets are in sync.
+ */
+
+ radeon_fifo_wait (1);
+ local_base = INREG(MC_FB_LOCATION) << 16;
+ if (local_base == rinfo->fb_local_base)
+ return;
+
+ rinfo->fb_local_base = local_base;
+
+ radeon_fifo_wait (3);
+ OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) |
+ (rinfo->fb_local_base >> 10));
+ OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+ OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+}
+
+static void radeonfb_prim_fillrect(struct radeonfb_info *rinfo,
+ const struct fb_fillrect *region)
+{
+ radeon_fifo_wait(4);
+
+ OUTREG(DP_GUI_MASTER_CNTL,
+ rinfo->dp_gui_master_cntl /* contains, like GMC_DST_32BPP */
+ | GMC_BRUSH_SOLID_COLOR
+ | ROP3_P);
+ if (radeon_get_dstbpp(rinfo->depth) != DST_8BPP)
+ OUTREG(DP_BRUSH_FRGD_CLR, rinfo->pseudo_palette[region->color]);
+ else
+ OUTREG(DP_BRUSH_FRGD_CLR, region->color);
+ OUTREG(DP_WRITE_MSK, 0xffffffff);
+ OUTREG(DP_CNTL, (DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM));
+
+ radeon_fifo_wait(2);
+ OUTREG(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL);
+ OUTREG(WAIT_UNTIL, (WAIT_2D_IDLECLEAN | WAIT_DMA_GUI_IDLE));
+
+ radeon_fifo_wait(2);
+ OUTREG(DST_Y_X, (region->dy << 16) | region->dx);
+ OUTREG(DST_WIDTH_HEIGHT, (region->width << 16) | region->height);
+}
+
+void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region)
+{
+ struct radeonfb_info *rinfo = info->par;
+ struct fb_fillrect modded;
+ int vxres, vyres;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ cfb_fillrect(info, region);
+ return;
+ }
+
+ radeon_fixup_offset(rinfo);
+
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+
+ memcpy(&modded, region, sizeof(struct fb_fillrect));
+
+ if(!modded.width || !modded.height ||
+ modded.dx >= vxres || modded.dy >= vyres)
+ return;
+
+ if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx;
+ if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
+
+ radeonfb_prim_fillrect(rinfo, &modded);
+}
+
+static void radeonfb_prim_copyarea(struct radeonfb_info *rinfo,
+ const struct fb_copyarea *area)
+{
+ int xdir, ydir;
+ u32 sx, sy, dx, dy, w, h;
+
+ w = area->width; h = area->height;
+ dx = area->dx; dy = area->dy;
+ sx = area->sx; sy = area->sy;
+ xdir = sx - dx;
+ ydir = sy - dy;
+
+ if ( xdir < 0 ) { sx += w-1; dx += w-1; }
+ if ( ydir < 0 ) { sy += h-1; dy += h-1; }
+
+ radeon_fifo_wait(3);
+ OUTREG(DP_GUI_MASTER_CNTL,
+ rinfo->dp_gui_master_cntl /* i.e. GMC_DST_32BPP */
+ | GMC_BRUSH_NONE
+ | GMC_SRC_DSTCOLOR
+ | ROP3_S
+ | DP_SRC_SOURCE_MEMORY );
+ OUTREG(DP_WRITE_MSK, 0xffffffff);
+ OUTREG(DP_CNTL, (xdir>=0 ? DST_X_LEFT_TO_RIGHT : 0)
+ | (ydir>=0 ? DST_Y_TOP_TO_BOTTOM : 0));
+
+ radeon_fifo_wait(2);
+ OUTREG(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL);
+ OUTREG(WAIT_UNTIL, (WAIT_2D_IDLECLEAN | WAIT_DMA_GUI_IDLE));
+
+ radeon_fifo_wait(3);
+ OUTREG(SRC_Y_X, (sy << 16) | sx);
+ OUTREG(DST_Y_X, (dy << 16) | dx);
+ OUTREG(DST_HEIGHT_WIDTH, (h << 16) | w);
+}
+
+
+void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct radeonfb_info *rinfo = info->par;
+ struct fb_copyarea modded;
+ u32 vxres, vyres;
+ modded.sx = area->sx;
+ modded.sy = area->sy;
+ modded.dx = area->dx;
+ modded.dy = area->dy;
+ modded.width = area->width;
+ modded.height = area->height;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ cfb_copyarea(info, area);
+ return;
+ }
+
+ radeon_fixup_offset(rinfo);
+
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+
+ if(!modded.width || !modded.height ||
+ modded.sx >= vxres || modded.sy >= vyres ||
+ modded.dx >= vxres || modded.dy >= vyres)
+ return;
+
+ if(modded.sx + modded.width > vxres) modded.width = vxres - modded.sx;
+ if(modded.dx + modded.width > vxres) modded.width = vxres - modded.dx;
+ if(modded.sy + modded.height > vyres) modded.height = vyres - modded.sy;
+ if(modded.dy + modded.height > vyres) modded.height = vyres - modded.dy;
+
+ radeonfb_prim_copyarea(rinfo, &modded);
+}
+
+void radeonfb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct radeonfb_info *rinfo = info->par;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+ radeon_engine_idle();
+
+ cfb_imageblit(info, image);
+}
+
+int radeonfb_sync(struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = info->par;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return 0;
+ radeon_engine_idle();
+
+ return 0;
+}
+
+void radeonfb_engine_reset(struct radeonfb_info *rinfo)
+{
+ u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
+ u32 host_path_cntl;
+
+ radeon_engine_flush (rinfo);
+
+ clock_cntl_index = INREG(CLOCK_CNTL_INDEX);
+ mclk_cntl = INPLL(MCLK_CNTL);
+
+ OUTPLL(MCLK_CNTL, (mclk_cntl |
+ FORCEON_MCLKA |
+ FORCEON_MCLKB |
+ FORCEON_YCLKA |
+ FORCEON_YCLKB |
+ FORCEON_MC |
+ FORCEON_AIC));
+
+ host_path_cntl = INREG(HOST_PATH_CNTL);
+ rbbm_soft_reset = INREG(RBBM_SOFT_RESET);
+
+ if (IS_R300_VARIANT(rinfo)) {
+ u32 tmp;
+
+ OUTREG(RBBM_SOFT_RESET, (rbbm_soft_reset |
+ SOFT_RESET_CP |
+ SOFT_RESET_HI |
+ SOFT_RESET_E2));
+ INREG(RBBM_SOFT_RESET);
+ OUTREG(RBBM_SOFT_RESET, 0);
+ tmp = INREG(RB2D_DSTCACHE_MODE);
+ OUTREG(RB2D_DSTCACHE_MODE, tmp | (1 << 17)); /* FIXME */
+ } else {
+ OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset |
+ SOFT_RESET_CP |
+ SOFT_RESET_HI |
+ SOFT_RESET_SE |
+ SOFT_RESET_RE |
+ SOFT_RESET_PP |
+ SOFT_RESET_E2 |
+ SOFT_RESET_RB);
+ INREG(RBBM_SOFT_RESET);
+ OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32)
+ ~(SOFT_RESET_CP |
+ SOFT_RESET_HI |
+ SOFT_RESET_SE |
+ SOFT_RESET_RE |
+ SOFT_RESET_PP |
+ SOFT_RESET_E2 |
+ SOFT_RESET_RB));
+ INREG(RBBM_SOFT_RESET);
+ }
+
+ OUTREG(HOST_PATH_CNTL, host_path_cntl | HDP_SOFT_RESET);
+ INREG(HOST_PATH_CNTL);
+ OUTREG(HOST_PATH_CNTL, host_path_cntl);
+
+ if (!IS_R300_VARIANT(rinfo))
+ OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset);
+
+ OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
+ OUTPLL(MCLK_CNTL, mclk_cntl);
+}
+
+void radeonfb_engine_init (struct radeonfb_info *rinfo)
+{
+ unsigned long temp;
+
+ /* disable 3D engine */
+ OUTREG(RB3D_CNTL, 0);
+
+ radeonfb_engine_reset(rinfo);
+
+ radeon_fifo_wait (1);
+ if (IS_R300_VARIANT(rinfo)) {
+ OUTREG(RB2D_DSTCACHE_MODE, INREG(RB2D_DSTCACHE_MODE) |
+ RB2D_DC_AUTOFLUSH_ENABLE |
+ RB2D_DC_DC_DISABLE_IGNORE_PE);
+ } else {
+ /* This needs to be double checked with ATI. Latest X driver
+ * completely "forgets" to set this register on < r3xx, and
+ * we used to just write 0 there... I'll keep the 0 and update
+ * that when we have sorted things out on X side.
+ */
+ OUTREG(RB2D_DSTCACHE_MODE, 0);
+ }
+
+ radeon_fifo_wait (3);
+ /* We re-read MC_FB_LOCATION from card as it can have been
+ * modified by XFree drivers (ouch !)
+ */
+ rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16;
+
+ OUTREG(DEFAULT_PITCH_OFFSET, (rinfo->pitch << 0x16) |
+ (rinfo->fb_local_base >> 10));
+ OUTREG(DST_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+ OUTREG(SRC_PITCH_OFFSET, (rinfo->pitch << 0x16) | (rinfo->fb_local_base >> 10));
+
+ radeon_fifo_wait (1);
+#if defined(__BIG_ENDIAN)
+ OUTREGP(DP_DATATYPE, HOST_BIG_ENDIAN_EN, ~HOST_BIG_ENDIAN_EN);
+#else
+ OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
+#endif
+ radeon_fifo_wait (2);
+ OUTREG(DEFAULT_SC_TOP_LEFT, 0);
+ OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX |
+ DEFAULT_SC_BOTTOM_MAX));
+
+ temp = radeon_get_dstbpp(rinfo->depth);
+ rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS);
+
+ radeon_fifo_wait (1);
+ OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl |
+ GMC_BRUSH_SOLID_COLOR |
+ GMC_SRC_DATATYPE_COLOR));
+
+ radeon_fifo_wait (7);
+
+ /* clear line drawing regs */
+ OUTREG(DST_LINE_START, 0);
+ OUTREG(DST_LINE_END, 0);
+
+ /* set brush color regs */
+ OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff);
+ OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000);
+
+ /* set source color regs */
+ OUTREG(DP_SRC_FRGD_CLR, 0xffffffff);
+ OUTREG(DP_SRC_BKGD_CLR, 0x00000000);
+
+ /* default write mask */
+ OUTREG(DP_WRITE_MSK, 0xffffffff);
+
+ radeon_engine_idle ();
+}
diff --git a/drivers/video/fbdev/aty/radeon_backlight.c b/drivers/video/fbdev/aty/radeon_backlight.c
new file mode 100644
index 000000000..301d6d6ae
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_backlight.c
@@ -0,0 +1,215 @@
+/*
+ * Backlight code for ATI Radeon based graphic cards
+ *
+ * Copyright (c) 2000 Ani Joshi <ajoshi@kernel.crashing.org>
+ * Copyright (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "radeonfb.h"
+#include <linux/backlight.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#define MAX_RADEON_LEVEL 0xFF
+
+struct radeon_bl_privdata {
+ struct radeonfb_info *rinfo;
+ uint8_t negative;
+};
+
+static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata,
+ int level)
+{
+ int rlevel;
+
+ /* Get and convert the value */
+ /* No locking of bl_curve since we read a single value */
+ rlevel = pdata->rinfo->info->bl_curve[level] *
+ FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL;
+
+ if (rlevel < 0)
+ rlevel = 0;
+ else if (rlevel > MAX_RADEON_LEVEL)
+ rlevel = MAX_RADEON_LEVEL;
+
+ if (pdata->negative)
+ rlevel = MAX_RADEON_LEVEL - rlevel;
+
+ return rlevel;
+}
+
+static int radeon_bl_update_status(struct backlight_device *bd)
+{
+ struct radeon_bl_privdata *pdata = bl_get_data(bd);
+ struct radeonfb_info *rinfo = pdata->rinfo;
+ u32 lvds_gen_cntl, tmpPixclksCntl;
+ int level;
+
+ if (rinfo->mon1_type != MT_LCD)
+ return 0;
+
+ /* We turn off the LCD completely instead of just dimming the
+ * backlight. This provides some greater power saving and the display
+ * is useless without backlight anyway.
+ */
+ if (bd->props.power != FB_BLANK_UNBLANK ||
+ bd->props.fb_blank != FB_BLANK_UNBLANK)
+ level = 0;
+ else
+ level = bd->props.brightness;
+
+ del_timer_sync(&rinfo->lvds_timer);
+ radeon_engine_idle();
+
+ lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
+ if (level > 0) {
+ lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
+ if (!(lvds_gen_cntl & LVDS_BLON) || !(lvds_gen_cntl & LVDS_ON)) {
+ lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_DIGON);
+ lvds_gen_cntl |= LVDS_BLON | LVDS_EN;
+ OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+ lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+ lvds_gen_cntl |=
+ (radeon_bl_get_level_brightness(pdata, level) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
+ lvds_gen_cntl |= LVDS_ON;
+ lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_BL_MOD_EN);
+ rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
+ mod_timer(&rinfo->lvds_timer,
+ jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+ } else {
+ lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+ lvds_gen_cntl |=
+ (radeon_bl_get_level_brightness(pdata, level) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
+ OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+ }
+ rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+ rinfo->init_state.lvds_gen_cntl |= rinfo->pending_lvds_gen_cntl
+ & LVDS_STATE_MASK;
+ } else {
+ /* Asic bug, when turning off LVDS_ON, we have to make sure
+ RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
+ */
+ tmpPixclksCntl = INPLL(PIXCLKS_CNTL);
+ if (rinfo->is_mobility || rinfo->is_IGP)
+ OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
+ lvds_gen_cntl &= ~(LVDS_BL_MOD_LEVEL_MASK | LVDS_BL_MOD_EN);
+ lvds_gen_cntl |= (radeon_bl_get_level_brightness(pdata, 0) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
+ lvds_gen_cntl |= LVDS_DISPLAY_DIS;
+ OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+ udelay(100);
+ lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN);
+ OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+ lvds_gen_cntl &= ~(LVDS_DIGON);
+ rinfo->pending_lvds_gen_cntl = lvds_gen_cntl;
+ mod_timer(&rinfo->lvds_timer,
+ jiffies + msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+ if (rinfo->is_mobility || rinfo->is_IGP)
+ OUTPLL(PIXCLKS_CNTL, tmpPixclksCntl);
+ }
+ rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+ rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
+
+ return 0;
+}
+
+static const struct backlight_ops radeon_bl_data = {
+ .update_status = radeon_bl_update_status,
+};
+
+void radeonfb_bl_init(struct radeonfb_info *rinfo)
+{
+ struct backlight_properties props;
+ struct backlight_device *bd;
+ struct radeon_bl_privdata *pdata;
+ char name[12];
+
+ if (rinfo->mon1_type != MT_LCD)
+ return;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+ if (!pmac_has_backlight_type("ati") &&
+ !pmac_has_backlight_type("mnca"))
+ return;
+#endif
+
+ pdata = kmalloc(sizeof(struct radeon_bl_privdata), GFP_KERNEL);
+ if (!pdata) {
+ printk("radeonfb: Memory allocation failed\n");
+ goto error;
+ }
+
+ snprintf(name, sizeof(name), "radeonbl%d", rinfo->info->node);
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
+ bd = backlight_device_register(name, rinfo->info->dev, pdata,
+ &radeon_bl_data, &props);
+ if (IS_ERR(bd)) {
+ rinfo->info->bl_dev = NULL;
+ printk("radeonfb: Backlight registration failed\n");
+ goto error;
+ }
+
+ pdata->rinfo = rinfo;
+
+ /* Pardon me for that hack... maybe some day we can figure out in what
+ * direction backlight should work on a given panel?
+ */
+ pdata->negative =
+ (rinfo->family != CHIP_FAMILY_RV200 &&
+ rinfo->family != CHIP_FAMILY_RV250 &&
+ rinfo->family != CHIP_FAMILY_RV280 &&
+ rinfo->family != CHIP_FAMILY_RV350);
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+ pdata->negative = pdata->negative ||
+ of_machine_is_compatible("PowerBook4,3") ||
+ of_machine_is_compatible("PowerBook6,3") ||
+ of_machine_is_compatible("PowerBook6,5");
+#endif
+
+ rinfo->info->bl_dev = bd;
+ fb_bl_default_curve(rinfo->info, 0,
+ 63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL,
+ 217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL);
+
+ bd->props.brightness = bd->props.max_brightness;
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+
+ printk("radeonfb: Backlight initialized (%s)\n", name);
+
+ return;
+
+error:
+ kfree(pdata);
+ return;
+}
+
+void radeonfb_bl_exit(struct radeonfb_info *rinfo)
+{
+ struct backlight_device *bd = rinfo->info->bl_dev;
+
+ if (bd) {
+ struct radeon_bl_privdata *pdata;
+
+ pdata = bl_get_data(bd);
+ backlight_device_unregister(bd);
+ kfree(pdata);
+ rinfo->info->bl_dev = NULL;
+
+ printk("radeonfb: Backlight unloaded\n");
+ }
+}
diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c
new file mode 100644
index 000000000..c6109a385
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_base.c
@@ -0,0 +1,2694 @@
+/*
+ * drivers/video/aty/radeon_base.c
+ *
+ * framebuffer driver for ATI Radeon chipset video boards
+ *
+ * Copyright 2003 Ben. Herrenschmidt <benh@kernel.crashing.org>
+ * Copyright 2000 Ani Joshi <ajoshi@kernel.crashing.org>
+ *
+ * i2c bits from Luca Tettamanti <kronos@kronoz.cjb.net>
+ *
+ * Special thanks to ATI DevRel team for their hardware donations.
+ *
+ * ...Insert GPL boilerplate here...
+ *
+ * Significant portions of this driver apdated from XFree86 Radeon
+ * driver which has the following copyright notice:
+ *
+ * Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
+ * VA Linux Systems Inc., Fremont, California.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
+ * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * XFree86 driver authors:
+ *
+ * Kevin E. Martin <martin@xfree86.org>
+ * Rickard E. Faith <faith@valinux.com>
+ * Alan Hourihane <alanh@fairlite.demon.co.uk>
+ *
+ */
+
+
+#define RADEON_VERSION "0.2.0"
+
+#include "radeonfb.h"
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/fb.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <linux/uaccess.h>
+
+#ifdef CONFIG_PPC
+
+#include "../macmodes.h"
+
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
+
+#endif /* CONFIG_PPC */
+
+#include <video/radeon.h>
+#include <linux/radeonfb.h>
+
+#include "../edid.h" // MOVE THAT TO include/video
+#include "ati_ids.h"
+
+#define MAX_MAPPED_VRAM (2048*2048*4)
+#define MIN_MAPPED_VRAM (1024*768*1)
+
+#define CHIP_DEF(id, family, flags) \
+ { PCI_VENDOR_ID_ATI, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (flags) | (CHIP_FAMILY_##family) }
+
+static const struct pci_device_id radeonfb_pci_table[] = {
+ /* Radeon Xpress 200m */
+ CHIP_DEF(PCI_CHIP_RS480_5955, RS480, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RS482_5975, RS480, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ /* Mobility M6 */
+ CHIP_DEF(PCI_CHIP_RADEON_LY, RV100, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RADEON_LZ, RV100, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ /* Radeon VE/7000 */
+ CHIP_DEF(PCI_CHIP_RV100_QY, RV100, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV100_QZ, RV100, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RN50, RV100, CHIP_HAS_CRTC2),
+ /* Radeon IGP320M (U1) */
+ CHIP_DEF(PCI_CHIP_RS100_4336, RS100, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ /* Radeon IGP320 (A3) */
+ CHIP_DEF(PCI_CHIP_RS100_4136, RS100, CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+ /* IGP330M/340M/350M (U2) */
+ CHIP_DEF(PCI_CHIP_RS200_4337, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ /* IGP330/340/350 (A4) */
+ CHIP_DEF(PCI_CHIP_RS200_4137, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+ /* Mobility 7000 IGP */
+ CHIP_DEF(PCI_CHIP_RS250_4437, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ /* 7000 IGP (A4+) */
+ CHIP_DEF(PCI_CHIP_RS250_4237, RS200, CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+ /* 8500 AIW */
+ CHIP_DEF(PCI_CHIP_R200_BB, R200, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R200_BC, R200, CHIP_HAS_CRTC2),
+ /* 8700/8800 */
+ CHIP_DEF(PCI_CHIP_R200_QH, R200, CHIP_HAS_CRTC2),
+ /* 8500 */
+ CHIP_DEF(PCI_CHIP_R200_QL, R200, CHIP_HAS_CRTC2),
+ /* 9100 */
+ CHIP_DEF(PCI_CHIP_R200_QM, R200, CHIP_HAS_CRTC2),
+ /* Mobility M7 */
+ CHIP_DEF(PCI_CHIP_RADEON_LW, RV200, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RADEON_LX, RV200, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ /* 7500 */
+ CHIP_DEF(PCI_CHIP_RV200_QW, RV200, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV200_QX, RV200, CHIP_HAS_CRTC2),
+ /* Mobility M9 */
+ CHIP_DEF(PCI_CHIP_RV250_Ld, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV250_Le, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV250_Lf, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV250_Lg, RV250, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ /* 9000/Pro */
+ CHIP_DEF(PCI_CHIP_RV250_If, RV250, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV250_Ig, RV250, CHIP_HAS_CRTC2),
+
+ CHIP_DEF(PCI_CHIP_RC410_5A62, RC410, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ /* Mobility 9100 IGP (U3) */
+ CHIP_DEF(PCI_CHIP_RS300_5835, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RS350_7835, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP | CHIP_IS_MOBILITY),
+ /* 9100 IGP (A5) */
+ CHIP_DEF(PCI_CHIP_RS300_5834, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+ CHIP_DEF(PCI_CHIP_RS350_7834, RS300, CHIP_HAS_CRTC2 | CHIP_IS_IGP),
+ /* Mobility 9200 (M9+) */
+ CHIP_DEF(PCI_CHIP_RV280_5C61, RV280, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV280_5C63, RV280, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ /* 9200 */
+ CHIP_DEF(PCI_CHIP_RV280_5960, RV280, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV280_5961, RV280, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV280_5962, RV280, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV280_5964, RV280, CHIP_HAS_CRTC2),
+ /* 9500 */
+ CHIP_DEF(PCI_CHIP_R300_AD, R300, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R300_AE, R300, CHIP_HAS_CRTC2),
+ /* 9600TX / FireGL Z1 */
+ CHIP_DEF(PCI_CHIP_R300_AF, R300, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R300_AG, R300, CHIP_HAS_CRTC2),
+ /* 9700/9500/Pro/FireGL X1 */
+ CHIP_DEF(PCI_CHIP_R300_ND, R300, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R300_NE, R300, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R300_NF, R300, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R300_NG, R300, CHIP_HAS_CRTC2),
+ /* Mobility M10/M11 */
+ CHIP_DEF(PCI_CHIP_RV350_NP, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV350_NQ, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV350_NR, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV350_NS, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV350_NT, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV350_NV, RV350, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ /* 9600/FireGL T2 */
+ CHIP_DEF(PCI_CHIP_RV350_AP, RV350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV350_AQ, RV350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV360_AR, RV350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV350_AS, RV350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV350_AT, RV350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV350_AV, RV350, CHIP_HAS_CRTC2),
+ /* 9800/Pro/FileGL X2 */
+ CHIP_DEF(PCI_CHIP_R350_AH, R350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R350_AI, R350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R350_AJ, R350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R350_AK, R350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R350_NH, R350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R350_NI, R350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R360_NJ, R350, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R350_NK, R350, CHIP_HAS_CRTC2),
+ /* Newer stuff */
+ CHIP_DEF(PCI_CHIP_RV380_3E50, RV380, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV380_3E54, RV380, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV380_3150, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV380_3154, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV370_5B60, RV380, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV370_5B62, RV380, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV370_5B63, RV380, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV370_5B64, RV380, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV370_5B65, RV380, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_RV370_5460, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_RV370_5464, RV380, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_R420_JH, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R420_JI, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R420_JJ, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R420_JK, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R420_JL, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R420_JM, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R420_JN, R420, CHIP_HAS_CRTC2 | CHIP_IS_MOBILITY),
+ CHIP_DEF(PCI_CHIP_R420_JP, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_UH, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_UI, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_UJ, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_UK, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_UQ, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_UR, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_UT, R420, CHIP_HAS_CRTC2),
+ CHIP_DEF(PCI_CHIP_R423_5D57, R420, CHIP_HAS_CRTC2),
+ /* Original Radeon/7200 */
+ CHIP_DEF(PCI_CHIP_RADEON_QD, RADEON, 0),
+ CHIP_DEF(PCI_CHIP_RADEON_QE, RADEON, 0),
+ CHIP_DEF(PCI_CHIP_RADEON_QF, RADEON, 0),
+ CHIP_DEF(PCI_CHIP_RADEON_QG, RADEON, 0),
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, radeonfb_pci_table);
+
+
+typedef struct {
+ u16 reg;
+ u32 val;
+} reg_val;
+
+
+/* these common regs are cleared before mode setting so they do not
+ * interfere with anything
+ */
+static reg_val common_regs[] = {
+ { OVR_CLR, 0 },
+ { OVR_WID_LEFT_RIGHT, 0 },
+ { OVR_WID_TOP_BOTTOM, 0 },
+ { OV0_SCALE_CNTL, 0 },
+ { SUBPIC_CNTL, 0 },
+ { VIPH_CONTROL, 0 },
+ { I2C_CNTL_1, 0 },
+ { GEN_INT_CNTL, 0 },
+ { CAP0_TRIG_CNTL, 0 },
+ { CAP1_TRIG_CNTL, 0 },
+};
+
+/*
+ * globals
+ */
+
+static char *mode_option;
+static char *monitor_layout;
+static bool noaccel = 0;
+static int default_dynclk = -2;
+static bool nomodeset = 0;
+static bool ignore_edid = 0;
+static bool mirror = 0;
+static int panel_yres = 0;
+static bool force_dfp = 0;
+static bool force_measure_pll = 0;
+static bool nomtrr = 0;
+static bool force_sleep;
+static bool ignore_devlist;
+#ifdef CONFIG_PMAC_BACKLIGHT
+static int backlight = 1;
+#else
+static int backlight = 0;
+#endif
+
+/* Note about this function: we have some rare cases where we must not schedule,
+ * this typically happen with our special "wake up early" hook which allows us to
+ * wake up the graphic chip (and thus get the console back) before everything else
+ * on some machines that support that mechanism. At this point, interrupts are off
+ * and scheduling is not permitted
+ */
+void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms)
+{
+ if (rinfo->no_schedule || oops_in_progress)
+ mdelay(ms);
+ else
+ msleep(ms);
+}
+
+void radeon_pll_errata_after_index_slow(struct radeonfb_info *rinfo)
+{
+ /* Called if (rinfo->errata & CHIP_ERRATA_PLL_DUMMYREADS) is set */
+ (void)INREG(CLOCK_CNTL_DATA);
+ (void)INREG(CRTC_GEN_CNTL);
+}
+
+void radeon_pll_errata_after_data_slow(struct radeonfb_info *rinfo)
+{
+ if (rinfo->errata & CHIP_ERRATA_PLL_DELAY) {
+ /* we can't deal with posted writes here ... */
+ _radeon_msleep(rinfo, 5);
+ }
+ if (rinfo->errata & CHIP_ERRATA_R300_CG) {
+ u32 save, tmp;
+ save = INREG(CLOCK_CNTL_INDEX);
+ tmp = save & ~(0x3f | PLL_WR_EN);
+ OUTREG(CLOCK_CNTL_INDEX, tmp);
+ tmp = INREG(CLOCK_CNTL_DATA);
+ OUTREG(CLOCK_CNTL_INDEX, save);
+ }
+}
+
+void _OUTREGP(struct radeonfb_info *rinfo, u32 addr, u32 val, u32 mask)
+{
+ unsigned long flags;
+ unsigned int tmp;
+
+ spin_lock_irqsave(&rinfo->reg_lock, flags);
+ tmp = INREG(addr);
+ tmp &= (mask);
+ tmp |= (val);
+ OUTREG(addr, tmp);
+ spin_unlock_irqrestore(&rinfo->reg_lock, flags);
+}
+
+u32 __INPLL(struct radeonfb_info *rinfo, u32 addr)
+{
+ u32 data;
+
+ OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f);
+ radeon_pll_errata_after_index(rinfo);
+ data = INREG(CLOCK_CNTL_DATA);
+ radeon_pll_errata_after_data(rinfo);
+ return data;
+}
+
+void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index, u32 val)
+{
+ OUTREG8(CLOCK_CNTL_INDEX, (index & 0x0000003f) | 0x00000080);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG(CLOCK_CNTL_DATA, val);
+ radeon_pll_errata_after_data(rinfo);
+}
+
+void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
+ u32 val, u32 mask)
+{
+ unsigned int tmp;
+
+ tmp = __INPLL(rinfo, index);
+ tmp &= (mask);
+ tmp |= (val);
+ __OUTPLL(rinfo, index, tmp);
+}
+
+void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries)
+{
+ int i;
+
+ for (i=0; i<2000000; i++) {
+ if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
+ return;
+ udelay(1);
+ }
+ printk(KERN_ERR "radeonfb: FIFO Timeout !\n");
+}
+
+void radeon_engine_flush(struct radeonfb_info *rinfo)
+{
+ int i;
+
+ /* Initiate flush */
+ OUTREGP(DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
+ ~RB2D_DC_FLUSH_ALL);
+
+ /* Ensure FIFO is empty, ie, make sure the flush commands
+ * has reached the cache
+ */
+ _radeon_fifo_wait(rinfo, 64);
+
+ /* Wait for the flush to complete */
+ for (i=0; i < 2000000; i++) {
+ if (!(INREG(DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
+ return;
+ udelay(1);
+ }
+ printk(KERN_ERR "radeonfb: Flush Timeout !\n");
+}
+
+void _radeon_engine_idle(struct radeonfb_info *rinfo)
+{
+ int i;
+
+ /* ensure FIFO is empty before waiting for idle */
+ _radeon_fifo_wait(rinfo, 64);
+
+ for (i=0; i<2000000; i++) {
+ if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
+ radeon_engine_flush(rinfo);
+ return;
+ }
+ udelay(1);
+ }
+ printk(KERN_ERR "radeonfb: Idle Timeout !\n");
+}
+
+
+
+static void radeon_unmap_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev)
+{
+ if (!rinfo->bios_seg)
+ return;
+ pci_unmap_rom(dev, rinfo->bios_seg);
+}
+
+static int radeon_map_ROM(struct radeonfb_info *rinfo, struct pci_dev *dev)
+{
+ void __iomem *rom;
+ u16 dptr;
+ u8 rom_type;
+ size_t rom_size;
+
+ /* If this is a primary card, there is a shadow copy of the
+ * ROM somewhere in the first meg. We will just ignore the copy
+ * and use the ROM directly.
+ */
+
+ /* Fix from ATI for problem with Radeon hardware not leaving ROM enabled */
+ unsigned int temp;
+ temp = INREG(MPP_TB_CONFIG);
+ temp &= 0x00ffffffu;
+ temp |= 0x04 << 24;
+ OUTREG(MPP_TB_CONFIG, temp);
+ temp = INREG(MPP_TB_CONFIG);
+
+ rom = pci_map_rom(dev, &rom_size);
+ if (!rom) {
+ printk(KERN_ERR "radeonfb (%s): ROM failed to map\n",
+ pci_name(rinfo->pdev));
+ return -ENOMEM;
+ }
+
+ rinfo->bios_seg = rom;
+
+ /* Very simple test to make sure it appeared */
+ if (BIOS_IN16(0) != 0xaa55) {
+ printk(KERN_DEBUG "radeonfb (%s): Invalid ROM signature %x "
+ "should be 0xaa55\n",
+ pci_name(rinfo->pdev), BIOS_IN16(0));
+ goto failed;
+ }
+ /* Look for the PCI data to check the ROM type */
+ dptr = BIOS_IN16(0x18);
+
+ /* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM
+ * for now, until I've verified this works everywhere. The goal here is more
+ * to phase out Open Firmware images.
+ *
+ * Currently, we only look at the first PCI data, we could iteratre and deal with
+ * them all, and we should use fb_bios_start relative to start of image and not
+ * relative start of ROM, but so far, I never found a dual-image ATI card
+ *
+ * typedef struct {
+ * u32 signature; + 0x00
+ * u16 vendor; + 0x04
+ * u16 device; + 0x06
+ * u16 reserved_1; + 0x08
+ * u16 dlen; + 0x0a
+ * u8 drevision; + 0x0c
+ * u8 class_hi; + 0x0d
+ * u16 class_lo; + 0x0e
+ * u16 ilen; + 0x10
+ * u16 irevision; + 0x12
+ * u8 type; + 0x14
+ * u8 indicator; + 0x15
+ * u16 reserved_2; + 0x16
+ * } pci_data_t;
+ */
+ if (BIOS_IN32(dptr) != (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) {
+ printk(KERN_WARNING "radeonfb (%s): PCI DATA signature in ROM"
+ "incorrect: %08x\n", pci_name(rinfo->pdev), BIOS_IN32(dptr));
+ goto anyway;
+ }
+ rom_type = BIOS_IN8(dptr + 0x14);
+ switch(rom_type) {
+ case 0:
+ printk(KERN_INFO "radeonfb: Found Intel x86 BIOS ROM Image\n");
+ break;
+ case 1:
+ printk(KERN_INFO "radeonfb: Found Open Firmware ROM Image\n");
+ goto failed;
+ case 2:
+ printk(KERN_INFO "radeonfb: Found HP PA-RISC ROM Image\n");
+ goto failed;
+ default:
+ printk(KERN_INFO "radeonfb: Found unknown type %d ROM Image\n", rom_type);
+ goto failed;
+ }
+ anyway:
+ /* Locate the flat panel infos, do some sanity checking !!! */
+ rinfo->fp_bios_start = BIOS_IN16(0x48);
+ return 0;
+
+ failed:
+ rinfo->bios_seg = NULL;
+ radeon_unmap_ROM(rinfo, dev);
+ return -ENXIO;
+}
+
+#ifdef CONFIG_X86
+static int radeon_find_mem_vbios(struct radeonfb_info *rinfo)
+{
+ /* I simplified this code as we used to miss the signatures in
+ * a lot of case. It's now closer to XFree, we just don't check
+ * for signatures at all... Something better will have to be done
+ * if we end up having conflicts
+ */
+ u32 segstart;
+ void __iomem *rom_base = NULL;
+
+ for(segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
+ rom_base = ioremap(segstart, 0x10000);
+ if (rom_base == NULL)
+ return -ENOMEM;
+ if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa)
+ break;
+ iounmap(rom_base);
+ rom_base = NULL;
+ }
+ if (rom_base == NULL)
+ return -ENXIO;
+
+ /* Locate the flat panel infos, do some sanity checking !!! */
+ rinfo->bios_seg = rom_base;
+ rinfo->fp_bios_start = BIOS_IN16(0x48);
+
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+/*
+ * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device
+ * tree. Hopefully, ATI OF driver is kind enough to fill these
+ */
+static int radeon_read_xtal_OF(struct radeonfb_info *rinfo)
+{
+ struct device_node *dp = rinfo->of_node;
+ const u32 *val;
+
+ if (dp == NULL)
+ return -ENODEV;
+ val = of_get_property(dp, "ATY,RefCLK", NULL);
+ if (!val || !*val) {
+ printk(KERN_WARNING "radeonfb: No ATY,RefCLK property !\n");
+ return -EINVAL;
+ }
+
+ rinfo->pll.ref_clk = (*val) / 10;
+
+ val = of_get_property(dp, "ATY,SCLK", NULL);
+ if (val && *val)
+ rinfo->pll.sclk = (*val) / 10;
+
+ val = of_get_property(dp, "ATY,MCLK", NULL);
+ if (val && *val)
+ rinfo->pll.mclk = (*val) / 10;
+
+ return 0;
+}
+#endif /* CONFIG_PPC || CONFIG_SPARC */
+
+/*
+ * Read PLL infos from chip registers
+ */
+static int radeon_probe_pll_params(struct radeonfb_info *rinfo)
+{
+ unsigned char ppll_div_sel;
+ unsigned Ns, Nm, M;
+ unsigned sclk, mclk, tmp, ref_div;
+ int hTotal, vTotal, num, denom, m, n;
+ unsigned long long hz, vclk;
+ long xtal;
+ ktime_t start_time, stop_time;
+ u64 total_usecs;
+ int i;
+
+ /* Ugh, we cut interrupts, bad bad bad, but we want some precision
+ * here, so... --BenH
+ */
+
+ /* Flush PCI buffers ? */
+ tmp = INREG16(DEVICE_ID);
+
+ local_irq_disable();
+
+ for(i=0; i<1000000; i++)
+ if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0)
+ break;
+
+ start_time = ktime_get();
+
+ for(i=0; i<1000000; i++)
+ if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) != 0)
+ break;
+
+ for(i=0; i<1000000; i++)
+ if (((INREG(CRTC_VLINE_CRNT_VLINE) >> 16) & 0x3ff) == 0)
+ break;
+
+ stop_time = ktime_get();
+
+ local_irq_enable();
+
+ total_usecs = ktime_us_delta(stop_time, start_time);
+ if (total_usecs >= 10 * USEC_PER_SEC || total_usecs == 0)
+ return -1;
+ hz = USEC_PER_SEC/(u32)total_usecs;
+
+ hTotal = ((INREG(CRTC_H_TOTAL_DISP) & 0x1ff) + 1) * 8;
+ vTotal = ((INREG(CRTC_V_TOTAL_DISP) & 0x3ff) + 1);
+ vclk = (long long)hTotal * (long long)vTotal * hz;
+
+ switch((INPLL(PPLL_REF_DIV) & 0x30000) >> 16) {
+ case 0:
+ default:
+ num = 1;
+ denom = 1;
+ break;
+ case 1:
+ n = ((INPLL(M_SPLL_REF_FB_DIV) >> 16) & 0xff);
+ m = (INPLL(M_SPLL_REF_FB_DIV) & 0xff);
+ num = 2*n;
+ denom = 2*m;
+ break;
+ case 2:
+ n = ((INPLL(M_SPLL_REF_FB_DIV) >> 8) & 0xff);
+ m = (INPLL(M_SPLL_REF_FB_DIV) & 0xff);
+ num = 2*n;
+ denom = 2*m;
+ break;
+ }
+
+ ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3;
+ radeon_pll_errata_after_index(rinfo);
+
+ n = (INPLL(PPLL_DIV_0 + ppll_div_sel) & 0x7ff);
+ m = (INPLL(PPLL_REF_DIV) & 0x3ff);
+
+ num *= n;
+ denom *= m;
+
+ switch ((INPLL(PPLL_DIV_0 + ppll_div_sel) >> 16) & 0x7) {
+ case 1:
+ denom *= 2;
+ break;
+ case 2:
+ denom *= 4;
+ break;
+ case 3:
+ denom *= 8;
+ break;
+ case 4:
+ denom *= 3;
+ break;
+ case 6:
+ denom *= 6;
+ break;
+ case 7:
+ denom *= 12;
+ break;
+ }
+
+ vclk *= denom;
+ do_div(vclk, 1000 * num);
+ xtal = vclk;
+
+ if ((xtal > 26900) && (xtal < 27100))
+ xtal = 2700;
+ else if ((xtal > 14200) && (xtal < 14400))
+ xtal = 1432;
+ else if ((xtal > 29400) && (xtal < 29600))
+ xtal = 2950;
+ else {
+ printk(KERN_WARNING "xtal calculation failed: %ld\n", xtal);
+ return -1;
+ }
+
+ tmp = INPLL(M_SPLL_REF_FB_DIV);
+ ref_div = INPLL(PPLL_REF_DIV) & 0x3ff;
+
+ Ns = (tmp & 0xff0000) >> 16;
+ Nm = (tmp & 0xff00) >> 8;
+ M = (tmp & 0xff);
+ sclk = round_div((2 * Ns * xtal), (2 * M));
+ mclk = round_div((2 * Nm * xtal), (2 * M));
+
+ /* we're done, hopefully these are sane values */
+ rinfo->pll.ref_clk = xtal;
+ rinfo->pll.ref_div = ref_div;
+ rinfo->pll.sclk = sclk;
+ rinfo->pll.mclk = mclk;
+
+ return 0;
+}
+
+/*
+ * Retrieve PLL infos by different means (BIOS, Open Firmware, register probing...)
+ */
+static void radeon_get_pllinfo(struct radeonfb_info *rinfo)
+{
+ /*
+ * In the case nothing works, these are defaults; they are mostly
+ * incomplete, however. It does provide ppll_max and _min values
+ * even for most other methods, however.
+ */
+ switch (rinfo->chipset) {
+ case PCI_DEVICE_ID_ATI_RADEON_QW:
+ case PCI_DEVICE_ID_ATI_RADEON_QX:
+ rinfo->pll.ppll_max = 35000;
+ rinfo->pll.ppll_min = 12000;
+ rinfo->pll.mclk = 23000;
+ rinfo->pll.sclk = 23000;
+ rinfo->pll.ref_clk = 2700;
+ break;
+ case PCI_DEVICE_ID_ATI_RADEON_QL:
+ case PCI_DEVICE_ID_ATI_RADEON_QN:
+ case PCI_DEVICE_ID_ATI_RADEON_QO:
+ case PCI_DEVICE_ID_ATI_RADEON_Ql:
+ case PCI_DEVICE_ID_ATI_RADEON_BB:
+ rinfo->pll.ppll_max = 35000;
+ rinfo->pll.ppll_min = 12000;
+ rinfo->pll.mclk = 27500;
+ rinfo->pll.sclk = 27500;
+ rinfo->pll.ref_clk = 2700;
+ break;
+ case PCI_DEVICE_ID_ATI_RADEON_Id:
+ case PCI_DEVICE_ID_ATI_RADEON_Ie:
+ case PCI_DEVICE_ID_ATI_RADEON_If:
+ case PCI_DEVICE_ID_ATI_RADEON_Ig:
+ rinfo->pll.ppll_max = 35000;
+ rinfo->pll.ppll_min = 12000;
+ rinfo->pll.mclk = 25000;
+ rinfo->pll.sclk = 25000;
+ rinfo->pll.ref_clk = 2700;
+ break;
+ case PCI_DEVICE_ID_ATI_RADEON_ND:
+ case PCI_DEVICE_ID_ATI_RADEON_NE:
+ case PCI_DEVICE_ID_ATI_RADEON_NF:
+ case PCI_DEVICE_ID_ATI_RADEON_NG:
+ rinfo->pll.ppll_max = 40000;
+ rinfo->pll.ppll_min = 20000;
+ rinfo->pll.mclk = 27000;
+ rinfo->pll.sclk = 27000;
+ rinfo->pll.ref_clk = 2700;
+ break;
+ case PCI_DEVICE_ID_ATI_RADEON_QD:
+ case PCI_DEVICE_ID_ATI_RADEON_QE:
+ case PCI_DEVICE_ID_ATI_RADEON_QF:
+ case PCI_DEVICE_ID_ATI_RADEON_QG:
+ default:
+ rinfo->pll.ppll_max = 35000;
+ rinfo->pll.ppll_min = 12000;
+ rinfo->pll.mclk = 16600;
+ rinfo->pll.sclk = 16600;
+ rinfo->pll.ref_clk = 2700;
+ break;
+ }
+ rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
+
+
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+ /*
+ * Retrieve PLL infos from Open Firmware first
+ */
+ if (!force_measure_pll && radeon_read_xtal_OF(rinfo) == 0) {
+ printk(KERN_INFO "radeonfb: Retrieved PLL infos from Open Firmware\n");
+ goto found;
+ }
+#endif /* CONFIG_PPC || CONFIG_SPARC */
+
+ /*
+ * Check out if we have an X86 which gave us some PLL informations
+ * and if yes, retrieve them
+ */
+ if (!force_measure_pll && rinfo->bios_seg) {
+ u16 pll_info_block = BIOS_IN16(rinfo->fp_bios_start + 0x30);
+
+ rinfo->pll.sclk = BIOS_IN16(pll_info_block + 0x08);
+ rinfo->pll.mclk = BIOS_IN16(pll_info_block + 0x0a);
+ rinfo->pll.ref_clk = BIOS_IN16(pll_info_block + 0x0e);
+ rinfo->pll.ref_div = BIOS_IN16(pll_info_block + 0x10);
+ rinfo->pll.ppll_min = BIOS_IN32(pll_info_block + 0x12);
+ rinfo->pll.ppll_max = BIOS_IN32(pll_info_block + 0x16);
+
+ printk(KERN_INFO "radeonfb: Retrieved PLL infos from BIOS\n");
+ goto found;
+ }
+
+ /*
+ * We didn't get PLL parameters from either OF or BIOS, we try to
+ * probe them
+ */
+ if (radeon_probe_pll_params(rinfo) == 0) {
+ printk(KERN_INFO "radeonfb: Retrieved PLL infos from registers\n");
+ goto found;
+ }
+
+ /*
+ * Fall back to already-set defaults...
+ */
+ printk(KERN_INFO "radeonfb: Used default PLL infos\n");
+
+found:
+ /*
+ * Some methods fail to retrieve SCLK and MCLK values, we apply default
+ * settings in this case (200Mhz). If that really happens often, we
+ * could fetch from registers instead...
+ */
+ if (rinfo->pll.mclk == 0)
+ rinfo->pll.mclk = 20000;
+ if (rinfo->pll.sclk == 0)
+ rinfo->pll.sclk = 20000;
+
+ printk("radeonfb: Reference=%d.%02d MHz (RefDiv=%d) Memory=%d.%02d Mhz, System=%d.%02d MHz\n",
+ rinfo->pll.ref_clk / 100, rinfo->pll.ref_clk % 100,
+ rinfo->pll.ref_div,
+ rinfo->pll.mclk / 100, rinfo->pll.mclk % 100,
+ rinfo->pll.sclk / 100, rinfo->pll.sclk % 100);
+ printk("radeonfb: PLL min %d max %d\n", rinfo->pll.ppll_min, rinfo->pll.ppll_max);
+}
+
+static int radeonfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = info->par;
+ struct fb_var_screeninfo v;
+ int nom, den;
+ unsigned int pitch;
+
+ if (radeon_match_mode(rinfo, &v, var))
+ return -EINVAL;
+
+ switch (v.bits_per_pixel) {
+ case 0 ... 8:
+ v.bits_per_pixel = 8;
+ break;
+ case 9 ... 16:
+ v.bits_per_pixel = 16;
+ break;
+ case 17 ... 24:
+#if 0 /* Doesn't seem to work */
+ v.bits_per_pixel = 24;
+ break;
+#endif
+ return -EINVAL;
+ case 25 ... 32:
+ v.bits_per_pixel = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (var_to_depth(&v)) {
+ case 8:
+ nom = den = 1;
+ v.red.offset = v.green.offset = v.blue.offset = 0;
+ v.red.length = v.green.length = v.blue.length = 8;
+ v.transp.offset = v.transp.length = 0;
+ break;
+ case 15:
+ nom = 2;
+ den = 1;
+ v.red.offset = 10;
+ v.green.offset = 5;
+ v.blue.offset = 0;
+ v.red.length = v.green.length = v.blue.length = 5;
+ v.transp.offset = v.transp.length = 0;
+ break;
+ case 16:
+ nom = 2;
+ den = 1;
+ v.red.offset = 11;
+ v.green.offset = 5;
+ v.blue.offset = 0;
+ v.red.length = 5;
+ v.green.length = 6;
+ v.blue.length = 5;
+ v.transp.offset = v.transp.length = 0;
+ break;
+ case 24:
+ nom = 4;
+ den = 1;
+ v.red.offset = 16;
+ v.green.offset = 8;
+ v.blue.offset = 0;
+ v.red.length = v.blue.length = v.green.length = 8;
+ v.transp.offset = v.transp.length = 0;
+ break;
+ case 32:
+ nom = 4;
+ den = 1;
+ v.red.offset = 16;
+ v.green.offset = 8;
+ v.blue.offset = 0;
+ v.red.length = v.blue.length = v.green.length = 8;
+ v.transp.offset = 24;
+ v.transp.length = 8;
+ break;
+ default:
+ printk ("radeonfb: mode %dx%dx%d rejected, color depth invalid\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ return -EINVAL;
+ }
+
+ if (v.yres_virtual < v.yres)
+ v.yres_virtual = v.yres;
+ if (v.xres_virtual < v.xres)
+ v.xres_virtual = v.xres;
+
+
+ /* XXX I'm adjusting xres_virtual to the pitch, that may help XFree
+ * with some panels, though I don't quite like this solution
+ */
+ if (rinfo->info->flags & FBINFO_HWACCEL_DISABLED) {
+ v.xres_virtual = v.xres_virtual & ~7ul;
+ } else {
+ pitch = ((v.xres_virtual * ((v.bits_per_pixel + 1) / 8) + 0x3f)
+ & ~(0x3f)) >> 6;
+ v.xres_virtual = (pitch << 6) / ((v.bits_per_pixel + 1) / 8);
+ }
+
+ if (((v.xres_virtual * v.yres_virtual * nom) / den) > rinfo->mapped_vram)
+ return -EINVAL;
+
+ if (v.xres_virtual < v.xres)
+ v.xres = v.xres_virtual;
+
+ if (v.xoffset > v.xres_virtual - v.xres)
+ v.xoffset = v.xres_virtual - v.xres - 1;
+
+ if (v.yoffset > v.yres_virtual - v.yres)
+ v.yoffset = v.yres_virtual - v.yres - 1;
+
+ v.red.msb_right = v.green.msb_right = v.blue.msb_right =
+ v.transp.offset = v.transp.length =
+ v.transp.msb_right = 0;
+
+ memcpy(var, &v, sizeof(v));
+
+ return 0;
+}
+
+
+static int radeonfb_pan_display (struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = info->par;
+
+ if ((var->xoffset + info->var.xres > info->var.xres_virtual)
+ || (var->yoffset + info->var.yres > info->var.yres_virtual))
+ return -EINVAL;
+
+ if (rinfo->asleep)
+ return 0;
+
+ radeon_fifo_wait(2);
+ OUTREG(CRTC_OFFSET, (var->yoffset * info->fix.line_length +
+ var->xoffset * info->var.bits_per_pixel / 8) & ~7);
+ return 0;
+}
+
+
+static int radeonfb_ioctl (struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct radeonfb_info *rinfo = info->par;
+ unsigned int tmp;
+ u32 value = 0;
+ int rc;
+
+ switch (cmd) {
+ /*
+ * TODO: set mirror accordingly for non-Mobility chipsets with 2 CRTC's
+ * and do something better using 2nd CRTC instead of just hackish
+ * routing to second output
+ */
+ case FBIO_RADEON_SET_MIRROR:
+ if (!rinfo->is_mobility)
+ return -EINVAL;
+
+ rc = get_user(value, (__u32 __user *)arg);
+
+ if (rc)
+ return rc;
+
+ radeon_fifo_wait(2);
+ if (value & 0x01) {
+ tmp = INREG(LVDS_GEN_CNTL);
+
+ tmp |= (LVDS_ON | LVDS_BLON);
+ } else {
+ tmp = INREG(LVDS_GEN_CNTL);
+
+ tmp &= ~(LVDS_ON | LVDS_BLON);
+ }
+
+ OUTREG(LVDS_GEN_CNTL, tmp);
+
+ if (value & 0x02) {
+ tmp = INREG(CRTC_EXT_CNTL);
+ tmp |= CRTC_CRT_ON;
+
+ mirror = 1;
+ } else {
+ tmp = INREG(CRTC_EXT_CNTL);
+ tmp &= ~CRTC_CRT_ON;
+
+ mirror = 0;
+ }
+
+ OUTREG(CRTC_EXT_CNTL, tmp);
+
+ return 0;
+ case FBIO_RADEON_GET_MIRROR:
+ if (!rinfo->is_mobility)
+ return -EINVAL;
+
+ tmp = INREG(LVDS_GEN_CNTL);
+ if ((LVDS_ON | LVDS_BLON) & tmp)
+ value |= 0x01;
+
+ tmp = INREG(CRTC_EXT_CNTL);
+ if (CRTC_CRT_ON & tmp)
+ value |= 0x02;
+
+ return put_user(value, (__u32 __user *)arg);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+
+int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch)
+{
+ u32 val;
+ u32 tmp_pix_clks;
+ int unblank = 0;
+
+ if (rinfo->lock_blank)
+ return 0;
+
+ radeon_engine_idle();
+
+ val = INREG(CRTC_EXT_CNTL);
+ val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
+ CRTC_VSYNC_DIS);
+ switch (blank) {
+ case FB_BLANK_VSYNC_SUSPEND:
+ val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS);
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS);
+ break;
+ case FB_BLANK_POWERDOWN:
+ val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS |
+ CRTC_HSYNC_DIS);
+ break;
+ case FB_BLANK_NORMAL:
+ val |= CRTC_DISPLAY_DIS;
+ break;
+ case FB_BLANK_UNBLANK:
+ default:
+ unblank = 1;
+ }
+ OUTREG(CRTC_EXT_CNTL, val);
+
+
+ switch (rinfo->mon1_type) {
+ case MT_DFP:
+ if (unblank)
+ OUTREGP(FP_GEN_CNTL, (FP_FPON | FP_TMDS_EN),
+ ~(FP_FPON | FP_TMDS_EN));
+ else {
+ if (mode_switch || blank == FB_BLANK_NORMAL)
+ break;
+ OUTREGP(FP_GEN_CNTL, 0, ~(FP_FPON | FP_TMDS_EN));
+ }
+ break;
+ case MT_LCD:
+ del_timer_sync(&rinfo->lvds_timer);
+ val = INREG(LVDS_GEN_CNTL);
+ if (unblank) {
+ u32 target_val = (val & ~LVDS_DISPLAY_DIS) | LVDS_BLON | LVDS_ON
+ | LVDS_EN | (rinfo->init_state.lvds_gen_cntl
+ & (LVDS_DIGON | LVDS_BL_MOD_EN));
+ if ((val ^ target_val) == LVDS_DISPLAY_DIS)
+ OUTREG(LVDS_GEN_CNTL, target_val);
+ else if ((val ^ target_val) != 0) {
+ OUTREG(LVDS_GEN_CNTL, target_val
+ & ~(LVDS_ON | LVDS_BL_MOD_EN));
+ rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+ rinfo->init_state.lvds_gen_cntl |=
+ target_val & LVDS_STATE_MASK;
+ if (mode_switch) {
+ radeon_msleep(rinfo->panel_info.pwr_delay);
+ OUTREG(LVDS_GEN_CNTL, target_val);
+ }
+ else {
+ rinfo->pending_lvds_gen_cntl = target_val;
+ mod_timer(&rinfo->lvds_timer,
+ jiffies +
+ msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+ }
+ }
+ } else {
+ val |= LVDS_DISPLAY_DIS;
+ OUTREG(LVDS_GEN_CNTL, val);
+
+ /* We don't do a full switch-off on a simple mode switch */
+ if (mode_switch || blank == FB_BLANK_NORMAL)
+ break;
+
+ /* Asic bug, when turning off LVDS_ON, we have to make sure
+ * RADEON_PIXCLK_LVDS_ALWAYS_ON bit is off
+ */
+ tmp_pix_clks = INPLL(PIXCLKS_CNTL);
+ if (rinfo->is_mobility || rinfo->is_IGP)
+ OUTPLLP(PIXCLKS_CNTL, 0, ~PIXCLK_LVDS_ALWAYS_ONb);
+ val &= ~(LVDS_BL_MOD_EN);
+ OUTREG(LVDS_GEN_CNTL, val);
+ udelay(100);
+ val &= ~(LVDS_ON | LVDS_EN);
+ OUTREG(LVDS_GEN_CNTL, val);
+ val &= ~LVDS_DIGON;
+ rinfo->pending_lvds_gen_cntl = val;
+ mod_timer(&rinfo->lvds_timer,
+ jiffies +
+ msecs_to_jiffies(rinfo->panel_info.pwr_delay));
+ rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+ rinfo->init_state.lvds_gen_cntl |= val & LVDS_STATE_MASK;
+ if (rinfo->is_mobility || rinfo->is_IGP)
+ OUTPLL(PIXCLKS_CNTL, tmp_pix_clks);
+ }
+ break;
+ case MT_CRT:
+ // todo: powerdown DAC
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int radeonfb_blank (int blank, struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = info->par;
+
+ if (rinfo->asleep)
+ return 0;
+
+ return radeon_screen_blank(rinfo, blank, 0);
+}
+
+static int radeon_setcolreg (unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct radeonfb_info *rinfo)
+{
+ u32 pindex;
+ unsigned int i;
+
+
+ if (regno > 255)
+ return -EINVAL;
+
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ rinfo->palette[regno].red = red;
+ rinfo->palette[regno].green = green;
+ rinfo->palette[regno].blue = blue;
+
+ /* default */
+ pindex = regno;
+
+ if (!rinfo->asleep) {
+ radeon_fifo_wait(9);
+
+ if (rinfo->bpp == 16) {
+ pindex = regno * 8;
+
+ if (rinfo->depth == 16 && regno > 63)
+ return -EINVAL;
+ if (rinfo->depth == 15 && regno > 31)
+ return -EINVAL;
+
+ /* For 565, the green component is mixed one order
+ * below
+ */
+ if (rinfo->depth == 16) {
+ OUTREG(PALETTE_INDEX, pindex>>1);
+ OUTREG(PALETTE_DATA,
+ (rinfo->palette[regno>>1].red << 16) |
+ (green << 8) |
+ (rinfo->palette[regno>>1].blue));
+ green = rinfo->palette[regno<<1].green;
+ }
+ }
+
+ if (rinfo->depth != 16 || regno < 32) {
+ OUTREG(PALETTE_INDEX, pindex);
+ OUTREG(PALETTE_DATA, (red << 16) |
+ (green << 8) | blue);
+ }
+ }
+ if (regno < 16) {
+ u32 *pal = rinfo->info->pseudo_palette;
+ switch (rinfo->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;
+ }
+ }
+ return 0;
+}
+
+static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = info->par;
+ u32 dac_cntl2, vclk_cntl = 0;
+ int rc;
+
+ if (!rinfo->asleep) {
+ if (rinfo->is_mobility) {
+ vclk_cntl = INPLL(VCLK_ECP_CNTL);
+ OUTPLL(VCLK_ECP_CNTL,
+ vclk_cntl & ~PIXCLK_DAC_ALWAYS_ONb);
+ }
+
+ /* Make sure we are on first palette */
+ if (rinfo->has_CRTC2) {
+ dac_cntl2 = INREG(DAC_CNTL2);
+ dac_cntl2 &= ~DAC2_PALETTE_ACCESS_CNTL;
+ OUTREG(DAC_CNTL2, dac_cntl2);
+ }
+ }
+
+ rc = radeon_setcolreg (regno, red, green, blue, transp, rinfo);
+
+ if (!rinfo->asleep && rinfo->is_mobility)
+ OUTPLL(VCLK_ECP_CNTL, vclk_cntl);
+
+ return rc;
+}
+
+static int radeonfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = info->par;
+ u16 *red, *green, *blue, *transp;
+ u32 dac_cntl2, vclk_cntl = 0;
+ int i, start, rc = 0;
+
+ if (!rinfo->asleep) {
+ if (rinfo->is_mobility) {
+ vclk_cntl = INPLL(VCLK_ECP_CNTL);
+ OUTPLL(VCLK_ECP_CNTL,
+ vclk_cntl & ~PIXCLK_DAC_ALWAYS_ONb);
+ }
+
+ /* Make sure we are on first palette */
+ if (rinfo->has_CRTC2) {
+ dac_cntl2 = INREG(DAC_CNTL2);
+ dac_cntl2 &= ~DAC2_PALETTE_ACCESS_CNTL;
+ OUTREG(DAC_CNTL2, dac_cntl2);
+ }
+ }
+
+ red = cmap->red;
+ green = cmap->green;
+ blue = cmap->blue;
+ transp = cmap->transp;
+ start = cmap->start;
+
+ for (i = 0; i < cmap->len; i++) {
+ u_int hred, hgreen, hblue, htransp = 0xffff;
+
+ hred = *red++;
+ hgreen = *green++;
+ hblue = *blue++;
+ if (transp)
+ htransp = *transp++;
+ rc = radeon_setcolreg (start++, hred, hgreen, hblue, htransp,
+ rinfo);
+ if (rc)
+ break;
+ }
+
+ if (!rinfo->asleep && rinfo->is_mobility)
+ OUTPLL(VCLK_ECP_CNTL, vclk_cntl);
+
+ return rc;
+}
+
+static void radeon_save_state (struct radeonfb_info *rinfo,
+ struct radeon_regs *save)
+{
+ /* CRTC regs */
+ save->crtc_gen_cntl = INREG(CRTC_GEN_CNTL);
+ save->crtc_ext_cntl = INREG(CRTC_EXT_CNTL);
+ save->crtc_more_cntl = INREG(CRTC_MORE_CNTL);
+ save->dac_cntl = INREG(DAC_CNTL);
+ save->crtc_h_total_disp = INREG(CRTC_H_TOTAL_DISP);
+ save->crtc_h_sync_strt_wid = INREG(CRTC_H_SYNC_STRT_WID);
+ save->crtc_v_total_disp = INREG(CRTC_V_TOTAL_DISP);
+ save->crtc_v_sync_strt_wid = INREG(CRTC_V_SYNC_STRT_WID);
+ save->crtc_pitch = INREG(CRTC_PITCH);
+ save->surface_cntl = INREG(SURFACE_CNTL);
+
+ /* FP regs */
+ save->fp_crtc_h_total_disp = INREG(FP_CRTC_H_TOTAL_DISP);
+ save->fp_crtc_v_total_disp = INREG(FP_CRTC_V_TOTAL_DISP);
+ save->fp_gen_cntl = INREG(FP_GEN_CNTL);
+ save->fp_h_sync_strt_wid = INREG(FP_H_SYNC_STRT_WID);
+ save->fp_horz_stretch = INREG(FP_HORZ_STRETCH);
+ save->fp_v_sync_strt_wid = INREG(FP_V_SYNC_STRT_WID);
+ save->fp_vert_stretch = INREG(FP_VERT_STRETCH);
+ save->lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
+ save->lvds_pll_cntl = INREG(LVDS_PLL_CNTL);
+ save->tmds_crc = INREG(TMDS_CRC);
+ save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL);
+ save->vclk_ecp_cntl = INPLL(VCLK_ECP_CNTL);
+
+ /* PLL regs */
+ save->clk_cntl_index = INREG(CLOCK_CNTL_INDEX) & ~0x3f;
+ radeon_pll_errata_after_index(rinfo);
+ save->ppll_div_3 = INPLL(PPLL_DIV_3);
+ save->ppll_ref_div = INPLL(PPLL_REF_DIV);
+}
+
+
+static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *mode)
+{
+ int i;
+
+ radeon_fifo_wait(20);
+
+ /* Workaround from XFree */
+ if (rinfo->is_mobility) {
+ /* A temporal workaround for the occasional blanking on certain laptop
+ * panels. This appears to related to the PLL divider registers
+ * (fail to lock?). It occurs even when all dividers are the same
+ * with their old settings. In this case we really don't need to
+ * fiddle with PLL registers. By doing this we can avoid the blanking
+ * problem with some panels.
+ */
+ if ((mode->ppll_ref_div == (INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK)) &&
+ (mode->ppll_div_3 == (INPLL(PPLL_DIV_3) &
+ (PPLL_POST3_DIV_MASK | PPLL_FB3_DIV_MASK)))) {
+ /* We still have to force a switch to selected PPLL div thanks to
+ * an XFree86 driver bug which will switch it away in some cases
+ * even when using UseFDev */
+ OUTREGP(CLOCK_CNTL_INDEX,
+ mode->clk_cntl_index & PPLL_DIV_SEL_MASK,
+ ~PPLL_DIV_SEL_MASK);
+ radeon_pll_errata_after_index(rinfo);
+ radeon_pll_errata_after_data(rinfo);
+ return;
+ }
+ }
+
+ /* Swich VCKL clock input to CPUCLK so it stays fed while PPLL updates*/
+ OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_CPUCLK, ~VCLK_SRC_SEL_MASK);
+
+ /* Reset PPLL & enable atomic update */
+ OUTPLLP(PPLL_CNTL,
+ PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN,
+ ~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN));
+
+ /* Switch to selected PPLL divider */
+ OUTREGP(CLOCK_CNTL_INDEX,
+ mode->clk_cntl_index & PPLL_DIV_SEL_MASK,
+ ~PPLL_DIV_SEL_MASK);
+ radeon_pll_errata_after_index(rinfo);
+ radeon_pll_errata_after_data(rinfo);
+
+ /* Set PPLL ref. div */
+ if (IS_R300_VARIANT(rinfo) ||
+ rinfo->family == CHIP_FAMILY_RS300 ||
+ rinfo->family == CHIP_FAMILY_RS400 ||
+ rinfo->family == CHIP_FAMILY_RS480) {
+ if (mode->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK) {
+ /* When restoring console mode, use saved PPLL_REF_DIV
+ * setting.
+ */
+ OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, 0);
+ } else {
+ /* R300 uses ref_div_acc field as real ref divider */
+ OUTPLLP(PPLL_REF_DIV,
+ (mode->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT),
+ ~R300_PPLL_REF_DIV_ACC_MASK);
+ }
+ } else
+ OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, ~PPLL_REF_DIV_MASK);
+
+ /* Set PPLL divider 3 & post divider*/
+ OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_FB3_DIV_MASK);
+ OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_POST3_DIV_MASK);
+
+ /* Write update */
+ while (INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R)
+ ;
+ OUTPLLP(PPLL_REF_DIV, PPLL_ATOMIC_UPDATE_W, ~PPLL_ATOMIC_UPDATE_W);
+
+ /* Wait read update complete */
+ /* FIXME: Certain revisions of R300 can't recover here. Not sure of
+ the cause yet, but this workaround will mask the problem for now.
+ Other chips usually will pass at the very first test, so the
+ workaround shouldn't have any effect on them. */
+ for (i = 0; (i < 10000 && INPLL(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R); i++)
+ ;
+
+ OUTPLL(HTOTAL_CNTL, 0);
+
+ /* Clear reset & atomic update */
+ OUTPLLP(PPLL_CNTL, 0,
+ ~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN));
+
+ /* We may want some locking ... oh well */
+ radeon_msleep(5);
+
+ /* Switch back VCLK source to PPLL */
+ OUTPLLP(VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK);
+}
+
+/*
+ * Timer function for delayed LVDS panel power up/down
+ */
+static void radeon_lvds_timer_func(struct timer_list *t)
+{
+ struct radeonfb_info *rinfo = from_timer(rinfo, t, lvds_timer);
+
+ radeon_engine_idle();
+
+ OUTREG(LVDS_GEN_CNTL, rinfo->pending_lvds_gen_cntl);
+}
+
+/*
+ * Apply a video mode. This will apply the whole register set, including
+ * the PLL registers, to the card
+ */
+void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
+ int regs_only)
+{
+ int i;
+ int primary_mon = PRIMARY_MONITOR(rinfo);
+
+ if (nomodeset)
+ return;
+
+ if (!regs_only)
+ radeon_screen_blank(rinfo, FB_BLANK_NORMAL, 0);
+
+ radeon_fifo_wait(31);
+ for (i=0; i<10; i++)
+ OUTREG(common_regs[i].reg, common_regs[i].val);
+
+ /* Apply surface registers */
+ for (i=0; i<8; i++) {
+ OUTREG(SURFACE0_LOWER_BOUND + 0x10*i, mode->surf_lower_bound[i]);
+ OUTREG(SURFACE0_UPPER_BOUND + 0x10*i, mode->surf_upper_bound[i]);
+ OUTREG(SURFACE0_INFO + 0x10*i, mode->surf_info[i]);
+ }
+
+ OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl);
+ OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl,
+ ~(CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS));
+ OUTREG(CRTC_MORE_CNTL, mode->crtc_more_cntl);
+ OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING);
+ OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp);
+ OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid);
+ OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp);
+ OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid);
+ OUTREG(CRTC_OFFSET, 0);
+ OUTREG(CRTC_OFFSET_CNTL, 0);
+ OUTREG(CRTC_PITCH, mode->crtc_pitch);
+ OUTREG(SURFACE_CNTL, mode->surface_cntl);
+
+ radeon_write_pll_regs(rinfo, mode);
+
+ if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
+ radeon_fifo_wait(10);
+ OUTREG(FP_CRTC_H_TOTAL_DISP, mode->fp_crtc_h_total_disp);
+ OUTREG(FP_CRTC_V_TOTAL_DISP, mode->fp_crtc_v_total_disp);
+ OUTREG(FP_H_SYNC_STRT_WID, mode->fp_h_sync_strt_wid);
+ OUTREG(FP_V_SYNC_STRT_WID, mode->fp_v_sync_strt_wid);
+ OUTREG(FP_HORZ_STRETCH, mode->fp_horz_stretch);
+ OUTREG(FP_VERT_STRETCH, mode->fp_vert_stretch);
+ OUTREG(FP_GEN_CNTL, mode->fp_gen_cntl);
+ OUTREG(TMDS_CRC, mode->tmds_crc);
+ OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl);
+ }
+
+ if (!regs_only)
+ radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 0);
+
+ radeon_fifo_wait(2);
+ OUTPLL(VCLK_ECP_CNTL, mode->vclk_ecp_cntl);
+
+ return;
+}
+
+/*
+ * Calculate the PLL values for a given mode
+ */
+static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *regs,
+ unsigned long freq)
+{
+ static const struct {
+ int divider;
+ int bitvalue;
+ } *post_div,
+ post_divs[] = {
+ { 1, 0 },
+ { 2, 1 },
+ { 4, 2 },
+ { 8, 3 },
+ { 3, 4 },
+ { 16, 5 },
+ { 6, 6 },
+ { 12, 7 },
+ { 0, 0 },
+ };
+ int fb_div, pll_output_freq = 0;
+ int uses_dvo = 0;
+
+ /* Check if the DVO port is enabled and sourced from the primary CRTC. I'm
+ * not sure which model starts having FP2_GEN_CNTL, I assume anything more
+ * recent than an r(v)100...
+ */
+#if 1
+ /* XXX I had reports of flicker happening with the cinema display
+ * on TMDS1 that seem to be fixed if I also forbit odd dividers in
+ * this case. This could just be a bandwidth calculation issue, I
+ * haven't implemented the bandwidth code yet, but in the meantime,
+ * forcing uses_dvo to 1 fixes it and shouln't have bad side effects,
+ * I haven't seen a case were were absolutely needed an odd PLL
+ * divider. I'll find a better fix once I have more infos on the
+ * real cause of the problem.
+ */
+ while (rinfo->has_CRTC2) {
+ u32 fp2_gen_cntl = INREG(FP2_GEN_CNTL);
+ u32 disp_output_cntl;
+ int source;
+
+ /* FP2 path not enabled */
+ if ((fp2_gen_cntl & FP2_ON) == 0)
+ break;
+ /* Not all chip revs have the same format for this register,
+ * extract the source selection
+ */
+ if (rinfo->family == CHIP_FAMILY_R200 || IS_R300_VARIANT(rinfo)) {
+ source = (fp2_gen_cntl >> 10) & 0x3;
+ /* sourced from transform unit, check for transform unit
+ * own source
+ */
+ if (source == 3) {
+ disp_output_cntl = INREG(DISP_OUTPUT_CNTL);
+ source = (disp_output_cntl >> 12) & 0x3;
+ }
+ } else
+ source = (fp2_gen_cntl >> 13) & 0x1;
+ /* sourced from CRTC2 -> exit */
+ if (source == 1)
+ break;
+
+ /* so we end up on CRTC1, let's set uses_dvo to 1 now */
+ uses_dvo = 1;
+ break;
+ }
+#else
+ uses_dvo = 1;
+#endif
+ if (freq > rinfo->pll.ppll_max)
+ freq = rinfo->pll.ppll_max;
+ if (freq*12 < rinfo->pll.ppll_min)
+ freq = rinfo->pll.ppll_min / 12;
+ pr_debug("freq = %lu, PLL min = %u, PLL max = %u\n",
+ freq, rinfo->pll.ppll_min, rinfo->pll.ppll_max);
+
+ for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
+ pll_output_freq = post_div->divider * freq;
+ /* If we output to the DVO port (external TMDS), we don't allow an
+ * odd PLL divider as those aren't supported on this path
+ */
+ if (uses_dvo && (post_div->divider & 1))
+ continue;
+ if (pll_output_freq >= rinfo->pll.ppll_min &&
+ pll_output_freq <= rinfo->pll.ppll_max)
+ break;
+ }
+
+ /* If we fall through the bottom, try the "default value"
+ given by the terminal post_div->bitvalue */
+ if ( !post_div->divider ) {
+ post_div = &post_divs[post_div->bitvalue];
+ pll_output_freq = post_div->divider * freq;
+ }
+ pr_debug("ref_div = %d, ref_clk = %d, output_freq = %d\n",
+ rinfo->pll.ref_div, rinfo->pll.ref_clk,
+ pll_output_freq);
+
+ /* If we fall through the bottom, try the "default value"
+ given by the terminal post_div->bitvalue */
+ if ( !post_div->divider ) {
+ post_div = &post_divs[post_div->bitvalue];
+ pll_output_freq = post_div->divider * freq;
+ }
+ pr_debug("ref_div = %d, ref_clk = %d, output_freq = %d\n",
+ rinfo->pll.ref_div, rinfo->pll.ref_clk,
+ pll_output_freq);
+
+ fb_div = round_div(rinfo->pll.ref_div*pll_output_freq,
+ rinfo->pll.ref_clk);
+ regs->ppll_ref_div = rinfo->pll.ref_div;
+ regs->ppll_div_3 = fb_div | (post_div->bitvalue << 16);
+
+ pr_debug("post div = 0x%x\n", post_div->bitvalue);
+ pr_debug("fb_div = 0x%x\n", fb_div);
+ pr_debug("ppll_div_3 = 0x%x\n", regs->ppll_div_3);
+}
+
+static int radeonfb_set_par(struct fb_info *info)
+{
+ struct radeonfb_info *rinfo = info->par;
+ struct fb_var_screeninfo *mode = &info->var;
+ struct radeon_regs *newmode;
+ int hTotal, vTotal, hSyncStart, hSyncEnd,
+ hSyncPol, vSyncStart, vSyncEnd, vSyncPol, cSync;
+ u8 hsync_adj_tab[] = {0, 0x12, 9, 9, 6, 5};
+ u8 hsync_fudge_fp[] = {2, 2, 0, 0, 5, 5};
+ u32 sync, h_sync_pol, v_sync_pol, dotClock, pixClock;
+ int i, freq;
+ int format = 0;
+ int nopllcalc = 0;
+ int hsync_start, hsync_fudge, bytpp, hsync_wid, vsync_wid;
+ int primary_mon = PRIMARY_MONITOR(rinfo);
+ int depth = var_to_depth(mode);
+ int use_rmx = 0;
+
+ newmode = kmalloc(sizeof(struct radeon_regs), GFP_KERNEL);
+ if (!newmode)
+ return -ENOMEM;
+
+ /* We always want engine to be idle on a mode switch, even
+ * if we won't actually change the mode
+ */
+ radeon_engine_idle();
+
+ hSyncStart = mode->xres + mode->right_margin;
+ hSyncEnd = hSyncStart + mode->hsync_len;
+ hTotal = hSyncEnd + mode->left_margin;
+
+ vSyncStart = mode->yres + mode->lower_margin;
+ vSyncEnd = vSyncStart + mode->vsync_len;
+ vTotal = vSyncEnd + mode->upper_margin;
+ pixClock = mode->pixclock;
+
+ sync = mode->sync;
+ h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+ v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+ if (primary_mon == MT_DFP || primary_mon == MT_LCD) {
+ if (rinfo->panel_info.xres < mode->xres)
+ mode->xres = rinfo->panel_info.xres;
+ if (rinfo->panel_info.yres < mode->yres)
+ mode->yres = rinfo->panel_info.yres;
+
+ hTotal = mode->xres + rinfo->panel_info.hblank;
+ hSyncStart = mode->xres + rinfo->panel_info.hOver_plus;
+ hSyncEnd = hSyncStart + rinfo->panel_info.hSync_width;
+
+ vTotal = mode->yres + rinfo->panel_info.vblank;
+ vSyncStart = mode->yres + rinfo->panel_info.vOver_plus;
+ vSyncEnd = vSyncStart + rinfo->panel_info.vSync_width;
+
+ h_sync_pol = !rinfo->panel_info.hAct_high;
+ v_sync_pol = !rinfo->panel_info.vAct_high;
+
+ pixClock = 100000000 / rinfo->panel_info.clock;
+
+ if (rinfo->panel_info.use_bios_dividers) {
+ nopllcalc = 1;
+ newmode->ppll_div_3 = rinfo->panel_info.fbk_divider |
+ (rinfo->panel_info.post_divider << 16);
+ newmode->ppll_ref_div = rinfo->panel_info.ref_divider;
+ }
+ }
+ dotClock = 1000000000 / pixClock;
+ freq = dotClock / 10; /* x100 */
+
+ pr_debug("hStart = %d, hEnd = %d, hTotal = %d\n",
+ hSyncStart, hSyncEnd, hTotal);
+ pr_debug("vStart = %d, vEnd = %d, vTotal = %d\n",
+ vSyncStart, vSyncEnd, vTotal);
+
+ hsync_wid = (hSyncEnd - hSyncStart) / 8;
+ vsync_wid = vSyncEnd - vSyncStart;
+ if (hsync_wid == 0)
+ hsync_wid = 1;
+ else if (hsync_wid > 0x3f) /* max */
+ hsync_wid = 0x3f;
+
+ if (vsync_wid == 0)
+ vsync_wid = 1;
+ else if (vsync_wid > 0x1f) /* max */
+ vsync_wid = 0x1f;
+
+ hSyncPol = mode->sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
+ vSyncPol = mode->sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
+
+ cSync = mode->sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
+
+ format = radeon_get_dstbpp(depth);
+ bytpp = mode->bits_per_pixel >> 3;
+
+ if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD))
+ hsync_fudge = hsync_fudge_fp[format-1];
+ else
+ hsync_fudge = hsync_adj_tab[format-1];
+
+ hsync_start = hSyncStart - 8 + hsync_fudge;
+
+ newmode->crtc_gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN |
+ (format << 8);
+
+ /* Clear auto-center etc... */
+ newmode->crtc_more_cntl = rinfo->init_state.crtc_more_cntl;
+ newmode->crtc_more_cntl &= 0xfffffff0;
+
+ if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
+ newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN;
+ if (mirror)
+ newmode->crtc_ext_cntl |= CRTC_CRT_ON;
+
+ newmode->crtc_gen_cntl &= ~(CRTC_DBL_SCAN_EN |
+ CRTC_INTERLACE_EN);
+ } else {
+ newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN |
+ CRTC_CRT_ON;
+ }
+
+ newmode->dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN |
+ DAC_8BIT_EN;
+
+ newmode->crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) |
+ (((mode->xres / 8) - 1) << 16));
+
+ newmode->crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
+ (hsync_wid << 16) | (h_sync_pol << 23));
+
+ newmode->crtc_v_total_disp = ((vTotal - 1) & 0xffff) |
+ ((mode->yres - 1) << 16);
+
+ newmode->crtc_v_sync_strt_wid = (((vSyncStart - 1) & 0xfff) |
+ (vsync_wid << 16) | (v_sync_pol << 23));
+
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED)) {
+ /* We first calculate the engine pitch */
+ rinfo->pitch = ((mode->xres_virtual * ((mode->bits_per_pixel + 1) / 8) + 0x3f)
+ & ~(0x3f)) >> 6;
+
+ /* Then, re-multiply it to get the CRTC pitch */
+ newmode->crtc_pitch = (rinfo->pitch << 3) / ((mode->bits_per_pixel + 1) / 8);
+ } else
+ newmode->crtc_pitch = (mode->xres_virtual >> 3);
+
+ newmode->crtc_pitch |= (newmode->crtc_pitch << 16);
+
+ /*
+ * It looks like recent chips have a problem with SURFACE_CNTL,
+ * setting SURF_TRANSLATION_DIS completely disables the
+ * swapper as well, so we leave it unset now.
+ */
+ newmode->surface_cntl = 0;
+
+#if defined(__BIG_ENDIAN)
+
+ /* Setup swapping on both apertures, though we currently
+ * only use aperture 0, enabling swapper on aperture 1
+ * won't harm
+ */
+ switch (mode->bits_per_pixel) {
+ case 16:
+ newmode->surface_cntl |= NONSURF_AP0_SWP_16BPP;
+ newmode->surface_cntl |= NONSURF_AP1_SWP_16BPP;
+ break;
+ case 24:
+ case 32:
+ newmode->surface_cntl |= NONSURF_AP0_SWP_32BPP;
+ newmode->surface_cntl |= NONSURF_AP1_SWP_32BPP;
+ break;
+ }
+#endif
+
+ /* Clear surface registers */
+ for (i=0; i<8; i++) {
+ newmode->surf_lower_bound[i] = 0;
+ newmode->surf_upper_bound[i] = 0x1f;
+ newmode->surf_info[i] = 0;
+ }
+
+ pr_debug("h_total_disp = 0x%x\t hsync_strt_wid = 0x%x\n",
+ newmode->crtc_h_total_disp, newmode->crtc_h_sync_strt_wid);
+ pr_debug("v_total_disp = 0x%x\t vsync_strt_wid = 0x%x\n",
+ newmode->crtc_v_total_disp, newmode->crtc_v_sync_strt_wid);
+
+ rinfo->bpp = mode->bits_per_pixel;
+ rinfo->depth = depth;
+
+ pr_debug("pixclock = %lu\n", (unsigned long)pixClock);
+ pr_debug("freq = %lu\n", (unsigned long)freq);
+
+ /* We use PPLL_DIV_3 */
+ newmode->clk_cntl_index = 0x300;
+
+ /* Calculate PPLL value if necessary */
+ if (!nopllcalc)
+ radeon_calc_pll_regs(rinfo, newmode, freq);
+
+ newmode->vclk_ecp_cntl = rinfo->init_state.vclk_ecp_cntl;
+
+ if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
+ unsigned int hRatio, vRatio;
+
+ if (mode->xres > rinfo->panel_info.xres)
+ mode->xres = rinfo->panel_info.xres;
+ if (mode->yres > rinfo->panel_info.yres)
+ mode->yres = rinfo->panel_info.yres;
+
+ newmode->fp_horz_stretch = (((rinfo->panel_info.xres / 8) - 1)
+ << HORZ_PANEL_SHIFT);
+ newmode->fp_vert_stretch = ((rinfo->panel_info.yres - 1)
+ << VERT_PANEL_SHIFT);
+
+ if (mode->xres != rinfo->panel_info.xres) {
+ hRatio = round_div(mode->xres * HORZ_STRETCH_RATIO_MAX,
+ rinfo->panel_info.xres);
+ newmode->fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) |
+ (newmode->fp_horz_stretch &
+ (HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
+ HORZ_AUTO_RATIO_INC)));
+ newmode->fp_horz_stretch |= (HORZ_STRETCH_BLEND |
+ HORZ_STRETCH_ENABLE);
+ use_rmx = 1;
+ }
+ newmode->fp_horz_stretch &= ~HORZ_AUTO_RATIO;
+
+ if (mode->yres != rinfo->panel_info.yres) {
+ vRatio = round_div(mode->yres * VERT_STRETCH_RATIO_MAX,
+ rinfo->panel_info.yres);
+ newmode->fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) |
+ (newmode->fp_vert_stretch &
+ (VERT_PANEL_SIZE | VERT_STRETCH_RESERVED)));
+ newmode->fp_vert_stretch |= (VERT_STRETCH_BLEND |
+ VERT_STRETCH_ENABLE);
+ use_rmx = 1;
+ }
+ newmode->fp_vert_stretch &= ~VERT_AUTO_RATIO_EN;
+
+ newmode->fp_gen_cntl = (rinfo->init_state.fp_gen_cntl & (u32)
+ ~(FP_SEL_CRTC2 |
+ FP_RMX_HVSYNC_CONTROL_EN |
+ FP_DFP_SYNC_SEL |
+ FP_CRT_SYNC_SEL |
+ FP_CRTC_LOCK_8DOT |
+ FP_USE_SHADOW_EN |
+ FP_CRTC_USE_SHADOW_VEND |
+ FP_CRT_SYNC_ALT));
+
+ newmode->fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR |
+ FP_CRTC_DONT_SHADOW_HEND |
+ FP_PANEL_FORMAT);
+
+ if (IS_R300_VARIANT(rinfo) ||
+ (rinfo->family == CHIP_FAMILY_R200)) {
+ newmode->fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK;
+ if (use_rmx)
+ newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX;
+ else
+ newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1;
+ } else
+ newmode->fp_gen_cntl |= FP_SEL_CRTC1;
+
+ newmode->lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl;
+ newmode->lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl;
+ newmode->tmds_crc = rinfo->init_state.tmds_crc;
+ newmode->tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl;
+
+ if (primary_mon == MT_LCD) {
+ newmode->lvds_gen_cntl |= (LVDS_ON | LVDS_BLON);
+ newmode->fp_gen_cntl &= ~(FP_FPON | FP_TMDS_EN);
+ } else {
+ /* DFP */
+ newmode->fp_gen_cntl |= (FP_FPON | FP_TMDS_EN);
+ newmode->tmds_transmitter_cntl &= ~(TMDS_PLLRST);
+ /* TMDS_PLL_EN bit is reversed on RV (and mobility) chips */
+ if (IS_R300_VARIANT(rinfo) ||
+ (rinfo->family == CHIP_FAMILY_R200) || !rinfo->has_CRTC2)
+ newmode->tmds_transmitter_cntl &= ~TMDS_PLL_EN;
+ else
+ newmode->tmds_transmitter_cntl |= TMDS_PLL_EN;
+ newmode->crtc_ext_cntl &= ~CRTC_CRT_ON;
+ }
+
+ newmode->fp_crtc_h_total_disp = (((rinfo->panel_info.hblank / 8) & 0x3ff) |
+ (((mode->xres / 8) - 1) << 16));
+ newmode->fp_crtc_v_total_disp = (rinfo->panel_info.vblank & 0xffff) |
+ ((mode->yres - 1) << 16);
+ newmode->fp_h_sync_strt_wid = ((rinfo->panel_info.hOver_plus & 0x1fff) |
+ (hsync_wid << 16) | (h_sync_pol << 23));
+ newmode->fp_v_sync_strt_wid = ((rinfo->panel_info.vOver_plus & 0xfff) |
+ (vsync_wid << 16) | (v_sync_pol << 23));
+ }
+
+ /* do it! */
+ if (!rinfo->asleep) {
+ memcpy(&rinfo->state, newmode, sizeof(*newmode));
+ radeon_write_mode (rinfo, newmode, 0);
+ /* (re)initialize the engine */
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+ radeonfb_engine_init (rinfo);
+ }
+ /* Update fix */
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+ info->fix.line_length = rinfo->pitch*64;
+ else
+ info->fix.line_length = mode->xres_virtual
+ * ((mode->bits_per_pixel + 1) / 8);
+ info->fix.visual = rinfo->depth == 8 ? FB_VISUAL_PSEUDOCOLOR
+ : FB_VISUAL_DIRECTCOLOR;
+
+#ifdef CONFIG_BOOTX_TEXT
+ /* Update debug text engine */
+ btext_update_display(rinfo->fb_base_phys, mode->xres, mode->yres,
+ rinfo->depth, info->fix.line_length);
+#endif
+
+ kfree(newmode);
+ return 0;
+}
+
+
+static struct fb_ops radeonfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = radeonfb_check_var,
+ .fb_set_par = radeonfb_set_par,
+ .fb_setcolreg = radeonfb_setcolreg,
+ .fb_setcmap = radeonfb_setcmap,
+ .fb_pan_display = radeonfb_pan_display,
+ .fb_blank = radeonfb_blank,
+ .fb_ioctl = radeonfb_ioctl,
+ .fb_sync = radeonfb_sync,
+ .fb_fillrect = radeonfb_fillrect,
+ .fb_copyarea = radeonfb_copyarea,
+ .fb_imageblit = radeonfb_imageblit,
+};
+
+
+static int radeon_set_fbinfo(struct radeonfb_info *rinfo)
+{
+ struct fb_info *info = rinfo->info;
+
+ info->par = rinfo;
+ info->pseudo_palette = rinfo->pseudo_palette;
+ info->flags = FBINFO_DEFAULT
+ | FBINFO_HWACCEL_COPYAREA
+ | FBINFO_HWACCEL_FILLRECT
+ | FBINFO_HWACCEL_XPAN
+ | FBINFO_HWACCEL_YPAN;
+ info->fbops = &radeonfb_ops;
+ info->screen_base = rinfo->fb_base;
+ info->screen_size = rinfo->mapped_vram;
+ /* Fill fix common fields */
+ strlcpy(info->fix.id, rinfo->name, sizeof(info->fix.id));
+ info->fix.smem_start = rinfo->fb_base_phys;
+ info->fix.smem_len = rinfo->video_ram;
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ info->fix.xpanstep = 8;
+ info->fix.ypanstep = 1;
+ info->fix.ywrapstep = 0;
+ info->fix.type_aux = 0;
+ info->fix.mmio_start = rinfo->mmio_base_phys;
+ info->fix.mmio_len = RADEON_REGSIZE;
+ info->fix.accel = FB_ACCEL_ATI_RADEON;
+
+ fb_alloc_cmap(&info->cmap, 256, 0);
+
+ if (noaccel)
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+
+ return 0;
+}
+
+/*
+ * This reconfigure the card's internal memory map. In theory, we'd like
+ * to setup the card's memory at the same address as it's PCI bus address,
+ * and the AGP aperture right after that so that system RAM on 32 bits
+ * machines at least, is directly accessible. However, doing so would
+ * conflict with the current XFree drivers...
+ * Ultimately, I hope XFree, GATOS and ATI binary drivers will all agree
+ * on the proper way to set this up and duplicate this here. In the meantime,
+ * I put the card's memory at 0 in card space and AGP at some random high
+ * local (0xe0000000 for now) that will be changed by XFree/DRI anyway
+ */
+#ifdef CONFIG_PPC
+#undef SET_MC_FB_FROM_APERTURE
+static void fixup_memory_mappings(struct radeonfb_info *rinfo)
+{
+ u32 save_crtc_gen_cntl, save_crtc2_gen_cntl = 0;
+ u32 save_crtc_ext_cntl;
+ u32 aper_base, aper_size;
+ u32 agp_base;
+
+ /* First, we disable display to avoid interfering */
+ if (rinfo->has_CRTC2) {
+ save_crtc2_gen_cntl = INREG(CRTC2_GEN_CNTL);
+ OUTREG(CRTC2_GEN_CNTL, save_crtc2_gen_cntl | CRTC2_DISP_REQ_EN_B);
+ }
+ save_crtc_gen_cntl = INREG(CRTC_GEN_CNTL);
+ save_crtc_ext_cntl = INREG(CRTC_EXT_CNTL);
+
+ OUTREG(CRTC_EXT_CNTL, save_crtc_ext_cntl | CRTC_DISPLAY_DIS);
+ OUTREG(CRTC_GEN_CNTL, save_crtc_gen_cntl | CRTC_DISP_REQ_EN_B);
+ mdelay(100);
+
+ aper_base = INREG(CNFG_APER_0_BASE);
+ aper_size = INREG(CNFG_APER_SIZE);
+
+#ifdef SET_MC_FB_FROM_APERTURE
+ /* Set framebuffer to be at the same address as set in PCI BAR */
+ OUTREG(MC_FB_LOCATION,
+ ((aper_base + aper_size - 1) & 0xffff0000) | (aper_base >> 16));
+ rinfo->fb_local_base = aper_base;
+#else
+ OUTREG(MC_FB_LOCATION, 0x7fff0000);
+ rinfo->fb_local_base = 0;
+#endif
+ agp_base = aper_base + aper_size;
+ if (agp_base & 0xf0000000)
+ agp_base = (aper_base | 0x0fffffff) + 1;
+
+ /* Set AGP to be just after the framebuffer on a 256Mb boundary. This
+ * assumes the FB isn't mapped to 0xf0000000 or above, but this is
+ * always the case on PPCs afaik.
+ */
+#ifdef SET_MC_FB_FROM_APERTURE
+ OUTREG(MC_AGP_LOCATION, 0xffff0000 | (agp_base >> 16));
+#else
+ OUTREG(MC_AGP_LOCATION, 0xffffe000);
+#endif
+
+ /* Fixup the display base addresses & engine offsets while we
+ * are at it as well
+ */
+#ifdef SET_MC_FB_FROM_APERTURE
+ OUTREG(DISPLAY_BASE_ADDR, aper_base);
+ if (rinfo->has_CRTC2)
+ OUTREG(CRTC2_DISPLAY_BASE_ADDR, aper_base);
+ OUTREG(OV0_BASE_ADDR, aper_base);
+#else
+ OUTREG(DISPLAY_BASE_ADDR, 0);
+ if (rinfo->has_CRTC2)
+ OUTREG(CRTC2_DISPLAY_BASE_ADDR, 0);
+ OUTREG(OV0_BASE_ADDR, 0);
+#endif
+ mdelay(100);
+
+ /* Restore display settings */
+ OUTREG(CRTC_GEN_CNTL, save_crtc_gen_cntl);
+ OUTREG(CRTC_EXT_CNTL, save_crtc_ext_cntl);
+ if (rinfo->has_CRTC2)
+ OUTREG(CRTC2_GEN_CNTL, save_crtc2_gen_cntl);
+
+ pr_debug("aper_base: %08x MC_FB_LOC to: %08x, MC_AGP_LOC to: %08x\n",
+ aper_base,
+ ((aper_base + aper_size - 1) & 0xffff0000) | (aper_base >> 16),
+ 0xffff0000 | (agp_base >> 16));
+}
+#endif /* CONFIG_PPC */
+
+
+static void radeon_identify_vram(struct radeonfb_info *rinfo)
+{
+ u32 tmp;
+
+ /* framebuffer size */
+ if ((rinfo->family == CHIP_FAMILY_RS100) ||
+ (rinfo->family == CHIP_FAMILY_RS200) ||
+ (rinfo->family == CHIP_FAMILY_RS300) ||
+ (rinfo->family == CHIP_FAMILY_RC410) ||
+ (rinfo->family == CHIP_FAMILY_RS400) ||
+ (rinfo->family == CHIP_FAMILY_RS480) ) {
+ u32 tom = INREG(NB_TOM);
+ tmp = ((((tom >> 16) - (tom & 0xffff) + 1) << 6) * 1024);
+
+ radeon_fifo_wait(6);
+ OUTREG(MC_FB_LOCATION, tom);
+ OUTREG(DISPLAY_BASE_ADDR, (tom & 0xffff) << 16);
+ OUTREG(CRTC2_DISPLAY_BASE_ADDR, (tom & 0xffff) << 16);
+ OUTREG(OV0_BASE_ADDR, (tom & 0xffff) << 16);
+
+ /* This is supposed to fix the crtc2 noise problem. */
+ OUTREG(GRPH2_BUFFER_CNTL, INREG(GRPH2_BUFFER_CNTL) & ~0x7f0000);
+
+ if ((rinfo->family == CHIP_FAMILY_RS100) ||
+ (rinfo->family == CHIP_FAMILY_RS200)) {
+ /* This is to workaround the asic bug for RMX, some versions
+ of BIOS doesn't have this register initialized correctly.
+ */
+ OUTREGP(CRTC_MORE_CNTL, CRTC_H_CUTOFF_ACTIVE_EN,
+ ~CRTC_H_CUTOFF_ACTIVE_EN);
+ }
+ } else {
+ tmp = INREG(CNFG_MEMSIZE);
+ }
+
+ /* mem size is bits [28:0], mask off the rest */
+ rinfo->video_ram = tmp & CNFG_MEMSIZE_MASK;
+
+ /*
+ * Hack to get around some busted production M6's
+ * reporting no ram
+ */
+ if (rinfo->video_ram == 0) {
+ switch (rinfo->pdev->device) {
+ case PCI_CHIP_RADEON_LY:
+ case PCI_CHIP_RADEON_LZ:
+ rinfo->video_ram = 8192 * 1024;
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ /*
+ * Now try to identify VRAM type
+ */
+ if (rinfo->is_IGP || (rinfo->family >= CHIP_FAMILY_R300) ||
+ (INREG(MEM_SDRAM_MODE_REG) & (1<<30)))
+ rinfo->vram_ddr = 1;
+ else
+ rinfo->vram_ddr = 0;
+
+ tmp = INREG(MEM_CNTL);
+ if (IS_R300_VARIANT(rinfo)) {
+ tmp &= R300_MEM_NUM_CHANNELS_MASK;
+ switch (tmp) {
+ case 0: rinfo->vram_width = 64; break;
+ case 1: rinfo->vram_width = 128; break;
+ case 2: rinfo->vram_width = 256; break;
+ default: rinfo->vram_width = 128; break;
+ }
+ } else if ((rinfo->family == CHIP_FAMILY_RV100) ||
+ (rinfo->family == CHIP_FAMILY_RS100) ||
+ (rinfo->family == CHIP_FAMILY_RS200)){
+ if (tmp & RV100_MEM_HALF_MODE)
+ rinfo->vram_width = 32;
+ else
+ rinfo->vram_width = 64;
+ } else {
+ if (tmp & MEM_NUM_CHANNELS_MASK)
+ rinfo->vram_width = 128;
+ else
+ rinfo->vram_width = 64;
+ }
+
+ /* This may not be correct, as some cards can have half of channel disabled
+ * ToDo: identify these cases
+ */
+
+ pr_debug("radeonfb (%s): Found %ldk of %s %d bits wide videoram\n",
+ pci_name(rinfo->pdev),
+ rinfo->video_ram / 1024,
+ rinfo->vram_ddr ? "DDR" : "SDRAM",
+ rinfo->vram_width);
+}
+
+/*
+ * Sysfs
+ */
+
+static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u8 *edid)
+{
+ return memory_read_from_buffer(buf, count, &off, edid, EDID_LENGTH);
+}
+
+
+static ssize_t radeon_show_edid1(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct radeonfb_info *rinfo = info->par;
+
+ return radeon_show_one_edid(buf, off, count, rinfo->mon1_EDID);
+}
+
+
+static ssize_t radeon_show_edid2(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct radeonfb_info *rinfo = info->par;
+
+ return radeon_show_one_edid(buf, off, count, rinfo->mon2_EDID);
+}
+
+static const struct bin_attribute edid1_attr = {
+ .attr = {
+ .name = "edid1",
+ .mode = 0444,
+ },
+ .size = EDID_LENGTH,
+ .read = radeon_show_edid1,
+};
+
+static const struct bin_attribute edid2_attr = {
+ .attr = {
+ .name = "edid2",
+ .mode = 0444,
+ },
+ .size = EDID_LENGTH,
+ .read = radeon_show_edid2,
+};
+
+static int radeon_kick_out_firmware_fb(struct pci_dev *pdev)
+{
+ struct apertures_struct *ap;
+
+ ap = alloc_apertures(1);
+ if (!ap)
+ return -ENOMEM;
+
+ ap->ranges[0].base = pci_resource_start(pdev, 0);
+ ap->ranges[0].size = pci_resource_len(pdev, 0);
+
+ remove_conflicting_framebuffers(ap, KBUILD_MODNAME, false);
+
+ kfree(ap);
+
+ return 0;
+}
+
+static int radeonfb_pci_register(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct fb_info *info;
+ struct radeonfb_info *rinfo;
+ int ret;
+ unsigned char c1, c2;
+ int err = 0;
+
+ pr_debug("radeonfb_pci_register BEGIN\n");
+
+ /* Enable device in PCI config */
+ ret = pci_enable_device(pdev);
+ if (ret < 0) {
+ printk(KERN_ERR "radeonfb (%s): Cannot enable PCI device\n",
+ pci_name(pdev));
+ goto err_out;
+ }
+
+ info = framebuffer_alloc(sizeof(struct radeonfb_info), &pdev->dev);
+ if (!info) {
+ printk (KERN_ERR "radeonfb (%s): could not allocate memory\n",
+ pci_name(pdev));
+ ret = -ENOMEM;
+ goto err_disable;
+ }
+ rinfo = info->par;
+ rinfo->info = info;
+ rinfo->pdev = pdev;
+
+ spin_lock_init(&rinfo->reg_lock);
+ timer_setup(&rinfo->lvds_timer, radeon_lvds_timer_func, 0);
+
+ c1 = ent->device >> 8;
+ c2 = ent->device & 0xff;
+ if (isprint(c1) && isprint(c2))
+ snprintf(rinfo->name, sizeof(rinfo->name),
+ "ATI Radeon %x \"%c%c\"", ent->device & 0xffff, c1, c2);
+ else
+ snprintf(rinfo->name, sizeof(rinfo->name),
+ "ATI Radeon %x", ent->device & 0xffff);
+
+ rinfo->family = ent->driver_data & CHIP_FAMILY_MASK;
+ rinfo->chipset = pdev->device;
+ rinfo->has_CRTC2 = (ent->driver_data & CHIP_HAS_CRTC2) != 0;
+ rinfo->is_mobility = (ent->driver_data & CHIP_IS_MOBILITY) != 0;
+ rinfo->is_IGP = (ent->driver_data & CHIP_IS_IGP) != 0;
+
+ /* Set base addrs */
+ rinfo->fb_base_phys = pci_resource_start (pdev, 0);
+ rinfo->mmio_base_phys = pci_resource_start (pdev, 2);
+
+ ret = radeon_kick_out_firmware_fb(pdev);
+ if (ret)
+ goto err_release_fb;
+
+ /* request the mem regions */
+ ret = pci_request_region(pdev, 0, "radeonfb framebuffer");
+ if (ret < 0) {
+ printk( KERN_ERR "radeonfb (%s): cannot request region 0.\n",
+ pci_name(rinfo->pdev));
+ goto err_release_fb;
+ }
+
+ ret = pci_request_region(pdev, 2, "radeonfb mmio");
+ if (ret < 0) {
+ printk( KERN_ERR "radeonfb (%s): cannot request region 2.\n",
+ pci_name(rinfo->pdev));
+ goto err_release_pci0;
+ }
+
+ /* map the regions */
+ rinfo->mmio_base = ioremap(rinfo->mmio_base_phys, RADEON_REGSIZE);
+ if (!rinfo->mmio_base) {
+ printk(KERN_ERR "radeonfb (%s): cannot map MMIO\n",
+ pci_name(rinfo->pdev));
+ ret = -EIO;
+ goto err_release_pci2;
+ }
+
+ rinfo->fb_local_base = INREG(MC_FB_LOCATION) << 16;
+
+ /*
+ * Check for errata
+ */
+ rinfo->errata = 0;
+ if (rinfo->family == CHIP_FAMILY_R300 &&
+ (INREG(CNFG_CNTL) & CFG_ATI_REV_ID_MASK)
+ == CFG_ATI_REV_A11)
+ rinfo->errata |= CHIP_ERRATA_R300_CG;
+
+ if (rinfo->family == CHIP_FAMILY_RV200 ||
+ rinfo->family == CHIP_FAMILY_RS200)
+ rinfo->errata |= CHIP_ERRATA_PLL_DUMMYREADS;
+
+ if (rinfo->family == CHIP_FAMILY_RV100 ||
+ rinfo->family == CHIP_FAMILY_RS100 ||
+ rinfo->family == CHIP_FAMILY_RS200)
+ rinfo->errata |= CHIP_ERRATA_PLL_DELAY;
+
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+ /* On PPC, we obtain the OF device-node pointer to the firmware
+ * data for this chip
+ */
+ rinfo->of_node = pci_device_to_OF_node(pdev);
+ if (rinfo->of_node == NULL)
+ printk(KERN_WARNING "radeonfb (%s): Cannot match card to OF node !\n",
+ pci_name(rinfo->pdev));
+
+#endif /* CONFIG_PPC || CONFIG_SPARC */
+#ifdef CONFIG_PPC
+ /* On PPC, the firmware sets up a memory mapping that tends
+ * to cause lockups when enabling the engine. We reconfigure
+ * the card internal memory mappings properly
+ */
+ fixup_memory_mappings(rinfo);
+#endif /* CONFIG_PPC */
+
+ /* Get VRAM size and type */
+ radeon_identify_vram(rinfo);
+
+ rinfo->mapped_vram = min_t(unsigned long, MAX_MAPPED_VRAM, rinfo->video_ram);
+
+ do {
+ rinfo->fb_base = ioremap_wc(rinfo->fb_base_phys,
+ rinfo->mapped_vram);
+ } while (rinfo->fb_base == NULL &&
+ ((rinfo->mapped_vram /= 2) >= MIN_MAPPED_VRAM));
+
+ if (rinfo->fb_base == NULL) {
+ printk (KERN_ERR "radeonfb (%s): cannot map FB\n",
+ pci_name(rinfo->pdev));
+ ret = -EIO;
+ goto err_unmap_rom;
+ }
+
+ pr_debug("radeonfb (%s): mapped %ldk videoram\n", pci_name(rinfo->pdev),
+ rinfo->mapped_vram/1024);
+
+ /*
+ * Map the BIOS ROM if any and retrieve PLL parameters from
+ * the BIOS. We skip that on mobility chips as the real panel
+ * values we need aren't in the ROM but in the BIOS image in
+ * memory. This is definitely not the best meacnism though,
+ * we really need the arch code to tell us which is the "primary"
+ * video adapter to use the memory image (or better, the arch
+ * should provide us a copy of the BIOS image to shield us from
+ * archs who would store that elsewhere and/or could initialize
+ * more than one adapter during boot).
+ */
+ if (!rinfo->is_mobility)
+ radeon_map_ROM(rinfo, pdev);
+
+ /*
+ * On x86, the primary display on laptop may have it's BIOS
+ * ROM elsewhere, try to locate it at the legacy memory hole.
+ * We probably need to make sure this is the primary display,
+ * but that is difficult without some arch support.
+ */
+#ifdef CONFIG_X86
+ if (rinfo->bios_seg == NULL)
+ radeon_find_mem_vbios(rinfo);
+#endif
+
+ /* If both above failed, try the BIOS ROM again for mobility
+ * chips
+ */
+ if (rinfo->bios_seg == NULL && rinfo->is_mobility)
+ radeon_map_ROM(rinfo, pdev);
+
+ /* Get informations about the board's PLL */
+ radeon_get_pllinfo(rinfo);
+
+#ifdef CONFIG_FB_RADEON_I2C
+ /* Register I2C bus */
+ radeon_create_i2c_busses(rinfo);
+#endif
+
+ /* set all the vital stuff */
+ radeon_set_fbinfo (rinfo);
+
+ /* Probe screen types */
+ radeon_probe_screens(rinfo, monitor_layout, ignore_edid);
+
+ /* Build mode list, check out panel native model */
+ radeon_check_modes(rinfo, mode_option);
+
+ /* Register some sysfs stuff (should be done better) */
+ if (rinfo->mon1_EDID)
+ err |= sysfs_create_bin_file(&rinfo->pdev->dev.kobj,
+ &edid1_attr);
+ if (rinfo->mon2_EDID)
+ err |= sysfs_create_bin_file(&rinfo->pdev->dev.kobj,
+ &edid2_attr);
+ if (err)
+ pr_warn("%s() Creating sysfs files failed, continuing\n",
+ __func__);
+
+ /* save current mode regs before we switch into the new one
+ * so we can restore this upon __exit
+ */
+ radeon_save_state (rinfo, &rinfo->init_state);
+ memcpy(&rinfo->state, &rinfo->init_state, sizeof(struct radeon_regs));
+
+ /* Setup Power Management capabilities */
+ if (default_dynclk < -1) {
+ /* -2 is special: means ON on mobility chips and do not
+ * change on others
+ */
+ radeonfb_pm_init(rinfo, rinfo->is_mobility ? 1 : -1, ignore_devlist, force_sleep);
+ } else
+ radeonfb_pm_init(rinfo, default_dynclk, ignore_devlist, force_sleep);
+
+ pci_set_drvdata(pdev, info);
+
+ /* Register with fbdev layer */
+ ret = register_framebuffer(info);
+ if (ret < 0) {
+ printk (KERN_ERR "radeonfb (%s): could not register framebuffer\n",
+ pci_name(rinfo->pdev));
+ goto err_unmap_fb;
+ }
+
+ if (!nomtrr)
+ rinfo->wc_cookie = arch_phys_wc_add(rinfo->fb_base_phys,
+ rinfo->video_ram);
+
+ if (backlight)
+ radeonfb_bl_init(rinfo);
+
+ printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name);
+
+ if (rinfo->bios_seg)
+ radeon_unmap_ROM(rinfo, pdev);
+ pr_debug("radeonfb_pci_register END\n");
+
+ return 0;
+err_unmap_fb:
+ iounmap(rinfo->fb_base);
+err_unmap_rom:
+ kfree(rinfo->mon1_EDID);
+ kfree(rinfo->mon2_EDID);
+ if (rinfo->mon1_modedb)
+ fb_destroy_modedb(rinfo->mon1_modedb);
+ fb_dealloc_cmap(&info->cmap);
+#ifdef CONFIG_FB_RADEON_I2C
+ radeon_delete_i2c_busses(rinfo);
+#endif
+ if (rinfo->bios_seg)
+ radeon_unmap_ROM(rinfo, pdev);
+ iounmap(rinfo->mmio_base);
+err_release_pci2:
+ pci_release_region(pdev, 2);
+err_release_pci0:
+ pci_release_region(pdev, 0);
+err_release_fb:
+ framebuffer_release(info);
+err_disable:
+err_out:
+ return ret;
+}
+
+
+
+static void radeonfb_pci_unregister(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct radeonfb_info *rinfo = info->par;
+
+ if (!rinfo)
+ return;
+
+ radeonfb_pm_exit(rinfo);
+
+ if (rinfo->mon1_EDID)
+ sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid1_attr);
+ if (rinfo->mon2_EDID)
+ sysfs_remove_bin_file(&rinfo->pdev->dev.kobj, &edid2_attr);
+
+#if 0
+ /* restore original state
+ *
+ * Doesn't quite work yet, I suspect if we come from a legacy
+ * VGA mode (or worse, text mode), we need to do some VGA black
+ * magic here that I know nothing about. --BenH
+ */
+ radeon_write_mode (rinfo, &rinfo->init_state, 1);
+ #endif
+
+ del_timer_sync(&rinfo->lvds_timer);
+ arch_phys_wc_del(rinfo->wc_cookie);
+ unregister_framebuffer(info);
+
+ radeonfb_bl_exit(rinfo);
+
+ iounmap(rinfo->mmio_base);
+ iounmap(rinfo->fb_base);
+
+ pci_release_region(pdev, 2);
+ pci_release_region(pdev, 0);
+
+ kfree(rinfo->mon1_EDID);
+ kfree(rinfo->mon2_EDID);
+ if (rinfo->mon1_modedb)
+ fb_destroy_modedb(rinfo->mon1_modedb);
+#ifdef CONFIG_FB_RADEON_I2C
+ radeon_delete_i2c_busses(rinfo);
+#endif
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+}
+
+
+static struct pci_driver radeonfb_driver = {
+ .name = "radeonfb",
+ .id_table = radeonfb_pci_table,
+ .probe = radeonfb_pci_register,
+ .remove = radeonfb_pci_unregister,
+#ifdef CONFIG_PM
+ .suspend = radeonfb_pci_suspend,
+ .resume = radeonfb_pci_resume,
+#endif /* CONFIG_PM */
+};
+
+#ifndef MODULE
+static int __init radeonfb_setup (char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep (&options, ",")) != NULL) {
+ if (!*this_opt)
+ continue;
+
+ if (!strncmp(this_opt, "noaccel", 7)) {
+ noaccel = 1;
+ } else if (!strncmp(this_opt, "mirror", 6)) {
+ mirror = 1;
+ } else if (!strncmp(this_opt, "force_dfp", 9)) {
+ force_dfp = 1;
+ } else if (!strncmp(this_opt, "panel_yres:", 11)) {
+ panel_yres = simple_strtoul((this_opt+11), NULL, 0);
+ } else if (!strncmp(this_opt, "backlight:", 10)) {
+ backlight = simple_strtoul(this_opt+10, NULL, 0);
+ } else if (!strncmp(this_opt, "nomtrr", 6)) {
+ nomtrr = 1;
+ } else if (!strncmp(this_opt, "nomodeset", 9)) {
+ nomodeset = 1;
+ } else if (!strncmp(this_opt, "force_measure_pll", 17)) {
+ force_measure_pll = 1;
+ } else if (!strncmp(this_opt, "ignore_edid", 11)) {
+ ignore_edid = 1;
+#if defined(CONFIG_PM) && defined(CONFIG_X86)
+ } else if (!strncmp(this_opt, "force_sleep", 11)) {
+ force_sleep = 1;
+ } else if (!strncmp(this_opt, "ignore_devlist", 14)) {
+ ignore_devlist = 1;
+#endif
+ } else
+ mode_option = this_opt;
+ }
+ return 0;
+}
+#endif /* MODULE */
+
+static int __init radeonfb_init (void)
+{
+#ifndef MODULE
+ char *option = NULL;
+
+ if (fb_get_options("radeonfb", &option))
+ return -ENODEV;
+ radeonfb_setup(option);
+#endif
+ return pci_register_driver (&radeonfb_driver);
+}
+
+
+static void __exit radeonfb_exit (void)
+{
+ pci_unregister_driver (&radeonfb_driver);
+}
+
+module_init(radeonfb_init);
+module_exit(radeonfb_exit);
+
+MODULE_AUTHOR("Ani Joshi");
+MODULE_DESCRIPTION("framebuffer driver for ATI Radeon chipset");
+MODULE_LICENSE("GPL");
+module_param(noaccel, bool, 0);
+module_param(default_dynclk, int, 0);
+MODULE_PARM_DESC(default_dynclk, "int: -2=enable on mobility only,-1=do not change,0=off,1=on");
+MODULE_PARM_DESC(noaccel, "bool: disable acceleration");
+module_param(nomodeset, bool, 0);
+MODULE_PARM_DESC(nomodeset, "bool: disable actual setting of video mode");
+module_param(mirror, bool, 0);
+MODULE_PARM_DESC(mirror, "bool: mirror the display to both monitors");
+module_param(force_dfp, bool, 0);
+MODULE_PARM_DESC(force_dfp, "bool: force display to dfp");
+module_param(ignore_edid, bool, 0);
+MODULE_PARM_DESC(ignore_edid, "bool: Ignore EDID data when doing DDC probe");
+module_param(monitor_layout, charp, 0);
+MODULE_PARM_DESC(monitor_layout, "Specify monitor mapping (like XFree86)");
+module_param(force_measure_pll, bool, 0);
+MODULE_PARM_DESC(force_measure_pll, "Force measurement of PLL (debug)");
+module_param(nomtrr, bool, 0);
+MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
+module_param(panel_yres, int, 0);
+MODULE_PARM_DESC(panel_yres, "int: set panel yres");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
+#if defined(CONFIG_PM) && defined(CONFIG_X86)
+module_param(force_sleep, bool, 0);
+MODULE_PARM_DESC(force_sleep, "bool: force D2 sleep mode on all hardware");
+module_param(ignore_devlist, bool, 0);
+MODULE_PARM_DESC(ignore_devlist, "bool: ignore workarounds for bugs in specific laptops");
+#endif
diff --git a/drivers/video/fbdev/aty/radeon_i2c.c b/drivers/video/fbdev/aty/radeon_i2c.c
new file mode 100644
index 000000000..269b12ebb
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_i2c.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "radeonfb.h"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/io.h>
+
+#include <video/radeon.h>
+#include "../edid.h"
+
+static void radeon_gpio_setscl(void* data, int state)
+{
+ struct radeon_i2c_chan *chan = data;
+ struct radeonfb_info *rinfo = chan->rinfo;
+ u32 val;
+
+ val = INREG(chan->ddc_reg) & ~(VGA_DDC_CLK_OUT_EN);
+ if (!state)
+ val |= VGA_DDC_CLK_OUT_EN;
+
+ OUTREG(chan->ddc_reg, val);
+ (void)INREG(chan->ddc_reg);
+}
+
+static void radeon_gpio_setsda(void* data, int state)
+{
+ struct radeon_i2c_chan *chan = data;
+ struct radeonfb_info *rinfo = chan->rinfo;
+ u32 val;
+
+ val = INREG(chan->ddc_reg) & ~(VGA_DDC_DATA_OUT_EN);
+ if (!state)
+ val |= VGA_DDC_DATA_OUT_EN;
+
+ OUTREG(chan->ddc_reg, val);
+ (void)INREG(chan->ddc_reg);
+}
+
+static int radeon_gpio_getscl(void* data)
+{
+ struct radeon_i2c_chan *chan = data;
+ struct radeonfb_info *rinfo = chan->rinfo;
+ u32 val;
+
+ val = INREG(chan->ddc_reg);
+
+ return (val & VGA_DDC_CLK_INPUT) ? 1 : 0;
+}
+
+static int radeon_gpio_getsda(void* data)
+{
+ struct radeon_i2c_chan *chan = data;
+ struct radeonfb_info *rinfo = chan->rinfo;
+ u32 val;
+
+ val = INREG(chan->ddc_reg);
+
+ return (val & VGA_DDC_DATA_INPUT) ? 1 : 0;
+}
+
+static int radeon_setup_i2c_bus(struct radeon_i2c_chan *chan, const char *name)
+{
+ int rc;
+
+ snprintf(chan->adapter.name, sizeof(chan->adapter.name),
+ "radeonfb %s", name);
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &chan->rinfo->pdev->dev;
+ chan->algo.setsda = radeon_gpio_setsda;
+ chan->algo.setscl = radeon_gpio_setscl;
+ chan->algo.getsda = radeon_gpio_getsda;
+ chan->algo.getscl = radeon_gpio_getscl;
+ chan->algo.udelay = 10;
+ chan->algo.timeout = 20;
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ /* Raise SCL and SDA */
+ radeon_gpio_setsda(chan, 1);
+ radeon_gpio_setscl(chan, 1);
+ udelay(20);
+
+ rc = i2c_bit_add_bus(&chan->adapter);
+ if (rc == 0)
+ dev_dbg(&chan->rinfo->pdev->dev, "I2C bus %s registered.\n", name);
+ else
+ dev_warn(&chan->rinfo->pdev->dev, "Failed to register I2C bus %s.\n", name);
+ return rc;
+}
+
+void radeon_create_i2c_busses(struct radeonfb_info *rinfo)
+{
+ rinfo->i2c[0].rinfo = rinfo;
+ rinfo->i2c[0].ddc_reg = GPIO_MONID;
+#ifndef CONFIG_PPC
+ rinfo->i2c[0].adapter.class = I2C_CLASS_HWMON;
+#endif
+ radeon_setup_i2c_bus(&rinfo->i2c[0], "monid");
+
+ rinfo->i2c[1].rinfo = rinfo;
+ rinfo->i2c[1].ddc_reg = GPIO_DVI_DDC;
+ radeon_setup_i2c_bus(&rinfo->i2c[1], "dvi");
+
+ rinfo->i2c[2].rinfo = rinfo;
+ rinfo->i2c[2].ddc_reg = GPIO_VGA_DDC;
+ radeon_setup_i2c_bus(&rinfo->i2c[2], "vga");
+
+ rinfo->i2c[3].rinfo = rinfo;
+ rinfo->i2c[3].ddc_reg = GPIO_CRT2_DDC;
+ radeon_setup_i2c_bus(&rinfo->i2c[3], "crt2");
+}
+
+void radeon_delete_i2c_busses(struct radeonfb_info *rinfo)
+{
+ if (rinfo->i2c[0].rinfo)
+ i2c_del_adapter(&rinfo->i2c[0].adapter);
+ rinfo->i2c[0].rinfo = NULL;
+
+ if (rinfo->i2c[1].rinfo)
+ i2c_del_adapter(&rinfo->i2c[1].adapter);
+ rinfo->i2c[1].rinfo = NULL;
+
+ if (rinfo->i2c[2].rinfo)
+ i2c_del_adapter(&rinfo->i2c[2].adapter);
+ rinfo->i2c[2].rinfo = NULL;
+
+ if (rinfo->i2c[3].rinfo)
+ i2c_del_adapter(&rinfo->i2c[3].adapter);
+ rinfo->i2c[3].rinfo = NULL;
+}
+
+int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn,
+ u8 **out_edid)
+{
+ u8 *edid;
+
+ edid = fb_ddc_read(&rinfo->i2c[conn-1].adapter);
+
+ if (out_edid)
+ *out_edid = edid;
+ if (!edid) {
+ pr_debug("radeonfb: I2C (port %d) ... not found\n", conn);
+ return MT_NONE;
+ }
+ if (edid[0x14] & 0x80) {
+ /* Fix detection using BIOS tables */
+ if (rinfo->is_mobility /*&& conn == ddc_dvi*/ &&
+ (INREG(LVDS_GEN_CNTL) & LVDS_ON)) {
+ pr_debug("radeonfb: I2C (port %d) ... found LVDS panel\n", conn);
+ return MT_LCD;
+ } else {
+ pr_debug("radeonfb: I2C (port %d) ... found TMDS panel\n", conn);
+ return MT_DFP;
+ }
+ }
+ pr_debug("radeonfb: I2C (port %d) ... found CRT display\n", conn);
+ return MT_CRT;
+}
+
diff --git a/drivers/video/fbdev/aty/radeon_monitor.c b/drivers/video/fbdev/aty/radeon_monitor.c
new file mode 100644
index 000000000..9966c58aa
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_monitor.c
@@ -0,0 +1,1053 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "radeonfb.h"
+
+#include <linux/slab.h>
+
+#include "../edid.h"
+
+static const struct fb_var_screeninfo radeonfb_default_var = {
+ .xres = 640,
+ .yres = 480,
+ .xres_virtual = 640,
+ .yres_virtual = 480,
+ .bits_per_pixel = 8,
+ .red = { .length = 8 },
+ .green = { .length = 8 },
+ .blue = { .length = 8 },
+ .activate = FB_ACTIVATE_NOW,
+ .height = -1,
+ .width = -1,
+ .pixclock = 39721,
+ .left_margin = 40,
+ .right_margin = 24,
+ .upper_margin = 32,
+ .lower_margin = 11,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .vmode = FB_VMODE_NONINTERLACED
+};
+
+static char *radeon_get_mon_name(int type)
+{
+ char *pret = NULL;
+
+ switch (type) {
+ case MT_NONE:
+ pret = "no";
+ break;
+ case MT_CRT:
+ pret = "CRT";
+ break;
+ case MT_DFP:
+ pret = "DFP";
+ break;
+ case MT_LCD:
+ pret = "LCD";
+ break;
+ case MT_CTV:
+ pret = "CTV";
+ break;
+ case MT_STV:
+ pret = "STV";
+ break;
+ }
+
+ return pret;
+}
+
+
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+/*
+ * Try to find monitor informations & EDID data out of the Open Firmware
+ * device-tree. This also contains some "hacks" to work around a few machine
+ * models with broken OF probing by hard-coding known EDIDs for some Mac
+ * laptops internal LVDS panel. (XXX: not done yet)
+ */
+static int radeon_parse_montype_prop(struct device_node *dp, u8 **out_EDID,
+ int hdno)
+{
+ static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID",
+ "EDID1", "EDID2", NULL };
+ const u8 *pedid = NULL;
+ const u8 *pmt = NULL;
+ u8 *tmp;
+ int i, mt = MT_NONE;
+
+ pr_debug("analyzing OF properties...\n");
+ pmt = of_get_property(dp, "display-type", NULL);
+ if (!pmt)
+ return MT_NONE;
+ pr_debug("display-type: %s\n", pmt);
+ /* OF says "LCD" for DFP as well, we discriminate from the caller of this
+ * function
+ */
+ if (!strcmp(pmt, "LCD") || !strcmp(pmt, "DFP"))
+ mt = MT_DFP;
+ else if (!strcmp(pmt, "CRT"))
+ mt = MT_CRT;
+ else {
+ if (strcmp(pmt, "NONE") != 0)
+ printk(KERN_WARNING "radeonfb: Unknown OF display-type: %s\n",
+ pmt);
+ return MT_NONE;
+ }
+
+ for (i = 0; propnames[i] != NULL; ++i) {
+ pedid = of_get_property(dp, propnames[i], NULL);
+ if (pedid != NULL)
+ break;
+ }
+ /* We didn't find the EDID in the leaf node, some cards will actually
+ * put EDID1/EDID2 in the parent, look for these (typically M6 tipb).
+ * single-head cards have hdno == -1 and skip this step
+ */
+ if (pedid == NULL && dp->parent && (hdno != -1))
+ pedid = of_get_property(dp->parent,
+ (hdno == 0) ? "EDID1" : "EDID2", NULL);
+ if (pedid == NULL && dp->parent && (hdno == 0))
+ pedid = of_get_property(dp->parent, "EDID", NULL);
+ if (pedid == NULL)
+ return mt;
+
+ tmp = kmemdup(pedid, EDID_LENGTH, GFP_KERNEL);
+ if (!tmp)
+ return mt;
+ *out_EDID = tmp;
+ return mt;
+}
+
+static int radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_no,
+ u8 **out_EDID)
+{
+ struct device_node *dp;
+
+ pr_debug("radeon_probe_OF_head\n");
+
+ dp = rinfo->of_node;
+ while (dp == NULL)
+ return MT_NONE;
+
+ if (rinfo->has_CRTC2) {
+ const char *pname;
+ int len, second = 0;
+
+ dp = dp->child;
+ do {
+ if (!dp)
+ return MT_NONE;
+ pname = of_get_property(dp, "name", NULL);
+ if (!pname)
+ return MT_NONE;
+ len = strlen(pname);
+ pr_debug("head: %s (letter: %c, head_no: %d)\n",
+ pname, pname[len-1], head_no);
+ if (pname[len-1] == 'A' && head_no == 0) {
+ int mt = radeon_parse_montype_prop(dp, out_EDID, 0);
+ /* Maybe check for LVDS_GEN_CNTL here ? I need to check out
+ * what OF does when booting with lid closed
+ */
+ if (mt == MT_DFP && rinfo->is_mobility)
+ mt = MT_LCD;
+ return mt;
+ } else if (pname[len-1] == 'B' && head_no == 1)
+ return radeon_parse_montype_prop(dp, out_EDID, 1);
+ second = 1;
+ dp = dp->sibling;
+ } while(!second);
+ } else {
+ if (head_no > 0)
+ return MT_NONE;
+ return radeon_parse_montype_prop(dp, out_EDID, -1);
+ }
+ return MT_NONE;
+}
+#endif /* CONFIG_PPC || CONFIG_SPARC */
+
+
+static int radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo)
+{
+ unsigned long tmp, tmp0;
+ char stmp[30];
+ int i;
+
+ if (!rinfo->bios_seg)
+ return 0;
+
+ if (!(tmp = BIOS_IN16(rinfo->fp_bios_start + 0x40))) {
+ printk(KERN_ERR "radeonfb: Failed to detect DFP panel info using BIOS\n");
+ rinfo->panel_info.pwr_delay = 200;
+ return 0;
+ }
+
+ for(i=0; i<24; i++)
+ stmp[i] = BIOS_IN8(tmp+i+1);
+ stmp[24] = 0;
+ printk("radeonfb: panel ID string: %s\n", stmp);
+ rinfo->panel_info.xres = BIOS_IN16(tmp + 25);
+ rinfo->panel_info.yres = BIOS_IN16(tmp + 27);
+ printk("radeonfb: detected LVDS panel size from BIOS: %dx%d\n",
+ rinfo->panel_info.xres, rinfo->panel_info.yres);
+
+ rinfo->panel_info.pwr_delay = BIOS_IN16(tmp + 44);
+ pr_debug("BIOS provided panel power delay: %d\n", rinfo->panel_info.pwr_delay);
+ if (rinfo->panel_info.pwr_delay > 2000 || rinfo->panel_info.pwr_delay <= 0)
+ rinfo->panel_info.pwr_delay = 2000;
+
+ /*
+ * Some panels only work properly with some divider combinations
+ */
+ rinfo->panel_info.ref_divider = BIOS_IN16(tmp + 46);
+ rinfo->panel_info.post_divider = BIOS_IN8(tmp + 48);
+ rinfo->panel_info.fbk_divider = BIOS_IN16(tmp + 49);
+ if (rinfo->panel_info.ref_divider != 0 &&
+ rinfo->panel_info.fbk_divider > 3) {
+ rinfo->panel_info.use_bios_dividers = 1;
+ printk(KERN_INFO "radeondb: BIOS provided dividers will be used\n");
+ pr_debug("ref_divider = %x\n", rinfo->panel_info.ref_divider);
+ pr_debug("post_divider = %x\n", rinfo->panel_info.post_divider);
+ pr_debug("fbk_divider = %x\n", rinfo->panel_info.fbk_divider);
+ }
+ pr_debug("Scanning BIOS table ...\n");
+ for(i=0; i<32; i++) {
+ tmp0 = BIOS_IN16(tmp+64+i*2);
+ if (tmp0 == 0)
+ break;
+ pr_debug(" %d x %d\n", BIOS_IN16(tmp0), BIOS_IN16(tmp0+2));
+ if ((BIOS_IN16(tmp0) == rinfo->panel_info.xres) &&
+ (BIOS_IN16(tmp0+2) == rinfo->panel_info.yres)) {
+ rinfo->panel_info.hblank = (BIOS_IN16(tmp0+17) - BIOS_IN16(tmp0+19)) * 8;
+ rinfo->panel_info.hOver_plus = ((BIOS_IN16(tmp0+21) -
+ BIOS_IN16(tmp0+19) -1) * 8) & 0x7fff;
+ rinfo->panel_info.hSync_width = BIOS_IN8(tmp0+23) * 8;
+ rinfo->panel_info.vblank = BIOS_IN16(tmp0+24) - BIOS_IN16(tmp0+26);
+ rinfo->panel_info.vOver_plus = (BIOS_IN16(tmp0+28) & 0x7ff) - BIOS_IN16(tmp0+26);
+ rinfo->panel_info.vSync_width = (BIOS_IN16(tmp0+28) & 0xf800) >> 11;
+ rinfo->panel_info.clock = BIOS_IN16(tmp0+9);
+ /* Assume high active syncs for now until ATI tells me more... maybe we
+ * can probe register values here ?
+ */
+ rinfo->panel_info.hAct_high = 1;
+ rinfo->panel_info.vAct_high = 1;
+ /* Mark panel infos valid */
+ rinfo->panel_info.valid = 1;
+
+ pr_debug("Found panel in BIOS table:\n");
+ pr_debug(" hblank: %d\n", rinfo->panel_info.hblank);
+ pr_debug(" hOver_plus: %d\n", rinfo->panel_info.hOver_plus);
+ pr_debug(" hSync_width: %d\n", rinfo->panel_info.hSync_width);
+ pr_debug(" vblank: %d\n", rinfo->panel_info.vblank);
+ pr_debug(" vOver_plus: %d\n", rinfo->panel_info.vOver_plus);
+ pr_debug(" vSync_width: %d\n", rinfo->panel_info.vSync_width);
+ pr_debug(" clock: %d\n", rinfo->panel_info.clock);
+
+ return 1;
+ }
+ }
+ pr_debug("Didn't find panel in BIOS table !\n");
+
+ return 0;
+}
+
+/* Try to extract the connector informations from the BIOS. This
+ * doesn't quite work yet, but it's output is still useful for
+ * debugging
+ */
+static void radeon_parse_connector_info(struct radeonfb_info *rinfo)
+{
+ int offset, chips, connectors, tmp, i, conn, type;
+
+ static char* __conn_type_table[16] = {
+ "NONE", "Proprietary", "CRT", "DVI-I", "DVI-D", "Unknown", "Unknown",
+ "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown",
+ "Unknown", "Unknown", "Unknown"
+ };
+
+ if (!rinfo->bios_seg)
+ return;
+
+ offset = BIOS_IN16(rinfo->fp_bios_start + 0x50);
+ if (offset == 0) {
+ printk(KERN_WARNING "radeonfb: No connector info table detected\n");
+ return;
+ }
+
+ /* Don't do much more at this point but displaying the data if
+ * DEBUG is enabled
+ */
+ chips = BIOS_IN8(offset++) >> 4;
+ pr_debug("%d chips in connector info\n", chips);
+ for (i = 0; i < chips; i++) {
+ tmp = BIOS_IN8(offset++);
+ connectors = tmp & 0x0f;
+ pr_debug(" - chip %d has %d connectors\n", tmp >> 4, connectors);
+ for (conn = 0; ; conn++) {
+ tmp = BIOS_IN16(offset);
+ if (tmp == 0)
+ break;
+ offset += 2;
+ type = (tmp >> 12) & 0x0f;
+ pr_debug(" * connector %d of type %d (%s) : %04x\n",
+ conn, type, __conn_type_table[type], tmp);
+ }
+ }
+}
+
+
+/*
+ * Probe physical connection of a CRT. This code comes from XFree
+ * as well and currently is only implemented for the CRT DAC, the
+ * code for the TVDAC is commented out in XFree as "non working"
+ */
+static int radeon_crt_is_connected(struct radeonfb_info *rinfo, int is_crt_dac)
+{
+ int connected = 0;
+
+ /* the monitor either wasn't connected or it is a non-DDC CRT.
+ * try to probe it
+ */
+ if (is_crt_dac) {
+ unsigned long ulOrigVCLK_ECP_CNTL;
+ unsigned long ulOrigDAC_CNTL;
+ unsigned long ulOrigDAC_EXT_CNTL;
+ unsigned long ulOrigCRTC_EXT_CNTL;
+ unsigned long ulData;
+ unsigned long ulMask;
+
+ ulOrigVCLK_ECP_CNTL = INPLL(VCLK_ECP_CNTL);
+
+ ulData = ulOrigVCLK_ECP_CNTL;
+ ulData &= ~(PIXCLK_ALWAYS_ONb
+ | PIXCLK_DAC_ALWAYS_ONb);
+ ulMask = ~(PIXCLK_ALWAYS_ONb
+ | PIXCLK_DAC_ALWAYS_ONb);
+ OUTPLLP(VCLK_ECP_CNTL, ulData, ulMask);
+
+ ulOrigCRTC_EXT_CNTL = INREG(CRTC_EXT_CNTL);
+ ulData = ulOrigCRTC_EXT_CNTL;
+ ulData |= CRTC_CRT_ON;
+ OUTREG(CRTC_EXT_CNTL, ulData);
+
+ ulOrigDAC_EXT_CNTL = INREG(DAC_EXT_CNTL);
+ ulData = ulOrigDAC_EXT_CNTL;
+ ulData &= ~DAC_FORCE_DATA_MASK;
+ ulData |= (DAC_FORCE_BLANK_OFF_EN
+ |DAC_FORCE_DATA_EN
+ |DAC_FORCE_DATA_SEL_MASK);
+ if ((rinfo->family == CHIP_FAMILY_RV250) ||
+ (rinfo->family == CHIP_FAMILY_RV280))
+ ulData |= (0x01b6 << DAC_FORCE_DATA_SHIFT);
+ else
+ ulData |= (0x01ac << DAC_FORCE_DATA_SHIFT);
+
+ OUTREG(DAC_EXT_CNTL, ulData);
+
+ ulOrigDAC_CNTL = INREG(DAC_CNTL);
+ ulData = ulOrigDAC_CNTL;
+ ulData |= DAC_CMP_EN;
+ ulData &= ~(DAC_RANGE_CNTL_MASK
+ | DAC_PDWN);
+ ulData |= 0x2;
+ OUTREG(DAC_CNTL, ulData);
+
+ mdelay(1);
+
+ ulData = INREG(DAC_CNTL);
+ connected = (DAC_CMP_OUTPUT & ulData) ? 1 : 0;
+
+ ulData = ulOrigVCLK_ECP_CNTL;
+ ulMask = 0xFFFFFFFFL;
+ OUTPLLP(VCLK_ECP_CNTL, ulData, ulMask);
+
+ OUTREG(DAC_CNTL, ulOrigDAC_CNTL );
+ OUTREG(DAC_EXT_CNTL, ulOrigDAC_EXT_CNTL );
+ OUTREG(CRTC_EXT_CNTL, ulOrigCRTC_EXT_CNTL);
+ }
+
+ return connected ? MT_CRT : MT_NONE;
+}
+
+/*
+ * Parse the "monitor_layout" string if any. This code is mostly
+ * copied from XFree's radeon driver
+ */
+static int radeon_parse_monitor_layout(struct radeonfb_info *rinfo,
+ const char *monitor_layout)
+{
+ char s1[5], s2[5];
+ int i = 0, second = 0;
+ const char *s;
+
+ if (!monitor_layout)
+ return 0;
+
+ s = monitor_layout;
+ do {
+ switch(*s) {
+ case ',':
+ s1[i] = '\0';
+ i = 0;
+ second = 1;
+ break;
+ case ' ':
+ case '\0':
+ break;
+ default:
+ if (i > 4)
+ break;
+ if (second)
+ s2[i] = *s;
+ else
+ s1[i] = *s;
+ i++;
+ }
+
+ if (i > 4)
+ i = 4;
+
+ } while (*s++);
+ if (second)
+ s2[i] = 0;
+ else {
+ s1[i] = 0;
+ s2[0] = 0;
+ }
+ if (strcmp(s1, "CRT") == 0)
+ rinfo->mon1_type = MT_CRT;
+ else if (strcmp(s1, "TMDS") == 0)
+ rinfo->mon1_type = MT_DFP;
+ else if (strcmp(s1, "LVDS") == 0)
+ rinfo->mon1_type = MT_LCD;
+
+ if (strcmp(s2, "CRT") == 0)
+ rinfo->mon2_type = MT_CRT;
+ else if (strcmp(s2, "TMDS") == 0)
+ rinfo->mon2_type = MT_DFP;
+ else if (strcmp(s2, "LVDS") == 0)
+ rinfo->mon2_type = MT_LCD;
+
+ return 1;
+}
+
+/*
+ * Probe display on both primary and secondary card's connector (if any)
+ * by various available techniques (i2c, OF device tree, BIOS, ...) and
+ * try to retrieve EDID. The algorithm here comes from XFree's radeon
+ * driver
+ */
+void radeon_probe_screens(struct radeonfb_info *rinfo,
+ const char *monitor_layout, int ignore_edid)
+{
+#ifdef CONFIG_FB_RADEON_I2C
+ int ddc_crt2_used = 0;
+#endif
+ int tmp, i;
+
+ radeon_parse_connector_info(rinfo);
+
+ if (radeon_parse_monitor_layout(rinfo, monitor_layout)) {
+
+ /*
+ * If user specified a monitor_layout option, use it instead
+ * of auto-detecting. Maybe we should only use this argument
+ * on the first radeon card probed or provide a way to specify
+ * a layout for each card ?
+ */
+
+ pr_debug("Using specified monitor layout: %s", monitor_layout);
+#ifdef CONFIG_FB_RADEON_I2C
+ if (!ignore_edid) {
+ if (rinfo->mon1_type != MT_NONE)
+ if (!radeon_probe_i2c_connector(rinfo, ddc_dvi, &rinfo->mon1_EDID)) {
+ radeon_probe_i2c_connector(rinfo, ddc_crt2, &rinfo->mon1_EDID);
+ ddc_crt2_used = 1;
+ }
+ if (rinfo->mon2_type != MT_NONE)
+ if (!radeon_probe_i2c_connector(rinfo, ddc_vga, &rinfo->mon2_EDID) &&
+ !ddc_crt2_used)
+ radeon_probe_i2c_connector(rinfo, ddc_crt2, &rinfo->mon2_EDID);
+ }
+#endif /* CONFIG_FB_RADEON_I2C */
+ if (rinfo->mon1_type == MT_NONE) {
+ if (rinfo->mon2_type != MT_NONE) {
+ rinfo->mon1_type = rinfo->mon2_type;
+ rinfo->mon1_EDID = rinfo->mon2_EDID;
+ } else {
+ rinfo->mon1_type = MT_CRT;
+ printk(KERN_INFO "radeonfb: No valid monitor, assuming CRT on first port\n");
+ }
+ rinfo->mon2_type = MT_NONE;
+ rinfo->mon2_EDID = NULL;
+ }
+ } else {
+ /*
+ * Auto-detecting display type (well... trying to ...)
+ */
+
+ pr_debug("Starting monitor auto detection...\n");
+
+#if defined(DEBUG) && defined(CONFIG_FB_RADEON_I2C)
+ {
+ u8 *EDIDs[4] = { NULL, NULL, NULL, NULL };
+ int mon_types[4] = {MT_NONE, MT_NONE, MT_NONE, MT_NONE};
+ int i;
+
+ for (i = 0; i < 4; i++)
+ mon_types[i] = radeon_probe_i2c_connector(rinfo,
+ i+1, &EDIDs[i]);
+ }
+#endif /* DEBUG */
+ /*
+ * Old single head cards
+ */
+ if (!rinfo->has_CRTC2) {
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
+ &rinfo->mon1_EDID);
+#endif /* CONFIG_PPC || CONFIG_SPARC */
+#ifdef CONFIG_FB_RADEON_I2C
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type =
+ radeon_probe_i2c_connector(rinfo, ddc_dvi,
+ &rinfo->mon1_EDID);
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type =
+ radeon_probe_i2c_connector(rinfo, ddc_vga,
+ &rinfo->mon1_EDID);
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type =
+ radeon_probe_i2c_connector(rinfo, ddc_crt2,
+ &rinfo->mon1_EDID);
+#endif /* CONFIG_FB_RADEON_I2C */
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type = MT_CRT;
+ goto bail;
+ }
+
+ /*
+ * Check for cards with reversed DACs or TMDS controllers using BIOS
+ */
+ if (rinfo->bios_seg &&
+ (tmp = BIOS_IN16(rinfo->fp_bios_start + 0x50))) {
+ for (i = 1; i < 4; i++) {
+ unsigned int tmp0;
+
+ if (!BIOS_IN8(tmp + i*2) && i > 1)
+ break;
+ tmp0 = BIOS_IN16(tmp + i*2);
+ if ((!(tmp0 & 0x01)) && (((tmp0 >> 8) & 0x0f) == ddc_dvi)) {
+ rinfo->reversed_DAC = 1;
+ printk(KERN_INFO "radeonfb: Reversed DACs detected\n");
+ }
+ if ((((tmp0 >> 8) & 0x0f) == ddc_dvi) && ((tmp0 >> 4) & 0x01)) {
+ rinfo->reversed_TMDS = 1;
+ printk(KERN_INFO "radeonfb: Reversed TMDS detected\n");
+ }
+ }
+ }
+
+ /*
+ * Probe primary head (DVI or laptop internal panel)
+ */
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0,
+ &rinfo->mon1_EDID);
+#endif /* CONFIG_PPC || CONFIG_SPARC */
+#ifdef CONFIG_FB_RADEON_I2C
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi,
+ &rinfo->mon1_EDID);
+ if (rinfo->mon1_type == MT_NONE) {
+ rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_crt2,
+ &rinfo->mon1_EDID);
+ if (rinfo->mon1_type != MT_NONE)
+ ddc_crt2_used = 1;
+ }
+#endif /* CONFIG_FB_RADEON_I2C */
+ if (rinfo->mon1_type == MT_NONE && rinfo->is_mobility &&
+ ((rinfo->bios_seg && (INREG(BIOS_4_SCRATCH) & 4))
+ || (INREG(LVDS_GEN_CNTL) & LVDS_ON))) {
+ rinfo->mon1_type = MT_LCD;
+ printk("Non-DDC laptop panel detected\n");
+ }
+ if (rinfo->mon1_type == MT_NONE)
+ rinfo->mon1_type = radeon_crt_is_connected(rinfo, rinfo->reversed_DAC);
+
+ /*
+ * Probe secondary head (mostly VGA, can be DVI)
+ */
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+ if (rinfo->mon2_type == MT_NONE)
+ rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1,
+ &rinfo->mon2_EDID);
+#endif /* CONFIG_PPC || defined(CONFIG_SPARC) */
+#ifdef CONFIG_FB_RADEON_I2C
+ if (rinfo->mon2_type == MT_NONE)
+ rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_vga,
+ &rinfo->mon2_EDID);
+ if (rinfo->mon2_type == MT_NONE && !ddc_crt2_used)
+ rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_crt2,
+ &rinfo->mon2_EDID);
+#endif /* CONFIG_FB_RADEON_I2C */
+ if (rinfo->mon2_type == MT_NONE)
+ rinfo->mon2_type = radeon_crt_is_connected(rinfo, !rinfo->reversed_DAC);
+
+ /*
+ * If we only detected port 2, we swap them, if none detected,
+ * assume CRT (maybe fallback to old BIOS_SCRATCH stuff ? or look
+ * at FP registers ?)
+ */
+ if (rinfo->mon1_type == MT_NONE) {
+ if (rinfo->mon2_type != MT_NONE) {
+ rinfo->mon1_type = rinfo->mon2_type;
+ rinfo->mon1_EDID = rinfo->mon2_EDID;
+ } else
+ rinfo->mon1_type = MT_CRT;
+ rinfo->mon2_type = MT_NONE;
+ rinfo->mon2_EDID = NULL;
+ }
+
+ /*
+ * Deal with reversed TMDS
+ */
+ if (rinfo->reversed_TMDS) {
+ /* Always keep internal TMDS as primary head */
+ if (rinfo->mon1_type == MT_DFP || rinfo->mon2_type == MT_DFP) {
+ int tmp_type = rinfo->mon1_type;
+ u8 *tmp_EDID = rinfo->mon1_EDID;
+ rinfo->mon1_type = rinfo->mon2_type;
+ rinfo->mon1_EDID = rinfo->mon2_EDID;
+ rinfo->mon2_type = tmp_type;
+ rinfo->mon2_EDID = tmp_EDID;
+ if (rinfo->mon1_type == MT_CRT || rinfo->mon2_type == MT_CRT)
+ rinfo->reversed_DAC ^= 1;
+ }
+ }
+ }
+ if (ignore_edid) {
+ kfree(rinfo->mon1_EDID);
+ rinfo->mon1_EDID = NULL;
+ kfree(rinfo->mon2_EDID);
+ rinfo->mon2_EDID = NULL;
+ }
+
+ bail:
+ printk(KERN_INFO "radeonfb: Monitor 1 type %s found\n",
+ radeon_get_mon_name(rinfo->mon1_type));
+ if (rinfo->mon1_EDID)
+ printk(KERN_INFO "radeonfb: EDID probed\n");
+ if (!rinfo->has_CRTC2)
+ return;
+ printk(KERN_INFO "radeonfb: Monitor 2 type %s found\n",
+ radeon_get_mon_name(rinfo->mon2_type));
+ if (rinfo->mon2_EDID)
+ printk(KERN_INFO "radeonfb: EDID probed\n");
+}
+
+
+/*
+ * This function applies any arch/model/machine specific fixups
+ * to the panel info. It may eventually alter EDID block as
+ * well or whatever is specific to a given model and not probed
+ * properly by the default code
+ */
+static void radeon_fixup_panel_info(struct radeonfb_info *rinfo)
+{
+#ifdef CONFIG_PPC
+ /*
+ * LCD Flat panels should use fixed dividers, we enfore that on
+ * PPC only for now...
+ */
+ if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type == MT_LCD
+ && rinfo->is_mobility) {
+ int ppll_div_sel;
+ u32 ppll_divn;
+ ppll_div_sel = INREG8(CLOCK_CNTL_INDEX + 1) & 0x3;
+ radeon_pll_errata_after_index(rinfo);
+ ppll_divn = INPLL(PPLL_DIV_0 + ppll_div_sel);
+ rinfo->panel_info.ref_divider = rinfo->pll.ref_div;
+ rinfo->panel_info.fbk_divider = ppll_divn & 0x7ff;
+ rinfo->panel_info.post_divider = (ppll_divn >> 16) & 0x7;
+ rinfo->panel_info.use_bios_dividers = 1;
+
+ printk(KERN_DEBUG "radeonfb: Using Firmware dividers 0x%08x "
+ "from PPLL %d\n",
+ rinfo->panel_info.fbk_divider |
+ (rinfo->panel_info.post_divider << 16),
+ ppll_div_sel);
+ }
+#endif /* CONFIG_PPC */
+}
+
+
+/*
+ * Fill up panel infos from a mode definition, either returned by the EDID
+ * or from the default mode when we can't do any better
+ */
+static void radeon_var_to_panel_info(struct radeonfb_info *rinfo, struct fb_var_screeninfo *var)
+{
+ rinfo->panel_info.xres = var->xres;
+ rinfo->panel_info.yres = var->yres;
+ rinfo->panel_info.clock = 100000000 / var->pixclock;
+ rinfo->panel_info.hOver_plus = var->right_margin;
+ rinfo->panel_info.hSync_width = var->hsync_len;
+ rinfo->panel_info.hblank = var->left_margin +
+ (var->right_margin + var->hsync_len);
+ rinfo->panel_info.vOver_plus = var->lower_margin;
+ rinfo->panel_info.vSync_width = var->vsync_len;
+ rinfo->panel_info.vblank = var->upper_margin +
+ (var->lower_margin + var->vsync_len);
+ rinfo->panel_info.hAct_high =
+ (var->sync & FB_SYNC_HOR_HIGH_ACT) != 0;
+ rinfo->panel_info.vAct_high =
+ (var->sync & FB_SYNC_VERT_HIGH_ACT) != 0;
+ rinfo->panel_info.valid = 1;
+ /* We use a default of 200ms for the panel power delay,
+ * I need to have a real schedule() instead of mdelay's in the panel code.
+ * we might be possible to figure out a better power delay either from
+ * MacOS OF tree or from the EDID block (proprietary extensions ?)
+ */
+ rinfo->panel_info.pwr_delay = 200;
+}
+
+static void radeon_videomode_to_var(struct fb_var_screeninfo *var,
+ const struct fb_videomode *mode)
+{
+ var->xres = mode->xres;
+ var->yres = mode->yres;
+ var->xres_virtual = mode->xres;
+ var->yres_virtual = mode->yres;
+ var->xoffset = 0;
+ var->yoffset = 0;
+ var->pixclock = mode->pixclock;
+ var->left_margin = mode->left_margin;
+ var->right_margin = mode->right_margin;
+ var->upper_margin = mode->upper_margin;
+ var->lower_margin = mode->lower_margin;
+ var->hsync_len = mode->hsync_len;
+ var->vsync_len = mode->vsync_len;
+ var->sync = mode->sync;
+ var->vmode = mode->vmode;
+}
+
+#ifdef CONFIG_PPC_PSERIES
+static int is_powerblade(const char *model)
+{
+ struct device_node *root;
+ const char* cp;
+ int len, l, rc = 0;
+
+ root = of_find_node_by_path("/");
+ if (root && model) {
+ l = strlen(model);
+ cp = of_get_property(root, "model", &len);
+ if (cp)
+ rc = memcmp(model, cp, min(len, l)) == 0;
+ of_node_put(root);
+ }
+ return rc;
+}
+#endif
+
+/*
+ * Build the modedb for head 1 (head 2 will come later), check panel infos
+ * from either BIOS or EDID, and pick up the default mode
+ */
+void radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_option)
+{
+ struct fb_info * info = rinfo->info;
+ int has_default_mode = 0;
+
+ /*
+ * Fill default var first
+ */
+ info->var = radeonfb_default_var;
+ INIT_LIST_HEAD(&info->modelist);
+
+ /*
+ * First check out what BIOS has to say
+ */
+ if (rinfo->mon1_type == MT_LCD)
+ radeon_get_panel_info_BIOS(rinfo);
+
+ /*
+ * Parse EDID detailed timings and deduce panel infos if any. Right now
+ * we only deal with first entry returned by parse_EDID, we may do better
+ * some day...
+ */
+ if (!rinfo->panel_info.use_bios_dividers && rinfo->mon1_type != MT_CRT
+ && rinfo->mon1_EDID) {
+ struct fb_var_screeninfo var;
+ pr_debug("Parsing EDID data for panel info\n");
+ if (fb_parse_edid(rinfo->mon1_EDID, &var) == 0) {
+ if (var.xres >= rinfo->panel_info.xres &&
+ var.yres >= rinfo->panel_info.yres)
+ radeon_var_to_panel_info(rinfo, &var);
+ }
+ }
+
+ /*
+ * Do any additional platform/arch fixups to the panel infos
+ */
+ radeon_fixup_panel_info(rinfo);
+
+ /*
+ * If we have some valid panel infos, we setup the default mode based on
+ * those
+ */
+ if (rinfo->mon1_type != MT_CRT && rinfo->panel_info.valid) {
+ struct fb_var_screeninfo *var = &info->var;
+
+ pr_debug("Setting up default mode based on panel info\n");
+ var->xres = rinfo->panel_info.xres;
+ var->yres = rinfo->panel_info.yres;
+ var->xres_virtual = rinfo->panel_info.xres;
+ var->yres_virtual = rinfo->panel_info.yres;
+ var->xoffset = var->yoffset = 0;
+ var->bits_per_pixel = 8;
+ var->pixclock = 100000000 / rinfo->panel_info.clock;
+ var->left_margin = (rinfo->panel_info.hblank - rinfo->panel_info.hOver_plus
+ - rinfo->panel_info.hSync_width);
+ var->right_margin = rinfo->panel_info.hOver_plus;
+ var->upper_margin = (rinfo->panel_info.vblank - rinfo->panel_info.vOver_plus
+ - rinfo->panel_info.vSync_width);
+ var->lower_margin = rinfo->panel_info.vOver_plus;
+ var->hsync_len = rinfo->panel_info.hSync_width;
+ var->vsync_len = rinfo->panel_info.vSync_width;
+ var->sync = 0;
+ if (rinfo->panel_info.hAct_high)
+ var->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (rinfo->panel_info.vAct_high)
+ var->sync |= FB_SYNC_VERT_HIGH_ACT;
+ var->vmode = 0;
+ has_default_mode = 1;
+ }
+
+ /*
+ * Now build modedb from EDID
+ */
+ if (rinfo->mon1_EDID) {
+ fb_edid_to_monspecs(rinfo->mon1_EDID, &info->monspecs);
+ fb_videomode_to_modelist(info->monspecs.modedb,
+ info->monspecs.modedb_len,
+ &info->modelist);
+ rinfo->mon1_modedb = info->monspecs.modedb;
+ rinfo->mon1_dbsize = info->monspecs.modedb_len;
+ }
+
+
+ /*
+ * Finally, if we don't have panel infos we need to figure some (or
+ * we try to read it from card), we try to pick a default mode
+ * and create some panel infos. Whatever...
+ */
+ if (rinfo->mon1_type != MT_CRT && !rinfo->panel_info.valid) {
+ struct fb_videomode *modedb;
+ int dbsize;
+ char modename[32];
+
+ pr_debug("Guessing panel info...\n");
+ if (rinfo->panel_info.xres == 0 || rinfo->panel_info.yres == 0) {
+ u32 tmp = INREG(FP_HORZ_STRETCH) & HORZ_PANEL_SIZE;
+ rinfo->panel_info.xres = ((tmp >> HORZ_PANEL_SHIFT) + 1) * 8;
+ tmp = INREG(FP_VERT_STRETCH) & VERT_PANEL_SIZE;
+ rinfo->panel_info.yres = (tmp >> VERT_PANEL_SHIFT) + 1;
+ }
+ if (rinfo->panel_info.xres == 0 || rinfo->panel_info.yres == 0) {
+ printk(KERN_WARNING "radeonfb: Can't find panel size, going back to CRT\n");
+ rinfo->mon1_type = MT_CRT;
+ goto pickup_default;
+ }
+ printk(KERN_WARNING "radeonfb: Assuming panel size %dx%d\n",
+ rinfo->panel_info.xres, rinfo->panel_info.yres);
+ modedb = rinfo->mon1_modedb;
+ dbsize = rinfo->mon1_dbsize;
+ snprintf(modename, 31, "%dx%d", rinfo->panel_info.xres, rinfo->panel_info.yres);
+ if (fb_find_mode(&info->var, info, modename,
+ modedb, dbsize, NULL, 8) == 0) {
+ printk(KERN_WARNING "radeonfb: Can't find mode for panel size, going back to CRT\n");
+ rinfo->mon1_type = MT_CRT;
+ goto pickup_default;
+ }
+ has_default_mode = 1;
+ radeon_var_to_panel_info(rinfo, &info->var);
+ }
+
+ pickup_default:
+ /*
+ * Apply passed-in mode option if any
+ */
+ if (mode_option) {
+ if (fb_find_mode(&info->var, info, mode_option,
+ info->monspecs.modedb,
+ info->monspecs.modedb_len, NULL, 8) != 0)
+ has_default_mode = 1;
+ }
+
+#ifdef CONFIG_PPC_PSERIES
+ if (!has_default_mode && (
+ is_powerblade("IBM,8842") || /* JS20 */
+ is_powerblade("IBM,8844") || /* JS21 */
+ is_powerblade("IBM,7998") || /* JS12/JS21/JS22 */
+ is_powerblade("IBM,0792") || /* QS21 */
+ is_powerblade("IBM,0793") /* QS22 */
+ )) {
+ printk("Falling back to 800x600 on JSxx hardware\n");
+ if (fb_find_mode(&info->var, info, "800x600@60",
+ info->monspecs.modedb,
+ info->monspecs.modedb_len, NULL, 8) != 0)
+ has_default_mode = 1;
+ }
+#endif
+
+ /*
+ * Still no mode, let's pick up a default from the db
+ */
+ if (!has_default_mode && info->monspecs.modedb != NULL) {
+ struct fb_monspecs *specs = &info->monspecs;
+ struct fb_videomode *modedb = NULL;
+
+ /* get preferred timing */
+ if (specs->misc & FB_MISC_1ST_DETAIL) {
+ int i;
+
+ for (i = 0; i < specs->modedb_len; i++) {
+ if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
+ modedb = &specs->modedb[i];
+ break;
+ }
+ }
+ } else {
+ /* otherwise, get first mode in database */
+ modedb = &specs->modedb[0];
+ }
+ if (modedb != NULL) {
+ info->var.bits_per_pixel = 8;
+ radeon_videomode_to_var(&info->var, modedb);
+ has_default_mode = 1;
+ }
+ }
+ if (1) {
+ struct fb_videomode mode;
+ /* Make sure that whatever mode got selected is actually in the
+ * modelist or the kernel may die
+ */
+ fb_var_to_videomode(&mode, &info->var);
+ fb_add_videomode(&mode, &info->modelist);
+ }
+}
+
+/*
+ * The code below is used to pick up a mode in check_var and
+ * set_var. It should be made generic
+ */
+
+/*
+ * This is used when looking for modes. We assign a "distance" value
+ * to a mode in the modedb depending how "close" it is from what we
+ * are looking for.
+ * Currently, we don't compare that much, we could do better but
+ * the current fbcon doesn't quite mind ;)
+ */
+static int radeon_compare_modes(const struct fb_var_screeninfo *var,
+ const struct fb_videomode *mode)
+{
+ int distance = 0;
+
+ distance = mode->yres - var->yres;
+ distance += (mode->xres - var->xres)/2;
+ return distance;
+}
+
+/*
+ * This function is called by check_var, it gets the passed in mode parameter, and
+ * outputs a valid mode matching the passed-in one as closely as possible.
+ * We need something better ultimately. Things like fbcon basically pass us out
+ * current mode with xres/yres hacked, while things like XFree will actually
+ * produce a full timing that we should respect as much as possible.
+ *
+ * This is why I added the FB_ACTIVATE_FIND that is used by fbcon. Without this,
+ * we do a simple spec match, that's all. With it, we actually look for a mode in
+ * either our monitor modedb or the vesa one if none
+ *
+ */
+int radeon_match_mode(struct radeonfb_info *rinfo,
+ struct fb_var_screeninfo *dest,
+ const struct fb_var_screeninfo *src)
+{
+ const struct fb_videomode *db = vesa_modes;
+ int i, dbsize = 34;
+ int has_rmx, native_db = 0;
+ int distance = INT_MAX;
+ const struct fb_videomode *candidate = NULL;
+
+ /* Start with a copy of the requested mode */
+ memcpy(dest, src, sizeof(struct fb_var_screeninfo));
+
+ /* Check if we have a modedb built from EDID */
+ if (rinfo->mon1_modedb) {
+ db = rinfo->mon1_modedb;
+ dbsize = rinfo->mon1_dbsize;
+ native_db = 1;
+ }
+
+ /* Check if we have a scaler allowing any fancy mode */
+ has_rmx = rinfo->mon1_type == MT_LCD || rinfo->mon1_type == MT_DFP;
+
+ /* If we have a scaler and are passed FB_ACTIVATE_TEST or
+ * FB_ACTIVATE_NOW, just do basic checking and return if the
+ * mode match
+ */
+ if ((src->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST ||
+ (src->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ /* We don't have an RMX, validate timings. If we don't have
+ * monspecs, we should be paranoid and not let use go above
+ * 640x480-60, but I assume userland knows what it's doing here
+ * (though I may be proven wrong...)
+ */
+ if (has_rmx == 0 && rinfo->mon1_modedb)
+ if (fb_validate_mode((struct fb_var_screeninfo *)src, rinfo->info))
+ return -EINVAL;
+ return 0;
+ }
+
+ /* Now look for a mode in the database */
+ while (db) {
+ for (i = 0; i < dbsize; i++) {
+ int d;
+
+ if (db[i].yres < src->yres)
+ continue;
+ if (db[i].xres < src->xres)
+ continue;
+ d = radeon_compare_modes(src, &db[i]);
+ /* If the new mode is at least as good as the previous one,
+ * then it's our new candidate
+ */
+ if (d < distance) {
+ candidate = &db[i];
+ distance = d;
+ }
+ }
+ db = NULL;
+ /* If we have a scaler, we allow any mode from the database */
+ if (native_db && has_rmx) {
+ db = vesa_modes;
+ dbsize = 34;
+ native_db = 0;
+ }
+ }
+
+ /* If we have found a match, return it */
+ if (candidate != NULL) {
+ radeon_videomode_to_var(dest, candidate);
+ return 0;
+ }
+
+ /* If we haven't and don't have a scaler, fail */
+ if (!has_rmx)
+ return -EINVAL;
+
+ return 0;
+}
diff --git a/drivers/video/fbdev/aty/radeon_pm.c b/drivers/video/fbdev/aty/radeon_pm.c
new file mode 100644
index 000000000..e695adb0e
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_pm.c
@@ -0,0 +1,2910 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/video/aty/radeon_pm.c
+ *
+ * Copyright 2003,2004 Ben. Herrenschmidt <benh@kernel.crashing.org>
+ * Copyright 2004 Paul Mackerras <paulus@samba.org>
+ *
+ * This is the power management code for ATI radeon chipsets. It contains
+ * some dynamic clock PM enable/disable code similar to what X.org does,
+ * some D2-state (APM-style) sleep/wakeup code for use on some PowerMacs,
+ * and the necessary bits to re-initialize from scratch a few chips found
+ * on PowerMacs as well. The later could be extended to more platforms
+ * provided the memory controller configuration code be made more generic,
+ * and you can get the proper mode register commands for your RAMs.
+ * Those things may be found in the BIOS image...
+ */
+
+#include "radeonfb.h"
+
+#include <linux/console.h>
+#include <linux/agp_backend.h>
+
+#ifdef CONFIG_PPC_PMAC
+#include <asm/machdep.h>
+#include <asm/prom.h>
+#include <asm/pmac_feature.h>
+#endif
+
+#include "ati_ids.h"
+
+/*
+ * Workarounds for bugs in PC laptops:
+ * - enable D2 sleep in some IBM Thinkpads
+ * - special case for Samsung P35
+ *
+ * Whitelist by subsystem vendor/device because
+ * its the subsystem vendor's fault!
+ */
+
+#if defined(CONFIG_PM) && defined(CONFIG_X86)
+static void radeon_reinitialize_M10(struct radeonfb_info *rinfo);
+
+struct radeon_device_id {
+ const char *ident; /* (arbitrary) Name */
+ const unsigned short subsystem_vendor; /* Subsystem Vendor ID */
+ const unsigned short subsystem_device; /* Subsystem Device ID */
+ const enum radeon_pm_mode pm_mode_modifier; /* modify pm_mode */
+ const reinit_function_ptr new_reinit_func; /* changed reinit_func */
+};
+
+#define BUGFIX(model, sv, sd, pm, fn) { \
+ .ident = model, \
+ .subsystem_vendor = sv, \
+ .subsystem_device = sd, \
+ .pm_mode_modifier = pm, \
+ .new_reinit_func = fn \
+}
+
+static struct radeon_device_id radeon_workaround_list[] = {
+ BUGFIX("IBM Thinkpad R32",
+ PCI_VENDOR_ID_IBM, 0x1905,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad R40",
+ PCI_VENDOR_ID_IBM, 0x0526,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad R40",
+ PCI_VENDOR_ID_IBM, 0x0527,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad R50/R51/T40/T41",
+ PCI_VENDOR_ID_IBM, 0x0531,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad R51/T40/T41/T42",
+ PCI_VENDOR_ID_IBM, 0x0530,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad T30",
+ PCI_VENDOR_ID_IBM, 0x0517,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad T40p",
+ PCI_VENDOR_ID_IBM, 0x054d,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad T42",
+ PCI_VENDOR_ID_IBM, 0x0550,
+ radeon_pm_d2, NULL),
+ BUGFIX("IBM Thinkpad X31/X32",
+ PCI_VENDOR_ID_IBM, 0x052f,
+ radeon_pm_d2, NULL),
+ BUGFIX("Samsung P35",
+ PCI_VENDOR_ID_SAMSUNG, 0xc00c,
+ radeon_pm_off, radeon_reinitialize_M10),
+ BUGFIX("Acer Aspire 2010",
+ PCI_VENDOR_ID_AI, 0x0061,
+ radeon_pm_off, radeon_reinitialize_M10),
+ BUGFIX("Acer Travelmate 290D/292LMi",
+ PCI_VENDOR_ID_AI, 0x005a,
+ radeon_pm_off, radeon_reinitialize_M10),
+ { .ident = NULL }
+};
+
+static int radeon_apply_workarounds(struct radeonfb_info *rinfo)
+{
+ struct radeon_device_id *id;
+
+ for (id = radeon_workaround_list; id->ident != NULL; id++ )
+ if ((id->subsystem_vendor == rinfo->pdev->subsystem_vendor ) &&
+ (id->subsystem_device == rinfo->pdev->subsystem_device )) {
+
+ /* we found a device that requires workaround */
+ printk(KERN_DEBUG "radeonfb: %s detected"
+ ", enabling workaround\n", id->ident);
+
+ rinfo->pm_mode |= id->pm_mode_modifier;
+
+ if (id->new_reinit_func != NULL)
+ rinfo->reinit_func = id->new_reinit_func;
+
+ return 1;
+ }
+ return 0; /* not found */
+}
+
+#else /* defined(CONFIG_PM) && defined(CONFIG_X86) */
+static inline int radeon_apply_workarounds(struct radeonfb_info *rinfo)
+{
+ return 0;
+}
+#endif /* defined(CONFIG_PM) && defined(CONFIG_X86) */
+
+
+
+static void radeon_pm_disable_dynamic_mode(struct radeonfb_info *rinfo)
+{
+ u32 tmp;
+
+ /* RV100 */
+ if ((rinfo->family == CHIP_FAMILY_RV100) && (!rinfo->is_mobility)) {
+ if (rinfo->has_CRTC2) {
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp &= ~SCLK_CNTL__DYN_STOP_LAT_MASK;
+ tmp |= SCLK_CNTL__CP_MAX_DYN_STOP_LAT | SCLK_CNTL__FORCEON_MASK;
+ OUTPLL(pllSCLK_CNTL, tmp);
+ }
+ tmp = INPLL(pllMCLK_CNTL);
+ tmp |= (MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_MCLKB |
+ MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB |
+ MCLK_CNTL__FORCE_AIC |
+ MCLK_CNTL__FORCE_MC);
+ OUTPLL(pllMCLK_CNTL, tmp);
+ return;
+ }
+ /* R100 */
+ if (!rinfo->has_CRTC2) {
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_HDP |
+ SCLK_CNTL__FORCE_DISP1 | SCLK_CNTL__FORCE_TOP |
+ SCLK_CNTL__FORCE_E2 | SCLK_CNTL__FORCE_SE |
+ SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_VIP |
+ SCLK_CNTL__FORCE_RE | SCLK_CNTL__FORCE_PB |
+ SCLK_CNTL__FORCE_TAM | SCLK_CNTL__FORCE_TDM |
+ SCLK_CNTL__FORCE_RB);
+ OUTPLL(pllSCLK_CNTL, tmp);
+ return;
+ }
+ /* RV350 (M10/M11) */
+ if (rinfo->family == CHIP_FAMILY_RV350) {
+ /* for RV350/M10/M11, no delays are required. */
+ tmp = INPLL(pllSCLK_CNTL2);
+ tmp |= (SCLK_CNTL2__R300_FORCE_TCL |
+ SCLK_CNTL2__R300_FORCE_GA |
+ SCLK_CNTL2__R300_FORCE_CBA);
+ OUTPLL(pllSCLK_CNTL2, tmp);
+
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp |= (SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_CP |
+ SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 |
+ SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_E2 |
+ SCLK_CNTL__R300_FORCE_VAP | SCLK_CNTL__FORCE_IDCT |
+ SCLK_CNTL__FORCE_VIP | SCLK_CNTL__R300_FORCE_SR |
+ SCLK_CNTL__R300_FORCE_PX | SCLK_CNTL__R300_FORCE_TX |
+ SCLK_CNTL__R300_FORCE_US | SCLK_CNTL__FORCE_TV_SCLK |
+ SCLK_CNTL__R300_FORCE_SU | SCLK_CNTL__FORCE_OV0);
+ OUTPLL(pllSCLK_CNTL, tmp);
+
+ tmp = INPLL(pllSCLK_MORE_CNTL);
+ tmp |= (SCLK_MORE_CNTL__FORCE_DISPREGS | SCLK_MORE_CNTL__FORCE_MC_GUI |
+ SCLK_MORE_CNTL__FORCE_MC_HOST);
+ OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+ tmp = INPLL(pllMCLK_CNTL);
+ tmp |= (MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_MCLKB |
+ MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB |
+ MCLK_CNTL__FORCE_MC);
+ OUTPLL(pllMCLK_CNTL, tmp);
+
+ tmp = INPLL(pllVCLK_ECP_CNTL);
+ tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+ VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb |
+ VCLK_ECP_CNTL__R300_DISP_DAC_PIXCLK_DAC_BLANK_OFF);
+ OUTPLL(pllVCLK_ECP_CNTL, tmp);
+
+ tmp = INPLL(pllPIXCLKS_CNTL);
+ tmp &= ~(PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb |
+ PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_DISP_DAC_PIXCLK_DAC2_BLANK_OFF);
+ OUTPLL(pllPIXCLKS_CNTL, tmp);
+
+ return;
+ }
+
+ /* Default */
+
+ /* Force Core Clocks */
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp |= (SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_E2);
+
+ /* XFree doesn't do that case, but we had this code from Apple and it
+ * seem necessary for proper suspend/resume operations
+ */
+ if (rinfo->is_mobility) {
+ tmp |= SCLK_CNTL__FORCE_HDP|
+ SCLK_CNTL__FORCE_DISP1|
+ SCLK_CNTL__FORCE_DISP2|
+ SCLK_CNTL__FORCE_TOP|
+ SCLK_CNTL__FORCE_SE|
+ SCLK_CNTL__FORCE_IDCT|
+ SCLK_CNTL__FORCE_VIP|
+ SCLK_CNTL__FORCE_PB|
+ SCLK_CNTL__FORCE_RE|
+ SCLK_CNTL__FORCE_TAM|
+ SCLK_CNTL__FORCE_TDM|
+ SCLK_CNTL__FORCE_RB|
+ SCLK_CNTL__FORCE_TV_SCLK|
+ SCLK_CNTL__FORCE_SUBPIC|
+ SCLK_CNTL__FORCE_OV0;
+ }
+ else if (rinfo->family == CHIP_FAMILY_R300 ||
+ rinfo->family == CHIP_FAMILY_R350) {
+ tmp |= SCLK_CNTL__FORCE_HDP |
+ SCLK_CNTL__FORCE_DISP1 |
+ SCLK_CNTL__FORCE_DISP2 |
+ SCLK_CNTL__FORCE_TOP |
+ SCLK_CNTL__FORCE_IDCT |
+ SCLK_CNTL__FORCE_VIP;
+ }
+ OUTPLL(pllSCLK_CNTL, tmp);
+ radeon_msleep(16);
+
+ if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) {
+ tmp = INPLL(pllSCLK_CNTL2);
+ tmp |= SCLK_CNTL2__R300_FORCE_TCL |
+ SCLK_CNTL2__R300_FORCE_GA |
+ SCLK_CNTL2__R300_FORCE_CBA;
+ OUTPLL(pllSCLK_CNTL2, tmp);
+ radeon_msleep(16);
+ }
+
+ tmp = INPLL(pllCLK_PIN_CNTL);
+ tmp &= ~CLK_PIN_CNTL__SCLK_DYN_START_CNTL;
+ OUTPLL(pllCLK_PIN_CNTL, tmp);
+ radeon_msleep(15);
+
+ if (rinfo->is_IGP) {
+ /* Weird ... X is _un_ forcing clocks here, I think it's
+ * doing backward. Imitate it for now...
+ */
+ tmp = INPLL(pllMCLK_CNTL);
+ tmp &= ~(MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_YCLKA);
+ OUTPLL(pllMCLK_CNTL, tmp);
+ radeon_msleep(16);
+ }
+ /* Hrm... same shit, X doesn't do that but I have to */
+ else if (rinfo->is_mobility) {
+ tmp = INPLL(pllMCLK_CNTL);
+ tmp |= (MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_MCLKB |
+ MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB);
+ OUTPLL(pllMCLK_CNTL, tmp);
+ radeon_msleep(16);
+
+ tmp = INPLL(pllMCLK_MISC);
+ tmp &= ~(MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT|
+ MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT|
+ MCLK_MISC__MC_MCLK_DYN_ENABLE|
+ MCLK_MISC__IO_MCLK_DYN_ENABLE);
+ OUTPLL(pllMCLK_MISC, tmp);
+ radeon_msleep(15);
+ }
+
+ if (rinfo->is_mobility) {
+ tmp = INPLL(pllSCLK_MORE_CNTL);
+ tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS|
+ SCLK_MORE_CNTL__FORCE_MC_GUI|
+ SCLK_MORE_CNTL__FORCE_MC_HOST;
+ OUTPLL(pllSCLK_MORE_CNTL, tmp);
+ radeon_msleep(16);
+ }
+
+ tmp = INPLL(pllPIXCLKS_CNTL);
+ tmp &= ~(PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb);
+ OUTPLL(pllPIXCLKS_CNTL, tmp);
+ radeon_msleep(16);
+
+ tmp = INPLL( pllVCLK_ECP_CNTL);
+ tmp &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+ VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+ OUTPLL( pllVCLK_ECP_CNTL, tmp);
+ radeon_msleep(16);
+}
+
+static void radeon_pm_enable_dynamic_mode(struct radeonfb_info *rinfo)
+{
+ u32 tmp;
+
+ /* R100 */
+ if (!rinfo->has_CRTC2) {
+ tmp = INPLL(pllSCLK_CNTL);
+
+ if ((INREG(CNFG_CNTL) & CFG_ATI_REV_ID_MASK) > CFG_ATI_REV_A13)
+ tmp &= ~(SCLK_CNTL__FORCE_CP | SCLK_CNTL__FORCE_RB);
+ tmp &= ~(SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 |
+ SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_SE |
+ SCLK_CNTL__FORCE_IDCT | SCLK_CNTL__FORCE_RE |
+ SCLK_CNTL__FORCE_PB | SCLK_CNTL__FORCE_TAM |
+ SCLK_CNTL__FORCE_TDM);
+ OUTPLL(pllSCLK_CNTL, tmp);
+ return;
+ }
+
+ /* M10/M11 */
+ if (rinfo->family == CHIP_FAMILY_RV350) {
+ tmp = INPLL(pllSCLK_CNTL2);
+ tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL |
+ SCLK_CNTL2__R300_FORCE_GA |
+ SCLK_CNTL2__R300_FORCE_CBA);
+ tmp |= (SCLK_CNTL2__R300_TCL_MAX_DYN_STOP_LAT |
+ SCLK_CNTL2__R300_GA_MAX_DYN_STOP_LAT |
+ SCLK_CNTL2__R300_CBA_MAX_DYN_STOP_LAT);
+ OUTPLL(pllSCLK_CNTL2, tmp);
+
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp &= ~(SCLK_CNTL__FORCE_DISP2 | SCLK_CNTL__FORCE_CP |
+ SCLK_CNTL__FORCE_HDP | SCLK_CNTL__FORCE_DISP1 |
+ SCLK_CNTL__FORCE_TOP | SCLK_CNTL__FORCE_E2 |
+ SCLK_CNTL__R300_FORCE_VAP | SCLK_CNTL__FORCE_IDCT |
+ SCLK_CNTL__FORCE_VIP | SCLK_CNTL__R300_FORCE_SR |
+ SCLK_CNTL__R300_FORCE_PX | SCLK_CNTL__R300_FORCE_TX |
+ SCLK_CNTL__R300_FORCE_US | SCLK_CNTL__FORCE_TV_SCLK |
+ SCLK_CNTL__R300_FORCE_SU | SCLK_CNTL__FORCE_OV0);
+ tmp |= SCLK_CNTL__DYN_STOP_LAT_MASK;
+ OUTPLL(pllSCLK_CNTL, tmp);
+
+ tmp = INPLL(pllSCLK_MORE_CNTL);
+ tmp &= ~SCLK_MORE_CNTL__FORCEON;
+ tmp |= SCLK_MORE_CNTL__DISPREGS_MAX_DYN_STOP_LAT |
+ SCLK_MORE_CNTL__MC_GUI_MAX_DYN_STOP_LAT |
+ SCLK_MORE_CNTL__MC_HOST_MAX_DYN_STOP_LAT;
+ OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+ tmp = INPLL(pllVCLK_ECP_CNTL);
+ tmp |= (VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+ VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+ OUTPLL(pllVCLK_ECP_CNTL, tmp);
+
+ tmp = INPLL(pllPIXCLKS_CNTL);
+ tmp |= (PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb |
+ PIXCLKS_CNTL__DISP_TVOUT_PIXCLK_TV_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_DVOCLK_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_PIXCLK_DVO_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_PIXCLK_TRANS_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_PIXCLK_TVO_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_P2G2CLK_ALWAYS_ONb |
+ PIXCLKS_CNTL__R300_P2G2CLK_DAC_ALWAYS_ONb);
+ OUTPLL(pllPIXCLKS_CNTL, tmp);
+
+ tmp = INPLL(pllMCLK_MISC);
+ tmp |= (MCLK_MISC__MC_MCLK_DYN_ENABLE |
+ MCLK_MISC__IO_MCLK_DYN_ENABLE);
+ OUTPLL(pllMCLK_MISC, tmp);
+
+ tmp = INPLL(pllMCLK_CNTL);
+ tmp |= (MCLK_CNTL__FORCE_MCLKA | MCLK_CNTL__FORCE_MCLKB);
+ tmp &= ~(MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB |
+ MCLK_CNTL__FORCE_MC);
+
+ /* Some releases of vbios have set DISABLE_MC_MCLKA
+ * and DISABLE_MC_MCLKB bits in the vbios table. Setting these
+ * bits will cause H/W hang when reading video memory with dynamic
+ * clocking enabled.
+ */
+ if ((tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKA) &&
+ (tmp & MCLK_CNTL__R300_DISABLE_MC_MCLKB)) {
+ /* If both bits are set, then check the active channels */
+ tmp = INPLL(pllMCLK_CNTL);
+ if (rinfo->vram_width == 64) {
+ if (INREG(MEM_CNTL) & R300_MEM_USE_CD_CH_ONLY)
+ tmp &= ~MCLK_CNTL__R300_DISABLE_MC_MCLKB;
+ else
+ tmp &= ~MCLK_CNTL__R300_DISABLE_MC_MCLKA;
+ } else {
+ tmp &= ~(MCLK_CNTL__R300_DISABLE_MC_MCLKA |
+ MCLK_CNTL__R300_DISABLE_MC_MCLKB);
+ }
+ }
+ OUTPLL(pllMCLK_CNTL, tmp);
+ return;
+ }
+
+ /* R300 */
+ if (rinfo->family == CHIP_FAMILY_R300 || rinfo->family == CHIP_FAMILY_R350) {
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp &= ~(SCLK_CNTL__R300_FORCE_VAP);
+ tmp |= SCLK_CNTL__FORCE_CP;
+ OUTPLL(pllSCLK_CNTL, tmp);
+ radeon_msleep(15);
+
+ tmp = INPLL(pllSCLK_CNTL2);
+ tmp &= ~(SCLK_CNTL2__R300_FORCE_TCL |
+ SCLK_CNTL2__R300_FORCE_GA |
+ SCLK_CNTL2__R300_FORCE_CBA);
+ OUTPLL(pllSCLK_CNTL2, tmp);
+ }
+
+ /* Others */
+
+ tmp = INPLL( pllCLK_PWRMGT_CNTL);
+ tmp &= ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK|
+ CLK_PWRMGT_CNTL__DISP_DYN_STOP_LAT_MASK|
+ CLK_PWRMGT_CNTL__DYN_STOP_MODE_MASK);
+ tmp |= CLK_PWRMGT_CNTL__ENGINE_DYNCLK_MODE_MASK |
+ (0x01 << CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT__SHIFT);
+ OUTPLL( pllCLK_PWRMGT_CNTL, tmp);
+ radeon_msleep(15);
+
+ tmp = INPLL(pllCLK_PIN_CNTL);
+ tmp |= CLK_PIN_CNTL__SCLK_DYN_START_CNTL;
+ OUTPLL(pllCLK_PIN_CNTL, tmp);
+ radeon_msleep(15);
+
+ /* When DRI is enabled, setting DYN_STOP_LAT to zero can cause some R200
+ * to lockup randomly, leave them as set by BIOS.
+ */
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp &= ~SCLK_CNTL__FORCEON_MASK;
+
+ /*RAGE_6::A11 A12 A12N1 A13, RV250::A11 A12, R300*/
+ if ((rinfo->family == CHIP_FAMILY_RV250 &&
+ ((INREG(CNFG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13)) ||
+ ((rinfo->family == CHIP_FAMILY_RV100) &&
+ ((INREG(CNFG_CNTL) & CFG_ATI_REV_ID_MASK) <= CFG_ATI_REV_A13))) {
+ tmp |= SCLK_CNTL__FORCE_CP;
+ tmp |= SCLK_CNTL__FORCE_VIP;
+ }
+ OUTPLL(pllSCLK_CNTL, tmp);
+ radeon_msleep(15);
+
+ if ((rinfo->family == CHIP_FAMILY_RV200) ||
+ (rinfo->family == CHIP_FAMILY_RV250) ||
+ (rinfo->family == CHIP_FAMILY_RV280)) {
+ tmp = INPLL(pllSCLK_MORE_CNTL);
+ tmp &= ~SCLK_MORE_CNTL__FORCEON;
+
+ /* RV200::A11 A12 RV250::A11 A12 */
+ if (((rinfo->family == CHIP_FAMILY_RV200) ||
+ (rinfo->family == CHIP_FAMILY_RV250)) &&
+ ((INREG(CNFG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13))
+ tmp |= SCLK_MORE_CNTL__FORCEON;
+
+ OUTPLL(pllSCLK_MORE_CNTL, tmp);
+ radeon_msleep(15);
+ }
+
+
+ /* RV200::A11 A12, RV250::A11 A12 */
+ if (((rinfo->family == CHIP_FAMILY_RV200) ||
+ (rinfo->family == CHIP_FAMILY_RV250)) &&
+ ((INREG(CNFG_CNTL) & CFG_ATI_REV_ID_MASK) < CFG_ATI_REV_A13)) {
+ tmp = INPLL(pllPLL_PWRMGT_CNTL);
+ tmp |= PLL_PWRMGT_CNTL__TCL_BYPASS_DISABLE;
+ OUTPLL(pllPLL_PWRMGT_CNTL, tmp);
+ radeon_msleep(15);
+ }
+
+ tmp = INPLL(pllPIXCLKS_CNTL);
+ tmp |= PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb;
+ OUTPLL(pllPIXCLKS_CNTL, tmp);
+ radeon_msleep(15);
+
+ tmp = INPLL(pllVCLK_ECP_CNTL);
+ tmp |= VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
+ VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb;
+ OUTPLL(pllVCLK_ECP_CNTL, tmp);
+
+ /* X doesn't do that ... hrm, we do on mobility && Macs */
+#ifdef CONFIG_PPC
+ if (rinfo->is_mobility) {
+ tmp = INPLL(pllMCLK_CNTL);
+ tmp &= ~(MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_MCLKB |
+ MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB);
+ OUTPLL(pllMCLK_CNTL, tmp);
+ radeon_msleep(15);
+
+ tmp = INPLL(pllMCLK_MISC);
+ tmp |= MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT|
+ MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT|
+ MCLK_MISC__MC_MCLK_DYN_ENABLE|
+ MCLK_MISC__IO_MCLK_DYN_ENABLE;
+ OUTPLL(pllMCLK_MISC, tmp);
+ radeon_msleep(15);
+ }
+#endif /* CONFIG_PPC */
+}
+
+#ifdef CONFIG_PM
+
+static void OUTMC( struct radeonfb_info *rinfo, u8 indx, u32 value)
+{
+ OUTREG( MC_IND_INDEX, indx | MC_IND_INDEX__MC_IND_WR_EN);
+ OUTREG( MC_IND_DATA, value);
+}
+
+static u32 INMC(struct radeonfb_info *rinfo, u8 indx)
+{
+ OUTREG( MC_IND_INDEX, indx);
+ return INREG( MC_IND_DATA);
+}
+
+static void radeon_pm_save_regs(struct radeonfb_info *rinfo, int saving_for_d3)
+{
+ rinfo->save_regs[0] = INPLL(PLL_PWRMGT_CNTL);
+ rinfo->save_regs[1] = INPLL(CLK_PWRMGT_CNTL);
+ rinfo->save_regs[2] = INPLL(MCLK_CNTL);
+ rinfo->save_regs[3] = INPLL(SCLK_CNTL);
+ rinfo->save_regs[4] = INPLL(CLK_PIN_CNTL);
+ rinfo->save_regs[5] = INPLL(VCLK_ECP_CNTL);
+ rinfo->save_regs[6] = INPLL(PIXCLKS_CNTL);
+ rinfo->save_regs[7] = INPLL(MCLK_MISC);
+ rinfo->save_regs[8] = INPLL(P2PLL_CNTL);
+
+ rinfo->save_regs[9] = INREG(DISP_MISC_CNTL);
+ rinfo->save_regs[10] = INREG(DISP_PWR_MAN);
+ rinfo->save_regs[11] = INREG(LVDS_GEN_CNTL);
+ rinfo->save_regs[13] = INREG(TV_DAC_CNTL);
+ rinfo->save_regs[14] = INREG(BUS_CNTL1);
+ rinfo->save_regs[15] = INREG(CRTC_OFFSET_CNTL);
+ rinfo->save_regs[16] = INREG(AGP_CNTL);
+ rinfo->save_regs[17] = (INREG(CRTC_GEN_CNTL) & 0xfdffffff) | 0x04000000;
+ rinfo->save_regs[18] = (INREG(CRTC2_GEN_CNTL) & 0xfdffffff) | 0x04000000;
+ rinfo->save_regs[19] = INREG(GPIOPAD_A);
+ rinfo->save_regs[20] = INREG(GPIOPAD_EN);
+ rinfo->save_regs[21] = INREG(GPIOPAD_MASK);
+ rinfo->save_regs[22] = INREG(ZV_LCDPAD_A);
+ rinfo->save_regs[23] = INREG(ZV_LCDPAD_EN);
+ rinfo->save_regs[24] = INREG(ZV_LCDPAD_MASK);
+ rinfo->save_regs[25] = INREG(GPIO_VGA_DDC);
+ rinfo->save_regs[26] = INREG(GPIO_DVI_DDC);
+ rinfo->save_regs[27] = INREG(GPIO_MONID);
+ rinfo->save_regs[28] = INREG(GPIO_CRT2_DDC);
+
+ rinfo->save_regs[29] = INREG(SURFACE_CNTL);
+ rinfo->save_regs[30] = INREG(MC_FB_LOCATION);
+ rinfo->save_regs[31] = INREG(DISPLAY_BASE_ADDR);
+ rinfo->save_regs[32] = INREG(MC_AGP_LOCATION);
+ rinfo->save_regs[33] = INREG(CRTC2_DISPLAY_BASE_ADDR);
+
+ rinfo->save_regs[34] = INPLL(SCLK_MORE_CNTL);
+ rinfo->save_regs[35] = INREG(MEM_SDRAM_MODE_REG);
+ rinfo->save_regs[36] = INREG(BUS_CNTL);
+ rinfo->save_regs[39] = INREG(RBBM_CNTL);
+ rinfo->save_regs[40] = INREG(DAC_CNTL);
+ rinfo->save_regs[41] = INREG(HOST_PATH_CNTL);
+ rinfo->save_regs[37] = INREG(MPP_TB_CONFIG);
+ rinfo->save_regs[38] = INREG(FCP_CNTL);
+
+ if (rinfo->is_mobility) {
+ rinfo->save_regs[12] = INREG(LVDS_PLL_CNTL);
+ rinfo->save_regs[43] = INPLL(pllSSPLL_CNTL);
+ rinfo->save_regs[44] = INPLL(pllSSPLL_REF_DIV);
+ rinfo->save_regs[45] = INPLL(pllSSPLL_DIV_0);
+ rinfo->save_regs[90] = INPLL(pllSS_INT_CNTL);
+ rinfo->save_regs[91] = INPLL(pllSS_TST_CNTL);
+ rinfo->save_regs[81] = INREG(LVDS_GEN_CNTL);
+ }
+
+ if (rinfo->family >= CHIP_FAMILY_RV200) {
+ rinfo->save_regs[42] = INREG(MEM_REFRESH_CNTL);
+ rinfo->save_regs[46] = INREG(MC_CNTL);
+ rinfo->save_regs[47] = INREG(MC_INIT_GFX_LAT_TIMER);
+ rinfo->save_regs[48] = INREG(MC_INIT_MISC_LAT_TIMER);
+ rinfo->save_regs[49] = INREG(MC_TIMING_CNTL);
+ rinfo->save_regs[50] = INREG(MC_READ_CNTL_AB);
+ rinfo->save_regs[51] = INREG(MC_IOPAD_CNTL);
+ rinfo->save_regs[52] = INREG(MC_CHIP_IO_OE_CNTL_AB);
+ rinfo->save_regs[53] = INREG(MC_DEBUG);
+ }
+ rinfo->save_regs[54] = INREG(PAMAC0_DLY_CNTL);
+ rinfo->save_regs[55] = INREG(PAMAC1_DLY_CNTL);
+ rinfo->save_regs[56] = INREG(PAD_CTLR_MISC);
+ rinfo->save_regs[57] = INREG(FW_CNTL);
+
+ if (rinfo->family >= CHIP_FAMILY_R300) {
+ rinfo->save_regs[58] = INMC(rinfo, ixR300_MC_MC_INIT_WR_LAT_TIMER);
+ rinfo->save_regs[59] = INMC(rinfo, ixR300_MC_IMP_CNTL);
+ rinfo->save_regs[60] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_C0);
+ rinfo->save_regs[61] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_C1);
+ rinfo->save_regs[62] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_D0);
+ rinfo->save_regs[63] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_D1);
+ rinfo->save_regs[64] = INMC(rinfo, ixR300_MC_BIST_CNTL_3);
+ rinfo->save_regs[65] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A0);
+ rinfo->save_regs[66] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1);
+ rinfo->save_regs[67] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B0);
+ rinfo->save_regs[68] = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1);
+ rinfo->save_regs[69] = INMC(rinfo, ixR300_MC_DEBUG_CNTL);
+ rinfo->save_regs[70] = INMC(rinfo, ixR300_MC_DLL_CNTL);
+ rinfo->save_regs[71] = INMC(rinfo, ixR300_MC_IMP_CNTL_0);
+ rinfo->save_regs[72] = INMC(rinfo, ixR300_MC_ELPIDA_CNTL);
+ rinfo->save_regs[96] = INMC(rinfo, ixR300_MC_READ_CNTL_CD);
+ } else {
+ rinfo->save_regs[59] = INMC(rinfo, ixMC_IMP_CNTL);
+ rinfo->save_regs[65] = INMC(rinfo, ixMC_CHP_IO_CNTL_A0);
+ rinfo->save_regs[66] = INMC(rinfo, ixMC_CHP_IO_CNTL_A1);
+ rinfo->save_regs[67] = INMC(rinfo, ixMC_CHP_IO_CNTL_B0);
+ rinfo->save_regs[68] = INMC(rinfo, ixMC_CHP_IO_CNTL_B1);
+ rinfo->save_regs[71] = INMC(rinfo, ixMC_IMP_CNTL_0);
+ }
+
+ rinfo->save_regs[73] = INPLL(pllMPLL_CNTL);
+ rinfo->save_regs[74] = INPLL(pllSPLL_CNTL);
+ rinfo->save_regs[75] = INPLL(pllMPLL_AUX_CNTL);
+ rinfo->save_regs[76] = INPLL(pllSPLL_AUX_CNTL);
+ rinfo->save_regs[77] = INPLL(pllM_SPLL_REF_FB_DIV);
+ rinfo->save_regs[78] = INPLL(pllAGP_PLL_CNTL);
+ rinfo->save_regs[79] = INREG(PAMAC2_DLY_CNTL);
+
+ rinfo->save_regs[80] = INREG(OV0_BASE_ADDR);
+ rinfo->save_regs[82] = INREG(FP_GEN_CNTL);
+ rinfo->save_regs[83] = INREG(FP2_GEN_CNTL);
+ rinfo->save_regs[84] = INREG(TMDS_CNTL);
+ rinfo->save_regs[85] = INREG(TMDS_TRANSMITTER_CNTL);
+ rinfo->save_regs[86] = INREG(DISP_OUTPUT_CNTL);
+ rinfo->save_regs[87] = INREG(DISP_HW_DEBUG);
+ rinfo->save_regs[88] = INREG(TV_MASTER_CNTL);
+ rinfo->save_regs[89] = INPLL(pllP2PLL_REF_DIV);
+ rinfo->save_regs[92] = INPLL(pllPPLL_DIV_0);
+ rinfo->save_regs[93] = INPLL(pllPPLL_CNTL);
+ rinfo->save_regs[94] = INREG(GRPH_BUFFER_CNTL);
+ rinfo->save_regs[95] = INREG(GRPH2_BUFFER_CNTL);
+ rinfo->save_regs[96] = INREG(HDP_DEBUG);
+ rinfo->save_regs[97] = INPLL(pllMDLL_CKO);
+ rinfo->save_regs[98] = INPLL(pllMDLL_RDCKA);
+ rinfo->save_regs[99] = INPLL(pllMDLL_RDCKB);
+}
+
+static void radeon_pm_restore_regs(struct radeonfb_info *rinfo)
+{
+ OUTPLL(P2PLL_CNTL, rinfo->save_regs[8] & 0xFFFFFFFE); /* First */
+
+ OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]);
+ OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]);
+ OUTPLL(MCLK_CNTL, rinfo->save_regs[2]);
+ OUTPLL(SCLK_CNTL, rinfo->save_regs[3]);
+ OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]);
+ OUTPLL(VCLK_ECP_CNTL, rinfo->save_regs[5]);
+ OUTPLL(PIXCLKS_CNTL, rinfo->save_regs[6]);
+ OUTPLL(MCLK_MISC, rinfo->save_regs[7]);
+ if (rinfo->family == CHIP_FAMILY_RV350)
+ OUTPLL(SCLK_MORE_CNTL, rinfo->save_regs[34]);
+
+ OUTREG(SURFACE_CNTL, rinfo->save_regs[29]);
+ OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+ OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+ OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+ OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+ OUTREG(CNFG_MEMSIZE, rinfo->video_ram);
+
+ OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+ OUTREG(DISP_PWR_MAN, rinfo->save_regs[10]);
+ OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11]);
+ OUTREG(LVDS_PLL_CNTL,rinfo->save_regs[12]);
+ OUTREG(TV_DAC_CNTL, rinfo->save_regs[13]);
+ OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
+ OUTREG(CRTC_OFFSET_CNTL, rinfo->save_regs[15]);
+ OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+ OUTREG(CRTC_GEN_CNTL, rinfo->save_regs[17]);
+ OUTREG(CRTC2_GEN_CNTL, rinfo->save_regs[18]);
+ OUTPLL(P2PLL_CNTL, rinfo->save_regs[8]);
+
+ OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
+ OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
+ OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
+ OUTREG(ZV_LCDPAD_A, rinfo->save_regs[22]);
+ OUTREG(ZV_LCDPAD_EN, rinfo->save_regs[23]);
+ OUTREG(ZV_LCDPAD_MASK, rinfo->save_regs[24]);
+ OUTREG(GPIO_VGA_DDC, rinfo->save_regs[25]);
+ OUTREG(GPIO_DVI_DDC, rinfo->save_regs[26]);
+ OUTREG(GPIO_MONID, rinfo->save_regs[27]);
+ OUTREG(GPIO_CRT2_DDC, rinfo->save_regs[28]);
+}
+
+static void radeon_pm_disable_iopad(struct radeonfb_info *rinfo)
+{
+ OUTREG(GPIOPAD_MASK, 0x0001ffff);
+ OUTREG(GPIOPAD_EN, 0x00000400);
+ OUTREG(GPIOPAD_A, 0x00000000);
+ OUTREG(ZV_LCDPAD_MASK, 0x00000000);
+ OUTREG(ZV_LCDPAD_EN, 0x00000000);
+ OUTREG(ZV_LCDPAD_A, 0x00000000);
+ OUTREG(GPIO_VGA_DDC, 0x00030000);
+ OUTREG(GPIO_DVI_DDC, 0x00000000);
+ OUTREG(GPIO_MONID, 0x00030000);
+ OUTREG(GPIO_CRT2_DDC, 0x00000000);
+}
+
+static void radeon_pm_program_v2clk(struct radeonfb_info *rinfo)
+{
+ /* Set v2clk to 65MHz */
+ if (rinfo->family <= CHIP_FAMILY_RV280) {
+ OUTPLL(pllPIXCLKS_CNTL,
+ __INPLL(rinfo, pllPIXCLKS_CNTL)
+ & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK);
+
+ OUTPLL(pllP2PLL_REF_DIV, 0x0000000c);
+ OUTPLL(pllP2PLL_CNTL, 0x0000bf00);
+ } else {
+ OUTPLL(pllP2PLL_REF_DIV, 0x0000000c);
+ INPLL(pllP2PLL_REF_DIV);
+ OUTPLL(pllP2PLL_CNTL, 0x0000a700);
+ }
+
+ OUTPLL(pllP2PLL_DIV_0, 0x00020074 | P2PLL_DIV_0__P2PLL_ATOMIC_UPDATE_W);
+
+ OUTPLL(pllP2PLL_CNTL, INPLL(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_SLEEP);
+ mdelay(1);
+
+ OUTPLL(pllP2PLL_CNTL, INPLL(pllP2PLL_CNTL) & ~P2PLL_CNTL__P2PLL_RESET);
+ mdelay( 1);
+
+ OUTPLL(pllPIXCLKS_CNTL,
+ (INPLL(pllPIXCLKS_CNTL) & ~PIXCLKS_CNTL__PIX2CLK_SRC_SEL_MASK)
+ | (0x03 << PIXCLKS_CNTL__PIX2CLK_SRC_SEL__SHIFT));
+ mdelay( 1);
+}
+
+static void radeon_pm_low_current(struct radeonfb_info *rinfo)
+{
+ u32 reg;
+
+ reg = INREG(BUS_CNTL1);
+ if (rinfo->family <= CHIP_FAMILY_RV280) {
+ reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK;
+ reg |= BUS_CNTL1_AGPCLK_VALID | (1<<BUS_CNTL1_MOBILE_PLATFORM_SEL_SHIFT);
+ } else {
+ reg |= 0x4080;
+ }
+ OUTREG(BUS_CNTL1, reg);
+
+ reg = INPLL(PLL_PWRMGT_CNTL);
+ reg |= PLL_PWRMGT_CNTL_SPLL_TURNOFF | PLL_PWRMGT_CNTL_PPLL_TURNOFF |
+ PLL_PWRMGT_CNTL_P2PLL_TURNOFF | PLL_PWRMGT_CNTL_TVPLL_TURNOFF;
+ reg &= ~PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK;
+ reg &= ~PLL_PWRMGT_CNTL_MOBILE_SU;
+ OUTPLL(PLL_PWRMGT_CNTL, reg);
+
+ reg = INREG(TV_DAC_CNTL);
+ reg &= ~(TV_DAC_CNTL_BGADJ_MASK |TV_DAC_CNTL_DACADJ_MASK);
+ reg |=TV_DAC_CNTL_BGSLEEP | TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD |
+ TV_DAC_CNTL_BDACPD |
+ (8<<TV_DAC_CNTL_BGADJ__SHIFT) | (8<<TV_DAC_CNTL_DACADJ__SHIFT);
+ OUTREG(TV_DAC_CNTL, reg);
+
+ reg = INREG(TMDS_TRANSMITTER_CNTL);
+ reg &= ~(TMDS_PLL_EN | TMDS_PLLRST);
+ OUTREG(TMDS_TRANSMITTER_CNTL, reg);
+
+ reg = INREG(DAC_CNTL);
+ reg &= ~DAC_CMP_EN;
+ OUTREG(DAC_CNTL, reg);
+
+ reg = INREG(DAC_CNTL2);
+ reg &= ~DAC2_CMP_EN;
+ OUTREG(DAC_CNTL2, reg);
+
+ reg = INREG(TV_DAC_CNTL);
+ reg &= ~TV_DAC_CNTL_DETECT;
+ OUTREG(TV_DAC_CNTL, reg);
+}
+
+static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
+{
+
+ u32 sclk_cntl, mclk_cntl, sclk_more_cntl;
+
+ u32 pll_pwrmgt_cntl;
+ u32 clk_pwrmgt_cntl;
+ u32 clk_pin_cntl;
+ u32 vclk_ecp_cntl;
+ u32 pixclks_cntl;
+ u32 disp_mis_cntl;
+ u32 disp_pwr_man;
+ u32 tmp;
+
+ /* Force Core Clocks */
+ sclk_cntl = INPLL( pllSCLK_CNTL);
+ sclk_cntl |= SCLK_CNTL__IDCT_MAX_DYN_STOP_LAT|
+ SCLK_CNTL__VIP_MAX_DYN_STOP_LAT|
+ SCLK_CNTL__RE_MAX_DYN_STOP_LAT|
+ SCLK_CNTL__PB_MAX_DYN_STOP_LAT|
+ SCLK_CNTL__TAM_MAX_DYN_STOP_LAT|
+ SCLK_CNTL__TDM_MAX_DYN_STOP_LAT|
+ SCLK_CNTL__RB_MAX_DYN_STOP_LAT|
+
+ SCLK_CNTL__FORCE_DISP2|
+ SCLK_CNTL__FORCE_CP|
+ SCLK_CNTL__FORCE_HDP|
+ SCLK_CNTL__FORCE_DISP1|
+ SCLK_CNTL__FORCE_TOP|
+ SCLK_CNTL__FORCE_E2|
+ SCLK_CNTL__FORCE_SE|
+ SCLK_CNTL__FORCE_IDCT|
+ SCLK_CNTL__FORCE_VIP|
+
+ SCLK_CNTL__FORCE_PB|
+ SCLK_CNTL__FORCE_TAM|
+ SCLK_CNTL__FORCE_TDM|
+ SCLK_CNTL__FORCE_RB|
+ SCLK_CNTL__FORCE_TV_SCLK|
+ SCLK_CNTL__FORCE_SUBPIC|
+ SCLK_CNTL__FORCE_OV0;
+ if (rinfo->family <= CHIP_FAMILY_RV280)
+ sclk_cntl |= SCLK_CNTL__FORCE_RE;
+ else
+ sclk_cntl |= SCLK_CNTL__SE_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__E2_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__TV_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__HDP_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__CP_MAX_DYN_STOP_LAT;
+
+ OUTPLL( pllSCLK_CNTL, sclk_cntl);
+
+ sclk_more_cntl = INPLL(pllSCLK_MORE_CNTL);
+ sclk_more_cntl |= SCLK_MORE_CNTL__FORCE_DISPREGS |
+ SCLK_MORE_CNTL__FORCE_MC_GUI |
+ SCLK_MORE_CNTL__FORCE_MC_HOST;
+
+ OUTPLL(pllSCLK_MORE_CNTL, sclk_more_cntl);
+
+
+ mclk_cntl = INPLL( pllMCLK_CNTL);
+ mclk_cntl &= ~( MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_MCLKB |
+ MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB |
+ MCLK_CNTL__FORCE_MC
+ );
+ OUTPLL( pllMCLK_CNTL, mclk_cntl);
+
+ /* Force Display clocks */
+ vclk_ecp_cntl = INPLL( pllVCLK_ECP_CNTL);
+ vclk_ecp_cntl &= ~(VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb
+ | VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
+ vclk_ecp_cntl |= VCLK_ECP_CNTL__ECP_FORCE_ON;
+ OUTPLL( pllVCLK_ECP_CNTL, vclk_ecp_cntl);
+
+
+ pixclks_cntl = INPLL( pllPIXCLKS_CNTL);
+ pixclks_cntl &= ~( PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb |
+ PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb|
+ PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb);
+
+ OUTPLL( pllPIXCLKS_CNTL, pixclks_cntl);
+
+ /* Switch off LVDS interface */
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) &
+ ~(LVDS_BLON | LVDS_EN | LVDS_ON | LVDS_DIGON));
+
+ /* Enable System power management */
+ pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL);
+
+ pll_pwrmgt_cntl |= PLL_PWRMGT_CNTL__SPLL_TURNOFF |
+ PLL_PWRMGT_CNTL__MPLL_TURNOFF|
+ PLL_PWRMGT_CNTL__PPLL_TURNOFF|
+ PLL_PWRMGT_CNTL__P2PLL_TURNOFF|
+ PLL_PWRMGT_CNTL__TVPLL_TURNOFF;
+
+ OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
+
+ clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL);
+
+ clk_pwrmgt_cntl &= ~( CLK_PWRMGT_CNTL__MPLL_PWRMGT_OFF|
+ CLK_PWRMGT_CNTL__SPLL_PWRMGT_OFF|
+ CLK_PWRMGT_CNTL__PPLL_PWRMGT_OFF|
+ CLK_PWRMGT_CNTL__P2PLL_PWRMGT_OFF|
+ CLK_PWRMGT_CNTL__MCLK_TURNOFF|
+ CLK_PWRMGT_CNTL__SCLK_TURNOFF|
+ CLK_PWRMGT_CNTL__PCLK_TURNOFF|
+ CLK_PWRMGT_CNTL__P2CLK_TURNOFF|
+ CLK_PWRMGT_CNTL__TVPLL_PWRMGT_OFF|
+ CLK_PWRMGT_CNTL__GLOBAL_PMAN_EN|
+ CLK_PWRMGT_CNTL__ENGINE_DYNCLK_MODE|
+ CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK|
+ CLK_PWRMGT_CNTL__CG_NO1_DEBUG_MASK
+ );
+
+ clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL__GLOBAL_PMAN_EN
+ | CLK_PWRMGT_CNTL__DISP_PM;
+
+ OUTPLL( pllCLK_PWRMGT_CNTL, clk_pwrmgt_cntl);
+
+ clk_pin_cntl = INPLL( pllCLK_PIN_CNTL);
+
+ clk_pin_cntl &= ~CLK_PIN_CNTL__ACCESS_REGS_IN_SUSPEND;
+
+ /* because both INPLL and OUTPLL take the same lock, that's why. */
+ tmp = INPLL( pllMCLK_MISC) | MCLK_MISC__EN_MCLK_TRISTATE_IN_SUSPEND;
+ OUTPLL( pllMCLK_MISC, tmp);
+
+ /* BUS_CNTL1__MOBILE_PLATORM_SEL setting is northbridge chipset
+ * and radeon chip dependent. Thus we only enable it on Mac for
+ * now (until we get more info on how to compute the correct
+ * value for various X86 bridges).
+ */
+#ifdef CONFIG_PPC_PMAC
+ if (machine_is(powermac)) {
+ /* AGP PLL control */
+ if (rinfo->family <= CHIP_FAMILY_RV280) {
+ OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | BUS_CNTL1__AGPCLK_VALID);
+ OUTREG(BUS_CNTL1,
+ (INREG(BUS_CNTL1) & ~BUS_CNTL1__MOBILE_PLATFORM_SEL_MASK)
+ | (2<<BUS_CNTL1__MOBILE_PLATFORM_SEL__SHIFT)); // 440BX
+ } else {
+ OUTREG(BUS_CNTL1, INREG(BUS_CNTL1));
+ OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & ~0x4000) | 0x8000);
+ }
+ }
+#endif
+
+ OUTREG(CRTC_OFFSET_CNTL, (INREG(CRTC_OFFSET_CNTL)
+ & ~CRTC_OFFSET_CNTL__CRTC_STEREO_SYNC_OUT_EN));
+
+ clk_pin_cntl &= ~CLK_PIN_CNTL__CG_CLK_TO_OUTPIN;
+ clk_pin_cntl |= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;
+ OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
+
+ /* Solano2M */
+ OUTREG(AGP_CNTL,
+ (INREG(AGP_CNTL) & ~(AGP_CNTL__MAX_IDLE_CLK_MASK))
+ | (0x20<<AGP_CNTL__MAX_IDLE_CLK__SHIFT));
+
+ /* ACPI mode */
+ /* because both INPLL and OUTPLL take the same lock, that's why. */
+ tmp = INPLL( pllPLL_PWRMGT_CNTL) & ~PLL_PWRMGT_CNTL__PM_MODE_SEL;
+ OUTPLL( pllPLL_PWRMGT_CNTL, tmp);
+
+
+ disp_mis_cntl = INREG(DISP_MISC_CNTL);
+
+ disp_mis_cntl &= ~( DISP_MISC_CNTL__SOFT_RESET_GRPH_PP |
+ DISP_MISC_CNTL__SOFT_RESET_SUBPIC_PP |
+ DISP_MISC_CNTL__SOFT_RESET_OV0_PP |
+ DISP_MISC_CNTL__SOFT_RESET_GRPH_SCLK|
+ DISP_MISC_CNTL__SOFT_RESET_SUBPIC_SCLK|
+ DISP_MISC_CNTL__SOFT_RESET_OV0_SCLK|
+ DISP_MISC_CNTL__SOFT_RESET_GRPH2_PP|
+ DISP_MISC_CNTL__SOFT_RESET_GRPH2_SCLK|
+ DISP_MISC_CNTL__SOFT_RESET_LVDS|
+ DISP_MISC_CNTL__SOFT_RESET_TMDS|
+ DISP_MISC_CNTL__SOFT_RESET_DIG_TMDS|
+ DISP_MISC_CNTL__SOFT_RESET_TV);
+
+ OUTREG(DISP_MISC_CNTL, disp_mis_cntl);
+
+ disp_pwr_man = INREG(DISP_PWR_MAN);
+
+ disp_pwr_man &= ~( DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN |
+ DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN |
+ DISP_PWR_MAN__DISP_PWR_MAN_DPMS_MASK|
+ DISP_PWR_MAN__DISP_D3_RST|
+ DISP_PWR_MAN__DISP_D3_REG_RST
+ );
+
+ disp_pwr_man |= DISP_PWR_MAN__DISP_D3_GRPH_RST|
+ DISP_PWR_MAN__DISP_D3_SUBPIC_RST|
+ DISP_PWR_MAN__DISP_D3_OV0_RST|
+ DISP_PWR_MAN__DISP_D1D2_GRPH_RST|
+ DISP_PWR_MAN__DISP_D1D2_SUBPIC_RST|
+ DISP_PWR_MAN__DISP_D1D2_OV0_RST|
+ DISP_PWR_MAN__DIG_TMDS_ENABLE_RST|
+ DISP_PWR_MAN__TV_ENABLE_RST|
+// DISP_PWR_MAN__AUTO_PWRUP_EN|
+ 0;
+
+ OUTREG(DISP_PWR_MAN, disp_pwr_man);
+
+ clk_pwrmgt_cntl = INPLL( pllCLK_PWRMGT_CNTL);
+ pll_pwrmgt_cntl = INPLL( pllPLL_PWRMGT_CNTL) ;
+ clk_pin_cntl = INPLL( pllCLK_PIN_CNTL);
+ disp_pwr_man = INREG(DISP_PWR_MAN);
+
+
+ /* D2 */
+ clk_pwrmgt_cntl |= CLK_PWRMGT_CNTL__DISP_PM;
+ pll_pwrmgt_cntl |= PLL_PWRMGT_CNTL__MOBILE_SU | PLL_PWRMGT_CNTL__SU_SCLK_USE_BCLK;
+ clk_pin_cntl |= CLK_PIN_CNTL__XTALIN_ALWAYS_ONb;
+ disp_pwr_man &= ~(DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN_MASK
+ | DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN_MASK);
+
+ OUTPLL( pllCLK_PWRMGT_CNTL, clk_pwrmgt_cntl);
+ OUTPLL( pllPLL_PWRMGT_CNTL, pll_pwrmgt_cntl);
+ OUTPLL( pllCLK_PIN_CNTL, clk_pin_cntl);
+ OUTREG(DISP_PWR_MAN, disp_pwr_man);
+
+ /* disable display request & disable display */
+ OUTREG( CRTC_GEN_CNTL, (INREG( CRTC_GEN_CNTL) & ~CRTC_GEN_CNTL__CRTC_EN)
+ | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B);
+ OUTREG( CRTC2_GEN_CNTL, (INREG( CRTC2_GEN_CNTL) & ~CRTC2_GEN_CNTL__CRTC2_EN)
+ | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B);
+
+ mdelay(17);
+
+}
+
+static void radeon_pm_yclk_mclk_sync(struct radeonfb_info *rinfo)
+{
+ u32 mc_chp_io_cntl_a1, mc_chp_io_cntl_b1;
+
+ mc_chp_io_cntl_a1 = INMC( rinfo, ixMC_CHP_IO_CNTL_A1)
+ & ~MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA_MASK;
+ mc_chp_io_cntl_b1 = INMC( rinfo, ixMC_CHP_IO_CNTL_B1)
+ & ~MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB_MASK;
+
+ OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1
+ | (1<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT));
+ OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1
+ | (1<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT));
+
+ OUTMC( rinfo, ixMC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1);
+ OUTMC( rinfo, ixMC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1);
+
+ mdelay( 1);
+}
+
+static void radeon_pm_yclk_mclk_sync_m10(struct radeonfb_info *rinfo)
+{
+ u32 mc_chp_io_cntl_a1, mc_chp_io_cntl_b1;
+
+ mc_chp_io_cntl_a1 = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1)
+ & ~MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA_MASK;
+ mc_chp_io_cntl_b1 = INMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1)
+ & ~MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB_MASK;
+
+ OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_A1,
+ mc_chp_io_cntl_a1 | (1<<MC_CHP_IO_CNTL_A1__MEM_SYNC_ENA__SHIFT));
+ OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_B1,
+ mc_chp_io_cntl_b1 | (1<<MC_CHP_IO_CNTL_B1__MEM_SYNC_ENB__SHIFT));
+
+ OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_A1, mc_chp_io_cntl_a1);
+ OUTMC( rinfo, ixR300_MC_CHP_IO_CNTL_B1, mc_chp_io_cntl_b1);
+
+ mdelay( 1);
+}
+
+static void radeon_pm_program_mode_reg(struct radeonfb_info *rinfo, u16 value,
+ u8 delay_required)
+{
+ u32 mem_sdram_mode;
+
+ mem_sdram_mode = INREG( MEM_SDRAM_MODE_REG);
+
+ mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_MODE_REG_MASK;
+ mem_sdram_mode |= (value<<MEM_SDRAM_MODE_REG__MEM_MODE_REG__SHIFT)
+ | MEM_SDRAM_MODE_REG__MEM_CFG_TYPE;
+ OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+ if (delay_required >= 2)
+ mdelay(1);
+
+ mem_sdram_mode |= MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET;
+ OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+ if (delay_required >= 2)
+ mdelay(1);
+
+ mem_sdram_mode &= ~MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET;
+ OUTREG( MEM_SDRAM_MODE_REG, mem_sdram_mode);
+ if (delay_required >= 2)
+ mdelay(1);
+
+ if (delay_required) {
+ do {
+ if (delay_required >= 2)
+ mdelay(1);
+ } while ((INREG(MC_STATUS)
+ & (MC_STATUS__MEM_PWRUP_COMPL_A |
+ MC_STATUS__MEM_PWRUP_COMPL_B)) == 0);
+ }
+}
+
+static void radeon_pm_m10_program_mode_wait(struct radeonfb_info *rinfo)
+{
+ int cnt;
+
+ for (cnt = 0; cnt < 100; ++cnt) {
+ mdelay(1);
+ if (INREG(MC_STATUS) & (MC_STATUS__MEM_PWRUP_COMPL_A
+ | MC_STATUS__MEM_PWRUP_COMPL_B))
+ break;
+ }
+}
+
+
+static void radeon_pm_enable_dll(struct radeonfb_info *rinfo)
+{
+#define DLL_RESET_DELAY 5
+#define DLL_SLEEP_DELAY 1
+
+ u32 cko = INPLL(pllMDLL_CKO) | MDLL_CKO__MCKOA_SLEEP
+ | MDLL_CKO__MCKOA_RESET;
+ u32 cka = INPLL(pllMDLL_RDCKA) | MDLL_RDCKA__MRDCKA0_SLEEP
+ | MDLL_RDCKA__MRDCKA1_SLEEP | MDLL_RDCKA__MRDCKA0_RESET
+ | MDLL_RDCKA__MRDCKA1_RESET;
+ u32 ckb = INPLL(pllMDLL_RDCKB) | MDLL_RDCKB__MRDCKB0_SLEEP
+ | MDLL_RDCKB__MRDCKB1_SLEEP | MDLL_RDCKB__MRDCKB0_RESET
+ | MDLL_RDCKB__MRDCKB1_RESET;
+
+ /* Setting up the DLL range for write */
+ OUTPLL(pllMDLL_CKO, cko);
+ OUTPLL(pllMDLL_RDCKA, cka);
+ OUTPLL(pllMDLL_RDCKB, ckb);
+
+ mdelay(DLL_RESET_DELAY*2);
+
+ cko &= ~(MDLL_CKO__MCKOA_SLEEP | MDLL_CKO__MCKOB_SLEEP);
+ OUTPLL(pllMDLL_CKO, cko);
+ mdelay(DLL_SLEEP_DELAY);
+ cko &= ~(MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET);
+ OUTPLL(pllMDLL_CKO, cko);
+ mdelay(DLL_RESET_DELAY);
+
+ cka &= ~(MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP);
+ OUTPLL(pllMDLL_RDCKA, cka);
+ mdelay(DLL_SLEEP_DELAY);
+ cka &= ~(MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET);
+ OUTPLL(pllMDLL_RDCKA, cka);
+ mdelay(DLL_RESET_DELAY);
+
+ ckb &= ~(MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP);
+ OUTPLL(pllMDLL_RDCKB, ckb);
+ mdelay(DLL_SLEEP_DELAY);
+ ckb &= ~(MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET);
+ OUTPLL(pllMDLL_RDCKB, ckb);
+ mdelay(DLL_RESET_DELAY);
+
+
+#undef DLL_RESET_DELAY
+#undef DLL_SLEEP_DELAY
+}
+
+static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo)
+{
+ u32 dll_value;
+ u32 dll_sleep_mask = 0;
+ u32 dll_reset_mask = 0;
+ u32 mc;
+
+#define DLL_RESET_DELAY 5
+#define DLL_SLEEP_DELAY 1
+
+ OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]);
+ mc = INREG(MC_CNTL);
+ /* Check which channels are enabled */
+ switch (mc & 0x3) {
+ case 1:
+ if (mc & 0x4)
+ break;
+ /* fall through */
+ case 2:
+ dll_sleep_mask |= MDLL_R300_RDCK__MRDCKB_SLEEP;
+ dll_reset_mask |= MDLL_R300_RDCK__MRDCKB_RESET;
+ /* fall through */
+ case 0:
+ dll_sleep_mask |= MDLL_R300_RDCK__MRDCKA_SLEEP;
+ dll_reset_mask |= MDLL_R300_RDCK__MRDCKA_RESET;
+ }
+ switch (mc & 0x3) {
+ case 1:
+ if (!(mc & 0x4))
+ break;
+ /* fall through */
+ case 2:
+ dll_sleep_mask |= MDLL_R300_RDCK__MRDCKD_SLEEP;
+ dll_reset_mask |= MDLL_R300_RDCK__MRDCKD_RESET;
+ dll_sleep_mask |= MDLL_R300_RDCK__MRDCKC_SLEEP;
+ dll_reset_mask |= MDLL_R300_RDCK__MRDCKC_RESET;
+ }
+
+ dll_value = INPLL(pllMDLL_RDCKA);
+
+ /* Power Up */
+ dll_value &= ~(dll_sleep_mask);
+ OUTPLL(pllMDLL_RDCKA, dll_value);
+ mdelay( DLL_SLEEP_DELAY);
+
+ dll_value &= ~(dll_reset_mask);
+ OUTPLL(pllMDLL_RDCKA, dll_value);
+ mdelay( DLL_RESET_DELAY);
+
+#undef DLL_RESET_DELAY
+#undef DLL_SLEEP_DELAY
+}
+
+
+static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo)
+{
+ u32 crtcGenCntl, crtcGenCntl2, memRefreshCntl, crtc_more_cntl,
+ fp_gen_cntl, fp2_gen_cntl;
+
+ crtcGenCntl = INREG( CRTC_GEN_CNTL);
+ crtcGenCntl2 = INREG( CRTC2_GEN_CNTL);
+
+ crtc_more_cntl = INREG( CRTC_MORE_CNTL);
+ fp_gen_cntl = INREG( FP_GEN_CNTL);
+ fp2_gen_cntl = INREG( FP2_GEN_CNTL);
+
+
+ OUTREG( CRTC_MORE_CNTL, 0);
+ OUTREG( FP_GEN_CNTL, 0);
+ OUTREG( FP2_GEN_CNTL,0);
+
+ OUTREG( CRTC_GEN_CNTL, (crtcGenCntl | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B) );
+ OUTREG( CRTC2_GEN_CNTL, (crtcGenCntl2 | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B) );
+
+ /* This is the code for the Aluminium PowerBooks M10 / iBooks M11 */
+ if (rinfo->family == CHIP_FAMILY_RV350) {
+ u32 sdram_mode_reg = rinfo->save_regs[35];
+ static const u32 default_mrtable[] =
+ { 0x21320032,
+ 0x21321000, 0xa1321000, 0x21321000, 0xffffffff,
+ 0x21320032, 0xa1320032, 0x21320032, 0xffffffff,
+ 0x21321002, 0xa1321002, 0x21321002, 0xffffffff,
+ 0x21320132, 0xa1320132, 0x21320132, 0xffffffff,
+ 0x21320032, 0xa1320032, 0x21320032, 0xffffffff,
+ 0x31320032 };
+
+ const u32 *mrtable = default_mrtable;
+ int i, mrtable_size = ARRAY_SIZE(default_mrtable);
+
+ mdelay(30);
+
+ /* Disable refresh */
+ memRefreshCntl = INREG( MEM_REFRESH_CNTL)
+ & ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS;
+ OUTREG( MEM_REFRESH_CNTL, memRefreshCntl
+ | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+ /* Configure and enable M & SPLLs */
+ radeon_pm_enable_dll_m10(rinfo);
+ radeon_pm_yclk_mclk_sync_m10(rinfo);
+
+#ifdef CONFIG_PPC
+ if (rinfo->of_node != NULL) {
+ int size;
+
+ mrtable = of_get_property(rinfo->of_node, "ATY,MRT", &size);
+ if (mrtable)
+ mrtable_size = size >> 2;
+ else
+ mrtable = default_mrtable;
+ }
+#endif /* CONFIG_PPC */
+
+ /* Program the SDRAM */
+ sdram_mode_reg = mrtable[0];
+ OUTREG(MEM_SDRAM_MODE_REG, sdram_mode_reg);
+ for (i = 0; i < mrtable_size; i++) {
+ if (mrtable[i] == 0xffffffffu)
+ radeon_pm_m10_program_mode_wait(rinfo);
+ else {
+ sdram_mode_reg &= ~(MEM_SDRAM_MODE_REG__MEM_MODE_REG_MASK
+ | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE
+ | MEM_SDRAM_MODE_REG__MEM_SDRAM_RESET);
+ sdram_mode_reg |= mrtable[i];
+
+ OUTREG(MEM_SDRAM_MODE_REG, sdram_mode_reg);
+ mdelay(1);
+ }
+ }
+
+ /* Restore memory refresh */
+ OUTREG(MEM_REFRESH_CNTL, memRefreshCntl);
+ mdelay(30);
+
+ }
+ /* Here come the desktop RV200 "QW" card */
+ else if (!rinfo->is_mobility && rinfo->family == CHIP_FAMILY_RV200) {
+ /* Disable refresh */
+ memRefreshCntl = INREG( MEM_REFRESH_CNTL)
+ & ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS;
+ OUTREG(MEM_REFRESH_CNTL, memRefreshCntl
+ | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+ mdelay(30);
+
+ /* Reset memory */
+ OUTREG(MEM_SDRAM_MODE_REG,
+ INREG( MEM_SDRAM_MODE_REG) & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+ radeon_pm_program_mode_reg(rinfo, 0x2002, 2);
+ radeon_pm_program_mode_reg(rinfo, 0x0132, 2);
+ radeon_pm_program_mode_reg(rinfo, 0x0032, 2);
+
+ OUTREG(MEM_SDRAM_MODE_REG,
+ INREG(MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+ OUTREG( MEM_REFRESH_CNTL, memRefreshCntl);
+
+ }
+ /* The M6 */
+ else if (rinfo->is_mobility && rinfo->family == CHIP_FAMILY_RV100) {
+ /* Disable refresh */
+ memRefreshCntl = INREG(EXT_MEM_CNTL) & ~(1 << 20);
+ OUTREG( EXT_MEM_CNTL, memRefreshCntl | (1 << 20));
+
+ /* Reset memory */
+ OUTREG( MEM_SDRAM_MODE_REG,
+ INREG( MEM_SDRAM_MODE_REG)
+ & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+ /* DLL */
+ radeon_pm_enable_dll(rinfo);
+
+ /* MLCK / YCLK sync */
+ radeon_pm_yclk_mclk_sync(rinfo);
+
+ /* Program Mode Register */
+ radeon_pm_program_mode_reg(rinfo, 0x2000, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x2001, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x2002, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x0132, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x0032, 1);
+
+ /* Complete & re-enable refresh */
+ OUTREG( MEM_SDRAM_MODE_REG,
+ INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+ OUTREG(EXT_MEM_CNTL, memRefreshCntl);
+ }
+ /* And finally, the M7..M9 models, including M9+ (RV280) */
+ else if (rinfo->is_mobility) {
+
+ /* Disable refresh */
+ memRefreshCntl = INREG( MEM_REFRESH_CNTL)
+ & ~MEM_REFRESH_CNTL__MEM_REFRESH_DIS;
+ OUTREG( MEM_REFRESH_CNTL, memRefreshCntl
+ | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+ /* Reset memory */
+ OUTREG( MEM_SDRAM_MODE_REG,
+ INREG( MEM_SDRAM_MODE_REG)
+ & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+ /* DLL */
+ radeon_pm_enable_dll(rinfo);
+
+ /* MLCK / YCLK sync */
+ radeon_pm_yclk_mclk_sync(rinfo);
+
+ /* M6, M7 and M9 so far ... */
+ if (rinfo->family <= CHIP_FAMILY_RV250) {
+ radeon_pm_program_mode_reg(rinfo, 0x2000, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x2001, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x2002, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x0132, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x0032, 1);
+ }
+ /* M9+ (iBook G4) */
+ else if (rinfo->family == CHIP_FAMILY_RV280) {
+ radeon_pm_program_mode_reg(rinfo, 0x2000, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x0132, 1);
+ radeon_pm_program_mode_reg(rinfo, 0x0032, 1);
+ }
+
+ /* Complete & re-enable refresh */
+ OUTREG( MEM_SDRAM_MODE_REG,
+ INREG( MEM_SDRAM_MODE_REG) | MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+
+ OUTREG( MEM_REFRESH_CNTL, memRefreshCntl);
+ }
+
+ OUTREG( CRTC_GEN_CNTL, crtcGenCntl);
+ OUTREG( CRTC2_GEN_CNTL, crtcGenCntl2);
+ OUTREG( FP_GEN_CNTL, fp_gen_cntl);
+ OUTREG( FP2_GEN_CNTL, fp2_gen_cntl);
+
+ OUTREG( CRTC_MORE_CNTL, crtc_more_cntl);
+
+ mdelay( 15);
+}
+
+#if defined(CONFIG_PM)
+#if defined(CONFIG_X86) || defined(CONFIG_PPC_PMAC)
+static void radeon_pm_reset_pad_ctlr_strength(struct radeonfb_info *rinfo)
+{
+ u32 tmp, tmp2;
+ int i,j;
+
+ /* Reset the PAD_CTLR_STRENGTH & wait for it to be stable */
+ INREG(PAD_CTLR_STRENGTH);
+ OUTREG(PAD_CTLR_STRENGTH, INREG(PAD_CTLR_STRENGTH) & ~PAD_MANUAL_OVERRIDE);
+ tmp = INREG(PAD_CTLR_STRENGTH);
+ for (i = j = 0; i < 65; ++i) {
+ mdelay(1);
+ tmp2 = INREG(PAD_CTLR_STRENGTH);
+ if (tmp != tmp2) {
+ tmp = tmp2;
+ i = 0;
+ j++;
+ if (j > 10) {
+ printk(KERN_WARNING "radeon: PAD_CTLR_STRENGTH doesn't "
+ "stabilize !\n");
+ break;
+ }
+ }
+ }
+}
+
+static void radeon_pm_all_ppls_off(struct radeonfb_info *rinfo)
+{
+ u32 tmp;
+
+ tmp = INPLL(pllPPLL_CNTL);
+ OUTPLL(pllPPLL_CNTL, tmp | 0x3);
+ tmp = INPLL(pllP2PLL_CNTL);
+ OUTPLL(pllP2PLL_CNTL, tmp | 0x3);
+ tmp = INPLL(pllSPLL_CNTL);
+ OUTPLL(pllSPLL_CNTL, tmp | 0x3);
+ tmp = INPLL(pllMPLL_CNTL);
+ OUTPLL(pllMPLL_CNTL, tmp | 0x3);
+}
+
+static void radeon_pm_start_mclk_sclk(struct radeonfb_info *rinfo)
+{
+ u32 tmp;
+
+ /* Switch SPLL to PCI source */
+ tmp = INPLL(pllSCLK_CNTL);
+ OUTPLL(pllSCLK_CNTL, tmp & ~SCLK_CNTL__SCLK_SRC_SEL_MASK);
+
+ /* Reconfigure SPLL charge pump, VCO gain, duty cycle */
+ tmp = INPLL(pllSPLL_CNTL);
+ OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+ radeon_pll_errata_after_data(rinfo);
+
+ /* Set SPLL feedback divider */
+ tmp = INPLL(pllM_SPLL_REF_FB_DIV);
+ tmp = (tmp & 0xff00fffful) | (rinfo->save_regs[77] & 0x00ff0000ul);
+ OUTPLL(pllM_SPLL_REF_FB_DIV, tmp);
+
+ /* Power up SPLL */
+ tmp = INPLL(pllSPLL_CNTL);
+ OUTPLL(pllSPLL_CNTL, tmp & ~1);
+ (void)INPLL(pllSPLL_CNTL);
+
+ mdelay(10);
+
+ /* Release SPLL reset */
+ tmp = INPLL(pllSPLL_CNTL);
+ OUTPLL(pllSPLL_CNTL, tmp & ~0x2);
+ (void)INPLL(pllSPLL_CNTL);
+
+ mdelay(10);
+
+ /* Select SCLK source */
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp &= ~SCLK_CNTL__SCLK_SRC_SEL_MASK;
+ tmp |= rinfo->save_regs[3] & SCLK_CNTL__SCLK_SRC_SEL_MASK;
+ OUTPLL(pllSCLK_CNTL, tmp);
+ (void)INPLL(pllSCLK_CNTL);
+
+ mdelay(10);
+
+ /* Reconfigure MPLL charge pump, VCO gain, duty cycle */
+ tmp = INPLL(pllMPLL_CNTL);
+ OUTREG8(CLOCK_CNTL_INDEX, pllMPLL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+ radeon_pll_errata_after_data(rinfo);
+
+ /* Set MPLL feedback divider */
+ tmp = INPLL(pllM_SPLL_REF_FB_DIV);
+ tmp = (tmp & 0xffff00fful) | (rinfo->save_regs[77] & 0x0000ff00ul);
+
+ OUTPLL(pllM_SPLL_REF_FB_DIV, tmp);
+ /* Power up MPLL */
+ tmp = INPLL(pllMPLL_CNTL);
+ OUTPLL(pllMPLL_CNTL, tmp & ~0x2);
+ (void)INPLL(pllMPLL_CNTL);
+
+ mdelay(10);
+
+ /* Un-reset MPLL */
+ tmp = INPLL(pllMPLL_CNTL);
+ OUTPLL(pllMPLL_CNTL, tmp & ~0x1);
+ (void)INPLL(pllMPLL_CNTL);
+
+ mdelay(10);
+
+ /* Select source for MCLK */
+ tmp = INPLL(pllMCLK_CNTL);
+ tmp |= rinfo->save_regs[2] & 0xffff;
+ OUTPLL(pllMCLK_CNTL, tmp);
+ (void)INPLL(pllMCLK_CNTL);
+
+ mdelay(10);
+}
+
+static void radeon_pm_m10_disable_spread_spectrum(struct radeonfb_info *rinfo)
+{
+ u32 r2ec;
+
+ /* GACK ! I though we didn't have a DDA on Radeon's anymore
+ * here we rewrite with the same value, ... I suppose we clear
+ * some bits that are already clear ? Or maybe this 0x2ec
+ * register is something new ?
+ */
+ mdelay(20);
+ r2ec = INREG(VGA_DDA_ON_OFF);
+ OUTREG(VGA_DDA_ON_OFF, r2ec);
+ mdelay(1);
+
+ /* Spread spectrum PLLL off */
+ OUTPLL(pllSSPLL_CNTL, 0xbf03);
+
+ /* Spread spectrum disabled */
+ OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90] & ~3);
+
+ /* The trace shows read & rewrite of LVDS_PLL_CNTL here with same
+ * value, not sure what for...
+ */
+
+ r2ec |= 0x3f0;
+ OUTREG(VGA_DDA_ON_OFF, r2ec);
+ mdelay(1);
+}
+
+static void radeon_pm_m10_enable_lvds_spread_spectrum(struct radeonfb_info *rinfo)
+{
+ u32 r2ec, tmp;
+
+ /* GACK (bis) ! I though we didn't have a DDA on Radeon's anymore
+ * here we rewrite with the same value, ... I suppose we clear/set
+ * some bits that are already clear/set ?
+ */
+ r2ec = INREG(VGA_DDA_ON_OFF);
+ OUTREG(VGA_DDA_ON_OFF, r2ec);
+ mdelay(1);
+
+ /* Enable spread spectrum */
+ OUTPLL(pllSSPLL_CNTL, rinfo->save_regs[43] | 3);
+ mdelay(3);
+
+ OUTPLL(pllSSPLL_REF_DIV, rinfo->save_regs[44]);
+ OUTPLL(pllSSPLL_DIV_0, rinfo->save_regs[45]);
+ tmp = INPLL(pllSSPLL_CNTL);
+ OUTPLL(pllSSPLL_CNTL, tmp & ~0x2);
+ mdelay(6);
+ tmp = INPLL(pllSSPLL_CNTL);
+ OUTPLL(pllSSPLL_CNTL, tmp & ~0x1);
+ mdelay(5);
+
+ OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90]);
+
+ r2ec |= 8;
+ OUTREG(VGA_DDA_ON_OFF, r2ec);
+ mdelay(20);
+
+ /* Enable LVDS interface */
+ tmp = INREG(LVDS_GEN_CNTL);
+ OUTREG(LVDS_GEN_CNTL, tmp | LVDS_EN);
+
+ /* Enable LVDS_PLL */
+ tmp = INREG(LVDS_PLL_CNTL);
+ tmp &= ~0x30000;
+ tmp |= 0x10000;
+ OUTREG(LVDS_PLL_CNTL, tmp);
+
+ OUTPLL(pllSCLK_MORE_CNTL, rinfo->save_regs[34]);
+ OUTPLL(pllSS_TST_CNTL, rinfo->save_regs[91]);
+
+ /* The trace reads that one here, waiting for something to settle down ? */
+ INREG(RBBM_STATUS);
+
+ /* Ugh ? SS_TST_DEC is supposed to be a read register in the
+ * R300 register spec at least...
+ */
+ tmp = INPLL(pllSS_TST_CNTL);
+ tmp |= 0x00400000;
+ OUTPLL(pllSS_TST_CNTL, tmp);
+}
+
+static void radeon_pm_restore_pixel_pll(struct radeonfb_info *rinfo)
+{
+ u32 tmp;
+
+ OUTREG8(CLOCK_CNTL_INDEX, pllHTOTAL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA, 0);
+ radeon_pll_errata_after_data(rinfo);
+
+ tmp = INPLL(pllVCLK_ECP_CNTL);
+ OUTPLL(pllVCLK_ECP_CNTL, tmp | 0x80);
+ mdelay(5);
+
+ tmp = INPLL(pllPPLL_REF_DIV);
+ tmp = (tmp & ~PPLL_REF_DIV_MASK) | rinfo->pll.ref_div;
+ OUTPLL(pllPPLL_REF_DIV, tmp);
+ INPLL(pllPPLL_REF_DIV);
+
+ /* Reconfigure SPLL charge pump, VCO gain, duty cycle,
+ * probably useless since we already did it ...
+ */
+ tmp = INPLL(pllPPLL_CNTL);
+ OUTREG8(CLOCK_CNTL_INDEX, pllSPLL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+ radeon_pll_errata_after_data(rinfo);
+
+ /* Restore our "reference" PPLL divider set by firmware
+ * according to proper spread spectrum calculations
+ */
+ OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]);
+
+ tmp = INPLL(pllPPLL_CNTL);
+ OUTPLL(pllPPLL_CNTL, tmp & ~0x2);
+ mdelay(5);
+
+ tmp = INPLL(pllPPLL_CNTL);
+ OUTPLL(pllPPLL_CNTL, tmp & ~0x1);
+ mdelay(5);
+
+ tmp = INPLL(pllVCLK_ECP_CNTL);
+ OUTPLL(pllVCLK_ECP_CNTL, tmp | 3);
+ mdelay(5);
+
+ tmp = INPLL(pllVCLK_ECP_CNTL);
+ OUTPLL(pllVCLK_ECP_CNTL, tmp | 3);
+ mdelay(5);
+
+ /* Switch pixel clock to firmware default div 0 */
+ OUTREG8(CLOCK_CNTL_INDEX+1, 0);
+ radeon_pll_errata_after_index(rinfo);
+ radeon_pll_errata_after_data(rinfo);
+}
+
+static void radeon_pm_m10_reconfigure_mc(struct radeonfb_info *rinfo)
+{
+ OUTREG(MC_CNTL, rinfo->save_regs[46]);
+ OUTREG(MC_INIT_GFX_LAT_TIMER, rinfo->save_regs[47]);
+ OUTREG(MC_INIT_MISC_LAT_TIMER, rinfo->save_regs[48]);
+ OUTREG(MEM_SDRAM_MODE_REG,
+ rinfo->save_regs[35] & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+ OUTREG(MC_TIMING_CNTL, rinfo->save_regs[49]);
+ OUTREG(MEM_REFRESH_CNTL, rinfo->save_regs[42]);
+ OUTREG(MC_READ_CNTL_AB, rinfo->save_regs[50]);
+ OUTREG(MC_CHIP_IO_OE_CNTL_AB, rinfo->save_regs[52]);
+ OUTREG(MC_IOPAD_CNTL, rinfo->save_regs[51]);
+ OUTREG(MC_DEBUG, rinfo->save_regs[53]);
+
+ OUTMC(rinfo, ixR300_MC_MC_INIT_WR_LAT_TIMER, rinfo->save_regs[58]);
+ OUTMC(rinfo, ixR300_MC_IMP_CNTL, rinfo->save_regs[59]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_C0, rinfo->save_regs[60]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_C1, rinfo->save_regs[61]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_D0, rinfo->save_regs[62]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_D1, rinfo->save_regs[63]);
+ OUTMC(rinfo, ixR300_MC_BIST_CNTL_3, rinfo->save_regs[64]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_A0, rinfo->save_regs[65]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_A1, rinfo->save_regs[66]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_B0, rinfo->save_regs[67]);
+ OUTMC(rinfo, ixR300_MC_CHP_IO_CNTL_B1, rinfo->save_regs[68]);
+ OUTMC(rinfo, ixR300_MC_DEBUG_CNTL, rinfo->save_regs[69]);
+ OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]);
+ OUTMC(rinfo, ixR300_MC_IMP_CNTL_0, rinfo->save_regs[71]);
+ OUTMC(rinfo, ixR300_MC_ELPIDA_CNTL, rinfo->save_regs[72]);
+ OUTMC(rinfo, ixR300_MC_READ_CNTL_CD, rinfo->save_regs[96]);
+ OUTREG(MC_IND_INDEX, 0);
+}
+
+static void radeon_reinitialize_M10(struct radeonfb_info *rinfo)
+{
+ u32 tmp, i;
+
+ /* Restore a bunch of registers first */
+ OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+ OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+ OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+ OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+ OUTREG(OV0_BASE_ADDR, rinfo->save_regs[80]);
+ OUTREG(CNFG_MEMSIZE, rinfo->video_ram);
+ OUTREG(BUS_CNTL, rinfo->save_regs[36]);
+ OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
+ OUTREG(MPP_TB_CONFIG, rinfo->save_regs[37]);
+ OUTREG(FCP_CNTL, rinfo->save_regs[38]);
+ OUTREG(RBBM_CNTL, rinfo->save_regs[39]);
+ OUTREG(DAC_CNTL, rinfo->save_regs[40]);
+ OUTREG(DAC_MACRO_CNTL, (INREG(DAC_MACRO_CNTL) & ~0x6) | 8);
+ OUTREG(DAC_MACRO_CNTL, (INREG(DAC_MACRO_CNTL) & ~0x6) | 8);
+
+ /* Hrm... */
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | DAC2_EXPAND_MODE);
+
+ /* Reset the PAD CTLR */
+ radeon_pm_reset_pad_ctlr_strength(rinfo);
+
+ /* Some PLLs are Read & written identically in the trace here...
+ * I suppose it's actually to switch them all off & reset,
+ * let's assume off is what we want. I'm just doing that for all major PLLs now.
+ */
+ radeon_pm_all_ppls_off(rinfo);
+
+ /* Clear tiling, reset swappers */
+ INREG(SURFACE_CNTL);
+ OUTREG(SURFACE_CNTL, 0);
+
+ /* Some black magic with TV_DAC_CNTL, we should restore those from backups
+ * rather than hard coding...
+ */
+ tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_BGADJ_MASK;
+ tmp |= 8 << TV_DAC_CNTL_BGADJ__SHIFT;
+ OUTREG(TV_DAC_CNTL, tmp);
+
+ tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_DACADJ_MASK;
+ tmp |= 7 << TV_DAC_CNTL_DACADJ__SHIFT;
+ OUTREG(TV_DAC_CNTL, tmp);
+
+ /* More registers restored */
+ OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+ OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]);
+ OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+
+ /* Hrmmm ... What is that ? */
+ tmp = rinfo->save_regs[1]
+ & ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK |
+ CLK_PWRMGT_CNTL__MC_BUSY);
+ OUTPLL(pllCLK_PWRMGT_CNTL, tmp);
+
+ OUTREG(PAD_CTLR_MISC, rinfo->save_regs[56]);
+ OUTREG(FW_CNTL, rinfo->save_regs[57]);
+ OUTREG(HDP_DEBUG, rinfo->save_regs[96]);
+ OUTREG(PAMAC0_DLY_CNTL, rinfo->save_regs[54]);
+ OUTREG(PAMAC1_DLY_CNTL, rinfo->save_regs[55]);
+ OUTREG(PAMAC2_DLY_CNTL, rinfo->save_regs[79]);
+
+ /* Restore Memory Controller configuration */
+ radeon_pm_m10_reconfigure_mc(rinfo);
+
+ /* Make sure CRTC's dont touch memory */
+ OUTREG(CRTC_GEN_CNTL, INREG(CRTC_GEN_CNTL)
+ | CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B);
+ OUTREG(CRTC2_GEN_CNTL, INREG(CRTC2_GEN_CNTL)
+ | CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B);
+ mdelay(30);
+
+ /* Disable SDRAM refresh */
+ OUTREG(MEM_REFRESH_CNTL, INREG(MEM_REFRESH_CNTL)
+ | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+ /* Restore XTALIN routing (CLK_PIN_CNTL) */
+ OUTPLL(pllCLK_PIN_CNTL, rinfo->save_regs[4]);
+
+ /* Switch MCLK, YCLK and SCLK PLLs to PCI source & force them ON */
+ tmp = rinfo->save_regs[2] & 0xff000000;
+ tmp |= MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_MCLKB |
+ MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB |
+ MCLK_CNTL__FORCE_MC;
+ OUTPLL(pllMCLK_CNTL, tmp);
+
+ /* Force all clocks on in SCLK */
+ tmp = INPLL(pllSCLK_CNTL);
+ tmp |= SCLK_CNTL__FORCE_DISP2|
+ SCLK_CNTL__FORCE_CP|
+ SCLK_CNTL__FORCE_HDP|
+ SCLK_CNTL__FORCE_DISP1|
+ SCLK_CNTL__FORCE_TOP|
+ SCLK_CNTL__FORCE_E2|
+ SCLK_CNTL__FORCE_SE|
+ SCLK_CNTL__FORCE_IDCT|
+ SCLK_CNTL__FORCE_VIP|
+ SCLK_CNTL__FORCE_PB|
+ SCLK_CNTL__FORCE_TAM|
+ SCLK_CNTL__FORCE_TDM|
+ SCLK_CNTL__FORCE_RB|
+ SCLK_CNTL__FORCE_TV_SCLK|
+ SCLK_CNTL__FORCE_SUBPIC|
+ SCLK_CNTL__FORCE_OV0;
+ tmp |= SCLK_CNTL__CP_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__HDP_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__TV_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__E2_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__SE_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__IDCT_MAX_DYN_STOP_LAT|
+ SCLK_CNTL__VIP_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__RE_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__PB_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__TAM_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__TDM_MAX_DYN_STOP_LAT |
+ SCLK_CNTL__RB_MAX_DYN_STOP_LAT;
+ OUTPLL(pllSCLK_CNTL, tmp);
+
+ OUTPLL(pllVCLK_ECP_CNTL, 0);
+ OUTPLL(pllPIXCLKS_CNTL, 0);
+ OUTPLL(pllMCLK_MISC,
+ MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT |
+ MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT);
+
+ mdelay(5);
+
+ /* Restore the M_SPLL_REF_FB_DIV, MPLL_AUX_CNTL and SPLL_AUX_CNTL values */
+ OUTPLL(pllM_SPLL_REF_FB_DIV, rinfo->save_regs[77]);
+ OUTPLL(pllMPLL_AUX_CNTL, rinfo->save_regs[75]);
+ OUTPLL(pllSPLL_AUX_CNTL, rinfo->save_regs[76]);
+
+ /* Now restore the major PLLs settings, keeping them off & reset though */
+ OUTPLL(pllPPLL_CNTL, rinfo->save_regs[93] | 0x3);
+ OUTPLL(pllP2PLL_CNTL, rinfo->save_regs[8] | 0x3);
+ OUTPLL(pllMPLL_CNTL, rinfo->save_regs[73] | 0x03);
+ OUTPLL(pllSPLL_CNTL, rinfo->save_regs[74] | 0x03);
+
+ /* Restore MC DLL state and switch it off/reset too */
+ OUTMC(rinfo, ixR300_MC_DLL_CNTL, rinfo->save_regs[70]);
+
+ /* Switch MDLL off & reset */
+ OUTPLL(pllMDLL_RDCKA, rinfo->save_regs[98] | 0xff);
+ mdelay(5);
+
+ /* Setup some black magic bits in PLL_PWRMGT_CNTL. Hrm... we saved
+ * 0xa1100007... and MacOS writes 0xa1000007 ..
+ */
+ OUTPLL(pllPLL_PWRMGT_CNTL, rinfo->save_regs[0]);
+
+ /* Restore more stuffs */
+ OUTPLL(pllHTOTAL_CNTL, 0);
+ OUTPLL(pllHTOTAL2_CNTL, 0);
+
+ /* More PLL initial configuration */
+ tmp = INPLL(pllSCLK_CNTL2); /* What for ? */
+ OUTPLL(pllSCLK_CNTL2, tmp);
+
+ tmp = INPLL(pllSCLK_MORE_CNTL);
+ tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS | /* a guess */
+ SCLK_MORE_CNTL__FORCE_MC_GUI |
+ SCLK_MORE_CNTL__FORCE_MC_HOST;
+ OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+ /* Now we actually start MCLK and SCLK */
+ radeon_pm_start_mclk_sclk(rinfo);
+
+ /* Full reset sdrams, this also re-inits the MDLL */
+ radeon_pm_full_reset_sdram(rinfo);
+
+ /* Fill palettes */
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x20);
+ for (i=0; i<256; i++)
+ OUTREG(PALETTE_30_DATA, 0x15555555);
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~20);
+ udelay(20);
+ for (i=0; i<256; i++)
+ OUTREG(PALETTE_30_DATA, 0x15555555);
+
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~0x20);
+ mdelay(3);
+
+ /* Restore TMDS */
+ OUTREG(FP_GEN_CNTL, rinfo->save_regs[82]);
+ OUTREG(FP2_GEN_CNTL, rinfo->save_regs[83]);
+
+ /* Set LVDS registers but keep interface & pll down */
+ OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11] &
+ ~(LVDS_EN | LVDS_ON | LVDS_DIGON | LVDS_BLON | LVDS_BL_MOD_EN));
+ OUTREG(LVDS_PLL_CNTL, (rinfo->save_regs[12] & ~0xf0000) | 0x20000);
+
+ OUTREG(DISP_OUTPUT_CNTL, rinfo->save_regs[86]);
+
+ /* Restore GPIOPAD state */
+ OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
+ OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
+ OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
+
+ /* write some stuff to the framebuffer... */
+ for (i = 0; i < 0x8000; ++i)
+ writeb(0, rinfo->fb_base + i);
+
+ mdelay(40);
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_DIGON | LVDS_ON);
+ mdelay(40);
+
+ /* Restore a few more things */
+ OUTREG(GRPH_BUFFER_CNTL, rinfo->save_regs[94]);
+ OUTREG(GRPH2_BUFFER_CNTL, rinfo->save_regs[95]);
+
+ /* Take care of spread spectrum & PPLLs now */
+ radeon_pm_m10_disable_spread_spectrum(rinfo);
+ radeon_pm_restore_pixel_pll(rinfo);
+
+ /* GRRRR... I can't figure out the proper LVDS power sequence, and the
+ * code I have for blank/unblank doesn't quite work on some laptop models
+ * it seems ... Hrm. What I have here works most of the time ...
+ */
+ radeon_pm_m10_enable_lvds_spread_spectrum(rinfo);
+}
+#endif
+
+#ifdef CONFIG_PPC
+#ifdef CONFIG_PPC_PMAC
+static void radeon_pm_m9p_reconfigure_mc(struct radeonfb_info *rinfo)
+{
+ OUTREG(MC_CNTL, rinfo->save_regs[46]);
+ OUTREG(MC_INIT_GFX_LAT_TIMER, rinfo->save_regs[47]);
+ OUTREG(MC_INIT_MISC_LAT_TIMER, rinfo->save_regs[48]);
+ OUTREG(MEM_SDRAM_MODE_REG,
+ rinfo->save_regs[35] & ~MEM_SDRAM_MODE_REG__MC_INIT_COMPLETE);
+ OUTREG(MC_TIMING_CNTL, rinfo->save_regs[49]);
+ OUTREG(MC_READ_CNTL_AB, rinfo->save_regs[50]);
+ OUTREG(MEM_REFRESH_CNTL, rinfo->save_regs[42]);
+ OUTREG(MC_IOPAD_CNTL, rinfo->save_regs[51]);
+ OUTREG(MC_DEBUG, rinfo->save_regs[53]);
+ OUTREG(MC_CHIP_IO_OE_CNTL_AB, rinfo->save_regs[52]);
+
+ OUTMC(rinfo, ixMC_IMP_CNTL, rinfo->save_regs[59] /*0x00f460d6*/);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_A0, rinfo->save_regs[65] /*0xfecfa666*/);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, rinfo->save_regs[66] /*0x141555ff*/);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_B0, rinfo->save_regs[67] /*0xfecfa666*/);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, rinfo->save_regs[68] /*0x141555ff*/);
+ OUTMC(rinfo, ixMC_IMP_CNTL_0, rinfo->save_regs[71] /*0x00009249*/);
+ OUTREG(MC_IND_INDEX, 0);
+ OUTREG(CNFG_MEMSIZE, rinfo->video_ram);
+
+ mdelay(20);
+}
+
+static void radeon_reinitialize_M9P(struct radeonfb_info *rinfo)
+{
+ u32 tmp, i;
+
+ /* Restore a bunch of registers first */
+ OUTREG(SURFACE_CNTL, rinfo->save_regs[29]);
+ OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+ OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+ OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+ OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+ OUTREG(OV0_BASE_ADDR, rinfo->save_regs[80]);
+ OUTREG(BUS_CNTL, rinfo->save_regs[36]);
+ OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
+ OUTREG(MPP_TB_CONFIG, rinfo->save_regs[37]);
+ OUTREG(FCP_CNTL, rinfo->save_regs[38]);
+ OUTREG(RBBM_CNTL, rinfo->save_regs[39]);
+
+ OUTREG(DAC_CNTL, rinfo->save_regs[40]);
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | DAC2_EXPAND_MODE);
+
+ /* Reset the PAD CTLR */
+ radeon_pm_reset_pad_ctlr_strength(rinfo);
+
+ /* Some PLLs are Read & written identically in the trace here...
+ * I suppose it's actually to switch them all off & reset,
+ * let's assume off is what we want. I'm just doing that for all major PLLs now.
+ */
+ radeon_pm_all_ppls_off(rinfo);
+
+ /* Clear tiling, reset swappers */
+ INREG(SURFACE_CNTL);
+ OUTREG(SURFACE_CNTL, 0);
+
+ /* Some black magic with TV_DAC_CNTL, we should restore those from backups
+ * rather than hard coding...
+ */
+ tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_BGADJ_MASK;
+ tmp |= 6 << TV_DAC_CNTL_BGADJ__SHIFT;
+ OUTREG(TV_DAC_CNTL, tmp);
+
+ tmp = INREG(TV_DAC_CNTL) & ~TV_DAC_CNTL_DACADJ_MASK;
+ tmp |= 6 << TV_DAC_CNTL_DACADJ__SHIFT;
+ OUTREG(TV_DAC_CNTL, tmp);
+
+ OUTPLL(pllAGP_PLL_CNTL, rinfo->save_regs[78]);
+
+ OUTREG(PAMAC0_DLY_CNTL, rinfo->save_regs[54]);
+ OUTREG(PAMAC1_DLY_CNTL, rinfo->save_regs[55]);
+ OUTREG(PAMAC2_DLY_CNTL, rinfo->save_regs[79]);
+
+ OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+ OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]); /* MacOS sets that to 0 !!! */
+ OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+
+ tmp = rinfo->save_regs[1]
+ & ~(CLK_PWRMGT_CNTL__ACTIVE_HILO_LAT_MASK |
+ CLK_PWRMGT_CNTL__MC_BUSY);
+ OUTPLL(pllCLK_PWRMGT_CNTL, tmp);
+
+ OUTREG(FW_CNTL, rinfo->save_regs[57]);
+
+ /* Disable SDRAM refresh */
+ OUTREG(MEM_REFRESH_CNTL, INREG(MEM_REFRESH_CNTL)
+ | MEM_REFRESH_CNTL__MEM_REFRESH_DIS);
+
+ /* Restore XTALIN routing (CLK_PIN_CNTL) */
+ OUTPLL(pllCLK_PIN_CNTL, rinfo->save_regs[4]);
+
+ /* Force MCLK to be PCI sourced and forced ON */
+ tmp = rinfo->save_regs[2] & 0xff000000;
+ tmp |= MCLK_CNTL__FORCE_MCLKA |
+ MCLK_CNTL__FORCE_MCLKB |
+ MCLK_CNTL__FORCE_YCLKA |
+ MCLK_CNTL__FORCE_YCLKB |
+ MCLK_CNTL__FORCE_MC |
+ MCLK_CNTL__FORCE_AIC;
+ OUTPLL(pllMCLK_CNTL, tmp);
+
+ /* Force SCLK to be PCI sourced with a bunch forced */
+ tmp = 0 |
+ SCLK_CNTL__FORCE_DISP2|
+ SCLK_CNTL__FORCE_CP|
+ SCLK_CNTL__FORCE_HDP|
+ SCLK_CNTL__FORCE_DISP1|
+ SCLK_CNTL__FORCE_TOP|
+ SCLK_CNTL__FORCE_E2|
+ SCLK_CNTL__FORCE_SE|
+ SCLK_CNTL__FORCE_IDCT|
+ SCLK_CNTL__FORCE_VIP|
+ SCLK_CNTL__FORCE_RE|
+ SCLK_CNTL__FORCE_PB|
+ SCLK_CNTL__FORCE_TAM|
+ SCLK_CNTL__FORCE_TDM|
+ SCLK_CNTL__FORCE_RB;
+ OUTPLL(pllSCLK_CNTL, tmp);
+
+ /* Clear VCLK_ECP_CNTL & PIXCLKS_CNTL */
+ OUTPLL(pllVCLK_ECP_CNTL, 0);
+ OUTPLL(pllPIXCLKS_CNTL, 0);
+
+ /* Setup MCLK_MISC, non dynamic mode */
+ OUTPLL(pllMCLK_MISC,
+ MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT |
+ MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT);
+
+ mdelay(5);
+
+ /* Set back the default clock dividers */
+ OUTPLL(pllM_SPLL_REF_FB_DIV, rinfo->save_regs[77]);
+ OUTPLL(pllMPLL_AUX_CNTL, rinfo->save_regs[75]);
+ OUTPLL(pllSPLL_AUX_CNTL, rinfo->save_regs[76]);
+
+ /* PPLL and P2PLL default values & off */
+ OUTPLL(pllPPLL_CNTL, rinfo->save_regs[93] | 0x3);
+ OUTPLL(pllP2PLL_CNTL, rinfo->save_regs[8] | 0x3);
+
+ /* S and M PLLs are reset & off, configure them */
+ OUTPLL(pllMPLL_CNTL, rinfo->save_regs[73] | 0x03);
+ OUTPLL(pllSPLL_CNTL, rinfo->save_regs[74] | 0x03);
+
+ /* Default values for MDLL ... fixme */
+ OUTPLL(pllMDLL_CKO, 0x9c009c);
+ OUTPLL(pllMDLL_RDCKA, 0x08830883);
+ OUTPLL(pllMDLL_RDCKB, 0x08830883);
+ mdelay(5);
+
+ /* Restore PLL_PWRMGT_CNTL */ // XXXX
+ tmp = rinfo->save_regs[0];
+ tmp &= ~PLL_PWRMGT_CNTL_SU_SCLK_USE_BCLK;
+ tmp |= PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK;
+ OUTPLL(PLL_PWRMGT_CNTL, tmp);
+
+ /* Clear HTOTAL_CNTL & HTOTAL2_CNTL */
+ OUTPLL(pllHTOTAL_CNTL, 0);
+ OUTPLL(pllHTOTAL2_CNTL, 0);
+
+ /* All outputs off */
+ OUTREG(CRTC_GEN_CNTL, 0x04000000);
+ OUTREG(CRTC2_GEN_CNTL, 0x04000000);
+ OUTREG(FP_GEN_CNTL, 0x00004008);
+ OUTREG(FP2_GEN_CNTL, 0x00000008);
+ OUTREG(LVDS_GEN_CNTL, 0x08000008);
+
+ /* Restore Memory Controller configuration */
+ radeon_pm_m9p_reconfigure_mc(rinfo);
+
+ /* Now we actually start MCLK and SCLK */
+ radeon_pm_start_mclk_sclk(rinfo);
+
+ /* Full reset sdrams, this also re-inits the MDLL */
+ radeon_pm_full_reset_sdram(rinfo);
+
+ /* Fill palettes */
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x20);
+ for (i=0; i<256; i++)
+ OUTREG(PALETTE_30_DATA, 0x15555555);
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~20);
+ udelay(20);
+ for (i=0; i<256; i++)
+ OUTREG(PALETTE_30_DATA, 0x15555555);
+
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) & ~0x20);
+ mdelay(3);
+
+ /* Restore TV stuff, make sure TV DAC is down */
+ OUTREG(TV_MASTER_CNTL, rinfo->save_regs[88]);
+ OUTREG(TV_DAC_CNTL, rinfo->save_regs[13] | 0x07000000);
+
+ /* Restore GPIOS. MacOS does some magic here with one of the GPIO bits,
+ * possibly related to the weird PLL related workarounds and to the
+ * fact that CLK_PIN_CNTL is tweaked in ways I don't fully understand,
+ * but we keep things the simple way here
+ */
+ OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
+ OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
+ OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
+
+ /* Now do things with SCLK_MORE_CNTL. Force bits are already set, copy
+ * high bits from backup
+ */
+ tmp = INPLL(pllSCLK_MORE_CNTL) & 0x0000ffff;
+ tmp |= rinfo->save_regs[34] & 0xffff0000;
+ tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS;
+ OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+ tmp = INPLL(pllSCLK_MORE_CNTL) & 0x0000ffff;
+ tmp |= rinfo->save_regs[34] & 0xffff0000;
+ tmp |= SCLK_MORE_CNTL__FORCE_DISPREGS;
+ OUTPLL(pllSCLK_MORE_CNTL, tmp);
+
+ OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11] &
+ ~(LVDS_EN | LVDS_ON | LVDS_DIGON | LVDS_BLON | LVDS_BL_MOD_EN));
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_BLON);
+ OUTREG(LVDS_PLL_CNTL, (rinfo->save_regs[12] & ~0xf0000) | 0x20000);
+ mdelay(20);
+
+ /* write some stuff to the framebuffer... */
+ for (i = 0; i < 0x8000; ++i)
+ writeb(0, rinfo->fb_base + i);
+
+ OUTREG(0x2ec, 0x6332a020);
+ OUTPLL(pllSSPLL_REF_DIV, rinfo->save_regs[44] /*0x3f */);
+ OUTPLL(pllSSPLL_DIV_0, rinfo->save_regs[45] /*0x000081bb */);
+ tmp = INPLL(pllSSPLL_CNTL);
+ tmp &= ~2;
+ OUTPLL(pllSSPLL_CNTL, tmp);
+ mdelay(6);
+ tmp &= ~1;
+ OUTPLL(pllSSPLL_CNTL, tmp);
+ mdelay(5);
+ tmp |= 3;
+ OUTPLL(pllSSPLL_CNTL, tmp);
+ mdelay(5);
+
+ OUTPLL(pllSS_INT_CNTL, rinfo->save_regs[90] & ~3);/*0x0020300c*/
+ OUTREG(0x2ec, 0x6332a3f0);
+ mdelay(17);
+
+ OUTPLL(pllPPLL_REF_DIV, rinfo->pll.ref_div);
+ OUTPLL(pllPPLL_DIV_0, rinfo->save_regs[92]);
+
+ mdelay(40);
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) | LVDS_DIGON | LVDS_ON);
+ mdelay(40);
+
+ /* Restore a few more things */
+ OUTREG(GRPH_BUFFER_CNTL, rinfo->save_regs[94]);
+ OUTREG(GRPH2_BUFFER_CNTL, rinfo->save_regs[95]);
+
+ /* Restore PPLL, spread spectrum & LVDS */
+ radeon_pm_m10_disable_spread_spectrum(rinfo);
+ radeon_pm_restore_pixel_pll(rinfo);
+ radeon_pm_m10_enable_lvds_spread_spectrum(rinfo);
+}
+#endif
+#endif
+
+#if 0 /* Not ready yet */
+static void radeon_reinitialize_QW(struct radeonfb_info *rinfo)
+{
+ int i;
+ u32 tmp, tmp2;
+ u32 cko, cka, ckb;
+ u32 cgc, cec, c2gc;
+
+ OUTREG(MC_AGP_LOCATION, rinfo->save_regs[32]);
+ OUTREG(DISPLAY_BASE_ADDR, rinfo->save_regs[31]);
+ OUTREG(CRTC2_DISPLAY_BASE_ADDR, rinfo->save_regs[33]);
+ OUTREG(MC_FB_LOCATION, rinfo->save_regs[30]);
+ OUTREG(BUS_CNTL, rinfo->save_regs[36]);
+ OUTREG(RBBM_CNTL, rinfo->save_regs[39]);
+
+ INREG(PAD_CTLR_STRENGTH);
+ OUTREG(PAD_CTLR_STRENGTH, INREG(PAD_CTLR_STRENGTH) & ~0x10000);
+ for (i = 0; i < 65; ++i) {
+ mdelay(1);
+ INREG(PAD_CTLR_STRENGTH);
+ }
+
+ OUTREG(DISP_TEST_DEBUG_CNTL, INREG(DISP_TEST_DEBUG_CNTL) | 0x10000000);
+ OUTREG(OV0_FLAG_CNTRL, INREG(OV0_FLAG_CNTRL) | 0x100);
+ OUTREG(CRTC_GEN_CNTL, INREG(CRTC_GEN_CNTL));
+ OUTREG(DAC_CNTL, 0xff00410a);
+ OUTREG(CRTC2_GEN_CNTL, INREG(CRTC2_GEN_CNTL));
+ OUTREG(DAC_CNTL2, INREG(DAC_CNTL2) | 0x4000);
+
+ OUTREG(SURFACE_CNTL, rinfo->save_regs[29]);
+ OUTREG(AGP_CNTL, rinfo->save_regs[16]);
+ OUTREG(HOST_PATH_CNTL, rinfo->save_regs[41]);
+ OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
+
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_A0, 0xf7bb4433);
+ OUTREG(MC_IND_INDEX, 0);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_B0, 0xf7bb4433);
+ OUTREG(MC_IND_INDEX, 0);
+
+ OUTREG(CRTC_MORE_CNTL, INREG(CRTC_MORE_CNTL));
+
+ tmp = INPLL(pllVCLK_ECP_CNTL);
+ OUTPLL(pllVCLK_ECP_CNTL, tmp);
+ tmp = INPLL(pllPIXCLKS_CNTL);
+ OUTPLL(pllPIXCLKS_CNTL, tmp);
+
+ OUTPLL(MCLK_CNTL, 0xaa3f0000);
+ OUTPLL(SCLK_CNTL, 0xffff0000);
+ OUTPLL(pllMPLL_AUX_CNTL, 6);
+ OUTPLL(pllSPLL_AUX_CNTL, 1);
+ OUTPLL(MDLL_CKO, 0x9f009f);
+ OUTPLL(MDLL_RDCKA, 0x830083);
+ OUTPLL(pllMDLL_RDCKB, 0x830083);
+ OUTPLL(PPLL_CNTL, 0xa433);
+ OUTPLL(P2PLL_CNTL, 0xa433);
+ OUTPLL(MPLL_CNTL, 0x0400a403);
+ OUTPLL(SPLL_CNTL, 0x0400a433);
+
+ tmp = INPLL(M_SPLL_REF_FB_DIV);
+ OUTPLL(M_SPLL_REF_FB_DIV, tmp);
+ tmp = INPLL(M_SPLL_REF_FB_DIV);
+ OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0xc);
+ INPLL(M_SPLL_REF_FB_DIV);
+
+ tmp = INPLL(MPLL_CNTL);
+ OUTREG8(CLOCK_CNTL_INDEX, MPLL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+ radeon_pll_errata_after_data(rinfo);
+
+ tmp = INPLL(M_SPLL_REF_FB_DIV);
+ OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x5900);
+
+ tmp = INPLL(MPLL_CNTL);
+ OUTPLL(MPLL_CNTL, tmp & ~0x2);
+ mdelay(1);
+ tmp = INPLL(MPLL_CNTL);
+ OUTPLL(MPLL_CNTL, tmp & ~0x1);
+ mdelay(10);
+
+ OUTPLL(MCLK_CNTL, 0xaa3f1212);
+ mdelay(1);
+
+ INPLL(M_SPLL_REF_FB_DIV);
+ INPLL(MCLK_CNTL);
+ INPLL(M_SPLL_REF_FB_DIV);
+
+ tmp = INPLL(SPLL_CNTL);
+ OUTREG8(CLOCK_CNTL_INDEX, SPLL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA + 1, (tmp >> 8) & 0xff);
+ radeon_pll_errata_after_data(rinfo);
+
+ tmp = INPLL(M_SPLL_REF_FB_DIV);
+ OUTPLL(M_SPLL_REF_FB_DIV, tmp | 0x780000);
+
+ tmp = INPLL(SPLL_CNTL);
+ OUTPLL(SPLL_CNTL, tmp & ~0x1);
+ mdelay(1);
+ tmp = INPLL(SPLL_CNTL);
+ OUTPLL(SPLL_CNTL, tmp & ~0x2);
+ mdelay(10);
+
+ tmp = INPLL(SCLK_CNTL);
+ OUTPLL(SCLK_CNTL, tmp | 2);
+ mdelay(1);
+
+ cko = INPLL(pllMDLL_CKO);
+ cka = INPLL(pllMDLL_RDCKA);
+ ckb = INPLL(pllMDLL_RDCKB);
+
+ cko &= ~(MDLL_CKO__MCKOA_SLEEP | MDLL_CKO__MCKOB_SLEEP);
+ OUTPLL(pllMDLL_CKO, cko);
+ mdelay(1);
+ cko &= ~(MDLL_CKO__MCKOA_RESET | MDLL_CKO__MCKOB_RESET);
+ OUTPLL(pllMDLL_CKO, cko);
+ mdelay(5);
+
+ cka &= ~(MDLL_RDCKA__MRDCKA0_SLEEP | MDLL_RDCKA__MRDCKA1_SLEEP);
+ OUTPLL(pllMDLL_RDCKA, cka);
+ mdelay(1);
+ cka &= ~(MDLL_RDCKA__MRDCKA0_RESET | MDLL_RDCKA__MRDCKA1_RESET);
+ OUTPLL(pllMDLL_RDCKA, cka);
+ mdelay(5);
+
+ ckb &= ~(MDLL_RDCKB__MRDCKB0_SLEEP | MDLL_RDCKB__MRDCKB1_SLEEP);
+ OUTPLL(pllMDLL_RDCKB, ckb);
+ mdelay(1);
+ ckb &= ~(MDLL_RDCKB__MRDCKB0_RESET | MDLL_RDCKB__MRDCKB1_RESET);
+ OUTPLL(pllMDLL_RDCKB, ckb);
+ mdelay(5);
+
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, 0x151550ff);
+ OUTREG(MC_IND_INDEX, 0);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, 0x151550ff);
+ OUTREG(MC_IND_INDEX, 0);
+ mdelay(1);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_A1, 0x141550ff);
+ OUTREG(MC_IND_INDEX, 0);
+ OUTMC(rinfo, ixMC_CHP_IO_CNTL_B1, 0x141550ff);
+ OUTREG(MC_IND_INDEX, 0);
+ mdelay(1);
+
+ OUTPLL(pllHTOTAL_CNTL, 0);
+ OUTPLL(pllHTOTAL2_CNTL, 0);
+
+ OUTREG(MEM_CNTL, 0x29002901);
+ OUTREG(MEM_SDRAM_MODE_REG, 0x45320032); /* XXX use save_regs[35]? */
+ OUTREG(EXT_MEM_CNTL, 0x1a394333);
+ OUTREG(MEM_IO_CNTL_A1, 0x0aac0aac);
+ OUTREG(MEM_INIT_LATENCY_TIMER, 0x34444444);
+ OUTREG(MEM_REFRESH_CNTL, 0x1f1f7218); /* XXX or save_regs[42]? */
+ OUTREG(MC_DEBUG, 0);
+ OUTREG(MEM_IO_OE_CNTL, 0x04300430);
+
+ OUTMC(rinfo, ixMC_IMP_CNTL, 0x00f460d6);
+ OUTREG(MC_IND_INDEX, 0);
+ OUTMC(rinfo, ixMC_IMP_CNTL_0, 0x00009249);
+ OUTREG(MC_IND_INDEX, 0);
+
+ OUTREG(CNFG_MEMSIZE, rinfo->video_ram);
+
+ radeon_pm_full_reset_sdram(rinfo);
+
+ INREG(FP_GEN_CNTL);
+ OUTREG(TMDS_CNTL, 0x01000000); /* XXX ? */
+ tmp = INREG(FP_GEN_CNTL);
+ tmp |= FP_CRTC_DONT_SHADOW_HEND | FP_CRTC_DONT_SHADOW_VPAR | 0x200;
+ OUTREG(FP_GEN_CNTL, tmp);
+
+ tmp = INREG(DISP_OUTPUT_CNTL);
+ tmp &= ~0x400;
+ OUTREG(DISP_OUTPUT_CNTL, tmp);
+
+ OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]);
+ OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]);
+ OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]);
+
+ tmp = INPLL(MCLK_MISC);
+ tmp |= MCLK_MISC__MC_MCLK_DYN_ENABLE | MCLK_MISC__IO_MCLK_DYN_ENABLE;
+ OUTPLL(MCLK_MISC, tmp);
+
+ tmp = INPLL(SCLK_CNTL);
+ OUTPLL(SCLK_CNTL, tmp);
+
+ OUTREG(CRTC_MORE_CNTL, 0);
+ OUTREG8(CRTC_GEN_CNTL+1, 6);
+ OUTREG8(CRTC_GEN_CNTL+3, 1);
+ OUTREG(CRTC_PITCH, 32);
+
+ tmp = INPLL(VCLK_ECP_CNTL);
+ OUTPLL(VCLK_ECP_CNTL, tmp);
+
+ tmp = INPLL(PPLL_CNTL);
+ OUTPLL(PPLL_CNTL, tmp);
+
+ /* palette stuff and BIOS_1_SCRATCH... */
+
+ tmp = INREG(FP_GEN_CNTL);
+ tmp2 = INREG(TMDS_TRANSMITTER_CNTL);
+ tmp |= 2;
+ OUTREG(FP_GEN_CNTL, tmp);
+ mdelay(5);
+ OUTREG(FP_GEN_CNTL, tmp);
+ mdelay(5);
+ OUTREG(TMDS_TRANSMITTER_CNTL, tmp2);
+ OUTREG(CRTC_MORE_CNTL, 0);
+ mdelay(20);
+
+ tmp = INREG(CRTC_MORE_CNTL);
+ OUTREG(CRTC_MORE_CNTL, tmp);
+
+ cgc = INREG(CRTC_GEN_CNTL);
+ cec = INREG(CRTC_EXT_CNTL);
+ c2gc = INREG(CRTC2_GEN_CNTL);
+
+ OUTREG(CRTC_H_SYNC_STRT_WID, 0x008e0580);
+ OUTREG(CRTC_H_TOTAL_DISP, 0x009f00d2);
+ OUTREG8(CLOCK_CNTL_INDEX, HTOTAL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA, 0);
+ radeon_pll_errata_after_data(rinfo);
+ OUTREG(CRTC_V_SYNC_STRT_WID, 0x00830403);
+ OUTREG(CRTC_V_TOTAL_DISP, 0x03ff0429);
+ OUTREG(FP_CRTC_H_TOTAL_DISP, 0x009f0033);
+ OUTREG(FP_H_SYNC_STRT_WID, 0x008e0080);
+ OUTREG(CRT_CRTC_H_SYNC_STRT_WID, 0x008e0080);
+ OUTREG(FP_CRTC_V_TOTAL_DISP, 0x03ff002a);
+ OUTREG(FP_V_SYNC_STRT_WID, 0x00830004);
+ OUTREG(CRT_CRTC_V_SYNC_STRT_WID, 0x00830004);
+ OUTREG(FP_HORZ_VERT_ACTIVE, 0x009f03ff);
+ OUTREG(FP_HORZ_STRETCH, 0);
+ OUTREG(FP_VERT_STRETCH, 0);
+ OUTREG(OVR_CLR, 0);
+ OUTREG(OVR_WID_LEFT_RIGHT, 0);
+ OUTREG(OVR_WID_TOP_BOTTOM, 0);
+
+ tmp = INPLL(PPLL_REF_DIV);
+ tmp = (tmp & ~PPLL_REF_DIV_MASK) | rinfo->pll.ref_div;
+ OUTPLL(PPLL_REF_DIV, tmp);
+ INPLL(PPLL_REF_DIV);
+
+ OUTREG8(CLOCK_CNTL_INDEX, PPLL_CNTL + PLL_WR_EN);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG8(CLOCK_CNTL_DATA + 1, 0xbc);
+ radeon_pll_errata_after_data(rinfo);
+
+ tmp = INREG(CLOCK_CNTL_INDEX);
+ radeon_pll_errata_after_index(rinfo);
+ OUTREG(CLOCK_CNTL_INDEX, tmp & 0xff);
+ radeon_pll_errata_after_index(rinfo);
+ radeon_pll_errata_after_data(rinfo);
+
+ OUTPLL(PPLL_DIV_0, 0x48090);
+
+ tmp = INPLL(PPLL_CNTL);
+ OUTPLL(PPLL_CNTL, tmp & ~0x2);
+ mdelay(1);
+ tmp = INPLL(PPLL_CNTL);
+ OUTPLL(PPLL_CNTL, tmp & ~0x1);
+ mdelay(10);
+
+ tmp = INPLL(VCLK_ECP_CNTL);
+ OUTPLL(VCLK_ECP_CNTL, tmp | 3);
+ mdelay(1);
+
+ tmp = INPLL(VCLK_ECP_CNTL);
+ OUTPLL(VCLK_ECP_CNTL, tmp);
+
+ c2gc |= CRTC2_DISP_REQ_EN_B;
+ OUTREG(CRTC2_GEN_CNTL, c2gc);
+ cgc |= CRTC_EN;
+ OUTREG(CRTC_GEN_CNTL, cgc);
+ OUTREG(CRTC_EXT_CNTL, cec);
+ OUTREG(CRTC_PITCH, 0xa0);
+ OUTREG(CRTC_OFFSET, 0);
+ OUTREG(CRTC_OFFSET_CNTL, 0);
+
+ OUTREG(GRPH_BUFFER_CNTL, 0x20117c7c);
+ OUTREG(GRPH2_BUFFER_CNTL, 0x00205c5c);
+
+ tmp2 = INREG(FP_GEN_CNTL);
+ tmp = INREG(TMDS_TRANSMITTER_CNTL);
+ OUTREG(0x2a8, 0x0000061b);
+ tmp |= TMDS_PLL_EN;
+ OUTREG(TMDS_TRANSMITTER_CNTL, tmp);
+ mdelay(1);
+ tmp &= ~TMDS_PLLRST;
+ OUTREG(TMDS_TRANSMITTER_CNTL, tmp);
+ tmp2 &= ~2;
+ tmp2 |= FP_TMDS_EN;
+ OUTREG(FP_GEN_CNTL, tmp2);
+ mdelay(5);
+ tmp2 |= FP_FPON;
+ OUTREG(FP_GEN_CNTL, tmp2);
+
+ OUTREG(CUR_HORZ_VERT_OFF, CUR_LOCK | 1);
+ cgc = INREG(CRTC_GEN_CNTL);
+ OUTREG(CUR_HORZ_VERT_POSN, 0xbfff0fff);
+ cgc |= 0x10000;
+ OUTREG(CUR_OFFSET, 0);
+}
+#endif /* 0 */
+
+#endif /* CONFIG_PPC */
+
+static void radeonfb_whack_power_state(struct radeonfb_info *rinfo, pci_power_t state)
+{
+ u16 pwr_cmd;
+
+ for (;;) {
+ pci_read_config_word(rinfo->pdev,
+ rinfo->pdev->pm_cap + PCI_PM_CTRL,
+ &pwr_cmd);
+ if (pwr_cmd & state)
+ break;
+ pwr_cmd = (pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | state;
+ pci_write_config_word(rinfo->pdev,
+ rinfo->pdev->pm_cap + PCI_PM_CTRL,
+ pwr_cmd);
+ msleep(500);
+ }
+ rinfo->pdev->current_state = state;
+}
+
+static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
+{
+ u32 tmp;
+
+ if (!rinfo->pdev->pm_cap)
+ return;
+
+ /* Set the chip into appropriate suspend mode (we use D2,
+ * D3 would require a compete re-initialization of the chip,
+ * including PCI config registers, clocks, AGP conf, ...)
+ */
+ if (suspend) {
+ printk(KERN_DEBUG "radeonfb (%s): switching to D2 state...\n",
+ pci_name(rinfo->pdev));
+
+ /* Disable dynamic power management of clocks for the
+ * duration of the suspend/resume process
+ */
+ radeon_pm_disable_dynamic_mode(rinfo);
+
+ /* Save some registers */
+ radeon_pm_save_regs(rinfo, 0);
+
+ /* Prepare mobility chips for suspend.
+ */
+ if (rinfo->is_mobility) {
+ /* Program V2CLK */
+ radeon_pm_program_v2clk(rinfo);
+
+ /* Disable IO PADs */
+ radeon_pm_disable_iopad(rinfo);
+
+ /* Set low current */
+ radeon_pm_low_current(rinfo);
+
+ /* Prepare chip for power management */
+ radeon_pm_setup_for_suspend(rinfo);
+
+ if (rinfo->family <= CHIP_FAMILY_RV280) {
+ /* Reset the MDLL */
+ /* because both INPLL and OUTPLL take the same
+ * lock, that's why. */
+ tmp = INPLL( pllMDLL_CKO) | MDLL_CKO__MCKOA_RESET
+ | MDLL_CKO__MCKOB_RESET;
+ OUTPLL( pllMDLL_CKO, tmp );
+ }
+ }
+
+ /* Switch PCI power management to D2. */
+ pci_disable_device(rinfo->pdev);
+ pci_save_state(rinfo->pdev);
+ /* The chip seems to need us to whack the PM register
+ * repeatedly until it sticks. We do that -prior- to
+ * calling pci_set_power_state()
+ */
+ radeonfb_whack_power_state(rinfo, PCI_D2);
+ __pci_complete_power_transition(rinfo->pdev, PCI_D2);
+ } else {
+ printk(KERN_DEBUG "radeonfb (%s): switching to D0 state...\n",
+ pci_name(rinfo->pdev));
+
+ if (rinfo->family <= CHIP_FAMILY_RV250) {
+ /* Reset the SDRAM controller */
+ radeon_pm_full_reset_sdram(rinfo);
+
+ /* Restore some registers */
+ radeon_pm_restore_regs(rinfo);
+ } else {
+ /* Restore registers first */
+ radeon_pm_restore_regs(rinfo);
+ /* init sdram controller */
+ radeon_pm_full_reset_sdram(rinfo);
+ }
+ }
+}
+
+int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct radeonfb_info *rinfo = info->par;
+
+ if (mesg.event == pdev->dev.power.power_state.event)
+ return 0;
+
+ printk(KERN_DEBUG "radeonfb (%s): suspending for event: %d...\n",
+ pci_name(pdev), mesg.event);
+
+ /* For suspend-to-disk, we cheat here. We don't suspend anything and
+ * let fbcon continue drawing until we are all set. That shouldn't
+ * really cause any problem at this point, provided that the wakeup
+ * code knows that any state in memory may not match the HW
+ */
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE: /* about to take snapshot */
+ case PM_EVENT_PRETHAW: /* before restoring snapshot */
+ goto done;
+ }
+
+ console_lock();
+
+ fb_set_suspend(info, 1);
+
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED)) {
+ /* Make sure engine is reset */
+ radeon_engine_idle();
+ radeonfb_engine_reset(rinfo);
+ radeon_engine_idle();
+ }
+
+ /* Blank display and LCD */
+ radeon_screen_blank(rinfo, FB_BLANK_POWERDOWN, 1);
+
+ /* Sleep */
+ rinfo->asleep = 1;
+ rinfo->lock_blank = 1;
+ del_timer_sync(&rinfo->lvds_timer);
+
+#ifdef CONFIG_PPC_PMAC
+ /* On powermac, we have hooks to properly suspend/resume AGP now,
+ * use them here. We'll ultimately need some generic support here,
+ * but the generic code isn't quite ready for that yet
+ */
+ pmac_suspend_agp_for_card(pdev);
+#endif /* CONFIG_PPC_PMAC */
+
+ /* It's unclear whether or when the generic code will do that, so let's
+ * do it ourselves. We save state before we do any power management
+ */
+ pci_save_state(pdev);
+
+ /* If we support wakeup from poweroff, we save all regs we can including cfg
+ * space
+ */
+ if (rinfo->pm_mode & radeon_pm_off) {
+ /* Always disable dynamic clocks or weird things are happening when
+ * the chip goes off (basically the panel doesn't shut down properly
+ * and we crash on wakeup),
+ * also, we want the saved regs context to have no dynamic clocks in
+ * it, we'll restore the dynamic clocks state on wakeup
+ */
+ radeon_pm_disable_dynamic_mode(rinfo);
+ msleep(50);
+ radeon_pm_save_regs(rinfo, 1);
+
+ if (rinfo->is_mobility && !(rinfo->pm_mode & radeon_pm_d2)) {
+ /* Switch off LVDS interface */
+ usleep_range(1000, 2000);
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_BL_MOD_EN));
+ usleep_range(1000, 2000);
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_EN | LVDS_ON));
+ OUTREG(LVDS_PLL_CNTL, (INREG(LVDS_PLL_CNTL) & ~30000) | 0x20000);
+ msleep(20);
+ OUTREG(LVDS_GEN_CNTL, INREG(LVDS_GEN_CNTL) & ~(LVDS_DIGON));
+ }
+ pci_disable_device(pdev);
+ }
+ /* If we support D2, we go to it (should be fixed later with a flag forcing
+ * D3 only for some laptops)
+ */
+ if (rinfo->pm_mode & radeon_pm_d2)
+ radeon_set_suspend(rinfo, 1);
+
+ console_unlock();
+
+ done:
+ pdev->dev.power.power_state = mesg;
+
+ return 0;
+}
+
+static int radeon_check_power_loss(struct radeonfb_info *rinfo)
+{
+ return rinfo->save_regs[4] != INPLL(CLK_PIN_CNTL) ||
+ rinfo->save_regs[2] != INPLL(MCLK_CNTL) ||
+ rinfo->save_regs[3] != INPLL(SCLK_CNTL);
+}
+
+int radeonfb_pci_resume(struct pci_dev *pdev)
+{
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct radeonfb_info *rinfo = info->par;
+ int rc = 0;
+
+ if (pdev->dev.power.power_state.event == PM_EVENT_ON)
+ return 0;
+
+ if (rinfo->no_schedule) {
+ if (!console_trylock())
+ return 0;
+ } else
+ console_lock();
+
+ printk(KERN_DEBUG "radeonfb (%s): resuming from state: %d...\n",
+ pci_name(pdev), pdev->dev.power.power_state.event);
+
+ /* PCI state will have been restored by the core, so
+ * we should be in D0 now with our config space fully
+ * restored
+ */
+ if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+ /* Wakeup chip */
+ if ((rinfo->pm_mode & radeon_pm_off) && radeon_check_power_loss(rinfo)) {
+ if (rinfo->reinit_func != NULL)
+ rinfo->reinit_func(rinfo);
+ else {
+ printk(KERN_ERR "radeonfb (%s): can't resume radeon from"
+ " D3 cold, need softboot !", pci_name(pdev));
+ rc = -EIO;
+ goto bail;
+ }
+ }
+ /* If we support D2, try to resume... we should check what was our
+ * state though... (were we really in D2 state ?). Right now, this code
+ * is only enable on Macs so it's fine.
+ */
+ else if (rinfo->pm_mode & radeon_pm_d2)
+ radeon_set_suspend(rinfo, 0);
+
+ rinfo->asleep = 0;
+ } else
+ radeon_engine_idle();
+
+ /* Restore display & engine */
+ radeon_write_mode (rinfo, &rinfo->state, 1);
+ if (!(info->flags & FBINFO_HWACCEL_DISABLED))
+ radeonfb_engine_init (rinfo);
+
+ fb_pan_display(info, &info->var);
+ fb_set_cmap(&info->cmap, info);
+
+ /* Refresh */
+ fb_set_suspend(info, 0);
+
+ /* Unblank */
+ rinfo->lock_blank = 0;
+ radeon_screen_blank(rinfo, FB_BLANK_UNBLANK, 1);
+
+#ifdef CONFIG_PPC_PMAC
+ /* On powermac, we have hooks to properly suspend/resume AGP now,
+ * use them here. We'll ultimately need some generic support here,
+ * but the generic code isn't quite ready for that yet
+ */
+ pmac_resume_agp_for_card(pdev);
+#endif /* CONFIG_PPC_PMAC */
+
+
+ /* Check status of dynclk */
+ if (rinfo->dynclk == 1)
+ radeon_pm_enable_dynamic_mode(rinfo);
+ else if (rinfo->dynclk == 0)
+ radeon_pm_disable_dynamic_mode(rinfo);
+
+ pdev->dev.power.power_state = PMSG_ON;
+
+ bail:
+ console_unlock();
+
+ return rc;
+}
+
+#ifdef CONFIG_PPC__disabled
+static void radeonfb_early_resume(void *data)
+{
+ struct radeonfb_info *rinfo = data;
+
+ rinfo->no_schedule = 1;
+ pci_restore_state(rinfo->pdev);
+ radeonfb_pci_resume(rinfo->pdev);
+ rinfo->no_schedule = 0;
+}
+#endif /* CONFIG_PPC */
+
+#endif /* CONFIG_PM */
+
+void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep)
+{
+ /* Enable/Disable dynamic clocks: TODO add sysfs access */
+ if (rinfo->family == CHIP_FAMILY_RS480)
+ rinfo->dynclk = -1;
+ else
+ rinfo->dynclk = dynclk;
+
+ if (rinfo->dynclk == 1) {
+ radeon_pm_enable_dynamic_mode(rinfo);
+ printk("radeonfb: Dynamic Clock Power Management enabled\n");
+ } else if (rinfo->dynclk == 0) {
+ radeon_pm_disable_dynamic_mode(rinfo);
+ printk("radeonfb: Dynamic Clock Power Management disabled\n");
+ }
+
+#if defined(CONFIG_PM)
+#if defined(CONFIG_PPC_PMAC)
+ /* Check if we can power manage on suspend/resume. We can do
+ * D2 on M6, M7 and M9, and we can resume from D3 cold a few other
+ * "Mac" cards, but that's all. We need more infos about what the
+ * BIOS does tho. Right now, all this PM stuff is pmac-only for that
+ * reason. --BenH
+ */
+ if (machine_is(powermac) && rinfo->of_node) {
+ if (rinfo->is_mobility && rinfo->pdev->pm_cap &&
+ rinfo->family <= CHIP_FAMILY_RV250)
+ rinfo->pm_mode |= radeon_pm_d2;
+
+ /* We can restart Jasper (M10 chip in albooks), BlueStone (7500 chip
+ * in some desktop G4s), Via (M9+ chip on iBook G4) and
+ * Snowy (M11 chip on iBook G4 manufactured after July 2005)
+ */
+ if (!strcmp(rinfo->of_node->name, "ATY,JasperParent") ||
+ !strcmp(rinfo->of_node->name, "ATY,SnowyParent")) {
+ rinfo->reinit_func = radeon_reinitialize_M10;
+ rinfo->pm_mode |= radeon_pm_off;
+ }
+#if 0 /* Not ready yet */
+ if (!strcmp(rinfo->of_node->name, "ATY,BlueStoneParent")) {
+ rinfo->reinit_func = radeon_reinitialize_QW;
+ rinfo->pm_mode |= radeon_pm_off;
+ }
+#endif
+ if (!strcmp(rinfo->of_node->name, "ATY,ViaParent")) {
+ rinfo->reinit_func = radeon_reinitialize_M9P;
+ rinfo->pm_mode |= radeon_pm_off;
+ }
+
+ /* If any of the above is set, we assume the machine can sleep/resume.
+ * It's a bit of a "shortcut" but will work fine. Ideally, we need infos
+ * from the platform about what happens to the chip...
+ * Now we tell the platform about our capability
+ */
+ if (rinfo->pm_mode != radeon_pm_none) {
+ pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, rinfo->of_node, 0, 1);
+#if 0 /* Disable the early video resume hack for now as it's causing problems, among
+ * others we now rely on the PCI core restoring the config space for us, which
+ * isn't the case with that hack, and that code path causes various things to
+ * be called with interrupts off while they shouldn't. I'm leaving the code in
+ * as it can be useful for debugging purposes
+ */
+ pmac_set_early_video_resume(radeonfb_early_resume, rinfo);
+#endif
+ }
+
+#if 0
+ /* Power down TV DAC, that saves a significant amount of power,
+ * we'll have something better once we actually have some TVOut
+ * support
+ */
+ OUTREG(TV_DAC_CNTL, INREG(TV_DAC_CNTL) | 0x07000000);
+#endif
+ }
+#endif /* defined(CONFIG_PPC_PMAC) */
+#endif /* defined(CONFIG_PM) */
+
+ if (ignore_devlist)
+ printk(KERN_DEBUG
+ "radeonfb: skipping test for device workarounds\n");
+ else
+ radeon_apply_workarounds(rinfo);
+
+ if (force_sleep) {
+ printk(KERN_DEBUG
+ "radeonfb: forcefully enabling D2 sleep mode\n");
+ rinfo->pm_mode |= radeon_pm_d2;
+ }
+}
+
+void radeonfb_pm_exit(struct radeonfb_info *rinfo)
+{
+#if defined(CONFIG_PM) && defined(CONFIG_PPC_PMAC)
+ if (rinfo->pm_mode != radeon_pm_none)
+ pmac_set_early_video_resume(NULL, NULL);
+#endif
+}
diff --git a/drivers/video/fbdev/aty/radeonfb.h b/drivers/video/fbdev/aty/radeonfb.h
new file mode 100644
index 000000000..131b34dd6
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeonfb.h
@@ -0,0 +1,521 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __RADEONFB_H__
+#define __RADEONFB_H__
+
+#ifdef CONFIG_FB_RADEON_DEBUG
+#define DEBUG 1
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+
+#ifdef CONFIG_FB_RADEON_I2C
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#endif
+
+#include <asm/io.h>
+
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+#include <asm/prom.h>
+#endif
+
+#include <video/radeon.h>
+
+/***************************************************************
+ * Most of the definitions here are adapted right from XFree86 *
+ ***************************************************************/
+
+
+/*
+ * Chip families. Must fit in the low 16 bits of a long word
+ */
+enum radeon_family {
+ CHIP_FAMILY_UNKNOW,
+ CHIP_FAMILY_LEGACY,
+ CHIP_FAMILY_RADEON,
+ CHIP_FAMILY_RV100,
+ CHIP_FAMILY_RS100, /* U1 (IGP320M) or A3 (IGP320)*/
+ CHIP_FAMILY_RV200,
+ CHIP_FAMILY_RS200, /* U2 (IGP330M/340M/350M) or A4 (IGP330/340/345/350),
+ RS250 (IGP 7000) */
+ CHIP_FAMILY_R200,
+ CHIP_FAMILY_RV250,
+ CHIP_FAMILY_RS300, /* Radeon 9000 IGP */
+ CHIP_FAMILY_RV280,
+ CHIP_FAMILY_R300,
+ CHIP_FAMILY_R350,
+ CHIP_FAMILY_RV350,
+ CHIP_FAMILY_RV380, /* RV370/RV380/M22/M24 */
+ CHIP_FAMILY_R420, /* R420/R423/M18 */
+ CHIP_FAMILY_RC410,
+ CHIP_FAMILY_RS400,
+ CHIP_FAMILY_RS480,
+ CHIP_FAMILY_LAST,
+};
+
+#define IS_RV100_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_RV100) || \
+ ((rinfo)->family == CHIP_FAMILY_RV200) || \
+ ((rinfo)->family == CHIP_FAMILY_RS100) || \
+ ((rinfo)->family == CHIP_FAMILY_RS200) || \
+ ((rinfo)->family == CHIP_FAMILY_RV250) || \
+ ((rinfo)->family == CHIP_FAMILY_RV280) || \
+ ((rinfo)->family == CHIP_FAMILY_RS300))
+
+
+#define IS_R300_VARIANT(rinfo) (((rinfo)->family == CHIP_FAMILY_R300) || \
+ ((rinfo)->family == CHIP_FAMILY_RV350) || \
+ ((rinfo)->family == CHIP_FAMILY_R350) || \
+ ((rinfo)->family == CHIP_FAMILY_RV380) || \
+ ((rinfo)->family == CHIP_FAMILY_R420) || \
+ ((rinfo)->family == CHIP_FAMILY_RC410) || \
+ ((rinfo)->family == CHIP_FAMILY_RS480))
+
+/*
+ * Chip flags
+ */
+enum radeon_chip_flags {
+ CHIP_FAMILY_MASK = 0x0000ffffUL,
+ CHIP_FLAGS_MASK = 0xffff0000UL,
+ CHIP_IS_MOBILITY = 0x00010000UL,
+ CHIP_IS_IGP = 0x00020000UL,
+ CHIP_HAS_CRTC2 = 0x00040000UL,
+};
+
+/*
+ * Errata workarounds
+ */
+enum radeon_errata {
+ CHIP_ERRATA_R300_CG = 0x00000001,
+ CHIP_ERRATA_PLL_DUMMYREADS = 0x00000002,
+ CHIP_ERRATA_PLL_DELAY = 0x00000004,
+};
+
+
+/*
+ * Monitor types
+ */
+enum radeon_montype {
+ MT_NONE = 0,
+ MT_CRT, /* CRT */
+ MT_LCD, /* LCD */
+ MT_DFP, /* DVI */
+ MT_CTV, /* composite TV */
+ MT_STV /* S-Video out */
+};
+
+/*
+ * DDC i2c ports
+ */
+enum ddc_type {
+ ddc_none,
+ ddc_monid,
+ ddc_dvi,
+ ddc_vga,
+ ddc_crt2,
+};
+
+/*
+ * Connector types
+ */
+enum conn_type {
+ conn_none,
+ conn_proprietary,
+ conn_crt,
+ conn_DVI_I,
+ conn_DVI_D,
+};
+
+
+/*
+ * PLL infos
+ */
+struct pll_info {
+ int ppll_max;
+ int ppll_min;
+ int sclk, mclk;
+ int ref_div;
+ int ref_clk;
+};
+
+
+/*
+ * This structure contains the various registers manipulated by this
+ * driver for setting or restoring a mode. It's mostly copied from
+ * XFree's RADEONSaveRec structure. A few chip settings might still be
+ * tweaked without beeing reflected or saved in these registers though
+ */
+struct radeon_regs {
+ /* Common registers */
+ u32 ovr_clr;
+ u32 ovr_wid_left_right;
+ u32 ovr_wid_top_bottom;
+ u32 ov0_scale_cntl;
+ u32 mpp_tb_config;
+ u32 mpp_gp_config;
+ u32 subpic_cntl;
+ u32 viph_control;
+ u32 i2c_cntl_1;
+ u32 gen_int_cntl;
+ u32 cap0_trig_cntl;
+ u32 cap1_trig_cntl;
+ u32 bus_cntl;
+ u32 surface_cntl;
+ u32 bios_5_scratch;
+
+ /* Other registers to save for VT switches or driver load/unload */
+ u32 dp_datatype;
+ u32 rbbm_soft_reset;
+ u32 clock_cntl_index;
+ u32 amcgpio_en_reg;
+ u32 amcgpio_mask;
+
+ /* Surface/tiling registers */
+ u32 surf_lower_bound[8];
+ u32 surf_upper_bound[8];
+ u32 surf_info[8];
+
+ /* CRTC registers */
+ u32 crtc_gen_cntl;
+ u32 crtc_ext_cntl;
+ u32 dac_cntl;
+ u32 crtc_h_total_disp;
+ u32 crtc_h_sync_strt_wid;
+ u32 crtc_v_total_disp;
+ u32 crtc_v_sync_strt_wid;
+ u32 crtc_offset;
+ u32 crtc_offset_cntl;
+ u32 crtc_pitch;
+ u32 disp_merge_cntl;
+ u32 grph_buffer_cntl;
+ u32 crtc_more_cntl;
+
+ /* CRTC2 registers */
+ u32 crtc2_gen_cntl;
+ u32 dac2_cntl;
+ u32 disp_output_cntl;
+ u32 disp_hw_debug;
+ u32 disp2_merge_cntl;
+ u32 grph2_buffer_cntl;
+ u32 crtc2_h_total_disp;
+ u32 crtc2_h_sync_strt_wid;
+ u32 crtc2_v_total_disp;
+ u32 crtc2_v_sync_strt_wid;
+ u32 crtc2_offset;
+ u32 crtc2_offset_cntl;
+ u32 crtc2_pitch;
+
+ /* Flat panel regs */
+ u32 fp_crtc_h_total_disp;
+ u32 fp_crtc_v_total_disp;
+ u32 fp_gen_cntl;
+ u32 fp2_gen_cntl;
+ u32 fp_h_sync_strt_wid;
+ u32 fp2_h_sync_strt_wid;
+ u32 fp_horz_stretch;
+ u32 fp_panel_cntl;
+ u32 fp_v_sync_strt_wid;
+ u32 fp2_v_sync_strt_wid;
+ u32 fp_vert_stretch;
+ u32 lvds_gen_cntl;
+ u32 lvds_pll_cntl;
+ u32 tmds_crc;
+ u32 tmds_transmitter_cntl;
+
+ /* Computed values for PLL */
+ u32 dot_clock_freq;
+ int feedback_div;
+ int post_div;
+
+ /* PLL registers */
+ u32 ppll_div_3;
+ u32 ppll_ref_div;
+ u32 vclk_ecp_cntl;
+ u32 clk_cntl_index;
+
+ /* Computed values for PLL2 */
+ u32 dot_clock_freq_2;
+ int feedback_div_2;
+ int post_div_2;
+
+ /* PLL2 registers */
+ u32 p2pll_ref_div;
+ u32 p2pll_div_0;
+ u32 htotal_cntl2;
+
+ /* Palette */
+ int palette_valid;
+};
+
+struct panel_info {
+ int xres, yres;
+ int valid;
+ int clock;
+ int hOver_plus, hSync_width, hblank;
+ int vOver_plus, vSync_width, vblank;
+ int hAct_high, vAct_high, interlaced;
+ int pwr_delay;
+ int use_bios_dividers;
+ int ref_divider;
+ int post_divider;
+ int fbk_divider;
+};
+
+struct radeonfb_info;
+
+#ifdef CONFIG_FB_RADEON_I2C
+struct radeon_i2c_chan {
+ struct radeonfb_info *rinfo;
+ u32 ddc_reg;
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+};
+#endif
+
+enum radeon_pm_mode {
+ radeon_pm_none = 0, /* Nothing supported */
+ radeon_pm_d2 = 0x00000001, /* Can do D2 state */
+ radeon_pm_off = 0x00000002, /* Can resume from D3 cold */
+};
+
+typedef void (*reinit_function_ptr)(struct radeonfb_info *rinfo);
+
+struct radeonfb_info {
+ struct fb_info *info;
+
+ struct radeon_regs state;
+ struct radeon_regs init_state;
+
+ char name[50];
+
+ unsigned long mmio_base_phys;
+ unsigned long fb_base_phys;
+
+ void __iomem *mmio_base;
+ void __iomem *fb_base;
+
+ unsigned long fb_local_base;
+
+ struct pci_dev *pdev;
+#if defined(CONFIG_PPC) || defined(CONFIG_SPARC)
+ struct device_node *of_node;
+#endif
+
+ void __iomem *bios_seg;
+ int fp_bios_start;
+
+ u32 pseudo_palette[16];
+ struct { u8 red, green, blue, pad; }
+ palette[256];
+
+ int chipset;
+ u8 family;
+ u8 rev;
+ unsigned int errata;
+ unsigned long video_ram;
+ unsigned long mapped_vram;
+ int vram_width;
+ int vram_ddr;
+
+ int pitch, bpp, depth;
+
+ int has_CRTC2;
+ int is_mobility;
+ int is_IGP;
+ int reversed_DAC;
+ int reversed_TMDS;
+ struct panel_info panel_info;
+ int mon1_type;
+ u8 *mon1_EDID;
+ struct fb_videomode *mon1_modedb;
+ int mon1_dbsize;
+ int mon2_type;
+ u8 *mon2_EDID;
+
+ u32 dp_gui_master_cntl;
+
+ struct pll_info pll;
+
+ int wc_cookie;
+
+ u32 save_regs[100];
+ int asleep;
+ int lock_blank;
+ int dynclk;
+ int no_schedule;
+ enum radeon_pm_mode pm_mode;
+ reinit_function_ptr reinit_func;
+
+ /* Lock on register access */
+ spinlock_t reg_lock;
+
+ /* Timer used for delayed LVDS operations */
+ struct timer_list lvds_timer;
+ u32 pending_lvds_gen_cntl;
+
+#ifdef CONFIG_FB_RADEON_I2C
+ struct radeon_i2c_chan i2c[4];
+#endif
+};
+
+
+#define PRIMARY_MONITOR(rinfo) (rinfo->mon1_type)
+
+
+/*
+ * IO macros
+ */
+
+void _radeon_msleep(struct radeonfb_info *rinfo, unsigned long ms);
+
+#define INREG8(addr) readb((rinfo->mmio_base)+addr)
+#define OUTREG8(addr,val) writeb(val, (rinfo->mmio_base)+addr)
+#define INREG16(addr) readw((rinfo->mmio_base)+addr)
+#define OUTREG16(addr,val) writew(val, (rinfo->mmio_base)+addr)
+#define INREG(addr) readl((rinfo->mmio_base)+addr)
+#define OUTREG(addr,val) writel(val, (rinfo->mmio_base)+addr)
+
+void _OUTREGP(struct radeonfb_info *rinfo, u32 addr, u32 val, u32 mask);
+
+#define OUTREGP(addr,val,mask) _OUTREGP(rinfo, addr, val,mask)
+
+/*
+ * Note about PLL register accesses:
+ *
+ * I have removed the spinlock on them on purpose. The driver now
+ * expects that it will only manipulate the PLL registers in normal
+ * task environment, where radeon_msleep() will be called, protected
+ * by a semaphore (currently the console semaphore) so that no conflict
+ * will happen on the PLL register index.
+ *
+ * With the latest changes to the VT layer, this is guaranteed for all
+ * calls except the actual drawing/blits which aren't supposed to use
+ * the PLL registers anyway
+ *
+ * This is very important for the workarounds to work properly. The only
+ * possible exception to this rule is the call to unblank(), which may
+ * be done at irq time if an oops is in progress.
+ */
+void radeon_pll_errata_after_index_slow(struct radeonfb_info *rinfo);
+static inline void radeon_pll_errata_after_index(struct radeonfb_info *rinfo)
+{
+ if (rinfo->errata & CHIP_ERRATA_PLL_DUMMYREADS)
+ radeon_pll_errata_after_index_slow(rinfo);
+}
+
+void radeon_pll_errata_after_data_slow(struct radeonfb_info *rinfo);
+static inline void radeon_pll_errata_after_data(struct radeonfb_info *rinfo)
+{
+ if (rinfo->errata & (CHIP_ERRATA_PLL_DELAY|CHIP_ERRATA_R300_CG))
+ radeon_pll_errata_after_data_slow(rinfo);
+}
+
+u32 __INPLL(struct radeonfb_info *rinfo, u32 addr);
+void __OUTPLL(struct radeonfb_info *rinfo, unsigned int index, u32 val);
+void __OUTPLLP(struct radeonfb_info *rinfo, unsigned int index,
+ u32 val, u32 mask);
+
+#define INPLL(addr) __INPLL(rinfo, addr)
+#define OUTPLL(index, val) __OUTPLL(rinfo, index, val)
+#define OUTPLLP(index, val, mask) __OUTPLLP(rinfo, index, val, mask)
+
+
+#define BIOS_IN8(v) (readb(rinfo->bios_seg + (v)))
+#define BIOS_IN16(v) (readb(rinfo->bios_seg + (v)) | \
+ (readb(rinfo->bios_seg + (v) + 1) << 8))
+#define BIOS_IN32(v) (readb(rinfo->bios_seg + (v)) | \
+ (readb(rinfo->bios_seg + (v) + 1) << 8) | \
+ (readb(rinfo->bios_seg + (v) + 2) << 16) | \
+ (readb(rinfo->bios_seg + (v) + 3) << 24))
+
+/*
+ * Inline utilities
+ */
+static inline int round_div(int num, int den)
+{
+ return (num + (den / 2)) / den;
+}
+
+static inline int var_to_depth(const struct fb_var_screeninfo *var)
+{
+ if (var->bits_per_pixel != 16)
+ return var->bits_per_pixel;
+ return (var->green.length == 5) ? 15 : 16;
+}
+
+static inline u32 radeon_get_dstbpp(u16 depth)
+{
+ switch (depth) {
+ case 8:
+ return DST_8BPP;
+ case 15:
+ return DST_15BPP;
+ case 16:
+ return DST_16BPP;
+ case 32:
+ return DST_32BPP;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * 2D Engine helper routines
+ */
+
+void _radeon_fifo_wait(struct radeonfb_info *rinfo, int entries);
+void radeon_engine_flush(struct radeonfb_info *rinfo);
+void _radeon_engine_idle(struct radeonfb_info *rinfo);
+
+#define radeon_engine_idle() _radeon_engine_idle(rinfo)
+#define radeon_fifo_wait(entries) _radeon_fifo_wait(rinfo,entries)
+#define radeon_msleep(ms) _radeon_msleep(rinfo,ms)
+
+
+/* I2C Functions */
+extern void radeon_create_i2c_busses(struct radeonfb_info *rinfo);
+extern void radeon_delete_i2c_busses(struct radeonfb_info *rinfo);
+extern int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 **out_edid);
+
+/* PM Functions */
+extern int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state);
+extern int radeonfb_pci_resume(struct pci_dev *pdev);
+extern void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlist, int force_sleep);
+extern void radeonfb_pm_exit(struct radeonfb_info *rinfo);
+
+/* Monitor probe functions */
+extern void radeon_probe_screens(struct radeonfb_info *rinfo,
+ const char *monitor_layout, int ignore_edid);
+extern void radeon_check_modes(struct radeonfb_info *rinfo, const char *mode_option);
+extern int radeon_match_mode(struct radeonfb_info *rinfo,
+ struct fb_var_screeninfo *dest,
+ const struct fb_var_screeninfo *src);
+
+/* Accel functions */
+extern void radeonfb_fillrect(struct fb_info *info, const struct fb_fillrect *region);
+extern void radeonfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+extern void radeonfb_imageblit(struct fb_info *p, const struct fb_image *image);
+extern int radeonfb_sync(struct fb_info *info);
+extern void radeonfb_engine_init (struct radeonfb_info *rinfo);
+extern void radeonfb_engine_reset(struct radeonfb_info *rinfo);
+
+/* Other functions */
+extern int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch);
+extern void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
+ int reg_only);
+
+/* Backlight functions */
+#ifdef CONFIG_FB_RADEON_BACKLIGHT
+extern void radeonfb_bl_init(struct radeonfb_info *rinfo);
+extern void radeonfb_bl_exit(struct radeonfb_info *rinfo);
+#else
+static inline void radeonfb_bl_init(struct radeonfb_info *rinfo) {}
+static inline void radeonfb_bl_exit(struct radeonfb_info *rinfo) {}
+#endif
+
+#endif /* __RADEONFB_H__ */