162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  ATI Frame Buffer Device Driver Core
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *	Copyright (C) 2004  Alex Kern <alex.kern@gmx.de>
562306a36Sopenharmony_ci *	Copyright (C) 1997-2001  Geert Uytterhoeven
662306a36Sopenharmony_ci *	Copyright (C) 1998  Bernd Harries
762306a36Sopenharmony_ci *	Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  This driver supports the following ATI graphics chips:
1062306a36Sopenharmony_ci *    - ATI Mach64
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  To do: add support for
1362306a36Sopenharmony_ci *    - ATI Rage128 (from aty128fb.c)
1462306a36Sopenharmony_ci *    - ATI Radeon (from radeonfb.c)
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *  This driver is partly based on the PowerMac console driver:
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *	Copyright (C) 1996 Paul Mackerras
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *  and on the PowerMac ATI/mach64 display driver:
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *	Copyright (C) 1997 Michael AK Tesch
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci *	      with work by Jon Howell
2562306a36Sopenharmony_ci *			   Harry AC Eaton
2662306a36Sopenharmony_ci *			   Anthony Tong <atong@uiuc.edu>
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci *  Generic LCD support written by Daniel Mantione, ported from 2.4.20 by Alex Kern
2962306a36Sopenharmony_ci *  Many Thanks to Ville Syrjälä for patches and fixing nasting 16 bit color bug.
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
3262306a36Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
3362306a36Sopenharmony_ci *  more details.
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci *  Many thanks to Nitya from ATI devrel for support and patience !
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/******************************************************************************
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci  TODO:
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci    - cursor support on all cards and all ramdacs.
4362306a36Sopenharmony_ci    - cursor parameters controlable via ioctl()s.
4462306a36Sopenharmony_ci    - guess PLL and MCLK based on the original PLL register values initialized
4562306a36Sopenharmony_ci      by Open Firmware (if they are initialized). BIOS is done
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci    (Anyone with Mac to help with this?)
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci******************************************************************************/
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include <linux/aperture.h>
5262306a36Sopenharmony_ci#include <linux/compat.h>
5362306a36Sopenharmony_ci#include <linux/module.h>
5462306a36Sopenharmony_ci#include <linux/moduleparam.h>
5562306a36Sopenharmony_ci#include <linux/kernel.h>
5662306a36Sopenharmony_ci#include <linux/errno.h>
5762306a36Sopenharmony_ci#include <linux/string.h>
5862306a36Sopenharmony_ci#include <linux/mm.h>
5962306a36Sopenharmony_ci#include <linux/slab.h>
6062306a36Sopenharmony_ci#include <linux/vmalloc.h>
6162306a36Sopenharmony_ci#include <linux/delay.h>
6262306a36Sopenharmony_ci#include <linux/compiler.h>
6362306a36Sopenharmony_ci#include <linux/console.h>
6462306a36Sopenharmony_ci#include <linux/fb.h>
6562306a36Sopenharmony_ci#include <linux/init.h>
6662306a36Sopenharmony_ci#include <linux/pci.h>
6762306a36Sopenharmony_ci#include <linux/interrupt.h>
6862306a36Sopenharmony_ci#include <linux/spinlock.h>
6962306a36Sopenharmony_ci#include <linux/wait.h>
7062306a36Sopenharmony_ci#include <linux/backlight.h>
7162306a36Sopenharmony_ci#include <linux/reboot.h>
7262306a36Sopenharmony_ci#include <linux/dmi.h>
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#include <asm/io.h>
7562306a36Sopenharmony_ci#include <linux/uaccess.h>
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#include <video/mach64.h>
7862306a36Sopenharmony_ci#include "atyfb.h"
7962306a36Sopenharmony_ci#include "ati_ids.h"
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#ifdef __powerpc__
8262306a36Sopenharmony_ci#include <asm/machdep.h>
8362306a36Sopenharmony_ci#include "../macmodes.h"
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci#ifdef __sparc__
8662306a36Sopenharmony_ci#include <asm/fbio.h>
8762306a36Sopenharmony_ci#include <asm/oplib.h>
8862306a36Sopenharmony_ci#include <asm/prom.h>
8962306a36Sopenharmony_ci#endif
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU
9262306a36Sopenharmony_ci#include <linux/adb.h>
9362306a36Sopenharmony_ci#include <linux/pmu.h>
9462306a36Sopenharmony_ci#endif
9562306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
9662306a36Sopenharmony_ci#include <asm/btext.h>
9762306a36Sopenharmony_ci#endif
9862306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
9962306a36Sopenharmony_ci#include <asm/backlight.h>
10062306a36Sopenharmony_ci#endif
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/*
10362306a36Sopenharmony_ci * Debug flags.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_ci#undef DEBUG
10662306a36Sopenharmony_ci/*#define DEBUG*/
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* Make sure n * PAGE_SIZE is protected at end of Aperture for GUI-regs */
10962306a36Sopenharmony_ci/*  - must be large enough to catch all GUI-Regs   */
11062306a36Sopenharmony_ci/*  - must be aligned to a PAGE boundary           */
11162306a36Sopenharmony_ci#define GUI_RESERVE	(1 * PAGE_SIZE)
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci/* FIXME: remove the FAIL definition */
11462306a36Sopenharmony_ci#define FAIL(msg) do { \
11562306a36Sopenharmony_ci	if (!(var->activate & FB_ACTIVATE_TEST)) \
11662306a36Sopenharmony_ci		printk(KERN_CRIT "atyfb: " msg "\n"); \
11762306a36Sopenharmony_ci	return -EINVAL; \
11862306a36Sopenharmony_ci} while (0)
11962306a36Sopenharmony_ci#define FAIL_MAX(msg, x, _max_) do { \
12062306a36Sopenharmony_ci	if (x > _max_) { \
12162306a36Sopenharmony_ci		if (!(var->activate & FB_ACTIVATE_TEST)) \
12262306a36Sopenharmony_ci			printk(KERN_CRIT "atyfb: " msg " %x(%x)\n", x, _max_); \
12362306a36Sopenharmony_ci		return -EINVAL; \
12462306a36Sopenharmony_ci	} \
12562306a36Sopenharmony_ci} while (0)
12662306a36Sopenharmony_ci#ifdef DEBUG
12762306a36Sopenharmony_ci#define DPRINTK(fmt, args...)	printk(KERN_DEBUG "atyfb: " fmt, ## args)
12862306a36Sopenharmony_ci#else
12962306a36Sopenharmony_ci#define DPRINTK(fmt, args...)	no_printk(fmt, ##args)
13062306a36Sopenharmony_ci#endif
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define PRINTKI(fmt, args...)	printk(KERN_INFO "atyfb: " fmt, ## args)
13362306a36Sopenharmony_ci#define PRINTKE(fmt, args...)	printk(KERN_ERR "atyfb: " fmt, ## args)
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#if defined(CONFIG_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_GENERIC_LCD) || \
13662306a36Sopenharmony_cidefined(CONFIG_FB_ATY_BACKLIGHT) || defined (CONFIG_PPC_PMAC)
13762306a36Sopenharmony_cistatic const u32 lt_lcd_regs[] = {
13862306a36Sopenharmony_ci	CNFG_PANEL_LG,
13962306a36Sopenharmony_ci	LCD_GEN_CNTL_LG,
14062306a36Sopenharmony_ci	DSTN_CONTROL_LG,
14162306a36Sopenharmony_ci	HFB_PITCH_ADDR_LG,
14262306a36Sopenharmony_ci	HORZ_STRETCHING_LG,
14362306a36Sopenharmony_ci	VERT_STRETCHING_LG,
14462306a36Sopenharmony_ci	0, /* EXT_VERT_STRETCH */
14562306a36Sopenharmony_ci	LT_GIO_LG,
14662306a36Sopenharmony_ci	POWER_MANAGEMENT_LG
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_civoid aty_st_lcd(int index, u32 val, const struct atyfb_par *par)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	if (M64_HAS(LT_LCD_REGS)) {
15262306a36Sopenharmony_ci		aty_st_le32(lt_lcd_regs[index], val, par);
15362306a36Sopenharmony_ci	} else {
15462306a36Sopenharmony_ci		unsigned long temp;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci		/* write addr byte */
15762306a36Sopenharmony_ci		temp = aty_ld_le32(LCD_INDEX, par);
15862306a36Sopenharmony_ci		aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
15962306a36Sopenharmony_ci		/* write the register value */
16062306a36Sopenharmony_ci		aty_st_le32(LCD_DATA, val, par);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciu32 aty_ld_lcd(int index, const struct atyfb_par *par)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	if (M64_HAS(LT_LCD_REGS)) {
16762306a36Sopenharmony_ci		return aty_ld_le32(lt_lcd_regs[index], par);
16862306a36Sopenharmony_ci	} else {
16962306a36Sopenharmony_ci		unsigned long temp;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		/* write addr byte */
17262306a36Sopenharmony_ci		temp = aty_ld_le32(LCD_INDEX, par);
17362306a36Sopenharmony_ci		aty_st_le32(LCD_INDEX, (temp & ~LCD_INDEX_MASK) | index, par);
17462306a36Sopenharmony_ci		/* read the register value */
17562306a36Sopenharmony_ci		return aty_ld_le32(LCD_DATA, par);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci#else /* defined(CONFIG_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_BACKLIGHT) ||
17962306a36Sopenharmony_ci	 defined(CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_PPC_PMAC) */
18062306a36Sopenharmony_civoid aty_st_lcd(int index, u32 val, const struct atyfb_par *par)
18162306a36Sopenharmony_ci{ }
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciu32 aty_ld_lcd(int index, const struct atyfb_par *par)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci#endif /* defined(CONFIG_PMAC_BACKLIGHT) || defined(CONFIG_FB_ATY_BACKLIGHT) ||
18862306a36Sopenharmony_ci	  defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_PPC_PMAC) */
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
19162306a36Sopenharmony_ci/*
19262306a36Sopenharmony_ci * ATIReduceRatio --
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci * Reduce a fraction by factoring out the largest common divider of the
19562306a36Sopenharmony_ci * fraction's numerator and denominator.
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_cistatic void ATIReduceRatio(int *Numerator, int *Denominator)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	int Multiplier, Divider, Remainder;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	Multiplier = *Numerator;
20262306a36Sopenharmony_ci	Divider = *Denominator;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	while ((Remainder = Multiplier % Divider)) {
20562306a36Sopenharmony_ci		Multiplier = Divider;
20662306a36Sopenharmony_ci		Divider = Remainder;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	*Numerator /= Divider;
21062306a36Sopenharmony_ci	*Denominator /= Divider;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci#endif
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * The Hardware parameters for each card
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistruct pci_mmap_map {
21862306a36Sopenharmony_ci	unsigned long voff;
21962306a36Sopenharmony_ci	unsigned long poff;
22062306a36Sopenharmony_ci	unsigned long size;
22162306a36Sopenharmony_ci	unsigned long prot_flag;
22262306a36Sopenharmony_ci	unsigned long prot_mask;
22362306a36Sopenharmony_ci};
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic const struct fb_fix_screeninfo atyfb_fix = {
22662306a36Sopenharmony_ci	.id		= "ATY Mach64",
22762306a36Sopenharmony_ci	.type		= FB_TYPE_PACKED_PIXELS,
22862306a36Sopenharmony_ci	.visual		= FB_VISUAL_PSEUDOCOLOR,
22962306a36Sopenharmony_ci	.xpanstep	= 8,
23062306a36Sopenharmony_ci	.ypanstep	= 1,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * Frame buffer device API
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic int atyfb_open(struct fb_info *info, int user);
23862306a36Sopenharmony_cistatic int atyfb_release(struct fb_info *info, int user);
23962306a36Sopenharmony_cistatic int atyfb_check_var(struct fb_var_screeninfo *var,
24062306a36Sopenharmony_ci			   struct fb_info *info);
24162306a36Sopenharmony_cistatic int atyfb_set_par(struct fb_info *info);
24262306a36Sopenharmony_cistatic int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
24362306a36Sopenharmony_ci			   u_int transp, struct fb_info *info);
24462306a36Sopenharmony_cistatic int atyfb_pan_display(struct fb_var_screeninfo *var,
24562306a36Sopenharmony_ci			     struct fb_info *info);
24662306a36Sopenharmony_cistatic int atyfb_blank(int blank, struct fb_info *info);
24762306a36Sopenharmony_cistatic int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg);
24862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
24962306a36Sopenharmony_cistatic int atyfb_compat_ioctl(struct fb_info *info, u_int cmd, u_long arg)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	return atyfb_ioctl(info, cmd, (u_long)compat_ptr(arg));
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci#endif
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci#ifdef __sparc__
25662306a36Sopenharmony_cistatic int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
25762306a36Sopenharmony_ci#endif
25862306a36Sopenharmony_cistatic int atyfb_sync(struct fb_info *info);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/*
26162306a36Sopenharmony_ci * Internal routines
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int aty_init(struct fb_info *info);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc);
26962306a36Sopenharmony_cistatic int aty_var_to_crtc(const struct fb_info *info,
27062306a36Sopenharmony_ci			   const struct fb_var_screeninfo *var,
27162306a36Sopenharmony_ci			   struct crtc *crtc);
27262306a36Sopenharmony_cistatic int aty_crtc_to_var(const struct crtc *crtc,
27362306a36Sopenharmony_ci			   struct fb_var_screeninfo *var);
27462306a36Sopenharmony_cistatic void set_off_pitch(struct atyfb_par *par, const struct fb_info *info);
27562306a36Sopenharmony_ci#ifdef CONFIG_PPC
27662306a36Sopenharmony_cistatic int read_aty_sense(const struct atyfb_par *par);
27762306a36Sopenharmony_ci#endif
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic DEFINE_MUTEX(reboot_lock);
28062306a36Sopenharmony_cistatic struct fb_info *reboot_info;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/*
28362306a36Sopenharmony_ci * Interface used by the world
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic struct fb_var_screeninfo default_var = {
28762306a36Sopenharmony_ci	/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
28862306a36Sopenharmony_ci	640, 480, 640, 480, 0, 0, 8, 0,
28962306a36Sopenharmony_ci	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
29062306a36Sopenharmony_ci	0, 0, -1, -1, 0, 39722, 48, 16, 33, 10, 96, 2,
29162306a36Sopenharmony_ci	0, FB_VMODE_NONINTERLACED
29262306a36Sopenharmony_ci};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic const struct fb_videomode defmode = {
29562306a36Sopenharmony_ci	/* 640x480 @ 60 Hz, 31.5 kHz hsync */
29662306a36Sopenharmony_ci	NULL, 60, 640, 480, 39721, 40, 24, 32, 11, 96, 2,
29762306a36Sopenharmony_ci	0, FB_VMODE_NONINTERLACED
29862306a36Sopenharmony_ci};
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic struct fb_ops atyfb_ops = {
30162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
30262306a36Sopenharmony_ci	.fb_open	= atyfb_open,
30362306a36Sopenharmony_ci	.fb_release	= atyfb_release,
30462306a36Sopenharmony_ci	.fb_check_var	= atyfb_check_var,
30562306a36Sopenharmony_ci	.fb_set_par	= atyfb_set_par,
30662306a36Sopenharmony_ci	.fb_setcolreg	= atyfb_setcolreg,
30762306a36Sopenharmony_ci	.fb_pan_display	= atyfb_pan_display,
30862306a36Sopenharmony_ci	.fb_blank	= atyfb_blank,
30962306a36Sopenharmony_ci	.fb_ioctl	= atyfb_ioctl,
31062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
31162306a36Sopenharmony_ci	.fb_compat_ioctl = atyfb_compat_ioctl,
31262306a36Sopenharmony_ci#endif
31362306a36Sopenharmony_ci	.fb_fillrect	= atyfb_fillrect,
31462306a36Sopenharmony_ci	.fb_copyarea	= atyfb_copyarea,
31562306a36Sopenharmony_ci	.fb_imageblit	= atyfb_imageblit,
31662306a36Sopenharmony_ci#ifdef __sparc__
31762306a36Sopenharmony_ci	.fb_mmap	= atyfb_mmap,
31862306a36Sopenharmony_ci#endif
31962306a36Sopenharmony_ci	.fb_sync	= atyfb_sync,
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic bool noaccel;
32362306a36Sopenharmony_cistatic bool nomtrr;
32462306a36Sopenharmony_cistatic int vram;
32562306a36Sopenharmony_cistatic int pll;
32662306a36Sopenharmony_cistatic int mclk;
32762306a36Sopenharmony_cistatic int xclk;
32862306a36Sopenharmony_cistatic int comp_sync = -1;
32962306a36Sopenharmony_cistatic char *mode;
33062306a36Sopenharmony_cistatic int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci#ifdef CONFIG_PPC
33362306a36Sopenharmony_cistatic int default_vmode = VMODE_CHOOSE;
33462306a36Sopenharmony_cistatic int default_cmode = CMODE_CHOOSE;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cimodule_param_named(vmode, default_vmode, int, 0);
33762306a36Sopenharmony_ciMODULE_PARM_DESC(vmode, "int: video mode for mac");
33862306a36Sopenharmony_cimodule_param_named(cmode, default_cmode, int, 0);
33962306a36Sopenharmony_ciMODULE_PARM_DESC(cmode, "int: color mode for mac");
34062306a36Sopenharmony_ci#endif
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci#ifdef CONFIG_ATARI
34362306a36Sopenharmony_cistatic unsigned int mach64_count = 0;
34462306a36Sopenharmony_cistatic unsigned long phys_vmembase[FB_MAX] = { 0, };
34562306a36Sopenharmony_cistatic unsigned long phys_size[FB_MAX] = { 0, };
34662306a36Sopenharmony_cistatic unsigned long phys_guiregbase[FB_MAX] = { 0, };
34762306a36Sopenharmony_ci#endif
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/* top -> down is an evolution of mach64 chipset, any corrections? */
35062306a36Sopenharmony_ci#define ATI_CHIP_88800GX   (M64F_GX)
35162306a36Sopenharmony_ci#define ATI_CHIP_88800CX   (M64F_GX)
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci#define ATI_CHIP_264CT     (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
35462306a36Sopenharmony_ci#define ATI_CHIP_264ET     (M64F_CT | M64F_INTEGRATED | M64F_CT_BUS | M64F_MAGIC_FIFO)
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci#define ATI_CHIP_264VT     (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_MAGIC_FIFO)
35762306a36Sopenharmony_ci#define ATI_CHIP_264GT     (M64F_GT | M64F_INTEGRATED               | M64F_MAGIC_FIFO | M64F_EXTRA_BRIGHT)
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci#define ATI_CHIP_264VTB    (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP)
36062306a36Sopenharmony_ci#define ATI_CHIP_264VT3    (M64F_VT | M64F_INTEGRATED | M64F_VT_BUS | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL)
36162306a36Sopenharmony_ci#define ATI_CHIP_264VT4    (M64F_VT | M64F_INTEGRATED               | M64F_GTB_DSP)
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/* FIXME what is this chip? */
36462306a36Sopenharmony_ci#define ATI_CHIP_264LT     (M64F_GT | M64F_INTEGRATED               | M64F_GTB_DSP)
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/* make sets shorter */
36762306a36Sopenharmony_ci#define ATI_MODERN_SET     (M64F_GT | M64F_INTEGRATED               | M64F_GTB_DSP | M64F_EXTRA_BRIGHT)
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci#define ATI_CHIP_264GTB    (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
37062306a36Sopenharmony_ci/*#define ATI_CHIP_264GTDVD  ?*/
37162306a36Sopenharmony_ci#define ATI_CHIP_264LTG    (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL)
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci#define ATI_CHIP_264GT2C   (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE)
37462306a36Sopenharmony_ci#define ATI_CHIP_264GTPRO  (ATI_MODERN_SET | M64F_SDRAM_MAGIC_PLL | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
37562306a36Sopenharmony_ci#define ATI_CHIP_264LTPRO  (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D)
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci#define ATI_CHIP_264XL     (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM)
37862306a36Sopenharmony_ci#define ATI_CHIP_MOBILITY  (ATI_MODERN_SET | M64F_HW_TRIPLE | M64F_FIFO_32 | M64F_RESET_3D | M64F_XL_DLL | M64F_MFB_FORCE_4 | M64F_XL_MEM | M64F_MOBIL_BUS)
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic struct {
38162306a36Sopenharmony_ci	u16 pci_id;
38262306a36Sopenharmony_ci	const char *name;
38362306a36Sopenharmony_ci	int pll, mclk, xclk, ecp_max;
38462306a36Sopenharmony_ci	u32 features;
38562306a36Sopenharmony_ci} aty_chips[] = {
38662306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GX
38762306a36Sopenharmony_ci	/* Mach64 GX */
38862306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GX, "ATI888GX00 (Mach64 GX)", 135, 50, 50, 0, ATI_CHIP_88800GX },
38962306a36Sopenharmony_ci	{ PCI_CHIP_MACH64CX, "ATI888CX00 (Mach64 CX)", 135, 50, 50, 0, ATI_CHIP_88800CX },
39062306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GX */
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
39362306a36Sopenharmony_ci	{ PCI_CHIP_MACH64CT, "ATI264CT (Mach64 CT)", 135, 60, 60, 0, ATI_CHIP_264CT },
39462306a36Sopenharmony_ci	{ PCI_CHIP_MACH64ET, "ATI264ET (Mach64 ET)", 135, 60, 60, 0, ATI_CHIP_264ET },
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* FIXME what is this chip? */
39762306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LT, "ATI264LT (Mach64 LT)", 135, 63, 63, 0, ATI_CHIP_264LT },
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	{ PCI_CHIP_MACH64VT, "ATI264VT (Mach64 VT)", 170, 67, 67, 80, ATI_CHIP_264VT },
40062306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GT, "3D RAGE (Mach64 GT)", 135, 63, 63, 80, ATI_CHIP_264GT },
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	{ PCI_CHIP_MACH64VU, "ATI264VT3 (Mach64 VU)", 200, 67, 67, 80, ATI_CHIP_264VT3 },
40362306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GU, "3D RAGE II+ (Mach64 GU)", 200, 67, 67, 100, ATI_CHIP_264GTB },
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LG, "3D RAGE LT (Mach64 LG)", 230, 63, 63, 100, ATI_CHIP_264LTG | M64F_LT_LCD_REGS | M64F_G3_PB_1024x768 },
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	{ PCI_CHIP_MACH64VV, "ATI264VT4 (Mach64 VV)", 230, 83, 83, 100, ATI_CHIP_264VT4 },
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GV, "3D RAGE IIC (Mach64 GV, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
41062306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GW, "3D RAGE IIC (Mach64 GW, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
41162306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GY, "3D RAGE IIC (Mach64 GY, PCI)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
41262306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GZ, "3D RAGE IIC (Mach64 GZ, AGP)", 230, 83, 83, 100, ATI_CHIP_264GT2C },
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GB, "3D RAGE PRO (Mach64 GB, BGA, AGP)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
41562306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GD, "3D RAGE PRO (Mach64 GD, BGA, AGP 1x)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
41662306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GI, "3D RAGE PRO (Mach64 GI, BGA, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO | M64F_MAGIC_VRAM_SIZE },
41762306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GP, "3D RAGE PRO (Mach64 GP, PQFP, PCI)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
41862306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GQ, "3D RAGE PRO (Mach64 GQ, PQFP, PCI, limited 3D)", 230, 100, 100, 125, ATI_CHIP_264GTPRO },
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LB, "3D RAGE LT PRO (Mach64 LB, AGP)", 236, 75, 100, 135, ATI_CHIP_264LTPRO },
42162306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LD, "3D RAGE LT PRO (Mach64 LD, AGP)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
42262306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LI, "3D RAGE LT PRO (Mach64 LI, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1_1 | M64F_G3_PB_1024x768 },
42362306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LP, "3D RAGE LT PRO (Mach64 LP, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO | M64F_G3_PB_1024x768 },
42462306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LQ, "3D RAGE LT PRO (Mach64 LQ, PCI)", 230, 100, 100, 135, ATI_CHIP_264LTPRO },
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GM, "3D RAGE XL (Mach64 GM, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
42762306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GN, "3D RAGE XC (Mach64 GN, AGP 2x)", 230, 83, 63, 135, ATI_CHIP_264XL },
42862306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GO, "3D RAGE XL (Mach64 GO, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
42962306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GL, "3D RAGE XC (Mach64 GL, PCI-66)", 230, 83, 63, 135, ATI_CHIP_264XL },
43062306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GR, "3D RAGE XL (Mach64 GR, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL | M64F_SDRAM_MAGIC_PLL },
43162306a36Sopenharmony_ci	{ PCI_CHIP_MACH64GS, "3D RAGE XC (Mach64 GS, PCI-33)", 230, 83, 63, 135, ATI_CHIP_264XL },
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LM, "3D RAGE Mobility P/M (Mach64 LM, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
43462306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LN, "3D RAGE Mobility L (Mach64 LN, AGP 2x)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
43562306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LR, "3D RAGE Mobility P/M (Mach64 LR, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
43662306a36Sopenharmony_ci	{ PCI_CHIP_MACH64LS, "3D RAGE Mobility L (Mach64 LS, PCI)", 230, 83, 125, 135, ATI_CHIP_MOBILITY },
43762306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_CT */
43862306a36Sopenharmony_ci};
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/*
44162306a36Sopenharmony_ci * Last page of 8 MB (4 MB on ISA) aperture is MMIO,
44262306a36Sopenharmony_ci * unless the auxiliary register aperture is used.
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic void aty_fudge_framebuffer_len(struct fb_info *info)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (!par->aux_start &&
44962306a36Sopenharmony_ci	    (info->fix.smem_len == 0x800000 ||
45062306a36Sopenharmony_ci	     (par->bus_type == ISA && info->fix.smem_len == 0x400000)))
45162306a36Sopenharmony_ci		info->fix.smem_len -= GUI_RESERVE;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int correct_chipset(struct atyfb_par *par)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	u8 rev;
45762306a36Sopenharmony_ci	u16 type;
45862306a36Sopenharmony_ci	u32 chip_id;
45962306a36Sopenharmony_ci	const char *name;
46062306a36Sopenharmony_ci	int i;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	for (i = (int)ARRAY_SIZE(aty_chips) - 1; i >= 0; i--)
46362306a36Sopenharmony_ci		if (par->pci_id == aty_chips[i].pci_id)
46462306a36Sopenharmony_ci			break;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (i < 0)
46762306a36Sopenharmony_ci		return -ENODEV;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	name = aty_chips[i].name;
47062306a36Sopenharmony_ci	par->pll_limits.pll_max = aty_chips[i].pll;
47162306a36Sopenharmony_ci	par->pll_limits.mclk = aty_chips[i].mclk;
47262306a36Sopenharmony_ci	par->pll_limits.xclk = aty_chips[i].xclk;
47362306a36Sopenharmony_ci	par->pll_limits.ecp_max = aty_chips[i].ecp_max;
47462306a36Sopenharmony_ci	par->features = aty_chips[i].features;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	chip_id = aty_ld_le32(CNFG_CHIP_ID, par);
47762306a36Sopenharmony_ci	type = chip_id & CFG_CHIP_TYPE;
47862306a36Sopenharmony_ci	rev = (chip_id & CFG_CHIP_REV) >> 24;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	switch (par->pci_id) {
48162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GX
48262306a36Sopenharmony_ci	case PCI_CHIP_MACH64GX:
48362306a36Sopenharmony_ci		if (type != 0x00d7)
48462306a36Sopenharmony_ci			return -ENODEV;
48562306a36Sopenharmony_ci		break;
48662306a36Sopenharmony_ci	case PCI_CHIP_MACH64CX:
48762306a36Sopenharmony_ci		if (type != 0x0057)
48862306a36Sopenharmony_ci			return -ENODEV;
48962306a36Sopenharmony_ci		break;
49062306a36Sopenharmony_ci#endif
49162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
49262306a36Sopenharmony_ci	case PCI_CHIP_MACH64VT:
49362306a36Sopenharmony_ci		switch (rev & 0x07) {
49462306a36Sopenharmony_ci		case 0x00:
49562306a36Sopenharmony_ci			switch (rev & 0xc0) {
49662306a36Sopenharmony_ci			case 0x00:
49762306a36Sopenharmony_ci				name = "ATI264VT (A3) (Mach64 VT)";
49862306a36Sopenharmony_ci				par->pll_limits.pll_max = 170;
49962306a36Sopenharmony_ci				par->pll_limits.mclk = 67;
50062306a36Sopenharmony_ci				par->pll_limits.xclk = 67;
50162306a36Sopenharmony_ci				par->pll_limits.ecp_max = 80;
50262306a36Sopenharmony_ci				par->features = ATI_CHIP_264VT;
50362306a36Sopenharmony_ci				break;
50462306a36Sopenharmony_ci			case 0x40:
50562306a36Sopenharmony_ci				name = "ATI264VT2 (A4) (Mach64 VT)";
50662306a36Sopenharmony_ci				par->pll_limits.pll_max = 200;
50762306a36Sopenharmony_ci				par->pll_limits.mclk = 67;
50862306a36Sopenharmony_ci				par->pll_limits.xclk = 67;
50962306a36Sopenharmony_ci				par->pll_limits.ecp_max = 80;
51062306a36Sopenharmony_ci				par->features = ATI_CHIP_264VT | M64F_MAGIC_POSTDIV;
51162306a36Sopenharmony_ci				break;
51262306a36Sopenharmony_ci			}
51362306a36Sopenharmony_ci			break;
51462306a36Sopenharmony_ci		case 0x01:
51562306a36Sopenharmony_ci			name = "ATI264VT3 (B1) (Mach64 VT)";
51662306a36Sopenharmony_ci			par->pll_limits.pll_max = 200;
51762306a36Sopenharmony_ci			par->pll_limits.mclk = 67;
51862306a36Sopenharmony_ci			par->pll_limits.xclk = 67;
51962306a36Sopenharmony_ci			par->pll_limits.ecp_max = 80;
52062306a36Sopenharmony_ci			par->features = ATI_CHIP_264VTB;
52162306a36Sopenharmony_ci			break;
52262306a36Sopenharmony_ci		case 0x02:
52362306a36Sopenharmony_ci			name = "ATI264VT3 (B2) (Mach64 VT)";
52462306a36Sopenharmony_ci			par->pll_limits.pll_max = 200;
52562306a36Sopenharmony_ci			par->pll_limits.mclk = 67;
52662306a36Sopenharmony_ci			par->pll_limits.xclk = 67;
52762306a36Sopenharmony_ci			par->pll_limits.ecp_max = 80;
52862306a36Sopenharmony_ci			par->features = ATI_CHIP_264VT3;
52962306a36Sopenharmony_ci			break;
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	case PCI_CHIP_MACH64GT:
53362306a36Sopenharmony_ci		switch (rev & 0x07) {
53462306a36Sopenharmony_ci		case 0x01:
53562306a36Sopenharmony_ci			name = "3D RAGE II (Mach64 GT)";
53662306a36Sopenharmony_ci			par->pll_limits.pll_max = 170;
53762306a36Sopenharmony_ci			par->pll_limits.mclk = 67;
53862306a36Sopenharmony_ci			par->pll_limits.xclk = 67;
53962306a36Sopenharmony_ci			par->pll_limits.ecp_max = 80;
54062306a36Sopenharmony_ci			par->features = ATI_CHIP_264GTB;
54162306a36Sopenharmony_ci			break;
54262306a36Sopenharmony_ci		case 0x02:
54362306a36Sopenharmony_ci			name = "3D RAGE II+ (Mach64 GT)";
54462306a36Sopenharmony_ci			par->pll_limits.pll_max = 200;
54562306a36Sopenharmony_ci			par->pll_limits.mclk = 67;
54662306a36Sopenharmony_ci			par->pll_limits.xclk = 67;
54762306a36Sopenharmony_ci			par->pll_limits.ecp_max = 100;
54862306a36Sopenharmony_ci			par->features = ATI_CHIP_264GTB;
54962306a36Sopenharmony_ci			break;
55062306a36Sopenharmony_ci		}
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci#endif
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	PRINTKI("%s [0x%04x rev 0x%02x]\n", name, type, rev);
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic char ram_dram[] __maybe_unused = "DRAM";
56062306a36Sopenharmony_cistatic char ram_resv[] __maybe_unused = "RESV";
56162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GX
56262306a36Sopenharmony_cistatic char ram_vram[] = "VRAM";
56362306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GX */
56462306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
56562306a36Sopenharmony_cistatic char ram_edo[] = "EDO";
56662306a36Sopenharmony_cistatic char ram_sdram[] = "SDRAM (1:1)";
56762306a36Sopenharmony_cistatic char ram_sgram[] = "SGRAM (1:1)";
56862306a36Sopenharmony_cistatic char ram_sdram32[] = "SDRAM (2:1) (32-bit)";
56962306a36Sopenharmony_cistatic char ram_wram[] = "WRAM";
57062306a36Sopenharmony_cistatic char ram_off[] = "OFF";
57162306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_CT */
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GX
57562306a36Sopenharmony_cistatic char *aty_gx_ram[8] = {
57662306a36Sopenharmony_ci	ram_dram, ram_vram, ram_vram, ram_dram,
57762306a36Sopenharmony_ci	ram_dram, ram_vram, ram_vram, ram_resv
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GX */
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
58262306a36Sopenharmony_cistatic char *aty_ct_ram[8] = {
58362306a36Sopenharmony_ci	ram_off, ram_dram, ram_edo, ram_edo,
58462306a36Sopenharmony_ci	ram_sdram, ram_sgram, ram_wram, ram_resv
58562306a36Sopenharmony_ci};
58662306a36Sopenharmony_cistatic char *aty_xl_ram[8] = {
58762306a36Sopenharmony_ci	ram_off, ram_dram, ram_edo, ram_edo,
58862306a36Sopenharmony_ci	ram_sdram, ram_sgram, ram_sdram32, ram_resv
58962306a36Sopenharmony_ci};
59062306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_CT */
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic u32 atyfb_get_pixclock(struct fb_var_screeninfo *var,
59362306a36Sopenharmony_ci			      struct atyfb_par *par)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	u32 pixclock = var->pixclock;
59662306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
59762306a36Sopenharmony_ci	u32 lcd_on_off;
59862306a36Sopenharmony_ci	par->pll.ct.xres = 0;
59962306a36Sopenharmony_ci	if (par->lcd_table != 0) {
60062306a36Sopenharmony_ci		lcd_on_off = aty_ld_lcd(LCD_GEN_CNTL, par);
60162306a36Sopenharmony_ci		if (lcd_on_off & LCD_ON) {
60262306a36Sopenharmony_ci			par->pll.ct.xres = var->xres;
60362306a36Sopenharmony_ci			pixclock = par->lcd_pixclock;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci#endif
60762306a36Sopenharmony_ci	return pixclock;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci#if defined(CONFIG_PPC)
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/*
61362306a36Sopenharmony_ci * Apple monitor sense
61462306a36Sopenharmony_ci */
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic int read_aty_sense(const struct atyfb_par *par)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	int sense, i;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0x31003100, par); /* drive outputs high */
62162306a36Sopenharmony_ci	__delay(200);
62262306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0, par); /* turn off outputs */
62362306a36Sopenharmony_ci	__delay(2000);
62462306a36Sopenharmony_ci	i = aty_ld_le32(GP_IO, par); /* get primary sense value */
62562306a36Sopenharmony_ci	sense = ((i & 0x3000) >> 3) | (i & 0x100);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* drive each sense line low in turn and collect the other 2 */
62862306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0x20000000, par); /* drive A low */
62962306a36Sopenharmony_ci	__delay(2000);
63062306a36Sopenharmony_ci	i = aty_ld_le32(GP_IO, par);
63162306a36Sopenharmony_ci	sense |= ((i & 0x1000) >> 7) | ((i & 0x100) >> 4);
63262306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0x20002000, par); /* drive A high again */
63362306a36Sopenharmony_ci	__delay(200);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0x10000000, par); /* drive B low */
63662306a36Sopenharmony_ci	__delay(2000);
63762306a36Sopenharmony_ci	i = aty_ld_le32(GP_IO, par);
63862306a36Sopenharmony_ci	sense |= ((i & 0x2000) >> 10) | ((i & 0x100) >> 6);
63962306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0x10001000, par); /* drive B high again */
64062306a36Sopenharmony_ci	__delay(200);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0x01000000, par); /* drive C low */
64362306a36Sopenharmony_ci	__delay(2000);
64462306a36Sopenharmony_ci	sense |= (aty_ld_le32(GP_IO, par) & 0x3000) >> 12;
64562306a36Sopenharmony_ci	aty_st_le32(GP_IO, 0, par); /* turn off outputs */
64662306a36Sopenharmony_ci	return sense;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci#endif /* defined(CONFIG_PPC) */
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci/*
65462306a36Sopenharmony_ci * CRTC programming
65562306a36Sopenharmony_ci */
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
66062306a36Sopenharmony_ci	if (par->lcd_table != 0) {
66162306a36Sopenharmony_ci		if (!M64_HAS(LT_LCD_REGS)) {
66262306a36Sopenharmony_ci			crtc->lcd_index = aty_ld_le32(LCD_INDEX, par);
66362306a36Sopenharmony_ci			aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
66462306a36Sopenharmony_ci		}
66562306a36Sopenharmony_ci		crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par);
66662306a36Sopenharmony_ci		crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		/* switch to non shadow registers */
67062306a36Sopenharmony_ci		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
67162306a36Sopenharmony_ci			   ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		/* save stretching */
67462306a36Sopenharmony_ci		crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
67562306a36Sopenharmony_ci		crtc->vert_stretching = aty_ld_lcd(VERT_STRETCHING, par);
67662306a36Sopenharmony_ci		if (!M64_HAS(LT_LCD_REGS))
67762306a36Sopenharmony_ci			crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci#endif
68062306a36Sopenharmony_ci	crtc->h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
68162306a36Sopenharmony_ci	crtc->h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
68262306a36Sopenharmony_ci	crtc->v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
68362306a36Sopenharmony_ci	crtc->v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
68462306a36Sopenharmony_ci	crtc->vline_crnt_vline = aty_ld_le32(CRTC_VLINE_CRNT_VLINE, par);
68562306a36Sopenharmony_ci	crtc->off_pitch = aty_ld_le32(CRTC_OFF_PITCH, par);
68662306a36Sopenharmony_ci	crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
68962306a36Sopenharmony_ci	if (par->lcd_table != 0) {
69062306a36Sopenharmony_ci		/* switch to shadow registers */
69162306a36Sopenharmony_ci		aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
69262306a36Sopenharmony_ci			   SHADOW_EN | SHADOW_RW_EN, par);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		crtc->shadow_h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
69562306a36Sopenharmony_ci		crtc->shadow_h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
69662306a36Sopenharmony_ci		crtc->shadow_v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
69762306a36Sopenharmony_ci		crtc->shadow_v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		aty_st_le32(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GENERIC_LCD */
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
70762306a36Sopenharmony_ci	if (par->lcd_table != 0) {
70862306a36Sopenharmony_ci		/* stop CRTC */
70962306a36Sopenharmony_ci		aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl &
71062306a36Sopenharmony_ci			    ~(CRTC_EXT_DISP_EN | CRTC_EN), par);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		/* update non-shadow registers first */
71362306a36Sopenharmony_ci		aty_st_lcd(CNFG_PANEL, crtc->lcd_config_panel, par);
71462306a36Sopenharmony_ci		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl &
71562306a36Sopenharmony_ci			   ~(CRTC_RW_SELECT | SHADOW_EN | SHADOW_RW_EN), par);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		/* temporarily disable stretching */
71862306a36Sopenharmony_ci		aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching &
71962306a36Sopenharmony_ci			   ~(HORZ_STRETCH_MODE | HORZ_STRETCH_EN), par);
72062306a36Sopenharmony_ci		aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching &
72162306a36Sopenharmony_ci			   ~(VERT_STRETCH_RATIO1 | VERT_STRETCH_RATIO2 |
72262306a36Sopenharmony_ci			     VERT_STRETCH_USE0 | VERT_STRETCH_EN), par);
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci#endif
72562306a36Sopenharmony_ci	/* turn off CRT */
72662306a36Sopenharmony_ci	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl & ~CRTC_EN, par);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	DPRINTK("setting up CRTC\n");
72962306a36Sopenharmony_ci	DPRINTK("set primary CRT to %ix%i %c%c composite %c\n",
73062306a36Sopenharmony_ci		((((crtc->h_tot_disp >> 16) & 0xff) + 1) << 3),
73162306a36Sopenharmony_ci		(((crtc->v_tot_disp >> 16) & 0x7ff) + 1),
73262306a36Sopenharmony_ci		(crtc->h_sync_strt_wid & 0x200000) ? 'N' : 'P',
73362306a36Sopenharmony_ci		(crtc->v_sync_strt_wid & 0x200000) ? 'N' : 'P',
73462306a36Sopenharmony_ci		(crtc->gen_cntl & CRTC_CSYNC_EN) ? 'P' : 'N');
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	DPRINTK("CRTC_H_TOTAL_DISP: %x\n", crtc->h_tot_disp);
73762306a36Sopenharmony_ci	DPRINTK("CRTC_H_SYNC_STRT_WID: %x\n", crtc->h_sync_strt_wid);
73862306a36Sopenharmony_ci	DPRINTK("CRTC_V_TOTAL_DISP: %x\n", crtc->v_tot_disp);
73962306a36Sopenharmony_ci	DPRINTK("CRTC_V_SYNC_STRT_WID: %x\n", crtc->v_sync_strt_wid);
74062306a36Sopenharmony_ci	DPRINTK("CRTC_OFF_PITCH: %x\n", crtc->off_pitch);
74162306a36Sopenharmony_ci	DPRINTK("CRTC_VLINE_CRNT_VLINE: %x\n", crtc->vline_crnt_vline);
74262306a36Sopenharmony_ci	DPRINTK("CRTC_GEN_CNTL: %x\n", crtc->gen_cntl);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	aty_st_le32(CRTC_H_TOTAL_DISP, crtc->h_tot_disp, par);
74562306a36Sopenharmony_ci	aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->h_sync_strt_wid, par);
74662306a36Sopenharmony_ci	aty_st_le32(CRTC_V_TOTAL_DISP, crtc->v_tot_disp, par);
74762306a36Sopenharmony_ci	aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->v_sync_strt_wid, par);
74862306a36Sopenharmony_ci	aty_st_le32(CRTC_OFF_PITCH, crtc->off_pitch, par);
74962306a36Sopenharmony_ci	aty_st_le32(CRTC_VLINE_CRNT_VLINE, crtc->vline_crnt_vline, par);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	aty_st_le32(CRTC_GEN_CNTL, crtc->gen_cntl, par);
75262306a36Sopenharmony_ci#if 0
75362306a36Sopenharmony_ci	FIXME
75462306a36Sopenharmony_ci	if (par->accel_flags & FB_ACCELF_TEXT)
75562306a36Sopenharmony_ci		aty_init_engine(par, info);
75662306a36Sopenharmony_ci#endif
75762306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
75862306a36Sopenharmony_ci	/* after setting the CRTC registers we should set the LCD registers. */
75962306a36Sopenharmony_ci	if (par->lcd_table != 0) {
76062306a36Sopenharmony_ci		/* switch to shadow registers */
76162306a36Sopenharmony_ci		aty_st_lcd(LCD_GEN_CNTL, (crtc->lcd_gen_cntl & ~CRTC_RW_SELECT) |
76262306a36Sopenharmony_ci			   SHADOW_EN | SHADOW_RW_EN, par);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		DPRINTK("set shadow CRT to %ix%i %c%c\n",
76562306a36Sopenharmony_ci			((((crtc->shadow_h_tot_disp >> 16) & 0xff) + 1) << 3),
76662306a36Sopenharmony_ci			(((crtc->shadow_v_tot_disp >> 16) & 0x7ff) + 1),
76762306a36Sopenharmony_ci			(crtc->shadow_h_sync_strt_wid & 0x200000) ? 'N' : 'P',
76862306a36Sopenharmony_ci			(crtc->shadow_v_sync_strt_wid & 0x200000) ? 'N' : 'P');
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		DPRINTK("SHADOW CRTC_H_TOTAL_DISP: %x\n",
77162306a36Sopenharmony_ci			crtc->shadow_h_tot_disp);
77262306a36Sopenharmony_ci		DPRINTK("SHADOW CRTC_H_SYNC_STRT_WID: %x\n",
77362306a36Sopenharmony_ci			crtc->shadow_h_sync_strt_wid);
77462306a36Sopenharmony_ci		DPRINTK("SHADOW CRTC_V_TOTAL_DISP: %x\n",
77562306a36Sopenharmony_ci			crtc->shadow_v_tot_disp);
77662306a36Sopenharmony_ci		DPRINTK("SHADOW CRTC_V_SYNC_STRT_WID: %x\n",
77762306a36Sopenharmony_ci			crtc->shadow_v_sync_strt_wid);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		aty_st_le32(CRTC_H_TOTAL_DISP, crtc->shadow_h_tot_disp, par);
78062306a36Sopenharmony_ci		aty_st_le32(CRTC_H_SYNC_STRT_WID, crtc->shadow_h_sync_strt_wid, par);
78162306a36Sopenharmony_ci		aty_st_le32(CRTC_V_TOTAL_DISP, crtc->shadow_v_tot_disp, par);
78262306a36Sopenharmony_ci		aty_st_le32(CRTC_V_SYNC_STRT_WID, crtc->shadow_v_sync_strt_wid, par);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		/* restore CRTC selection & shadow state and enable stretching */
78562306a36Sopenharmony_ci		DPRINTK("LCD_GEN_CNTL: %x\n", crtc->lcd_gen_cntl);
78662306a36Sopenharmony_ci		DPRINTK("HORZ_STRETCHING: %x\n", crtc->horz_stretching);
78762306a36Sopenharmony_ci		DPRINTK("VERT_STRETCHING: %x\n", crtc->vert_stretching);
78862306a36Sopenharmony_ci		if (!M64_HAS(LT_LCD_REGS))
78962306a36Sopenharmony_ci			DPRINTK("EXT_VERT_STRETCH: %x\n", crtc->ext_vert_stretch);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);
79262306a36Sopenharmony_ci		aty_st_lcd(HORZ_STRETCHING, crtc->horz_stretching, par);
79362306a36Sopenharmony_ci		aty_st_lcd(VERT_STRETCHING, crtc->vert_stretching, par);
79462306a36Sopenharmony_ci		if (!M64_HAS(LT_LCD_REGS)) {
79562306a36Sopenharmony_ci			aty_st_lcd(EXT_VERT_STRETCH, crtc->ext_vert_stretch, par);
79662306a36Sopenharmony_ci			aty_ld_le32(LCD_INDEX, par);
79762306a36Sopenharmony_ci			aty_st_le32(LCD_INDEX, crtc->lcd_index, par);
79862306a36Sopenharmony_ci		}
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GENERIC_LCD */
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic u32 calc_line_length(struct atyfb_par *par, u32 vxres, u32 bpp)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	u32 line_length = vxres * bpp / 8;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (par->ram_type == SGRAM ||
80862306a36Sopenharmony_ci	    (!M64_HAS(XL_MEM) && par->ram_type == WRAM))
80962306a36Sopenharmony_ci		line_length = (line_length + 63) & ~63;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	return line_length;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic int aty_var_to_crtc(const struct fb_info *info,
81562306a36Sopenharmony_ci			   const struct fb_var_screeninfo *var,
81662306a36Sopenharmony_ci			   struct crtc *crtc)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
81962306a36Sopenharmony_ci	u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
82062306a36Sopenharmony_ci	u32 sync, vmode;
82162306a36Sopenharmony_ci	u32 h_total, h_disp, h_sync_strt, h_sync_end, h_sync_dly, h_sync_wid, h_sync_pol;
82262306a36Sopenharmony_ci	u32 v_total, v_disp, v_sync_strt, v_sync_end, v_sync_wid, v_sync_pol, c_sync;
82362306a36Sopenharmony_ci	u32 pix_width, dp_pix_width, dp_chain_mask;
82462306a36Sopenharmony_ci	u32 line_length;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* input */
82762306a36Sopenharmony_ci	xres = (var->xres + 7) & ~7;
82862306a36Sopenharmony_ci	yres = var->yres;
82962306a36Sopenharmony_ci	vxres = (var->xres_virtual + 7) & ~7;
83062306a36Sopenharmony_ci	vyres = var->yres_virtual;
83162306a36Sopenharmony_ci	xoffset = (var->xoffset + 7) & ~7;
83262306a36Sopenharmony_ci	yoffset = var->yoffset;
83362306a36Sopenharmony_ci	bpp = var->bits_per_pixel;
83462306a36Sopenharmony_ci	if (bpp == 16)
83562306a36Sopenharmony_ci		bpp = (var->green.length == 5) ? 15 : 16;
83662306a36Sopenharmony_ci	sync = var->sync;
83762306a36Sopenharmony_ci	vmode = var->vmode;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* convert (and round up) and validate */
84062306a36Sopenharmony_ci	if (vxres < xres + xoffset)
84162306a36Sopenharmony_ci		vxres = xres + xoffset;
84262306a36Sopenharmony_ci	h_disp = xres;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (vyres < yres + yoffset)
84562306a36Sopenharmony_ci		vyres = yres + yoffset;
84662306a36Sopenharmony_ci	v_disp = yres;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (bpp <= 8) {
84962306a36Sopenharmony_ci		bpp = 8;
85062306a36Sopenharmony_ci		pix_width = CRTC_PIX_WIDTH_8BPP;
85162306a36Sopenharmony_ci		dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
85262306a36Sopenharmony_ci			BYTE_ORDER_LSB_TO_MSB;
85362306a36Sopenharmony_ci		dp_chain_mask = DP_CHAIN_8BPP;
85462306a36Sopenharmony_ci	} else if (bpp <= 15) {
85562306a36Sopenharmony_ci		bpp = 16;
85662306a36Sopenharmony_ci		pix_width = CRTC_PIX_WIDTH_15BPP;
85762306a36Sopenharmony_ci		dp_pix_width = HOST_15BPP | SRC_15BPP | DST_15BPP |
85862306a36Sopenharmony_ci			BYTE_ORDER_LSB_TO_MSB;
85962306a36Sopenharmony_ci		dp_chain_mask = DP_CHAIN_15BPP;
86062306a36Sopenharmony_ci	} else if (bpp <= 16) {
86162306a36Sopenharmony_ci		bpp = 16;
86262306a36Sopenharmony_ci		pix_width = CRTC_PIX_WIDTH_16BPP;
86362306a36Sopenharmony_ci		dp_pix_width = HOST_16BPP | SRC_16BPP | DST_16BPP |
86462306a36Sopenharmony_ci			BYTE_ORDER_LSB_TO_MSB;
86562306a36Sopenharmony_ci		dp_chain_mask = DP_CHAIN_16BPP;
86662306a36Sopenharmony_ci	} else if (bpp <= 24 && M64_HAS(INTEGRATED)) {
86762306a36Sopenharmony_ci		bpp = 24;
86862306a36Sopenharmony_ci		pix_width = CRTC_PIX_WIDTH_24BPP;
86962306a36Sopenharmony_ci		dp_pix_width = HOST_8BPP | SRC_8BPP | DST_8BPP |
87062306a36Sopenharmony_ci			BYTE_ORDER_LSB_TO_MSB;
87162306a36Sopenharmony_ci		dp_chain_mask = DP_CHAIN_24BPP;
87262306a36Sopenharmony_ci	} else if (bpp <= 32) {
87362306a36Sopenharmony_ci		bpp = 32;
87462306a36Sopenharmony_ci		pix_width = CRTC_PIX_WIDTH_32BPP;
87562306a36Sopenharmony_ci		dp_pix_width = HOST_32BPP | SRC_32BPP | DST_32BPP |
87662306a36Sopenharmony_ci			BYTE_ORDER_LSB_TO_MSB;
87762306a36Sopenharmony_ci		dp_chain_mask = DP_CHAIN_32BPP;
87862306a36Sopenharmony_ci	} else
87962306a36Sopenharmony_ci		FAIL("invalid bpp");
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	line_length = calc_line_length(par, vxres, bpp);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (vyres * line_length > info->fix.smem_len)
88462306a36Sopenharmony_ci		FAIL("not enough video RAM");
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
88762306a36Sopenharmony_ci	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if ((xres > 1920) || (yres > 1200)) {
89062306a36Sopenharmony_ci		FAIL("MACH64 chips are designed for max 1920x1200\n"
89162306a36Sopenharmony_ci		     "select another resolution.");
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci	h_sync_strt = h_disp + var->right_margin;
89462306a36Sopenharmony_ci	h_sync_end = h_sync_strt + var->hsync_len;
89562306a36Sopenharmony_ci	h_sync_dly  = var->right_margin & 7;
89662306a36Sopenharmony_ci	h_total = h_sync_end + h_sync_dly + var->left_margin;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	v_sync_strt = v_disp + var->lower_margin;
89962306a36Sopenharmony_ci	v_sync_end = v_sync_strt + var->vsync_len;
90062306a36Sopenharmony_ci	v_total = v_sync_end + var->upper_margin;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
90362306a36Sopenharmony_ci	if (par->lcd_table != 0) {
90462306a36Sopenharmony_ci		if (!M64_HAS(LT_LCD_REGS)) {
90562306a36Sopenharmony_ci			u32 lcd_index = aty_ld_le32(LCD_INDEX, par);
90662306a36Sopenharmony_ci			crtc->lcd_index = lcd_index &
90762306a36Sopenharmony_ci				~(LCD_INDEX_MASK | LCD_DISPLAY_DIS |
90862306a36Sopenharmony_ci				  LCD_SRC_SEL | CRTC2_DISPLAY_DIS);
90962306a36Sopenharmony_ci			aty_st_le32(LCD_INDEX, lcd_index, par);
91062306a36Sopenharmony_ci		}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci		if (!M64_HAS(MOBIL_BUS))
91362306a36Sopenharmony_ci			crtc->lcd_index |= CRTC2_DISPLAY_DIS;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		crtc->lcd_config_panel = aty_ld_lcd(CNFG_PANEL, par) | 0x4000;
91662306a36Sopenharmony_ci		crtc->lcd_gen_cntl = aty_ld_lcd(LCD_GEN_CNTL, par) & ~CRTC_RW_SELECT;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		crtc->lcd_gen_cntl &=
91962306a36Sopenharmony_ci			~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 | TVCLK_PM_EN |
92062306a36Sopenharmony_ci			/*VCLK_DAC_PM_EN | USE_SHADOWED_VEND |*/
92162306a36Sopenharmony_ci			USE_SHADOWED_ROWCUR | SHADOW_EN | SHADOW_RW_EN);
92262306a36Sopenharmony_ci		crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR | LOCK_8DOT;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		if ((crtc->lcd_gen_cntl & LCD_ON) &&
92562306a36Sopenharmony_ci		    ((xres > par->lcd_width) || (yres > par->lcd_height))) {
92662306a36Sopenharmony_ci			/*
92762306a36Sopenharmony_ci			 * We cannot display the mode on the LCD. If the CRT is
92862306a36Sopenharmony_ci			 * enabled we can turn off the LCD.
92962306a36Sopenharmony_ci			 * If the CRT is off, it isn't a good idea to switch it
93062306a36Sopenharmony_ci			 * on; we don't know if one is connected. So it's better
93162306a36Sopenharmony_ci			 * to fail then.
93262306a36Sopenharmony_ci			 */
93362306a36Sopenharmony_ci			if (crtc->lcd_gen_cntl & CRT_ON) {
93462306a36Sopenharmony_ci				if (!(var->activate & FB_ACTIVATE_TEST))
93562306a36Sopenharmony_ci					PRINTKI("Disable LCD panel, because video mode does not fit.\n");
93662306a36Sopenharmony_ci				crtc->lcd_gen_cntl &= ~LCD_ON;
93762306a36Sopenharmony_ci				/*aty_st_lcd(LCD_GEN_CNTL, crtc->lcd_gen_cntl, par);*/
93862306a36Sopenharmony_ci			} else {
93962306a36Sopenharmony_ci				if (!(var->activate & FB_ACTIVATE_TEST))
94062306a36Sopenharmony_ci					PRINTKE("Video mode exceeds size of LCD panel.\nConnect this computer to a conventional monitor if you really need this mode.\n");
94162306a36Sopenharmony_ci				return -EINVAL;
94262306a36Sopenharmony_ci			}
94362306a36Sopenharmony_ci		}
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if ((par->lcd_table != 0) && (crtc->lcd_gen_cntl & LCD_ON)) {
94762306a36Sopenharmony_ci		int VScan = 1;
94862306a36Sopenharmony_ci		/* bpp -> bytespp, 1,4 -> 0; 8 -> 2; 15,16 -> 1; 24 -> 6; 32 -> 5
94962306a36Sopenharmony_ci		const u8 DFP_h_sync_dly_LT[] = { 0, 2, 1, 6, 5 };
95062306a36Sopenharmony_ci		const u8 ADD_to_strt_wid_and_dly_LT_DAC[] = { 0, 5, 6, 9, 9, 12, 12 };  */
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci		vmode &= ~(FB_VMODE_DOUBLE | FB_VMODE_INTERLACED);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		/*
95562306a36Sopenharmony_ci		 * This is horror! When we simulate, say 640x480 on an 800x600
95662306a36Sopenharmony_ci		 * LCD monitor, the CRTC should be programmed 800x600 values for
95762306a36Sopenharmony_ci		 * the non visible part, but 640x480 for the visible part.
95862306a36Sopenharmony_ci		 * This code has been tested on a laptop with it's 1400x1050 LCD
95962306a36Sopenharmony_ci		 * monitor and a conventional monitor both switched on.
96062306a36Sopenharmony_ci		 * Tested modes: 1280x1024, 1152x864, 1024x768, 800x600,
96162306a36Sopenharmony_ci		 * works with little glitches also with DOUBLESCAN modes
96262306a36Sopenharmony_ci		 */
96362306a36Sopenharmony_ci		if (yres < par->lcd_height) {
96462306a36Sopenharmony_ci			VScan = par->lcd_height / yres;
96562306a36Sopenharmony_ci			if (VScan > 1) {
96662306a36Sopenharmony_ci				VScan = 2;
96762306a36Sopenharmony_ci				vmode |= FB_VMODE_DOUBLE;
96862306a36Sopenharmony_ci			}
96962306a36Sopenharmony_ci		}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci		h_sync_strt = h_disp + par->lcd_right_margin;
97262306a36Sopenharmony_ci		h_sync_end = h_sync_strt + par->lcd_hsync_len;
97362306a36Sopenharmony_ci		h_sync_dly = /*DFP_h_sync_dly[ ( bpp + 1 ) / 3 ]; */par->lcd_hsync_dly;
97462306a36Sopenharmony_ci		h_total = h_disp + par->lcd_hblank_len;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci		v_sync_strt = v_disp + par->lcd_lower_margin / VScan;
97762306a36Sopenharmony_ci		v_sync_end = v_sync_strt + par->lcd_vsync_len / VScan;
97862306a36Sopenharmony_ci		v_total = v_disp + par->lcd_vblank_len / VScan;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GENERIC_LCD */
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	h_disp = (h_disp >> 3) - 1;
98362306a36Sopenharmony_ci	h_sync_strt = (h_sync_strt >> 3) - 1;
98462306a36Sopenharmony_ci	h_sync_end = (h_sync_end >> 3) - 1;
98562306a36Sopenharmony_ci	h_total = (h_total >> 3) - 1;
98662306a36Sopenharmony_ci	h_sync_wid = h_sync_end - h_sync_strt;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	FAIL_MAX("h_disp too large", h_disp, 0xff);
98962306a36Sopenharmony_ci	FAIL_MAX("h_sync_strt too large", h_sync_strt, 0x1ff);
99062306a36Sopenharmony_ci	/*FAIL_MAX("h_sync_wid too large", h_sync_wid, 0x1f);*/
99162306a36Sopenharmony_ci	if (h_sync_wid > 0x1f)
99262306a36Sopenharmony_ci		h_sync_wid = 0x1f;
99362306a36Sopenharmony_ci	FAIL_MAX("h_total too large", h_total, 0x1ff);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (vmode & FB_VMODE_DOUBLE) {
99662306a36Sopenharmony_ci		v_disp <<= 1;
99762306a36Sopenharmony_ci		v_sync_strt <<= 1;
99862306a36Sopenharmony_ci		v_sync_end <<= 1;
99962306a36Sopenharmony_ci		v_total <<= 1;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	v_disp--;
100362306a36Sopenharmony_ci	v_sync_strt--;
100462306a36Sopenharmony_ci	v_sync_end--;
100562306a36Sopenharmony_ci	v_total--;
100662306a36Sopenharmony_ci	v_sync_wid = v_sync_end - v_sync_strt;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	FAIL_MAX("v_disp too large", v_disp, 0x7ff);
100962306a36Sopenharmony_ci	FAIL_MAX("v_sync_stsrt too large", v_sync_strt, 0x7ff);
101062306a36Sopenharmony_ci	/*FAIL_MAX("v_sync_wid too large", v_sync_wid, 0x1f);*/
101162306a36Sopenharmony_ci	if (v_sync_wid > 0x1f)
101262306a36Sopenharmony_ci		v_sync_wid = 0x1f;
101362306a36Sopenharmony_ci	FAIL_MAX("v_total too large", v_total, 0x7ff);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? CRTC_CSYNC_EN : 0;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* output */
101862306a36Sopenharmony_ci	crtc->vxres = vxres;
101962306a36Sopenharmony_ci	crtc->vyres = vyres;
102062306a36Sopenharmony_ci	crtc->xoffset = xoffset;
102162306a36Sopenharmony_ci	crtc->yoffset = yoffset;
102262306a36Sopenharmony_ci	crtc->bpp = bpp;
102362306a36Sopenharmony_ci	crtc->off_pitch =
102462306a36Sopenharmony_ci		((yoffset * line_length + xoffset * bpp / 8) / 8) |
102562306a36Sopenharmony_ci		((line_length / bpp) << 22);
102662306a36Sopenharmony_ci	crtc->vline_crnt_vline = 0;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	crtc->h_tot_disp = h_total | (h_disp << 16);
102962306a36Sopenharmony_ci	crtc->h_sync_strt_wid = (h_sync_strt & 0xff) | (h_sync_dly << 8) |
103062306a36Sopenharmony_ci		((h_sync_strt & 0x100) << 4) | (h_sync_wid << 16) |
103162306a36Sopenharmony_ci		(h_sync_pol << 21);
103262306a36Sopenharmony_ci	crtc->v_tot_disp = v_total | (v_disp << 16);
103362306a36Sopenharmony_ci	crtc->v_sync_strt_wid = v_sync_strt | (v_sync_wid << 16) |
103462306a36Sopenharmony_ci		(v_sync_pol << 21);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/* crtc->gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_PRESERVED_MASK; */
103762306a36Sopenharmony_ci	crtc->gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN | pix_width | c_sync;
103862306a36Sopenharmony_ci	crtc->gen_cntl |= CRTC_VGA_LINEAR;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* Enable doublescan mode if requested */
104162306a36Sopenharmony_ci	if (vmode & FB_VMODE_DOUBLE)
104262306a36Sopenharmony_ci		crtc->gen_cntl |= CRTC_DBL_SCAN_EN;
104362306a36Sopenharmony_ci	/* Enable interlaced mode if requested */
104462306a36Sopenharmony_ci	if (vmode & FB_VMODE_INTERLACED)
104562306a36Sopenharmony_ci		crtc->gen_cntl |= CRTC_INTERLACE_EN;
104662306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
104762306a36Sopenharmony_ci	if (par->lcd_table != 0) {
104862306a36Sopenharmony_ci		u32 vdisplay = yres;
104962306a36Sopenharmony_ci		if (vmode & FB_VMODE_DOUBLE)
105062306a36Sopenharmony_ci			vdisplay <<= 1;
105162306a36Sopenharmony_ci		crtc->gen_cntl &= ~(CRTC2_EN | CRTC2_PIX_WIDTH);
105262306a36Sopenharmony_ci		crtc->lcd_gen_cntl &= ~(HORZ_DIVBY2_EN | DIS_HOR_CRT_DIVBY2 |
105362306a36Sopenharmony_ci					/*TVCLK_PM_EN | VCLK_DAC_PM_EN |*/
105462306a36Sopenharmony_ci					USE_SHADOWED_VEND |
105562306a36Sopenharmony_ci					USE_SHADOWED_ROWCUR |
105662306a36Sopenharmony_ci					SHADOW_EN | SHADOW_RW_EN);
105762306a36Sopenharmony_ci		crtc->lcd_gen_cntl |= DONT_SHADOW_VPAR/* | LOCK_8DOT*/;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		/* MOBILITY M1 tested, FIXME: LT */
106062306a36Sopenharmony_ci		crtc->horz_stretching = aty_ld_lcd(HORZ_STRETCHING, par);
106162306a36Sopenharmony_ci		if (!M64_HAS(LT_LCD_REGS))
106262306a36Sopenharmony_ci			crtc->ext_vert_stretch = aty_ld_lcd(EXT_VERT_STRETCH, par) &
106362306a36Sopenharmony_ci				~(AUTO_VERT_RATIO | VERT_STRETCH_MODE | VERT_STRETCH_RATIO3);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci		crtc->horz_stretching &= ~(HORZ_STRETCH_RATIO |
106662306a36Sopenharmony_ci					   HORZ_STRETCH_LOOP | AUTO_HORZ_RATIO |
106762306a36Sopenharmony_ci					   HORZ_STRETCH_MODE | HORZ_STRETCH_EN);
106862306a36Sopenharmony_ci		if (xres < par->lcd_width && crtc->lcd_gen_cntl & LCD_ON) {
106962306a36Sopenharmony_ci			do {
107062306a36Sopenharmony_ci				/*
107162306a36Sopenharmony_ci				 * The horizontal blender misbehaves when
107262306a36Sopenharmony_ci				 * HDisplay is less than a certain threshold
107362306a36Sopenharmony_ci				 * (440 for a 1024-wide panel).  It doesn't
107462306a36Sopenharmony_ci				 * stretch such modes enough.  Use pixel
107562306a36Sopenharmony_ci				 * replication instead of blending to stretch
107662306a36Sopenharmony_ci				 * modes that can be made to exactly fit the
107762306a36Sopenharmony_ci				 * panel width.  The undocumented "NoLCDBlend"
107862306a36Sopenharmony_ci				 * option allows the pixel-replicated mode to
107962306a36Sopenharmony_ci				 * be slightly wider or narrower than the
108062306a36Sopenharmony_ci				 * panel width.  It also causes a mode that is
108162306a36Sopenharmony_ci				 * exactly half as wide as the panel to be
108262306a36Sopenharmony_ci				 * pixel-replicated, rather than blended.
108362306a36Sopenharmony_ci				 */
108462306a36Sopenharmony_ci				int HDisplay  = xres & ~7;
108562306a36Sopenharmony_ci				int nStretch  = par->lcd_width / HDisplay;
108662306a36Sopenharmony_ci				int Remainder = par->lcd_width % HDisplay;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci				if ((!Remainder && ((nStretch > 2))) ||
108962306a36Sopenharmony_ci				    (((HDisplay * 16) / par->lcd_width) < 7)) {
109062306a36Sopenharmony_ci					static const char StretchLoops[] = { 10, 12, 13, 15, 16 };
109162306a36Sopenharmony_ci					int horz_stretch_loop = -1, BestRemainder;
109262306a36Sopenharmony_ci					int Numerator = HDisplay, Denominator = par->lcd_width;
109362306a36Sopenharmony_ci					int Index = 5;
109462306a36Sopenharmony_ci					ATIReduceRatio(&Numerator, &Denominator);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci					BestRemainder = (Numerator * 16) / Denominator;
109762306a36Sopenharmony_ci					while (--Index >= 0) {
109862306a36Sopenharmony_ci						Remainder = ((Denominator - Numerator) * StretchLoops[Index]) %
109962306a36Sopenharmony_ci							Denominator;
110062306a36Sopenharmony_ci						if (Remainder < BestRemainder) {
110162306a36Sopenharmony_ci							horz_stretch_loop = Index;
110262306a36Sopenharmony_ci							if (!(BestRemainder = Remainder))
110362306a36Sopenharmony_ci								break;
110462306a36Sopenharmony_ci						}
110562306a36Sopenharmony_ci					}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci					if ((horz_stretch_loop >= 0) && !BestRemainder) {
110862306a36Sopenharmony_ci						int horz_stretch_ratio = 0, Accumulator = 0;
110962306a36Sopenharmony_ci						int reuse_previous = 1;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci						Index = StretchLoops[horz_stretch_loop];
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci						while (--Index >= 0) {
111462306a36Sopenharmony_ci							if (Accumulator > 0)
111562306a36Sopenharmony_ci								horz_stretch_ratio |= reuse_previous;
111662306a36Sopenharmony_ci							else
111762306a36Sopenharmony_ci								Accumulator += Denominator;
111862306a36Sopenharmony_ci							Accumulator -= Numerator;
111962306a36Sopenharmony_ci							reuse_previous <<= 1;
112062306a36Sopenharmony_ci						}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci						crtc->horz_stretching |= (HORZ_STRETCH_EN |
112362306a36Sopenharmony_ci							((horz_stretch_loop & HORZ_STRETCH_LOOP) << 16) |
112462306a36Sopenharmony_ci							(horz_stretch_ratio & HORZ_STRETCH_RATIO));
112562306a36Sopenharmony_ci						break;      /* Out of the do { ... } while (0) */
112662306a36Sopenharmony_ci					}
112762306a36Sopenharmony_ci				}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci				crtc->horz_stretching |= (HORZ_STRETCH_MODE | HORZ_STRETCH_EN |
113062306a36Sopenharmony_ci					(((HDisplay * (HORZ_STRETCH_BLEND + 1)) / par->lcd_width) & HORZ_STRETCH_BLEND));
113162306a36Sopenharmony_ci			} while (0);
113262306a36Sopenharmony_ci		}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci		if (vdisplay < par->lcd_height && crtc->lcd_gen_cntl & LCD_ON) {
113562306a36Sopenharmony_ci			crtc->vert_stretching = (VERT_STRETCH_USE0 | VERT_STRETCH_EN |
113662306a36Sopenharmony_ci				(((vdisplay * (VERT_STRETCH_RATIO0 + 1)) / par->lcd_height) & VERT_STRETCH_RATIO0));
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci			if (!M64_HAS(LT_LCD_REGS) &&
113962306a36Sopenharmony_ci			    xres <= (M64_HAS(MOBIL_BUS) ? 1024 : 800))
114062306a36Sopenharmony_ci				crtc->ext_vert_stretch |= VERT_STRETCH_MODE;
114162306a36Sopenharmony_ci		} else {
114262306a36Sopenharmony_ci			/*
114362306a36Sopenharmony_ci			 * Don't use vertical blending if the mode is too wide
114462306a36Sopenharmony_ci			 * or not vertically stretched.
114562306a36Sopenharmony_ci			 */
114662306a36Sopenharmony_ci			crtc->vert_stretching = 0;
114762306a36Sopenharmony_ci		}
114862306a36Sopenharmony_ci		/* copy to shadow crtc */
114962306a36Sopenharmony_ci		crtc->shadow_h_tot_disp = crtc->h_tot_disp;
115062306a36Sopenharmony_ci		crtc->shadow_h_sync_strt_wid = crtc->h_sync_strt_wid;
115162306a36Sopenharmony_ci		crtc->shadow_v_tot_disp = crtc->v_tot_disp;
115262306a36Sopenharmony_ci		crtc->shadow_v_sync_strt_wid = crtc->v_sync_strt_wid;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GENERIC_LCD */
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	if (M64_HAS(MAGIC_FIFO)) {
115762306a36Sopenharmony_ci		/* FIXME: display FIFO low watermark values */
115862306a36Sopenharmony_ci		crtc->gen_cntl |= (aty_ld_le32(CRTC_GEN_CNTL, par) & CRTC_FIFO_LWM);
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci	crtc->dp_pix_width = dp_pix_width;
116162306a36Sopenharmony_ci	crtc->dp_chain_mask = dp_chain_mask;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	return 0;
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int aty_crtc_to_var(const struct crtc *crtc,
116762306a36Sopenharmony_ci			   struct fb_var_screeninfo *var)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	u32 xres, yres, bpp, left, right, upper, lower, hslen, vslen, sync;
117062306a36Sopenharmony_ci	u32 h_total, h_disp, h_sync_strt, h_sync_dly, h_sync_wid, h_sync_pol;
117162306a36Sopenharmony_ci	u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
117262306a36Sopenharmony_ci	u32 pix_width;
117362306a36Sopenharmony_ci	u32 double_scan, interlace;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	/* input */
117662306a36Sopenharmony_ci	h_total = crtc->h_tot_disp & 0x1ff;
117762306a36Sopenharmony_ci	h_disp = (crtc->h_tot_disp >> 16) & 0xff;
117862306a36Sopenharmony_ci	h_sync_strt = (crtc->h_sync_strt_wid & 0xff) | ((crtc->h_sync_strt_wid >> 4) & 0x100);
117962306a36Sopenharmony_ci	h_sync_dly = (crtc->h_sync_strt_wid >> 8) & 0x7;
118062306a36Sopenharmony_ci	h_sync_wid = (crtc->h_sync_strt_wid >> 16) & 0x1f;
118162306a36Sopenharmony_ci	h_sync_pol = (crtc->h_sync_strt_wid >> 21) & 0x1;
118262306a36Sopenharmony_ci	v_total = crtc->v_tot_disp & 0x7ff;
118362306a36Sopenharmony_ci	v_disp = (crtc->v_tot_disp >> 16) & 0x7ff;
118462306a36Sopenharmony_ci	v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
118562306a36Sopenharmony_ci	v_sync_wid = (crtc->v_sync_strt_wid >> 16) & 0x1f;
118662306a36Sopenharmony_ci	v_sync_pol = (crtc->v_sync_strt_wid >> 21) & 0x1;
118762306a36Sopenharmony_ci	c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
118862306a36Sopenharmony_ci	pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
118962306a36Sopenharmony_ci	double_scan = crtc->gen_cntl & CRTC_DBL_SCAN_EN;
119062306a36Sopenharmony_ci	interlace = crtc->gen_cntl & CRTC_INTERLACE_EN;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	/* convert */
119362306a36Sopenharmony_ci	xres = (h_disp + 1) * 8;
119462306a36Sopenharmony_ci	yres = v_disp + 1;
119562306a36Sopenharmony_ci	left = (h_total - h_sync_strt - h_sync_wid) * 8 - h_sync_dly;
119662306a36Sopenharmony_ci	right = (h_sync_strt - h_disp) * 8 + h_sync_dly;
119762306a36Sopenharmony_ci	hslen = h_sync_wid * 8;
119862306a36Sopenharmony_ci	upper = v_total - v_sync_strt - v_sync_wid;
119962306a36Sopenharmony_ci	lower = v_sync_strt - v_disp;
120062306a36Sopenharmony_ci	vslen = v_sync_wid;
120162306a36Sopenharmony_ci	sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
120262306a36Sopenharmony_ci		(v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
120362306a36Sopenharmony_ci		(c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	switch (pix_width) {
120662306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_8BPP:
120762306a36Sopenharmony_ci		bpp = 8;
120862306a36Sopenharmony_ci		var->red.offset = 0;
120962306a36Sopenharmony_ci		var->red.length = 8;
121062306a36Sopenharmony_ci		var->green.offset = 0;
121162306a36Sopenharmony_ci		var->green.length = 8;
121262306a36Sopenharmony_ci		var->blue.offset = 0;
121362306a36Sopenharmony_ci		var->blue.length = 8;
121462306a36Sopenharmony_ci		var->transp.offset = 0;
121562306a36Sopenharmony_ci		var->transp.length = 0;
121662306a36Sopenharmony_ci		break;
121762306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_15BPP:	/* RGB 555 */
121862306a36Sopenharmony_ci		bpp = 16;
121962306a36Sopenharmony_ci		var->red.offset = 10;
122062306a36Sopenharmony_ci		var->red.length = 5;
122162306a36Sopenharmony_ci		var->green.offset = 5;
122262306a36Sopenharmony_ci		var->green.length = 5;
122362306a36Sopenharmony_ci		var->blue.offset = 0;
122462306a36Sopenharmony_ci		var->blue.length = 5;
122562306a36Sopenharmony_ci		var->transp.offset = 0;
122662306a36Sopenharmony_ci		var->transp.length = 0;
122762306a36Sopenharmony_ci		break;
122862306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_16BPP:	/* RGB 565 */
122962306a36Sopenharmony_ci		bpp = 16;
123062306a36Sopenharmony_ci		var->red.offset = 11;
123162306a36Sopenharmony_ci		var->red.length = 5;
123262306a36Sopenharmony_ci		var->green.offset = 5;
123362306a36Sopenharmony_ci		var->green.length = 6;
123462306a36Sopenharmony_ci		var->blue.offset = 0;
123562306a36Sopenharmony_ci		var->blue.length = 5;
123662306a36Sopenharmony_ci		var->transp.offset = 0;
123762306a36Sopenharmony_ci		var->transp.length = 0;
123862306a36Sopenharmony_ci		break;
123962306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_24BPP:	/* RGB 888 */
124062306a36Sopenharmony_ci		bpp = 24;
124162306a36Sopenharmony_ci		var->red.offset = 16;
124262306a36Sopenharmony_ci		var->red.length = 8;
124362306a36Sopenharmony_ci		var->green.offset = 8;
124462306a36Sopenharmony_ci		var->green.length = 8;
124562306a36Sopenharmony_ci		var->blue.offset = 0;
124662306a36Sopenharmony_ci		var->blue.length = 8;
124762306a36Sopenharmony_ci		var->transp.offset = 0;
124862306a36Sopenharmony_ci		var->transp.length = 0;
124962306a36Sopenharmony_ci		break;
125062306a36Sopenharmony_ci	case CRTC_PIX_WIDTH_32BPP:	/* ARGB 8888 */
125162306a36Sopenharmony_ci		bpp = 32;
125262306a36Sopenharmony_ci		var->red.offset = 16;
125362306a36Sopenharmony_ci		var->red.length = 8;
125462306a36Sopenharmony_ci		var->green.offset = 8;
125562306a36Sopenharmony_ci		var->green.length = 8;
125662306a36Sopenharmony_ci		var->blue.offset = 0;
125762306a36Sopenharmony_ci		var->blue.length = 8;
125862306a36Sopenharmony_ci		var->transp.offset = 24;
125962306a36Sopenharmony_ci		var->transp.length = 8;
126062306a36Sopenharmony_ci		break;
126162306a36Sopenharmony_ci	default:
126262306a36Sopenharmony_ci		PRINTKE("Invalid pixel width\n");
126362306a36Sopenharmony_ci		return -EINVAL;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	/* output */
126762306a36Sopenharmony_ci	var->xres = xres;
126862306a36Sopenharmony_ci	var->yres = yres;
126962306a36Sopenharmony_ci	var->xres_virtual = crtc->vxres;
127062306a36Sopenharmony_ci	var->yres_virtual = crtc->vyres;
127162306a36Sopenharmony_ci	var->bits_per_pixel = bpp;
127262306a36Sopenharmony_ci	var->left_margin = left;
127362306a36Sopenharmony_ci	var->right_margin = right;
127462306a36Sopenharmony_ci	var->upper_margin = upper;
127562306a36Sopenharmony_ci	var->lower_margin = lower;
127662306a36Sopenharmony_ci	var->hsync_len = hslen;
127762306a36Sopenharmony_ci	var->vsync_len = vslen;
127862306a36Sopenharmony_ci	var->sync = sync;
127962306a36Sopenharmony_ci	var->vmode = FB_VMODE_NONINTERLACED;
128062306a36Sopenharmony_ci	/*
128162306a36Sopenharmony_ci	 * In double scan mode, the vertical parameters are doubled,
128262306a36Sopenharmony_ci	 * so we need to halve them to get the right values.
128362306a36Sopenharmony_ci	 * In interlaced mode the values are already correct,
128462306a36Sopenharmony_ci	 * so no correction is necessary.
128562306a36Sopenharmony_ci	 */
128662306a36Sopenharmony_ci	if (interlace)
128762306a36Sopenharmony_ci		var->vmode = FB_VMODE_INTERLACED;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (double_scan) {
129062306a36Sopenharmony_ci		var->vmode = FB_VMODE_DOUBLE;
129162306a36Sopenharmony_ci		var->yres >>= 1;
129262306a36Sopenharmony_ci		var->upper_margin >>= 1;
129362306a36Sopenharmony_ci		var->lower_margin >>= 1;
129462306a36Sopenharmony_ci		var->vsync_len >>= 1;
129562306a36Sopenharmony_ci	}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	return 0;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic int atyfb_set_par(struct fb_info *info)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
130562306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
130662306a36Sopenharmony_ci	u32 tmp, pixclock;
130762306a36Sopenharmony_ci	int err;
130862306a36Sopenharmony_ci#ifdef DEBUG
130962306a36Sopenharmony_ci	struct fb_var_screeninfo debug;
131062306a36Sopenharmony_ci	u32 pixclock_in_ps;
131162306a36Sopenharmony_ci#endif
131262306a36Sopenharmony_ci	if (par->asleep)
131362306a36Sopenharmony_ci		return 0;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	err = aty_var_to_crtc(info, var, &par->crtc);
131662306a36Sopenharmony_ci	if (err)
131762306a36Sopenharmony_ci		return err;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	pixclock = atyfb_get_pixclock(var, par);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (pixclock == 0) {
132262306a36Sopenharmony_ci		PRINTKE("Invalid pixclock\n");
132362306a36Sopenharmony_ci		return -EINVAL;
132462306a36Sopenharmony_ci	} else {
132562306a36Sopenharmony_ci		err = par->pll_ops->var_to_pll(info, pixclock,
132662306a36Sopenharmony_ci					       var->bits_per_pixel, &par->pll);
132762306a36Sopenharmony_ci		if (err)
132862306a36Sopenharmony_ci			return err;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	par->accel_flags = var->accel_flags; /* hack */
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (var->accel_flags) {
133462306a36Sopenharmony_ci		atyfb_ops.fb_sync = atyfb_sync;
133562306a36Sopenharmony_ci		info->flags &= ~FBINFO_HWACCEL_DISABLED;
133662306a36Sopenharmony_ci	} else {
133762306a36Sopenharmony_ci		atyfb_ops.fb_sync = NULL;
133862306a36Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_DISABLED;
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	if (par->blitter_may_be_busy)
134262306a36Sopenharmony_ci		wait_for_idle(par);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	aty_set_crtc(par, &par->crtc);
134562306a36Sopenharmony_ci	par->dac_ops->set_dac(info, &par->pll,
134662306a36Sopenharmony_ci			      var->bits_per_pixel, par->accel_flags);
134762306a36Sopenharmony_ci	par->pll_ops->set_pll(info, &par->pll);
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci#ifdef DEBUG
135062306a36Sopenharmony_ci	if (par->pll_ops && par->pll_ops->pll_to_var)
135162306a36Sopenharmony_ci		pixclock_in_ps = par->pll_ops->pll_to_var(info, &par->pll);
135262306a36Sopenharmony_ci	else
135362306a36Sopenharmony_ci		pixclock_in_ps = 0;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (0 == pixclock_in_ps) {
135662306a36Sopenharmony_ci		PRINTKE("ALERT ops->pll_to_var get 0\n");
135762306a36Sopenharmony_ci		pixclock_in_ps = pixclock;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	memset(&debug, 0, sizeof(debug));
136162306a36Sopenharmony_ci	if (!aty_crtc_to_var(&par->crtc, &debug)) {
136262306a36Sopenharmony_ci		u32 hSync, vRefresh;
136362306a36Sopenharmony_ci		u32 h_disp, h_sync_strt, h_sync_end, h_total;
136462306a36Sopenharmony_ci		u32 v_disp, v_sync_strt, v_sync_end, v_total;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci		h_disp = debug.xres;
136762306a36Sopenharmony_ci		h_sync_strt = h_disp + debug.right_margin;
136862306a36Sopenharmony_ci		h_sync_end = h_sync_strt + debug.hsync_len;
136962306a36Sopenharmony_ci		h_total = h_sync_end + debug.left_margin;
137062306a36Sopenharmony_ci		v_disp = debug.yres;
137162306a36Sopenharmony_ci		v_sync_strt = v_disp + debug.lower_margin;
137262306a36Sopenharmony_ci		v_sync_end = v_sync_strt + debug.vsync_len;
137362306a36Sopenharmony_ci		v_total = v_sync_end + debug.upper_margin;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci		hSync = 1000000000 / (pixclock_in_ps * h_total);
137662306a36Sopenharmony_ci		vRefresh = (hSync * 1000) / v_total;
137762306a36Sopenharmony_ci		if (par->crtc.gen_cntl & CRTC_INTERLACE_EN)
137862306a36Sopenharmony_ci			vRefresh *= 2;
137962306a36Sopenharmony_ci		if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
138062306a36Sopenharmony_ci			vRefresh /= 2;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci		DPRINTK("atyfb_set_par\n");
138362306a36Sopenharmony_ci		DPRINTK(" Set Visible Mode to %ix%i-%i\n",
138462306a36Sopenharmony_ci			var->xres, var->yres, var->bits_per_pixel);
138562306a36Sopenharmony_ci		DPRINTK(" Virtual resolution %ix%i, "
138662306a36Sopenharmony_ci			"pixclock_in_ps %i (calculated %i)\n",
138762306a36Sopenharmony_ci			var->xres_virtual, var->yres_virtual,
138862306a36Sopenharmony_ci			pixclock, pixclock_in_ps);
138962306a36Sopenharmony_ci		DPRINTK(" Dot clock:           %i MHz\n",
139062306a36Sopenharmony_ci			1000000 / pixclock_in_ps);
139162306a36Sopenharmony_ci		DPRINTK(" Horizontal sync:     %i kHz\n", hSync);
139262306a36Sopenharmony_ci		DPRINTK(" Vertical refresh:    %i Hz\n", vRefresh);
139362306a36Sopenharmony_ci		DPRINTK(" x  style: %i.%03i %i %i %i %i   %i %i %i %i\n",
139462306a36Sopenharmony_ci			1000000 / pixclock_in_ps, 1000000 % pixclock_in_ps,
139562306a36Sopenharmony_ci			h_disp, h_sync_strt, h_sync_end, h_total,
139662306a36Sopenharmony_ci			v_disp, v_sync_strt, v_sync_end, v_total);
139762306a36Sopenharmony_ci		DPRINTK(" fb style: %i  %i %i %i %i %i %i %i %i\n",
139862306a36Sopenharmony_ci			pixclock_in_ps,
139962306a36Sopenharmony_ci			debug.left_margin, h_disp, debug.right_margin, debug.hsync_len,
140062306a36Sopenharmony_ci			debug.upper_margin, v_disp, debug.lower_margin, debug.vsync_len);
140162306a36Sopenharmony_ci	}
140262306a36Sopenharmony_ci#endif /* DEBUG */
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if (!M64_HAS(INTEGRATED)) {
140562306a36Sopenharmony_ci		/* Don't forget MEM_CNTL */
140662306a36Sopenharmony_ci		tmp = aty_ld_le32(MEM_CNTL, par) & 0xf0ffffff;
140762306a36Sopenharmony_ci		switch (var->bits_per_pixel) {
140862306a36Sopenharmony_ci		case 8:
140962306a36Sopenharmony_ci			tmp |= 0x02000000;
141062306a36Sopenharmony_ci			break;
141162306a36Sopenharmony_ci		case 16:
141262306a36Sopenharmony_ci			tmp |= 0x03000000;
141362306a36Sopenharmony_ci			break;
141462306a36Sopenharmony_ci		case 32:
141562306a36Sopenharmony_ci			tmp |= 0x06000000;
141662306a36Sopenharmony_ci			break;
141762306a36Sopenharmony_ci		}
141862306a36Sopenharmony_ci		aty_st_le32(MEM_CNTL, tmp, par);
141962306a36Sopenharmony_ci	} else {
142062306a36Sopenharmony_ci		tmp = aty_ld_le32(MEM_CNTL, par) & 0xf00fffff;
142162306a36Sopenharmony_ci		if (!M64_HAS(MAGIC_POSTDIV))
142262306a36Sopenharmony_ci			tmp |= par->mem_refresh_rate << 20;
142362306a36Sopenharmony_ci		switch (var->bits_per_pixel) {
142462306a36Sopenharmony_ci		case 8:
142562306a36Sopenharmony_ci		case 24:
142662306a36Sopenharmony_ci			tmp |= 0x00000000;
142762306a36Sopenharmony_ci			break;
142862306a36Sopenharmony_ci		case 16:
142962306a36Sopenharmony_ci			tmp |= 0x04000000;
143062306a36Sopenharmony_ci			break;
143162306a36Sopenharmony_ci		case 32:
143262306a36Sopenharmony_ci			tmp |= 0x08000000;
143362306a36Sopenharmony_ci			break;
143462306a36Sopenharmony_ci		}
143562306a36Sopenharmony_ci		if (M64_HAS(CT_BUS)) {
143662306a36Sopenharmony_ci			aty_st_le32(DAC_CNTL, 0x87010184, par);
143762306a36Sopenharmony_ci			aty_st_le32(BUS_CNTL, 0x680000f9, par);
143862306a36Sopenharmony_ci		} else if (M64_HAS(VT_BUS)) {
143962306a36Sopenharmony_ci			aty_st_le32(DAC_CNTL, 0x87010184, par);
144062306a36Sopenharmony_ci			aty_st_le32(BUS_CNTL, 0x680000f9, par);
144162306a36Sopenharmony_ci		} else if (M64_HAS(MOBIL_BUS)) {
144262306a36Sopenharmony_ci			aty_st_le32(DAC_CNTL, 0x80010102, par);
144362306a36Sopenharmony_ci			aty_st_le32(BUS_CNTL, 0x7b33a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
144462306a36Sopenharmony_ci		} else {
144562306a36Sopenharmony_ci			/* GT */
144662306a36Sopenharmony_ci			aty_st_le32(DAC_CNTL, 0x86010102, par);
144762306a36Sopenharmony_ci			aty_st_le32(BUS_CNTL, 0x7b23a040 | (par->aux_start ? BUS_APER_REG_DIS : 0), par);
144862306a36Sopenharmony_ci			aty_st_le32(EXT_MEM_CNTL, aty_ld_le32(EXT_MEM_CNTL, par) | 0x5000001, par);
144962306a36Sopenharmony_ci		}
145062306a36Sopenharmony_ci		aty_st_le32(MEM_CNTL, tmp, par);
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci	aty_st_8(DAC_MASK, 0xff, par);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	info->fix.line_length = calc_line_length(par, var->xres_virtual,
145562306a36Sopenharmony_ci						 var->bits_per_pixel);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	info->fix.visual = var->bits_per_pixel <= 8 ?
145862306a36Sopenharmony_ci		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	/* Initialize the graphics engine */
146162306a36Sopenharmony_ci	if (par->accel_flags & FB_ACCELF_TEXT)
146262306a36Sopenharmony_ci		aty_init_engine(par, info);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci#ifdef CONFIG_BOOTX_TEXT
146562306a36Sopenharmony_ci	btext_update_display(info->fix.smem_start,
146662306a36Sopenharmony_ci		(((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8,
146762306a36Sopenharmony_ci		((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1,
146862306a36Sopenharmony_ci		var->bits_per_pixel,
146962306a36Sopenharmony_ci		par->crtc.vxres * var->bits_per_pixel / 8);
147062306a36Sopenharmony_ci#endif /* CONFIG_BOOTX_TEXT */
147162306a36Sopenharmony_ci#ifdef DEBUG
147262306a36Sopenharmony_ci{
147362306a36Sopenharmony_ci	/* dump non shadow CRTC, pll, LCD registers */
147462306a36Sopenharmony_ci	int i; u32 base;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	/* CRTC registers */
147762306a36Sopenharmony_ci	base = 0x2000;
147862306a36Sopenharmony_ci	printk("debug atyfb: Mach64 non-shadow register values:");
147962306a36Sopenharmony_ci	for (i = 0; i < 256; i = i+4) {
148062306a36Sopenharmony_ci		if (i % 16 == 0) {
148162306a36Sopenharmony_ci			pr_cont("\n");
148262306a36Sopenharmony_ci			printk("debug atyfb: 0x%04X: ", base + i);
148362306a36Sopenharmony_ci		}
148462306a36Sopenharmony_ci		pr_cont(" %08X", aty_ld_le32(i, par));
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci	pr_cont("\n\n");
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
148962306a36Sopenharmony_ci	/* PLL registers */
149062306a36Sopenharmony_ci	base = 0x00;
149162306a36Sopenharmony_ci	printk("debug atyfb: Mach64 PLL register values:");
149262306a36Sopenharmony_ci	for (i = 0; i < 64; i++) {
149362306a36Sopenharmony_ci		if (i % 16 == 0) {
149462306a36Sopenharmony_ci			pr_cont("\n");
149562306a36Sopenharmony_ci			printk("debug atyfb: 0x%02X: ", base + i);
149662306a36Sopenharmony_ci		}
149762306a36Sopenharmony_ci		if (i % 4 == 0)
149862306a36Sopenharmony_ci			pr_cont(" ");
149962306a36Sopenharmony_ci		pr_cont("%02X", aty_ld_pll_ct(i, par));
150062306a36Sopenharmony_ci	}
150162306a36Sopenharmony_ci	pr_cont("\n\n");
150262306a36Sopenharmony_ci#endif	/* CONFIG_FB_ATY_CT */
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
150562306a36Sopenharmony_ci	if (par->lcd_table != 0) {
150662306a36Sopenharmony_ci		/* LCD registers */
150762306a36Sopenharmony_ci		base = 0x00;
150862306a36Sopenharmony_ci		printk("debug atyfb: LCD register values:");
150962306a36Sopenharmony_ci		if (M64_HAS(LT_LCD_REGS)) {
151062306a36Sopenharmony_ci			for (i = 0; i <= POWER_MANAGEMENT; i++) {
151162306a36Sopenharmony_ci				if (i == EXT_VERT_STRETCH)
151262306a36Sopenharmony_ci					continue;
151362306a36Sopenharmony_ci				pr_cont("\ndebug atyfb: 0x%04X: ",
151462306a36Sopenharmony_ci				       lt_lcd_regs[i]);
151562306a36Sopenharmony_ci				pr_cont(" %08X", aty_ld_lcd(i, par));
151662306a36Sopenharmony_ci			}
151762306a36Sopenharmony_ci		} else {
151862306a36Sopenharmony_ci			for (i = 0; i < 64; i++) {
151962306a36Sopenharmony_ci				if (i % 4 == 0)
152062306a36Sopenharmony_ci					pr_cont("\ndebug atyfb: 0x%02X: ",
152162306a36Sopenharmony_ci					       base + i);
152262306a36Sopenharmony_ci				pr_cont(" %08X", aty_ld_lcd(i, par));
152362306a36Sopenharmony_ci			}
152462306a36Sopenharmony_ci		}
152562306a36Sopenharmony_ci		pr_cont("\n\n");
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GENERIC_LCD */
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci#endif /* DEBUG */
153062306a36Sopenharmony_ci	return 0;
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_cistatic int atyfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
153662306a36Sopenharmony_ci	int err;
153762306a36Sopenharmony_ci	struct crtc crtc;
153862306a36Sopenharmony_ci	union aty_pll pll;
153962306a36Sopenharmony_ci	u32 pixclock;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	memcpy(&pll, &par->pll, sizeof(pll));
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	err = aty_var_to_crtc(info, var, &crtc);
154462306a36Sopenharmony_ci	if (err)
154562306a36Sopenharmony_ci		return err;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	pixclock = atyfb_get_pixclock(var, par);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (pixclock == 0) {
155062306a36Sopenharmony_ci		if (!(var->activate & FB_ACTIVATE_TEST))
155162306a36Sopenharmony_ci			PRINTKE("Invalid pixclock\n");
155262306a36Sopenharmony_ci		return -EINVAL;
155362306a36Sopenharmony_ci	} else {
155462306a36Sopenharmony_ci		err = par->pll_ops->var_to_pll(info, pixclock,
155562306a36Sopenharmony_ci					       var->bits_per_pixel, &pll);
155662306a36Sopenharmony_ci		if (err)
155762306a36Sopenharmony_ci			return err;
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	if (var->accel_flags & FB_ACCELF_TEXT)
156162306a36Sopenharmony_ci		info->var.accel_flags = FB_ACCELF_TEXT;
156262306a36Sopenharmony_ci	else
156362306a36Sopenharmony_ci		info->var.accel_flags = 0;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	aty_crtc_to_var(&crtc, var);
156662306a36Sopenharmony_ci	var->pixclock = par->pll_ops->pll_to_var(info, &pll);
156762306a36Sopenharmony_ci	return 0;
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_cistatic void set_off_pitch(struct atyfb_par *par, const struct fb_info *info)
157162306a36Sopenharmony_ci{
157262306a36Sopenharmony_ci	u32 xoffset = info->var.xoffset;
157362306a36Sopenharmony_ci	u32 yoffset = info->var.yoffset;
157462306a36Sopenharmony_ci	u32 line_length = info->fix.line_length;
157562306a36Sopenharmony_ci	u32 bpp = info->var.bits_per_pixel;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	par->crtc.off_pitch =
157862306a36Sopenharmony_ci		((yoffset * line_length + xoffset * bpp / 8) / 8) |
157962306a36Sopenharmony_ci		((line_length / bpp) << 22);
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci/*
158462306a36Sopenharmony_ci * Open/Release the frame buffer device
158562306a36Sopenharmony_ci */
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_cistatic int atyfb_open(struct fb_info *info, int user)
158862306a36Sopenharmony_ci{
158962306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	if (user) {
159262306a36Sopenharmony_ci		par->open++;
159362306a36Sopenharmony_ci#ifdef __sparc__
159462306a36Sopenharmony_ci		par->mmaped = 0;
159562306a36Sopenharmony_ci#endif
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci	return 0;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_cistatic irqreturn_t aty_irq(int irq, void *dev_id)
160162306a36Sopenharmony_ci{
160262306a36Sopenharmony_ci	struct atyfb_par *par = dev_id;
160362306a36Sopenharmony_ci	int handled = 0;
160462306a36Sopenharmony_ci	u32 int_cntl;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	spin_lock(&par->int_lock);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	int_cntl = aty_ld_le32(CRTC_INT_CNTL, par);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	if (int_cntl & CRTC_VBLANK_INT) {
161162306a36Sopenharmony_ci		/* clear interrupt */
161262306a36Sopenharmony_ci		aty_st_le32(CRTC_INT_CNTL, (int_cntl & CRTC_INT_EN_MASK) |
161362306a36Sopenharmony_ci			    CRTC_VBLANK_INT_AK, par);
161462306a36Sopenharmony_ci		par->vblank.count++;
161562306a36Sopenharmony_ci		if (par->vblank.pan_display) {
161662306a36Sopenharmony_ci			par->vblank.pan_display = 0;
161762306a36Sopenharmony_ci			aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
161862306a36Sopenharmony_ci		}
161962306a36Sopenharmony_ci		wake_up_interruptible(&par->vblank.wait);
162062306a36Sopenharmony_ci		handled = 1;
162162306a36Sopenharmony_ci	}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	spin_unlock(&par->int_lock);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
162662306a36Sopenharmony_ci}
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_cistatic int aty_enable_irq(struct atyfb_par *par, int reenable)
162962306a36Sopenharmony_ci{
163062306a36Sopenharmony_ci	u32 int_cntl;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	if (!test_and_set_bit(0, &par->irq_flags)) {
163362306a36Sopenharmony_ci		if (request_irq(par->irq, aty_irq, IRQF_SHARED, "atyfb", par)) {
163462306a36Sopenharmony_ci			clear_bit(0, &par->irq_flags);
163562306a36Sopenharmony_ci			return -EINVAL;
163662306a36Sopenharmony_ci		}
163762306a36Sopenharmony_ci		spin_lock_irq(&par->int_lock);
163862306a36Sopenharmony_ci		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
163962306a36Sopenharmony_ci		/* clear interrupt */
164062306a36Sopenharmony_ci		aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_AK, par);
164162306a36Sopenharmony_ci		/* enable interrupt */
164262306a36Sopenharmony_ci		aty_st_le32(CRTC_INT_CNTL, int_cntl | CRTC_VBLANK_INT_EN, par);
164362306a36Sopenharmony_ci		spin_unlock_irq(&par->int_lock);
164462306a36Sopenharmony_ci	} else if (reenable) {
164562306a36Sopenharmony_ci		spin_lock_irq(&par->int_lock);
164662306a36Sopenharmony_ci		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
164762306a36Sopenharmony_ci		if (!(int_cntl & CRTC_VBLANK_INT_EN)) {
164862306a36Sopenharmony_ci			printk("atyfb: someone disabled IRQ [%08x]\n",
164962306a36Sopenharmony_ci			       int_cntl);
165062306a36Sopenharmony_ci			/* re-enable interrupt */
165162306a36Sopenharmony_ci			aty_st_le32(CRTC_INT_CNTL, int_cntl |
165262306a36Sopenharmony_ci				    CRTC_VBLANK_INT_EN, par);
165362306a36Sopenharmony_ci		}
165462306a36Sopenharmony_ci		spin_unlock_irq(&par->int_lock);
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	return 0;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic int aty_disable_irq(struct atyfb_par *par)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	u32 int_cntl;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	if (test_and_clear_bit(0, &par->irq_flags)) {
166562306a36Sopenharmony_ci		if (par->vblank.pan_display) {
166662306a36Sopenharmony_ci			par->vblank.pan_display = 0;
166762306a36Sopenharmony_ci			aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
166862306a36Sopenharmony_ci		}
166962306a36Sopenharmony_ci		spin_lock_irq(&par->int_lock);
167062306a36Sopenharmony_ci		int_cntl = aty_ld_le32(CRTC_INT_CNTL, par) & CRTC_INT_EN_MASK;
167162306a36Sopenharmony_ci		/* disable interrupt */
167262306a36Sopenharmony_ci		aty_st_le32(CRTC_INT_CNTL, int_cntl & ~CRTC_VBLANK_INT_EN, par);
167362306a36Sopenharmony_ci		spin_unlock_irq(&par->int_lock);
167462306a36Sopenharmony_ci		free_irq(par->irq, par);
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	return 0;
167862306a36Sopenharmony_ci}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_cistatic int atyfb_release(struct fb_info *info, int user)
168162306a36Sopenharmony_ci{
168262306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
168362306a36Sopenharmony_ci#ifdef __sparc__
168462306a36Sopenharmony_ci	int was_mmaped;
168562306a36Sopenharmony_ci#endif
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	if (!user)
168862306a36Sopenharmony_ci		return 0;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	par->open--;
169162306a36Sopenharmony_ci	mdelay(1);
169262306a36Sopenharmony_ci	wait_for_idle(par);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	if (par->open)
169562306a36Sopenharmony_ci		return 0;
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci#ifdef __sparc__
169862306a36Sopenharmony_ci	was_mmaped = par->mmaped;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	par->mmaped = 0;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	if (was_mmaped) {
170362306a36Sopenharmony_ci		struct fb_var_screeninfo var;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci		/*
170662306a36Sopenharmony_ci		 * Now reset the default display config, we have
170762306a36Sopenharmony_ci		 * no idea what the program(s) which mmap'd the
170862306a36Sopenharmony_ci		 * chip did to the configuration, nor whether it
170962306a36Sopenharmony_ci		 * restored it correctly.
171062306a36Sopenharmony_ci		 */
171162306a36Sopenharmony_ci		var = default_var;
171262306a36Sopenharmony_ci		if (noaccel)
171362306a36Sopenharmony_ci			var.accel_flags &= ~FB_ACCELF_TEXT;
171462306a36Sopenharmony_ci		else
171562306a36Sopenharmony_ci			var.accel_flags |= FB_ACCELF_TEXT;
171662306a36Sopenharmony_ci		if (var.yres == var.yres_virtual) {
171762306a36Sopenharmony_ci			u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2));
171862306a36Sopenharmony_ci			var.yres_virtual =
171962306a36Sopenharmony_ci				((videoram * 8) / var.bits_per_pixel) /
172062306a36Sopenharmony_ci				var.xres_virtual;
172162306a36Sopenharmony_ci			if (var.yres_virtual < var.yres)
172262306a36Sopenharmony_ci				var.yres_virtual = var.yres;
172362306a36Sopenharmony_ci		}
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci#endif
172662306a36Sopenharmony_ci	aty_disable_irq(par);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	return 0;
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci/*
173262306a36Sopenharmony_ci * Pan or Wrap the Display
173362306a36Sopenharmony_ci *
173462306a36Sopenharmony_ci * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
173562306a36Sopenharmony_ci */
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic int atyfb_pan_display(struct fb_var_screeninfo *var,
173862306a36Sopenharmony_ci			     struct fb_info *info)
173962306a36Sopenharmony_ci{
174062306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
174162306a36Sopenharmony_ci	u32 xres, yres, xoffset, yoffset;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	xres = (((par->crtc.h_tot_disp >> 16) & 0xff) + 1) * 8;
174462306a36Sopenharmony_ci	yres = ((par->crtc.v_tot_disp >> 16) & 0x7ff) + 1;
174562306a36Sopenharmony_ci	if (par->crtc.gen_cntl & CRTC_DBL_SCAN_EN)
174662306a36Sopenharmony_ci		yres >>= 1;
174762306a36Sopenharmony_ci	xoffset = (var->xoffset + 7) & ~7;
174862306a36Sopenharmony_ci	yoffset = var->yoffset;
174962306a36Sopenharmony_ci	if (xoffset + xres > par->crtc.vxres ||
175062306a36Sopenharmony_ci	    yoffset + yres > par->crtc.vyres)
175162306a36Sopenharmony_ci		return -EINVAL;
175262306a36Sopenharmony_ci	info->var.xoffset = xoffset;
175362306a36Sopenharmony_ci	info->var.yoffset = yoffset;
175462306a36Sopenharmony_ci	if (par->asleep)
175562306a36Sopenharmony_ci		return 0;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	set_off_pitch(par, info);
175862306a36Sopenharmony_ci	if ((var->activate & FB_ACTIVATE_VBL) && !aty_enable_irq(par, 0)) {
175962306a36Sopenharmony_ci		par->vblank.pan_display = 1;
176062306a36Sopenharmony_ci	} else {
176162306a36Sopenharmony_ci		par->vblank.pan_display = 0;
176262306a36Sopenharmony_ci		aty_st_le32(CRTC_OFF_PITCH, par->crtc.off_pitch, par);
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	return 0;
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic int aty_waitforvblank(struct atyfb_par *par, u32 crtc)
176962306a36Sopenharmony_ci{
177062306a36Sopenharmony_ci	struct aty_interrupt *vbl;
177162306a36Sopenharmony_ci	unsigned int count;
177262306a36Sopenharmony_ci	int ret;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	switch (crtc) {
177562306a36Sopenharmony_ci	case 0:
177662306a36Sopenharmony_ci		vbl = &par->vblank;
177762306a36Sopenharmony_ci		break;
177862306a36Sopenharmony_ci	default:
177962306a36Sopenharmony_ci		return -ENODEV;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	ret = aty_enable_irq(par, 0);
178362306a36Sopenharmony_ci	if (ret)
178462306a36Sopenharmony_ci		return ret;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	count = vbl->count;
178762306a36Sopenharmony_ci	ret = wait_event_interruptible_timeout(vbl->wait,
178862306a36Sopenharmony_ci					       count != vbl->count, HZ/10);
178962306a36Sopenharmony_ci	if (ret < 0)
179062306a36Sopenharmony_ci		return ret;
179162306a36Sopenharmony_ci	if (ret == 0) {
179262306a36Sopenharmony_ci		aty_enable_irq(par, 1);
179362306a36Sopenharmony_ci		return -ETIMEDOUT;
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	return 0;
179762306a36Sopenharmony_ci}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci#ifdef DEBUG
180162306a36Sopenharmony_ci#define ATYIO_CLKR		0x41545900	/* ATY\00 */
180262306a36Sopenharmony_ci#define ATYIO_CLKW		0x41545901	/* ATY\01 */
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_cistruct atyclk {
180562306a36Sopenharmony_ci	u32 ref_clk_per;
180662306a36Sopenharmony_ci	u8 pll_ref_div;
180762306a36Sopenharmony_ci	u8 mclk_fb_div;
180862306a36Sopenharmony_ci	u8 mclk_post_div;	/* 1,2,3,4,8 */
180962306a36Sopenharmony_ci	u8 mclk_fb_mult;	/* 2 or 4 */
181062306a36Sopenharmony_ci	u8 xclk_post_div;	/* 1,2,3,4,8 */
181162306a36Sopenharmony_ci	u8 vclk_fb_div;
181262306a36Sopenharmony_ci	u8 vclk_post_div;	/* 1,2,3,4,6,8,12 */
181362306a36Sopenharmony_ci	u32 dsp_xclks_per_row;	/* 0-16383 */
181462306a36Sopenharmony_ci	u32 dsp_loop_latency;	/* 0-15 */
181562306a36Sopenharmony_ci	u32 dsp_precision;	/* 0-7 */
181662306a36Sopenharmony_ci	u32 dsp_on;		/* 0-2047 */
181762306a36Sopenharmony_ci	u32 dsp_off;		/* 0-2047 */
181862306a36Sopenharmony_ci};
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci#define ATYIO_FEATR		0x41545902	/* ATY\02 */
182162306a36Sopenharmony_ci#define ATYIO_FEATW		0x41545903	/* ATY\03 */
182262306a36Sopenharmony_ci#endif
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cistatic int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
182562306a36Sopenharmony_ci{
182662306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
182762306a36Sopenharmony_ci#ifdef __sparc__
182862306a36Sopenharmony_ci	struct fbtype fbtyp;
182962306a36Sopenharmony_ci#endif
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	switch (cmd) {
183262306a36Sopenharmony_ci#ifdef __sparc__
183362306a36Sopenharmony_ci	case FBIOGTYPE:
183462306a36Sopenharmony_ci		fbtyp.fb_type = FBTYPE_PCI_GENERIC;
183562306a36Sopenharmony_ci		fbtyp.fb_width = par->crtc.vxres;
183662306a36Sopenharmony_ci		fbtyp.fb_height = par->crtc.vyres;
183762306a36Sopenharmony_ci		fbtyp.fb_depth = info->var.bits_per_pixel;
183862306a36Sopenharmony_ci		fbtyp.fb_cmsize = info->cmap.len;
183962306a36Sopenharmony_ci		fbtyp.fb_size = info->fix.smem_len;
184062306a36Sopenharmony_ci		if (copy_to_user((struct fbtype __user *) arg, &fbtyp,
184162306a36Sopenharmony_ci				 sizeof(fbtyp)))
184262306a36Sopenharmony_ci			return -EFAULT;
184362306a36Sopenharmony_ci		break;
184462306a36Sopenharmony_ci#endif /* __sparc__ */
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	case FBIO_WAITFORVSYNC:
184762306a36Sopenharmony_ci		{
184862306a36Sopenharmony_ci			u32 crtc;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci			if (get_user(crtc, (__u32 __user *) arg))
185162306a36Sopenharmony_ci				return -EFAULT;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci			return aty_waitforvblank(par, crtc);
185462306a36Sopenharmony_ci		}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci#if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
185762306a36Sopenharmony_ci	case ATYIO_CLKR:
185862306a36Sopenharmony_ci		if (M64_HAS(INTEGRATED)) {
185962306a36Sopenharmony_ci			struct atyclk clk = { 0 };
186062306a36Sopenharmony_ci			union aty_pll *pll = &par->pll;
186162306a36Sopenharmony_ci			u32 dsp_config = pll->ct.dsp_config;
186262306a36Sopenharmony_ci			u32 dsp_on_off = pll->ct.dsp_on_off;
186362306a36Sopenharmony_ci			clk.ref_clk_per = par->ref_clk_per;
186462306a36Sopenharmony_ci			clk.pll_ref_div = pll->ct.pll_ref_div;
186562306a36Sopenharmony_ci			clk.mclk_fb_div = pll->ct.mclk_fb_div;
186662306a36Sopenharmony_ci			clk.mclk_post_div = pll->ct.mclk_post_div_real;
186762306a36Sopenharmony_ci			clk.mclk_fb_mult = pll->ct.mclk_fb_mult;
186862306a36Sopenharmony_ci			clk.xclk_post_div = pll->ct.xclk_post_div_real;
186962306a36Sopenharmony_ci			clk.vclk_fb_div = pll->ct.vclk_fb_div;
187062306a36Sopenharmony_ci			clk.vclk_post_div = pll->ct.vclk_post_div_real;
187162306a36Sopenharmony_ci			clk.dsp_xclks_per_row = dsp_config & 0x3fff;
187262306a36Sopenharmony_ci			clk.dsp_loop_latency = (dsp_config >> 16) & 0xf;
187362306a36Sopenharmony_ci			clk.dsp_precision = (dsp_config >> 20) & 7;
187462306a36Sopenharmony_ci			clk.dsp_off = dsp_on_off & 0x7ff;
187562306a36Sopenharmony_ci			clk.dsp_on = (dsp_on_off >> 16) & 0x7ff;
187662306a36Sopenharmony_ci			if (copy_to_user((struct atyclk __user *) arg, &clk,
187762306a36Sopenharmony_ci					 sizeof(clk)))
187862306a36Sopenharmony_ci				return -EFAULT;
187962306a36Sopenharmony_ci		} else
188062306a36Sopenharmony_ci			return -EINVAL;
188162306a36Sopenharmony_ci		break;
188262306a36Sopenharmony_ci	case ATYIO_CLKW:
188362306a36Sopenharmony_ci		if (M64_HAS(INTEGRATED)) {
188462306a36Sopenharmony_ci			struct atyclk clk;
188562306a36Sopenharmony_ci			union aty_pll *pll = &par->pll;
188662306a36Sopenharmony_ci			if (copy_from_user(&clk, (struct atyclk __user *) arg,
188762306a36Sopenharmony_ci					   sizeof(clk)))
188862306a36Sopenharmony_ci				return -EFAULT;
188962306a36Sopenharmony_ci			par->ref_clk_per = clk.ref_clk_per;
189062306a36Sopenharmony_ci			pll->ct.pll_ref_div = clk.pll_ref_div;
189162306a36Sopenharmony_ci			pll->ct.mclk_fb_div = clk.mclk_fb_div;
189262306a36Sopenharmony_ci			pll->ct.mclk_post_div_real = clk.mclk_post_div;
189362306a36Sopenharmony_ci			pll->ct.mclk_fb_mult = clk.mclk_fb_mult;
189462306a36Sopenharmony_ci			pll->ct.xclk_post_div_real = clk.xclk_post_div;
189562306a36Sopenharmony_ci			pll->ct.vclk_fb_div = clk.vclk_fb_div;
189662306a36Sopenharmony_ci			pll->ct.vclk_post_div_real = clk.vclk_post_div;
189762306a36Sopenharmony_ci			pll->ct.dsp_config = (clk.dsp_xclks_per_row & 0x3fff) |
189862306a36Sopenharmony_ci				((clk.dsp_loop_latency & 0xf) << 16) |
189962306a36Sopenharmony_ci				((clk.dsp_precision & 7) << 20);
190062306a36Sopenharmony_ci			pll->ct.dsp_on_off = (clk.dsp_off & 0x7ff) |
190162306a36Sopenharmony_ci				((clk.dsp_on & 0x7ff) << 16);
190262306a36Sopenharmony_ci			/*aty_calc_pll_ct(info, &pll->ct);*/
190362306a36Sopenharmony_ci			aty_set_pll_ct(info, pll);
190462306a36Sopenharmony_ci		} else
190562306a36Sopenharmony_ci			return -EINVAL;
190662306a36Sopenharmony_ci		break;
190762306a36Sopenharmony_ci	case ATYIO_FEATR:
190862306a36Sopenharmony_ci		if (get_user(par->features, (u32 __user *) arg))
190962306a36Sopenharmony_ci			return -EFAULT;
191062306a36Sopenharmony_ci		break;
191162306a36Sopenharmony_ci	case ATYIO_FEATW:
191262306a36Sopenharmony_ci		if (put_user(par->features, (u32 __user *) arg))
191362306a36Sopenharmony_ci			return -EFAULT;
191462306a36Sopenharmony_ci		break;
191562306a36Sopenharmony_ci#endif /* DEBUG && CONFIG_FB_ATY_CT */
191662306a36Sopenharmony_ci	default:
191762306a36Sopenharmony_ci		return -EINVAL;
191862306a36Sopenharmony_ci	}
191962306a36Sopenharmony_ci	return 0;
192062306a36Sopenharmony_ci}
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cistatic int atyfb_sync(struct fb_info *info)
192362306a36Sopenharmony_ci{
192462306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	if (par->blitter_may_be_busy)
192762306a36Sopenharmony_ci		wait_for_idle(par);
192862306a36Sopenharmony_ci	return 0;
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci#ifdef __sparc__
193262306a36Sopenharmony_cistatic int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
193362306a36Sopenharmony_ci{
193462306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
193562306a36Sopenharmony_ci	unsigned int size, page, map_size = 0;
193662306a36Sopenharmony_ci	unsigned long map_offset = 0;
193762306a36Sopenharmony_ci	unsigned long off;
193862306a36Sopenharmony_ci	int i;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	if (!par->mmap_map)
194162306a36Sopenharmony_ci		return -ENXIO;
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
194462306a36Sopenharmony_ci		return -EINVAL;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	off = vma->vm_pgoff << PAGE_SHIFT;
194762306a36Sopenharmony_ci	size = vma->vm_end - vma->vm_start;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	/* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	if (((vma->vm_pgoff == 0) && (size == info->fix.smem_len)) ||
195262306a36Sopenharmony_ci	    ((off == info->fix.smem_len) && (size == PAGE_SIZE)))
195362306a36Sopenharmony_ci		off += 0x8000000000000000UL;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	vma->vm_pgoff = off >> PAGE_SHIFT;	/* propagate off changes */
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	/* Each page, see which map applies */
195862306a36Sopenharmony_ci	for (page = 0; page < size;) {
195962306a36Sopenharmony_ci		map_size = 0;
196062306a36Sopenharmony_ci		for (i = 0; par->mmap_map[i].size; i++) {
196162306a36Sopenharmony_ci			unsigned long start = par->mmap_map[i].voff;
196262306a36Sopenharmony_ci			unsigned long end = start + par->mmap_map[i].size;
196362306a36Sopenharmony_ci			unsigned long offset = off + page;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci			if (start > offset)
196662306a36Sopenharmony_ci				continue;
196762306a36Sopenharmony_ci			if (offset >= end)
196862306a36Sopenharmony_ci				continue;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci			map_size = par->mmap_map[i].size - (offset - start);
197162306a36Sopenharmony_ci			map_offset = par->mmap_map[i].poff + (offset - start);
197262306a36Sopenharmony_ci			break;
197362306a36Sopenharmony_ci		}
197462306a36Sopenharmony_ci		if (!map_size) {
197562306a36Sopenharmony_ci			page += PAGE_SIZE;
197662306a36Sopenharmony_ci			continue;
197762306a36Sopenharmony_ci		}
197862306a36Sopenharmony_ci		if (page + map_size > size)
197962306a36Sopenharmony_ci			map_size = size - page;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci		pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask);
198262306a36Sopenharmony_ci		pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci		if (remap_pfn_range(vma, vma->vm_start + page,
198562306a36Sopenharmony_ci			map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot))
198662306a36Sopenharmony_ci			return -EAGAIN;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci		page += map_size;
198962306a36Sopenharmony_ci	}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	if (!map_size)
199262306a36Sopenharmony_ci		return -EINVAL;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	if (!par->mmaped)
199562306a36Sopenharmony_ci		par->mmaped = 1;
199662306a36Sopenharmony_ci	return 0;
199762306a36Sopenharmony_ci}
199862306a36Sopenharmony_ci#endif /* __sparc__ */
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci#if defined(CONFIG_PCI)
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
200562306a36Sopenharmony_ci/* Power management routines. Those are used for PowerBook sleep.
200662306a36Sopenharmony_ci */
200762306a36Sopenharmony_cistatic int aty_power_mgmt(int sleep, struct atyfb_par *par)
200862306a36Sopenharmony_ci{
200962306a36Sopenharmony_ci	u32 pm;
201062306a36Sopenharmony_ci	int timeout;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	pm = aty_ld_lcd(POWER_MANAGEMENT, par);
201362306a36Sopenharmony_ci	pm = (pm & ~PWR_MGT_MODE_MASK) | PWR_MGT_MODE_REG;
201462306a36Sopenharmony_ci	aty_st_lcd(POWER_MANAGEMENT, pm, par);
201562306a36Sopenharmony_ci	pm = aty_ld_lcd(POWER_MANAGEMENT, par);
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	timeout = 2000;
201862306a36Sopenharmony_ci	if (sleep) {
201962306a36Sopenharmony_ci		/* Sleep */
202062306a36Sopenharmony_ci		pm &= ~PWR_MGT_ON;
202162306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
202262306a36Sopenharmony_ci		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
202362306a36Sopenharmony_ci		udelay(10);
202462306a36Sopenharmony_ci		pm &= ~(PWR_BLON | AUTO_PWR_UP);
202562306a36Sopenharmony_ci		pm |= SUSPEND_NOW;
202662306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
202762306a36Sopenharmony_ci		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
202862306a36Sopenharmony_ci		udelay(10);
202962306a36Sopenharmony_ci		pm |= PWR_MGT_ON;
203062306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
203162306a36Sopenharmony_ci		do {
203262306a36Sopenharmony_ci			pm = aty_ld_lcd(POWER_MANAGEMENT, par);
203362306a36Sopenharmony_ci			mdelay(1);
203462306a36Sopenharmony_ci			if ((--timeout) == 0)
203562306a36Sopenharmony_ci				break;
203662306a36Sopenharmony_ci		} while ((pm & PWR_MGT_STATUS_MASK) != PWR_MGT_STATUS_SUSPEND);
203762306a36Sopenharmony_ci	} else {
203862306a36Sopenharmony_ci		/* Wakeup */
203962306a36Sopenharmony_ci		pm &= ~PWR_MGT_ON;
204062306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
204162306a36Sopenharmony_ci		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
204262306a36Sopenharmony_ci		udelay(10);
204362306a36Sopenharmony_ci		pm &= ~SUSPEND_NOW;
204462306a36Sopenharmony_ci		pm |= (PWR_BLON | AUTO_PWR_UP);
204562306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
204662306a36Sopenharmony_ci		pm = aty_ld_lcd(POWER_MANAGEMENT, par);
204762306a36Sopenharmony_ci		udelay(10);
204862306a36Sopenharmony_ci		pm |= PWR_MGT_ON;
204962306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
205062306a36Sopenharmony_ci		do {
205162306a36Sopenharmony_ci			pm = aty_ld_lcd(POWER_MANAGEMENT, par);
205262306a36Sopenharmony_ci			mdelay(1);
205362306a36Sopenharmony_ci			if ((--timeout) == 0)
205462306a36Sopenharmony_ci				break;
205562306a36Sopenharmony_ci		} while ((pm & PWR_MGT_STATUS_MASK) != 0);
205662306a36Sopenharmony_ci	}
205762306a36Sopenharmony_ci	mdelay(500);
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	return timeout ? 0 : -EIO;
206062306a36Sopenharmony_ci}
206162306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC */
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_cistatic int atyfb_pci_suspend_late(struct device *dev, pm_message_t state)
206462306a36Sopenharmony_ci{
206562306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
206662306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
206762306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	if (state.event == pdev->dev.power.power_state.event)
207062306a36Sopenharmony_ci		return 0;
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	console_lock();
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	fb_set_suspend(info, 1);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	/* Idle & reset engine */
207762306a36Sopenharmony_ci	wait_for_idle(par);
207862306a36Sopenharmony_ci	aty_reset_engine(par);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	/* Blank display and LCD */
208162306a36Sopenharmony_ci	atyfb_blank(FB_BLANK_POWERDOWN, info);
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	par->asleep = 1;
208462306a36Sopenharmony_ci	par->lock_blank = 1;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	/*
208762306a36Sopenharmony_ci	 * Because we may change PCI D state ourselves, we need to
208862306a36Sopenharmony_ci	 * first save the config space content so the core can
208962306a36Sopenharmony_ci	 * restore it properly on resume.
209062306a36Sopenharmony_ci	 */
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
209362306a36Sopenharmony_ci	/* Set chip to "suspend" mode */
209462306a36Sopenharmony_ci	if (machine_is(powermac) && aty_power_mgmt(1, par)) {
209562306a36Sopenharmony_ci		par->asleep = 0;
209662306a36Sopenharmony_ci		par->lock_blank = 0;
209762306a36Sopenharmony_ci		atyfb_blank(FB_BLANK_UNBLANK, info);
209862306a36Sopenharmony_ci		fb_set_suspend(info, 0);
209962306a36Sopenharmony_ci		console_unlock();
210062306a36Sopenharmony_ci		return -EIO;
210162306a36Sopenharmony_ci	}
210262306a36Sopenharmony_ci#endif
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	console_unlock();
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	pdev->dev.power.power_state = state;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	return 0;
210962306a36Sopenharmony_ci}
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_cistatic int __maybe_unused atyfb_pci_suspend(struct device *dev)
211262306a36Sopenharmony_ci{
211362306a36Sopenharmony_ci	return atyfb_pci_suspend_late(dev, PMSG_SUSPEND);
211462306a36Sopenharmony_ci}
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_cistatic int __maybe_unused atyfb_pci_hibernate(struct device *dev)
211762306a36Sopenharmony_ci{
211862306a36Sopenharmony_ci	return atyfb_pci_suspend_late(dev, PMSG_HIBERNATE);
211962306a36Sopenharmony_ci}
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_cistatic int __maybe_unused atyfb_pci_freeze(struct device *dev)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	return atyfb_pci_suspend_late(dev, PMSG_FREEZE);
212462306a36Sopenharmony_ci}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_cistatic void aty_resume_chip(struct fb_info *info)
212762306a36Sopenharmony_ci{
212862306a36Sopenharmony_ci	struct atyfb_par *par = info->par;
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	aty_st_le32(MEM_CNTL, par->mem_cntl, par);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	if (par->pll_ops->resume_pll)
213362306a36Sopenharmony_ci		par->pll_ops->resume_pll(info, &par->pll);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	if (par->aux_start)
213662306a36Sopenharmony_ci		aty_st_le32(BUS_CNTL,
213762306a36Sopenharmony_ci			aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par);
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_cistatic int __maybe_unused atyfb_pci_resume(struct device *dev)
214162306a36Sopenharmony_ci{
214262306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev);
214362306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
214462306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	if (pdev->dev.power.power_state.event == PM_EVENT_ON)
214762306a36Sopenharmony_ci		return 0;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	console_lock();
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	/*
215262306a36Sopenharmony_ci	 * PCI state will have been restored by the core, so
215362306a36Sopenharmony_ci	 * we should be in D0 now with our config space fully
215462306a36Sopenharmony_ci	 * restored
215562306a36Sopenharmony_ci	 */
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
215862306a36Sopenharmony_ci	if (machine_is(powermac) &&
215962306a36Sopenharmony_ci	    pdev->dev.power.power_state.event == PM_EVENT_SUSPEND)
216062306a36Sopenharmony_ci		aty_power_mgmt(0, par);
216162306a36Sopenharmony_ci#endif
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	aty_resume_chip(info);
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	par->asleep = 0;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	/* Restore display */
216862306a36Sopenharmony_ci	atyfb_set_par(info);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	/* Refresh */
217162306a36Sopenharmony_ci	fb_set_suspend(info, 0);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	/* Unblank */
217462306a36Sopenharmony_ci	par->lock_blank = 0;
217562306a36Sopenharmony_ci	atyfb_blank(FB_BLANK_UNBLANK, info);
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	console_unlock();
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	pdev->dev.power.power_state = PMSG_ON;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	return 0;
218262306a36Sopenharmony_ci}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_cistatic const struct dev_pm_ops atyfb_pci_pm_ops = {
218562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
218662306a36Sopenharmony_ci	.suspend	= atyfb_pci_suspend,
218762306a36Sopenharmony_ci	.resume		= atyfb_pci_resume,
218862306a36Sopenharmony_ci	.freeze		= atyfb_pci_freeze,
218962306a36Sopenharmony_ci	.thaw		= atyfb_pci_resume,
219062306a36Sopenharmony_ci	.poweroff	= atyfb_pci_hibernate,
219162306a36Sopenharmony_ci	.restore	= atyfb_pci_resume,
219262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
219362306a36Sopenharmony_ci};
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci#endif /*  defined(CONFIG_PCI) */
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci/* Backlight */
219862306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_BACKLIGHT
219962306a36Sopenharmony_ci#define MAX_LEVEL 0xFF
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_cistatic int aty_bl_get_level_brightness(struct atyfb_par *par, int level)
220262306a36Sopenharmony_ci{
220362306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pdev);
220462306a36Sopenharmony_ci	int atylevel;
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	/* Get and convert the value */
220762306a36Sopenharmony_ci	/* No locking of bl_curve since we read a single value */
220862306a36Sopenharmony_ci	atylevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	if (atylevel < 0)
221162306a36Sopenharmony_ci		atylevel = 0;
221262306a36Sopenharmony_ci	else if (atylevel > MAX_LEVEL)
221362306a36Sopenharmony_ci		atylevel = MAX_LEVEL;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	return atylevel;
221662306a36Sopenharmony_ci}
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_cistatic int aty_bl_update_status(struct backlight_device *bd)
221962306a36Sopenharmony_ci{
222062306a36Sopenharmony_ci	struct atyfb_par *par = bl_get_data(bd);
222162306a36Sopenharmony_ci	unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par);
222262306a36Sopenharmony_ci	int level = backlight_get_brightness(bd);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	reg |= (BLMOD_EN | BIASMOD_EN);
222562306a36Sopenharmony_ci	if (level > 0) {
222662306a36Sopenharmony_ci		reg &= ~BIAS_MOD_LEVEL_MASK;
222762306a36Sopenharmony_ci		reg |= (aty_bl_get_level_brightness(par, level) << BIAS_MOD_LEVEL_SHIFT);
222862306a36Sopenharmony_ci	} else {
222962306a36Sopenharmony_ci		reg &= ~BIAS_MOD_LEVEL_MASK;
223062306a36Sopenharmony_ci		reg |= (aty_bl_get_level_brightness(par, 0) << BIAS_MOD_LEVEL_SHIFT);
223162306a36Sopenharmony_ci	}
223262306a36Sopenharmony_ci	aty_st_lcd(LCD_MISC_CNTL, reg, par);
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_ci	return 0;
223562306a36Sopenharmony_ci}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_cistatic const struct backlight_ops aty_bl_data = {
223862306a36Sopenharmony_ci	.update_status	= aty_bl_update_status,
223962306a36Sopenharmony_ci};
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_cistatic void aty_bl_init(struct atyfb_par *par)
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	struct backlight_properties props;
224462306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(par->pdev);
224562306a36Sopenharmony_ci	struct backlight_device *bd;
224662306a36Sopenharmony_ci	char name[12];
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
224962306a36Sopenharmony_ci	if (!pmac_has_backlight_type("ati"))
225062306a36Sopenharmony_ci		return;
225162306a36Sopenharmony_ci#endif
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	snprintf(name, sizeof(name), "atybl%d", info->node);
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
225662306a36Sopenharmony_ci	props.type = BACKLIGHT_RAW;
225762306a36Sopenharmony_ci	props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
225862306a36Sopenharmony_ci	bd = backlight_device_register(name, info->device, par, &aty_bl_data,
225962306a36Sopenharmony_ci				       &props);
226062306a36Sopenharmony_ci	if (IS_ERR(bd)) {
226162306a36Sopenharmony_ci		info->bl_dev = NULL;
226262306a36Sopenharmony_ci		printk(KERN_WARNING "aty: Backlight registration failed\n");
226362306a36Sopenharmony_ci		goto error;
226462306a36Sopenharmony_ci	}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	info->bl_dev = bd;
226762306a36Sopenharmony_ci	fb_bl_default_curve(info, 0,
226862306a36Sopenharmony_ci			    0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL,
226962306a36Sopenharmony_ci			    0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL);
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	bd->props.brightness = bd->props.max_brightness;
227262306a36Sopenharmony_ci	bd->props.power = FB_BLANK_UNBLANK;
227362306a36Sopenharmony_ci	backlight_update_status(bd);
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	printk("aty: Backlight initialized (%s)\n", name);
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	return;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_cierror:
228062306a36Sopenharmony_ci	return;
228162306a36Sopenharmony_ci}
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci#ifdef CONFIG_PCI
228462306a36Sopenharmony_cistatic void aty_bl_exit(struct backlight_device *bd)
228562306a36Sopenharmony_ci{
228662306a36Sopenharmony_ci	backlight_device_unregister(bd);
228762306a36Sopenharmony_ci	printk("aty: Backlight unloaded\n");
228862306a36Sopenharmony_ci}
228962306a36Sopenharmony_ci#endif /* CONFIG_PCI */
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_BACKLIGHT */
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_cistatic void aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
229462306a36Sopenharmony_ci{
229562306a36Sopenharmony_ci	static const int ragepro_tbl[] = {
229662306a36Sopenharmony_ci		44, 50, 55, 66, 75, 80, 100
229762306a36Sopenharmony_ci	};
229862306a36Sopenharmony_ci	static const int ragexl_tbl[] = {
229962306a36Sopenharmony_ci		50, 66, 75, 83, 90, 95, 100, 105,
230062306a36Sopenharmony_ci		110, 115, 120, 125, 133, 143, 166
230162306a36Sopenharmony_ci	};
230262306a36Sopenharmony_ci	const int *refresh_tbl;
230362306a36Sopenharmony_ci	int i, size;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	if (M64_HAS(XL_MEM)) {
230662306a36Sopenharmony_ci		refresh_tbl = ragexl_tbl;
230762306a36Sopenharmony_ci		size = ARRAY_SIZE(ragexl_tbl);
230862306a36Sopenharmony_ci	} else {
230962306a36Sopenharmony_ci		refresh_tbl = ragepro_tbl;
231062306a36Sopenharmony_ci		size = ARRAY_SIZE(ragepro_tbl);
231162306a36Sopenharmony_ci	}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
231462306a36Sopenharmony_ci		if (xclk < refresh_tbl[i])
231562306a36Sopenharmony_ci			break;
231662306a36Sopenharmony_ci	}
231762306a36Sopenharmony_ci	par->mem_refresh_rate = i;
231862306a36Sopenharmony_ci}
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci/*
232162306a36Sopenharmony_ci * Initialisation
232262306a36Sopenharmony_ci */
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_cistatic struct fb_info *fb_list = NULL;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci#if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
232762306a36Sopenharmony_cistatic int atyfb_get_timings_from_lcd(struct atyfb_par *par,
232862306a36Sopenharmony_ci				      struct fb_var_screeninfo *var)
232962306a36Sopenharmony_ci{
233062306a36Sopenharmony_ci	int ret = -EINVAL;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (par->lcd_table != 0 && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
233362306a36Sopenharmony_ci		*var = default_var;
233462306a36Sopenharmony_ci		var->xres = var->xres_virtual = par->lcd_hdisp;
233562306a36Sopenharmony_ci		var->right_margin = par->lcd_right_margin;
233662306a36Sopenharmony_ci		var->left_margin = par->lcd_hblank_len -
233762306a36Sopenharmony_ci			(par->lcd_right_margin + par->lcd_hsync_dly +
233862306a36Sopenharmony_ci			 par->lcd_hsync_len);
233962306a36Sopenharmony_ci		var->hsync_len = par->lcd_hsync_len + par->lcd_hsync_dly;
234062306a36Sopenharmony_ci		var->yres = var->yres_virtual = par->lcd_vdisp;
234162306a36Sopenharmony_ci		var->lower_margin = par->lcd_lower_margin;
234262306a36Sopenharmony_ci		var->upper_margin = par->lcd_vblank_len -
234362306a36Sopenharmony_ci			(par->lcd_lower_margin + par->lcd_vsync_len);
234462306a36Sopenharmony_ci		var->vsync_len = par->lcd_vsync_len;
234562306a36Sopenharmony_ci		var->pixclock = par->lcd_pixclock;
234662306a36Sopenharmony_ci		ret = 0;
234762306a36Sopenharmony_ci	}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_ci	return ret;
235062306a36Sopenharmony_ci}
235162306a36Sopenharmony_ci#endif /* defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) */
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_cistatic int aty_init(struct fb_info *info)
235462306a36Sopenharmony_ci{
235562306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
235662306a36Sopenharmony_ci	const char *ramname = NULL, *xtal;
235762306a36Sopenharmony_ci	int gtb_memsize, has_var = 0;
235862306a36Sopenharmony_ci	struct fb_var_screeninfo var;
235962306a36Sopenharmony_ci	int ret;
236062306a36Sopenharmony_ci#ifdef CONFIG_ATARI
236162306a36Sopenharmony_ci	u8 dac_type;
236262306a36Sopenharmony_ci#endif
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	init_waitqueue_head(&par->vblank.wait);
236562306a36Sopenharmony_ci	spin_lock_init(&par->int_lock);
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GX
236862306a36Sopenharmony_ci	if (!M64_HAS(INTEGRATED)) {
236962306a36Sopenharmony_ci		u32 stat0;
237062306a36Sopenharmony_ci		u8 dac_subtype, clk_type;
237162306a36Sopenharmony_ci		stat0 = aty_ld_le32(CNFG_STAT0, par);
237262306a36Sopenharmony_ci		par->bus_type = (stat0 >> 0) & 0x07;
237362306a36Sopenharmony_ci		par->ram_type = (stat0 >> 3) & 0x07;
237462306a36Sopenharmony_ci		ramname = aty_gx_ram[par->ram_type];
237562306a36Sopenharmony_ci		/* FIXME: clockchip/RAMDAC probing? */
237662306a36Sopenharmony_ci#ifdef CONFIG_ATARI
237762306a36Sopenharmony_ci		clk_type = CLK_ATI18818_1;
237862306a36Sopenharmony_ci		dac_type = (stat0 >> 9) & 0x07;
237962306a36Sopenharmony_ci		if (dac_type == 0x07)
238062306a36Sopenharmony_ci			dac_subtype = DAC_ATT20C408;
238162306a36Sopenharmony_ci		else
238262306a36Sopenharmony_ci			dac_subtype = (aty_ld_8(SCRATCH_REG1 + 1, par) & 0xF0) | dac_type;
238362306a36Sopenharmony_ci#else
238462306a36Sopenharmony_ci		dac_subtype = DAC_IBMRGB514;
238562306a36Sopenharmony_ci		clk_type = CLK_IBMRGB514;
238662306a36Sopenharmony_ci#endif
238762306a36Sopenharmony_ci		switch (dac_subtype) {
238862306a36Sopenharmony_ci		case DAC_IBMRGB514:
238962306a36Sopenharmony_ci			par->dac_ops = &aty_dac_ibm514;
239062306a36Sopenharmony_ci			break;
239162306a36Sopenharmony_ci#ifdef CONFIG_ATARI
239262306a36Sopenharmony_ci		case DAC_ATI68860_B:
239362306a36Sopenharmony_ci		case DAC_ATI68860_C:
239462306a36Sopenharmony_ci			par->dac_ops = &aty_dac_ati68860b;
239562306a36Sopenharmony_ci			break;
239662306a36Sopenharmony_ci		case DAC_ATT20C408:
239762306a36Sopenharmony_ci		case DAC_ATT21C498:
239862306a36Sopenharmony_ci			par->dac_ops = &aty_dac_att21c498;
239962306a36Sopenharmony_ci			break;
240062306a36Sopenharmony_ci#endif
240162306a36Sopenharmony_ci		default:
240262306a36Sopenharmony_ci			PRINTKI("aty_init: DAC type not implemented yet!\n");
240362306a36Sopenharmony_ci			par->dac_ops = &aty_dac_unsupported;
240462306a36Sopenharmony_ci			break;
240562306a36Sopenharmony_ci		}
240662306a36Sopenharmony_ci		switch (clk_type) {
240762306a36Sopenharmony_ci#ifdef CONFIG_ATARI
240862306a36Sopenharmony_ci		case CLK_ATI18818_1:
240962306a36Sopenharmony_ci			par->pll_ops = &aty_pll_ati18818_1;
241062306a36Sopenharmony_ci			break;
241162306a36Sopenharmony_ci#else
241262306a36Sopenharmony_ci		case CLK_IBMRGB514:
241362306a36Sopenharmony_ci			par->pll_ops = &aty_pll_ibm514;
241462306a36Sopenharmony_ci			break;
241562306a36Sopenharmony_ci#endif
241662306a36Sopenharmony_ci		default:
241762306a36Sopenharmony_ci			PRINTKI("aty_init: CLK type not implemented yet!");
241862306a36Sopenharmony_ci			par->pll_ops = &aty_pll_unsupported;
241962306a36Sopenharmony_ci			break;
242062306a36Sopenharmony_ci		}
242162306a36Sopenharmony_ci	}
242262306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GX */
242362306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
242462306a36Sopenharmony_ci	if (M64_HAS(INTEGRATED)) {
242562306a36Sopenharmony_ci		par->dac_ops = &aty_dac_ct;
242662306a36Sopenharmony_ci		par->pll_ops = &aty_pll_ct;
242762306a36Sopenharmony_ci		par->bus_type = PCI;
242862306a36Sopenharmony_ci		par->ram_type = (aty_ld_le32(CNFG_STAT0, par) & 0x07);
242962306a36Sopenharmony_ci		if (M64_HAS(XL_MEM))
243062306a36Sopenharmony_ci			ramname = aty_xl_ram[par->ram_type];
243162306a36Sopenharmony_ci		else
243262306a36Sopenharmony_ci			ramname = aty_ct_ram[par->ram_type];
243362306a36Sopenharmony_ci		/* for many chips, the mclk is 67 MHz for SDRAM, 63 MHz otherwise */
243462306a36Sopenharmony_ci		if (par->pll_limits.mclk == 67 && par->ram_type < SDRAM)
243562306a36Sopenharmony_ci			par->pll_limits.mclk = 63;
243662306a36Sopenharmony_ci		/* Mobility + 32bit memory interface need halved XCLK. */
243762306a36Sopenharmony_ci		if (M64_HAS(MOBIL_BUS) && par->ram_type == SDRAM32)
243862306a36Sopenharmony_ci			par->pll_limits.xclk = (par->pll_limits.xclk + 1) >> 1;
243962306a36Sopenharmony_ci	}
244062306a36Sopenharmony_ci#endif
244162306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC
244262306a36Sopenharmony_ci	/*
244362306a36Sopenharmony_ci	 * The Apple iBook1 uses non-standard memory frequencies.
244462306a36Sopenharmony_ci	 * We detect it and set the frequency manually.
244562306a36Sopenharmony_ci	 */
244662306a36Sopenharmony_ci	if (of_machine_is_compatible("PowerBook2,1")) {
244762306a36Sopenharmony_ci		par->pll_limits.mclk = 70;
244862306a36Sopenharmony_ci		par->pll_limits.xclk = 53;
244962306a36Sopenharmony_ci	}
245062306a36Sopenharmony_ci#endif
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	/* Allow command line to override clocks. */
245362306a36Sopenharmony_ci	if (pll)
245462306a36Sopenharmony_ci		par->pll_limits.pll_max = pll;
245562306a36Sopenharmony_ci	if (mclk)
245662306a36Sopenharmony_ci		par->pll_limits.mclk = mclk;
245762306a36Sopenharmony_ci	if (xclk)
245862306a36Sopenharmony_ci		par->pll_limits.xclk = xclk;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	aty_calc_mem_refresh(par, par->pll_limits.xclk);
246162306a36Sopenharmony_ci	par->pll_per = 1000000/par->pll_limits.pll_max;
246262306a36Sopenharmony_ci	par->mclk_per = 1000000/par->pll_limits.mclk;
246362306a36Sopenharmony_ci	par->xclk_per = 1000000/par->pll_limits.xclk;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	par->ref_clk_per = 1000000000000ULL / 14318180;
246662306a36Sopenharmony_ci	xtal = "14.31818";
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
246962306a36Sopenharmony_ci	if (M64_HAS(GTB_DSP)) {
247062306a36Sopenharmony_ci		u8 pll_ref_div = aty_ld_pll_ct(PLL_REF_DIV, par);
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci		if (pll_ref_div) {
247362306a36Sopenharmony_ci			int diff1, diff2;
247462306a36Sopenharmony_ci			diff1 = 510 * 14 / pll_ref_div - par->pll_limits.pll_max;
247562306a36Sopenharmony_ci			diff2 = 510 * 29 / pll_ref_div - par->pll_limits.pll_max;
247662306a36Sopenharmony_ci			if (diff1 < 0)
247762306a36Sopenharmony_ci				diff1 = -diff1;
247862306a36Sopenharmony_ci			if (diff2 < 0)
247962306a36Sopenharmony_ci				diff2 = -diff2;
248062306a36Sopenharmony_ci			if (diff2 < diff1) {
248162306a36Sopenharmony_ci				par->ref_clk_per = 1000000000000ULL / 29498928;
248262306a36Sopenharmony_ci				xtal = "29.498928";
248362306a36Sopenharmony_ci			}
248462306a36Sopenharmony_ci		}
248562306a36Sopenharmony_ci	}
248662306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_CT */
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_ci	/* save previous video mode */
248962306a36Sopenharmony_ci	aty_get_crtc(par, &par->saved_crtc);
249062306a36Sopenharmony_ci	if (par->pll_ops->get_pll)
249162306a36Sopenharmony_ci		par->pll_ops->get_pll(info, &par->saved_pll);
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	par->mem_cntl = aty_ld_le32(MEM_CNTL, par);
249462306a36Sopenharmony_ci	gtb_memsize = M64_HAS(GTB_DSP);
249562306a36Sopenharmony_ci	if (gtb_memsize)
249662306a36Sopenharmony_ci		/* 0xF used instead of MEM_SIZE_ALIAS */
249762306a36Sopenharmony_ci		switch (par->mem_cntl & 0xF) {
249862306a36Sopenharmony_ci		case MEM_SIZE_512K:
249962306a36Sopenharmony_ci			info->fix.smem_len = 0x80000;
250062306a36Sopenharmony_ci			break;
250162306a36Sopenharmony_ci		case MEM_SIZE_1M:
250262306a36Sopenharmony_ci			info->fix.smem_len = 0x100000;
250362306a36Sopenharmony_ci			break;
250462306a36Sopenharmony_ci		case MEM_SIZE_2M_GTB:
250562306a36Sopenharmony_ci			info->fix.smem_len = 0x200000;
250662306a36Sopenharmony_ci			break;
250762306a36Sopenharmony_ci		case MEM_SIZE_4M_GTB:
250862306a36Sopenharmony_ci			info->fix.smem_len = 0x400000;
250962306a36Sopenharmony_ci			break;
251062306a36Sopenharmony_ci		case MEM_SIZE_6M_GTB:
251162306a36Sopenharmony_ci			info->fix.smem_len = 0x600000;
251262306a36Sopenharmony_ci			break;
251362306a36Sopenharmony_ci		case MEM_SIZE_8M_GTB:
251462306a36Sopenharmony_ci			info->fix.smem_len = 0x800000;
251562306a36Sopenharmony_ci			break;
251662306a36Sopenharmony_ci		default:
251762306a36Sopenharmony_ci			info->fix.smem_len = 0x80000;
251862306a36Sopenharmony_ci	} else
251962306a36Sopenharmony_ci		switch (par->mem_cntl & MEM_SIZE_ALIAS) {
252062306a36Sopenharmony_ci		case MEM_SIZE_512K:
252162306a36Sopenharmony_ci			info->fix.smem_len = 0x80000;
252262306a36Sopenharmony_ci			break;
252362306a36Sopenharmony_ci		case MEM_SIZE_1M:
252462306a36Sopenharmony_ci			info->fix.smem_len = 0x100000;
252562306a36Sopenharmony_ci			break;
252662306a36Sopenharmony_ci		case MEM_SIZE_2M:
252762306a36Sopenharmony_ci			info->fix.smem_len = 0x200000;
252862306a36Sopenharmony_ci			break;
252962306a36Sopenharmony_ci		case MEM_SIZE_4M:
253062306a36Sopenharmony_ci			info->fix.smem_len = 0x400000;
253162306a36Sopenharmony_ci			break;
253262306a36Sopenharmony_ci		case MEM_SIZE_6M:
253362306a36Sopenharmony_ci			info->fix.smem_len = 0x600000;
253462306a36Sopenharmony_ci			break;
253562306a36Sopenharmony_ci		case MEM_SIZE_8M:
253662306a36Sopenharmony_ci			info->fix.smem_len = 0x800000;
253762306a36Sopenharmony_ci			break;
253862306a36Sopenharmony_ci		default:
253962306a36Sopenharmony_ci			info->fix.smem_len = 0x80000;
254062306a36Sopenharmony_ci		}
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	if (M64_HAS(MAGIC_VRAM_SIZE)) {
254362306a36Sopenharmony_ci		if (aty_ld_le32(CNFG_STAT1, par) & 0x40000000)
254462306a36Sopenharmony_ci			info->fix.smem_len += 0x400000;
254562306a36Sopenharmony_ci	}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	if (vram) {
254862306a36Sopenharmony_ci		info->fix.smem_len = vram * 1024;
254962306a36Sopenharmony_ci		par->mem_cntl &= ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS);
255062306a36Sopenharmony_ci		if (info->fix.smem_len <= 0x80000)
255162306a36Sopenharmony_ci			par->mem_cntl |= MEM_SIZE_512K;
255262306a36Sopenharmony_ci		else if (info->fix.smem_len <= 0x100000)
255362306a36Sopenharmony_ci			par->mem_cntl |= MEM_SIZE_1M;
255462306a36Sopenharmony_ci		else if (info->fix.smem_len <= 0x200000)
255562306a36Sopenharmony_ci			par->mem_cntl |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M;
255662306a36Sopenharmony_ci		else if (info->fix.smem_len <= 0x400000)
255762306a36Sopenharmony_ci			par->mem_cntl |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M;
255862306a36Sopenharmony_ci		else if (info->fix.smem_len <= 0x600000)
255962306a36Sopenharmony_ci			par->mem_cntl |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M;
256062306a36Sopenharmony_ci		else
256162306a36Sopenharmony_ci			par->mem_cntl |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M;
256262306a36Sopenharmony_ci		aty_st_le32(MEM_CNTL, par->mem_cntl, par);
256362306a36Sopenharmony_ci	}
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	/*
256662306a36Sopenharmony_ci	 * Reg Block 0 (CT-compatible block) is at mmio_start
256762306a36Sopenharmony_ci	 * Reg Block 1 (multimedia extensions) is at mmio_start - 0x400
256862306a36Sopenharmony_ci	 */
256962306a36Sopenharmony_ci	if (M64_HAS(GX)) {
257062306a36Sopenharmony_ci		info->fix.mmio_len = 0x400;
257162306a36Sopenharmony_ci		info->fix.accel = FB_ACCEL_ATI_MACH64GX;
257262306a36Sopenharmony_ci	} else if (M64_HAS(CT)) {
257362306a36Sopenharmony_ci		info->fix.mmio_len = 0x400;
257462306a36Sopenharmony_ci		info->fix.accel = FB_ACCEL_ATI_MACH64CT;
257562306a36Sopenharmony_ci	} else if (M64_HAS(VT)) {
257662306a36Sopenharmony_ci		info->fix.mmio_start -= 0x400;
257762306a36Sopenharmony_ci		info->fix.mmio_len = 0x800;
257862306a36Sopenharmony_ci		info->fix.accel = FB_ACCEL_ATI_MACH64VT;
257962306a36Sopenharmony_ci	} else {/* GT */
258062306a36Sopenharmony_ci		info->fix.mmio_start -= 0x400;
258162306a36Sopenharmony_ci		info->fix.mmio_len = 0x800;
258262306a36Sopenharmony_ci		info->fix.accel = FB_ACCEL_ATI_MACH64GT;
258362306a36Sopenharmony_ci	}
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	PRINTKI("%d%c %s, %s MHz XTAL, %d MHz PLL, %d Mhz MCLK, %d MHz XCLK\n",
258662306a36Sopenharmony_ci		info->fix.smem_len == 0x80000 ? 512 : (info->fix.smem_len>>20),
258762306a36Sopenharmony_ci		info->fix.smem_len == 0x80000 ? 'K' : 'M', ramname, xtal,
258862306a36Sopenharmony_ci		par->pll_limits.pll_max, par->pll_limits.mclk,
258962306a36Sopenharmony_ci		par->pll_limits.xclk);
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci#if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
259262306a36Sopenharmony_ci	if (M64_HAS(INTEGRATED)) {
259362306a36Sopenharmony_ci		int i;
259462306a36Sopenharmony_ci		printk("debug atyfb: BUS_CNTL DAC_CNTL MEM_CNTL "
259562306a36Sopenharmony_ci		       "EXT_MEM_CNTL CRTC_GEN_CNTL DSP_CONFIG "
259662306a36Sopenharmony_ci		       "DSP_ON_OFF CLOCK_CNTL\n"
259762306a36Sopenharmony_ci		       "debug atyfb: %08x %08x %08x "
259862306a36Sopenharmony_ci		       "%08x     %08x      %08x   "
259962306a36Sopenharmony_ci		       "%08x   %08x\n"
260062306a36Sopenharmony_ci		       "debug atyfb: PLL",
260162306a36Sopenharmony_ci		       aty_ld_le32(BUS_CNTL, par),
260262306a36Sopenharmony_ci		       aty_ld_le32(DAC_CNTL, par),
260362306a36Sopenharmony_ci		       aty_ld_le32(MEM_CNTL, par),
260462306a36Sopenharmony_ci		       aty_ld_le32(EXT_MEM_CNTL, par),
260562306a36Sopenharmony_ci		       aty_ld_le32(CRTC_GEN_CNTL, par),
260662306a36Sopenharmony_ci		       aty_ld_le32(DSP_CONFIG, par),
260762306a36Sopenharmony_ci		       aty_ld_le32(DSP_ON_OFF, par),
260862306a36Sopenharmony_ci		       aty_ld_le32(CLOCK_CNTL, par));
260962306a36Sopenharmony_ci		for (i = 0; i < 40; i++)
261062306a36Sopenharmony_ci			pr_cont(" %02x", aty_ld_pll_ct(i, par));
261162306a36Sopenharmony_ci		pr_cont("\n");
261262306a36Sopenharmony_ci	}
261362306a36Sopenharmony_ci#endif
261462306a36Sopenharmony_ci	if (par->pll_ops->init_pll)
261562306a36Sopenharmony_ci		par->pll_ops->init_pll(info, &par->pll);
261662306a36Sopenharmony_ci	if (par->pll_ops->resume_pll)
261762306a36Sopenharmony_ci		par->pll_ops->resume_pll(info, &par->pll);
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	aty_fudge_framebuffer_len(info);
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci	/*
262262306a36Sopenharmony_ci	 * Disable register access through the linear aperture
262362306a36Sopenharmony_ci	 * if the auxiliary aperture is used so we can access
262462306a36Sopenharmony_ci	 * the full 8 MB of video RAM on 8 MB boards.
262562306a36Sopenharmony_ci	 */
262662306a36Sopenharmony_ci	if (par->aux_start)
262762306a36Sopenharmony_ci		aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) |
262862306a36Sopenharmony_ci			    BUS_APER_REG_DIS, par);
262962306a36Sopenharmony_ci
263062306a36Sopenharmony_ci	if (!nomtrr)
263162306a36Sopenharmony_ci		/*
263262306a36Sopenharmony_ci		 * Only the ioremap_wc()'d area will get WC here
263362306a36Sopenharmony_ci		 * since ioremap_uc() was used on the entire PCI BAR.
263462306a36Sopenharmony_ci		 */
263562306a36Sopenharmony_ci		par->wc_cookie = arch_phys_wc_add(par->res_start,
263662306a36Sopenharmony_ci						  par->res_size);
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	info->fbops = &atyfb_ops;
263962306a36Sopenharmony_ci	info->pseudo_palette = par->pseudo_palette;
264062306a36Sopenharmony_ci	info->flags = FBINFO_HWACCEL_IMAGEBLIT |
264162306a36Sopenharmony_ci		      FBINFO_HWACCEL_FILLRECT  |
264262306a36Sopenharmony_ci		      FBINFO_HWACCEL_COPYAREA  |
264362306a36Sopenharmony_ci		      FBINFO_HWACCEL_YPAN      |
264462306a36Sopenharmony_ci		      FBINFO_READS_FAST;
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci#ifdef CONFIG_PMAC_BACKLIGHT
264762306a36Sopenharmony_ci	if (M64_HAS(G3_PB_1_1) && of_machine_is_compatible("PowerBook1,1")) {
264862306a36Sopenharmony_ci		/*
264962306a36Sopenharmony_ci		 * these bits let the 101 powerbook
265062306a36Sopenharmony_ci		 * wake up from sleep -- paulus
265162306a36Sopenharmony_ci		 */
265262306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, aty_ld_lcd(POWER_MANAGEMENT, par) |
265362306a36Sopenharmony_ci			   USE_F32KHZ | TRISTATE_MEM_EN, par);
265462306a36Sopenharmony_ci	} else
265562306a36Sopenharmony_ci#endif
265662306a36Sopenharmony_ci
265762306a36Sopenharmony_ci	memset(&var, 0, sizeof(var));
265862306a36Sopenharmony_ci#ifdef CONFIG_PPC
265962306a36Sopenharmony_ci	if (machine_is(powermac)) {
266062306a36Sopenharmony_ci		/*
266162306a36Sopenharmony_ci		 * FIXME: The NVRAM stuff should be put in a Mac-specific file,
266262306a36Sopenharmony_ci		 *        as it applies to all Mac video cards
266362306a36Sopenharmony_ci		 */
266462306a36Sopenharmony_ci		if (mode) {
266562306a36Sopenharmony_ci			if (mac_find_mode(&var, info, mode, 8))
266662306a36Sopenharmony_ci				has_var = 1;
266762306a36Sopenharmony_ci		} else {
266862306a36Sopenharmony_ci			if (default_vmode == VMODE_CHOOSE) {
266962306a36Sopenharmony_ci				int sense;
267062306a36Sopenharmony_ci				if (M64_HAS(G3_PB_1024x768))
267162306a36Sopenharmony_ci					/* G3 PowerBook with 1024x768 LCD */
267262306a36Sopenharmony_ci					default_vmode = VMODE_1024_768_60;
267362306a36Sopenharmony_ci				else if (of_machine_is_compatible("iMac"))
267462306a36Sopenharmony_ci					default_vmode = VMODE_1024_768_75;
267562306a36Sopenharmony_ci				else if (of_machine_is_compatible("PowerBook2,1"))
267662306a36Sopenharmony_ci					/* iBook with 800x600 LCD */
267762306a36Sopenharmony_ci					default_vmode = VMODE_800_600_60;
267862306a36Sopenharmony_ci				else
267962306a36Sopenharmony_ci					default_vmode = VMODE_640_480_67;
268062306a36Sopenharmony_ci				sense = read_aty_sense(par);
268162306a36Sopenharmony_ci				PRINTKI("monitor sense=%x, mode %d\n",
268262306a36Sopenharmony_ci					sense,  mac_map_monitor_sense(sense));
268362306a36Sopenharmony_ci			}
268462306a36Sopenharmony_ci			if (default_vmode <= 0 || default_vmode > VMODE_MAX)
268562306a36Sopenharmony_ci				default_vmode = VMODE_640_480_60;
268662306a36Sopenharmony_ci			if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
268762306a36Sopenharmony_ci				default_cmode = CMODE_8;
268862306a36Sopenharmony_ci			if (!mac_vmode_to_var(default_vmode, default_cmode,
268962306a36Sopenharmony_ci					      &var))
269062306a36Sopenharmony_ci				has_var = 1;
269162306a36Sopenharmony_ci		}
269262306a36Sopenharmony_ci	}
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci#endif /* !CONFIG_PPC */
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci#if defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD)
269762306a36Sopenharmony_ci	if (!atyfb_get_timings_from_lcd(par, &var))
269862306a36Sopenharmony_ci		has_var = 1;
269962306a36Sopenharmony_ci#endif
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci	if (mode && fb_find_mode(&var, info, mode, NULL, 0, &defmode, 8))
270262306a36Sopenharmony_ci		has_var = 1;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	if (!has_var)
270562306a36Sopenharmony_ci		var = default_var;
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_ci	if (noaccel)
270862306a36Sopenharmony_ci		var.accel_flags &= ~FB_ACCELF_TEXT;
270962306a36Sopenharmony_ci	else
271062306a36Sopenharmony_ci		var.accel_flags |= FB_ACCELF_TEXT;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	if (comp_sync != -1) {
271362306a36Sopenharmony_ci		if (!comp_sync)
271462306a36Sopenharmony_ci			var.sync &= ~FB_SYNC_COMP_HIGH_ACT;
271562306a36Sopenharmony_ci		else
271662306a36Sopenharmony_ci			var.sync |= FB_SYNC_COMP_HIGH_ACT;
271762306a36Sopenharmony_ci	}
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci	if (var.yres == var.yres_virtual) {
272062306a36Sopenharmony_ci		u32 videoram = (info->fix.smem_len - (PAGE_SIZE << 2));
272162306a36Sopenharmony_ci		var.yres_virtual = ((videoram * 8) / var.bits_per_pixel) / var.xres_virtual;
272262306a36Sopenharmony_ci		if (var.yres_virtual < var.yres)
272362306a36Sopenharmony_ci			var.yres_virtual = var.yres;
272462306a36Sopenharmony_ci	}
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	ret = atyfb_check_var(&var, info);
272762306a36Sopenharmony_ci	if (ret) {
272862306a36Sopenharmony_ci		PRINTKE("can't set default video mode\n");
272962306a36Sopenharmony_ci		goto aty_init_exit;
273062306a36Sopenharmony_ci	}
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
273362306a36Sopenharmony_ci	if (!noaccel && M64_HAS(INTEGRATED))
273462306a36Sopenharmony_ci		aty_init_cursor(info, &atyfb_ops);
273562306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_CT */
273662306a36Sopenharmony_ci	info->var = var;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	ret = fb_alloc_cmap(&info->cmap, 256, 0);
273962306a36Sopenharmony_ci	if (ret < 0)
274062306a36Sopenharmony_ci		goto aty_init_exit;
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	ret = register_framebuffer(info);
274362306a36Sopenharmony_ci	if (ret < 0) {
274462306a36Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
274562306a36Sopenharmony_ci		goto aty_init_exit;
274662306a36Sopenharmony_ci	}
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	if (M64_HAS(MOBIL_BUS) && backlight) {
274962306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_BACKLIGHT
275062306a36Sopenharmony_ci		aty_bl_init(par);
275162306a36Sopenharmony_ci#endif
275262306a36Sopenharmony_ci	}
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci	fb_list = info;
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	PRINTKI("fb%d: %s frame buffer device on %s\n",
275762306a36Sopenharmony_ci		info->node, info->fix.id, par->bus_type == ISA ? "ISA" : "PCI");
275862306a36Sopenharmony_ci	return 0;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ciaty_init_exit:
276162306a36Sopenharmony_ci	/* restore video mode */
276262306a36Sopenharmony_ci	aty_set_crtc(par, &par->saved_crtc);
276362306a36Sopenharmony_ci	par->pll_ops->set_pll(info, &par->saved_pll);
276462306a36Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci	return ret;
276762306a36Sopenharmony_ci}
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci#if defined(CONFIG_ATARI) && !defined(MODULE)
277062306a36Sopenharmony_cistatic int store_video_par(char *video_str, unsigned char m64_num)
277162306a36Sopenharmony_ci{
277262306a36Sopenharmony_ci	char *p;
277362306a36Sopenharmony_ci	unsigned long vmembase, size, guiregbase;
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_ci	PRINTKI("store_video_par() '%s' \n", video_str);
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	if (!(p = strsep(&video_str, ";")) || !*p)
277862306a36Sopenharmony_ci		goto mach64_invalid;
277962306a36Sopenharmony_ci	vmembase = simple_strtoul(p, NULL, 0);
278062306a36Sopenharmony_ci	if (!(p = strsep(&video_str, ";")) || !*p)
278162306a36Sopenharmony_ci		goto mach64_invalid;
278262306a36Sopenharmony_ci	size = simple_strtoul(p, NULL, 0);
278362306a36Sopenharmony_ci	if (!(p = strsep(&video_str, ";")) || !*p)
278462306a36Sopenharmony_ci		goto mach64_invalid;
278562306a36Sopenharmony_ci	guiregbase = simple_strtoul(p, NULL, 0);
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	phys_vmembase[m64_num] = vmembase;
278862306a36Sopenharmony_ci	phys_size[m64_num] = size;
278962306a36Sopenharmony_ci	phys_guiregbase[m64_num] = guiregbase;
279062306a36Sopenharmony_ci	PRINTKI("stored them all: $%08lX $%08lX $%08lX \n", vmembase, size,
279162306a36Sopenharmony_ci		guiregbase);
279262306a36Sopenharmony_ci	return 0;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci mach64_invalid:
279562306a36Sopenharmony_ci	phys_vmembase[m64_num] = 0;
279662306a36Sopenharmony_ci	return -1;
279762306a36Sopenharmony_ci}
279862306a36Sopenharmony_ci#endif /* CONFIG_ATARI && !MODULE */
279962306a36Sopenharmony_ci
280062306a36Sopenharmony_ci/*
280162306a36Sopenharmony_ci * Blank the display.
280262306a36Sopenharmony_ci */
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_cistatic int atyfb_blank(int blank, struct fb_info *info)
280562306a36Sopenharmony_ci{
280662306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
280762306a36Sopenharmony_ci	u32 gen_cntl;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	if (par->lock_blank || par->asleep)
281062306a36Sopenharmony_ci		return 0;
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
281362306a36Sopenharmony_ci	if (par->lcd_table && blank > FB_BLANK_NORMAL &&
281462306a36Sopenharmony_ci	    (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
281562306a36Sopenharmony_ci		u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
281662306a36Sopenharmony_ci		pm &= ~PWR_BLON;
281762306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
281862306a36Sopenharmony_ci	}
281962306a36Sopenharmony_ci#endif
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
282262306a36Sopenharmony_ci	gen_cntl &= ~0x400004c;
282362306a36Sopenharmony_ci	switch (blank) {
282462306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
282562306a36Sopenharmony_ci		break;
282662306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
282762306a36Sopenharmony_ci		gen_cntl |= 0x4000040;
282862306a36Sopenharmony_ci		break;
282962306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
283062306a36Sopenharmony_ci		gen_cntl |= 0x4000048;
283162306a36Sopenharmony_ci		break;
283262306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
283362306a36Sopenharmony_ci		gen_cntl |= 0x4000044;
283462306a36Sopenharmony_ci		break;
283562306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
283662306a36Sopenharmony_ci		gen_cntl |= 0x400004c;
283762306a36Sopenharmony_ci		break;
283862306a36Sopenharmony_ci	}
283962306a36Sopenharmony_ci	aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par);
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
284262306a36Sopenharmony_ci	if (par->lcd_table && blank <= FB_BLANK_NORMAL &&
284362306a36Sopenharmony_ci	    (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) {
284462306a36Sopenharmony_ci		u32 pm = aty_ld_lcd(POWER_MANAGEMENT, par);
284562306a36Sopenharmony_ci		pm |= PWR_BLON;
284662306a36Sopenharmony_ci		aty_st_lcd(POWER_MANAGEMENT, pm, par);
284762306a36Sopenharmony_ci	}
284862306a36Sopenharmony_ci#endif
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci	return 0;
285162306a36Sopenharmony_ci}
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_cistatic void aty_st_pal(u_int regno, u_int red, u_int green, u_int blue,
285462306a36Sopenharmony_ci		       const struct atyfb_par *par)
285562306a36Sopenharmony_ci{
285662306a36Sopenharmony_ci	aty_st_8(DAC_W_INDEX, regno, par);
285762306a36Sopenharmony_ci	aty_st_8(DAC_DATA, red, par);
285862306a36Sopenharmony_ci	aty_st_8(DAC_DATA, green, par);
285962306a36Sopenharmony_ci	aty_st_8(DAC_DATA, blue, par);
286062306a36Sopenharmony_ci}
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci/*
286362306a36Sopenharmony_ci * Set a single color register. The values supplied are already
286462306a36Sopenharmony_ci * rounded down to the hardware's capabilities (according to the
286562306a36Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno.
286662306a36Sopenharmony_ci * !! 4 & 8 =  PSEUDO, > 8 = DIRECTCOLOR
286762306a36Sopenharmony_ci */
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_cistatic int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
287062306a36Sopenharmony_ci			   u_int transp, struct fb_info *info)
287162306a36Sopenharmony_ci{
287262306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
287362306a36Sopenharmony_ci	int i, depth;
287462306a36Sopenharmony_ci	u32 *pal = info->pseudo_palette;
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	depth = info->var.bits_per_pixel;
287762306a36Sopenharmony_ci	if (depth == 16)
287862306a36Sopenharmony_ci		depth = (info->var.green.length == 5) ? 15 : 16;
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci	if (par->asleep)
288162306a36Sopenharmony_ci		return 0;
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	if (regno > 255 ||
288462306a36Sopenharmony_ci	    (depth == 16 && regno > 63) ||
288562306a36Sopenharmony_ci	    (depth == 15 && regno > 31))
288662306a36Sopenharmony_ci		return 1;
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci	red >>= 8;
288962306a36Sopenharmony_ci	green >>= 8;
289062306a36Sopenharmony_ci	blue >>= 8;
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	par->palette[regno].red = red;
289362306a36Sopenharmony_ci	par->palette[regno].green = green;
289462306a36Sopenharmony_ci	par->palette[regno].blue = blue;
289562306a36Sopenharmony_ci
289662306a36Sopenharmony_ci	if (regno < 16) {
289762306a36Sopenharmony_ci		switch (depth) {
289862306a36Sopenharmony_ci		case 15:
289962306a36Sopenharmony_ci			pal[regno] = (regno << 10) | (regno << 5) | regno;
290062306a36Sopenharmony_ci			break;
290162306a36Sopenharmony_ci		case 16:
290262306a36Sopenharmony_ci			pal[regno] = (regno << 11) | (regno << 5) | regno;
290362306a36Sopenharmony_ci			break;
290462306a36Sopenharmony_ci		case 24:
290562306a36Sopenharmony_ci			pal[regno] = (regno << 16) | (regno << 8) | regno;
290662306a36Sopenharmony_ci			break;
290762306a36Sopenharmony_ci		case 32:
290862306a36Sopenharmony_ci			i = (regno << 8) | regno;
290962306a36Sopenharmony_ci			pal[regno] = (i << 16) | i;
291062306a36Sopenharmony_ci			break;
291162306a36Sopenharmony_ci		}
291262306a36Sopenharmony_ci	}
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	i = aty_ld_8(DAC_CNTL, par) & 0xfc;
291562306a36Sopenharmony_ci	if (M64_HAS(EXTRA_BRIGHT))
291662306a36Sopenharmony_ci		i |= 0x2; /* DAC_CNTL | 0x2 turns off the extra brightness for gt */
291762306a36Sopenharmony_ci	aty_st_8(DAC_CNTL, i, par);
291862306a36Sopenharmony_ci	aty_st_8(DAC_MASK, 0xff, par);
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	if (M64_HAS(INTEGRATED)) {
292162306a36Sopenharmony_ci		if (depth == 16) {
292262306a36Sopenharmony_ci			if (regno < 32)
292362306a36Sopenharmony_ci				aty_st_pal(regno << 3, red,
292462306a36Sopenharmony_ci					   par->palette[regno << 1].green,
292562306a36Sopenharmony_ci					   blue, par);
292662306a36Sopenharmony_ci			red = par->palette[regno >> 1].red;
292762306a36Sopenharmony_ci			blue = par->palette[regno >> 1].blue;
292862306a36Sopenharmony_ci			regno <<= 2;
292962306a36Sopenharmony_ci		} else if (depth == 15) {
293062306a36Sopenharmony_ci			regno <<= 3;
293162306a36Sopenharmony_ci			for (i = 0; i < 8; i++)
293262306a36Sopenharmony_ci				aty_st_pal(regno + i, red, green, blue, par);
293362306a36Sopenharmony_ci		}
293462306a36Sopenharmony_ci	}
293562306a36Sopenharmony_ci	aty_st_pal(regno, red, green, blue, par);
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_ci	return 0;
293862306a36Sopenharmony_ci}
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci#ifdef CONFIG_PCI
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci#ifdef __sparc__
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_cistatic int atyfb_setup_sparc(struct pci_dev *pdev, struct fb_info *info,
294562306a36Sopenharmony_ci			     unsigned long addr)
294662306a36Sopenharmony_ci{
294762306a36Sopenharmony_ci	struct atyfb_par *par = info->par;
294862306a36Sopenharmony_ci	struct device_node *dp;
294962306a36Sopenharmony_ci	u32 mem, chip_id;
295062306a36Sopenharmony_ci	int i, j, ret;
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci	/*
295362306a36Sopenharmony_ci	 * Map memory-mapped registers.
295462306a36Sopenharmony_ci	 */
295562306a36Sopenharmony_ci	par->ati_regbase = (void *)addr + 0x7ffc00UL;
295662306a36Sopenharmony_ci	info->fix.mmio_start = addr + 0x7ffc00UL;
295762306a36Sopenharmony_ci
295862306a36Sopenharmony_ci	/*
295962306a36Sopenharmony_ci	 * Map in big-endian aperture.
296062306a36Sopenharmony_ci	 */
296162306a36Sopenharmony_ci	info->screen_base = (char *) (addr + 0x800000UL);
296262306a36Sopenharmony_ci	info->fix.smem_start = addr + 0x800000UL;
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	/*
296562306a36Sopenharmony_ci	 * Figure mmap addresses from PCI config space.
296662306a36Sopenharmony_ci	 * Split Framebuffer in big- and little-endian halfs.
296762306a36Sopenharmony_ci	 */
296862306a36Sopenharmony_ci	for (i = 0; i < 6 && pdev->resource[i].start; i++)
296962306a36Sopenharmony_ci		/* nothing */ ;
297062306a36Sopenharmony_ci	j = i + 4;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	par->mmap_map = kcalloc(j, sizeof(*par->mmap_map), GFP_ATOMIC);
297362306a36Sopenharmony_ci	if (!par->mmap_map) {
297462306a36Sopenharmony_ci		PRINTKE("atyfb_setup_sparc() can't alloc mmap_map\n");
297562306a36Sopenharmony_ci		return -ENOMEM;
297662306a36Sopenharmony_ci	}
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci	for (i = 0, j = 2; i < 6 && pdev->resource[i].start; i++) {
297962306a36Sopenharmony_ci		struct resource *rp = &pdev->resource[i];
298062306a36Sopenharmony_ci		int io, breg = PCI_BASE_ADDRESS_0 + (i << 2);
298162306a36Sopenharmony_ci		unsigned long base;
298262306a36Sopenharmony_ci		u32 size, pbase;
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci		base = rp->start;
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci		io = (rp->flags & IORESOURCE_IO);
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_ci		size = rp->end - base + 1;
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci		pci_read_config_dword(pdev, breg, &pbase);
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci		if (io)
299362306a36Sopenharmony_ci			size &= ~1;
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci		/*
299662306a36Sopenharmony_ci		 * Map the framebuffer a second time, this time without
299762306a36Sopenharmony_ci		 * the braindead _PAGE_IE setting. This is used by the
299862306a36Sopenharmony_ci		 * fixed Xserver, but we need to maintain the old mapping
299962306a36Sopenharmony_ci		 * to stay compatible with older ones...
300062306a36Sopenharmony_ci		 */
300162306a36Sopenharmony_ci		if (base == addr) {
300262306a36Sopenharmony_ci			par->mmap_map[j].voff = (pbase + 0x10000000) & PAGE_MASK;
300362306a36Sopenharmony_ci			par->mmap_map[j].poff = base & PAGE_MASK;
300462306a36Sopenharmony_ci			par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK;
300562306a36Sopenharmony_ci			par->mmap_map[j].prot_mask = _PAGE_CACHE;
300662306a36Sopenharmony_ci			par->mmap_map[j].prot_flag = _PAGE_E;
300762306a36Sopenharmony_ci			j++;
300862306a36Sopenharmony_ci		}
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci		/*
301162306a36Sopenharmony_ci		 * Here comes the old framebuffer mapping with _PAGE_IE
301262306a36Sopenharmony_ci		 * set for the big endian half of the framebuffer...
301362306a36Sopenharmony_ci		 */
301462306a36Sopenharmony_ci		if (base == addr) {
301562306a36Sopenharmony_ci			par->mmap_map[j].voff = (pbase + 0x800000) & PAGE_MASK;
301662306a36Sopenharmony_ci			par->mmap_map[j].poff = (base + 0x800000) & PAGE_MASK;
301762306a36Sopenharmony_ci			par->mmap_map[j].size = 0x800000;
301862306a36Sopenharmony_ci			par->mmap_map[j].prot_mask = _PAGE_CACHE;
301962306a36Sopenharmony_ci			par->mmap_map[j].prot_flag = _PAGE_E | _PAGE_IE;
302062306a36Sopenharmony_ci			size -= 0x800000;
302162306a36Sopenharmony_ci			j++;
302262306a36Sopenharmony_ci		}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci		par->mmap_map[j].voff = pbase & PAGE_MASK;
302562306a36Sopenharmony_ci		par->mmap_map[j].poff = base & PAGE_MASK;
302662306a36Sopenharmony_ci		par->mmap_map[j].size = (size + ~PAGE_MASK) & PAGE_MASK;
302762306a36Sopenharmony_ci		par->mmap_map[j].prot_mask = _PAGE_CACHE;
302862306a36Sopenharmony_ci		par->mmap_map[j].prot_flag = _PAGE_E;
302962306a36Sopenharmony_ci		j++;
303062306a36Sopenharmony_ci	}
303162306a36Sopenharmony_ci
303262306a36Sopenharmony_ci	ret = correct_chipset(par);
303362306a36Sopenharmony_ci	if (ret)
303462306a36Sopenharmony_ci		return ret;
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci	if (IS_XL(pdev->device)) {
303762306a36Sopenharmony_ci		/*
303862306a36Sopenharmony_ci		 * Fix PROMs idea of MEM_CNTL settings...
303962306a36Sopenharmony_ci		 */
304062306a36Sopenharmony_ci		mem = aty_ld_le32(MEM_CNTL, par);
304162306a36Sopenharmony_ci		chip_id = aty_ld_le32(CNFG_CHIP_ID, par);
304262306a36Sopenharmony_ci		if (((chip_id & CFG_CHIP_TYPE) == VT_CHIP_ID) && !((chip_id >> 24) & 1)) {
304362306a36Sopenharmony_ci			switch (mem & 0x0f) {
304462306a36Sopenharmony_ci			case 3:
304562306a36Sopenharmony_ci				mem = (mem & ~(0x0f)) | 2;
304662306a36Sopenharmony_ci				break;
304762306a36Sopenharmony_ci			case 7:
304862306a36Sopenharmony_ci				mem = (mem & ~(0x0f)) | 3;
304962306a36Sopenharmony_ci				break;
305062306a36Sopenharmony_ci			case 9:
305162306a36Sopenharmony_ci				mem = (mem & ~(0x0f)) | 4;
305262306a36Sopenharmony_ci				break;
305362306a36Sopenharmony_ci			case 11:
305462306a36Sopenharmony_ci				mem = (mem & ~(0x0f)) | 5;
305562306a36Sopenharmony_ci				break;
305662306a36Sopenharmony_ci			default:
305762306a36Sopenharmony_ci				break;
305862306a36Sopenharmony_ci			}
305962306a36Sopenharmony_ci			if ((aty_ld_le32(CNFG_STAT0, par) & 7) >= SDRAM)
306062306a36Sopenharmony_ci				mem &= ~(0x00700000);
306162306a36Sopenharmony_ci		}
306262306a36Sopenharmony_ci		mem &= ~(0xcf80e000);	/* Turn off all undocumented bits. */
306362306a36Sopenharmony_ci		aty_st_le32(MEM_CNTL, mem, par);
306462306a36Sopenharmony_ci	}
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	dp = pci_device_to_OF_node(pdev);
306762306a36Sopenharmony_ci	if (dp == of_console_device) {
306862306a36Sopenharmony_ci		struct fb_var_screeninfo *var = &default_var;
306962306a36Sopenharmony_ci		unsigned int N, P, Q, M, T, R;
307062306a36Sopenharmony_ci		struct crtc crtc;
307162306a36Sopenharmony_ci		u8 pll_regs[16];
307262306a36Sopenharmony_ci		u8 clock_cntl;
307362306a36Sopenharmony_ci
307462306a36Sopenharmony_ci		crtc.vxres = of_getintprop_default(dp, "width", 1024);
307562306a36Sopenharmony_ci		crtc.vyres = of_getintprop_default(dp, "height", 768);
307662306a36Sopenharmony_ci		var->bits_per_pixel = of_getintprop_default(dp, "depth", 8);
307762306a36Sopenharmony_ci		var->xoffset = var->yoffset = 0;
307862306a36Sopenharmony_ci		crtc.h_tot_disp = aty_ld_le32(CRTC_H_TOTAL_DISP, par);
307962306a36Sopenharmony_ci		crtc.h_sync_strt_wid = aty_ld_le32(CRTC_H_SYNC_STRT_WID, par);
308062306a36Sopenharmony_ci		crtc.v_tot_disp = aty_ld_le32(CRTC_V_TOTAL_DISP, par);
308162306a36Sopenharmony_ci		crtc.v_sync_strt_wid = aty_ld_le32(CRTC_V_SYNC_STRT_WID, par);
308262306a36Sopenharmony_ci		crtc.gen_cntl = aty_ld_le32(CRTC_GEN_CNTL, par);
308362306a36Sopenharmony_ci		aty_crtc_to_var(&crtc, var);
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci		/*
308662306a36Sopenharmony_ci		 * Read the PLL to figure actual Refresh Rate.
308762306a36Sopenharmony_ci		 */
308862306a36Sopenharmony_ci		clock_cntl = aty_ld_8(CLOCK_CNTL, par);
308962306a36Sopenharmony_ci		/* DPRINTK("CLOCK_CNTL %02x\n", clock_cntl); */
309062306a36Sopenharmony_ci		for (i = 0; i < 16; i++)
309162306a36Sopenharmony_ci			pll_regs[i] = aty_ld_pll_ct(i, par);
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci		/*
309462306a36Sopenharmony_ci		 * PLL Reference Divider M:
309562306a36Sopenharmony_ci		 */
309662306a36Sopenharmony_ci		M = pll_regs[PLL_REF_DIV];
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci		/*
309962306a36Sopenharmony_ci		 * PLL Feedback Divider N (Dependent on CLOCK_CNTL):
310062306a36Sopenharmony_ci		 */
310162306a36Sopenharmony_ci		N = pll_regs[VCLK0_FB_DIV + (clock_cntl & 3)];
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci		/*
310462306a36Sopenharmony_ci		 * PLL Post Divider P (Dependent on CLOCK_CNTL):
310562306a36Sopenharmony_ci		 */
310662306a36Sopenharmony_ci		P = aty_postdividers[((pll_regs[VCLK_POST_DIV] >> ((clock_cntl & 3) << 1)) & 3) |
310762306a36Sopenharmony_ci		                     ((pll_regs[PLL_EXT_CNTL] >> (2 + (clock_cntl & 3))) & 4)];
310862306a36Sopenharmony_ci
310962306a36Sopenharmony_ci		/*
311062306a36Sopenharmony_ci		 * PLL Divider Q:
311162306a36Sopenharmony_ci		 */
311262306a36Sopenharmony_ci		Q = N / P;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci		/*
311562306a36Sopenharmony_ci		 * Target Frequency:
311662306a36Sopenharmony_ci		 *
311762306a36Sopenharmony_ci		 *      T * M
311862306a36Sopenharmony_ci		 * Q = -------
311962306a36Sopenharmony_ci		 *      2 * R
312062306a36Sopenharmony_ci		 *
312162306a36Sopenharmony_ci		 * where R is XTALIN (= 14318 or 29498 kHz).
312262306a36Sopenharmony_ci		 */
312362306a36Sopenharmony_ci		if (IS_XL(pdev->device))
312462306a36Sopenharmony_ci			R = 29498;
312562306a36Sopenharmony_ci		else
312662306a36Sopenharmony_ci			R = 14318;
312762306a36Sopenharmony_ci
312862306a36Sopenharmony_ci		T = 2 * Q * R / M;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci		default_var.pixclock = 1000000000 / T;
313162306a36Sopenharmony_ci	}
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	return 0;
313462306a36Sopenharmony_ci}
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci#else /* __sparc__ */
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci#ifdef __i386__
313962306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
314062306a36Sopenharmony_cistatic void aty_init_lcd(struct atyfb_par *par, u32 bios_base)
314162306a36Sopenharmony_ci{
314262306a36Sopenharmony_ci	u32 driv_inf_tab, sig;
314362306a36Sopenharmony_ci	u16 lcd_ofs;
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci	/*
314662306a36Sopenharmony_ci	 * To support an LCD panel, we should know it's dimensions and
314762306a36Sopenharmony_ci	 *  it's desired pixel clock.
314862306a36Sopenharmony_ci	 * There are two ways to do it:
314962306a36Sopenharmony_ci	 *  - Check the startup video mode and calculate the panel
315062306a36Sopenharmony_ci	 *    size from it. This is unreliable.
315162306a36Sopenharmony_ci	 *  - Read it from the driver information table in the video BIOS.
315262306a36Sopenharmony_ci	 */
315362306a36Sopenharmony_ci	/* Address of driver information table is at offset 0x78. */
315462306a36Sopenharmony_ci	driv_inf_tab = bios_base + *((u16 *)(bios_base+0x78));
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	/* Check for the driver information table signature. */
315762306a36Sopenharmony_ci	sig = *(u32 *)driv_inf_tab;
315862306a36Sopenharmony_ci	if ((sig == 0x54504c24) || /* Rage LT pro */
315962306a36Sopenharmony_ci	    (sig == 0x544d5224) || /* Rage mobility */
316062306a36Sopenharmony_ci	    (sig == 0x54435824) || /* Rage XC */
316162306a36Sopenharmony_ci	    (sig == 0x544c5824)) { /* Rage XL */
316262306a36Sopenharmony_ci		PRINTKI("BIOS contains driver information table.\n");
316362306a36Sopenharmony_ci		lcd_ofs = *(u16 *)(driv_inf_tab + 10);
316462306a36Sopenharmony_ci		par->lcd_table = 0;
316562306a36Sopenharmony_ci		if (lcd_ofs != 0)
316662306a36Sopenharmony_ci			par->lcd_table = bios_base + lcd_ofs;
316762306a36Sopenharmony_ci	}
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	if (par->lcd_table != 0) {
317062306a36Sopenharmony_ci		char model[24];
317162306a36Sopenharmony_ci		char strbuf[16];
317262306a36Sopenharmony_ci		char refresh_rates_buf[100];
317362306a36Sopenharmony_ci		int id, tech, f, i, m, default_refresh_rate;
317462306a36Sopenharmony_ci		char *txtcolour;
317562306a36Sopenharmony_ci		char *txtmonitor;
317662306a36Sopenharmony_ci		char *txtdual;
317762306a36Sopenharmony_ci		char *txtformat;
317862306a36Sopenharmony_ci		u16 width, height, panel_type, refresh_rates;
317962306a36Sopenharmony_ci		u16 *lcdmodeptr;
318062306a36Sopenharmony_ci		u32 format;
318162306a36Sopenharmony_ci		u8 lcd_refresh_rates[16] = { 50, 56, 60, 67, 70, 72, 75, 76, 85,
318262306a36Sopenharmony_ci					     90, 100, 120, 140, 150, 160, 200 };
318362306a36Sopenharmony_ci		/*
318462306a36Sopenharmony_ci		 * The most important information is the panel size at
318562306a36Sopenharmony_ci		 * offset 25 and 27, but there's some other nice information
318662306a36Sopenharmony_ci		 * which we print to the screen.
318762306a36Sopenharmony_ci		 */
318862306a36Sopenharmony_ci		id = *(u8 *)par->lcd_table;
318962306a36Sopenharmony_ci		strscpy(model, (char *)par->lcd_table+1, sizeof(model));
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci		width = par->lcd_width = *(u16 *)(par->lcd_table+25);
319262306a36Sopenharmony_ci		height = par->lcd_height = *(u16 *)(par->lcd_table+27);
319362306a36Sopenharmony_ci		panel_type = *(u16 *)(par->lcd_table+29);
319462306a36Sopenharmony_ci		if (panel_type & 1)
319562306a36Sopenharmony_ci			txtcolour = "colour";
319662306a36Sopenharmony_ci		else
319762306a36Sopenharmony_ci			txtcolour = "monochrome";
319862306a36Sopenharmony_ci		if (panel_type & 2)
319962306a36Sopenharmony_ci			txtdual = "dual (split) ";
320062306a36Sopenharmony_ci		else
320162306a36Sopenharmony_ci			txtdual = "";
320262306a36Sopenharmony_ci		tech = (panel_type >> 2) & 63;
320362306a36Sopenharmony_ci		switch (tech) {
320462306a36Sopenharmony_ci		case 0:
320562306a36Sopenharmony_ci			txtmonitor = "passive matrix";
320662306a36Sopenharmony_ci			break;
320762306a36Sopenharmony_ci		case 1:
320862306a36Sopenharmony_ci			txtmonitor = "active matrix";
320962306a36Sopenharmony_ci			break;
321062306a36Sopenharmony_ci		case 2:
321162306a36Sopenharmony_ci			txtmonitor = "active addressed STN";
321262306a36Sopenharmony_ci			break;
321362306a36Sopenharmony_ci		case 3:
321462306a36Sopenharmony_ci			txtmonitor = "EL";
321562306a36Sopenharmony_ci			break;
321662306a36Sopenharmony_ci		case 4:
321762306a36Sopenharmony_ci			txtmonitor = "plasma";
321862306a36Sopenharmony_ci			break;
321962306a36Sopenharmony_ci		default:
322062306a36Sopenharmony_ci			txtmonitor = "unknown";
322162306a36Sopenharmony_ci		}
322262306a36Sopenharmony_ci		format = *(u32 *)(par->lcd_table+57);
322362306a36Sopenharmony_ci		if (tech == 0 || tech == 2) {
322462306a36Sopenharmony_ci			switch (format & 7) {
322562306a36Sopenharmony_ci			case 0:
322662306a36Sopenharmony_ci				txtformat = "12 bit interface";
322762306a36Sopenharmony_ci				break;
322862306a36Sopenharmony_ci			case 1:
322962306a36Sopenharmony_ci				txtformat = "16 bit interface";
323062306a36Sopenharmony_ci				break;
323162306a36Sopenharmony_ci			case 2:
323262306a36Sopenharmony_ci				txtformat = "24 bit interface";
323362306a36Sopenharmony_ci				break;
323462306a36Sopenharmony_ci			default:
323562306a36Sopenharmony_ci				txtformat = "unknown format";
323662306a36Sopenharmony_ci			}
323762306a36Sopenharmony_ci		} else {
323862306a36Sopenharmony_ci			switch (format & 7) {
323962306a36Sopenharmony_ci			case 0:
324062306a36Sopenharmony_ci				txtformat = "8 colours";
324162306a36Sopenharmony_ci				break;
324262306a36Sopenharmony_ci			case 1:
324362306a36Sopenharmony_ci				txtformat = "512 colours";
324462306a36Sopenharmony_ci				break;
324562306a36Sopenharmony_ci			case 2:
324662306a36Sopenharmony_ci				txtformat = "4096 colours";
324762306a36Sopenharmony_ci				break;
324862306a36Sopenharmony_ci			case 4:
324962306a36Sopenharmony_ci				txtformat = "262144 colours (LT mode)";
325062306a36Sopenharmony_ci				break;
325162306a36Sopenharmony_ci			case 5:
325262306a36Sopenharmony_ci				txtformat = "16777216 colours";
325362306a36Sopenharmony_ci				break;
325462306a36Sopenharmony_ci			case 6:
325562306a36Sopenharmony_ci				txtformat = "262144 colours (FDPI-2 mode)";
325662306a36Sopenharmony_ci				break;
325762306a36Sopenharmony_ci			default:
325862306a36Sopenharmony_ci				txtformat = "unknown format";
325962306a36Sopenharmony_ci			}
326062306a36Sopenharmony_ci		}
326162306a36Sopenharmony_ci		PRINTKI("%s%s %s monitor detected: %s\n",
326262306a36Sopenharmony_ci			txtdual, txtcolour, txtmonitor, model);
326362306a36Sopenharmony_ci		PRINTKI("       id=%d, %dx%d pixels, %s\n",
326462306a36Sopenharmony_ci			id, width, height, txtformat);
326562306a36Sopenharmony_ci		refresh_rates_buf[0] = 0;
326662306a36Sopenharmony_ci		refresh_rates = *(u16 *)(par->lcd_table+62);
326762306a36Sopenharmony_ci		m = 1;
326862306a36Sopenharmony_ci		f = 0;
326962306a36Sopenharmony_ci		for (i = 0; i < 16; i++) {
327062306a36Sopenharmony_ci			if (refresh_rates & m) {
327162306a36Sopenharmony_ci				if (f == 0) {
327262306a36Sopenharmony_ci					sprintf(strbuf, "%d",
327362306a36Sopenharmony_ci						lcd_refresh_rates[i]);
327462306a36Sopenharmony_ci					f++;
327562306a36Sopenharmony_ci				} else {
327662306a36Sopenharmony_ci					sprintf(strbuf, ",%d",
327762306a36Sopenharmony_ci						lcd_refresh_rates[i]);
327862306a36Sopenharmony_ci				}
327962306a36Sopenharmony_ci				strcat(refresh_rates_buf, strbuf);
328062306a36Sopenharmony_ci			}
328162306a36Sopenharmony_ci			m = m << 1;
328262306a36Sopenharmony_ci		}
328362306a36Sopenharmony_ci		default_refresh_rate = (*(u8 *)(par->lcd_table+61) & 0xf0) >> 4;
328462306a36Sopenharmony_ci		PRINTKI("       supports refresh rates [%s], default %d Hz\n",
328562306a36Sopenharmony_ci			refresh_rates_buf, lcd_refresh_rates[default_refresh_rate]);
328662306a36Sopenharmony_ci		par->lcd_refreshrate = lcd_refresh_rates[default_refresh_rate];
328762306a36Sopenharmony_ci		/*
328862306a36Sopenharmony_ci		 * We now need to determine the crtc parameters for the
328962306a36Sopenharmony_ci		 * LCD monitor. This is tricky, because they are not stored
329062306a36Sopenharmony_ci		 * individually in the BIOS. Instead, the BIOS contains a
329162306a36Sopenharmony_ci		 * table of display modes that work for this monitor.
329262306a36Sopenharmony_ci		 *
329362306a36Sopenharmony_ci		 * The idea is that we search for a mode of the same dimensions
329462306a36Sopenharmony_ci		 * as the dimensions of the LCD monitor. Say our LCD monitor
329562306a36Sopenharmony_ci		 * is 800x600 pixels, we search for a 800x600 monitor.
329662306a36Sopenharmony_ci		 * The CRTC parameters we find here are the ones that we need
329762306a36Sopenharmony_ci		 * to use to simulate other resolutions on the LCD screen.
329862306a36Sopenharmony_ci		 */
329962306a36Sopenharmony_ci		lcdmodeptr = (u16 *)(par->lcd_table + 64);
330062306a36Sopenharmony_ci		while (*lcdmodeptr != 0) {
330162306a36Sopenharmony_ci			u32 modeptr;
330262306a36Sopenharmony_ci			u16 mwidth, mheight, lcd_hsync_start, lcd_vsync_start;
330362306a36Sopenharmony_ci			modeptr = bios_base + *lcdmodeptr;
330462306a36Sopenharmony_ci
330562306a36Sopenharmony_ci			mwidth = *((u16 *)(modeptr+0));
330662306a36Sopenharmony_ci			mheight = *((u16 *)(modeptr+2));
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_ci			if (mwidth == width && mheight == height) {
330962306a36Sopenharmony_ci				par->lcd_pixclock = 100000000 / *((u16 *)(modeptr+9));
331062306a36Sopenharmony_ci				par->lcd_htotal = *((u16 *)(modeptr+17)) & 511;
331162306a36Sopenharmony_ci				par->lcd_hdisp = *((u16 *)(modeptr+19)) & 511;
331262306a36Sopenharmony_ci				lcd_hsync_start = *((u16 *)(modeptr+21)) & 511;
331362306a36Sopenharmony_ci				par->lcd_hsync_dly = (*((u16 *)(modeptr+21)) >> 9) & 7;
331462306a36Sopenharmony_ci				par->lcd_hsync_len = *((u8 *)(modeptr+23)) & 63;
331562306a36Sopenharmony_ci
331662306a36Sopenharmony_ci				par->lcd_vtotal = *((u16 *)(modeptr+24)) & 2047;
331762306a36Sopenharmony_ci				par->lcd_vdisp = *((u16 *)(modeptr+26)) & 2047;
331862306a36Sopenharmony_ci				lcd_vsync_start = *((u16 *)(modeptr+28)) & 2047;
331962306a36Sopenharmony_ci				par->lcd_vsync_len = (*((u16 *)(modeptr+28)) >> 11) & 31;
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_ci				par->lcd_htotal = (par->lcd_htotal + 1) * 8;
332262306a36Sopenharmony_ci				par->lcd_hdisp = (par->lcd_hdisp + 1) * 8;
332362306a36Sopenharmony_ci				lcd_hsync_start = (lcd_hsync_start + 1) * 8;
332462306a36Sopenharmony_ci				par->lcd_hsync_len = par->lcd_hsync_len * 8;
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci				par->lcd_vtotal++;
332762306a36Sopenharmony_ci				par->lcd_vdisp++;
332862306a36Sopenharmony_ci				lcd_vsync_start++;
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci				par->lcd_right_margin = lcd_hsync_start - par->lcd_hdisp;
333162306a36Sopenharmony_ci				par->lcd_lower_margin = lcd_vsync_start - par->lcd_vdisp;
333262306a36Sopenharmony_ci				par->lcd_hblank_len = par->lcd_htotal - par->lcd_hdisp;
333362306a36Sopenharmony_ci				par->lcd_vblank_len = par->lcd_vtotal - par->lcd_vdisp;
333462306a36Sopenharmony_ci				break;
333562306a36Sopenharmony_ci			}
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci			lcdmodeptr++;
333862306a36Sopenharmony_ci		}
333962306a36Sopenharmony_ci		if (*lcdmodeptr == 0) {
334062306a36Sopenharmony_ci			PRINTKE("LCD monitor CRTC parameters not found!!!\n");
334162306a36Sopenharmony_ci			/* To do: Switch to CRT if possible. */
334262306a36Sopenharmony_ci		} else {
334362306a36Sopenharmony_ci			PRINTKI("       LCD CRTC parameters: %d.%d  %d %d %d %d  %d %d %d %d\n",
334462306a36Sopenharmony_ci				1000000 / par->lcd_pixclock, 1000000 % par->lcd_pixclock,
334562306a36Sopenharmony_ci				par->lcd_hdisp,
334662306a36Sopenharmony_ci				par->lcd_hdisp + par->lcd_right_margin,
334762306a36Sopenharmony_ci				par->lcd_hdisp + par->lcd_right_margin
334862306a36Sopenharmony_ci					+ par->lcd_hsync_dly + par->lcd_hsync_len,
334962306a36Sopenharmony_ci				par->lcd_htotal,
335062306a36Sopenharmony_ci				par->lcd_vdisp,
335162306a36Sopenharmony_ci				par->lcd_vdisp + par->lcd_lower_margin,
335262306a36Sopenharmony_ci				par->lcd_vdisp + par->lcd_lower_margin + par->lcd_vsync_len,
335362306a36Sopenharmony_ci				par->lcd_vtotal);
335462306a36Sopenharmony_ci			PRINTKI("                          : %d %d %d %d %d %d %d %d %d\n",
335562306a36Sopenharmony_ci				par->lcd_pixclock,
335662306a36Sopenharmony_ci				par->lcd_hblank_len - (par->lcd_right_margin +
335762306a36Sopenharmony_ci					par->lcd_hsync_dly + par->lcd_hsync_len),
335862306a36Sopenharmony_ci				par->lcd_hdisp,
335962306a36Sopenharmony_ci				par->lcd_right_margin,
336062306a36Sopenharmony_ci				par->lcd_hsync_len,
336162306a36Sopenharmony_ci				par->lcd_vblank_len - (par->lcd_lower_margin + par->lcd_vsync_len),
336262306a36Sopenharmony_ci				par->lcd_vdisp,
336362306a36Sopenharmony_ci				par->lcd_lower_margin,
336462306a36Sopenharmony_ci				par->lcd_vsync_len);
336562306a36Sopenharmony_ci		}
336662306a36Sopenharmony_ci	}
336762306a36Sopenharmony_ci}
336862306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GENERIC_LCD */
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_cistatic int init_from_bios(struct atyfb_par *par)
337162306a36Sopenharmony_ci{
337262306a36Sopenharmony_ci	u32 bios_base, rom_addr;
337362306a36Sopenharmony_ci	int ret;
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci	rom_addr = 0xc0000 + ((aty_ld_le32(SCRATCH_REG1, par) & 0x7f) << 11);
337662306a36Sopenharmony_ci	bios_base = (unsigned long)ioremap(rom_addr, 0x10000);
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci	/* The BIOS starts with 0xaa55. */
337962306a36Sopenharmony_ci	if (*((u16 *)bios_base) == 0xaa55) {
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci		u8 *bios_ptr;
338262306a36Sopenharmony_ci		u16 rom_table_offset, freq_table_offset;
338362306a36Sopenharmony_ci		PLL_BLOCK_MACH64 pll_block;
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_ci		PRINTKI("Mach64 BIOS is located at %x, mapped at %x.\n", rom_addr, bios_base);
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_ci		/* check for frequncy table */
338862306a36Sopenharmony_ci		bios_ptr = (u8*)bios_base;
338962306a36Sopenharmony_ci		rom_table_offset = (u16)(bios_ptr[0x48] | (bios_ptr[0x49] << 8));
339062306a36Sopenharmony_ci		freq_table_offset = bios_ptr[rom_table_offset + 16] | (bios_ptr[rom_table_offset + 17] << 8);
339162306a36Sopenharmony_ci		memcpy(&pll_block, bios_ptr + freq_table_offset, sizeof(PLL_BLOCK_MACH64));
339262306a36Sopenharmony_ci
339362306a36Sopenharmony_ci		PRINTKI("BIOS frequency table:\n");
339462306a36Sopenharmony_ci		PRINTKI("PCLK_min_freq %d, PCLK_max_freq %d, ref_freq %d, ref_divider %d\n",
339562306a36Sopenharmony_ci			pll_block.PCLK_min_freq, pll_block.PCLK_max_freq,
339662306a36Sopenharmony_ci			pll_block.ref_freq, pll_block.ref_divider);
339762306a36Sopenharmony_ci		PRINTKI("MCLK_pwd %d, MCLK_max_freq %d, XCLK_max_freq %d, SCLK_freq %d\n",
339862306a36Sopenharmony_ci			pll_block.MCLK_pwd, pll_block.MCLK_max_freq,
339962306a36Sopenharmony_ci			pll_block.XCLK_max_freq, pll_block.SCLK_freq);
340062306a36Sopenharmony_ci
340162306a36Sopenharmony_ci		par->pll_limits.pll_min = pll_block.PCLK_min_freq/100;
340262306a36Sopenharmony_ci		par->pll_limits.pll_max = pll_block.PCLK_max_freq/100;
340362306a36Sopenharmony_ci		par->pll_limits.ref_clk = pll_block.ref_freq/100;
340462306a36Sopenharmony_ci		par->pll_limits.ref_div = pll_block.ref_divider;
340562306a36Sopenharmony_ci		par->pll_limits.sclk = pll_block.SCLK_freq/100;
340662306a36Sopenharmony_ci		par->pll_limits.mclk = pll_block.MCLK_max_freq/100;
340762306a36Sopenharmony_ci		par->pll_limits.mclk_pm = pll_block.MCLK_pwd/100;
340862306a36Sopenharmony_ci		par->pll_limits.xclk = pll_block.XCLK_max_freq/100;
340962306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GENERIC_LCD
341062306a36Sopenharmony_ci		aty_init_lcd(par, bios_base);
341162306a36Sopenharmony_ci#endif
341262306a36Sopenharmony_ci		ret = 0;
341362306a36Sopenharmony_ci	} else {
341462306a36Sopenharmony_ci		PRINTKE("no BIOS frequency table found, use parameters\n");
341562306a36Sopenharmony_ci		ret = -ENXIO;
341662306a36Sopenharmony_ci	}
341762306a36Sopenharmony_ci	iounmap((void __iomem *)bios_base);
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	return ret;
342062306a36Sopenharmony_ci}
342162306a36Sopenharmony_ci#endif /* __i386__ */
342262306a36Sopenharmony_ci
342362306a36Sopenharmony_cistatic int atyfb_setup_generic(struct pci_dev *pdev, struct fb_info *info,
342462306a36Sopenharmony_ci			       unsigned long addr)
342562306a36Sopenharmony_ci{
342662306a36Sopenharmony_ci	struct atyfb_par *par = info->par;
342762306a36Sopenharmony_ci	u16 tmp;
342862306a36Sopenharmony_ci	unsigned long raddr;
342962306a36Sopenharmony_ci	struct resource *rrp;
343062306a36Sopenharmony_ci	int ret = 0;
343162306a36Sopenharmony_ci
343262306a36Sopenharmony_ci	raddr = addr + 0x7ff000UL;
343362306a36Sopenharmony_ci	rrp = &pdev->resource[2];
343462306a36Sopenharmony_ci	if ((rrp->flags & IORESOURCE_MEM) &&
343562306a36Sopenharmony_ci	    request_mem_region(rrp->start, resource_size(rrp), "atyfb")) {
343662306a36Sopenharmony_ci		par->aux_start = rrp->start;
343762306a36Sopenharmony_ci		par->aux_size = resource_size(rrp);
343862306a36Sopenharmony_ci		raddr = rrp->start;
343962306a36Sopenharmony_ci		PRINTKI("using auxiliary register aperture\n");
344062306a36Sopenharmony_ci	}
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci	info->fix.mmio_start = raddr;
344362306a36Sopenharmony_ci#if defined(__i386__) || defined(__ia64__)
344462306a36Sopenharmony_ci	/*
344562306a36Sopenharmony_ci	 * By using strong UC we force the MTRR to never have an
344662306a36Sopenharmony_ci	 * effect on the MMIO region on both non-PAT and PAT systems.
344762306a36Sopenharmony_ci	 */
344862306a36Sopenharmony_ci	par->ati_regbase = ioremap_uc(info->fix.mmio_start, 0x1000);
344962306a36Sopenharmony_ci#else
345062306a36Sopenharmony_ci	par->ati_regbase = ioremap(info->fix.mmio_start, 0x1000);
345162306a36Sopenharmony_ci#endif
345262306a36Sopenharmony_ci	if (par->ati_regbase == NULL)
345362306a36Sopenharmony_ci		return -ENOMEM;
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci	info->fix.mmio_start += par->aux_start ? 0x400 : 0xc00;
345662306a36Sopenharmony_ci	par->ati_regbase += par->aux_start ? 0x400 : 0xc00;
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_ci	/*
345962306a36Sopenharmony_ci	 * Enable memory-space accesses using config-space
346062306a36Sopenharmony_ci	 * command register.
346162306a36Sopenharmony_ci	 */
346262306a36Sopenharmony_ci	pci_read_config_word(pdev, PCI_COMMAND, &tmp);
346362306a36Sopenharmony_ci	if (!(tmp & PCI_COMMAND_MEMORY)) {
346462306a36Sopenharmony_ci		tmp |= PCI_COMMAND_MEMORY;
346562306a36Sopenharmony_ci		pci_write_config_word(pdev, PCI_COMMAND, tmp);
346662306a36Sopenharmony_ci	}
346762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
346862306a36Sopenharmony_ci	/* Use the big-endian aperture */
346962306a36Sopenharmony_ci	addr += 0x800000;
347062306a36Sopenharmony_ci#endif
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci	/* Map in frame buffer */
347362306a36Sopenharmony_ci	info->fix.smem_start = addr;
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	/*
347662306a36Sopenharmony_ci	 * The framebuffer is not always 8 MiB, that's just the size of the
347762306a36Sopenharmony_ci	 * PCI BAR. We temporarily abuse smem_len here to store the size
347862306a36Sopenharmony_ci	 * of the BAR. aty_init() will later correct it to match the actual
347962306a36Sopenharmony_ci	 * framebuffer size.
348062306a36Sopenharmony_ci	 *
348162306a36Sopenharmony_ci	 * On devices that don't have the auxiliary register aperture, the
348262306a36Sopenharmony_ci	 * registers are housed at the top end of the framebuffer PCI BAR.
348362306a36Sopenharmony_ci	 * aty_fudge_framebuffer_len() is used to reduce smem_len to not
348462306a36Sopenharmony_ci	 * overlap with the registers.
348562306a36Sopenharmony_ci	 */
348662306a36Sopenharmony_ci	info->fix.smem_len = 0x800000;
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_ci	aty_fudge_framebuffer_len(info);
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci	info->screen_base = ioremap_wc(info->fix.smem_start,
349162306a36Sopenharmony_ci				       info->fix.smem_len);
349262306a36Sopenharmony_ci	if (info->screen_base == NULL) {
349362306a36Sopenharmony_ci		ret = -ENOMEM;
349462306a36Sopenharmony_ci		goto atyfb_setup_generic_fail;
349562306a36Sopenharmony_ci	}
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci	ret = correct_chipset(par);
349862306a36Sopenharmony_ci	if (ret)
349962306a36Sopenharmony_ci		goto atyfb_setup_generic_fail;
350062306a36Sopenharmony_ci#ifdef __i386__
350162306a36Sopenharmony_ci	ret = init_from_bios(par);
350262306a36Sopenharmony_ci	if (ret)
350362306a36Sopenharmony_ci		goto atyfb_setup_generic_fail;
350462306a36Sopenharmony_ci#endif
350562306a36Sopenharmony_ci	/* according to ATI, we should use clock 3 for acelerated mode */
350662306a36Sopenharmony_ci	par->clk_wr_offset = 3;
350762306a36Sopenharmony_ci
350862306a36Sopenharmony_ci	return 0;
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ciatyfb_setup_generic_fail:
351162306a36Sopenharmony_ci	iounmap(par->ati_regbase);
351262306a36Sopenharmony_ci	par->ati_regbase = NULL;
351362306a36Sopenharmony_ci	if (info->screen_base) {
351462306a36Sopenharmony_ci		iounmap(info->screen_base);
351562306a36Sopenharmony_ci		info->screen_base = NULL;
351662306a36Sopenharmony_ci	}
351762306a36Sopenharmony_ci	return ret;
351862306a36Sopenharmony_ci}
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_ci#endif /* !__sparc__ */
352162306a36Sopenharmony_ci
352262306a36Sopenharmony_cistatic int atyfb_pci_probe(struct pci_dev *pdev,
352362306a36Sopenharmony_ci			   const struct pci_device_id *ent)
352462306a36Sopenharmony_ci{
352562306a36Sopenharmony_ci	unsigned long addr, res_start, res_size;
352662306a36Sopenharmony_ci	struct fb_info *info;
352762306a36Sopenharmony_ci	struct resource *rp;
352862306a36Sopenharmony_ci	struct atyfb_par *par;
352962306a36Sopenharmony_ci	int rc;
353062306a36Sopenharmony_ci
353162306a36Sopenharmony_ci	rc = aperture_remove_conflicting_pci_devices(pdev, "atyfb");
353262306a36Sopenharmony_ci	if (rc)
353362306a36Sopenharmony_ci		return rc;
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	/* Enable device in PCI config */
353662306a36Sopenharmony_ci	if (pci_enable_device(pdev)) {
353762306a36Sopenharmony_ci		PRINTKE("Cannot enable PCI device\n");
353862306a36Sopenharmony_ci		return -ENXIO;
353962306a36Sopenharmony_ci	}
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_ci	/* Find which resource to use */
354262306a36Sopenharmony_ci	rp = &pdev->resource[0];
354362306a36Sopenharmony_ci	if (rp->flags & IORESOURCE_IO)
354462306a36Sopenharmony_ci		rp = &pdev->resource[1];
354562306a36Sopenharmony_ci	addr = rp->start;
354662306a36Sopenharmony_ci	if (!addr)
354762306a36Sopenharmony_ci		return -ENXIO;
354862306a36Sopenharmony_ci
354962306a36Sopenharmony_ci	/* Reserve space */
355062306a36Sopenharmony_ci	res_start = rp->start;
355162306a36Sopenharmony_ci	res_size = resource_size(rp);
355262306a36Sopenharmony_ci	if (!request_mem_region(res_start, res_size, "atyfb"))
355362306a36Sopenharmony_ci		return -EBUSY;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	/* Allocate framebuffer */
355662306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct atyfb_par), &pdev->dev);
355762306a36Sopenharmony_ci	if (!info)
355862306a36Sopenharmony_ci		return -ENOMEM;
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci	par = info->par;
356162306a36Sopenharmony_ci	par->bus_type = PCI;
356262306a36Sopenharmony_ci	info->fix = atyfb_fix;
356362306a36Sopenharmony_ci	info->device = &pdev->dev;
356462306a36Sopenharmony_ci	par->pci_id = pdev->device;
356562306a36Sopenharmony_ci	par->res_start = res_start;
356662306a36Sopenharmony_ci	par->res_size = res_size;
356762306a36Sopenharmony_ci	par->irq = pdev->irq;
356862306a36Sopenharmony_ci	par->pdev = pdev;
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_ci	/* Setup "info" structure */
357162306a36Sopenharmony_ci#ifdef __sparc__
357262306a36Sopenharmony_ci	rc = atyfb_setup_sparc(pdev, info, addr);
357362306a36Sopenharmony_ci#else
357462306a36Sopenharmony_ci	rc = atyfb_setup_generic(pdev, info, addr);
357562306a36Sopenharmony_ci#endif
357662306a36Sopenharmony_ci	if (rc)
357762306a36Sopenharmony_ci		goto err_release_mem;
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci	pci_set_drvdata(pdev, info);
358062306a36Sopenharmony_ci
358162306a36Sopenharmony_ci	/* Init chip & register framebuffer */
358262306a36Sopenharmony_ci	rc = aty_init(info);
358362306a36Sopenharmony_ci	if (rc)
358462306a36Sopenharmony_ci		goto err_release_io;
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci#ifdef __sparc__
358762306a36Sopenharmony_ci	/*
358862306a36Sopenharmony_ci	 * Add /dev/fb mmap values.
358962306a36Sopenharmony_ci	 */
359062306a36Sopenharmony_ci	par->mmap_map[0].voff = 0x8000000000000000UL;
359162306a36Sopenharmony_ci	par->mmap_map[0].poff = (unsigned long) info->screen_base & PAGE_MASK;
359262306a36Sopenharmony_ci	par->mmap_map[0].size = info->fix.smem_len;
359362306a36Sopenharmony_ci	par->mmap_map[0].prot_mask = _PAGE_CACHE;
359462306a36Sopenharmony_ci	par->mmap_map[0].prot_flag = _PAGE_E;
359562306a36Sopenharmony_ci	par->mmap_map[1].voff = par->mmap_map[0].voff + info->fix.smem_len;
359662306a36Sopenharmony_ci	par->mmap_map[1].poff = (long)par->ati_regbase & PAGE_MASK;
359762306a36Sopenharmony_ci	par->mmap_map[1].size = PAGE_SIZE;
359862306a36Sopenharmony_ci	par->mmap_map[1].prot_mask = _PAGE_CACHE;
359962306a36Sopenharmony_ci	par->mmap_map[1].prot_flag = _PAGE_E;
360062306a36Sopenharmony_ci#endif /* __sparc__ */
360162306a36Sopenharmony_ci
360262306a36Sopenharmony_ci	mutex_lock(&reboot_lock);
360362306a36Sopenharmony_ci	if (!reboot_info)
360462306a36Sopenharmony_ci		reboot_info = info;
360562306a36Sopenharmony_ci	mutex_unlock(&reboot_lock);
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci	return 0;
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_cierr_release_io:
361062306a36Sopenharmony_ci#ifdef __sparc__
361162306a36Sopenharmony_ci	kfree(par->mmap_map);
361262306a36Sopenharmony_ci#else
361362306a36Sopenharmony_ci	if (par->ati_regbase)
361462306a36Sopenharmony_ci		iounmap(par->ati_regbase);
361562306a36Sopenharmony_ci	if (info->screen_base)
361662306a36Sopenharmony_ci		iounmap(info->screen_base);
361762306a36Sopenharmony_ci#endif
361862306a36Sopenharmony_cierr_release_mem:
361962306a36Sopenharmony_ci	if (par->aux_start)
362062306a36Sopenharmony_ci		release_mem_region(par->aux_start, par->aux_size);
362162306a36Sopenharmony_ci
362262306a36Sopenharmony_ci	release_mem_region(par->res_start, par->res_size);
362362306a36Sopenharmony_ci	framebuffer_release(info);
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci	return rc;
362662306a36Sopenharmony_ci}
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci#endif /* CONFIG_PCI */
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci#ifdef CONFIG_ATARI
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_cistatic int __init atyfb_atari_probe(void)
363362306a36Sopenharmony_ci{
363462306a36Sopenharmony_ci	struct atyfb_par *par;
363562306a36Sopenharmony_ci	struct fb_info *info;
363662306a36Sopenharmony_ci	int m64_num;
363762306a36Sopenharmony_ci	u32 clock_r;
363862306a36Sopenharmony_ci	int num_found = 0;
363962306a36Sopenharmony_ci
364062306a36Sopenharmony_ci	for (m64_num = 0; m64_num < mach64_count; m64_num++) {
364162306a36Sopenharmony_ci		if (!phys_vmembase[m64_num] || !phys_size[m64_num] ||
364262306a36Sopenharmony_ci		    !phys_guiregbase[m64_num]) {
364362306a36Sopenharmony_ci			PRINTKI("phys_*[%d] parameters not set => "
364462306a36Sopenharmony_ci				"returning early. \n", m64_num);
364562306a36Sopenharmony_ci			continue;
364662306a36Sopenharmony_ci		}
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci		info = framebuffer_alloc(sizeof(struct atyfb_par), NULL);
364962306a36Sopenharmony_ci		if (!info)
365062306a36Sopenharmony_ci			return -ENOMEM;
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci		par = info->par;
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ci		info->fix = atyfb_fix;
365562306a36Sopenharmony_ci
365662306a36Sopenharmony_ci		par->irq = (unsigned int) -1; /* something invalid */
365762306a36Sopenharmony_ci
365862306a36Sopenharmony_ci		/*
365962306a36Sopenharmony_ci		 * Map the video memory (physical address given)
366062306a36Sopenharmony_ci		 * to somewhere in the kernel address space.
366162306a36Sopenharmony_ci		 */
366262306a36Sopenharmony_ci		info->screen_base = ioremap_wc(phys_vmembase[m64_num],
366362306a36Sopenharmony_ci					       phys_size[m64_num]);
366462306a36Sopenharmony_ci		info->fix.smem_start = (unsigned long)info->screen_base; /* Fake! */
366562306a36Sopenharmony_ci		par->ati_regbase = ioremap(phys_guiregbase[m64_num], 0x10000) +
366662306a36Sopenharmony_ci						0xFC00ul;
366762306a36Sopenharmony_ci		info->fix.mmio_start = (unsigned long)par->ati_regbase; /* Fake! */
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci		aty_st_le32(CLOCK_CNTL, 0x12345678, par);
367062306a36Sopenharmony_ci		clock_r = aty_ld_le32(CLOCK_CNTL, par);
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci		switch (clock_r & 0x003F) {
367362306a36Sopenharmony_ci		case 0x12:
367462306a36Sopenharmony_ci			par->clk_wr_offset = 3; /*  */
367562306a36Sopenharmony_ci			break;
367662306a36Sopenharmony_ci		case 0x34:
367762306a36Sopenharmony_ci			par->clk_wr_offset = 2; /* Medusa ST-IO ISA Adapter etc. */
367862306a36Sopenharmony_ci			break;
367962306a36Sopenharmony_ci		case 0x16:
368062306a36Sopenharmony_ci			par->clk_wr_offset = 1; /*  */
368162306a36Sopenharmony_ci			break;
368262306a36Sopenharmony_ci		case 0x38:
368362306a36Sopenharmony_ci			par->clk_wr_offset = 0; /* Panther 1 ISA Adapter (Gerald) */
368462306a36Sopenharmony_ci			break;
368562306a36Sopenharmony_ci		}
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci		/* Fake pci_id for correct_chipset() */
368862306a36Sopenharmony_ci		switch (aty_ld_le32(CNFG_CHIP_ID, par) & CFG_CHIP_TYPE) {
368962306a36Sopenharmony_ci		case 0x00d7:
369062306a36Sopenharmony_ci			par->pci_id = PCI_CHIP_MACH64GX;
369162306a36Sopenharmony_ci			break;
369262306a36Sopenharmony_ci		case 0x0057:
369362306a36Sopenharmony_ci			par->pci_id = PCI_CHIP_MACH64CX;
369462306a36Sopenharmony_ci			break;
369562306a36Sopenharmony_ci		default:
369662306a36Sopenharmony_ci			break;
369762306a36Sopenharmony_ci		}
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci		if (correct_chipset(par) || aty_init(info)) {
370062306a36Sopenharmony_ci			iounmap(info->screen_base);
370162306a36Sopenharmony_ci			iounmap(par->ati_regbase);
370262306a36Sopenharmony_ci			framebuffer_release(info);
370362306a36Sopenharmony_ci		} else {
370462306a36Sopenharmony_ci			num_found++;
370562306a36Sopenharmony_ci		}
370662306a36Sopenharmony_ci	}
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ci	return num_found ? 0 : -ENXIO;
370962306a36Sopenharmony_ci}
371062306a36Sopenharmony_ci
371162306a36Sopenharmony_ci#endif /* CONFIG_ATARI */
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_ci#ifdef CONFIG_PCI
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_cistatic void atyfb_remove(struct fb_info *info)
371662306a36Sopenharmony_ci{
371762306a36Sopenharmony_ci	struct atyfb_par *par = (struct atyfb_par *) info->par;
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci	/* restore video mode */
372062306a36Sopenharmony_ci	aty_set_crtc(par, &par->saved_crtc);
372162306a36Sopenharmony_ci	par->pll_ops->set_pll(info, &par->saved_pll);
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_BACKLIGHT
372462306a36Sopenharmony_ci	if (M64_HAS(MOBIL_BUS))
372562306a36Sopenharmony_ci		aty_bl_exit(info->bl_dev);
372662306a36Sopenharmony_ci#endif
372762306a36Sopenharmony_ci
372862306a36Sopenharmony_ci	unregister_framebuffer(info);
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci#ifndef __sparc__
373362306a36Sopenharmony_ci	if (par->ati_regbase)
373462306a36Sopenharmony_ci		iounmap(par->ati_regbase);
373562306a36Sopenharmony_ci	if (info->screen_base)
373662306a36Sopenharmony_ci		iounmap(info->screen_base);
373762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
373862306a36Sopenharmony_ci	if (info->sprite.addr)
373962306a36Sopenharmony_ci		iounmap(info->sprite.addr);
374062306a36Sopenharmony_ci#endif
374162306a36Sopenharmony_ci#endif
374262306a36Sopenharmony_ci#ifdef __sparc__
374362306a36Sopenharmony_ci	kfree(par->mmap_map);
374462306a36Sopenharmony_ci#endif
374562306a36Sopenharmony_ci	if (par->aux_start)
374662306a36Sopenharmony_ci		release_mem_region(par->aux_start, par->aux_size);
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci	if (par->res_start)
374962306a36Sopenharmony_ci		release_mem_region(par->res_start, par->res_size);
375062306a36Sopenharmony_ci
375162306a36Sopenharmony_ci	framebuffer_release(info);
375262306a36Sopenharmony_ci}
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci
375562306a36Sopenharmony_cistatic void atyfb_pci_remove(struct pci_dev *pdev)
375662306a36Sopenharmony_ci{
375762306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
375862306a36Sopenharmony_ci
375962306a36Sopenharmony_ci	mutex_lock(&reboot_lock);
376062306a36Sopenharmony_ci	if (reboot_info == info)
376162306a36Sopenharmony_ci		reboot_info = NULL;
376262306a36Sopenharmony_ci	mutex_unlock(&reboot_lock);
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci	atyfb_remove(info);
376562306a36Sopenharmony_ci}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_cistatic const struct pci_device_id atyfb_pci_tbl[] = {
376862306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_GX
376962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GX) },
377062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CX) },
377162306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_GX */
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci#ifdef CONFIG_FB_ATY_CT
377462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64CT) },
377562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64ET) },
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LT) },
377862306a36Sopenharmony_ci
377962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VT) },
378062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GT) },
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VU) },
378362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GU) },
378462306a36Sopenharmony_ci
378562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LG) },
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64VV) },
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GV) },
379062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GW) },
379162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GY) },
379262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GZ) },
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GB) },
379562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GD) },
379662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GI) },
379762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GP) },
379862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GQ) },
379962306a36Sopenharmony_ci
380062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LB) },
380162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LD) },
380262306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LI) },
380362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LP) },
380462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LQ) },
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GM) },
380762306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GN) },
380862306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GO) },
380962306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GL) },
381062306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GR) },
381162306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64GS) },
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LM) },
381462306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LN) },
381562306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LR) },
381662306a36Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_CHIP_MACH64LS) },
381762306a36Sopenharmony_ci#endif /* CONFIG_FB_ATY_CT */
381862306a36Sopenharmony_ci	{ }
381962306a36Sopenharmony_ci};
382062306a36Sopenharmony_ci
382162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, atyfb_pci_tbl);
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_cistatic struct pci_driver atyfb_driver = {
382462306a36Sopenharmony_ci	.name		= "atyfb",
382562306a36Sopenharmony_ci	.id_table	= atyfb_pci_tbl,
382662306a36Sopenharmony_ci	.probe		= atyfb_pci_probe,
382762306a36Sopenharmony_ci	.remove		= atyfb_pci_remove,
382862306a36Sopenharmony_ci	.driver.pm	= &atyfb_pci_pm_ops,
382962306a36Sopenharmony_ci};
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ci#endif /* CONFIG_PCI */
383262306a36Sopenharmony_ci
383362306a36Sopenharmony_ci#ifndef MODULE
383462306a36Sopenharmony_cistatic int __init atyfb_setup(char *options)
383562306a36Sopenharmony_ci{
383662306a36Sopenharmony_ci	char *this_opt;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	if (!options || !*options)
383962306a36Sopenharmony_ci		return 0;
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
384262306a36Sopenharmony_ci		if (!strncmp(this_opt, "noaccel", 7)) {
384362306a36Sopenharmony_ci			noaccel = true;
384462306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "nomtrr", 6)) {
384562306a36Sopenharmony_ci			nomtrr = true;
384662306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "vram:", 5))
384762306a36Sopenharmony_ci			vram = simple_strtoul(this_opt + 5, NULL, 0);
384862306a36Sopenharmony_ci		else if (!strncmp(this_opt, "pll:", 4))
384962306a36Sopenharmony_ci			pll = simple_strtoul(this_opt + 4, NULL, 0);
385062306a36Sopenharmony_ci		else if (!strncmp(this_opt, "mclk:", 5))
385162306a36Sopenharmony_ci			mclk = simple_strtoul(this_opt + 5, NULL, 0);
385262306a36Sopenharmony_ci		else if (!strncmp(this_opt, "xclk:", 5))
385362306a36Sopenharmony_ci			xclk = simple_strtoul(this_opt+5, NULL, 0);
385462306a36Sopenharmony_ci		else if (!strncmp(this_opt, "comp_sync:", 10))
385562306a36Sopenharmony_ci			comp_sync = simple_strtoul(this_opt+10, NULL, 0);
385662306a36Sopenharmony_ci		else if (!strncmp(this_opt, "backlight:", 10))
385762306a36Sopenharmony_ci			backlight = simple_strtoul(this_opt+10, NULL, 0);
385862306a36Sopenharmony_ci#ifdef CONFIG_PPC
385962306a36Sopenharmony_ci		else if (!strncmp(this_opt, "vmode:", 6)) {
386062306a36Sopenharmony_ci			unsigned int vmode =
386162306a36Sopenharmony_ci			    simple_strtoul(this_opt + 6, NULL, 0);
386262306a36Sopenharmony_ci			if (vmode > 0 && vmode <= VMODE_MAX)
386362306a36Sopenharmony_ci				default_vmode = vmode;
386462306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "cmode:", 6)) {
386562306a36Sopenharmony_ci			unsigned int cmode =
386662306a36Sopenharmony_ci			    simple_strtoul(this_opt + 6, NULL, 0);
386762306a36Sopenharmony_ci			switch (cmode) {
386862306a36Sopenharmony_ci			case 0:
386962306a36Sopenharmony_ci			case 8:
387062306a36Sopenharmony_ci				default_cmode = CMODE_8;
387162306a36Sopenharmony_ci				break;
387262306a36Sopenharmony_ci			case 15:
387362306a36Sopenharmony_ci			case 16:
387462306a36Sopenharmony_ci				default_cmode = CMODE_16;
387562306a36Sopenharmony_ci				break;
387662306a36Sopenharmony_ci			case 24:
387762306a36Sopenharmony_ci			case 32:
387862306a36Sopenharmony_ci				default_cmode = CMODE_32;
387962306a36Sopenharmony_ci				break;
388062306a36Sopenharmony_ci			}
388162306a36Sopenharmony_ci		}
388262306a36Sopenharmony_ci#endif
388362306a36Sopenharmony_ci#ifdef CONFIG_ATARI
388462306a36Sopenharmony_ci		/*
388562306a36Sopenharmony_ci		 * Why do we need this silly Mach64 argument?
388662306a36Sopenharmony_ci		 * We are already here because of mach64= so its redundant.
388762306a36Sopenharmony_ci		 */
388862306a36Sopenharmony_ci		else if (MACH_IS_ATARI
388962306a36Sopenharmony_ci			 && (!strncmp(this_opt, "Mach64:", 7))) {
389062306a36Sopenharmony_ci			static unsigned char m64_num;
389162306a36Sopenharmony_ci			static char mach64_str[80];
389262306a36Sopenharmony_ci			strscpy(mach64_str, this_opt + 7, sizeof(mach64_str));
389362306a36Sopenharmony_ci			if (!store_video_par(mach64_str, m64_num)) {
389462306a36Sopenharmony_ci				m64_num++;
389562306a36Sopenharmony_ci				mach64_count = m64_num;
389662306a36Sopenharmony_ci			}
389762306a36Sopenharmony_ci		}
389862306a36Sopenharmony_ci#endif
389962306a36Sopenharmony_ci		else
390062306a36Sopenharmony_ci			mode = this_opt;
390162306a36Sopenharmony_ci	}
390262306a36Sopenharmony_ci	return 0;
390362306a36Sopenharmony_ci}
390462306a36Sopenharmony_ci#endif  /*  MODULE  */
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_cistatic int atyfb_reboot_notify(struct notifier_block *nb,
390762306a36Sopenharmony_ci			       unsigned long code, void *unused)
390862306a36Sopenharmony_ci{
390962306a36Sopenharmony_ci	struct atyfb_par *par;
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	if (code != SYS_RESTART)
391262306a36Sopenharmony_ci		return NOTIFY_DONE;
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_ci	mutex_lock(&reboot_lock);
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci	if (!reboot_info)
391762306a36Sopenharmony_ci		goto out;
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci	lock_fb_info(reboot_info);
392062306a36Sopenharmony_ci
392162306a36Sopenharmony_ci	par = reboot_info->par;
392262306a36Sopenharmony_ci
392362306a36Sopenharmony_ci	/*
392462306a36Sopenharmony_ci	 * HP OmniBook 500's BIOS doesn't like the state of the
392562306a36Sopenharmony_ci	 * hardware after atyfb has been used. Restore the hardware
392662306a36Sopenharmony_ci	 * to the original state to allow successful reboots.
392762306a36Sopenharmony_ci	 */
392862306a36Sopenharmony_ci	aty_set_crtc(par, &par->saved_crtc);
392962306a36Sopenharmony_ci	par->pll_ops->set_pll(reboot_info, &par->saved_pll);
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ci	unlock_fb_info(reboot_info);
393262306a36Sopenharmony_ci out:
393362306a36Sopenharmony_ci	mutex_unlock(&reboot_lock);
393462306a36Sopenharmony_ci
393562306a36Sopenharmony_ci	return NOTIFY_DONE;
393662306a36Sopenharmony_ci}
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_cistatic struct notifier_block atyfb_reboot_notifier = {
393962306a36Sopenharmony_ci	.notifier_call = atyfb_reboot_notify,
394062306a36Sopenharmony_ci};
394162306a36Sopenharmony_ci
394262306a36Sopenharmony_cistatic const struct dmi_system_id atyfb_reboot_ids[] __initconst = {
394362306a36Sopenharmony_ci	{
394462306a36Sopenharmony_ci		.ident = "HP OmniBook 500",
394562306a36Sopenharmony_ci		.matches = {
394662306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
394762306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "HP OmniBook PC"),
394862306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_VERSION, "HP OmniBook 500 FA"),
394962306a36Sopenharmony_ci		},
395062306a36Sopenharmony_ci	},
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_ci	{ }
395362306a36Sopenharmony_ci};
395462306a36Sopenharmony_cistatic bool registered_notifier = false;
395562306a36Sopenharmony_ci
395662306a36Sopenharmony_cistatic int __init atyfb_init(void)
395762306a36Sopenharmony_ci{
395862306a36Sopenharmony_ci	int err1 = 1, err2 = 1;
395962306a36Sopenharmony_ci#ifndef MODULE
396062306a36Sopenharmony_ci	char *option = NULL;
396162306a36Sopenharmony_ci#endif
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_ci	if (fb_modesetting_disabled("atyfb"))
396462306a36Sopenharmony_ci		return -ENODEV;
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_ci#ifndef MODULE
396762306a36Sopenharmony_ci	if (fb_get_options("atyfb", &option))
396862306a36Sopenharmony_ci		return -ENODEV;
396962306a36Sopenharmony_ci	atyfb_setup(option);
397062306a36Sopenharmony_ci#endif
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci#ifdef CONFIG_PCI
397362306a36Sopenharmony_ci	err1 = pci_register_driver(&atyfb_driver);
397462306a36Sopenharmony_ci#endif
397562306a36Sopenharmony_ci#ifdef CONFIG_ATARI
397662306a36Sopenharmony_ci	err2 = atyfb_atari_probe();
397762306a36Sopenharmony_ci#endif
397862306a36Sopenharmony_ci
397962306a36Sopenharmony_ci	if (err1 && err2)
398062306a36Sopenharmony_ci		return -ENODEV;
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_ci	if (dmi_check_system(atyfb_reboot_ids)) {
398362306a36Sopenharmony_ci		register_reboot_notifier(&atyfb_reboot_notifier);
398462306a36Sopenharmony_ci		registered_notifier = true;
398562306a36Sopenharmony_ci	}
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_ci	return 0;
398862306a36Sopenharmony_ci}
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_cistatic void __exit atyfb_exit(void)
399162306a36Sopenharmony_ci{
399262306a36Sopenharmony_ci	if (registered_notifier)
399362306a36Sopenharmony_ci		unregister_reboot_notifier(&atyfb_reboot_notifier);
399462306a36Sopenharmony_ci
399562306a36Sopenharmony_ci#ifdef CONFIG_PCI
399662306a36Sopenharmony_ci	pci_unregister_driver(&atyfb_driver);
399762306a36Sopenharmony_ci#endif
399862306a36Sopenharmony_ci}
399962306a36Sopenharmony_ci
400062306a36Sopenharmony_cimodule_init(atyfb_init);
400162306a36Sopenharmony_cimodule_exit(atyfb_exit);
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for ATI Mach64 cards");
400462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
400562306a36Sopenharmony_cimodule_param(noaccel, bool, 0);
400662306a36Sopenharmony_ciMODULE_PARM_DESC(noaccel, "bool: disable acceleration");
400762306a36Sopenharmony_cimodule_param(vram, int, 0);
400862306a36Sopenharmony_ciMODULE_PARM_DESC(vram, "int: override size of video ram");
400962306a36Sopenharmony_cimodule_param(pll, int, 0);
401062306a36Sopenharmony_ciMODULE_PARM_DESC(pll, "int: override video clock");
401162306a36Sopenharmony_cimodule_param(mclk, int, 0);
401262306a36Sopenharmony_ciMODULE_PARM_DESC(mclk, "int: override memory clock");
401362306a36Sopenharmony_cimodule_param(xclk, int, 0);
401462306a36Sopenharmony_ciMODULE_PARM_DESC(xclk, "int: override accelerated engine clock");
401562306a36Sopenharmony_cimodule_param(comp_sync, int, 0);
401662306a36Sopenharmony_ciMODULE_PARM_DESC(comp_sync, "Set composite sync signal to low (0) or high (1)");
401762306a36Sopenharmony_cimodule_param(mode, charp, 0);
401862306a36Sopenharmony_ciMODULE_PARM_DESC(mode, "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
401962306a36Sopenharmony_cimodule_param(nomtrr, bool, 0);
402062306a36Sopenharmony_ciMODULE_PARM_DESC(nomtrr, "bool: disable use of MTRR registers");
4021