162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * linux/drivers/video/savagefb.c -- S3 Savage Framebuffer Driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>
562306a36Sopenharmony_ci *                          Sven Neumann <neo@directfb.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Card specific code is based on XFree86's savage driver.
962306a36Sopenharmony_ci * Framebuffer framework code is based on code of cyber2000fb and tdfxfb.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
1262306a36Sopenharmony_ci * Public License.  See the file COPYING in the main directory of this
1362306a36Sopenharmony_ci * archive for more details.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * 0.4.0 (neo)
1662306a36Sopenharmony_ci *  - hardware accelerated clear and move
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * 0.3.2 (dok)
1962306a36Sopenharmony_ci *  - wait for vertical retrace before writing to cr67
2062306a36Sopenharmony_ci *    at the beginning of savagefb_set_par
2162306a36Sopenharmony_ci *  - use synchronization registers cr23 and cr26
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * 0.3.1 (dok)
2462306a36Sopenharmony_ci *  - reset 3D engine
2562306a36Sopenharmony_ci *  - don't return alpha bits for 32bit format
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * 0.3.0 (dok)
2862306a36Sopenharmony_ci *  - added WaitIdle functions for all Savage types
2962306a36Sopenharmony_ci *  - do WaitIdle before mode switching
3062306a36Sopenharmony_ci *  - code cleanup
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * 0.2.0 (dok)
3362306a36Sopenharmony_ci *  - first working version
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * TODO
3762306a36Sopenharmony_ci * - clock validations in decode_var
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * BUGS
4062306a36Sopenharmony_ci * - white margin on bootup
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include <linux/aperture.h>
4562306a36Sopenharmony_ci#include <linux/module.h>
4662306a36Sopenharmony_ci#include <linux/kernel.h>
4762306a36Sopenharmony_ci#include <linux/errno.h>
4862306a36Sopenharmony_ci#include <linux/string.h>
4962306a36Sopenharmony_ci#include <linux/mm.h>
5062306a36Sopenharmony_ci#include <linux/slab.h>
5162306a36Sopenharmony_ci#include <linux/delay.h>
5262306a36Sopenharmony_ci#include <linux/fb.h>
5362306a36Sopenharmony_ci#include <linux/pci.h>
5462306a36Sopenharmony_ci#include <linux/init.h>
5562306a36Sopenharmony_ci#include <linux/console.h>
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#include <asm/io.h>
5862306a36Sopenharmony_ci#include <asm/irq.h>
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#include "savagefb.h"
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define SAVAGEFB_VERSION "0.4.0_2.6"
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic char *mode_option = NULL;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#ifdef MODULE
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciMODULE_AUTHOR("(c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>");
7362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7462306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#endif
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void vgaHWSeqReset(struct savagefb_par *par, int start)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	if (start)
8462306a36Sopenharmony_ci		VGAwSEQ(0x00, 0x01, par);	/* Synchronous Reset */
8562306a36Sopenharmony_ci	else
8662306a36Sopenharmony_ci		VGAwSEQ(0x00, 0x03, par);	/* End Reset */
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void vgaHWProtect(struct savagefb_par *par, int on)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	unsigned char tmp;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (on) {
9462306a36Sopenharmony_ci		/*
9562306a36Sopenharmony_ci		 * Turn off screen and disable sequencer.
9662306a36Sopenharmony_ci		 */
9762306a36Sopenharmony_ci		tmp = VGArSEQ(0x01, par);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		vgaHWSeqReset(par, 1);	        /* start synchronous reset */
10062306a36Sopenharmony_ci		VGAwSEQ(0x01, tmp | 0x20, par);/* disable the display */
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		VGAenablePalette(par);
10362306a36Sopenharmony_ci	} else {
10462306a36Sopenharmony_ci		/*
10562306a36Sopenharmony_ci		 * Reenable sequencer, then turn on screen.
10662306a36Sopenharmony_ci		 */
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		tmp = VGArSEQ(0x01, par);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		VGAwSEQ(0x01, tmp & ~0x20, par);/* reenable display */
11162306a36Sopenharmony_ci		vgaHWSeqReset(par, 0);	        /* clear synchronous reset */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		VGAdisablePalette(par);
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void vgaHWRestore(struct savagefb_par  *par, struct savage_reg *reg)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	int i;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	VGAwMISC(reg->MiscOutReg, par);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	for (i = 1; i < 5; i++)
12462306a36Sopenharmony_ci		VGAwSEQ(i, reg->Sequencer[i], par);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or
12762306a36Sopenharmony_ci	   CRTC[17] */
12862306a36Sopenharmony_ci	VGAwCR(17, reg->CRTC[17] & ~0x80, par);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	for (i = 0; i < 25; i++)
13162306a36Sopenharmony_ci		VGAwCR(i, reg->CRTC[i], par);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	for (i = 0; i < 9; i++)
13462306a36Sopenharmony_ci		VGAwGR(i, reg->Graphics[i], par);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	VGAenablePalette(par);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	for (i = 0; i < 21; i++)
13962306a36Sopenharmony_ci		VGAwATTR(i, reg->Attribute[i], par);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	VGAdisablePalette(par);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic void vgaHWInit(struct fb_var_screeninfo *var,
14562306a36Sopenharmony_ci		      struct savagefb_par            *par,
14662306a36Sopenharmony_ci		      struct xtimings                *timings,
14762306a36Sopenharmony_ci		      struct savage_reg              *reg)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	reg->MiscOutReg = 0x23;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT))
15262306a36Sopenharmony_ci		reg->MiscOutReg |= 0x40;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT))
15562306a36Sopenharmony_ci		reg->MiscOutReg |= 0x80;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/*
15862306a36Sopenharmony_ci	 * Time Sequencer
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	reg->Sequencer[0x00] = 0x00;
16162306a36Sopenharmony_ci	reg->Sequencer[0x01] = 0x01;
16262306a36Sopenharmony_ci	reg->Sequencer[0x02] = 0x0F;
16362306a36Sopenharmony_ci	reg->Sequencer[0x03] = 0x00;          /* Font select */
16462306a36Sopenharmony_ci	reg->Sequencer[0x04] = 0x0E;          /* Misc */
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/*
16762306a36Sopenharmony_ci	 * CRTC Controller
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	reg->CRTC[0x00] = (timings->HTotal >> 3) - 5;
17062306a36Sopenharmony_ci	reg->CRTC[0x01] = (timings->HDisplay >> 3) - 1;
17162306a36Sopenharmony_ci	reg->CRTC[0x02] = (timings->HSyncStart >> 3) - 1;
17262306a36Sopenharmony_ci	reg->CRTC[0x03] = (((timings->HSyncEnd >> 3)  - 1) & 0x1f) | 0x80;
17362306a36Sopenharmony_ci	reg->CRTC[0x04] = (timings->HSyncStart >> 3);
17462306a36Sopenharmony_ci	reg->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) |
17562306a36Sopenharmony_ci		(((timings->HSyncEnd >> 3)) & 0x1f);
17662306a36Sopenharmony_ci	reg->CRTC[0x06] = (timings->VTotal - 2) & 0xFF;
17762306a36Sopenharmony_ci	reg->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) |
17862306a36Sopenharmony_ci		(((timings->VDisplay - 1) & 0x100) >> 7) |
17962306a36Sopenharmony_ci		((timings->VSyncStart & 0x100) >> 6) |
18062306a36Sopenharmony_ci		(((timings->VSyncStart - 1) & 0x100) >> 5) |
18162306a36Sopenharmony_ci		0x10 |
18262306a36Sopenharmony_ci		(((timings->VTotal - 2) & 0x200) >> 4) |
18362306a36Sopenharmony_ci		(((timings->VDisplay - 1) & 0x200) >> 3) |
18462306a36Sopenharmony_ci		((timings->VSyncStart & 0x200) >> 2);
18562306a36Sopenharmony_ci	reg->CRTC[0x08] = 0x00;
18662306a36Sopenharmony_ci	reg->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (timings->dblscan)
18962306a36Sopenharmony_ci		reg->CRTC[0x09] |= 0x80;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	reg->CRTC[0x0a] = 0x00;
19262306a36Sopenharmony_ci	reg->CRTC[0x0b] = 0x00;
19362306a36Sopenharmony_ci	reg->CRTC[0x0c] = 0x00;
19462306a36Sopenharmony_ci	reg->CRTC[0x0d] = 0x00;
19562306a36Sopenharmony_ci	reg->CRTC[0x0e] = 0x00;
19662306a36Sopenharmony_ci	reg->CRTC[0x0f] = 0x00;
19762306a36Sopenharmony_ci	reg->CRTC[0x10] = timings->VSyncStart & 0xff;
19862306a36Sopenharmony_ci	reg->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20;
19962306a36Sopenharmony_ci	reg->CRTC[0x12] = (timings->VDisplay - 1) & 0xff;
20062306a36Sopenharmony_ci	reg->CRTC[0x13] = var->xres_virtual >> 4;
20162306a36Sopenharmony_ci	reg->CRTC[0x14] = 0x00;
20262306a36Sopenharmony_ci	reg->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff;
20362306a36Sopenharmony_ci	reg->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff;
20462306a36Sopenharmony_ci	reg->CRTC[0x17] = 0xc3;
20562306a36Sopenharmony_ci	reg->CRTC[0x18] = 0xff;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/*
20862306a36Sopenharmony_ci	 * are these unnecessary?
20962306a36Sopenharmony_ci	 * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
21062306a36Sopenharmony_ci	 * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/*
21462306a36Sopenharmony_ci	 * Graphics Display Controller
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci	reg->Graphics[0x00] = 0x00;
21762306a36Sopenharmony_ci	reg->Graphics[0x01] = 0x00;
21862306a36Sopenharmony_ci	reg->Graphics[0x02] = 0x00;
21962306a36Sopenharmony_ci	reg->Graphics[0x03] = 0x00;
22062306a36Sopenharmony_ci	reg->Graphics[0x04] = 0x00;
22162306a36Sopenharmony_ci	reg->Graphics[0x05] = 0x40;
22262306a36Sopenharmony_ci	reg->Graphics[0x06] = 0x05;   /* only map 64k VGA memory !!!! */
22362306a36Sopenharmony_ci	reg->Graphics[0x07] = 0x0F;
22462306a36Sopenharmony_ci	reg->Graphics[0x08] = 0xFF;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	reg->Attribute[0x00]  = 0x00; /* standard colormap translation */
22862306a36Sopenharmony_ci	reg->Attribute[0x01]  = 0x01;
22962306a36Sopenharmony_ci	reg->Attribute[0x02]  = 0x02;
23062306a36Sopenharmony_ci	reg->Attribute[0x03]  = 0x03;
23162306a36Sopenharmony_ci	reg->Attribute[0x04]  = 0x04;
23262306a36Sopenharmony_ci	reg->Attribute[0x05]  = 0x05;
23362306a36Sopenharmony_ci	reg->Attribute[0x06]  = 0x06;
23462306a36Sopenharmony_ci	reg->Attribute[0x07]  = 0x07;
23562306a36Sopenharmony_ci	reg->Attribute[0x08]  = 0x08;
23662306a36Sopenharmony_ci	reg->Attribute[0x09]  = 0x09;
23762306a36Sopenharmony_ci	reg->Attribute[0x0a] = 0x0A;
23862306a36Sopenharmony_ci	reg->Attribute[0x0b] = 0x0B;
23962306a36Sopenharmony_ci	reg->Attribute[0x0c] = 0x0C;
24062306a36Sopenharmony_ci	reg->Attribute[0x0d] = 0x0D;
24162306a36Sopenharmony_ci	reg->Attribute[0x0e] = 0x0E;
24262306a36Sopenharmony_ci	reg->Attribute[0x0f] = 0x0F;
24362306a36Sopenharmony_ci	reg->Attribute[0x10] = 0x41;
24462306a36Sopenharmony_ci	reg->Attribute[0x11] = 0xFF;
24562306a36Sopenharmony_ci	reg->Attribute[0x12] = 0x0F;
24662306a36Sopenharmony_ci	reg->Attribute[0x13] = 0x00;
24762306a36Sopenharmony_ci	reg->Attribute[0x14] = 0x00;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/* -------------------- Hardware specific routines ------------------------- */
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/*
25362306a36Sopenharmony_ci * Hardware Acceleration for SavageFB
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/* Wait for fifo space */
25762306a36Sopenharmony_cistatic void
25862306a36Sopenharmony_cisavage3D_waitfifo(struct savagefb_par *par, int space)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	int slots = MAXFIFO - space;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	while ((savage_in32(0x48C00, par) & 0x0000ffff) > slots);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void
26662306a36Sopenharmony_cisavage4_waitfifo(struct savagefb_par *par, int space)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int slots = MAXFIFO - space;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x001fffff) > slots);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void
27462306a36Sopenharmony_cisavage2000_waitfifo(struct savagefb_par *par, int space)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	int slots = MAXFIFO - space;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x0000ffff) > slots);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/* Wait for idle accelerator */
28262306a36Sopenharmony_cistatic void
28362306a36Sopenharmony_cisavage3D_waitidle(struct savagefb_par *par)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	while ((savage_in32(0x48C00, par) & 0x0008ffff) != 0x80000);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void
28962306a36Sopenharmony_cisavage4_waitidle(struct savagefb_par *par)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x00a00000) != 0x00a00000);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void
29562306a36Sopenharmony_cisavage2000_waitidle(struct savagefb_par *par)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x009fffff));
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_ACCEL
30162306a36Sopenharmony_cistatic void
30262306a36Sopenharmony_ciSavageSetup2DEngine(struct savagefb_par  *par)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	unsigned long GlobalBitmapDescriptor;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE;
30762306a36Sopenharmony_ci	BCI_BD_SET_BPP(GlobalBitmapDescriptor, par->depth);
30862306a36Sopenharmony_ci	BCI_BD_SET_STRIDE(GlobalBitmapDescriptor, par->vwidth);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	switch(par->chip) {
31162306a36Sopenharmony_ci	case S3_SAVAGE3D:
31262306a36Sopenharmony_ci	case S3_SAVAGE_MX:
31362306a36Sopenharmony_ci		/* Disable BCI */
31462306a36Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
31562306a36Sopenharmony_ci		/* Setup BCI command overflow buffer */
31662306a36Sopenharmony_ci		savage_out32(0x48C14,
31762306a36Sopenharmony_ci			     (par->cob_offset >> 11) | (par->cob_index << 29),
31862306a36Sopenharmony_ci			     par);
31962306a36Sopenharmony_ci		/* Program shadow status update. */
32062306a36Sopenharmony_ci		savage_out32(0x48C10, 0x78207220, par);
32162306a36Sopenharmony_ci		savage_out32(0x48C0C, 0, par);
32262306a36Sopenharmony_ci		/* Enable BCI and command overflow buffer */
32362306a36Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x0C, par);
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	case S3_SAVAGE4:
32662306a36Sopenharmony_ci	case S3_TWISTER:
32762306a36Sopenharmony_ci	case S3_PROSAVAGE:
32862306a36Sopenharmony_ci	case S3_PROSAVAGEDDR:
32962306a36Sopenharmony_ci	case S3_SUPERSAVAGE:
33062306a36Sopenharmony_ci		/* Disable BCI */
33162306a36Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
33262306a36Sopenharmony_ci		/* Program shadow status update */
33362306a36Sopenharmony_ci		savage_out32(0x48C10, 0x00700040, par);
33462306a36Sopenharmony_ci		savage_out32(0x48C0C, 0, par);
33562306a36Sopenharmony_ci		/* Enable BCI without the COB */
33662306a36Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x08, par);
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	case S3_SAVAGE2000:
33962306a36Sopenharmony_ci		/* Disable BCI */
34062306a36Sopenharmony_ci		savage_out32(0x48C18, 0, par);
34162306a36Sopenharmony_ci		/* Setup BCI command overflow buffer */
34262306a36Sopenharmony_ci		savage_out32(0x48C18,
34362306a36Sopenharmony_ci			     (par->cob_offset >> 7) | (par->cob_index),
34462306a36Sopenharmony_ci			     par);
34562306a36Sopenharmony_ci		/* Disable shadow status update */
34662306a36Sopenharmony_ci		savage_out32(0x48A30, 0, par);
34762306a36Sopenharmony_ci		/* Enable BCI and command overflow buffer */
34862306a36Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x00280000,
34962306a36Sopenharmony_ci			     par);
35062306a36Sopenharmony_ci		break;
35162306a36Sopenharmony_ci	    default:
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci	/* Turn on 16-bit register access. */
35562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
35662306a36Sopenharmony_ci	vga_out8(0x3d5, 0x0c, par);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* Set stride to use GBD. */
35962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
36062306a36Sopenharmony_ci	vga_out8(0x3d5, vga_in8(0x3d5, par) | 0xC1, par);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* Enable 2D engine. */
36362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
36462306a36Sopenharmony_ci	vga_out8(0x3d5, 0x01, par);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	savage_out32(MONO_PAT_0, ~0, par);
36762306a36Sopenharmony_ci	savage_out32(MONO_PAT_1, ~0, par);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Setup plane masks */
37062306a36Sopenharmony_ci	savage_out32(0x8128, ~0, par); /* enable all write planes */
37162306a36Sopenharmony_ci	savage_out32(0x812C, ~0, par); /* enable all read planes */
37262306a36Sopenharmony_ci	savage_out16(0x8134, 0x27, par);
37362306a36Sopenharmony_ci	savage_out16(0x8136, 0x07, par);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/* Now set the GBD */
37662306a36Sopenharmony_ci	par->bci_ptr = 0;
37762306a36Sopenharmony_ci	par->SavageWaitFifo(par, 4);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1);
38062306a36Sopenharmony_ci	BCI_SEND(0);
38162306a36Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2);
38262306a36Sopenharmony_ci	BCI_SEND(GlobalBitmapDescriptor);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/*
38562306a36Sopenharmony_ci	 * I don't know why, sending this twice fixes the initial black screen,
38662306a36Sopenharmony_ci	 * prevents X from crashing at least in Toshiba laptops with SavageIX.
38762306a36Sopenharmony_ci	 * --Tony
38862306a36Sopenharmony_ci	 */
38962306a36Sopenharmony_ci	par->bci_ptr = 0;
39062306a36Sopenharmony_ci	par->SavageWaitFifo(par, 4);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1);
39362306a36Sopenharmony_ci	BCI_SEND(0);
39462306a36Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2);
39562306a36Sopenharmony_ci	BCI_SEND(GlobalBitmapDescriptor);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic void savagefb_set_clip(struct fb_info *info)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
40162306a36Sopenharmony_ci	int cmd;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW;
40462306a36Sopenharmony_ci	par->bci_ptr = 0;
40562306a36Sopenharmony_ci	par->SavageWaitFifo(par,3);
40662306a36Sopenharmony_ci	BCI_SEND(cmd);
40762306a36Sopenharmony_ci	BCI_SEND(BCI_CLIP_TL(0, 0));
40862306a36Sopenharmony_ci	BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff));
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci#else
41162306a36Sopenharmony_cistatic void SavageSetup2DEngine(struct savagefb_par  *par) {}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci#endif
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
41662306a36Sopenharmony_ci			    int min_n2, int max_n2, long freq_min,
41762306a36Sopenharmony_ci			    long freq_max, unsigned int *mdiv,
41862306a36Sopenharmony_ci			    unsigned int *ndiv, unsigned int *r)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	long diff, best_diff;
42162306a36Sopenharmony_ci	unsigned int m;
42262306a36Sopenharmony_ci	unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	if (freq < freq_min / (1 << max_n2)) {
42562306a36Sopenharmony_ci		printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
42662306a36Sopenharmony_ci		freq = freq_min / (1 << max_n2);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci	if (freq > freq_max / (1 << min_n2)) {
42962306a36Sopenharmony_ci		printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
43062306a36Sopenharmony_ci		freq = freq_max / (1 << min_n2);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/* work out suitable timings */
43462306a36Sopenharmony_ci	best_diff = freq;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	for (n2=min_n2; n2<=max_n2; n2++) {
43762306a36Sopenharmony_ci		for (n1=min_n1+2; n1<=max_n1+2; n1++) {
43862306a36Sopenharmony_ci			m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
43962306a36Sopenharmony_ci				BASE_FREQ;
44062306a36Sopenharmony_ci			if (m < min_m+2 || m > 127+2)
44162306a36Sopenharmony_ci				continue;
44262306a36Sopenharmony_ci			if ((m * BASE_FREQ >= freq_min * n1) &&
44362306a36Sopenharmony_ci			    (m * BASE_FREQ <= freq_max * n1)) {
44462306a36Sopenharmony_ci				diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
44562306a36Sopenharmony_ci				if (diff < 0)
44662306a36Sopenharmony_ci					diff = -diff;
44762306a36Sopenharmony_ci				if (diff < best_diff) {
44862306a36Sopenharmony_ci					best_diff = diff;
44962306a36Sopenharmony_ci					best_m = m;
45062306a36Sopenharmony_ci					best_n1 = n1;
45162306a36Sopenharmony_ci					best_n2 = n2;
45262306a36Sopenharmony_ci				}
45362306a36Sopenharmony_ci			}
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	*ndiv = best_n1 - 2;
45862306a36Sopenharmony_ci	*r = best_n2;
45962306a36Sopenharmony_ci	*mdiv = best_m - 2;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int common_calc_clock(long freq, int min_m, int min_n1, int max_n1,
46362306a36Sopenharmony_ci			     int min_n2, int max_n2, long freq_min,
46462306a36Sopenharmony_ci			     long freq_max, unsigned char *mdiv,
46562306a36Sopenharmony_ci			     unsigned char *ndiv)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	long diff, best_diff;
46862306a36Sopenharmony_ci	unsigned int m;
46962306a36Sopenharmony_ci	unsigned char n1, n2;
47062306a36Sopenharmony_ci	unsigned char best_n1 = 16+2, best_n2 = 2, best_m = 125+2;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	best_diff = freq;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	for (n2 = min_n2; n2 <= max_n2; n2++) {
47562306a36Sopenharmony_ci		for (n1 = min_n1+2; n1 <= max_n1+2; n1++) {
47662306a36Sopenharmony_ci			m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
47762306a36Sopenharmony_ci				BASE_FREQ;
47862306a36Sopenharmony_ci			if (m < min_m + 2 || m > 127+2)
47962306a36Sopenharmony_ci				continue;
48062306a36Sopenharmony_ci			if ((m * BASE_FREQ >= freq_min * n1) &&
48162306a36Sopenharmony_ci			    (m * BASE_FREQ <= freq_max * n1)) {
48262306a36Sopenharmony_ci				diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
48362306a36Sopenharmony_ci				if (diff < 0)
48462306a36Sopenharmony_ci					diff = -diff;
48562306a36Sopenharmony_ci				if (diff < best_diff) {
48662306a36Sopenharmony_ci					best_diff = diff;
48762306a36Sopenharmony_ci					best_m = m;
48862306a36Sopenharmony_ci					best_n1 = n1;
48962306a36Sopenharmony_ci					best_n2 = n2;
49062306a36Sopenharmony_ci				}
49162306a36Sopenharmony_ci			}
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (max_n1 == 63)
49662306a36Sopenharmony_ci		*ndiv = (best_n1 - 2) | (best_n2 << 6);
49762306a36Sopenharmony_ci	else
49862306a36Sopenharmony_ci		*ndiv = (best_n1 - 2) | (best_n2 << 5);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	*mdiv = best_m - 2;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci#ifdef SAVAGEFB_DEBUG
50662306a36Sopenharmony_ci/* This function is used to debug, it prints out the contents of s3 regs */
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic void SavagePrintRegs(struct savagefb_par *par)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	unsigned char i;
51162306a36Sopenharmony_ci	int vgaCRIndex = 0x3d4;
51262306a36Sopenharmony_ci	int vgaCRReg = 0x3d5;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	printk(KERN_DEBUG "SR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE "
51562306a36Sopenharmony_ci	       "xF");
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	for (i = 0; i < 0x70; i++) {
51862306a36Sopenharmony_ci		if (!(i % 16))
51962306a36Sopenharmony_ci			printk(KERN_DEBUG "\nSR%xx ", i >> 4);
52062306a36Sopenharmony_ci		vga_out8(0x3c4, i, par);
52162306a36Sopenharmony_ci		printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par));
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	printk(KERN_DEBUG "\n\nCR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC "
52562306a36Sopenharmony_ci	       "xD xE xF");
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	for (i = 0; i < 0xB7; i++) {
52862306a36Sopenharmony_ci		if (!(i % 16))
52962306a36Sopenharmony_ci			printk(KERN_DEBUG "\nCR%xx ", i >> 4);
53062306a36Sopenharmony_ci		vga_out8(vgaCRIndex, i, par);
53162306a36Sopenharmony_ci		printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par));
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	printk(KERN_DEBUG "\n\n");
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci#endif
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic void savage_get_default_par(struct savagefb_par *par, struct savage_reg *reg)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	unsigned char cr3a, cr53, cr66;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
54562306a36Sopenharmony_ci	vga_out16(0x3d4, 0xa039, par);
54662306a36Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
54962306a36Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
55062306a36Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
55162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
55262306a36Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
55362306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
55462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
55562306a36Sopenharmony_ci	cr53 = vga_in8(0x3d5, par);
55662306a36Sopenharmony_ci	vga_out8(0x3d5, cr53 & 0x7f, par);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
55962306a36Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
56062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
56162306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
56462306a36Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
56562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
56662306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* unlock extended seq regs */
56962306a36Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
57062306a36Sopenharmony_ci	reg->SR08 = vga_in8(0x3c5, par);
57162306a36Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* now save all the extended regs we need */
57462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
57562306a36Sopenharmony_ci	reg->CR31 = vga_in8(0x3d5, par);
57662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
57762306a36Sopenharmony_ci	reg->CR32 = vga_in8(0x3d5, par);
57862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x34, par);
57962306a36Sopenharmony_ci	reg->CR34 = vga_in8(0x3d5, par);
58062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
58162306a36Sopenharmony_ci	reg->CR36 = vga_in8(0x3d5, par);
58262306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
58362306a36Sopenharmony_ci	reg->CR3A = vga_in8(0x3d5, par);
58462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
58562306a36Sopenharmony_ci	reg->CR40 = vga_in8(0x3d5, par);
58662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x42, par);
58762306a36Sopenharmony_ci	reg->CR42 = vga_in8(0x3d5, par);
58862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x45, par);
58962306a36Sopenharmony_ci	reg->CR45 = vga_in8(0x3d5, par);
59062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
59162306a36Sopenharmony_ci	reg->CR50 = vga_in8(0x3d5, par);
59262306a36Sopenharmony_ci	vga_out8(0x3d4, 0x51, par);
59362306a36Sopenharmony_ci	reg->CR51 = vga_in8(0x3d5, par);
59462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
59562306a36Sopenharmony_ci	reg->CR53 = vga_in8(0x3d5, par);
59662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
59762306a36Sopenharmony_ci	reg->CR58 = vga_in8(0x3d5, par);
59862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x60, par);
59962306a36Sopenharmony_ci	reg->CR60 = vga_in8(0x3d5, par);
60062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
60162306a36Sopenharmony_ci	reg->CR66 = vga_in8(0x3d5, par);
60262306a36Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
60362306a36Sopenharmony_ci	reg->CR67 = vga_in8(0x3d5, par);
60462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
60562306a36Sopenharmony_ci	reg->CR68 = vga_in8(0x3d5, par);
60662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
60762306a36Sopenharmony_ci	reg->CR69 = vga_in8(0x3d5, par);
60862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
60962306a36Sopenharmony_ci	reg->CR6F = vga_in8(0x3d5, par);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x33, par);
61262306a36Sopenharmony_ci	reg->CR33 = vga_in8(0x3d5, par);
61362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
61462306a36Sopenharmony_ci	reg->CR86 = vga_in8(0x3d5, par);
61562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
61662306a36Sopenharmony_ci	reg->CR88 = vga_in8(0x3d5, par);
61762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x90, par);
61862306a36Sopenharmony_ci	reg->CR90 = vga_in8(0x3d5, par);
61962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x91, par);
62062306a36Sopenharmony_ci	reg->CR91 = vga_in8(0x3d5, par);
62162306a36Sopenharmony_ci	vga_out8(0x3d4, 0xb0, par);
62262306a36Sopenharmony_ci	reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* extended mode timing regs */
62562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3b, par);
62662306a36Sopenharmony_ci	reg->CR3B = vga_in8(0x3d5, par);
62762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3c, par);
62862306a36Sopenharmony_ci	reg->CR3C = vga_in8(0x3d5, par);
62962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x43, par);
63062306a36Sopenharmony_ci	reg->CR43 = vga_in8(0x3d5, par);
63162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x5d, par);
63262306a36Sopenharmony_ci	reg->CR5D = vga_in8(0x3d5, par);
63362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x5e, par);
63462306a36Sopenharmony_ci	reg->CR5E = vga_in8(0x3d5, par);
63562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x65, par);
63662306a36Sopenharmony_ci	reg->CR65 = vga_in8(0x3d5, par);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	/* save seq extended regs for DCLK PLL programming */
63962306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
64062306a36Sopenharmony_ci	reg->SR0E = vga_in8(0x3c5, par);
64162306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
64262306a36Sopenharmony_ci	reg->SR0F = vga_in8(0x3c5, par);
64362306a36Sopenharmony_ci	vga_out8(0x3c4, 0x10, par);
64462306a36Sopenharmony_ci	reg->SR10 = vga_in8(0x3c5, par);
64562306a36Sopenharmony_ci	vga_out8(0x3c4, 0x11, par);
64662306a36Sopenharmony_ci	reg->SR11 = vga_in8(0x3c5, par);
64762306a36Sopenharmony_ci	vga_out8(0x3c4, 0x12, par);
64862306a36Sopenharmony_ci	reg->SR12 = vga_in8(0x3c5, par);
64962306a36Sopenharmony_ci	vga_out8(0x3c4, 0x13, par);
65062306a36Sopenharmony_ci	reg->SR13 = vga_in8(0x3c5, par);
65162306a36Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
65262306a36Sopenharmony_ci	reg->SR29 = vga_in8(0x3c5, par);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
65562306a36Sopenharmony_ci	reg->SR15 = vga_in8(0x3c5, par);
65662306a36Sopenharmony_ci	vga_out8(0x3c4, 0x30, par);
65762306a36Sopenharmony_ci	reg->SR30 = vga_in8(0x3c5, par);
65862306a36Sopenharmony_ci	vga_out8(0x3c4, 0x18, par);
65962306a36Sopenharmony_ci	reg->SR18 = vga_in8(0x3c5, par);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Save flat panel expansion registers. */
66262306a36Sopenharmony_ci	if (par->chip == S3_SAVAGE_MX) {
66362306a36Sopenharmony_ci		int i;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		for (i = 0; i < 8; i++) {
66662306a36Sopenharmony_ci			vga_out8(0x3c4, 0x54+i, par);
66762306a36Sopenharmony_ci			reg->SR54[i] = vga_in8(0x3c5, par);
66862306a36Sopenharmony_ci		}
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
67262306a36Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
67362306a36Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
67462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
67562306a36Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
67662306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/* now save MIU regs */
67962306a36Sopenharmony_ci	if (par->chip != S3_SAVAGE_MX) {
68062306a36Sopenharmony_ci		reg->MMPR0 = savage_in32(FIFO_CONTROL_REG, par);
68162306a36Sopenharmony_ci		reg->MMPR1 = savage_in32(MIU_CONTROL_REG, par);
68262306a36Sopenharmony_ci		reg->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par);
68362306a36Sopenharmony_ci		reg->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
68762306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
68862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
68962306a36Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic void savage_set_default_par(struct savagefb_par *par,
69362306a36Sopenharmony_ci				struct savage_reg *reg)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	unsigned char cr3a, cr53, cr66;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
69862306a36Sopenharmony_ci	vga_out16(0x3d4, 0xa039, par);
69962306a36Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
70262306a36Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
70362306a36Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
70462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
70562306a36Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
70662306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
70762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
70862306a36Sopenharmony_ci	cr53 = vga_in8(0x3d5, par);
70962306a36Sopenharmony_ci	vga_out8(0x3d5, cr53 & 0x7f, par);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
71262306a36Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
71362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
71462306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
71762306a36Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
71862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
71962306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* unlock extended seq regs */
72262306a36Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
72362306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR08, par);
72462306a36Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* now restore all the extended regs we need */
72762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
72862306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR31, par);
72962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
73062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR32, par);
73162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x34, par);
73262306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR34, par);
73362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
73462306a36Sopenharmony_ci	vga_out8(0x3d5,reg->CR36, par);
73562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
73662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR3A, par);
73762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
73862306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR40, par);
73962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x42, par);
74062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR42, par);
74162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x45, par);
74262306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR45, par);
74362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
74462306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR50, par);
74562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x51, par);
74662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR51, par);
74762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
74862306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR53, par);
74962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
75062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR58, par);
75162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x60, par);
75262306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR60, par);
75362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
75462306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR66, par);
75562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
75662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR67, par);
75762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
75862306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR68, par);
75962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
76062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR69, par);
76162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
76262306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR6F, par);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x33, par);
76562306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR33, par);
76662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
76762306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR86, par);
76862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
76962306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR88, par);
77062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x90, par);
77162306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR90, par);
77262306a36Sopenharmony_ci	vga_out8(0x3d4, 0x91, par);
77362306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR91, par);
77462306a36Sopenharmony_ci	vga_out8(0x3d4, 0xb0, par);
77562306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CRB0, par);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* extended mode timing regs */
77862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3b, par);
77962306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR3B, par);
78062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3c, par);
78162306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR3C, par);
78262306a36Sopenharmony_ci	vga_out8(0x3d4, 0x43, par);
78362306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR43, par);
78462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x5d, par);
78562306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR5D, par);
78662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x5e, par);
78762306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR5E, par);
78862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x65, par);
78962306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR65, par);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* save seq extended regs for DCLK PLL programming */
79262306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
79362306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR0E, par);
79462306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
79562306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR0F, par);
79662306a36Sopenharmony_ci	vga_out8(0x3c4, 0x10, par);
79762306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR10, par);
79862306a36Sopenharmony_ci	vga_out8(0x3c4, 0x11, par);
79962306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR11, par);
80062306a36Sopenharmony_ci	vga_out8(0x3c4, 0x12, par);
80162306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR12, par);
80262306a36Sopenharmony_ci	vga_out8(0x3c4, 0x13, par);
80362306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR13, par);
80462306a36Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
80562306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR29, par);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
80862306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR15, par);
80962306a36Sopenharmony_ci	vga_out8(0x3c4, 0x30, par);
81062306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR30, par);
81162306a36Sopenharmony_ci	vga_out8(0x3c4, 0x18, par);
81262306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR18, par);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/* Save flat panel expansion registers. */
81562306a36Sopenharmony_ci	if (par->chip == S3_SAVAGE_MX) {
81662306a36Sopenharmony_ci		int i;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		for (i = 0; i < 8; i++) {
81962306a36Sopenharmony_ci			vga_out8(0x3c4, 0x54+i, par);
82062306a36Sopenharmony_ci			vga_out8(0x3c5, reg->SR54[i], par);
82162306a36Sopenharmony_ci		}
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
82562306a36Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
82662306a36Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
82762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
82862306a36Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
82962306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* now save MIU regs */
83262306a36Sopenharmony_ci	if (par->chip != S3_SAVAGE_MX) {
83362306a36Sopenharmony_ci		savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
83462306a36Sopenharmony_ci		savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
83562306a36Sopenharmony_ci		savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
83662306a36Sopenharmony_ci		savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
84062306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
84162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
84262306a36Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic void savage_update_var(struct fb_var_screeninfo *var,
84662306a36Sopenharmony_ci			      const struct fb_videomode *modedb)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	var->xres = var->xres_virtual = modedb->xres;
84962306a36Sopenharmony_ci	var->yres = modedb->yres;
85062306a36Sopenharmony_ci        if (var->yres_virtual < var->yres)
85162306a36Sopenharmony_ci	    var->yres_virtual = var->yres;
85262306a36Sopenharmony_ci        var->xoffset = var->yoffset = 0;
85362306a36Sopenharmony_ci        var->pixclock = modedb->pixclock;
85462306a36Sopenharmony_ci        var->left_margin = modedb->left_margin;
85562306a36Sopenharmony_ci        var->right_margin = modedb->right_margin;
85662306a36Sopenharmony_ci        var->upper_margin = modedb->upper_margin;
85762306a36Sopenharmony_ci        var->lower_margin = modedb->lower_margin;
85862306a36Sopenharmony_ci        var->hsync_len = modedb->hsync_len;
85962306a36Sopenharmony_ci        var->vsync_len = modedb->vsync_len;
86062306a36Sopenharmony_ci        var->sync = modedb->sync;
86162306a36Sopenharmony_ci        var->vmode = modedb->vmode;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int savagefb_check_var(struct fb_var_screeninfo   *var,
86562306a36Sopenharmony_ci			      struct fb_info *info)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
86862306a36Sopenharmony_ci	int memlen, vramlen, mode_valid = 0;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	DBG("savagefb_check_var");
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (!var->pixclock)
87362306a36Sopenharmony_ci		return -EINVAL;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	var->transp.offset = 0;
87662306a36Sopenharmony_ci	var->transp.length = 0;
87762306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
87862306a36Sopenharmony_ci	case 8:
87962306a36Sopenharmony_ci		var->red.offset = var->green.offset =
88062306a36Sopenharmony_ci			var->blue.offset = 0;
88162306a36Sopenharmony_ci		var->red.length = var->green.length =
88262306a36Sopenharmony_ci			var->blue.length = var->bits_per_pixel;
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci	case 16:
88562306a36Sopenharmony_ci		var->red.offset = 11;
88662306a36Sopenharmony_ci		var->red.length = 5;
88762306a36Sopenharmony_ci		var->green.offset = 5;
88862306a36Sopenharmony_ci		var->green.length = 6;
88962306a36Sopenharmony_ci		var->blue.offset = 0;
89062306a36Sopenharmony_ci		var->blue.length = 5;
89162306a36Sopenharmony_ci		break;
89262306a36Sopenharmony_ci	case 32:
89362306a36Sopenharmony_ci		var->transp.offset = 24;
89462306a36Sopenharmony_ci		var->transp.length = 8;
89562306a36Sopenharmony_ci		var->red.offset = 16;
89662306a36Sopenharmony_ci		var->red.length = 8;
89762306a36Sopenharmony_ci		var->green.offset = 8;
89862306a36Sopenharmony_ci		var->green.length = 8;
89962306a36Sopenharmony_ci		var->blue.offset = 0;
90062306a36Sopenharmony_ci		var->blue.length = 8;
90162306a36Sopenharmony_ci		break;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	default:
90462306a36Sopenharmony_ci		return -EINVAL;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
90862306a36Sopenharmony_ci	    !info->monspecs.dclkmax || !fb_validate_mode(var, info))
90962306a36Sopenharmony_ci		mode_valid = 1;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	/* calculate modeline if supported by monitor */
91262306a36Sopenharmony_ci	if (!mode_valid && info->monspecs.gtf) {
91362306a36Sopenharmony_ci		if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
91462306a36Sopenharmony_ci			mode_valid = 1;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (!mode_valid) {
91862306a36Sopenharmony_ci		const struct fb_videomode *mode;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		mode = fb_find_best_mode(var, &info->modelist);
92162306a36Sopenharmony_ci		if (mode) {
92262306a36Sopenharmony_ci			savage_update_var(var, mode);
92362306a36Sopenharmony_ci			mode_valid = 1;
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (!mode_valid && info->monspecs.modedb_len)
92862306a36Sopenharmony_ci		return -EINVAL;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* Is the mode larger than the LCD panel? */
93162306a36Sopenharmony_ci	if (par->SavagePanelWidth &&
93262306a36Sopenharmony_ci	    (var->xres > par->SavagePanelWidth ||
93362306a36Sopenharmony_ci	     var->yres > par->SavagePanelHeight)) {
93462306a36Sopenharmony_ci		printk(KERN_INFO "Mode (%dx%d) larger than the LCD panel "
93562306a36Sopenharmony_ci		       "(%dx%d)\n", var->xres,  var->yres,
93662306a36Sopenharmony_ci		       par->SavagePanelWidth,
93762306a36Sopenharmony_ci		       par->SavagePanelHeight);
93862306a36Sopenharmony_ci		return -1;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (var->yres_virtual < var->yres)
94262306a36Sopenharmony_ci		var->yres_virtual = var->yres;
94362306a36Sopenharmony_ci	if (var->xres_virtual < var->xres)
94462306a36Sopenharmony_ci		var->xres_virtual = var->xres;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	vramlen = info->fix.smem_len;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	memlen = var->xres_virtual * var->bits_per_pixel *
94962306a36Sopenharmony_ci		var->yres_virtual / 8;
95062306a36Sopenharmony_ci	if (memlen > vramlen) {
95162306a36Sopenharmony_ci		var->yres_virtual = vramlen * 8 /
95262306a36Sopenharmony_ci			(var->xres_virtual * var->bits_per_pixel);
95362306a36Sopenharmony_ci		memlen = var->xres_virtual * var->bits_per_pixel *
95462306a36Sopenharmony_ci			var->yres_virtual / 8;
95562306a36Sopenharmony_ci	}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/* we must round yres/xres down, we already rounded y/xres_virtual up
95862306a36Sopenharmony_ci	   if it was possible. We should return -EINVAL, but I disagree */
95962306a36Sopenharmony_ci	if (var->yres_virtual < var->yres)
96062306a36Sopenharmony_ci		var->yres = var->yres_virtual;
96162306a36Sopenharmony_ci	if (var->xres_virtual < var->xres)
96262306a36Sopenharmony_ci		var->xres = var->xres_virtual;
96362306a36Sopenharmony_ci	if (var->xoffset + var->xres > var->xres_virtual)
96462306a36Sopenharmony_ci		var->xoffset = var->xres_virtual - var->xres;
96562306a36Sopenharmony_ci	if (var->yoffset + var->yres > var->yres_virtual)
96662306a36Sopenharmony_ci		var->yoffset = var->yres_virtual - var->yres;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	return 0;
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic int savagefb_decode_var(struct fb_var_screeninfo   *var,
97362306a36Sopenharmony_ci			       struct savagefb_par        *par,
97462306a36Sopenharmony_ci			       struct savage_reg          *reg)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct xtimings timings;
97762306a36Sopenharmony_ci	int width, dclk, i, j; /*, refresh; */
97862306a36Sopenharmony_ci	unsigned int m, n, r;
97962306a36Sopenharmony_ci	unsigned char tmp = 0;
98062306a36Sopenharmony_ci	unsigned int pixclock = var->pixclock;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	DBG("savagefb_decode_var");
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	memset(&timings, 0, sizeof(timings));
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (!pixclock) pixclock = 10000;	/* 10ns = 100MHz */
98762306a36Sopenharmony_ci	timings.Clock = 1000000000 / pixclock;
98862306a36Sopenharmony_ci	if (timings.Clock < 1) timings.Clock = 1;
98962306a36Sopenharmony_ci	timings.dblscan = var->vmode & FB_VMODE_DOUBLE;
99062306a36Sopenharmony_ci	timings.interlaced = var->vmode & FB_VMODE_INTERLACED;
99162306a36Sopenharmony_ci	timings.HDisplay = var->xres;
99262306a36Sopenharmony_ci	timings.HSyncStart = timings.HDisplay + var->right_margin;
99362306a36Sopenharmony_ci	timings.HSyncEnd = timings.HSyncStart + var->hsync_len;
99462306a36Sopenharmony_ci	timings.HTotal = timings.HSyncEnd + var->left_margin;
99562306a36Sopenharmony_ci	timings.VDisplay = var->yres;
99662306a36Sopenharmony_ci	timings.VSyncStart = timings.VDisplay + var->lower_margin;
99762306a36Sopenharmony_ci	timings.VSyncEnd = timings.VSyncStart + var->vsync_len;
99862306a36Sopenharmony_ci	timings.VTotal = timings.VSyncEnd + var->upper_margin;
99962306a36Sopenharmony_ci	timings.sync = var->sync;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	par->depth  = var->bits_per_pixel;
100362306a36Sopenharmony_ci	par->vwidth = var->xres_virtual;
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (var->bits_per_pixel == 16  &&  par->chip == S3_SAVAGE3D) {
100662306a36Sopenharmony_ci		timings.HDisplay *= 2;
100762306a36Sopenharmony_ci		timings.HSyncStart *= 2;
100862306a36Sopenharmony_ci		timings.HSyncEnd *= 2;
100962306a36Sopenharmony_ci		timings.HTotal *= 2;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	/*
101362306a36Sopenharmony_ci	 * This will allocate the datastructure and initialize all of the
101462306a36Sopenharmony_ci	 * generic VGA registers.
101562306a36Sopenharmony_ci	 */
101662306a36Sopenharmony_ci	vgaHWInit(var, par, &timings, reg);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	/* We need to set CR67 whether or not we use the BIOS. */
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	dclk = timings.Clock;
102162306a36Sopenharmony_ci	reg->CR67 = 0x00;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	switch(var->bits_per_pixel) {
102462306a36Sopenharmony_ci	case 8:
102562306a36Sopenharmony_ci		if ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))
102662306a36Sopenharmony_ci			reg->CR67 = 0x10;	/* 8bpp, 2 pixels/clock */
102762306a36Sopenharmony_ci		else
102862306a36Sopenharmony_ci			reg->CR67 = 0x00;	/* 8bpp, 1 pixel/clock */
102962306a36Sopenharmony_ci		break;
103062306a36Sopenharmony_ci	case 15:
103162306a36Sopenharmony_ci		if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
103262306a36Sopenharmony_ci		    ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
103362306a36Sopenharmony_ci			reg->CR67 = 0x30;	/* 15bpp, 2 pixel/clock */
103462306a36Sopenharmony_ci		else
103562306a36Sopenharmony_ci			reg->CR67 = 0x20;	/* 15bpp, 1 pixels/clock */
103662306a36Sopenharmony_ci		break;
103762306a36Sopenharmony_ci	case 16:
103862306a36Sopenharmony_ci		if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
103962306a36Sopenharmony_ci		   ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
104062306a36Sopenharmony_ci			reg->CR67 = 0x50;	/* 16bpp, 2 pixel/clock */
104162306a36Sopenharmony_ci		else
104262306a36Sopenharmony_ci			reg->CR67 = 0x40;	/* 16bpp, 1 pixels/clock */
104362306a36Sopenharmony_ci		break;
104462306a36Sopenharmony_ci	case 24:
104562306a36Sopenharmony_ci		reg->CR67 = 0x70;
104662306a36Sopenharmony_ci		break;
104762306a36Sopenharmony_ci	case 32:
104862306a36Sopenharmony_ci		reg->CR67 = 0xd0;
104962306a36Sopenharmony_ci		break;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/*
105362306a36Sopenharmony_ci	 * Either BIOS use is disabled, or we failed to find a suitable
105462306a36Sopenharmony_ci	 * match.  Fall back to traditional register-crunching.
105562306a36Sopenharmony_ci	 */
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
105862306a36Sopenharmony_ci	tmp = vga_in8(0x3d5, par);
105962306a36Sopenharmony_ci	if (1 /*FIXME:psav->pci_burst*/)
106062306a36Sopenharmony_ci		reg->CR3A = (tmp & 0x7f) | 0x15;
106162306a36Sopenharmony_ci	else
106262306a36Sopenharmony_ci		reg->CR3A = tmp | 0x95;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	reg->CR53 = 0x00;
106562306a36Sopenharmony_ci	reg->CR31 = 0x8c;
106662306a36Sopenharmony_ci	reg->CR66 = 0x89;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
106962306a36Sopenharmony_ci	reg->CR58 = vga_in8(0x3d5, par) & 0x80;
107062306a36Sopenharmony_ci	reg->CR58 |= 0x13;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	reg->SR15 = 0x03 | 0x80;
107362306a36Sopenharmony_ci	reg->SR18 = 0x00;
107462306a36Sopenharmony_ci	reg->CR43 = reg->CR45 = reg->CR65 = 0x00;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
107762306a36Sopenharmony_ci	reg->CR40 = vga_in8(0x3d5, par) & ~0x01;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	reg->MMPR0 = 0x010400;
108062306a36Sopenharmony_ci	reg->MMPR1 = 0x00;
108162306a36Sopenharmony_ci	reg->MMPR2 = 0x0808;
108262306a36Sopenharmony_ci	reg->MMPR3 = 0x08080810;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
108562306a36Sopenharmony_ci	/* m = 107; n = 4; r = 2; */
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (par->MCLK <= 0) {
108862306a36Sopenharmony_ci		reg->SR10 = 255;
108962306a36Sopenharmony_ci		reg->SR11 = 255;
109062306a36Sopenharmony_ci	} else {
109162306a36Sopenharmony_ci		common_calc_clock(par->MCLK, 1, 1, 31, 0, 3, 135000, 270000,
109262306a36Sopenharmony_ci				   &reg->SR11, &reg->SR10);
109362306a36Sopenharmony_ci		/*      reg->SR10 = 80; // MCLK == 286000 */
109462306a36Sopenharmony_ci		/*      reg->SR11 = 125; */
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	reg->SR12 = (r << 6) | (n & 0x3f);
109862306a36Sopenharmony_ci	reg->SR13 = m & 0xff;
109962306a36Sopenharmony_ci	reg->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (var->bits_per_pixel < 24)
110262306a36Sopenharmony_ci		reg->MMPR0 -= 0x8000;
110362306a36Sopenharmony_ci	else
110462306a36Sopenharmony_ci		reg->MMPR0 -= 0x4000;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (timings.interlaced)
110762306a36Sopenharmony_ci		reg->CR42 = 0x20;
110862306a36Sopenharmony_ci	else
110962306a36Sopenharmony_ci		reg->CR42 = 0x00;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	reg->CR34 = 0x10; /* display fifo */
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) |
111462306a36Sopenharmony_ci		((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) |
111562306a36Sopenharmony_ci		((((timings.HSyncStart >> 3) - 1) & 0x100) >> 6) |
111662306a36Sopenharmony_ci		((timings.HSyncStart & 0x800) >> 7);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 64)
111962306a36Sopenharmony_ci		i |= 0x08;
112062306a36Sopenharmony_ci	if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32)
112162306a36Sopenharmony_ci		i |= 0x20;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	j = (reg->CRTC[0] + ((i & 0x01) << 8) +
112462306a36Sopenharmony_ci	     reg->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	if (j - (reg->CRTC[4] + ((i & 0x10) << 4)) < 4) {
112762306a36Sopenharmony_ci		if (reg->CRTC[4] + ((i & 0x10) << 4) + 4 <=
112862306a36Sopenharmony_ci		    reg->CRTC[0] + ((i & 0x01) << 8))
112962306a36Sopenharmony_ci			j = reg->CRTC[4] + ((i & 0x10) << 4) + 4;
113062306a36Sopenharmony_ci		else
113162306a36Sopenharmony_ci			j = reg->CRTC[0] + ((i & 0x01) << 8) + 1;
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	reg->CR3B = j & 0xff;
113562306a36Sopenharmony_ci	i |= (j & 0x100) >> 2;
113662306a36Sopenharmony_ci	reg->CR3C = (reg->CRTC[0] + ((i & 0x01) << 8)) / 2;
113762306a36Sopenharmony_ci	reg->CR5D = i;
113862306a36Sopenharmony_ci	reg->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) |
113962306a36Sopenharmony_ci		(((timings.VDisplay - 1) & 0x400) >> 9) |
114062306a36Sopenharmony_ci		(((timings.VSyncStart) & 0x400) >> 8) |
114162306a36Sopenharmony_ci		(((timings.VSyncStart) & 0x400) >> 6) | 0x40;
114262306a36Sopenharmony_ci	width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3;
114362306a36Sopenharmony_ci	reg->CR91 = reg->CRTC[19] = 0xff & width;
114462306a36Sopenharmony_ci	reg->CR51 = (0x300 & width) >> 4;
114562306a36Sopenharmony_ci	reg->CR90 = 0x80 | (width >> 8);
114662306a36Sopenharmony_ci	reg->MiscOutReg |= 0x0c;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	/* Set frame buffer description. */
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (var->bits_per_pixel <= 8)
115162306a36Sopenharmony_ci		reg->CR50 = 0;
115262306a36Sopenharmony_ci	else if (var->bits_per_pixel <= 16)
115362306a36Sopenharmony_ci		reg->CR50 = 0x10;
115462306a36Sopenharmony_ci	else
115562306a36Sopenharmony_ci		reg->CR50 = 0x30;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (var->xres_virtual <= 640)
115862306a36Sopenharmony_ci		reg->CR50 |= 0x40;
115962306a36Sopenharmony_ci	else if (var->xres_virtual == 800)
116062306a36Sopenharmony_ci		reg->CR50 |= 0x80;
116162306a36Sopenharmony_ci	else if (var->xres_virtual == 1024)
116262306a36Sopenharmony_ci		reg->CR50 |= 0x00;
116362306a36Sopenharmony_ci	else if (var->xres_virtual == 1152)
116462306a36Sopenharmony_ci		reg->CR50 |= 0x01;
116562306a36Sopenharmony_ci	else if (var->xres_virtual == 1280)
116662306a36Sopenharmony_ci		reg->CR50 |= 0xc0;
116762306a36Sopenharmony_ci	else if (var->xres_virtual == 1600)
116862306a36Sopenharmony_ci		reg->CR50 |= 0x81;
116962306a36Sopenharmony_ci	else
117062306a36Sopenharmony_ci		reg->CR50 |= 0xc1;	/* Use GBD */
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (par->chip == S3_SAVAGE2000)
117362306a36Sopenharmony_ci		reg->CR33 = 0x08;
117462306a36Sopenharmony_ci	else
117562306a36Sopenharmony_ci		reg->CR33 = 0x20;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	reg->CRTC[0x17] = 0xeb;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	reg->CR67 |= 1;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
118262306a36Sopenharmony_ci	reg->CR36 = vga_in8(0x3d5, par);
118362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
118462306a36Sopenharmony_ci	reg->CR68 = vga_in8(0x3d5, par);
118562306a36Sopenharmony_ci	reg->CR69 = 0;
118662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
118762306a36Sopenharmony_ci	reg->CR6F = vga_in8(0x3d5, par);
118862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
118962306a36Sopenharmony_ci	reg->CR86 = vga_in8(0x3d5, par);
119062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
119162306a36Sopenharmony_ci	reg->CR88 = vga_in8(0x3d5, par) | 0x08;
119262306a36Sopenharmony_ci	vga_out8(0x3d4, 0xb0, par);
119362306a36Sopenharmony_ci	reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	return 0;
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci/*
120162306a36Sopenharmony_ci *    Set a single color register. Return != 0 for invalid regno.
120262306a36Sopenharmony_ci */
120362306a36Sopenharmony_cistatic int savagefb_setcolreg(unsigned        regno,
120462306a36Sopenharmony_ci			      unsigned        red,
120562306a36Sopenharmony_ci			      unsigned        green,
120662306a36Sopenharmony_ci			      unsigned        blue,
120762306a36Sopenharmony_ci			      unsigned        transp,
120862306a36Sopenharmony_ci			      struct fb_info *info)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (regno >= NR_PALETTE)
121362306a36Sopenharmony_ci		return -EINVAL;
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	par->palette[regno].red    = red;
121662306a36Sopenharmony_ci	par->palette[regno].green  = green;
121762306a36Sopenharmony_ci	par->palette[regno].blue   = blue;
121862306a36Sopenharmony_ci	par->palette[regno].transp = transp;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
122162306a36Sopenharmony_ci	case 8:
122262306a36Sopenharmony_ci		vga_out8(0x3c8, regno, par);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		vga_out8(0x3c9, red   >> 10, par);
122562306a36Sopenharmony_ci		vga_out8(0x3c9, green >> 10, par);
122662306a36Sopenharmony_ci		vga_out8(0x3c9, blue  >> 10, par);
122762306a36Sopenharmony_ci		break;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	case 16:
123062306a36Sopenharmony_ci		if (regno < 16)
123162306a36Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] =
123262306a36Sopenharmony_ci				((red   & 0xf800)      ) |
123362306a36Sopenharmony_ci				((green & 0xfc00) >>  5) |
123462306a36Sopenharmony_ci				((blue  & 0xf800) >> 11);
123562306a36Sopenharmony_ci		break;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	case 24:
123862306a36Sopenharmony_ci		if (regno < 16)
123962306a36Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] =
124062306a36Sopenharmony_ci				((red    & 0xff00) <<  8) |
124162306a36Sopenharmony_ci				((green  & 0xff00)      ) |
124262306a36Sopenharmony_ci				((blue   & 0xff00) >>  8);
124362306a36Sopenharmony_ci		break;
124462306a36Sopenharmony_ci	case 32:
124562306a36Sopenharmony_ci		if (regno < 16)
124662306a36Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] =
124762306a36Sopenharmony_ci				((transp & 0xff00) << 16) |
124862306a36Sopenharmony_ci				((red    & 0xff00) <<  8) |
124962306a36Sopenharmony_ci				((green  & 0xff00)      ) |
125062306a36Sopenharmony_ci				((blue   & 0xff00) >>  8);
125162306a36Sopenharmony_ci		break;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	default:
125462306a36Sopenharmony_ci		return 1;
125562306a36Sopenharmony_ci	}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	return 0;
125862306a36Sopenharmony_ci}
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_cistatic void savagefb_set_par_int(struct savagefb_par  *par, struct savage_reg *reg)
126162306a36Sopenharmony_ci{
126262306a36Sopenharmony_ci	unsigned char tmp, cr3a, cr66, cr67;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	DBG("savagefb_set_par_int");
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	par->SavageWaitIdle(par);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	vga_out8(0x3c2, 0x23, par);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
127162306a36Sopenharmony_ci	vga_out16(0x3d4, 0xa539, par);
127262306a36Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	vgaHWProtect(par, 1);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	/*
127762306a36Sopenharmony_ci	 * Some Savage/MX and /IX systems go nuts when trying to exit the
127862306a36Sopenharmony_ci	 * server after WindowMaker has displayed a gradient background.  I
127962306a36Sopenharmony_ci	 * haven't been able to find what causes it, but a non-destructive
128062306a36Sopenharmony_ci	 * switch to mode 3 here seems to eliminate the issue.
128162306a36Sopenharmony_ci	 */
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	VerticalRetraceWait(par);
128462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
128562306a36Sopenharmony_ci	cr67 = vga_in8(0x3d5, par);
128662306a36Sopenharmony_ci	vga_out8(0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x23, par);
128962306a36Sopenharmony_ci	vga_out8(0x3d5, 0x00, par);
129062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x26, par);
129162306a36Sopenharmony_ci	vga_out8(0x3d5, 0x00, par);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* restore extended regs */
129462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
129562306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR66, par);
129662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
129762306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR3A, par);
129862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
129962306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR31, par);
130062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
130162306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR32, par);
130262306a36Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
130362306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR58, par);
130462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
130562306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR53 & 0x7f, par);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/* Restore DCLK registers. */
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
131262306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR0E, par);
131362306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
131462306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR0F, par);
131562306a36Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
131662306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR29, par);
131762306a36Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
131862306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR15, par);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	/* Restore flat panel expansion registers. */
132162306a36Sopenharmony_ci	if (par->chip == S3_SAVAGE_MX) {
132262306a36Sopenharmony_ci		int i;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci		for (i = 0; i < 8; i++) {
132562306a36Sopenharmony_ci			vga_out8(0x3c4, 0x54+i, par);
132662306a36Sopenharmony_ci			vga_out8(0x3c5, reg->SR54[i], par);
132762306a36Sopenharmony_ci		}
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	vgaHWRestore (par, reg);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/* extended mode timing registers */
133362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
133462306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR53, par);
133562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x5d, par);
133662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR5D, par);
133762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x5e, par);
133862306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR5E, par);
133962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3b, par);
134062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR3B, par);
134162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3c, par);
134262306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR3C, par);
134362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x43, par);
134462306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR43, par);
134562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x65, par);
134662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR65, par);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	/* restore the desired video mode with cr67 */
134962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
135062306a36Sopenharmony_ci	/* following part not present in X11 driver */
135162306a36Sopenharmony_ci	cr67 = vga_in8(0x3d5, par) & 0xf;
135262306a36Sopenharmony_ci	vga_out8(0x3d5, 0x50 | cr67, par);
135362306a36Sopenharmony_ci	mdelay(10);
135462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
135562306a36Sopenharmony_ci	/* end of part */
135662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR67 & ~0x0c, par);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	/* other mode timing and extended regs */
135962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x34, par);
136062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR34, par);
136162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
136262306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR40, par);
136362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x42, par);
136462306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR42, par);
136562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x45, par);
136662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR45, par);
136762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
136862306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR50, par);
136962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x51, par);
137062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR51, par);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	/* memory timings */
137362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
137462306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR36, par);
137562306a36Sopenharmony_ci	vga_out8(0x3d4, 0x60, par);
137662306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR60, par);
137762306a36Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
137862306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR68, par);
137962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
138062306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR69, par);
138162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
138262306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR6F, par);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x33, par);
138562306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR33, par);
138662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
138762306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR86, par);
138862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
138962306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR88, par);
139062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x90, par);
139162306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR90, par);
139262306a36Sopenharmony_ci	vga_out8(0x3d4, 0x91, par);
139362306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR91, par);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (par->chip == S3_SAVAGE4) {
139662306a36Sopenharmony_ci		vga_out8(0x3d4, 0xb0, par);
139762306a36Sopenharmony_ci		vga_out8(0x3d5, reg->CRB0, par);
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
140162306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR32, par);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* unlock extended seq regs */
140462306a36Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
140562306a36Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	/* Restore extended sequencer regs for MCLK. SR10 == 255 indicates
140862306a36Sopenharmony_ci	 * that we should leave the default SR10 and SR11 values there.
140962306a36Sopenharmony_ci	 */
141062306a36Sopenharmony_ci	if (reg->SR10 != 255) {
141162306a36Sopenharmony_ci		vga_out8(0x3c4, 0x10, par);
141262306a36Sopenharmony_ci		vga_out8(0x3c5, reg->SR10, par);
141362306a36Sopenharmony_ci		vga_out8(0x3c4, 0x11, par);
141462306a36Sopenharmony_ci		vga_out8(0x3c5, reg->SR11, par);
141562306a36Sopenharmony_ci	}
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	/* restore extended seq regs for dclk */
141862306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
141962306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR0E, par);
142062306a36Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
142162306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR0F, par);
142262306a36Sopenharmony_ci	vga_out8(0x3c4, 0x12, par);
142362306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR12, par);
142462306a36Sopenharmony_ci	vga_out8(0x3c4, 0x13, par);
142562306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR13, par);
142662306a36Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
142762306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR29, par);
142862306a36Sopenharmony_ci	vga_out8(0x3c4, 0x18, par);
142962306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR18, par);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	/* load new m, n pll values for dclk & mclk */
143262306a36Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
143362306a36Sopenharmony_ci	tmp = vga_in8(0x3c5, par) & ~0x21;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	vga_out8(0x3c5, tmp | 0x03, par);
143662306a36Sopenharmony_ci	vga_out8(0x3c5, tmp | 0x23, par);
143762306a36Sopenharmony_ci	vga_out8(0x3c5, tmp | 0x03, par);
143862306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR15, par);
143962306a36Sopenharmony_ci	udelay(100);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	vga_out8(0x3c4, 0x30, par);
144262306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR30, par);
144362306a36Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
144462306a36Sopenharmony_ci	vga_out8(0x3c5, reg->SR08, par);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	/* now write out cr67 in full, possibly starting STREAMS */
144762306a36Sopenharmony_ci	VerticalRetraceWait(par);
144862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
144962306a36Sopenharmony_ci	vga_out8(0x3d5, reg->CR67, par);
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
145262306a36Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
145362306a36Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
145462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
145562306a36Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
145662306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	if (par->chip != S3_SAVAGE_MX) {
145962306a36Sopenharmony_ci		VerticalRetraceWait(par);
146062306a36Sopenharmony_ci		savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
146162306a36Sopenharmony_ci		par->SavageWaitIdle(par);
146262306a36Sopenharmony_ci		savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
146362306a36Sopenharmony_ci		par->SavageWaitIdle(par);
146462306a36Sopenharmony_ci		savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
146562306a36Sopenharmony_ci		par->SavageWaitIdle(par);
146662306a36Sopenharmony_ci		savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
147062306a36Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
147162306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
147262306a36Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	SavageSetup2DEngine(par);
147562306a36Sopenharmony_ci	vgaHWProtect(par, 0);
147662306a36Sopenharmony_ci}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_cistatic void savagefb_update_start(struct savagefb_par *par, int base)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	/* program the start address registers */
148162306a36Sopenharmony_ci	vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par);
148262306a36Sopenharmony_ci	vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par);
148362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
148462306a36Sopenharmony_ci	vga_out8(0x3d5, (base & 0x7f0000) >> 16, par);
148562306a36Sopenharmony_ci}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cistatic void savagefb_set_fix(struct fb_info *info)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	info->fix.line_length = info->var.xres_virtual *
149162306a36Sopenharmony_ci		info->var.bits_per_pixel / 8;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	if (info->var.bits_per_pixel == 8) {
149462306a36Sopenharmony_ci		info->fix.visual      = FB_VISUAL_PSEUDOCOLOR;
149562306a36Sopenharmony_ci		info->fix.xpanstep    = 4;
149662306a36Sopenharmony_ci	} else {
149762306a36Sopenharmony_ci		info->fix.visual      = FB_VISUAL_TRUECOLOR;
149862306a36Sopenharmony_ci		info->fix.xpanstep    = 2;
149962306a36Sopenharmony_ci	}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic int savagefb_set_par(struct fb_info *info)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
150662306a36Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
150762306a36Sopenharmony_ci	int err;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	DBG("savagefb_set_par");
151062306a36Sopenharmony_ci	err = savagefb_decode_var(var, par, &par->state);
151162306a36Sopenharmony_ci	if (err)
151262306a36Sopenharmony_ci		return err;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	if (par->dacSpeedBpp <= 0) {
151562306a36Sopenharmony_ci		if (var->bits_per_pixel > 24)
151662306a36Sopenharmony_ci			par->dacSpeedBpp = par->clock[3];
151762306a36Sopenharmony_ci		else if (var->bits_per_pixel >= 24)
151862306a36Sopenharmony_ci			par->dacSpeedBpp = par->clock[2];
151962306a36Sopenharmony_ci		else if ((var->bits_per_pixel > 8) && (var->bits_per_pixel < 24))
152062306a36Sopenharmony_ci			par->dacSpeedBpp = par->clock[1];
152162306a36Sopenharmony_ci		else if (var->bits_per_pixel <= 8)
152262306a36Sopenharmony_ci			par->dacSpeedBpp = par->clock[0];
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	/* Set ramdac limits */
152662306a36Sopenharmony_ci	par->maxClock = par->dacSpeedBpp;
152762306a36Sopenharmony_ci	par->minClock = 10000;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	savagefb_set_par_int(par, &par->state);
153062306a36Sopenharmony_ci	fb_set_cmap(&info->cmap, info);
153162306a36Sopenharmony_ci	savagefb_set_fix(info);
153262306a36Sopenharmony_ci	savagefb_set_clip(info);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	SavagePrintRegs(par);
153562306a36Sopenharmony_ci	return 0;
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci/*
153962306a36Sopenharmony_ci *    Pan or Wrap the Display
154062306a36Sopenharmony_ci */
154162306a36Sopenharmony_cistatic int savagefb_pan_display(struct fb_var_screeninfo *var,
154262306a36Sopenharmony_ci				struct fb_info           *info)
154362306a36Sopenharmony_ci{
154462306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
154562306a36Sopenharmony_ci	int base;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	base = (var->yoffset * info->fix.line_length
154862306a36Sopenharmony_ci	     + (var->xoffset & ~1) * ((info->var.bits_per_pixel+7) / 8)) >> 2;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	savagefb_update_start(par, base);
155162306a36Sopenharmony_ci	return 0;
155262306a36Sopenharmony_ci}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cistatic int savagefb_blank(int blank, struct fb_info *info)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
155762306a36Sopenharmony_ci	u8 sr8 = 0, srd = 0;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	if (par->display_type == DISP_CRT) {
156062306a36Sopenharmony_ci		vga_out8(0x3c4, 0x08, par);
156162306a36Sopenharmony_ci		sr8 = vga_in8(0x3c5, par);
156262306a36Sopenharmony_ci		sr8 |= 0x06;
156362306a36Sopenharmony_ci		vga_out8(0x3c5, sr8, par);
156462306a36Sopenharmony_ci		vga_out8(0x3c4, 0x0d, par);
156562306a36Sopenharmony_ci		srd = vga_in8(0x3c5, par);
156662306a36Sopenharmony_ci		srd &= 0x50;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci		switch (blank) {
156962306a36Sopenharmony_ci		case FB_BLANK_UNBLANK:
157062306a36Sopenharmony_ci		case FB_BLANK_NORMAL:
157162306a36Sopenharmony_ci			break;
157262306a36Sopenharmony_ci		case FB_BLANK_VSYNC_SUSPEND:
157362306a36Sopenharmony_ci			srd |= 0x10;
157462306a36Sopenharmony_ci			break;
157562306a36Sopenharmony_ci		case FB_BLANK_HSYNC_SUSPEND:
157662306a36Sopenharmony_ci			srd |= 0x40;
157762306a36Sopenharmony_ci			break;
157862306a36Sopenharmony_ci		case FB_BLANK_POWERDOWN:
157962306a36Sopenharmony_ci			srd |= 0x50;
158062306a36Sopenharmony_ci			break;
158162306a36Sopenharmony_ci		}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci		vga_out8(0x3c4, 0x0d, par);
158462306a36Sopenharmony_ci		vga_out8(0x3c5, srd, par);
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (par->display_type == DISP_LCD ||
158862306a36Sopenharmony_ci	    par->display_type == DISP_DFP) {
158962306a36Sopenharmony_ci		switch(blank) {
159062306a36Sopenharmony_ci		case FB_BLANK_UNBLANK:
159162306a36Sopenharmony_ci		case FB_BLANK_NORMAL:
159262306a36Sopenharmony_ci			vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
159362306a36Sopenharmony_ci			vga_out8(0x3c5, vga_in8(0x3c5, par) | 0x10, par);
159462306a36Sopenharmony_ci			break;
159562306a36Sopenharmony_ci		case FB_BLANK_VSYNC_SUSPEND:
159662306a36Sopenharmony_ci		case FB_BLANK_HSYNC_SUSPEND:
159762306a36Sopenharmony_ci		case FB_BLANK_POWERDOWN:
159862306a36Sopenharmony_ci			vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
159962306a36Sopenharmony_ci			vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x10, par);
160062306a36Sopenharmony_ci			break;
160162306a36Sopenharmony_ci		}
160262306a36Sopenharmony_ci	}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	return (blank == FB_BLANK_NORMAL) ? 1 : 0;
160562306a36Sopenharmony_ci}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_cistatic int savagefb_open(struct fb_info *info, int user)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	mutex_lock(&par->open_lock);
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	if (!par->open_count) {
161462306a36Sopenharmony_ci		memset(&par->vgastate, 0, sizeof(par->vgastate));
161562306a36Sopenharmony_ci		par->vgastate.flags = VGA_SAVE_CMAP | VGA_SAVE_FONTS |
161662306a36Sopenharmony_ci			VGA_SAVE_MODE;
161762306a36Sopenharmony_ci		par->vgastate.vgabase = par->mmio.vbase + 0x8000;
161862306a36Sopenharmony_ci		save_vga(&par->vgastate);
161962306a36Sopenharmony_ci		savage_get_default_par(par, &par->initial);
162062306a36Sopenharmony_ci	}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	par->open_count++;
162362306a36Sopenharmony_ci	mutex_unlock(&par->open_lock);
162462306a36Sopenharmony_ci	return 0;
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_cistatic int savagefb_release(struct fb_info *info, int user)
162862306a36Sopenharmony_ci{
162962306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	mutex_lock(&par->open_lock);
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (par->open_count == 1) {
163462306a36Sopenharmony_ci		savage_set_default_par(par, &par->initial);
163562306a36Sopenharmony_ci		restore_vga(&par->vgastate);
163662306a36Sopenharmony_ci	}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	par->open_count--;
163962306a36Sopenharmony_ci	mutex_unlock(&par->open_lock);
164062306a36Sopenharmony_ci	return 0;
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic const struct fb_ops savagefb_ops = {
164462306a36Sopenharmony_ci	.owner          = THIS_MODULE,
164562306a36Sopenharmony_ci	.fb_open        = savagefb_open,
164662306a36Sopenharmony_ci	.fb_release     = savagefb_release,
164762306a36Sopenharmony_ci	.fb_check_var   = savagefb_check_var,
164862306a36Sopenharmony_ci	.fb_set_par     = savagefb_set_par,
164962306a36Sopenharmony_ci	.fb_setcolreg   = savagefb_setcolreg,
165062306a36Sopenharmony_ci	.fb_pan_display = savagefb_pan_display,
165162306a36Sopenharmony_ci	.fb_blank       = savagefb_blank,
165262306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL)
165362306a36Sopenharmony_ci	.fb_fillrect    = savagefb_fillrect,
165462306a36Sopenharmony_ci	.fb_copyarea    = savagefb_copyarea,
165562306a36Sopenharmony_ci	.fb_imageblit   = savagefb_imageblit,
165662306a36Sopenharmony_ci	.fb_sync        = savagefb_sync,
165762306a36Sopenharmony_ci#else
165862306a36Sopenharmony_ci	.fb_fillrect    = cfb_fillrect,
165962306a36Sopenharmony_ci	.fb_copyarea    = cfb_copyarea,
166062306a36Sopenharmony_ci	.fb_imageblit   = cfb_imageblit,
166162306a36Sopenharmony_ci#endif
166262306a36Sopenharmony_ci};
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic const struct fb_var_screeninfo savagefb_var800x600x8 = {
166762306a36Sopenharmony_ci	.accel_flags =	FB_ACCELF_TEXT,
166862306a36Sopenharmony_ci	.xres =		800,
166962306a36Sopenharmony_ci	.yres =		600,
167062306a36Sopenharmony_ci	.xres_virtual =  800,
167162306a36Sopenharmony_ci	.yres_virtual =  600,
167262306a36Sopenharmony_ci	.bits_per_pixel = 8,
167362306a36Sopenharmony_ci	.pixclock =	25000,
167462306a36Sopenharmony_ci	.left_margin =	88,
167562306a36Sopenharmony_ci	.right_margin =	40,
167662306a36Sopenharmony_ci	.upper_margin =	23,
167762306a36Sopenharmony_ci	.lower_margin =	1,
167862306a36Sopenharmony_ci	.hsync_len =	128,
167962306a36Sopenharmony_ci	.vsync_len =	4,
168062306a36Sopenharmony_ci	.sync =		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
168162306a36Sopenharmony_ci	.vmode =	FB_VMODE_NONINTERLACED
168262306a36Sopenharmony_ci};
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_cistatic void savage_enable_mmio(struct savagefb_par *par)
168562306a36Sopenharmony_ci{
168662306a36Sopenharmony_ci	unsigned char val;
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	DBG("savage_enable_mmio\n");
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	val = vga_in8(0x3c3, par);
169162306a36Sopenharmony_ci	vga_out8(0x3c3, val | 0x01, par);
169262306a36Sopenharmony_ci	val = vga_in8(0x3cc, par);
169362306a36Sopenharmony_ci	vga_out8(0x3c2, val | 0x01, par);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	if (par->chip >= S3_SAVAGE4) {
169662306a36Sopenharmony_ci		vga_out8(0x3d4, 0x40, par);
169762306a36Sopenharmony_ci		val = vga_in8(0x3d5, par);
169862306a36Sopenharmony_ci		vga_out8(0x3d5, val | 1, par);
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_cistatic void savage_disable_mmio(struct savagefb_par *par)
170462306a36Sopenharmony_ci{
170562306a36Sopenharmony_ci	unsigned char val;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	DBG("savage_disable_mmio\n");
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	if (par->chip >= S3_SAVAGE4) {
171062306a36Sopenharmony_ci		vga_out8(0x3d4, 0x40, par);
171162306a36Sopenharmony_ci		val = vga_in8(0x3d5, par);
171262306a36Sopenharmony_ci		vga_out8(0x3d5, val | 1, par);
171362306a36Sopenharmony_ci	}
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_cistatic int savage_map_mmio(struct fb_info *info)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
172062306a36Sopenharmony_ci	DBG("savage_map_mmio");
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	if (S3_SAVAGE3D_SERIES(par->chip))
172362306a36Sopenharmony_ci		par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
172462306a36Sopenharmony_ci			SAVAGE_NEWMMIO_REGBASE_S3;
172562306a36Sopenharmony_ci	else
172662306a36Sopenharmony_ci		par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
172762306a36Sopenharmony_ci			SAVAGE_NEWMMIO_REGBASE_S4;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	par->mmio.len = SAVAGE_NEWMMIO_REGSIZE;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	par->mmio.vbase = ioremap(par->mmio.pbase, par->mmio.len);
173262306a36Sopenharmony_ci	if (!par->mmio.vbase) {
173362306a36Sopenharmony_ci		printk("savagefb: unable to map memory mapped IO\n");
173462306a36Sopenharmony_ci		return -ENOMEM;
173562306a36Sopenharmony_ci	} else
173662306a36Sopenharmony_ci		printk(KERN_INFO "savagefb: mapped io at %p\n",
173762306a36Sopenharmony_ci			par->mmio.vbase);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	info->fix.mmio_start = par->mmio.pbase;
174062306a36Sopenharmony_ci	info->fix.mmio_len   = par->mmio.len;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET);
174362306a36Sopenharmony_ci	par->bci_ptr  = 0;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	savage_enable_mmio(par);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	return 0;
174862306a36Sopenharmony_ci}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_cistatic void savage_unmap_mmio(struct fb_info *info)
175162306a36Sopenharmony_ci{
175262306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
175362306a36Sopenharmony_ci	DBG("savage_unmap_mmio");
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	savage_disable_mmio(par);
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (par->mmio.vbase) {
175862306a36Sopenharmony_ci		iounmap(par->mmio.vbase);
175962306a36Sopenharmony_ci		par->mmio.vbase = NULL;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_cistatic int savage_map_video(struct fb_info *info, int video_len)
176462306a36Sopenharmony_ci{
176562306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
176662306a36Sopenharmony_ci	int resource;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	DBG("savage_map_video");
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	if (S3_SAVAGE3D_SERIES(par->chip))
177162306a36Sopenharmony_ci		resource = 0;
177262306a36Sopenharmony_ci	else
177362306a36Sopenharmony_ci		resource = 1;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	par->video.pbase = pci_resource_start(par->pcidev, resource);
177662306a36Sopenharmony_ci	par->video.len   = video_len;
177762306a36Sopenharmony_ci	par->video.vbase = ioremap_wc(par->video.pbase, par->video.len);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	if (!par->video.vbase) {
178062306a36Sopenharmony_ci		printk("savagefb: unable to map screen memory\n");
178162306a36Sopenharmony_ci		return -ENOMEM;
178262306a36Sopenharmony_ci	} else
178362306a36Sopenharmony_ci		printk(KERN_INFO "savagefb: mapped framebuffer at %p, "
178462306a36Sopenharmony_ci		       "pbase == %x\n", par->video.vbase, par->video.pbase);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	info->fix.smem_start = par->video.pbase;
178762306a36Sopenharmony_ci	info->fix.smem_len   = par->video.len - par->cob_size;
178862306a36Sopenharmony_ci	info->screen_base    = par->video.vbase;
178962306a36Sopenharmony_ci	par->video.wc_cookie = arch_phys_wc_add(par->video.pbase, video_len);
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	/* Clear framebuffer, it's all white in memory after boot */
179262306a36Sopenharmony_ci	memset_io(par->video.vbase, 0, par->video.len);
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	return 0;
179562306a36Sopenharmony_ci}
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_cistatic void savage_unmap_video(struct fb_info *info)
179862306a36Sopenharmony_ci{
179962306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	DBG("savage_unmap_video");
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci	if (par->video.vbase) {
180462306a36Sopenharmony_ci		arch_phys_wc_del(par->video.wc_cookie);
180562306a36Sopenharmony_ci		iounmap(par->video.vbase);
180662306a36Sopenharmony_ci		par->video.vbase = NULL;
180762306a36Sopenharmony_ci		info->screen_base = NULL;
180862306a36Sopenharmony_ci	}
180962306a36Sopenharmony_ci}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_cistatic int savage_init_hw(struct savagefb_par *par)
181262306a36Sopenharmony_ci{
181362306a36Sopenharmony_ci	unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	static unsigned char RamSavage3D[] = { 8, 4, 4, 2 };
181662306a36Sopenharmony_ci	static unsigned char RamSavage4[] =  { 2, 4, 8, 12, 16, 32, 64, 32 };
181762306a36Sopenharmony_ci	static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
181862306a36Sopenharmony_ci	static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
181962306a36Sopenharmony_ci	int videoRam, videoRambytes, dvi;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	DBG("savage_init_hw");
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	/* unprotect CRTC[0-7] */
182462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x11, par);
182562306a36Sopenharmony_ci	tmp = vga_in8(0x3d5, par);
182662306a36Sopenharmony_ci	vga_out8(0x3d5, tmp & 0x7f, par);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	/* unlock extended regs */
182962306a36Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
183062306a36Sopenharmony_ci	vga_out16(0x3d4, 0xa039, par);
183162306a36Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
183462306a36Sopenharmony_ci	tmp = vga_in8(0x3d5, par);
183562306a36Sopenharmony_ci	vga_out8(0x3d5, tmp & ~0x01, par);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	/* unlock sys regs */
183862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x38, par);
183962306a36Sopenharmony_ci	vga_out8(0x3d5, 0x48, par);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	/* Unlock system registers. */
184262306a36Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	/* Next go on to detect amount of installed ram */
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);            /* for register CR36 (CONFG_REG1), */
184762306a36Sopenharmony_ci	config1 = vga_in8(0x3d5, par);    /* get amount of vram installed */
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	/* Compute the amount of video memory and offscreen memory. */
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	switch  (par->chip) {
185262306a36Sopenharmony_ci	case S3_SAVAGE3D:
185362306a36Sopenharmony_ci		videoRam = RamSavage3D[(config1 & 0xC0) >> 6 ] * 1024;
185462306a36Sopenharmony_ci		break;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	case S3_SAVAGE4:
185762306a36Sopenharmony_ci		/*
185862306a36Sopenharmony_ci		 * The Savage4 has one ugly special case to consider.  On
185962306a36Sopenharmony_ci		 * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB
186062306a36Sopenharmony_ci		 * when it really means 8MB.  Why do it the same when you
186162306a36Sopenharmony_ci		 * can do it different...
186262306a36Sopenharmony_ci		 */
186362306a36Sopenharmony_ci		vga_out8(0x3d4, 0x68, par);	/* memory control 1 */
186462306a36Sopenharmony_ci		if ((vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6))
186562306a36Sopenharmony_ci			RamSavage4[1] = 8;
186662306a36Sopenharmony_ci		fallthrough;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	case S3_SAVAGE2000:
186962306a36Sopenharmony_ci		videoRam = RamSavage4[(config1 & 0xE0) >> 5] * 1024;
187062306a36Sopenharmony_ci		break;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	case S3_SAVAGE_MX:
187362306a36Sopenharmony_ci	case S3_SUPERSAVAGE:
187462306a36Sopenharmony_ci		videoRam = RamSavageMX[(config1 & 0x0E) >> 1] * 1024;
187562306a36Sopenharmony_ci		break;
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	case S3_PROSAVAGE:
187862306a36Sopenharmony_ci	case S3_PROSAVAGEDDR:
187962306a36Sopenharmony_ci	case S3_TWISTER:
188062306a36Sopenharmony_ci		videoRam = RamSavageNB[(config1 & 0xE0) >> 5] * 1024;
188162306a36Sopenharmony_ci		break;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	default:
188462306a36Sopenharmony_ci		/* How did we get here? */
188562306a36Sopenharmony_ci		videoRam = 0;
188662306a36Sopenharmony_ci		break;
188762306a36Sopenharmony_ci	}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	videoRambytes = videoRam * 1024;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	printk(KERN_INFO "savagefb: probed videoram:  %dk\n", videoRam);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	/* reset graphics engine to avoid memory corruption */
189462306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
189562306a36Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
189662306a36Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x02, par);
189762306a36Sopenharmony_ci	usleep_range(10000, 11000);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
190062306a36Sopenharmony_ci	vga_out8(0x3d5, cr66 & ~0x02, par);	/* clear reset flag */
190162306a36Sopenharmony_ci	usleep_range(10000, 11000);
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci	/*
190562306a36Sopenharmony_ci	 * reset memory interface, 3D engine, AGP master, PCI master,
190662306a36Sopenharmony_ci	 * master engine unit, motion compensation/LPB
190762306a36Sopenharmony_ci	 */
190862306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3f, par);
190962306a36Sopenharmony_ci	cr3f = vga_in8(0x3d5, par);
191062306a36Sopenharmony_ci	vga_out8(0x3d5, cr3f | 0x08, par);
191162306a36Sopenharmony_ci	usleep_range(10000, 11000);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	vga_out8(0x3d4, 0x3f, par);
191462306a36Sopenharmony_ci	vga_out8(0x3d5, cr3f & ~0x08, par);	/* clear reset flags */
191562306a36Sopenharmony_ci	usleep_range(10000, 11000);
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	/* Savage ramdac speeds */
191862306a36Sopenharmony_ci	par->numClocks = 4;
191962306a36Sopenharmony_ci	par->clock[0] = 250000;
192062306a36Sopenharmony_ci	par->clock[1] = 250000;
192162306a36Sopenharmony_ci	par->clock[2] = 220000;
192262306a36Sopenharmony_ci	par->clock[3] = 220000;
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	/* detect current mclk */
192562306a36Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
192662306a36Sopenharmony_ci	sr8 = vga_in8(0x3c5, par);
192762306a36Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
192862306a36Sopenharmony_ci	vga_out8(0x3c4, 0x10, par);
192962306a36Sopenharmony_ci	n = vga_in8(0x3c5, par);
193062306a36Sopenharmony_ci	vga_out8(0x3c4, 0x11, par);
193162306a36Sopenharmony_ci	m = vga_in8(0x3c5, par);
193262306a36Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
193362306a36Sopenharmony_ci	vga_out8(0x3c5, sr8, par);
193462306a36Sopenharmony_ci	m &= 0x7f;
193562306a36Sopenharmony_ci	n1 = n & 0x1f;
193662306a36Sopenharmony_ci	n2 = (n >> 5) & 0x03;
193762306a36Sopenharmony_ci	par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
193862306a36Sopenharmony_ci	printk(KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
193962306a36Sopenharmony_ci		par->MCLK);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	/* check for DVI/flat panel */
194262306a36Sopenharmony_ci	dvi = 0;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	if (par->chip == S3_SAVAGE4) {
194562306a36Sopenharmony_ci		unsigned char sr30 = 0x00;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci		vga_out8(0x3c4, 0x30, par);
194862306a36Sopenharmony_ci		/* clear bit 1 */
194962306a36Sopenharmony_ci		vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x02, par);
195062306a36Sopenharmony_ci		sr30 = vga_in8(0x3c5, par);
195162306a36Sopenharmony_ci		if (sr30 & 0x02 /*0x04 */) {
195262306a36Sopenharmony_ci			dvi = 1;
195362306a36Sopenharmony_ci			printk("savagefb: Digital Flat Panel Detected\n");
195462306a36Sopenharmony_ci		}
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	if ((S3_SAVAGE_MOBILE_SERIES(par->chip) ||
195862306a36Sopenharmony_ci	     S3_MOBILE_TWISTER_SERIES(par->chip)) && !par->crtonly)
195962306a36Sopenharmony_ci		par->display_type = DISP_LCD;
196062306a36Sopenharmony_ci	else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi))
196162306a36Sopenharmony_ci		par->display_type = DISP_DFP;
196262306a36Sopenharmony_ci	else
196362306a36Sopenharmony_ci		par->display_type = DISP_CRT;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	/* Check LCD panel parrmation */
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	if (par->display_type == DISP_LCD) {
196862306a36Sopenharmony_ci		unsigned char cr6b = VGArCR(0x6b, par);
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci		int panelX = (VGArSEQ(0x61, par) +
197162306a36Sopenharmony_ci			      ((VGArSEQ(0x66, par) & 0x02) << 7) + 1) * 8;
197262306a36Sopenharmony_ci		int panelY = (VGArSEQ(0x69, par) +
197362306a36Sopenharmony_ci			      ((VGArSEQ(0x6e, par) & 0x70) << 4) + 1);
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci		char * sTechnology = "Unknown";
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci		/* OK, I admit it.  I don't know how to limit the max dot clock
197862306a36Sopenharmony_ci		 * for LCD panels of various sizes.  I thought I copied the
197962306a36Sopenharmony_ci		 * formula from the BIOS, but many users have parrmed me of
198062306a36Sopenharmony_ci		 * my folly.
198162306a36Sopenharmony_ci		 *
198262306a36Sopenharmony_ci		 * Instead, I'll abandon any attempt to automatically limit the
198362306a36Sopenharmony_ci		 * clock, and add an LCDClock option to XF86Config.  Some day,
198462306a36Sopenharmony_ci		 * I should come back to this.
198562306a36Sopenharmony_ci		 */
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci		enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */
198862306a36Sopenharmony_ci			ActiveCRT = 0x01,
198962306a36Sopenharmony_ci			ActiveLCD = 0x02,
199062306a36Sopenharmony_ci			ActiveTV = 0x04,
199162306a36Sopenharmony_ci			ActiveCRT2 = 0x20,
199262306a36Sopenharmony_ci			ActiveDUO = 0x80
199362306a36Sopenharmony_ci		};
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci		if ((VGArSEQ(0x39, par) & 0x03) == 0) {
199662306a36Sopenharmony_ci			sTechnology = "TFT";
199762306a36Sopenharmony_ci		} else if ((VGArSEQ(0x30, par) & 0x01) == 0) {
199862306a36Sopenharmony_ci			sTechnology = "DSTN";
199962306a36Sopenharmony_ci		} else 	{
200062306a36Sopenharmony_ci			sTechnology = "STN";
200162306a36Sopenharmony_ci		}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci		printk(KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n",
200462306a36Sopenharmony_ci		       panelX, panelY, sTechnology,
200562306a36Sopenharmony_ci		       cr6b & ActiveLCD ? "and active" : "but not active");
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci		if (cr6b & ActiveLCD) 	{
200862306a36Sopenharmony_ci			/*
200962306a36Sopenharmony_ci			 * If the LCD is active and panel expansion is enabled,
201062306a36Sopenharmony_ci			 * we probably want to kill the HW cursor.
201162306a36Sopenharmony_ci			 */
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci			printk(KERN_INFO "savagefb: Limiting video mode to "
201462306a36Sopenharmony_ci				"%dx%d\n", panelX, panelY);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci			par->SavagePanelWidth = panelX;
201762306a36Sopenharmony_ci			par->SavagePanelHeight = panelY;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci		} else
202062306a36Sopenharmony_ci			par->display_type = DISP_CRT;
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	savage_get_default_par(par, &par->state);
202462306a36Sopenharmony_ci	par->save = par->state;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	if (S3_SAVAGE4_SERIES(par->chip)) {
202762306a36Sopenharmony_ci		/*
202862306a36Sopenharmony_ci		 * The Savage4 and ProSavage have COB coherency bugs which
202962306a36Sopenharmony_ci		 * render the buffer useless.  We disable it.
203062306a36Sopenharmony_ci		 */
203162306a36Sopenharmony_ci		par->cob_index = 2;
203262306a36Sopenharmony_ci		par->cob_size = 0x8000 << par->cob_index;
203362306a36Sopenharmony_ci		par->cob_offset = videoRambytes;
203462306a36Sopenharmony_ci	} else {
203562306a36Sopenharmony_ci		/* We use 128kB for the COB on all chips. */
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci		par->cob_index  = 7;
203862306a36Sopenharmony_ci		par->cob_size   = 0x400 << par->cob_index;
203962306a36Sopenharmony_ci		par->cob_offset = videoRambytes - par->cob_size;
204062306a36Sopenharmony_ci	}
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	return videoRambytes;
204362306a36Sopenharmony_ci}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_cistatic int savage_init_fb_info(struct fb_info *info, struct pci_dev *dev,
204662306a36Sopenharmony_ci			       const struct pci_device_id *id)
204762306a36Sopenharmony_ci{
204862306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
204962306a36Sopenharmony_ci	int err = 0;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	par->pcidev  = dev;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	info->fix.type	   = FB_TYPE_PACKED_PIXELS;
205462306a36Sopenharmony_ci	info->fix.type_aux	   = 0;
205562306a36Sopenharmony_ci	info->fix.ypanstep	   = 1;
205662306a36Sopenharmony_ci	info->fix.ywrapstep   = 0;
205762306a36Sopenharmony_ci	info->fix.accel       = id->driver_data;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	switch (info->fix.accel) {
206062306a36Sopenharmony_ci	case FB_ACCEL_SUPERSAVAGE:
206162306a36Sopenharmony_ci		par->chip = S3_SUPERSAVAGE;
206262306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "SuperSavage");
206362306a36Sopenharmony_ci		break;
206462306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE4:
206562306a36Sopenharmony_ci		par->chip = S3_SAVAGE4;
206662306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage4");
206762306a36Sopenharmony_ci		break;
206862306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE3D:
206962306a36Sopenharmony_ci		par->chip = S3_SAVAGE3D;
207062306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage3D");
207162306a36Sopenharmony_ci		break;
207262306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE3D_MV:
207362306a36Sopenharmony_ci		par->chip = S3_SAVAGE3D;
207462306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage3D-MV");
207562306a36Sopenharmony_ci		break;
207662306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE2000:
207762306a36Sopenharmony_ci		par->chip = S3_SAVAGE2000;
207862306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage2000");
207962306a36Sopenharmony_ci		break;
208062306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE_MX_MV:
208162306a36Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
208262306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/MX-MV");
208362306a36Sopenharmony_ci		break;
208462306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE_MX:
208562306a36Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
208662306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/MX");
208762306a36Sopenharmony_ci		break;
208862306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE_IX_MV:
208962306a36Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
209062306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/IX-MV");
209162306a36Sopenharmony_ci		break;
209262306a36Sopenharmony_ci	case FB_ACCEL_SAVAGE_IX:
209362306a36Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
209462306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/IX");
209562306a36Sopenharmony_ci		break;
209662306a36Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_PM:
209762306a36Sopenharmony_ci		par->chip = S3_PROSAVAGE;
209862306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavagePM");
209962306a36Sopenharmony_ci		break;
210062306a36Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_KM:
210162306a36Sopenharmony_ci		par->chip = S3_PROSAVAGE;
210262306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavageKM");
210362306a36Sopenharmony_ci		break;
210462306a36Sopenharmony_ci	case FB_ACCEL_S3TWISTER_P:
210562306a36Sopenharmony_ci		par->chip = S3_TWISTER;
210662306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "TwisterP");
210762306a36Sopenharmony_ci		break;
210862306a36Sopenharmony_ci	case FB_ACCEL_S3TWISTER_K:
210962306a36Sopenharmony_ci		par->chip = S3_TWISTER;
211062306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "TwisterK");
211162306a36Sopenharmony_ci		break;
211262306a36Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_DDR:
211362306a36Sopenharmony_ci		par->chip = S3_PROSAVAGEDDR;
211462306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavageDDR");
211562306a36Sopenharmony_ci		break;
211662306a36Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_DDRK:
211762306a36Sopenharmony_ci		par->chip = S3_PROSAVAGEDDR;
211862306a36Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavage8");
211962306a36Sopenharmony_ci		break;
212062306a36Sopenharmony_ci	}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	if (S3_SAVAGE3D_SERIES(par->chip)) {
212362306a36Sopenharmony_ci		par->SavageWaitIdle = savage3D_waitidle;
212462306a36Sopenharmony_ci		par->SavageWaitFifo = savage3D_waitfifo;
212562306a36Sopenharmony_ci	} else if (S3_SAVAGE4_SERIES(par->chip) ||
212662306a36Sopenharmony_ci		   S3_SUPERSAVAGE == par->chip) {
212762306a36Sopenharmony_ci		par->SavageWaitIdle = savage4_waitidle;
212862306a36Sopenharmony_ci		par->SavageWaitFifo = savage4_waitfifo;
212962306a36Sopenharmony_ci	} else {
213062306a36Sopenharmony_ci		par->SavageWaitIdle = savage2000_waitidle;
213162306a36Sopenharmony_ci		par->SavageWaitFifo = savage2000_waitfifo;
213262306a36Sopenharmony_ci	}
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	info->var.nonstd      = 0;
213562306a36Sopenharmony_ci	info->var.activate    = FB_ACTIVATE_NOW;
213662306a36Sopenharmony_ci	info->var.width       = -1;
213762306a36Sopenharmony_ci	info->var.height      = -1;
213862306a36Sopenharmony_ci	info->var.accel_flags = 0;
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci	info->fbops          = &savagefb_ops;
214162306a36Sopenharmony_ci	info->flags          = FBINFO_HWACCEL_YPAN |
214262306a36Sopenharmony_ci		               FBINFO_HWACCEL_XPAN;
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	info->pseudo_palette = par->pseudo_palette;
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL)
214762306a36Sopenharmony_ci	/* FIFO size + padding for commands */
214862306a36Sopenharmony_ci	info->pixmap.addr = kcalloc(8, 1024, GFP_KERNEL);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	err = -ENOMEM;
215162306a36Sopenharmony_ci	if (info->pixmap.addr) {
215262306a36Sopenharmony_ci		info->pixmap.size = 8*1024;
215362306a36Sopenharmony_ci		info->pixmap.scan_align = 4;
215462306a36Sopenharmony_ci		info->pixmap.buf_align = 4;
215562306a36Sopenharmony_ci		info->pixmap.access_align = 32;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci		err = fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
215862306a36Sopenharmony_ci		if (!err)
215962306a36Sopenharmony_ci			info->flags |= FBINFO_HWACCEL_COPYAREA |
216062306a36Sopenharmony_ci				       FBINFO_HWACCEL_FILLRECT |
216162306a36Sopenharmony_ci				       FBINFO_HWACCEL_IMAGEBLIT;
216262306a36Sopenharmony_ci		else
216362306a36Sopenharmony_ci			kfree(info->pixmap.addr);
216462306a36Sopenharmony_ci	}
216562306a36Sopenharmony_ci#endif
216662306a36Sopenharmony_ci	return err;
216762306a36Sopenharmony_ci}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_cistatic int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id)
217262306a36Sopenharmony_ci{
217362306a36Sopenharmony_ci	struct fb_info *info;
217462306a36Sopenharmony_ci	struct savagefb_par *par;
217562306a36Sopenharmony_ci	u_int h_sync, v_sync;
217662306a36Sopenharmony_ci	unsigned char __maybe_unused *edid;
217762306a36Sopenharmony_ci	int err, lpitch;
217862306a36Sopenharmony_ci	int video_len;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	DBG("savagefb_probe");
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	err = aperture_remove_conflicting_pci_devices(dev, "savagefb");
218362306a36Sopenharmony_ci	if (err)
218462306a36Sopenharmony_ci		return err;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev);
218762306a36Sopenharmony_ci	if (!info)
218862306a36Sopenharmony_ci		return -ENOMEM;
218962306a36Sopenharmony_ci	par = info->par;
219062306a36Sopenharmony_ci	mutex_init(&par->open_lock);
219162306a36Sopenharmony_ci	err = pci_enable_device(dev);
219262306a36Sopenharmony_ci	if (err)
219362306a36Sopenharmony_ci		goto failed_enable;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	if ((err = pci_request_regions(dev, "savagefb"))) {
219662306a36Sopenharmony_ci		printk(KERN_ERR "cannot request PCI regions\n");
219762306a36Sopenharmony_ci		goto failed_enable;
219862306a36Sopenharmony_ci	}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	err = -ENOMEM;
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	if ((err = savage_init_fb_info(info, dev, id)))
220362306a36Sopenharmony_ci		goto failed_init;
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	err = savage_map_mmio(info);
220662306a36Sopenharmony_ci	if (err)
220762306a36Sopenharmony_ci		goto failed_mmio;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	video_len = savage_init_hw(par);
221062306a36Sopenharmony_ci	/* FIXME: can't be negative */
221162306a36Sopenharmony_ci	if (video_len < 0) {
221262306a36Sopenharmony_ci		err = video_len;
221362306a36Sopenharmony_ci		goto failed_mmio;
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	err = savage_map_video(info, video_len);
221762306a36Sopenharmony_ci	if (err)
221862306a36Sopenharmony_ci		goto failed_video;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	INIT_LIST_HEAD(&info->modelist);
222162306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_I2C)
222262306a36Sopenharmony_ci	savagefb_create_i2c_busses(info);
222362306a36Sopenharmony_ci	savagefb_probe_i2c_connector(info, &edid);
222462306a36Sopenharmony_ci	fb_edid_to_monspecs(edid, &info->monspecs);
222562306a36Sopenharmony_ci	kfree(edid);
222662306a36Sopenharmony_ci	fb_videomode_to_modelist(info->monspecs.modedb,
222762306a36Sopenharmony_ci				 info->monspecs.modedb_len,
222862306a36Sopenharmony_ci				 &info->modelist);
222962306a36Sopenharmony_ci#endif
223062306a36Sopenharmony_ci	info->var = savagefb_var800x600x8;
223162306a36Sopenharmony_ci	/* if a panel was detected, default to a CVT mode instead */
223262306a36Sopenharmony_ci	if (par->SavagePanelWidth) {
223362306a36Sopenharmony_ci		struct fb_videomode cvt_mode;
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci		memset(&cvt_mode, 0, sizeof(cvt_mode));
223662306a36Sopenharmony_ci		cvt_mode.xres = par->SavagePanelWidth;
223762306a36Sopenharmony_ci		cvt_mode.yres = par->SavagePanelHeight;
223862306a36Sopenharmony_ci		cvt_mode.refresh = 60;
223962306a36Sopenharmony_ci		/* FIXME: if we know there is only the panel
224062306a36Sopenharmony_ci		 * we can enable reduced blanking as well */
224162306a36Sopenharmony_ci		if (fb_find_mode_cvt(&cvt_mode, 0, 0))
224262306a36Sopenharmony_ci			printk(KERN_WARNING "No CVT mode found for panel\n");
224362306a36Sopenharmony_ci		else if (fb_find_mode(&info->var, info, NULL, NULL, 0,
224462306a36Sopenharmony_ci				      &cvt_mode, 0) != 3)
224562306a36Sopenharmony_ci			info->var = savagefb_var800x600x8;
224662306a36Sopenharmony_ci	}
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	if (mode_option) {
224962306a36Sopenharmony_ci		fb_find_mode(&info->var, info, mode_option,
225062306a36Sopenharmony_ci			     info->monspecs.modedb, info->monspecs.modedb_len,
225162306a36Sopenharmony_ci			     NULL, 8);
225262306a36Sopenharmony_ci	} else if (info->monspecs.modedb != NULL) {
225362306a36Sopenharmony_ci		const struct fb_videomode *mode;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci		mode = fb_find_best_display(&info->monspecs, &info->modelist);
225662306a36Sopenharmony_ci		savage_update_var(&info->var, mode);
225762306a36Sopenharmony_ci	}
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	/* maximize virtual vertical length */
226062306a36Sopenharmony_ci	lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3);
226162306a36Sopenharmony_ci	info->var.yres_virtual = info->fix.smem_len/lpitch;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	if (info->var.yres_virtual < info->var.yres) {
226462306a36Sopenharmony_ci		err = -ENOMEM;
226562306a36Sopenharmony_ci		goto failed;
226662306a36Sopenharmony_ci	}
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL)
226962306a36Sopenharmony_ci	/*
227062306a36Sopenharmony_ci	 * The clipping coordinates are masked with 0xFFF, so limit our
227162306a36Sopenharmony_ci	 * virtual resolutions to these sizes.
227262306a36Sopenharmony_ci	 */
227362306a36Sopenharmony_ci	if (info->var.yres_virtual > 0x1000)
227462306a36Sopenharmony_ci		info->var.yres_virtual = 0x1000;
227562306a36Sopenharmony_ci
227662306a36Sopenharmony_ci	if (info->var.xres_virtual > 0x1000)
227762306a36Sopenharmony_ci		info->var.xres_virtual = 0x1000;
227862306a36Sopenharmony_ci#endif
227962306a36Sopenharmony_ci	savagefb_check_var(&info->var, info);
228062306a36Sopenharmony_ci	savagefb_set_fix(info);
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	/*
228362306a36Sopenharmony_ci	 * Calculate the hsync and vsync frequencies.  Note that
228462306a36Sopenharmony_ci	 * we split the 1e12 constant up so that we can preserve
228562306a36Sopenharmony_ci	 * the precision and fit the results into 32-bit registers.
228662306a36Sopenharmony_ci	 *  (1953125000 * 512 = 1e12)
228762306a36Sopenharmony_ci	 */
228862306a36Sopenharmony_ci	h_sync = 1953125000 / info->var.pixclock;
228962306a36Sopenharmony_ci	h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin +
229062306a36Sopenharmony_ci				 info->var.right_margin +
229162306a36Sopenharmony_ci				 info->var.hsync_len);
229262306a36Sopenharmony_ci	v_sync = h_sync / (info->var.yres + info->var.upper_margin +
229362306a36Sopenharmony_ci			   info->var.lower_margin + info->var.vsync_len);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": "
229662306a36Sopenharmony_ci	       "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
229762306a36Sopenharmony_ci	       info->fix.smem_len >> 10,
229862306a36Sopenharmony_ci	       info->var.xres, info->var.yres,
229962306a36Sopenharmony_ci	       h_sync / 1000, h_sync % 1000, v_sync);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci	fb_destroy_modedb(info->monspecs.modedb);
230362306a36Sopenharmony_ci	info->monspecs.modedb = NULL;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	err = register_framebuffer(info);
230662306a36Sopenharmony_ci	if (err < 0)
230762306a36Sopenharmony_ci		goto failed;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	printk(KERN_INFO "fb: S3 %s frame buffer device\n",
231062306a36Sopenharmony_ci	       info->fix.id);
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	/*
231362306a36Sopenharmony_ci	 * Our driver data
231462306a36Sopenharmony_ci	 */
231562306a36Sopenharmony_ci	pci_set_drvdata(dev, info);
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	return 0;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci failed:
232062306a36Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_I2C
232162306a36Sopenharmony_ci	savagefb_delete_i2c_busses(info);
232262306a36Sopenharmony_ci#endif
232362306a36Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 0, 0);
232462306a36Sopenharmony_ci	savage_unmap_video(info);
232562306a36Sopenharmony_ci failed_video:
232662306a36Sopenharmony_ci	savage_unmap_mmio(info);
232762306a36Sopenharmony_ci failed_mmio:
232862306a36Sopenharmony_ci	kfree(info->pixmap.addr);
232962306a36Sopenharmony_ci failed_init:
233062306a36Sopenharmony_ci	pci_release_regions(dev);
233162306a36Sopenharmony_ci failed_enable:
233262306a36Sopenharmony_ci	framebuffer_release(info);
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	return err;
233562306a36Sopenharmony_ci}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_cistatic void savagefb_remove(struct pci_dev *dev)
233862306a36Sopenharmony_ci{
233962306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(dev);
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci	DBG("savagefb_remove");
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	if (info) {
234462306a36Sopenharmony_ci		unregister_framebuffer(info);
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_I2C
234762306a36Sopenharmony_ci		savagefb_delete_i2c_busses(info);
234862306a36Sopenharmony_ci#endif
234962306a36Sopenharmony_ci		fb_alloc_cmap(&info->cmap, 0, 0);
235062306a36Sopenharmony_ci		savage_unmap_video(info);
235162306a36Sopenharmony_ci		savage_unmap_mmio(info);
235262306a36Sopenharmony_ci		kfree(info->pixmap.addr);
235362306a36Sopenharmony_ci		pci_release_regions(dev);
235462306a36Sopenharmony_ci		framebuffer_release(info);
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci}
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_cistatic int savagefb_suspend_late(struct device *dev, pm_message_t mesg)
235962306a36Sopenharmony_ci{
236062306a36Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
236162306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	DBG("savagefb_suspend");
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	if (mesg.event == PM_EVENT_PRETHAW)
236662306a36Sopenharmony_ci		mesg.event = PM_EVENT_FREEZE;
236762306a36Sopenharmony_ci	par->pm_state = mesg.event;
236862306a36Sopenharmony_ci	dev->power.power_state = mesg;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	/*
237162306a36Sopenharmony_ci	 * For PM_EVENT_FREEZE, do not power down so the console
237262306a36Sopenharmony_ci	 * can remain active.
237362306a36Sopenharmony_ci	 */
237462306a36Sopenharmony_ci	if (mesg.event == PM_EVENT_FREEZE)
237562306a36Sopenharmony_ci		return 0;
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	console_lock();
237862306a36Sopenharmony_ci	fb_set_suspend(info, 1);
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ci	if (info->fbops->fb_sync)
238162306a36Sopenharmony_ci		info->fbops->fb_sync(info);
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	savagefb_blank(FB_BLANK_POWERDOWN, info);
238462306a36Sopenharmony_ci	savage_set_default_par(par, &par->save);
238562306a36Sopenharmony_ci	savage_disable_mmio(par);
238662306a36Sopenharmony_ci	console_unlock();
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	return 0;
238962306a36Sopenharmony_ci}
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_cistatic int __maybe_unused savagefb_suspend(struct device *dev)
239262306a36Sopenharmony_ci{
239362306a36Sopenharmony_ci	return savagefb_suspend_late(dev, PMSG_SUSPEND);
239462306a36Sopenharmony_ci}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_cistatic int __maybe_unused savagefb_hibernate(struct device *dev)
239762306a36Sopenharmony_ci{
239862306a36Sopenharmony_ci	return savagefb_suspend_late(dev, PMSG_HIBERNATE);
239962306a36Sopenharmony_ci}
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_cistatic int __maybe_unused savagefb_freeze(struct device *dev)
240262306a36Sopenharmony_ci{
240362306a36Sopenharmony_ci	return savagefb_suspend_late(dev, PMSG_FREEZE);
240462306a36Sopenharmony_ci}
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_cistatic int __maybe_unused savagefb_resume(struct device *dev)
240762306a36Sopenharmony_ci{
240862306a36Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
240962306a36Sopenharmony_ci	struct savagefb_par *par = info->par;
241062306a36Sopenharmony_ci	int cur_state = par->pm_state;
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_ci	DBG("savage_resume");
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	par->pm_state = PM_EVENT_ON;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	/*
241762306a36Sopenharmony_ci	 * The adapter was not powered down coming back from a
241862306a36Sopenharmony_ci	 * PM_EVENT_FREEZE.
241962306a36Sopenharmony_ci	 */
242062306a36Sopenharmony_ci	if (cur_state == PM_EVENT_FREEZE)
242162306a36Sopenharmony_ci		return 0;
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	console_lock();
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	savage_enable_mmio(par);
242662306a36Sopenharmony_ci	savage_init_hw(par);
242762306a36Sopenharmony_ci	savagefb_set_par(info);
242862306a36Sopenharmony_ci	fb_set_suspend(info, 0);
242962306a36Sopenharmony_ci	savagefb_blank(FB_BLANK_UNBLANK, info);
243062306a36Sopenharmony_ci	console_unlock();
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	return 0;
243362306a36Sopenharmony_ci}
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_cistatic const struct dev_pm_ops savagefb_pm_ops = {
243662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
243762306a36Sopenharmony_ci	.suspend	= savagefb_suspend,
243862306a36Sopenharmony_ci	.resume		= savagefb_resume,
243962306a36Sopenharmony_ci	.freeze		= savagefb_freeze,
244062306a36Sopenharmony_ci	.thaw		= savagefb_resume,
244162306a36Sopenharmony_ci	.poweroff	= savagefb_hibernate,
244262306a36Sopenharmony_ci	.restore	= savagefb_resume,
244362306a36Sopenharmony_ci#endif
244462306a36Sopenharmony_ci};
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_cistatic const struct pci_device_id savagefb_devices[] = {
244762306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128,
244862306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64,
245162306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C,
245462306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR,
245762306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR,
246062306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR,
246362306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR,
246662306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR,
246962306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR,
247262306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4,
247562306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4},
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D,
247862306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D},
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV,
248162306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV},
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000,
248462306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000},
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV,
248762306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV},
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX,
249062306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX},
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV,
249362306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV},
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX,
249662306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX},
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM,
249962306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM},
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM,
250262306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM},
250362306a36Sopenharmony_ci
250462306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P,
250562306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P},
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K,
250862306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K},
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR,
251162306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR},
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK,
251462306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK},
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	{0, 0, 0, 0, 0, 0, 0}
251762306a36Sopenharmony_ci};
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, savagefb_devices);
252062306a36Sopenharmony_ci
252162306a36Sopenharmony_cistatic struct pci_driver savagefb_driver = {
252262306a36Sopenharmony_ci	.name =     "savagefb",
252362306a36Sopenharmony_ci	.id_table = savagefb_devices,
252462306a36Sopenharmony_ci	.probe =    savagefb_probe,
252562306a36Sopenharmony_ci	.driver.pm = &savagefb_pm_ops,
252662306a36Sopenharmony_ci	.remove =   savagefb_remove,
252762306a36Sopenharmony_ci};
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci/* **************************** exit-time only **************************** */
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_cistatic void __exit savage_done(void)
253262306a36Sopenharmony_ci{
253362306a36Sopenharmony_ci	DBG("savage_done");
253462306a36Sopenharmony_ci	pci_unregister_driver(&savagefb_driver);
253562306a36Sopenharmony_ci}
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci/* ************************* init in-kernel code ************************** */
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_cistatic int __init savagefb_setup(char *options)
254162306a36Sopenharmony_ci{
254262306a36Sopenharmony_ci#ifndef MODULE
254362306a36Sopenharmony_ci	char *this_opt;
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci	if (!options || !*options)
254662306a36Sopenharmony_ci		return 0;
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
254962306a36Sopenharmony_ci		mode_option = this_opt;
255062306a36Sopenharmony_ci	}
255162306a36Sopenharmony_ci#endif /* !MODULE */
255262306a36Sopenharmony_ci	return 0;
255362306a36Sopenharmony_ci}
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_cistatic int __init savagefb_init(void)
255662306a36Sopenharmony_ci{
255762306a36Sopenharmony_ci	char *option;
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	DBG("savagefb_init");
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	if (fb_modesetting_disabled("savagefb"))
256262306a36Sopenharmony_ci		return -ENODEV;
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	if (fb_get_options("savagefb", &option))
256562306a36Sopenharmony_ci		return -ENODEV;
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ci	savagefb_setup(option);
256862306a36Sopenharmony_ci	return pci_register_driver(&savagefb_driver);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci}
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_cimodule_init(savagefb_init);
257362306a36Sopenharmony_cimodule_exit(savage_done);
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_cimodule_param(mode_option, charp, 0);
257662306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Specify initial video mode");
2577