xref: /openbmc/linux/drivers/video/fbdev/aty/atyfb_base.c (revision f55de6ec375da89f89f1a76e1b998e5f14878c06)
1f7018c21STomi Valkeinen /*
2f7018c21STomi Valkeinen  *  ATI Frame Buffer Device Driver Core
3f7018c21STomi Valkeinen  *
4f7018c21STomi Valkeinen  *	Copyright (C) 2004  Alex Kern <alex.kern@gmx.de>
5f7018c21STomi Valkeinen  *	Copyright (C) 1997-2001  Geert Uytterhoeven
6f7018c21STomi Valkeinen  *	Copyright (C) 1998  Bernd Harries
7f7018c21STomi Valkeinen  *	Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
8f7018c21STomi Valkeinen  *
9f7018c21STomi Valkeinen  *  This driver supports the following ATI graphics chips:
10f7018c21STomi Valkeinen  *    - ATI Mach64
11f7018c21STomi Valkeinen  *
12f7018c21STomi Valkeinen  *  To do: add support for
13f7018c21STomi Valkeinen  *    - ATI Rage128 (from aty128fb.c)
14f7018c21STomi Valkeinen  *    - ATI Radeon (from radeonfb.c)
15f7018c21STomi Valkeinen  *
16f7018c21STomi Valkeinen  *  This driver is partly based on the PowerMac console driver:
17f7018c21STomi Valkeinen  *
18f7018c21STomi Valkeinen  *	Copyright (C) 1996 Paul Mackerras
19f7018c21STomi Valkeinen  *
20f7018c21STomi Valkeinen  *  and on the PowerMac ATI/mach64 display driver:
21f7018c21STomi Valkeinen  *
22f7018c21STomi Valkeinen  *	Copyright (C) 1997 Michael AK Tesch
23f7018c21STomi Valkeinen  *
24f7018c21STomi Valkeinen  *	      with work by Jon Howell
25f7018c21STomi Valkeinen  *			   Harry AC Eaton
26f7018c21STomi Valkeinen  *			   Anthony Tong <atong@uiuc.edu>
27f7018c21STomi Valkeinen  *
28f7018c21STomi Valkeinen  *  Generic LCD support written by Daniel Mantione, ported from 2.4.20 by Alex Kern
29f7018c21STomi Valkeinen  *  Many Thanks to Ville Syrjälä for patches and fixing nasting 16 bit color bug.
30f7018c21STomi Valkeinen  *
31f7018c21STomi Valkeinen  *  This file is subject to the terms and conditions of the GNU General Public
32f7018c21STomi Valkeinen  *  License. See the file COPYING in the main directory of this archive for
33f7018c21STomi Valkeinen  *  more details.
34f7018c21STomi Valkeinen  *
35f7018c21STomi Valkeinen  *  Many thanks to Nitya from ATI devrel for support and patience !
36f7018c21STomi Valkeinen  */
37f7018c21STomi Valkeinen 
38f7018c21STomi Valkeinen /******************************************************************************
39f7018c21STomi Valkeinen 
40f7018c21STomi Valkeinen   TODO:
41f7018c21STomi Valkeinen 
42f7018c21STomi Valkeinen     - cursor support on all cards and all ramdacs.
43f7018c21STomi Valkeinen     - cursor parameters controlable via ioctl()s.
44f7018c21STomi Valkeinen     - guess PLL and MCLK based on the original PLL register values initialized
45f7018c21STomi Valkeinen       by Open Firmware (if they are initialized). BIOS is done
46f7018c21STomi Valkeinen 
47f7018c21STomi Valkeinen     (Anyone with Mac to help with this?)
48f7018c21STomi Valkeinen 
49f7018c21STomi Valkeinen ******************************************************************************/
50f7018c21STomi Valkeinen 
51f7018c21STomi Valkeinen 
52f7018c21STomi Valkeinen #include <linux/module.h>
53f7018c21STomi Valkeinen #include <linux/moduleparam.h>
54f7018c21STomi Valkeinen #include <linux/kernel.h>
55f7018c21STomi Valkeinen #include <linux/errno.h>
56f7018c21STomi Valkeinen #include <linux/string.h>
57f7018c21STomi Valkeinen #include <linux/mm.h>
58f7018c21STomi Valkeinen #include <linux/slab.h>
59f7018c21STomi Valkeinen #include <linux/vmalloc.h>
60f7018c21STomi Valkeinen #include <linux/delay.h>
61f7018c21STomi Valkeinen #include <linux/compiler.h>
62f7018c21STomi Valkeinen #include <linux/console.h>
63f7018c21STomi Valkeinen #include <linux/fb.h>
64f7018c21STomi Valkeinen #include <linux/init.h>
65f7018c21STomi Valkeinen #include <linux/pci.h>
66f7018c21STomi Valkeinen #include <linux/interrupt.h>
67f7018c21STomi Valkeinen #include <linux/spinlock.h>
68f7018c21STomi Valkeinen #include <linux/wait.h>
69f7018c21STomi Valkeinen #include <linux/backlight.h>
70f7018c21STomi Valkeinen #include <linux/reboot.h>
71f7018c21STomi Valkeinen #include <linux/dmi.h>
72f7018c21STomi Valkeinen 
73f7018c21STomi Valkeinen #include <asm/io.h>
74f7018c21STomi Valkeinen #include <linux/uaccess.h>
75f7018c21STomi Valkeinen 
76f7018c21STomi Valkeinen #include <video/mach64.h>
77f7018c21STomi Valkeinen #include "atyfb.h"
78f7018c21STomi Valkeinen #include "ati_ids.h"
79f7018c21STomi Valkeinen 
80f7018c21STomi Valkeinen #ifdef __powerpc__
81f7018c21STomi Valkeinen #include <asm/machdep.h>
82f7018c21STomi Valkeinen #include <asm/prom.h>
83f7018c21STomi Valkeinen #include "../macmodes.h"
84f7018c21STomi Valkeinen #endif
85f7018c21STomi Valkeinen #ifdef __sparc__
86f7018c21STomi Valkeinen #include <asm/fbio.h>
87f7018c21STomi Valkeinen #include <asm/oplib.h>
88f7018c21STomi Valkeinen #include <asm/prom.h>
89f7018c21STomi Valkeinen #endif
90f7018c21STomi Valkeinen 
91f7018c21STomi Valkeinen #ifdef CONFIG_ADB_PMU
92f7018c21STomi Valkeinen #include <linux/adb.h>
93f7018c21STomi Valkeinen #include <linux/pmu.h>
94f7018c21STomi Valkeinen #endif
95f7018c21STomi Valkeinen #ifdef CONFIG_BOOTX_TEXT
96f7018c21STomi Valkeinen #include <asm/btext.h>
97f7018c21STomi Valkeinen #endif
98f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
99f7018c21STomi Valkeinen #include <asm/backlight.h>
100f7018c21STomi Valkeinen #endif
101f7018c21STomi Valkeinen #ifdef CONFIG_MTRR
102f7018c21STomi Valkeinen #include <asm/mtrr.h>
103f7018c21STomi Valkeinen #endif
104f7018c21STomi Valkeinen 
105f7018c21STomi Valkeinen /*
106f7018c21STomi Valkeinen  * Debug flags.
107f7018c21STomi Valkeinen  */
108f7018c21STomi Valkeinen #undef DEBUG
109f7018c21STomi Valkeinen /*#define DEBUG*/
110f7018c21STomi Valkeinen 
111f7018c21STomi Valkeinen /* Make sure n * PAGE_SIZE is protected at end of Aperture for GUI-regs */
112f7018c21STomi Valkeinen /*  - must be large enough to catch all GUI-Regs   */
113f7018c21STomi Valkeinen /*  - must be aligned to a PAGE boundary           */
114f7018c21STomi Valkeinen #define GUI_RESERVE	(1 * PAGE_SIZE)
115f7018c21STomi Valkeinen 
116f7018c21STomi Valkeinen /* FIXME: remove the FAIL definition */
117f7018c21STomi Valkeinen #define FAIL(msg) do { \
118f7018c21STomi Valkeinen 	if (!(var->activate & FB_ACTIVATE_TEST)) \
119f7018c21STomi Valkeinen 		printk(KERN_CRIT "atyfb: " msg "\n"); \
120f7018c21STomi Valkeinen 	return -EINVAL; \
121f7018c21STomi Valkeinen } while (0)
122f7018c21STomi Valkeinen #define FAIL_MAX(msg, x, _max_) do { \
123f7018c21STomi Valkeinen 	if (x > _max_) { \
124f7018c21STomi Valkeinen 		if (!(var->activate & FB_ACTIVATE_TEST)) \
125f7018c21STomi Valkeinen 			printk(KERN_CRIT "atyfb: " msg " %x(%x)\n", x, _max_); \
126f7018c21STomi Valkeinen 		return -EINVAL; \
127f7018c21STomi Valkeinen 	} \
128f7018c21STomi Valkeinen } while (0)
129f7018c21STomi Valkeinen #ifdef DEBUG
130f7018c21STomi Valkeinen #define DPRINTK(fmt, args...)	printk(KERN_DEBUG "atyfb: " fmt, ## args)
131f7018c21STomi Valkeinen #else
132f7018c21STomi Valkeinen #define DPRINTK(fmt, args...)
133f7018c21STomi Valkeinen #endif
134f7018c21STomi Valkeinen 
135f7018c21STomi Valkeinen #define PRINTKI(fmt, args...)	printk(KERN_INFO "atyfb: " fmt, ## args)
136f7018c21STomi Valkeinen #define PRINTKE(fmt, args...)	printk(KERN_ERR "atyfb: " fmt, ## args)
137f7018c21STomi Valkeinen 
138f7018c21STomi Valkeinen #if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \
139f7018c21STomi Valkeinen defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_FB_ATY_BACKLIGHT)
140f7018c21STomi Valkeinen static const u32 lt_lcd_regs[] = {
141f7018c21STomi Valkeinen 	CNFG_PANEL_LG,
142f7018c21STomi Valkeinen 	LCD_GEN_CNTL_LG,
143f7018c21STomi Valkeinen 	DSTN_CONTROL_LG,
144f7018c21STomi Valkeinen 	HFB_PITCH_ADDR_LG,
145f7018c21STomi Valkeinen 	HORZ_STRETCHING_LG,
146f7018c21STomi Valkeinen 	VERT_STRETCHING_LG,
147f7018c21STomi Valkeinen 	0, /* EXT_VERT_STRETCH */
148f7018c21STomi Valkeinen 	LT_GIO_LG,
149f7018c21STomi Valkeinen 	POWER_MANAGEMENT_LG
150f7018c21STomi Valkeinen };
151f7018c21STomi Valkeinen 
152f7018c21STomi Valkeinen void aty_st_lcd(int index, u32 val, const struct atyfb_par *par)
153f7018c21STomi Valkeinen {
154f7018c21STomi Valkeinen 	if (M64_HAS(LT_LCD_REGS)) {
155f7018c21STomi Valkeinen 		aty_st_le32(lt_lcd_regs[index], val, par);
156f7018c21STomi Valkeinen 	} else {
157f7018c21STomi Valkeinen 		unsigned long temp;
158f7018c21STomi Valkeinen 
159f7018c21STomi Valkeinen 		/* write addr byte */
160f7018c21STomi Valkeinen 		temp = aty_ld_le32(LCD_INDEX, par);
161f7018c21STomi Valkeinen 		aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
162f7018c21STomi Valkeinen 		/* write the register value */
163f7018c21STomi Valkeinen 		aty_st_le32(LCD_DATA, val, par);
164f7018c21STomi Valkeinen 	}
165f7018c21STomi Valkeinen }
166f7018c21STomi Valkeinen 
167f7018c21STomi Valkeinen u32 aty_ld_lcd(int index, const struct atyfb_par *par)
168f7018c21STomi Valkeinen {
169f7018c21STomi Valkeinen 	if (M64_HAS(LT_LCD_REGS)) {
170f7018c21STomi Valkeinen 		return aty_ld_le32(lt_lcd_regs[index], par);
171f7018c21STomi Valkeinen 	} else {
172f7018c21STomi Valkeinen 		unsigned long temp;
173f7018c21STomi Valkeinen 
174f7018c21STomi Valkeinen 		/* write addr byte */
175f7018c21STomi Valkeinen 		temp = aty_ld_le32(LCD_INDEX, par);
176f7018c21STomi Valkeinen 		aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
177f7018c21STomi Valkeinen 		/* read the register value */
178f7018c21STomi Valkeinen 		return aty_ld_le32(LCD_DATA, par);
179f7018c21STomi Valkeinen 	}
180f7018c21STomi Valkeinen }
181f7018c21STomi Valkeinen #endif /* defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) */
182f7018c21STomi Valkeinen 
183f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
184f7018c21STomi Valkeinen /*
185f7018c21STomi Valkeinen  * ATIReduceRatio --
186f7018c21STomi Valkeinen  *
187f7018c21STomi Valkeinen  * Reduce a fraction by factoring out the largest common divider of the
188f7018c21STomi Valkeinen  * fraction's numerator and denominator.
189f7018c21STomi Valkeinen  */
190f7018c21STomi Valkeinen static void ATIReduceRatio(int *Numerator, int *Denominator)
191f7018c21STomi Valkeinen {
192f7018c21STomi Valkeinen 	int Multiplier, Divider, Remainder;
193f7018c21STomi Valkeinen 
194f7018c21STomi Valkeinen 	Multiplier = *Numerator;
195f7018c21STomi Valkeinen 	Divider = *Denominator;
196f7018c21STomi Valkeinen 
197f7018c21STomi Valkeinen 	while ((Remainder = Multiplier % Divider)) {
198f7018c21STomi Valkeinen 		Multiplier = Divider;
199f7018c21STomi Valkeinen 		Divider = Remainder;
200f7018c21STomi Valkeinen 	}
201f7018c21STomi Valkeinen 
202f7018c21STomi Valkeinen 	*Numerator /= Divider;
203f7018c21STomi Valkeinen 	*Denominator /= Divider;
204f7018c21STomi Valkeinen }
205f7018c21STomi Valkeinen #endif
206f7018c21STomi Valkeinen /*
207f7018c21STomi Valkeinen  * The Hardware parameters for each card
208f7018c21STomi Valkeinen  */
209f7018c21STomi Valkeinen 
210f7018c21STomi Valkeinen struct pci_mmap_map {
211f7018c21STomi Valkeinen 	unsigned long voff;
212f7018c21STomi Valkeinen 	unsigned long poff;
213f7018c21STomi Valkeinen 	unsigned long size;
214f7018c21STomi Valkeinen 	unsigned long prot_flag;
215f7018c21STomi Valkeinen 	unsigned long prot_mask;
216f7018c21STomi Valkeinen };
217f7018c21STomi Valkeinen 
218f7018c21STomi Valkeinen static struct fb_fix_screeninfo atyfb_fix = {
219f7018c21STomi Valkeinen 	.id		= "ATY Mach64",
220f7018c21STomi Valkeinen 	.type		= FB_TYPE_PACKED_PIXELS,
221f7018c21STomi Valkeinen 	.visual		= FB_VISUAL_PSEUDOCOLOR,
222f7018c21STomi Valkeinen 	.xpanstep	= 8,
223f7018c21STomi Valkeinen 	.ypanstep	= 1,
224f7018c21STomi Valkeinen };
225f7018c21STomi Valkeinen 
226f7018c21STomi Valkeinen /*
227f7018c21STomi Valkeinen  * Frame buffer device API
228f7018c21STomi Valkeinen  */
229f7018c21STomi Valkeinen 
230f7018c21STomi Valkeinen static int atyfb_open(struct fb_info *info, int user);
231f7018c21STomi Valkeinen static int atyfb_release(struct fb_info *info, int user);
232f7018c21STomi Valkeinen static int atyfb_check_var(struct fb_var_screeninfo *var,
233f7018c21STomi Valkeinen 			   struct fb_info *info);
234f7018c21STomi Valkeinen static int atyfb_set_par(struct fb_info *info);
235f7018c21STomi Valkeinen static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
236f7018c21STomi Valkeinen 			   u_int transp, struct fb_info *info);
237f7018c21STomi Valkeinen static int atyfb_pan_display(struct fb_var_screeninfo *var,
238f7018c21STomi Valkeinen 			     struct fb_info *info);
239f7018c21STomi Valkeinen static int atyfb_blank(int blank, struct fb_info *info);
240f7018c21STomi Valkeinen static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg);
241f7018c21STomi Valkeinen #ifdef __sparc__
242f7018c21STomi Valkeinen static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
243f7018c21STomi Valkeinen #endif
244f7018c21STomi Valkeinen static int atyfb_sync(struct fb_info *info);
245f7018c21STomi Valkeinen 
246f7018c21STomi Valkeinen /*
247f7018c21STomi Valkeinen  * Internal routines
248f7018c21STomi Valkeinen  */
249f7018c21STomi Valkeinen 
250f7018c21STomi Valkeinen static int aty_init(struct fb_info *info);
251f7018c21STomi Valkeinen 
252f7018c21STomi Valkeinen static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc);
253f7018c21STomi Valkeinen 
254f7018c21STomi Valkeinen static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc);
255f7018c21STomi Valkeinen static int aty_var_to_crtc(const struct fb_info *info,
256f7018c21STomi Valkeinen 			   const struct fb_var_screeninfo *var,
257f7018c21STomi Valkeinen 			   struct crtc *crtc);
258f7018c21STomi Valkeinen static int aty_crtc_to_var(const struct crtc *crtc,
259f7018c21STomi Valkeinen 			   struct fb_var_screeninfo *var);
260f7018c21STomi Valkeinen static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info);
261f7018c21STomi Valkeinen #ifdef CONFIG_PPC
262f7018c21STomi Valkeinen static int read_aty_sense(const struct atyfb_par *par);
263f7018c21STomi Valkeinen #endif
264f7018c21STomi Valkeinen 
265f7018c21STomi Valkeinen static DEFINE_MUTEX(reboot_lock);
266f7018c21STomi Valkeinen static struct fb_info *reboot_info;
267f7018c21STomi Valkeinen 
268f7018c21STomi Valkeinen /*
269f7018c21STomi Valkeinen  * Interface used by the world
270f7018c21STomi Valkeinen  */
271f7018c21STomi Valkeinen 
272f7018c21STomi Valkeinen static struct fb_var_screeninfo default_var = {
273f7018c21STomi Valkeinen 	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
274f7018c21STomi Valkeinen 	640, 480, 640, 480, 0, 0, 8, 0,
275f7018c21STomi Valkeinen 	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
276f7018c21STomi Valkeinen 	0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
277f7018c21STomi Valkeinen 	0, FB_VMODE_NONINTERLACED
278f7018c21STomi Valkeinen };
279f7018c21STomi Valkeinen 
280f7018c21STomi Valkeinen static struct fb_videomode defmode = {
281f7018c21STomi Valkeinen 	/* 640x480 @ 60 Hz, 31.5 kHz hsync */
282f7018c21STomi Valkeinen 	NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
283f7018c21STomi Valkeinen 	0, FB_VMODE_NONINTERLACED
284f7018c21STomi Valkeinen };
285f7018c21STomi Valkeinen 
286f7018c21STomi Valkeinen static struct fb_ops atyfb_ops = {
287f7018c21STomi Valkeinen 	.owner		= THIS_MODULE,
288f7018c21STomi Valkeinen 	.fb_open	= atyfb_open,
289f7018c21STomi Valkeinen 	.fb_release	= atyfb_release,
290f7018c21STomi Valkeinen 	.fb_check_var	= atyfb_check_var,
291f7018c21STomi Valkeinen 	.fb_set_par	= atyfb_set_par,
292f7018c21STomi Valkeinen 	.fb_setcolreg	= atyfb_setcolreg,
293f7018c21STomi Valkeinen 	.fb_pan_display	= atyfb_pan_display,
294f7018c21STomi Valkeinen 	.fb_blank	= atyfb_blank,
295f7018c21STomi Valkeinen 	.fb_ioctl	= atyfb_ioctl,
296f7018c21STomi Valkeinen 	.fb_fillrect	= atyfb_fillrect,
297f7018c21STomi Valkeinen 	.fb_copyarea	= atyfb_copyarea,
298f7018c21STomi Valkeinen 	.fb_imageblit	= atyfb_imageblit,
299f7018c21STomi Valkeinen #ifdef __sparc__
300f7018c21STomi Valkeinen 	.fb_mmap	= atyfb_mmap,
301f7018c21STomi Valkeinen #endif
302f7018c21STomi Valkeinen 	.fb_sync	= atyfb_sync,
303f7018c21STomi Valkeinen };
304f7018c21STomi Valkeinen 
305f7018c21STomi Valkeinen static bool noaccel;
306f7018c21STomi Valkeinen #ifdef CONFIG_MTRR
307f7018c21STomi Valkeinen static bool nomtrr;
308f7018c21STomi Valkeinen #endif
309f7018c21STomi Valkeinen static int vram;
310f7018c21STomi Valkeinen static int pll;
311f7018c21STomi Valkeinen static int mclk;
312f7018c21STomi Valkeinen static int xclk;
313f7018c21STomi Valkeinen static int comp_sync = -1;
314f7018c21STomi Valkeinen static char *mode;
315f7018c21STomi Valkeinen 
316f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
317f7018c21STomi Valkeinen static int backlight = 1;
318f7018c21STomi Valkeinen #else
319f7018c21STomi Valkeinen static int backlight = 0;
320f7018c21STomi Valkeinen #endif
321f7018c21STomi Valkeinen 
322f7018c21STomi Valkeinen #ifdef CONFIG_PPC
323f7018c21STomi Valkeinen static int default_vmode = VMODE_CHOOSE;
324f7018c21STomi Valkeinen static int default_cmode = CMODE_CHOOSE;
325f7018c21STomi Valkeinen 
326f7018c21STomi Valkeinen module_param_named(vmode, default_vmode, int, 0);
327f7018c21STomi Valkeinen MODULE_PARM_DESC(vmode, "int: video mode for mac");
328f7018c21STomi Valkeinen module_param_named(cmode, default_cmode, int, 0);
329f7018c21STomi Valkeinen MODULE_PARM_DESC(cmode, "int: color mode for mac");
330f7018c21STomi Valkeinen #endif
331f7018c21STomi Valkeinen 
332f7018c21STomi Valkeinen #ifdef CONFIG_ATARI
333f7018c21STomi Valkeinen static unsigned int mach64_count = 0;
334f7018c21STomi Valkeinen static unsigned long phys_vmembase[FB_MAX] = { 0, };
335f7018c21STomi Valkeinen static unsigned long phys_size[FB_MAX] = { 0, };
336f7018c21STomi Valkeinen static unsigned long phys_guiregbase[FB_MAX] = { 0, };
337f7018c21STomi Valkeinen #endif
338f7018c21STomi Valkeinen 
339f7018c21STomi Valkeinen /* top -> down is an evolution of mach64 chipset, any corrections? */
340f7018c21STomi Valkeinen #define ATI_CHIP_88800GX   (M64F_GX)
341f7018c21STomi Valkeinen #define ATI_CHIP_88800CX   (M64F_GX)
342f7018c21STomi Valkeinen 
343f7018c21STomi Valkeinen #define ATI_CHIP_264CT     (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
344f7018c21STomi Valkeinen #define ATI_CHIP_264ET     (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
345f7018c21STomi Valkeinen 
346f7018c21STomi Valkeinen #define ATI_CHIP_264VT     (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_MAGIC_FIFO)
347f7018c21STomi Valkeinen #define ATI_CHIP_264GT     (M64F_GT | M64F_INTEGRATED               | M64F_MAGIC_FIFO | M64F_EXTRA_BRIGHT)
348f7018c21STomi Valkeinen 
349f7018c21STomi Valkeinen #define ATI_CHIP_264VTB    (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP)
350f7018c21STomi Valkeinen #define ATI_CHIP_264VT3    (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL)
351f7018c21STomi Valkeinen #define ATI_CHIP_264VT4    (M64F_VT | M64F_INTEGRATED               | M64F_GTB_DSP)
352f7018c21STomi Valkeinen 
353f7018c21STomi Valkeinen /* FIXME what is this chip? */
354f7018c21STomi Valkeinen #define ATI_CHIP_264LT     (M64F_GT | M64F_INTEGRATED               | M64F_GTB_DSP)
355f7018c21STomi Valkeinen 
356f7018c21STomi Valkeinen /* make sets shorter */
357f7018c21STomi Valkeinen #define ATI_MODERN_SET     (M64F_GT | M64F_INTEGRATED               | M64F_GTB_DSP | M64F_EXTRA_BRIGHT)
358f7018c21STomi Valkeinen 
359f7018c21STomi Valkeinen #define ATI_CHIP_264GTB    (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
360f7018c21STomi Valkeinen /*#define ATI_CHIP_264GTDVD  ?*/
361f7018c21STomi Valkeinen #define ATI_CHIP_264LTG    (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
362f7018c21STomi Valkeinen 
363f7018c21STomi Valkeinen #define ATI_CHIP_264GT2C   (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE)
364f7018c21STomi Valkeinen #define ATI_CHIP_264GTPRO  (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
365f7018c21STomi Valkeinen #define ATI_CHIP_264LTPRO  (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
366f7018c21STomi Valkeinen 
367f7018c21STomi Valkeinen #define ATI_CHIP_264XL     (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM)
368f7018c21STomi Valkeinen #define ATI_CHIP_MOBILITY  (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM | M64F_MOBIL_BUS)
369f7018c21STomi Valkeinen 
370f7018c21STomi Valkeinen static struct {
371f7018c21STomi Valkeinen 	u16 pci_id;
372f7018c21STomi Valkeinen 	const char *name;
373f7018c21STomi Valkeinen 	int pll, mclk, xclk, ecp_max;
374f7018c21STomi Valkeinen 	u32 features;
375f7018c21STomi Valkeinen } aty_chips[] = {
376f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX
377f7018c21STomi Valkeinen 	/* Mach64 GX */
378f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GX, "ATI888GX00 (Mach64 GX)", 135, 50, 50, 0, ATI_CHIP_88800GX },
379f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64CX, "ATI888CX00 (Mach64 CX)", 135, 50, 50, 0, ATI_CHIP_88800CX },
380f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */
381f7018c21STomi Valkeinen 
382f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
383f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64CT, "ATI264CT (Mach64 CT)", 135, 60, 60, 0, ATI_CHIP_264CT },
384f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64ET, "ATI264ET (Mach64 ET)", 135, 60, 60, 0, ATI_CHIP_264ET },
385f7018c21STomi Valkeinen 
386f7018c21STomi Valkeinen 	/* FIXME what is this chip? */
387f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LT, "ATI264LT (Mach64 LT)", 135, 63, 63, 0, ATI_CHIP_264LT },
388f7018c21STomi Valkeinen 
389f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64VT, "ATI264VT (Mach64 VT)", 170, 67, 67, 80, ATI_CHIP_264VT },
390f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GT, "3D RAGE (Mach64 GT)", 135, 63, 63, 80, ATI_CHIP_264GT },
391f7018c21STomi Valkeinen 
392f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64VU, "ATI264VT3 (Mach64 VU)", 200, 67, 67, 80, ATI_CHIP_264VT3 },
393f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GU, "3D RAGE II+ (Mach64 GU)", 200, 67, 67, 100, ATI_CHIP_264GTB },
394f7018c21STomi Valkeinen 
395f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LG, "3D RAGE LT (Mach64 LG)", 230, 63, 63, 100, ATI_CHIP_264LTG | M64F_LT_LCD_REGS | M64F_G3_PB_1024x768 },
396f7018c21STomi Valkeinen 
397f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64VV, "ATI264VT4 (Mach64 VV)", 230, 83, 83, 100, ATI_CHIP_264VT4 },
398f7018c21STomi Valkeinen 
399f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GV, "3D RAGE IIC (Mach64 GV, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
400f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GW, "3D RAGE IIC (Mach64 GW, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
401f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GY, "3D RAGE IIC (Mach64 GY, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
402f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GZ, "3D RAGE IIC (Mach64 GZ, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
403f7018c21STomi Valkeinen 
404f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GB, "3D RAGE PRO (Mach64 GB, BGA, AGP)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
405f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GD, "3D RAGE PRO (Mach64 GD, BGA, AGP 1x)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
406f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GI, "3D RAGE PRO (Mach64 GI, BGA, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO | M64F_MAGIC_VRAM_SIZE },
407f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GP, "3D RAGE PRO (Mach64 GP, PQFP, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
408f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GQ, "3D RAGE PRO (Mach64 GQ, PQFP, PCI, limited 3D)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
409f7018c21STomi Valkeinen 
410f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LB, "3D RAGE LT PRO (Mach64 LB, AGP)", 236, 75, 100, 135, ATI_CHIP_264LTPRO },
411f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LD, "3D RAGE LT PRO (Mach64 LD, AGP)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
412f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LI, "3D RAGE LT PRO (Mach64 LI, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1_1 | M64F_G3_PB_1024x768 },
413f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LP, "3D RAGE LT PRO (Mach64 LP, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1024x768 },
414f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LQ, "3D RAGE LT PRO (Mach64 LQ, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
415f7018c21STomi Valkeinen 
416f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
417f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GN, "3D RAGE XC (Mach64 GN, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
418f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
419f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GL, "3D RAGE XC (Mach64 GL, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
420f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL | M64F_SDRAM_MAGIC_PLL },
421f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64GS, "3D RAGE XC (Mach64 GS, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL },
422f7018c21STomi Valkeinen 
423f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LM, "3D RAGE Mobility P/M (Mach64 LM, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
424f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LN, "3D RAGE Mobility L (Mach64 LN, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
425f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LR, "3D RAGE Mobility P/M (Mach64 LR, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
426f7018c21STomi Valkeinen 	{ PCI_CHIP_MACH64LS, "3D RAGE Mobility L (Mach64 LS, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
427f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */
428f7018c21STomi Valkeinen };
429f7018c21STomi Valkeinen 
430eacd2d54SLuis R. Rodriguez /*
431eacd2d54SLuis R. Rodriguez  * Last page of 8 MB (4 MB on ISA) aperture is MMIO,
432eacd2d54SLuis R. Rodriguez  * unless the auxiliary register aperture is used.
433eacd2d54SLuis R. Rodriguez  */
434eacd2d54SLuis R. Rodriguez static void aty_fudge_framebuffer_len(struct fb_info *info)
435eacd2d54SLuis R. Rodriguez {
436eacd2d54SLuis R. Rodriguez 	struct atyfb_par *par = (struct atyfb_par *) info->par;
437eacd2d54SLuis R. Rodriguez 
438eacd2d54SLuis R. Rodriguez 	if (!par->aux_start &&
439eacd2d54SLuis R. Rodriguez 	    (info->fix.smem_len == 0x800000 ||
440eacd2d54SLuis R. Rodriguez 	     (par->bus_type == ISA && info->fix.smem_len == 0x400000)))
441eacd2d54SLuis R. Rodriguez 		info->fix.smem_len -= GUI_RESERVE;
442eacd2d54SLuis R. Rodriguez }
443eacd2d54SLuis R. Rodriguez 
444f7018c21STomi Valkeinen static int correct_chipset(struct atyfb_par *par)
445f7018c21STomi Valkeinen {
446f7018c21STomi Valkeinen 	u8 rev;
447f7018c21STomi Valkeinen 	u16 type;
448f7018c21STomi Valkeinen 	u32 chip_id;
449f7018c21STomi Valkeinen 	const char *name;
450f7018c21STomi Valkeinen 	int i;
451f7018c21STomi Valkeinen 
452f7018c21STomi Valkeinen 	for (i = (int)ARRAY_SIZE(aty_chips) - 1; i >= 0; i--)
453f7018c21STomi Valkeinen 		if (par->pci_id == aty_chips[i].pci_id)
454f7018c21STomi Valkeinen 			break;
455f7018c21STomi Valkeinen 
456f7018c21STomi Valkeinen 	if (i < 0)
457f7018c21STomi Valkeinen 		return -ENODEV;
458f7018c21STomi Valkeinen 
459f7018c21STomi Valkeinen 	name = aty_chips[i].name;
460f7018c21STomi Valkeinen 	par->pll_limits.pll_max = aty_chips[i].pll;
461f7018c21STomi Valkeinen 	par->pll_limits.mclk = aty_chips[i].mclk;
462f7018c21STomi Valkeinen 	par->pll_limits.xclk = aty_chips[i].xclk;
463f7018c21STomi Valkeinen 	par->pll_limits.ecp_max = aty_chips[i].ecp_max;
464f7018c21STomi Valkeinen 	par->features = aty_chips[i].features;
465f7018c21STomi Valkeinen 
466f7018c21STomi Valkeinen 	chip_id = aty_ld_le32(CNFG_CHIP_ID, par);
467f7018c21STomi Valkeinen 	type = chip_id & CFG_CHIP_TYPE;
468f7018c21STomi Valkeinen 	rev = (chip_id & CFG_CHIP_REV) >> 24;
469f7018c21STomi Valkeinen 
470f7018c21STomi Valkeinen 	switch (par->pci_id) {
471f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX
472f7018c21STomi Valkeinen 	case PCI_CHIP_MACH64GX:
473f7018c21STomi Valkeinen 		if (type != 0x00d7)
474f7018c21STomi Valkeinen 			return -ENODEV;
475f7018c21STomi Valkeinen 		break;
476f7018c21STomi Valkeinen 	case PCI_CHIP_MACH64CX:
477f7018c21STomi Valkeinen 		if (type != 0x0057)
478f7018c21STomi Valkeinen 			return -ENODEV;
479f7018c21STomi Valkeinen 		break;
480f7018c21STomi Valkeinen #endif
481f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
482f7018c21STomi Valkeinen 	case PCI_CHIP_MACH64VT:
483f7018c21STomi Valkeinen 		switch (rev & 0x07) {
484f7018c21STomi Valkeinen 		case 0x00:
485f7018c21STomi Valkeinen 			switch (rev & 0xc0) {
486f7018c21STomi Valkeinen 			case 0x00:
487f7018c21STomi Valkeinen 				name = "ATI264VT (A3) (Mach64 VT)";
488f7018c21STomi Valkeinen 				par->pll_limits.pll_max = 170;
489f7018c21STomi Valkeinen 				par->pll_limits.mclk = 67;
490f7018c21STomi Valkeinen 				par->pll_limits.xclk = 67;
491f7018c21STomi Valkeinen 				par->pll_limits.ecp_max = 80;
492f7018c21STomi Valkeinen 				par->features = ATI_CHIP_264VT;
493f7018c21STomi Valkeinen 				break;
494f7018c21STomi Valkeinen 			case 0x40:
495f7018c21STomi Valkeinen 				name = "ATI264VT2 (A4) (Mach64 VT)";
496f7018c21STomi Valkeinen 				par->pll_limits.pll_max = 200;
497f7018c21STomi Valkeinen 				par->pll_limits.mclk = 67;
498f7018c21STomi Valkeinen 				par->pll_limits.xclk = 67;
499f7018c21STomi Valkeinen 				par->pll_limits.ecp_max = 80;
500f7018c21STomi Valkeinen 				par->features = ATI_CHIP_264VT | M64F_MAGIC_POSTDIV;
501f7018c21STomi Valkeinen 				break;
502f7018c21STomi Valkeinen 			}
503f7018c21STomi Valkeinen 			break;
504f7018c21STomi Valkeinen 		case 0x01:
505f7018c21STomi Valkeinen 			name = "ATI264VT3 (B1) (Mach64 VT)";
506f7018c21STomi Valkeinen 			par->pll_limits.pll_max = 200;
507f7018c21STomi Valkeinen 			par->pll_limits.mclk = 67;
508f7018c21STomi Valkeinen 			par->pll_limits.xclk = 67;
509f7018c21STomi Valkeinen 			par->pll_limits.ecp_max = 80;
510f7018c21STomi Valkeinen 			par->features = ATI_CHIP_264VTB;
511f7018c21STomi Valkeinen 			break;
512f7018c21STomi Valkeinen 		case 0x02:
513f7018c21STomi Valkeinen 			name = "ATI264VT3 (B2) (Mach64 VT)";
514f7018c21STomi Valkeinen 			par->pll_limits.pll_max = 200;
515f7018c21STomi Valkeinen 			par->pll_limits.mclk = 67;
516f7018c21STomi Valkeinen 			par->pll_limits.xclk = 67;
517f7018c21STomi Valkeinen 			par->pll_limits.ecp_max = 80;
518f7018c21STomi Valkeinen 			par->features = ATI_CHIP_264VT3;
519f7018c21STomi Valkeinen 			break;
520f7018c21STomi Valkeinen 		}
521f7018c21STomi Valkeinen 		break;
522f7018c21STomi Valkeinen 	case PCI_CHIP_MACH64GT:
523f7018c21STomi Valkeinen 		switch (rev & 0x07) {
524f7018c21STomi Valkeinen 		case 0x01:
525f7018c21STomi Valkeinen 			name = "3D RAGE II (Mach64 GT)";
526f7018c21STomi Valkeinen 			par->pll_limits.pll_max = 170;
527f7018c21STomi Valkeinen 			par->pll_limits.mclk = 67;
528f7018c21STomi Valkeinen 			par->pll_limits.xclk = 67;
529f7018c21STomi Valkeinen 			par->pll_limits.ecp_max = 80;
530f7018c21STomi Valkeinen 			par->features = ATI_CHIP_264GTB;
531f7018c21STomi Valkeinen 			break;
532f7018c21STomi Valkeinen 		case 0x02:
533f7018c21STomi Valkeinen 			name = "3D RAGE II+ (Mach64 GT)";
534f7018c21STomi Valkeinen 			par->pll_limits.pll_max = 200;
535f7018c21STomi Valkeinen 			par->pll_limits.mclk = 67;
536f7018c21STomi Valkeinen 			par->pll_limits.xclk = 67;
537f7018c21STomi Valkeinen 			par->pll_limits.ecp_max = 100;
538f7018c21STomi Valkeinen 			par->features = ATI_CHIP_264GTB;
539f7018c21STomi Valkeinen 			break;
540f7018c21STomi Valkeinen 		}
541f7018c21STomi Valkeinen 		break;
542f7018c21STomi Valkeinen #endif
543f7018c21STomi Valkeinen 	}
544f7018c21STomi Valkeinen 
545f7018c21STomi Valkeinen 	PRINTKI("%s [0x%04x rev 0x%02x]\n", name, type, rev);
546f7018c21STomi Valkeinen 	return 0;
547f7018c21STomi Valkeinen }
548f7018c21STomi Valkeinen 
549f7018c21STomi Valkeinen static char ram_dram[] __maybe_unused = "DRAM";
550f7018c21STomi Valkeinen static char ram_resv[] __maybe_unused = "RESV";
551f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX
552f7018c21STomi Valkeinen static char ram_vram[] = "VRAM";
553f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */
554f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
555f7018c21STomi Valkeinen static char ram_edo[] = "EDO";
556f7018c21STomi Valkeinen static char ram_sdram[] = "SDRAM (1:1)";
557f7018c21STomi Valkeinen static char ram_sgram[] = "SGRAM (1:1)";
558f7018c21STomi Valkeinen static char ram_sdram32[] = "SDRAM (2:1) (32-bit)";
559f7018c21STomi Valkeinen static char ram_wram[] = "WRAM";
560f7018c21STomi Valkeinen static char ram_off[] = "OFF";
561f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */
562f7018c21STomi Valkeinen 
563f7018c21STomi Valkeinen 
564f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX
565f7018c21STomi Valkeinen static char *aty_gx_ram[8] = {
566f7018c21STomi Valkeinen 	ram_dram, ram_vram, ram_vram, ram_dram,
567f7018c21STomi Valkeinen 	ram_dram, ram_vram, ram_vram, ram_resv
568f7018c21STomi Valkeinen };
569f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */
570f7018c21STomi Valkeinen 
571f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
572f7018c21STomi Valkeinen static char *aty_ct_ram[8] = {
573f7018c21STomi Valkeinen 	ram_off, ram_dram, ram_edo, ram_edo,
574f7018c21STomi Valkeinen 	ram_sdram, ram_sgram, ram_wram, ram_resv
575f7018c21STomi Valkeinen };
576f7018c21STomi Valkeinen static char *aty_xl_ram[8] = {
577f7018c21STomi Valkeinen 	ram_off, ram_dram, ram_edo, ram_edo,
578f7018c21STomi Valkeinen 	ram_sdram, ram_sgram, ram_sdram32, ram_resv
579f7018c21STomi Valkeinen };
580f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */
581f7018c21STomi Valkeinen 
582f7018c21STomi Valkeinen static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var,
583f7018c21STomi Valkeinen 			      struct atyfb_par *par)
584f7018c21STomi Valkeinen {
585f7018c21STomi Valkeinen 	u32 pixclock = var->pixclock;
586f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
587f7018c21STomi Valkeinen 	u32 lcd_on_off;
588f7018c21STomi Valkeinen 	par->pll.ct.xres = 0;
589f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
590f7018c21STomi Valkeinen 		lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par);
591f7018c21STomi Valkeinen 		if (lcd_on_off & LCD_ON) {
592f7018c21STomi Valkeinen 			par->pll.ct.xres = var->xres;
593f7018c21STomi Valkeinen 			pixclock = par->lcd_pixclock;
594f7018c21STomi Valkeinen 		}
595f7018c21STomi Valkeinen 	}
596f7018c21STomi Valkeinen #endif
597f7018c21STomi Valkeinen 	return pixclock;
598f7018c21STomi Valkeinen }
599f7018c21STomi Valkeinen 
600f7018c21STomi Valkeinen #if defined(CONFIG_PPC)
601f7018c21STomi Valkeinen 
602f7018c21STomi Valkeinen /*
603f7018c21STomi Valkeinen  * Apple monitor sense
604f7018c21STomi Valkeinen  */
605f7018c21STomi Valkeinen 
606f7018c21STomi Valkeinen static int read_aty_sense(const struct atyfb_par *par)
607f7018c21STomi Valkeinen {
608f7018c21STomi Valkeinen 	int sense, i;
609f7018c21STomi Valkeinen 
610f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */
611f7018c21STomi Valkeinen 	__delay(200);
612f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0, par); /* turn off outputs */
613f7018c21STomi Valkeinen 	__delay(2000);
614f7018c21STomi Valkeinen 	i = aty_ld_le32(GP_IO, par); /* get primary sense value */
615f7018c21STomi Valkeinen 	sense = ((i & 0x3000) >> 3) | (i & 0x100);
616f7018c21STomi Valkeinen 
617f7018c21STomi Valkeinen 	/* drive each sense line low in turn and collect the other 2 */
618f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0x20000000, par); /* drive A low */
619f7018c21STomi Valkeinen 	__delay(2000);
620f7018c21STomi Valkeinen 	i = aty_ld_le32(GP_IO, par);
621f7018c21STomi Valkeinen 	sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
622f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0x20002000, par); /* drive A high again */
623f7018c21STomi Valkeinen 	__delay(200);
624f7018c21STomi Valkeinen 
625f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0x10000000, par); /* drive B low */
626f7018c21STomi Valkeinen 	__delay(2000);
627f7018c21STomi Valkeinen 	i = aty_ld_le32(GP_IO, par);
628f7018c21STomi Valkeinen 	sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
629f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0x10001000, par); /* drive B high again */
630f7018c21STomi Valkeinen 	__delay(200);
631f7018c21STomi Valkeinen 
632f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0x01000000, par); /* drive C low */
633f7018c21STomi Valkeinen 	__delay(2000);
634f7018c21STomi Valkeinen 	sense |= (aty_ld_le32(GP_IO, par) & 0x3000) >> 12;
635f7018c21STomi Valkeinen 	aty_st_le32(GP_IO, 0, par); /* turn off outputs */
636f7018c21STomi Valkeinen 	return sense;
637f7018c21STomi Valkeinen }
638f7018c21STomi Valkeinen 
639f7018c21STomi Valkeinen #endif /* defined(CONFIG_PPC) */
640f7018c21STomi Valkeinen 
641f7018c21STomi Valkeinen /* ------------------------------------------------------------------------- */
642f7018c21STomi Valkeinen 
643f7018c21STomi Valkeinen /*
644f7018c21STomi Valkeinen  * CRTC programming
645f7018c21STomi Valkeinen  */
646f7018c21STomi Valkeinen 
647f7018c21STomi Valkeinen static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc)
648f7018c21STomi Valkeinen {
649f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
650f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
651f7018c21STomi Valkeinen 		if (!M64_HAS(LT_LCD_REGS)) {
652f7018c21STomi Valkeinen 			crtc->lcd_index = aty_ld_le32(LCD_INDEX, par);
653f7018c21STomi Valkeinen 			aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
654f7018c21STomi Valkeinen 		}
655f7018c21STomi Valkeinen 		crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par);
656f7018c21STomi Valkeinen 		crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par);
657f7018c21STomi Valkeinen 
658f7018c21STomi Valkeinen 
659f7018c21STomi Valkeinen 		/* switch to non shadow registers */
660f7018c21STomi Valkeinen 		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
661f7018c21STomi Valkeinen 			   ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
662f7018c21STomi Valkeinen 
663f7018c21STomi Valkeinen 		/* save stretching */
664f7018c21STomi Valkeinen 		crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
665f7018c21STomi Valkeinen 		crtc->vert_stretching = aty_ld_lcd(VERT_STRETCHING, par);
666f7018c21STomi Valkeinen 		if (!M64_HAS(LT_LCD_REGS))
667f7018c21STomi Valkeinen 			crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par);
668f7018c21STomi Valkeinen 	}
669f7018c21STomi Valkeinen #endif
670f7018c21STomi Valkeinen 	crtc->h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
671f7018c21STomi Valkeinen 	crtc->h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
672f7018c21STomi Valkeinen 	crtc->v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
673f7018c21STomi Valkeinen 	crtc->v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
674f7018c21STomi Valkeinen 	crtc->vline_crnt_vline = aty_ld_le32(CRTC_VLINE_CRNT_VLINE, par);
675f7018c21STomi Valkeinen 	crtc->off_pitch = aty_ld_le32(CRTC_OFF_PITCH, par);
676f7018c21STomi Valkeinen 	crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
677f7018c21STomi Valkeinen 
678f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
679f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
680f7018c21STomi Valkeinen 		/* switch to shadow registers */
681f7018c21STomi Valkeinen 		aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
682f7018c21STomi Valkeinen 			   SHADOW_EN | SHADOW_RW_EN, par);
683f7018c21STomi Valkeinen 
684f7018c21STomi Valkeinen 		crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
685f7018c21STomi Valkeinen 		crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
686f7018c21STomi Valkeinen 		crtc->shadow_v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
687f7018c21STomi Valkeinen 		crtc->shadow_v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
688f7018c21STomi Valkeinen 
689f7018c21STomi Valkeinen 		aty_st_le32(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
690f7018c21STomi Valkeinen 	}
691f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */
692f7018c21STomi Valkeinen }
693f7018c21STomi Valkeinen 
694f7018c21STomi Valkeinen static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc)
695f7018c21STomi Valkeinen {
696f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
697f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
698f7018c21STomi Valkeinen 		/* stop CRTC */
699f7018c21STomi Valkeinen 		aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl &
700f7018c21STomi Valkeinen 			    ~(CRTC_EXT_DISP_EN | CRTC_EN), par);
701f7018c21STomi Valkeinen 
702f7018c21STomi Valkeinen 		/* update non-shadow registers first */
703f7018c21STomi Valkeinen 		aty_st_lcd(CNFG_PANEL, crtc->lcd_config_panel, par);
704f7018c21STomi Valkeinen 		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
705f7018c21STomi Valkeinen 			   ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
706f7018c21STomi Valkeinen 
707f7018c21STomi Valkeinen 		/* temporarily disable stretching */
708f7018c21STomi Valkeinen 		aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching &
709f7018c21STomi Valkeinen 			   ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par);
710f7018c21STomi Valkeinen 		aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching &
711f7018c21STomi Valkeinen 			   ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 |
712f7018c21STomi Valkeinen 			     VERT_STRETCH_USE0 | VERT_STRETCH_EN), par);
713f7018c21STomi Valkeinen 	}
714f7018c21STomi Valkeinen #endif
715f7018c21STomi Valkeinen 	/* turn off CRT */
716f7018c21STomi Valkeinen 	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~CRTC_EN, par);
717f7018c21STomi Valkeinen 
718f7018c21STomi Valkeinen 	DPRINTK("setting up CRTC\n");
719f7018c21STomi Valkeinen 	DPRINTK("set primary CRT to %ix%i %c%c composite %c\n",
720f7018c21STomi Valkeinen 		((((crtc->h_tot_disp >> 16) & 0xff) + 1) << 3),
721f7018c21STomi Valkeinen 		(((crtc->v_tot_disp >> 16) & 0x7ff) + 1),
722f7018c21STomi Valkeinen 		(crtc->h_sync_strt_wid & 0x200000) ? 'N' : 'P',
723f7018c21STomi Valkeinen 		(crtc->v_sync_strt_wid & 0x200000) ? 'N' : 'P',
724f7018c21STomi Valkeinen 		(crtc->gen_cntl & CRTC_CSYNC_EN) ? 'P' : 'N');
725f7018c21STomi Valkeinen 
726f7018c21STomi Valkeinen 	DPRINTK("CRTC_H_TOTAL_DISP: %x\n", crtc->h_tot_disp);
727f7018c21STomi Valkeinen 	DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n", crtc->h_sync_strt_wid);
728f7018c21STomi Valkeinen 	DPRINTK("CRTC_V_TOTAL_DISP: %x\n", crtc->v_tot_disp);
729f7018c21STomi Valkeinen 	DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n", crtc->v_sync_strt_wid);
730f7018c21STomi Valkeinen 	DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch);
731f7018c21STomi Valkeinen 	DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline);
732f7018c21STomi Valkeinen 	DPRINTK("CRTC_GEN_CNTL: %x\n", crtc->gen_cntl);
733f7018c21STomi Valkeinen 
734f7018c21STomi Valkeinen 	aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par);
735f7018c21STomi Valkeinen 	aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par);
736f7018c21STomi Valkeinen 	aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_tot_disp, par);
737f7018c21STomi Valkeinen 	aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid, par);
738f7018c21STomi Valkeinen 	aty_st_le32(CRTC_OFF_PITCH, crtc->off_pitch, par);
739f7018c21STomi Valkeinen 	aty_st_le32(CRTC_VLINE_CRNT_VLINE, crtc->vline_crnt_vline, par);
740f7018c21STomi Valkeinen 
741f7018c21STomi Valkeinen 	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl, par);
742f7018c21STomi Valkeinen #if 0
743f7018c21STomi Valkeinen 	FIXME
744f7018c21STomi Valkeinen 	if (par->accel_flags & FB_ACCELF_TEXT)
745f7018c21STomi Valkeinen 		aty_init_engine(par, info);
746f7018c21STomi Valkeinen #endif
747f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
748f7018c21STomi Valkeinen 	/* after setting the CRTC registers we should set the LCD registers. */
749f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
750f7018c21STomi Valkeinen 		/* switch to shadow registers */
751f7018c21STomi Valkeinen 		aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
752f7018c21STomi Valkeinen 			   SHADOW_EN | SHADOW_RW_EN, par);
753f7018c21STomi Valkeinen 
754f7018c21STomi Valkeinen 		DPRINTK("set shadow CRT to %ix%i %c%c\n",
755f7018c21STomi Valkeinen 			((((crtc->shadow_h_tot_disp >> 16) & 0xff) + 1) << 3),
756f7018c21STomi Valkeinen 			(((crtc->shadow_v_tot_disp >> 16) & 0x7ff) + 1),
757f7018c21STomi Valkeinen 			(crtc->shadow_h_sync_strt_wid & 0x200000) ? 'N' : 'P',
758f7018c21STomi Valkeinen 			(crtc->shadow_v_sync_strt_wid & 0x200000) ? 'N' : 'P');
759f7018c21STomi Valkeinen 
760f7018c21STomi Valkeinen 		DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n",
761f7018c21STomi Valkeinen 			crtc->shadow_h_tot_disp);
762f7018c21STomi Valkeinen 		DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n",
763f7018c21STomi Valkeinen 			crtc->shadow_h_sync_strt_wid);
764f7018c21STomi Valkeinen 		DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n",
765f7018c21STomi Valkeinen 			crtc->shadow_v_tot_disp);
766f7018c21STomi Valkeinen 		DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n",
767f7018c21STomi Valkeinen 			crtc->shadow_v_sync_strt_wid);
768f7018c21STomi Valkeinen 
769f7018c21STomi Valkeinen 		aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par);
770f7018c21STomi Valkeinen 		aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par);
771f7018c21STomi Valkeinen 		aty_st_le32(CRTC_V_TOTAL_DISP, crtc->shadow_v_tot_disp, par);
772f7018c21STomi Valkeinen 		aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->shadow_v_sync_strt_wid, par);
773f7018c21STomi Valkeinen 
774f7018c21STomi Valkeinen 		/* restore CRTC selection & shadow state and enable stretching */
775f7018c21STomi Valkeinen 		DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl);
776f7018c21STomi Valkeinen 		DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching);
777f7018c21STomi Valkeinen 		DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching);
778f7018c21STomi Valkeinen 		if (!M64_HAS(LT_LCD_REGS))
779f7018c21STomi Valkeinen 			DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch);
780f7018c21STomi Valkeinen 
781f7018c21STomi Valkeinen 		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
782f7018c21STomi Valkeinen 		aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par);
783f7018c21STomi Valkeinen 		aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par);
784f7018c21STomi Valkeinen 		if (!M64_HAS(LT_LCD_REGS)) {
785f7018c21STomi Valkeinen 			aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par);
786f7018c21STomi Valkeinen 			aty_ld_le32(LCD_INDEX, par);
787f7018c21STomi Valkeinen 			aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
788f7018c21STomi Valkeinen 		}
789f7018c21STomi Valkeinen 	}
790f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */
791f7018c21STomi Valkeinen }
792f7018c21STomi Valkeinen 
793f7018c21STomi Valkeinen static u32 calc_line_length(struct atyfb_par *par, u32 vxres, u32 bpp)
794f7018c21STomi Valkeinen {
795f7018c21STomi Valkeinen 	u32 line_length = vxres * bpp / 8;
796f7018c21STomi Valkeinen 
797f7018c21STomi Valkeinen 	if (par->ram_type == SGRAM ||
798f7018c21STomi Valkeinen 	    (!M64_HAS(XL_MEM) && par->ram_type == WRAM))
799f7018c21STomi Valkeinen 		line_length = (line_length + 63) & ~63;
800f7018c21STomi Valkeinen 
801f7018c21STomi Valkeinen 	return line_length;
802f7018c21STomi Valkeinen }
803f7018c21STomi Valkeinen 
804f7018c21STomi Valkeinen static int aty_var_to_crtc(const struct fb_info *info,
805f7018c21STomi Valkeinen 			   const struct fb_var_screeninfo *var,
806f7018c21STomi Valkeinen 			   struct crtc *crtc)
807f7018c21STomi Valkeinen {
808f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
809f7018c21STomi Valkeinen 	u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
810f7018c21STomi Valkeinen 	u32 sync, vmode, vdisplay;
811f7018c21STomi Valkeinen 	u32 h_total, h_disp, h_sync_strt, h_sync_end, h_sync_dly, h_sync_wid, h_sync_pol;
812f7018c21STomi Valkeinen 	u32 v_total, v_disp, v_sync_strt, v_sync_end, v_sync_wid, v_sync_pol, c_sync;
813f7018c21STomi Valkeinen 	u32 pix_width, dp_pix_width, dp_chain_mask;
814f7018c21STomi Valkeinen 	u32 line_length;
815f7018c21STomi Valkeinen 
816f7018c21STomi Valkeinen 	/* input */
817f7018c21STomi Valkeinen 	xres = (var->xres + 7) & ~7;
818f7018c21STomi Valkeinen 	yres = var->yres;
819f7018c21STomi Valkeinen 	vxres = (var->xres_virtual + 7) & ~7;
820f7018c21STomi Valkeinen 	vyres = var->yres_virtual;
821f7018c21STomi Valkeinen 	xoffset = (var->xoffset + 7) & ~7;
822f7018c21STomi Valkeinen 	yoffset = var->yoffset;
823f7018c21STomi Valkeinen 	bpp = var->bits_per_pixel;
824f7018c21STomi Valkeinen 	if (bpp == 16)
825f7018c21STomi Valkeinen 		bpp = (var->green.length == 5) ? 15 : 16;
826f7018c21STomi Valkeinen 	sync = var->sync;
827f7018c21STomi Valkeinen 	vmode = var->vmode;
828f7018c21STomi Valkeinen 
829f7018c21STomi Valkeinen 	/* convert (and round up) and validate */
830f7018c21STomi Valkeinen 	if (vxres < xres + xoffset)
831f7018c21STomi Valkeinen 		vxres = xres + xoffset;
832f7018c21STomi Valkeinen 	h_disp = xres;
833f7018c21STomi Valkeinen 
834f7018c21STomi Valkeinen 	if (vyres < yres + yoffset)
835f7018c21STomi Valkeinen 		vyres = yres + yoffset;
836f7018c21STomi Valkeinen 	v_disp = yres;
837f7018c21STomi Valkeinen 
838f7018c21STomi Valkeinen 	if (bpp <= 8) {
839f7018c21STomi Valkeinen 		bpp = 8;
840f7018c21STomi Valkeinen 		pix_width = CRTC_PIX_WIDTH_8BPP;
841f7018c21STomi Valkeinen 		dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
842f7018c21STomi Valkeinen 			BYTE_ORDER_LSB_TO_MSB;
843f7018c21STomi Valkeinen 		dp_chain_mask = DP_CHAIN_8BPP;
844f7018c21STomi Valkeinen 	} else if (bpp <= 15) {
845f7018c21STomi Valkeinen 		bpp = 16;
846f7018c21STomi Valkeinen 		pix_width = CRTC_PIX_WIDTH_15BPP;
847f7018c21STomi Valkeinen 		dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP |
848f7018c21STomi Valkeinen 			BYTE_ORDER_LSB_TO_MSB;
849f7018c21STomi Valkeinen 		dp_chain_mask = DP_CHAIN_15BPP;
850f7018c21STomi Valkeinen 	} else if (bpp <= 16) {
851f7018c21STomi Valkeinen 		bpp = 16;
852f7018c21STomi Valkeinen 		pix_width = CRTC_PIX_WIDTH_16BPP;
853f7018c21STomi Valkeinen 		dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP |
854f7018c21STomi Valkeinen 			BYTE_ORDER_LSB_TO_MSB;
855f7018c21STomi Valkeinen 		dp_chain_mask = DP_CHAIN_16BPP;
856f7018c21STomi Valkeinen 	} else if (bpp <= 24 && M64_HAS(INTEGRATED)) {
857f7018c21STomi Valkeinen 		bpp = 24;
858f7018c21STomi Valkeinen 		pix_width = CRTC_PIX_WIDTH_24BPP;
859f7018c21STomi Valkeinen 		dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
860f7018c21STomi Valkeinen 			BYTE_ORDER_LSB_TO_MSB;
861f7018c21STomi Valkeinen 		dp_chain_mask = DP_CHAIN_24BPP;
862f7018c21STomi Valkeinen 	} else if (bpp <= 32) {
863f7018c21STomi Valkeinen 		bpp = 32;
864f7018c21STomi Valkeinen 		pix_width = CRTC_PIX_WIDTH_32BPP;
865f7018c21STomi Valkeinen 		dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP |
866f7018c21STomi Valkeinen 			BYTE_ORDER_LSB_TO_MSB;
867f7018c21STomi Valkeinen 		dp_chain_mask = DP_CHAIN_32BPP;
868f7018c21STomi Valkeinen 	} else
869f7018c21STomi Valkeinen 		FAIL("invalid bpp");
870f7018c21STomi Valkeinen 
871f7018c21STomi Valkeinen 	line_length = calc_line_length(par, vxres, bpp);
872f7018c21STomi Valkeinen 
873f7018c21STomi Valkeinen 	if (vyres * line_length > info->fix.smem_len)
874f7018c21STomi Valkeinen 		FAIL("not enough video RAM");
875f7018c21STomi Valkeinen 
876f7018c21STomi Valkeinen 	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
877f7018c21STomi Valkeinen 	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
878f7018c21STomi Valkeinen 
879f7018c21STomi Valkeinen 	if ((xres > 1920) || (yres > 1200)) {
880f7018c21STomi Valkeinen 		FAIL("MACH64 chips are designed for max 1920x1200\n"
881f7018c21STomi Valkeinen 		     "select another resolution.");
882f7018c21STomi Valkeinen 	}
883f7018c21STomi Valkeinen 	h_sync_strt = h_disp + var->right_margin;
884f7018c21STomi Valkeinen 	h_sync_end = h_sync_strt + var->hsync_len;
885f7018c21STomi Valkeinen 	h_sync_dly  = var->right_margin & 7;
886f7018c21STomi Valkeinen 	h_total = h_sync_end + h_sync_dly + var->left_margin;
887f7018c21STomi Valkeinen 
888f7018c21STomi Valkeinen 	v_sync_strt = v_disp + var->lower_margin;
889f7018c21STomi Valkeinen 	v_sync_end = v_sync_strt + var->vsync_len;
890f7018c21STomi Valkeinen 	v_total = v_sync_end + var->upper_margin;
891f7018c21STomi Valkeinen 
892f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
893f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
894f7018c21STomi Valkeinen 		if (!M64_HAS(LT_LCD_REGS)) {
895f7018c21STomi Valkeinen 			u32 lcd_index = aty_ld_le32(LCD_INDEX, par);
896f7018c21STomi Valkeinen 			crtc->lcd_index = lcd_index &
897f7018c21STomi Valkeinen 				~(LCD_INDEX_MASK | LCD_DISPLAY_DIS |
898f7018c21STomi Valkeinen 				  LCD_SRC_SEL | CRTC2_DISPLAY_DIS);
899f7018c21STomi Valkeinen 			aty_st_le32(LCD_INDEX, lcd_index, par);
900f7018c21STomi Valkeinen 		}
901f7018c21STomi Valkeinen 
902f7018c21STomi Valkeinen 		if (!M64_HAS(MOBIL_BUS))
903f7018c21STomi Valkeinen 			crtc->lcd_index |= CRTC2_DISPLAY_DIS;
904f7018c21STomi Valkeinen 
905f7018c21STomi Valkeinen 		crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par) | 0x4000;
906f7018c21STomi Valkeinen 		crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par) & ~CRTC_RW_SELECT;
907f7018c21STomi Valkeinen 
908f7018c21STomi Valkeinen 		crtc->lcd_gen_cntl &=
909f7018c21STomi Valkeinen 			~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | TVCLK_PM_EN |
910f7018c21STomi Valkeinen 			/*VCLK_DAC_PM_EN | USE_SHADOWED_VEND |*/
911f7018c21STomi Valkeinen 			USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN);
912f7018c21STomi Valkeinen 		crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT;
913f7018c21STomi Valkeinen 
914f7018c21STomi Valkeinen 		if ((crtc->lcd_gen_cntl & LCD_ON) &&
915f7018c21STomi Valkeinen 		    ((xres > par->lcd_width) || (yres > par->lcd_height))) {
916f7018c21STomi Valkeinen 			/*
917f7018c21STomi Valkeinen 			 * We cannot display the mode on the LCD. If the CRT is
918f7018c21STomi Valkeinen 			 * enabled we can turn off the LCD.
919f7018c21STomi Valkeinen 			 * If the CRT is off, it isn't a good idea to switch it
920f7018c21STomi Valkeinen 			 * on; we don't know if one is connected. So it's better
921f7018c21STomi Valkeinen 			 * to fail then.
922f7018c21STomi Valkeinen 			 */
923f7018c21STomi Valkeinen 			if (crtc->lcd_gen_cntl & CRT_ON) {
924f7018c21STomi Valkeinen 				if (!(var->activate & FB_ACTIVATE_TEST))
925f7018c21STomi Valkeinen 					PRINTKI("Disable LCD panel, because video mode does not fit.\n");
926f7018c21STomi Valkeinen 				crtc->lcd_gen_cntl &= ~LCD_ON;
927f7018c21STomi Valkeinen 				/*aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);*/
928f7018c21STomi Valkeinen 			} else {
929f7018c21STomi Valkeinen 				if (!(var->activate & FB_ACTIVATE_TEST))
930f7018c21STomi Valkeinen 					PRINTKE("Video mode exceeds size of LCD panel.\nConnect this computer to a conventional monitor if you really need this mode.\n");
931f7018c21STomi Valkeinen 				return -EINVAL;
932f7018c21STomi Valkeinen 			}
933f7018c21STomi Valkeinen 		}
934f7018c21STomi Valkeinen 	}
935f7018c21STomi Valkeinen 
936f7018c21STomi Valkeinen 	if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) {
937f7018c21STomi Valkeinen 		int VScan = 1;
938f7018c21STomi Valkeinen 		/* bpp -> bytespp, 1,4 -> 0; 8 -> 2; 15,16 -> 1; 24 -> 6; 32 -> 5
939f7018c21STomi Valkeinen 		const u8 DFP_h_sync_dly_LT[] = { 0, 2, 1, 6, 5 };
940f7018c21STomi Valkeinen 		const u8 ADD_to_strt_wid_and_dly_LT_DAC[] = { 0, 5, 6, 9, 9, 12, 12 };  */
941f7018c21STomi Valkeinen 
942f7018c21STomi Valkeinen 		vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED);
943f7018c21STomi Valkeinen 
944f7018c21STomi Valkeinen 		/*
945f7018c21STomi Valkeinen 		 * This is horror! When we simulate, say 640x480 on an 800x600
946f7018c21STomi Valkeinen 		 * LCD monitor, the CRTC should be programmed 800x600 values for
947f7018c21STomi Valkeinen 		 * the non visible part, but 640x480 for the visible part.
948f7018c21STomi Valkeinen 		 * This code has been tested on a laptop with it's 1400x1050 LCD
949f7018c21STomi Valkeinen 		 * monitor and a conventional monitor both switched on.
950f7018c21STomi Valkeinen 		 * Tested modes: 1280x1024, 1152x864, 1024x768, 800x600,
951f7018c21STomi Valkeinen 		 * works with little glitches also with DOUBLESCAN modes
952f7018c21STomi Valkeinen 		 */
953f7018c21STomi Valkeinen 		if (yres < par->lcd_height) {
954f7018c21STomi Valkeinen 			VScan = par->lcd_height / yres;
955f7018c21STomi Valkeinen 			if (VScan > 1) {
956f7018c21STomi Valkeinen 				VScan = 2;
957f7018c21STomi Valkeinen 				vmode |= FB_VMODE_DOUBLE;
958f7018c21STomi Valkeinen 			}
959f7018c21STomi Valkeinen 		}
960f7018c21STomi Valkeinen 
961f7018c21STomi Valkeinen 		h_sync_strt = h_disp + par->lcd_right_margin;
962f7018c21STomi Valkeinen 		h_sync_end = h_sync_strt + par->lcd_hsync_len;
963f7018c21STomi Valkeinen 		h_sync_dly = /*DFP_h_sync_dly[ ( bpp + 1 ) / 3 ]; */par->lcd_hsync_dly;
964f7018c21STomi Valkeinen 		h_total = h_disp + par->lcd_hblank_len;
965f7018c21STomi Valkeinen 
966f7018c21STomi Valkeinen 		v_sync_strt = v_disp + par->lcd_lower_margin / VScan;
967f7018c21STomi Valkeinen 		v_sync_end = v_sync_strt + par->lcd_vsync_len / VScan;
968f7018c21STomi Valkeinen 		v_total = v_disp + par->lcd_vblank_len / VScan;
969f7018c21STomi Valkeinen 	}
970f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */
971f7018c21STomi Valkeinen 
972f7018c21STomi Valkeinen 	h_disp = (h_disp >> 3) - 1;
973f7018c21STomi Valkeinen 	h_sync_strt = (h_sync_strt >> 3) - 1;
974f7018c21STomi Valkeinen 	h_sync_end = (h_sync_end >> 3) - 1;
975f7018c21STomi Valkeinen 	h_total = (h_total >> 3) - 1;
976f7018c21STomi Valkeinen 	h_sync_wid = h_sync_end - h_sync_strt;
977f7018c21STomi Valkeinen 
978f7018c21STomi Valkeinen 	FAIL_MAX("h_disp too large", h_disp, 0xff);
979f7018c21STomi Valkeinen 	FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff);
980f7018c21STomi Valkeinen 	/*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/
981f7018c21STomi Valkeinen 	if (h_sync_wid > 0x1f)
982f7018c21STomi Valkeinen 		h_sync_wid = 0x1f;
983f7018c21STomi Valkeinen 	FAIL_MAX("h_total too large", h_total, 0x1ff);
984f7018c21STomi Valkeinen 
985f7018c21STomi Valkeinen 	if (vmode & FB_VMODE_DOUBLE) {
986f7018c21STomi Valkeinen 		v_disp <<= 1;
987f7018c21STomi Valkeinen 		v_sync_strt <<= 1;
988f7018c21STomi Valkeinen 		v_sync_end <<= 1;
989f7018c21STomi Valkeinen 		v_total <<= 1;
990f7018c21STomi Valkeinen 	}
991f7018c21STomi Valkeinen 
992f7018c21STomi Valkeinen 	vdisplay = yres;
993f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
994f7018c21STomi Valkeinen 	if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON))
995f7018c21STomi Valkeinen 		vdisplay  = par->lcd_height;
996f7018c21STomi Valkeinen #endif
997f7018c21STomi Valkeinen 
998f7018c21STomi Valkeinen 	v_disp--;
999f7018c21STomi Valkeinen 	v_sync_strt--;
1000f7018c21STomi Valkeinen 	v_sync_end--;
1001f7018c21STomi Valkeinen 	v_total--;
1002f7018c21STomi Valkeinen 	v_sync_wid = v_sync_end - v_sync_strt;
1003f7018c21STomi Valkeinen 
1004f7018c21STomi Valkeinen 	FAIL_MAX("v_disp too large", v_disp, 0x7ff);
1005f7018c21STomi Valkeinen 	FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff);
1006f7018c21STomi Valkeinen 	/*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/
1007f7018c21STomi Valkeinen 	if (v_sync_wid > 0x1f)
1008f7018c21STomi Valkeinen 		v_sync_wid = 0x1f;
1009f7018c21STomi Valkeinen 	FAIL_MAX("v_total too large", v_total, 0x7ff);
1010f7018c21STomi Valkeinen 
1011f7018c21STomi Valkeinen 	c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0;
1012f7018c21STomi Valkeinen 
1013f7018c21STomi Valkeinen 	/* output */
1014f7018c21STomi Valkeinen 	crtc->vxres = vxres;
1015f7018c21STomi Valkeinen 	crtc->vyres = vyres;
1016f7018c21STomi Valkeinen 	crtc->xoffset = xoffset;
1017f7018c21STomi Valkeinen 	crtc->yoffset = yoffset;
1018f7018c21STomi Valkeinen 	crtc->bpp = bpp;
1019f7018c21STomi Valkeinen 	crtc->off_pitch =
1020f7018c21STomi Valkeinen 		((yoffset * line_length + xoffset * bpp / 8) / 8) |
1021f7018c21STomi Valkeinen 		((line_length / bpp) << 22);
1022f7018c21STomi Valkeinen 	crtc->vline_crnt_vline = 0;
1023f7018c21STomi Valkeinen 
1024f7018c21STomi Valkeinen 	crtc->h_tot_disp = h_total | (h_disp << 16);
1025f7018c21STomi Valkeinen 	crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly << 8) |
1026f7018c21STomi Valkeinen 		((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16) |
1027f7018c21STomi Valkeinen 		(h_sync_pol << 21);
1028f7018c21STomi Valkeinen 	crtc->v_tot_disp = v_total | (v_disp << 16);
1029f7018c21STomi Valkeinen 	crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
1030f7018c21STomi Valkeinen 		(v_sync_pol << 21);
1031f7018c21STomi Valkeinen 
1032f7018c21STomi Valkeinen 	/* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */
1033f7018c21STomi Valkeinen 	crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync;
1034f7018c21STomi Valkeinen 	crtc->gen_cntl |= CRTC_VGA_LINEAR;
1035f7018c21STomi Valkeinen 
1036f7018c21STomi Valkeinen 	/* Enable doublescan mode if requested */
1037f7018c21STomi Valkeinen 	if (vmode & FB_VMODE_DOUBLE)
1038f7018c21STomi Valkeinen 		crtc->gen_cntl |= CRTC_DBL_SCAN_EN;
1039f7018c21STomi Valkeinen 	/* Enable interlaced mode if requested */
1040f7018c21STomi Valkeinen 	if (vmode & FB_VMODE_INTERLACED)
1041f7018c21STomi Valkeinen 		crtc->gen_cntl |= CRTC_INTERLACE_EN;
1042f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
1043f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
1044f7018c21STomi Valkeinen 		vdisplay = yres;
1045f7018c21STomi Valkeinen 		if (vmode & FB_VMODE_DOUBLE)
1046f7018c21STomi Valkeinen 			vdisplay <<= 1;
1047f7018c21STomi Valkeinen 		crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH);
1048f7018c21STomi Valkeinen 		crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 |
1049f7018c21STomi Valkeinen 					/*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/
1050f7018c21STomi Valkeinen 					USE_SHADOWED_VEND |
1051f7018c21STomi Valkeinen 					USE_SHADOWED_ROWCUR |
1052f7018c21STomi Valkeinen 					SHADOW_EN | SHADOW_RW_EN);
1053f7018c21STomi Valkeinen 		crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR/* | LOCK_8DOT*/;
1054f7018c21STomi Valkeinen 
1055f7018c21STomi Valkeinen 		/* MOBILITY M1 tested, FIXME: LT */
1056f7018c21STomi Valkeinen 		crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
1057f7018c21STomi Valkeinen 		if (!M64_HAS(LT_LCD_REGS))
1058f7018c21STomi Valkeinen 			crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) &
1059f7018c21STomi Valkeinen 				~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3);
1060f7018c21STomi Valkeinen 
1061f7018c21STomi Valkeinen 		crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO |
1062f7018c21STomi Valkeinen 					   HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO |
1063f7018c21STomi Valkeinen 					   HORZ_STRETCH_MODE | HORZ_STRETCH_EN);
1064f7018c21STomi Valkeinen 		if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) {
1065f7018c21STomi Valkeinen 			do {
1066f7018c21STomi Valkeinen 				/*
1067f7018c21STomi Valkeinen 				 * The horizontal blender misbehaves when
1068f7018c21STomi Valkeinen 				 * HDisplay is less than a certain threshold
1069f7018c21STomi Valkeinen 				 * (440 for a 1024-wide panel).  It doesn't
1070f7018c21STomi Valkeinen 				 * stretch such modes enough.  Use pixel
1071f7018c21STomi Valkeinen 				 * replication instead of blending to stretch
1072f7018c21STomi Valkeinen 				 * modes that can be made to exactly fit the
1073f7018c21STomi Valkeinen 				 * panel width.  The undocumented "NoLCDBlend"
1074f7018c21STomi Valkeinen 				 * option allows the pixel-replicated mode to
1075f7018c21STomi Valkeinen 				 * be slightly wider or narrower than the
1076f7018c21STomi Valkeinen 				 * panel width.  It also causes a mode that is
1077f7018c21STomi Valkeinen 				 * exactly half as wide as the panel to be
1078f7018c21STomi Valkeinen 				 * pixel-replicated, rather than blended.
1079f7018c21STomi Valkeinen 				 */
1080f7018c21STomi Valkeinen 				int HDisplay  = xres & ~7;
1081f7018c21STomi Valkeinen 				int nStretch  = par->lcd_width / HDisplay;
1082f7018c21STomi Valkeinen 				int Remainder = par->lcd_width % HDisplay;
1083f7018c21STomi Valkeinen 
1084f7018c21STomi Valkeinen 				if ((!Remainder && ((nStretch > 2))) ||
1085f7018c21STomi Valkeinen 				    (((HDisplay * 16) / par->lcd_width) < 7)) {
1086f7018c21STomi Valkeinen 					static const char StretchLoops[] = { 10, 12, 13, 15, 16 };
1087f7018c21STomi Valkeinen 					int horz_stretch_loop = -1, BestRemainder;
1088f7018c21STomi Valkeinen 					int Numerator = HDisplay, Denominator = par->lcd_width;
1089f7018c21STomi Valkeinen 					int Index = 5;
1090f7018c21STomi Valkeinen 					ATIReduceRatio(&Numerator, &Denominator);
1091f7018c21STomi Valkeinen 
1092f7018c21STomi Valkeinen 					BestRemainder = (Numerator * 16) / Denominator;
1093f7018c21STomi Valkeinen 					while (--Index >= 0) {
1094f7018c21STomi Valkeinen 						Remainder = ((Denominator - Numerator) * StretchLoops[Index]) %
1095f7018c21STomi Valkeinen 							Denominator;
1096f7018c21STomi Valkeinen 						if (Remainder < BestRemainder) {
1097f7018c21STomi Valkeinen 							horz_stretch_loop = Index;
1098f7018c21STomi Valkeinen 							if (!(BestRemainder = Remainder))
1099f7018c21STomi Valkeinen 								break;
1100f7018c21STomi Valkeinen 						}
1101f7018c21STomi Valkeinen 					}
1102f7018c21STomi Valkeinen 
1103f7018c21STomi Valkeinen 					if ((horz_stretch_loop >= 0) && !BestRemainder) {
1104f7018c21STomi Valkeinen 						int horz_stretch_ratio = 0, Accumulator = 0;
1105f7018c21STomi Valkeinen 						int reuse_previous = 1;
1106f7018c21STomi Valkeinen 
1107f7018c21STomi Valkeinen 						Index = StretchLoops[horz_stretch_loop];
1108f7018c21STomi Valkeinen 
1109f7018c21STomi Valkeinen 						while (--Index >= 0) {
1110f7018c21STomi Valkeinen 							if (Accumulator > 0)
1111f7018c21STomi Valkeinen 								horz_stretch_ratio |= reuse_previous;
1112f7018c21STomi Valkeinen 							else
1113f7018c21STomi Valkeinen 								Accumulator += Denominator;
1114f7018c21STomi Valkeinen 							Accumulator -= Numerator;
1115f7018c21STomi Valkeinen 							reuse_previous <<= 1;
1116f7018c21STomi Valkeinen 						}
1117f7018c21STomi Valkeinen 
1118f7018c21STomi Valkeinen 						crtc->horz_stretching |= (HORZ_STRETCH_EN |
1119f7018c21STomi Valkeinen 							((horz_stretch_loop & HORZ_STRETCH_LOOP) << 16) |
1120f7018c21STomi Valkeinen 							(horz_stretch_ratio & HORZ_STRETCH_RATIO));
1121f7018c21STomi Valkeinen 						break;      /* Out of the do { ... } while (0) */
1122f7018c21STomi Valkeinen 					}
1123f7018c21STomi Valkeinen 				}
1124f7018c21STomi Valkeinen 
1125f7018c21STomi Valkeinen 				crtc->horz_stretching |= (HORZ_STRETCH_MODE | HORZ_STRETCH_EN |
1126f7018c21STomi Valkeinen 					(((HDisplay * (HORZ_STRETCH_BLEND + 1)) / par->lcd_width) & HORZ_STRETCH_BLEND));
1127f7018c21STomi Valkeinen 			} while (0);
1128f7018c21STomi Valkeinen 		}
1129f7018c21STomi Valkeinen 
1130f7018c21STomi Valkeinen 		if (vdisplay < par->lcd_height && crtc->lcd_gen_cntl & LCD_ON) {
1131f7018c21STomi Valkeinen 			crtc->vert_stretching = (VERT_STRETCH_USE0 | VERT_STRETCH_EN |
1132f7018c21STomi Valkeinen 				(((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0));
1133f7018c21STomi Valkeinen 
1134f7018c21STomi Valkeinen 			if (!M64_HAS(LT_LCD_REGS) &&
1135f7018c21STomi Valkeinen 			    xres <= (M64_HAS(MOBIL_BUS) ? 1024 : 800))
1136f7018c21STomi Valkeinen 				crtc->ext_vert_stretch |= VERT_STRETCH_MODE;
1137f7018c21STomi Valkeinen 		} else {
1138f7018c21STomi Valkeinen 			/*
1139f7018c21STomi Valkeinen 			 * Don't use vertical blending if the mode is too wide
1140f7018c21STomi Valkeinen 			 * or not vertically stretched.
1141f7018c21STomi Valkeinen 			 */
1142f7018c21STomi Valkeinen 			crtc->vert_stretching = 0;
1143f7018c21STomi Valkeinen 		}
1144f7018c21STomi Valkeinen 		/* copy to shadow crtc */
1145f7018c21STomi Valkeinen 		crtc->shadow_h_tot_disp = crtc->h_tot_disp;
1146f7018c21STomi Valkeinen 		crtc->shadow_h_sync_strt_wid = crtc->h_sync_strt_wid;
1147f7018c21STomi Valkeinen 		crtc->shadow_v_tot_disp = crtc->v_tot_disp;
1148f7018c21STomi Valkeinen 		crtc->shadow_v_sync_strt_wid = crtc->v_sync_strt_wid;
1149f7018c21STomi Valkeinen 	}
1150f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */
1151f7018c21STomi Valkeinen 
1152f7018c21STomi Valkeinen 	if (M64_HAS(MAGIC_FIFO)) {
1153f7018c21STomi Valkeinen 		/* FIXME: display FIFO low watermark values */
1154f7018c21STomi Valkeinen 		crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_FIFO_LWM);
1155f7018c21STomi Valkeinen 	}
1156f7018c21STomi Valkeinen 	crtc->dp_pix_width = dp_pix_width;
1157f7018c21STomi Valkeinen 	crtc->dp_chain_mask = dp_chain_mask;
1158f7018c21STomi Valkeinen 
1159f7018c21STomi Valkeinen 	return 0;
1160f7018c21STomi Valkeinen }
1161f7018c21STomi Valkeinen 
1162f7018c21STomi Valkeinen static int aty_crtc_to_var(const struct crtc *crtc,
1163f7018c21STomi Valkeinen 			   struct fb_var_screeninfo *var)
1164f7018c21STomi Valkeinen {
1165f7018c21STomi Valkeinen 	u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync;
1166f7018c21STomi Valkeinen 	u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
1167f7018c21STomi Valkeinen 	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
1168f7018c21STomi Valkeinen 	u32 pix_width;
1169f7018c21STomi Valkeinen 	u32 double_scan, interlace;
1170f7018c21STomi Valkeinen 
1171f7018c21STomi Valkeinen 	/* input */
1172f7018c21STomi Valkeinen 	h_total = crtc->h_tot_disp & 0x1ff;
1173f7018c21STomi Valkeinen 	h_disp = (crtc->h_tot_disp >> 16) & 0xff;
1174f7018c21STomi Valkeinen 	h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | ((crtc->h_sync_strt_wid >> 4) & 0x100);
1175f7018c21STomi Valkeinen 	h_sync_dly = (crtc->h_sync_strt_wid >> 8) & 0x7;
1176f7018c21STomi Valkeinen 	h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x1f;
1177f7018c21STomi Valkeinen 	h_sync_pol = (crtc->h_sync_strt_wid >> 21) & 0x1;
1178f7018c21STomi Valkeinen 	v_total = crtc->v_tot_disp & 0x7ff;
1179f7018c21STomi Valkeinen 	v_disp = (crtc->v_tot_disp >> 16) & 0x7ff;
1180f7018c21STomi Valkeinen 	v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
1181f7018c21STomi Valkeinen 	v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f;
1182f7018c21STomi Valkeinen 	v_sync_pol = (crtc->v_sync_strt_wid >> 21) & 0x1;
1183f7018c21STomi Valkeinen 	c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
1184f7018c21STomi Valkeinen 	pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
1185f7018c21STomi Valkeinen 	double_scan = crtc->gen_cntl & CRTC_DBL_SCAN_EN;
1186f7018c21STomi Valkeinen 	interlace = crtc->gen_cntl & CRTC_INTERLACE_EN;
1187f7018c21STomi Valkeinen 
1188f7018c21STomi Valkeinen 	/* convert */
1189f7018c21STomi Valkeinen 	xres = (h_disp + 1) * 8;
1190f7018c21STomi Valkeinen 	yres = v_disp + 1;
1191f7018c21STomi Valkeinen 	left = (h_total - h_sync_strt - h_sync_wid) * 8 - h_sync_dly;
1192f7018c21STomi Valkeinen 	right = (h_sync_strt - h_disp) * 8 + h_sync_dly;
1193f7018c21STomi Valkeinen 	hslen = h_sync_wid * 8;
1194f7018c21STomi Valkeinen 	upper = v_total - v_sync_strt - v_sync_wid;
1195f7018c21STomi Valkeinen 	lower = v_sync_strt - v_disp;
1196f7018c21STomi Valkeinen 	vslen = v_sync_wid;
1197f7018c21STomi Valkeinen 	sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
1198f7018c21STomi Valkeinen 		(v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
1199f7018c21STomi Valkeinen 		(c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
1200f7018c21STomi Valkeinen 
1201f7018c21STomi Valkeinen 	switch (pix_width) {
1202f7018c21STomi Valkeinen #if 0
1203f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_4BPP:
1204f7018c21STomi Valkeinen 		bpp = 4;
1205f7018c21STomi Valkeinen 		var->red.offset = 0;
1206f7018c21STomi Valkeinen 		var->red.length = 8;
1207f7018c21STomi Valkeinen 		var->green.offset = 0;
1208f7018c21STomi Valkeinen 		var->green.length = 8;
1209f7018c21STomi Valkeinen 		var->blue.offset = 0;
1210f7018c21STomi Valkeinen 		var->blue.length = 8;
1211f7018c21STomi Valkeinen 		var->transp.offset = 0;
1212f7018c21STomi Valkeinen 		var->transp.length = 0;
1213f7018c21STomi Valkeinen 		break;
1214f7018c21STomi Valkeinen #endif
1215f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_8BPP:
1216f7018c21STomi Valkeinen 		bpp = 8;
1217f7018c21STomi Valkeinen 		var->red.offset = 0;
1218f7018c21STomi Valkeinen 		var->red.length = 8;
1219f7018c21STomi Valkeinen 		var->green.offset = 0;
1220f7018c21STomi Valkeinen 		var->green.length = 8;
1221f7018c21STomi Valkeinen 		var->blue.offset = 0;
1222f7018c21STomi Valkeinen 		var->blue.length = 8;
1223f7018c21STomi Valkeinen 		var->transp.offset = 0;
1224f7018c21STomi Valkeinen 		var->transp.length = 0;
1225f7018c21STomi Valkeinen 		break;
1226f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_15BPP:	/* RGB 555 */
1227f7018c21STomi Valkeinen 		bpp = 16;
1228f7018c21STomi Valkeinen 		var->red.offset = 10;
1229f7018c21STomi Valkeinen 		var->red.length = 5;
1230f7018c21STomi Valkeinen 		var->green.offset = 5;
1231f7018c21STomi Valkeinen 		var->green.length = 5;
1232f7018c21STomi Valkeinen 		var->blue.offset = 0;
1233f7018c21STomi Valkeinen 		var->blue.length = 5;
1234f7018c21STomi Valkeinen 		var->transp.offset = 0;
1235f7018c21STomi Valkeinen 		var->transp.length = 0;
1236f7018c21STomi Valkeinen 		break;
1237f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_16BPP:	/* RGB 565 */
1238f7018c21STomi Valkeinen 		bpp = 16;
1239f7018c21STomi Valkeinen 		var->red.offset = 11;
1240f7018c21STomi Valkeinen 		var->red.length = 5;
1241f7018c21STomi Valkeinen 		var->green.offset = 5;
1242f7018c21STomi Valkeinen 		var->green.length = 6;
1243f7018c21STomi Valkeinen 		var->blue.offset = 0;
1244f7018c21STomi Valkeinen 		var->blue.length = 5;
1245f7018c21STomi Valkeinen 		var->transp.offset = 0;
1246f7018c21STomi Valkeinen 		var->transp.length = 0;
1247f7018c21STomi Valkeinen 		break;
1248f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_24BPP:	/* RGB 888 */
1249f7018c21STomi Valkeinen 		bpp = 24;
1250f7018c21STomi Valkeinen 		var->red.offset = 16;
1251f7018c21STomi Valkeinen 		var->red.length = 8;
1252f7018c21STomi Valkeinen 		var->green.offset = 8;
1253f7018c21STomi Valkeinen 		var->green.length = 8;
1254f7018c21STomi Valkeinen 		var->blue.offset = 0;
1255f7018c21STomi Valkeinen 		var->blue.length = 8;
1256f7018c21STomi Valkeinen 		var->transp.offset = 0;
1257f7018c21STomi Valkeinen 		var->transp.length = 0;
1258f7018c21STomi Valkeinen 		break;
1259f7018c21STomi Valkeinen 	case CRTC_PIX_WIDTH_32BPP:	/* ARGB 8888 */
1260f7018c21STomi Valkeinen 		bpp = 32;
1261f7018c21STomi Valkeinen 		var->red.offset = 16;
1262f7018c21STomi Valkeinen 		var->red.length = 8;
1263f7018c21STomi Valkeinen 		var->green.offset = 8;
1264f7018c21STomi Valkeinen 		var->green.length = 8;
1265f7018c21STomi Valkeinen 		var->blue.offset = 0;
1266f7018c21STomi Valkeinen 		var->blue.length = 8;
1267f7018c21STomi Valkeinen 		var->transp.offset = 24;
1268f7018c21STomi Valkeinen 		var->transp.length = 8;
1269f7018c21STomi Valkeinen 		break;
1270f7018c21STomi Valkeinen 	default:
1271f7018c21STomi Valkeinen 		PRINTKE("Invalid pixel width\n");
1272f7018c21STomi Valkeinen 		return -EINVAL;
1273f7018c21STomi Valkeinen 	}
1274f7018c21STomi Valkeinen 
1275f7018c21STomi Valkeinen 	/* output */
1276f7018c21STomi Valkeinen 	var->xres = xres;
1277f7018c21STomi Valkeinen 	var->yres = yres;
1278f7018c21STomi Valkeinen 	var->xres_virtual = crtc->vxres;
1279f7018c21STomi Valkeinen 	var->yres_virtual = crtc->vyres;
1280f7018c21STomi Valkeinen 	var->bits_per_pixel = bpp;
1281f7018c21STomi Valkeinen 	var->left_margin = left;
1282f7018c21STomi Valkeinen 	var->right_margin = right;
1283f7018c21STomi Valkeinen 	var->upper_margin = upper;
1284f7018c21STomi Valkeinen 	var->lower_margin = lower;
1285f7018c21STomi Valkeinen 	var->hsync_len = hslen;
1286f7018c21STomi Valkeinen 	var->vsync_len = vslen;
1287f7018c21STomi Valkeinen 	var->sync = sync;
1288f7018c21STomi Valkeinen 	var->vmode = FB_VMODE_NONINTERLACED;
1289f7018c21STomi Valkeinen 	/*
1290f7018c21STomi Valkeinen 	 * In double scan mode, the vertical parameters are doubled,
1291f7018c21STomi Valkeinen 	 * so we need to halve them to get the right values.
1292f7018c21STomi Valkeinen 	 * In interlaced mode the values are already correct,
1293f7018c21STomi Valkeinen 	 * so no correction is necessary.
1294f7018c21STomi Valkeinen 	 */
1295f7018c21STomi Valkeinen 	if (interlace)
1296f7018c21STomi Valkeinen 		var->vmode = FB_VMODE_INTERLACED;
1297f7018c21STomi Valkeinen 
1298f7018c21STomi Valkeinen 	if (double_scan) {
1299f7018c21STomi Valkeinen 		var->vmode = FB_VMODE_DOUBLE;
1300f7018c21STomi Valkeinen 		var->yres >>= 1;
1301f7018c21STomi Valkeinen 		var->upper_margin >>= 1;
1302f7018c21STomi Valkeinen 		var->lower_margin >>= 1;
1303f7018c21STomi Valkeinen 		var->vsync_len >>= 1;
1304f7018c21STomi Valkeinen 	}
1305f7018c21STomi Valkeinen 
1306f7018c21STomi Valkeinen 	return 0;
1307f7018c21STomi Valkeinen }
1308f7018c21STomi Valkeinen 
1309f7018c21STomi Valkeinen /* ------------------------------------------------------------------------- */
1310f7018c21STomi Valkeinen 
1311f7018c21STomi Valkeinen static int atyfb_set_par(struct fb_info *info)
1312f7018c21STomi Valkeinen {
1313f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1314f7018c21STomi Valkeinen 	struct fb_var_screeninfo *var = &info->var;
1315f7018c21STomi Valkeinen 	u32 tmp, pixclock;
1316f7018c21STomi Valkeinen 	int err;
1317f7018c21STomi Valkeinen #ifdef DEBUG
1318f7018c21STomi Valkeinen 	struct fb_var_screeninfo debug;
1319f7018c21STomi Valkeinen 	u32 pixclock_in_ps;
1320f7018c21STomi Valkeinen #endif
1321f7018c21STomi Valkeinen 	if (par->asleep)
1322f7018c21STomi Valkeinen 		return 0;
1323f7018c21STomi Valkeinen 
1324f7018c21STomi Valkeinen 	err = aty_var_to_crtc(info, var, &par->crtc);
1325f7018c21STomi Valkeinen 	if (err)
1326f7018c21STomi Valkeinen 		return err;
1327f7018c21STomi Valkeinen 
1328f7018c21STomi Valkeinen 	pixclock = atyfb_get_pixclock(var, par);
1329f7018c21STomi Valkeinen 
1330f7018c21STomi Valkeinen 	if (pixclock == 0) {
1331f7018c21STomi Valkeinen 		PRINTKE("Invalid pixclock\n");
1332f7018c21STomi Valkeinen 		return -EINVAL;
1333f7018c21STomi Valkeinen 	} else {
1334f7018c21STomi Valkeinen 		err = par->pll_ops->var_to_pll(info, pixclock,
1335f7018c21STomi Valkeinen 					       var->bits_per_pixel, &par->pll);
1336f7018c21STomi Valkeinen 		if (err)
1337f7018c21STomi Valkeinen 			return err;
1338f7018c21STomi Valkeinen 	}
1339f7018c21STomi Valkeinen 
1340f7018c21STomi Valkeinen 	par->accel_flags = var->accel_flags; /* hack */
1341f7018c21STomi Valkeinen 
1342f7018c21STomi Valkeinen 	if (var->accel_flags) {
1343f7018c21STomi Valkeinen 		info->fbops->fb_sync = atyfb_sync;
1344f7018c21STomi Valkeinen 		info->flags &= ~FBINFO_HWACCEL_DISABLED;
1345f7018c21STomi Valkeinen 	} else {
1346f7018c21STomi Valkeinen 		info->fbops->fb_sync = NULL;
1347f7018c21STomi Valkeinen 		info->flags |= FBINFO_HWACCEL_DISABLED;
1348f7018c21STomi Valkeinen 	}
1349f7018c21STomi Valkeinen 
1350f7018c21STomi Valkeinen 	if (par->blitter_may_be_busy)
1351f7018c21STomi Valkeinen 		wait_for_idle(par);
1352f7018c21STomi Valkeinen 
1353f7018c21STomi Valkeinen 	aty_set_crtc(par, &par->crtc);
1354f7018c21STomi Valkeinen 	par->dac_ops->set_dac(info, &par->pll,
1355f7018c21STomi Valkeinen 			      var->bits_per_pixel, par->accel_flags);
1356f7018c21STomi Valkeinen 	par->pll_ops->set_pll(info, &par->pll);
1357f7018c21STomi Valkeinen 
1358f7018c21STomi Valkeinen #ifdef DEBUG
1359f7018c21STomi Valkeinen 	if (par->pll_ops && par->pll_ops->pll_to_var)
1360f7018c21STomi Valkeinen 		pixclock_in_ps = par->pll_ops->pll_to_var(info, &par->pll);
1361f7018c21STomi Valkeinen 	else
1362f7018c21STomi Valkeinen 		pixclock_in_ps = 0;
1363f7018c21STomi Valkeinen 
1364f7018c21STomi Valkeinen 	if (0 == pixclock_in_ps) {
1365f7018c21STomi Valkeinen 		PRINTKE("ALERT ops->pll_to_var get 0\n");
1366f7018c21STomi Valkeinen 		pixclock_in_ps = pixclock;
1367f7018c21STomi Valkeinen 	}
1368f7018c21STomi Valkeinen 
1369f7018c21STomi Valkeinen 	memset(&debug, 0, sizeof(debug));
1370f7018c21STomi Valkeinen 	if (!aty_crtc_to_var(&par->crtc, &debug)) {
1371f7018c21STomi Valkeinen 		u32 hSync, vRefresh;
1372f7018c21STomi Valkeinen 		u32 h_disp, h_sync_strt, h_sync_end, h_total;
1373f7018c21STomi Valkeinen 		u32 v_disp, v_sync_strt, v_sync_end, v_total;
1374f7018c21STomi Valkeinen 
1375f7018c21STomi Valkeinen 		h_disp = debug.xres;
1376f7018c21STomi Valkeinen 		h_sync_strt = h_disp + debug.right_margin;
1377f7018c21STomi Valkeinen 		h_sync_end = h_sync_strt + debug.hsync_len;
1378f7018c21STomi Valkeinen 		h_total = h_sync_end + debug.left_margin;
1379f7018c21STomi Valkeinen 		v_disp = debug.yres;
1380f7018c21STomi Valkeinen 		v_sync_strt = v_disp + debug.lower_margin;
1381f7018c21STomi Valkeinen 		v_sync_end = v_sync_strt + debug.vsync_len;
1382f7018c21STomi Valkeinen 		v_total = v_sync_end + debug.upper_margin;
1383f7018c21STomi Valkeinen 
1384f7018c21STomi Valkeinen 		hSync = 1000000000 / (pixclock_in_ps * h_total);
1385f7018c21STomi Valkeinen 		vRefresh = (hSync * 1000) / v_total;
1386f7018c21STomi Valkeinen 		if (par->crtc.gen_cntl & CRTC_INTERLACE_EN)
1387f7018c21STomi Valkeinen 			vRefresh *= 2;
1388f7018c21STomi Valkeinen 		if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
1389f7018c21STomi Valkeinen 			vRefresh /= 2;
1390f7018c21STomi Valkeinen 
1391f7018c21STomi Valkeinen 		DPRINTK("atyfb_set_par\n");
1392f7018c21STomi Valkeinen 		DPRINTK(" Set Visible Mode to %ix%i-%i\n",
1393f7018c21STomi Valkeinen 			var->xres, var->yres, var->bits_per_pixel);
1394f7018c21STomi Valkeinen 		DPRINTK(" Virtual resolution %ix%i, "
1395f7018c21STomi Valkeinen 			"pixclock_in_ps %i (calculated %i)\n",
1396f7018c21STomi Valkeinen 			var->xres_virtual, var->yres_virtual,
1397f7018c21STomi Valkeinen 			pixclock, pixclock_in_ps);
1398f7018c21STomi Valkeinen 		DPRINTK(" Dot clock:           %i MHz\n",
1399f7018c21STomi Valkeinen 			1000000 / pixclock_in_ps);
1400f7018c21STomi Valkeinen 		DPRINTK(" Horizontal sync:     %i kHz\n", hSync);
1401f7018c21STomi Valkeinen 		DPRINTK(" Vertical refresh:    %i Hz\n", vRefresh);
1402f7018c21STomi Valkeinen 		DPRINTK(" x  style: %i.%03i %i %i %i %i   %i %i %i %i\n",
1403f7018c21STomi Valkeinen 			1000000 / pixclock_in_ps, 1000000 % pixclock_in_ps,
1404f7018c21STomi Valkeinen 			h_disp, h_sync_strt, h_sync_end, h_total,
1405f7018c21STomi Valkeinen 			v_disp, v_sync_strt, v_sync_end, v_total);
1406f7018c21STomi Valkeinen 		DPRINTK(" fb style: %i  %i %i %i %i %i %i %i %i\n",
1407f7018c21STomi Valkeinen 			pixclock_in_ps,
1408f7018c21STomi Valkeinen 			debug.left_margin, h_disp, debug.right_margin, debug.hsync_len,
1409f7018c21STomi Valkeinen 			debug.upper_margin, v_disp, debug.lower_margin, debug.vsync_len);
1410f7018c21STomi Valkeinen 	}
1411f7018c21STomi Valkeinen #endif /* DEBUG */
1412f7018c21STomi Valkeinen 
1413f7018c21STomi Valkeinen 	if (!M64_HAS(INTEGRATED)) {
1414f7018c21STomi Valkeinen 		/* Don't forget MEM_CNTL */
1415f7018c21STomi Valkeinen 		tmp = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff;
1416f7018c21STomi Valkeinen 		switch (var->bits_per_pixel) {
1417f7018c21STomi Valkeinen 		case 8:
1418f7018c21STomi Valkeinen 			tmp |= 0x02000000;
1419f7018c21STomi Valkeinen 			break;
1420f7018c21STomi Valkeinen 		case 16:
1421f7018c21STomi Valkeinen 			tmp |= 0x03000000;
1422f7018c21STomi Valkeinen 			break;
1423f7018c21STomi Valkeinen 		case 32:
1424f7018c21STomi Valkeinen 			tmp |= 0x06000000;
1425f7018c21STomi Valkeinen 			break;
1426f7018c21STomi Valkeinen 		}
1427f7018c21STomi Valkeinen 		aty_st_le32(MEM_CNTL, tmp, par);
1428f7018c21STomi Valkeinen 	} else {
1429f7018c21STomi Valkeinen 		tmp = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff;
1430f7018c21STomi Valkeinen 		if (!M64_HAS(MAGIC_POSTDIV))
1431f7018c21STomi Valkeinen 			tmp |= par->mem_refresh_rate << 20;
1432f7018c21STomi Valkeinen 		switch (var->bits_per_pixel) {
1433f7018c21STomi Valkeinen 		case 8:
1434f7018c21STomi Valkeinen 		case 24:
1435f7018c21STomi Valkeinen 			tmp |= 0x00000000;
1436f7018c21STomi Valkeinen 			break;
1437f7018c21STomi Valkeinen 		case 16:
1438f7018c21STomi Valkeinen 			tmp |= 0x04000000;
1439f7018c21STomi Valkeinen 			break;
1440f7018c21STomi Valkeinen 		case 32:
1441f7018c21STomi Valkeinen 			tmp |= 0x08000000;
1442f7018c21STomi Valkeinen 			break;
1443f7018c21STomi Valkeinen 		}
1444f7018c21STomi Valkeinen 		if (M64_HAS(CT_BUS)) {
1445f7018c21STomi Valkeinen 			aty_st_le32(DAC_CNTL, 0x87010184, par);
1446f7018c21STomi Valkeinen 			aty_st_le32(BUS_CNTL, 0x680000f9, par);
1447f7018c21STomi Valkeinen 		} else if (M64_HAS(VT_BUS)) {
1448f7018c21STomi Valkeinen 			aty_st_le32(DAC_CNTL, 0x87010184, par);
1449f7018c21STomi Valkeinen 			aty_st_le32(BUS_CNTL, 0x680000f9, par);
1450f7018c21STomi Valkeinen 		} else if (M64_HAS(MOBIL_BUS)) {
1451f7018c21STomi Valkeinen 			aty_st_le32(DAC_CNTL, 0x80010102, par);
1452f7018c21STomi Valkeinen 			aty_st_le32(BUS_CNTL, 0x7b33a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
1453f7018c21STomi Valkeinen 		} else {
1454f7018c21STomi Valkeinen 			/* GT */
1455f7018c21STomi Valkeinen 			aty_st_le32(DAC_CNTL, 0x86010102, par);
1456f7018c21STomi Valkeinen 			aty_st_le32(BUS_CNTL, 0x7b23a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
1457f7018c21STomi Valkeinen 			aty_st_le32(EXT_MEM_CNTL, aty_ld_le32(EXT_MEM_CNTL, par) | 0x5000001, par);
1458f7018c21STomi Valkeinen 		}
1459f7018c21STomi Valkeinen 		aty_st_le32(MEM_CNTL, tmp, par);
1460f7018c21STomi Valkeinen 	}
1461f7018c21STomi Valkeinen 	aty_st_8(DAC_MASK, 0xff, par);
1462f7018c21STomi Valkeinen 
1463f7018c21STomi Valkeinen 	info->fix.line_length = calc_line_length(par, var->xres_virtual,
1464f7018c21STomi Valkeinen 						 var->bits_per_pixel);
1465f7018c21STomi Valkeinen 
1466f7018c21STomi Valkeinen 	info->fix.visual = var->bits_per_pixel <= 8 ?
1467f7018c21STomi Valkeinen 		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
1468f7018c21STomi Valkeinen 
1469f7018c21STomi Valkeinen 	/* Initialize the graphics engine */
1470f7018c21STomi Valkeinen 	if (par->accel_flags & FB_ACCELF_TEXT)
1471f7018c21STomi Valkeinen 		aty_init_engine(par, info);
1472f7018c21STomi Valkeinen 
1473f7018c21STomi Valkeinen #ifdef CONFIG_BOOTX_TEXT
1474f7018c21STomi Valkeinen 	btext_update_display(info->fix.smem_start,
1475f7018c21STomi Valkeinen 		(((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8,
1476f7018c21STomi Valkeinen 		((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1,
1477f7018c21STomi Valkeinen 		var->bits_per_pixel,
1478f7018c21STomi Valkeinen 		par->crtc.vxres * var->bits_per_pixel / 8);
1479f7018c21STomi Valkeinen #endif /* CONFIG_BOOTX_TEXT */
1480f7018c21STomi Valkeinen #if 0
1481f7018c21STomi Valkeinen 	/* switch to accelerator mode */
1482f7018c21STomi Valkeinen 	if (!(par->crtc.gen_cntl & CRTC_EXT_DISP_EN))
1483f7018c21STomi Valkeinen 		aty_st_le32(CRTC_GEN_CNTL, par->crtc.gen_cntl | CRTC_EXT_DISP_EN, par);
1484f7018c21STomi Valkeinen #endif
1485f7018c21STomi Valkeinen #ifdef DEBUG
1486f7018c21STomi Valkeinen {
1487f7018c21STomi Valkeinen 	/* dump non shadow CRTC, pll, LCD registers */
1488f7018c21STomi Valkeinen 	int i; u32 base;
1489f7018c21STomi Valkeinen 
1490f7018c21STomi Valkeinen 	/* CRTC registers */
1491f7018c21STomi Valkeinen 	base = 0x2000;
1492f7018c21STomi Valkeinen 	printk("debug atyfb: Mach64 non-shadow register values:");
1493f7018c21STomi Valkeinen 	for (i = 0; i < 256; i = i+4) {
1494f7018c21STomi Valkeinen 		if (i % 16 == 0)
1495f7018c21STomi Valkeinen 			printk("\ndebug atyfb: 0x%04X: ", base + i);
1496f7018c21STomi Valkeinen 		printk(" %08X", aty_ld_le32(i, par));
1497f7018c21STomi Valkeinen 	}
1498f7018c21STomi Valkeinen 	printk("\n\n");
1499f7018c21STomi Valkeinen 
1500f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
1501f7018c21STomi Valkeinen 	/* PLL registers */
1502f7018c21STomi Valkeinen 	base = 0x00;
1503f7018c21STomi Valkeinen 	printk("debug atyfb: Mach64 PLL register values:");
1504f7018c21STomi Valkeinen 	for (i = 0; i < 64; i++) {
1505f7018c21STomi Valkeinen 		if (i % 16 == 0)
1506f7018c21STomi Valkeinen 			printk("\ndebug atyfb: 0x%02X: ", base + i);
1507f7018c21STomi Valkeinen 		if (i % 4 == 0)
1508f7018c21STomi Valkeinen 			printk(" ");
1509f7018c21STomi Valkeinen 		printk("%02X", aty_ld_pll_ct(i, par));
1510f7018c21STomi Valkeinen 	}
1511f7018c21STomi Valkeinen 	printk("\n\n");
1512f7018c21STomi Valkeinen #endif	/* CONFIG_FB_ATY_CT */
1513f7018c21STomi Valkeinen 
1514f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
1515f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
1516f7018c21STomi Valkeinen 		/* LCD registers */
1517f7018c21STomi Valkeinen 		base = 0x00;
1518f7018c21STomi Valkeinen 		printk("debug atyfb: LCD register values:");
1519f7018c21STomi Valkeinen 		if (M64_HAS(LT_LCD_REGS)) {
1520f7018c21STomi Valkeinen 			for (i = 0; i <= POWER_MANAGEMENT; i++) {
1521f7018c21STomi Valkeinen 				if (i == EXT_VERT_STRETCH)
1522f7018c21STomi Valkeinen 					continue;
1523f7018c21STomi Valkeinen 				printk("\ndebug atyfb: 0x%04X: ",
1524f7018c21STomi Valkeinen 				       lt_lcd_regs[i]);
1525f7018c21STomi Valkeinen 				printk(" %08X", aty_ld_lcd(i, par));
1526f7018c21STomi Valkeinen 			}
1527f7018c21STomi Valkeinen 		} else {
1528f7018c21STomi Valkeinen 			for (i = 0; i < 64; i++) {
1529f7018c21STomi Valkeinen 				if (i % 4 == 0)
1530f7018c21STomi Valkeinen 					printk("\ndebug atyfb: 0x%02X: ",
1531f7018c21STomi Valkeinen 					       base + i);
1532f7018c21STomi Valkeinen 				printk(" %08X", aty_ld_lcd(i, par));
1533f7018c21STomi Valkeinen 			}
1534f7018c21STomi Valkeinen 		}
1535f7018c21STomi Valkeinen 		printk("\n\n");
1536f7018c21STomi Valkeinen 	}
1537f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */
1538f7018c21STomi Valkeinen }
1539f7018c21STomi Valkeinen #endif /* DEBUG */
1540f7018c21STomi Valkeinen 	return 0;
1541f7018c21STomi Valkeinen }
1542f7018c21STomi Valkeinen 
1543f7018c21STomi Valkeinen static int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
1544f7018c21STomi Valkeinen {
1545f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1546f7018c21STomi Valkeinen 	int err;
1547f7018c21STomi Valkeinen 	struct crtc crtc;
1548f7018c21STomi Valkeinen 	union aty_pll pll;
1549f7018c21STomi Valkeinen 	u32 pixclock;
1550f7018c21STomi Valkeinen 
1551f7018c21STomi Valkeinen 	memcpy(&pll, &par->pll, sizeof(pll));
1552f7018c21STomi Valkeinen 
1553f7018c21STomi Valkeinen 	err = aty_var_to_crtc(info, var, &crtc);
1554f7018c21STomi Valkeinen 	if (err)
1555f7018c21STomi Valkeinen 		return err;
1556f7018c21STomi Valkeinen 
1557f7018c21STomi Valkeinen 	pixclock = atyfb_get_pixclock(var, par);
1558f7018c21STomi Valkeinen 
1559f7018c21STomi Valkeinen 	if (pixclock == 0) {
1560f7018c21STomi Valkeinen 		if (!(var->activate & FB_ACTIVATE_TEST))
1561f7018c21STomi Valkeinen 			PRINTKE("Invalid pixclock\n");
1562f7018c21STomi Valkeinen 		return -EINVAL;
1563f7018c21STomi Valkeinen 	} else {
1564f7018c21STomi Valkeinen 		err = par->pll_ops->var_to_pll(info, pixclock,
1565f7018c21STomi Valkeinen 					       var->bits_per_pixel, &pll);
1566f7018c21STomi Valkeinen 		if (err)
1567f7018c21STomi Valkeinen 			return err;
1568f7018c21STomi Valkeinen 	}
1569f7018c21STomi Valkeinen 
1570f7018c21STomi Valkeinen 	if (var->accel_flags & FB_ACCELF_TEXT)
1571f7018c21STomi Valkeinen 		info->var.accel_flags = FB_ACCELF_TEXT;
1572f7018c21STomi Valkeinen 	else
1573f7018c21STomi Valkeinen 		info->var.accel_flags = 0;
1574f7018c21STomi Valkeinen 
1575f7018c21STomi Valkeinen 	aty_crtc_to_var(&crtc, var);
1576f7018c21STomi Valkeinen 	var->pixclock = par->pll_ops->pll_to_var(info, &pll);
1577f7018c21STomi Valkeinen 	return 0;
1578f7018c21STomi Valkeinen }
1579f7018c21STomi Valkeinen 
1580f7018c21STomi Valkeinen static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info)
1581f7018c21STomi Valkeinen {
1582f7018c21STomi Valkeinen 	u32 xoffset = info->var.xoffset;
1583f7018c21STomi Valkeinen 	u32 yoffset = info->var.yoffset;
1584f7018c21STomi Valkeinen 	u32 line_length = info->fix.line_length;
1585f7018c21STomi Valkeinen 	u32 bpp = info->var.bits_per_pixel;
1586f7018c21STomi Valkeinen 
1587f7018c21STomi Valkeinen 	par->crtc.off_pitch =
1588f7018c21STomi Valkeinen 		((yoffset * line_length + xoffset * bpp / 8) / 8) |
1589f7018c21STomi Valkeinen 		((line_length / bpp) << 22);
1590f7018c21STomi Valkeinen }
1591f7018c21STomi Valkeinen 
1592f7018c21STomi Valkeinen 
1593f7018c21STomi Valkeinen /*
1594f7018c21STomi Valkeinen  * Open/Release the frame buffer device
1595f7018c21STomi Valkeinen  */
1596f7018c21STomi Valkeinen 
1597f7018c21STomi Valkeinen static int atyfb_open(struct fb_info *info, int user)
1598f7018c21STomi Valkeinen {
1599f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1600f7018c21STomi Valkeinen 
1601f7018c21STomi Valkeinen 	if (user) {
1602f7018c21STomi Valkeinen 		par->open++;
1603f7018c21STomi Valkeinen #ifdef __sparc__
1604f7018c21STomi Valkeinen 		par->mmaped = 0;
1605f7018c21STomi Valkeinen #endif
1606f7018c21STomi Valkeinen 	}
1607f7018c21STomi Valkeinen 	return 0;
1608f7018c21STomi Valkeinen }
1609f7018c21STomi Valkeinen 
1610f7018c21STomi Valkeinen static irqreturn_t aty_irq(int irq, void *dev_id)
1611f7018c21STomi Valkeinen {
1612f7018c21STomi Valkeinen 	struct atyfb_par *par = dev_id;
1613f7018c21STomi Valkeinen 	int handled = 0;
1614f7018c21STomi Valkeinen 	u32 int_cntl;
1615f7018c21STomi Valkeinen 
1616f7018c21STomi Valkeinen 	spin_lock(&par->int_lock);
1617f7018c21STomi Valkeinen 
1618f7018c21STomi Valkeinen 	int_cntl = aty_ld_le32(CRTC_INT_CNTL, par);
1619f7018c21STomi Valkeinen 
1620f7018c21STomi Valkeinen 	if (int_cntl & CRTC_VBLANK_INT) {
1621f7018c21STomi Valkeinen 		/* clear interrupt */
1622f7018c21STomi Valkeinen 		aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) |
1623f7018c21STomi Valkeinen 			    CRTC_VBLANK_INT_AK, par);
1624f7018c21STomi Valkeinen 		par->vblank.count++;
1625f7018c21STomi Valkeinen 		if (par->vblank.pan_display) {
1626f7018c21STomi Valkeinen 			par->vblank.pan_display = 0;
1627f7018c21STomi Valkeinen 			aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
1628f7018c21STomi Valkeinen 		}
1629f7018c21STomi Valkeinen 		wake_up_interruptible(&par->vblank.wait);
1630f7018c21STomi Valkeinen 		handled = 1;
1631f7018c21STomi Valkeinen 	}
1632f7018c21STomi Valkeinen 
1633f7018c21STomi Valkeinen 	spin_unlock(&par->int_lock);
1634f7018c21STomi Valkeinen 
1635f7018c21STomi Valkeinen 	return IRQ_RETVAL(handled);
1636f7018c21STomi Valkeinen }
1637f7018c21STomi Valkeinen 
1638f7018c21STomi Valkeinen static int aty_enable_irq(struct atyfb_par *par, int reenable)
1639f7018c21STomi Valkeinen {
1640f7018c21STomi Valkeinen 	u32 int_cntl;
1641f7018c21STomi Valkeinen 
1642f7018c21STomi Valkeinen 	if (!test_and_set_bit(0, &par->irq_flags)) {
1643f7018c21STomi Valkeinen 		if (request_irq(par->irq, aty_irq, IRQF_SHARED, "atyfb", par)) {
1644f7018c21STomi Valkeinen 			clear_bit(0, &par->irq_flags);
1645f7018c21STomi Valkeinen 			return -EINVAL;
1646f7018c21STomi Valkeinen 		}
1647f7018c21STomi Valkeinen 		spin_lock_irq(&par->int_lock);
1648f7018c21STomi Valkeinen 		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
1649f7018c21STomi Valkeinen 		/* clear interrupt */
1650f7018c21STomi Valkeinen 		aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_AK, par);
1651f7018c21STomi Valkeinen 		/* enable interrupt */
1652f7018c21STomi Valkeinen 		aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par);
1653f7018c21STomi Valkeinen 		spin_unlock_irq(&par->int_lock);
1654f7018c21STomi Valkeinen 	} else if (reenable) {
1655f7018c21STomi Valkeinen 		spin_lock_irq(&par->int_lock);
1656f7018c21STomi Valkeinen 		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
1657f7018c21STomi Valkeinen 		if (!(int_cntl & CRTC_VBLANK_INT_EN)) {
1658f7018c21STomi Valkeinen 			printk("atyfb: someone disabled IRQ [%08x]\n",
1659f7018c21STomi Valkeinen 			       int_cntl);
1660f7018c21STomi Valkeinen 			/* re-enable interrupt */
1661f7018c21STomi Valkeinen 			aty_st_le32(CRTC_INT_CNTL, int_cntl |
1662f7018c21STomi Valkeinen 				    CRTC_VBLANK_INT_EN, par);
1663f7018c21STomi Valkeinen 		}
1664f7018c21STomi Valkeinen 		spin_unlock_irq(&par->int_lock);
1665f7018c21STomi Valkeinen 	}
1666f7018c21STomi Valkeinen 
1667f7018c21STomi Valkeinen 	return 0;
1668f7018c21STomi Valkeinen }
1669f7018c21STomi Valkeinen 
1670f7018c21STomi Valkeinen static int aty_disable_irq(struct atyfb_par *par)
1671f7018c21STomi Valkeinen {
1672f7018c21STomi Valkeinen 	u32 int_cntl;
1673f7018c21STomi Valkeinen 
1674f7018c21STomi Valkeinen 	if (test_and_clear_bit(0, &par->irq_flags)) {
1675f7018c21STomi Valkeinen 		if (par->vblank.pan_display) {
1676f7018c21STomi Valkeinen 			par->vblank.pan_display = 0;
1677f7018c21STomi Valkeinen 			aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
1678f7018c21STomi Valkeinen 		}
1679f7018c21STomi Valkeinen 		spin_lock_irq(&par->int_lock);
1680f7018c21STomi Valkeinen 		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
1681f7018c21STomi Valkeinen 		/* disable interrupt */
1682f7018c21STomi Valkeinen 		aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par);
1683f7018c21STomi Valkeinen 		spin_unlock_irq(&par->int_lock);
1684f7018c21STomi Valkeinen 		free_irq(par->irq, par);
1685f7018c21STomi Valkeinen 	}
1686f7018c21STomi Valkeinen 
1687f7018c21STomi Valkeinen 	return 0;
1688f7018c21STomi Valkeinen }
1689f7018c21STomi Valkeinen 
1690f7018c21STomi Valkeinen static int atyfb_release(struct fb_info *info, int user)
1691f7018c21STomi Valkeinen {
1692f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1693f7018c21STomi Valkeinen #ifdef __sparc__
1694f7018c21STomi Valkeinen 	int was_mmaped;
1695f7018c21STomi Valkeinen #endif
1696f7018c21STomi Valkeinen 
1697f7018c21STomi Valkeinen 	if (!user)
1698f7018c21STomi Valkeinen 		return 0;
1699f7018c21STomi Valkeinen 
1700f7018c21STomi Valkeinen 	par->open--;
1701f7018c21STomi Valkeinen 	mdelay(1);
1702f7018c21STomi Valkeinen 	wait_for_idle(par);
1703f7018c21STomi Valkeinen 
1704f7018c21STomi Valkeinen 	if (par->open)
1705f7018c21STomi Valkeinen 		return 0;
1706f7018c21STomi Valkeinen 
1707f7018c21STomi Valkeinen #ifdef __sparc__
1708f7018c21STomi Valkeinen 	was_mmaped = par->mmaped;
1709f7018c21STomi Valkeinen 
1710f7018c21STomi Valkeinen 	par->mmaped = 0;
1711f7018c21STomi Valkeinen 
1712f7018c21STomi Valkeinen 	if (was_mmaped) {
1713f7018c21STomi Valkeinen 		struct fb_var_screeninfo var;
1714f7018c21STomi Valkeinen 
1715f7018c21STomi Valkeinen 		/*
1716f7018c21STomi Valkeinen 		 * Now reset the default display config, we have
1717f7018c21STomi Valkeinen 		 * no idea what the program(s) which mmap'd the
1718f7018c21STomi Valkeinen 		 * chip did to the configuration, nor whether it
1719f7018c21STomi Valkeinen 		 * restored it correctly.
1720f7018c21STomi Valkeinen 		 */
1721f7018c21STomi Valkeinen 		var = default_var;
1722f7018c21STomi Valkeinen 		if (noaccel)
1723f7018c21STomi Valkeinen 			var.accel_flags &= ~FB_ACCELF_TEXT;
1724f7018c21STomi Valkeinen 		else
1725f7018c21STomi Valkeinen 			var.accel_flags |= FB_ACCELF_TEXT;
1726f7018c21STomi Valkeinen 		if (var.yres == var.yres_virtual) {
1727f7018c21STomi Valkeinen 			u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2));
1728f7018c21STomi Valkeinen 			var.yres_virtual =
1729f7018c21STomi Valkeinen 				((videoram * 8) / var.bits_per_pixel) /
1730f7018c21STomi Valkeinen 				var.xres_virtual;
1731f7018c21STomi Valkeinen 			if (var.yres_virtual < var.yres)
1732f7018c21STomi Valkeinen 				var.yres_virtual = var.yres;
1733f7018c21STomi Valkeinen 		}
1734f7018c21STomi Valkeinen 	}
1735f7018c21STomi Valkeinen #endif
1736f7018c21STomi Valkeinen 	aty_disable_irq(par);
1737f7018c21STomi Valkeinen 
1738f7018c21STomi Valkeinen 	return 0;
1739f7018c21STomi Valkeinen }
1740f7018c21STomi Valkeinen 
1741f7018c21STomi Valkeinen /*
1742f7018c21STomi Valkeinen  * Pan or Wrap the Display
1743f7018c21STomi Valkeinen  *
1744f7018c21STomi Valkeinen  * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
1745f7018c21STomi Valkeinen  */
1746f7018c21STomi Valkeinen 
1747f7018c21STomi Valkeinen static int atyfb_pan_display(struct fb_var_screeninfo *var,
1748f7018c21STomi Valkeinen 			     struct fb_info *info)
1749f7018c21STomi Valkeinen {
1750f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1751f7018c21STomi Valkeinen 	u32 xres, yres, xoffset, yoffset;
1752f7018c21STomi Valkeinen 
1753f7018c21STomi Valkeinen 	xres = (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8;
1754f7018c21STomi Valkeinen 	yres = ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1;
1755f7018c21STomi Valkeinen 	if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
1756f7018c21STomi Valkeinen 		yres >>= 1;
1757f7018c21STomi Valkeinen 	xoffset = (var->xoffset + 7) & ~7;
1758f7018c21STomi Valkeinen 	yoffset = var->yoffset;
1759f7018c21STomi Valkeinen 	if (xoffset + xres > par->crtc.vxres ||
1760f7018c21STomi Valkeinen 	    yoffset + yres > par->crtc.vyres)
1761f7018c21STomi Valkeinen 		return -EINVAL;
1762f7018c21STomi Valkeinen 	info->var.xoffset = xoffset;
1763f7018c21STomi Valkeinen 	info->var.yoffset = yoffset;
1764f7018c21STomi Valkeinen 	if (par->asleep)
1765f7018c21STomi Valkeinen 		return 0;
1766f7018c21STomi Valkeinen 
1767f7018c21STomi Valkeinen 	set_off_pitch(par, info);
1768f7018c21STomi Valkeinen 	if ((var->activate & FB_ACTIVATE_VBL) && !aty_enable_irq(par, 0)) {
1769f7018c21STomi Valkeinen 		par->vblank.pan_display = 1;
1770f7018c21STomi Valkeinen 	} else {
1771f7018c21STomi Valkeinen 		par->vblank.pan_display = 0;
1772f7018c21STomi Valkeinen 		aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
1773f7018c21STomi Valkeinen 	}
1774f7018c21STomi Valkeinen 
1775f7018c21STomi Valkeinen 	return 0;
1776f7018c21STomi Valkeinen }
1777f7018c21STomi Valkeinen 
1778f7018c21STomi Valkeinen static int aty_waitforvblank(struct atyfb_par *par, u32 crtc)
1779f7018c21STomi Valkeinen {
1780f7018c21STomi Valkeinen 	struct aty_interrupt *vbl;
1781f7018c21STomi Valkeinen 	unsigned int count;
1782f7018c21STomi Valkeinen 	int ret;
1783f7018c21STomi Valkeinen 
1784f7018c21STomi Valkeinen 	switch (crtc) {
1785f7018c21STomi Valkeinen 	case 0:
1786f7018c21STomi Valkeinen 		vbl = &par->vblank;
1787f7018c21STomi Valkeinen 		break;
1788f7018c21STomi Valkeinen 	default:
1789f7018c21STomi Valkeinen 		return -ENODEV;
1790f7018c21STomi Valkeinen 	}
1791f7018c21STomi Valkeinen 
1792f7018c21STomi Valkeinen 	ret = aty_enable_irq(par, 0);
1793f7018c21STomi Valkeinen 	if (ret)
1794f7018c21STomi Valkeinen 		return ret;
1795f7018c21STomi Valkeinen 
1796f7018c21STomi Valkeinen 	count = vbl->count;
1797f7018c21STomi Valkeinen 	ret = wait_event_interruptible_timeout(vbl->wait,
1798f7018c21STomi Valkeinen 					       count != vbl->count, HZ/10);
1799f7018c21STomi Valkeinen 	if (ret < 0)
1800f7018c21STomi Valkeinen 		return ret;
1801f7018c21STomi Valkeinen 	if (ret == 0) {
1802f7018c21STomi Valkeinen 		aty_enable_irq(par, 1);
1803f7018c21STomi Valkeinen 		return -ETIMEDOUT;
1804f7018c21STomi Valkeinen 	}
1805f7018c21STomi Valkeinen 
1806f7018c21STomi Valkeinen 	return 0;
1807f7018c21STomi Valkeinen }
1808f7018c21STomi Valkeinen 
1809f7018c21STomi Valkeinen 
1810f7018c21STomi Valkeinen #ifdef DEBUG
1811f7018c21STomi Valkeinen #define ATYIO_CLKR		0x41545900	/* ATY\00 */
1812f7018c21STomi Valkeinen #define ATYIO_CLKW		0x41545901	/* ATY\01 */
1813f7018c21STomi Valkeinen 
1814f7018c21STomi Valkeinen struct atyclk {
1815f7018c21STomi Valkeinen 	u32 ref_clk_per;
1816f7018c21STomi Valkeinen 	u8 pll_ref_div;
1817f7018c21STomi Valkeinen 	u8 mclk_fb_div;
1818f7018c21STomi Valkeinen 	u8 mclk_post_div;	/* 1,2,3,4,8 */
1819f7018c21STomi Valkeinen 	u8 mclk_fb_mult;	/* 2 or 4 */
1820f7018c21STomi Valkeinen 	u8 xclk_post_div;	/* 1,2,3,4,8 */
1821f7018c21STomi Valkeinen 	u8 vclk_fb_div;
1822f7018c21STomi Valkeinen 	u8 vclk_post_div;	/* 1,2,3,4,6,8,12 */
1823f7018c21STomi Valkeinen 	u32 dsp_xclks_per_row;	/* 0-16383 */
1824f7018c21STomi Valkeinen 	u32 dsp_loop_latency;	/* 0-15 */
1825f7018c21STomi Valkeinen 	u32 dsp_precision;	/* 0-7 */
1826f7018c21STomi Valkeinen 	u32 dsp_on;		/* 0-2047 */
1827f7018c21STomi Valkeinen 	u32 dsp_off;		/* 0-2047 */
1828f7018c21STomi Valkeinen };
1829f7018c21STomi Valkeinen 
1830f7018c21STomi Valkeinen #define ATYIO_FEATR		0x41545902	/* ATY\02 */
1831f7018c21STomi Valkeinen #define ATYIO_FEATW		0x41545903	/* ATY\03 */
1832f7018c21STomi Valkeinen #endif
1833f7018c21STomi Valkeinen 
1834f7018c21STomi Valkeinen static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
1835f7018c21STomi Valkeinen {
1836f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1837f7018c21STomi Valkeinen #ifdef __sparc__
1838f7018c21STomi Valkeinen 	struct fbtype fbtyp;
1839f7018c21STomi Valkeinen #endif
1840f7018c21STomi Valkeinen 
1841f7018c21STomi Valkeinen 	switch (cmd) {
1842f7018c21STomi Valkeinen #ifdef __sparc__
1843f7018c21STomi Valkeinen 	case FBIOGTYPE:
1844f7018c21STomi Valkeinen 		fbtyp.fb_type = FBTYPE_PCI_GENERIC;
1845f7018c21STomi Valkeinen 		fbtyp.fb_width = par->crtc.vxres;
1846f7018c21STomi Valkeinen 		fbtyp.fb_height = par->crtc.vyres;
1847f7018c21STomi Valkeinen 		fbtyp.fb_depth = info->var.bits_per_pixel;
1848f7018c21STomi Valkeinen 		fbtyp.fb_cmsize = info->cmap.len;
1849f7018c21STomi Valkeinen 		fbtyp.fb_size = info->fix.smem_len;
1850f7018c21STomi Valkeinen 		if (copy_to_user((struct fbtype __user *) arg, &fbtyp,
1851f7018c21STomi Valkeinen 				 sizeof(fbtyp)))
1852f7018c21STomi Valkeinen 			return -EFAULT;
1853f7018c21STomi Valkeinen 		break;
1854f7018c21STomi Valkeinen #endif /* __sparc__ */
1855f7018c21STomi Valkeinen 
1856f7018c21STomi Valkeinen 	case FBIO_WAITFORVSYNC:
1857f7018c21STomi Valkeinen 		{
1858f7018c21STomi Valkeinen 			u32 crtc;
1859f7018c21STomi Valkeinen 
1860f7018c21STomi Valkeinen 			if (get_user(crtc, (__u32 __user *) arg))
1861f7018c21STomi Valkeinen 				return -EFAULT;
1862f7018c21STomi Valkeinen 
1863f7018c21STomi Valkeinen 			return aty_waitforvblank(par, crtc);
1864f7018c21STomi Valkeinen 		}
1865f7018c21STomi Valkeinen 
1866f7018c21STomi Valkeinen #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
1867f7018c21STomi Valkeinen 	case ATYIO_CLKR:
1868f7018c21STomi Valkeinen 		if (M64_HAS(INTEGRATED)) {
1869f7018c21STomi Valkeinen 			struct atyclk clk;
1870f7018c21STomi Valkeinen 			union aty_pll *pll = &par->pll;
1871f7018c21STomi Valkeinen 			u32 dsp_config = pll->ct.dsp_config;
1872f7018c21STomi Valkeinen 			u32 dsp_on_off = pll->ct.dsp_on_off;
1873f7018c21STomi Valkeinen 			clk.ref_clk_per = par->ref_clk_per;
1874f7018c21STomi Valkeinen 			clk.pll_ref_div = pll->ct.pll_ref_div;
1875f7018c21STomi Valkeinen 			clk.mclk_fb_div = pll->ct.mclk_fb_div;
1876f7018c21STomi Valkeinen 			clk.mclk_post_div = pll->ct.mclk_post_div_real;
1877f7018c21STomi Valkeinen 			clk.mclk_fb_mult = pll->ct.mclk_fb_mult;
1878f7018c21STomi Valkeinen 			clk.xclk_post_div = pll->ct.xclk_post_div_real;
1879f7018c21STomi Valkeinen 			clk.vclk_fb_div = pll->ct.vclk_fb_div;
1880f7018c21STomi Valkeinen 			clk.vclk_post_div = pll->ct.vclk_post_div_real;
1881f7018c21STomi Valkeinen 			clk.dsp_xclks_per_row = dsp_config & 0x3fff;
1882f7018c21STomi Valkeinen 			clk.dsp_loop_latency = (dsp_config >> 16) & 0xf;
1883f7018c21STomi Valkeinen 			clk.dsp_precision = (dsp_config >> 20) & 7;
1884f7018c21STomi Valkeinen 			clk.dsp_off = dsp_on_off & 0x7ff;
1885f7018c21STomi Valkeinen 			clk.dsp_on = (dsp_on_off >> 16) & 0x7ff;
1886f7018c21STomi Valkeinen 			if (copy_to_user((struct atyclk __user *) arg, &clk,
1887f7018c21STomi Valkeinen 					 sizeof(clk)))
1888f7018c21STomi Valkeinen 				return -EFAULT;
1889f7018c21STomi Valkeinen 		} else
1890f7018c21STomi Valkeinen 			return -EINVAL;
1891f7018c21STomi Valkeinen 		break;
1892f7018c21STomi Valkeinen 	case ATYIO_CLKW:
1893f7018c21STomi Valkeinen 		if (M64_HAS(INTEGRATED)) {
1894f7018c21STomi Valkeinen 			struct atyclk clk;
1895f7018c21STomi Valkeinen 			union aty_pll *pll = &par->pll;
1896f7018c21STomi Valkeinen 			if (copy_from_user(&clk, (struct atyclk __user *) arg,
1897f7018c21STomi Valkeinen 					   sizeof(clk)))
1898f7018c21STomi Valkeinen 				return -EFAULT;
1899f7018c21STomi Valkeinen 			par->ref_clk_per = clk.ref_clk_per;
1900f7018c21STomi Valkeinen 			pll->ct.pll_ref_div = clk.pll_ref_div;
1901f7018c21STomi Valkeinen 			pll->ct.mclk_fb_div = clk.mclk_fb_div;
1902f7018c21STomi Valkeinen 			pll->ct.mclk_post_div_real = clk.mclk_post_div;
1903f7018c21STomi Valkeinen 			pll->ct.mclk_fb_mult = clk.mclk_fb_mult;
1904f7018c21STomi Valkeinen 			pll->ct.xclk_post_div_real = clk.xclk_post_div;
1905f7018c21STomi Valkeinen 			pll->ct.vclk_fb_div = clk.vclk_fb_div;
1906f7018c21STomi Valkeinen 			pll->ct.vclk_post_div_real = clk.vclk_post_div;
1907f7018c21STomi Valkeinen 			pll->ct.dsp_config = (clk.dsp_xclks_per_row & 0x3fff) |
1908f7018c21STomi Valkeinen 				((clk.dsp_loop_latency & 0xf) << 16) |
1909f7018c21STomi Valkeinen 				((clk.dsp_precision & 7) << 20);
1910f7018c21STomi Valkeinen 			pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) |
1911f7018c21STomi Valkeinen 				((clk.dsp_on & 0x7ff) << 16);
1912f7018c21STomi Valkeinen 			/*aty_calc_pll_ct(info, &pll->ct);*/
1913f7018c21STomi Valkeinen 			aty_set_pll_ct(info, pll);
1914f7018c21STomi Valkeinen 		} else
1915f7018c21STomi Valkeinen 			return -EINVAL;
1916f7018c21STomi Valkeinen 		break;
1917f7018c21STomi Valkeinen 	case ATYIO_FEATR:
1918f7018c21STomi Valkeinen 		if (get_user(par->features, (u32 __user *) arg))
1919f7018c21STomi Valkeinen 			return -EFAULT;
1920f7018c21STomi Valkeinen 		break;
1921f7018c21STomi Valkeinen 	case ATYIO_FEATW:
1922f7018c21STomi Valkeinen 		if (put_user(par->features, (u32 __user *) arg))
1923f7018c21STomi Valkeinen 			return -EFAULT;
1924f7018c21STomi Valkeinen 		break;
1925f7018c21STomi Valkeinen #endif /* DEBUG && CONFIG_FB_ATY_CT */
1926f7018c21STomi Valkeinen 	default:
1927f7018c21STomi Valkeinen 		return -EINVAL;
1928f7018c21STomi Valkeinen 	}
1929f7018c21STomi Valkeinen 	return 0;
1930f7018c21STomi Valkeinen }
1931f7018c21STomi Valkeinen 
1932f7018c21STomi Valkeinen static int atyfb_sync(struct fb_info *info)
1933f7018c21STomi Valkeinen {
1934f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1935f7018c21STomi Valkeinen 
1936f7018c21STomi Valkeinen 	if (par->blitter_may_be_busy)
1937f7018c21STomi Valkeinen 		wait_for_idle(par);
1938f7018c21STomi Valkeinen 	return 0;
1939f7018c21STomi Valkeinen }
1940f7018c21STomi Valkeinen 
1941f7018c21STomi Valkeinen #ifdef __sparc__
1942f7018c21STomi Valkeinen static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
1943f7018c21STomi Valkeinen {
1944f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
1945f7018c21STomi Valkeinen 	unsigned int size, page, map_size = 0;
1946f7018c21STomi Valkeinen 	unsigned long map_offset = 0;
1947f7018c21STomi Valkeinen 	unsigned long off;
1948f7018c21STomi Valkeinen 	int i;
1949f7018c21STomi Valkeinen 
1950f7018c21STomi Valkeinen 	if (!par->mmap_map)
1951f7018c21STomi Valkeinen 		return -ENXIO;
1952f7018c21STomi Valkeinen 
1953f7018c21STomi Valkeinen 	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
1954f7018c21STomi Valkeinen 		return -EINVAL;
1955f7018c21STomi Valkeinen 
1956f7018c21STomi Valkeinen 	off = vma->vm_pgoff << PAGE_SHIFT;
1957f7018c21STomi Valkeinen 	size = vma->vm_end - vma->vm_start;
1958f7018c21STomi Valkeinen 
1959f7018c21STomi Valkeinen 	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
1960f7018c21STomi Valkeinen 
1961f7018c21STomi Valkeinen 	if (((vma->vm_pgoff == 0) && (size == info->fix.smem_len)) ||
1962f7018c21STomi Valkeinen 	    ((off == info->fix.smem_len) && (size == PAGE_SIZE)))
1963f7018c21STomi Valkeinen 		off += 0x8000000000000000UL;
1964f7018c21STomi Valkeinen 
1965f7018c21STomi Valkeinen 	vma->vm_pgoff = off >> PAGE_SHIFT;	/* propagate off changes */
1966f7018c21STomi Valkeinen 
1967f7018c21STomi Valkeinen 	/* Each page, see which map applies */
1968f7018c21STomi Valkeinen 	for (page = 0; page < size;) {
1969f7018c21STomi Valkeinen 		map_size = 0;
1970f7018c21STomi Valkeinen 		for (i = 0; par->mmap_map[i].size; i++) {
1971f7018c21STomi Valkeinen 			unsigned long start = par->mmap_map[i].voff;
1972f7018c21STomi Valkeinen 			unsigned long end = start + par->mmap_map[i].size;
1973f7018c21STomi Valkeinen 			unsigned long offset = off + page;
1974f7018c21STomi Valkeinen 
1975f7018c21STomi Valkeinen 			if (start > offset)
1976f7018c21STomi Valkeinen 				continue;
1977f7018c21STomi Valkeinen 			if (offset >= end)
1978f7018c21STomi Valkeinen 				continue;
1979f7018c21STomi Valkeinen 
1980f7018c21STomi Valkeinen 			map_size = par->mmap_map[i].size - (offset - start);
1981f7018c21STomi Valkeinen 			map_offset = par->mmap_map[i].poff + (offset - start);
1982f7018c21STomi Valkeinen 			break;
1983f7018c21STomi Valkeinen 		}
1984f7018c21STomi Valkeinen 		if (!map_size) {
1985f7018c21STomi Valkeinen 			page += PAGE_SIZE;
1986f7018c21STomi Valkeinen 			continue;
1987f7018c21STomi Valkeinen 		}
1988f7018c21STomi Valkeinen 		if (page + map_size > size)
1989f7018c21STomi Valkeinen 			map_size = size - page;
1990f7018c21STomi Valkeinen 
1991f7018c21STomi Valkeinen 		pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask);
1992f7018c21STomi Valkeinen 		pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag;
1993f7018c21STomi Valkeinen 
1994f7018c21STomi Valkeinen 		if (remap_pfn_range(vma, vma->vm_start + page,
1995f7018c21STomi Valkeinen 			map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot))
1996f7018c21STomi Valkeinen 			return -EAGAIN;
1997f7018c21STomi Valkeinen 
1998f7018c21STomi Valkeinen 		page += map_size;
1999f7018c21STomi Valkeinen 	}
2000f7018c21STomi Valkeinen 
2001f7018c21STomi Valkeinen 	if (!map_size)
2002f7018c21STomi Valkeinen 		return -EINVAL;
2003f7018c21STomi Valkeinen 
2004f7018c21STomi Valkeinen 	if (!par->mmaped)
2005f7018c21STomi Valkeinen 		par->mmaped = 1;
2006f7018c21STomi Valkeinen 	return 0;
2007f7018c21STomi Valkeinen }
2008f7018c21STomi Valkeinen #endif /* __sparc__ */
2009f7018c21STomi Valkeinen 
2010f7018c21STomi Valkeinen 
2011f7018c21STomi Valkeinen 
2012f7018c21STomi Valkeinen #if defined(CONFIG_PM) && defined(CONFIG_PCI)
2013f7018c21STomi Valkeinen 
2014f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
2015f7018c21STomi Valkeinen /* Power management routines. Those are used for PowerBook sleep.
2016f7018c21STomi Valkeinen  */
2017f7018c21STomi Valkeinen static int aty_power_mgmt(int sleep, struct atyfb_par *par)
2018f7018c21STomi Valkeinen {
2019f7018c21STomi Valkeinen 	u32 pm;
2020f7018c21STomi Valkeinen 	int timeout;
2021f7018c21STomi Valkeinen 
2022f7018c21STomi Valkeinen 	pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2023f7018c21STomi Valkeinen 	pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG;
2024f7018c21STomi Valkeinen 	aty_st_lcd(POWER_MANAGEMENT, pm, par);
2025f7018c21STomi Valkeinen 	pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2026f7018c21STomi Valkeinen 
2027f7018c21STomi Valkeinen 	timeout = 2000;
2028f7018c21STomi Valkeinen 	if (sleep) {
2029f7018c21STomi Valkeinen 		/* Sleep */
2030f7018c21STomi Valkeinen 		pm &= ~PWR_MGT_ON;
2031f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2032f7018c21STomi Valkeinen 		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2033f7018c21STomi Valkeinen 		udelay(10);
2034f7018c21STomi Valkeinen 		pm &= ~(PWR_BLON | AUTO_PWR_UP);
2035f7018c21STomi Valkeinen 		pm |= SUSPEND_NOW;
2036f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2037f7018c21STomi Valkeinen 		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2038f7018c21STomi Valkeinen 		udelay(10);
2039f7018c21STomi Valkeinen 		pm |= PWR_MGT_ON;
2040f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2041f7018c21STomi Valkeinen 		do {
2042f7018c21STomi Valkeinen 			pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2043f7018c21STomi Valkeinen 			mdelay(1);
2044f7018c21STomi Valkeinen 			if ((--timeout) == 0)
2045f7018c21STomi Valkeinen 				break;
2046f7018c21STomi Valkeinen 		} while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND);
2047f7018c21STomi Valkeinen 	} else {
2048f7018c21STomi Valkeinen 		/* Wakeup */
2049f7018c21STomi Valkeinen 		pm &= ~PWR_MGT_ON;
2050f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2051f7018c21STomi Valkeinen 		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2052f7018c21STomi Valkeinen 		udelay(10);
2053f7018c21STomi Valkeinen 		pm &= ~SUSPEND_NOW;
2054f7018c21STomi Valkeinen 		pm |= (PWR_BLON | AUTO_PWR_UP);
2055f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2056f7018c21STomi Valkeinen 		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2057f7018c21STomi Valkeinen 		udelay(10);
2058f7018c21STomi Valkeinen 		pm |= PWR_MGT_ON;
2059f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2060f7018c21STomi Valkeinen 		do {
2061f7018c21STomi Valkeinen 			pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2062f7018c21STomi Valkeinen 			mdelay(1);
2063f7018c21STomi Valkeinen 			if ((--timeout) == 0)
2064f7018c21STomi Valkeinen 				break;
2065f7018c21STomi Valkeinen 		} while ((pm & PWR_MGT_STATUS_MASK) != 0);
2066f7018c21STomi Valkeinen 	}
2067f7018c21STomi Valkeinen 	mdelay(500);
2068f7018c21STomi Valkeinen 
2069f7018c21STomi Valkeinen 	return timeout ? 0 : -EIO;
2070f7018c21STomi Valkeinen }
2071f7018c21STomi Valkeinen #endif /* CONFIG_PPC_PMAC */
2072f7018c21STomi Valkeinen 
2073f7018c21STomi Valkeinen static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
2074f7018c21STomi Valkeinen {
2075f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(pdev);
2076f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
2077f7018c21STomi Valkeinen 
2078f7018c21STomi Valkeinen 	if (state.event == pdev->dev.power.power_state.event)
2079f7018c21STomi Valkeinen 		return 0;
2080f7018c21STomi Valkeinen 
2081f7018c21STomi Valkeinen 	console_lock();
2082f7018c21STomi Valkeinen 
2083f7018c21STomi Valkeinen 	fb_set_suspend(info, 1);
2084f7018c21STomi Valkeinen 
2085f7018c21STomi Valkeinen 	/* Idle & reset engine */
2086f7018c21STomi Valkeinen 	wait_for_idle(par);
2087f7018c21STomi Valkeinen 	aty_reset_engine(par);
2088f7018c21STomi Valkeinen 
2089f7018c21STomi Valkeinen 	/* Blank display and LCD */
2090f7018c21STomi Valkeinen 	atyfb_blank(FB_BLANK_POWERDOWN, info);
2091f7018c21STomi Valkeinen 
2092f7018c21STomi Valkeinen 	par->asleep = 1;
2093f7018c21STomi Valkeinen 	par->lock_blank = 1;
2094f7018c21STomi Valkeinen 
2095f7018c21STomi Valkeinen 	/*
2096f7018c21STomi Valkeinen 	 * Because we may change PCI D state ourselves, we need to
2097f7018c21STomi Valkeinen 	 * first save the config space content so the core can
2098f7018c21STomi Valkeinen 	 * restore it properly on resume.
2099f7018c21STomi Valkeinen 	 */
2100f7018c21STomi Valkeinen 	pci_save_state(pdev);
2101f7018c21STomi Valkeinen 
2102f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
2103f7018c21STomi Valkeinen 	/* Set chip to "suspend" mode */
2104f7018c21STomi Valkeinen 	if (machine_is(powermac) && aty_power_mgmt(1, par)) {
2105f7018c21STomi Valkeinen 		par->asleep = 0;
2106f7018c21STomi Valkeinen 		par->lock_blank = 0;
2107f7018c21STomi Valkeinen 		atyfb_blank(FB_BLANK_UNBLANK, info);
2108f7018c21STomi Valkeinen 		fb_set_suspend(info, 0);
2109f7018c21STomi Valkeinen 		console_unlock();
2110f7018c21STomi Valkeinen 		return -EIO;
2111f7018c21STomi Valkeinen 	}
2112f7018c21STomi Valkeinen #else
2113f7018c21STomi Valkeinen 	pci_set_power_state(pdev, pci_choose_state(pdev, state));
2114f7018c21STomi Valkeinen #endif
2115f7018c21STomi Valkeinen 
2116f7018c21STomi Valkeinen 	console_unlock();
2117f7018c21STomi Valkeinen 
2118f7018c21STomi Valkeinen 	pdev->dev.power.power_state = state;
2119f7018c21STomi Valkeinen 
2120f7018c21STomi Valkeinen 	return 0;
2121f7018c21STomi Valkeinen }
2122f7018c21STomi Valkeinen 
2123f7018c21STomi Valkeinen static void aty_resume_chip(struct fb_info *info)
2124f7018c21STomi Valkeinen {
2125f7018c21STomi Valkeinen 	struct atyfb_par *par = info->par;
2126f7018c21STomi Valkeinen 
2127f7018c21STomi Valkeinen 	aty_st_le32(MEM_CNTL, par->mem_cntl, par);
2128f7018c21STomi Valkeinen 
2129f7018c21STomi Valkeinen 	if (par->pll_ops->resume_pll)
2130f7018c21STomi Valkeinen 		par->pll_ops->resume_pll(info, &par->pll);
2131f7018c21STomi Valkeinen 
2132f7018c21STomi Valkeinen 	if (par->aux_start)
2133f7018c21STomi Valkeinen 		aty_st_le32(BUS_CNTL,
2134f7018c21STomi Valkeinen 			aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par);
2135f7018c21STomi Valkeinen }
2136f7018c21STomi Valkeinen 
2137f7018c21STomi Valkeinen static int atyfb_pci_resume(struct pci_dev *pdev)
2138f7018c21STomi Valkeinen {
2139f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(pdev);
2140f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
2141f7018c21STomi Valkeinen 
2142f7018c21STomi Valkeinen 	if (pdev->dev.power.power_state.event == PM_EVENT_ON)
2143f7018c21STomi Valkeinen 		return 0;
2144f7018c21STomi Valkeinen 
2145f7018c21STomi Valkeinen 	console_lock();
2146f7018c21STomi Valkeinen 
2147f7018c21STomi Valkeinen 	/*
2148f7018c21STomi Valkeinen 	 * PCI state will have been restored by the core, so
2149f7018c21STomi Valkeinen 	 * we should be in D0 now with our config space fully
2150f7018c21STomi Valkeinen 	 * restored
2151f7018c21STomi Valkeinen 	 */
2152f7018c21STomi Valkeinen 
2153f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
2154f7018c21STomi Valkeinen 	if (machine_is(powermac) &&
2155f7018c21STomi Valkeinen 	    pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
2156f7018c21STomi Valkeinen 		aty_power_mgmt(0, par);
2157f7018c21STomi Valkeinen #endif
2158f7018c21STomi Valkeinen 
2159f7018c21STomi Valkeinen 	aty_resume_chip(info);
2160f7018c21STomi Valkeinen 
2161f7018c21STomi Valkeinen 	par->asleep = 0;
2162f7018c21STomi Valkeinen 
2163f7018c21STomi Valkeinen 	/* Restore display */
2164f7018c21STomi Valkeinen 	atyfb_set_par(info);
2165f7018c21STomi Valkeinen 
2166f7018c21STomi Valkeinen 	/* Refresh */
2167f7018c21STomi Valkeinen 	fb_set_suspend(info, 0);
2168f7018c21STomi Valkeinen 
2169f7018c21STomi Valkeinen 	/* Unblank */
2170f7018c21STomi Valkeinen 	par->lock_blank = 0;
2171f7018c21STomi Valkeinen 	atyfb_blank(FB_BLANK_UNBLANK, info);
2172f7018c21STomi Valkeinen 
2173f7018c21STomi Valkeinen 	console_unlock();
2174f7018c21STomi Valkeinen 
2175f7018c21STomi Valkeinen 	pdev->dev.power.power_state = PMSG_ON;
2176f7018c21STomi Valkeinen 
2177f7018c21STomi Valkeinen 	return 0;
2178f7018c21STomi Valkeinen }
2179f7018c21STomi Valkeinen 
2180f7018c21STomi Valkeinen #endif /*  defined(CONFIG_PM) && defined(CONFIG_PCI) */
2181f7018c21STomi Valkeinen 
2182f7018c21STomi Valkeinen /* Backlight */
2183f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_BACKLIGHT
2184f7018c21STomi Valkeinen #define MAX_LEVEL 0xFF
2185f7018c21STomi Valkeinen 
2186f7018c21STomi Valkeinen static int aty_bl_get_level_brightness(struct atyfb_par *par, int level)
2187f7018c21STomi Valkeinen {
2188f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(par->pdev);
2189f7018c21STomi Valkeinen 	int atylevel;
2190f7018c21STomi Valkeinen 
2191f7018c21STomi Valkeinen 	/* Get and convert the value */
2192f7018c21STomi Valkeinen 	/* No locking of bl_curve since we read a single value */
2193f7018c21STomi Valkeinen 	atylevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL;
2194f7018c21STomi Valkeinen 
2195f7018c21STomi Valkeinen 	if (atylevel < 0)
2196f7018c21STomi Valkeinen 		atylevel = 0;
2197f7018c21STomi Valkeinen 	else if (atylevel > MAX_LEVEL)
2198f7018c21STomi Valkeinen 		atylevel = MAX_LEVEL;
2199f7018c21STomi Valkeinen 
2200f7018c21STomi Valkeinen 	return atylevel;
2201f7018c21STomi Valkeinen }
2202f7018c21STomi Valkeinen 
2203f7018c21STomi Valkeinen static int aty_bl_update_status(struct backlight_device *bd)
2204f7018c21STomi Valkeinen {
2205f7018c21STomi Valkeinen 	struct atyfb_par *par = bl_get_data(bd);
2206f7018c21STomi Valkeinen 	unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par);
2207f7018c21STomi Valkeinen 	int level;
2208f7018c21STomi Valkeinen 
2209f7018c21STomi Valkeinen 	if (bd->props.power != FB_BLANK_UNBLANK ||
2210f7018c21STomi Valkeinen 	    bd->props.fb_blank != FB_BLANK_UNBLANK)
2211f7018c21STomi Valkeinen 		level = 0;
2212f7018c21STomi Valkeinen 	else
2213f7018c21STomi Valkeinen 		level = bd->props.brightness;
2214f7018c21STomi Valkeinen 
2215f7018c21STomi Valkeinen 	reg |= (BLMOD_EN | BIASMOD_EN);
2216f7018c21STomi Valkeinen 	if (level > 0) {
2217f7018c21STomi Valkeinen 		reg &= ~BIAS_MOD_LEVEL_MASK;
2218f7018c21STomi Valkeinen 		reg |= (aty_bl_get_level_brightness(par, level) << BIAS_MOD_LEVEL_SHIFT);
2219f7018c21STomi Valkeinen 	} else {
2220f7018c21STomi Valkeinen 		reg &= ~BIAS_MOD_LEVEL_MASK;
2221f7018c21STomi Valkeinen 		reg |= (aty_bl_get_level_brightness(par, 0) << BIAS_MOD_LEVEL_SHIFT);
2222f7018c21STomi Valkeinen 	}
2223f7018c21STomi Valkeinen 	aty_st_lcd(LCD_MISC_CNTL, reg, par);
2224f7018c21STomi Valkeinen 
2225f7018c21STomi Valkeinen 	return 0;
2226f7018c21STomi Valkeinen }
2227f7018c21STomi Valkeinen 
2228f7018c21STomi Valkeinen static const struct backlight_ops aty_bl_data = {
2229f7018c21STomi Valkeinen 	.update_status	= aty_bl_update_status,
2230f7018c21STomi Valkeinen };
2231f7018c21STomi Valkeinen 
2232f7018c21STomi Valkeinen static void aty_bl_init(struct atyfb_par *par)
2233f7018c21STomi Valkeinen {
2234f7018c21STomi Valkeinen 	struct backlight_properties props;
2235f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(par->pdev);
2236f7018c21STomi Valkeinen 	struct backlight_device *bd;
2237f7018c21STomi Valkeinen 	char name[12];
2238f7018c21STomi Valkeinen 
2239f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
2240f7018c21STomi Valkeinen 	if (!pmac_has_backlight_type("ati"))
2241f7018c21STomi Valkeinen 		return;
2242f7018c21STomi Valkeinen #endif
2243f7018c21STomi Valkeinen 
2244f7018c21STomi Valkeinen 	snprintf(name, sizeof(name), "atybl%d", info->node);
2245f7018c21STomi Valkeinen 
2246f7018c21STomi Valkeinen 	memset(&props, 0, sizeof(struct backlight_properties));
2247f7018c21STomi Valkeinen 	props.type = BACKLIGHT_RAW;
2248f7018c21STomi Valkeinen 	props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
2249f7018c21STomi Valkeinen 	bd = backlight_device_register(name, info->dev, par, &aty_bl_data,
2250f7018c21STomi Valkeinen 				       &props);
2251f7018c21STomi Valkeinen 	if (IS_ERR(bd)) {
2252f7018c21STomi Valkeinen 		info->bl_dev = NULL;
2253f7018c21STomi Valkeinen 		printk(KERN_WARNING "aty: Backlight registration failed\n");
2254f7018c21STomi Valkeinen 		goto error;
2255f7018c21STomi Valkeinen 	}
2256f7018c21STomi Valkeinen 
2257f7018c21STomi Valkeinen 	info->bl_dev = bd;
2258f7018c21STomi Valkeinen 	fb_bl_default_curve(info, 0,
2259f7018c21STomi Valkeinen 			    0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL,
2260f7018c21STomi Valkeinen 			    0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL);
2261f7018c21STomi Valkeinen 
2262f7018c21STomi Valkeinen 	bd->props.brightness = bd->props.max_brightness;
2263f7018c21STomi Valkeinen 	bd->props.power = FB_BLANK_UNBLANK;
2264f7018c21STomi Valkeinen 	backlight_update_status(bd);
2265f7018c21STomi Valkeinen 
2266f7018c21STomi Valkeinen 	printk("aty: Backlight initialized (%s)\n", name);
2267f7018c21STomi Valkeinen 
2268f7018c21STomi Valkeinen 	return;
2269f7018c21STomi Valkeinen 
2270f7018c21STomi Valkeinen error:
2271f7018c21STomi Valkeinen 	return;
2272f7018c21STomi Valkeinen }
2273f7018c21STomi Valkeinen 
2274f7018c21STomi Valkeinen #ifdef CONFIG_PCI
2275f7018c21STomi Valkeinen static void aty_bl_exit(struct backlight_device *bd)
2276f7018c21STomi Valkeinen {
2277f7018c21STomi Valkeinen 	backlight_device_unregister(bd);
2278f7018c21STomi Valkeinen 	printk("aty: Backlight unloaded\n");
2279f7018c21STomi Valkeinen }
2280f7018c21STomi Valkeinen #endif /* CONFIG_PCI */
2281f7018c21STomi Valkeinen 
2282f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_BACKLIGHT */
2283f7018c21STomi Valkeinen 
2284f7018c21STomi Valkeinen static void aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
2285f7018c21STomi Valkeinen {
2286f7018c21STomi Valkeinen 	const int ragepro_tbl[] = {
2287f7018c21STomi Valkeinen 		44, 50, 55, 66, 75, 80, 100
2288f7018c21STomi Valkeinen 	};
2289f7018c21STomi Valkeinen 	const int ragexl_tbl[] = {
2290f7018c21STomi Valkeinen 		50, 66, 75, 83, 90, 95, 100, 105,
2291f7018c21STomi Valkeinen 		110, 115, 120, 125, 133, 143, 166
2292f7018c21STomi Valkeinen 	};
2293f7018c21STomi Valkeinen 	const int *refresh_tbl;
2294f7018c21STomi Valkeinen 	int i, size;
2295f7018c21STomi Valkeinen 
2296f7018c21STomi Valkeinen 	if (M64_HAS(XL_MEM)) {
2297f7018c21STomi Valkeinen 		refresh_tbl = ragexl_tbl;
2298f7018c21STomi Valkeinen 		size = ARRAY_SIZE(ragexl_tbl);
2299f7018c21STomi Valkeinen 	} else {
2300f7018c21STomi Valkeinen 		refresh_tbl = ragepro_tbl;
2301f7018c21STomi Valkeinen 		size = ARRAY_SIZE(ragepro_tbl);
2302f7018c21STomi Valkeinen 	}
2303f7018c21STomi Valkeinen 
2304f7018c21STomi Valkeinen 	for (i = 0; i < size; i++) {
2305f7018c21STomi Valkeinen 		if (xclk < refresh_tbl[i])
2306f7018c21STomi Valkeinen 			break;
2307f7018c21STomi Valkeinen 	}
2308f7018c21STomi Valkeinen 	par->mem_refresh_rate = i;
2309f7018c21STomi Valkeinen }
2310f7018c21STomi Valkeinen 
2311f7018c21STomi Valkeinen /*
2312f7018c21STomi Valkeinen  * Initialisation
2313f7018c21STomi Valkeinen  */
2314f7018c21STomi Valkeinen 
2315f7018c21STomi Valkeinen static struct fb_info *fb_list = NULL;
2316f7018c21STomi Valkeinen 
2317f7018c21STomi Valkeinen #if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
2318f7018c21STomi Valkeinen static int atyfb_get_timings_from_lcd(struct atyfb_par *par,
2319f7018c21STomi Valkeinen 				      struct fb_var_screeninfo *var)
2320f7018c21STomi Valkeinen {
2321f7018c21STomi Valkeinen 	int ret = -EINVAL;
2322f7018c21STomi Valkeinen 
2323f7018c21STomi Valkeinen 	if (par->lcd_table != 0 && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
2324f7018c21STomi Valkeinen 		*var = default_var;
2325f7018c21STomi Valkeinen 		var->xres = var->xres_virtual = par->lcd_hdisp;
2326f7018c21STomi Valkeinen 		var->right_margin = par->lcd_right_margin;
2327f7018c21STomi Valkeinen 		var->left_margin = par->lcd_hblank_len -
2328f7018c21STomi Valkeinen 			(par->lcd_right_margin + par->lcd_hsync_dly +
2329f7018c21STomi Valkeinen 			 par->lcd_hsync_len);
2330f7018c21STomi Valkeinen 		var->hsync_len = par->lcd_hsync_len + par->lcd_hsync_dly;
2331f7018c21STomi Valkeinen 		var->yres = var->yres_virtual = par->lcd_vdisp;
2332f7018c21STomi Valkeinen 		var->lower_margin = par->lcd_lower_margin;
2333f7018c21STomi Valkeinen 		var->upper_margin = par->lcd_vblank_len -
2334f7018c21STomi Valkeinen 			(par->lcd_lower_margin + par->lcd_vsync_len);
2335f7018c21STomi Valkeinen 		var->vsync_len = par->lcd_vsync_len;
2336f7018c21STomi Valkeinen 		var->pixclock = par->lcd_pixclock;
2337f7018c21STomi Valkeinen 		ret = 0;
2338f7018c21STomi Valkeinen 	}
2339f7018c21STomi Valkeinen 
2340f7018c21STomi Valkeinen 	return ret;
2341f7018c21STomi Valkeinen }
2342f7018c21STomi Valkeinen #endif /* defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) */
2343f7018c21STomi Valkeinen 
2344f7018c21STomi Valkeinen static int aty_init(struct fb_info *info)
2345f7018c21STomi Valkeinen {
2346f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
2347f7018c21STomi Valkeinen 	const char *ramname = NULL, *xtal;
2348f7018c21STomi Valkeinen 	int gtb_memsize, has_var = 0;
2349f7018c21STomi Valkeinen 	struct fb_var_screeninfo var;
2350f7018c21STomi Valkeinen 	int ret;
2351f7018c21STomi Valkeinen 
2352f7018c21STomi Valkeinen 	init_waitqueue_head(&par->vblank.wait);
2353f7018c21STomi Valkeinen 	spin_lock_init(&par->int_lock);
2354f7018c21STomi Valkeinen 
2355f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX
2356f7018c21STomi Valkeinen 	if (!M64_HAS(INTEGRATED)) {
2357f7018c21STomi Valkeinen 		u32 stat0;
2358f7018c21STomi Valkeinen 		u8 dac_type, dac_subtype, clk_type;
2359f7018c21STomi Valkeinen 		stat0 = aty_ld_le32(CNFG_STAT0, par);
2360f7018c21STomi Valkeinen 		par->bus_type = (stat0 >> 0) & 0x07;
2361f7018c21STomi Valkeinen 		par->ram_type = (stat0 >> 3) & 0x07;
2362f7018c21STomi Valkeinen 		ramname = aty_gx_ram[par->ram_type];
2363f7018c21STomi Valkeinen 		/* FIXME: clockchip/RAMDAC probing? */
2364f7018c21STomi Valkeinen 		dac_type = (aty_ld_le32(DAC_CNTL, par) >> 16) & 0x07;
2365f7018c21STomi Valkeinen #ifdef CONFIG_ATARI
2366f7018c21STomi Valkeinen 		clk_type = CLK_ATI18818_1;
2367f7018c21STomi Valkeinen 		dac_type = (stat0 >> 9) & 0x07;
2368f7018c21STomi Valkeinen 		if (dac_type == 0x07)
2369f7018c21STomi Valkeinen 			dac_subtype = DAC_ATT20C408;
2370f7018c21STomi Valkeinen 		else
2371f7018c21STomi Valkeinen 			dac_subtype = (aty_ld_8(SCRATCH_REG1 + 1, par) & 0xF0) | dac_type;
2372f7018c21STomi Valkeinen #else
2373f7018c21STomi Valkeinen 		dac_type = DAC_IBMRGB514;
2374f7018c21STomi Valkeinen 		dac_subtype = DAC_IBMRGB514;
2375f7018c21STomi Valkeinen 		clk_type = CLK_IBMRGB514;
2376f7018c21STomi Valkeinen #endif
2377f7018c21STomi Valkeinen 		switch (dac_subtype) {
2378f7018c21STomi Valkeinen 		case DAC_IBMRGB514:
2379f7018c21STomi Valkeinen 			par->dac_ops = &aty_dac_ibm514;
2380f7018c21STomi Valkeinen 			break;
2381f7018c21STomi Valkeinen #ifdef CONFIG_ATARI
2382f7018c21STomi Valkeinen 		case DAC_ATI68860_B:
2383f7018c21STomi Valkeinen 		case DAC_ATI68860_C:
2384f7018c21STomi Valkeinen 			par->dac_ops = &aty_dac_ati68860b;
2385f7018c21STomi Valkeinen 			break;
2386f7018c21STomi Valkeinen 		case DAC_ATT20C408:
2387f7018c21STomi Valkeinen 		case DAC_ATT21C498:
2388f7018c21STomi Valkeinen 			par->dac_ops = &aty_dac_att21c498;
2389f7018c21STomi Valkeinen 			break;
2390f7018c21STomi Valkeinen #endif
2391f7018c21STomi Valkeinen 		default:
2392f7018c21STomi Valkeinen 			PRINTKI("aty_init: DAC type not implemented yet!\n");
2393f7018c21STomi Valkeinen 			par->dac_ops = &aty_dac_unsupported;
2394f7018c21STomi Valkeinen 			break;
2395f7018c21STomi Valkeinen 		}
2396f7018c21STomi Valkeinen 		switch (clk_type) {
2397f7018c21STomi Valkeinen #ifdef CONFIG_ATARI
2398f7018c21STomi Valkeinen 		case CLK_ATI18818_1:
2399f7018c21STomi Valkeinen 			par->pll_ops = &aty_pll_ati18818_1;
2400f7018c21STomi Valkeinen 			break;
2401f7018c21STomi Valkeinen #else
2402f7018c21STomi Valkeinen 		case CLK_IBMRGB514:
2403f7018c21STomi Valkeinen 			par->pll_ops = &aty_pll_ibm514;
2404f7018c21STomi Valkeinen 			break;
2405f7018c21STomi Valkeinen #endif
2406f7018c21STomi Valkeinen #if 0 /* dead code */
2407f7018c21STomi Valkeinen 		case CLK_STG1703:
2408f7018c21STomi Valkeinen 			par->pll_ops = &aty_pll_stg1703;
2409f7018c21STomi Valkeinen 			break;
2410f7018c21STomi Valkeinen 		case CLK_CH8398:
2411f7018c21STomi Valkeinen 			par->pll_ops = &aty_pll_ch8398;
2412f7018c21STomi Valkeinen 			break;
2413f7018c21STomi Valkeinen 		case CLK_ATT20C408:
2414f7018c21STomi Valkeinen 			par->pll_ops = &aty_pll_att20c408;
2415f7018c21STomi Valkeinen 			break;
2416f7018c21STomi Valkeinen #endif
2417f7018c21STomi Valkeinen 		default:
2418f7018c21STomi Valkeinen 			PRINTKI("aty_init: CLK type not implemented yet!");
2419f7018c21STomi Valkeinen 			par->pll_ops = &aty_pll_unsupported;
2420f7018c21STomi Valkeinen 			break;
2421f7018c21STomi Valkeinen 		}
2422f7018c21STomi Valkeinen 	}
2423f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */
2424f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
2425f7018c21STomi Valkeinen 	if (M64_HAS(INTEGRATED)) {
2426f7018c21STomi Valkeinen 		par->dac_ops = &aty_dac_ct;
2427f7018c21STomi Valkeinen 		par->pll_ops = &aty_pll_ct;
2428f7018c21STomi Valkeinen 		par->bus_type = PCI;
2429f7018c21STomi Valkeinen 		par->ram_type = (aty_ld_le32(CNFG_STAT0, par) & 0x07);
2430f7018c21STomi Valkeinen 		if (M64_HAS(XL_MEM))
2431f7018c21STomi Valkeinen 			ramname = aty_xl_ram[par->ram_type];
2432f7018c21STomi Valkeinen 		else
2433f7018c21STomi Valkeinen 			ramname = aty_ct_ram[par->ram_type];
2434f7018c21STomi Valkeinen 		/* for many chips, the mclk is 67 MHz for SDRAM, 63 MHz otherwise */
2435f7018c21STomi Valkeinen 		if (par->pll_limits.mclk == 67 && par->ram_type < SDRAM)
2436f7018c21STomi Valkeinen 			par->pll_limits.mclk = 63;
2437f7018c21STomi Valkeinen 		/* Mobility + 32bit memory interface need halved XCLK. */
2438f7018c21STomi Valkeinen 		if (M64_HAS(MOBIL_BUS) && par->ram_type == SDRAM32)
2439f7018c21STomi Valkeinen 			par->pll_limits.xclk = (par->pll_limits.xclk + 1) >> 1;
2440f7018c21STomi Valkeinen 	}
2441f7018c21STomi Valkeinen #endif
2442f7018c21STomi Valkeinen #ifdef CONFIG_PPC_PMAC
2443f7018c21STomi Valkeinen 	/*
2444f7018c21STomi Valkeinen 	 * The Apple iBook1 uses non-standard memory frequencies.
2445f7018c21STomi Valkeinen 	 * We detect it and set the frequency manually.
2446f7018c21STomi Valkeinen 	 */
2447f7018c21STomi Valkeinen 	if (of_machine_is_compatible("PowerBook2,1")) {
2448f7018c21STomi Valkeinen 		par->pll_limits.mclk = 70;
2449f7018c21STomi Valkeinen 		par->pll_limits.xclk = 53;
2450f7018c21STomi Valkeinen 	}
2451f7018c21STomi Valkeinen #endif
2452f7018c21STomi Valkeinen 
2453f7018c21STomi Valkeinen 	/* Allow command line to override clocks. */
2454f7018c21STomi Valkeinen 	if (pll)
2455f7018c21STomi Valkeinen 		par->pll_limits.pll_max = pll;
2456f7018c21STomi Valkeinen 	if (mclk)
2457f7018c21STomi Valkeinen 		par->pll_limits.mclk = mclk;
2458f7018c21STomi Valkeinen 	if (xclk)
2459f7018c21STomi Valkeinen 		par->pll_limits.xclk = xclk;
2460f7018c21STomi Valkeinen 
2461f7018c21STomi Valkeinen 	aty_calc_mem_refresh(par, par->pll_limits.xclk);
2462f7018c21STomi Valkeinen 	par->pll_per = 1000000/par->pll_limits.pll_max;
2463f7018c21STomi Valkeinen 	par->mclk_per = 1000000/par->pll_limits.mclk;
2464f7018c21STomi Valkeinen 	par->xclk_per = 1000000/par->pll_limits.xclk;
2465f7018c21STomi Valkeinen 
2466f7018c21STomi Valkeinen 	par->ref_clk_per = 1000000000000ULL / 14318180;
2467f7018c21STomi Valkeinen 	xtal = "14.31818";
2468f7018c21STomi Valkeinen 
2469f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
2470f7018c21STomi Valkeinen 	if (M64_HAS(GTB_DSP)) {
2471f7018c21STomi Valkeinen 		u8 pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par);
2472f7018c21STomi Valkeinen 
2473f7018c21STomi Valkeinen 		if (pll_ref_div) {
2474f7018c21STomi Valkeinen 			int diff1, diff2;
2475f7018c21STomi Valkeinen 			diff1 = 510 * 14 / pll_ref_div - par->pll_limits.pll_max;
2476f7018c21STomi Valkeinen 			diff2 = 510 * 29 / pll_ref_div - par->pll_limits.pll_max;
2477f7018c21STomi Valkeinen 			if (diff1 < 0)
2478f7018c21STomi Valkeinen 				diff1 = -diff1;
2479f7018c21STomi Valkeinen 			if (diff2 < 0)
2480f7018c21STomi Valkeinen 				diff2 = -diff2;
2481f7018c21STomi Valkeinen 			if (diff2 < diff1) {
2482f7018c21STomi Valkeinen 				par->ref_clk_per = 1000000000000ULL / 29498928;
2483f7018c21STomi Valkeinen 				xtal = "29.498928";
2484f7018c21STomi Valkeinen 			}
2485f7018c21STomi Valkeinen 		}
2486f7018c21STomi Valkeinen 	}
2487f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */
2488f7018c21STomi Valkeinen 
2489f7018c21STomi Valkeinen 	/* save previous video mode */
2490f7018c21STomi Valkeinen 	aty_get_crtc(par, &par->saved_crtc);
2491f7018c21STomi Valkeinen 	if (par->pll_ops->get_pll)
2492f7018c21STomi Valkeinen 		par->pll_ops->get_pll(info, &par->saved_pll);
2493f7018c21STomi Valkeinen 
2494f7018c21STomi Valkeinen 	par->mem_cntl = aty_ld_le32(MEM_CNTL, par);
2495f7018c21STomi Valkeinen 	gtb_memsize = M64_HAS(GTB_DSP);
2496f7018c21STomi Valkeinen 	if (gtb_memsize)
2497f7018c21STomi Valkeinen 		/* 0xF used instead of MEM_SIZE_ALIAS */
2498f7018c21STomi Valkeinen 		switch (par->mem_cntl & 0xF) {
2499f7018c21STomi Valkeinen 		case MEM_SIZE_512K:
2500f7018c21STomi Valkeinen 			info->fix.smem_len = 0x80000;
2501f7018c21STomi Valkeinen 			break;
2502f7018c21STomi Valkeinen 		case MEM_SIZE_1M:
2503f7018c21STomi Valkeinen 			info->fix.smem_len = 0x100000;
2504f7018c21STomi Valkeinen 			break;
2505f7018c21STomi Valkeinen 		case MEM_SIZE_2M_GTB:
2506f7018c21STomi Valkeinen 			info->fix.smem_len = 0x200000;
2507f7018c21STomi Valkeinen 			break;
2508f7018c21STomi Valkeinen 		case MEM_SIZE_4M_GTB:
2509f7018c21STomi Valkeinen 			info->fix.smem_len = 0x400000;
2510f7018c21STomi Valkeinen 			break;
2511f7018c21STomi Valkeinen 		case MEM_SIZE_6M_GTB:
2512f7018c21STomi Valkeinen 			info->fix.smem_len = 0x600000;
2513f7018c21STomi Valkeinen 			break;
2514f7018c21STomi Valkeinen 		case MEM_SIZE_8M_GTB:
2515f7018c21STomi Valkeinen 			info->fix.smem_len = 0x800000;
2516f7018c21STomi Valkeinen 			break;
2517f7018c21STomi Valkeinen 		default:
2518f7018c21STomi Valkeinen 			info->fix.smem_len = 0x80000;
2519f7018c21STomi Valkeinen 	} else
2520f7018c21STomi Valkeinen 		switch (par->mem_cntl & MEM_SIZE_ALIAS) {
2521f7018c21STomi Valkeinen 		case MEM_SIZE_512K:
2522f7018c21STomi Valkeinen 			info->fix.smem_len = 0x80000;
2523f7018c21STomi Valkeinen 			break;
2524f7018c21STomi Valkeinen 		case MEM_SIZE_1M:
2525f7018c21STomi Valkeinen 			info->fix.smem_len = 0x100000;
2526f7018c21STomi Valkeinen 			break;
2527f7018c21STomi Valkeinen 		case MEM_SIZE_2M:
2528f7018c21STomi Valkeinen 			info->fix.smem_len = 0x200000;
2529f7018c21STomi Valkeinen 			break;
2530f7018c21STomi Valkeinen 		case MEM_SIZE_4M:
2531f7018c21STomi Valkeinen 			info->fix.smem_len = 0x400000;
2532f7018c21STomi Valkeinen 			break;
2533f7018c21STomi Valkeinen 		case MEM_SIZE_6M:
2534f7018c21STomi Valkeinen 			info->fix.smem_len = 0x600000;
2535f7018c21STomi Valkeinen 			break;
2536f7018c21STomi Valkeinen 		case MEM_SIZE_8M:
2537f7018c21STomi Valkeinen 			info->fix.smem_len = 0x800000;
2538f7018c21STomi Valkeinen 			break;
2539f7018c21STomi Valkeinen 		default:
2540f7018c21STomi Valkeinen 			info->fix.smem_len = 0x80000;
2541f7018c21STomi Valkeinen 		}
2542f7018c21STomi Valkeinen 
2543f7018c21STomi Valkeinen 	if (M64_HAS(MAGIC_VRAM_SIZE)) {
2544f7018c21STomi Valkeinen 		if (aty_ld_le32(CNFG_STAT1, par) & 0x40000000)
2545f7018c21STomi Valkeinen 			info->fix.smem_len += 0x400000;
2546f7018c21STomi Valkeinen 	}
2547f7018c21STomi Valkeinen 
2548f7018c21STomi Valkeinen 	if (vram) {
2549f7018c21STomi Valkeinen 		info->fix.smem_len = vram * 1024;
2550f7018c21STomi Valkeinen 		par->mem_cntl &= ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS);
2551f7018c21STomi Valkeinen 		if (info->fix.smem_len <= 0x80000)
2552f7018c21STomi Valkeinen 			par->mem_cntl |= MEM_SIZE_512K;
2553f7018c21STomi Valkeinen 		else if (info->fix.smem_len <= 0x100000)
2554f7018c21STomi Valkeinen 			par->mem_cntl |= MEM_SIZE_1M;
2555f7018c21STomi Valkeinen 		else if (info->fix.smem_len <= 0x200000)
2556f7018c21STomi Valkeinen 			par->mem_cntl |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M;
2557f7018c21STomi Valkeinen 		else if (info->fix.smem_len <= 0x400000)
2558f7018c21STomi Valkeinen 			par->mem_cntl |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M;
2559f7018c21STomi Valkeinen 		else if (info->fix.smem_len <= 0x600000)
2560f7018c21STomi Valkeinen 			par->mem_cntl |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M;
2561f7018c21STomi Valkeinen 		else
2562f7018c21STomi Valkeinen 			par->mem_cntl |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M;
2563f7018c21STomi Valkeinen 		aty_st_le32(MEM_CNTL, par->mem_cntl, par);
2564f7018c21STomi Valkeinen 	}
2565f7018c21STomi Valkeinen 
2566f7018c21STomi Valkeinen 	/*
2567f7018c21STomi Valkeinen 	 * Reg Block 0 (CT-compatible block) is at mmio_start
2568f7018c21STomi Valkeinen 	 * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400
2569f7018c21STomi Valkeinen 	 */
2570f7018c21STomi Valkeinen 	if (M64_HAS(GX)) {
2571f7018c21STomi Valkeinen 		info->fix.mmio_len = 0x400;
2572f7018c21STomi Valkeinen 		info->fix.accel = FB_ACCEL_ATI_MACH64GX;
2573f7018c21STomi Valkeinen 	} else if (M64_HAS(CT)) {
2574f7018c21STomi Valkeinen 		info->fix.mmio_len = 0x400;
2575f7018c21STomi Valkeinen 		info->fix.accel = FB_ACCEL_ATI_MACH64CT;
2576f7018c21STomi Valkeinen 	} else if (M64_HAS(VT)) {
2577f7018c21STomi Valkeinen 		info->fix.mmio_start -= 0x400;
2578f7018c21STomi Valkeinen 		info->fix.mmio_len = 0x800;
2579f7018c21STomi Valkeinen 		info->fix.accel = FB_ACCEL_ATI_MACH64VT;
2580f7018c21STomi Valkeinen 	} else {/* GT */
2581f7018c21STomi Valkeinen 		info->fix.mmio_start -= 0x400;
2582f7018c21STomi Valkeinen 		info->fix.mmio_len = 0x800;
2583f7018c21STomi Valkeinen 		info->fix.accel = FB_ACCEL_ATI_MACH64GT;
2584f7018c21STomi Valkeinen 	}
2585f7018c21STomi Valkeinen 
2586f7018c21STomi Valkeinen 	PRINTKI("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK, %d MHz XCLK\n",
2587f7018c21STomi Valkeinen 		info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len>>20),
2588f7018c21STomi Valkeinen 		info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal,
2589f7018c21STomi Valkeinen 		par->pll_limits.pll_max, par->pll_limits.mclk,
2590f7018c21STomi Valkeinen 		par->pll_limits.xclk);
2591f7018c21STomi Valkeinen 
2592f7018c21STomi Valkeinen #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
2593f7018c21STomi Valkeinen 	if (M64_HAS(INTEGRATED)) {
2594f7018c21STomi Valkeinen 		int i;
2595f7018c21STomi Valkeinen 		printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL "
2596f7018c21STomi Valkeinen 		       "EXT_MEM_CNTL CRTC_GEN_CNTL DSP_CONFIG "
2597f7018c21STomi Valkeinen 		       "DSP_ON_OFF CLOCK_CNTL\n"
2598f7018c21STomi Valkeinen 		       "debug atyfb: %08x %08x %08x "
2599f7018c21STomi Valkeinen 		       "%08x     %08x      %08x   "
2600f7018c21STomi Valkeinen 		       "%08x   %08x\n"
2601f7018c21STomi Valkeinen 		       "debug atyfb: PLL",
2602f7018c21STomi Valkeinen 		       aty_ld_le32(BUS_CNTL, par),
2603f7018c21STomi Valkeinen 		       aty_ld_le32(DAC_CNTL, par),
2604f7018c21STomi Valkeinen 		       aty_ld_le32(MEM_CNTL, par),
2605f7018c21STomi Valkeinen 		       aty_ld_le32(EXT_MEM_CNTL, par),
2606f7018c21STomi Valkeinen 		       aty_ld_le32(CRTC_GEN_CNTL, par),
2607f7018c21STomi Valkeinen 		       aty_ld_le32(DSP_CONFIG, par),
2608f7018c21STomi Valkeinen 		       aty_ld_le32(DSP_ON_OFF, par),
2609f7018c21STomi Valkeinen 		       aty_ld_le32(CLOCK_CNTL, par));
2610f7018c21STomi Valkeinen 		for (i = 0; i < 40; i++)
2611f7018c21STomi Valkeinen 			printk(" %02x", aty_ld_pll_ct(i, par));
2612f7018c21STomi Valkeinen 		printk("\n");
2613f7018c21STomi Valkeinen 	}
2614f7018c21STomi Valkeinen #endif
2615f7018c21STomi Valkeinen 	if (par->pll_ops->init_pll)
2616f7018c21STomi Valkeinen 		par->pll_ops->init_pll(info, &par->pll);
2617f7018c21STomi Valkeinen 	if (par->pll_ops->resume_pll)
2618f7018c21STomi Valkeinen 		par->pll_ops->resume_pll(info, &par->pll);
2619f7018c21STomi Valkeinen 
2620eacd2d54SLuis R. Rodriguez 	aty_fudge_framebuffer_len(info);
2621f7018c21STomi Valkeinen 
2622f7018c21STomi Valkeinen 	/*
2623f7018c21STomi Valkeinen 	 * Disable register access through the linear aperture
2624f7018c21STomi Valkeinen 	 * if the auxiliary aperture is used so we can access
2625f7018c21STomi Valkeinen 	 * the full 8 MB of video RAM on 8 MB boards.
2626f7018c21STomi Valkeinen 	 */
2627f7018c21STomi Valkeinen 	if (par->aux_start)
2628f7018c21STomi Valkeinen 		aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) |
2629f7018c21STomi Valkeinen 			    BUS_APER_REG_DIS, par);
2630f7018c21STomi Valkeinen 
2631f7018c21STomi Valkeinen #ifdef CONFIG_MTRR
2632f7018c21STomi Valkeinen 	par->mtrr_aper = -1;
2633f7018c21STomi Valkeinen 	par->mtrr_reg = -1;
2634f7018c21STomi Valkeinen 	if (!nomtrr) {
2635f7018c21STomi Valkeinen 		/* Cover the whole resource. */
2636f7018c21STomi Valkeinen 		par->mtrr_aper = mtrr_add(par->res_start, par->res_size,
2637f7018c21STomi Valkeinen 					  MTRR_TYPE_WRCOMB, 1);
2638f7018c21STomi Valkeinen 		if (par->mtrr_aper >= 0 && !par->aux_start) {
2639f7018c21STomi Valkeinen 			/* Make a hole for mmio. */
2640f7018c21STomi Valkeinen 			par->mtrr_reg = mtrr_add(par->res_start + 0x800000 -
2641f7018c21STomi Valkeinen 						 GUI_RESERVE, GUI_RESERVE,
2642f7018c21STomi Valkeinen 						 MTRR_TYPE_UNCACHABLE, 1);
2643f7018c21STomi Valkeinen 			if (par->mtrr_reg < 0) {
2644f7018c21STomi Valkeinen 				mtrr_del(par->mtrr_aper, 0, 0);
2645f7018c21STomi Valkeinen 				par->mtrr_aper = -1;
2646f7018c21STomi Valkeinen 			}
2647f7018c21STomi Valkeinen 		}
2648f7018c21STomi Valkeinen 	}
2649f7018c21STomi Valkeinen #endif
2650f7018c21STomi Valkeinen 
2651f7018c21STomi Valkeinen 	info->fbops = &atyfb_ops;
2652f7018c21STomi Valkeinen 	info->pseudo_palette = par->pseudo_palette;
2653f7018c21STomi Valkeinen 	info->flags = FBINFO_DEFAULT           |
2654f7018c21STomi Valkeinen 		      FBINFO_HWACCEL_IMAGEBLIT |
2655f7018c21STomi Valkeinen 		      FBINFO_HWACCEL_FILLRECT  |
2656f7018c21STomi Valkeinen 		      FBINFO_HWACCEL_COPYAREA  |
2657f7018c21STomi Valkeinen 		      FBINFO_HWACCEL_YPAN      |
2658f7018c21STomi Valkeinen 		      FBINFO_READS_FAST;
2659f7018c21STomi Valkeinen 
2660f7018c21STomi Valkeinen #ifdef CONFIG_PMAC_BACKLIGHT
2661f7018c21STomi Valkeinen 	if (M64_HAS(G3_PB_1_1) && of_machine_is_compatible("PowerBook1,1")) {
2662f7018c21STomi Valkeinen 		/*
2663f7018c21STomi Valkeinen 		 * these bits let the 101 powerbook
2664f7018c21STomi Valkeinen 		 * wake up from sleep -- paulus
2665f7018c21STomi Valkeinen 		 */
2666f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) |
2667f7018c21STomi Valkeinen 			   USE_F32KHZ | TRISTATE_MEM_EN, par);
2668f7018c21STomi Valkeinen 	} else
2669f7018c21STomi Valkeinen #endif
2670f7018c21STomi Valkeinen 	if (M64_HAS(MOBIL_BUS) && backlight) {
2671f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_BACKLIGHT
2672f7018c21STomi Valkeinen 		aty_bl_init(par);
2673f7018c21STomi Valkeinen #endif
2674f7018c21STomi Valkeinen 	}
2675f7018c21STomi Valkeinen 
2676f7018c21STomi Valkeinen 	memset(&var, 0, sizeof(var));
2677f7018c21STomi Valkeinen #ifdef CONFIG_PPC
2678f7018c21STomi Valkeinen 	if (machine_is(powermac)) {
2679f7018c21STomi Valkeinen 		/*
2680f7018c21STomi Valkeinen 		 * FIXME: The NVRAM stuff should be put in a Mac-specific file,
2681f7018c21STomi Valkeinen 		 *        as it applies to all Mac video cards
2682f7018c21STomi Valkeinen 		 */
2683f7018c21STomi Valkeinen 		if (mode) {
2684f7018c21STomi Valkeinen 			if (mac_find_mode(&var, info, mode, 8))
2685f7018c21STomi Valkeinen 				has_var = 1;
2686f7018c21STomi Valkeinen 		} else {
2687f7018c21STomi Valkeinen 			if (default_vmode == VMODE_CHOOSE) {
2688f7018c21STomi Valkeinen 				int sense;
2689f7018c21STomi Valkeinen 				if (M64_HAS(G3_PB_1024x768))
2690f7018c21STomi Valkeinen 					/* G3 PowerBook with 1024x768 LCD */
2691f7018c21STomi Valkeinen 					default_vmode = VMODE_1024_768_60;
2692f7018c21STomi Valkeinen 				else if (of_machine_is_compatible("iMac"))
2693f7018c21STomi Valkeinen 					default_vmode = VMODE_1024_768_75;
2694f7018c21STomi Valkeinen 				else if (of_machine_is_compatible("PowerBook2,1"))
2695f7018c21STomi Valkeinen 					/* iBook with 800x600 LCD */
2696f7018c21STomi Valkeinen 					default_vmode = VMODE_800_600_60;
2697f7018c21STomi Valkeinen 				else
2698f7018c21STomi Valkeinen 					default_vmode = VMODE_640_480_67;
2699f7018c21STomi Valkeinen 				sense = read_aty_sense(par);
2700f7018c21STomi Valkeinen 				PRINTKI("monitor sense=%x, mode %d\n",
2701f7018c21STomi Valkeinen 					sense,  mac_map_monitor_sense(sense));
2702f7018c21STomi Valkeinen 			}
2703f7018c21STomi Valkeinen 			if (default_vmode <= 0 || default_vmode > VMODE_MAX)
2704f7018c21STomi Valkeinen 				default_vmode = VMODE_640_480_60;
2705f7018c21STomi Valkeinen 			if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
2706f7018c21STomi Valkeinen 				default_cmode = CMODE_8;
2707f7018c21STomi Valkeinen 			if (!mac_vmode_to_var(default_vmode, default_cmode,
2708f7018c21STomi Valkeinen 					      &var))
2709f7018c21STomi Valkeinen 				has_var = 1;
2710f7018c21STomi Valkeinen 		}
2711f7018c21STomi Valkeinen 	}
2712f7018c21STomi Valkeinen 
2713f7018c21STomi Valkeinen #endif /* !CONFIG_PPC */
2714f7018c21STomi Valkeinen 
2715f7018c21STomi Valkeinen #if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
2716f7018c21STomi Valkeinen 	if (!atyfb_get_timings_from_lcd(par, &var))
2717f7018c21STomi Valkeinen 		has_var = 1;
2718f7018c21STomi Valkeinen #endif
2719f7018c21STomi Valkeinen 
2720f7018c21STomi Valkeinen 	if (mode && fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8))
2721f7018c21STomi Valkeinen 		has_var = 1;
2722f7018c21STomi Valkeinen 
2723f7018c21STomi Valkeinen 	if (!has_var)
2724f7018c21STomi Valkeinen 		var = default_var;
2725f7018c21STomi Valkeinen 
2726f7018c21STomi Valkeinen 	if (noaccel)
2727f7018c21STomi Valkeinen 		var.accel_flags &= ~FB_ACCELF_TEXT;
2728f7018c21STomi Valkeinen 	else
2729f7018c21STomi Valkeinen 		var.accel_flags |= FB_ACCELF_TEXT;
2730f7018c21STomi Valkeinen 
2731f7018c21STomi Valkeinen 	if (comp_sync != -1) {
2732f7018c21STomi Valkeinen 		if (!comp_sync)
2733f7018c21STomi Valkeinen 			var.sync &= ~FB_SYNC_COMP_HIGH_ACT;
2734f7018c21STomi Valkeinen 		else
2735f7018c21STomi Valkeinen 			var.sync |= FB_SYNC_COMP_HIGH_ACT;
2736f7018c21STomi Valkeinen 	}
2737f7018c21STomi Valkeinen 
2738f7018c21STomi Valkeinen 	if (var.yres == var.yres_virtual) {
2739f7018c21STomi Valkeinen 		u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2));
2740f7018c21STomi Valkeinen 		var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual;
2741f7018c21STomi Valkeinen 		if (var.yres_virtual < var.yres)
2742f7018c21STomi Valkeinen 			var.yres_virtual = var.yres;
2743f7018c21STomi Valkeinen 	}
2744f7018c21STomi Valkeinen 
2745f7018c21STomi Valkeinen 	ret = atyfb_check_var(&var, info);
2746f7018c21STomi Valkeinen 	if (ret) {
2747f7018c21STomi Valkeinen 		PRINTKE("can't set default video mode\n");
2748f7018c21STomi Valkeinen 		goto aty_init_exit;
2749f7018c21STomi Valkeinen 	}
2750f7018c21STomi Valkeinen 
2751f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
2752f7018c21STomi Valkeinen 	if (!noaccel && M64_HAS(INTEGRATED))
2753f7018c21STomi Valkeinen 		aty_init_cursor(info);
2754f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */
2755f7018c21STomi Valkeinen 	info->var = var;
2756f7018c21STomi Valkeinen 
2757f7018c21STomi Valkeinen 	ret = fb_alloc_cmap(&info->cmap, 256, 0);
2758f7018c21STomi Valkeinen 	if (ret < 0)
2759f7018c21STomi Valkeinen 		goto aty_init_exit;
2760f7018c21STomi Valkeinen 
2761f7018c21STomi Valkeinen 	ret = register_framebuffer(info);
2762f7018c21STomi Valkeinen 	if (ret < 0) {
2763f7018c21STomi Valkeinen 		fb_dealloc_cmap(&info->cmap);
2764f7018c21STomi Valkeinen 		goto aty_init_exit;
2765f7018c21STomi Valkeinen 	}
2766f7018c21STomi Valkeinen 
2767f7018c21STomi Valkeinen 	fb_list = info;
2768f7018c21STomi Valkeinen 
2769f7018c21STomi Valkeinen 	PRINTKI("fb%d: %s frame buffer device on %s\n",
2770f7018c21STomi Valkeinen 		info->node, info->fix.id, par->bus_type == ISA ? "ISA" : "PCI");
2771f7018c21STomi Valkeinen 	return 0;
2772f7018c21STomi Valkeinen 
2773f7018c21STomi Valkeinen aty_init_exit:
2774f7018c21STomi Valkeinen 	/* restore video mode */
2775f7018c21STomi Valkeinen 	aty_set_crtc(par, &par->saved_crtc);
2776f7018c21STomi Valkeinen 	par->pll_ops->set_pll(info, &par->saved_pll);
2777f7018c21STomi Valkeinen 
2778f7018c21STomi Valkeinen #ifdef CONFIG_MTRR
2779f7018c21STomi Valkeinen 	if (par->mtrr_reg >= 0) {
2780f7018c21STomi Valkeinen 		mtrr_del(par->mtrr_reg, 0, 0);
2781f7018c21STomi Valkeinen 		par->mtrr_reg = -1;
2782f7018c21STomi Valkeinen 	}
2783f7018c21STomi Valkeinen 	if (par->mtrr_aper >= 0) {
2784f7018c21STomi Valkeinen 		mtrr_del(par->mtrr_aper, 0, 0);
2785f7018c21STomi Valkeinen 		par->mtrr_aper = -1;
2786f7018c21STomi Valkeinen 	}
2787f7018c21STomi Valkeinen #endif
2788f7018c21STomi Valkeinen 	return ret;
2789f7018c21STomi Valkeinen }
2790f7018c21STomi Valkeinen 
2791f7018c21STomi Valkeinen #if defined(CONFIG_ATARI) && !defined(MODULE)
2792f7018c21STomi Valkeinen static int store_video_par(char *video_str, unsigned char m64_num)
2793f7018c21STomi Valkeinen {
2794f7018c21STomi Valkeinen 	char *p;
2795f7018c21STomi Valkeinen 	unsigned long vmembase, size, guiregbase;
2796f7018c21STomi Valkeinen 
2797f7018c21STomi Valkeinen 	PRINTKI("store_video_par() '%s' \n", video_str);
2798f7018c21STomi Valkeinen 
2799f7018c21STomi Valkeinen 	if (!(p = strsep(&video_str, ";")) || !*p)
2800f7018c21STomi Valkeinen 		goto mach64_invalid;
2801f7018c21STomi Valkeinen 	vmembase = simple_strtoul(p, NULL, 0);
2802f7018c21STomi Valkeinen 	if (!(p = strsep(&video_str, ";")) || !*p)
2803f7018c21STomi Valkeinen 		goto mach64_invalid;
2804f7018c21STomi Valkeinen 	size = simple_strtoul(p, NULL, 0);
2805f7018c21STomi Valkeinen 	if (!(p = strsep(&video_str, ";")) || !*p)
2806f7018c21STomi Valkeinen 		goto mach64_invalid;
2807f7018c21STomi Valkeinen 	guiregbase = simple_strtoul(p, NULL, 0);
2808f7018c21STomi Valkeinen 
2809f7018c21STomi Valkeinen 	phys_vmembase[m64_num] = vmembase;
2810f7018c21STomi Valkeinen 	phys_size[m64_num] = size;
2811f7018c21STomi Valkeinen 	phys_guiregbase[m64_num] = guiregbase;
2812f7018c21STomi Valkeinen 	PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size,
2813f7018c21STomi Valkeinen 		guiregbase);
2814f7018c21STomi Valkeinen 	return 0;
2815f7018c21STomi Valkeinen 
2816f7018c21STomi Valkeinen  mach64_invalid:
2817f7018c21STomi Valkeinen 	phys_vmembase[m64_num] = 0;
2818f7018c21STomi Valkeinen 	return -1;
2819f7018c21STomi Valkeinen }
2820f7018c21STomi Valkeinen #endif /* CONFIG_ATARI && !MODULE */
2821f7018c21STomi Valkeinen 
2822f7018c21STomi Valkeinen /*
2823f7018c21STomi Valkeinen  * Blank the display.
2824f7018c21STomi Valkeinen  */
2825f7018c21STomi Valkeinen 
2826f7018c21STomi Valkeinen static int atyfb_blank(int blank, struct fb_info *info)
2827f7018c21STomi Valkeinen {
2828f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
2829f7018c21STomi Valkeinen 	u32 gen_cntl;
2830f7018c21STomi Valkeinen 
2831f7018c21STomi Valkeinen 	if (par->lock_blank || par->asleep)
2832f7018c21STomi Valkeinen 		return 0;
2833f7018c21STomi Valkeinen 
2834f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
2835f7018c21STomi Valkeinen 	if (par->lcd_table && blank > FB_BLANK_NORMAL &&
2836f7018c21STomi Valkeinen 	    (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
2837f7018c21STomi Valkeinen 		u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2838f7018c21STomi Valkeinen 		pm &= ~PWR_BLON;
2839f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2840f7018c21STomi Valkeinen 	}
2841f7018c21STomi Valkeinen #endif
2842f7018c21STomi Valkeinen 
2843f7018c21STomi Valkeinen 	gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
2844f7018c21STomi Valkeinen 	gen_cntl &= ~0x400004c;
2845f7018c21STomi Valkeinen 	switch (blank) {
2846f7018c21STomi Valkeinen 	case FB_BLANK_UNBLANK:
2847f7018c21STomi Valkeinen 		break;
2848f7018c21STomi Valkeinen 	case FB_BLANK_NORMAL:
2849f7018c21STomi Valkeinen 		gen_cntl |= 0x4000040;
2850f7018c21STomi Valkeinen 		break;
2851f7018c21STomi Valkeinen 	case FB_BLANK_VSYNC_SUSPEND:
2852f7018c21STomi Valkeinen 		gen_cntl |= 0x4000048;
2853f7018c21STomi Valkeinen 		break;
2854f7018c21STomi Valkeinen 	case FB_BLANK_HSYNC_SUSPEND:
2855f7018c21STomi Valkeinen 		gen_cntl |= 0x4000044;
2856f7018c21STomi Valkeinen 		break;
2857f7018c21STomi Valkeinen 	case FB_BLANK_POWERDOWN:
2858f7018c21STomi Valkeinen 		gen_cntl |= 0x400004c;
2859f7018c21STomi Valkeinen 		break;
2860f7018c21STomi Valkeinen 	}
2861f7018c21STomi Valkeinen 	aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par);
2862f7018c21STomi Valkeinen 
2863f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
2864f7018c21STomi Valkeinen 	if (par->lcd_table && blank <= FB_BLANK_NORMAL &&
2865f7018c21STomi Valkeinen 	    (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
2866f7018c21STomi Valkeinen 		u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
2867f7018c21STomi Valkeinen 		pm |= PWR_BLON;
2868f7018c21STomi Valkeinen 		aty_st_lcd(POWER_MANAGEMENT, pm, par);
2869f7018c21STomi Valkeinen 	}
2870f7018c21STomi Valkeinen #endif
2871f7018c21STomi Valkeinen 
2872f7018c21STomi Valkeinen 	return 0;
2873f7018c21STomi Valkeinen }
2874f7018c21STomi Valkeinen 
2875f7018c21STomi Valkeinen static void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue,
2876f7018c21STomi Valkeinen 		       const struct atyfb_par *par)
2877f7018c21STomi Valkeinen {
2878f7018c21STomi Valkeinen 	aty_st_8(DAC_W_INDEX, regno, par);
2879f7018c21STomi Valkeinen 	aty_st_8(DAC_DATA, red, par);
2880f7018c21STomi Valkeinen 	aty_st_8(DAC_DATA, green, par);
2881f7018c21STomi Valkeinen 	aty_st_8(DAC_DATA, blue, par);
2882f7018c21STomi Valkeinen }
2883f7018c21STomi Valkeinen 
2884f7018c21STomi Valkeinen /*
2885f7018c21STomi Valkeinen  * Set a single color register. The values supplied are already
2886f7018c21STomi Valkeinen  * rounded down to the hardware's capabilities (according to the
2887f7018c21STomi Valkeinen  * entries in the var structure). Return != 0 for invalid regno.
2888f7018c21STomi Valkeinen  * !! 4 & 8 =  PSEUDO, > 8 = DIRECTCOLOR
2889f7018c21STomi Valkeinen  */
2890f7018c21STomi Valkeinen 
2891f7018c21STomi Valkeinen static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
2892f7018c21STomi Valkeinen 			   u_int transp, struct fb_info *info)
2893f7018c21STomi Valkeinen {
2894f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
2895f7018c21STomi Valkeinen 	int i, depth;
2896f7018c21STomi Valkeinen 	u32 *pal = info->pseudo_palette;
2897f7018c21STomi Valkeinen 
2898f7018c21STomi Valkeinen 	depth = info->var.bits_per_pixel;
2899f7018c21STomi Valkeinen 	if (depth == 16)
2900f7018c21STomi Valkeinen 		depth = (info->var.green.length == 5) ? 15 : 16;
2901f7018c21STomi Valkeinen 
2902f7018c21STomi Valkeinen 	if (par->asleep)
2903f7018c21STomi Valkeinen 		return 0;
2904f7018c21STomi Valkeinen 
2905f7018c21STomi Valkeinen 	if (regno > 255 ||
2906f7018c21STomi Valkeinen 	    (depth == 16 && regno > 63) ||
2907f7018c21STomi Valkeinen 	    (depth == 15 && regno > 31))
2908f7018c21STomi Valkeinen 		return 1;
2909f7018c21STomi Valkeinen 
2910f7018c21STomi Valkeinen 	red >>= 8;
2911f7018c21STomi Valkeinen 	green >>= 8;
2912f7018c21STomi Valkeinen 	blue >>= 8;
2913f7018c21STomi Valkeinen 
2914f7018c21STomi Valkeinen 	par->palette[regno].red = red;
2915f7018c21STomi Valkeinen 	par->palette[regno].green = green;
2916f7018c21STomi Valkeinen 	par->palette[regno].blue = blue;
2917f7018c21STomi Valkeinen 
2918f7018c21STomi Valkeinen 	if (regno < 16) {
2919f7018c21STomi Valkeinen 		switch (depth) {
2920f7018c21STomi Valkeinen 		case 15:
2921f7018c21STomi Valkeinen 			pal[regno] = (regno << 10) | (regno << 5) | regno;
2922f7018c21STomi Valkeinen 			break;
2923f7018c21STomi Valkeinen 		case 16:
2924f7018c21STomi Valkeinen 			pal[regno] = (regno << 11) | (regno << 5) | regno;
2925f7018c21STomi Valkeinen 			break;
2926f7018c21STomi Valkeinen 		case 24:
2927f7018c21STomi Valkeinen 			pal[regno] = (regno << 16) | (regno << 8) | regno;
2928f7018c21STomi Valkeinen 			break;
2929f7018c21STomi Valkeinen 		case 32:
2930f7018c21STomi Valkeinen 			i = (regno << 8) | regno;
2931f7018c21STomi Valkeinen 			pal[regno] = (i << 16) | i;
2932f7018c21STomi Valkeinen 			break;
2933f7018c21STomi Valkeinen 		}
2934f7018c21STomi Valkeinen 	}
2935f7018c21STomi Valkeinen 
2936f7018c21STomi Valkeinen 	i = aty_ld_8(DAC_CNTL, par) & 0xfc;
2937f7018c21STomi Valkeinen 	if (M64_HAS(EXTRA_BRIGHT))
2938f7018c21STomi Valkeinen 		i |= 0x2; /* DAC_CNTL | 0x2 turns off the extra brightness for gt */
2939f7018c21STomi Valkeinen 	aty_st_8(DAC_CNTL, i, par);
2940f7018c21STomi Valkeinen 	aty_st_8(DAC_MASK, 0xff, par);
2941f7018c21STomi Valkeinen 
2942f7018c21STomi Valkeinen 	if (M64_HAS(INTEGRATED)) {
2943f7018c21STomi Valkeinen 		if (depth == 16) {
2944f7018c21STomi Valkeinen 			if (regno < 32)
2945f7018c21STomi Valkeinen 				aty_st_pal(regno << 3, red,
2946f7018c21STomi Valkeinen 					   par->palette[regno << 1].green,
2947f7018c21STomi Valkeinen 					   blue, par);
2948f7018c21STomi Valkeinen 			red = par->palette[regno >> 1].red;
2949f7018c21STomi Valkeinen 			blue = par->palette[regno >> 1].blue;
2950f7018c21STomi Valkeinen 			regno <<= 2;
2951f7018c21STomi Valkeinen 		} else if (depth == 15) {
2952f7018c21STomi Valkeinen 			regno <<= 3;
2953f7018c21STomi Valkeinen 			for (i = 0; i < 8; i++)
2954f7018c21STomi Valkeinen 				aty_st_pal(regno + i, red, green, blue, par);
2955f7018c21STomi Valkeinen 		}
2956f7018c21STomi Valkeinen 	}
2957f7018c21STomi Valkeinen 	aty_st_pal(regno, red, green, blue, par);
2958f7018c21STomi Valkeinen 
2959f7018c21STomi Valkeinen 	return 0;
2960f7018c21STomi Valkeinen }
2961f7018c21STomi Valkeinen 
2962f7018c21STomi Valkeinen #ifdef CONFIG_PCI
2963f7018c21STomi Valkeinen 
2964f7018c21STomi Valkeinen #ifdef __sparc__
2965f7018c21STomi Valkeinen 
2966f7018c21STomi Valkeinen static int atyfb_setup_sparc(struct pci_dev *pdev, struct fb_info *info,
2967f7018c21STomi Valkeinen 			     unsigned long addr)
2968f7018c21STomi Valkeinen {
2969f7018c21STomi Valkeinen 	struct atyfb_par *par = info->par;
2970f7018c21STomi Valkeinen 	struct device_node *dp;
2971f7018c21STomi Valkeinen 	u32 mem, chip_id;
2972f7018c21STomi Valkeinen 	int i, j, ret;
2973f7018c21STomi Valkeinen 
2974f7018c21STomi Valkeinen 	/*
2975f7018c21STomi Valkeinen 	 * Map memory-mapped registers.
2976f7018c21STomi Valkeinen 	 */
2977f7018c21STomi Valkeinen 	par->ati_regbase = (void *)addr + 0x7ffc00UL;
2978f7018c21STomi Valkeinen 	info->fix.mmio_start = addr + 0x7ffc00UL;
2979f7018c21STomi Valkeinen 
2980f7018c21STomi Valkeinen 	/*
2981f7018c21STomi Valkeinen 	 * Map in big-endian aperture.
2982f7018c21STomi Valkeinen 	 */
2983f7018c21STomi Valkeinen 	info->screen_base = (char *) (addr + 0x800000UL);
2984f7018c21STomi Valkeinen 	info->fix.smem_start = addr + 0x800000UL;
2985f7018c21STomi Valkeinen 
2986f7018c21STomi Valkeinen 	/*
2987f7018c21STomi Valkeinen 	 * Figure mmap addresses from PCI config space.
2988f7018c21STomi Valkeinen 	 * Split Framebuffer in big- and little-endian halfs.
2989f7018c21STomi Valkeinen 	 */
2990f7018c21STomi Valkeinen 	for (i = 0; i < 6 && pdev->resource[i].start; i++)
2991f7018c21STomi Valkeinen 		/* nothing */ ;
2992f7018c21STomi Valkeinen 	j = i + 4;
2993f7018c21STomi Valkeinen 
2994f7018c21STomi Valkeinen 	par->mmap_map = kcalloc(j, sizeof(*par->mmap_map), GFP_ATOMIC);
2995f7018c21STomi Valkeinen 	if (!par->mmap_map) {
2996f7018c21STomi Valkeinen 		PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n");
2997f7018c21STomi Valkeinen 		return -ENOMEM;
2998f7018c21STomi Valkeinen 	}
2999f7018c21STomi Valkeinen 
3000f7018c21STomi Valkeinen 	for (i = 0, j = 2; i < 6 && pdev->resource[i].start; i++) {
3001f7018c21STomi Valkeinen 		struct resource *rp = &pdev->resource[i];
3002f7018c21STomi Valkeinen 		int io, breg = PCI_BASE_ADDRESS_0 + (i << 2);
3003f7018c21STomi Valkeinen 		unsigned long base;
3004f7018c21STomi Valkeinen 		u32 size, pbase;
3005f7018c21STomi Valkeinen 
3006f7018c21STomi Valkeinen 		base = rp->start;
3007f7018c21STomi Valkeinen 
3008f7018c21STomi Valkeinen 		io = (rp->flags & IORESOURCE_IO);
3009f7018c21STomi Valkeinen 
3010f7018c21STomi Valkeinen 		size = rp->end - base + 1;
3011f7018c21STomi Valkeinen 
3012f7018c21STomi Valkeinen 		pci_read_config_dword(pdev, breg, &pbase);
3013f7018c21STomi Valkeinen 
3014f7018c21STomi Valkeinen 		if (io)
3015f7018c21STomi Valkeinen 			size &= ~1;
3016f7018c21STomi Valkeinen 
3017f7018c21STomi Valkeinen 		/*
3018f7018c21STomi Valkeinen 		 * Map the framebuffer a second time, this time without
3019f7018c21STomi Valkeinen 		 * the braindead _PAGE_IE setting. This is used by the
3020f7018c21STomi Valkeinen 		 * fixed Xserver, but we need to maintain the old mapping
3021f7018c21STomi Valkeinen 		 * to stay compatible with older ones...
3022f7018c21STomi Valkeinen 		 */
3023f7018c21STomi Valkeinen 		if (base == addr) {
3024f7018c21STomi Valkeinen 			par->mmap_map[j].voff = (pbase + 0x10000000) & PAGE_MASK;
3025f7018c21STomi Valkeinen 			par->mmap_map[j].poff = base & PAGE_MASK;
3026f7018c21STomi Valkeinen 			par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK;
3027f7018c21STomi Valkeinen 			par->mmap_map[j].prot_mask = _PAGE_CACHE;
3028f7018c21STomi Valkeinen 			par->mmap_map[j].prot_flag = _PAGE_E;
3029f7018c21STomi Valkeinen 			j++;
3030f7018c21STomi Valkeinen 		}
3031f7018c21STomi Valkeinen 
3032f7018c21STomi Valkeinen 		/*
3033f7018c21STomi Valkeinen 		 * Here comes the old framebuffer mapping with _PAGE_IE
3034f7018c21STomi Valkeinen 		 * set for the big endian half of the framebuffer...
3035f7018c21STomi Valkeinen 		 */
3036f7018c21STomi Valkeinen 		if (base == addr) {
3037f7018c21STomi Valkeinen 			par->mmap_map[j].voff = (pbase + 0x800000) & PAGE_MASK;
3038f7018c21STomi Valkeinen 			par->mmap_map[j].poff = (base + 0x800000) & PAGE_MASK;
3039f7018c21STomi Valkeinen 			par->mmap_map[j].size = 0x800000;
3040f7018c21STomi Valkeinen 			par->mmap_map[j].prot_mask = _PAGE_CACHE;
3041f7018c21STomi Valkeinen 			par->mmap_map[j].prot_flag = _PAGE_E | _PAGE_IE;
3042f7018c21STomi Valkeinen 			size -= 0x800000;
3043f7018c21STomi Valkeinen 			j++;
3044f7018c21STomi Valkeinen 		}
3045f7018c21STomi Valkeinen 
3046f7018c21STomi Valkeinen 		par->mmap_map[j].voff = pbase & PAGE_MASK;
3047f7018c21STomi Valkeinen 		par->mmap_map[j].poff = base & PAGE_MASK;
3048f7018c21STomi Valkeinen 		par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK;
3049f7018c21STomi Valkeinen 		par->mmap_map[j].prot_mask = _PAGE_CACHE;
3050f7018c21STomi Valkeinen 		par->mmap_map[j].prot_flag = _PAGE_E;
3051f7018c21STomi Valkeinen 		j++;
3052f7018c21STomi Valkeinen 	}
3053f7018c21STomi Valkeinen 
3054f7018c21STomi Valkeinen 	ret = correct_chipset(par);
3055f7018c21STomi Valkeinen 	if (ret)
3056f7018c21STomi Valkeinen 		return ret;
3057f7018c21STomi Valkeinen 
3058f7018c21STomi Valkeinen 	if (IS_XL(pdev->device)) {
3059f7018c21STomi Valkeinen 		/*
3060f7018c21STomi Valkeinen 		 * Fix PROMs idea of MEM_CNTL settings...
3061f7018c21STomi Valkeinen 		 */
3062f7018c21STomi Valkeinen 		mem = aty_ld_le32(MEM_CNTL, par);
3063f7018c21STomi Valkeinen 		chip_id = aty_ld_le32(CNFG_CHIP_ID, par);
3064f7018c21STomi Valkeinen 		if (((chip_id & CFG_CHIP_TYPE) == VT_CHIP_ID) && !((chip_id >> 24) & 1)) {
3065f7018c21STomi Valkeinen 			switch (mem & 0x0f) {
3066f7018c21STomi Valkeinen 			case 3:
3067f7018c21STomi Valkeinen 				mem = (mem & ~(0x0f)) | 2;
3068f7018c21STomi Valkeinen 				break;
3069f7018c21STomi Valkeinen 			case 7:
3070f7018c21STomi Valkeinen 				mem = (mem & ~(0x0f)) | 3;
3071f7018c21STomi Valkeinen 				break;
3072f7018c21STomi Valkeinen 			case 9:
3073f7018c21STomi Valkeinen 				mem = (mem & ~(0x0f)) | 4;
3074f7018c21STomi Valkeinen 				break;
3075f7018c21STomi Valkeinen 			case 11:
3076f7018c21STomi Valkeinen 				mem = (mem & ~(0x0f)) | 5;
3077f7018c21STomi Valkeinen 				break;
3078f7018c21STomi Valkeinen 			default:
3079f7018c21STomi Valkeinen 				break;
3080f7018c21STomi Valkeinen 			}
3081f7018c21STomi Valkeinen 			if ((aty_ld_le32(CNFG_STAT0, par) & 7) >= SDRAM)
3082f7018c21STomi Valkeinen 				mem &= ~(0x00700000);
3083f7018c21STomi Valkeinen 		}
3084f7018c21STomi Valkeinen 		mem &= ~(0xcf80e000);	/* Turn off all undocumented bits. */
3085f7018c21STomi Valkeinen 		aty_st_le32(MEM_CNTL, mem, par);
3086f7018c21STomi Valkeinen 	}
3087f7018c21STomi Valkeinen 
3088f7018c21STomi Valkeinen 	dp = pci_device_to_OF_node(pdev);
3089f7018c21STomi Valkeinen 	if (dp == of_console_device) {
3090f7018c21STomi Valkeinen 		struct fb_var_screeninfo *var = &default_var;
3091f7018c21STomi Valkeinen 		unsigned int N, P, Q, M, T, R;
3092f7018c21STomi Valkeinen 		u32 v_total, h_total;
3093f7018c21STomi Valkeinen 		struct crtc crtc;
3094f7018c21STomi Valkeinen 		u8 pll_regs[16];
3095f7018c21STomi Valkeinen 		u8 clock_cntl;
3096f7018c21STomi Valkeinen 
3097f7018c21STomi Valkeinen 		crtc.vxres = of_getintprop_default(dp, "width", 1024);
3098f7018c21STomi Valkeinen 		crtc.vyres = of_getintprop_default(dp, "height", 768);
3099f7018c21STomi Valkeinen 		var->bits_per_pixel = of_getintprop_default(dp, "depth", 8);
3100f7018c21STomi Valkeinen 		var->xoffset = var->yoffset = 0;
3101f7018c21STomi Valkeinen 		crtc.h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
3102f7018c21STomi Valkeinen 		crtc.h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
3103f7018c21STomi Valkeinen 		crtc.v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
3104f7018c21STomi Valkeinen 		crtc.v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
3105f7018c21STomi Valkeinen 		crtc.gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
3106f7018c21STomi Valkeinen 		aty_crtc_to_var(&crtc, var);
3107f7018c21STomi Valkeinen 
3108f7018c21STomi Valkeinen 		h_total = var->xres + var->right_margin + var->hsync_len + var->left_margin;
3109f7018c21STomi Valkeinen 		v_total = var->yres + var->lower_margin + var->vsync_len + var->upper_margin;
3110f7018c21STomi Valkeinen 
3111f7018c21STomi Valkeinen 		/*
3112f7018c21STomi Valkeinen 		 * Read the PLL to figure actual Refresh Rate.
3113f7018c21STomi Valkeinen 		 */
3114f7018c21STomi Valkeinen 		clock_cntl = aty_ld_8(CLOCK_CNTL, par);
3115f7018c21STomi Valkeinen 		/* DPRINTK("CLOCK_CNTL %02x\n", clock_cntl); */
3116f7018c21STomi Valkeinen 		for (i = 0; i < 16; i++)
3117f7018c21STomi Valkeinen 			pll_regs[i] = aty_ld_pll_ct(i, par);
3118f7018c21STomi Valkeinen 
3119f7018c21STomi Valkeinen 		/*
3120f7018c21STomi Valkeinen 		 * PLL Reference Divider M:
3121f7018c21STomi Valkeinen 		 */
3122f7018c21STomi Valkeinen 		M = pll_regs[2];
3123f7018c21STomi Valkeinen 
3124f7018c21STomi Valkeinen 		/*
3125f7018c21STomi Valkeinen 		 * PLL Feedback Divider N (Dependent on CLOCK_CNTL):
3126f7018c21STomi Valkeinen 		 */
3127f7018c21STomi Valkeinen 		N = pll_regs[7 + (clock_cntl & 3)];
3128f7018c21STomi Valkeinen 
3129f7018c21STomi Valkeinen 		/*
3130f7018c21STomi Valkeinen 		 * PLL Post Divider P (Dependent on CLOCK_CNTL):
3131f7018c21STomi Valkeinen 		 */
3132f7018c21STomi Valkeinen 		P = 1 << (pll_regs[6] >> ((clock_cntl & 3) << 1));
3133f7018c21STomi Valkeinen 
3134f7018c21STomi Valkeinen 		/*
3135f7018c21STomi Valkeinen 		 * PLL Divider Q:
3136f7018c21STomi Valkeinen 		 */
3137f7018c21STomi Valkeinen 		Q = N / P;
3138f7018c21STomi Valkeinen 
3139f7018c21STomi Valkeinen 		/*
3140f7018c21STomi Valkeinen 		 * Target Frequency:
3141f7018c21STomi Valkeinen 		 *
3142f7018c21STomi Valkeinen 		 *      T * M
3143f7018c21STomi Valkeinen 		 * Q = -------
3144f7018c21STomi Valkeinen 		 *      2 * R
3145f7018c21STomi Valkeinen 		 *
3146f7018c21STomi Valkeinen 		 * where R is XTALIN (= 14318 or 29498 kHz).
3147f7018c21STomi Valkeinen 		 */
3148f7018c21STomi Valkeinen 		if (IS_XL(pdev->device))
3149f7018c21STomi Valkeinen 			R = 29498;
3150f7018c21STomi Valkeinen 		else
3151f7018c21STomi Valkeinen 			R = 14318;
3152f7018c21STomi Valkeinen 
3153f7018c21STomi Valkeinen 		T = 2 * Q * R / M;
3154f7018c21STomi Valkeinen 
3155f7018c21STomi Valkeinen 		default_var.pixclock = 1000000000 / T;
3156f7018c21STomi Valkeinen 	}
3157f7018c21STomi Valkeinen 
3158f7018c21STomi Valkeinen 	return 0;
3159f7018c21STomi Valkeinen }
3160f7018c21STomi Valkeinen 
3161f7018c21STomi Valkeinen #else /* __sparc__ */
3162f7018c21STomi Valkeinen 
3163f7018c21STomi Valkeinen #ifdef __i386__
3164f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
3165f7018c21STomi Valkeinen static void aty_init_lcd(struct atyfb_par *par, u32 bios_base)
3166f7018c21STomi Valkeinen {
3167f7018c21STomi Valkeinen 	u32 driv_inf_tab, sig;
3168f7018c21STomi Valkeinen 	u16 lcd_ofs;
3169f7018c21STomi Valkeinen 
3170f7018c21STomi Valkeinen 	/*
3171f7018c21STomi Valkeinen 	 * To support an LCD panel, we should know it's dimensions and
3172f7018c21STomi Valkeinen 	 *  it's desired pixel clock.
3173f7018c21STomi Valkeinen 	 * There are two ways to do it:
3174f7018c21STomi Valkeinen 	 *  - Check the startup video mode and calculate the panel
3175f7018c21STomi Valkeinen 	 *    size from it. This is unreliable.
3176f7018c21STomi Valkeinen 	 *  - Read it from the driver information table in the video BIOS.
3177f7018c21STomi Valkeinen 	 */
3178f7018c21STomi Valkeinen 	/* Address of driver information table is at offset 0x78. */
3179f7018c21STomi Valkeinen 	driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78));
3180f7018c21STomi Valkeinen 
3181f7018c21STomi Valkeinen 	/* Check for the driver information table signature. */
3182f7018c21STomi Valkeinen 	sig = *(u32 *)driv_inf_tab;
3183f7018c21STomi Valkeinen 	if ((sig == 0x54504c24) || /* Rage LT pro */
3184f7018c21STomi Valkeinen 	    (sig == 0x544d5224) || /* Rage mobility */
3185f7018c21STomi Valkeinen 	    (sig == 0x54435824) || /* Rage XC */
3186f7018c21STomi Valkeinen 	    (sig == 0x544c5824)) { /* Rage XL */
3187f7018c21STomi Valkeinen 		PRINTKI("BIOS contains driver information table.\n");
3188f7018c21STomi Valkeinen 		lcd_ofs = *(u16 *)(driv_inf_tab + 10);
3189f7018c21STomi Valkeinen 		par->lcd_table = 0;
3190f7018c21STomi Valkeinen 		if (lcd_ofs != 0)
3191f7018c21STomi Valkeinen 			par->lcd_table = bios_base + lcd_ofs;
3192f7018c21STomi Valkeinen 	}
3193f7018c21STomi Valkeinen 
3194f7018c21STomi Valkeinen 	if (par->lcd_table != 0) {
3195f7018c21STomi Valkeinen 		char model[24];
3196f7018c21STomi Valkeinen 		char strbuf[16];
3197f7018c21STomi Valkeinen 		char refresh_rates_buf[100];
3198f7018c21STomi Valkeinen 		int id, tech, f, i, m, default_refresh_rate;
3199f7018c21STomi Valkeinen 		char *txtcolour;
3200f7018c21STomi Valkeinen 		char *txtmonitor;
3201f7018c21STomi Valkeinen 		char *txtdual;
3202f7018c21STomi Valkeinen 		char *txtformat;
3203f7018c21STomi Valkeinen 		u16 width, height, panel_type, refresh_rates;
3204f7018c21STomi Valkeinen 		u16 *lcdmodeptr;
3205f7018c21STomi Valkeinen 		u32 format;
3206f7018c21STomi Valkeinen 		u8 lcd_refresh_rates[16] = { 50, 56, 60, 67, 70, 72, 75, 76, 85,
3207f7018c21STomi Valkeinen 					     90, 100, 120, 140, 150, 160, 200 };
3208f7018c21STomi Valkeinen 		/*
3209f7018c21STomi Valkeinen 		 * The most important information is the panel size at
3210f7018c21STomi Valkeinen 		 * offset 25 and 27, but there's some other nice information
3211f7018c21STomi Valkeinen 		 * which we print to the screen.
3212f7018c21STomi Valkeinen 		 */
3213f7018c21STomi Valkeinen 		id = *(u8 *)par->lcd_table;
3214f7018c21STomi Valkeinen 		strncpy(model, (char *)par->lcd_table+1, 24);
3215f7018c21STomi Valkeinen 		model[23] = 0;
3216f7018c21STomi Valkeinen 
3217f7018c21STomi Valkeinen 		width = par->lcd_width = *(u16 *)(par->lcd_table+25);
3218f7018c21STomi Valkeinen 		height = par->lcd_height = *(u16 *)(par->lcd_table+27);
3219f7018c21STomi Valkeinen 		panel_type = *(u16 *)(par->lcd_table+29);
3220f7018c21STomi Valkeinen 		if (panel_type & 1)
3221f7018c21STomi Valkeinen 			txtcolour = "colour";
3222f7018c21STomi Valkeinen 		else
3223f7018c21STomi Valkeinen 			txtcolour = "monochrome";
3224f7018c21STomi Valkeinen 		if (panel_type & 2)
3225f7018c21STomi Valkeinen 			txtdual = "dual (split) ";
3226f7018c21STomi Valkeinen 		else
3227f7018c21STomi Valkeinen 			txtdual = "";
3228f7018c21STomi Valkeinen 		tech = (panel_type >> 2) & 63;
3229f7018c21STomi Valkeinen 		switch (tech) {
3230f7018c21STomi Valkeinen 		case 0:
3231f7018c21STomi Valkeinen 			txtmonitor = "passive matrix";
3232f7018c21STomi Valkeinen 			break;
3233f7018c21STomi Valkeinen 		case 1:
3234f7018c21STomi Valkeinen 			txtmonitor = "active matrix";
3235f7018c21STomi Valkeinen 			break;
3236f7018c21STomi Valkeinen 		case 2:
3237f7018c21STomi Valkeinen 			txtmonitor = "active addressed STN";
3238f7018c21STomi Valkeinen 			break;
3239f7018c21STomi Valkeinen 		case 3:
3240f7018c21STomi Valkeinen 			txtmonitor = "EL";
3241f7018c21STomi Valkeinen 			break;
3242f7018c21STomi Valkeinen 		case 4:
3243f7018c21STomi Valkeinen 			txtmonitor = "plasma";
3244f7018c21STomi Valkeinen 			break;
3245f7018c21STomi Valkeinen 		default:
3246f7018c21STomi Valkeinen 			txtmonitor = "unknown";
3247f7018c21STomi Valkeinen 		}
3248f7018c21STomi Valkeinen 		format = *(u32 *)(par->lcd_table+57);
3249f7018c21STomi Valkeinen 		if (tech == 0 || tech == 2) {
3250f7018c21STomi Valkeinen 			switch (format & 7) {
3251f7018c21STomi Valkeinen 			case 0:
3252f7018c21STomi Valkeinen 				txtformat = "12 bit interface";
3253f7018c21STomi Valkeinen 				break;
3254f7018c21STomi Valkeinen 			case 1:
3255f7018c21STomi Valkeinen 				txtformat = "16 bit interface";
3256f7018c21STomi Valkeinen 				break;
3257f7018c21STomi Valkeinen 			case 2:
3258f7018c21STomi Valkeinen 				txtformat = "24 bit interface";
3259f7018c21STomi Valkeinen 				break;
3260f7018c21STomi Valkeinen 			default:
3261f7018c21STomi Valkeinen 				txtformat = "unknown format";
3262f7018c21STomi Valkeinen 			}
3263f7018c21STomi Valkeinen 		} else {
3264f7018c21STomi Valkeinen 			switch (format & 7) {
3265f7018c21STomi Valkeinen 			case 0:
3266f7018c21STomi Valkeinen 				txtformat = "8 colours";
3267f7018c21STomi Valkeinen 				break;
3268f7018c21STomi Valkeinen 			case 1:
3269f7018c21STomi Valkeinen 				txtformat = "512 colours";
3270f7018c21STomi Valkeinen 				break;
3271f7018c21STomi Valkeinen 			case 2:
3272f7018c21STomi Valkeinen 				txtformat = "4096 colours";
3273f7018c21STomi Valkeinen 				break;
3274f7018c21STomi Valkeinen 			case 4:
3275f7018c21STomi Valkeinen 				txtformat = "262144 colours (LT mode)";
3276f7018c21STomi Valkeinen 				break;
3277f7018c21STomi Valkeinen 			case 5:
3278f7018c21STomi Valkeinen 				txtformat = "16777216 colours";
3279f7018c21STomi Valkeinen 				break;
3280f7018c21STomi Valkeinen 			case 6:
3281f7018c21STomi Valkeinen 				txtformat = "262144 colours (FDPI-2 mode)";
3282f7018c21STomi Valkeinen 				break;
3283f7018c21STomi Valkeinen 			default:
3284f7018c21STomi Valkeinen 				txtformat = "unknown format";
3285f7018c21STomi Valkeinen 			}
3286f7018c21STomi Valkeinen 		}
3287f7018c21STomi Valkeinen 		PRINTKI("%s%s %s monitor detected: %s\n",
3288f7018c21STomi Valkeinen 			txtdual, txtcolour, txtmonitor, model);
3289f7018c21STomi Valkeinen 		PRINTKI("       id=%d, %dx%d pixels, %s\n",
3290f7018c21STomi Valkeinen 			id, width, height, txtformat);
3291f7018c21STomi Valkeinen 		refresh_rates_buf[0] = 0;
3292f7018c21STomi Valkeinen 		refresh_rates = *(u16 *)(par->lcd_table+62);
3293f7018c21STomi Valkeinen 		m = 1;
3294f7018c21STomi Valkeinen 		f = 0;
3295f7018c21STomi Valkeinen 		for (i = 0; i < 16; i++) {
3296f7018c21STomi Valkeinen 			if (refresh_rates & m) {
3297f7018c21STomi Valkeinen 				if (f == 0) {
3298f7018c21STomi Valkeinen 					sprintf(strbuf, "%d",
3299f7018c21STomi Valkeinen 						lcd_refresh_rates[i]);
3300f7018c21STomi Valkeinen 					f++;
3301f7018c21STomi Valkeinen 				} else {
3302f7018c21STomi Valkeinen 					sprintf(strbuf, ",%d",
3303f7018c21STomi Valkeinen 						lcd_refresh_rates[i]);
3304f7018c21STomi Valkeinen 				}
3305f7018c21STomi Valkeinen 				strcat(refresh_rates_buf, strbuf);
3306f7018c21STomi Valkeinen 			}
3307f7018c21STomi Valkeinen 			m = m << 1;
3308f7018c21STomi Valkeinen 		}
3309f7018c21STomi Valkeinen 		default_refresh_rate = (*(u8 *)(par->lcd_table+61) & 0xf0) >> 4;
3310f7018c21STomi Valkeinen 		PRINTKI("       supports refresh rates [%s], default %d Hz\n",
3311f7018c21STomi Valkeinen 			refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]);
3312f7018c21STomi Valkeinen 		par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate];
3313f7018c21STomi Valkeinen 		/*
3314f7018c21STomi Valkeinen 		 * We now need to determine the crtc parameters for the
3315f7018c21STomi Valkeinen 		 * LCD monitor. This is tricky, because they are not stored
3316f7018c21STomi Valkeinen 		 * individually in the BIOS. Instead, the BIOS contains a
3317f7018c21STomi Valkeinen 		 * table of display modes that work for this monitor.
3318f7018c21STomi Valkeinen 		 *
3319f7018c21STomi Valkeinen 		 * The idea is that we search for a mode of the same dimensions
3320f7018c21STomi Valkeinen 		 * as the dimensions of the LCD monitor. Say our LCD monitor
3321f7018c21STomi Valkeinen 		 * is 800x600 pixels, we search for a 800x600 monitor.
3322f7018c21STomi Valkeinen 		 * The CRTC parameters we find here are the ones that we need
3323f7018c21STomi Valkeinen 		 * to use to simulate other resolutions on the LCD screen.
3324f7018c21STomi Valkeinen 		 */
3325f7018c21STomi Valkeinen 		lcdmodeptr = (u16 *)(par->lcd_table + 64);
3326f7018c21STomi Valkeinen 		while (*lcdmodeptr != 0) {
3327f7018c21STomi Valkeinen 			u32 modeptr;
3328f7018c21STomi Valkeinen 			u16 mwidth, mheight, lcd_hsync_start, lcd_vsync_start;
3329f7018c21STomi Valkeinen 			modeptr = bios_base + *lcdmodeptr;
3330f7018c21STomi Valkeinen 
3331f7018c21STomi Valkeinen 			mwidth = *((u16 *)(modeptr+0));
3332f7018c21STomi Valkeinen 			mheight = *((u16 *)(modeptr+2));
3333f7018c21STomi Valkeinen 
3334f7018c21STomi Valkeinen 			if (mwidth == width && mheight == height) {
3335f7018c21STomi Valkeinen 				par->lcd_pixclock = 100000000 / *((u16 *)(modeptr+9));
3336f7018c21STomi Valkeinen 				par->lcd_htotal = *((u16 *)(modeptr+17)) & 511;
3337f7018c21STomi Valkeinen 				par->lcd_hdisp = *((u16 *)(modeptr+19)) & 511;
3338f7018c21STomi Valkeinen 				lcd_hsync_start = *((u16 *)(modeptr+21)) & 511;
3339f7018c21STomi Valkeinen 				par->lcd_hsync_dly = (*((u16 *)(modeptr+21)) >> 9) & 7;
3340f7018c21STomi Valkeinen 				par->lcd_hsync_len = *((u8 *)(modeptr+23)) & 63;
3341f7018c21STomi Valkeinen 
3342f7018c21STomi Valkeinen 				par->lcd_vtotal = *((u16 *)(modeptr+24)) & 2047;
3343f7018c21STomi Valkeinen 				par->lcd_vdisp = *((u16 *)(modeptr+26)) & 2047;
3344f7018c21STomi Valkeinen 				lcd_vsync_start = *((u16 *)(modeptr+28)) & 2047;
3345f7018c21STomi Valkeinen 				par->lcd_vsync_len = (*((u16 *)(modeptr+28)) >> 11) & 31;
3346f7018c21STomi Valkeinen 
3347f7018c21STomi Valkeinen 				par->lcd_htotal = (par->lcd_htotal + 1) * 8;
3348f7018c21STomi Valkeinen 				par->lcd_hdisp = (par->lcd_hdisp + 1) * 8;
3349f7018c21STomi Valkeinen 				lcd_hsync_start = (lcd_hsync_start + 1) * 8;
3350f7018c21STomi Valkeinen 				par->lcd_hsync_len = par->lcd_hsync_len * 8;
3351f7018c21STomi Valkeinen 
3352f7018c21STomi Valkeinen 				par->lcd_vtotal++;
3353f7018c21STomi Valkeinen 				par->lcd_vdisp++;
3354f7018c21STomi Valkeinen 				lcd_vsync_start++;
3355f7018c21STomi Valkeinen 
3356f7018c21STomi Valkeinen 				par->lcd_right_margin = lcd_hsync_start - par->lcd_hdisp;
3357f7018c21STomi Valkeinen 				par->lcd_lower_margin = lcd_vsync_start - par->lcd_vdisp;
3358f7018c21STomi Valkeinen 				par->lcd_hblank_len = par->lcd_htotal - par->lcd_hdisp;
3359f7018c21STomi Valkeinen 				par->lcd_vblank_len = par->lcd_vtotal - par->lcd_vdisp;
3360f7018c21STomi Valkeinen 				break;
3361f7018c21STomi Valkeinen 			}
3362f7018c21STomi Valkeinen 
3363f7018c21STomi Valkeinen 			lcdmodeptr++;
3364f7018c21STomi Valkeinen 		}
3365f7018c21STomi Valkeinen 		if (*lcdmodeptr == 0) {
3366f7018c21STomi Valkeinen 			PRINTKE("LCD monitor CRTC parameters not found!!!\n");
3367f7018c21STomi Valkeinen 			/* To do: Switch to CRT if possible. */
3368f7018c21STomi Valkeinen 		} else {
3369f7018c21STomi Valkeinen 			PRINTKI("       LCD CRTC parameters: %d.%d  %d %d %d %d  %d %d %d %d\n",
3370f7018c21STomi Valkeinen 				1000000 / par->lcd_pixclock, 1000000 % par->lcd_pixclock,
3371f7018c21STomi Valkeinen 				par->lcd_hdisp,
3372f7018c21STomi Valkeinen 				par->lcd_hdisp + par->lcd_right_margin,
3373f7018c21STomi Valkeinen 				par->lcd_hdisp + par->lcd_right_margin
3374f7018c21STomi Valkeinen 					+ par->lcd_hsync_dly + par->lcd_hsync_len,
3375f7018c21STomi Valkeinen 				par->lcd_htotal,
3376f7018c21STomi Valkeinen 				par->lcd_vdisp,
3377f7018c21STomi Valkeinen 				par->lcd_vdisp + par->lcd_lower_margin,
3378f7018c21STomi Valkeinen 				par->lcd_vdisp + par->lcd_lower_margin + par->lcd_vsync_len,
3379f7018c21STomi Valkeinen 				par->lcd_vtotal);
3380f7018c21STomi Valkeinen 			PRINTKI("                          : %d %d %d %d %d %d %d %d %d\n",
3381f7018c21STomi Valkeinen 				par->lcd_pixclock,
3382f7018c21STomi Valkeinen 				par->lcd_hblank_len - (par->lcd_right_margin +
3383f7018c21STomi Valkeinen 					par->lcd_hsync_dly + par->lcd_hsync_len),
3384f7018c21STomi Valkeinen 				par->lcd_hdisp,
3385f7018c21STomi Valkeinen 				par->lcd_right_margin,
3386f7018c21STomi Valkeinen 				par->lcd_hsync_len,
3387f7018c21STomi Valkeinen 				par->lcd_vblank_len - (par->lcd_lower_margin + par->lcd_vsync_len),
3388f7018c21STomi Valkeinen 				par->lcd_vdisp,
3389f7018c21STomi Valkeinen 				par->lcd_lower_margin,
3390f7018c21STomi Valkeinen 				par->lcd_vsync_len);
3391f7018c21STomi Valkeinen 		}
3392f7018c21STomi Valkeinen 	}
3393f7018c21STomi Valkeinen }
3394f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GENERIC_LCD */
3395f7018c21STomi Valkeinen 
3396f7018c21STomi Valkeinen static int init_from_bios(struct atyfb_par *par)
3397f7018c21STomi Valkeinen {
3398f7018c21STomi Valkeinen 	u32 bios_base, rom_addr;
3399f7018c21STomi Valkeinen 	int ret;
3400f7018c21STomi Valkeinen 
3401f7018c21STomi Valkeinen 	rom_addr = 0xc0000 + ((aty_ld_le32(SCRATCH_REG1, par) & 0x7f) << 11);
3402f7018c21STomi Valkeinen 	bios_base = (unsigned long)ioremap(rom_addr, 0x10000);
3403f7018c21STomi Valkeinen 
3404f7018c21STomi Valkeinen 	/* The BIOS starts with 0xaa55. */
3405f7018c21STomi Valkeinen 	if (*((u16 *)bios_base) == 0xaa55) {
3406f7018c21STomi Valkeinen 
3407f7018c21STomi Valkeinen 		u8 *bios_ptr;
3408f7018c21STomi Valkeinen 		u16 rom_table_offset, freq_table_offset;
3409f7018c21STomi Valkeinen 		PLL_BLOCK_MACH64 pll_block;
3410f7018c21STomi Valkeinen 
3411f7018c21STomi Valkeinen 		PRINTKI("Mach64 BIOS is located at %x, mapped at %x.\n", rom_addr, bios_base);
3412f7018c21STomi Valkeinen 
3413f7018c21STomi Valkeinen 		/* check for frequncy table */
3414f7018c21STomi Valkeinen 		bios_ptr = (u8*)bios_base;
3415f7018c21STomi Valkeinen 		rom_table_offset = (u16)(bios_ptr[0x48] | (bios_ptr[0x49] << 8));
3416f7018c21STomi Valkeinen 		freq_table_offset = bios_ptr[rom_table_offset + 16] | (bios_ptr[rom_table_offset + 17] << 8);
3417f7018c21STomi Valkeinen 		memcpy(&pll_block, bios_ptr + freq_table_offset, sizeof(PLL_BLOCK_MACH64));
3418f7018c21STomi Valkeinen 
3419f7018c21STomi Valkeinen 		PRINTKI("BIOS frequency table:\n");
3420f7018c21STomi Valkeinen 		PRINTKI("PCLK_min_freq %d, PCLK_max_freq %d, ref_freq %d, ref_divider %d\n",
3421f7018c21STomi Valkeinen 			pll_block.PCLK_min_freq, pll_block.PCLK_max_freq,
3422f7018c21STomi Valkeinen 			pll_block.ref_freq, pll_block.ref_divider);
3423f7018c21STomi Valkeinen 		PRINTKI("MCLK_pwd %d, MCLK_max_freq %d, XCLK_max_freq %d, SCLK_freq %d\n",
3424f7018c21STomi Valkeinen 			pll_block.MCLK_pwd, pll_block.MCLK_max_freq,
3425f7018c21STomi Valkeinen 			pll_block.XCLK_max_freq, pll_block.SCLK_freq);
3426f7018c21STomi Valkeinen 
3427f7018c21STomi Valkeinen 		par->pll_limits.pll_min = pll_block.PCLK_min_freq/100;
3428f7018c21STomi Valkeinen 		par->pll_limits.pll_max = pll_block.PCLK_max_freq/100;
3429f7018c21STomi Valkeinen 		par->pll_limits.ref_clk = pll_block.ref_freq/100;
3430f7018c21STomi Valkeinen 		par->pll_limits.ref_div = pll_block.ref_divider;
3431f7018c21STomi Valkeinen 		par->pll_limits.sclk = pll_block.SCLK_freq/100;
3432f7018c21STomi Valkeinen 		par->pll_limits.mclk = pll_block.MCLK_max_freq/100;
3433f7018c21STomi Valkeinen 		par->pll_limits.mclk_pm = pll_block.MCLK_pwd/100;
3434f7018c21STomi Valkeinen 		par->pll_limits.xclk = pll_block.XCLK_max_freq/100;
3435f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GENERIC_LCD
3436f7018c21STomi Valkeinen 		aty_init_lcd(par, bios_base);
3437f7018c21STomi Valkeinen #endif
3438f7018c21STomi Valkeinen 		ret = 0;
3439f7018c21STomi Valkeinen 	} else {
3440f7018c21STomi Valkeinen 		PRINTKE("no BIOS frequency table found, use parameters\n");
3441f7018c21STomi Valkeinen 		ret = -ENXIO;
3442f7018c21STomi Valkeinen 	}
3443f7018c21STomi Valkeinen 	iounmap((void __iomem *)bios_base);
3444f7018c21STomi Valkeinen 
3445f7018c21STomi Valkeinen 	return ret;
3446f7018c21STomi Valkeinen }
3447f7018c21STomi Valkeinen #endif /* __i386__ */
3448f7018c21STomi Valkeinen 
3449f7018c21STomi Valkeinen static int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info,
3450f7018c21STomi Valkeinen 			       unsigned long addr)
3451f7018c21STomi Valkeinen {
3452f7018c21STomi Valkeinen 	struct atyfb_par *par = info->par;
3453f7018c21STomi Valkeinen 	u16 tmp;
3454f7018c21STomi Valkeinen 	unsigned long raddr;
3455f7018c21STomi Valkeinen 	struct resource *rrp;
3456f7018c21STomi Valkeinen 	int ret = 0;
3457f7018c21STomi Valkeinen 
3458f7018c21STomi Valkeinen 	raddr = addr + 0x7ff000UL;
3459f7018c21STomi Valkeinen 	rrp = &pdev->resource[2];
3460f7018c21STomi Valkeinen 	if ((rrp->flags & IORESOURCE_MEM) &&
3461f7018c21STomi Valkeinen 	    request_mem_region(rrp->start, resource_size(rrp), "atyfb")) {
3462f7018c21STomi Valkeinen 		par->aux_start = rrp->start;
3463f7018c21STomi Valkeinen 		par->aux_size = resource_size(rrp);
3464f7018c21STomi Valkeinen 		raddr = rrp->start;
3465f7018c21STomi Valkeinen 		PRINTKI("using auxiliary register aperture\n");
3466f7018c21STomi Valkeinen 	}
3467f7018c21STomi Valkeinen 
3468f7018c21STomi Valkeinen 	info->fix.mmio_start = raddr;
3469f7018c21STomi Valkeinen 	par->ati_regbase = ioremap(info->fix.mmio_start, 0x1000);
3470f7018c21STomi Valkeinen 	if (par->ati_regbase == NULL)
3471f7018c21STomi Valkeinen 		return -ENOMEM;
3472f7018c21STomi Valkeinen 
3473f7018c21STomi Valkeinen 	info->fix.mmio_start += par->aux_start ? 0x400 : 0xc00;
3474f7018c21STomi Valkeinen 	par->ati_regbase += par->aux_start ? 0x400 : 0xc00;
3475f7018c21STomi Valkeinen 
3476f7018c21STomi Valkeinen 	/*
3477f7018c21STomi Valkeinen 	 * Enable memory-space accesses using config-space
3478f7018c21STomi Valkeinen 	 * command register.
3479f7018c21STomi Valkeinen 	 */
3480f7018c21STomi Valkeinen 	pci_read_config_word(pdev, PCI_COMMAND, &tmp);
3481f7018c21STomi Valkeinen 	if (!(tmp & PCI_COMMAND_MEMORY)) {
3482f7018c21STomi Valkeinen 		tmp |= PCI_COMMAND_MEMORY;
3483f7018c21STomi Valkeinen 		pci_write_config_word(pdev, PCI_COMMAND, tmp);
3484f7018c21STomi Valkeinen 	}
3485f7018c21STomi Valkeinen #ifdef __BIG_ENDIAN
3486f7018c21STomi Valkeinen 	/* Use the big-endian aperture */
3487f7018c21STomi Valkeinen 	addr += 0x800000;
3488f7018c21STomi Valkeinen #endif
3489f7018c21STomi Valkeinen 
3490f7018c21STomi Valkeinen 	/* Map in frame buffer */
3491f7018c21STomi Valkeinen 	info->fix.smem_start = addr;
3492*f55de6ecSLuis R. Rodriguez 
3493*f55de6ecSLuis R. Rodriguez 	/*
3494*f55de6ecSLuis R. Rodriguez 	 * The framebuffer is not always 8 MiB, that's just the size of the
3495*f55de6ecSLuis R. Rodriguez 	 * PCI BAR. We temporarily abuse smem_len here to store the size
3496*f55de6ecSLuis R. Rodriguez 	 * of the BAR. aty_init() will later correct it to match the actual
3497*f55de6ecSLuis R. Rodriguez 	 * framebuffer size.
3498*f55de6ecSLuis R. Rodriguez 	 *
3499*f55de6ecSLuis R. Rodriguez 	 * On devices that don't have the auxiliary register aperture, the
3500*f55de6ecSLuis R. Rodriguez 	 * registers are housed at the top end of the framebuffer PCI BAR.
3501*f55de6ecSLuis R. Rodriguez 	 * aty_fudge_framebuffer_len() is used to reduce smem_len to not
3502*f55de6ecSLuis R. Rodriguez 	 * overlap with the registers.
3503*f55de6ecSLuis R. Rodriguez 	 */
3504*f55de6ecSLuis R. Rodriguez 	info->fix.smem_len = 0x800000;
3505*f55de6ecSLuis R. Rodriguez 
3506*f55de6ecSLuis R. Rodriguez 	info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
3507f7018c21STomi Valkeinen 	if (info->screen_base == NULL) {
3508f7018c21STomi Valkeinen 		ret = -ENOMEM;
3509f7018c21STomi Valkeinen 		goto atyfb_setup_generic_fail;
3510f7018c21STomi Valkeinen 	}
3511f7018c21STomi Valkeinen 
3512f7018c21STomi Valkeinen 	ret = correct_chipset(par);
3513f7018c21STomi Valkeinen 	if (ret)
3514f7018c21STomi Valkeinen 		goto atyfb_setup_generic_fail;
3515f7018c21STomi Valkeinen #ifdef __i386__
3516f7018c21STomi Valkeinen 	ret = init_from_bios(par);
3517f7018c21STomi Valkeinen 	if (ret)
3518f7018c21STomi Valkeinen 		goto atyfb_setup_generic_fail;
3519f7018c21STomi Valkeinen #endif
3520f7018c21STomi Valkeinen 	if (!(aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_EXT_DISP_EN))
3521f7018c21STomi Valkeinen 		par->clk_wr_offset = (inb(R_GENMO) & 0x0CU) >> 2;
3522f7018c21STomi Valkeinen 	else
3523f7018c21STomi Valkeinen 		par->clk_wr_offset = aty_ld_8(CLOCK_CNTL, par) & 0x03U;
3524f7018c21STomi Valkeinen 
3525f7018c21STomi Valkeinen 	/* according to ATI, we should use clock 3 for acelerated mode */
3526f7018c21STomi Valkeinen 	par->clk_wr_offset = 3;
3527f7018c21STomi Valkeinen 
3528f7018c21STomi Valkeinen 	return 0;
3529f7018c21STomi Valkeinen 
3530f7018c21STomi Valkeinen atyfb_setup_generic_fail:
3531f7018c21STomi Valkeinen 	iounmap(par->ati_regbase);
3532f7018c21STomi Valkeinen 	par->ati_regbase = NULL;
3533f7018c21STomi Valkeinen 	if (info->screen_base) {
3534f7018c21STomi Valkeinen 		iounmap(info->screen_base);
3535f7018c21STomi Valkeinen 		info->screen_base = NULL;
3536f7018c21STomi Valkeinen 	}
3537f7018c21STomi Valkeinen 	return ret;
3538f7018c21STomi Valkeinen }
3539f7018c21STomi Valkeinen 
3540f7018c21STomi Valkeinen #endif /* !__sparc__ */
3541f7018c21STomi Valkeinen 
3542f7018c21STomi Valkeinen static int atyfb_pci_probe(struct pci_dev *pdev,
3543f7018c21STomi Valkeinen 			   const struct pci_device_id *ent)
3544f7018c21STomi Valkeinen {
3545f7018c21STomi Valkeinen 	unsigned long addr, res_start, res_size;
3546f7018c21STomi Valkeinen 	struct fb_info *info;
3547f7018c21STomi Valkeinen 	struct resource *rp;
3548f7018c21STomi Valkeinen 	struct atyfb_par *par;
3549f7018c21STomi Valkeinen 	int rc = -ENOMEM;
3550f7018c21STomi Valkeinen 
3551f7018c21STomi Valkeinen 	/* Enable device in PCI config */
3552f7018c21STomi Valkeinen 	if (pci_enable_device(pdev)) {
3553f7018c21STomi Valkeinen 		PRINTKE("Cannot enable PCI device\n");
3554f7018c21STomi Valkeinen 		return -ENXIO;
3555f7018c21STomi Valkeinen 	}
3556f7018c21STomi Valkeinen 
3557f7018c21STomi Valkeinen 	/* Find which resource to use */
3558f7018c21STomi Valkeinen 	rp = &pdev->resource[0];
3559f7018c21STomi Valkeinen 	if (rp->flags & IORESOURCE_IO)
3560f7018c21STomi Valkeinen 		rp = &pdev->resource[1];
3561f7018c21STomi Valkeinen 	addr = rp->start;
3562f7018c21STomi Valkeinen 	if (!addr)
3563f7018c21STomi Valkeinen 		return -ENXIO;
3564f7018c21STomi Valkeinen 
3565f7018c21STomi Valkeinen 	/* Reserve space */
3566f7018c21STomi Valkeinen 	res_start = rp->start;
3567f7018c21STomi Valkeinen 	res_size = resource_size(rp);
3568f7018c21STomi Valkeinen 	if (!request_mem_region(res_start, res_size, "atyfb"))
3569f7018c21STomi Valkeinen 		return -EBUSY;
3570f7018c21STomi Valkeinen 
3571f7018c21STomi Valkeinen 	/* Allocate framebuffer */
3572f7018c21STomi Valkeinen 	info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev);
3573f7018c21STomi Valkeinen 	if (!info) {
3574f7018c21STomi Valkeinen 		PRINTKE("atyfb_pci_probe() can't alloc fb_info\n");
3575f7018c21STomi Valkeinen 		return -ENOMEM;
3576f7018c21STomi Valkeinen 	}
3577f7018c21STomi Valkeinen 	par = info->par;
3578f7018c21STomi Valkeinen 	info->fix = atyfb_fix;
3579f7018c21STomi Valkeinen 	info->device = &pdev->dev;
3580f7018c21STomi Valkeinen 	par->pci_id = pdev->device;
3581f7018c21STomi Valkeinen 	par->res_start = res_start;
3582f7018c21STomi Valkeinen 	par->res_size = res_size;
3583f7018c21STomi Valkeinen 	par->irq = pdev->irq;
3584f7018c21STomi Valkeinen 	par->pdev = pdev;
3585f7018c21STomi Valkeinen 
3586f7018c21STomi Valkeinen 	/* Setup "info" structure */
3587f7018c21STomi Valkeinen #ifdef __sparc__
3588f7018c21STomi Valkeinen 	rc = atyfb_setup_sparc(pdev, info, addr);
3589f7018c21STomi Valkeinen #else
3590f7018c21STomi Valkeinen 	rc = atyfb_setup_generic(pdev, info, addr);
3591f7018c21STomi Valkeinen #endif
3592f7018c21STomi Valkeinen 	if (rc)
3593f7018c21STomi Valkeinen 		goto err_release_mem;
3594f7018c21STomi Valkeinen 
3595f7018c21STomi Valkeinen 	pci_set_drvdata(pdev, info);
3596f7018c21STomi Valkeinen 
3597f7018c21STomi Valkeinen 	/* Init chip & register framebuffer */
3598f7018c21STomi Valkeinen 	rc = aty_init(info);
3599f7018c21STomi Valkeinen 	if (rc)
3600f7018c21STomi Valkeinen 		goto err_release_io;
3601f7018c21STomi Valkeinen 
3602f7018c21STomi Valkeinen #ifdef __sparc__
3603f7018c21STomi Valkeinen 	/*
3604f7018c21STomi Valkeinen 	 * Add /dev/fb mmap values.
3605f7018c21STomi Valkeinen 	 */
3606f7018c21STomi Valkeinen 	par->mmap_map[0].voff = 0x8000000000000000UL;
3607f7018c21STomi Valkeinen 	par->mmap_map[0].poff = (unsigned long) info->screen_base & PAGE_MASK;
3608f7018c21STomi Valkeinen 	par->mmap_map[0].size = info->fix.smem_len;
3609f7018c21STomi Valkeinen 	par->mmap_map[0].prot_mask = _PAGE_CACHE;
3610f7018c21STomi Valkeinen 	par->mmap_map[0].prot_flag = _PAGE_E;
3611f7018c21STomi Valkeinen 	par->mmap_map[1].voff = par->mmap_map[0].voff + info->fix.smem_len;
3612f7018c21STomi Valkeinen 	par->mmap_map[1].poff = (long)par->ati_regbase & PAGE_MASK;
3613f7018c21STomi Valkeinen 	par->mmap_map[1].size = PAGE_SIZE;
3614f7018c21STomi Valkeinen 	par->mmap_map[1].prot_mask = _PAGE_CACHE;
3615f7018c21STomi Valkeinen 	par->mmap_map[1].prot_flag = _PAGE_E;
3616f7018c21STomi Valkeinen #endif /* __sparc__ */
3617f7018c21STomi Valkeinen 
3618f7018c21STomi Valkeinen 	mutex_lock(&reboot_lock);
3619f7018c21STomi Valkeinen 	if (!reboot_info)
3620f7018c21STomi Valkeinen 		reboot_info = info;
3621f7018c21STomi Valkeinen 	mutex_unlock(&reboot_lock);
3622f7018c21STomi Valkeinen 
3623f7018c21STomi Valkeinen 	return 0;
3624f7018c21STomi Valkeinen 
3625f7018c21STomi Valkeinen err_release_io:
3626f7018c21STomi Valkeinen #ifdef __sparc__
3627f7018c21STomi Valkeinen 	kfree(par->mmap_map);
3628f7018c21STomi Valkeinen #else
3629f7018c21STomi Valkeinen 	if (par->ati_regbase)
3630f7018c21STomi Valkeinen 		iounmap(par->ati_regbase);
3631f7018c21STomi Valkeinen 	if (info->screen_base)
3632f7018c21STomi Valkeinen 		iounmap(info->screen_base);
3633f7018c21STomi Valkeinen #endif
3634f7018c21STomi Valkeinen err_release_mem:
3635f7018c21STomi Valkeinen 	if (par->aux_start)
3636f7018c21STomi Valkeinen 		release_mem_region(par->aux_start, par->aux_size);
3637f7018c21STomi Valkeinen 
3638f7018c21STomi Valkeinen 	release_mem_region(par->res_start, par->res_size);
3639f7018c21STomi Valkeinen 	framebuffer_release(info);
3640f7018c21STomi Valkeinen 
3641f7018c21STomi Valkeinen 	return rc;
3642f7018c21STomi Valkeinen }
3643f7018c21STomi Valkeinen 
3644f7018c21STomi Valkeinen #endif /* CONFIG_PCI */
3645f7018c21STomi Valkeinen 
3646f7018c21STomi Valkeinen #ifdef CONFIG_ATARI
3647f7018c21STomi Valkeinen 
3648f7018c21STomi Valkeinen static int __init atyfb_atari_probe(void)
3649f7018c21STomi Valkeinen {
3650f7018c21STomi Valkeinen 	struct atyfb_par *par;
3651f7018c21STomi Valkeinen 	struct fb_info *info;
3652f7018c21STomi Valkeinen 	int m64_num;
3653f7018c21STomi Valkeinen 	u32 clock_r;
3654f7018c21STomi Valkeinen 	int num_found = 0;
3655f7018c21STomi Valkeinen 
3656f7018c21STomi Valkeinen 	for (m64_num = 0; m64_num < mach64_count; m64_num++) {
3657f7018c21STomi Valkeinen 		if (!phys_vmembase[m64_num] || !phys_size[m64_num] ||
3658f7018c21STomi Valkeinen 		    !phys_guiregbase[m64_num]) {
3659f7018c21STomi Valkeinen 			PRINTKI("phys_*[%d] parameters not set => "
3660f7018c21STomi Valkeinen 				"returning early. \n", m64_num);
3661f7018c21STomi Valkeinen 			continue;
3662f7018c21STomi Valkeinen 		}
3663f7018c21STomi Valkeinen 
3664f7018c21STomi Valkeinen 		info = framebuffer_alloc(sizeof(struct atyfb_par), NULL);
3665f7018c21STomi Valkeinen 		if (!info) {
3666f7018c21STomi Valkeinen 			PRINTKE("atyfb_atari_probe() can't alloc fb_info\n");
3667f7018c21STomi Valkeinen 			return -ENOMEM;
3668f7018c21STomi Valkeinen 		}
3669f7018c21STomi Valkeinen 		par = info->par;
3670f7018c21STomi Valkeinen 
3671f7018c21STomi Valkeinen 		info->fix = atyfb_fix;
3672f7018c21STomi Valkeinen 
3673f7018c21STomi Valkeinen 		par->irq = (unsigned int) -1; /* something invalid */
3674f7018c21STomi Valkeinen 
3675f7018c21STomi Valkeinen 		/*
3676f7018c21STomi Valkeinen 		 * Map the video memory (physical address given)
3677f7018c21STomi Valkeinen 		 * to somewhere in the kernel address space.
3678f7018c21STomi Valkeinen 		 */
3679f7018c21STomi Valkeinen 		info->screen_base = ioremap(phys_vmembase[m64_num], phys_size[m64_num]);
3680f7018c21STomi Valkeinen 		info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */
3681f7018c21STomi Valkeinen 		par->ati_regbase = ioremap(phys_guiregbase[m64_num], 0x10000) +
3682f7018c21STomi Valkeinen 						0xFC00ul;
3683f7018c21STomi Valkeinen 		info->fix.mmio_start = (unsigned long)par->ati_regbase; /* Fake! */
3684f7018c21STomi Valkeinen 
3685f7018c21STomi Valkeinen 		aty_st_le32(CLOCK_CNTL, 0x12345678, par);
3686f7018c21STomi Valkeinen 		clock_r = aty_ld_le32(CLOCK_CNTL, par);
3687f7018c21STomi Valkeinen 
3688f7018c21STomi Valkeinen 		switch (clock_r & 0x003F) {
3689f7018c21STomi Valkeinen 		case 0x12:
3690f7018c21STomi Valkeinen 			par->clk_wr_offset = 3; /*  */
3691f7018c21STomi Valkeinen 			break;
3692f7018c21STomi Valkeinen 		case 0x34:
3693f7018c21STomi Valkeinen 			par->clk_wr_offset = 2; /* Medusa ST-IO ISA Adapter etc. */
3694f7018c21STomi Valkeinen 			break;
3695f7018c21STomi Valkeinen 		case 0x16:
3696f7018c21STomi Valkeinen 			par->clk_wr_offset = 1; /*  */
3697f7018c21STomi Valkeinen 			break;
3698f7018c21STomi Valkeinen 		case 0x38:
3699f7018c21STomi Valkeinen 			par->clk_wr_offset = 0; /* Panther 1 ISA Adapter (Gerald) */
3700f7018c21STomi Valkeinen 			break;
3701f7018c21STomi Valkeinen 		}
3702f7018c21STomi Valkeinen 
3703f7018c21STomi Valkeinen 		/* Fake pci_id for correct_chipset() */
3704f7018c21STomi Valkeinen 		switch (aty_ld_le32(CNFG_CHIP_ID, par) & CFG_CHIP_TYPE) {
3705f7018c21STomi Valkeinen 		case 0x00d7:
3706f7018c21STomi Valkeinen 			par->pci_id = PCI_CHIP_MACH64GX;
3707f7018c21STomi Valkeinen 			break;
3708f7018c21STomi Valkeinen 		case 0x0057:
3709f7018c21STomi Valkeinen 			par->pci_id = PCI_CHIP_MACH64CX;
3710f7018c21STomi Valkeinen 			break;
3711f7018c21STomi Valkeinen 		default:
3712f7018c21STomi Valkeinen 			break;
3713f7018c21STomi Valkeinen 		}
3714f7018c21STomi Valkeinen 
3715f7018c21STomi Valkeinen 		if (correct_chipset(par) || aty_init(info)) {
3716f7018c21STomi Valkeinen 			iounmap(info->screen_base);
3717f7018c21STomi Valkeinen 			iounmap(par->ati_regbase);
3718f7018c21STomi Valkeinen 			framebuffer_release(info);
3719f7018c21STomi Valkeinen 		} else {
3720f7018c21STomi Valkeinen 			num_found++;
3721f7018c21STomi Valkeinen 		}
3722f7018c21STomi Valkeinen 	}
3723f7018c21STomi Valkeinen 
3724f7018c21STomi Valkeinen 	return num_found ? 0 : -ENXIO;
3725f7018c21STomi Valkeinen }
3726f7018c21STomi Valkeinen 
3727f7018c21STomi Valkeinen #endif /* CONFIG_ATARI */
3728f7018c21STomi Valkeinen 
3729f7018c21STomi Valkeinen #ifdef CONFIG_PCI
3730f7018c21STomi Valkeinen 
3731f7018c21STomi Valkeinen static void atyfb_remove(struct fb_info *info)
3732f7018c21STomi Valkeinen {
3733f7018c21STomi Valkeinen 	struct atyfb_par *par = (struct atyfb_par *) info->par;
3734f7018c21STomi Valkeinen 
3735f7018c21STomi Valkeinen 	/* restore video mode */
3736f7018c21STomi Valkeinen 	aty_set_crtc(par, &par->saved_crtc);
3737f7018c21STomi Valkeinen 	par->pll_ops->set_pll(info, &par->saved_pll);
3738f7018c21STomi Valkeinen 
3739f7018c21STomi Valkeinen 	unregister_framebuffer(info);
3740f7018c21STomi Valkeinen 
3741f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_BACKLIGHT
3742f7018c21STomi Valkeinen 	if (M64_HAS(MOBIL_BUS))
3743f7018c21STomi Valkeinen 		aty_bl_exit(info->bl_dev);
3744f7018c21STomi Valkeinen #endif
3745f7018c21STomi Valkeinen 
3746f7018c21STomi Valkeinen #ifdef CONFIG_MTRR
3747f7018c21STomi Valkeinen 	if (par->mtrr_reg >= 0) {
3748f7018c21STomi Valkeinen 		mtrr_del(par->mtrr_reg, 0, 0);
3749f7018c21STomi Valkeinen 		par->mtrr_reg = -1;
3750f7018c21STomi Valkeinen 	}
3751f7018c21STomi Valkeinen 	if (par->mtrr_aper >= 0) {
3752f7018c21STomi Valkeinen 		mtrr_del(par->mtrr_aper, 0, 0);
3753f7018c21STomi Valkeinen 		par->mtrr_aper = -1;
3754f7018c21STomi Valkeinen 	}
3755f7018c21STomi Valkeinen #endif
3756f7018c21STomi Valkeinen #ifndef __sparc__
3757f7018c21STomi Valkeinen 	if (par->ati_regbase)
3758f7018c21STomi Valkeinen 		iounmap(par->ati_regbase);
3759f7018c21STomi Valkeinen 	if (info->screen_base)
3760f7018c21STomi Valkeinen 		iounmap(info->screen_base);
3761f7018c21STomi Valkeinen #ifdef __BIG_ENDIAN
3762f7018c21STomi Valkeinen 	if (info->sprite.addr)
3763f7018c21STomi Valkeinen 		iounmap(info->sprite.addr);
3764f7018c21STomi Valkeinen #endif
3765f7018c21STomi Valkeinen #endif
3766f7018c21STomi Valkeinen #ifdef __sparc__
3767f7018c21STomi Valkeinen 	kfree(par->mmap_map);
3768f7018c21STomi Valkeinen #endif
3769f7018c21STomi Valkeinen 	if (par->aux_start)
3770f7018c21STomi Valkeinen 		release_mem_region(par->aux_start, par->aux_size);
3771f7018c21STomi Valkeinen 
3772f7018c21STomi Valkeinen 	if (par->res_start)
3773f7018c21STomi Valkeinen 		release_mem_region(par->res_start, par->res_size);
3774f7018c21STomi Valkeinen 
3775f7018c21STomi Valkeinen 	framebuffer_release(info);
3776f7018c21STomi Valkeinen }
3777f7018c21STomi Valkeinen 
3778f7018c21STomi Valkeinen 
3779f7018c21STomi Valkeinen static void atyfb_pci_remove(struct pci_dev *pdev)
3780f7018c21STomi Valkeinen {
3781f7018c21STomi Valkeinen 	struct fb_info *info = pci_get_drvdata(pdev);
3782f7018c21STomi Valkeinen 
3783f7018c21STomi Valkeinen 	mutex_lock(&reboot_lock);
3784f7018c21STomi Valkeinen 	if (reboot_info == info)
3785f7018c21STomi Valkeinen 		reboot_info = NULL;
3786f7018c21STomi Valkeinen 	mutex_unlock(&reboot_lock);
3787f7018c21STomi Valkeinen 
3788f7018c21STomi Valkeinen 	atyfb_remove(info);
3789f7018c21STomi Valkeinen }
3790f7018c21STomi Valkeinen 
3791f7018c21STomi Valkeinen static struct pci_device_id atyfb_pci_tbl[] = {
3792f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_GX
3793f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GX) },
3794f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CX) },
3795f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_GX */
3796f7018c21STomi Valkeinen 
3797f7018c21STomi Valkeinen #ifdef CONFIG_FB_ATY_CT
3798f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CT) },
3799f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64ET) },
3800f7018c21STomi Valkeinen 
3801f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LT) },
3802f7018c21STomi Valkeinen 
3803f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VT) },
3804f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GT) },
3805f7018c21STomi Valkeinen 
3806f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VU) },
3807f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GU) },
3808f7018c21STomi Valkeinen 
3809f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LG) },
3810f7018c21STomi Valkeinen 
3811f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VV) },
3812f7018c21STomi Valkeinen 
3813f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GV) },
3814f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GW) },
3815f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GY) },
3816f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GZ) },
3817f7018c21STomi Valkeinen 
3818f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GB) },
3819f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GD) },
3820f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GI) },
3821f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GP) },
3822f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GQ) },
3823f7018c21STomi Valkeinen 
3824f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LB) },
3825f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LD) },
3826f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LI) },
3827f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LP) },
3828f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LQ) },
3829f7018c21STomi Valkeinen 
3830f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GM) },
3831f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GN) },
3832f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GO) },
3833f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GL) },
3834f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GR) },
3835f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GS) },
3836f7018c21STomi Valkeinen 
3837f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LM) },
3838f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LN) },
3839f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LR) },
3840f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LS) },
3841f7018c21STomi Valkeinen #endif /* CONFIG_FB_ATY_CT */
3842f7018c21STomi Valkeinen 	{ }
3843f7018c21STomi Valkeinen };
3844f7018c21STomi Valkeinen 
3845f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, atyfb_pci_tbl);
3846f7018c21STomi Valkeinen 
3847f7018c21STomi Valkeinen static struct pci_driver atyfb_driver = {
3848f7018c21STomi Valkeinen 	.name		= "atyfb",
3849f7018c21STomi Valkeinen 	.id_table	= atyfb_pci_tbl,
3850f7018c21STomi Valkeinen 	.probe		= atyfb_pci_probe,
3851f7018c21STomi Valkeinen 	.remove		= atyfb_pci_remove,
3852f7018c21STomi Valkeinen #ifdef CONFIG_PM
3853f7018c21STomi Valkeinen 	.suspend	= atyfb_pci_suspend,
3854f7018c21STomi Valkeinen 	.resume		= atyfb_pci_resume,
3855f7018c21STomi Valkeinen #endif /* CONFIG_PM */
3856f7018c21STomi Valkeinen };
3857f7018c21STomi Valkeinen 
3858f7018c21STomi Valkeinen #endif /* CONFIG_PCI */
3859f7018c21STomi Valkeinen 
3860f7018c21STomi Valkeinen #ifndef MODULE
3861f7018c21STomi Valkeinen static int __init atyfb_setup(char *options)
3862f7018c21STomi Valkeinen {
3863f7018c21STomi Valkeinen 	char *this_opt;
3864f7018c21STomi Valkeinen 
3865f7018c21STomi Valkeinen 	if (!options || !*options)
3866f7018c21STomi Valkeinen 		return 0;
3867f7018c21STomi Valkeinen 
3868f7018c21STomi Valkeinen 	while ((this_opt = strsep(&options, ",")) != NULL) {
3869f7018c21STomi Valkeinen 		if (!strncmp(this_opt, "noaccel", 7)) {
3870f7018c21STomi Valkeinen 			noaccel = 1;
3871f7018c21STomi Valkeinen #ifdef CONFIG_MTRR
3872f7018c21STomi Valkeinen 		} else if (!strncmp(this_opt, "nomtrr", 6)) {
3873f7018c21STomi Valkeinen 			nomtrr = 1;
3874f7018c21STomi Valkeinen #endif
3875f7018c21STomi Valkeinen 		} else if (!strncmp(this_opt, "vram:", 5))
3876f7018c21STomi Valkeinen 			vram = simple_strtoul(this_opt + 5, NULL, 0);
3877f7018c21STomi Valkeinen 		else if (!strncmp(this_opt, "pll:", 4))
3878f7018c21STomi Valkeinen 			pll = simple_strtoul(this_opt + 4, NULL, 0);
3879f7018c21STomi Valkeinen 		else if (!strncmp(this_opt, "mclk:", 5))
3880f7018c21STomi Valkeinen 			mclk = simple_strtoul(this_opt + 5, NULL, 0);
3881f7018c21STomi Valkeinen 		else if (!strncmp(this_opt, "xclk:", 5))
3882f7018c21STomi Valkeinen 			xclk = simple_strtoul(this_opt+5, NULL, 0);
3883f7018c21STomi Valkeinen 		else if (!strncmp(this_opt, "comp_sync:", 10))
3884f7018c21STomi Valkeinen 			comp_sync = simple_strtoul(this_opt+10, NULL, 0);
3885f7018c21STomi Valkeinen 		else if (!strncmp(this_opt, "backlight:", 10))
3886f7018c21STomi Valkeinen 			backlight = simple_strtoul(this_opt+10, NULL, 0);
3887f7018c21STomi Valkeinen #ifdef CONFIG_PPC
3888f7018c21STomi Valkeinen 		else if (!strncmp(this_opt, "vmode:", 6)) {
3889f7018c21STomi Valkeinen 			unsigned int vmode =
3890f7018c21STomi Valkeinen 			    simple_strtoul(this_opt + 6, NULL, 0);
3891f7018c21STomi Valkeinen 			if (vmode > 0 && vmode <= VMODE_MAX)
3892f7018c21STomi Valkeinen 				default_vmode = vmode;
3893f7018c21STomi Valkeinen 		} else if (!strncmp(this_opt, "cmode:", 6)) {
3894f7018c21STomi Valkeinen 			unsigned int cmode =
3895f7018c21STomi Valkeinen 			    simple_strtoul(this_opt + 6, NULL, 0);
3896f7018c21STomi Valkeinen 			switch (cmode) {
3897f7018c21STomi Valkeinen 			case 0:
3898f7018c21STomi Valkeinen 			case 8:
3899f7018c21STomi Valkeinen 				default_cmode = CMODE_8;
3900f7018c21STomi Valkeinen 				break;
3901f7018c21STomi Valkeinen 			case 15:
3902f7018c21STomi Valkeinen 			case 16:
3903f7018c21STomi Valkeinen 				default_cmode = CMODE_16;
3904f7018c21STomi Valkeinen 				break;
3905f7018c21STomi Valkeinen 			case 24:
3906f7018c21STomi Valkeinen 			case 32:
3907f7018c21STomi Valkeinen 				default_cmode = CMODE_32;
3908f7018c21STomi Valkeinen 				break;
3909f7018c21STomi Valkeinen 			}
3910f7018c21STomi Valkeinen 		}
3911f7018c21STomi Valkeinen #endif
3912f7018c21STomi Valkeinen #ifdef CONFIG_ATARI
3913f7018c21STomi Valkeinen 		/*
3914f7018c21STomi Valkeinen 		 * Why do we need this silly Mach64 argument?
3915f7018c21STomi Valkeinen 		 * We are already here because of mach64= so its redundant.
3916f7018c21STomi Valkeinen 		 */
3917f7018c21STomi Valkeinen 		else if (MACH_IS_ATARI
3918f7018c21STomi Valkeinen 			 && (!strncmp(this_opt, "Mach64:", 7))) {
3919f7018c21STomi Valkeinen 			static unsigned char m64_num;
3920f7018c21STomi Valkeinen 			static char mach64_str[80];
3921f7018c21STomi Valkeinen 			strlcpy(mach64_str, this_opt + 7, sizeof(mach64_str));
3922f7018c21STomi Valkeinen 			if (!store_video_par(mach64_str, m64_num)) {
3923f7018c21STomi Valkeinen 				m64_num++;
3924f7018c21STomi Valkeinen 				mach64_count = m64_num;
3925f7018c21STomi Valkeinen 			}
3926f7018c21STomi Valkeinen 		}
3927f7018c21STomi Valkeinen #endif
3928f7018c21STomi Valkeinen 		else
3929f7018c21STomi Valkeinen 			mode = this_opt;
3930f7018c21STomi Valkeinen 	}
3931f7018c21STomi Valkeinen 	return 0;
3932f7018c21STomi Valkeinen }
3933f7018c21STomi Valkeinen #endif  /*  MODULE  */
3934f7018c21STomi Valkeinen 
3935f7018c21STomi Valkeinen static int atyfb_reboot_notify(struct notifier_block *nb,
3936f7018c21STomi Valkeinen 			       unsigned long code, void *unused)
3937f7018c21STomi Valkeinen {
3938f7018c21STomi Valkeinen 	struct atyfb_par *par;
3939f7018c21STomi Valkeinen 
3940f7018c21STomi Valkeinen 	if (code != SYS_RESTART)
3941f7018c21STomi Valkeinen 		return NOTIFY_DONE;
3942f7018c21STomi Valkeinen 
3943f7018c21STomi Valkeinen 	mutex_lock(&reboot_lock);
3944f7018c21STomi Valkeinen 
3945f7018c21STomi Valkeinen 	if (!reboot_info)
3946f7018c21STomi Valkeinen 		goto out;
3947f7018c21STomi Valkeinen 
3948f7018c21STomi Valkeinen 	if (!lock_fb_info(reboot_info))
3949f7018c21STomi Valkeinen 		goto out;
3950f7018c21STomi Valkeinen 
3951f7018c21STomi Valkeinen 	par = reboot_info->par;
3952f7018c21STomi Valkeinen 
3953f7018c21STomi Valkeinen 	/*
3954f7018c21STomi Valkeinen 	 * HP OmniBook 500's BIOS doesn't like the state of the
3955f7018c21STomi Valkeinen 	 * hardware after atyfb has been used. Restore the hardware
3956f7018c21STomi Valkeinen 	 * to the original state to allow successful reboots.
3957f7018c21STomi Valkeinen 	 */
3958f7018c21STomi Valkeinen 	aty_set_crtc(par, &par->saved_crtc);
3959f7018c21STomi Valkeinen 	par->pll_ops->set_pll(reboot_info, &par->saved_pll);
3960f7018c21STomi Valkeinen 
3961f7018c21STomi Valkeinen 	unlock_fb_info(reboot_info);
3962f7018c21STomi Valkeinen  out:
3963f7018c21STomi Valkeinen 	mutex_unlock(&reboot_lock);
3964f7018c21STomi Valkeinen 
3965f7018c21STomi Valkeinen 	return NOTIFY_DONE;
3966f7018c21STomi Valkeinen }
3967f7018c21STomi Valkeinen 
3968f7018c21STomi Valkeinen static struct notifier_block atyfb_reboot_notifier = {
3969f7018c21STomi Valkeinen 	.notifier_call = atyfb_reboot_notify,
3970f7018c21STomi Valkeinen };
3971f7018c21STomi Valkeinen 
3972a8fc91afSMathias Krause static const struct dmi_system_id atyfb_reboot_ids[] __initconst = {
3973f7018c21STomi Valkeinen 	{
3974f7018c21STomi Valkeinen 		.ident = "HP OmniBook 500",
3975f7018c21STomi Valkeinen 		.matches = {
3976f7018c21STomi Valkeinen 			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
3977f7018c21STomi Valkeinen 			DMI_MATCH(DMI_PRODUCT_NAME, "HP OmniBook PC"),
3978f7018c21STomi Valkeinen 			DMI_MATCH(DMI_PRODUCT_VERSION, "HP OmniBook 500 FA"),
3979f7018c21STomi Valkeinen 		},
3980f7018c21STomi Valkeinen 	},
3981f7018c21STomi Valkeinen 
3982f7018c21STomi Valkeinen 	{ }
3983f7018c21STomi Valkeinen };
3984a8fc91afSMathias Krause static bool registered_notifier = false;
3985f7018c21STomi Valkeinen 
3986f7018c21STomi Valkeinen static int __init atyfb_init(void)
3987f7018c21STomi Valkeinen {
3988f7018c21STomi Valkeinen 	int err1 = 1, err2 = 1;
3989f7018c21STomi Valkeinen #ifndef MODULE
3990f7018c21STomi Valkeinen 	char *option = NULL;
3991f7018c21STomi Valkeinen 
3992f7018c21STomi Valkeinen 	if (fb_get_options("atyfb", &option))
3993f7018c21STomi Valkeinen 		return -ENODEV;
3994f7018c21STomi Valkeinen 	atyfb_setup(option);
3995f7018c21STomi Valkeinen #endif
3996f7018c21STomi Valkeinen 
3997f7018c21STomi Valkeinen #ifdef CONFIG_PCI
3998f7018c21STomi Valkeinen 	err1 = pci_register_driver(&atyfb_driver);
3999f7018c21STomi Valkeinen #endif
4000f7018c21STomi Valkeinen #ifdef CONFIG_ATARI
4001f7018c21STomi Valkeinen 	err2 = atyfb_atari_probe();
4002f7018c21STomi Valkeinen #endif
4003f7018c21STomi Valkeinen 
4004f7018c21STomi Valkeinen 	if (err1 && err2)
4005f7018c21STomi Valkeinen 		return -ENODEV;
4006f7018c21STomi Valkeinen 
4007a8fc91afSMathias Krause 	if (dmi_check_system(atyfb_reboot_ids)) {
4008f7018c21STomi Valkeinen 		register_reboot_notifier(&atyfb_reboot_notifier);
4009a8fc91afSMathias Krause 		registered_notifier = true;
4010a8fc91afSMathias Krause 	}
4011f7018c21STomi Valkeinen 
4012f7018c21STomi Valkeinen 	return 0;
4013f7018c21STomi Valkeinen }
4014f7018c21STomi Valkeinen 
4015f7018c21STomi Valkeinen static void __exit atyfb_exit(void)
4016f7018c21STomi Valkeinen {
4017a8fc91afSMathias Krause 	if (registered_notifier)
4018f7018c21STomi Valkeinen 		unregister_reboot_notifier(&atyfb_reboot_notifier);
4019f7018c21STomi Valkeinen 
4020f7018c21STomi Valkeinen #ifdef CONFIG_PCI
4021f7018c21STomi Valkeinen 	pci_unregister_driver(&atyfb_driver);
4022f7018c21STomi Valkeinen #endif
4023f7018c21STomi Valkeinen }
4024f7018c21STomi Valkeinen 
4025f7018c21STomi Valkeinen module_init(atyfb_init);
4026f7018c21STomi Valkeinen module_exit(atyfb_exit);
4027f7018c21STomi Valkeinen 
4028f7018c21STomi Valkeinen MODULE_DESCRIPTION("FBDev driver for ATI Mach64 cards");
4029f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
4030f7018c21STomi Valkeinen module_param(noaccel, bool, 0);
4031f7018c21STomi Valkeinen MODULE_PARM_DESC(noaccel, "bool: disable acceleration");
4032f7018c21STomi Valkeinen module_param(vram, int, 0);
4033f7018c21STomi Valkeinen MODULE_PARM_DESC(vram, "int: override size of video ram");
4034f7018c21STomi Valkeinen module_param(pll, int, 0);
4035f7018c21STomi Valkeinen MODULE_PARM_DESC(pll, "int: override video clock");
4036f7018c21STomi Valkeinen module_param(mclk, int, 0);
4037f7018c21STomi Valkeinen MODULE_PARM_DESC(mclk, "int: override memory clock");
4038f7018c21STomi Valkeinen module_param(xclk, int, 0);
4039f7018c21STomi Valkeinen MODULE_PARM_DESC(xclk, "int: override accelerated engine clock");
4040f7018c21STomi Valkeinen module_param(comp_sync, int, 0);
4041f7018c21STomi Valkeinen MODULE_PARM_DESC(comp_sync, "Set composite sync signal to low (0) or high (1)");
4042f7018c21STomi Valkeinen module_param(mode, charp, 0);
4043f7018c21STomi Valkeinen MODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
4044f7018c21STomi Valkeinen #ifdef CONFIG_MTRR
4045f7018c21STomi Valkeinen module_param(nomtrr, bool, 0);
4046f7018c21STomi Valkeinen MODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
4047f7018c21STomi Valkeinen #endif
4048