162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* $Id: aty128fb.c,v 1.1.1.1.36.1 1999/12/11 09:03:05 Exp $
362306a36Sopenharmony_ci *  linux/drivers/video/aty128fb.c -- Frame buffer device for ATI Rage128
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1999-2003, Brad Douglas <brad@neruo.com>
662306a36Sopenharmony_ci *  Copyright (C) 1999, Anthony Tong <atong@uiuc.edu>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *                Ani Joshi / Jeff Garzik
962306a36Sopenharmony_ci *                      - Code cleanup
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *                Michel Danzer <michdaen@iiic.ethz.ch>
1262306a36Sopenharmony_ci *                      - 15/16 bit cleanup
1362306a36Sopenharmony_ci *                      - fix panning
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci *                Benjamin Herrenschmidt
1662306a36Sopenharmony_ci *                      - pmac-specific PM stuff
1762306a36Sopenharmony_ci *			- various fixes & cleanups
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *                Andreas Hundt <andi@convergence.de>
2062306a36Sopenharmony_ci *                      - FB_ACTIVATE fixes
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *		  Paul Mackerras <paulus@samba.org>
2362306a36Sopenharmony_ci *			- Convert to new framebuffer API,
2462306a36Sopenharmony_ci *			  fix colormap setting at 16 bits/pixel (565)
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci *		  Paul Mundt
2762306a36Sopenharmony_ci *		  	- PCI hotplug
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci *		  Jon Smirl <jonsmirl@yahoo.com>
3062306a36Sopenharmony_ci * 			- PCI ID update
3162306a36Sopenharmony_ci * 			- replace ROM BIOS search
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci *  Based off of Geert's atyfb.c and vfb.c.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci *  TODO:
3662306a36Sopenharmony_ci *		- monitor sensing (DDC)
3762306a36Sopenharmony_ci *              - virtual display
3862306a36Sopenharmony_ci *		- other platform support (only ppc/x86 supported)
3962306a36Sopenharmony_ci *		- hardware cursor support
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci *    Please cc: your patches to brad@neruo.com.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci * A special note of gratitude to ATI's devrel for providing documentation,
4662306a36Sopenharmony_ci * example code and hardware. Thanks Nitya.	-atong and brad
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#include <linux/aperture.h>
5162306a36Sopenharmony_ci#include <linux/module.h>
5262306a36Sopenharmony_ci#include <linux/moduleparam.h>
5362306a36Sopenharmony_ci#include <linux/kernel.h>
5462306a36Sopenharmony_ci#include <linux/errno.h>
5562306a36Sopenharmony_ci#include <linux/string.h>
5662306a36Sopenharmony_ci#include <linux/mm.h>
5762306a36Sopenharmony_ci#include <linux/vmalloc.h>
5862306a36Sopenharmony_ci#include <linux/delay.h>
5962306a36Sopenharmony_ci#include <linux/interrupt.h>
6062306a36Sopenharmony_ci#include <linux/uaccess.h>
6162306a36Sopenharmony_ci#include <linux/fb.h>
6262306a36Sopenharmony_ci#include <linux/init.h>
6362306a36Sopenharmony_ci#include <linux/pci.h>
6462306a36Sopenharmony_ci#include <linux/ioport.h>
6562306a36Sopenharmony_ci#include <linux/console.h>
6662306a36Sopenharmony_ci#include <linux/backlight.h>
6762306a36Sopenharmony_ci#include <asm/io.h>
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
7062306a36Sopenharmony_ci#include <asm/machdep.h>
7162306a36Sopenharmony_ci#include <asm/pmac_feature.h>
7262306a36Sopenharmony_ci#include "../macmodes.h"
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
7662306a36Sopenharmony_ci#include <asm/backlight.h>
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
8062306a36Sopenharmony_ci#include <asm/btext.h>
8162306a36Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#include <video/aty128.h>
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* Debug flag */
8662306a36Sopenharmony_ci#undef DEBUG
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#ifdef DEBUG
8962306a36Sopenharmony_ci#define DBG(fmt, args...) \
9062306a36Sopenharmony_ci	printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
9162306a36Sopenharmony_ci#else
9262306a36Sopenharmony_ci#define DBG(fmt, args...)
9362306a36Sopenharmony_ci#endif
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#ifndef CONFIG_PPC_PMAC
9662306a36Sopenharmony_ci/* default mode */
9762306a36Sopenharmony_cistatic const struct fb_var_screeninfo default_var = {
9862306a36Sopenharmony_ci	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
9962306a36Sopenharmony_ci	640, 480, 640, 480, 0, 0, 8, 0,
10062306a36Sopenharmony_ci	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
10162306a36Sopenharmony_ci	0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
10262306a36Sopenharmony_ci	0, FB_VMODE_NONINTERLACED
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#else /* CONFIG_PPC_PMAC */
10662306a36Sopenharmony_ci/* default to 1024x768 at 75Hz on PPC - this will work
10762306a36Sopenharmony_ci * on the iMac, the usual 640x480 @ 60Hz doesn't. */
10862306a36Sopenharmony_cistatic const struct fb_var_screeninfo default_var = {
10962306a36Sopenharmony_ci	/* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
11062306a36Sopenharmony_ci	1024, 768, 1024, 768, 0, 0, 8, 0,
11162306a36Sopenharmony_ci	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
11262306a36Sopenharmony_ci	0, 0, -1, -1, 0, 12699, 160, 32, 28, 1, 96, 3,
11362306a36Sopenharmony_ci	FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
11462306a36Sopenharmony_ci	FB_VMODE_NONINTERLACED
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/* default modedb mode */
11962306a36Sopenharmony_ci/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
12062306a36Sopenharmony_cistatic const struct fb_videomode defaultmode = {
12162306a36Sopenharmony_ci	.refresh =	60,
12262306a36Sopenharmony_ci	.xres =		640,
12362306a36Sopenharmony_ci	.yres =		480,
12462306a36Sopenharmony_ci	.pixclock =	39722,
12562306a36Sopenharmony_ci	.left_margin =	48,
12662306a36Sopenharmony_ci	.right_margin =	16,
12762306a36Sopenharmony_ci	.upper_margin =	33,
12862306a36Sopenharmony_ci	.lower_margin =	10,
12962306a36Sopenharmony_ci	.hsync_len =	96,
13062306a36Sopenharmony_ci	.vsync_len =	2,
13162306a36Sopenharmony_ci	.sync =		0,
13262306a36Sopenharmony_ci	.vmode =	FB_VMODE_NONINTERLACED
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* Chip generations */
13662306a36Sopenharmony_cienum {
13762306a36Sopenharmony_ci	rage_128,
13862306a36Sopenharmony_ci	rage_128_pci,
13962306a36Sopenharmony_ci	rage_128_pro,
14062306a36Sopenharmony_ci	rage_128_pro_pci,
14162306a36Sopenharmony_ci	rage_M3,
14262306a36Sopenharmony_ci	rage_M3_pci,
14362306a36Sopenharmony_ci	rage_M4,
14462306a36Sopenharmony_ci	rage_128_ultra,
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* Must match above enum */
14862306a36Sopenharmony_cistatic char * const r128_family[] = {
14962306a36Sopenharmony_ci	"AGP",
15062306a36Sopenharmony_ci	"PCI",
15162306a36Sopenharmony_ci	"PRO AGP",
15262306a36Sopenharmony_ci	"PRO PCI",
15362306a36Sopenharmony_ci	"M3 AGP",
15462306a36Sopenharmony_ci	"M3 PCI",
15562306a36Sopenharmony_ci	"M4 AGP",
15662306a36Sopenharmony_ci	"Ultra AGP",
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/*
16062306a36Sopenharmony_ci * PCI driver prototypes
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_cistatic int aty128_probe(struct pci_dev *pdev,
16362306a36Sopenharmony_ci                               const struct pci_device_id *ent);
16462306a36Sopenharmony_cistatic void aty128_remove(struct pci_dev *pdev);
16562306a36Sopenharmony_cistatic int aty128_pci_suspend_late(struct device *dev, pm_message_t state);
16662306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_suspend(struct device *dev);
16762306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_hibernate(struct device *dev);
16862306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_freeze(struct device *dev);
16962306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_resume(struct device *dev);
17062306a36Sopenharmony_cistatic int aty128_do_resume(struct pci_dev *pdev);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic const struct dev_pm_ops aty128_pci_pm_ops = {
17362306a36Sopenharmony_ci	.suspend	= aty128_pci_suspend,
17462306a36Sopenharmony_ci	.resume		= aty128_pci_resume,
17562306a36Sopenharmony_ci	.freeze		= aty128_pci_freeze,
17662306a36Sopenharmony_ci	.thaw		= aty128_pci_resume,
17762306a36Sopenharmony_ci	.poweroff	= aty128_pci_hibernate,
17862306a36Sopenharmony_ci	.restore	= aty128_pci_resume,
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/* supported Rage128 chipsets */
18262306a36Sopenharmony_cistatic const struct pci_device_id aty128_pci_tbl[] = {
18362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LE,
18462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3_pci },
18562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_LF,
18662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M3 },
18762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_MF,
18862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
18962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_ML,
19062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_M4 },
19162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PA,
19262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
19362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PB,
19462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
19562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PC,
19662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
19762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PD,
19862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
19962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PE,
20062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
20162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PF,
20262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
20362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PG,
20462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
20562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PH,
20662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
20762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PI,
20862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
20962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PJ,
21062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
21162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PK,
21262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
21362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PL,
21462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
21562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PM,
21662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
21762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PN,
21862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
21962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PO,
22062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
22162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PP,
22262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
22362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PQ,
22462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
22562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PR,
22662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro_pci },
22762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PS,
22862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
22962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PT,
23062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
23162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PU,
23262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
23362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PV,
23462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
23562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PW,
23662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
23762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_PX,
23862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pro },
23962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RE,
24062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
24162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RF,
24262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
24362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RG,
24462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
24562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RK,
24662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
24762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_RL,
24862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
24962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SE,
25062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
25162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SF,
25262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_pci },
25362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SG,
25462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
25562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SH,
25662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
25762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SK,
25862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
25962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SL,
26062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
26162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SM,
26262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
26362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_SN,
26462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128 },
26562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TF,
26662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
26762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TL,
26862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
26962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TR,
27062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
27162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TS,
27262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
27362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TT,
27462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
27562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RAGE128_TU,
27662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, rage_128_ultra },
27762306a36Sopenharmony_ci	{ 0, }
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, aty128_pci_tbl);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic struct pci_driver aty128fb_driver = {
28362306a36Sopenharmony_ci	.name		= "aty128fb",
28462306a36Sopenharmony_ci	.id_table	= aty128_pci_tbl,
28562306a36Sopenharmony_ci	.probe		= aty128_probe,
28662306a36Sopenharmony_ci	.remove		= aty128_remove,
28762306a36Sopenharmony_ci	.driver.pm	= &aty128_pci_pm_ops,
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/* packed BIOS settings */
29162306a36Sopenharmony_ci#ifndef CONFIG_PPC
29262306a36Sopenharmony_citypedef struct {
29362306a36Sopenharmony_ci	u8 clock_chip_type;
29462306a36Sopenharmony_ci	u8 struct_size;
29562306a36Sopenharmony_ci	u8 accelerator_entry;
29662306a36Sopenharmony_ci	u8 VGA_entry;
29762306a36Sopenharmony_ci	u16 VGA_table_offset;
29862306a36Sopenharmony_ci	u16 POST_table_offset;
29962306a36Sopenharmony_ci	u16 XCLK;
30062306a36Sopenharmony_ci	u16 MCLK;
30162306a36Sopenharmony_ci	u8 num_PLL_blocks;
30262306a36Sopenharmony_ci	u8 size_PLL_blocks;
30362306a36Sopenharmony_ci	u16 PCLK_ref_freq;
30462306a36Sopenharmony_ci	u16 PCLK_ref_divider;
30562306a36Sopenharmony_ci	u32 PCLK_min_freq;
30662306a36Sopenharmony_ci	u32 PCLK_max_freq;
30762306a36Sopenharmony_ci	u16 MCLK_ref_freq;
30862306a36Sopenharmony_ci	u16 MCLK_ref_divider;
30962306a36Sopenharmony_ci	u32 MCLK_min_freq;
31062306a36Sopenharmony_ci	u32 MCLK_max_freq;
31162306a36Sopenharmony_ci	u16 XCLK_ref_freq;
31262306a36Sopenharmony_ci	u16 XCLK_ref_divider;
31362306a36Sopenharmony_ci	u32 XCLK_min_freq;
31462306a36Sopenharmony_ci	u32 XCLK_max_freq;
31562306a36Sopenharmony_ci} __attribute__ ((packed)) PLL_BLOCK;
31662306a36Sopenharmony_ci#endif /* !CONFIG_PPC */
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/* onboard memory information */
31962306a36Sopenharmony_cistruct aty128_meminfo {
32062306a36Sopenharmony_ci	u8 ML;
32162306a36Sopenharmony_ci	u8 MB;
32262306a36Sopenharmony_ci	u8 Trcd;
32362306a36Sopenharmony_ci	u8 Trp;
32462306a36Sopenharmony_ci	u8 Twr;
32562306a36Sopenharmony_ci	u8 CL;
32662306a36Sopenharmony_ci	u8 Tr2w;
32762306a36Sopenharmony_ci	u8 LoopLatency;
32862306a36Sopenharmony_ci	u8 DspOn;
32962306a36Sopenharmony_ci	u8 Rloop;
33062306a36Sopenharmony_ci	const char *name;
33162306a36Sopenharmony_ci};
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/* various memory configurations */
33462306a36Sopenharmony_cistatic const struct aty128_meminfo sdr_128 = {
33562306a36Sopenharmony_ci	.ML = 4,
33662306a36Sopenharmony_ci	.MB = 4,
33762306a36Sopenharmony_ci	.Trcd = 3,
33862306a36Sopenharmony_ci	.Trp = 3,
33962306a36Sopenharmony_ci	.Twr = 1,
34062306a36Sopenharmony_ci	.CL = 3,
34162306a36Sopenharmony_ci	.Tr2w = 1,
34262306a36Sopenharmony_ci	.LoopLatency = 16,
34362306a36Sopenharmony_ci	.DspOn = 30,
34462306a36Sopenharmony_ci	.Rloop = 16,
34562306a36Sopenharmony_ci	.name = "128-bit SDR SGRAM (1:1)",
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct aty128_meminfo sdr_sgram = {
34962306a36Sopenharmony_ci	.ML = 4,
35062306a36Sopenharmony_ci	.MB = 4,
35162306a36Sopenharmony_ci	.Trcd = 1,
35262306a36Sopenharmony_ci	.Trp = 2,
35362306a36Sopenharmony_ci	.Twr = 1,
35462306a36Sopenharmony_ci	.CL = 2,
35562306a36Sopenharmony_ci	.Tr2w = 1,
35662306a36Sopenharmony_ci	.LoopLatency = 16,
35762306a36Sopenharmony_ci	.DspOn = 24,
35862306a36Sopenharmony_ci	.Rloop = 16,
35962306a36Sopenharmony_ci	.name = "64-bit SDR SGRAM (2:1)",
36062306a36Sopenharmony_ci};
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic const struct aty128_meminfo ddr_sgram = {
36362306a36Sopenharmony_ci	.ML = 4,
36462306a36Sopenharmony_ci	.MB = 4,
36562306a36Sopenharmony_ci	.Trcd = 3,
36662306a36Sopenharmony_ci	.Trp = 3,
36762306a36Sopenharmony_ci	.Twr = 2,
36862306a36Sopenharmony_ci	.CL = 3,
36962306a36Sopenharmony_ci	.Tr2w = 1,
37062306a36Sopenharmony_ci	.LoopLatency = 16,
37162306a36Sopenharmony_ci	.DspOn = 31,
37262306a36Sopenharmony_ci	.Rloop = 16,
37362306a36Sopenharmony_ci	.name = "64-bit DDR SGRAM",
37462306a36Sopenharmony_ci};
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic const struct fb_fix_screeninfo aty128fb_fix = {
37762306a36Sopenharmony_ci	.id		= "ATY Rage128",
37862306a36Sopenharmony_ci	.type		= FB_TYPE_PACKED_PIXELS,
37962306a36Sopenharmony_ci	.visual		= FB_VISUAL_PSEUDOCOLOR,
38062306a36Sopenharmony_ci	.xpanstep	= 8,
38162306a36Sopenharmony_ci	.ypanstep	= 1,
38262306a36Sopenharmony_ci	.mmio_len	= 0x2000,
38362306a36Sopenharmony_ci	.accel		= FB_ACCEL_ATI_RAGE128,
38462306a36Sopenharmony_ci};
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic char *mode_option = NULL;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
38962306a36Sopenharmony_cistatic int default_vmode = VMODE_1024_768_60;
39062306a36Sopenharmony_cistatic int default_cmode = CMODE_8;
39162306a36Sopenharmony_ci#endif
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int default_crt_on = 0;
39462306a36Sopenharmony_cistatic int default_lcd_on = 1;
39562306a36Sopenharmony_cistatic bool mtrr = true;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
39862306a36Sopenharmony_cistatic int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT);
39962306a36Sopenharmony_ci#endif
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci/* PLL constants */
40262306a36Sopenharmony_cistruct aty128_constants {
40362306a36Sopenharmony_ci	u32 ref_clk;
40462306a36Sopenharmony_ci	u32 ppll_min;
40562306a36Sopenharmony_ci	u32 ppll_max;
40662306a36Sopenharmony_ci	u32 ref_divider;
40762306a36Sopenharmony_ci	u32 xclk;
40862306a36Sopenharmony_ci	u32 fifo_width;
40962306a36Sopenharmony_ci	u32 fifo_depth;
41062306a36Sopenharmony_ci};
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_cistruct aty128_crtc {
41362306a36Sopenharmony_ci	u32 gen_cntl;
41462306a36Sopenharmony_ci	u32 h_total, h_sync_strt_wid;
41562306a36Sopenharmony_ci	u32 v_total, v_sync_strt_wid;
41662306a36Sopenharmony_ci	u32 pitch;
41762306a36Sopenharmony_ci	u32 offset, offset_cntl;
41862306a36Sopenharmony_ci	u32 xoffset, yoffset;
41962306a36Sopenharmony_ci	u32 vxres, vyres;
42062306a36Sopenharmony_ci	u32 depth, bpp;
42162306a36Sopenharmony_ci};
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistruct aty128_pll {
42462306a36Sopenharmony_ci	u32 post_divider;
42562306a36Sopenharmony_ci	u32 feedback_divider;
42662306a36Sopenharmony_ci	u32 vclk;
42762306a36Sopenharmony_ci};
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistruct aty128_ddafifo {
43062306a36Sopenharmony_ci	u32 dda_config;
43162306a36Sopenharmony_ci	u32 dda_on_off;
43262306a36Sopenharmony_ci};
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* register values for a specific mode */
43562306a36Sopenharmony_cistruct aty128fb_par {
43662306a36Sopenharmony_ci	struct aty128_crtc crtc;
43762306a36Sopenharmony_ci	struct aty128_pll pll;
43862306a36Sopenharmony_ci	struct aty128_ddafifo fifo_reg;
43962306a36Sopenharmony_ci	u32 accel_flags;
44062306a36Sopenharmony_ci	struct aty128_constants constants;  /* PLL and others      */
44162306a36Sopenharmony_ci	void __iomem *regbase;              /* remapped mmio       */
44262306a36Sopenharmony_ci	u32 vram_size;                      /* onboard video ram   */
44362306a36Sopenharmony_ci	int chip_gen;
44462306a36Sopenharmony_ci	const struct aty128_meminfo *mem;   /* onboard mem info    */
44562306a36Sopenharmony_ci	int wc_cookie;
44662306a36Sopenharmony_ci	int blitter_may_be_busy;
44762306a36Sopenharmony_ci	int fifo_slots;                 /* free slots in FIFO (64 max) */
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	int crt_on, lcd_on;
45062306a36Sopenharmony_ci	struct pci_dev *pdev;
45162306a36Sopenharmony_ci	struct fb_info *next;
45262306a36Sopenharmony_ci	int	asleep;
45362306a36Sopenharmony_ci	int	lock_blank;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	u8	red[32];		/* see aty128fb_setcolreg */
45662306a36Sopenharmony_ci	u8	green[64];
45762306a36Sopenharmony_ci	u8	blue[32];
45862306a36Sopenharmony_ci	u32	pseudo_palette[16];	/* used for TRUECOLOR */
45962306a36Sopenharmony_ci};
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci#define round_div(n, d) ((n+(d/2))/d)
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int aty128fb_check_var(struct fb_var_screeninfo *var,
46562306a36Sopenharmony_ci			      struct fb_info *info);
46662306a36Sopenharmony_cistatic int aty128fb_set_par(struct fb_info *info);
46762306a36Sopenharmony_cistatic int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
46862306a36Sopenharmony_ci			      u_int transp, struct fb_info *info);
46962306a36Sopenharmony_cistatic int aty128fb_pan_display(struct fb_var_screeninfo *var,
47062306a36Sopenharmony_ci			   struct fb_info *fb);
47162306a36Sopenharmony_cistatic int aty128fb_blank(int blank, struct fb_info *fb);
47262306a36Sopenharmony_cistatic int aty128fb_ioctl(struct fb_info *info, u_int cmd, unsigned long arg);
47362306a36Sopenharmony_cistatic int aty128fb_sync(struct fb_info *info);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci    /*
47662306a36Sopenharmony_ci     *  Internal routines
47762306a36Sopenharmony_ci     */
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic int aty128_encode_var(struct fb_var_screeninfo *var,
48062306a36Sopenharmony_ci                             const struct aty128fb_par *par);
48162306a36Sopenharmony_cistatic int aty128_decode_var(struct fb_var_screeninfo *var,
48262306a36Sopenharmony_ci                             struct aty128fb_par *par);
48362306a36Sopenharmony_cistatic void aty128_timings(struct aty128fb_par *par);
48462306a36Sopenharmony_cistatic void aty128_init_engine(struct aty128fb_par *par);
48562306a36Sopenharmony_cistatic void aty128_reset_engine(const struct aty128fb_par *par);
48662306a36Sopenharmony_cistatic void aty128_flush_pixel_cache(const struct aty128fb_par *par);
48762306a36Sopenharmony_cistatic void do_wait_for_fifo(u16 entries, struct aty128fb_par *par);
48862306a36Sopenharmony_cistatic void wait_for_fifo(u16 entries, struct aty128fb_par *par);
48962306a36Sopenharmony_cistatic void wait_for_idle(struct aty128fb_par *par);
49062306a36Sopenharmony_cistatic u32 depth_to_dst(u32 depth);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
49362306a36Sopenharmony_cistatic void aty128_bl_set_power(struct fb_info *info, int power);
49462306a36Sopenharmony_ci#endif
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci#define BIOS_IN8(v)  	(readb(bios + (v)))
49762306a36Sopenharmony_ci#define BIOS_IN16(v) 	(readb(bios + (v)) | \
49862306a36Sopenharmony_ci			  (readb(bios + (v) + 1) << 8))
49962306a36Sopenharmony_ci#define BIOS_IN32(v) 	(readb(bios + (v)) | \
50062306a36Sopenharmony_ci			  (readb(bios + (v) + 1) << 8) | \
50162306a36Sopenharmony_ci			  (readb(bios + (v) + 2) << 16) | \
50262306a36Sopenharmony_ci			  (readb(bios + (v) + 3) << 24))
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic const struct fb_ops aty128fb_ops = {
50662306a36Sopenharmony_ci	.owner		= THIS_MODULE,
50762306a36Sopenharmony_ci	FB_DEFAULT_IOMEM_OPS,
50862306a36Sopenharmony_ci	.fb_check_var	= aty128fb_check_var,
50962306a36Sopenharmony_ci	.fb_set_par	= aty128fb_set_par,
51062306a36Sopenharmony_ci	.fb_setcolreg	= aty128fb_setcolreg,
51162306a36Sopenharmony_ci	.fb_pan_display = aty128fb_pan_display,
51262306a36Sopenharmony_ci	.fb_blank	= aty128fb_blank,
51362306a36Sopenharmony_ci	.fb_ioctl	= aty128fb_ioctl,
51462306a36Sopenharmony_ci	.fb_sync	= aty128fb_sync,
51562306a36Sopenharmony_ci};
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci    /*
51862306a36Sopenharmony_ci     * Functions to read from/write to the mmio registers
51962306a36Sopenharmony_ci     *	- endian conversions may possibly be avoided by
52062306a36Sopenharmony_ci     *    using the other register aperture. TODO.
52162306a36Sopenharmony_ci     */
52262306a36Sopenharmony_cistatic inline u32 _aty_ld_le32(volatile unsigned int regindex,
52362306a36Sopenharmony_ci			       const struct aty128fb_par *par)
52462306a36Sopenharmony_ci{
52562306a36Sopenharmony_ci	return readl (par->regbase + regindex);
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic inline void _aty_st_le32(volatile unsigned int regindex, u32 val,
52962306a36Sopenharmony_ci				const struct aty128fb_par *par)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	writel (val, par->regbase + regindex);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic inline u8 _aty_ld_8(unsigned int regindex,
53562306a36Sopenharmony_ci			   const struct aty128fb_par *par)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	return readb (par->regbase + regindex);
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic inline void _aty_st_8(unsigned int regindex, u8 val,
54162306a36Sopenharmony_ci			     const struct aty128fb_par *par)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	writeb (val, par->regbase + regindex);
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci#define aty_ld_le32(regindex)		_aty_ld_le32(regindex, par)
54762306a36Sopenharmony_ci#define aty_st_le32(regindex, val)	_aty_st_le32(regindex, val, par)
54862306a36Sopenharmony_ci#define aty_ld_8(regindex)		_aty_ld_8(regindex, par)
54962306a36Sopenharmony_ci#define aty_st_8(regindex, val)		_aty_st_8(regindex, val, par)
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci    /*
55262306a36Sopenharmony_ci     * Functions to read from/write to the pll registers
55362306a36Sopenharmony_ci     */
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci#define aty_ld_pll(pll_index)		_aty_ld_pll(pll_index, par)
55662306a36Sopenharmony_ci#define aty_st_pll(pll_index, val)	_aty_st_pll(pll_index, val, par)
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic u32 _aty_ld_pll(unsigned int pll_index,
56062306a36Sopenharmony_ci		       const struct aty128fb_par *par)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F);
56362306a36Sopenharmony_ci	return aty_ld_le32(CLOCK_CNTL_DATA);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic void _aty_st_pll(unsigned int pll_index, u32 val,
56862306a36Sopenharmony_ci			const struct aty128fb_par *par)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN);
57162306a36Sopenharmony_ci	aty_st_le32(CLOCK_CNTL_DATA, val);
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/* return true when the PLL has completed an atomic update */
57662306a36Sopenharmony_cistatic int aty_pll_readupdate(const struct aty128fb_par *par)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	return !(aty_ld_pll(PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic void aty_pll_wait_readupdate(const struct aty128fb_par *par)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	unsigned long timeout = jiffies + HZ/100; // should be more than enough
58562306a36Sopenharmony_ci	int reset = 1;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	while (time_before(jiffies, timeout))
58862306a36Sopenharmony_ci		if (aty_pll_readupdate(par)) {
58962306a36Sopenharmony_ci			reset = 0;
59062306a36Sopenharmony_ci			break;
59162306a36Sopenharmony_ci		}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (reset)	/* reset engine?? */
59462306a36Sopenharmony_ci		printk(KERN_DEBUG "aty128fb: PLL write timeout!\n");
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci/* tell PLL to update */
59962306a36Sopenharmony_cistatic void aty_pll_writeupdate(const struct aty128fb_par *par)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	aty_pll_wait_readupdate(par);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	aty_st_pll(PPLL_REF_DIV,
60462306a36Sopenharmony_ci		   aty_ld_pll(PPLL_REF_DIV) | PPLL_ATOMIC_UPDATE_W);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci/* write to the scratch register to test r/w functionality */
60962306a36Sopenharmony_cistatic int register_test(const struct aty128fb_par *par)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	u32 val;
61262306a36Sopenharmony_ci	int flag = 0;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	val = aty_ld_le32(BIOS_0_SCRATCH);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	aty_st_le32(BIOS_0_SCRATCH, 0x55555555);
61762306a36Sopenharmony_ci	if (aty_ld_le32(BIOS_0_SCRATCH) == 0x55555555) {
61862306a36Sopenharmony_ci		aty_st_le32(BIOS_0_SCRATCH, 0xAAAAAAAA);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		if (aty_ld_le32(BIOS_0_SCRATCH) == 0xAAAAAAAA)
62162306a36Sopenharmony_ci			flag = 1;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	aty_st_le32(BIOS_0_SCRATCH, val);	// restore value
62562306a36Sopenharmony_ci	return flag;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci/*
63062306a36Sopenharmony_ci * Accelerator engine functions
63162306a36Sopenharmony_ci */
63262306a36Sopenharmony_cistatic void do_wait_for_fifo(u16 entries, struct aty128fb_par *par)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	int i;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	for (;;) {
63762306a36Sopenharmony_ci		for (i = 0; i < 2000000; i++) {
63862306a36Sopenharmony_ci			par->fifo_slots = aty_ld_le32(GUI_STAT) & 0x0fff;
63962306a36Sopenharmony_ci			if (par->fifo_slots >= entries)
64062306a36Sopenharmony_ci				return;
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci		aty128_reset_engine(par);
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic void wait_for_idle(struct aty128fb_par *par)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	int i;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	do_wait_for_fifo(64, par);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for (;;) {
65462306a36Sopenharmony_ci		for (i = 0; i < 2000000; i++) {
65562306a36Sopenharmony_ci			if (!(aty_ld_le32(GUI_STAT) & (1 << 31))) {
65662306a36Sopenharmony_ci				aty128_flush_pixel_cache(par);
65762306a36Sopenharmony_ci				par->blitter_may_be_busy = 0;
65862306a36Sopenharmony_ci				return;
65962306a36Sopenharmony_ci			}
66062306a36Sopenharmony_ci		}
66162306a36Sopenharmony_ci		aty128_reset_engine(par);
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void wait_for_fifo(u16 entries, struct aty128fb_par *par)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	if (par->fifo_slots < entries)
66962306a36Sopenharmony_ci		do_wait_for_fifo(64, par);
67062306a36Sopenharmony_ci	par->fifo_slots -= entries;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cistatic void aty128_flush_pixel_cache(const struct aty128fb_par *par)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	int i;
67762306a36Sopenharmony_ci	u32 tmp;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	tmp = aty_ld_le32(PC_NGUI_CTLSTAT);
68062306a36Sopenharmony_ci	tmp &= ~(0x00ff);
68162306a36Sopenharmony_ci	tmp |= 0x00ff;
68262306a36Sopenharmony_ci	aty_st_le32(PC_NGUI_CTLSTAT, tmp);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	for (i = 0; i < 2000000; i++)
68562306a36Sopenharmony_ci		if (!(aty_ld_le32(PC_NGUI_CTLSTAT) & PC_BUSY))
68662306a36Sopenharmony_ci			break;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic void aty128_reset_engine(const struct aty128fb_par *par)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	u32 gen_reset_cntl, clock_cntl_index, mclk_cntl;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	aty128_flush_pixel_cache(par);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	clock_cntl_index = aty_ld_le32(CLOCK_CNTL_INDEX);
69762306a36Sopenharmony_ci	mclk_cntl = aty_ld_pll(MCLK_CNTL);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	aty_st_pll(MCLK_CNTL, mclk_cntl | 0x00030000);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	gen_reset_cntl = aty_ld_le32(GEN_RESET_CNTL);
70262306a36Sopenharmony_ci	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl | SOFT_RESET_GUI);
70362306a36Sopenharmony_ci	aty_ld_le32(GEN_RESET_CNTL);
70462306a36Sopenharmony_ci	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl & ~(SOFT_RESET_GUI));
70562306a36Sopenharmony_ci	aty_ld_le32(GEN_RESET_CNTL);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	aty_st_pll(MCLK_CNTL, mclk_cntl);
70862306a36Sopenharmony_ci	aty_st_le32(CLOCK_CNTL_INDEX, clock_cntl_index);
70962306a36Sopenharmony_ci	aty_st_le32(GEN_RESET_CNTL, gen_reset_cntl);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* use old pio mode */
71262306a36Sopenharmony_ci	aty_st_le32(PM4_BUFFER_CNTL, PM4_BUFFER_CNTL_NONPM4);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	DBG("engine reset");
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic void aty128_init_engine(struct aty128fb_par *par)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	u32 pitch_value;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	wait_for_idle(par);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* 3D scaler not spoken here */
72562306a36Sopenharmony_ci	wait_for_fifo(1, par);
72662306a36Sopenharmony_ci	aty_st_le32(SCALE_3D_CNTL, 0x00000000);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	aty128_reset_engine(par);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	pitch_value = par->crtc.pitch;
73162306a36Sopenharmony_ci	if (par->crtc.bpp == 24) {
73262306a36Sopenharmony_ci		pitch_value = pitch_value * 3;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	wait_for_fifo(4, par);
73662306a36Sopenharmony_ci	/* setup engine offset registers */
73762306a36Sopenharmony_ci	aty_st_le32(DEFAULT_OFFSET, 0x00000000);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* setup engine pitch registers */
74062306a36Sopenharmony_ci	aty_st_le32(DEFAULT_PITCH, pitch_value);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/* set the default scissor register to max dimensions */
74362306a36Sopenharmony_ci	aty_st_le32(DEFAULT_SC_BOTTOM_RIGHT, (0x1FFF << 16) | 0x1FFF);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* set the drawing controls registers */
74662306a36Sopenharmony_ci	aty_st_le32(DP_GUI_MASTER_CNTL,
74762306a36Sopenharmony_ci		    GMC_SRC_PITCH_OFFSET_DEFAULT		|
74862306a36Sopenharmony_ci		    GMC_DST_PITCH_OFFSET_DEFAULT		|
74962306a36Sopenharmony_ci		    GMC_SRC_CLIP_DEFAULT			|
75062306a36Sopenharmony_ci		    GMC_DST_CLIP_DEFAULT			|
75162306a36Sopenharmony_ci		    GMC_BRUSH_SOLIDCOLOR			|
75262306a36Sopenharmony_ci		    (depth_to_dst(par->crtc.depth) << 8)	|
75362306a36Sopenharmony_ci		    GMC_SRC_DSTCOLOR			|
75462306a36Sopenharmony_ci		    GMC_BYTE_ORDER_MSB_TO_LSB		|
75562306a36Sopenharmony_ci		    GMC_DP_CONVERSION_TEMP_6500		|
75662306a36Sopenharmony_ci		    ROP3_PATCOPY				|
75762306a36Sopenharmony_ci		    GMC_DP_SRC_RECT				|
75862306a36Sopenharmony_ci		    GMC_3D_FCN_EN_CLR			|
75962306a36Sopenharmony_ci		    GMC_DST_CLR_CMP_FCN_CLEAR		|
76062306a36Sopenharmony_ci		    GMC_AUX_CLIP_CLEAR			|
76162306a36Sopenharmony_ci		    GMC_WRITE_MASK_SET);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	wait_for_fifo(8, par);
76462306a36Sopenharmony_ci	/* clear the line drawing registers */
76562306a36Sopenharmony_ci	aty_st_le32(DST_BRES_ERR, 0);
76662306a36Sopenharmony_ci	aty_st_le32(DST_BRES_INC, 0);
76762306a36Sopenharmony_ci	aty_st_le32(DST_BRES_DEC, 0);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/* set brush color registers */
77062306a36Sopenharmony_ci	aty_st_le32(DP_BRUSH_FRGD_CLR, 0xFFFFFFFF); /* white */
77162306a36Sopenharmony_ci	aty_st_le32(DP_BRUSH_BKGD_CLR, 0x00000000); /* black */
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	/* set source color registers */
77462306a36Sopenharmony_ci	aty_st_le32(DP_SRC_FRGD_CLR, 0xFFFFFFFF);   /* white */
77562306a36Sopenharmony_ci	aty_st_le32(DP_SRC_BKGD_CLR, 0x00000000);   /* black */
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* default write mask */
77862306a36Sopenharmony_ci	aty_st_le32(DP_WRITE_MASK, 0xFFFFFFFF);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* Wait for all the writes to be completed before returning */
78162306a36Sopenharmony_ci	wait_for_idle(par);
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci/* convert depth values to their register representation */
78662306a36Sopenharmony_cistatic u32 depth_to_dst(u32 depth)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	if (depth <= 8)
78962306a36Sopenharmony_ci		return DST_8BPP;
79062306a36Sopenharmony_ci	else if (depth <= 15)
79162306a36Sopenharmony_ci		return DST_15BPP;
79262306a36Sopenharmony_ci	else if (depth == 16)
79362306a36Sopenharmony_ci		return DST_16BPP;
79462306a36Sopenharmony_ci	else if (depth <= 24)
79562306a36Sopenharmony_ci		return DST_24BPP;
79662306a36Sopenharmony_ci	else if (depth <= 32)
79762306a36Sopenharmony_ci		return DST_32BPP;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return -EINVAL;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci/*
80362306a36Sopenharmony_ci * PLL informations retreival
80462306a36Sopenharmony_ci */
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci#ifndef __sparc__
80862306a36Sopenharmony_cistatic void __iomem *aty128_map_ROM(const struct aty128fb_par *par,
80962306a36Sopenharmony_ci				    struct pci_dev *dev)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	u16 dptr;
81262306a36Sopenharmony_ci	u8 rom_type;
81362306a36Sopenharmony_ci	void __iomem *bios;
81462306a36Sopenharmony_ci	size_t rom_size;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci    	/* Fix from ATI for problem with Rage128 hardware not leaving ROM enabled */
81762306a36Sopenharmony_ci    	unsigned int temp;
81862306a36Sopenharmony_ci	temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
81962306a36Sopenharmony_ci	temp &= 0x00ffffffu;
82062306a36Sopenharmony_ci	temp |= 0x04 << 24;
82162306a36Sopenharmony_ci	aty_st_le32(RAGE128_MPP_TB_CONFIG, temp);
82262306a36Sopenharmony_ci	temp = aty_ld_le32(RAGE128_MPP_TB_CONFIG);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	bios = pci_map_rom(dev, &rom_size);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (!bios) {
82762306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: ROM failed to map\n");
82862306a36Sopenharmony_ci		return NULL;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* Very simple test to make sure it appeared */
83262306a36Sopenharmony_ci	if (BIOS_IN16(0) != 0xaa55) {
83362306a36Sopenharmony_ci		printk(KERN_DEBUG "aty128fb: Invalid ROM signature %x should "
83462306a36Sopenharmony_ci			" be 0xaa55\n", BIOS_IN16(0));
83562306a36Sopenharmony_ci		goto failed;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* Look for the PCI data to check the ROM type */
83962306a36Sopenharmony_ci	dptr = BIOS_IN16(0x18);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* Check the PCI data signature. If it's wrong, we still assume a normal
84262306a36Sopenharmony_ci	 * x86 ROM for now, until I've verified this works everywhere.
84362306a36Sopenharmony_ci	 * The goal here is more to phase out Open Firmware images.
84462306a36Sopenharmony_ci	 *
84562306a36Sopenharmony_ci	 * Currently, we only look at the first PCI data, we could iteratre and
84662306a36Sopenharmony_ci	 * deal with them all, and we should use fb_bios_start relative to start
84762306a36Sopenharmony_ci	 * of image and not relative start of ROM, but so far, I never found a
84862306a36Sopenharmony_ci	 * dual-image ATI card.
84962306a36Sopenharmony_ci	 *
85062306a36Sopenharmony_ci	 * typedef struct {
85162306a36Sopenharmony_ci	 * 	u32	signature;	+ 0x00
85262306a36Sopenharmony_ci	 * 	u16	vendor;		+ 0x04
85362306a36Sopenharmony_ci	 * 	u16	device;		+ 0x06
85462306a36Sopenharmony_ci	 * 	u16	reserved_1;	+ 0x08
85562306a36Sopenharmony_ci	 * 	u16	dlen;		+ 0x0a
85662306a36Sopenharmony_ci	 * 	u8	drevision;	+ 0x0c
85762306a36Sopenharmony_ci	 * 	u8	class_hi;	+ 0x0d
85862306a36Sopenharmony_ci	 * 	u16	class_lo;	+ 0x0e
85962306a36Sopenharmony_ci	 * 	u16	ilen;		+ 0x10
86062306a36Sopenharmony_ci	 * 	u16	irevision;	+ 0x12
86162306a36Sopenharmony_ci	 * 	u8	type;		+ 0x14
86262306a36Sopenharmony_ci	 * 	u8	indicator;	+ 0x15
86362306a36Sopenharmony_ci	 * 	u16	reserved_2;	+ 0x16
86462306a36Sopenharmony_ci	 * } pci_data_t;
86562306a36Sopenharmony_ci	 */
86662306a36Sopenharmony_ci	if (BIOS_IN32(dptr) !=  (('R' << 24) | ('I' << 16) | ('C' << 8) | 'P')) {
86762306a36Sopenharmony_ci		printk(KERN_WARNING "aty128fb: PCI DATA signature in ROM incorrect: %08x\n",
86862306a36Sopenharmony_ci		       BIOS_IN32(dptr));
86962306a36Sopenharmony_ci		goto anyway;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	rom_type = BIOS_IN8(dptr + 0x14);
87262306a36Sopenharmony_ci	switch(rom_type) {
87362306a36Sopenharmony_ci	case 0:
87462306a36Sopenharmony_ci		printk(KERN_INFO "aty128fb: Found Intel x86 BIOS ROM Image\n");
87562306a36Sopenharmony_ci		break;
87662306a36Sopenharmony_ci	case 1:
87762306a36Sopenharmony_ci		printk(KERN_INFO "aty128fb: Found Open Firmware ROM Image\n");
87862306a36Sopenharmony_ci		goto failed;
87962306a36Sopenharmony_ci	case 2:
88062306a36Sopenharmony_ci		printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n");
88162306a36Sopenharmony_ci		goto failed;
88262306a36Sopenharmony_ci	default:
88362306a36Sopenharmony_ci		printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n",
88462306a36Sopenharmony_ci		       rom_type);
88562306a36Sopenharmony_ci		goto failed;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci anyway:
88862306a36Sopenharmony_ci	return bios;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci failed:
89162306a36Sopenharmony_ci	pci_unmap_rom(dev, bios);
89262306a36Sopenharmony_ci	return NULL;
89362306a36Sopenharmony_ci}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_cistatic void aty128_get_pllinfo(struct aty128fb_par *par,
89662306a36Sopenharmony_ci			       unsigned char __iomem *bios)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	unsigned int bios_hdr;
89962306a36Sopenharmony_ci	unsigned int bios_pll;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	bios_hdr = BIOS_IN16(0x48);
90262306a36Sopenharmony_ci	bios_pll = BIOS_IN16(bios_hdr + 0x30);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	par->constants.ppll_max = BIOS_IN32(bios_pll + 0x16);
90562306a36Sopenharmony_ci	par->constants.ppll_min = BIOS_IN32(bios_pll + 0x12);
90662306a36Sopenharmony_ci	par->constants.xclk = BIOS_IN16(bios_pll + 0x08);
90762306a36Sopenharmony_ci	par->constants.ref_divider = BIOS_IN16(bios_pll + 0x10);
90862306a36Sopenharmony_ci	par->constants.ref_clk = BIOS_IN16(bios_pll + 0x0e);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	DBG("ppll_max %d ppll_min %d xclk %d ref_divider %d ref clock %d\n",
91162306a36Sopenharmony_ci			par->constants.ppll_max, par->constants.ppll_min,
91262306a36Sopenharmony_ci			par->constants.xclk, par->constants.ref_divider,
91362306a36Sopenharmony_ci			par->constants.ref_clk);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci#ifdef CONFIG_X86
91862306a36Sopenharmony_cistatic void __iomem *aty128_find_mem_vbios(struct aty128fb_par *par)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	/* I simplified this code as we used to miss the signatures in
92162306a36Sopenharmony_ci	 * a lot of case. It's now closer to XFree, we just don't check
92262306a36Sopenharmony_ci	 * for signatures at all... Something better will have to be done
92362306a36Sopenharmony_ci	 * if we end up having conflicts
92462306a36Sopenharmony_ci	 */
92562306a36Sopenharmony_ci        u32  segstart;
92662306a36Sopenharmony_ci        unsigned char __iomem *rom_base = NULL;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci        for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
92962306a36Sopenharmony_ci                rom_base = ioremap(segstart, 0x10000);
93062306a36Sopenharmony_ci		if (rom_base == NULL)
93162306a36Sopenharmony_ci			return NULL;
93262306a36Sopenharmony_ci		if (readb(rom_base) == 0x55 && readb(rom_base + 1) == 0xaa)
93362306a36Sopenharmony_ci	                break;
93462306a36Sopenharmony_ci                iounmap(rom_base);
93562306a36Sopenharmony_ci		rom_base = NULL;
93662306a36Sopenharmony_ci        }
93762306a36Sopenharmony_ci	return rom_base;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci#endif
94062306a36Sopenharmony_ci#endif /* ndef(__sparc__) */
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci/* fill in known card constants if pll_block is not available */
94362306a36Sopenharmony_cistatic void aty128_timings(struct aty128fb_par *par)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci#ifdef CONFIG_PPC
94662306a36Sopenharmony_ci	/* instead of a table lookup, assume OF has properly
94762306a36Sopenharmony_ci	 * setup the PLL registers and use their values
94862306a36Sopenharmony_ci	 * to set the XCLK values and reference divider values */
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	u32 x_mpll_ref_fb_div;
95162306a36Sopenharmony_ci	u32 xclk_cntl;
95262306a36Sopenharmony_ci	u32 Nx, M;
95362306a36Sopenharmony_ci	static const unsigned int PostDivSet[] = { 0, 1, 2, 4, 8, 3, 6, 12 };
95462306a36Sopenharmony_ci#endif
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (!par->constants.ref_clk)
95762306a36Sopenharmony_ci		par->constants.ref_clk = 2950;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci#ifdef CONFIG_PPC
96062306a36Sopenharmony_ci	x_mpll_ref_fb_div = aty_ld_pll(X_MPLL_REF_FB_DIV);
96162306a36Sopenharmony_ci	xclk_cntl = aty_ld_pll(XCLK_CNTL) & 0x7;
96262306a36Sopenharmony_ci	Nx = (x_mpll_ref_fb_div & 0x00ff00) >> 8;
96362306a36Sopenharmony_ci	M  = x_mpll_ref_fb_div & 0x0000ff;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	par->constants.xclk = round_div((2 * Nx * par->constants.ref_clk),
96662306a36Sopenharmony_ci					(M * PostDivSet[xclk_cntl]));
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	par->constants.ref_divider =
96962306a36Sopenharmony_ci		aty_ld_pll(PPLL_REF_DIV) & PPLL_REF_DIV_MASK;
97062306a36Sopenharmony_ci#endif
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (!par->constants.ref_divider) {
97362306a36Sopenharmony_ci		par->constants.ref_divider = 0x3b;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci		aty_st_pll(X_MPLL_REF_FB_DIV, 0x004c4c1e);
97662306a36Sopenharmony_ci		aty_pll_writeupdate(par);
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci	aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider);
97962306a36Sopenharmony_ci	aty_pll_writeupdate(par);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	/* from documentation */
98262306a36Sopenharmony_ci	if (!par->constants.ppll_min)
98362306a36Sopenharmony_ci		par->constants.ppll_min = 12500;
98462306a36Sopenharmony_ci	if (!par->constants.ppll_max)
98562306a36Sopenharmony_ci		par->constants.ppll_max = 25000;    /* 23000 on some cards? */
98662306a36Sopenharmony_ci	if (!par->constants.xclk)
98762306a36Sopenharmony_ci		par->constants.xclk = 0x1d4d;	     /* same as mclk */
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	par->constants.fifo_width = 128;
99062306a36Sopenharmony_ci	par->constants.fifo_depth = 32;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	switch (aty_ld_le32(MEM_CNTL) & 0x3) {
99362306a36Sopenharmony_ci	case 0:
99462306a36Sopenharmony_ci		par->mem = &sdr_128;
99562306a36Sopenharmony_ci		break;
99662306a36Sopenharmony_ci	case 1:
99762306a36Sopenharmony_ci		par->mem = &sdr_sgram;
99862306a36Sopenharmony_ci		break;
99962306a36Sopenharmony_ci	case 2:
100062306a36Sopenharmony_ci		par->mem = &ddr_sgram;
100162306a36Sopenharmony_ci		break;
100262306a36Sopenharmony_ci	default:
100362306a36Sopenharmony_ci		par->mem = &sdr_sgram;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci/*
101062306a36Sopenharmony_ci * CRTC programming
101162306a36Sopenharmony_ci */
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci/* Program the CRTC registers */
101462306a36Sopenharmony_cistatic void aty128_set_crtc(const struct aty128_crtc *crtc,
101562306a36Sopenharmony_ci			    const struct aty128fb_par *par)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl);
101862306a36Sopenharmony_ci	aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_total);
101962306a36Sopenharmony_ci	aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid);
102062306a36Sopenharmony_ci	aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_total);
102162306a36Sopenharmony_ci	aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid);
102262306a36Sopenharmony_ci	aty_st_le32(CRTC_PITCH, crtc->pitch);
102362306a36Sopenharmony_ci	aty_st_le32(CRTC_OFFSET, crtc->offset);
102462306a36Sopenharmony_ci	aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
102562306a36Sopenharmony_ci	/* Disable ATOMIC updating.  Is this the right place? */
102662306a36Sopenharmony_ci	aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000));
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_cistatic int aty128_var_to_crtc(const struct fb_var_screeninfo *var,
103162306a36Sopenharmony_ci			      struct aty128_crtc *crtc,
103262306a36Sopenharmony_ci			      const struct aty128fb_par *par)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst;
103562306a36Sopenharmony_ci	u32 left, right, upper, lower, hslen, vslen, sync, vmode;
103662306a36Sopenharmony_ci	u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol;
103762306a36Sopenharmony_ci	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
103862306a36Sopenharmony_ci	u32 depth, bytpp;
103962306a36Sopenharmony_ci	u8 mode_bytpp[7] = { 0, 0, 1, 2, 2, 3, 4 };
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* input */
104262306a36Sopenharmony_ci	xres = var->xres;
104362306a36Sopenharmony_ci	yres = var->yres;
104462306a36Sopenharmony_ci	vxres   = var->xres_virtual;
104562306a36Sopenharmony_ci	vyres   = var->yres_virtual;
104662306a36Sopenharmony_ci	xoffset = var->xoffset;
104762306a36Sopenharmony_ci	yoffset = var->yoffset;
104862306a36Sopenharmony_ci	bpp   = var->bits_per_pixel;
104962306a36Sopenharmony_ci	left  = var->left_margin;
105062306a36Sopenharmony_ci	right = var->right_margin;
105162306a36Sopenharmony_ci	upper = var->upper_margin;
105262306a36Sopenharmony_ci	lower = var->lower_margin;
105362306a36Sopenharmony_ci	hslen = var->hsync_len;
105462306a36Sopenharmony_ci	vslen = var->vsync_len;
105562306a36Sopenharmony_ci	sync  = var->sync;
105662306a36Sopenharmony_ci	vmode = var->vmode;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (bpp != 16)
105962306a36Sopenharmony_ci		depth = bpp;
106062306a36Sopenharmony_ci	else
106162306a36Sopenharmony_ci		depth = (var->green.length == 6) ? 16 : 15;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/* check for mode eligibility
106462306a36Sopenharmony_ci	 * accept only non interlaced modes */
106562306a36Sopenharmony_ci	if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
106662306a36Sopenharmony_ci		return -EINVAL;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	/* convert (and round up) and validate */
106962306a36Sopenharmony_ci	xres = (xres + 7) & ~7;
107062306a36Sopenharmony_ci	xoffset = (xoffset + 7) & ~7;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (vxres < xres + xoffset)
107362306a36Sopenharmony_ci		vxres = xres + xoffset;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	if (vyres < yres + yoffset)
107662306a36Sopenharmony_ci		vyres = yres + yoffset;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* convert depth into ATI register depth */
107962306a36Sopenharmony_ci	dst = depth_to_dst(depth);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (dst == -EINVAL) {
108262306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n");
108362306a36Sopenharmony_ci		return -EINVAL;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* convert register depth to bytes per pixel */
108762306a36Sopenharmony_ci	bytpp = mode_bytpp[dst];
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	/* make sure there is enough video ram for the mode */
109062306a36Sopenharmony_ci	if ((u32)(vxres * vyres * bytpp) > par->vram_size) {
109162306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: Not enough memory for mode\n");
109262306a36Sopenharmony_ci		return -EINVAL;
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	h_disp = (xres >> 3) - 1;
109662306a36Sopenharmony_ci	h_total = (((xres + right + hslen + left) >> 3) - 1) & 0xFFFFL;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	v_disp = yres - 1;
109962306a36Sopenharmony_ci	v_total = (yres + upper + vslen + lower - 1) & 0xFFFFL;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	/* check to make sure h_total and v_total are in range */
110262306a36Sopenharmony_ci	if (((h_total >> 3) - 1) > 0x1ff || (v_total - 1) > 0x7FF) {
110362306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: invalid width ranges\n");
110462306a36Sopenharmony_ci		return -EINVAL;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	h_sync_wid = (hslen + 7) >> 3;
110862306a36Sopenharmony_ci	if (h_sync_wid == 0)
110962306a36Sopenharmony_ci		h_sync_wid = 1;
111062306a36Sopenharmony_ci	else if (h_sync_wid > 0x3f)        /* 0x3f = max hwidth */
111162306a36Sopenharmony_ci		h_sync_wid = 0x3f;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	h_sync_strt = (h_disp << 3) + right;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	v_sync_wid = vslen;
111662306a36Sopenharmony_ci	if (v_sync_wid == 0)
111762306a36Sopenharmony_ci		v_sync_wid = 1;
111862306a36Sopenharmony_ci	else if (v_sync_wid > 0x1f)        /* 0x1f = max vwidth */
111962306a36Sopenharmony_ci		v_sync_wid = 0x1f;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	v_sync_strt = v_disp + lower;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
112462306a36Sopenharmony_ci	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	crtc->h_total = h_total | (h_disp << 16);
113162306a36Sopenharmony_ci	crtc->v_total = v_total | (v_disp << 16);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	crtc->h_sync_strt_wid = h_sync_strt | (h_sync_wid << 16) |
113462306a36Sopenharmony_ci	        (h_sync_pol << 23);
113562306a36Sopenharmony_ci	crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
113662306a36Sopenharmony_ci                (v_sync_pol << 23);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	crtc->pitch = vxres >> 3;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	crtc->offset = 0;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
114362306a36Sopenharmony_ci		crtc->offset_cntl = 0x00010000;
114462306a36Sopenharmony_ci	else
114562306a36Sopenharmony_ci		crtc->offset_cntl = 0;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	crtc->vxres = vxres;
114862306a36Sopenharmony_ci	crtc->vyres = vyres;
114962306a36Sopenharmony_ci	crtc->xoffset = xoffset;
115062306a36Sopenharmony_ci	crtc->yoffset = yoffset;
115162306a36Sopenharmony_ci	crtc->depth = depth;
115262306a36Sopenharmony_ci	crtc->bpp = bpp;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	return 0;
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic int aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* fill in pixel info */
116262306a36Sopenharmony_ci	var->red.msb_right = 0;
116362306a36Sopenharmony_ci	var->green.msb_right = 0;
116462306a36Sopenharmony_ci	var->blue.offset = 0;
116562306a36Sopenharmony_ci	var->blue.msb_right = 0;
116662306a36Sopenharmony_ci	var->transp.offset = 0;
116762306a36Sopenharmony_ci	var->transp.length = 0;
116862306a36Sopenharmony_ci	var->transp.msb_right = 0;
116962306a36Sopenharmony_ci	switch (pix_width) {
117062306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_8BPP:
117162306a36Sopenharmony_ci		var->bits_per_pixel = 8;
117262306a36Sopenharmony_ci		var->red.offset = 0;
117362306a36Sopenharmony_ci		var->red.length = 8;
117462306a36Sopenharmony_ci		var->green.offset = 0;
117562306a36Sopenharmony_ci		var->green.length = 8;
117662306a36Sopenharmony_ci		var->blue.length = 8;
117762306a36Sopenharmony_ci		break;
117862306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_15BPP:
117962306a36Sopenharmony_ci		var->bits_per_pixel = 16;
118062306a36Sopenharmony_ci		var->red.offset = 10;
118162306a36Sopenharmony_ci		var->red.length = 5;
118262306a36Sopenharmony_ci		var->green.offset = 5;
118362306a36Sopenharmony_ci		var->green.length = 5;
118462306a36Sopenharmony_ci		var->blue.length = 5;
118562306a36Sopenharmony_ci		break;
118662306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_16BPP:
118762306a36Sopenharmony_ci		var->bits_per_pixel = 16;
118862306a36Sopenharmony_ci		var->red.offset = 11;
118962306a36Sopenharmony_ci		var->red.length = 5;
119062306a36Sopenharmony_ci		var->green.offset = 5;
119162306a36Sopenharmony_ci		var->green.length = 6;
119262306a36Sopenharmony_ci		var->blue.length = 5;
119362306a36Sopenharmony_ci		break;
119462306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_24BPP:
119562306a36Sopenharmony_ci		var->bits_per_pixel = 24;
119662306a36Sopenharmony_ci		var->red.offset = 16;
119762306a36Sopenharmony_ci		var->red.length = 8;
119862306a36Sopenharmony_ci		var->green.offset = 8;
119962306a36Sopenharmony_ci		var->green.length = 8;
120062306a36Sopenharmony_ci		var->blue.length = 8;
120162306a36Sopenharmony_ci		break;
120262306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_32BPP:
120362306a36Sopenharmony_ci		var->bits_per_pixel = 32;
120462306a36Sopenharmony_ci		var->red.offset = 16;
120562306a36Sopenharmony_ci		var->red.length = 8;
120662306a36Sopenharmony_ci		var->green.offset = 8;
120762306a36Sopenharmony_ci		var->green.length = 8;
120862306a36Sopenharmony_ci		var->blue.length = 8;
120962306a36Sopenharmony_ci		var->transp.offset = 24;
121062306a36Sopenharmony_ci		var->transp.length = 8;
121162306a36Sopenharmony_ci		break;
121262306a36Sopenharmony_ci	default:
121362306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: Invalid pixel width\n");
121462306a36Sopenharmony_ci		return -EINVAL;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	return 0;
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_cistatic int aty128_crtc_to_var(const struct aty128_crtc *crtc,
122262306a36Sopenharmony_ci			      struct fb_var_screeninfo *var)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci	u32 xres, yres, left, right, upper, lower, hslen, vslen, sync;
122562306a36Sopenharmony_ci	u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
122662306a36Sopenharmony_ci	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
122762306a36Sopenharmony_ci	u32 pix_width;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	/* fun with masking */
123062306a36Sopenharmony_ci	h_total     = crtc->h_total & 0x1ff;
123162306a36Sopenharmony_ci	h_disp      = (crtc->h_total >> 16) & 0xff;
123262306a36Sopenharmony_ci	h_sync_strt = (crtc->h_sync_strt_wid >> 3) & 0x1ff;
123362306a36Sopenharmony_ci	h_sync_dly  = crtc->h_sync_strt_wid & 0x7;
123462306a36Sopenharmony_ci	h_sync_wid  = (crtc->h_sync_strt_wid >> 16) & 0x3f;
123562306a36Sopenharmony_ci	h_sync_pol  = (crtc->h_sync_strt_wid >> 23) & 0x1;
123662306a36Sopenharmony_ci	v_total     = crtc->v_total & 0x7ff;
123762306a36Sopenharmony_ci	v_disp      = (crtc->v_total >> 16) & 0x7ff;
123862306a36Sopenharmony_ci	v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
123962306a36Sopenharmony_ci	v_sync_wid  = (crtc->v_sync_strt_wid >> 16) & 0x1f;
124062306a36Sopenharmony_ci	v_sync_pol  = (crtc->v_sync_strt_wid >> 23) & 0x1;
124162306a36Sopenharmony_ci	c_sync      = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
124262306a36Sopenharmony_ci	pix_width   = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* do conversions */
124562306a36Sopenharmony_ci	xres  = (h_disp + 1) << 3;
124662306a36Sopenharmony_ci	yres  = v_disp + 1;
124762306a36Sopenharmony_ci	left  = ((h_total - h_sync_strt - h_sync_wid) << 3) - h_sync_dly;
124862306a36Sopenharmony_ci	right = ((h_sync_strt - h_disp) << 3) + h_sync_dly;
124962306a36Sopenharmony_ci	hslen = h_sync_wid << 3;
125062306a36Sopenharmony_ci	upper = v_total - v_sync_strt - v_sync_wid;
125162306a36Sopenharmony_ci	lower = v_sync_strt - v_disp;
125262306a36Sopenharmony_ci	vslen = v_sync_wid;
125362306a36Sopenharmony_ci	sync  = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
125462306a36Sopenharmony_ci		(v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
125562306a36Sopenharmony_ci		(c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	aty128_pix_width_to_var(pix_width, var);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	var->xres = xres;
126062306a36Sopenharmony_ci	var->yres = yres;
126162306a36Sopenharmony_ci	var->xres_virtual = crtc->vxres;
126262306a36Sopenharmony_ci	var->yres_virtual = crtc->vyres;
126362306a36Sopenharmony_ci	var->xoffset = crtc->xoffset;
126462306a36Sopenharmony_ci	var->yoffset = crtc->yoffset;
126562306a36Sopenharmony_ci	var->left_margin  = left;
126662306a36Sopenharmony_ci	var->right_margin = right;
126762306a36Sopenharmony_ci	var->upper_margin = upper;
126862306a36Sopenharmony_ci	var->lower_margin = lower;
126962306a36Sopenharmony_ci	var->hsync_len = hslen;
127062306a36Sopenharmony_ci	var->vsync_len = vslen;
127162306a36Sopenharmony_ci	var->sync  = sync;
127262306a36Sopenharmony_ci	var->vmode = FB_VMODE_NONINTERLACED;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	return 0;
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic void aty128_set_crt_enable(struct aty128fb_par *par, int on)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	if (on) {
128062306a36Sopenharmony_ci		aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) |
128162306a36Sopenharmony_ci			    CRT_CRTC_ON);
128262306a36Sopenharmony_ci		aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) |
128362306a36Sopenharmony_ci			    DAC_PALETTE2_SNOOP_EN));
128462306a36Sopenharmony_ci	} else
128562306a36Sopenharmony_ci		aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) &
128662306a36Sopenharmony_ci			    ~CRT_CRTC_ON);
128762306a36Sopenharmony_ci}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	u32 reg;
129262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
129362306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pdev);
129462306a36Sopenharmony_ci#endif
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	if (on) {
129762306a36Sopenharmony_ci		reg = aty_ld_le32(LVDS_GEN_CNTL);
129862306a36Sopenharmony_ci		reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION;
129962306a36Sopenharmony_ci		reg &= ~LVDS_DISPLAY_DIS;
130062306a36Sopenharmony_ci		aty_st_le32(LVDS_GEN_CNTL, reg);
130162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
130262306a36Sopenharmony_ci		aty128_bl_set_power(info, FB_BLANK_UNBLANK);
130362306a36Sopenharmony_ci#endif
130462306a36Sopenharmony_ci	} else {
130562306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
130662306a36Sopenharmony_ci		aty128_bl_set_power(info, FB_BLANK_POWERDOWN);
130762306a36Sopenharmony_ci#endif
130862306a36Sopenharmony_ci		reg = aty_ld_le32(LVDS_GEN_CNTL);
130962306a36Sopenharmony_ci		reg |= LVDS_DISPLAY_DIS;
131062306a36Sopenharmony_ci		aty_st_le32(LVDS_GEN_CNTL, reg);
131162306a36Sopenharmony_ci		mdelay(100);
131262306a36Sopenharmony_ci		reg &= ~(LVDS_ON /*| LVDS_EN*/);
131362306a36Sopenharmony_ci		aty_st_le32(LVDS_GEN_CNTL, reg);
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic void aty128_set_pll(struct aty128_pll *pll,
131862306a36Sopenharmony_ci			   const struct aty128fb_par *par)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	u32 div3;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	/* register values for post dividers */
132362306a36Sopenharmony_ci	static const unsigned char post_conv[] = {
132462306a36Sopenharmony_ci		2, 0, 1, 4, 2, 2, 6, 2, 3, 2, 2, 2, 7
132562306a36Sopenharmony_ci	};
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	/* select PPLL_DIV_3 */
132862306a36Sopenharmony_ci	aty_st_le32(CLOCK_CNTL_INDEX, aty_ld_le32(CLOCK_CNTL_INDEX) | (3 << 8));
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* reset PLL */
133162306a36Sopenharmony_ci	aty_st_pll(PPLL_CNTL,
133262306a36Sopenharmony_ci		   aty_ld_pll(PPLL_CNTL) | PPLL_RESET | PPLL_ATOMIC_UPDATE_EN);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	/* write the reference divider */
133562306a36Sopenharmony_ci	aty_pll_wait_readupdate(par);
133662306a36Sopenharmony_ci	aty_st_pll(PPLL_REF_DIV, par->constants.ref_divider & 0x3ff);
133762306a36Sopenharmony_ci	aty_pll_writeupdate(par);
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	div3 = aty_ld_pll(PPLL_DIV_3);
134062306a36Sopenharmony_ci	div3 &= ~PPLL_FB3_DIV_MASK;
134162306a36Sopenharmony_ci	div3 |= pll->feedback_divider;
134262306a36Sopenharmony_ci	div3 &= ~PPLL_POST3_DIV_MASK;
134362306a36Sopenharmony_ci	div3 |= post_conv[pll->post_divider] << 16;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* write feedback and post dividers */
134662306a36Sopenharmony_ci	aty_pll_wait_readupdate(par);
134762306a36Sopenharmony_ci	aty_st_pll(PPLL_DIV_3, div3);
134862306a36Sopenharmony_ci	aty_pll_writeupdate(par);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	aty_pll_wait_readupdate(par);
135162306a36Sopenharmony_ci	aty_st_pll(HTOTAL_CNTL, 0);	/* no horiz crtc adjustment */
135262306a36Sopenharmony_ci	aty_pll_writeupdate(par);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/* clear the reset, just in case */
135562306a36Sopenharmony_ci	aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET);
135662306a36Sopenharmony_ci}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cistatic int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
136062306a36Sopenharmony_ci			     const struct aty128fb_par *par)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	const struct aty128_constants c = par->constants;
136362306a36Sopenharmony_ci	static const unsigned char post_dividers[] = { 1, 2, 4, 8, 3, 6, 12 };
136462306a36Sopenharmony_ci	u32 output_freq;
136562306a36Sopenharmony_ci	u32 vclk;        /* in .01 MHz */
136662306a36Sopenharmony_ci	int i = 0;
136762306a36Sopenharmony_ci	u32 n, d;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	vclk = 100000000 / period_in_ps;	/* convert units to 10 kHz */
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* adjust pixel clock if necessary */
137262306a36Sopenharmony_ci	if (vclk > c.ppll_max)
137362306a36Sopenharmony_ci		vclk = c.ppll_max;
137462306a36Sopenharmony_ci	if (vclk * 12 < c.ppll_min)
137562306a36Sopenharmony_ci		vclk = c.ppll_min/12;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/* now, find an acceptable divider */
137862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(post_dividers); i++) {
137962306a36Sopenharmony_ci		output_freq = post_dividers[i] * vclk;
138062306a36Sopenharmony_ci		if (output_freq >= c.ppll_min && output_freq <= c.ppll_max) {
138162306a36Sopenharmony_ci			pll->post_divider = post_dividers[i];
138262306a36Sopenharmony_ci			break;
138362306a36Sopenharmony_ci		}
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if (i == ARRAY_SIZE(post_dividers))
138762306a36Sopenharmony_ci		return -EINVAL;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	/* calculate feedback divider */
139062306a36Sopenharmony_ci	n = c.ref_divider * output_freq;
139162306a36Sopenharmony_ci	d = c.ref_clk;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	pll->feedback_divider = round_div(n, d);
139462306a36Sopenharmony_ci	pll->vclk = vclk;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	DBG("post %d feedback %d vlck %d output %d ref_divider %d "
139762306a36Sopenharmony_ci	    "vclk_per: %d\n", pll->post_divider,
139862306a36Sopenharmony_ci	    pll->feedback_divider, vclk, output_freq,
139962306a36Sopenharmony_ci	    c.ref_divider, period_in_ps);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	return 0;
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_cistatic int aty128_pll_to_var(const struct aty128_pll *pll,
140662306a36Sopenharmony_ci			     struct fb_var_screeninfo *var)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	var->pixclock = 100000000 / pll->vclk;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	return 0;
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_cistatic void aty128_set_fifo(const struct aty128_ddafifo *dsp,
141562306a36Sopenharmony_ci			    const struct aty128fb_par *par)
141662306a36Sopenharmony_ci{
141762306a36Sopenharmony_ci	aty_st_le32(DDA_CONFIG, dsp->dda_config);
141862306a36Sopenharmony_ci	aty_st_le32(DDA_ON_OFF, dsp->dda_on_off);
141962306a36Sopenharmony_ci}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic int aty128_ddafifo(struct aty128_ddafifo *dsp,
142362306a36Sopenharmony_ci			  const struct aty128_pll *pll,
142462306a36Sopenharmony_ci			  u32 depth,
142562306a36Sopenharmony_ci			  const struct aty128fb_par *par)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	const struct aty128_meminfo *m = par->mem;
142862306a36Sopenharmony_ci	u32 xclk = par->constants.xclk;
142962306a36Sopenharmony_ci	u32 fifo_width = par->constants.fifo_width;
143062306a36Sopenharmony_ci	u32 fifo_depth = par->constants.fifo_depth;
143162306a36Sopenharmony_ci	s32 x, b, p, ron, roff;
143262306a36Sopenharmony_ci	u32 n, d, bpp;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	/* round up to multiple of 8 */
143562306a36Sopenharmony_ci	bpp = (depth+7) & ~7;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	n = xclk * fifo_width;
143862306a36Sopenharmony_ci	d = pll->vclk * bpp;
143962306a36Sopenharmony_ci	x = round_div(n, d);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	ron = 4 * m->MB +
144262306a36Sopenharmony_ci		3 * ((m->Trcd - 2 > 0) ? m->Trcd - 2 : 0) +
144362306a36Sopenharmony_ci		2 * m->Trp +
144462306a36Sopenharmony_ci		m->Twr +
144562306a36Sopenharmony_ci		m->CL +
144662306a36Sopenharmony_ci		m->Tr2w +
144762306a36Sopenharmony_ci		x;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	DBG("x %x\n", x);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	b = 0;
145262306a36Sopenharmony_ci	while (x) {
145362306a36Sopenharmony_ci		x >>= 1;
145462306a36Sopenharmony_ci		b++;
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci	p = b + 1;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	ron <<= (11 - p);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	n <<= (11 - p);
146162306a36Sopenharmony_ci	x = round_div(n, d);
146262306a36Sopenharmony_ci	roff = x * (fifo_depth - 4);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	if ((ron + m->Rloop) >= roff) {
146562306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: Mode out of range!\n");
146662306a36Sopenharmony_ci		return -EINVAL;
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	DBG("p: %x rloop: %x x: %x ron: %x roff: %x\n",
147062306a36Sopenharmony_ci	    p, m->Rloop, x, ron, roff);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	dsp->dda_config = p << 16 | m->Rloop << 20 | x;
147362306a36Sopenharmony_ci	dsp->dda_on_off = ron << 16 | roff;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	return 0;
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci/*
148062306a36Sopenharmony_ci * This actually sets the video mode.
148162306a36Sopenharmony_ci */
148262306a36Sopenharmony_cistatic int aty128fb_set_par(struct fb_info *info)
148362306a36Sopenharmony_ci{
148462306a36Sopenharmony_ci	struct aty128fb_par *par = info->par;
148562306a36Sopenharmony_ci	u32 config;
148662306a36Sopenharmony_ci	int err;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if ((err = aty128_decode_var(&info->var, par)) != 0)
148962306a36Sopenharmony_ci		return err;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	if (par->blitter_may_be_busy)
149262306a36Sopenharmony_ci		wait_for_idle(par);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* clear all registers that may interfere with mode setting */
149562306a36Sopenharmony_ci	aty_st_le32(OVR_CLR, 0);
149662306a36Sopenharmony_ci	aty_st_le32(OVR_WID_LEFT_RIGHT, 0);
149762306a36Sopenharmony_ci	aty_st_le32(OVR_WID_TOP_BOTTOM, 0);
149862306a36Sopenharmony_ci	aty_st_le32(OV0_SCALE_CNTL, 0);
149962306a36Sopenharmony_ci	aty_st_le32(MPP_TB_CONFIG, 0);
150062306a36Sopenharmony_ci	aty_st_le32(MPP_GP_CONFIG, 0);
150162306a36Sopenharmony_ci	aty_st_le32(SUBPIC_CNTL, 0);
150262306a36Sopenharmony_ci	aty_st_le32(VIPH_CONTROL, 0);
150362306a36Sopenharmony_ci	aty_st_le32(I2C_CNTL_1, 0);         /* turn off i2c */
150462306a36Sopenharmony_ci	aty_st_le32(GEN_INT_CNTL, 0);	/* turn off interrupts */
150562306a36Sopenharmony_ci	aty_st_le32(CAP0_TRIG_CNTL, 0);
150662306a36Sopenharmony_ci	aty_st_le32(CAP1_TRIG_CNTL, 0);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	aty_st_8(CRTC_EXT_CNTL + 1, 4);	/* turn video off */
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	aty128_set_crtc(&par->crtc, par);
151162306a36Sopenharmony_ci	aty128_set_pll(&par->pll, par);
151262306a36Sopenharmony_ci	aty128_set_fifo(&par->fifo_reg, par);
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	config = aty_ld_le32(CNFG_CNTL) & ~3;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci#if defined(__BIG_ENDIAN)
151762306a36Sopenharmony_ci	if (par->crtc.bpp == 32)
151862306a36Sopenharmony_ci		config |= 2;	/* make aperture do 32 bit swapping */
151962306a36Sopenharmony_ci	else if (par->crtc.bpp == 16)
152062306a36Sopenharmony_ci		config |= 1;	/* make aperture do 16 bit swapping */
152162306a36Sopenharmony_ci#endif
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	aty_st_le32(CNFG_CNTL, config);
152462306a36Sopenharmony_ci	aty_st_8(CRTC_EXT_CNTL + 1, 0);	/* turn the video back on */
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	info->fix.line_length = (par->crtc.vxres * par->crtc.bpp) >> 3;
152762306a36Sopenharmony_ci	info->fix.visual = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR
152862306a36Sopenharmony_ci		: FB_VISUAL_DIRECTCOLOR;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	if (par->chip_gen == rage_M3) {
153162306a36Sopenharmony_ci		aty128_set_crt_enable(par, par->crt_on);
153262306a36Sopenharmony_ci		aty128_set_lcd_enable(par, par->lcd_on);
153362306a36Sopenharmony_ci	}
153462306a36Sopenharmony_ci	if (par->accel_flags & FB_ACCELF_TEXT)
153562306a36Sopenharmony_ci		aty128_init_engine(par);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
153862306a36Sopenharmony_ci	btext_update_display(info->fix.smem_start,
153962306a36Sopenharmony_ci			     (((par->crtc.h_total>>16) & 0xff)+1)*8,
154062306a36Sopenharmony_ci			     ((par->crtc.v_total>>16) & 0x7ff)+1,
154162306a36Sopenharmony_ci			     par->crtc.bpp,
154262306a36Sopenharmony_ci			     par->crtc.vxres*par->crtc.bpp/8);
154362306a36Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	return 0;
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci/*
154962306a36Sopenharmony_ci *  encode/decode the User Defined Part of the Display
155062306a36Sopenharmony_ci */
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cistatic int aty128_decode_var(struct fb_var_screeninfo *var,
155362306a36Sopenharmony_ci			     struct aty128fb_par *par)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	int err;
155662306a36Sopenharmony_ci	struct aty128_crtc crtc;
155762306a36Sopenharmony_ci	struct aty128_pll pll;
155862306a36Sopenharmony_ci	struct aty128_ddafifo fifo_reg;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	if ((err = aty128_var_to_crtc(var, &crtc, par)))
156162306a36Sopenharmony_ci		return err;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	if ((err = aty128_var_to_pll(var->pixclock, &pll, par)))
156462306a36Sopenharmony_ci		return err;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	if ((err = aty128_ddafifo(&fifo_reg, &pll, crtc.depth, par)))
156762306a36Sopenharmony_ci		return err;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	par->crtc = crtc;
157062306a36Sopenharmony_ci	par->pll = pll;
157162306a36Sopenharmony_ci	par->fifo_reg = fifo_reg;
157262306a36Sopenharmony_ci	par->accel_flags = var->accel_flags;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	return 0;
157562306a36Sopenharmony_ci}
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistatic int aty128_encode_var(struct fb_var_screeninfo *var,
157962306a36Sopenharmony_ci			     const struct aty128fb_par *par)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	int err;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	if ((err = aty128_crtc_to_var(&par->crtc, var)))
158462306a36Sopenharmony_ci		return err;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	if ((err = aty128_pll_to_var(&par->pll, var)))
158762306a36Sopenharmony_ci		return err;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	var->nonstd = 0;
159062306a36Sopenharmony_ci	var->activate = 0;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	var->height = -1;
159362306a36Sopenharmony_ci	var->width = -1;
159462306a36Sopenharmony_ci	var->accel_flags = par->accel_flags;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	return 0;
159762306a36Sopenharmony_ci}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_cistatic int aty128fb_check_var(struct fb_var_screeninfo *var,
160162306a36Sopenharmony_ci			      struct fb_info *info)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci	struct aty128fb_par par;
160462306a36Sopenharmony_ci	int err;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	par = *(struct aty128fb_par *)info->par;
160762306a36Sopenharmony_ci	if ((err = aty128_decode_var(var, &par)) != 0)
160862306a36Sopenharmony_ci		return err;
160962306a36Sopenharmony_ci	aty128_encode_var(var, &par);
161062306a36Sopenharmony_ci	return 0;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci/*
161562306a36Sopenharmony_ci *  Pan or Wrap the Display
161662306a36Sopenharmony_ci */
161762306a36Sopenharmony_cistatic int aty128fb_pan_display(struct fb_var_screeninfo *var,
161862306a36Sopenharmony_ci				struct fb_info *fb)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct aty128fb_par *par = fb->par;
162162306a36Sopenharmony_ci	u32 xoffset, yoffset;
162262306a36Sopenharmony_ci	u32 offset;
162362306a36Sopenharmony_ci	u32 xres, yres;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	xres = (((par->crtc.h_total >> 16) & 0xff) + 1) << 3;
162662306a36Sopenharmony_ci	yres = ((par->crtc.v_total >> 16) & 0x7ff) + 1;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	xoffset = (var->xoffset +7) & ~7;
162962306a36Sopenharmony_ci	yoffset = var->yoffset;
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	if (xoffset+xres > par->crtc.vxres || yoffset+yres > par->crtc.vyres)
163262306a36Sopenharmony_ci		return -EINVAL;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	par->crtc.xoffset = xoffset;
163562306a36Sopenharmony_ci	par->crtc.yoffset = yoffset;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3))
163862306a36Sopenharmony_ci									  & ~7;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	if (par->crtc.bpp == 24)
164162306a36Sopenharmony_ci		offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	aty_st_le32(CRTC_OFFSET, offset);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	return 0;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci/*
165062306a36Sopenharmony_ci *  Helper function to store a single palette register
165162306a36Sopenharmony_ci */
165262306a36Sopenharmony_cistatic void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
165362306a36Sopenharmony_ci			  struct aty128fb_par *par)
165462306a36Sopenharmony_ci{
165562306a36Sopenharmony_ci	if (par->chip_gen == rage_M3) {
165662306a36Sopenharmony_ci		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) &
165762306a36Sopenharmony_ci			    ~DAC_PALETTE_ACCESS_CNTL);
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	aty_st_8(PALETTE_INDEX, regno);
166162306a36Sopenharmony_ci	aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
166262306a36Sopenharmony_ci}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_cistatic int aty128fb_sync(struct fb_info *info)
166562306a36Sopenharmony_ci{
166662306a36Sopenharmony_ci	struct aty128fb_par *par = info->par;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	if (par->blitter_may_be_busy)
166962306a36Sopenharmony_ci		wait_for_idle(par);
167062306a36Sopenharmony_ci	return 0;
167162306a36Sopenharmony_ci}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci#ifndef MODULE
167462306a36Sopenharmony_cistatic int aty128fb_setup(char *options)
167562306a36Sopenharmony_ci{
167662306a36Sopenharmony_ci	char *this_opt;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	if (!options || !*options)
167962306a36Sopenharmony_ci		return 0;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
168262306a36Sopenharmony_ci		if (!strncmp(this_opt, "lcd:", 4)) {
168362306a36Sopenharmony_ci			default_lcd_on = simple_strtoul(this_opt+4, NULL, 0);
168462306a36Sopenharmony_ci			continue;
168562306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "crt:", 4)) {
168662306a36Sopenharmony_ci			default_crt_on = simple_strtoul(this_opt+4, NULL, 0);
168762306a36Sopenharmony_ci			continue;
168862306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "backlight:", 10)) {
168962306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
169062306a36Sopenharmony_ci			backlight = simple_strtoul(this_opt+10, NULL, 0);
169162306a36Sopenharmony_ci#endif
169262306a36Sopenharmony_ci			continue;
169362306a36Sopenharmony_ci		}
169462306a36Sopenharmony_ci		if(!strncmp(this_opt, "nomtrr", 6)) {
169562306a36Sopenharmony_ci			mtrr = false;
169662306a36Sopenharmony_ci			continue;
169762306a36Sopenharmony_ci		}
169862306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
169962306a36Sopenharmony_ci		/* vmode and cmode deprecated */
170062306a36Sopenharmony_ci		if (!strncmp(this_opt, "vmode:", 6)) {
170162306a36Sopenharmony_ci			unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
170262306a36Sopenharmony_ci			if (vmode > 0 && vmode <= VMODE_MAX)
170362306a36Sopenharmony_ci				default_vmode = vmode;
170462306a36Sopenharmony_ci			continue;
170562306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "cmode:", 6)) {
170662306a36Sopenharmony_ci			unsigned int cmode = simple_strtoul(this_opt+6, NULL, 0);
170762306a36Sopenharmony_ci			switch (cmode) {
170862306a36Sopenharmony_ci			case 0:
170962306a36Sopenharmony_ci			case 8:
171062306a36Sopenharmony_ci				default_cmode = CMODE_8;
171162306a36Sopenharmony_ci				break;
171262306a36Sopenharmony_ci			case 15:
171362306a36Sopenharmony_ci			case 16:
171462306a36Sopenharmony_ci				default_cmode = CMODE_16;
171562306a36Sopenharmony_ci				break;
171662306a36Sopenharmony_ci			case 24:
171762306a36Sopenharmony_ci			case 32:
171862306a36Sopenharmony_ci				default_cmode = CMODE_32;
171962306a36Sopenharmony_ci				break;
172062306a36Sopenharmony_ci			}
172162306a36Sopenharmony_ci			continue;
172262306a36Sopenharmony_ci		}
172362306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
172462306a36Sopenharmony_ci		mode_option = this_opt;
172562306a36Sopenharmony_ci	}
172662306a36Sopenharmony_ci	return 0;
172762306a36Sopenharmony_ci}
172862306a36Sopenharmony_ci#endif  /*  MODULE  */
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci/* Backlight */
173162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
173262306a36Sopenharmony_ci#define MAX_LEVEL 0xFF
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_cistatic int aty128_bl_get_level_brightness(struct aty128fb_par *par,
173562306a36Sopenharmony_ci		int level)
173662306a36Sopenharmony_ci{
173762306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pdev);
173862306a36Sopenharmony_ci	int atylevel;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	/* Get and convert the value */
174162306a36Sopenharmony_ci	/* No locking of bl_curve since we read a single value */
174262306a36Sopenharmony_ci	atylevel = MAX_LEVEL -
174362306a36Sopenharmony_ci		(info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL);
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	if (atylevel < 0)
174662306a36Sopenharmony_ci		atylevel = 0;
174762306a36Sopenharmony_ci	else if (atylevel > MAX_LEVEL)
174862306a36Sopenharmony_ci		atylevel = MAX_LEVEL;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	return atylevel;
175162306a36Sopenharmony_ci}
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci/* We turn off the LCD completely instead of just dimming the backlight.
175462306a36Sopenharmony_ci * This provides greater power saving and the display is useless without
175562306a36Sopenharmony_ci * backlight anyway
175662306a36Sopenharmony_ci */
175762306a36Sopenharmony_ci#define BACKLIGHT_LVDS_OFF
175862306a36Sopenharmony_ci/* That one prevents proper CRT output with LCD off */
175962306a36Sopenharmony_ci#undef BACKLIGHT_DAC_OFF
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_cistatic int aty128_bl_update_status(struct backlight_device *bd)
176262306a36Sopenharmony_ci{
176362306a36Sopenharmony_ci	struct aty128fb_par *par = bl_get_data(bd);
176462306a36Sopenharmony_ci	unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
176562306a36Sopenharmony_ci	int level;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	if (!par->lcd_on)
176862306a36Sopenharmony_ci		level = 0;
176962306a36Sopenharmony_ci	else
177062306a36Sopenharmony_ci		level = backlight_get_brightness(bd);
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	reg |= LVDS_BL_MOD_EN | LVDS_BLON;
177362306a36Sopenharmony_ci	if (level > 0) {
177462306a36Sopenharmony_ci		reg |= LVDS_DIGION;
177562306a36Sopenharmony_ci		if (!(reg & LVDS_ON)) {
177662306a36Sopenharmony_ci			reg &= ~LVDS_BLON;
177762306a36Sopenharmony_ci			aty_st_le32(LVDS_GEN_CNTL, reg);
177862306a36Sopenharmony_ci			aty_ld_le32(LVDS_GEN_CNTL);
177962306a36Sopenharmony_ci			mdelay(10);
178062306a36Sopenharmony_ci			reg |= LVDS_BLON;
178162306a36Sopenharmony_ci			aty_st_le32(LVDS_GEN_CNTL, reg);
178262306a36Sopenharmony_ci		}
178362306a36Sopenharmony_ci		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
178462306a36Sopenharmony_ci		reg |= (aty128_bl_get_level_brightness(par, level) <<
178562306a36Sopenharmony_ci			LVDS_BL_MOD_LEVEL_SHIFT);
178662306a36Sopenharmony_ci#ifdef BACKLIGHT_LVDS_OFF
178762306a36Sopenharmony_ci		reg |= LVDS_ON | LVDS_EN;
178862306a36Sopenharmony_ci		reg &= ~LVDS_DISPLAY_DIS;
178962306a36Sopenharmony_ci#endif
179062306a36Sopenharmony_ci		aty_st_le32(LVDS_GEN_CNTL, reg);
179162306a36Sopenharmony_ci#ifdef BACKLIGHT_DAC_OFF
179262306a36Sopenharmony_ci		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
179362306a36Sopenharmony_ci#endif
179462306a36Sopenharmony_ci	} else {
179562306a36Sopenharmony_ci		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
179662306a36Sopenharmony_ci		reg |= (aty128_bl_get_level_brightness(par, 0) <<
179762306a36Sopenharmony_ci			LVDS_BL_MOD_LEVEL_SHIFT);
179862306a36Sopenharmony_ci#ifdef BACKLIGHT_LVDS_OFF
179962306a36Sopenharmony_ci		reg |= LVDS_DISPLAY_DIS;
180062306a36Sopenharmony_ci		aty_st_le32(LVDS_GEN_CNTL, reg);
180162306a36Sopenharmony_ci		aty_ld_le32(LVDS_GEN_CNTL);
180262306a36Sopenharmony_ci		udelay(10);
180362306a36Sopenharmony_ci		reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
180462306a36Sopenharmony_ci#endif
180562306a36Sopenharmony_ci		aty_st_le32(LVDS_GEN_CNTL, reg);
180662306a36Sopenharmony_ci#ifdef BACKLIGHT_DAC_OFF
180762306a36Sopenharmony_ci		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
180862306a36Sopenharmony_ci#endif
180962306a36Sopenharmony_ci	}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	return 0;
181262306a36Sopenharmony_ci}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_cistatic const struct backlight_ops aty128_bl_data = {
181562306a36Sopenharmony_ci	.update_status	= aty128_bl_update_status,
181662306a36Sopenharmony_ci};
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_cistatic void aty128_bl_set_power(struct fb_info *info, int power)
181962306a36Sopenharmony_ci{
182062306a36Sopenharmony_ci	if (info->bl_dev) {
182162306a36Sopenharmony_ci		info->bl_dev->props.power = power;
182262306a36Sopenharmony_ci		backlight_update_status(info->bl_dev);
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci}
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_cistatic void aty128_bl_init(struct aty128fb_par *par)
182762306a36Sopenharmony_ci{
182862306a36Sopenharmony_ci	struct backlight_properties props;
182962306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pdev);
183062306a36Sopenharmony_ci	struct backlight_device *bd;
183162306a36Sopenharmony_ci	char name[12];
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	/* Could be extended to Rage128Pro LVDS output too */
183462306a36Sopenharmony_ci	if (par->chip_gen != rage_M3)
183562306a36Sopenharmony_ci		return;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
183862306a36Sopenharmony_ci	if (!pmac_has_backlight_type("ati"))
183962306a36Sopenharmony_ci		return;
184062306a36Sopenharmony_ci#endif
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	snprintf(name, sizeof(name), "aty128bl%d", info->node);
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
184562306a36Sopenharmony_ci	props.type = BACKLIGHT_RAW;
184662306a36Sopenharmony_ci	props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
184762306a36Sopenharmony_ci	bd = backlight_device_register(name, info->device, par, &aty128_bl_data,
184862306a36Sopenharmony_ci				       &props);
184962306a36Sopenharmony_ci	if (IS_ERR(bd)) {
185062306a36Sopenharmony_ci		info->bl_dev = NULL;
185162306a36Sopenharmony_ci		printk(KERN_WARNING "aty128: Backlight registration failed\n");
185262306a36Sopenharmony_ci		goto error;
185362306a36Sopenharmony_ci	}
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	info->bl_dev = bd;
185662306a36Sopenharmony_ci	fb_bl_default_curve(info, 0,
185762306a36Sopenharmony_ci		 63 * FB_BACKLIGHT_MAX / MAX_LEVEL,
185862306a36Sopenharmony_ci		219 * FB_BACKLIGHT_MAX / MAX_LEVEL);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	bd->props.brightness = bd->props.max_brightness;
186162306a36Sopenharmony_ci	bd->props.power = FB_BLANK_UNBLANK;
186262306a36Sopenharmony_ci	backlight_update_status(bd);
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	printk("aty128: Backlight initialized (%s)\n", name);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	return;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_cierror:
186962306a36Sopenharmony_ci	return;
187062306a36Sopenharmony_ci}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_cistatic void aty128_bl_exit(struct backlight_device *bd)
187362306a36Sopenharmony_ci{
187462306a36Sopenharmony_ci	backlight_device_unregister(bd);
187562306a36Sopenharmony_ci	printk("aty128: Backlight unloaded\n");
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY128_BACKLIGHT */
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci/*
188062306a36Sopenharmony_ci *  Initialisation
188162306a36Sopenharmony_ci */
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC__disabled
188462306a36Sopenharmony_cistatic void aty128_early_resume(void *data)
188562306a36Sopenharmony_ci{
188662306a36Sopenharmony_ci        struct aty128fb_par *par = data;
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	if (!console_trylock())
188962306a36Sopenharmony_ci		return;
189062306a36Sopenharmony_ci	pci_restore_state(par->pdev);
189162306a36Sopenharmony_ci	aty128_do_resume(par->pdev);
189262306a36Sopenharmony_ci	console_unlock();
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_cistatic int aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
189762306a36Sopenharmony_ci{
189862306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
189962306a36Sopenharmony_ci	struct aty128fb_par *par = info->par;
190062306a36Sopenharmony_ci	struct fb_var_screeninfo var;
190162306a36Sopenharmony_ci	char video_card[50];
190262306a36Sopenharmony_ci	u8 chip_rev;
190362306a36Sopenharmony_ci	u32 dac;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	/* Get the chip revision */
190662306a36Sopenharmony_ci	chip_rev = (aty_ld_le32(CNFG_CNTL) >> 16) & 0x1F;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	strcpy(video_card, "Rage128 XX ");
190962306a36Sopenharmony_ci	video_card[8] = ent->device >> 8;
191062306a36Sopenharmony_ci	video_card[9] = ent->device & 0xFF;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	/* range check to make sure */
191362306a36Sopenharmony_ci	if (ent->driver_data < ARRAY_SIZE(r128_family))
191462306a36Sopenharmony_ci		strlcat(video_card, r128_family[ent->driver_data],
191562306a36Sopenharmony_ci			sizeof(video_card));
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	if (par->vram_size % (1024 * 1024) == 0)
192062306a36Sopenharmony_ci		printk("%dM %s\n", par->vram_size / (1024*1024), par->mem->name);
192162306a36Sopenharmony_ci	else
192262306a36Sopenharmony_ci		printk("%dk %s\n", par->vram_size / 1024, par->mem->name);
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	par->chip_gen = ent->driver_data;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	/* fill in info */
192762306a36Sopenharmony_ci	info->fbops = &aty128fb_ops;
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	par->lcd_on = default_lcd_on;
193062306a36Sopenharmony_ci	par->crt_on = default_crt_on;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	var = default_var;
193362306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
193462306a36Sopenharmony_ci	if (machine_is(powermac)) {
193562306a36Sopenharmony_ci		/* Indicate sleep capability */
193662306a36Sopenharmony_ci		if (par->chip_gen == rage_M3) {
193762306a36Sopenharmony_ci			pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
193862306a36Sopenharmony_ci#if 0 /* Disable the early video resume hack for now as it's causing problems,
193962306a36Sopenharmony_ci       * among others we now rely on the PCI core restoring the config space
194062306a36Sopenharmony_ci       * for us, which isn't the case with that hack, and that code path causes
194162306a36Sopenharmony_ci       * various things to be called with interrupts off while they shouldn't.
194262306a36Sopenharmony_ci       * I'm leaving the code in as it can be useful for debugging purposes
194362306a36Sopenharmony_ci       */
194462306a36Sopenharmony_ci			pmac_set_early_video_resume(aty128_early_resume, par);
194562306a36Sopenharmony_ci#endif
194662306a36Sopenharmony_ci		}
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci		/* Find default mode */
194962306a36Sopenharmony_ci		if (mode_option) {
195062306a36Sopenharmony_ci			if (!mac_find_mode(&var, info, mode_option, 8))
195162306a36Sopenharmony_ci				var = default_var;
195262306a36Sopenharmony_ci		} else {
195362306a36Sopenharmony_ci			if (default_vmode <= 0 || default_vmode > VMODE_MAX)
195462306a36Sopenharmony_ci				default_vmode = VMODE_1024_768_60;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci			/* iMacs need that resolution
195762306a36Sopenharmony_ci			 * PowerMac2,1 first r128 iMacs
195862306a36Sopenharmony_ci			 * PowerMac2,2 summer 2000 iMacs
195962306a36Sopenharmony_ci			 * PowerMac4,1 january 2001 iMacs "flower power"
196062306a36Sopenharmony_ci			 */
196162306a36Sopenharmony_ci			if (of_machine_is_compatible("PowerMac2,1") ||
196262306a36Sopenharmony_ci			    of_machine_is_compatible("PowerMac2,2") ||
196362306a36Sopenharmony_ci			    of_machine_is_compatible("PowerMac4,1"))
196462306a36Sopenharmony_ci				default_vmode = VMODE_1024_768_75;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci			/* iBook SE */
196762306a36Sopenharmony_ci			if (of_machine_is_compatible("PowerBook2,2"))
196862306a36Sopenharmony_ci				default_vmode = VMODE_800_600_60;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci			/* PowerBook Firewire (Pismo), iBook Dual USB */
197162306a36Sopenharmony_ci			if (of_machine_is_compatible("PowerBook3,1") ||
197262306a36Sopenharmony_ci			    of_machine_is_compatible("PowerBook4,1"))
197362306a36Sopenharmony_ci				default_vmode = VMODE_1024_768_60;
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci			/* PowerBook Titanium */
197662306a36Sopenharmony_ci			if (of_machine_is_compatible("PowerBook3,2"))
197762306a36Sopenharmony_ci				default_vmode = VMODE_1152_768_60;
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci			if (default_cmode > 16)
198062306a36Sopenharmony_ci				default_cmode = CMODE_32;
198162306a36Sopenharmony_ci			else if (default_cmode > 8)
198262306a36Sopenharmony_ci				default_cmode = CMODE_16;
198362306a36Sopenharmony_ci			else
198462306a36Sopenharmony_ci				default_cmode = CMODE_8;
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci			if (mac_vmode_to_var(default_vmode, default_cmode, &var))
198762306a36Sopenharmony_ci				var = default_var;
198862306a36Sopenharmony_ci		}
198962306a36Sopenharmony_ci	} else
199062306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
199162306a36Sopenharmony_ci	{
199262306a36Sopenharmony_ci		if (mode_option)
199362306a36Sopenharmony_ci			if (fb_find_mode(&var, info, mode_option, NULL,
199462306a36Sopenharmony_ci					 0, &defaultmode, 8) == 0)
199562306a36Sopenharmony_ci				var = default_var;
199662306a36Sopenharmony_ci	}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	var.accel_flags &= ~FB_ACCELF_TEXT;
199962306a36Sopenharmony_ci//	var.accel_flags |= FB_ACCELF_TEXT;/* FIXME Will add accel later */
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	if (aty128fb_check_var(&var, info)) {
200262306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: Cannot set default mode.\n");
200362306a36Sopenharmony_ci		return 0;
200462306a36Sopenharmony_ci	}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	/* setup the DAC the way we like it */
200762306a36Sopenharmony_ci	dac = aty_ld_le32(DAC_CNTL);
200862306a36Sopenharmony_ci	dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL);
200962306a36Sopenharmony_ci	dac |= DAC_MASK;
201062306a36Sopenharmony_ci	if (par->chip_gen == rage_M3)
201162306a36Sopenharmony_ci		dac |= DAC_PALETTE2_SNOOP_EN;
201262306a36Sopenharmony_ci	aty_st_le32(DAC_CNTL, dac);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	/* turn off bus mastering, just in case */
201562306a36Sopenharmony_ci	aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL) | BUS_MASTER_DIS);
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	info->var = var;
201862306a36Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 256, 0);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	var.activate = FB_ACTIVATE_NOW;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	aty128_init_engine(par);
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	par->pdev = pdev;
202562306a36Sopenharmony_ci	par->asleep = 0;
202662306a36Sopenharmony_ci	par->lock_blank = 0;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	if (register_framebuffer(info) < 0)
202962306a36Sopenharmony_ci		return 0;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
203262306a36Sopenharmony_ci	if (backlight)
203362306a36Sopenharmony_ci		aty128_bl_init(par);
203462306a36Sopenharmony_ci#endif
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	fb_info(info, "%s frame buffer device on %s\n",
203762306a36Sopenharmony_ci		info->fix.id, video_card);
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	return 1;	/* success! */
204062306a36Sopenharmony_ci}
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci#ifdef CONFIG_PCI
204362306a36Sopenharmony_ci/* register a card    ++ajoshi */
204462306a36Sopenharmony_cistatic int aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
204562306a36Sopenharmony_ci{
204662306a36Sopenharmony_ci	unsigned long fb_addr, reg_addr;
204762306a36Sopenharmony_ci	struct aty128fb_par *par;
204862306a36Sopenharmony_ci	struct fb_info *info;
204962306a36Sopenharmony_ci	int err;
205062306a36Sopenharmony_ci#ifndef __sparc__
205162306a36Sopenharmony_ci	void __iomem *bios = NULL;
205262306a36Sopenharmony_ci#endif
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	err = aperture_remove_conflicting_pci_devices(pdev, "aty128fb");
205562306a36Sopenharmony_ci	if (err)
205662306a36Sopenharmony_ci		return err;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	/* Enable device in PCI config */
205962306a36Sopenharmony_ci	if ((err = pci_enable_device(pdev))) {
206062306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n",
206162306a36Sopenharmony_ci				err);
206262306a36Sopenharmony_ci		return -ENODEV;
206362306a36Sopenharmony_ci	}
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	fb_addr = pci_resource_start(pdev, 0);
206662306a36Sopenharmony_ci	if (!request_mem_region(fb_addr, pci_resource_len(pdev, 0),
206762306a36Sopenharmony_ci				"aty128fb FB")) {
206862306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: cannot reserve frame "
206962306a36Sopenharmony_ci				"buffer memory\n");
207062306a36Sopenharmony_ci		return -ENODEV;
207162306a36Sopenharmony_ci	}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	reg_addr = pci_resource_start(pdev, 2);
207462306a36Sopenharmony_ci	if (!request_mem_region(reg_addr, pci_resource_len(pdev, 2),
207562306a36Sopenharmony_ci				"aty128fb MMIO")) {
207662306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: cannot reserve MMIO region\n");
207762306a36Sopenharmony_ci		goto err_free_fb;
207862306a36Sopenharmony_ci	}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	/* We have the resources. Now virtualize them */
208162306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct aty128fb_par), &pdev->dev);
208262306a36Sopenharmony_ci	if (!info)
208362306a36Sopenharmony_ci		goto err_free_mmio;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	par = info->par;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	info->pseudo_palette = par->pseudo_palette;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	/* Virtualize mmio region */
209062306a36Sopenharmony_ci	info->fix.mmio_start = reg_addr;
209162306a36Sopenharmony_ci	par->regbase = pci_ioremap_bar(pdev, 2);
209262306a36Sopenharmony_ci	if (!par->regbase)
209362306a36Sopenharmony_ci		goto err_free_info;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	/* Grab memory size from the card */
209662306a36Sopenharmony_ci	// How does this relate to the resource length from the PCI hardware?
209762306a36Sopenharmony_ci	par->vram_size = aty_ld_le32(CNFG_MEMSIZE) & 0x03FFFFFF;
209862306a36Sopenharmony_ci
209962306a36Sopenharmony_ci	/* Virtualize the framebuffer */
210062306a36Sopenharmony_ci	info->screen_base = ioremap_wc(fb_addr, par->vram_size);
210162306a36Sopenharmony_ci	if (!info->screen_base)
210262306a36Sopenharmony_ci		goto err_unmap_out;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	/* Set up info->fix */
210562306a36Sopenharmony_ci	info->fix = aty128fb_fix;
210662306a36Sopenharmony_ci	info->fix.smem_start = fb_addr;
210762306a36Sopenharmony_ci	info->fix.smem_len = par->vram_size;
210862306a36Sopenharmony_ci	info->fix.mmio_start = reg_addr;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	/* If we can't test scratch registers, something is seriously wrong */
211162306a36Sopenharmony_ci	if (!register_test(par)) {
211262306a36Sopenharmony_ci		printk(KERN_ERR "aty128fb: Can't write to video register!\n");
211362306a36Sopenharmony_ci		goto err_out;
211462306a36Sopenharmony_ci	}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci#ifndef __sparc__
211762306a36Sopenharmony_ci	bios = aty128_map_ROM(par, pdev);
211862306a36Sopenharmony_ci#ifdef CONFIG_X86
211962306a36Sopenharmony_ci	if (bios == NULL)
212062306a36Sopenharmony_ci		bios = aty128_find_mem_vbios(par);
212162306a36Sopenharmony_ci#endif
212262306a36Sopenharmony_ci	if (bios == NULL)
212362306a36Sopenharmony_ci		printk(KERN_INFO "aty128fb: BIOS not located, guessing timings.\n");
212462306a36Sopenharmony_ci	else {
212562306a36Sopenharmony_ci		printk(KERN_INFO "aty128fb: Rage128 BIOS located\n");
212662306a36Sopenharmony_ci		aty128_get_pllinfo(par, bios);
212762306a36Sopenharmony_ci		pci_unmap_rom(pdev, bios);
212862306a36Sopenharmony_ci	}
212962306a36Sopenharmony_ci#endif /* __sparc__ */
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	aty128_timings(par);
213262306a36Sopenharmony_ci	pci_set_drvdata(pdev, info);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (!aty128_init(pdev, ent))
213562306a36Sopenharmony_ci		goto err_out;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	if (mtrr)
213862306a36Sopenharmony_ci		par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
213962306a36Sopenharmony_ci						  par->vram_size);
214062306a36Sopenharmony_ci	return 0;
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_cierr_out:
214362306a36Sopenharmony_ci	iounmap(info->screen_base);
214462306a36Sopenharmony_cierr_unmap_out:
214562306a36Sopenharmony_ci	iounmap(par->regbase);
214662306a36Sopenharmony_cierr_free_info:
214762306a36Sopenharmony_ci	framebuffer_release(info);
214862306a36Sopenharmony_cierr_free_mmio:
214962306a36Sopenharmony_ci	release_mem_region(pci_resource_start(pdev, 2),
215062306a36Sopenharmony_ci			pci_resource_len(pdev, 2));
215162306a36Sopenharmony_cierr_free_fb:
215262306a36Sopenharmony_ci	release_mem_region(pci_resource_start(pdev, 0),
215362306a36Sopenharmony_ci			pci_resource_len(pdev, 0));
215462306a36Sopenharmony_ci	return -ENODEV;
215562306a36Sopenharmony_ci}
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_cistatic void aty128_remove(struct pci_dev *pdev)
215862306a36Sopenharmony_ci{
215962306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
216062306a36Sopenharmony_ci	struct aty128fb_par *par;
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	if (!info)
216362306a36Sopenharmony_ci		return;
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	par = info->par;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY128_BACKLIGHT
216862306a36Sopenharmony_ci	aty128_bl_exit(info->bl_dev);
216962306a36Sopenharmony_ci#endif
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	unregister_framebuffer(info);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
217462306a36Sopenharmony_ci	iounmap(par->regbase);
217562306a36Sopenharmony_ci	iounmap(info->screen_base);
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	release_mem_region(pci_resource_start(pdev, 0),
217862306a36Sopenharmony_ci			   pci_resource_len(pdev, 0));
217962306a36Sopenharmony_ci	release_mem_region(pci_resource_start(pdev, 2),
218062306a36Sopenharmony_ci			   pci_resource_len(pdev, 2));
218162306a36Sopenharmony_ci	framebuffer_release(info);
218262306a36Sopenharmony_ci}
218362306a36Sopenharmony_ci#endif /* CONFIG_PCI */
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci    /*
218862306a36Sopenharmony_ci     *  Blank the display.
218962306a36Sopenharmony_ci     */
219062306a36Sopenharmony_cistatic int aty128fb_blank(int blank, struct fb_info *fb)
219162306a36Sopenharmony_ci{
219262306a36Sopenharmony_ci	struct aty128fb_par *par = fb->par;
219362306a36Sopenharmony_ci	u8 state;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	if (par->lock_blank || par->asleep)
219662306a36Sopenharmony_ci		return 0;
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	switch (blank) {
219962306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
220062306a36Sopenharmony_ci		state = 4;
220162306a36Sopenharmony_ci		break;
220262306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
220362306a36Sopenharmony_ci		state = 6;
220462306a36Sopenharmony_ci		break;
220562306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
220662306a36Sopenharmony_ci		state = 5;
220762306a36Sopenharmony_ci		break;
220862306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
220962306a36Sopenharmony_ci		state = 7;
221062306a36Sopenharmony_ci		break;
221162306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
221262306a36Sopenharmony_ci	default:
221362306a36Sopenharmony_ci		state = 0;
221462306a36Sopenharmony_ci		break;
221562306a36Sopenharmony_ci	}
221662306a36Sopenharmony_ci	aty_st_8(CRTC_EXT_CNTL+1, state);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	if (par->chip_gen == rage_M3) {
221962306a36Sopenharmony_ci		aty128_set_crt_enable(par, par->crt_on && !blank);
222062306a36Sopenharmony_ci		aty128_set_lcd_enable(par, par->lcd_on && !blank);
222162306a36Sopenharmony_ci	}
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	return 0;
222462306a36Sopenharmony_ci}
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci/*
222762306a36Sopenharmony_ci *  Set a single color register. The values supplied are already
222862306a36Sopenharmony_ci *  rounded down to the hardware's capabilities (according to the
222962306a36Sopenharmony_ci *  entries in the var structure). Return != 0 for invalid regno.
223062306a36Sopenharmony_ci */
223162306a36Sopenharmony_cistatic int aty128fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
223262306a36Sopenharmony_ci			      u_int transp, struct fb_info *info)
223362306a36Sopenharmony_ci{
223462306a36Sopenharmony_ci	struct aty128fb_par *par = info->par;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	if (regno > 255
223762306a36Sopenharmony_ci	    || (par->crtc.depth == 16 && regno > 63)
223862306a36Sopenharmony_ci	    || (par->crtc.depth == 15 && regno > 31))
223962306a36Sopenharmony_ci		return 1;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	red >>= 8;
224262306a36Sopenharmony_ci	green >>= 8;
224362306a36Sopenharmony_ci	blue >>= 8;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	if (regno < 16) {
224662306a36Sopenharmony_ci		int i;
224762306a36Sopenharmony_ci		u32 *pal = info->pseudo_palette;
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci		switch (par->crtc.depth) {
225062306a36Sopenharmony_ci		case 15:
225162306a36Sopenharmony_ci			pal[regno] = (regno << 10) | (regno << 5) | regno;
225262306a36Sopenharmony_ci			break;
225362306a36Sopenharmony_ci		case 16:
225462306a36Sopenharmony_ci			pal[regno] = (regno << 11) | (regno << 6) | regno;
225562306a36Sopenharmony_ci			break;
225662306a36Sopenharmony_ci		case 24:
225762306a36Sopenharmony_ci			pal[regno] = (regno << 16) | (regno << 8) | regno;
225862306a36Sopenharmony_ci			break;
225962306a36Sopenharmony_ci		case 32:
226062306a36Sopenharmony_ci			i = (regno << 8) | regno;
226162306a36Sopenharmony_ci			pal[regno] = (i << 16) | i;
226262306a36Sopenharmony_ci			break;
226362306a36Sopenharmony_ci		}
226462306a36Sopenharmony_ci	}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	if (par->crtc.depth == 16 && regno > 0) {
226762306a36Sopenharmony_ci		/*
226862306a36Sopenharmony_ci		 * With the 5-6-5 split of bits for RGB at 16 bits/pixel, we
226962306a36Sopenharmony_ci		 * have 32 slots for R and B values but 64 slots for G values.
227062306a36Sopenharmony_ci		 * Thus the R and B values go in one slot but the G value
227162306a36Sopenharmony_ci		 * goes in a different slot, and we have to avoid disturbing
227262306a36Sopenharmony_ci		 * the other fields in the slots we touch.
227362306a36Sopenharmony_ci		 */
227462306a36Sopenharmony_ci		par->green[regno] = green;
227562306a36Sopenharmony_ci		if (regno < 32) {
227662306a36Sopenharmony_ci			par->red[regno] = red;
227762306a36Sopenharmony_ci			par->blue[regno] = blue;
227862306a36Sopenharmony_ci			aty128_st_pal(regno * 8, red, par->green[regno*2],
227962306a36Sopenharmony_ci				      blue, par);
228062306a36Sopenharmony_ci		}
228162306a36Sopenharmony_ci		red = par->red[regno/2];
228262306a36Sopenharmony_ci		blue = par->blue[regno/2];
228362306a36Sopenharmony_ci		regno <<= 2;
228462306a36Sopenharmony_ci	} else if (par->crtc.bpp == 16)
228562306a36Sopenharmony_ci		regno <<= 3;
228662306a36Sopenharmony_ci	aty128_st_pal(regno, red, green, blue, par);
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	return 0;
228962306a36Sopenharmony_ci}
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci#define ATY_MIRROR_LCD_ON	0x00000001
229262306a36Sopenharmony_ci#define ATY_MIRROR_CRT_ON	0x00000002
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci/* out param: u32*	backlight value: 0 to 15 */
229562306a36Sopenharmony_ci#define FBIO_ATY128_GET_MIRROR	_IOR('@', 1, __u32)
229662306a36Sopenharmony_ci/* in param: u32*	backlight value: 0 to 15 */
229762306a36Sopenharmony_ci#define FBIO_ATY128_SET_MIRROR	_IOW('@', 2, __u32)
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_cistatic int aty128fb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
230062306a36Sopenharmony_ci{
230162306a36Sopenharmony_ci	struct aty128fb_par *par = info->par;
230262306a36Sopenharmony_ci	u32 value;
230362306a36Sopenharmony_ci	int rc;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	switch (cmd) {
230662306a36Sopenharmony_ci	case FBIO_ATY128_SET_MIRROR:
230762306a36Sopenharmony_ci		if (par->chip_gen != rage_M3)
230862306a36Sopenharmony_ci			return -EINVAL;
230962306a36Sopenharmony_ci		rc = get_user(value, (__u32 __user *)arg);
231062306a36Sopenharmony_ci		if (rc)
231162306a36Sopenharmony_ci			return rc;
231262306a36Sopenharmony_ci		par->lcd_on = (value & 0x01) != 0;
231362306a36Sopenharmony_ci		par->crt_on = (value & 0x02) != 0;
231462306a36Sopenharmony_ci		if (!par->crt_on && !par->lcd_on)
231562306a36Sopenharmony_ci			par->lcd_on = 1;
231662306a36Sopenharmony_ci		aty128_set_crt_enable(par, par->crt_on);
231762306a36Sopenharmony_ci		aty128_set_lcd_enable(par, par->lcd_on);
231862306a36Sopenharmony_ci		return 0;
231962306a36Sopenharmony_ci	case FBIO_ATY128_GET_MIRROR:
232062306a36Sopenharmony_ci		if (par->chip_gen != rage_M3)
232162306a36Sopenharmony_ci			return -EINVAL;
232262306a36Sopenharmony_ci		value = (par->crt_on << 1) | par->lcd_on;
232362306a36Sopenharmony_ci		return put_user(value, (__u32 __user *)arg);
232462306a36Sopenharmony_ci	}
232562306a36Sopenharmony_ci	return -EINVAL;
232662306a36Sopenharmony_ci}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_cistatic void aty128_set_suspend(struct aty128fb_par *par, int suspend)
232962306a36Sopenharmony_ci{
233062306a36Sopenharmony_ci	u32	pmgt;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (!par->pdev->pm_cap)
233362306a36Sopenharmony_ci		return;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	/* Set the chip into the appropriate suspend mode (we use D2,
233662306a36Sopenharmony_ci	 * D3 would require a complete re-initialisation of the chip,
233762306a36Sopenharmony_ci	 * including PCI config registers, clocks, AGP configuration, ...)
233862306a36Sopenharmony_ci	 *
233962306a36Sopenharmony_ci	 * For resume, the core will have already brought us back to D0
234062306a36Sopenharmony_ci	 */
234162306a36Sopenharmony_ci	if (suspend) {
234262306a36Sopenharmony_ci		/* Make sure CRTC2 is reset. Remove that the day we decide to
234362306a36Sopenharmony_ci		 * actually use CRTC2 and replace it with real code for disabling
234462306a36Sopenharmony_ci		 * the CRTC2 output during sleep
234562306a36Sopenharmony_ci		 */
234662306a36Sopenharmony_ci		aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) &
234762306a36Sopenharmony_ci			~(CRTC2_EN));
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci		/* Set the power management mode to be PCI based */
235062306a36Sopenharmony_ci		/* Use this magic value for now */
235162306a36Sopenharmony_ci		pmgt = 0x0c005407;
235262306a36Sopenharmony_ci		aty_st_pll(POWER_MANAGEMENT, pmgt);
235362306a36Sopenharmony_ci		(void)aty_ld_pll(POWER_MANAGEMENT);
235462306a36Sopenharmony_ci		aty_st_le32(BUS_CNTL1, 0x00000010);
235562306a36Sopenharmony_ci		aty_st_le32(MEM_POWER_MISC, 0x0c830000);
235662306a36Sopenharmony_ci		msleep(100);
235762306a36Sopenharmony_ci	}
235862306a36Sopenharmony_ci}
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_cistatic int aty128_pci_suspend_late(struct device *dev, pm_message_t state)
236162306a36Sopenharmony_ci{
236262306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
236362306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
236462306a36Sopenharmony_ci	struct aty128fb_par *par = info->par;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	/* We don't do anything but D2, for now we return 0, but
236762306a36Sopenharmony_ci	 * we may want to change that. How do we know if the BIOS
236862306a36Sopenharmony_ci	 * can properly take care of D3 ? Also, with swsusp, we
236962306a36Sopenharmony_ci	 * know we'll be rebooted, ...
237062306a36Sopenharmony_ci	 */
237162306a36Sopenharmony_ci#ifndef CONFIG_PPC_PMAC
237262306a36Sopenharmony_ci	/* HACK ALERT ! Once I find a proper way to say to each driver
237362306a36Sopenharmony_ci	 * individually what will happen with it's PCI slot, I'll change
237462306a36Sopenharmony_ci	 * that. On laptops, the AGP slot is just unclocked, so D2 is
237562306a36Sopenharmony_ci	 * expected, while on desktops, the card is powered off
237662306a36Sopenharmony_ci	 */
237762306a36Sopenharmony_ci	return 0;
237862306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	if (state.event == pdev->dev.power.power_state.event)
238162306a36Sopenharmony_ci		return 0;
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	printk(KERN_DEBUG "aty128fb: suspending...\n");
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	console_lock();
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci	fb_set_suspend(info, 1);
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	/* Make sure engine is reset */
239062306a36Sopenharmony_ci	wait_for_idle(par);
239162306a36Sopenharmony_ci	aty128_reset_engine(par);
239262306a36Sopenharmony_ci	wait_for_idle(par);
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	/* Blank display and LCD */
239562306a36Sopenharmony_ci	aty128fb_blank(FB_BLANK_POWERDOWN, info);
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	/* Sleep */
239862306a36Sopenharmony_ci	par->asleep = 1;
239962306a36Sopenharmony_ci	par->lock_blank = 1;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
240262306a36Sopenharmony_ci	/* On powermac, we have hooks to properly suspend/resume AGP now,
240362306a36Sopenharmony_ci	 * use them here. We'll ultimately need some generic support here,
240462306a36Sopenharmony_ci	 * but the generic code isn't quite ready for that yet
240562306a36Sopenharmony_ci	 */
240662306a36Sopenharmony_ci	pmac_suspend_agp_for_card(pdev);
240762306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	/* We need a way to make sure the fbdev layer will _not_ touch the
241062306a36Sopenharmony_ci	 * framebuffer before we put the chip to suspend state. On 2.4, I
241162306a36Sopenharmony_ci	 * used dummy fb ops, 2.5 need proper support for this at the
241262306a36Sopenharmony_ci	 * fbdev level
241362306a36Sopenharmony_ci	 */
241462306a36Sopenharmony_ci	if (state.event != PM_EVENT_ON)
241562306a36Sopenharmony_ci		aty128_set_suspend(par, 1);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	console_unlock();
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	pdev->dev.power.power_state = state;
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	return 0;
242262306a36Sopenharmony_ci}
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_suspend(struct device *dev)
242562306a36Sopenharmony_ci{
242662306a36Sopenharmony_ci	return aty128_pci_suspend_late(dev, PMSG_SUSPEND);
242762306a36Sopenharmony_ci}
242862306a36Sopenharmony_ci
242962306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_hibernate(struct device *dev)
243062306a36Sopenharmony_ci{
243162306a36Sopenharmony_ci	return aty128_pci_suspend_late(dev, PMSG_HIBERNATE);
243262306a36Sopenharmony_ci}
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_freeze(struct device *dev)
243562306a36Sopenharmony_ci{
243662306a36Sopenharmony_ci	return aty128_pci_suspend_late(dev, PMSG_FREEZE);
243762306a36Sopenharmony_ci}
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_cistatic int aty128_do_resume(struct pci_dev *pdev)
244062306a36Sopenharmony_ci{
244162306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
244262306a36Sopenharmony_ci	struct aty128fb_par *par = info->par;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	if (pdev->dev.power.power_state.event == PM_EVENT_ON)
244562306a36Sopenharmony_ci		return 0;
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	/* PCI state will have been restored by the core, so
244862306a36Sopenharmony_ci	 * we should be in D0 now with our config space fully
244962306a36Sopenharmony_ci	 * restored
245062306a36Sopenharmony_ci	 */
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	/* Wakeup chip */
245362306a36Sopenharmony_ci	aty128_set_suspend(par, 0);
245462306a36Sopenharmony_ci	par->asleep = 0;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	/* Restore display & engine */
245762306a36Sopenharmony_ci	aty128_reset_engine(par);
245862306a36Sopenharmony_ci	wait_for_idle(par);
245962306a36Sopenharmony_ci	aty128fb_set_par(info);
246062306a36Sopenharmony_ci	fb_pan_display(info, &info->var);
246162306a36Sopenharmony_ci	fb_set_cmap(&info->cmap, info);
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	/* Refresh */
246462306a36Sopenharmony_ci	fb_set_suspend(info, 0);
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	/* Unblank */
246762306a36Sopenharmony_ci	par->lock_blank = 0;
246862306a36Sopenharmony_ci	aty128fb_blank(0, info);
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
247162306a36Sopenharmony_ci	/* On powermac, we have hooks to properly suspend/resume AGP now,
247262306a36Sopenharmony_ci	 * use them here. We'll ultimately need some generic support here,
247362306a36Sopenharmony_ci	 * but the generic code isn't quite ready for that yet
247462306a36Sopenharmony_ci	 */
247562306a36Sopenharmony_ci	pmac_resume_agp_for_card(pdev);
247662306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	pdev->dev.power.power_state = PMSG_ON;
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	printk(KERN_DEBUG "aty128fb: resumed !\n");
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	return 0;
248362306a36Sopenharmony_ci}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_cistatic int __maybe_unused aty128_pci_resume(struct device *dev)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	int rc;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	console_lock();
249062306a36Sopenharmony_ci	rc = aty128_do_resume(to_pci_dev(dev));
249162306a36Sopenharmony_ci	console_unlock();
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	return rc;
249462306a36Sopenharmony_ci}
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_cistatic int aty128fb_init(void)
249862306a36Sopenharmony_ci{
249962306a36Sopenharmony_ci#ifndef MODULE
250062306a36Sopenharmony_ci	char *option = NULL;
250162306a36Sopenharmony_ci#endif
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	if (fb_modesetting_disabled("aty128fb"))
250462306a36Sopenharmony_ci		return -ENODEV;
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci#ifndef MODULE
250762306a36Sopenharmony_ci	if (fb_get_options("aty128fb", &option))
250862306a36Sopenharmony_ci		return -ENODEV;
250962306a36Sopenharmony_ci	aty128fb_setup(option);
251062306a36Sopenharmony_ci#endif
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci	return pci_register_driver(&aty128fb_driver);
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_cistatic void __exit aty128fb_exit(void)
251662306a36Sopenharmony_ci{
251762306a36Sopenharmony_ci	pci_unregister_driver(&aty128fb_driver);
251862306a36Sopenharmony_ci}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_cimodule_init(aty128fb_init);
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_cimodule_exit(aty128fb_exit);
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ciMODULE_AUTHOR("(c)1999-2003 Brad Douglas <brad@neruo.com>");
252562306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
252662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
252762306a36Sopenharmony_cimodule_param(mode_option, charp, 0);
252862306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
252962306a36Sopenharmony_cimodule_param_named(nomtrr, mtrr, invbool, 0);
253062306a36Sopenharmony_ciMODULE_PARM_DESC(nomtrr, "bool: Disable MTRR support (0 or 1=disabled) (default=0)");
2531