xref: /openbmc/linux/drivers/video/fbdev/aty/aty128fb.c (revision 9b7ee467)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f7018c21STomi Valkeinen /* $Id: aty128fb.c,v 1.1.1.1.36.1 1999/12/11 09:03:05 Exp $
3f7018c21STomi Valkeinen  *  linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128
4f7018c21STomi Valkeinen  *
5f7018c21STomi Valkeinen  *  Copyright (C) 1999-2003, Brad Douglas <brad@neruo.com>
6f7018c21STomi Valkeinen  *  Copyright (C) 1999, Anthony Tong <atong@uiuc.edu>
7f7018c21STomi Valkeinen  *
8f7018c21STomi Valkeinen  *                Ani Joshi / Jeff Garzik
9f7018c21STomi Valkeinen  *                      - Code cleanup
10f7018c21STomi Valkeinen  *
11f7018c21STomi Valkeinen  *                Michel Danzer <michdaen@iiic.ethz.ch>
12f7018c21STomi Valkeinen  *                      - 15/16 bit cleanup
13f7018c21STomi Valkeinen  *                      - fix panning
14f7018c21STomi Valkeinen  *
15f7018c21STomi Valkeinen  *                Benjamin Herrenschmidt
16f7018c21STomi Valkeinen  *                      - pmac-specific PM stuff
17f7018c21STomi Valkeinen  *			- various fixes & cleanups
18f7018c21STomi Valkeinen  *
19f7018c21STomi Valkeinen  *                Andreas Hundt <andi@convergence.de>
20f7018c21STomi Valkeinen  *                      - FB_ACTIVATE fixes
21f7018c21STomi Valkeinen  *
22f7018c21STomi Valkeinen  *		  Paul Mackerras <paulus@samba.org>
23f7018c21STomi Valkeinen  *			- Convert to new framebuffer API,
24f7018c21STomi Valkeinen  *			  fix colormap setting at 16 bits/pixel (565)
25f7018c21STomi Valkeinen  *
26f7018c21STomi Valkeinen  *		  Paul Mundt
27f7018c21STomi Valkeinen  *		  	- PCI hotplug
28f7018c21STomi Valkeinen  *
29f7018c21STomi Valkeinen  *		  Jon Smirl <jonsmirl@yahoo.com>
30f7018c21STomi Valkeinen  * 			- PCI ID update
31f7018c21STomi Valkeinen  * 			- replace ROM BIOS search
32f7018c21STomi Valkeinen  *
33f7018c21STomi Valkeinen  *  Based off of Geert's atyfb.c and vfb.c.
34f7018c21STomi Valkeinen  *
35f7018c21STomi Valkeinen  *  TODO:
36f7018c21STomi Valkeinen  *		- monitor sensing (DDC)
37f7018c21STomi Valkeinen  *              - virtual display
38f7018c21STomi Valkeinen  *		- other platform support (only ppc/x86 supported)
39f7018c21STomi Valkeinen  *		- hardware cursor support
40f7018c21STomi Valkeinen  *
41f7018c21STomi Valkeinen  *    Please cc: your patches to brad@neruo.com.
42f7018c21STomi Valkeinen  */
43f7018c21STomi Valkeinen 
44f7018c21STomi Valkeinen /*
45f7018c21STomi Valkeinen  * A special note of gratitude to ATI's devrel for providing documentation,
46f7018c21STomi Valkeinen  * example code and hardware. Thanks Nitya.	-atong and brad
47f7018c21STomi Valkeinen  */
48f7018c21STomi Valkeinen 
49f7018c21STomi Valkeinen 
50145eed48SThomas Zimmermann #include <linux/aperture.h>
51f7018c21STomi Valkeinen #include <linux/module.h>
52f7018c21STomi Valkeinen #include <linux/moduleparam.h>
53f7018c21STomi Valkeinen #include <linux/kernel.h>
54f7018c21STomi Valkeinen #include <linux/errno.h>
55f7018c21STomi Valkeinen #include <linux/string.h>
56f7018c21STomi Valkeinen #include <linux/mm.h>
57f7018c21STomi Valkeinen #include <linux/vmalloc.h>
58f7018c21STomi Valkeinen #include <linux/delay.h>
59f7018c21STomi Valkeinen #include <linux/interrupt.h>
60f7018c21STomi Valkeinen #include <linux/uaccess.h>
61f7018c21STomi Valkeinen #include <linux/fb.h>
62f7018c21STomi Valkeinen #include <linux/init.h>
63f7018c21STomi Valkeinen #include <linux/pci.h>
64f7018c21STomi Valkeinen #include <linux/ioport.h>
65f7018c21STomi Valkeinen #include <linux/console.h>
66f7018c21STomi Valkeinen #include <linux/backlight.h>
67f7018c21STomi Valkeinen #include <asm/io.h>
68f7018c21STomi Valkeinen 
69f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
70f7018c21STomi Valkeinen #include <asm/machdep.h>
71f7018c21STomi Valkeinen #include <asm/pmac_feature.h>
72f7018c21STomi Valkeinen #include "../macmodes.h"
73f7018c21STomi Valkeinen #endif
74f7018c21STomi Valkeinen 
75f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
76f7018c21STomi Valkeinen #include <asm/backlight.h>
77f7018c21STomi Valkeinen #endif
78f7018c21STomi Valkeinen 
79f7018c21STomi Valkeinen #ifdef CONFIG_BOOTX_TEXT
80f7018c21STomi Valkeinen #include <asm/btext.h>
81f7018c21STomi Valkeinen #endif /* CONFIG_BOOTX_TEXT */
82f7018c21STomi Valkeinen 
83f7018c21STomi Valkeinen #include <video/aty128.h>
84f7018c21STomi Valkeinen 
85f7018c21STomi Valkeinen /* Debug flag */
86f7018c21STomi Valkeinen #undef DEBUG
87f7018c21STomi Valkeinen 
88f7018c21STomi Valkeinen #ifdef DEBUG
89f7018c21STomi Valkeinen #define DBG(fmt, args...) \
90f7018c21STomi Valkeinen 	printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
91f7018c21STomi Valkeinen #else
92f7018c21STomi Valkeinen #define DBG(fmt, args...)
93f7018c21STomi Valkeinen #endif
94f7018c21STomi Valkeinen 
95f7018c21STomi Valkeinen #ifndef CONFIG_PPC_PMAC
96f7018c21STomi Valkeinen /* default mode */
97ca9384c5SJulia Lawall static const struct fb_var_screeninfo default_var = {
98f7018c21STomi Valkeinen 	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
99f7018c21STomi Valkeinen 	640, 480, 640, 480, 0, 0, 8, 0,
100f7018c21STomi Valkeinen 	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
101f7018c21STomi Valkeinen 	0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
102f7018c21STomi Valkeinen 	0, FB_VMODE_NONINTERLACED
103f7018c21STomi Valkeinen };
104f7018c21STomi Valkeinen 
105f7018c21STomi Valkeinen #else /* CONFIG_PPC_PMAC */
106f7018c21STomi Valkeinen /* default to 1024x768 at 75Hz on PPC - this will work
107f7018c21STomi Valkeinen  * on the iMac, the usual 640x480 @ 60Hz doesn't. */
108ca9384c5SJulia Lawall static const struct fb_var_screeninfo default_var = {
109f7018c21STomi Valkeinen 	/* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
110f7018c21STomi Valkeinen 	1024, 768, 1024, 768, 0, 0, 8, 0,
111f7018c21STomi Valkeinen 	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
112f7018c21STomi Valkeinen 	0, 0, -1, -1, 0, 12699, 160, 32, 28, 1, 96, 3,
113f7018c21STomi Valkeinen 	FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
114f7018c21STomi Valkeinen 	FB_VMODE_NONINTERLACED
115f7018c21STomi Valkeinen };
116f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
117f7018c21STomi Valkeinen 
118f7018c21STomi Valkeinen /* default modedb mode */
119f7018c21STomi Valkeinen /* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
12058ec01ceSBhumika Goyal static const struct fb_videomode defaultmode = {
121f7018c21STomi Valkeinen 	.refresh =	60,
122f7018c21STomi Valkeinen 	.xres =		640,
123f7018c21STomi Valkeinen 	.yres =		480,
124f7018c21STomi Valkeinen 	.pixclock =	39722,
125f7018c21STomi Valkeinen 	.left_margin =	48,
126f7018c21STomi Valkeinen 	.right_margin =	16,
127f7018c21STomi Valkeinen 	.upper_margin =	33,
128f7018c21STomi Valkeinen 	.lower_margin =	10,
129f7018c21STomi Valkeinen 	.hsync_len =	96,
130f7018c21STomi Valkeinen 	.vsync_len =	2,
131f7018c21STomi Valkeinen 	.sync =		0,
132f7018c21STomi Valkeinen 	.vmode =	FB_VMODE_NONINTERLACED
133f7018c21STomi Valkeinen };
134f7018c21STomi Valkeinen 
135f7018c21STomi Valkeinen /* Chip generations */
136f7018c21STomi Valkeinen enum {
137f7018c21STomi Valkeinen 	rage_128,
138f7018c21STomi Valkeinen 	rage_128_pci,
139f7018c21STomi Valkeinen 	rage_128_pro,
140f7018c21STomi Valkeinen 	rage_128_pro_pci,
141f7018c21STomi Valkeinen 	rage_M3,
142f7018c21STomi Valkeinen 	rage_M3_pci,
143f7018c21STomi Valkeinen 	rage_M4,
144f7018c21STomi Valkeinen 	rage_128_ultra,
145f7018c21STomi Valkeinen };
146f7018c21STomi Valkeinen 
147f7018c21STomi Valkeinen /* Must match above enum */
148f7018c21STomi Valkeinen static char * const r128_family[] = {
149f7018c21STomi Valkeinen 	"AGP",
150f7018c21STomi Valkeinen 	"PCI",
151f7018c21STomi Valkeinen 	"PRO AGP",
152f7018c21STomi Valkeinen 	"PRO PCI",
153f7018c21STomi Valkeinen 	"M3 AGP",
154f7018c21STomi Valkeinen 	"M3 PCI",
155f7018c21STomi Valkeinen 	"M4 AGP",
156f7018c21STomi Valkeinen 	"Ultra AGP",
157f7018c21STomi Valkeinen };
158f7018c21STomi Valkeinen 
159f7018c21STomi Valkeinen /*
160f7018c21STomi Valkeinen  * PCI driver prototypes
161f7018c21STomi Valkeinen  */
162f7018c21STomi Valkeinen static int aty128_probe(struct pci_dev *pdev,
163f7018c21STomi Valkeinen                                const struct pci_device_id *ent);
164f7018c21STomi Valkeinen static void aty128_remove(struct pci_dev *pdev);
165c1a47776SVaibhav Gupta static int aty128_pci_suspend_late(struct device *dev, pm_message_t state);
166c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_suspend(struct device *dev);
167c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_hibernate(struct device *dev);
168c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_freeze(struct device *dev);
169c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_resume(struct device *dev);
170f7018c21STomi Valkeinen static int aty128_do_resume(struct pci_dev *pdev);
171f7018c21STomi Valkeinen 
172c1a47776SVaibhav Gupta static const struct dev_pm_ops aty128_pci_pm_ops = {
173c1a47776SVaibhav Gupta 	.suspend	= aty128_pci_suspend,
174c1a47776SVaibhav Gupta 	.resume		= aty128_pci_resume,
175c1a47776SVaibhav Gupta 	.freeze		= aty128_pci_freeze,
176c1a47776SVaibhav Gupta 	.thaw		= aty128_pci_resume,
177c1a47776SVaibhav Gupta 	.poweroff	= aty128_pci_hibernate,
178c1a47776SVaibhav Gupta 	.restore	= aty128_pci_resume,
179c1a47776SVaibhav Gupta };
180c1a47776SVaibhav Gupta 
181f7018c21STomi Valkeinen /* supported Rage128 chipsets */
182c622d7a3SArvind Yadav static const struct pci_device_id aty128_pci_tbl[] = {
183f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE,
184f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci },
185f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF,
186f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 },
187f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF,
188f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
189f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML,
190f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
191f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA,
192f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
193f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB,
194f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
195f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC,
196f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
197f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD,
198f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
199f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE,
200f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
201f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF,
202f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
203f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG,
204f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
205f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH,
206f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
207f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI,
208f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
209f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ,
210f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
211f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK,
212f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
213f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL,
214f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
215f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM,
216f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
217f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN,
218f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
219f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO,
220f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
221f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP,
222f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
223f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ,
224f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
225f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR,
226f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
227f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS,
228f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
229f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT,
230f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
231f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU,
232f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
233f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV,
234f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
235f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW,
236f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
237f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX,
238f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
239f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE,
240f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
241f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF,
242f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
243f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG,
244f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
245f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK,
246f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
247f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL,
248f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
249f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE,
250f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
251f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF,
252f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
253f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG,
254f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
255f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH,
256f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
257f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK,
258f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
259f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL,
260f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
261f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM,
262f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
263f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN,
264f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
265f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF,
266f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
267f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL,
268f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
269f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR,
270f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
271f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS,
272f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
273f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT,
274f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
275f7018c21STomi Valkeinen 	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU,
276f7018c21STomi Valkeinen 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
277f7018c21STomi Valkeinen 	{ 0, }
278f7018c21STomi Valkeinen };
279f7018c21STomi Valkeinen 
280f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, aty128_pci_tbl);
281f7018c21STomi Valkeinen 
282f7018c21STomi Valkeinen static struct pci_driver aty128fb_driver = {
283f7018c21STomi Valkeinen 	.name		= "aty128fb",
284f7018c21STomi Valkeinen 	.id_table	= aty128_pci_tbl,
285f7018c21STomi Valkeinen 	.probe		= aty128_probe,
286f7018c21STomi Valkeinen 	.remove		= aty128_remove,
287c1a47776SVaibhav Gupta 	.driver.pm	= &aty128_pci_pm_ops,
288f7018c21STomi Valkeinen };
289f7018c21STomi Valkeinen 
290f7018c21STomi Valkeinen /* packed BIOS settings */
291f7018c21STomi Valkeinen #ifndef CONFIG_PPC
292f7018c21STomi Valkeinen typedef struct {
293f7018c21STomi Valkeinen 	u8 clock_chip_type;
294f7018c21STomi Valkeinen 	u8 struct_size;
295f7018c21STomi Valkeinen 	u8 accelerator_entry;
296f7018c21STomi Valkeinen 	u8 VGA_entry;
297f7018c21STomi Valkeinen 	u16 VGA_table_offset;
298f7018c21STomi Valkeinen 	u16 POST_table_offset;
299f7018c21STomi Valkeinen 	u16 XCLK;
300f7018c21STomi Valkeinen 	u16 MCLK;
301f7018c21STomi Valkeinen 	u8 num_PLL_blocks;
302f7018c21STomi Valkeinen 	u8 size_PLL_blocks;
303f7018c21STomi Valkeinen 	u16 PCLK_ref_freq;
304f7018c21STomi Valkeinen 	u16 PCLK_ref_divider;
305f7018c21STomi Valkeinen 	u32 PCLK_min_freq;
306f7018c21STomi Valkeinen 	u32 PCLK_max_freq;
307f7018c21STomi Valkeinen 	u16 MCLK_ref_freq;
308f7018c21STomi Valkeinen 	u16 MCLK_ref_divider;
309f7018c21STomi Valkeinen 	u32 MCLK_min_freq;
310f7018c21STomi Valkeinen 	u32 MCLK_max_freq;
311f7018c21STomi Valkeinen 	u16 XCLK_ref_freq;
312f7018c21STomi Valkeinen 	u16 XCLK_ref_divider;
313f7018c21STomi Valkeinen 	u32 XCLK_min_freq;
314f7018c21STomi Valkeinen 	u32 XCLK_max_freq;
315f7018c21STomi Valkeinen } __attribute__ ((packed)) PLL_BLOCK;
316f7018c21STomi Valkeinen #endif /* !CONFIG_PPC */
317f7018c21STomi Valkeinen 
318f7018c21STomi Valkeinen /* onboard memory information */
319f7018c21STomi Valkeinen struct aty128_meminfo {
320f7018c21STomi Valkeinen 	u8 ML;
321f7018c21STomi Valkeinen 	u8 MB;
322f7018c21STomi Valkeinen 	u8 Trcd;
323f7018c21STomi Valkeinen 	u8 Trp;
324f7018c21STomi Valkeinen 	u8 Twr;
325f7018c21STomi Valkeinen 	u8 CL;
326f7018c21STomi Valkeinen 	u8 Tr2w;
327f7018c21STomi Valkeinen 	u8 LoopLatency;
328f7018c21STomi Valkeinen 	u8 DspOn;
329f7018c21STomi Valkeinen 	u8 Rloop;
330f7018c21STomi Valkeinen 	const char *name;
331f7018c21STomi Valkeinen };
332f7018c21STomi Valkeinen 
333f7018c21STomi Valkeinen /* various memory configurations */
33408424464SJulia Lawall static const struct aty128_meminfo sdr_128 = {
33508424464SJulia Lawall 	.ML = 4,
33608424464SJulia Lawall 	.MB = 4,
33708424464SJulia Lawall 	.Trcd = 3,
33808424464SJulia Lawall 	.Trp = 3,
33908424464SJulia Lawall 	.Twr = 1,
34008424464SJulia Lawall 	.CL = 3,
34108424464SJulia Lawall 	.Tr2w = 1,
34208424464SJulia Lawall 	.LoopLatency = 16,
34308424464SJulia Lawall 	.DspOn = 30,
34408424464SJulia Lawall 	.Rloop = 16,
34508424464SJulia Lawall 	.name = "128-bit SDR SGRAM (1:1)",
34608424464SJulia Lawall };
34708424464SJulia Lawall 
34808424464SJulia Lawall static const struct aty128_meminfo sdr_sgram = {
34908424464SJulia Lawall 	.ML = 4,
35008424464SJulia Lawall 	.MB = 4,
35108424464SJulia Lawall 	.Trcd = 1,
35208424464SJulia Lawall 	.Trp = 2,
35308424464SJulia Lawall 	.Twr = 1,
35408424464SJulia Lawall 	.CL = 2,
35508424464SJulia Lawall 	.Tr2w = 1,
35608424464SJulia Lawall 	.LoopLatency = 16,
35708424464SJulia Lawall 	.DspOn = 24,
35808424464SJulia Lawall 	.Rloop = 16,
35908424464SJulia Lawall 	.name = "64-bit SDR SGRAM (2:1)",
36008424464SJulia Lawall };
36108424464SJulia Lawall 
36208424464SJulia Lawall static const struct aty128_meminfo ddr_sgram = {
36308424464SJulia Lawall 	.ML = 4,
36408424464SJulia Lawall 	.MB = 4,
36508424464SJulia Lawall 	.Trcd = 3,
36608424464SJulia Lawall 	.Trp = 3,
36708424464SJulia Lawall 	.Twr = 2,
36808424464SJulia Lawall 	.CL = 3,
36908424464SJulia Lawall 	.Tr2w = 1,
37008424464SJulia Lawall 	.LoopLatency = 16,
37108424464SJulia Lawall 	.DspOn = 31,
37208424464SJulia Lawall 	.Rloop = 16,
37308424464SJulia Lawall 	.name = "64-bit DDR SGRAM",
37408424464SJulia Lawall };
375f7018c21STomi Valkeinen 
376ca9384c5SJulia Lawall static const struct fb_fix_screeninfo aty128fb_fix = {
377f7018c21STomi Valkeinen 	.id		= "ATY Rage128",
378f7018c21STomi Valkeinen 	.type		= FB_TYPE_PACKED_PIXELS,
379f7018c21STomi Valkeinen 	.visual		= FB_VISUAL_PSEUDOCOLOR,
380f7018c21STomi Valkeinen 	.xpanstep	= 8,
381f7018c21STomi Valkeinen 	.ypanstep	= 1,
382f7018c21STomi Valkeinen 	.mmio_len	= 0x2000,
383f7018c21STomi Valkeinen 	.accel		= FB_ACCEL_ATI_RAGE128,
384f7018c21STomi Valkeinen };
385f7018c21STomi Valkeinen 
386f7018c21STomi Valkeinen static char *mode_option = NULL;
387f7018c21STomi Valkeinen 
388f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
389f7018c21STomi Valkeinen static int default_vmode = VMODE_1024_768_60;
390f7018c21STomi Valkeinen static int default_cmode = CMODE_8;
391f7018c21STomi Valkeinen #endif
392f7018c21STomi Valkeinen 
393f7018c21STomi Valkeinen static int default_crt_on = 0;
394f7018c21STomi Valkeinen static int default_lcd_on = 1;
395f7018c21STomi Valkeinen static bool mtrr = true;
396f7018c21STomi Valkeinen 
397f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
39872081524SJoe Perches static int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT);
399f7018c21STomi Valkeinen #endif
400f7018c21STomi Valkeinen 
401f7018c21STomi Valkeinen /* PLL constants */
402f7018c21STomi Valkeinen struct aty128_constants {
403f7018c21STomi Valkeinen 	u32 ref_clk;
404f7018c21STomi Valkeinen 	u32 ppll_min;
405f7018c21STomi Valkeinen 	u32 ppll_max;
406f7018c21STomi Valkeinen 	u32 ref_divider;
407f7018c21STomi Valkeinen 	u32 xclk;
408f7018c21STomi Valkeinen 	u32 fifo_width;
409f7018c21STomi Valkeinen 	u32 fifo_depth;
410f7018c21STomi Valkeinen };
411f7018c21STomi Valkeinen 
412f7018c21STomi Valkeinen struct aty128_crtc {
413f7018c21STomi Valkeinen 	u32 gen_cntl;
414f7018c21STomi Valkeinen 	u32 h_total, h_sync_strt_wid;
415f7018c21STomi Valkeinen 	u32 v_total, v_sync_strt_wid;
416f7018c21STomi Valkeinen 	u32 pitch;
417f7018c21STomi Valkeinen 	u32 offset, offset_cntl;
418f7018c21STomi Valkeinen 	u32 xoffset, yoffset;
419f7018c21STomi Valkeinen 	u32 vxres, vyres;
420f7018c21STomi Valkeinen 	u32 depth, bpp;
421f7018c21STomi Valkeinen };
422f7018c21STomi Valkeinen 
423f7018c21STomi Valkeinen struct aty128_pll {
424f7018c21STomi Valkeinen 	u32 post_divider;
425f7018c21STomi Valkeinen 	u32 feedback_divider;
426f7018c21STomi Valkeinen 	u32 vclk;
427f7018c21STomi Valkeinen };
428f7018c21STomi Valkeinen 
429f7018c21STomi Valkeinen struct aty128_ddafifo {
430f7018c21STomi Valkeinen 	u32 dda_config;
431f7018c21STomi Valkeinen 	u32 dda_on_off;
432f7018c21STomi Valkeinen };
433f7018c21STomi Valkeinen 
434f7018c21STomi Valkeinen /* register values for a specific mode */
435f7018c21STomi Valkeinen struct aty128fb_par {
436f7018c21STomi Valkeinen 	struct aty128_crtc crtc;
437f7018c21STomi Valkeinen 	struct aty128_pll pll;
438f7018c21STomi Valkeinen 	struct aty128_ddafifo fifo_reg;
439f7018c21STomi Valkeinen 	u32 accel_flags;
440f7018c21STomi Valkeinen 	struct aty128_constants constants;  /* PLL and others      */
441f7018c21STomi Valkeinen 	void __iomem *regbase;              /* remapped mmio       */
442f7018c21STomi Valkeinen 	u32 vram_size;                      /* onboard video ram   */
443f7018c21STomi Valkeinen 	int chip_gen;
444f7018c21STomi Valkeinen 	const struct aty128_meminfo *mem;   /* onboard mem info    */
445f4447ddeSLuis R. Rodriguez 	int wc_cookie;
446f7018c21STomi Valkeinen 	int blitter_may_be_busy;
447f7018c21STomi Valkeinen 	int fifo_slots;                 /* free slots in FIFO (64 max) */
448f7018c21STomi Valkeinen 
449f7018c21STomi Valkeinen 	int crt_on, lcd_on;
450f7018c21STomi Valkeinen 	struct pci_dev *pdev;
451f7018c21STomi Valkeinen 	struct fb_info *next;
452f7018c21STomi Valkeinen 	int	asleep;
453f7018c21STomi Valkeinen 	int	lock_blank;
454f7018c21STomi Valkeinen 
455f7018c21STomi Valkeinen 	u8	red[32];		/* see aty128fb_setcolreg */
456f7018c21STomi Valkeinen 	u8	green[64];
457f7018c21STomi Valkeinen 	u8	blue[32];
458f7018c21STomi Valkeinen 	u32	pseudo_palette[16];	/* used for TRUECOLOR */
459f7018c21STomi Valkeinen };
460f7018c21STomi Valkeinen 
461f7018c21STomi Valkeinen 
462f7018c21STomi Valkeinen #define round_div(n, d) ((n+(d/2))/d)
463f7018c21STomi Valkeinen 
464f7018c21STomi Valkeinen static int aty128fb_check_var(struct fb_var_screeninfo *var,
465f7018c21STomi Valkeinen 			      struct fb_info *info);
466f7018c21STomi Valkeinen static int aty128fb_set_par(struct fb_info *info);
467f7018c21STomi Valkeinen static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
468f7018c21STomi Valkeinen 			      u_int transp, struct fb_info *info);
469f7018c21STomi Valkeinen static int aty128fb_pan_display(struct fb_var_screeninfo *var,
470f7018c21STomi Valkeinen 			   struct fb_info *fb);
471f7018c21STomi Valkeinen static int aty128fb_blank(int blank, struct fb_info *fb);
472f7018c21STomi Valkeinen static int aty128fb_ioctl(struct fb_info *info, u_int cmd, unsigned long arg);
473f7018c21STomi Valkeinen static int aty128fb_sync(struct fb_info *info);
474f7018c21STomi Valkeinen 
475f7018c21STomi Valkeinen     /*
476f7018c21STomi Valkeinen      *  Internal routines
477f7018c21STomi Valkeinen      */
478f7018c21STomi Valkeinen 
479f7018c21STomi Valkeinen static int aty128_encode_var(struct fb_var_screeninfo *var,
480f7018c21STomi Valkeinen                              const struct aty128fb_par *par);
481f7018c21STomi Valkeinen static int aty128_decode_var(struct fb_var_screeninfo *var,
482f7018c21STomi Valkeinen                              struct aty128fb_par *par);
483f7018c21STomi Valkeinen static void aty128_timings(struct aty128fb_par *par);
484f7018c21STomi Valkeinen static void aty128_init_engine(struct aty128fb_par *par);
485f7018c21STomi Valkeinen static void aty128_reset_engine(const struct aty128fb_par *par);
486f7018c21STomi Valkeinen static void aty128_flush_pixel_cache(const struct aty128fb_par *par);
487f7018c21STomi Valkeinen static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par);
488f7018c21STomi Valkeinen static void wait_for_fifo(u16 entries, struct aty128fb_par *par);
489f7018c21STomi Valkeinen static void wait_for_idle(struct aty128fb_par *par);
490f7018c21STomi Valkeinen static u32 depth_to_dst(u32 depth);
491f7018c21STomi Valkeinen 
492f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
493f7018c21STomi Valkeinen static void aty128_bl_set_power(struct fb_info *info, int power);
494f7018c21STomi Valkeinen #endif
495f7018c21STomi Valkeinen 
496f7018c21STomi Valkeinen #define BIOS_IN8(v)  	(readb(bios + (v)))
497f7018c21STomi Valkeinen #define BIOS_IN16(v) 	(readb(bios + (v)) | \
498f7018c21STomi Valkeinen 			  (readb(bios + (v) + 1) << 8))
499f7018c21STomi Valkeinen #define BIOS_IN32(v) 	(readb(bios + (v)) | \
500f7018c21STomi Valkeinen 			  (readb(bios + (v) + 1) << 8) | \
501f7018c21STomi Valkeinen 			  (readb(bios + (v) + 2) << 16) | \
502f7018c21STomi Valkeinen 			  (readb(bios + (v) + 3) << 24))
503f7018c21STomi Valkeinen 
504f7018c21STomi Valkeinen 
5058a48ac33SJani Nikula static const struct fb_ops aty128fb_ops = {
506f7018c21STomi Valkeinen 	.owner		= THIS_MODULE,
507*9b7ee467SThomas Zimmermann 	FB_DEFAULT_IOMEM_OPS,
508f7018c21STomi Valkeinen 	.fb_check_var	= aty128fb_check_var,
509f7018c21STomi Valkeinen 	.fb_set_par	= aty128fb_set_par,
510f7018c21STomi Valkeinen 	.fb_setcolreg	= aty128fb_setcolreg,
511f7018c21STomi Valkeinen 	.fb_pan_display = aty128fb_pan_display,
512f7018c21STomi Valkeinen 	.fb_blank	= aty128fb_blank,
513f7018c21STomi Valkeinen 	.fb_ioctl	= aty128fb_ioctl,
514f7018c21STomi Valkeinen 	.fb_sync	= aty128fb_sync,
515f7018c21STomi Valkeinen };
516f7018c21STomi Valkeinen 
517f7018c21STomi Valkeinen     /*
518f7018c21STomi Valkeinen      * Functions to read from/write to the mmio registers
519f7018c21STomi Valkeinen      *	- endian conversions may possibly be avoided by
520f7018c21STomi Valkeinen      *    using the other register aperture. TODO.
521f7018c21STomi Valkeinen      */
_aty_ld_le32(volatile unsigned int regindex,const struct aty128fb_par * par)522f7018c21STomi Valkeinen static inline u32 _aty_ld_le32(volatile unsigned int regindex,
523f7018c21STomi Valkeinen 			       const struct aty128fb_par *par)
524f7018c21STomi Valkeinen {
525f7018c21STomi Valkeinen 	return readl (par->regbase + regindex);
526f7018c21STomi Valkeinen }
527f7018c21STomi Valkeinen 
_aty_st_le32(volatile unsigned int regindex,u32 val,const struct aty128fb_par * par)528f7018c21STomi Valkeinen static inline void _aty_st_le32(volatile unsigned int regindex, u32 val,
529f7018c21STomi Valkeinen 				const struct aty128fb_par *par)
530f7018c21STomi Valkeinen {
531f7018c21STomi Valkeinen 	writel (val, par->regbase + regindex);
532f7018c21STomi Valkeinen }
533f7018c21STomi Valkeinen 
_aty_ld_8(unsigned int regindex,const struct aty128fb_par * par)534f7018c21STomi Valkeinen static inline u8 _aty_ld_8(unsigned int regindex,
535f7018c21STomi Valkeinen 			   const struct aty128fb_par *par)
536f7018c21STomi Valkeinen {
537f7018c21STomi Valkeinen 	return readb (par->regbase + regindex);
538f7018c21STomi Valkeinen }
539f7018c21STomi Valkeinen 
_aty_st_8(unsigned int regindex,u8 val,const struct aty128fb_par * par)540f7018c21STomi Valkeinen static inline void _aty_st_8(unsigned int regindex, u8 val,
541f7018c21STomi Valkeinen 			     const struct aty128fb_par *par)
542f7018c21STomi Valkeinen {
543f7018c21STomi Valkeinen 	writeb (val, par->regbase + regindex);
544f7018c21STomi Valkeinen }
545f7018c21STomi Valkeinen 
546f7018c21STomi Valkeinen #define aty_ld_le32(regindex)		_aty_ld_le32(regindex, par)
547f7018c21STomi Valkeinen #define aty_st_le32(regindex, val)	_aty_st_le32(regindex, val, par)
548f7018c21STomi Valkeinen #define aty_ld_8(regindex)		_aty_ld_8(regindex, par)
549f7018c21STomi Valkeinen #define aty_st_8(regindex, val)		_aty_st_8(regindex, val, par)
550f7018c21STomi Valkeinen 
551f7018c21STomi Valkeinen     /*
552f7018c21STomi Valkeinen      * Functions to read from/write to the pll registers
553f7018c21STomi Valkeinen      */
554f7018c21STomi Valkeinen 
555f7018c21STomi Valkeinen #define aty_ld_pll(pll_index)		_aty_ld_pll(pll_index, par)
556f7018c21STomi Valkeinen #define aty_st_pll(pll_index, val)	_aty_st_pll(pll_index, val, par)
557f7018c21STomi Valkeinen 
558f7018c21STomi Valkeinen 
_aty_ld_pll(unsigned int pll_index,const struct aty128fb_par * par)559f7018c21STomi Valkeinen static u32 _aty_ld_pll(unsigned int pll_index,
560f7018c21STomi Valkeinen 		       const struct aty128fb_par *par)
561f7018c21STomi Valkeinen {
562f7018c21STomi Valkeinen 	aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F);
563f7018c21STomi Valkeinen 	return aty_ld_le32(CLOCK_CNTL_DATA);
564f7018c21STomi Valkeinen }
565f7018c21STomi Valkeinen 
566f7018c21STomi Valkeinen 
_aty_st_pll(unsigned int pll_index,u32 val,const struct aty128fb_par * par)567f7018c21STomi Valkeinen static void _aty_st_pll(unsigned int pll_index, u32 val,
568f7018c21STomi Valkeinen 			const struct aty128fb_par *par)
569f7018c21STomi Valkeinen {
570f7018c21STomi Valkeinen 	aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN);
571f7018c21STomi Valkeinen 	aty_st_le32(CLOCK_CNTL_DATA, val);
572f7018c21STomi Valkeinen }
573f7018c21STomi Valkeinen 
574f7018c21STomi Valkeinen 
575f7018c21STomi Valkeinen /* return true when the PLL has completed an atomic update */
aty_pll_readupdate(const struct aty128fb_par * par)576f7018c21STomi Valkeinen static int aty_pll_readupdate(const struct aty128fb_par *par)
577f7018c21STomi Valkeinen {
578f7018c21STomi Valkeinen 	return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R);
579f7018c21STomi Valkeinen }
580f7018c21STomi Valkeinen 
581f7018c21STomi Valkeinen 
aty_pll_wait_readupdate(const struct aty128fb_par * par)582f7018c21STomi Valkeinen static void aty_pll_wait_readupdate(const struct aty128fb_par *par)
583f7018c21STomi Valkeinen {
584f7018c21STomi Valkeinen 	unsigned long timeout = jiffies + HZ/100; // should be more than enough
585f7018c21STomi Valkeinen 	int reset = 1;
586f7018c21STomi Valkeinen 
587f7018c21STomi Valkeinen 	while (time_before(jiffies, timeout))
588f7018c21STomi Valkeinen 		if (aty_pll_readupdate(par)) {
589f7018c21STomi Valkeinen 			reset = 0;
590f7018c21STomi Valkeinen 			break;
591f7018c21STomi Valkeinen 		}
592f7018c21STomi Valkeinen 
593f7018c21STomi Valkeinen 	if (reset)	/* reset engine?? */
594f7018c21STomi Valkeinen 		printk(KERN_DEBUG "aty128fb: PLL write timeout!\n");
595f7018c21STomi Valkeinen }
596f7018c21STomi Valkeinen 
597f7018c21STomi Valkeinen 
598f7018c21STomi Valkeinen /* tell PLL to update */
aty_pll_writeupdate(const struct aty128fb_par * par)599f7018c21STomi Valkeinen static void aty_pll_writeupdate(const struct aty128fb_par *par)
600f7018c21STomi Valkeinen {
601f7018c21STomi Valkeinen 	aty_pll_wait_readupdate(par);
602f7018c21STomi Valkeinen 
603f7018c21STomi Valkeinen 	aty_st_pll(PPLL_REF_DIV,
604f7018c21STomi Valkeinen 		   aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W);
605f7018c21STomi Valkeinen }
606f7018c21STomi Valkeinen 
607f7018c21STomi Valkeinen 
608f7018c21STomi Valkeinen /* write to the scratch register to test r/w functionality */
register_test(const struct aty128fb_par * par)609f7018c21STomi Valkeinen static int register_test(const struct aty128fb_par *par)
610f7018c21STomi Valkeinen {
611f7018c21STomi Valkeinen 	u32 val;
612f7018c21STomi Valkeinen 	int flag = 0;
613f7018c21STomi Valkeinen 
614f7018c21STomi Valkeinen 	val = aty_ld_le32(BIOS_0_SCRATCH);
615f7018c21STomi Valkeinen 
616f7018c21STomi Valkeinen 	aty_st_le32(BIOS_0_SCRATCH, 0x55555555);
617f7018c21STomi Valkeinen 	if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) {
618f7018c21STomi Valkeinen 		aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA);
619f7018c21STomi Valkeinen 
620f7018c21STomi Valkeinen 		if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA)
621f7018c21STomi Valkeinen 			flag = 1;
622f7018c21STomi Valkeinen 	}
623f7018c21STomi Valkeinen 
624f7018c21STomi Valkeinen 	aty_st_le32(BIOS_0_SCRATCH, val);	// restore value
625f7018c21STomi Valkeinen 	return flag;
626f7018c21STomi Valkeinen }
627f7018c21STomi Valkeinen 
628f7018c21STomi Valkeinen 
629f7018c21STomi Valkeinen /*
630f7018c21STomi Valkeinen  * Accelerator engine functions
631f7018c21STomi Valkeinen  */
do_wait_for_fifo(u16 entries,struct aty128fb_par * par)632f7018c21STomi Valkeinen static void do_wait_for_fifo(u16 entries, struct aty128fb_par *par)
633f7018c21STomi Valkeinen {
634f7018c21STomi Valkeinen 	int i;
635f7018c21STomi Valkeinen 
636f7018c21STomi Valkeinen 	for (;;) {
637f7018c21STomi Valkeinen 		for (i = 0; i < 2000000; i++) {
638f7018c21STomi Valkeinen 			par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff;
639f7018c21STomi Valkeinen 			if (par->fifo_slots >= entries)
640f7018c21STomi Valkeinen 				return;
641f7018c21STomi Valkeinen 		}
642f7018c21STomi Valkeinen 		aty128_reset_engine(par);
643f7018c21STomi Valkeinen 	}
644f7018c21STomi Valkeinen }
645f7018c21STomi Valkeinen 
646f7018c21STomi Valkeinen 
wait_for_idle(struct aty128fb_par * par)647f7018c21STomi Valkeinen static void wait_for_idle(struct aty128fb_par *par)
648f7018c21STomi Valkeinen {
649f7018c21STomi Valkeinen 	int i;
650f7018c21STomi Valkeinen 
651f7018c21STomi Valkeinen 	do_wait_for_fifo(64, par);
652f7018c21STomi Valkeinen 
653f7018c21STomi Valkeinen 	for (;;) {
654f7018c21STomi Valkeinen 		for (i = 0; i < 2000000; i++) {
655f7018c21STomi Valkeinen 			if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) {
656f7018c21STomi Valkeinen 				aty128_flush_pixel_cache(par);
657f7018c21STomi Valkeinen 				par->blitter_may_be_busy = 0;
658f7018c21STomi Valkeinen 				return;
659f7018c21STomi Valkeinen 			}
660f7018c21STomi Valkeinen 		}
661f7018c21STomi Valkeinen 		aty128_reset_engine(par);
662f7018c21STomi Valkeinen 	}
663f7018c21STomi Valkeinen }
664f7018c21STomi Valkeinen 
665f7018c21STomi Valkeinen 
wait_for_fifo(u16 entries,struct aty128fb_par * par)666f7018c21STomi Valkeinen static void wait_for_fifo(u16 entries, struct aty128fb_par *par)
667f7018c21STomi Valkeinen {
668f7018c21STomi Valkeinen 	if (par->fifo_slots < entries)
669f7018c21STomi Valkeinen 		do_wait_for_fifo(64, par);
670f7018c21STomi Valkeinen 	par->fifo_slots -= entries;
671f7018c21STomi Valkeinen }
672f7018c21STomi Valkeinen 
673f7018c21STomi Valkeinen 
aty128_flush_pixel_cache(const struct aty128fb_par * par)674f7018c21STomi Valkeinen static void aty128_flush_pixel_cache(const struct aty128fb_par *par)
675f7018c21STomi Valkeinen {
676f7018c21STomi Valkeinen 	int i;
677f7018c21STomi Valkeinen 	u32 tmp;
678f7018c21STomi Valkeinen 
679f7018c21STomi Valkeinen 	tmp = aty_ld_le32(PC_NGUI_CTLSTAT);
680f7018c21STomi Valkeinen 	tmp &= ~(0x00ff);
681f7018c21STomi Valkeinen 	tmp |= 0x00ff;
682f7018c21STomi Valkeinen 	aty_st_le32(PC_NGUI_CTLSTAT, tmp);
683f7018c21STomi Valkeinen 
684f7018c21STomi Valkeinen 	for (i = 0; i < 2000000; i++)
685f7018c21STomi Valkeinen 		if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY))
686f7018c21STomi Valkeinen 			break;
687f7018c21STomi Valkeinen }
688f7018c21STomi Valkeinen 
689f7018c21STomi Valkeinen 
aty128_reset_engine(const struct aty128fb_par * par)690f7018c21STomi Valkeinen static void aty128_reset_engine(const struct aty128fb_par *par)
691f7018c21STomi Valkeinen {
692f7018c21STomi Valkeinen 	u32 gen_reset_cntl, clock_cntl_index, mclk_cntl;
693f7018c21STomi Valkeinen 
694f7018c21STomi Valkeinen 	aty128_flush_pixel_cache(par);
695f7018c21STomi Valkeinen 
696f7018c21STomi Valkeinen 	clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX);
697f7018c21STomi Valkeinen 	mclk_cntl = aty_ld_pll(MCLK_CNTL);
698f7018c21STomi Valkeinen 
699f7018c21STomi Valkeinen 	aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000);
700f7018c21STomi Valkeinen 
701f7018c21STomi Valkeinen 	gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL);
702f7018c21STomi Valkeinen 	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI);
703f7018c21STomi Valkeinen 	aty_ld_le32(GEN_RESET_CNTL);
704f7018c21STomi Valkeinen 	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI));
705f7018c21STomi Valkeinen 	aty_ld_le32(GEN_RESET_CNTL);
706f7018c21STomi Valkeinen 
707f7018c21STomi Valkeinen 	aty_st_pll(MCLK_CNTL, mclk_cntl);
708f7018c21STomi Valkeinen 	aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index);
709f7018c21STomi Valkeinen 	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl);
710f7018c21STomi Valkeinen 
711f7018c21STomi Valkeinen 	/* use old pio mode */
712f7018c21STomi Valkeinen 	aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4);
713f7018c21STomi Valkeinen 
714f7018c21STomi Valkeinen 	DBG("engine reset");
715f7018c21STomi Valkeinen }
716f7018c21STomi Valkeinen 
717f7018c21STomi Valkeinen 
aty128_init_engine(struct aty128fb_par * par)718f7018c21STomi Valkeinen static void aty128_init_engine(struct aty128fb_par *par)
719f7018c21STomi Valkeinen {
720f7018c21STomi Valkeinen 	u32 pitch_value;
721f7018c21STomi Valkeinen 
722f7018c21STomi Valkeinen 	wait_for_idle(par);
723f7018c21STomi Valkeinen 
724f7018c21STomi Valkeinen 	/* 3D scaler not spoken here */
725f7018c21STomi Valkeinen 	wait_for_fifo(1, par);
726f7018c21STomi Valkeinen 	aty_st_le32(SCALE_3D_CNTL, 0x00000000);
727f7018c21STomi Valkeinen 
728f7018c21STomi Valkeinen 	aty128_reset_engine(par);
729f7018c21STomi Valkeinen 
730f7018c21STomi Valkeinen 	pitch_value = par->crtc.pitch;
731f7018c21STomi Valkeinen 	if (par->crtc.bpp == 24) {
732f7018c21STomi Valkeinen 		pitch_value = pitch_value * 3;
733f7018c21STomi Valkeinen 	}
734f7018c21STomi Valkeinen 
735f7018c21STomi Valkeinen 	wait_for_fifo(4, par);
736f7018c21STomi Valkeinen 	/* setup engine offset registers */
737f7018c21STomi Valkeinen 	aty_st_le32(DEFAULT_OFFSET, 0x00000000);
738f7018c21STomi Valkeinen 
739f7018c21STomi Valkeinen 	/* setup engine pitch registers */
740f7018c21STomi Valkeinen 	aty_st_le32(DEFAULT_PITCH, pitch_value);
741f7018c21STomi Valkeinen 
742f7018c21STomi Valkeinen 	/* set the default scissor register to max dimensions */
743f7018c21STomi Valkeinen 	aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF);
744f7018c21STomi Valkeinen 
745f7018c21STomi Valkeinen 	/* set the drawing controls registers */
746f7018c21STomi Valkeinen 	aty_st_le32(DP_GUI_MASTER_CNTL,
747f7018c21STomi Valkeinen 		    GMC_SRC_PITCH_OFFSET_DEFAULT		|
748f7018c21STomi Valkeinen 		    GMC_DST_PITCH_OFFSET_DEFAULT		|
749f7018c21STomi Valkeinen 		    GMC_SRC_CLIP_DEFAULT			|
750f7018c21STomi Valkeinen 		    GMC_DST_CLIP_DEFAULT			|
751f7018c21STomi Valkeinen 		    GMC_BRUSH_SOLIDCOLOR			|
752f7018c21STomi Valkeinen 		    (depth_to_dst(par->crtc.depth) << 8)	|
753f7018c21STomi Valkeinen 		    GMC_SRC_DSTCOLOR			|
754f7018c21STomi Valkeinen 		    GMC_BYTE_ORDER_MSB_TO_LSB		|
755f7018c21STomi Valkeinen 		    GMC_DP_CONVERSION_TEMP_6500		|
756f7018c21STomi Valkeinen 		    ROP3_PATCOPY				|
757f7018c21STomi Valkeinen 		    GMC_DP_SRC_RECT				|
758f7018c21STomi Valkeinen 		    GMC_3D_FCN_EN_CLR			|
759f7018c21STomi Valkeinen 		    GMC_DST_CLR_CMP_FCN_CLEAR		|
760f7018c21STomi Valkeinen 		    GMC_AUX_CLIP_CLEAR			|
761f7018c21STomi Valkeinen 		    GMC_WRITE_MASK_SET);
762f7018c21STomi Valkeinen 
763f7018c21STomi Valkeinen 	wait_for_fifo(8, par);
764f7018c21STomi Valkeinen 	/* clear the line drawing registers */
765f7018c21STomi Valkeinen 	aty_st_le32(DST_BRES_ERR, 0);
766f7018c21STomi Valkeinen 	aty_st_le32(DST_BRES_INC, 0);
767f7018c21STomi Valkeinen 	aty_st_le32(DST_BRES_DEC, 0);
768f7018c21STomi Valkeinen 
769f7018c21STomi Valkeinen 	/* set brush color registers */
770f7018c21STomi Valkeinen 	aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */
771f7018c21STomi Valkeinen 	aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */
772f7018c21STomi Valkeinen 
773f7018c21STomi Valkeinen 	/* set source color registers */
774f7018c21STomi Valkeinen 	aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF);   /* white */
775f7018c21STomi Valkeinen 	aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000);   /* black */
776f7018c21STomi Valkeinen 
777f7018c21STomi Valkeinen 	/* default write mask */
778f7018c21STomi Valkeinen 	aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF);
779f7018c21STomi Valkeinen 
780f7018c21STomi Valkeinen 	/* Wait for all the writes to be completed before returning */
781f7018c21STomi Valkeinen 	wait_for_idle(par);
782f7018c21STomi Valkeinen }
783f7018c21STomi Valkeinen 
784f7018c21STomi Valkeinen 
785f7018c21STomi Valkeinen /* convert depth values to their register representation */
depth_to_dst(u32 depth)786f7018c21STomi Valkeinen static u32 depth_to_dst(u32 depth)
787f7018c21STomi Valkeinen {
788f7018c21STomi Valkeinen 	if (depth <= 8)
789f7018c21STomi Valkeinen 		return DST_8BPP;
790f7018c21STomi Valkeinen 	else if (depth <= 15)
791f7018c21STomi Valkeinen 		return DST_15BPP;
792f7018c21STomi Valkeinen 	else if (depth == 16)
793f7018c21STomi Valkeinen 		return DST_16BPP;
794f7018c21STomi Valkeinen 	else if (depth <= 24)
795f7018c21STomi Valkeinen 		return DST_24BPP;
796f7018c21STomi Valkeinen 	else if (depth <= 32)
797f7018c21STomi Valkeinen 		return DST_32BPP;
798f7018c21STomi Valkeinen 
799f7018c21STomi Valkeinen 	return -EINVAL;
800f7018c21STomi Valkeinen }
801f7018c21STomi Valkeinen 
802f7018c21STomi Valkeinen /*
803f7018c21STomi Valkeinen  * PLL informations retreival
804f7018c21STomi Valkeinen  */
805f7018c21STomi Valkeinen 
806f7018c21STomi Valkeinen 
807f7018c21STomi Valkeinen #ifndef __sparc__
aty128_map_ROM(const struct aty128fb_par * par,struct pci_dev * dev)808f7018c21STomi Valkeinen static void __iomem *aty128_map_ROM(const struct aty128fb_par *par,
809f7018c21STomi Valkeinen 				    struct pci_dev *dev)
810f7018c21STomi Valkeinen {
811f7018c21STomi Valkeinen 	u16 dptr;
812f7018c21STomi Valkeinen 	u8 rom_type;
813f7018c21STomi Valkeinen 	void __iomem *bios;
814f7018c21STomi Valkeinen 	size_t rom_size;
815f7018c21STomi Valkeinen 
816f7018c21STomi Valkeinen     	/* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */
817f7018c21STomi Valkeinen     	unsigned int temp;
818f7018c21STomi Valkeinen 	temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
819f7018c21STomi Valkeinen 	temp &= 0x00ffffffu;
820f7018c21STomi Valkeinen 	temp |= 0x04 << 24;
821f7018c21STomi Valkeinen 	aty_st_le32(RAGE128_MPP_TB_CONFIG, temp);
822f7018c21STomi Valkeinen 	temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
823f7018c21STomi Valkeinen 
824f7018c21STomi Valkeinen 	bios = pci_map_rom(dev, &rom_size);
825f7018c21STomi Valkeinen 
826f7018c21STomi Valkeinen 	if (!bios) {
827f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: ROM failed to map\n");
828f7018c21STomi Valkeinen 		return NULL;
829f7018c21STomi Valkeinen 	}
830f7018c21STomi Valkeinen 
831f7018c21STomi Valkeinen 	/* Very simple test to make sure it appeared */
832f7018c21STomi Valkeinen 	if (BIOS_IN16(0) != 0xaa55) {
833f7018c21STomi Valkeinen 		printk(KERN_DEBUG "aty128fb: Invalid ROM signature %x should "
834f7018c21STomi Valkeinen 			" be 0xaa55\n", BIOS_IN16(0));
835f7018c21STomi Valkeinen 		goto failed;
836f7018c21STomi Valkeinen 	}
837f7018c21STomi Valkeinen 
838f7018c21STomi Valkeinen 	/* Look for the PCI data to check the ROM type */
839f7018c21STomi Valkeinen 	dptr = BIOS_IN16(0x18);
840f7018c21STomi Valkeinen 
841f7018c21STomi Valkeinen 	/* Check the PCI data signature. If it's wrong, we still assume a normal
842f7018c21STomi Valkeinen 	 * x86 ROM for now, until I've verified this works everywhere.
843f7018c21STomi Valkeinen 	 * The goal here is more to phase out Open Firmware images.
844f7018c21STomi Valkeinen 	 *
845f7018c21STomi Valkeinen 	 * Currently, we only look at the first PCI data, we could iteratre and
846f7018c21STomi Valkeinen 	 * deal with them all, and we should use fb_bios_start relative to start
847f7018c21STomi Valkeinen 	 * of image and not relative start of ROM, but so far, I never found a
848f7018c21STomi Valkeinen 	 * dual-image ATI card.
849f7018c21STomi Valkeinen 	 *
850f7018c21STomi Valkeinen 	 * typedef struct {
851f7018c21STomi Valkeinen 	 * 	u32	signature;	+ 0x00
852f7018c21STomi Valkeinen 	 * 	u16	vendor;		+ 0x04
853f7018c21STomi Valkeinen 	 * 	u16	device;		+ 0x06
854f7018c21STomi Valkeinen 	 * 	u16	reserved_1;	+ 0x08
855f7018c21STomi Valkeinen 	 * 	u16	dlen;		+ 0x0a
856f7018c21STomi Valkeinen 	 * 	u8	drevision;	+ 0x0c
857f7018c21STomi Valkeinen 	 * 	u8	class_hi;	+ 0x0d
858f7018c21STomi Valkeinen 	 * 	u16	class_lo;	+ 0x0e
859f7018c21STomi Valkeinen 	 * 	u16	ilen;		+ 0x10
860f7018c21STomi Valkeinen 	 * 	u16	irevision;	+ 0x12
861f7018c21STomi Valkeinen 	 * 	u8	type;		+ 0x14
862f7018c21STomi Valkeinen 	 * 	u8	indicator;	+ 0x15
863f7018c21STomi Valkeinen 	 * 	u16	reserved_2;	+ 0x16
864f7018c21STomi Valkeinen 	 * } pci_data_t;
865f7018c21STomi Valkeinen 	 */
866f7018c21STomi Valkeinen 	if (BIOS_IN32(dptr) !=  (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) {
867f7018c21STomi Valkeinen 		printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n",
868f7018c21STomi Valkeinen 		       BIOS_IN32(dptr));
869f7018c21STomi Valkeinen 		goto anyway;
870f7018c21STomi Valkeinen 	}
871f7018c21STomi Valkeinen 	rom_type = BIOS_IN8(dptr + 0x14);
872f7018c21STomi Valkeinen 	switch(rom_type) {
873f7018c21STomi Valkeinen 	case 0:
874f7018c21STomi Valkeinen 		printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n");
875f7018c21STomi Valkeinen 		break;
876f7018c21STomi Valkeinen 	case 1:
877f7018c21STomi Valkeinen 		printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n");
878f7018c21STomi Valkeinen 		goto failed;
879f7018c21STomi Valkeinen 	case 2:
880f7018c21STomi Valkeinen 		printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n");
881f7018c21STomi Valkeinen 		goto failed;
882f7018c21STomi Valkeinen 	default:
883f7018c21STomi Valkeinen 		printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n",
884f7018c21STomi Valkeinen 		       rom_type);
885f7018c21STomi Valkeinen 		goto failed;
886f7018c21STomi Valkeinen 	}
887f7018c21STomi Valkeinen  anyway:
888f7018c21STomi Valkeinen 	return bios;
889f7018c21STomi Valkeinen 
890f7018c21STomi Valkeinen  failed:
891f7018c21STomi Valkeinen 	pci_unmap_rom(dev, bios);
892f7018c21STomi Valkeinen 	return NULL;
893f7018c21STomi Valkeinen }
894f7018c21STomi Valkeinen 
aty128_get_pllinfo(struct aty128fb_par * par,unsigned char __iomem * bios)895f7018c21STomi Valkeinen static void aty128_get_pllinfo(struct aty128fb_par *par,
896f7018c21STomi Valkeinen 			       unsigned char __iomem *bios)
897f7018c21STomi Valkeinen {
898f7018c21STomi Valkeinen 	unsigned int bios_hdr;
899f7018c21STomi Valkeinen 	unsigned int bios_pll;
900f7018c21STomi Valkeinen 
901f7018c21STomi Valkeinen 	bios_hdr = BIOS_IN16(0x48);
902f7018c21STomi Valkeinen 	bios_pll = BIOS_IN16(bios_hdr + 0x30);
903f7018c21STomi Valkeinen 
904f7018c21STomi Valkeinen 	par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16);
905f7018c21STomi Valkeinen 	par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12);
906f7018c21STomi Valkeinen 	par->constants.xclk = BIOS_IN16(bios_pll + 0x08);
907f7018c21STomi Valkeinen 	par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10);
908f7018c21STomi Valkeinen 	par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e);
909f7018c21STomi Valkeinen 
910f7018c21STomi Valkeinen 	DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n",
911f7018c21STomi Valkeinen 			par->constants.ppll_max, par->constants.ppll_min,
912f7018c21STomi Valkeinen 			par->constants.xclk, par->constants.ref_divider,
913f7018c21STomi Valkeinen 			par->constants.ref_clk);
914f7018c21STomi Valkeinen 
915f7018c21STomi Valkeinen }
916f7018c21STomi Valkeinen 
917f7018c21STomi Valkeinen #ifdef CONFIG_X86
aty128_find_mem_vbios(struct aty128fb_par * par)918f7018c21STomi Valkeinen static void __iomem *aty128_find_mem_vbios(struct aty128fb_par *par)
919f7018c21STomi Valkeinen {
920f7018c21STomi Valkeinen 	/* I simplified this code as we used to miss the signatures in
921f7018c21STomi Valkeinen 	 * a lot of case. It's now closer to XFree, we just don't check
922f7018c21STomi Valkeinen 	 * for signatures at all... Something better will have to be done
923f7018c21STomi Valkeinen 	 * if we end up having conflicts
924f7018c21STomi Valkeinen 	 */
925f7018c21STomi Valkeinen         u32  segstart;
926f7018c21STomi Valkeinen         unsigned char __iomem *rom_base = NULL;
927f7018c21STomi Valkeinen 
928f7018c21STomi Valkeinen         for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
929f7018c21STomi Valkeinen                 rom_base = ioremap(segstart, 0x10000);
930f7018c21STomi Valkeinen 		if (rom_base == NULL)
931f7018c21STomi Valkeinen 			return NULL;
932f7018c21STomi Valkeinen 		if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa)
933f7018c21STomi Valkeinen 	                break;
934f7018c21STomi Valkeinen                 iounmap(rom_base);
935f7018c21STomi Valkeinen 		rom_base = NULL;
936f7018c21STomi Valkeinen         }
937f7018c21STomi Valkeinen 	return rom_base;
938f7018c21STomi Valkeinen }
939f7018c21STomi Valkeinen #endif
940f7018c21STomi Valkeinen #endif /* ndef(__sparc__) */
941f7018c21STomi Valkeinen 
942f7018c21STomi Valkeinen /* fill in known card constants if pll_block is not available */
aty128_timings(struct aty128fb_par * par)943f7018c21STomi Valkeinen static void aty128_timings(struct aty128fb_par *par)
944f7018c21STomi Valkeinen {
945e7b410efSKevin Hao #ifdef CONFIG_PPC
946f7018c21STomi Valkeinen 	/* instead of a table lookup, assume OF has properly
947f7018c21STomi Valkeinen 	 * setup the PLL registers and use their values
948f7018c21STomi Valkeinen 	 * to set the XCLK values and reference divider values */
949f7018c21STomi Valkeinen 
950f7018c21STomi Valkeinen 	u32 x_mpll_ref_fb_div;
951f7018c21STomi Valkeinen 	u32 xclk_cntl;
952f7018c21STomi Valkeinen 	u32 Nx, M;
9530cd129deSColin Ian King 	static const unsigned int PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 };
954f7018c21STomi Valkeinen #endif
955f7018c21STomi Valkeinen 
956f7018c21STomi Valkeinen 	if (!par->constants.ref_clk)
957f7018c21STomi Valkeinen 		par->constants.ref_clk = 2950;
958f7018c21STomi Valkeinen 
959e7b410efSKevin Hao #ifdef CONFIG_PPC
960f7018c21STomi Valkeinen 	x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV);
961f7018c21STomi Valkeinen 	xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7;
962f7018c21STomi Valkeinen 	Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8;
963f7018c21STomi Valkeinen 	M  = x_mpll_ref_fb_div & 0x0000ff;
964f7018c21STomi Valkeinen 
965f7018c21STomi Valkeinen 	par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk),
966f7018c21STomi Valkeinen 					(M * PostDivSet[xclk_cntl]));
967f7018c21STomi Valkeinen 
968f7018c21STomi Valkeinen 	par->constants.ref_divider =
969f7018c21STomi Valkeinen 		aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
970f7018c21STomi Valkeinen #endif
971f7018c21STomi Valkeinen 
972f7018c21STomi Valkeinen 	if (!par->constants.ref_divider) {
973f7018c21STomi Valkeinen 		par->constants.ref_divider = 0x3b;
974f7018c21STomi Valkeinen 
975f7018c21STomi Valkeinen 		aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e);
976f7018c21STomi Valkeinen 		aty_pll_writeupdate(par);
977f7018c21STomi Valkeinen 	}
978f7018c21STomi Valkeinen 	aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider);
979f7018c21STomi Valkeinen 	aty_pll_writeupdate(par);
980f7018c21STomi Valkeinen 
981f7018c21STomi Valkeinen 	/* from documentation */
982f7018c21STomi Valkeinen 	if (!par->constants.ppll_min)
983f7018c21STomi Valkeinen 		par->constants.ppll_min = 12500;
984f7018c21STomi Valkeinen 	if (!par->constants.ppll_max)
985f7018c21STomi Valkeinen 		par->constants.ppll_max = 25000;    /* 23000 on some cards? */
986f7018c21STomi Valkeinen 	if (!par->constants.xclk)
987f7018c21STomi Valkeinen 		par->constants.xclk = 0x1d4d;	     /* same as mclk */
988f7018c21STomi Valkeinen 
989f7018c21STomi Valkeinen 	par->constants.fifo_width = 128;
990f7018c21STomi Valkeinen 	par->constants.fifo_depth = 32;
991f7018c21STomi Valkeinen 
992f7018c21STomi Valkeinen 	switch (aty_ld_le32(MEM_CNTL) & 0x3) {
993f7018c21STomi Valkeinen 	case 0:
994f7018c21STomi Valkeinen 		par->mem = &sdr_128;
995f7018c21STomi Valkeinen 		break;
996f7018c21STomi Valkeinen 	case 1:
997f7018c21STomi Valkeinen 		par->mem = &sdr_sgram;
998f7018c21STomi Valkeinen 		break;
999f7018c21STomi Valkeinen 	case 2:
1000f7018c21STomi Valkeinen 		par->mem = &ddr_sgram;
1001f7018c21STomi Valkeinen 		break;
1002f7018c21STomi Valkeinen 	default:
1003f7018c21STomi Valkeinen 		par->mem = &sdr_sgram;
1004f7018c21STomi Valkeinen 	}
1005f7018c21STomi Valkeinen }
1006f7018c21STomi Valkeinen 
1007f7018c21STomi Valkeinen 
1008f7018c21STomi Valkeinen 
1009f7018c21STomi Valkeinen /*
1010f7018c21STomi Valkeinen  * CRTC programming
1011f7018c21STomi Valkeinen  */
1012f7018c21STomi Valkeinen 
1013f7018c21STomi Valkeinen /* Program the CRTC registers */
aty128_set_crtc(const struct aty128_crtc * crtc,const struct aty128fb_par * par)1014f7018c21STomi Valkeinen static void aty128_set_crtc(const struct aty128_crtc *crtc,
1015f7018c21STomi Valkeinen 			    const struct aty128fb_par *par)
1016f7018c21STomi Valkeinen {
1017f7018c21STomi Valkeinen 	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl);
1018f7018c21STomi Valkeinen 	aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total);
1019f7018c21STomi Valkeinen 	aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid);
1020f7018c21STomi Valkeinen 	aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total);
1021f7018c21STomi Valkeinen 	aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid);
1022f7018c21STomi Valkeinen 	aty_st_le32(CRTC_PITCH, crtc->pitch);
1023f7018c21STomi Valkeinen 	aty_st_le32(CRTC_OFFSET, crtc->offset);
1024f7018c21STomi Valkeinen 	aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
1025f7018c21STomi Valkeinen 	/* Disable ATOMIC updating.  Is this the right place? */
1026f7018c21STomi Valkeinen 	aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000));
1027f7018c21STomi Valkeinen }
1028f7018c21STomi Valkeinen 
1029f7018c21STomi Valkeinen 
aty128_var_to_crtc(const struct fb_var_screeninfo * var,struct aty128_crtc * crtc,const struct aty128fb_par * par)1030f7018c21STomi Valkeinen static int aty128_var_to_crtc(const struct fb_var_screeninfo *var,
1031f7018c21STomi Valkeinen 			      struct aty128_crtc *crtc,
1032f7018c21STomi Valkeinen 			      const struct aty128fb_par *par)
1033f7018c21STomi Valkeinen {
1034f7018c21STomi Valkeinen 	u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst;
1035f7018c21STomi Valkeinen 	u32 left, right, upper, lower, hslen, vslen, sync, vmode;
1036f7018c21STomi Valkeinen 	u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol;
1037f7018c21STomi Valkeinen 	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
1038f7018c21STomi Valkeinen 	u32 depth, bytpp;
1039f7018c21STomi Valkeinen 	u8 mode_bytpp[7] = { 0, 0, 1, 2, 2, 3, 4 };
1040f7018c21STomi Valkeinen 
1041f7018c21STomi Valkeinen 	/* input */
1042f7018c21STomi Valkeinen 	xres = var->xres;
1043f7018c21STomi Valkeinen 	yres = var->yres;
1044f7018c21STomi Valkeinen 	vxres   = var->xres_virtual;
1045f7018c21STomi Valkeinen 	vyres   = var->yres_virtual;
1046f7018c21STomi Valkeinen 	xoffset = var->xoffset;
1047f7018c21STomi Valkeinen 	yoffset = var->yoffset;
1048f7018c21STomi Valkeinen 	bpp   = var->bits_per_pixel;
1049f7018c21STomi Valkeinen 	left  = var->left_margin;
1050f7018c21STomi Valkeinen 	right = var->right_margin;
1051f7018c21STomi Valkeinen 	upper = var->upper_margin;
1052f7018c21STomi Valkeinen 	lower = var->lower_margin;
1053f7018c21STomi Valkeinen 	hslen = var->hsync_len;
1054f7018c21STomi Valkeinen 	vslen = var->vsync_len;
1055f7018c21STomi Valkeinen 	sync  = var->sync;
1056f7018c21STomi Valkeinen 	vmode = var->vmode;
1057f7018c21STomi Valkeinen 
1058f7018c21STomi Valkeinen 	if (bpp != 16)
1059f7018c21STomi Valkeinen 		depth = bpp;
1060f7018c21STomi Valkeinen 	else
1061f7018c21STomi Valkeinen 		depth = (var->green.length == 6) ? 16 : 15;
1062f7018c21STomi Valkeinen 
1063f7018c21STomi Valkeinen 	/* check for mode eligibility
1064f7018c21STomi Valkeinen 	 * accept only non interlaced modes */
1065f7018c21STomi Valkeinen 	if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
1066f7018c21STomi Valkeinen 		return -EINVAL;
1067f7018c21STomi Valkeinen 
1068f7018c21STomi Valkeinen 	/* convert (and round up) and validate */
1069f7018c21STomi Valkeinen 	xres = (xres + 7) & ~7;
1070f7018c21STomi Valkeinen 	xoffset = (xoffset + 7) & ~7;
1071f7018c21STomi Valkeinen 
1072f7018c21STomi Valkeinen 	if (vxres < xres + xoffset)
1073f7018c21STomi Valkeinen 		vxres = xres + xoffset;
1074f7018c21STomi Valkeinen 
1075f7018c21STomi Valkeinen 	if (vyres < yres + yoffset)
1076f7018c21STomi Valkeinen 		vyres = yres + yoffset;
1077f7018c21STomi Valkeinen 
1078f7018c21STomi Valkeinen 	/* convert depth into ATI register depth */
1079f7018c21STomi Valkeinen 	dst = depth_to_dst(depth);
1080f7018c21STomi Valkeinen 
1081f7018c21STomi Valkeinen 	if (dst == -EINVAL) {
1082f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n");
1083f7018c21STomi Valkeinen 		return -EINVAL;
1084f7018c21STomi Valkeinen 	}
1085f7018c21STomi Valkeinen 
1086f7018c21STomi Valkeinen 	/* convert register depth to bytes per pixel */
1087f7018c21STomi Valkeinen 	bytpp = mode_bytpp[dst];
1088f7018c21STomi Valkeinen 
1089f7018c21STomi Valkeinen 	/* make sure there is enough video ram for the mode */
1090f7018c21STomi Valkeinen 	if ((u32)(vxres * vyres * bytpp) > par->vram_size) {
1091f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: Not enough memory for mode\n");
1092f7018c21STomi Valkeinen 		return -EINVAL;
1093f7018c21STomi Valkeinen 	}
1094f7018c21STomi Valkeinen 
1095f7018c21STomi Valkeinen 	h_disp = (xres >> 3) - 1;
1096f7018c21STomi Valkeinen 	h_total = (((xres + right + hslen + left) >> 3) - 1) & 0xFFFFL;
1097f7018c21STomi Valkeinen 
1098f7018c21STomi Valkeinen 	v_disp = yres - 1;
1099f7018c21STomi Valkeinen 	v_total = (yres + upper + vslen + lower - 1) & 0xFFFFL;
1100f7018c21STomi Valkeinen 
1101f7018c21STomi Valkeinen 	/* check to make sure h_total and v_total are in range */
1102f7018c21STomi Valkeinen 	if (((h_total >> 3) - 1) > 0x1ff || (v_total - 1) > 0x7FF) {
1103f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: invalid width ranges\n");
1104f7018c21STomi Valkeinen 		return -EINVAL;
1105f7018c21STomi Valkeinen 	}
1106f7018c21STomi Valkeinen 
1107f7018c21STomi Valkeinen 	h_sync_wid = (hslen + 7) >> 3;
1108f7018c21STomi Valkeinen 	if (h_sync_wid == 0)
1109f7018c21STomi Valkeinen 		h_sync_wid = 1;
1110f7018c21STomi Valkeinen 	else if (h_sync_wid > 0x3f)        /* 0x3f = max hwidth */
1111f7018c21STomi Valkeinen 		h_sync_wid = 0x3f;
1112f7018c21STomi Valkeinen 
1113f7018c21STomi Valkeinen 	h_sync_strt = (h_disp << 3) + right;
1114f7018c21STomi Valkeinen 
1115f7018c21STomi Valkeinen 	v_sync_wid = vslen;
1116f7018c21STomi Valkeinen 	if (v_sync_wid == 0)
1117f7018c21STomi Valkeinen 		v_sync_wid = 1;
1118f7018c21STomi Valkeinen 	else if (v_sync_wid > 0x1f)        /* 0x1f = max vwidth */
1119f7018c21STomi Valkeinen 		v_sync_wid = 0x1f;
1120f7018c21STomi Valkeinen 
1121f7018c21STomi Valkeinen 	v_sync_strt = v_disp + lower;
1122f7018c21STomi Valkeinen 
1123f7018c21STomi Valkeinen 	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
1124f7018c21STomi Valkeinen 	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
1125f7018c21STomi Valkeinen 
1126f7018c21STomi Valkeinen 	c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
1127f7018c21STomi Valkeinen 
1128f7018c21STomi Valkeinen 	crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8);
1129f7018c21STomi Valkeinen 
1130f7018c21STomi Valkeinen 	crtc->h_total = h_total | (h_disp << 16);
1131f7018c21STomi Valkeinen 	crtc->v_total = v_total | (v_disp << 16);
1132f7018c21STomi Valkeinen 
1133f7018c21STomi Valkeinen 	crtc->h_sync_strt_wid = h_sync_strt | (h_sync_wid << 16) |
1134f7018c21STomi Valkeinen 	        (h_sync_pol << 23);
1135f7018c21STomi Valkeinen 	crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
1136f7018c21STomi Valkeinen                 (v_sync_pol << 23);
1137f7018c21STomi Valkeinen 
1138f7018c21STomi Valkeinen 	crtc->pitch = vxres >> 3;
1139f7018c21STomi Valkeinen 
1140f7018c21STomi Valkeinen 	crtc->offset = 0;
1141f7018c21STomi Valkeinen 
1142f7018c21STomi Valkeinen 	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
1143f7018c21STomi Valkeinen 		crtc->offset_cntl = 0x00010000;
1144f7018c21STomi Valkeinen 	else
1145f7018c21STomi Valkeinen 		crtc->offset_cntl = 0;
1146f7018c21STomi Valkeinen 
1147f7018c21STomi Valkeinen 	crtc->vxres = vxres;
1148f7018c21STomi Valkeinen 	crtc->vyres = vyres;
1149f7018c21STomi Valkeinen 	crtc->xoffset = xoffset;
1150f7018c21STomi Valkeinen 	crtc->yoffset = yoffset;
1151f7018c21STomi Valkeinen 	crtc->depth = depth;
1152f7018c21STomi Valkeinen 	crtc->bpp = bpp;
1153f7018c21STomi Valkeinen 
1154f7018c21STomi Valkeinen 	return 0;
1155f7018c21STomi Valkeinen }
1156f7018c21STomi Valkeinen 
1157f7018c21STomi Valkeinen 
aty128_pix_width_to_var(int pix_width,struct fb_var_screeninfo * var)1158f7018c21STomi Valkeinen static int aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var)
1159f7018c21STomi Valkeinen {
1160f7018c21STomi Valkeinen 
1161f7018c21STomi Valkeinen 	/* fill in pixel info */
1162f7018c21STomi Valkeinen 	var->red.msb_right = 0;
1163f7018c21STomi Valkeinen 	var->green.msb_right = 0;
1164f7018c21STomi Valkeinen 	var->blue.offset = 0;
1165f7018c21STomi Valkeinen 	var->blue.msb_right = 0;
1166f7018c21STomi Valkeinen 	var->transp.offset = 0;
1167f7018c21STomi Valkeinen 	var->transp.length = 0;
1168f7018c21STomi Valkeinen 	var->transp.msb_right = 0;
1169f7018c21STomi Valkeinen 	switch (pix_width) {
1170f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_8BPP:
1171f7018c21STomi Valkeinen 		var->bits_per_pixel = 8;
1172f7018c21STomi Valkeinen 		var->red.offset = 0;
1173f7018c21STomi Valkeinen 		var->red.length = 8;
1174f7018c21STomi Valkeinen 		var->green.offset = 0;
1175f7018c21STomi Valkeinen 		var->green.length = 8;
1176f7018c21STomi Valkeinen 		var->blue.length = 8;
1177f7018c21STomi Valkeinen 		break;
1178f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_15BPP:
1179f7018c21STomi Valkeinen 		var->bits_per_pixel = 16;
1180f7018c21STomi Valkeinen 		var->red.offset = 10;
1181f7018c21STomi Valkeinen 		var->red.length = 5;
1182f7018c21STomi Valkeinen 		var->green.offset = 5;
1183f7018c21STomi Valkeinen 		var->green.length = 5;
1184f7018c21STomi Valkeinen 		var->blue.length = 5;
1185f7018c21STomi Valkeinen 		break;
1186f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_16BPP:
1187f7018c21STomi Valkeinen 		var->bits_per_pixel = 16;
1188f7018c21STomi Valkeinen 		var->red.offset = 11;
1189f7018c21STomi Valkeinen 		var->red.length = 5;
1190f7018c21STomi Valkeinen 		var->green.offset = 5;
1191f7018c21STomi Valkeinen 		var->green.length = 6;
1192f7018c21STomi Valkeinen 		var->blue.length = 5;
1193f7018c21STomi Valkeinen 		break;
1194f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_24BPP:
1195f7018c21STomi Valkeinen 		var->bits_per_pixel = 24;
1196f7018c21STomi Valkeinen 		var->red.offset = 16;
1197f7018c21STomi Valkeinen 		var->red.length = 8;
1198f7018c21STomi Valkeinen 		var->green.offset = 8;
1199f7018c21STomi Valkeinen 		var->green.length = 8;
1200f7018c21STomi Valkeinen 		var->blue.length = 8;
1201f7018c21STomi Valkeinen 		break;
1202f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_32BPP:
1203f7018c21STomi Valkeinen 		var->bits_per_pixel = 32;
1204f7018c21STomi Valkeinen 		var->red.offset = 16;
1205f7018c21STomi Valkeinen 		var->red.length = 8;
1206f7018c21STomi Valkeinen 		var->green.offset = 8;
1207f7018c21STomi Valkeinen 		var->green.length = 8;
1208f7018c21STomi Valkeinen 		var->blue.length = 8;
1209f7018c21STomi Valkeinen 		var->transp.offset = 24;
1210f7018c21STomi Valkeinen 		var->transp.length = 8;
1211f7018c21STomi Valkeinen 		break;
1212f7018c21STomi Valkeinen 	default:
1213f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: Invalid pixel width\n");
1214f7018c21STomi Valkeinen 		return -EINVAL;
1215f7018c21STomi Valkeinen 	}
1216f7018c21STomi Valkeinen 
1217f7018c21STomi Valkeinen 	return 0;
1218f7018c21STomi Valkeinen }
1219f7018c21STomi Valkeinen 
1220f7018c21STomi Valkeinen 
aty128_crtc_to_var(const struct aty128_crtc * crtc,struct fb_var_screeninfo * var)1221f7018c21STomi Valkeinen static int aty128_crtc_to_var(const struct aty128_crtc *crtc,
1222f7018c21STomi Valkeinen 			      struct fb_var_screeninfo *var)
1223f7018c21STomi Valkeinen {
1224f7018c21STomi Valkeinen 	u32 xres, yres, left, right, upper, lower, hslen, vslen, sync;
1225f7018c21STomi Valkeinen 	u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
1226f7018c21STomi Valkeinen 	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
1227f7018c21STomi Valkeinen 	u32 pix_width;
1228f7018c21STomi Valkeinen 
1229f7018c21STomi Valkeinen 	/* fun with masking */
1230f7018c21STomi Valkeinen 	h_total     = crtc->h_total & 0x1ff;
1231f7018c21STomi Valkeinen 	h_disp      = (crtc->h_total >> 16) & 0xff;
1232f7018c21STomi Valkeinen 	h_sync_strt = (crtc->h_sync_strt_wid >> 3) & 0x1ff;
1233f7018c21STomi Valkeinen 	h_sync_dly  = crtc->h_sync_strt_wid & 0x7;
1234f7018c21STomi Valkeinen 	h_sync_wid  = (crtc->h_sync_strt_wid >> 16) & 0x3f;
1235f7018c21STomi Valkeinen 	h_sync_pol  = (crtc->h_sync_strt_wid >> 23) & 0x1;
1236f7018c21STomi Valkeinen 	v_total     = crtc->v_total & 0x7ff;
1237f7018c21STomi Valkeinen 	v_disp      = (crtc->v_total >> 16) & 0x7ff;
1238f7018c21STomi Valkeinen 	v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
1239f7018c21STomi Valkeinen 	v_sync_wid  = (crtc->v_sync_strt_wid >> 16) & 0x1f;
1240f7018c21STomi Valkeinen 	v_sync_pol  = (crtc->v_sync_strt_wid >> 23) & 0x1;
1241f7018c21STomi Valkeinen 	c_sync      = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
1242f7018c21STomi Valkeinen 	pix_width   = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
1243f7018c21STomi Valkeinen 
1244f7018c21STomi Valkeinen 	/* do conversions */
1245f7018c21STomi Valkeinen 	xres  = (h_disp + 1) << 3;
1246f7018c21STomi Valkeinen 	yres  = v_disp + 1;
1247f7018c21STomi Valkeinen 	left  = ((h_total - h_sync_strt - h_sync_wid) << 3) - h_sync_dly;
1248f7018c21STomi Valkeinen 	right = ((h_sync_strt - h_disp) << 3) + h_sync_dly;
1249f7018c21STomi Valkeinen 	hslen = h_sync_wid << 3;
1250f7018c21STomi Valkeinen 	upper = v_total - v_sync_strt - v_sync_wid;
1251f7018c21STomi Valkeinen 	lower = v_sync_strt - v_disp;
1252f7018c21STomi Valkeinen 	vslen = v_sync_wid;
1253f7018c21STomi Valkeinen 	sync  = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
1254f7018c21STomi Valkeinen 		(v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
1255f7018c21STomi Valkeinen 		(c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
1256f7018c21STomi Valkeinen 
1257f7018c21STomi Valkeinen 	aty128_pix_width_to_var(pix_width, var);
1258f7018c21STomi Valkeinen 
1259f7018c21STomi Valkeinen 	var->xres = xres;
1260f7018c21STomi Valkeinen 	var->yres = yres;
1261f7018c21STomi Valkeinen 	var->xres_virtual = crtc->vxres;
1262f7018c21STomi Valkeinen 	var->yres_virtual = crtc->vyres;
1263f7018c21STomi Valkeinen 	var->xoffset = crtc->xoffset;
1264f7018c21STomi Valkeinen 	var->yoffset = crtc->yoffset;
1265f7018c21STomi Valkeinen 	var->left_margin  = left;
1266f7018c21STomi Valkeinen 	var->right_margin = right;
1267f7018c21STomi Valkeinen 	var->upper_margin = upper;
1268f7018c21STomi Valkeinen 	var->lower_margin = lower;
1269f7018c21STomi Valkeinen 	var->hsync_len = hslen;
1270f7018c21STomi Valkeinen 	var->vsync_len = vslen;
1271f7018c21STomi Valkeinen 	var->sync  = sync;
1272f7018c21STomi Valkeinen 	var->vmode = FB_VMODE_NONINTERLACED;
1273f7018c21STomi Valkeinen 
1274f7018c21STomi Valkeinen 	return 0;
1275f7018c21STomi Valkeinen }
1276f7018c21STomi Valkeinen 
aty128_set_crt_enable(struct aty128fb_par * par,int on)1277f7018c21STomi Valkeinen static void aty128_set_crt_enable(struct aty128fb_par *par, int on)
1278f7018c21STomi Valkeinen {
1279f7018c21STomi Valkeinen 	if (on) {
1280f7018c21STomi Valkeinen 		aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) |
1281f7018c21STomi Valkeinen 			    CRT_CRTC_ON);
1282f7018c21STomi Valkeinen 		aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) |
1283f7018c21STomi Valkeinen 			    DAC_PALETTE2_SNOOP_EN));
1284f7018c21STomi Valkeinen 	} else
1285f7018c21STomi Valkeinen 		aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) &
1286f7018c21STomi Valkeinen 			    ~CRT_CRTC_ON);
1287f7018c21STomi Valkeinen }
1288f7018c21STomi Valkeinen 
aty128_set_lcd_enable(struct aty128fb_par * par,int on)1289f7018c21STomi Valkeinen static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
1290f7018c21STomi Valkeinen {
1291f7018c21STomi Valkeinen 	u32 reg;
1292f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
1293f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(par->pdev);
1294f7018c21STomi Valkeinen #endif
1295f7018c21STomi Valkeinen 
1296f7018c21STomi Valkeinen 	if (on) {
1297f7018c21STomi Valkeinen 		reg = aty_ld_le32(LVDS_GEN_CNTL);
1298f7018c21STomi Valkeinen 		reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION;
1299f7018c21STomi Valkeinen 		reg &= ~LVDS_DISPLAY_DIS;
1300f7018c21STomi Valkeinen 		aty_st_le32(LVDS_GEN_CNTL, reg);
1301f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
1302f7018c21STomi Valkeinen 		aty128_bl_set_power(info, FB_BLANK_UNBLANK);
1303f7018c21STomi Valkeinen #endif
1304f7018c21STomi Valkeinen 	} else {
1305f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
1306f7018c21STomi Valkeinen 		aty128_bl_set_power(info, FB_BLANK_POWERDOWN);
1307f7018c21STomi Valkeinen #endif
1308f7018c21STomi Valkeinen 		reg = aty_ld_le32(LVDS_GEN_CNTL);
1309f7018c21STomi Valkeinen 		reg |= LVDS_DISPLAY_DIS;
1310f7018c21STomi Valkeinen 		aty_st_le32(LVDS_GEN_CNTL, reg);
1311f7018c21STomi Valkeinen 		mdelay(100);
1312f7018c21STomi Valkeinen 		reg &= ~(LVDS_ON /*| LVDS_EN*/);
1313f7018c21STomi Valkeinen 		aty_st_le32(LVDS_GEN_CNTL, reg);
1314f7018c21STomi Valkeinen 	}
1315f7018c21STomi Valkeinen }
1316f7018c21STomi Valkeinen 
aty128_set_pll(struct aty128_pll * pll,const struct aty128fb_par * par)1317f7018c21STomi Valkeinen static void aty128_set_pll(struct aty128_pll *pll,
1318f7018c21STomi Valkeinen 			   const struct aty128fb_par *par)
1319f7018c21STomi Valkeinen {
1320f7018c21STomi Valkeinen 	u32 div3;
1321f7018c21STomi Valkeinen 
13220cd129deSColin Ian King 	/* register values for post dividers */
13230cd129deSColin Ian King 	static const unsigned char post_conv[] = {
13240cd129deSColin Ian King 		2, 0, 1, 4, 2, 2, 6, 2, 3, 2, 2, 2, 7
13250cd129deSColin Ian King 	};
1326f7018c21STomi Valkeinen 
1327f7018c21STomi Valkeinen 	/* select PPLL_DIV_3 */
1328f7018c21STomi Valkeinen 	aty_st_le32(CLOCK_CNTL_INDEX, aty_ld_le32(CLOCK_CNTL_INDEX) | (3 << 8));
1329f7018c21STomi Valkeinen 
1330f7018c21STomi Valkeinen 	/* reset PLL */
1331f7018c21STomi Valkeinen 	aty_st_pll(PPLL_CNTL,
1332f7018c21STomi Valkeinen 		   aty_ld_pll(PPLL_CNTL) | PPLL_RESET | PPLL_ATOMIC_UPDATE_EN);
1333f7018c21STomi Valkeinen 
1334f7018c21STomi Valkeinen 	/* write the reference divider */
1335f7018c21STomi Valkeinen 	aty_pll_wait_readupdate(par);
1336f7018c21STomi Valkeinen 	aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider & 0x3ff);
1337f7018c21STomi Valkeinen 	aty_pll_writeupdate(par);
1338f7018c21STomi Valkeinen 
1339f7018c21STomi Valkeinen 	div3 = aty_ld_pll(PPLL_DIV_3);
1340f7018c21STomi Valkeinen 	div3 &= ~PPLL_FB3_DIV_MASK;
1341f7018c21STomi Valkeinen 	div3 |= pll->feedback_divider;
1342f7018c21STomi Valkeinen 	div3 &= ~PPLL_POST3_DIV_MASK;
1343f7018c21STomi Valkeinen 	div3 |= post_conv[pll->post_divider] << 16;
1344f7018c21STomi Valkeinen 
1345f7018c21STomi Valkeinen 	/* write feedback and post dividers */
1346f7018c21STomi Valkeinen 	aty_pll_wait_readupdate(par);
1347f7018c21STomi Valkeinen 	aty_st_pll(PPLL_DIV_3, div3);
1348f7018c21STomi Valkeinen 	aty_pll_writeupdate(par);
1349f7018c21STomi Valkeinen 
1350f7018c21STomi Valkeinen 	aty_pll_wait_readupdate(par);
1351f7018c21STomi Valkeinen 	aty_st_pll(HTOTAL_CNTL, 0);	/* no horiz crtc adjustment */
1352f7018c21STomi Valkeinen 	aty_pll_writeupdate(par);
1353f7018c21STomi Valkeinen 
1354f7018c21STomi Valkeinen 	/* clear the reset, just in case */
1355f7018c21STomi Valkeinen 	aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET);
1356f7018c21STomi Valkeinen }
1357f7018c21STomi Valkeinen 
1358f7018c21STomi Valkeinen 
aty128_var_to_pll(u32 period_in_ps,struct aty128_pll * pll,const struct aty128fb_par * par)1359f7018c21STomi Valkeinen static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
1360f7018c21STomi Valkeinen 			     const struct aty128fb_par *par)
1361f7018c21STomi Valkeinen {
1362f7018c21STomi Valkeinen 	const struct aty128_constants c = par->constants;
13630cd129deSColin Ian King 	static const unsigned char post_dividers[] = { 1, 2, 4, 8, 3, 6, 12 };
1364f7018c21STomi Valkeinen 	u32 output_freq;
1365f7018c21STomi Valkeinen 	u32 vclk;        /* in .01 MHz */
1366f7018c21STomi Valkeinen 	int i = 0;
1367f7018c21STomi Valkeinen 	u32 n, d;
1368f7018c21STomi Valkeinen 
1369f7018c21STomi Valkeinen 	vclk = 100000000 / period_in_ps;	/* convert units to 10 kHz */
1370f7018c21STomi Valkeinen 
1371f7018c21STomi Valkeinen 	/* adjust pixel clock if necessary */
1372f7018c21STomi Valkeinen 	if (vclk > c.ppll_max)
1373f7018c21STomi Valkeinen 		vclk = c.ppll_max;
1374f7018c21STomi Valkeinen 	if (vclk * 12 < c.ppll_min)
1375f7018c21STomi Valkeinen 		vclk = c.ppll_min/12;
1376f7018c21STomi Valkeinen 
1377f7018c21STomi Valkeinen 	/* now, find an acceptable divider */
1378f7018c21STomi Valkeinen 	for (i = 0; i < ARRAY_SIZE(post_dividers); i++) {
1379f7018c21STomi Valkeinen 		output_freq = post_dividers[i] * vclk;
1380f7018c21STomi Valkeinen 		if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) {
1381f7018c21STomi Valkeinen 			pll->post_divider = post_dividers[i];
1382f7018c21STomi Valkeinen 			break;
1383f7018c21STomi Valkeinen 		}
1384f7018c21STomi Valkeinen 	}
1385f7018c21STomi Valkeinen 
1386f7018c21STomi Valkeinen 	if (i == ARRAY_SIZE(post_dividers))
1387f7018c21STomi Valkeinen 		return -EINVAL;
1388f7018c21STomi Valkeinen 
1389f7018c21STomi Valkeinen 	/* calculate feedback divider */
1390f7018c21STomi Valkeinen 	n = c.ref_divider * output_freq;
1391f7018c21STomi Valkeinen 	d = c.ref_clk;
1392f7018c21STomi Valkeinen 
1393f7018c21STomi Valkeinen 	pll->feedback_divider = round_div(n, d);
1394f7018c21STomi Valkeinen 	pll->vclk = vclk;
1395f7018c21STomi Valkeinen 
1396f7018c21STomi Valkeinen 	DBG("post %d feedback %d vlck %d output %d ref_divider %d "
1397f7018c21STomi Valkeinen 	    "vclk_per: %d\n", pll->post_divider,
1398f7018c21STomi Valkeinen 	    pll->feedback_divider, vclk, output_freq,
1399f7018c21STomi Valkeinen 	    c.ref_divider, period_in_ps);
1400f7018c21STomi Valkeinen 
1401f7018c21STomi Valkeinen 	return 0;
1402f7018c21STomi Valkeinen }
1403f7018c21STomi Valkeinen 
1404f7018c21STomi Valkeinen 
aty128_pll_to_var(const struct aty128_pll * pll,struct fb_var_screeninfo * var)1405f7018c21STomi Valkeinen static int aty128_pll_to_var(const struct aty128_pll *pll,
1406f7018c21STomi Valkeinen 			     struct fb_var_screeninfo *var)
1407f7018c21STomi Valkeinen {
1408f7018c21STomi Valkeinen 	var->pixclock = 100000000 / pll->vclk;
1409f7018c21STomi Valkeinen 
1410f7018c21STomi Valkeinen 	return 0;
1411f7018c21STomi Valkeinen }
1412f7018c21STomi Valkeinen 
1413f7018c21STomi Valkeinen 
aty128_set_fifo(const struct aty128_ddafifo * dsp,const struct aty128fb_par * par)1414f7018c21STomi Valkeinen static void aty128_set_fifo(const struct aty128_ddafifo *dsp,
1415f7018c21STomi Valkeinen 			    const struct aty128fb_par *par)
1416f7018c21STomi Valkeinen {
1417f7018c21STomi Valkeinen 	aty_st_le32(DDA_CONFIG, dsp->dda_config);
1418f7018c21STomi Valkeinen 	aty_st_le32(DDA_ON_OFF, dsp->dda_on_off);
1419f7018c21STomi Valkeinen }
1420f7018c21STomi Valkeinen 
1421f7018c21STomi Valkeinen 
aty128_ddafifo(struct aty128_ddafifo * dsp,const struct aty128_pll * pll,u32 depth,const struct aty128fb_par * par)1422f7018c21STomi Valkeinen static int aty128_ddafifo(struct aty128_ddafifo *dsp,
1423f7018c21STomi Valkeinen 			  const struct aty128_pll *pll,
1424f7018c21STomi Valkeinen 			  u32 depth,
1425f7018c21STomi Valkeinen 			  const struct aty128fb_par *par)
1426f7018c21STomi Valkeinen {
1427f7018c21STomi Valkeinen 	const struct aty128_meminfo *m = par->mem;
1428f7018c21STomi Valkeinen 	u32 xclk = par->constants.xclk;
1429f7018c21STomi Valkeinen 	u32 fifo_width = par->constants.fifo_width;
1430f7018c21STomi Valkeinen 	u32 fifo_depth = par->constants.fifo_depth;
1431f7018c21STomi Valkeinen 	s32 x, b, p, ron, roff;
1432f7018c21STomi Valkeinen 	u32 n, d, bpp;
1433f7018c21STomi Valkeinen 
1434f7018c21STomi Valkeinen 	/* round up to multiple of 8 */
1435f7018c21STomi Valkeinen 	bpp = (depth+7) & ~7;
1436f7018c21STomi Valkeinen 
1437f7018c21STomi Valkeinen 	n = xclk * fifo_width;
1438f7018c21STomi Valkeinen 	d = pll->vclk * bpp;
1439f7018c21STomi Valkeinen 	x = round_div(n, d);
1440f7018c21STomi Valkeinen 
1441f7018c21STomi Valkeinen 	ron = 4 * m->MB +
1442f7018c21STomi Valkeinen 		3 * ((m->Trcd - 2 > 0) ? m->Trcd - 2 : 0) +
1443f7018c21STomi Valkeinen 		2 * m->Trp +
1444f7018c21STomi Valkeinen 		m->Twr +
1445f7018c21STomi Valkeinen 		m->CL +
1446f7018c21STomi Valkeinen 		m->Tr2w +
1447f7018c21STomi Valkeinen 		x;
1448f7018c21STomi Valkeinen 
1449f7018c21STomi Valkeinen 	DBG("x %x\n", x);
1450f7018c21STomi Valkeinen 
1451f7018c21STomi Valkeinen 	b = 0;
1452f7018c21STomi Valkeinen 	while (x) {
1453f7018c21STomi Valkeinen 		x >>= 1;
1454f7018c21STomi Valkeinen 		b++;
1455f7018c21STomi Valkeinen 	}
1456f7018c21STomi Valkeinen 	p = b + 1;
1457f7018c21STomi Valkeinen 
1458f7018c21STomi Valkeinen 	ron <<= (11 - p);
1459f7018c21STomi Valkeinen 
1460f7018c21STomi Valkeinen 	n <<= (11 - p);
1461f7018c21STomi Valkeinen 	x = round_div(n, d);
1462f7018c21STomi Valkeinen 	roff = x * (fifo_depth - 4);
1463f7018c21STomi Valkeinen 
1464f7018c21STomi Valkeinen 	if ((ron + m->Rloop) >= roff) {
1465f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: Mode out of range!\n");
1466f7018c21STomi Valkeinen 		return -EINVAL;
1467f7018c21STomi Valkeinen 	}
1468f7018c21STomi Valkeinen 
1469f7018c21STomi Valkeinen 	DBG("p: %x rloop: %x x: %x ron: %x roff: %x\n",
1470f7018c21STomi Valkeinen 	    p, m->Rloop, x, ron, roff);
1471f7018c21STomi Valkeinen 
1472f7018c21STomi Valkeinen 	dsp->dda_config = p << 16 | m->Rloop << 20 | x;
1473f7018c21STomi Valkeinen 	dsp->dda_on_off = ron << 16 | roff;
1474f7018c21STomi Valkeinen 
1475f7018c21STomi Valkeinen 	return 0;
1476f7018c21STomi Valkeinen }
1477f7018c21STomi Valkeinen 
1478f7018c21STomi Valkeinen 
1479f7018c21STomi Valkeinen /*
1480f7018c21STomi Valkeinen  * This actually sets the video mode.
1481f7018c21STomi Valkeinen  */
aty128fb_set_par(struct fb_info * info)1482f7018c21STomi Valkeinen static int aty128fb_set_par(struct fb_info *info)
1483f7018c21STomi Valkeinen {
1484f7018c21STomi Valkeinen 	struct aty128fb_par *par = info->par;
1485f7018c21STomi Valkeinen 	u32 config;
1486f7018c21STomi Valkeinen 	int err;
1487f7018c21STomi Valkeinen 
1488f7018c21STomi Valkeinen 	if ((err = aty128_decode_var(&info->var, par)) != 0)
1489f7018c21STomi Valkeinen 		return err;
1490f7018c21STomi Valkeinen 
1491f7018c21STomi Valkeinen 	if (par->blitter_may_be_busy)
1492f7018c21STomi Valkeinen 		wait_for_idle(par);
1493f7018c21STomi Valkeinen 
1494f7018c21STomi Valkeinen 	/* clear all registers that may interfere with mode setting */
1495f7018c21STomi Valkeinen 	aty_st_le32(OVR_CLR, 0);
1496f7018c21STomi Valkeinen 	aty_st_le32(OVR_WID_LEFT_RIGHT, 0);
1497f7018c21STomi Valkeinen 	aty_st_le32(OVR_WID_TOP_BOTTOM, 0);
1498f7018c21STomi Valkeinen 	aty_st_le32(OV0_SCALE_CNTL, 0);
1499f7018c21STomi Valkeinen 	aty_st_le32(MPP_TB_CONFIG, 0);
1500f7018c21STomi Valkeinen 	aty_st_le32(MPP_GP_CONFIG, 0);
1501f7018c21STomi Valkeinen 	aty_st_le32(SUBPIC_CNTL, 0);
1502f7018c21STomi Valkeinen 	aty_st_le32(VIPH_CONTROL, 0);
1503f7018c21STomi Valkeinen 	aty_st_le32(I2C_CNTL_1, 0);         /* turn off i2c */
1504f7018c21STomi Valkeinen 	aty_st_le32(GEN_INT_CNTL, 0);	/* turn off interrupts */
1505f7018c21STomi Valkeinen 	aty_st_le32(CAP0_TRIG_CNTL, 0);
1506f7018c21STomi Valkeinen 	aty_st_le32(CAP1_TRIG_CNTL, 0);
1507f7018c21STomi Valkeinen 
1508f7018c21STomi Valkeinen 	aty_st_8(CRTC_EXT_CNTL + 1, 4);	/* turn video off */
1509f7018c21STomi Valkeinen 
1510f7018c21STomi Valkeinen 	aty128_set_crtc(&par->crtc, par);
1511f7018c21STomi Valkeinen 	aty128_set_pll(&par->pll, par);
1512f7018c21STomi Valkeinen 	aty128_set_fifo(&par->fifo_reg, par);
1513f7018c21STomi Valkeinen 
1514f7018c21STomi Valkeinen 	config = aty_ld_le32(CNFG_CNTL) & ~3;
1515f7018c21STomi Valkeinen 
1516f7018c21STomi Valkeinen #if defined(__BIG_ENDIAN)
1517f7018c21STomi Valkeinen 	if (par->crtc.bpp == 32)
1518f7018c21STomi Valkeinen 		config |= 2;	/* make aperture do 32 bit swapping */
1519f7018c21STomi Valkeinen 	else if (par->crtc.bpp == 16)
1520f7018c21STomi Valkeinen 		config |= 1;	/* make aperture do 16 bit swapping */
1521f7018c21STomi Valkeinen #endif
1522f7018c21STomi Valkeinen 
1523f7018c21STomi Valkeinen 	aty_st_le32(CNFG_CNTL, config);
1524f7018c21STomi Valkeinen 	aty_st_8(CRTC_EXT_CNTL + 1, 0);	/* turn the video back on */
1525f7018c21STomi Valkeinen 
1526f7018c21STomi Valkeinen 	info->fix.line_length = (par->crtc.vxres * par->crtc.bpp) >> 3;
1527f7018c21STomi Valkeinen 	info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR
1528f7018c21STomi Valkeinen 		: FB_VISUAL_DIRECTCOLOR;
1529f7018c21STomi Valkeinen 
1530f7018c21STomi Valkeinen 	if (par->chip_gen == rage_M3) {
1531f7018c21STomi Valkeinen 		aty128_set_crt_enable(par, par->crt_on);
1532f7018c21STomi Valkeinen 		aty128_set_lcd_enable(par, par->lcd_on);
1533f7018c21STomi Valkeinen 	}
1534f7018c21STomi Valkeinen 	if (par->accel_flags & FB_ACCELF_TEXT)
1535f7018c21STomi Valkeinen 		aty128_init_engine(par);
1536f7018c21STomi Valkeinen 
1537f7018c21STomi Valkeinen #ifdef CONFIG_BOOTX_TEXT
1538f7018c21STomi Valkeinen 	btext_update_display(info->fix.smem_start,
1539f7018c21STomi Valkeinen 			     (((par->crtc.h_total>>16) & 0xff)+1)*8,
1540f7018c21STomi Valkeinen 			     ((par->crtc.v_total>>16) & 0x7ff)+1,
1541f7018c21STomi Valkeinen 			     par->crtc.bpp,
1542f7018c21STomi Valkeinen 			     par->crtc.vxres*par->crtc.bpp/8);
1543f7018c21STomi Valkeinen #endif /* CONFIG_BOOTX_TEXT */
1544f7018c21STomi Valkeinen 
1545f7018c21STomi Valkeinen 	return 0;
1546f7018c21STomi Valkeinen }
1547f7018c21STomi Valkeinen 
1548f7018c21STomi Valkeinen /*
1549f7018c21STomi Valkeinen  *  encode/decode the User Defined Part of the Display
1550f7018c21STomi Valkeinen  */
1551f7018c21STomi Valkeinen 
aty128_decode_var(struct fb_var_screeninfo * var,struct aty128fb_par * par)1552f7018c21STomi Valkeinen static int aty128_decode_var(struct fb_var_screeninfo *var,
1553f7018c21STomi Valkeinen 			     struct aty128fb_par *par)
1554f7018c21STomi Valkeinen {
1555f7018c21STomi Valkeinen 	int err;
1556f7018c21STomi Valkeinen 	struct aty128_crtc crtc;
1557f7018c21STomi Valkeinen 	struct aty128_pll pll;
1558f7018c21STomi Valkeinen 	struct aty128_ddafifo fifo_reg;
1559f7018c21STomi Valkeinen 
1560f7018c21STomi Valkeinen 	if ((err = aty128_var_to_crtc(var, &crtc, par)))
1561f7018c21STomi Valkeinen 		return err;
1562f7018c21STomi Valkeinen 
1563f7018c21STomi Valkeinen 	if ((err = aty128_var_to_pll(var->pixclock, &pll, par)))
1564f7018c21STomi Valkeinen 		return err;
1565f7018c21STomi Valkeinen 
1566f7018c21STomi Valkeinen 	if ((err = aty128_ddafifo(&fifo_reg, &pll, crtc.depth, par)))
1567f7018c21STomi Valkeinen 		return err;
1568f7018c21STomi Valkeinen 
1569f7018c21STomi Valkeinen 	par->crtc = crtc;
1570f7018c21STomi Valkeinen 	par->pll = pll;
1571f7018c21STomi Valkeinen 	par->fifo_reg = fifo_reg;
1572f7018c21STomi Valkeinen 	par->accel_flags = var->accel_flags;
1573f7018c21STomi Valkeinen 
1574f7018c21STomi Valkeinen 	return 0;
1575f7018c21STomi Valkeinen }
1576f7018c21STomi Valkeinen 
1577f7018c21STomi Valkeinen 
aty128_encode_var(struct fb_var_screeninfo * var,const struct aty128fb_par * par)1578f7018c21STomi Valkeinen static int aty128_encode_var(struct fb_var_screeninfo *var,
1579f7018c21STomi Valkeinen 			     const struct aty128fb_par *par)
1580f7018c21STomi Valkeinen {
1581f7018c21STomi Valkeinen 	int err;
1582f7018c21STomi Valkeinen 
1583f7018c21STomi Valkeinen 	if ((err = aty128_crtc_to_var(&par->crtc, var)))
1584f7018c21STomi Valkeinen 		return err;
1585f7018c21STomi Valkeinen 
1586f7018c21STomi Valkeinen 	if ((err = aty128_pll_to_var(&par->pll, var)))
1587f7018c21STomi Valkeinen 		return err;
1588f7018c21STomi Valkeinen 
1589f7018c21STomi Valkeinen 	var->nonstd = 0;
1590f7018c21STomi Valkeinen 	var->activate = 0;
1591f7018c21STomi Valkeinen 
1592f7018c21STomi Valkeinen 	var->height = -1;
1593f7018c21STomi Valkeinen 	var->width = -1;
1594f7018c21STomi Valkeinen 	var->accel_flags = par->accel_flags;
1595f7018c21STomi Valkeinen 
1596f7018c21STomi Valkeinen 	return 0;
1597f7018c21STomi Valkeinen }
1598f7018c21STomi Valkeinen 
1599f7018c21STomi Valkeinen 
aty128fb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)1600f7018c21STomi Valkeinen static int aty128fb_check_var(struct fb_var_screeninfo *var,
1601f7018c21STomi Valkeinen 			      struct fb_info *info)
1602f7018c21STomi Valkeinen {
1603f7018c21STomi Valkeinen 	struct aty128fb_par par;
1604f7018c21STomi Valkeinen 	int err;
1605f7018c21STomi Valkeinen 
1606f7018c21STomi Valkeinen 	par = *(struct aty128fb_par *)info->par;
1607f7018c21STomi Valkeinen 	if ((err = aty128_decode_var(var, &par)) != 0)
1608f7018c21STomi Valkeinen 		return err;
1609f7018c21STomi Valkeinen 	aty128_encode_var(var, &par);
1610f7018c21STomi Valkeinen 	return 0;
1611f7018c21STomi Valkeinen }
1612f7018c21STomi Valkeinen 
1613f7018c21STomi Valkeinen 
1614f7018c21STomi Valkeinen /*
1615f7018c21STomi Valkeinen  *  Pan or Wrap the Display
1616f7018c21STomi Valkeinen  */
aty128fb_pan_display(struct fb_var_screeninfo * var,struct fb_info * fb)1617f7018c21STomi Valkeinen static int aty128fb_pan_display(struct fb_var_screeninfo *var,
1618f7018c21STomi Valkeinen 				struct fb_info *fb)
1619f7018c21STomi Valkeinen {
1620f7018c21STomi Valkeinen 	struct aty128fb_par *par = fb->par;
1621f7018c21STomi Valkeinen 	u32 xoffset, yoffset;
1622f7018c21STomi Valkeinen 	u32 offset;
1623f7018c21STomi Valkeinen 	u32 xres, yres;
1624f7018c21STomi Valkeinen 
1625f7018c21STomi Valkeinen 	xres = (((par->crtc.h_total >> 16) & 0xff) + 1) << 3;
1626f7018c21STomi Valkeinen 	yres = ((par->crtc.v_total >> 16) & 0x7ff) + 1;
1627f7018c21STomi Valkeinen 
1628f7018c21STomi Valkeinen 	xoffset = (var->xoffset +7) & ~7;
1629f7018c21STomi Valkeinen 	yoffset = var->yoffset;
1630f7018c21STomi Valkeinen 
1631f7018c21STomi Valkeinen 	if (xoffset+xres > par->crtc.vxres || yoffset+yres > par->crtc.vyres)
1632f7018c21STomi Valkeinen 		return -EINVAL;
1633f7018c21STomi Valkeinen 
1634f7018c21STomi Valkeinen 	par->crtc.xoffset = xoffset;
1635f7018c21STomi Valkeinen 	par->crtc.yoffset = yoffset;
1636f7018c21STomi Valkeinen 
1637f7018c21STomi Valkeinen 	offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3))
1638f7018c21STomi Valkeinen 									  & ~7;
1639f7018c21STomi Valkeinen 
1640f7018c21STomi Valkeinen 	if (par->crtc.bpp == 24)
1641f7018c21STomi Valkeinen 		offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
1642f7018c21STomi Valkeinen 
1643f7018c21STomi Valkeinen 	aty_st_le32(CRTC_OFFSET, offset);
1644f7018c21STomi Valkeinen 
1645f7018c21STomi Valkeinen 	return 0;
1646f7018c21STomi Valkeinen }
1647f7018c21STomi Valkeinen 
1648f7018c21STomi Valkeinen 
1649f7018c21STomi Valkeinen /*
1650f7018c21STomi Valkeinen  *  Helper function to store a single palette register
1651f7018c21STomi Valkeinen  */
aty128_st_pal(u_int regno,u_int red,u_int green,u_int blue,struct aty128fb_par * par)1652f7018c21STomi Valkeinen static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
1653f7018c21STomi Valkeinen 			  struct aty128fb_par *par)
1654f7018c21STomi Valkeinen {
1655f7018c21STomi Valkeinen 	if (par->chip_gen == rage_M3) {
1656f7018c21STomi Valkeinen 		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) &
1657f7018c21STomi Valkeinen 			    ~DAC_PALETTE_ACCESS_CNTL);
1658f7018c21STomi Valkeinen 	}
1659f7018c21STomi Valkeinen 
1660f7018c21STomi Valkeinen 	aty_st_8(PALETTE_INDEX, regno);
1661f7018c21STomi Valkeinen 	aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
1662f7018c21STomi Valkeinen }
1663f7018c21STomi Valkeinen 
aty128fb_sync(struct fb_info * info)1664f7018c21STomi Valkeinen static int aty128fb_sync(struct fb_info *info)
1665f7018c21STomi Valkeinen {
1666f7018c21STomi Valkeinen 	struct aty128fb_par *par = info->par;
1667f7018c21STomi Valkeinen 
1668f7018c21STomi Valkeinen 	if (par->blitter_may_be_busy)
1669f7018c21STomi Valkeinen 		wait_for_idle(par);
1670f7018c21STomi Valkeinen 	return 0;
1671f7018c21STomi Valkeinen }
1672f7018c21STomi Valkeinen 
1673f7018c21STomi Valkeinen #ifndef MODULE
aty128fb_setup(char * options)1674f7018c21STomi Valkeinen static int aty128fb_setup(char *options)
1675f7018c21STomi Valkeinen {
1676f7018c21STomi Valkeinen 	char *this_opt;
1677f7018c21STomi Valkeinen 
1678f7018c21STomi Valkeinen 	if (!options || !*options)
1679f7018c21STomi Valkeinen 		return 0;
1680f7018c21STomi Valkeinen 
1681f7018c21STomi Valkeinen 	while ((this_opt = strsep(&options, ",")) != NULL) {
1682f7018c21STomi Valkeinen 		if (!strncmp(this_opt, "lcd:", 4)) {
1683f7018c21STomi Valkeinen 			default_lcd_on = simple_strtoul(this_opt+4, NULL, 0);
1684f7018c21STomi Valkeinen 			continue;
1685f7018c21STomi Valkeinen 		} else if (!strncmp(this_opt, "crt:", 4)) {
1686f7018c21STomi Valkeinen 			default_crt_on = simple_strtoul(this_opt+4, NULL, 0);
1687f7018c21STomi Valkeinen 			continue;
1688f7018c21STomi Valkeinen 		} else if (!strncmp(this_opt, "backlight:", 10)) {
1689f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
1690f7018c21STomi Valkeinen 			backlight = simple_strtoul(this_opt+10, NULL, 0);
1691f7018c21STomi Valkeinen #endif
1692f7018c21STomi Valkeinen 			continue;
1693f7018c21STomi Valkeinen 		}
1694f7018c21STomi Valkeinen 		if(!strncmp(this_opt, "nomtrr", 6)) {
1695cfb810f8SGustavo A. R. Silva 			mtrr = false;
1696f7018c21STomi Valkeinen 			continue;
1697f7018c21STomi Valkeinen 		}
1698f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
1699f7018c21STomi Valkeinen 		/* vmode and cmode deprecated */
1700f7018c21STomi Valkeinen 		if (!strncmp(this_opt, "vmode:", 6)) {
1701f7018c21STomi Valkeinen 			unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
1702f7018c21STomi Valkeinen 			if (vmode > 0 && vmode <= VMODE_MAX)
1703f7018c21STomi Valkeinen 				default_vmode = vmode;
1704f7018c21STomi Valkeinen 			continue;
1705f7018c21STomi Valkeinen 		} else if (!strncmp(this_opt, "cmode:", 6)) {
1706f7018c21STomi Valkeinen 			unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0);
1707f7018c21STomi Valkeinen 			switch (cmode) {
1708f7018c21STomi Valkeinen 			case 0:
1709f7018c21STomi Valkeinen 			case 8:
1710f7018c21STomi Valkeinen 				default_cmode = CMODE_8;
1711f7018c21STomi Valkeinen 				break;
1712f7018c21STomi Valkeinen 			case 15:
1713f7018c21STomi Valkeinen 			case 16:
1714f7018c21STomi Valkeinen 				default_cmode = CMODE_16;
1715f7018c21STomi Valkeinen 				break;
1716f7018c21STomi Valkeinen 			case 24:
1717f7018c21STomi Valkeinen 			case 32:
1718f7018c21STomi Valkeinen 				default_cmode = CMODE_32;
1719f7018c21STomi Valkeinen 				break;
1720f7018c21STomi Valkeinen 			}
1721f7018c21STomi Valkeinen 			continue;
1722f7018c21STomi Valkeinen 		}
1723f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
1724f7018c21STomi Valkeinen 		mode_option = this_opt;
1725f7018c21STomi Valkeinen 	}
1726f7018c21STomi Valkeinen 	return 0;
1727f7018c21STomi Valkeinen }
1728f7018c21STomi Valkeinen #endif  /*  MODULE  */
1729f7018c21STomi Valkeinen 
1730f7018c21STomi Valkeinen /* Backlight */
1731f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
1732f7018c21STomi Valkeinen #define MAX_LEVEL 0xFF
1733f7018c21STomi Valkeinen 
aty128_bl_get_level_brightness(struct aty128fb_par * par,int level)1734f7018c21STomi Valkeinen static int aty128_bl_get_level_brightness(struct aty128fb_par *par,
1735f7018c21STomi Valkeinen 		int level)
1736f7018c21STomi Valkeinen {
1737f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(par->pdev);
1738f7018c21STomi Valkeinen 	int atylevel;
1739f7018c21STomi Valkeinen 
1740f7018c21STomi Valkeinen 	/* Get and convert the value */
1741f7018c21STomi Valkeinen 	/* No locking of bl_curve since we read a single value */
1742f7018c21STomi Valkeinen 	atylevel = MAX_LEVEL -
1743f7018c21STomi Valkeinen 		(info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL);
1744f7018c21STomi Valkeinen 
1745f7018c21STomi Valkeinen 	if (atylevel < 0)
1746f7018c21STomi Valkeinen 		atylevel = 0;
1747f7018c21STomi Valkeinen 	else if (atylevel > MAX_LEVEL)
1748f7018c21STomi Valkeinen 		atylevel = MAX_LEVEL;
1749f7018c21STomi Valkeinen 
1750f7018c21STomi Valkeinen 	return atylevel;
1751f7018c21STomi Valkeinen }
1752f7018c21STomi Valkeinen 
1753f7018c21STomi Valkeinen /* We turn off the LCD completely instead of just dimming the backlight.
1754f7018c21STomi Valkeinen  * This provides greater power saving and the display is useless without
1755f7018c21STomi Valkeinen  * backlight anyway
1756f7018c21STomi Valkeinen  */
1757f7018c21STomi Valkeinen #define BACKLIGHT_LVDS_OFF
1758f7018c21STomi Valkeinen /* That one prevents proper CRT output with LCD off */
1759f7018c21STomi Valkeinen #undef BACKLIGHT_DAC_OFF
1760f7018c21STomi Valkeinen 
aty128_bl_update_status(struct backlight_device * bd)1761f7018c21STomi Valkeinen static int aty128_bl_update_status(struct backlight_device *bd)
1762f7018c21STomi Valkeinen {
1763f7018c21STomi Valkeinen 	struct aty128fb_par *par = bl_get_data(bd);
1764f7018c21STomi Valkeinen 	unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
1765f7018c21STomi Valkeinen 	int level;
1766f7018c21STomi Valkeinen 
17675b3fc998SStephen Kitt 	if (!par->lcd_on)
1768f7018c21STomi Valkeinen 		level = 0;
1769f7018c21STomi Valkeinen 	else
17705b3fc998SStephen Kitt 		level = backlight_get_brightness(bd);
1771f7018c21STomi Valkeinen 
1772f7018c21STomi Valkeinen 	reg |= LVDS_BL_MOD_EN | LVDS_BLON;
1773f7018c21STomi Valkeinen 	if (level > 0) {
1774f7018c21STomi Valkeinen 		reg |= LVDS_DIGION;
1775f7018c21STomi Valkeinen 		if (!(reg & LVDS_ON)) {
1776f7018c21STomi Valkeinen 			reg &= ~LVDS_BLON;
1777f7018c21STomi Valkeinen 			aty_st_le32(LVDS_GEN_CNTL, reg);
1778f7018c21STomi Valkeinen 			aty_ld_le32(LVDS_GEN_CNTL);
1779f7018c21STomi Valkeinen 			mdelay(10);
1780f7018c21STomi Valkeinen 			reg |= LVDS_BLON;
1781f7018c21STomi Valkeinen 			aty_st_le32(LVDS_GEN_CNTL, reg);
1782f7018c21STomi Valkeinen 		}
1783f7018c21STomi Valkeinen 		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
1784f7018c21STomi Valkeinen 		reg |= (aty128_bl_get_level_brightness(par, level) <<
1785f7018c21STomi Valkeinen 			LVDS_BL_MOD_LEVEL_SHIFT);
1786f7018c21STomi Valkeinen #ifdef BACKLIGHT_LVDS_OFF
1787f7018c21STomi Valkeinen 		reg |= LVDS_ON | LVDS_EN;
1788f7018c21STomi Valkeinen 		reg &= ~LVDS_DISPLAY_DIS;
1789f7018c21STomi Valkeinen #endif
1790f7018c21STomi Valkeinen 		aty_st_le32(LVDS_GEN_CNTL, reg);
1791f7018c21STomi Valkeinen #ifdef BACKLIGHT_DAC_OFF
1792f7018c21STomi Valkeinen 		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
1793f7018c21STomi Valkeinen #endif
1794f7018c21STomi Valkeinen 	} else {
1795f7018c21STomi Valkeinen 		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
1796f7018c21STomi Valkeinen 		reg |= (aty128_bl_get_level_brightness(par, 0) <<
1797f7018c21STomi Valkeinen 			LVDS_BL_MOD_LEVEL_SHIFT);
1798f7018c21STomi Valkeinen #ifdef BACKLIGHT_LVDS_OFF
1799f7018c21STomi Valkeinen 		reg |= LVDS_DISPLAY_DIS;
1800f7018c21STomi Valkeinen 		aty_st_le32(LVDS_GEN_CNTL, reg);
1801f7018c21STomi Valkeinen 		aty_ld_le32(LVDS_GEN_CNTL);
1802f7018c21STomi Valkeinen 		udelay(10);
1803f7018c21STomi Valkeinen 		reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
1804f7018c21STomi Valkeinen #endif
1805f7018c21STomi Valkeinen 		aty_st_le32(LVDS_GEN_CNTL, reg);
1806f7018c21STomi Valkeinen #ifdef BACKLIGHT_DAC_OFF
1807f7018c21STomi Valkeinen 		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
1808f7018c21STomi Valkeinen #endif
1809f7018c21STomi Valkeinen 	}
1810f7018c21STomi Valkeinen 
1811f7018c21STomi Valkeinen 	return 0;
1812f7018c21STomi Valkeinen }
1813f7018c21STomi Valkeinen 
1814f7018c21STomi Valkeinen static const struct backlight_ops aty128_bl_data = {
1815f7018c21STomi Valkeinen 	.update_status	= aty128_bl_update_status,
1816f7018c21STomi Valkeinen };
1817f7018c21STomi Valkeinen 
aty128_bl_set_power(struct fb_info * info,int power)1818f7018c21STomi Valkeinen static void aty128_bl_set_power(struct fb_info *info, int power)
1819f7018c21STomi Valkeinen {
1820f7018c21STomi Valkeinen 	if (info->bl_dev) {
1821f7018c21STomi Valkeinen 		info->bl_dev->props.power = power;
1822f7018c21STomi Valkeinen 		backlight_update_status(info->bl_dev);
1823f7018c21STomi Valkeinen 	}
1824f7018c21STomi Valkeinen }
1825f7018c21STomi Valkeinen 
aty128_bl_init(struct aty128fb_par * par)1826f7018c21STomi Valkeinen static void aty128_bl_init(struct aty128fb_par *par)
1827f7018c21STomi Valkeinen {
1828f7018c21STomi Valkeinen 	struct backlight_properties props;
1829f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(par->pdev);
1830f7018c21STomi Valkeinen 	struct backlight_device *bd;
1831f7018c21STomi Valkeinen 	char name[12];
1832f7018c21STomi Valkeinen 
1833f7018c21STomi Valkeinen 	/* Could be extended to Rage128Pro LVDS output too */
1834f7018c21STomi Valkeinen 	if (par->chip_gen != rage_M3)
1835f7018c21STomi Valkeinen 		return;
1836f7018c21STomi Valkeinen 
1837f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
1838f7018c21STomi Valkeinen 	if (!pmac_has_backlight_type("ati"))
1839f7018c21STomi Valkeinen 		return;
1840f7018c21STomi Valkeinen #endif
1841f7018c21STomi Valkeinen 
1842f7018c21STomi Valkeinen 	snprintf(name, sizeof(name), "aty128bl%d", info->node);
1843f7018c21STomi Valkeinen 
1844f7018c21STomi Valkeinen 	memset(&props, 0, sizeof(struct backlight_properties));
1845f7018c21STomi Valkeinen 	props.type = BACKLIGHT_RAW;
1846f7018c21STomi Valkeinen 	props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
18478f7bcbe3SThomas Zimmermann 	bd = backlight_device_register(name, info->device, par, &aty128_bl_data,
1848f7018c21STomi Valkeinen 				       &props);
1849f7018c21STomi Valkeinen 	if (IS_ERR(bd)) {
1850f7018c21STomi Valkeinen 		info->bl_dev = NULL;
1851f7018c21STomi Valkeinen 		printk(KERN_WARNING "aty128: Backlight registration failed\n");
1852f7018c21STomi Valkeinen 		goto error;
1853f7018c21STomi Valkeinen 	}
1854f7018c21STomi Valkeinen 
1855f7018c21STomi Valkeinen 	info->bl_dev = bd;
1856f7018c21STomi Valkeinen 	fb_bl_default_curve(info, 0,
1857f7018c21STomi Valkeinen 		 63 * FB_BACKLIGHT_MAX / MAX_LEVEL,
1858f7018c21STomi Valkeinen 		219 * FB_BACKLIGHT_MAX / MAX_LEVEL);
1859f7018c21STomi Valkeinen 
1860f7018c21STomi Valkeinen 	bd->props.brightness = bd->props.max_brightness;
1861f7018c21STomi Valkeinen 	bd->props.power = FB_BLANK_UNBLANK;
1862f7018c21STomi Valkeinen 	backlight_update_status(bd);
1863f7018c21STomi Valkeinen 
1864f7018c21STomi Valkeinen 	printk("aty128: Backlight initialized (%s)\n", name);
1865f7018c21STomi Valkeinen 
1866f7018c21STomi Valkeinen 	return;
1867f7018c21STomi Valkeinen 
1868f7018c21STomi Valkeinen error:
1869f7018c21STomi Valkeinen 	return;
1870f7018c21STomi Valkeinen }
1871f7018c21STomi Valkeinen 
aty128_bl_exit(struct backlight_device * bd)1872f7018c21STomi Valkeinen static void aty128_bl_exit(struct backlight_device *bd)
1873f7018c21STomi Valkeinen {
1874f7018c21STomi Valkeinen 	backlight_device_unregister(bd);
1875f7018c21STomi Valkeinen 	printk("aty128: Backlight unloaded\n");
1876f7018c21STomi Valkeinen }
1877f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY128_BACKLIGHT */
1878f7018c21STomi Valkeinen 
1879f7018c21STomi Valkeinen /*
1880f7018c21STomi Valkeinen  *  Initialisation
1881f7018c21STomi Valkeinen  */
1882f7018c21STomi Valkeinen 
1883f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC__disabled
aty128_early_resume(void * data)1884f7018c21STomi Valkeinen static void aty128_early_resume(void *data)
1885f7018c21STomi Valkeinen {
1886f7018c21STomi Valkeinen         struct aty128fb_par *par = data;
1887f7018c21STomi Valkeinen 
1888f7018c21STomi Valkeinen 	if (!console_trylock())
1889f7018c21STomi Valkeinen 		return;
1890f7018c21STomi Valkeinen 	pci_restore_state(par->pdev);
1891f7018c21STomi Valkeinen 	aty128_do_resume(par->pdev);
1892f7018c21STomi Valkeinen 	console_unlock();
1893f7018c21STomi Valkeinen }
1894f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
1895f7018c21STomi Valkeinen 
aty128_init(struct pci_dev * pdev,const struct pci_device_id * ent)1896f7018c21STomi Valkeinen static int aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
1897f7018c21STomi Valkeinen {
1898f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(pdev);
1899f7018c21STomi Valkeinen 	struct aty128fb_par *par = info->par;
1900f7018c21STomi Valkeinen 	struct fb_var_screeninfo var;
1901f7018c21STomi Valkeinen 	char video_card[50];
1902f7018c21STomi Valkeinen 	u8 chip_rev;
1903f7018c21STomi Valkeinen 	u32 dac;
1904f7018c21STomi Valkeinen 
1905f7018c21STomi Valkeinen 	/* Get the chip revision */
1906f7018c21STomi Valkeinen 	chip_rev = (aty_ld_le32(CNFG_CNTL) >> 16) & 0x1F;
1907f7018c21STomi Valkeinen 
1908f7018c21STomi Valkeinen 	strcpy(video_card, "Rage128 XX ");
1909f7018c21STomi Valkeinen 	video_card[8] = ent->device >> 8;
1910f7018c21STomi Valkeinen 	video_card[9] = ent->device & 0xFF;
1911f7018c21STomi Valkeinen 
1912f7018c21STomi Valkeinen 	/* range check to make sure */
1913f7018c21STomi Valkeinen 	if (ent->driver_data < ARRAY_SIZE(r128_family))
1914f7018c21STomi Valkeinen 		strlcat(video_card, r128_family[ent->driver_data],
1915f7018c21STomi Valkeinen 			sizeof(video_card));
1916f7018c21STomi Valkeinen 
1917f7018c21STomi Valkeinen 	printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
1918f7018c21STomi Valkeinen 
1919f7018c21STomi Valkeinen 	if (par->vram_size % (1024 * 1024) == 0)
1920f7018c21STomi Valkeinen 		printk("%dM %s\n", par->vram_size / (1024*1024), par->mem->name);
1921f7018c21STomi Valkeinen 	else
1922f7018c21STomi Valkeinen 		printk("%dk %s\n", par->vram_size / 1024, par->mem->name);
1923f7018c21STomi Valkeinen 
1924f7018c21STomi Valkeinen 	par->chip_gen = ent->driver_data;
1925f7018c21STomi Valkeinen 
1926f7018c21STomi Valkeinen 	/* fill in info */
1927f7018c21STomi Valkeinen 	info->fbops = &aty128fb_ops;
1928f7018c21STomi Valkeinen 
1929f7018c21STomi Valkeinen 	par->lcd_on = default_lcd_on;
1930f7018c21STomi Valkeinen 	par->crt_on = default_crt_on;
1931f7018c21STomi Valkeinen 
1932f7018c21STomi Valkeinen 	var = default_var;
1933f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
1934f7018c21STomi Valkeinen 	if (machine_is(powermac)) {
1935f7018c21STomi Valkeinen 		/* Indicate sleep capability */
1936f7018c21STomi Valkeinen 		if (par->chip_gen == rage_M3) {
1937f7018c21STomi Valkeinen 			pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
1938f7018c21STomi Valkeinen #if 0 /* Disable the early video resume hack for now as it's causing problems,
1939f7018c21STomi Valkeinen        * among others we now rely on the PCI core restoring the config space
1940f7018c21STomi Valkeinen        * for us, which isn't the case with that hack, and that code path causes
1941f7018c21STomi Valkeinen        * various things to be called with interrupts off while they shouldn't.
1942f7018c21STomi Valkeinen        * I'm leaving the code in as it can be useful for debugging purposes
1943f7018c21STomi Valkeinen        */
1944f7018c21STomi Valkeinen 			pmac_set_early_video_resume(aty128_early_resume, par);
1945f7018c21STomi Valkeinen #endif
1946f7018c21STomi Valkeinen 		}
1947f7018c21STomi Valkeinen 
1948f7018c21STomi Valkeinen 		/* Find default mode */
1949f7018c21STomi Valkeinen 		if (mode_option) {
1950f7018c21STomi Valkeinen 			if (!mac_find_mode(&var, info, mode_option, 8))
1951f7018c21STomi Valkeinen 				var = default_var;
1952f7018c21STomi Valkeinen 		} else {
1953f7018c21STomi Valkeinen 			if (default_vmode <= 0 || default_vmode > VMODE_MAX)
1954f7018c21STomi Valkeinen 				default_vmode = VMODE_1024_768_60;
1955f7018c21STomi Valkeinen 
1956f7018c21STomi Valkeinen 			/* iMacs need that resolution
1957f7018c21STomi Valkeinen 			 * PowerMac2,1 first r128 iMacs
1958f7018c21STomi Valkeinen 			 * PowerMac2,2 summer 2000 iMacs
1959f7018c21STomi Valkeinen 			 * PowerMac4,1 january 2001 iMacs "flower power"
1960f7018c21STomi Valkeinen 			 */
1961f7018c21STomi Valkeinen 			if (of_machine_is_compatible("PowerMac2,1") ||
1962f7018c21STomi Valkeinen 			    of_machine_is_compatible("PowerMac2,2") ||
1963f7018c21STomi Valkeinen 			    of_machine_is_compatible("PowerMac4,1"))
1964f7018c21STomi Valkeinen 				default_vmode = VMODE_1024_768_75;
1965f7018c21STomi Valkeinen 
1966f7018c21STomi Valkeinen 			/* iBook SE */
1967f7018c21STomi Valkeinen 			if (of_machine_is_compatible("PowerBook2,2"))
1968f7018c21STomi Valkeinen 				default_vmode = VMODE_800_600_60;
1969f7018c21STomi Valkeinen 
1970f7018c21STomi Valkeinen 			/* PowerBook Firewire (Pismo), iBook Dual USB */
1971f7018c21STomi Valkeinen 			if (of_machine_is_compatible("PowerBook3,1") ||
1972f7018c21STomi Valkeinen 			    of_machine_is_compatible("PowerBook4,1"))
1973f7018c21STomi Valkeinen 				default_vmode = VMODE_1024_768_60;
1974f7018c21STomi Valkeinen 
1975f7018c21STomi Valkeinen 			/* PowerBook Titanium */
1976f7018c21STomi Valkeinen 			if (of_machine_is_compatible("PowerBook3,2"))
1977f7018c21STomi Valkeinen 				default_vmode = VMODE_1152_768_60;
1978f7018c21STomi Valkeinen 
1979f7018c21STomi Valkeinen 			if (default_cmode > 16)
1980f7018c21STomi Valkeinen 				default_cmode = CMODE_32;
1981f7018c21STomi Valkeinen 			else if (default_cmode > 8)
1982f7018c21STomi Valkeinen 				default_cmode = CMODE_16;
1983f7018c21STomi Valkeinen 			else
1984f7018c21STomi Valkeinen 				default_cmode = CMODE_8;
1985f7018c21STomi Valkeinen 
1986f7018c21STomi Valkeinen 			if (mac_vmode_to_var(default_vmode, default_cmode, &var))
1987f7018c21STomi Valkeinen 				var = default_var;
1988f7018c21STomi Valkeinen 		}
1989f7018c21STomi Valkeinen 	} else
1990f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
1991f7018c21STomi Valkeinen 	{
1992f7018c21STomi Valkeinen 		if (mode_option)
1993f7018c21STomi Valkeinen 			if (fb_find_mode(&var, info, mode_option, NULL,
1994f7018c21STomi Valkeinen 					 0, &defaultmode, 8) == 0)
1995f7018c21STomi Valkeinen 				var = default_var;
1996f7018c21STomi Valkeinen 	}
1997f7018c21STomi Valkeinen 
1998f7018c21STomi Valkeinen 	var.accel_flags &= ~FB_ACCELF_TEXT;
1999f7018c21STomi Valkeinen //	var.accel_flags |= FB_ACCELF_TEXT;/* FIXME Will add accel later */
2000f7018c21STomi Valkeinen 
2001f7018c21STomi Valkeinen 	if (aty128fb_check_var(&var, info)) {
2002f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: Cannot set default mode.\n");
2003f7018c21STomi Valkeinen 		return 0;
2004f7018c21STomi Valkeinen 	}
2005f7018c21STomi Valkeinen 
2006f7018c21STomi Valkeinen 	/* setup the DAC the way we like it */
2007f7018c21STomi Valkeinen 	dac = aty_ld_le32(DAC_CNTL);
2008f7018c21STomi Valkeinen 	dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL);
2009f7018c21STomi Valkeinen 	dac |= DAC_MASK;
2010f7018c21STomi Valkeinen 	if (par->chip_gen == rage_M3)
2011f7018c21STomi Valkeinen 		dac |= DAC_PALETTE2_SNOOP_EN;
2012f7018c21STomi Valkeinen 	aty_st_le32(DAC_CNTL, dac);
2013f7018c21STomi Valkeinen 
2014f7018c21STomi Valkeinen 	/* turn off bus mastering, just in case */
2015f7018c21STomi Valkeinen 	aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_MASTER_DIS);
2016f7018c21STomi Valkeinen 
2017f7018c21STomi Valkeinen 	info->var = var;
2018f7018c21STomi Valkeinen 	fb_alloc_cmap(&info->cmap, 256, 0);
2019f7018c21STomi Valkeinen 
2020f7018c21STomi Valkeinen 	var.activate = FB_ACTIVATE_NOW;
2021f7018c21STomi Valkeinen 
2022f7018c21STomi Valkeinen 	aty128_init_engine(par);
2023f7018c21STomi Valkeinen 
2024f7018c21STomi Valkeinen 	par->pdev = pdev;
2025f7018c21STomi Valkeinen 	par->asleep = 0;
2026f7018c21STomi Valkeinen 	par->lock_blank = 0;
2027f7018c21STomi Valkeinen 
202885362a7bSThomas Zimmermann 	if (register_framebuffer(info) < 0)
202985362a7bSThomas Zimmermann 		return 0;
203085362a7bSThomas Zimmermann 
2031f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
2032f7018c21STomi Valkeinen 	if (backlight)
2033f7018c21STomi Valkeinen 		aty128_bl_init(par);
2034f7018c21STomi Valkeinen #endif
2035f7018c21STomi Valkeinen 
2036f7018c21STomi Valkeinen 	fb_info(info, "%s frame buffer device on %s\n",
2037f7018c21STomi Valkeinen 		info->fix.id, video_card);
2038f7018c21STomi Valkeinen 
2039f7018c21STomi Valkeinen 	return 1;	/* success! */
2040f7018c21STomi Valkeinen }
2041f7018c21STomi Valkeinen 
2042f7018c21STomi Valkeinen #ifdef CONFIG_PCI
2043f7018c21STomi Valkeinen /* register a card    ++ajoshi */
aty128_probe(struct pci_dev * pdev,const struct pci_device_id * ent)2044f7018c21STomi Valkeinen static int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
2045f7018c21STomi Valkeinen {
2046f7018c21STomi Valkeinen 	unsigned long fb_addr, reg_addr;
2047f7018c21STomi Valkeinen 	struct aty128fb_par *par;
2048f7018c21STomi Valkeinen 	struct fb_info *info;
2049f7018c21STomi Valkeinen 	int err;
2050f7018c21STomi Valkeinen #ifndef __sparc__
2051f7018c21STomi Valkeinen 	void __iomem *bios = NULL;
2052f7018c21STomi Valkeinen #endif
2053f7018c21STomi Valkeinen 
2054145eed48SThomas Zimmermann 	err = aperture_remove_conflicting_pci_devices(pdev, "aty128fb");
2055145eed48SThomas Zimmermann 	if (err)
2056145eed48SThomas Zimmermann 		return err;
2057145eed48SThomas Zimmermann 
2058f7018c21STomi Valkeinen 	/* Enable device in PCI config */
2059f7018c21STomi Valkeinen 	if ((err = pci_enable_device(pdev))) {
2060f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n",
2061f7018c21STomi Valkeinen 				err);
2062f7018c21STomi Valkeinen 		return -ENODEV;
2063f7018c21STomi Valkeinen 	}
2064f7018c21STomi Valkeinen 
2065f7018c21STomi Valkeinen 	fb_addr = pci_resource_start(pdev, 0);
2066f7018c21STomi Valkeinen 	if (!request_mem_region(fb_addr, pci_resource_len(pdev, 0),
2067f7018c21STomi Valkeinen 				"aty128fb FB")) {
2068f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: cannot reserve frame "
2069f7018c21STomi Valkeinen 				"buffer memory\n");
2070f7018c21STomi Valkeinen 		return -ENODEV;
2071f7018c21STomi Valkeinen 	}
2072f7018c21STomi Valkeinen 
2073f7018c21STomi Valkeinen 	reg_addr = pci_resource_start(pdev, 2);
2074f7018c21STomi Valkeinen 	if (!request_mem_region(reg_addr, pci_resource_len(pdev, 2),
2075f7018c21STomi Valkeinen 				"aty128fb MMIO")) {
2076f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n");
2077f7018c21STomi Valkeinen 		goto err_free_fb;
2078f7018c21STomi Valkeinen 	}
2079f7018c21STomi Valkeinen 
2080f7018c21STomi Valkeinen 	/* We have the resources. Now virtualize them */
2081f7018c21STomi Valkeinen 	info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev);
20820adcdbcbSBartlomiej Zolnierkiewicz 	if (!info)
2083f7018c21STomi Valkeinen 		goto err_free_mmio;
20840adcdbcbSBartlomiej Zolnierkiewicz 
2085f7018c21STomi Valkeinen 	par = info->par;
2086f7018c21STomi Valkeinen 
2087f7018c21STomi Valkeinen 	info->pseudo_palette = par->pseudo_palette;
2088f7018c21STomi Valkeinen 
2089f7018c21STomi Valkeinen 	/* Virtualize mmio region */
2090f7018c21STomi Valkeinen 	info->fix.mmio_start = reg_addr;
2091f7018c21STomi Valkeinen 	par->regbase = pci_ioremap_bar(pdev, 2);
2092f7018c21STomi Valkeinen 	if (!par->regbase)
2093f7018c21STomi Valkeinen 		goto err_free_info;
2094f7018c21STomi Valkeinen 
2095f7018c21STomi Valkeinen 	/* Grab memory size from the card */
2096f7018c21STomi Valkeinen 	// How does this relate to the resource length from the PCI hardware?
2097f7018c21STomi Valkeinen 	par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF;
2098f7018c21STomi Valkeinen 
2099f7018c21STomi Valkeinen 	/* Virtualize the framebuffer */
2100f4447ddeSLuis R. Rodriguez 	info->screen_base = ioremap_wc(fb_addr, par->vram_size);
2101f7018c21STomi Valkeinen 	if (!info->screen_base)
2102f7018c21STomi Valkeinen 		goto err_unmap_out;
2103f7018c21STomi Valkeinen 
2104f7018c21STomi Valkeinen 	/* Set up info->fix */
2105f7018c21STomi Valkeinen 	info->fix = aty128fb_fix;
2106f7018c21STomi Valkeinen 	info->fix.smem_start = fb_addr;
2107f7018c21STomi Valkeinen 	info->fix.smem_len = par->vram_size;
2108f7018c21STomi Valkeinen 	info->fix.mmio_start = reg_addr;
2109f7018c21STomi Valkeinen 
2110f7018c21STomi Valkeinen 	/* If we can't test scratch registers, something is seriously wrong */
2111f7018c21STomi Valkeinen 	if (!register_test(par)) {
2112f7018c21STomi Valkeinen 		printk(KERN_ERR "aty128fb: Can't write to video register!\n");
2113f7018c21STomi Valkeinen 		goto err_out;
2114f7018c21STomi Valkeinen 	}
2115f7018c21STomi Valkeinen 
2116f7018c21STomi Valkeinen #ifndef __sparc__
2117f7018c21STomi Valkeinen 	bios = aty128_map_ROM(par, pdev);
2118f7018c21STomi Valkeinen #ifdef CONFIG_X86
2119f7018c21STomi Valkeinen 	if (bios == NULL)
2120f7018c21STomi Valkeinen 		bios = aty128_find_mem_vbios(par);
2121f7018c21STomi Valkeinen #endif
2122f7018c21STomi Valkeinen 	if (bios == NULL)
2123f7018c21STomi Valkeinen 		printk(KERN_INFO "aty128fb: BIOS not located, guessing timings.\n");
2124f7018c21STomi Valkeinen 	else {
2125f7018c21STomi Valkeinen 		printk(KERN_INFO "aty128fb: Rage128 BIOS located\n");
2126f7018c21STomi Valkeinen 		aty128_get_pllinfo(par, bios);
2127f7018c21STomi Valkeinen 		pci_unmap_rom(pdev, bios);
2128f7018c21STomi Valkeinen 	}
2129f7018c21STomi Valkeinen #endif /* __sparc__ */
2130f7018c21STomi Valkeinen 
2131f7018c21STomi Valkeinen 	aty128_timings(par);
2132f7018c21STomi Valkeinen 	pci_set_drvdata(pdev, info);
2133f7018c21STomi Valkeinen 
2134f7018c21STomi Valkeinen 	if (!aty128_init(pdev, ent))
2135f7018c21STomi Valkeinen 		goto err_out;
2136f7018c21STomi Valkeinen 
2137f4447ddeSLuis R. Rodriguez 	if (mtrr)
2138f4447ddeSLuis R. Rodriguez 		par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
2139f4447ddeSLuis R. Rodriguez 						  par->vram_size);
2140f7018c21STomi Valkeinen 	return 0;
2141f7018c21STomi Valkeinen 
2142f7018c21STomi Valkeinen err_out:
2143f7018c21STomi Valkeinen 	iounmap(info->screen_base);
2144f7018c21STomi Valkeinen err_unmap_out:
2145f7018c21STomi Valkeinen 	iounmap(par->regbase);
2146f7018c21STomi Valkeinen err_free_info:
2147f7018c21STomi Valkeinen 	framebuffer_release(info);
2148f7018c21STomi Valkeinen err_free_mmio:
2149f7018c21STomi Valkeinen 	release_mem_region(pci_resource_start(pdev, 2),
2150f7018c21STomi Valkeinen 			pci_resource_len(pdev, 2));
2151f7018c21STomi Valkeinen err_free_fb:
2152f7018c21STomi Valkeinen 	release_mem_region(pci_resource_start(pdev, 0),
2153f7018c21STomi Valkeinen 			pci_resource_len(pdev, 0));
2154f7018c21STomi Valkeinen 	return -ENODEV;
2155f7018c21STomi Valkeinen }
2156f7018c21STomi Valkeinen 
aty128_remove(struct pci_dev * pdev)2157f7018c21STomi Valkeinen static void aty128_remove(struct pci_dev *pdev)
2158f7018c21STomi Valkeinen {
2159f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(pdev);
2160f7018c21STomi Valkeinen 	struct aty128fb_par *par;
2161f7018c21STomi Valkeinen 
2162f7018c21STomi Valkeinen 	if (!info)
2163f7018c21STomi Valkeinen 		return;
2164f7018c21STomi Valkeinen 
2165f7018c21STomi Valkeinen 	par = info->par;
2166f7018c21STomi Valkeinen 
2167f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY128_BACKLIGHT
2168f7018c21STomi Valkeinen 	aty128_bl_exit(info->bl_dev);
2169f7018c21STomi Valkeinen #endif
2170f7018c21STomi Valkeinen 
217185362a7bSThomas Zimmermann 	unregister_framebuffer(info);
217285362a7bSThomas Zimmermann 
2173f4447ddeSLuis R. Rodriguez 	arch_phys_wc_del(par->wc_cookie);
2174f7018c21STomi Valkeinen 	iounmap(par->regbase);
2175f7018c21STomi Valkeinen 	iounmap(info->screen_base);
2176f7018c21STomi Valkeinen 
2177f7018c21STomi Valkeinen 	release_mem_region(pci_resource_start(pdev, 0),
2178f7018c21STomi Valkeinen 			   pci_resource_len(pdev, 0));
2179f7018c21STomi Valkeinen 	release_mem_region(pci_resource_start(pdev, 2),
2180f7018c21STomi Valkeinen 			   pci_resource_len(pdev, 2));
2181f7018c21STomi Valkeinen 	framebuffer_release(info);
2182f7018c21STomi Valkeinen }
2183f7018c21STomi Valkeinen #endif /* CONFIG_PCI */
2184f7018c21STomi Valkeinen 
2185f7018c21STomi Valkeinen 
2186f7018c21STomi Valkeinen 
2187f7018c21STomi Valkeinen     /*
2188f7018c21STomi Valkeinen      *  Blank the display.
2189f7018c21STomi Valkeinen      */
aty128fb_blank(int blank,struct fb_info * fb)2190f7018c21STomi Valkeinen static int aty128fb_blank(int blank, struct fb_info *fb)
2191f7018c21STomi Valkeinen {
2192f7018c21STomi Valkeinen 	struct aty128fb_par *par = fb->par;
2193f7018c21STomi Valkeinen 	u8 state;
2194f7018c21STomi Valkeinen 
2195f7018c21STomi Valkeinen 	if (par->lock_blank || par->asleep)
2196f7018c21STomi Valkeinen 		return 0;
2197f7018c21STomi Valkeinen 
2198f7018c21STomi Valkeinen 	switch (blank) {
2199f7018c21STomi Valkeinen 	case FB_BLANK_NORMAL:
2200f7018c21STomi Valkeinen 		state = 4;
2201f7018c21STomi Valkeinen 		break;
2202f7018c21STomi Valkeinen 	case FB_BLANK_VSYNC_SUSPEND:
2203f7018c21STomi Valkeinen 		state = 6;
2204f7018c21STomi Valkeinen 		break;
2205f7018c21STomi Valkeinen 	case FB_BLANK_HSYNC_SUSPEND:
2206f7018c21STomi Valkeinen 		state = 5;
2207f7018c21STomi Valkeinen 		break;
2208f7018c21STomi Valkeinen 	case FB_BLANK_POWERDOWN:
2209f7018c21STomi Valkeinen 		state = 7;
2210f7018c21STomi Valkeinen 		break;
2211f7018c21STomi Valkeinen 	case FB_BLANK_UNBLANK:
2212f7018c21STomi Valkeinen 	default:
2213f7018c21STomi Valkeinen 		state = 0;
2214f7018c21STomi Valkeinen 		break;
2215f7018c21STomi Valkeinen 	}
2216f7018c21STomi Valkeinen 	aty_st_8(CRTC_EXT_CNTL+1, state);
2217f7018c21STomi Valkeinen 
2218f7018c21STomi Valkeinen 	if (par->chip_gen == rage_M3) {
2219f7018c21STomi Valkeinen 		aty128_set_crt_enable(par, par->crt_on && !blank);
2220f7018c21STomi Valkeinen 		aty128_set_lcd_enable(par, par->lcd_on && !blank);
2221f7018c21STomi Valkeinen 	}
2222f7018c21STomi Valkeinen 
2223f7018c21STomi Valkeinen 	return 0;
2224f7018c21STomi Valkeinen }
2225f7018c21STomi Valkeinen 
2226f7018c21STomi Valkeinen /*
2227f7018c21STomi Valkeinen  *  Set a single color register. The values supplied are already
2228f7018c21STomi Valkeinen  *  rounded down to the hardware's capabilities (according to the
2229f7018c21STomi Valkeinen  *  entries in the var structure). Return != 0 for invalid regno.
2230f7018c21STomi Valkeinen  */
aty128fb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)2231f7018c21STomi Valkeinen static int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
2232f7018c21STomi Valkeinen 			      u_int transp, struct fb_info *info)
2233f7018c21STomi Valkeinen {
2234f7018c21STomi Valkeinen 	struct aty128fb_par *par = info->par;
2235f7018c21STomi Valkeinen 
2236f7018c21STomi Valkeinen 	if (regno > 255
2237f7018c21STomi Valkeinen 	    || (par->crtc.depth == 16 && regno > 63)
2238f7018c21STomi Valkeinen 	    || (par->crtc.depth == 15 && regno > 31))
2239f7018c21STomi Valkeinen 		return 1;
2240f7018c21STomi Valkeinen 
2241f7018c21STomi Valkeinen 	red >>= 8;
2242f7018c21STomi Valkeinen 	green >>= 8;
2243f7018c21STomi Valkeinen 	blue >>= 8;
2244f7018c21STomi Valkeinen 
2245f7018c21STomi Valkeinen 	if (regno < 16) {
2246f7018c21STomi Valkeinen 		int i;
2247f7018c21STomi Valkeinen 		u32 *pal = info->pseudo_palette;
2248f7018c21STomi Valkeinen 
2249f7018c21STomi Valkeinen 		switch (par->crtc.depth) {
2250f7018c21STomi Valkeinen 		case 15:
2251f7018c21STomi Valkeinen 			pal[regno] = (regno << 10) | (regno << 5) | regno;
2252f7018c21STomi Valkeinen 			break;
2253f7018c21STomi Valkeinen 		case 16:
2254f7018c21STomi Valkeinen 			pal[regno] = (regno << 11) | (regno << 6) | regno;
2255f7018c21STomi Valkeinen 			break;
2256f7018c21STomi Valkeinen 		case 24:
2257f7018c21STomi Valkeinen 			pal[regno] = (regno << 16) | (regno << 8) | regno;
2258f7018c21STomi Valkeinen 			break;
2259f7018c21STomi Valkeinen 		case 32:
2260f7018c21STomi Valkeinen 			i = (regno << 8) | regno;
2261f7018c21STomi Valkeinen 			pal[regno] = (i << 16) | i;
2262f7018c21STomi Valkeinen 			break;
2263f7018c21STomi Valkeinen 		}
2264f7018c21STomi Valkeinen 	}
2265f7018c21STomi Valkeinen 
2266f7018c21STomi Valkeinen 	if (par->crtc.depth == 16 && regno > 0) {
2267f7018c21STomi Valkeinen 		/*
2268f7018c21STomi Valkeinen 		 * With the 5-6-5 split of bits for RGB at 16 bits/pixel, we
2269f7018c21STomi Valkeinen 		 * have 32 slots for R and B values but 64 slots for G values.
2270f7018c21STomi Valkeinen 		 * Thus the R and B values go in one slot but the G value
2271f7018c21STomi Valkeinen 		 * goes in a different slot, and we have to avoid disturbing
2272f7018c21STomi Valkeinen 		 * the other fields in the slots we touch.
2273f7018c21STomi Valkeinen 		 */
2274f7018c21STomi Valkeinen 		par->green[regno] = green;
2275f7018c21STomi Valkeinen 		if (regno < 32) {
2276f7018c21STomi Valkeinen 			par->red[regno] = red;
2277f7018c21STomi Valkeinen 			par->blue[regno] = blue;
2278f7018c21STomi Valkeinen 			aty128_st_pal(regno * 8, red, par->green[regno*2],
2279f7018c21STomi Valkeinen 				      blue, par);
2280f7018c21STomi Valkeinen 		}
2281f7018c21STomi Valkeinen 		red = par->red[regno/2];
2282f7018c21STomi Valkeinen 		blue = par->blue[regno/2];
2283f7018c21STomi Valkeinen 		regno <<= 2;
2284f7018c21STomi Valkeinen 	} else if (par->crtc.bpp == 16)
2285f7018c21STomi Valkeinen 		regno <<= 3;
2286f7018c21STomi Valkeinen 	aty128_st_pal(regno, red, green, blue, par);
2287f7018c21STomi Valkeinen 
2288f7018c21STomi Valkeinen 	return 0;
2289f7018c21STomi Valkeinen }
2290f7018c21STomi Valkeinen 
2291f7018c21STomi Valkeinen #define ATY_MIRROR_LCD_ON	0x00000001
2292f7018c21STomi Valkeinen #define ATY_MIRROR_CRT_ON	0x00000002
2293f7018c21STomi Valkeinen 
2294f7018c21STomi Valkeinen /* out param: u32*	backlight value: 0 to 15 */
2295f7018c21STomi Valkeinen #define FBIO_ATY128_GET_MIRROR	_IOR('@', 1, __u32)
2296f7018c21STomi Valkeinen /* in param: u32*	backlight value: 0 to 15 */
2297f7018c21STomi Valkeinen #define FBIO_ATY128_SET_MIRROR	_IOW('@', 2, __u32)
2298f7018c21STomi Valkeinen 
aty128fb_ioctl(struct fb_info * info,u_int cmd,u_long arg)2299f7018c21STomi Valkeinen static int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
2300f7018c21STomi Valkeinen {
2301f7018c21STomi Valkeinen 	struct aty128fb_par *par = info->par;
2302f7018c21STomi Valkeinen 	u32 value;
2303f7018c21STomi Valkeinen 	int rc;
2304f7018c21STomi Valkeinen 
2305f7018c21STomi Valkeinen 	switch (cmd) {
2306f7018c21STomi Valkeinen 	case FBIO_ATY128_SET_MIRROR:
2307f7018c21STomi Valkeinen 		if (par->chip_gen != rage_M3)
2308f7018c21STomi Valkeinen 			return -EINVAL;
2309f7018c21STomi Valkeinen 		rc = get_user(value, (__u32 __user *)arg);
2310f7018c21STomi Valkeinen 		if (rc)
2311f7018c21STomi Valkeinen 			return rc;
2312f7018c21STomi Valkeinen 		par->lcd_on = (value & 0x01) != 0;
2313f7018c21STomi Valkeinen 		par->crt_on = (value & 0x02) != 0;
2314f7018c21STomi Valkeinen 		if (!par->crt_on && !par->lcd_on)
2315f7018c21STomi Valkeinen 			par->lcd_on = 1;
2316f7018c21STomi Valkeinen 		aty128_set_crt_enable(par, par->crt_on);
2317f7018c21STomi Valkeinen 		aty128_set_lcd_enable(par, par->lcd_on);
2318f7018c21STomi Valkeinen 		return 0;
2319f7018c21STomi Valkeinen 	case FBIO_ATY128_GET_MIRROR:
2320f7018c21STomi Valkeinen 		if (par->chip_gen != rage_M3)
2321f7018c21STomi Valkeinen 			return -EINVAL;
2322f7018c21STomi Valkeinen 		value = (par->crt_on << 1) | par->lcd_on;
2323f7018c21STomi Valkeinen 		return put_user(value, (__u32 __user *)arg);
2324f7018c21STomi Valkeinen 	}
2325f7018c21STomi Valkeinen 	return -EINVAL;
2326f7018c21STomi Valkeinen }
2327f7018c21STomi Valkeinen 
aty128_set_suspend(struct aty128fb_par * par,int suspend)2328f7018c21STomi Valkeinen static void aty128_set_suspend(struct aty128fb_par *par, int suspend)
2329f7018c21STomi Valkeinen {
2330f7018c21STomi Valkeinen 	u32	pmgt;
2331f7018c21STomi Valkeinen 
2332f7018c21STomi Valkeinen 	if (!par->pdev->pm_cap)
2333f7018c21STomi Valkeinen 		return;
2334f7018c21STomi Valkeinen 
2335f7018c21STomi Valkeinen 	/* Set the chip into the appropriate suspend mode (we use D2,
2336f7018c21STomi Valkeinen 	 * D3 would require a complete re-initialisation of the chip,
2337f7018c21STomi Valkeinen 	 * including PCI config registers, clocks, AGP configuration, ...)
2338f7018c21STomi Valkeinen 	 *
2339f7018c21STomi Valkeinen 	 * For resume, the core will have already brought us back to D0
2340f7018c21STomi Valkeinen 	 */
2341f7018c21STomi Valkeinen 	if (suspend) {
2342f7018c21STomi Valkeinen 		/* Make sure CRTC2 is reset. Remove that the day we decide to
2343f7018c21STomi Valkeinen 		 * actually use CRTC2 and replace it with real code for disabling
2344f7018c21STomi Valkeinen 		 * the CRTC2 output during sleep
2345f7018c21STomi Valkeinen 		 */
2346f7018c21STomi Valkeinen 		aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) &
2347f7018c21STomi Valkeinen 			~(CRTC2_EN));
2348f7018c21STomi Valkeinen 
2349f7018c21STomi Valkeinen 		/* Set the power management mode to be PCI based */
2350f7018c21STomi Valkeinen 		/* Use this magic value for now */
2351f7018c21STomi Valkeinen 		pmgt = 0x0c005407;
2352f7018c21STomi Valkeinen 		aty_st_pll(POWER_MANAGEMENT, pmgt);
2353f7018c21STomi Valkeinen 		(void)aty_ld_pll(POWER_MANAGEMENT);
2354f7018c21STomi Valkeinen 		aty_st_le32(BUS_CNTL1, 0x00000010);
2355f7018c21STomi Valkeinen 		aty_st_le32(MEM_POWER_MISC, 0x0c830000);
235686c4e7c3SJia-Ju Bai 		msleep(100);
2357f7018c21STomi Valkeinen 	}
2358f7018c21STomi Valkeinen }
2359f7018c21STomi Valkeinen 
aty128_pci_suspend_late(struct device * dev,pm_message_t state)2360c1a47776SVaibhav Gupta static int aty128_pci_suspend_late(struct device *dev, pm_message_t state)
2361f7018c21STomi Valkeinen {
2362c1a47776SVaibhav Gupta 	struct pci_dev *pdev = to_pci_dev(dev);
2363f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(pdev);
2364f7018c21STomi Valkeinen 	struct aty128fb_par *par = info->par;
2365f7018c21STomi Valkeinen 
2366f7018c21STomi Valkeinen 	/* We don't do anything but D2, for now we return 0, but
2367f7018c21STomi Valkeinen 	 * we may want to change that. How do we know if the BIOS
2368f7018c21STomi Valkeinen 	 * can properly take care of D3 ? Also, with swsusp, we
2369f7018c21STomi Valkeinen 	 * know we'll be rebooted, ...
2370f7018c21STomi Valkeinen 	 */
2371f7018c21STomi Valkeinen #ifndef CONFIG_PPC_PMAC
2372f7018c21STomi Valkeinen 	/* HACK ALERT ! Once I find a proper way to say to each driver
2373f7018c21STomi Valkeinen 	 * individually what will happen with it's PCI slot, I'll change
2374f7018c21STomi Valkeinen 	 * that. On laptops, the AGP slot is just unclocked, so D2 is
2375f7018c21STomi Valkeinen 	 * expected, while on desktops, the card is powered off
2376f7018c21STomi Valkeinen 	 */
2377f7018c21STomi Valkeinen 	return 0;
2378f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
2379f7018c21STomi Valkeinen 
2380f7018c21STomi Valkeinen 	if (state.event == pdev->dev.power.power_state.event)
2381f7018c21STomi Valkeinen 		return 0;
2382f7018c21STomi Valkeinen 
2383f7018c21STomi Valkeinen 	printk(KERN_DEBUG "aty128fb: suspending...\n");
2384f7018c21STomi Valkeinen 
2385f7018c21STomi Valkeinen 	console_lock();
2386f7018c21STomi Valkeinen 
2387f7018c21STomi Valkeinen 	fb_set_suspend(info, 1);
2388f7018c21STomi Valkeinen 
2389f7018c21STomi Valkeinen 	/* Make sure engine is reset */
2390f7018c21STomi Valkeinen 	wait_for_idle(par);
2391f7018c21STomi Valkeinen 	aty128_reset_engine(par);
2392f7018c21STomi Valkeinen 	wait_for_idle(par);
2393f7018c21STomi Valkeinen 
2394f7018c21STomi Valkeinen 	/* Blank display and LCD */
2395f7018c21STomi Valkeinen 	aty128fb_blank(FB_BLANK_POWERDOWN, info);
2396f7018c21STomi Valkeinen 
2397f7018c21STomi Valkeinen 	/* Sleep */
2398f7018c21STomi Valkeinen 	par->asleep = 1;
2399f7018c21STomi Valkeinen 	par->lock_blank = 1;
2400f7018c21STomi Valkeinen 
2401f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
2402f7018c21STomi Valkeinen 	/* On powermac, we have hooks to properly suspend/resume AGP now,
2403f7018c21STomi Valkeinen 	 * use them here. We'll ultimately need some generic support here,
2404f7018c21STomi Valkeinen 	 * but the generic code isn't quite ready for that yet
2405f7018c21STomi Valkeinen 	 */
2406f7018c21STomi Valkeinen 	pmac_suspend_agp_for_card(pdev);
2407f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
2408f7018c21STomi Valkeinen 
2409f7018c21STomi Valkeinen 	/* We need a way to make sure the fbdev layer will _not_ touch the
2410f7018c21STomi Valkeinen 	 * framebuffer before we put the chip to suspend state. On 2.4, I
2411f7018c21STomi Valkeinen 	 * used dummy fb ops, 2.5 need proper support for this at the
2412f7018c21STomi Valkeinen 	 * fbdev level
2413f7018c21STomi Valkeinen 	 */
2414f7018c21STomi Valkeinen 	if (state.event != PM_EVENT_ON)
2415f7018c21STomi Valkeinen 		aty128_set_suspend(par, 1);
2416f7018c21STomi Valkeinen 
2417f7018c21STomi Valkeinen 	console_unlock();
2418f7018c21STomi Valkeinen 
2419f7018c21STomi Valkeinen 	pdev->dev.power.power_state = state;
2420f7018c21STomi Valkeinen 
2421f7018c21STomi Valkeinen 	return 0;
2422f7018c21STomi Valkeinen }
2423f7018c21STomi Valkeinen 
aty128_pci_suspend(struct device * dev)2424c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_suspend(struct device *dev)
2425c1a47776SVaibhav Gupta {
2426c1a47776SVaibhav Gupta 	return aty128_pci_suspend_late(dev, PMSG_SUSPEND);
2427c1a47776SVaibhav Gupta }
2428c1a47776SVaibhav Gupta 
aty128_pci_hibernate(struct device * dev)2429c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_hibernate(struct device *dev)
2430c1a47776SVaibhav Gupta {
2431c1a47776SVaibhav Gupta 	return aty128_pci_suspend_late(dev, PMSG_HIBERNATE);
2432c1a47776SVaibhav Gupta }
2433c1a47776SVaibhav Gupta 
aty128_pci_freeze(struct device * dev)2434c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_freeze(struct device *dev)
2435c1a47776SVaibhav Gupta {
2436c1a47776SVaibhav Gupta 	return aty128_pci_suspend_late(dev, PMSG_FREEZE);
2437c1a47776SVaibhav Gupta }
2438c1a47776SVaibhav Gupta 
aty128_do_resume(struct pci_dev * pdev)2439f7018c21STomi Valkeinen static int aty128_do_resume(struct pci_dev *pdev)
2440f7018c21STomi Valkeinen {
2441f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(pdev);
2442f7018c21STomi Valkeinen 	struct aty128fb_par *par = info->par;
2443f7018c21STomi Valkeinen 
2444f7018c21STomi Valkeinen 	if (pdev->dev.power.power_state.event == PM_EVENT_ON)
2445f7018c21STomi Valkeinen 		return 0;
2446f7018c21STomi Valkeinen 
2447f7018c21STomi Valkeinen 	/* PCI state will have been restored by the core, so
2448f7018c21STomi Valkeinen 	 * we should be in D0 now with our config space fully
2449f7018c21STomi Valkeinen 	 * restored
2450f7018c21STomi Valkeinen 	 */
2451f7018c21STomi Valkeinen 
2452f7018c21STomi Valkeinen 	/* Wakeup chip */
2453f7018c21STomi Valkeinen 	aty128_set_suspend(par, 0);
2454f7018c21STomi Valkeinen 	par->asleep = 0;
2455f7018c21STomi Valkeinen 
2456f7018c21STomi Valkeinen 	/* Restore display & engine */
2457f7018c21STomi Valkeinen 	aty128_reset_engine(par);
2458f7018c21STomi Valkeinen 	wait_for_idle(par);
2459f7018c21STomi Valkeinen 	aty128fb_set_par(info);
2460f7018c21STomi Valkeinen 	fb_pan_display(info, &info->var);
2461f7018c21STomi Valkeinen 	fb_set_cmap(&info->cmap, info);
2462f7018c21STomi Valkeinen 
2463f7018c21STomi Valkeinen 	/* Refresh */
2464f7018c21STomi Valkeinen 	fb_set_suspend(info, 0);
2465f7018c21STomi Valkeinen 
2466f7018c21STomi Valkeinen 	/* Unblank */
2467f7018c21STomi Valkeinen 	par->lock_blank = 0;
2468f7018c21STomi Valkeinen 	aty128fb_blank(0, info);
2469f7018c21STomi Valkeinen 
2470f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
2471f7018c21STomi Valkeinen 	/* On powermac, we have hooks to properly suspend/resume AGP now,
2472f7018c21STomi Valkeinen 	 * use them here. We'll ultimately need some generic support here,
2473f7018c21STomi Valkeinen 	 * but the generic code isn't quite ready for that yet
2474f7018c21STomi Valkeinen 	 */
2475f7018c21STomi Valkeinen 	pmac_resume_agp_for_card(pdev);
2476f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
2477f7018c21STomi Valkeinen 
2478f7018c21STomi Valkeinen 	pdev->dev.power.power_state = PMSG_ON;
2479f7018c21STomi Valkeinen 
2480f7018c21STomi Valkeinen 	printk(KERN_DEBUG "aty128fb: resumed !\n");
2481f7018c21STomi Valkeinen 
2482f7018c21STomi Valkeinen 	return 0;
2483f7018c21STomi Valkeinen }
2484f7018c21STomi Valkeinen 
aty128_pci_resume(struct device * dev)2485c1a47776SVaibhav Gupta static int __maybe_unused aty128_pci_resume(struct device *dev)
2486f7018c21STomi Valkeinen {
2487f7018c21STomi Valkeinen 	int rc;
2488f7018c21STomi Valkeinen 
2489f7018c21STomi Valkeinen 	console_lock();
2490c1a47776SVaibhav Gupta 	rc = aty128_do_resume(to_pci_dev(dev));
2491f7018c21STomi Valkeinen 	console_unlock();
2492f7018c21STomi Valkeinen 
2493f7018c21STomi Valkeinen 	return rc;
2494f7018c21STomi Valkeinen }
2495f7018c21STomi Valkeinen 
2496f7018c21STomi Valkeinen 
aty128fb_init(void)2497f7018c21STomi Valkeinen static int aty128fb_init(void)
2498f7018c21STomi Valkeinen {
2499f7018c21STomi Valkeinen #ifndef MODULE
2500f7018c21STomi Valkeinen 	char *option = NULL;
25010ba2fa8cSThomas Zimmermann #endif
2502f7018c21STomi Valkeinen 
25030ba2fa8cSThomas Zimmermann 	if (fb_modesetting_disabled("aty128fb"))
25040ba2fa8cSThomas Zimmermann 		return -ENODEV;
25050ba2fa8cSThomas Zimmermann 
25060ba2fa8cSThomas Zimmermann #ifndef MODULE
2507f7018c21STomi Valkeinen 	if (fb_get_options("aty128fb", &option))
2508f7018c21STomi Valkeinen 		return -ENODEV;
2509f7018c21STomi Valkeinen 	aty128fb_setup(option);
2510f7018c21STomi Valkeinen #endif
2511f7018c21STomi Valkeinen 
2512f7018c21STomi Valkeinen 	return pci_register_driver(&aty128fb_driver);
2513f7018c21STomi Valkeinen }
2514f7018c21STomi Valkeinen 
aty128fb_exit(void)2515f7018c21STomi Valkeinen static void __exit aty128fb_exit(void)
2516f7018c21STomi Valkeinen {
2517f7018c21STomi Valkeinen 	pci_unregister_driver(&aty128fb_driver);
2518f7018c21STomi Valkeinen }
2519f7018c21STomi Valkeinen 
2520f7018c21STomi Valkeinen module_init(aty128fb_init);
2521f7018c21STomi Valkeinen 
2522f7018c21STomi Valkeinen module_exit(aty128fb_exit);
2523f7018c21STomi Valkeinen 
2524f7018c21STomi Valkeinen MODULE_AUTHOR("(c)1999-2003 Brad Douglas <brad@neruo.com>");
2525f7018c21STomi Valkeinen MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
2526f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
2527f7018c21STomi Valkeinen module_param(mode_option, charp, 0);
2528f7018c21STomi Valkeinen MODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
2529f7018c21STomi Valkeinen module_param_named(nomtrr, mtrr, invbool, 0);
2530f7018c21STomi Valkeinen MODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)");
2531