18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * linux/drivers/video/savagefb.c -- S3 Savage Framebuffer Driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>
58c2ecf20Sopenharmony_ci *                          Sven Neumann <neo@directfb.org>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Card specific code is based on XFree86's savage driver.
98c2ecf20Sopenharmony_ci * Framebuffer framework code is based on code of cyber2000fb and tdfxfb.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
128c2ecf20Sopenharmony_ci * Public License.  See the file COPYING in the main directory of this
138c2ecf20Sopenharmony_ci * archive for more details.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * 0.4.0 (neo)
168c2ecf20Sopenharmony_ci *  - hardware accelerated clear and move
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * 0.3.2 (dok)
198c2ecf20Sopenharmony_ci *  - wait for vertical retrace before writing to cr67
208c2ecf20Sopenharmony_ci *    at the beginning of savagefb_set_par
218c2ecf20Sopenharmony_ci *  - use synchronization registers cr23 and cr26
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * 0.3.1 (dok)
248c2ecf20Sopenharmony_ci *  - reset 3D engine
258c2ecf20Sopenharmony_ci *  - don't return alpha bits for 32bit format
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * 0.3.0 (dok)
288c2ecf20Sopenharmony_ci *  - added WaitIdle functions for all Savage types
298c2ecf20Sopenharmony_ci *  - do WaitIdle before mode switching
308c2ecf20Sopenharmony_ci *  - code cleanup
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * 0.2.0 (dok)
338c2ecf20Sopenharmony_ci *  - first working version
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * TODO
378c2ecf20Sopenharmony_ci * - clock validations in decode_var
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * BUGS
408c2ecf20Sopenharmony_ci * - white margin on bootup
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <linux/module.h>
458c2ecf20Sopenharmony_ci#include <linux/kernel.h>
468c2ecf20Sopenharmony_ci#include <linux/errno.h>
478c2ecf20Sopenharmony_ci#include <linux/string.h>
488c2ecf20Sopenharmony_ci#include <linux/mm.h>
498c2ecf20Sopenharmony_ci#include <linux/slab.h>
508c2ecf20Sopenharmony_ci#include <linux/delay.h>
518c2ecf20Sopenharmony_ci#include <linux/fb.h>
528c2ecf20Sopenharmony_ci#include <linux/pci.h>
538c2ecf20Sopenharmony_ci#include <linux/init.h>
548c2ecf20Sopenharmony_ci#include <linux/console.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#include <asm/io.h>
578c2ecf20Sopenharmony_ci#include <asm/irq.h>
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#include "savagefb.h"
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define SAVAGEFB_VERSION "0.4.0_2.6"
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic char *mode_option = NULL;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#ifdef MODULE
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ciMODULE_AUTHOR("(c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>");
728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips");
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void vgaHWSeqReset(struct savagefb_par *par, int start)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	if (start)
838c2ecf20Sopenharmony_ci		VGAwSEQ(0x00, 0x01, par);	/* Synchronous Reset */
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		VGAwSEQ(0x00, 0x03, par);	/* End Reset */
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void vgaHWProtect(struct savagefb_par *par, int on)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	unsigned char tmp;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (on) {
938c2ecf20Sopenharmony_ci		/*
948c2ecf20Sopenharmony_ci		 * Turn off screen and disable sequencer.
958c2ecf20Sopenharmony_ci		 */
968c2ecf20Sopenharmony_ci		tmp = VGArSEQ(0x01, par);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		vgaHWSeqReset(par, 1);	        /* start synchronous reset */
998c2ecf20Sopenharmony_ci		VGAwSEQ(0x01, tmp | 0x20, par);/* disable the display */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		VGAenablePalette(par);
1028c2ecf20Sopenharmony_ci	} else {
1038c2ecf20Sopenharmony_ci		/*
1048c2ecf20Sopenharmony_ci		 * Reenable sequencer, then turn on screen.
1058c2ecf20Sopenharmony_ci		 */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		tmp = VGArSEQ(0x01, par);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		VGAwSEQ(0x01, tmp & ~0x20, par);/* reenable display */
1108c2ecf20Sopenharmony_ci		vgaHWSeqReset(par, 0);	        /* clear synchronous reset */
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		VGAdisablePalette(par);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void vgaHWRestore(struct savagefb_par  *par, struct savage_reg *reg)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int i;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	VGAwMISC(reg->MiscOutReg, par);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	for (i = 1; i < 5; i++)
1238c2ecf20Sopenharmony_ci		VGAwSEQ(i, reg->Sequencer[i], par);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or
1268c2ecf20Sopenharmony_ci	   CRTC[17] */
1278c2ecf20Sopenharmony_ci	VGAwCR(17, reg->CRTC[17] & ~0x80, par);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	for (i = 0; i < 25; i++)
1308c2ecf20Sopenharmony_ci		VGAwCR(i, reg->CRTC[i], par);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	for (i = 0; i < 9; i++)
1338c2ecf20Sopenharmony_ci		VGAwGR(i, reg->Graphics[i], par);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	VGAenablePalette(par);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	for (i = 0; i < 21; i++)
1388c2ecf20Sopenharmony_ci		VGAwATTR(i, reg->Attribute[i], par);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	VGAdisablePalette(par);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void vgaHWInit(struct fb_var_screeninfo *var,
1448c2ecf20Sopenharmony_ci		      struct savagefb_par            *par,
1458c2ecf20Sopenharmony_ci		      struct xtimings                *timings,
1468c2ecf20Sopenharmony_ci		      struct savage_reg              *reg)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	reg->MiscOutReg = 0x23;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT))
1518c2ecf20Sopenharmony_ci		reg->MiscOutReg |= 0x40;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT))
1548c2ecf20Sopenharmony_ci		reg->MiscOutReg |= 0x80;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/*
1578c2ecf20Sopenharmony_ci	 * Time Sequencer
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	reg->Sequencer[0x00] = 0x00;
1608c2ecf20Sopenharmony_ci	reg->Sequencer[0x01] = 0x01;
1618c2ecf20Sopenharmony_ci	reg->Sequencer[0x02] = 0x0F;
1628c2ecf20Sopenharmony_ci	reg->Sequencer[0x03] = 0x00;          /* Font select */
1638c2ecf20Sopenharmony_ci	reg->Sequencer[0x04] = 0x0E;          /* Misc */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * CRTC Controller
1678c2ecf20Sopenharmony_ci	 */
1688c2ecf20Sopenharmony_ci	reg->CRTC[0x00] = (timings->HTotal >> 3) - 5;
1698c2ecf20Sopenharmony_ci	reg->CRTC[0x01] = (timings->HDisplay >> 3) - 1;
1708c2ecf20Sopenharmony_ci	reg->CRTC[0x02] = (timings->HSyncStart >> 3) - 1;
1718c2ecf20Sopenharmony_ci	reg->CRTC[0x03] = (((timings->HSyncEnd >> 3)  - 1) & 0x1f) | 0x80;
1728c2ecf20Sopenharmony_ci	reg->CRTC[0x04] = (timings->HSyncStart >> 3);
1738c2ecf20Sopenharmony_ci	reg->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) |
1748c2ecf20Sopenharmony_ci		(((timings->HSyncEnd >> 3)) & 0x1f);
1758c2ecf20Sopenharmony_ci	reg->CRTC[0x06] = (timings->VTotal - 2) & 0xFF;
1768c2ecf20Sopenharmony_ci	reg->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) |
1778c2ecf20Sopenharmony_ci		(((timings->VDisplay - 1) & 0x100) >> 7) |
1788c2ecf20Sopenharmony_ci		((timings->VSyncStart & 0x100) >> 6) |
1798c2ecf20Sopenharmony_ci		(((timings->VSyncStart - 1) & 0x100) >> 5) |
1808c2ecf20Sopenharmony_ci		0x10 |
1818c2ecf20Sopenharmony_ci		(((timings->VTotal - 2) & 0x200) >> 4) |
1828c2ecf20Sopenharmony_ci		(((timings->VDisplay - 1) & 0x200) >> 3) |
1838c2ecf20Sopenharmony_ci		((timings->VSyncStart & 0x200) >> 2);
1848c2ecf20Sopenharmony_ci	reg->CRTC[0x08] = 0x00;
1858c2ecf20Sopenharmony_ci	reg->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (timings->dblscan)
1888c2ecf20Sopenharmony_ci		reg->CRTC[0x09] |= 0x80;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	reg->CRTC[0x0a] = 0x00;
1918c2ecf20Sopenharmony_ci	reg->CRTC[0x0b] = 0x00;
1928c2ecf20Sopenharmony_ci	reg->CRTC[0x0c] = 0x00;
1938c2ecf20Sopenharmony_ci	reg->CRTC[0x0d] = 0x00;
1948c2ecf20Sopenharmony_ci	reg->CRTC[0x0e] = 0x00;
1958c2ecf20Sopenharmony_ci	reg->CRTC[0x0f] = 0x00;
1968c2ecf20Sopenharmony_ci	reg->CRTC[0x10] = timings->VSyncStart & 0xff;
1978c2ecf20Sopenharmony_ci	reg->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20;
1988c2ecf20Sopenharmony_ci	reg->CRTC[0x12] = (timings->VDisplay - 1) & 0xff;
1998c2ecf20Sopenharmony_ci	reg->CRTC[0x13] = var->xres_virtual >> 4;
2008c2ecf20Sopenharmony_ci	reg->CRTC[0x14] = 0x00;
2018c2ecf20Sopenharmony_ci	reg->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff;
2028c2ecf20Sopenharmony_ci	reg->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff;
2038c2ecf20Sopenharmony_ci	reg->CRTC[0x17] = 0xc3;
2048c2ecf20Sopenharmony_ci	reg->CRTC[0x18] = 0xff;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/*
2078c2ecf20Sopenharmony_ci	 * are these unnecessary?
2088c2ecf20Sopenharmony_ci	 * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
2098c2ecf20Sopenharmony_ci	 * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Graphics Display Controller
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	reg->Graphics[0x00] = 0x00;
2168c2ecf20Sopenharmony_ci	reg->Graphics[0x01] = 0x00;
2178c2ecf20Sopenharmony_ci	reg->Graphics[0x02] = 0x00;
2188c2ecf20Sopenharmony_ci	reg->Graphics[0x03] = 0x00;
2198c2ecf20Sopenharmony_ci	reg->Graphics[0x04] = 0x00;
2208c2ecf20Sopenharmony_ci	reg->Graphics[0x05] = 0x40;
2218c2ecf20Sopenharmony_ci	reg->Graphics[0x06] = 0x05;   /* only map 64k VGA memory !!!! */
2228c2ecf20Sopenharmony_ci	reg->Graphics[0x07] = 0x0F;
2238c2ecf20Sopenharmony_ci	reg->Graphics[0x08] = 0xFF;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	reg->Attribute[0x00]  = 0x00; /* standard colormap translation */
2278c2ecf20Sopenharmony_ci	reg->Attribute[0x01]  = 0x01;
2288c2ecf20Sopenharmony_ci	reg->Attribute[0x02]  = 0x02;
2298c2ecf20Sopenharmony_ci	reg->Attribute[0x03]  = 0x03;
2308c2ecf20Sopenharmony_ci	reg->Attribute[0x04]  = 0x04;
2318c2ecf20Sopenharmony_ci	reg->Attribute[0x05]  = 0x05;
2328c2ecf20Sopenharmony_ci	reg->Attribute[0x06]  = 0x06;
2338c2ecf20Sopenharmony_ci	reg->Attribute[0x07]  = 0x07;
2348c2ecf20Sopenharmony_ci	reg->Attribute[0x08]  = 0x08;
2358c2ecf20Sopenharmony_ci	reg->Attribute[0x09]  = 0x09;
2368c2ecf20Sopenharmony_ci	reg->Attribute[0x0a] = 0x0A;
2378c2ecf20Sopenharmony_ci	reg->Attribute[0x0b] = 0x0B;
2388c2ecf20Sopenharmony_ci	reg->Attribute[0x0c] = 0x0C;
2398c2ecf20Sopenharmony_ci	reg->Attribute[0x0d] = 0x0D;
2408c2ecf20Sopenharmony_ci	reg->Attribute[0x0e] = 0x0E;
2418c2ecf20Sopenharmony_ci	reg->Attribute[0x0f] = 0x0F;
2428c2ecf20Sopenharmony_ci	reg->Attribute[0x10] = 0x41;
2438c2ecf20Sopenharmony_ci	reg->Attribute[0x11] = 0xFF;
2448c2ecf20Sopenharmony_ci	reg->Attribute[0x12] = 0x0F;
2458c2ecf20Sopenharmony_ci	reg->Attribute[0x13] = 0x00;
2468c2ecf20Sopenharmony_ci	reg->Attribute[0x14] = 0x00;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/* -------------------- Hardware specific routines ------------------------- */
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/*
2528c2ecf20Sopenharmony_ci * Hardware Acceleration for SavageFB
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/* Wait for fifo space */
2568c2ecf20Sopenharmony_cistatic void
2578c2ecf20Sopenharmony_cisavage3D_waitfifo(struct savagefb_par *par, int space)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	int slots = MAXFIFO - space;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	while ((savage_in32(0x48C00, par) & 0x0000ffff) > slots);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void
2658c2ecf20Sopenharmony_cisavage4_waitfifo(struct savagefb_par *par, int space)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	int slots = MAXFIFO - space;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x001fffff) > slots);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic void
2738c2ecf20Sopenharmony_cisavage2000_waitfifo(struct savagefb_par *par, int space)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	int slots = MAXFIFO - space;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x0000ffff) > slots);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/* Wait for idle accelerator */
2818c2ecf20Sopenharmony_cistatic void
2828c2ecf20Sopenharmony_cisavage3D_waitidle(struct savagefb_par *par)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	while ((savage_in32(0x48C00, par) & 0x0008ffff) != 0x80000);
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_cistatic void
2888c2ecf20Sopenharmony_cisavage4_waitidle(struct savagefb_par *par)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x00a00000) != 0x00a00000);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void
2948c2ecf20Sopenharmony_cisavage2000_waitidle(struct savagefb_par *par)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	while ((savage_in32(0x48C60, par) & 0x009fffff));
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_ACCEL
3008c2ecf20Sopenharmony_cistatic void
3018c2ecf20Sopenharmony_ciSavageSetup2DEngine(struct savagefb_par  *par)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	unsigned long GlobalBitmapDescriptor;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE;
3068c2ecf20Sopenharmony_ci	BCI_BD_SET_BPP(GlobalBitmapDescriptor, par->depth);
3078c2ecf20Sopenharmony_ci	BCI_BD_SET_STRIDE(GlobalBitmapDescriptor, par->vwidth);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	switch(par->chip) {
3108c2ecf20Sopenharmony_ci	case S3_SAVAGE3D:
3118c2ecf20Sopenharmony_ci	case S3_SAVAGE_MX:
3128c2ecf20Sopenharmony_ci		/* Disable BCI */
3138c2ecf20Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
3148c2ecf20Sopenharmony_ci		/* Setup BCI command overflow buffer */
3158c2ecf20Sopenharmony_ci		savage_out32(0x48C14,
3168c2ecf20Sopenharmony_ci			     (par->cob_offset >> 11) | (par->cob_index << 29),
3178c2ecf20Sopenharmony_ci			     par);
3188c2ecf20Sopenharmony_ci		/* Program shadow status update. */
3198c2ecf20Sopenharmony_ci		savage_out32(0x48C10, 0x78207220, par);
3208c2ecf20Sopenharmony_ci		savage_out32(0x48C0C, 0, par);
3218c2ecf20Sopenharmony_ci		/* Enable BCI and command overflow buffer */
3228c2ecf20Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x0C, par);
3238c2ecf20Sopenharmony_ci		break;
3248c2ecf20Sopenharmony_ci	case S3_SAVAGE4:
3258c2ecf20Sopenharmony_ci	case S3_TWISTER:
3268c2ecf20Sopenharmony_ci	case S3_PROSAVAGE:
3278c2ecf20Sopenharmony_ci	case S3_PROSAVAGEDDR:
3288c2ecf20Sopenharmony_ci	case S3_SUPERSAVAGE:
3298c2ecf20Sopenharmony_ci		/* Disable BCI */
3308c2ecf20Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
3318c2ecf20Sopenharmony_ci		/* Program shadow status update */
3328c2ecf20Sopenharmony_ci		savage_out32(0x48C10, 0x00700040, par);
3338c2ecf20Sopenharmony_ci		savage_out32(0x48C0C, 0, par);
3348c2ecf20Sopenharmony_ci		/* Enable BCI without the COB */
3358c2ecf20Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x08, par);
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case S3_SAVAGE2000:
3388c2ecf20Sopenharmony_ci		/* Disable BCI */
3398c2ecf20Sopenharmony_ci		savage_out32(0x48C18, 0, par);
3408c2ecf20Sopenharmony_ci		/* Setup BCI command overflow buffer */
3418c2ecf20Sopenharmony_ci		savage_out32(0x48C18,
3428c2ecf20Sopenharmony_ci			     (par->cob_offset >> 7) | (par->cob_index),
3438c2ecf20Sopenharmony_ci			     par);
3448c2ecf20Sopenharmony_ci		/* Disable shadow status update */
3458c2ecf20Sopenharmony_ci		savage_out32(0x48A30, 0, par);
3468c2ecf20Sopenharmony_ci		/* Enable BCI and command overflow buffer */
3478c2ecf20Sopenharmony_ci		savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x00280000,
3488c2ecf20Sopenharmony_ci			     par);
3498c2ecf20Sopenharmony_ci		break;
3508c2ecf20Sopenharmony_ci	    default:
3518c2ecf20Sopenharmony_ci		break;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci	/* Turn on 16-bit register access. */
3548c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
3558c2ecf20Sopenharmony_ci	vga_out8(0x3d5, 0x0c, par);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/* Set stride to use GBD. */
3588c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
3598c2ecf20Sopenharmony_ci	vga_out8(0x3d5, vga_in8(0x3d5, par) | 0xC1, par);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* Enable 2D engine. */
3628c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
3638c2ecf20Sopenharmony_ci	vga_out8(0x3d5, 0x01, par);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	savage_out32(MONO_PAT_0, ~0, par);
3668c2ecf20Sopenharmony_ci	savage_out32(MONO_PAT_1, ~0, par);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* Setup plane masks */
3698c2ecf20Sopenharmony_ci	savage_out32(0x8128, ~0, par); /* enable all write planes */
3708c2ecf20Sopenharmony_ci	savage_out32(0x812C, ~0, par); /* enable all read planes */
3718c2ecf20Sopenharmony_ci	savage_out16(0x8134, 0x27, par);
3728c2ecf20Sopenharmony_ci	savage_out16(0x8136, 0x07, par);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* Now set the GBD */
3758c2ecf20Sopenharmony_ci	par->bci_ptr = 0;
3768c2ecf20Sopenharmony_ci	par->SavageWaitFifo(par, 4);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1);
3798c2ecf20Sopenharmony_ci	BCI_SEND(0);
3808c2ecf20Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2);
3818c2ecf20Sopenharmony_ci	BCI_SEND(GlobalBitmapDescriptor);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/*
3848c2ecf20Sopenharmony_ci	 * I don't know why, sending this twice fixes the initial black screen,
3858c2ecf20Sopenharmony_ci	 * prevents X from crashing at least in Toshiba laptops with SavageIX.
3868c2ecf20Sopenharmony_ci	 * --Tony
3878c2ecf20Sopenharmony_ci	 */
3888c2ecf20Sopenharmony_ci	par->bci_ptr = 0;
3898c2ecf20Sopenharmony_ci	par->SavageWaitFifo(par, 4);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1);
3928c2ecf20Sopenharmony_ci	BCI_SEND(0);
3938c2ecf20Sopenharmony_ci	BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2);
3948c2ecf20Sopenharmony_ci	BCI_SEND(GlobalBitmapDescriptor);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_cistatic void savagefb_set_clip(struct fb_info *info)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
4008c2ecf20Sopenharmony_ci	int cmd;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW;
4038c2ecf20Sopenharmony_ci	par->bci_ptr = 0;
4048c2ecf20Sopenharmony_ci	par->SavageWaitFifo(par,3);
4058c2ecf20Sopenharmony_ci	BCI_SEND(cmd);
4068c2ecf20Sopenharmony_ci	BCI_SEND(BCI_CLIP_TL(0, 0));
4078c2ecf20Sopenharmony_ci	BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff));
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci#else
4108c2ecf20Sopenharmony_cistatic void SavageSetup2DEngine(struct savagefb_par  *par) {}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci#endif
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
4158c2ecf20Sopenharmony_ci			    int min_n2, int max_n2, long freq_min,
4168c2ecf20Sopenharmony_ci			    long freq_max, unsigned int *mdiv,
4178c2ecf20Sopenharmony_ci			    unsigned int *ndiv, unsigned int *r)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	long diff, best_diff;
4208c2ecf20Sopenharmony_ci	unsigned int m;
4218c2ecf20Sopenharmony_ci	unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (freq < freq_min / (1 << max_n2)) {
4248c2ecf20Sopenharmony_ci		printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
4258c2ecf20Sopenharmony_ci		freq = freq_min / (1 << max_n2);
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	if (freq > freq_max / (1 << min_n2)) {
4288c2ecf20Sopenharmony_ci		printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
4298c2ecf20Sopenharmony_ci		freq = freq_max / (1 << min_n2);
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* work out suitable timings */
4338c2ecf20Sopenharmony_ci	best_diff = freq;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	for (n2=min_n2; n2<=max_n2; n2++) {
4368c2ecf20Sopenharmony_ci		for (n1=min_n1+2; n1<=max_n1+2; n1++) {
4378c2ecf20Sopenharmony_ci			m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
4388c2ecf20Sopenharmony_ci				BASE_FREQ;
4398c2ecf20Sopenharmony_ci			if (m < min_m+2 || m > 127+2)
4408c2ecf20Sopenharmony_ci				continue;
4418c2ecf20Sopenharmony_ci			if ((m * BASE_FREQ >= freq_min * n1) &&
4428c2ecf20Sopenharmony_ci			    (m * BASE_FREQ <= freq_max * n1)) {
4438c2ecf20Sopenharmony_ci				diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
4448c2ecf20Sopenharmony_ci				if (diff < 0)
4458c2ecf20Sopenharmony_ci					diff = -diff;
4468c2ecf20Sopenharmony_ci				if (diff < best_diff) {
4478c2ecf20Sopenharmony_ci					best_diff = diff;
4488c2ecf20Sopenharmony_ci					best_m = m;
4498c2ecf20Sopenharmony_ci					best_n1 = n1;
4508c2ecf20Sopenharmony_ci					best_n2 = n2;
4518c2ecf20Sopenharmony_ci				}
4528c2ecf20Sopenharmony_ci			}
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	*ndiv = best_n1 - 2;
4578c2ecf20Sopenharmony_ci	*r = best_n2;
4588c2ecf20Sopenharmony_ci	*mdiv = best_m - 2;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic int common_calc_clock(long freq, int min_m, int min_n1, int max_n1,
4628c2ecf20Sopenharmony_ci			     int min_n2, int max_n2, long freq_min,
4638c2ecf20Sopenharmony_ci			     long freq_max, unsigned char *mdiv,
4648c2ecf20Sopenharmony_ci			     unsigned char *ndiv)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	long diff, best_diff;
4678c2ecf20Sopenharmony_ci	unsigned int m;
4688c2ecf20Sopenharmony_ci	unsigned char n1, n2;
4698c2ecf20Sopenharmony_ci	unsigned char best_n1 = 16+2, best_n2 = 2, best_m = 125+2;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	best_diff = freq;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	for (n2 = min_n2; n2 <= max_n2; n2++) {
4748c2ecf20Sopenharmony_ci		for (n1 = min_n1+2; n1 <= max_n1+2; n1++) {
4758c2ecf20Sopenharmony_ci			m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
4768c2ecf20Sopenharmony_ci				BASE_FREQ;
4778c2ecf20Sopenharmony_ci			if (m < min_m + 2 || m > 127+2)
4788c2ecf20Sopenharmony_ci				continue;
4798c2ecf20Sopenharmony_ci			if ((m * BASE_FREQ >= freq_min * n1) &&
4808c2ecf20Sopenharmony_ci			    (m * BASE_FREQ <= freq_max * n1)) {
4818c2ecf20Sopenharmony_ci				diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
4828c2ecf20Sopenharmony_ci				if (diff < 0)
4838c2ecf20Sopenharmony_ci					diff = -diff;
4848c2ecf20Sopenharmony_ci				if (diff < best_diff) {
4858c2ecf20Sopenharmony_ci					best_diff = diff;
4868c2ecf20Sopenharmony_ci					best_m = m;
4878c2ecf20Sopenharmony_ci					best_n1 = n1;
4888c2ecf20Sopenharmony_ci					best_n2 = n2;
4898c2ecf20Sopenharmony_ci				}
4908c2ecf20Sopenharmony_ci			}
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (max_n1 == 63)
4958c2ecf20Sopenharmony_ci		*ndiv = (best_n1 - 2) | (best_n2 << 6);
4968c2ecf20Sopenharmony_ci	else
4978c2ecf20Sopenharmony_ci		*ndiv = (best_n1 - 2) | (best_n2 << 5);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	*mdiv = best_m - 2;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return 0;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci#ifdef SAVAGEFB_DEBUG
5058c2ecf20Sopenharmony_ci/* This function is used to debug, it prints out the contents of s3 regs */
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void SavagePrintRegs(struct savagefb_par *par)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	unsigned char i;
5108c2ecf20Sopenharmony_ci	int vgaCRIndex = 0x3d4;
5118c2ecf20Sopenharmony_ci	int vgaCRReg = 0x3d5;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "SR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE "
5148c2ecf20Sopenharmony_ci	       "xF");
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	for (i = 0; i < 0x70; i++) {
5178c2ecf20Sopenharmony_ci		if (!(i % 16))
5188c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "\nSR%xx ", i >> 4);
5198c2ecf20Sopenharmony_ci		vga_out8(0x3c4, i, par);
5208c2ecf20Sopenharmony_ci		printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par));
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\n\nCR    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC "
5248c2ecf20Sopenharmony_ci	       "xD xE xF");
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	for (i = 0; i < 0xB7; i++) {
5278c2ecf20Sopenharmony_ci		if (!(i % 16))
5288c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "\nCR%xx ", i >> 4);
5298c2ecf20Sopenharmony_ci		vga_out8(vgaCRIndex, i, par);
5308c2ecf20Sopenharmony_ci		printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par));
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "\n\n");
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci#endif
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic void savage_get_default_par(struct savagefb_par *par, struct savage_reg *reg)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	unsigned char cr3a, cr53, cr66;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
5448c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0xa039, par);
5458c2ecf20Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
5488c2ecf20Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
5498c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
5508c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
5518c2ecf20Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
5528c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
5538c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
5548c2ecf20Sopenharmony_ci	cr53 = vga_in8(0x3d5, par);
5558c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr53 & 0x7f, par);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
5588c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
5598c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
5608c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
5638c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
5648c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
5658c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* unlock extended seq regs */
5688c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
5698c2ecf20Sopenharmony_ci	reg->SR08 = vga_in8(0x3c5, par);
5708c2ecf20Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* now save all the extended regs we need */
5738c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
5748c2ecf20Sopenharmony_ci	reg->CR31 = vga_in8(0x3d5, par);
5758c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
5768c2ecf20Sopenharmony_ci	reg->CR32 = vga_in8(0x3d5, par);
5778c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x34, par);
5788c2ecf20Sopenharmony_ci	reg->CR34 = vga_in8(0x3d5, par);
5798c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
5808c2ecf20Sopenharmony_ci	reg->CR36 = vga_in8(0x3d5, par);
5818c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
5828c2ecf20Sopenharmony_ci	reg->CR3A = vga_in8(0x3d5, par);
5838c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
5848c2ecf20Sopenharmony_ci	reg->CR40 = vga_in8(0x3d5, par);
5858c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x42, par);
5868c2ecf20Sopenharmony_ci	reg->CR42 = vga_in8(0x3d5, par);
5878c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x45, par);
5888c2ecf20Sopenharmony_ci	reg->CR45 = vga_in8(0x3d5, par);
5898c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
5908c2ecf20Sopenharmony_ci	reg->CR50 = vga_in8(0x3d5, par);
5918c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x51, par);
5928c2ecf20Sopenharmony_ci	reg->CR51 = vga_in8(0x3d5, par);
5938c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
5948c2ecf20Sopenharmony_ci	reg->CR53 = vga_in8(0x3d5, par);
5958c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
5968c2ecf20Sopenharmony_ci	reg->CR58 = vga_in8(0x3d5, par);
5978c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x60, par);
5988c2ecf20Sopenharmony_ci	reg->CR60 = vga_in8(0x3d5, par);
5998c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
6008c2ecf20Sopenharmony_ci	reg->CR66 = vga_in8(0x3d5, par);
6018c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
6028c2ecf20Sopenharmony_ci	reg->CR67 = vga_in8(0x3d5, par);
6038c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
6048c2ecf20Sopenharmony_ci	reg->CR68 = vga_in8(0x3d5, par);
6058c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
6068c2ecf20Sopenharmony_ci	reg->CR69 = vga_in8(0x3d5, par);
6078c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
6088c2ecf20Sopenharmony_ci	reg->CR6F = vga_in8(0x3d5, par);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x33, par);
6118c2ecf20Sopenharmony_ci	reg->CR33 = vga_in8(0x3d5, par);
6128c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
6138c2ecf20Sopenharmony_ci	reg->CR86 = vga_in8(0x3d5, par);
6148c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
6158c2ecf20Sopenharmony_ci	reg->CR88 = vga_in8(0x3d5, par);
6168c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x90, par);
6178c2ecf20Sopenharmony_ci	reg->CR90 = vga_in8(0x3d5, par);
6188c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x91, par);
6198c2ecf20Sopenharmony_ci	reg->CR91 = vga_in8(0x3d5, par);
6208c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0xb0, par);
6218c2ecf20Sopenharmony_ci	reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	/* extended mode timing regs */
6248c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3b, par);
6258c2ecf20Sopenharmony_ci	reg->CR3B = vga_in8(0x3d5, par);
6268c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3c, par);
6278c2ecf20Sopenharmony_ci	reg->CR3C = vga_in8(0x3d5, par);
6288c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x43, par);
6298c2ecf20Sopenharmony_ci	reg->CR43 = vga_in8(0x3d5, par);
6308c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x5d, par);
6318c2ecf20Sopenharmony_ci	reg->CR5D = vga_in8(0x3d5, par);
6328c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x5e, par);
6338c2ecf20Sopenharmony_ci	reg->CR5E = vga_in8(0x3d5, par);
6348c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x65, par);
6358c2ecf20Sopenharmony_ci	reg->CR65 = vga_in8(0x3d5, par);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	/* save seq extended regs for DCLK PLL programming */
6388c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
6398c2ecf20Sopenharmony_ci	reg->SR0E = vga_in8(0x3c5, par);
6408c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
6418c2ecf20Sopenharmony_ci	reg->SR0F = vga_in8(0x3c5, par);
6428c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x10, par);
6438c2ecf20Sopenharmony_ci	reg->SR10 = vga_in8(0x3c5, par);
6448c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x11, par);
6458c2ecf20Sopenharmony_ci	reg->SR11 = vga_in8(0x3c5, par);
6468c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x12, par);
6478c2ecf20Sopenharmony_ci	reg->SR12 = vga_in8(0x3c5, par);
6488c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x13, par);
6498c2ecf20Sopenharmony_ci	reg->SR13 = vga_in8(0x3c5, par);
6508c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
6518c2ecf20Sopenharmony_ci	reg->SR29 = vga_in8(0x3c5, par);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
6548c2ecf20Sopenharmony_ci	reg->SR15 = vga_in8(0x3c5, par);
6558c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x30, par);
6568c2ecf20Sopenharmony_ci	reg->SR30 = vga_in8(0x3c5, par);
6578c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x18, par);
6588c2ecf20Sopenharmony_ci	reg->SR18 = vga_in8(0x3c5, par);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/* Save flat panel expansion registers. */
6618c2ecf20Sopenharmony_ci	if (par->chip == S3_SAVAGE_MX) {
6628c2ecf20Sopenharmony_ci		int i;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci		for (i = 0; i < 8; i++) {
6658c2ecf20Sopenharmony_ci			vga_out8(0x3c4, 0x54+i, par);
6668c2ecf20Sopenharmony_ci			reg->SR54[i] = vga_in8(0x3c5, par);
6678c2ecf20Sopenharmony_ci		}
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
6718c2ecf20Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
6728c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
6738c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
6748c2ecf20Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
6758c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* now save MIU regs */
6788c2ecf20Sopenharmony_ci	if (par->chip != S3_SAVAGE_MX) {
6798c2ecf20Sopenharmony_ci		reg->MMPR0 = savage_in32(FIFO_CONTROL_REG, par);
6808c2ecf20Sopenharmony_ci		reg->MMPR1 = savage_in32(MIU_CONTROL_REG, par);
6818c2ecf20Sopenharmony_ci		reg->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par);
6828c2ecf20Sopenharmony_ci		reg->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par);
6838c2ecf20Sopenharmony_ci	}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
6868c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
6878c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
6888c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic void savage_set_default_par(struct savagefb_par *par,
6928c2ecf20Sopenharmony_ci				struct savage_reg *reg)
6938c2ecf20Sopenharmony_ci{
6948c2ecf20Sopenharmony_ci	unsigned char cr3a, cr53, cr66;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
6978c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0xa039, par);
6988c2ecf20Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
7018c2ecf20Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
7028c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
7038c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
7048c2ecf20Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
7058c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
7068c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
7078c2ecf20Sopenharmony_ci	cr53 = vga_in8(0x3d5, par);
7088c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr53 & 0x7f, par);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
7118c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
7128c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
7138c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
7168c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
7178c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
7188c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* unlock extended seq regs */
7218c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
7228c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR08, par);
7238c2ecf20Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	/* now restore all the extended regs we need */
7268c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
7278c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR31, par);
7288c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
7298c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR32, par);
7308c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x34, par);
7318c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR34, par);
7328c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
7338c2ecf20Sopenharmony_ci	vga_out8(0x3d5,reg->CR36, par);
7348c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
7358c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR3A, par);
7368c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
7378c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR40, par);
7388c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x42, par);
7398c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR42, par);
7408c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x45, par);
7418c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR45, par);
7428c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
7438c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR50, par);
7448c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x51, par);
7458c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR51, par);
7468c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
7478c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR53, par);
7488c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
7498c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR58, par);
7508c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x60, par);
7518c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR60, par);
7528c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
7538c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR66, par);
7548c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
7558c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR67, par);
7568c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
7578c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR68, par);
7588c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
7598c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR69, par);
7608c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
7618c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR6F, par);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x33, par);
7648c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR33, par);
7658c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
7668c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR86, par);
7678c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
7688c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR88, par);
7698c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x90, par);
7708c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR90, par);
7718c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x91, par);
7728c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR91, par);
7738c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0xb0, par);
7748c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CRB0, par);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* extended mode timing regs */
7778c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3b, par);
7788c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR3B, par);
7798c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3c, par);
7808c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR3C, par);
7818c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x43, par);
7828c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR43, par);
7838c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x5d, par);
7848c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR5D, par);
7858c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x5e, par);
7868c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR5E, par);
7878c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x65, par);
7888c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR65, par);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	/* save seq extended regs for DCLK PLL programming */
7918c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
7928c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR0E, par);
7938c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
7948c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR0F, par);
7958c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x10, par);
7968c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR10, par);
7978c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x11, par);
7988c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR11, par);
7998c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x12, par);
8008c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR12, par);
8018c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x13, par);
8028c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR13, par);
8038c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
8048c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR29, par);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
8078c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR15, par);
8088c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x30, par);
8098c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR30, par);
8108c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x18, par);
8118c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR18, par);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/* Save flat panel expansion registers. */
8148c2ecf20Sopenharmony_ci	if (par->chip == S3_SAVAGE_MX) {
8158c2ecf20Sopenharmony_ci		int i;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci		for (i = 0; i < 8; i++) {
8188c2ecf20Sopenharmony_ci			vga_out8(0x3c4, 0x54+i, par);
8198c2ecf20Sopenharmony_ci			vga_out8(0x3c5, reg->SR54[i], par);
8208c2ecf20Sopenharmony_ci		}
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
8248c2ecf20Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
8258c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
8268c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
8278c2ecf20Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
8288c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* now save MIU regs */
8318c2ecf20Sopenharmony_ci	if (par->chip != S3_SAVAGE_MX) {
8328c2ecf20Sopenharmony_ci		savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
8338c2ecf20Sopenharmony_ci		savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
8348c2ecf20Sopenharmony_ci		savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
8358c2ecf20Sopenharmony_ci		savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
8398c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
8408c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
8418c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic void savage_update_var(struct fb_var_screeninfo *var,
8458c2ecf20Sopenharmony_ci			      const struct fb_videomode *modedb)
8468c2ecf20Sopenharmony_ci{
8478c2ecf20Sopenharmony_ci	var->xres = var->xres_virtual = modedb->xres;
8488c2ecf20Sopenharmony_ci	var->yres = modedb->yres;
8498c2ecf20Sopenharmony_ci        if (var->yres_virtual < var->yres)
8508c2ecf20Sopenharmony_ci	    var->yres_virtual = var->yres;
8518c2ecf20Sopenharmony_ci        var->xoffset = var->yoffset = 0;
8528c2ecf20Sopenharmony_ci        var->pixclock = modedb->pixclock;
8538c2ecf20Sopenharmony_ci        var->left_margin = modedb->left_margin;
8548c2ecf20Sopenharmony_ci        var->right_margin = modedb->right_margin;
8558c2ecf20Sopenharmony_ci        var->upper_margin = modedb->upper_margin;
8568c2ecf20Sopenharmony_ci        var->lower_margin = modedb->lower_margin;
8578c2ecf20Sopenharmony_ci        var->hsync_len = modedb->hsync_len;
8588c2ecf20Sopenharmony_ci        var->vsync_len = modedb->vsync_len;
8598c2ecf20Sopenharmony_ci        var->sync = modedb->sync;
8608c2ecf20Sopenharmony_ci        var->vmode = modedb->vmode;
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic int savagefb_check_var(struct fb_var_screeninfo   *var,
8648c2ecf20Sopenharmony_ci			      struct fb_info *info)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
8678c2ecf20Sopenharmony_ci	int memlen, vramlen, mode_valid = 0;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	DBG("savagefb_check_var");
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	var->transp.offset = 0;
8728c2ecf20Sopenharmony_ci	var->transp.length = 0;
8738c2ecf20Sopenharmony_ci	switch (var->bits_per_pixel) {
8748c2ecf20Sopenharmony_ci	case 8:
8758c2ecf20Sopenharmony_ci		var->red.offset = var->green.offset =
8768c2ecf20Sopenharmony_ci			var->blue.offset = 0;
8778c2ecf20Sopenharmony_ci		var->red.length = var->green.length =
8788c2ecf20Sopenharmony_ci			var->blue.length = var->bits_per_pixel;
8798c2ecf20Sopenharmony_ci		break;
8808c2ecf20Sopenharmony_ci	case 16:
8818c2ecf20Sopenharmony_ci		var->red.offset = 11;
8828c2ecf20Sopenharmony_ci		var->red.length = 5;
8838c2ecf20Sopenharmony_ci		var->green.offset = 5;
8848c2ecf20Sopenharmony_ci		var->green.length = 6;
8858c2ecf20Sopenharmony_ci		var->blue.offset = 0;
8868c2ecf20Sopenharmony_ci		var->blue.length = 5;
8878c2ecf20Sopenharmony_ci		break;
8888c2ecf20Sopenharmony_ci	case 32:
8898c2ecf20Sopenharmony_ci		var->transp.offset = 24;
8908c2ecf20Sopenharmony_ci		var->transp.length = 8;
8918c2ecf20Sopenharmony_ci		var->red.offset = 16;
8928c2ecf20Sopenharmony_ci		var->red.length = 8;
8938c2ecf20Sopenharmony_ci		var->green.offset = 8;
8948c2ecf20Sopenharmony_ci		var->green.length = 8;
8958c2ecf20Sopenharmony_ci		var->blue.offset = 0;
8968c2ecf20Sopenharmony_ci		var->blue.length = 8;
8978c2ecf20Sopenharmony_ci		break;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	default:
9008c2ecf20Sopenharmony_ci		return -EINVAL;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
9048c2ecf20Sopenharmony_ci	    !info->monspecs.dclkmax || !fb_validate_mode(var, info))
9058c2ecf20Sopenharmony_ci		mode_valid = 1;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	/* calculate modeline if supported by monitor */
9088c2ecf20Sopenharmony_ci	if (!mode_valid && info->monspecs.gtf) {
9098c2ecf20Sopenharmony_ci		if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
9108c2ecf20Sopenharmony_ci			mode_valid = 1;
9118c2ecf20Sopenharmony_ci	}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	if (!mode_valid) {
9148c2ecf20Sopenharmony_ci		const struct fb_videomode *mode;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci		mode = fb_find_best_mode(var, &info->modelist);
9178c2ecf20Sopenharmony_ci		if (mode) {
9188c2ecf20Sopenharmony_ci			savage_update_var(var, mode);
9198c2ecf20Sopenharmony_ci			mode_valid = 1;
9208c2ecf20Sopenharmony_ci		}
9218c2ecf20Sopenharmony_ci	}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (!mode_valid && info->monspecs.modedb_len)
9248c2ecf20Sopenharmony_ci		return -EINVAL;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* Is the mode larger than the LCD panel? */
9278c2ecf20Sopenharmony_ci	if (par->SavagePanelWidth &&
9288c2ecf20Sopenharmony_ci	    (var->xres > par->SavagePanelWidth ||
9298c2ecf20Sopenharmony_ci	     var->yres > par->SavagePanelHeight)) {
9308c2ecf20Sopenharmony_ci		printk(KERN_INFO "Mode (%dx%d) larger than the LCD panel "
9318c2ecf20Sopenharmony_ci		       "(%dx%d)\n", var->xres,  var->yres,
9328c2ecf20Sopenharmony_ci		       par->SavagePanelWidth,
9338c2ecf20Sopenharmony_ci		       par->SavagePanelHeight);
9348c2ecf20Sopenharmony_ci		return -1;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	if (var->yres_virtual < var->yres)
9388c2ecf20Sopenharmony_ci		var->yres_virtual = var->yres;
9398c2ecf20Sopenharmony_ci	if (var->xres_virtual < var->xres)
9408c2ecf20Sopenharmony_ci		var->xres_virtual = var->xres;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	vramlen = info->fix.smem_len;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	memlen = var->xres_virtual * var->bits_per_pixel *
9458c2ecf20Sopenharmony_ci		var->yres_virtual / 8;
9468c2ecf20Sopenharmony_ci	if (memlen > vramlen) {
9478c2ecf20Sopenharmony_ci		var->yres_virtual = vramlen * 8 /
9488c2ecf20Sopenharmony_ci			(var->xres_virtual * var->bits_per_pixel);
9498c2ecf20Sopenharmony_ci		memlen = var->xres_virtual * var->bits_per_pixel *
9508c2ecf20Sopenharmony_ci			var->yres_virtual / 8;
9518c2ecf20Sopenharmony_ci	}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	/* we must round yres/xres down, we already rounded y/xres_virtual up
9548c2ecf20Sopenharmony_ci	   if it was possible. We should return -EINVAL, but I disagree */
9558c2ecf20Sopenharmony_ci	if (var->yres_virtual < var->yres)
9568c2ecf20Sopenharmony_ci		var->yres = var->yres_virtual;
9578c2ecf20Sopenharmony_ci	if (var->xres_virtual < var->xres)
9588c2ecf20Sopenharmony_ci		var->xres = var->xres_virtual;
9598c2ecf20Sopenharmony_ci	if (var->xoffset + var->xres > var->xres_virtual)
9608c2ecf20Sopenharmony_ci		var->xoffset = var->xres_virtual - var->xres;
9618c2ecf20Sopenharmony_ci	if (var->yoffset + var->yres > var->yres_virtual)
9628c2ecf20Sopenharmony_ci		var->yoffset = var->yres_virtual - var->yres;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	return 0;
9658c2ecf20Sopenharmony_ci}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_cistatic int savagefb_decode_var(struct fb_var_screeninfo   *var,
9698c2ecf20Sopenharmony_ci			       struct savagefb_par        *par,
9708c2ecf20Sopenharmony_ci			       struct savage_reg          *reg)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	struct xtimings timings;
9738c2ecf20Sopenharmony_ci	int width, dclk, i, j; /*, refresh; */
9748c2ecf20Sopenharmony_ci	unsigned int m, n, r;
9758c2ecf20Sopenharmony_ci	unsigned char tmp = 0;
9768c2ecf20Sopenharmony_ci	unsigned int pixclock = var->pixclock;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	DBG("savagefb_decode_var");
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	memset(&timings, 0, sizeof(timings));
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	if (!pixclock) pixclock = 10000;	/* 10ns = 100MHz */
9838c2ecf20Sopenharmony_ci	timings.Clock = 1000000000 / pixclock;
9848c2ecf20Sopenharmony_ci	if (timings.Clock < 1) timings.Clock = 1;
9858c2ecf20Sopenharmony_ci	timings.dblscan = var->vmode & FB_VMODE_DOUBLE;
9868c2ecf20Sopenharmony_ci	timings.interlaced = var->vmode & FB_VMODE_INTERLACED;
9878c2ecf20Sopenharmony_ci	timings.HDisplay = var->xres;
9888c2ecf20Sopenharmony_ci	timings.HSyncStart = timings.HDisplay + var->right_margin;
9898c2ecf20Sopenharmony_ci	timings.HSyncEnd = timings.HSyncStart + var->hsync_len;
9908c2ecf20Sopenharmony_ci	timings.HTotal = timings.HSyncEnd + var->left_margin;
9918c2ecf20Sopenharmony_ci	timings.VDisplay = var->yres;
9928c2ecf20Sopenharmony_ci	timings.VSyncStart = timings.VDisplay + var->lower_margin;
9938c2ecf20Sopenharmony_ci	timings.VSyncEnd = timings.VSyncStart + var->vsync_len;
9948c2ecf20Sopenharmony_ci	timings.VTotal = timings.VSyncEnd + var->upper_margin;
9958c2ecf20Sopenharmony_ci	timings.sync = var->sync;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	par->depth  = var->bits_per_pixel;
9998c2ecf20Sopenharmony_ci	par->vwidth = var->xres_virtual;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 16  &&  par->chip == S3_SAVAGE3D) {
10028c2ecf20Sopenharmony_ci		timings.HDisplay *= 2;
10038c2ecf20Sopenharmony_ci		timings.HSyncStart *= 2;
10048c2ecf20Sopenharmony_ci		timings.HSyncEnd *= 2;
10058c2ecf20Sopenharmony_ci		timings.HTotal *= 2;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	/*
10098c2ecf20Sopenharmony_ci	 * This will allocate the datastructure and initialize all of the
10108c2ecf20Sopenharmony_ci	 * generic VGA registers.
10118c2ecf20Sopenharmony_ci	 */
10128c2ecf20Sopenharmony_ci	vgaHWInit(var, par, &timings, reg);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	/* We need to set CR67 whether or not we use the BIOS. */
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	dclk = timings.Clock;
10178c2ecf20Sopenharmony_ci	reg->CR67 = 0x00;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	switch(var->bits_per_pixel) {
10208c2ecf20Sopenharmony_ci	case 8:
10218c2ecf20Sopenharmony_ci		if ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))
10228c2ecf20Sopenharmony_ci			reg->CR67 = 0x10;	/* 8bpp, 2 pixels/clock */
10238c2ecf20Sopenharmony_ci		else
10248c2ecf20Sopenharmony_ci			reg->CR67 = 0x00;	/* 8bpp, 1 pixel/clock */
10258c2ecf20Sopenharmony_ci		break;
10268c2ecf20Sopenharmony_ci	case 15:
10278c2ecf20Sopenharmony_ci		if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
10288c2ecf20Sopenharmony_ci		    ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
10298c2ecf20Sopenharmony_ci			reg->CR67 = 0x30;	/* 15bpp, 2 pixel/clock */
10308c2ecf20Sopenharmony_ci		else
10318c2ecf20Sopenharmony_ci			reg->CR67 = 0x20;	/* 15bpp, 1 pixels/clock */
10328c2ecf20Sopenharmony_ci		break;
10338c2ecf20Sopenharmony_ci	case 16:
10348c2ecf20Sopenharmony_ci		if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
10358c2ecf20Sopenharmony_ci		   ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
10368c2ecf20Sopenharmony_ci			reg->CR67 = 0x50;	/* 16bpp, 2 pixel/clock */
10378c2ecf20Sopenharmony_ci		else
10388c2ecf20Sopenharmony_ci			reg->CR67 = 0x40;	/* 16bpp, 1 pixels/clock */
10398c2ecf20Sopenharmony_ci		break;
10408c2ecf20Sopenharmony_ci	case 24:
10418c2ecf20Sopenharmony_ci		reg->CR67 = 0x70;
10428c2ecf20Sopenharmony_ci		break;
10438c2ecf20Sopenharmony_ci	case 32:
10448c2ecf20Sopenharmony_ci		reg->CR67 = 0xd0;
10458c2ecf20Sopenharmony_ci		break;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/*
10498c2ecf20Sopenharmony_ci	 * Either BIOS use is disabled, or we failed to find a suitable
10508c2ecf20Sopenharmony_ci	 * match.  Fall back to traditional register-crunching.
10518c2ecf20Sopenharmony_ci	 */
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
10548c2ecf20Sopenharmony_ci	tmp = vga_in8(0x3d5, par);
10558c2ecf20Sopenharmony_ci	if (1 /*FIXME:psav->pci_burst*/)
10568c2ecf20Sopenharmony_ci		reg->CR3A = (tmp & 0x7f) | 0x15;
10578c2ecf20Sopenharmony_ci	else
10588c2ecf20Sopenharmony_ci		reg->CR3A = tmp | 0x95;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	reg->CR53 = 0x00;
10618c2ecf20Sopenharmony_ci	reg->CR31 = 0x8c;
10628c2ecf20Sopenharmony_ci	reg->CR66 = 0x89;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
10658c2ecf20Sopenharmony_ci	reg->CR58 = vga_in8(0x3d5, par) & 0x80;
10668c2ecf20Sopenharmony_ci	reg->CR58 |= 0x13;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	reg->SR15 = 0x03 | 0x80;
10698c2ecf20Sopenharmony_ci	reg->SR18 = 0x00;
10708c2ecf20Sopenharmony_ci	reg->CR43 = reg->CR45 = reg->CR65 = 0x00;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
10738c2ecf20Sopenharmony_ci	reg->CR40 = vga_in8(0x3d5, par) & ~0x01;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	reg->MMPR0 = 0x010400;
10768c2ecf20Sopenharmony_ci	reg->MMPR1 = 0x00;
10778c2ecf20Sopenharmony_ci	reg->MMPR2 = 0x0808;
10788c2ecf20Sopenharmony_ci	reg->MMPR3 = 0x08080810;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
10818c2ecf20Sopenharmony_ci	/* m = 107; n = 4; r = 2; */
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (par->MCLK <= 0) {
10848c2ecf20Sopenharmony_ci		reg->SR10 = 255;
10858c2ecf20Sopenharmony_ci		reg->SR11 = 255;
10868c2ecf20Sopenharmony_ci	} else {
10878c2ecf20Sopenharmony_ci		common_calc_clock(par->MCLK, 1, 1, 31, 0, 3, 135000, 270000,
10888c2ecf20Sopenharmony_ci				   &reg->SR11, &reg->SR10);
10898c2ecf20Sopenharmony_ci		/*      reg->SR10 = 80; // MCLK == 286000 */
10908c2ecf20Sopenharmony_ci		/*      reg->SR11 = 125; */
10918c2ecf20Sopenharmony_ci	}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	reg->SR12 = (r << 6) | (n & 0x3f);
10948c2ecf20Sopenharmony_ci	reg->SR13 = m & 0xff;
10958c2ecf20Sopenharmony_ci	reg->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	if (var->bits_per_pixel < 24)
10988c2ecf20Sopenharmony_ci		reg->MMPR0 -= 0x8000;
10998c2ecf20Sopenharmony_ci	else
11008c2ecf20Sopenharmony_ci		reg->MMPR0 -= 0x4000;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (timings.interlaced)
11038c2ecf20Sopenharmony_ci		reg->CR42 = 0x20;
11048c2ecf20Sopenharmony_ci	else
11058c2ecf20Sopenharmony_ci		reg->CR42 = 0x00;
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	reg->CR34 = 0x10; /* display fifo */
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) |
11108c2ecf20Sopenharmony_ci		((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) |
11118c2ecf20Sopenharmony_ci		((((timings.HSyncStart >> 3) - 1) & 0x100) >> 6) |
11128c2ecf20Sopenharmony_ci		((timings.HSyncStart & 0x800) >> 7);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 64)
11158c2ecf20Sopenharmony_ci		i |= 0x08;
11168c2ecf20Sopenharmony_ci	if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32)
11178c2ecf20Sopenharmony_ci		i |= 0x20;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	j = (reg->CRTC[0] + ((i & 0x01) << 8) +
11208c2ecf20Sopenharmony_ci	     reg->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (j - (reg->CRTC[4] + ((i & 0x10) << 4)) < 4) {
11238c2ecf20Sopenharmony_ci		if (reg->CRTC[4] + ((i & 0x10) << 4) + 4 <=
11248c2ecf20Sopenharmony_ci		    reg->CRTC[0] + ((i & 0x01) << 8))
11258c2ecf20Sopenharmony_ci			j = reg->CRTC[4] + ((i & 0x10) << 4) + 4;
11268c2ecf20Sopenharmony_ci		else
11278c2ecf20Sopenharmony_ci			j = reg->CRTC[0] + ((i & 0x01) << 8) + 1;
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	reg->CR3B = j & 0xff;
11318c2ecf20Sopenharmony_ci	i |= (j & 0x100) >> 2;
11328c2ecf20Sopenharmony_ci	reg->CR3C = (reg->CRTC[0] + ((i & 0x01) << 8)) / 2;
11338c2ecf20Sopenharmony_ci	reg->CR5D = i;
11348c2ecf20Sopenharmony_ci	reg->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) |
11358c2ecf20Sopenharmony_ci		(((timings.VDisplay - 1) & 0x400) >> 9) |
11368c2ecf20Sopenharmony_ci		(((timings.VSyncStart) & 0x400) >> 8) |
11378c2ecf20Sopenharmony_ci		(((timings.VSyncStart) & 0x400) >> 6) | 0x40;
11388c2ecf20Sopenharmony_ci	width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3;
11398c2ecf20Sopenharmony_ci	reg->CR91 = reg->CRTC[19] = 0xff & width;
11408c2ecf20Sopenharmony_ci	reg->CR51 = (0x300 & width) >> 4;
11418c2ecf20Sopenharmony_ci	reg->CR90 = 0x80 | (width >> 8);
11428c2ecf20Sopenharmony_ci	reg->MiscOutReg |= 0x0c;
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	/* Set frame buffer description. */
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	if (var->bits_per_pixel <= 8)
11478c2ecf20Sopenharmony_ci		reg->CR50 = 0;
11488c2ecf20Sopenharmony_ci	else if (var->bits_per_pixel <= 16)
11498c2ecf20Sopenharmony_ci		reg->CR50 = 0x10;
11508c2ecf20Sopenharmony_ci	else
11518c2ecf20Sopenharmony_ci		reg->CR50 = 0x30;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	if (var->xres_virtual <= 640)
11548c2ecf20Sopenharmony_ci		reg->CR50 |= 0x40;
11558c2ecf20Sopenharmony_ci	else if (var->xres_virtual == 800)
11568c2ecf20Sopenharmony_ci		reg->CR50 |= 0x80;
11578c2ecf20Sopenharmony_ci	else if (var->xres_virtual == 1024)
11588c2ecf20Sopenharmony_ci		reg->CR50 |= 0x00;
11598c2ecf20Sopenharmony_ci	else if (var->xres_virtual == 1152)
11608c2ecf20Sopenharmony_ci		reg->CR50 |= 0x01;
11618c2ecf20Sopenharmony_ci	else if (var->xres_virtual == 1280)
11628c2ecf20Sopenharmony_ci		reg->CR50 |= 0xc0;
11638c2ecf20Sopenharmony_ci	else if (var->xres_virtual == 1600)
11648c2ecf20Sopenharmony_ci		reg->CR50 |= 0x81;
11658c2ecf20Sopenharmony_ci	else
11668c2ecf20Sopenharmony_ci		reg->CR50 |= 0xc1;	/* Use GBD */
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (par->chip == S3_SAVAGE2000)
11698c2ecf20Sopenharmony_ci		reg->CR33 = 0x08;
11708c2ecf20Sopenharmony_ci	else
11718c2ecf20Sopenharmony_ci		reg->CR33 = 0x20;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	reg->CRTC[0x17] = 0xeb;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	reg->CR67 |= 1;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
11788c2ecf20Sopenharmony_ci	reg->CR36 = vga_in8(0x3d5, par);
11798c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
11808c2ecf20Sopenharmony_ci	reg->CR68 = vga_in8(0x3d5, par);
11818c2ecf20Sopenharmony_ci	reg->CR69 = 0;
11828c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
11838c2ecf20Sopenharmony_ci	reg->CR6F = vga_in8(0x3d5, par);
11848c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
11858c2ecf20Sopenharmony_ci	reg->CR86 = vga_in8(0x3d5, par);
11868c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
11878c2ecf20Sopenharmony_ci	reg->CR88 = vga_in8(0x3d5, par) | 0x08;
11888c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0xb0, par);
11898c2ecf20Sopenharmony_ci	reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	return 0;
11928c2ecf20Sopenharmony_ci}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci/*
11978c2ecf20Sopenharmony_ci *    Set a single color register. Return != 0 for invalid regno.
11988c2ecf20Sopenharmony_ci */
11998c2ecf20Sopenharmony_cistatic int savagefb_setcolreg(unsigned        regno,
12008c2ecf20Sopenharmony_ci			      unsigned        red,
12018c2ecf20Sopenharmony_ci			      unsigned        green,
12028c2ecf20Sopenharmony_ci			      unsigned        blue,
12038c2ecf20Sopenharmony_ci			      unsigned        transp,
12048c2ecf20Sopenharmony_ci			      struct fb_info *info)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	if (regno >= NR_PALETTE)
12098c2ecf20Sopenharmony_ci		return -EINVAL;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	par->palette[regno].red    = red;
12128c2ecf20Sopenharmony_ci	par->palette[regno].green  = green;
12138c2ecf20Sopenharmony_ci	par->palette[regno].blue   = blue;
12148c2ecf20Sopenharmony_ci	par->palette[regno].transp = transp;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	switch (info->var.bits_per_pixel) {
12178c2ecf20Sopenharmony_ci	case 8:
12188c2ecf20Sopenharmony_ci		vga_out8(0x3c8, regno, par);
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci		vga_out8(0x3c9, red   >> 10, par);
12218c2ecf20Sopenharmony_ci		vga_out8(0x3c9, green >> 10, par);
12228c2ecf20Sopenharmony_ci		vga_out8(0x3c9, blue  >> 10, par);
12238c2ecf20Sopenharmony_ci		break;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	case 16:
12268c2ecf20Sopenharmony_ci		if (regno < 16)
12278c2ecf20Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] =
12288c2ecf20Sopenharmony_ci				((red   & 0xf800)      ) |
12298c2ecf20Sopenharmony_ci				((green & 0xfc00) >>  5) |
12308c2ecf20Sopenharmony_ci				((blue  & 0xf800) >> 11);
12318c2ecf20Sopenharmony_ci		break;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	case 24:
12348c2ecf20Sopenharmony_ci		if (regno < 16)
12358c2ecf20Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] =
12368c2ecf20Sopenharmony_ci				((red    & 0xff00) <<  8) |
12378c2ecf20Sopenharmony_ci				((green  & 0xff00)      ) |
12388c2ecf20Sopenharmony_ci				((blue   & 0xff00) >>  8);
12398c2ecf20Sopenharmony_ci		break;
12408c2ecf20Sopenharmony_ci	case 32:
12418c2ecf20Sopenharmony_ci		if (regno < 16)
12428c2ecf20Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] =
12438c2ecf20Sopenharmony_ci				((transp & 0xff00) << 16) |
12448c2ecf20Sopenharmony_ci				((red    & 0xff00) <<  8) |
12458c2ecf20Sopenharmony_ci				((green  & 0xff00)      ) |
12468c2ecf20Sopenharmony_ci				((blue   & 0xff00) >>  8);
12478c2ecf20Sopenharmony_ci		break;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	default:
12508c2ecf20Sopenharmony_ci		return 1;
12518c2ecf20Sopenharmony_ci	}
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	return 0;
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_cistatic void savagefb_set_par_int(struct savagefb_par  *par, struct savage_reg *reg)
12578c2ecf20Sopenharmony_ci{
12588c2ecf20Sopenharmony_ci	unsigned char tmp, cr3a, cr66, cr67;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	DBG("savagefb_set_par_int");
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	par->SavageWaitIdle(par);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	vga_out8(0x3c2, 0x23, par);
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
12678c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0xa539, par);
12688c2ecf20Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	vgaHWProtect(par, 1);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	/*
12738c2ecf20Sopenharmony_ci	 * Some Savage/MX and /IX systems go nuts when trying to exit the
12748c2ecf20Sopenharmony_ci	 * server after WindowMaker has displayed a gradient background.  I
12758c2ecf20Sopenharmony_ci	 * haven't been able to find what causes it, but a non-destructive
12768c2ecf20Sopenharmony_ci	 * switch to mode 3 here seems to eliminate the issue.
12778c2ecf20Sopenharmony_ci	 */
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	VerticalRetraceWait(par);
12808c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
12818c2ecf20Sopenharmony_ci	cr67 = vga_in8(0x3d5, par);
12828c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x23, par);
12858c2ecf20Sopenharmony_ci	vga_out8(0x3d5, 0x00, par);
12868c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x26, par);
12878c2ecf20Sopenharmony_ci	vga_out8(0x3d5, 0x00, par);
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	/* restore extended regs */
12908c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
12918c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR66, par);
12928c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
12938c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR3A, par);
12948c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x31, par);
12958c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR31, par);
12968c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
12978c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR32, par);
12988c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x58, par);
12998c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR58, par);
13008c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
13018c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR53 & 0x7f, par);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	/* Restore DCLK registers. */
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
13088c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR0E, par);
13098c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
13108c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR0F, par);
13118c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
13128c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR29, par);
13138c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
13148c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR15, par);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	/* Restore flat panel expansion registers. */
13178c2ecf20Sopenharmony_ci	if (par->chip == S3_SAVAGE_MX) {
13188c2ecf20Sopenharmony_ci		int i;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci		for (i = 0; i < 8; i++) {
13218c2ecf20Sopenharmony_ci			vga_out8(0x3c4, 0x54+i, par);
13228c2ecf20Sopenharmony_ci			vga_out8(0x3c5, reg->SR54[i], par);
13238c2ecf20Sopenharmony_ci		}
13248c2ecf20Sopenharmony_ci	}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	vgaHWRestore (par, reg);
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	/* extended mode timing registers */
13298c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x53, par);
13308c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR53, par);
13318c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x5d, par);
13328c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR5D, par);
13338c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x5e, par);
13348c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR5E, par);
13358c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3b, par);
13368c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR3B, par);
13378c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3c, par);
13388c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR3C, par);
13398c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x43, par);
13408c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR43, par);
13418c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x65, par);
13428c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR65, par);
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	/* restore the desired video mode with cr67 */
13458c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
13468c2ecf20Sopenharmony_ci	/* following part not present in X11 driver */
13478c2ecf20Sopenharmony_ci	cr67 = vga_in8(0x3d5, par) & 0xf;
13488c2ecf20Sopenharmony_ci	vga_out8(0x3d5, 0x50 | cr67, par);
13498c2ecf20Sopenharmony_ci	mdelay(10);
13508c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
13518c2ecf20Sopenharmony_ci	/* end of part */
13528c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR67 & ~0x0c, par);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	/* other mode timing and extended regs */
13558c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x34, par);
13568c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR34, par);
13578c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
13588c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR40, par);
13598c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x42, par);
13608c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR42, par);
13618c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x45, par);
13628c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR45, par);
13638c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x50, par);
13648c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR50, par);
13658c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x51, par);
13668c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR51, par);
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	/* memory timings */
13698c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);
13708c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR36, par);
13718c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x60, par);
13728c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR60, par);
13738c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x68, par);
13748c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR68, par);
13758c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
13768c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR69, par);
13778c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x6f, par);
13788c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR6F, par);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x33, par);
13818c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR33, par);
13828c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x86, par);
13838c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR86, par);
13848c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x88, par);
13858c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR88, par);
13868c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x90, par);
13878c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR90, par);
13888c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x91, par);
13898c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR91, par);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	if (par->chip == S3_SAVAGE4) {
13928c2ecf20Sopenharmony_ci		vga_out8(0x3d4, 0xb0, par);
13938c2ecf20Sopenharmony_ci		vga_out8(0x3d5, reg->CRB0, par);
13948c2ecf20Sopenharmony_ci	}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x32, par);
13978c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR32, par);
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	/* unlock extended seq regs */
14008c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
14018c2ecf20Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	/* Restore extended sequencer regs for MCLK. SR10 == 255 indicates
14048c2ecf20Sopenharmony_ci	 * that we should leave the default SR10 and SR11 values there.
14058c2ecf20Sopenharmony_ci	 */
14068c2ecf20Sopenharmony_ci	if (reg->SR10 != 255) {
14078c2ecf20Sopenharmony_ci		vga_out8(0x3c4, 0x10, par);
14088c2ecf20Sopenharmony_ci		vga_out8(0x3c5, reg->SR10, par);
14098c2ecf20Sopenharmony_ci		vga_out8(0x3c4, 0x11, par);
14108c2ecf20Sopenharmony_ci		vga_out8(0x3c5, reg->SR11, par);
14118c2ecf20Sopenharmony_ci	}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* restore extended seq regs for dclk */
14148c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0e, par);
14158c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR0E, par);
14168c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x0f, par);
14178c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR0F, par);
14188c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x12, par);
14198c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR12, par);
14208c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x13, par);
14218c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR13, par);
14228c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x29, par);
14238c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR29, par);
14248c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x18, par);
14258c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR18, par);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	/* load new m, n pll values for dclk & mclk */
14288c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x15, par);
14298c2ecf20Sopenharmony_ci	tmp = vga_in8(0x3c5, par) & ~0x21;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	vga_out8(0x3c5, tmp | 0x03, par);
14328c2ecf20Sopenharmony_ci	vga_out8(0x3c5, tmp | 0x23, par);
14338c2ecf20Sopenharmony_ci	vga_out8(0x3c5, tmp | 0x03, par);
14348c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR15, par);
14358c2ecf20Sopenharmony_ci	udelay(100);
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x30, par);
14388c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR30, par);
14398c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
14408c2ecf20Sopenharmony_ci	vga_out8(0x3c5, reg->SR08, par);
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* now write out cr67 in full, possibly starting STREAMS */
14438c2ecf20Sopenharmony_ci	VerticalRetraceWait(par);
14448c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x67, par);
14458c2ecf20Sopenharmony_ci	vga_out8(0x3d5, reg->CR67, par);
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
14488c2ecf20Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
14498c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x80, par);
14508c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
14518c2ecf20Sopenharmony_ci	cr3a = vga_in8(0x3d5, par);
14528c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a | 0x80, par);
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	if (par->chip != S3_SAVAGE_MX) {
14558c2ecf20Sopenharmony_ci		VerticalRetraceWait(par);
14568c2ecf20Sopenharmony_ci		savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
14578c2ecf20Sopenharmony_ci		par->SavageWaitIdle(par);
14588c2ecf20Sopenharmony_ci		savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
14598c2ecf20Sopenharmony_ci		par->SavageWaitIdle(par);
14608c2ecf20Sopenharmony_ci		savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
14618c2ecf20Sopenharmony_ci		par->SavageWaitIdle(par);
14628c2ecf20Sopenharmony_ci		savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
14668c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66, par);
14678c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3a, par);
14688c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3a, par);
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	SavageSetup2DEngine(par);
14718c2ecf20Sopenharmony_ci	vgaHWProtect(par, 0);
14728c2ecf20Sopenharmony_ci}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_cistatic void savagefb_update_start(struct savagefb_par *par, int base)
14758c2ecf20Sopenharmony_ci{
14768c2ecf20Sopenharmony_ci	/* program the start address registers */
14778c2ecf20Sopenharmony_ci	vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par);
14788c2ecf20Sopenharmony_ci	vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par);
14798c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x69, par);
14808c2ecf20Sopenharmony_ci	vga_out8(0x3d5, (base & 0x7f0000) >> 16, par);
14818c2ecf20Sopenharmony_ci}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_cistatic void savagefb_set_fix(struct fb_info *info)
14858c2ecf20Sopenharmony_ci{
14868c2ecf20Sopenharmony_ci	info->fix.line_length = info->var.xres_virtual *
14878c2ecf20Sopenharmony_ci		info->var.bits_per_pixel / 8;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	if (info->var.bits_per_pixel == 8) {
14908c2ecf20Sopenharmony_ci		info->fix.visual      = FB_VISUAL_PSEUDOCOLOR;
14918c2ecf20Sopenharmony_ci		info->fix.xpanstep    = 4;
14928c2ecf20Sopenharmony_ci	} else {
14938c2ecf20Sopenharmony_ci		info->fix.visual      = FB_VISUAL_TRUECOLOR;
14948c2ecf20Sopenharmony_ci		info->fix.xpanstep    = 2;
14958c2ecf20Sopenharmony_ci	}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci}
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_cistatic int savagefb_set_par(struct fb_info *info)
15008c2ecf20Sopenharmony_ci{
15018c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
15028c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
15038c2ecf20Sopenharmony_ci	int err;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	DBG("savagefb_set_par");
15068c2ecf20Sopenharmony_ci	err = savagefb_decode_var(var, par, &par->state);
15078c2ecf20Sopenharmony_ci	if (err)
15088c2ecf20Sopenharmony_ci		return err;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	if (par->dacSpeedBpp <= 0) {
15118c2ecf20Sopenharmony_ci		if (var->bits_per_pixel > 24)
15128c2ecf20Sopenharmony_ci			par->dacSpeedBpp = par->clock[3];
15138c2ecf20Sopenharmony_ci		else if (var->bits_per_pixel >= 24)
15148c2ecf20Sopenharmony_ci			par->dacSpeedBpp = par->clock[2];
15158c2ecf20Sopenharmony_ci		else if ((var->bits_per_pixel > 8) && (var->bits_per_pixel < 24))
15168c2ecf20Sopenharmony_ci			par->dacSpeedBpp = par->clock[1];
15178c2ecf20Sopenharmony_ci		else if (var->bits_per_pixel <= 8)
15188c2ecf20Sopenharmony_ci			par->dacSpeedBpp = par->clock[0];
15198c2ecf20Sopenharmony_ci	}
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	/* Set ramdac limits */
15228c2ecf20Sopenharmony_ci	par->maxClock = par->dacSpeedBpp;
15238c2ecf20Sopenharmony_ci	par->minClock = 10000;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	savagefb_set_par_int(par, &par->state);
15268c2ecf20Sopenharmony_ci	fb_set_cmap(&info->cmap, info);
15278c2ecf20Sopenharmony_ci	savagefb_set_fix(info);
15288c2ecf20Sopenharmony_ci	savagefb_set_clip(info);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	SavagePrintRegs(par);
15318c2ecf20Sopenharmony_ci	return 0;
15328c2ecf20Sopenharmony_ci}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci/*
15358c2ecf20Sopenharmony_ci *    Pan or Wrap the Display
15368c2ecf20Sopenharmony_ci */
15378c2ecf20Sopenharmony_cistatic int savagefb_pan_display(struct fb_var_screeninfo *var,
15388c2ecf20Sopenharmony_ci				struct fb_info           *info)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
15418c2ecf20Sopenharmony_ci	int base;
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	base = (var->yoffset * info->fix.line_length
15448c2ecf20Sopenharmony_ci	     + (var->xoffset & ~1) * ((info->var.bits_per_pixel+7) / 8)) >> 2;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	savagefb_update_start(par, base);
15478c2ecf20Sopenharmony_ci	return 0;
15488c2ecf20Sopenharmony_ci}
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_cistatic int savagefb_blank(int blank, struct fb_info *info)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
15538c2ecf20Sopenharmony_ci	u8 sr8 = 0, srd = 0;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	if (par->display_type == DISP_CRT) {
15568c2ecf20Sopenharmony_ci		vga_out8(0x3c4, 0x08, par);
15578c2ecf20Sopenharmony_ci		sr8 = vga_in8(0x3c5, par);
15588c2ecf20Sopenharmony_ci		sr8 |= 0x06;
15598c2ecf20Sopenharmony_ci		vga_out8(0x3c5, sr8, par);
15608c2ecf20Sopenharmony_ci		vga_out8(0x3c4, 0x0d, par);
15618c2ecf20Sopenharmony_ci		srd = vga_in8(0x3c5, par);
15628c2ecf20Sopenharmony_ci		srd &= 0x50;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci		switch (blank) {
15658c2ecf20Sopenharmony_ci		case FB_BLANK_UNBLANK:
15668c2ecf20Sopenharmony_ci		case FB_BLANK_NORMAL:
15678c2ecf20Sopenharmony_ci			break;
15688c2ecf20Sopenharmony_ci		case FB_BLANK_VSYNC_SUSPEND:
15698c2ecf20Sopenharmony_ci			srd |= 0x10;
15708c2ecf20Sopenharmony_ci			break;
15718c2ecf20Sopenharmony_ci		case FB_BLANK_HSYNC_SUSPEND:
15728c2ecf20Sopenharmony_ci			srd |= 0x40;
15738c2ecf20Sopenharmony_ci			break;
15748c2ecf20Sopenharmony_ci		case FB_BLANK_POWERDOWN:
15758c2ecf20Sopenharmony_ci			srd |= 0x50;
15768c2ecf20Sopenharmony_ci			break;
15778c2ecf20Sopenharmony_ci		}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci		vga_out8(0x3c4, 0x0d, par);
15808c2ecf20Sopenharmony_ci		vga_out8(0x3c5, srd, par);
15818c2ecf20Sopenharmony_ci	}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	if (par->display_type == DISP_LCD ||
15848c2ecf20Sopenharmony_ci	    par->display_type == DISP_DFP) {
15858c2ecf20Sopenharmony_ci		switch(blank) {
15868c2ecf20Sopenharmony_ci		case FB_BLANK_UNBLANK:
15878c2ecf20Sopenharmony_ci		case FB_BLANK_NORMAL:
15888c2ecf20Sopenharmony_ci			vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
15898c2ecf20Sopenharmony_ci			vga_out8(0x3c5, vga_in8(0x3c5, par) | 0x10, par);
15908c2ecf20Sopenharmony_ci			break;
15918c2ecf20Sopenharmony_ci		case FB_BLANK_VSYNC_SUSPEND:
15928c2ecf20Sopenharmony_ci		case FB_BLANK_HSYNC_SUSPEND:
15938c2ecf20Sopenharmony_ci		case FB_BLANK_POWERDOWN:
15948c2ecf20Sopenharmony_ci			vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
15958c2ecf20Sopenharmony_ci			vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x10, par);
15968c2ecf20Sopenharmony_ci			break;
15978c2ecf20Sopenharmony_ci		}
15988c2ecf20Sopenharmony_ci	}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	return (blank == FB_BLANK_NORMAL) ? 1 : 0;
16018c2ecf20Sopenharmony_ci}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_cistatic int savagefb_open(struct fb_info *info, int user)
16048c2ecf20Sopenharmony_ci{
16058c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	mutex_lock(&par->open_lock);
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	if (!par->open_count) {
16108c2ecf20Sopenharmony_ci		memset(&par->vgastate, 0, sizeof(par->vgastate));
16118c2ecf20Sopenharmony_ci		par->vgastate.flags = VGA_SAVE_CMAP | VGA_SAVE_FONTS |
16128c2ecf20Sopenharmony_ci			VGA_SAVE_MODE;
16138c2ecf20Sopenharmony_ci		par->vgastate.vgabase = par->mmio.vbase + 0x8000;
16148c2ecf20Sopenharmony_ci		save_vga(&par->vgastate);
16158c2ecf20Sopenharmony_ci		savage_get_default_par(par, &par->initial);
16168c2ecf20Sopenharmony_ci	}
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	par->open_count++;
16198c2ecf20Sopenharmony_ci	mutex_unlock(&par->open_lock);
16208c2ecf20Sopenharmony_ci	return 0;
16218c2ecf20Sopenharmony_ci}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_cistatic int savagefb_release(struct fb_info *info, int user)
16248c2ecf20Sopenharmony_ci{
16258c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	mutex_lock(&par->open_lock);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	if (par->open_count == 1) {
16308c2ecf20Sopenharmony_ci		savage_set_default_par(par, &par->initial);
16318c2ecf20Sopenharmony_ci		restore_vga(&par->vgastate);
16328c2ecf20Sopenharmony_ci	}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	par->open_count--;
16358c2ecf20Sopenharmony_ci	mutex_unlock(&par->open_lock);
16368c2ecf20Sopenharmony_ci	return 0;
16378c2ecf20Sopenharmony_ci}
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_cistatic const struct fb_ops savagefb_ops = {
16408c2ecf20Sopenharmony_ci	.owner          = THIS_MODULE,
16418c2ecf20Sopenharmony_ci	.fb_open        = savagefb_open,
16428c2ecf20Sopenharmony_ci	.fb_release     = savagefb_release,
16438c2ecf20Sopenharmony_ci	.fb_check_var   = savagefb_check_var,
16448c2ecf20Sopenharmony_ci	.fb_set_par     = savagefb_set_par,
16458c2ecf20Sopenharmony_ci	.fb_setcolreg   = savagefb_setcolreg,
16468c2ecf20Sopenharmony_ci	.fb_pan_display = savagefb_pan_display,
16478c2ecf20Sopenharmony_ci	.fb_blank       = savagefb_blank,
16488c2ecf20Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL)
16498c2ecf20Sopenharmony_ci	.fb_fillrect    = savagefb_fillrect,
16508c2ecf20Sopenharmony_ci	.fb_copyarea    = savagefb_copyarea,
16518c2ecf20Sopenharmony_ci	.fb_imageblit   = savagefb_imageblit,
16528c2ecf20Sopenharmony_ci	.fb_sync        = savagefb_sync,
16538c2ecf20Sopenharmony_ci#else
16548c2ecf20Sopenharmony_ci	.fb_fillrect    = cfb_fillrect,
16558c2ecf20Sopenharmony_ci	.fb_copyarea    = cfb_copyarea,
16568c2ecf20Sopenharmony_ci	.fb_imageblit   = cfb_imageblit,
16578c2ecf20Sopenharmony_ci#endif
16588c2ecf20Sopenharmony_ci};
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo savagefb_var800x600x8 = {
16638c2ecf20Sopenharmony_ci	.accel_flags =	FB_ACCELF_TEXT,
16648c2ecf20Sopenharmony_ci	.xres =		800,
16658c2ecf20Sopenharmony_ci	.yres =		600,
16668c2ecf20Sopenharmony_ci	.xres_virtual =  800,
16678c2ecf20Sopenharmony_ci	.yres_virtual =  600,
16688c2ecf20Sopenharmony_ci	.bits_per_pixel = 8,
16698c2ecf20Sopenharmony_ci	.pixclock =	25000,
16708c2ecf20Sopenharmony_ci	.left_margin =	88,
16718c2ecf20Sopenharmony_ci	.right_margin =	40,
16728c2ecf20Sopenharmony_ci	.upper_margin =	23,
16738c2ecf20Sopenharmony_ci	.lower_margin =	1,
16748c2ecf20Sopenharmony_ci	.hsync_len =	128,
16758c2ecf20Sopenharmony_ci	.vsync_len =	4,
16768c2ecf20Sopenharmony_ci	.sync =		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
16778c2ecf20Sopenharmony_ci	.vmode =	FB_VMODE_NONINTERLACED
16788c2ecf20Sopenharmony_ci};
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_cistatic void savage_enable_mmio(struct savagefb_par *par)
16818c2ecf20Sopenharmony_ci{
16828c2ecf20Sopenharmony_ci	unsigned char val;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	DBG("savage_enable_mmio\n");
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	val = vga_in8(0x3c3, par);
16878c2ecf20Sopenharmony_ci	vga_out8(0x3c3, val | 0x01, par);
16888c2ecf20Sopenharmony_ci	val = vga_in8(0x3cc, par);
16898c2ecf20Sopenharmony_ci	vga_out8(0x3c2, val | 0x01, par);
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci	if (par->chip >= S3_SAVAGE4) {
16928c2ecf20Sopenharmony_ci		vga_out8(0x3d4, 0x40, par);
16938c2ecf20Sopenharmony_ci		val = vga_in8(0x3d5, par);
16948c2ecf20Sopenharmony_ci		vga_out8(0x3d5, val | 1, par);
16958c2ecf20Sopenharmony_ci	}
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_cistatic void savage_disable_mmio(struct savagefb_par *par)
17008c2ecf20Sopenharmony_ci{
17018c2ecf20Sopenharmony_ci	unsigned char val;
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	DBG("savage_disable_mmio\n");
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	if (par->chip >= S3_SAVAGE4) {
17068c2ecf20Sopenharmony_ci		vga_out8(0x3d4, 0x40, par);
17078c2ecf20Sopenharmony_ci		val = vga_in8(0x3d5, par);
17088c2ecf20Sopenharmony_ci		vga_out8(0x3d5, val | 1, par);
17098c2ecf20Sopenharmony_ci	}
17108c2ecf20Sopenharmony_ci}
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_cistatic int savage_map_mmio(struct fb_info *info)
17148c2ecf20Sopenharmony_ci{
17158c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
17168c2ecf20Sopenharmony_ci	DBG("savage_map_mmio");
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	if (S3_SAVAGE3D_SERIES(par->chip))
17198c2ecf20Sopenharmony_ci		par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
17208c2ecf20Sopenharmony_ci			SAVAGE_NEWMMIO_REGBASE_S3;
17218c2ecf20Sopenharmony_ci	else
17228c2ecf20Sopenharmony_ci		par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
17238c2ecf20Sopenharmony_ci			SAVAGE_NEWMMIO_REGBASE_S4;
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	par->mmio.len = SAVAGE_NEWMMIO_REGSIZE;
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	par->mmio.vbase = ioremap(par->mmio.pbase, par->mmio.len);
17288c2ecf20Sopenharmony_ci	if (!par->mmio.vbase) {
17298c2ecf20Sopenharmony_ci		printk("savagefb: unable to map memory mapped IO\n");
17308c2ecf20Sopenharmony_ci		return -ENOMEM;
17318c2ecf20Sopenharmony_ci	} else
17328c2ecf20Sopenharmony_ci		printk(KERN_INFO "savagefb: mapped io at %p\n",
17338c2ecf20Sopenharmony_ci			par->mmio.vbase);
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	info->fix.mmio_start = par->mmio.pbase;
17368c2ecf20Sopenharmony_ci	info->fix.mmio_len   = par->mmio.len;
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET);
17398c2ecf20Sopenharmony_ci	par->bci_ptr  = 0;
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	savage_enable_mmio(par);
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	return 0;
17448c2ecf20Sopenharmony_ci}
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_cistatic void savage_unmap_mmio(struct fb_info *info)
17478c2ecf20Sopenharmony_ci{
17488c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
17498c2ecf20Sopenharmony_ci	DBG("savage_unmap_mmio");
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	savage_disable_mmio(par);
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	if (par->mmio.vbase) {
17548c2ecf20Sopenharmony_ci		iounmap(par->mmio.vbase);
17558c2ecf20Sopenharmony_ci		par->mmio.vbase = NULL;
17568c2ecf20Sopenharmony_ci	}
17578c2ecf20Sopenharmony_ci}
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_cistatic int savage_map_video(struct fb_info *info, int video_len)
17608c2ecf20Sopenharmony_ci{
17618c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
17628c2ecf20Sopenharmony_ci	int resource;
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	DBG("savage_map_video");
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	if (S3_SAVAGE3D_SERIES(par->chip))
17678c2ecf20Sopenharmony_ci		resource = 0;
17688c2ecf20Sopenharmony_ci	else
17698c2ecf20Sopenharmony_ci		resource = 1;
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	par->video.pbase = pci_resource_start(par->pcidev, resource);
17728c2ecf20Sopenharmony_ci	par->video.len   = video_len;
17738c2ecf20Sopenharmony_ci	par->video.vbase = ioremap_wc(par->video.pbase, par->video.len);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	if (!par->video.vbase) {
17768c2ecf20Sopenharmony_ci		printk("savagefb: unable to map screen memory\n");
17778c2ecf20Sopenharmony_ci		return -ENOMEM;
17788c2ecf20Sopenharmony_ci	} else
17798c2ecf20Sopenharmony_ci		printk(KERN_INFO "savagefb: mapped framebuffer at %p, "
17808c2ecf20Sopenharmony_ci		       "pbase == %x\n", par->video.vbase, par->video.pbase);
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	info->fix.smem_start = par->video.pbase;
17838c2ecf20Sopenharmony_ci	info->fix.smem_len   = par->video.len - par->cob_size;
17848c2ecf20Sopenharmony_ci	info->screen_base    = par->video.vbase;
17858c2ecf20Sopenharmony_ci	par->video.wc_cookie = arch_phys_wc_add(par->video.pbase, video_len);
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	/* Clear framebuffer, it's all white in memory after boot */
17888c2ecf20Sopenharmony_ci	memset_io(par->video.vbase, 0, par->video.len);
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci	return 0;
17918c2ecf20Sopenharmony_ci}
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_cistatic void savage_unmap_video(struct fb_info *info)
17948c2ecf20Sopenharmony_ci{
17958c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	DBG("savage_unmap_video");
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	if (par->video.vbase) {
18008c2ecf20Sopenharmony_ci		arch_phys_wc_del(par->video.wc_cookie);
18018c2ecf20Sopenharmony_ci		iounmap(par->video.vbase);
18028c2ecf20Sopenharmony_ci		par->video.vbase = NULL;
18038c2ecf20Sopenharmony_ci		info->screen_base = NULL;
18048c2ecf20Sopenharmony_ci	}
18058c2ecf20Sopenharmony_ci}
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_cistatic int savage_init_hw(struct savagefb_par *par)
18088c2ecf20Sopenharmony_ci{
18098c2ecf20Sopenharmony_ci	unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp;
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_ci	static unsigned char RamSavage3D[] = { 8, 4, 4, 2 };
18128c2ecf20Sopenharmony_ci	static unsigned char RamSavage4[] =  { 2, 4, 8, 12, 16, 32, 64, 32 };
18138c2ecf20Sopenharmony_ci	static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
18148c2ecf20Sopenharmony_ci	static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
18158c2ecf20Sopenharmony_ci	int videoRam, videoRambytes, dvi;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	DBG("savage_init_hw");
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	/* unprotect CRTC[0-7] */
18208c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x11, par);
18218c2ecf20Sopenharmony_ci	tmp = vga_in8(0x3d5, par);
18228c2ecf20Sopenharmony_ci	vga_out8(0x3d5, tmp & 0x7f, par);
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	/* unlock extended regs */
18258c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
18268c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0xa039, par);
18278c2ecf20Sopenharmony_ci	vga_out16(0x3c4, 0x0608, par);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x40, par);
18308c2ecf20Sopenharmony_ci	tmp = vga_in8(0x3d5, par);
18318c2ecf20Sopenharmony_ci	vga_out8(0x3d5, tmp & ~0x01, par);
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	/* unlock sys regs */
18348c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x38, par);
18358c2ecf20Sopenharmony_ci	vga_out8(0x3d5, 0x48, par);
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	/* Unlock system registers. */
18388c2ecf20Sopenharmony_ci	vga_out16(0x3d4, 0x4838, par);
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	/* Next go on to detect amount of installed ram */
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x36, par);            /* for register CR36 (CONFG_REG1), */
18438c2ecf20Sopenharmony_ci	config1 = vga_in8(0x3d5, par);    /* get amount of vram installed */
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	/* Compute the amount of video memory and offscreen memory. */
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	switch  (par->chip) {
18488c2ecf20Sopenharmony_ci	case S3_SAVAGE3D:
18498c2ecf20Sopenharmony_ci		videoRam = RamSavage3D[(config1 & 0xC0) >> 6 ] * 1024;
18508c2ecf20Sopenharmony_ci		break;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	case S3_SAVAGE4:
18538c2ecf20Sopenharmony_ci		/*
18548c2ecf20Sopenharmony_ci		 * The Savage4 has one ugly special case to consider.  On
18558c2ecf20Sopenharmony_ci		 * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB
18568c2ecf20Sopenharmony_ci		 * when it really means 8MB.  Why do it the same when you
18578c2ecf20Sopenharmony_ci		 * can do it different...
18588c2ecf20Sopenharmony_ci		 */
18598c2ecf20Sopenharmony_ci		vga_out8(0x3d4, 0x68, par);	/* memory control 1 */
18608c2ecf20Sopenharmony_ci		if ((vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6))
18618c2ecf20Sopenharmony_ci			RamSavage4[1] = 8;
18628c2ecf20Sopenharmony_ci		fallthrough;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	case S3_SAVAGE2000:
18658c2ecf20Sopenharmony_ci		videoRam = RamSavage4[(config1 & 0xE0) >> 5] * 1024;
18668c2ecf20Sopenharmony_ci		break;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	case S3_SAVAGE_MX:
18698c2ecf20Sopenharmony_ci	case S3_SUPERSAVAGE:
18708c2ecf20Sopenharmony_ci		videoRam = RamSavageMX[(config1 & 0x0E) >> 1] * 1024;
18718c2ecf20Sopenharmony_ci		break;
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	case S3_PROSAVAGE:
18748c2ecf20Sopenharmony_ci	case S3_PROSAVAGEDDR:
18758c2ecf20Sopenharmony_ci	case S3_TWISTER:
18768c2ecf20Sopenharmony_ci		videoRam = RamSavageNB[(config1 & 0xE0) >> 5] * 1024;
18778c2ecf20Sopenharmony_ci		break;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	default:
18808c2ecf20Sopenharmony_ci		/* How did we get here? */
18818c2ecf20Sopenharmony_ci		videoRam = 0;
18828c2ecf20Sopenharmony_ci		break;
18838c2ecf20Sopenharmony_ci	}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	videoRambytes = videoRam * 1024;
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	printk(KERN_INFO "savagefb: probed videoram:  %dk\n", videoRam);
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	/* reset graphics engine to avoid memory corruption */
18908c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
18918c2ecf20Sopenharmony_ci	cr66 = vga_in8(0x3d5, par);
18928c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66 | 0x02, par);
18938c2ecf20Sopenharmony_ci	usleep_range(10000, 11000);
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x66, par);
18968c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr66 & ~0x02, par);	/* clear reset flag */
18978c2ecf20Sopenharmony_ci	usleep_range(10000, 11000);
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	/*
19018c2ecf20Sopenharmony_ci	 * reset memory interface, 3D engine, AGP master, PCI master,
19028c2ecf20Sopenharmony_ci	 * master engine unit, motion compensation/LPB
19038c2ecf20Sopenharmony_ci	 */
19048c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3f, par);
19058c2ecf20Sopenharmony_ci	cr3f = vga_in8(0x3d5, par);
19068c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3f | 0x08, par);
19078c2ecf20Sopenharmony_ci	usleep_range(10000, 11000);
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci	vga_out8(0x3d4, 0x3f, par);
19108c2ecf20Sopenharmony_ci	vga_out8(0x3d5, cr3f & ~0x08, par);	/* clear reset flags */
19118c2ecf20Sopenharmony_ci	usleep_range(10000, 11000);
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_ci	/* Savage ramdac speeds */
19148c2ecf20Sopenharmony_ci	par->numClocks = 4;
19158c2ecf20Sopenharmony_ci	par->clock[0] = 250000;
19168c2ecf20Sopenharmony_ci	par->clock[1] = 250000;
19178c2ecf20Sopenharmony_ci	par->clock[2] = 220000;
19188c2ecf20Sopenharmony_ci	par->clock[3] = 220000;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	/* detect current mclk */
19218c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
19228c2ecf20Sopenharmony_ci	sr8 = vga_in8(0x3c5, par);
19238c2ecf20Sopenharmony_ci	vga_out8(0x3c5, 0x06, par);
19248c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x10, par);
19258c2ecf20Sopenharmony_ci	n = vga_in8(0x3c5, par);
19268c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x11, par);
19278c2ecf20Sopenharmony_ci	m = vga_in8(0x3c5, par);
19288c2ecf20Sopenharmony_ci	vga_out8(0x3c4, 0x08, par);
19298c2ecf20Sopenharmony_ci	vga_out8(0x3c5, sr8, par);
19308c2ecf20Sopenharmony_ci	m &= 0x7f;
19318c2ecf20Sopenharmony_ci	n1 = n & 0x1f;
19328c2ecf20Sopenharmony_ci	n2 = (n >> 5) & 0x03;
19338c2ecf20Sopenharmony_ci	par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
19348c2ecf20Sopenharmony_ci	printk(KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
19358c2ecf20Sopenharmony_ci		par->MCLK);
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	/* check for DVI/flat panel */
19388c2ecf20Sopenharmony_ci	dvi = 0;
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_ci	if (par->chip == S3_SAVAGE4) {
19418c2ecf20Sopenharmony_ci		unsigned char sr30 = 0x00;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci		vga_out8(0x3c4, 0x30, par);
19448c2ecf20Sopenharmony_ci		/* clear bit 1 */
19458c2ecf20Sopenharmony_ci		vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x02, par);
19468c2ecf20Sopenharmony_ci		sr30 = vga_in8(0x3c5, par);
19478c2ecf20Sopenharmony_ci		if (sr30 & 0x02 /*0x04 */) {
19488c2ecf20Sopenharmony_ci			dvi = 1;
19498c2ecf20Sopenharmony_ci			printk("savagefb: Digital Flat Panel Detected\n");
19508c2ecf20Sopenharmony_ci		}
19518c2ecf20Sopenharmony_ci	}
19528c2ecf20Sopenharmony_ci
19538c2ecf20Sopenharmony_ci	if ((S3_SAVAGE_MOBILE_SERIES(par->chip) ||
19548c2ecf20Sopenharmony_ci	     S3_MOBILE_TWISTER_SERIES(par->chip)) && !par->crtonly)
19558c2ecf20Sopenharmony_ci		par->display_type = DISP_LCD;
19568c2ecf20Sopenharmony_ci	else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi))
19578c2ecf20Sopenharmony_ci		par->display_type = DISP_DFP;
19588c2ecf20Sopenharmony_ci	else
19598c2ecf20Sopenharmony_ci		par->display_type = DISP_CRT;
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	/* Check LCD panel parrmation */
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci	if (par->display_type == DISP_LCD) {
19648c2ecf20Sopenharmony_ci		unsigned char cr6b = VGArCR(0x6b, par);
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci		int panelX = (VGArSEQ(0x61, par) +
19678c2ecf20Sopenharmony_ci			      ((VGArSEQ(0x66, par) & 0x02) << 7) + 1) * 8;
19688c2ecf20Sopenharmony_ci		int panelY = (VGArSEQ(0x69, par) +
19698c2ecf20Sopenharmony_ci			      ((VGArSEQ(0x6e, par) & 0x70) << 4) + 1);
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci		char * sTechnology = "Unknown";
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci		/* OK, I admit it.  I don't know how to limit the max dot clock
19748c2ecf20Sopenharmony_ci		 * for LCD panels of various sizes.  I thought I copied the
19758c2ecf20Sopenharmony_ci		 * formula from the BIOS, but many users have parrmed me of
19768c2ecf20Sopenharmony_ci		 * my folly.
19778c2ecf20Sopenharmony_ci		 *
19788c2ecf20Sopenharmony_ci		 * Instead, I'll abandon any attempt to automatically limit the
19798c2ecf20Sopenharmony_ci		 * clock, and add an LCDClock option to XF86Config.  Some day,
19808c2ecf20Sopenharmony_ci		 * I should come back to this.
19818c2ecf20Sopenharmony_ci		 */
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci		enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */
19848c2ecf20Sopenharmony_ci			ActiveCRT = 0x01,
19858c2ecf20Sopenharmony_ci			ActiveLCD = 0x02,
19868c2ecf20Sopenharmony_ci			ActiveTV = 0x04,
19878c2ecf20Sopenharmony_ci			ActiveCRT2 = 0x20,
19888c2ecf20Sopenharmony_ci			ActiveDUO = 0x80
19898c2ecf20Sopenharmony_ci		};
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci		if ((VGArSEQ(0x39, par) & 0x03) == 0) {
19928c2ecf20Sopenharmony_ci			sTechnology = "TFT";
19938c2ecf20Sopenharmony_ci		} else if ((VGArSEQ(0x30, par) & 0x01) == 0) {
19948c2ecf20Sopenharmony_ci			sTechnology = "DSTN";
19958c2ecf20Sopenharmony_ci		} else 	{
19968c2ecf20Sopenharmony_ci			sTechnology = "STN";
19978c2ecf20Sopenharmony_ci		}
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci		printk(KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n",
20008c2ecf20Sopenharmony_ci		       panelX, panelY, sTechnology,
20018c2ecf20Sopenharmony_ci		       cr6b & ActiveLCD ? "and active" : "but not active");
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci		if (cr6b & ActiveLCD) 	{
20048c2ecf20Sopenharmony_ci			/*
20058c2ecf20Sopenharmony_ci			 * If the LCD is active and panel expansion is enabled,
20068c2ecf20Sopenharmony_ci			 * we probably want to kill the HW cursor.
20078c2ecf20Sopenharmony_ci			 */
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci			printk(KERN_INFO "savagefb: Limiting video mode to "
20108c2ecf20Sopenharmony_ci				"%dx%d\n", panelX, panelY);
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci			par->SavagePanelWidth = panelX;
20138c2ecf20Sopenharmony_ci			par->SavagePanelHeight = panelY;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci		} else
20168c2ecf20Sopenharmony_ci			par->display_type = DISP_CRT;
20178c2ecf20Sopenharmony_ci	}
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci	savage_get_default_par(par, &par->state);
20208c2ecf20Sopenharmony_ci	par->save = par->state;
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	if (S3_SAVAGE4_SERIES(par->chip)) {
20238c2ecf20Sopenharmony_ci		/*
20248c2ecf20Sopenharmony_ci		 * The Savage4 and ProSavage have COB coherency bugs which
20258c2ecf20Sopenharmony_ci		 * render the buffer useless.  We disable it.
20268c2ecf20Sopenharmony_ci		 */
20278c2ecf20Sopenharmony_ci		par->cob_index = 2;
20288c2ecf20Sopenharmony_ci		par->cob_size = 0x8000 << par->cob_index;
20298c2ecf20Sopenharmony_ci		par->cob_offset = videoRambytes;
20308c2ecf20Sopenharmony_ci	} else {
20318c2ecf20Sopenharmony_ci		/* We use 128kB for the COB on all chips. */
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci		par->cob_index  = 7;
20348c2ecf20Sopenharmony_ci		par->cob_size   = 0x400 << par->cob_index;
20358c2ecf20Sopenharmony_ci		par->cob_offset = videoRambytes - par->cob_size;
20368c2ecf20Sopenharmony_ci	}
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	return videoRambytes;
20398c2ecf20Sopenharmony_ci}
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_cistatic int savage_init_fb_info(struct fb_info *info, struct pci_dev *dev,
20428c2ecf20Sopenharmony_ci			       const struct pci_device_id *id)
20438c2ecf20Sopenharmony_ci{
20448c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
20458c2ecf20Sopenharmony_ci	int err = 0;
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	par->pcidev  = dev;
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci	info->fix.type	   = FB_TYPE_PACKED_PIXELS;
20508c2ecf20Sopenharmony_ci	info->fix.type_aux	   = 0;
20518c2ecf20Sopenharmony_ci	info->fix.ypanstep	   = 1;
20528c2ecf20Sopenharmony_ci	info->fix.ywrapstep   = 0;
20538c2ecf20Sopenharmony_ci	info->fix.accel       = id->driver_data;
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	switch (info->fix.accel) {
20568c2ecf20Sopenharmony_ci	case FB_ACCEL_SUPERSAVAGE:
20578c2ecf20Sopenharmony_ci		par->chip = S3_SUPERSAVAGE;
20588c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "SuperSavage");
20598c2ecf20Sopenharmony_ci		break;
20608c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE4:
20618c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE4;
20628c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage4");
20638c2ecf20Sopenharmony_ci		break;
20648c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE3D:
20658c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE3D;
20668c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage3D");
20678c2ecf20Sopenharmony_ci		break;
20688c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE3D_MV:
20698c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE3D;
20708c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage3D-MV");
20718c2ecf20Sopenharmony_ci		break;
20728c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE2000:
20738c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE2000;
20748c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage2000");
20758c2ecf20Sopenharmony_ci		break;
20768c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE_MX_MV:
20778c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
20788c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/MX-MV");
20798c2ecf20Sopenharmony_ci		break;
20808c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE_MX:
20818c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
20828c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/MX");
20838c2ecf20Sopenharmony_ci		break;
20848c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE_IX_MV:
20858c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
20868c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/IX-MV");
20878c2ecf20Sopenharmony_ci		break;
20888c2ecf20Sopenharmony_ci	case FB_ACCEL_SAVAGE_IX:
20898c2ecf20Sopenharmony_ci		par->chip = S3_SAVAGE_MX;
20908c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "Savage/IX");
20918c2ecf20Sopenharmony_ci		break;
20928c2ecf20Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_PM:
20938c2ecf20Sopenharmony_ci		par->chip = S3_PROSAVAGE;
20948c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavagePM");
20958c2ecf20Sopenharmony_ci		break;
20968c2ecf20Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_KM:
20978c2ecf20Sopenharmony_ci		par->chip = S3_PROSAVAGE;
20988c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavageKM");
20998c2ecf20Sopenharmony_ci		break;
21008c2ecf20Sopenharmony_ci	case FB_ACCEL_S3TWISTER_P:
21018c2ecf20Sopenharmony_ci		par->chip = S3_TWISTER;
21028c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "TwisterP");
21038c2ecf20Sopenharmony_ci		break;
21048c2ecf20Sopenharmony_ci	case FB_ACCEL_S3TWISTER_K:
21058c2ecf20Sopenharmony_ci		par->chip = S3_TWISTER;
21068c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "TwisterK");
21078c2ecf20Sopenharmony_ci		break;
21088c2ecf20Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_DDR:
21098c2ecf20Sopenharmony_ci		par->chip = S3_PROSAVAGEDDR;
21108c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavageDDR");
21118c2ecf20Sopenharmony_ci		break;
21128c2ecf20Sopenharmony_ci	case FB_ACCEL_PROSAVAGE_DDRK:
21138c2ecf20Sopenharmony_ci		par->chip = S3_PROSAVAGEDDR;
21148c2ecf20Sopenharmony_ci		snprintf(info->fix.id, 16, "ProSavage8");
21158c2ecf20Sopenharmony_ci		break;
21168c2ecf20Sopenharmony_ci	}
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	if (S3_SAVAGE3D_SERIES(par->chip)) {
21198c2ecf20Sopenharmony_ci		par->SavageWaitIdle = savage3D_waitidle;
21208c2ecf20Sopenharmony_ci		par->SavageWaitFifo = savage3D_waitfifo;
21218c2ecf20Sopenharmony_ci	} else if (S3_SAVAGE4_SERIES(par->chip) ||
21228c2ecf20Sopenharmony_ci		   S3_SUPERSAVAGE == par->chip) {
21238c2ecf20Sopenharmony_ci		par->SavageWaitIdle = savage4_waitidle;
21248c2ecf20Sopenharmony_ci		par->SavageWaitFifo = savage4_waitfifo;
21258c2ecf20Sopenharmony_ci	} else {
21268c2ecf20Sopenharmony_ci		par->SavageWaitIdle = savage2000_waitidle;
21278c2ecf20Sopenharmony_ci		par->SavageWaitFifo = savage2000_waitfifo;
21288c2ecf20Sopenharmony_ci	}
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci	info->var.nonstd      = 0;
21318c2ecf20Sopenharmony_ci	info->var.activate    = FB_ACTIVATE_NOW;
21328c2ecf20Sopenharmony_ci	info->var.width       = -1;
21338c2ecf20Sopenharmony_ci	info->var.height      = -1;
21348c2ecf20Sopenharmony_ci	info->var.accel_flags = 0;
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci	info->fbops          = &savagefb_ops;
21378c2ecf20Sopenharmony_ci	info->flags          = FBINFO_DEFAULT |
21388c2ecf20Sopenharmony_ci		               FBINFO_HWACCEL_YPAN |
21398c2ecf20Sopenharmony_ci		               FBINFO_HWACCEL_XPAN;
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci	info->pseudo_palette = par->pseudo_palette;
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL)
21448c2ecf20Sopenharmony_ci	/* FIFO size + padding for commands */
21458c2ecf20Sopenharmony_ci	info->pixmap.addr = kcalloc(8, 1024, GFP_KERNEL);
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	err = -ENOMEM;
21488c2ecf20Sopenharmony_ci	if (info->pixmap.addr) {
21498c2ecf20Sopenharmony_ci		info->pixmap.size = 8*1024;
21508c2ecf20Sopenharmony_ci		info->pixmap.scan_align = 4;
21518c2ecf20Sopenharmony_ci		info->pixmap.buf_align = 4;
21528c2ecf20Sopenharmony_ci		info->pixmap.access_align = 32;
21538c2ecf20Sopenharmony_ci
21548c2ecf20Sopenharmony_ci		err = fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
21558c2ecf20Sopenharmony_ci		if (!err)
21568c2ecf20Sopenharmony_ci			info->flags |= FBINFO_HWACCEL_COPYAREA |
21578c2ecf20Sopenharmony_ci				       FBINFO_HWACCEL_FILLRECT |
21588c2ecf20Sopenharmony_ci				       FBINFO_HWACCEL_IMAGEBLIT;
21598c2ecf20Sopenharmony_ci		else
21608c2ecf20Sopenharmony_ci			kfree(info->pixmap.addr);
21618c2ecf20Sopenharmony_ci	}
21628c2ecf20Sopenharmony_ci#endif
21638c2ecf20Sopenharmony_ci	return err;
21648c2ecf20Sopenharmony_ci}
21658c2ecf20Sopenharmony_ci
21668c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------- */
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_cistatic int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id)
21698c2ecf20Sopenharmony_ci{
21708c2ecf20Sopenharmony_ci	struct fb_info *info;
21718c2ecf20Sopenharmony_ci	struct savagefb_par *par;
21728c2ecf20Sopenharmony_ci	u_int h_sync, v_sync;
21738c2ecf20Sopenharmony_ci	int err, lpitch;
21748c2ecf20Sopenharmony_ci	int video_len;
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci	DBG("savagefb_probe");
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev);
21798c2ecf20Sopenharmony_ci	if (!info)
21808c2ecf20Sopenharmony_ci		return -ENOMEM;
21818c2ecf20Sopenharmony_ci	par = info->par;
21828c2ecf20Sopenharmony_ci	mutex_init(&par->open_lock);
21838c2ecf20Sopenharmony_ci	err = pci_enable_device(dev);
21848c2ecf20Sopenharmony_ci	if (err)
21858c2ecf20Sopenharmony_ci		goto failed_enable;
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	if ((err = pci_request_regions(dev, "savagefb"))) {
21888c2ecf20Sopenharmony_ci		printk(KERN_ERR "cannot request PCI regions\n");
21898c2ecf20Sopenharmony_ci		goto failed_enable;
21908c2ecf20Sopenharmony_ci	}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	err = -ENOMEM;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if ((err = savage_init_fb_info(info, dev, id)))
21958c2ecf20Sopenharmony_ci		goto failed_init;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	err = savage_map_mmio(info);
21988c2ecf20Sopenharmony_ci	if (err)
21998c2ecf20Sopenharmony_ci		goto failed_mmio;
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_ci	video_len = savage_init_hw(par);
22028c2ecf20Sopenharmony_ci	/* FIXME: can't be negative */
22038c2ecf20Sopenharmony_ci	if (video_len < 0) {
22048c2ecf20Sopenharmony_ci		err = video_len;
22058c2ecf20Sopenharmony_ci		goto failed_mmio;
22068c2ecf20Sopenharmony_ci	}
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	err = savage_map_video(info, video_len);
22098c2ecf20Sopenharmony_ci	if (err)
22108c2ecf20Sopenharmony_ci		goto failed_video;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&info->modelist);
22138c2ecf20Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_I2C)
22148c2ecf20Sopenharmony_ci	savagefb_create_i2c_busses(info);
22158c2ecf20Sopenharmony_ci	savagefb_probe_i2c_connector(info, &par->edid);
22168c2ecf20Sopenharmony_ci	fb_edid_to_monspecs(par->edid, &info->monspecs);
22178c2ecf20Sopenharmony_ci	kfree(par->edid);
22188c2ecf20Sopenharmony_ci	fb_videomode_to_modelist(info->monspecs.modedb,
22198c2ecf20Sopenharmony_ci				 info->monspecs.modedb_len,
22208c2ecf20Sopenharmony_ci				 &info->modelist);
22218c2ecf20Sopenharmony_ci#endif
22228c2ecf20Sopenharmony_ci	info->var = savagefb_var800x600x8;
22238c2ecf20Sopenharmony_ci	/* if a panel was detected, default to a CVT mode instead */
22248c2ecf20Sopenharmony_ci	if (par->SavagePanelWidth) {
22258c2ecf20Sopenharmony_ci		struct fb_videomode cvt_mode;
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci		memset(&cvt_mode, 0, sizeof(cvt_mode));
22288c2ecf20Sopenharmony_ci		cvt_mode.xres = par->SavagePanelWidth;
22298c2ecf20Sopenharmony_ci		cvt_mode.yres = par->SavagePanelHeight;
22308c2ecf20Sopenharmony_ci		cvt_mode.refresh = 60;
22318c2ecf20Sopenharmony_ci		/* FIXME: if we know there is only the panel
22328c2ecf20Sopenharmony_ci		 * we can enable reduced blanking as well */
22338c2ecf20Sopenharmony_ci		if (fb_find_mode_cvt(&cvt_mode, 0, 0))
22348c2ecf20Sopenharmony_ci			printk(KERN_WARNING "No CVT mode found for panel\n");
22358c2ecf20Sopenharmony_ci		else if (fb_find_mode(&info->var, info, NULL, NULL, 0,
22368c2ecf20Sopenharmony_ci				      &cvt_mode, 0) != 3)
22378c2ecf20Sopenharmony_ci			info->var = savagefb_var800x600x8;
22388c2ecf20Sopenharmony_ci	}
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	if (mode_option) {
22418c2ecf20Sopenharmony_ci		fb_find_mode(&info->var, info, mode_option,
22428c2ecf20Sopenharmony_ci			     info->monspecs.modedb, info->monspecs.modedb_len,
22438c2ecf20Sopenharmony_ci			     NULL, 8);
22448c2ecf20Sopenharmony_ci	} else if (info->monspecs.modedb != NULL) {
22458c2ecf20Sopenharmony_ci		const struct fb_videomode *mode;
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci		mode = fb_find_best_display(&info->monspecs, &info->modelist);
22488c2ecf20Sopenharmony_ci		savage_update_var(&info->var, mode);
22498c2ecf20Sopenharmony_ci	}
22508c2ecf20Sopenharmony_ci
22518c2ecf20Sopenharmony_ci	/* maximize virtual vertical length */
22528c2ecf20Sopenharmony_ci	lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3);
22538c2ecf20Sopenharmony_ci	info->var.yres_virtual = info->fix.smem_len/lpitch;
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	if (info->var.yres_virtual < info->var.yres) {
22568c2ecf20Sopenharmony_ci		err = -ENOMEM;
22578c2ecf20Sopenharmony_ci		goto failed;
22588c2ecf20Sopenharmony_ci	}
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci#if defined(CONFIG_FB_SAVAGE_ACCEL)
22618c2ecf20Sopenharmony_ci	/*
22628c2ecf20Sopenharmony_ci	 * The clipping coordinates are masked with 0xFFF, so limit our
22638c2ecf20Sopenharmony_ci	 * virtual resolutions to these sizes.
22648c2ecf20Sopenharmony_ci	 */
22658c2ecf20Sopenharmony_ci	if (info->var.yres_virtual > 0x1000)
22668c2ecf20Sopenharmony_ci		info->var.yres_virtual = 0x1000;
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	if (info->var.xres_virtual > 0x1000)
22698c2ecf20Sopenharmony_ci		info->var.xres_virtual = 0x1000;
22708c2ecf20Sopenharmony_ci#endif
22718c2ecf20Sopenharmony_ci	err = savagefb_check_var(&info->var, info);
22728c2ecf20Sopenharmony_ci	if (err)
22738c2ecf20Sopenharmony_ci		goto failed;
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci	savagefb_set_fix(info);
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci	/*
22788c2ecf20Sopenharmony_ci	 * Calculate the hsync and vsync frequencies.  Note that
22798c2ecf20Sopenharmony_ci	 * we split the 1e12 constant up so that we can preserve
22808c2ecf20Sopenharmony_ci	 * the precision and fit the results into 32-bit registers.
22818c2ecf20Sopenharmony_ci	 *  (1953125000 * 512 = 1e12)
22828c2ecf20Sopenharmony_ci	 */
22838c2ecf20Sopenharmony_ci	h_sync = 1953125000 / info->var.pixclock;
22848c2ecf20Sopenharmony_ci	h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin +
22858c2ecf20Sopenharmony_ci				 info->var.right_margin +
22868c2ecf20Sopenharmony_ci				 info->var.hsync_len);
22878c2ecf20Sopenharmony_ci	v_sync = h_sync / (info->var.yres + info->var.upper_margin +
22888c2ecf20Sopenharmony_ci			   info->var.lower_margin + info->var.vsync_len);
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci	printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": "
22918c2ecf20Sopenharmony_ci	       "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
22928c2ecf20Sopenharmony_ci	       info->fix.smem_len >> 10,
22938c2ecf20Sopenharmony_ci	       info->var.xres, info->var.yres,
22948c2ecf20Sopenharmony_ci	       h_sync / 1000, h_sync % 1000, v_sync);
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci
22978c2ecf20Sopenharmony_ci	fb_destroy_modedb(info->monspecs.modedb);
22988c2ecf20Sopenharmony_ci	info->monspecs.modedb = NULL;
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci	err = register_framebuffer(info);
23018c2ecf20Sopenharmony_ci	if (err < 0)
23028c2ecf20Sopenharmony_ci		goto failed;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	printk(KERN_INFO "fb: S3 %s frame buffer device\n",
23058c2ecf20Sopenharmony_ci	       info->fix.id);
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	/*
23088c2ecf20Sopenharmony_ci	 * Our driver data
23098c2ecf20Sopenharmony_ci	 */
23108c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, info);
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	return 0;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci failed:
23158c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_I2C
23168c2ecf20Sopenharmony_ci	savagefb_delete_i2c_busses(info);
23178c2ecf20Sopenharmony_ci#endif
23188c2ecf20Sopenharmony_ci	fb_alloc_cmap(&info->cmap, 0, 0);
23198c2ecf20Sopenharmony_ci	savage_unmap_video(info);
23208c2ecf20Sopenharmony_ci failed_video:
23218c2ecf20Sopenharmony_ci	savage_unmap_mmio(info);
23228c2ecf20Sopenharmony_ci failed_mmio:
23238c2ecf20Sopenharmony_ci	kfree(info->pixmap.addr);
23248c2ecf20Sopenharmony_ci failed_init:
23258c2ecf20Sopenharmony_ci	pci_release_regions(dev);
23268c2ecf20Sopenharmony_ci failed_enable:
23278c2ecf20Sopenharmony_ci	framebuffer_release(info);
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	return err;
23308c2ecf20Sopenharmony_ci}
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_cistatic void savagefb_remove(struct pci_dev *dev)
23338c2ecf20Sopenharmony_ci{
23348c2ecf20Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(dev);
23358c2ecf20Sopenharmony_ci
23368c2ecf20Sopenharmony_ci	DBG("savagefb_remove");
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	if (info) {
23398c2ecf20Sopenharmony_ci		unregister_framebuffer(info);
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_ci#ifdef CONFIG_FB_SAVAGE_I2C
23428c2ecf20Sopenharmony_ci		savagefb_delete_i2c_busses(info);
23438c2ecf20Sopenharmony_ci#endif
23448c2ecf20Sopenharmony_ci		fb_alloc_cmap(&info->cmap, 0, 0);
23458c2ecf20Sopenharmony_ci		savage_unmap_video(info);
23468c2ecf20Sopenharmony_ci		savage_unmap_mmio(info);
23478c2ecf20Sopenharmony_ci		kfree(info->pixmap.addr);
23488c2ecf20Sopenharmony_ci		pci_release_regions(dev);
23498c2ecf20Sopenharmony_ci		framebuffer_release(info);
23508c2ecf20Sopenharmony_ci	}
23518c2ecf20Sopenharmony_ci}
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_cistatic int savagefb_suspend_late(struct device *dev, pm_message_t mesg)
23548c2ecf20Sopenharmony_ci{
23558c2ecf20Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
23568c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
23578c2ecf20Sopenharmony_ci
23588c2ecf20Sopenharmony_ci	DBG("savagefb_suspend");
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci	if (mesg.event == PM_EVENT_PRETHAW)
23618c2ecf20Sopenharmony_ci		mesg.event = PM_EVENT_FREEZE;
23628c2ecf20Sopenharmony_ci	par->pm_state = mesg.event;
23638c2ecf20Sopenharmony_ci	dev->power.power_state = mesg;
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	/*
23668c2ecf20Sopenharmony_ci	 * For PM_EVENT_FREEZE, do not power down so the console
23678c2ecf20Sopenharmony_ci	 * can remain active.
23688c2ecf20Sopenharmony_ci	 */
23698c2ecf20Sopenharmony_ci	if (mesg.event == PM_EVENT_FREEZE)
23708c2ecf20Sopenharmony_ci		return 0;
23718c2ecf20Sopenharmony_ci
23728c2ecf20Sopenharmony_ci	console_lock();
23738c2ecf20Sopenharmony_ci	fb_set_suspend(info, 1);
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci	if (info->fbops->fb_sync)
23768c2ecf20Sopenharmony_ci		info->fbops->fb_sync(info);
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	savagefb_blank(FB_BLANK_POWERDOWN, info);
23798c2ecf20Sopenharmony_ci	savage_set_default_par(par, &par->save);
23808c2ecf20Sopenharmony_ci	savage_disable_mmio(par);
23818c2ecf20Sopenharmony_ci	console_unlock();
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci	return 0;
23848c2ecf20Sopenharmony_ci}
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_cistatic int __maybe_unused savagefb_suspend(struct device *dev)
23878c2ecf20Sopenharmony_ci{
23888c2ecf20Sopenharmony_ci	return savagefb_suspend_late(dev, PMSG_SUSPEND);
23898c2ecf20Sopenharmony_ci}
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_cistatic int __maybe_unused savagefb_hibernate(struct device *dev)
23928c2ecf20Sopenharmony_ci{
23938c2ecf20Sopenharmony_ci	return savagefb_suspend_late(dev, PMSG_HIBERNATE);
23948c2ecf20Sopenharmony_ci}
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_cistatic int __maybe_unused savagefb_freeze(struct device *dev)
23978c2ecf20Sopenharmony_ci{
23988c2ecf20Sopenharmony_ci	return savagefb_suspend_late(dev, PMSG_FREEZE);
23998c2ecf20Sopenharmony_ci}
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_cistatic int __maybe_unused savagefb_resume(struct device *dev)
24028c2ecf20Sopenharmony_ci{
24038c2ecf20Sopenharmony_ci	struct fb_info *info = dev_get_drvdata(dev);
24048c2ecf20Sopenharmony_ci	struct savagefb_par *par = info->par;
24058c2ecf20Sopenharmony_ci	int cur_state = par->pm_state;
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	DBG("savage_resume");
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci	par->pm_state = PM_EVENT_ON;
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci	/*
24128c2ecf20Sopenharmony_ci	 * The adapter was not powered down coming back from a
24138c2ecf20Sopenharmony_ci	 * PM_EVENT_FREEZE.
24148c2ecf20Sopenharmony_ci	 */
24158c2ecf20Sopenharmony_ci	if (cur_state == PM_EVENT_FREEZE)
24168c2ecf20Sopenharmony_ci		return 0;
24178c2ecf20Sopenharmony_ci
24188c2ecf20Sopenharmony_ci	console_lock();
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_ci	savage_enable_mmio(par);
24218c2ecf20Sopenharmony_ci	savage_init_hw(par);
24228c2ecf20Sopenharmony_ci	savagefb_set_par(info);
24238c2ecf20Sopenharmony_ci	fb_set_suspend(info, 0);
24248c2ecf20Sopenharmony_ci	savagefb_blank(FB_BLANK_UNBLANK, info);
24258c2ecf20Sopenharmony_ci	console_unlock();
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_ci	return 0;
24288c2ecf20Sopenharmony_ci}
24298c2ecf20Sopenharmony_ci
24308c2ecf20Sopenharmony_cistatic const struct dev_pm_ops savagefb_pm_ops = {
24318c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
24328c2ecf20Sopenharmony_ci	.suspend	= savagefb_suspend,
24338c2ecf20Sopenharmony_ci	.resume		= savagefb_resume,
24348c2ecf20Sopenharmony_ci	.freeze		= savagefb_freeze,
24358c2ecf20Sopenharmony_ci	.thaw		= savagefb_resume,
24368c2ecf20Sopenharmony_ci	.poweroff	= savagefb_hibernate,
24378c2ecf20Sopenharmony_ci	.restore	= savagefb_resume,
24388c2ecf20Sopenharmony_ci#endif
24398c2ecf20Sopenharmony_ci};
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_cistatic const struct pci_device_id savagefb_devices[] = {
24428c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128,
24438c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24448c2ecf20Sopenharmony_ci
24458c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64,
24468c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24478c2ecf20Sopenharmony_ci
24488c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C,
24498c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24508c2ecf20Sopenharmony_ci
24518c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR,
24528c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR,
24558c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR,
24588c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR,
24618c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR,
24648c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR,
24678c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
24688c2ecf20Sopenharmony_ci
24698c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4,
24708c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4},
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D,
24738c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D},
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV,
24768c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV},
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000,
24798c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000},
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV,
24828c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV},
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX,
24858c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX},
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV,
24888c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV},
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX,
24918c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX},
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM,
24948c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM},
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM,
24978c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM},
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P,
25008c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P},
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K,
25038c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K},
25048c2ecf20Sopenharmony_ci
25058c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR,
25068c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR},
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK,
25098c2ecf20Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK},
25108c2ecf20Sopenharmony_ci
25118c2ecf20Sopenharmony_ci	{0, 0, 0, 0, 0, 0, 0}
25128c2ecf20Sopenharmony_ci};
25138c2ecf20Sopenharmony_ci
25148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, savagefb_devices);
25158c2ecf20Sopenharmony_ci
25168c2ecf20Sopenharmony_cistatic struct pci_driver savagefb_driver = {
25178c2ecf20Sopenharmony_ci	.name =     "savagefb",
25188c2ecf20Sopenharmony_ci	.id_table = savagefb_devices,
25198c2ecf20Sopenharmony_ci	.probe =    savagefb_probe,
25208c2ecf20Sopenharmony_ci	.driver.pm = &savagefb_pm_ops,
25218c2ecf20Sopenharmony_ci	.remove =   savagefb_remove,
25228c2ecf20Sopenharmony_ci};
25238c2ecf20Sopenharmony_ci
25248c2ecf20Sopenharmony_ci/* **************************** exit-time only **************************** */
25258c2ecf20Sopenharmony_ci
25268c2ecf20Sopenharmony_cistatic void __exit savage_done(void)
25278c2ecf20Sopenharmony_ci{
25288c2ecf20Sopenharmony_ci	DBG("savage_done");
25298c2ecf20Sopenharmony_ci	pci_unregister_driver(&savagefb_driver);
25308c2ecf20Sopenharmony_ci}
25318c2ecf20Sopenharmony_ci
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci/* ************************* init in-kernel code ************************** */
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_cistatic int __init savagefb_setup(char *options)
25368c2ecf20Sopenharmony_ci{
25378c2ecf20Sopenharmony_ci#ifndef MODULE
25388c2ecf20Sopenharmony_ci	char *this_opt;
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_ci	if (!options || !*options)
25418c2ecf20Sopenharmony_ci		return 0;
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
25448c2ecf20Sopenharmony_ci		mode_option = this_opt;
25458c2ecf20Sopenharmony_ci	}
25468c2ecf20Sopenharmony_ci#endif /* !MODULE */
25478c2ecf20Sopenharmony_ci	return 0;
25488c2ecf20Sopenharmony_ci}
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_cistatic int __init savagefb_init(void)
25518c2ecf20Sopenharmony_ci{
25528c2ecf20Sopenharmony_ci	char *option;
25538c2ecf20Sopenharmony_ci
25548c2ecf20Sopenharmony_ci	DBG("savagefb_init");
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	if (fb_get_options("savagefb", &option))
25578c2ecf20Sopenharmony_ci		return -ENODEV;
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	savagefb_setup(option);
25608c2ecf20Sopenharmony_ci	return pci_register_driver(&savagefb_driver);
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci}
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_cimodule_init(savagefb_init);
25658c2ecf20Sopenharmony_cimodule_exit(savage_done);
25668c2ecf20Sopenharmony_ci
25678c2ecf20Sopenharmony_cimodule_param(mode_option, charp, 0);
25688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Specify initial video mode");
2569