summaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/aty
diff options
context:
space:
mode:
authorNiklas Cassel <cassel@kernel.org>2026-09-16 15:00:32 +0200
committerNiklas Cassel <cassel@kernel.org>2026-09-16 16:18:00 +0200
commit8d836581f9b1f57ceaa2b47654754ef1260b410b (patch)
tree1dbd4d0dfb99c2e238356cbe947e5b0be3e3a571 /drivers/video/fbdev/aty
downloadlinux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.tar.gz
linux-stable-8d836581f9b1f57ceaa2b47654754ef1260b410b.zip
ata: libata-scsi: fix ata_dsm_trim_pages() kernel-docgrafted
make htmldocs fails with: Documentation/driver-api/libata:604: ./drivers/ata/libata-scsi.c:2301: ERROR: Unexpected indentation. [docutils] The Return: section of ata_dsm_trim_pages() ends its first sentence with a colon and continues with an indented bullet list. reStructuredText requires a blank line before an indented block, so docutils chokes on the list. Simply adding the missing blank line does not work either: Return: is a kernel-doc "special section", which is terminated by the first blank line, so the bullet list would end up in the Description section, detached from the sentence introducing it. Spell the two bounds out as prose instead, so that the Return: section stays self-contained. Documentation-only change. Fixes: e64e6b5dc867 ("ata: libata-scsi: scale DSM TRIM payload by MAX PAGES PER DSM COMMAND") Reported-by: Thomas Huth <thuth@redhat.com> Closes: https://lore.kernel.org/linux-ide/b0a0b8e8-cc4f-4c7b-8bc5-fee0712d405a@redhat.com/ Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Thomas Huth <thuth@redhat.com> Link: https://lore.kernel.org/r/20260916130031.29990-2-cassel@kernel.org Signed-off-by: Niklas Cassel <cassel@kernel.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.c2467
-rw-r--r--drivers/video/fbdev/aty/atyfb.h371
-rw-r--r--drivers/video/fbdev/aty/atyfb_base.c4012
-rw-r--r--drivers/video/fbdev/aty/mach64_accel.c429
-rw-r--r--drivers/video/fbdev/aty/mach64_ct.c651
-rw-r--r--drivers/video/fbdev/aty/mach64_cursor.c231
-rw-r--r--drivers/video/fbdev/aty/mach64_gx.c908
-rw-r--r--drivers/video/fbdev/aty/radeon_accel.c329
-rw-r--r--drivers/video/fbdev/aty/radeon_backlight.c208
-rw-r--r--drivers/video/fbdev/aty/radeon_base.c2655
-rw-r--r--drivers/video/fbdev/aty/radeon_i2c.c168
-rw-r--r--drivers/video/fbdev/aty/radeon_monitor.c1051
-rw-r--r--drivers/video/fbdev/aty/radeon_pm.c2927
-rw-r--r--drivers/video/fbdev/aty/radeonfb.h520
16 files changed, 17158 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..c349a1711
--- /dev/null
+++ b/drivers/video/fbdev/aty/aty128fb.c
@@ -0,0 +1,2467 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* $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/aperture.h>
+#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 "../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_late(struct device *dev, pm_message_t state);
+static int __maybe_unused aty128_pci_suspend(struct device *dev);
+static int __maybe_unused aty128_pci_hibernate(struct device *dev);
+static int __maybe_unused aty128_pci_freeze(struct device *dev);
+static int __maybe_unused aty128_pci_resume(struct device *dev);
+static int aty128_do_resume(struct pci_dev *pdev);
+
+static const struct dev_pm_ops aty128_pci_pm_ops = {
+ .suspend = aty128_pci_suspend,
+ .resume = aty128_pci_resume,
+ .freeze = aty128_pci_freeze,
+ .thaw = aty128_pci_resume,
+ .poweroff = aty128_pci_hibernate,
+ .restore = aty128_pci_resume,
+};
+
+/* supported Rage128 chipsets */
+static const struct pci_device_id aty128_pci_tbl[] = {
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_LE), .driver_data = rage_M3_pci },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_LF), .driver_data = rage_M3 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_MF), .driver_data = rage_M4 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_ML), .driver_data = rage_M4 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PA), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PB), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PC), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PD), .driver_data = rage_128_pro_pci },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PE), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PF), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PG), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PH), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PI), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PK), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PL), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PM), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PN), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PO), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PP), .driver_data = rage_128_pro_pci },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PR), .driver_data = rage_128_pro_pci },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PS), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PT), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PU), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PV), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PW), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_PX), .driver_data = rage_128_pro },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RE), .driver_data = rage_128_pci },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RF), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RG), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RK), .driver_data = rage_128_pci },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_RL), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SE), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SF), .driver_data = rage_128_pci },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SG), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SH), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SK), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SL), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SM), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_SN), .driver_data = rage_128 },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TF), .driver_data = rage_128_ultra },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TL), .driver_data = rage_128_ultra },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TR), .driver_data = rage_128_ultra },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TS), .driver_data = rage_128_ultra },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TT), .driver_data = rage_128_ultra },
+ { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_RAGE128_TU), .driver_data = rage_128_ultra },
+ { }
+};
+
+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,
+ .driver.pm = &aty128_pci_pm_ops,
+};
+
+/* 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_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
+static int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT);
+#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);
+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 const struct fb_ops aty128fb_ops = {
+ .owner = THIS_MODULE,
+ FB_DEFAULT_IOMEM_OPS,
+ .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,
+};
+
+ /*
+ * 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;
+ static const unsigned int 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, BACKLIGHT_POWER_ON);
+#endif
+ } else {
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ aty128_bl_set_power(info, BACKLIGHT_POWER_OFF);
+#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;
+
+ /* register values for post dividers */
+ static const unsigned char post_conv[] = {
+ 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;
+ static const 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) {
+ 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 (!par->lcd_on)
+ level = 0;
+ else
+ level = backlight_get_brightness(bd);
+
+ 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->device, 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 = BACKLIGHT_POWER_ON;
+ 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;
+
+ 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;
+
+ if (register_framebuffer(info) < 0)
+ return 0;
+
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ if (backlight)
+ aty128_bl_init(par);
+#endif
+
+ 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
+
+ err = aperture_remove_conflicting_pci_devices(pdev, "aty128fb");
+ if (err)
+ return err;
+
+ /* Enable device in PCI config */
+ err = pcim_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n",
+ err);
+ return -ENODEV;
+ }
+
+ fb_addr = pci_resource_start(pdev, 0);
+ if (!pcim_request_region(pdev, 0, "aty128fb FB")) {
+ printk(KERN_ERR "aty128fb: cannot reserve frame "
+ "buffer memory\n");
+ return -ENODEV;
+ }
+
+ reg_addr = pci_resource_start(pdev, 2);
+ if (!pcim_request_region(pdev, 2, "aty128fb MMIO")) {
+ printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n");
+ return -ENODEV;
+ }
+
+ /* We have the resources. Now virtualize them */
+ info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev);
+ if (!info)
+ goto err_free_info;
+
+ par = info->par;
+
+ info->pseudo_palette = par->pseudo_palette;
+
+ /* Virtualize mmio region */
+ info->fix.mmio_start = reg_addr;
+ par->regbase = devm_ioremap(&pdev->dev, pci_resource_start(pdev, 2),
+ pci_resource_len(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 = devm_ioremap_wc(&pdev->dev, fb_addr, par->vram_size);
+ if (!info->screen_base)
+ goto err_free_info;
+
+ /* 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_free_info;
+ }
+
+#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_free_info;
+
+ if (mtrr)
+ par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
+ par->vram_size);
+ return 0;
+
+err_free_info:
+ framebuffer_release(info);
+ 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;
+
+#ifdef CONFIG_FB_ATY128_BACKLIGHT
+ aty128_bl_exit(info->bl_dev);
+#endif
+
+ unregister_framebuffer(info);
+
+ arch_phys_wc_del(par->wc_cookie);
+
+ 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;
+}
+
+static void aty128_set_suspend(struct aty128fb_par *par, int suspend)
+{
+ u32 pmgt;
+
+ 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);
+ }
+}
+
+static int aty128_pci_suspend_late(struct device *dev, pm_message_t state)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct fb_info *info = pci_get_drvdata(pdev);
+ struct aty128fb_par *par = info->par;
+
+ /* 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 __maybe_unused aty128_pci_suspend(struct device *dev)
+{
+ return aty128_pci_suspend_late(dev, PMSG_SUSPEND);
+}
+
+static int __maybe_unused aty128_pci_hibernate(struct device *dev)
+{
+ return aty128_pci_suspend_late(dev, PMSG_HIBERNATE);
+}
+
+static int __maybe_unused aty128_pci_freeze(struct device *dev)
+{
+ return aty128_pci_suspend_late(dev, PMSG_FREEZE);
+}
+
+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 __maybe_unused aty128_pci_resume(struct device *dev)
+{
+ int rc;
+
+ console_lock();
+ rc = aty128_do_resume(to_pci_dev(dev));
+ console_unlock();
+
+ return rc;
+}
+
+
+static int aty128fb_init(void)
+{
+#ifndef MODULE
+ char *option = NULL;
+#endif
+
+ if (fb_modesetting_disabled("aty128fb"))
+ return -ENODEV;
+
+#ifndef MODULE
+ 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..30da3e82e
--- /dev/null
+++ b/drivers/video/fbdev/aty/atyfb.h
@@ -0,0 +1,371 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ATI Frame Buffer Device Driver Core Definitions
+ */
+
+#include <linux/io.h>
+#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;
+ unsigned fifo_space;
+ 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
+}
+
+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);
+
+ /*
+ * 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, struct fb_ops *atyfb_ops);
+
+ /*
+ * Hardware acceleration
+ */
+
+static inline void wait_for_fifo(u16 entries, struct atyfb_par *par)
+{
+ unsigned fifo_space = par->fifo_space;
+ while (entries > fifo_space) {
+ fifo_space = 16 - fls(aty_ld_le32(FIFO_STAT, par) & 0xffff);
+ }
+ par->fifo_space = fifo_space - 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(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..97cc8b6a9
--- /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/aperture.h>
+#include <linux/compat.h>
+#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 "../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...) no_printk(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_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_GENERIC_LCD) || \
+defined(CONFIG_FB_ATY_BACKLIGHT) || defined (CONFIG_PPC_PMAC)
+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);
+ }
+}
+#else /* defined(CONFIG_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_BACKLIGHT) ||
+ defined(CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_PPC_PMAC) */
+void aty_st_lcd(int index, u32 val, const struct atyfb_par *par)
+{ }
+
+u32 aty_ld_lcd(int index, const struct atyfb_par *par)
+{
+ return 0;
+}
+#endif /* defined(CONFIG_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_BACKLIGHT) ||
+ defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_PPC_PMAC) */
+
+#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 CONFIG_COMPAT
+static int atyfb_compat_ioctl(struct fb_info *info, u_int cmd, u_long arg)
+{
+ return atyfb_ioctl(info, cmd, (u_long)compat_ptr(arg));
+}
+#endif
+
+#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_DEFAULT_IOMEM_OPS_RDWR,
+ .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,
+#ifdef CONFIG_COMPAT
+ .fb_compat_ioctl = atyfb_compat_ioctl,
+#endif
+ .fb_fillrect = atyfb_fillrect,
+ .fb_copyarea = atyfb_copyarea,
+ .fb_imageblit = atyfb_imageblit,
+#ifdef __sparc__
+ .fb_mmap = atyfb_mmap,
+#else
+ __FB_DEFAULT_IOMEM_OPS_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;
+static int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT);
+
+#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) {
+ 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) {
+ atyfb_ops.fb_sync = atyfb_sync;
+ info->flags &= ~FBINFO_HWACCEL_DISABLED;
+ } else {
+ atyfb_ops.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 */
+#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) {
+ pr_cont("\n");
+ printk("debug atyfb: 0x%04X: ", base + i);
+ }
+ pr_cont(" %08X", aty_ld_le32(i, par));
+ }
+ pr_cont("\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) {
+ pr_cont("\n");
+ printk("debug atyfb: 0x%02X: ", base + i);
+ }
+ if (i % 4 == 0)
+ pr_cont(" ");
+ pr_cont("%02X", aty_ld_pll_ct(i, par));
+ }
+ pr_cont("\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;
+ pr_cont("\ndebug atyfb: 0x%04X: ",
+ lt_lcd_regs[i]);
+ pr_cont(" %08X", aty_ld_lcd(i, par));
+ }
+ } else {
+ for (i = 0; i < 64; i++) {
+ if (i % 4 == 0)
+ pr_cont("\ndebug atyfb: 0x%02X: ",
+ base + i);
+ pr_cont(" %08X", aty_ld_lcd(i, par));
+ }
+ }
+ pr_cont("\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_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_late(struct device *dev, pm_message_t state)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ 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.
+ */
+
+#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;
+ }
+#endif
+
+ console_unlock();
+
+ pdev->dev.power.power_state = state;
+
+ return 0;
+}
+
+static int __maybe_unused atyfb_pci_suspend(struct device *dev)
+{
+ return atyfb_pci_suspend_late(dev, PMSG_SUSPEND);
+}
+
+static int __maybe_unused atyfb_pci_hibernate(struct device *dev)
+{
+ return atyfb_pci_suspend_late(dev, PMSG_HIBERNATE);
+}
+
+static int __maybe_unused atyfb_pci_freeze(struct device *dev)
+{
+ return atyfb_pci_suspend_late(dev, PMSG_FREEZE);
+}
+
+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 __maybe_unused atyfb_pci_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ 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;
+}
+
+static const struct dev_pm_ops atyfb_pci_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = atyfb_pci_suspend,
+ .resume = atyfb_pci_resume,
+ .freeze = atyfb_pci_freeze,
+ .thaw = atyfb_pci_resume,
+ .poweroff = atyfb_pci_hibernate,
+ .restore = atyfb_pci_resume,
+#endif /* CONFIG_PM_SLEEP */
+};
+
+#endif /* 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 = backlight_get_brightness(bd);
+
+ 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->device, 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 = BACKLIGHT_POWER_ON;
+ 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
+ */
+
+#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;
+#ifdef CONFIG_ATARI
+ u8 dac_type;
+#endif
+
+ 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_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? */
+#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_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
+ 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++)
+ pr_cont(" %02x", aty_ld_pll_ct(i, par));
+ pr_cont("\n");
+ }
+#endif
+ if (par->pll_ops->init_pll) {
+ ret = par->pll_ops->init_pll(info, &par->pll);
+ if (ret)
+ return ret;
+ }
+
+ 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_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
+
+ 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, &atyfb_ops);
+#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;
+ }
+
+ if (M64_HAS(MOBIL_BUS) && backlight) {
+#ifdef CONFIG_FB_ATY_BACKLIGHT
+ aty_bl_init(par);
+#endif
+ }
+
+ 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 = kzalloc_objs(*par->mmap_map, j, 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;
+ 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);
+
+ /*
+ * 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;
+ strscpy(model, (char *)par->lcd_table+1, sizeof(model));
+
+ 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) &&
+ devm_request_mem_region(&pdev->dev, 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;
+#if defined(__i386__) || defined(__ia64__)
+ /*
+ * 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 = devm_ioremap_uc(&pdev->dev, info->fix.mmio_start, 0x1000);
+#else
+ par->ati_regbase = devm_ioremap(&pdev->dev, info->fix.mmio_start, 0x1000);
+#endif
+ 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 = devm_ioremap_wc(&pdev->dev, 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
+ /* according to ATI, we should use clock 3 for acelerated mode */
+ par->clk_wr_offset = 3;
+
+ return 0;
+
+atyfb_setup_generic_fail:
+ /* devm handles cleanup automatically on probe failure */
+ par->ati_regbase = NULL;
+ 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;
+
+ rc = aperture_remove_conflicting_pci_devices(pdev, "atyfb");
+ if (rc)
+ return rc;
+
+ /* Enable device in PCI config */
+ if (pcim_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 (!pcim_request_region(pdev, rp - pdev->resource, "atyfb"))
+ return -EBUSY;
+
+ /* Allocate framebuffer */
+ info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev);
+ if (!info)
+ 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);
+#endif
+ /* devm handles cleanup automatically for non-sparc PCI devices */
+err_release_mem:
+ 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)
+ 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);
+
+#ifdef CONFIG_FB_ATY_BACKLIGHT
+ if (M64_HAS(MOBIL_BUS))
+ aty_bl_exit(info->bl_dev);
+#endif
+
+ unregister_framebuffer(info);
+
+ arch_phys_wc_del(par->wc_cookie);
+
+#ifndef __sparc__
+ /* For PCI devices, devm handles unmapping automatically */
+ if (par->bus_type != PCI) {
+ 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->res_start && par->bus_type != PCI)
+ 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,
+ .driver.pm = &atyfb_pci_pm_ops,
+};
+
+#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 = true;
+ } else if (!strncmp(this_opt, "nomtrr", 6)) {
+ nomtrr = true;
+ } 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];
+ strscpy(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;
+
+ lock_fb_info(reboot_info);
+
+ 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;
+#endif
+
+ if (fb_modesetting_disabled("atyfb"))
+ return -ENODEV;
+
+#ifndef MODULE
+ 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..826fb04de
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_accel.c
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * ATI Mach64 Hardware Acceleration
+ */
+
+#include <linux/delay.h>
+#include <linux/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(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);
+
+ par->fifo_space = 0;
+}
+
+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..e967536af
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_ct.c
@@ -0,0 +1,651 @@
+// 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)
+{
+
+ /* write addr byte */
+ aty_st_8(CLOCK_CNTL_ADDR, (offset << 2) & PLL_ADDR, par);
+ /* read the register value */
+ return aty_ld_8(CLOCK_CNTL_DATA, par);
+}
+
+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;
+ u8 tmp, tmp2;
+
+#ifdef CONFIG_FB_ATY_GENERIC_LCD
+ u32 lcd_gen_cntrl = 0;
+#endif
+
+#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;
+#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);
+ aty_ld_le32(DSP_ON_OFF, par);
+ aty_ld_le32(VGA_DSP_CONFIG, par);
+ 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..e826cb7dd
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_cursor.c
@@ -0,0 +1,231 @@
+// 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 <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 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.
+ */
+
+/* compose pixels based on mask */
+static inline unsigned long comp(unsigned long set, unsigned long unset, unsigned long mask)
+{
+ return ((set ^ unset) & mask) ^ unset;
+}
+
+ /*
+ * 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_io(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, struct fb_ops *atyfb_ops)
+{
+ 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;
+
+ atyfb_ops->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..d06d24830
--- /dev/null
+++ b/drivers/video/fbdev/aty/mach64_gx.c
@@ -0,0 +1,908 @@
+// 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) {
+ return -EINVAL;
+ } else if (MHz100 < ABS_MIN_FREQ_2595) {
+ 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;
+ 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;
+
+ 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..32d7f32db
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_backlight.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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>
+ */
+
+#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.
+ */
+ level = backlight_get_brightness(bd);
+
+ timer_delete_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_obj(struct radeon_bl_privdata);
+ 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->device, 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 = BACKLIGHT_POWER_ON;
+ 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..b6b058cee
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_base.c
@@ -0,0 +1,2655 @@
+/*
+ * 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/aperture.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_DEVICE(PCI_VENDOR_ID_ATI, id), .driver_data = (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;
+static int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT);
+
+/* 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 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:
+ timer_delete_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 = timer_container_of(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,
+ vSyncStart, vSyncEnd;
+ 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, hsync_wid, vsync_wid;
+ int primary_mon = PRIMARY_MONITOR(rinfo);
+ int depth = var_to_depth(mode);
+ int use_rmx = 0;
+
+ newmode = kmalloc_obj(struct radeon_regs);
+ 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;
+
+ format = radeon_get_dstbpp(depth);
+
+ 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 const struct fb_ops radeonfb_ops = {
+ .owner = THIS_MODULE,
+ __FB_DEFAULT_IOMEM_OPS_RDWR,
+ .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,
+ __FB_DEFAULT_IOMEM_OPS_MMAP,
+};
+
+
+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_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 */
+ strscpy(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,
+ const struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct fb_info *info = dev_get_drvdata(dev);
+ 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,
+ const struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct fb_info *info = dev_get_drvdata(dev);
+ 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 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) {
+ 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 = aperture_remove_conflicting_pci_devices(pdev, KBUILD_MODNAME);
+ 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);
+ fb_destroy_modelist(&info->modelist);
+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);
+
+ timer_delete_sync(&rinfo->lvds_timer);
+ arch_phys_wc_del(rinfo->wc_cookie);
+ radeonfb_bl_exit(rinfo);
+ unregister_framebuffer(info);
+
+ 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);
+}
+
+#ifdef CONFIG_PM
+#define RADEONFB_PCI_PM_OPS (&radeonfb_pci_pm_ops)
+#else
+#define RADEONFB_PCI_PM_OPS NULL
+#endif
+
+static struct pci_driver radeonfb_driver = {
+ .name = "radeonfb",
+ .id_table = radeonfb_pci_table,
+ .probe = radeonfb_pci_register,
+ .remove = radeonfb_pci_unregister,
+ .driver.pm = RADEONFB_PCI_PM_OPS,
+};
+
+#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;
+#endif
+
+ if (fb_modesetting_disabled("radeonfb"))
+ return -ENODEV;
+
+#ifndef MODULE
+ 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..621d13a1a
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_monitor.c
@@ -0,0 +1,1051 @@
+// 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 i;
+
+ for (i = 0; i < 4; 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 enforce 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..5ff4a9640
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeon_pm.c
@@ -0,0 +1,2927 @@
+// 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/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;
+ fallthrough;
+ case 2:
+ dll_sleep_mask |= MDLL_R300_RDCK__MRDCKB_SLEEP;
+ dll_reset_mask |= MDLL_R300_RDCK__MRDCKB_RESET;
+ fallthrough;
+ 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;
+ fallthrough;
+ 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_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
+
+#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_platform_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);
+ }
+ }
+}
+
+static int radeonfb_pci_suspend_late(struct device *dev, pm_message_t mesg)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ 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;
+ timer_delete_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 */
+
+ /* 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));
+ }
+ }
+ /* 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 radeonfb_pci_suspend(struct device *dev)
+{
+ return radeonfb_pci_suspend_late(dev, PMSG_SUSPEND);
+}
+
+static int radeonfb_pci_hibernate(struct device *dev)
+{
+ return radeonfb_pci_suspend_late(dev, PMSG_HIBERNATE);
+}
+
+static int radeonfb_pci_freeze(struct device *dev)
+{
+ return radeonfb_pci_suspend_late(dev, PMSG_FREEZE);
+}
+
+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);
+}
+
+static int radeonfb_pci_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ 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;
+}
+
+const struct dev_pm_ops radeonfb_pci_pm_ops = {
+ .suspend = radeonfb_pci_suspend,
+ .resume = radeonfb_pci_resume,
+ .freeze = radeonfb_pci_freeze,
+ .thaw = radeonfb_pci_resume,
+ .poweroff = radeonfb_pci_hibernate,
+ .restore = radeonfb_pci_resume,
+};
+
+#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 (of_node_name_eq(rinfo->of_node, "ATY,JasperParent") ||
+ of_node_name_eq(rinfo->of_node, "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 (of_node_name_eq(rinfo->of_node, "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..91d81b576
--- /dev/null
+++ b/drivers/video/fbdev/aty/radeonfb.h
@@ -0,0 +1,520 @@
+/* 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>
+
+#ifdef 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 const struct dev_pm_ops radeonfb_pci_pm_ops;
+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__ */