1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * linux/drivers/video/savagefb.c -- S3 Savage Framebuffer Driver
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (c) 2001-2002 Denis Oliver Kropp <dok@directfb.org>
5*4882a593Smuzhiyun * Sven Neumann <neo@directfb.org>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Card specific code is based on XFree86's savage driver.
9*4882a593Smuzhiyun * Framebuffer framework code is based on code of cyber2000fb and tdfxfb.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General
12*4882a593Smuzhiyun * Public License. See the file COPYING in the main directory of this
13*4882a593Smuzhiyun * archive for more details.
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * 0.4.0 (neo)
16*4882a593Smuzhiyun * - hardware accelerated clear and move
17*4882a593Smuzhiyun *
18*4882a593Smuzhiyun * 0.3.2 (dok)
19*4882a593Smuzhiyun * - wait for vertical retrace before writing to cr67
20*4882a593Smuzhiyun * at the beginning of savagefb_set_par
21*4882a593Smuzhiyun * - use synchronization registers cr23 and cr26
22*4882a593Smuzhiyun *
23*4882a593Smuzhiyun * 0.3.1 (dok)
24*4882a593Smuzhiyun * - reset 3D engine
25*4882a593Smuzhiyun * - don't return alpha bits for 32bit format
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun * 0.3.0 (dok)
28*4882a593Smuzhiyun * - added WaitIdle functions for all Savage types
29*4882a593Smuzhiyun * - do WaitIdle before mode switching
30*4882a593Smuzhiyun * - code cleanup
31*4882a593Smuzhiyun *
32*4882a593Smuzhiyun * 0.2.0 (dok)
33*4882a593Smuzhiyun * - first working version
34*4882a593Smuzhiyun *
35*4882a593Smuzhiyun *
36*4882a593Smuzhiyun * TODO
37*4882a593Smuzhiyun * - clock validations in decode_var
38*4882a593Smuzhiyun *
39*4882a593Smuzhiyun * BUGS
40*4882a593Smuzhiyun * - white margin on bootup
41*4882a593Smuzhiyun *
42*4882a593Smuzhiyun */
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #include <linux/module.h>
45*4882a593Smuzhiyun #include <linux/kernel.h>
46*4882a593Smuzhiyun #include <linux/errno.h>
47*4882a593Smuzhiyun #include <linux/string.h>
48*4882a593Smuzhiyun #include <linux/mm.h>
49*4882a593Smuzhiyun #include <linux/slab.h>
50*4882a593Smuzhiyun #include <linux/delay.h>
51*4882a593Smuzhiyun #include <linux/fb.h>
52*4882a593Smuzhiyun #include <linux/pci.h>
53*4882a593Smuzhiyun #include <linux/init.h>
54*4882a593Smuzhiyun #include <linux/console.h>
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun #include <asm/io.h>
57*4882a593Smuzhiyun #include <asm/irq.h>
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun #include "savagefb.h"
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun #define SAVAGEFB_VERSION "0.4.0_2.6"
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun static char *mode_option = NULL;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun #ifdef MODULE
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun MODULE_AUTHOR("(c) 2001-2002 Denis Oliver Kropp <dok@directfb.org>");
72*4882a593Smuzhiyun MODULE_LICENSE("GPL");
73*4882a593Smuzhiyun MODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips");
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun #endif
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
79*4882a593Smuzhiyun
vgaHWSeqReset(struct savagefb_par * par,int start)80*4882a593Smuzhiyun static void vgaHWSeqReset(struct savagefb_par *par, int start)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun if (start)
83*4882a593Smuzhiyun VGAwSEQ(0x00, 0x01, par); /* Synchronous Reset */
84*4882a593Smuzhiyun else
85*4882a593Smuzhiyun VGAwSEQ(0x00, 0x03, par); /* End Reset */
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
vgaHWProtect(struct savagefb_par * par,int on)88*4882a593Smuzhiyun static void vgaHWProtect(struct savagefb_par *par, int on)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun unsigned char tmp;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun if (on) {
93*4882a593Smuzhiyun /*
94*4882a593Smuzhiyun * Turn off screen and disable sequencer.
95*4882a593Smuzhiyun */
96*4882a593Smuzhiyun tmp = VGArSEQ(0x01, par);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun vgaHWSeqReset(par, 1); /* start synchronous reset */
99*4882a593Smuzhiyun VGAwSEQ(0x01, tmp | 0x20, par);/* disable the display */
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun VGAenablePalette(par);
102*4882a593Smuzhiyun } else {
103*4882a593Smuzhiyun /*
104*4882a593Smuzhiyun * Reenable sequencer, then turn on screen.
105*4882a593Smuzhiyun */
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun tmp = VGArSEQ(0x01, par);
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun VGAwSEQ(0x01, tmp & ~0x20, par);/* reenable display */
110*4882a593Smuzhiyun vgaHWSeqReset(par, 0); /* clear synchronous reset */
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun VGAdisablePalette(par);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
vgaHWRestore(struct savagefb_par * par,struct savage_reg * reg)116*4882a593Smuzhiyun static void vgaHWRestore(struct savagefb_par *par, struct savage_reg *reg)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun int i;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun VGAwMISC(reg->MiscOutReg, par);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun for (i = 1; i < 5; i++)
123*4882a593Smuzhiyun VGAwSEQ(i, reg->Sequencer[i], par);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or
126*4882a593Smuzhiyun CRTC[17] */
127*4882a593Smuzhiyun VGAwCR(17, reg->CRTC[17] & ~0x80, par);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun for (i = 0; i < 25; i++)
130*4882a593Smuzhiyun VGAwCR(i, reg->CRTC[i], par);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun for (i = 0; i < 9; i++)
133*4882a593Smuzhiyun VGAwGR(i, reg->Graphics[i], par);
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun VGAenablePalette(par);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun for (i = 0; i < 21; i++)
138*4882a593Smuzhiyun VGAwATTR(i, reg->Attribute[i], par);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun VGAdisablePalette(par);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
vgaHWInit(struct fb_var_screeninfo * var,struct savagefb_par * par,struct xtimings * timings,struct savage_reg * reg)143*4882a593Smuzhiyun static void vgaHWInit(struct fb_var_screeninfo *var,
144*4882a593Smuzhiyun struct savagefb_par *par,
145*4882a593Smuzhiyun struct xtimings *timings,
146*4882a593Smuzhiyun struct savage_reg *reg)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun reg->MiscOutReg = 0x23;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT))
151*4882a593Smuzhiyun reg->MiscOutReg |= 0x40;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT))
154*4882a593Smuzhiyun reg->MiscOutReg |= 0x80;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun /*
157*4882a593Smuzhiyun * Time Sequencer
158*4882a593Smuzhiyun */
159*4882a593Smuzhiyun reg->Sequencer[0x00] = 0x00;
160*4882a593Smuzhiyun reg->Sequencer[0x01] = 0x01;
161*4882a593Smuzhiyun reg->Sequencer[0x02] = 0x0F;
162*4882a593Smuzhiyun reg->Sequencer[0x03] = 0x00; /* Font select */
163*4882a593Smuzhiyun reg->Sequencer[0x04] = 0x0E; /* Misc */
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun /*
166*4882a593Smuzhiyun * CRTC Controller
167*4882a593Smuzhiyun */
168*4882a593Smuzhiyun reg->CRTC[0x00] = (timings->HTotal >> 3) - 5;
169*4882a593Smuzhiyun reg->CRTC[0x01] = (timings->HDisplay >> 3) - 1;
170*4882a593Smuzhiyun reg->CRTC[0x02] = (timings->HSyncStart >> 3) - 1;
171*4882a593Smuzhiyun reg->CRTC[0x03] = (((timings->HSyncEnd >> 3) - 1) & 0x1f) | 0x80;
172*4882a593Smuzhiyun reg->CRTC[0x04] = (timings->HSyncStart >> 3);
173*4882a593Smuzhiyun reg->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) |
174*4882a593Smuzhiyun (((timings->HSyncEnd >> 3)) & 0x1f);
175*4882a593Smuzhiyun reg->CRTC[0x06] = (timings->VTotal - 2) & 0xFF;
176*4882a593Smuzhiyun reg->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) |
177*4882a593Smuzhiyun (((timings->VDisplay - 1) & 0x100) >> 7) |
178*4882a593Smuzhiyun ((timings->VSyncStart & 0x100) >> 6) |
179*4882a593Smuzhiyun (((timings->VSyncStart - 1) & 0x100) >> 5) |
180*4882a593Smuzhiyun 0x10 |
181*4882a593Smuzhiyun (((timings->VTotal - 2) & 0x200) >> 4) |
182*4882a593Smuzhiyun (((timings->VDisplay - 1) & 0x200) >> 3) |
183*4882a593Smuzhiyun ((timings->VSyncStart & 0x200) >> 2);
184*4882a593Smuzhiyun reg->CRTC[0x08] = 0x00;
185*4882a593Smuzhiyun reg->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun if (timings->dblscan)
188*4882a593Smuzhiyun reg->CRTC[0x09] |= 0x80;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun reg->CRTC[0x0a] = 0x00;
191*4882a593Smuzhiyun reg->CRTC[0x0b] = 0x00;
192*4882a593Smuzhiyun reg->CRTC[0x0c] = 0x00;
193*4882a593Smuzhiyun reg->CRTC[0x0d] = 0x00;
194*4882a593Smuzhiyun reg->CRTC[0x0e] = 0x00;
195*4882a593Smuzhiyun reg->CRTC[0x0f] = 0x00;
196*4882a593Smuzhiyun reg->CRTC[0x10] = timings->VSyncStart & 0xff;
197*4882a593Smuzhiyun reg->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20;
198*4882a593Smuzhiyun reg->CRTC[0x12] = (timings->VDisplay - 1) & 0xff;
199*4882a593Smuzhiyun reg->CRTC[0x13] = var->xres_virtual >> 4;
200*4882a593Smuzhiyun reg->CRTC[0x14] = 0x00;
201*4882a593Smuzhiyun reg->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff;
202*4882a593Smuzhiyun reg->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff;
203*4882a593Smuzhiyun reg->CRTC[0x17] = 0xc3;
204*4882a593Smuzhiyun reg->CRTC[0x18] = 0xff;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /*
207*4882a593Smuzhiyun * are these unnecessary?
208*4882a593Smuzhiyun * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
209*4882a593Smuzhiyun * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN|KGA_ENABLE_ON_ZERO);
210*4882a593Smuzhiyun */
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun /*
213*4882a593Smuzhiyun * Graphics Display Controller
214*4882a593Smuzhiyun */
215*4882a593Smuzhiyun reg->Graphics[0x00] = 0x00;
216*4882a593Smuzhiyun reg->Graphics[0x01] = 0x00;
217*4882a593Smuzhiyun reg->Graphics[0x02] = 0x00;
218*4882a593Smuzhiyun reg->Graphics[0x03] = 0x00;
219*4882a593Smuzhiyun reg->Graphics[0x04] = 0x00;
220*4882a593Smuzhiyun reg->Graphics[0x05] = 0x40;
221*4882a593Smuzhiyun reg->Graphics[0x06] = 0x05; /* only map 64k VGA memory !!!! */
222*4882a593Smuzhiyun reg->Graphics[0x07] = 0x0F;
223*4882a593Smuzhiyun reg->Graphics[0x08] = 0xFF;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun reg->Attribute[0x00] = 0x00; /* standard colormap translation */
227*4882a593Smuzhiyun reg->Attribute[0x01] = 0x01;
228*4882a593Smuzhiyun reg->Attribute[0x02] = 0x02;
229*4882a593Smuzhiyun reg->Attribute[0x03] = 0x03;
230*4882a593Smuzhiyun reg->Attribute[0x04] = 0x04;
231*4882a593Smuzhiyun reg->Attribute[0x05] = 0x05;
232*4882a593Smuzhiyun reg->Attribute[0x06] = 0x06;
233*4882a593Smuzhiyun reg->Attribute[0x07] = 0x07;
234*4882a593Smuzhiyun reg->Attribute[0x08] = 0x08;
235*4882a593Smuzhiyun reg->Attribute[0x09] = 0x09;
236*4882a593Smuzhiyun reg->Attribute[0x0a] = 0x0A;
237*4882a593Smuzhiyun reg->Attribute[0x0b] = 0x0B;
238*4882a593Smuzhiyun reg->Attribute[0x0c] = 0x0C;
239*4882a593Smuzhiyun reg->Attribute[0x0d] = 0x0D;
240*4882a593Smuzhiyun reg->Attribute[0x0e] = 0x0E;
241*4882a593Smuzhiyun reg->Attribute[0x0f] = 0x0F;
242*4882a593Smuzhiyun reg->Attribute[0x10] = 0x41;
243*4882a593Smuzhiyun reg->Attribute[0x11] = 0xFF;
244*4882a593Smuzhiyun reg->Attribute[0x12] = 0x0F;
245*4882a593Smuzhiyun reg->Attribute[0x13] = 0x00;
246*4882a593Smuzhiyun reg->Attribute[0x14] = 0x00;
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun /* -------------------- Hardware specific routines ------------------------- */
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun /*
252*4882a593Smuzhiyun * Hardware Acceleration for SavageFB
253*4882a593Smuzhiyun */
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun /* Wait for fifo space */
256*4882a593Smuzhiyun static void
savage3D_waitfifo(struct savagefb_par * par,int space)257*4882a593Smuzhiyun savage3D_waitfifo(struct savagefb_par *par, int space)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun int slots = MAXFIFO - space;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun while ((savage_in32(0x48C00, par) & 0x0000ffff) > slots);
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun static void
savage4_waitfifo(struct savagefb_par * par,int space)265*4882a593Smuzhiyun savage4_waitfifo(struct savagefb_par *par, int space)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun int slots = MAXFIFO - space;
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun while ((savage_in32(0x48C60, par) & 0x001fffff) > slots);
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun static void
savage2000_waitfifo(struct savagefb_par * par,int space)273*4882a593Smuzhiyun savage2000_waitfifo(struct savagefb_par *par, int space)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun int slots = MAXFIFO - space;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun while ((savage_in32(0x48C60, par) & 0x0000ffff) > slots);
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun /* Wait for idle accelerator */
281*4882a593Smuzhiyun static void
savage3D_waitidle(struct savagefb_par * par)282*4882a593Smuzhiyun savage3D_waitidle(struct savagefb_par *par)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun while ((savage_in32(0x48C00, par) & 0x0008ffff) != 0x80000);
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun static void
savage4_waitidle(struct savagefb_par * par)288*4882a593Smuzhiyun savage4_waitidle(struct savagefb_par *par)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun while ((savage_in32(0x48C60, par) & 0x00a00000) != 0x00a00000);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun static void
savage2000_waitidle(struct savagefb_par * par)294*4882a593Smuzhiyun savage2000_waitidle(struct savagefb_par *par)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun while ((savage_in32(0x48C60, par) & 0x009fffff));
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun #ifdef CONFIG_FB_SAVAGE_ACCEL
300*4882a593Smuzhiyun static void
SavageSetup2DEngine(struct savagefb_par * par)301*4882a593Smuzhiyun SavageSetup2DEngine(struct savagefb_par *par)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun unsigned long GlobalBitmapDescriptor;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE;
306*4882a593Smuzhiyun BCI_BD_SET_BPP(GlobalBitmapDescriptor, par->depth);
307*4882a593Smuzhiyun BCI_BD_SET_STRIDE(GlobalBitmapDescriptor, par->vwidth);
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun switch(par->chip) {
310*4882a593Smuzhiyun case S3_SAVAGE3D:
311*4882a593Smuzhiyun case S3_SAVAGE_MX:
312*4882a593Smuzhiyun /* Disable BCI */
313*4882a593Smuzhiyun savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
314*4882a593Smuzhiyun /* Setup BCI command overflow buffer */
315*4882a593Smuzhiyun savage_out32(0x48C14,
316*4882a593Smuzhiyun (par->cob_offset >> 11) | (par->cob_index << 29),
317*4882a593Smuzhiyun par);
318*4882a593Smuzhiyun /* Program shadow status update. */
319*4882a593Smuzhiyun savage_out32(0x48C10, 0x78207220, par);
320*4882a593Smuzhiyun savage_out32(0x48C0C, 0, par);
321*4882a593Smuzhiyun /* Enable BCI and command overflow buffer */
322*4882a593Smuzhiyun savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x0C, par);
323*4882a593Smuzhiyun break;
324*4882a593Smuzhiyun case S3_SAVAGE4:
325*4882a593Smuzhiyun case S3_TWISTER:
326*4882a593Smuzhiyun case S3_PROSAVAGE:
327*4882a593Smuzhiyun case S3_PROSAVAGEDDR:
328*4882a593Smuzhiyun case S3_SUPERSAVAGE:
329*4882a593Smuzhiyun /* Disable BCI */
330*4882a593Smuzhiyun savage_out32(0x48C18, savage_in32(0x48C18, par) & 0x3FF0, par);
331*4882a593Smuzhiyun /* Program shadow status update */
332*4882a593Smuzhiyun savage_out32(0x48C10, 0x00700040, par);
333*4882a593Smuzhiyun savage_out32(0x48C0C, 0, par);
334*4882a593Smuzhiyun /* Enable BCI without the COB */
335*4882a593Smuzhiyun savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x08, par);
336*4882a593Smuzhiyun break;
337*4882a593Smuzhiyun case S3_SAVAGE2000:
338*4882a593Smuzhiyun /* Disable BCI */
339*4882a593Smuzhiyun savage_out32(0x48C18, 0, par);
340*4882a593Smuzhiyun /* Setup BCI command overflow buffer */
341*4882a593Smuzhiyun savage_out32(0x48C18,
342*4882a593Smuzhiyun (par->cob_offset >> 7) | (par->cob_index),
343*4882a593Smuzhiyun par);
344*4882a593Smuzhiyun /* Disable shadow status update */
345*4882a593Smuzhiyun savage_out32(0x48A30, 0, par);
346*4882a593Smuzhiyun /* Enable BCI and command overflow buffer */
347*4882a593Smuzhiyun savage_out32(0x48C18, savage_in32(0x48C18, par) | 0x00280000,
348*4882a593Smuzhiyun par);
349*4882a593Smuzhiyun break;
350*4882a593Smuzhiyun default:
351*4882a593Smuzhiyun break;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun /* Turn on 16-bit register access. */
354*4882a593Smuzhiyun vga_out8(0x3d4, 0x31, par);
355*4882a593Smuzhiyun vga_out8(0x3d5, 0x0c, par);
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun /* Set stride to use GBD. */
358*4882a593Smuzhiyun vga_out8(0x3d4, 0x50, par);
359*4882a593Smuzhiyun vga_out8(0x3d5, vga_in8(0x3d5, par) | 0xC1, par);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun /* Enable 2D engine. */
362*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
363*4882a593Smuzhiyun vga_out8(0x3d5, 0x01, par);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun savage_out32(MONO_PAT_0, ~0, par);
366*4882a593Smuzhiyun savage_out32(MONO_PAT_1, ~0, par);
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun /* Setup plane masks */
369*4882a593Smuzhiyun savage_out32(0x8128, ~0, par); /* enable all write planes */
370*4882a593Smuzhiyun savage_out32(0x812C, ~0, par); /* enable all read planes */
371*4882a593Smuzhiyun savage_out16(0x8134, 0x27, par);
372*4882a593Smuzhiyun savage_out16(0x8136, 0x07, par);
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun /* Now set the GBD */
375*4882a593Smuzhiyun par->bci_ptr = 0;
376*4882a593Smuzhiyun par->SavageWaitFifo(par, 4);
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1);
379*4882a593Smuzhiyun BCI_SEND(0);
380*4882a593Smuzhiyun BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2);
381*4882a593Smuzhiyun BCI_SEND(GlobalBitmapDescriptor);
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun /*
384*4882a593Smuzhiyun * I don't know why, sending this twice fixes the initial black screen,
385*4882a593Smuzhiyun * prevents X from crashing at least in Toshiba laptops with SavageIX.
386*4882a593Smuzhiyun * --Tony
387*4882a593Smuzhiyun */
388*4882a593Smuzhiyun par->bci_ptr = 0;
389*4882a593Smuzhiyun par->SavageWaitFifo(par, 4);
390*4882a593Smuzhiyun
391*4882a593Smuzhiyun BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1);
392*4882a593Smuzhiyun BCI_SEND(0);
393*4882a593Smuzhiyun BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2);
394*4882a593Smuzhiyun BCI_SEND(GlobalBitmapDescriptor);
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
savagefb_set_clip(struct fb_info * info)397*4882a593Smuzhiyun static void savagefb_set_clip(struct fb_info *info)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun struct savagefb_par *par = info->par;
400*4882a593Smuzhiyun int cmd;
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW;
403*4882a593Smuzhiyun par->bci_ptr = 0;
404*4882a593Smuzhiyun par->SavageWaitFifo(par,3);
405*4882a593Smuzhiyun BCI_SEND(cmd);
406*4882a593Smuzhiyun BCI_SEND(BCI_CLIP_TL(0, 0));
407*4882a593Smuzhiyun BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff));
408*4882a593Smuzhiyun }
409*4882a593Smuzhiyun #else
SavageSetup2DEngine(struct savagefb_par * par)410*4882a593Smuzhiyun static void SavageSetup2DEngine(struct savagefb_par *par) {}
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun #endif
413*4882a593Smuzhiyun
SavageCalcClock(long freq,int min_m,int min_n1,int max_n1,int min_n2,int max_n2,long freq_min,long freq_max,unsigned int * mdiv,unsigned int * ndiv,unsigned int * r)414*4882a593Smuzhiyun static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
415*4882a593Smuzhiyun int min_n2, int max_n2, long freq_min,
416*4882a593Smuzhiyun long freq_max, unsigned int *mdiv,
417*4882a593Smuzhiyun unsigned int *ndiv, unsigned int *r)
418*4882a593Smuzhiyun {
419*4882a593Smuzhiyun long diff, best_diff;
420*4882a593Smuzhiyun unsigned int m;
421*4882a593Smuzhiyun unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2;
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun if (freq < freq_min / (1 << max_n2)) {
424*4882a593Smuzhiyun printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
425*4882a593Smuzhiyun freq = freq_min / (1 << max_n2);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun if (freq > freq_max / (1 << min_n2)) {
428*4882a593Smuzhiyun printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
429*4882a593Smuzhiyun freq = freq_max / (1 << min_n2);
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun /* work out suitable timings */
433*4882a593Smuzhiyun best_diff = freq;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun for (n2=min_n2; n2<=max_n2; n2++) {
436*4882a593Smuzhiyun for (n1=min_n1+2; n1<=max_n1+2; n1++) {
437*4882a593Smuzhiyun m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
438*4882a593Smuzhiyun BASE_FREQ;
439*4882a593Smuzhiyun if (m < min_m+2 || m > 127+2)
440*4882a593Smuzhiyun continue;
441*4882a593Smuzhiyun if ((m * BASE_FREQ >= freq_min * n1) &&
442*4882a593Smuzhiyun (m * BASE_FREQ <= freq_max * n1)) {
443*4882a593Smuzhiyun diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
444*4882a593Smuzhiyun if (diff < 0)
445*4882a593Smuzhiyun diff = -diff;
446*4882a593Smuzhiyun if (diff < best_diff) {
447*4882a593Smuzhiyun best_diff = diff;
448*4882a593Smuzhiyun best_m = m;
449*4882a593Smuzhiyun best_n1 = n1;
450*4882a593Smuzhiyun best_n2 = n2;
451*4882a593Smuzhiyun }
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun }
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun *ndiv = best_n1 - 2;
457*4882a593Smuzhiyun *r = best_n2;
458*4882a593Smuzhiyun *mdiv = best_m - 2;
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun
common_calc_clock(long freq,int min_m,int min_n1,int max_n1,int min_n2,int max_n2,long freq_min,long freq_max,unsigned char * mdiv,unsigned char * ndiv)461*4882a593Smuzhiyun static int common_calc_clock(long freq, int min_m, int min_n1, int max_n1,
462*4882a593Smuzhiyun int min_n2, int max_n2, long freq_min,
463*4882a593Smuzhiyun long freq_max, unsigned char *mdiv,
464*4882a593Smuzhiyun unsigned char *ndiv)
465*4882a593Smuzhiyun {
466*4882a593Smuzhiyun long diff, best_diff;
467*4882a593Smuzhiyun unsigned int m;
468*4882a593Smuzhiyun unsigned char n1, n2;
469*4882a593Smuzhiyun unsigned char best_n1 = 16+2, best_n2 = 2, best_m = 125+2;
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun best_diff = freq;
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun for (n2 = min_n2; n2 <= max_n2; n2++) {
474*4882a593Smuzhiyun for (n1 = min_n1+2; n1 <= max_n1+2; n1++) {
475*4882a593Smuzhiyun m = (freq * n1 * (1 << n2) + HALF_BASE_FREQ) /
476*4882a593Smuzhiyun BASE_FREQ;
477*4882a593Smuzhiyun if (m < min_m + 2 || m > 127+2)
478*4882a593Smuzhiyun continue;
479*4882a593Smuzhiyun if ((m * BASE_FREQ >= freq_min * n1) &&
480*4882a593Smuzhiyun (m * BASE_FREQ <= freq_max * n1)) {
481*4882a593Smuzhiyun diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
482*4882a593Smuzhiyun if (diff < 0)
483*4882a593Smuzhiyun diff = -diff;
484*4882a593Smuzhiyun if (diff < best_diff) {
485*4882a593Smuzhiyun best_diff = diff;
486*4882a593Smuzhiyun best_m = m;
487*4882a593Smuzhiyun best_n1 = n1;
488*4882a593Smuzhiyun best_n2 = n2;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun }
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun if (max_n1 == 63)
495*4882a593Smuzhiyun *ndiv = (best_n1 - 2) | (best_n2 << 6);
496*4882a593Smuzhiyun else
497*4882a593Smuzhiyun *ndiv = (best_n1 - 2) | (best_n2 << 5);
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun *mdiv = best_m - 2;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun return 0;
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun #ifdef SAVAGEFB_DEBUG
505*4882a593Smuzhiyun /* This function is used to debug, it prints out the contents of s3 regs */
506*4882a593Smuzhiyun
SavagePrintRegs(struct savagefb_par * par)507*4882a593Smuzhiyun static void SavagePrintRegs(struct savagefb_par *par)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun unsigned char i;
510*4882a593Smuzhiyun int vgaCRIndex = 0x3d4;
511*4882a593Smuzhiyun int vgaCRReg = 0x3d5;
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun printk(KERN_DEBUG "SR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE "
514*4882a593Smuzhiyun "xF");
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun for (i = 0; i < 0x70; i++) {
517*4882a593Smuzhiyun if (!(i % 16))
518*4882a593Smuzhiyun printk(KERN_DEBUG "\nSR%xx ", i >> 4);
519*4882a593Smuzhiyun vga_out8(0x3c4, i, par);
520*4882a593Smuzhiyun printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par));
521*4882a593Smuzhiyun }
522*4882a593Smuzhiyun
523*4882a593Smuzhiyun printk(KERN_DEBUG "\n\nCR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC "
524*4882a593Smuzhiyun "xD xE xF");
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun for (i = 0; i < 0xB7; i++) {
527*4882a593Smuzhiyun if (!(i % 16))
528*4882a593Smuzhiyun printk(KERN_DEBUG "\nCR%xx ", i >> 4);
529*4882a593Smuzhiyun vga_out8(vgaCRIndex, i, par);
530*4882a593Smuzhiyun printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par));
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun printk(KERN_DEBUG "\n\n");
534*4882a593Smuzhiyun }
535*4882a593Smuzhiyun #endif
536*4882a593Smuzhiyun
537*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
538*4882a593Smuzhiyun
savage_get_default_par(struct savagefb_par * par,struct savage_reg * reg)539*4882a593Smuzhiyun static void savage_get_default_par(struct savagefb_par *par, struct savage_reg *reg)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun unsigned char cr3a, cr53, cr66;
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun vga_out16(0x3d4, 0x4838, par);
544*4882a593Smuzhiyun vga_out16(0x3d4, 0xa039, par);
545*4882a593Smuzhiyun vga_out16(0x3c4, 0x0608, par);
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
548*4882a593Smuzhiyun cr66 = vga_in8(0x3d5, par);
549*4882a593Smuzhiyun vga_out8(0x3d5, cr66 | 0x80, par);
550*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
551*4882a593Smuzhiyun cr3a = vga_in8(0x3d5, par);
552*4882a593Smuzhiyun vga_out8(0x3d5, cr3a | 0x80, par);
553*4882a593Smuzhiyun vga_out8(0x3d4, 0x53, par);
554*4882a593Smuzhiyun cr53 = vga_in8(0x3d5, par);
555*4882a593Smuzhiyun vga_out8(0x3d5, cr53 & 0x7f, par);
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
558*4882a593Smuzhiyun vga_out8(0x3d5, cr66, par);
559*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
560*4882a593Smuzhiyun vga_out8(0x3d5, cr3a, par);
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
563*4882a593Smuzhiyun vga_out8(0x3d5, cr66, par);
564*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
565*4882a593Smuzhiyun vga_out8(0x3d5, cr3a, par);
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun /* unlock extended seq regs */
568*4882a593Smuzhiyun vga_out8(0x3c4, 0x08, par);
569*4882a593Smuzhiyun reg->SR08 = vga_in8(0x3c5, par);
570*4882a593Smuzhiyun vga_out8(0x3c5, 0x06, par);
571*4882a593Smuzhiyun
572*4882a593Smuzhiyun /* now save all the extended regs we need */
573*4882a593Smuzhiyun vga_out8(0x3d4, 0x31, par);
574*4882a593Smuzhiyun reg->CR31 = vga_in8(0x3d5, par);
575*4882a593Smuzhiyun vga_out8(0x3d4, 0x32, par);
576*4882a593Smuzhiyun reg->CR32 = vga_in8(0x3d5, par);
577*4882a593Smuzhiyun vga_out8(0x3d4, 0x34, par);
578*4882a593Smuzhiyun reg->CR34 = vga_in8(0x3d5, par);
579*4882a593Smuzhiyun vga_out8(0x3d4, 0x36, par);
580*4882a593Smuzhiyun reg->CR36 = vga_in8(0x3d5, par);
581*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
582*4882a593Smuzhiyun reg->CR3A = vga_in8(0x3d5, par);
583*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
584*4882a593Smuzhiyun reg->CR40 = vga_in8(0x3d5, par);
585*4882a593Smuzhiyun vga_out8(0x3d4, 0x42, par);
586*4882a593Smuzhiyun reg->CR42 = vga_in8(0x3d5, par);
587*4882a593Smuzhiyun vga_out8(0x3d4, 0x45, par);
588*4882a593Smuzhiyun reg->CR45 = vga_in8(0x3d5, par);
589*4882a593Smuzhiyun vga_out8(0x3d4, 0x50, par);
590*4882a593Smuzhiyun reg->CR50 = vga_in8(0x3d5, par);
591*4882a593Smuzhiyun vga_out8(0x3d4, 0x51, par);
592*4882a593Smuzhiyun reg->CR51 = vga_in8(0x3d5, par);
593*4882a593Smuzhiyun vga_out8(0x3d4, 0x53, par);
594*4882a593Smuzhiyun reg->CR53 = vga_in8(0x3d5, par);
595*4882a593Smuzhiyun vga_out8(0x3d4, 0x58, par);
596*4882a593Smuzhiyun reg->CR58 = vga_in8(0x3d5, par);
597*4882a593Smuzhiyun vga_out8(0x3d4, 0x60, par);
598*4882a593Smuzhiyun reg->CR60 = vga_in8(0x3d5, par);
599*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
600*4882a593Smuzhiyun reg->CR66 = vga_in8(0x3d5, par);
601*4882a593Smuzhiyun vga_out8(0x3d4, 0x67, par);
602*4882a593Smuzhiyun reg->CR67 = vga_in8(0x3d5, par);
603*4882a593Smuzhiyun vga_out8(0x3d4, 0x68, par);
604*4882a593Smuzhiyun reg->CR68 = vga_in8(0x3d5, par);
605*4882a593Smuzhiyun vga_out8(0x3d4, 0x69, par);
606*4882a593Smuzhiyun reg->CR69 = vga_in8(0x3d5, par);
607*4882a593Smuzhiyun vga_out8(0x3d4, 0x6f, par);
608*4882a593Smuzhiyun reg->CR6F = vga_in8(0x3d5, par);
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun vga_out8(0x3d4, 0x33, par);
611*4882a593Smuzhiyun reg->CR33 = vga_in8(0x3d5, par);
612*4882a593Smuzhiyun vga_out8(0x3d4, 0x86, par);
613*4882a593Smuzhiyun reg->CR86 = vga_in8(0x3d5, par);
614*4882a593Smuzhiyun vga_out8(0x3d4, 0x88, par);
615*4882a593Smuzhiyun reg->CR88 = vga_in8(0x3d5, par);
616*4882a593Smuzhiyun vga_out8(0x3d4, 0x90, par);
617*4882a593Smuzhiyun reg->CR90 = vga_in8(0x3d5, par);
618*4882a593Smuzhiyun vga_out8(0x3d4, 0x91, par);
619*4882a593Smuzhiyun reg->CR91 = vga_in8(0x3d5, par);
620*4882a593Smuzhiyun vga_out8(0x3d4, 0xb0, par);
621*4882a593Smuzhiyun reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun /* extended mode timing regs */
624*4882a593Smuzhiyun vga_out8(0x3d4, 0x3b, par);
625*4882a593Smuzhiyun reg->CR3B = vga_in8(0x3d5, par);
626*4882a593Smuzhiyun vga_out8(0x3d4, 0x3c, par);
627*4882a593Smuzhiyun reg->CR3C = vga_in8(0x3d5, par);
628*4882a593Smuzhiyun vga_out8(0x3d4, 0x43, par);
629*4882a593Smuzhiyun reg->CR43 = vga_in8(0x3d5, par);
630*4882a593Smuzhiyun vga_out8(0x3d4, 0x5d, par);
631*4882a593Smuzhiyun reg->CR5D = vga_in8(0x3d5, par);
632*4882a593Smuzhiyun vga_out8(0x3d4, 0x5e, par);
633*4882a593Smuzhiyun reg->CR5E = vga_in8(0x3d5, par);
634*4882a593Smuzhiyun vga_out8(0x3d4, 0x65, par);
635*4882a593Smuzhiyun reg->CR65 = vga_in8(0x3d5, par);
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun /* save seq extended regs for DCLK PLL programming */
638*4882a593Smuzhiyun vga_out8(0x3c4, 0x0e, par);
639*4882a593Smuzhiyun reg->SR0E = vga_in8(0x3c5, par);
640*4882a593Smuzhiyun vga_out8(0x3c4, 0x0f, par);
641*4882a593Smuzhiyun reg->SR0F = vga_in8(0x3c5, par);
642*4882a593Smuzhiyun vga_out8(0x3c4, 0x10, par);
643*4882a593Smuzhiyun reg->SR10 = vga_in8(0x3c5, par);
644*4882a593Smuzhiyun vga_out8(0x3c4, 0x11, par);
645*4882a593Smuzhiyun reg->SR11 = vga_in8(0x3c5, par);
646*4882a593Smuzhiyun vga_out8(0x3c4, 0x12, par);
647*4882a593Smuzhiyun reg->SR12 = vga_in8(0x3c5, par);
648*4882a593Smuzhiyun vga_out8(0x3c4, 0x13, par);
649*4882a593Smuzhiyun reg->SR13 = vga_in8(0x3c5, par);
650*4882a593Smuzhiyun vga_out8(0x3c4, 0x29, par);
651*4882a593Smuzhiyun reg->SR29 = vga_in8(0x3c5, par);
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun vga_out8(0x3c4, 0x15, par);
654*4882a593Smuzhiyun reg->SR15 = vga_in8(0x3c5, par);
655*4882a593Smuzhiyun vga_out8(0x3c4, 0x30, par);
656*4882a593Smuzhiyun reg->SR30 = vga_in8(0x3c5, par);
657*4882a593Smuzhiyun vga_out8(0x3c4, 0x18, par);
658*4882a593Smuzhiyun reg->SR18 = vga_in8(0x3c5, par);
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun /* Save flat panel expansion registers. */
661*4882a593Smuzhiyun if (par->chip == S3_SAVAGE_MX) {
662*4882a593Smuzhiyun int i;
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun for (i = 0; i < 8; i++) {
665*4882a593Smuzhiyun vga_out8(0x3c4, 0x54+i, par);
666*4882a593Smuzhiyun reg->SR54[i] = vga_in8(0x3c5, par);
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun }
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
671*4882a593Smuzhiyun cr66 = vga_in8(0x3d5, par);
672*4882a593Smuzhiyun vga_out8(0x3d5, cr66 | 0x80, par);
673*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
674*4882a593Smuzhiyun cr3a = vga_in8(0x3d5, par);
675*4882a593Smuzhiyun vga_out8(0x3d5, cr3a | 0x80, par);
676*4882a593Smuzhiyun
677*4882a593Smuzhiyun /* now save MIU regs */
678*4882a593Smuzhiyun if (par->chip != S3_SAVAGE_MX) {
679*4882a593Smuzhiyun reg->MMPR0 = savage_in32(FIFO_CONTROL_REG, par);
680*4882a593Smuzhiyun reg->MMPR1 = savage_in32(MIU_CONTROL_REG, par);
681*4882a593Smuzhiyun reg->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par);
682*4882a593Smuzhiyun reg->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par);
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
686*4882a593Smuzhiyun vga_out8(0x3d5, cr3a, par);
687*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
688*4882a593Smuzhiyun vga_out8(0x3d5, cr66, par);
689*4882a593Smuzhiyun }
690*4882a593Smuzhiyun
savage_set_default_par(struct savagefb_par * par,struct savage_reg * reg)691*4882a593Smuzhiyun static void savage_set_default_par(struct savagefb_par *par,
692*4882a593Smuzhiyun struct savage_reg *reg)
693*4882a593Smuzhiyun {
694*4882a593Smuzhiyun unsigned char cr3a, cr53, cr66;
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun vga_out16(0x3d4, 0x4838, par);
697*4882a593Smuzhiyun vga_out16(0x3d4, 0xa039, par);
698*4882a593Smuzhiyun vga_out16(0x3c4, 0x0608, par);
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
701*4882a593Smuzhiyun cr66 = vga_in8(0x3d5, par);
702*4882a593Smuzhiyun vga_out8(0x3d5, cr66 | 0x80, par);
703*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
704*4882a593Smuzhiyun cr3a = vga_in8(0x3d5, par);
705*4882a593Smuzhiyun vga_out8(0x3d5, cr3a | 0x80, par);
706*4882a593Smuzhiyun vga_out8(0x3d4, 0x53, par);
707*4882a593Smuzhiyun cr53 = vga_in8(0x3d5, par);
708*4882a593Smuzhiyun vga_out8(0x3d5, cr53 & 0x7f, par);
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
711*4882a593Smuzhiyun vga_out8(0x3d5, cr66, par);
712*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
713*4882a593Smuzhiyun vga_out8(0x3d5, cr3a, par);
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
716*4882a593Smuzhiyun vga_out8(0x3d5, cr66, par);
717*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
718*4882a593Smuzhiyun vga_out8(0x3d5, cr3a, par);
719*4882a593Smuzhiyun
720*4882a593Smuzhiyun /* unlock extended seq regs */
721*4882a593Smuzhiyun vga_out8(0x3c4, 0x08, par);
722*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR08, par);
723*4882a593Smuzhiyun vga_out8(0x3c5, 0x06, par);
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun /* now restore all the extended regs we need */
726*4882a593Smuzhiyun vga_out8(0x3d4, 0x31, par);
727*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR31, par);
728*4882a593Smuzhiyun vga_out8(0x3d4, 0x32, par);
729*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR32, par);
730*4882a593Smuzhiyun vga_out8(0x3d4, 0x34, par);
731*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR34, par);
732*4882a593Smuzhiyun vga_out8(0x3d4, 0x36, par);
733*4882a593Smuzhiyun vga_out8(0x3d5,reg->CR36, par);
734*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
735*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR3A, par);
736*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
737*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR40, par);
738*4882a593Smuzhiyun vga_out8(0x3d4, 0x42, par);
739*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR42, par);
740*4882a593Smuzhiyun vga_out8(0x3d4, 0x45, par);
741*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR45, par);
742*4882a593Smuzhiyun vga_out8(0x3d4, 0x50, par);
743*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR50, par);
744*4882a593Smuzhiyun vga_out8(0x3d4, 0x51, par);
745*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR51, par);
746*4882a593Smuzhiyun vga_out8(0x3d4, 0x53, par);
747*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR53, par);
748*4882a593Smuzhiyun vga_out8(0x3d4, 0x58, par);
749*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR58, par);
750*4882a593Smuzhiyun vga_out8(0x3d4, 0x60, par);
751*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR60, par);
752*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
753*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR66, par);
754*4882a593Smuzhiyun vga_out8(0x3d4, 0x67, par);
755*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR67, par);
756*4882a593Smuzhiyun vga_out8(0x3d4, 0x68, par);
757*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR68, par);
758*4882a593Smuzhiyun vga_out8(0x3d4, 0x69, par);
759*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR69, par);
760*4882a593Smuzhiyun vga_out8(0x3d4, 0x6f, par);
761*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR6F, par);
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun vga_out8(0x3d4, 0x33, par);
764*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR33, par);
765*4882a593Smuzhiyun vga_out8(0x3d4, 0x86, par);
766*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR86, par);
767*4882a593Smuzhiyun vga_out8(0x3d4, 0x88, par);
768*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR88, par);
769*4882a593Smuzhiyun vga_out8(0x3d4, 0x90, par);
770*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR90, par);
771*4882a593Smuzhiyun vga_out8(0x3d4, 0x91, par);
772*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR91, par);
773*4882a593Smuzhiyun vga_out8(0x3d4, 0xb0, par);
774*4882a593Smuzhiyun vga_out8(0x3d5, reg->CRB0, par);
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun /* extended mode timing regs */
777*4882a593Smuzhiyun vga_out8(0x3d4, 0x3b, par);
778*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR3B, par);
779*4882a593Smuzhiyun vga_out8(0x3d4, 0x3c, par);
780*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR3C, par);
781*4882a593Smuzhiyun vga_out8(0x3d4, 0x43, par);
782*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR43, par);
783*4882a593Smuzhiyun vga_out8(0x3d4, 0x5d, par);
784*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR5D, par);
785*4882a593Smuzhiyun vga_out8(0x3d4, 0x5e, par);
786*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR5E, par);
787*4882a593Smuzhiyun vga_out8(0x3d4, 0x65, par);
788*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR65, par);
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun /* save seq extended regs for DCLK PLL programming */
791*4882a593Smuzhiyun vga_out8(0x3c4, 0x0e, par);
792*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR0E, par);
793*4882a593Smuzhiyun vga_out8(0x3c4, 0x0f, par);
794*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR0F, par);
795*4882a593Smuzhiyun vga_out8(0x3c4, 0x10, par);
796*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR10, par);
797*4882a593Smuzhiyun vga_out8(0x3c4, 0x11, par);
798*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR11, par);
799*4882a593Smuzhiyun vga_out8(0x3c4, 0x12, par);
800*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR12, par);
801*4882a593Smuzhiyun vga_out8(0x3c4, 0x13, par);
802*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR13, par);
803*4882a593Smuzhiyun vga_out8(0x3c4, 0x29, par);
804*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR29, par);
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun vga_out8(0x3c4, 0x15, par);
807*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR15, par);
808*4882a593Smuzhiyun vga_out8(0x3c4, 0x30, par);
809*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR30, par);
810*4882a593Smuzhiyun vga_out8(0x3c4, 0x18, par);
811*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR18, par);
812*4882a593Smuzhiyun
813*4882a593Smuzhiyun /* Save flat panel expansion registers. */
814*4882a593Smuzhiyun if (par->chip == S3_SAVAGE_MX) {
815*4882a593Smuzhiyun int i;
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun for (i = 0; i < 8; i++) {
818*4882a593Smuzhiyun vga_out8(0x3c4, 0x54+i, par);
819*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR54[i], par);
820*4882a593Smuzhiyun }
821*4882a593Smuzhiyun }
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
824*4882a593Smuzhiyun cr66 = vga_in8(0x3d5, par);
825*4882a593Smuzhiyun vga_out8(0x3d5, cr66 | 0x80, par);
826*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
827*4882a593Smuzhiyun cr3a = vga_in8(0x3d5, par);
828*4882a593Smuzhiyun vga_out8(0x3d5, cr3a | 0x80, par);
829*4882a593Smuzhiyun
830*4882a593Smuzhiyun /* now save MIU regs */
831*4882a593Smuzhiyun if (par->chip != S3_SAVAGE_MX) {
832*4882a593Smuzhiyun savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
833*4882a593Smuzhiyun savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
834*4882a593Smuzhiyun savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
835*4882a593Smuzhiyun savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
836*4882a593Smuzhiyun }
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
839*4882a593Smuzhiyun vga_out8(0x3d5, cr3a, par);
840*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
841*4882a593Smuzhiyun vga_out8(0x3d5, cr66, par);
842*4882a593Smuzhiyun }
843*4882a593Smuzhiyun
savage_update_var(struct fb_var_screeninfo * var,const struct fb_videomode * modedb)844*4882a593Smuzhiyun static void savage_update_var(struct fb_var_screeninfo *var,
845*4882a593Smuzhiyun const struct fb_videomode *modedb)
846*4882a593Smuzhiyun {
847*4882a593Smuzhiyun var->xres = var->xres_virtual = modedb->xres;
848*4882a593Smuzhiyun var->yres = modedb->yres;
849*4882a593Smuzhiyun if (var->yres_virtual < var->yres)
850*4882a593Smuzhiyun var->yres_virtual = var->yres;
851*4882a593Smuzhiyun var->xoffset = var->yoffset = 0;
852*4882a593Smuzhiyun var->pixclock = modedb->pixclock;
853*4882a593Smuzhiyun var->left_margin = modedb->left_margin;
854*4882a593Smuzhiyun var->right_margin = modedb->right_margin;
855*4882a593Smuzhiyun var->upper_margin = modedb->upper_margin;
856*4882a593Smuzhiyun var->lower_margin = modedb->lower_margin;
857*4882a593Smuzhiyun var->hsync_len = modedb->hsync_len;
858*4882a593Smuzhiyun var->vsync_len = modedb->vsync_len;
859*4882a593Smuzhiyun var->sync = modedb->sync;
860*4882a593Smuzhiyun var->vmode = modedb->vmode;
861*4882a593Smuzhiyun }
862*4882a593Smuzhiyun
savagefb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)863*4882a593Smuzhiyun static int savagefb_check_var(struct fb_var_screeninfo *var,
864*4882a593Smuzhiyun struct fb_info *info)
865*4882a593Smuzhiyun {
866*4882a593Smuzhiyun struct savagefb_par *par = info->par;
867*4882a593Smuzhiyun int memlen, vramlen, mode_valid = 0;
868*4882a593Smuzhiyun
869*4882a593Smuzhiyun DBG("savagefb_check_var");
870*4882a593Smuzhiyun
871*4882a593Smuzhiyun var->transp.offset = 0;
872*4882a593Smuzhiyun var->transp.length = 0;
873*4882a593Smuzhiyun switch (var->bits_per_pixel) {
874*4882a593Smuzhiyun case 8:
875*4882a593Smuzhiyun var->red.offset = var->green.offset =
876*4882a593Smuzhiyun var->blue.offset = 0;
877*4882a593Smuzhiyun var->red.length = var->green.length =
878*4882a593Smuzhiyun var->blue.length = var->bits_per_pixel;
879*4882a593Smuzhiyun break;
880*4882a593Smuzhiyun case 16:
881*4882a593Smuzhiyun var->red.offset = 11;
882*4882a593Smuzhiyun var->red.length = 5;
883*4882a593Smuzhiyun var->green.offset = 5;
884*4882a593Smuzhiyun var->green.length = 6;
885*4882a593Smuzhiyun var->blue.offset = 0;
886*4882a593Smuzhiyun var->blue.length = 5;
887*4882a593Smuzhiyun break;
888*4882a593Smuzhiyun case 32:
889*4882a593Smuzhiyun var->transp.offset = 24;
890*4882a593Smuzhiyun var->transp.length = 8;
891*4882a593Smuzhiyun var->red.offset = 16;
892*4882a593Smuzhiyun var->red.length = 8;
893*4882a593Smuzhiyun var->green.offset = 8;
894*4882a593Smuzhiyun var->green.length = 8;
895*4882a593Smuzhiyun var->blue.offset = 0;
896*4882a593Smuzhiyun var->blue.length = 8;
897*4882a593Smuzhiyun break;
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun default:
900*4882a593Smuzhiyun return -EINVAL;
901*4882a593Smuzhiyun }
902*4882a593Smuzhiyun
903*4882a593Smuzhiyun if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
904*4882a593Smuzhiyun !info->monspecs.dclkmax || !fb_validate_mode(var, info))
905*4882a593Smuzhiyun mode_valid = 1;
906*4882a593Smuzhiyun
907*4882a593Smuzhiyun /* calculate modeline if supported by monitor */
908*4882a593Smuzhiyun if (!mode_valid && info->monspecs.gtf) {
909*4882a593Smuzhiyun if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
910*4882a593Smuzhiyun mode_valid = 1;
911*4882a593Smuzhiyun }
912*4882a593Smuzhiyun
913*4882a593Smuzhiyun if (!mode_valid) {
914*4882a593Smuzhiyun const struct fb_videomode *mode;
915*4882a593Smuzhiyun
916*4882a593Smuzhiyun mode = fb_find_best_mode(var, &info->modelist);
917*4882a593Smuzhiyun if (mode) {
918*4882a593Smuzhiyun savage_update_var(var, mode);
919*4882a593Smuzhiyun mode_valid = 1;
920*4882a593Smuzhiyun }
921*4882a593Smuzhiyun }
922*4882a593Smuzhiyun
923*4882a593Smuzhiyun if (!mode_valid && info->monspecs.modedb_len)
924*4882a593Smuzhiyun return -EINVAL;
925*4882a593Smuzhiyun
926*4882a593Smuzhiyun /* Is the mode larger than the LCD panel? */
927*4882a593Smuzhiyun if (par->SavagePanelWidth &&
928*4882a593Smuzhiyun (var->xres > par->SavagePanelWidth ||
929*4882a593Smuzhiyun var->yres > par->SavagePanelHeight)) {
930*4882a593Smuzhiyun printk(KERN_INFO "Mode (%dx%d) larger than the LCD panel "
931*4882a593Smuzhiyun "(%dx%d)\n", var->xres, var->yres,
932*4882a593Smuzhiyun par->SavagePanelWidth,
933*4882a593Smuzhiyun par->SavagePanelHeight);
934*4882a593Smuzhiyun return -1;
935*4882a593Smuzhiyun }
936*4882a593Smuzhiyun
937*4882a593Smuzhiyun if (var->yres_virtual < var->yres)
938*4882a593Smuzhiyun var->yres_virtual = var->yres;
939*4882a593Smuzhiyun if (var->xres_virtual < var->xres)
940*4882a593Smuzhiyun var->xres_virtual = var->xres;
941*4882a593Smuzhiyun
942*4882a593Smuzhiyun vramlen = info->fix.smem_len;
943*4882a593Smuzhiyun
944*4882a593Smuzhiyun memlen = var->xres_virtual * var->bits_per_pixel *
945*4882a593Smuzhiyun var->yres_virtual / 8;
946*4882a593Smuzhiyun if (memlen > vramlen) {
947*4882a593Smuzhiyun var->yres_virtual = vramlen * 8 /
948*4882a593Smuzhiyun (var->xres_virtual * var->bits_per_pixel);
949*4882a593Smuzhiyun memlen = var->xres_virtual * var->bits_per_pixel *
950*4882a593Smuzhiyun var->yres_virtual / 8;
951*4882a593Smuzhiyun }
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun /* we must round yres/xres down, we already rounded y/xres_virtual up
954*4882a593Smuzhiyun if it was possible. We should return -EINVAL, but I disagree */
955*4882a593Smuzhiyun if (var->yres_virtual < var->yres)
956*4882a593Smuzhiyun var->yres = var->yres_virtual;
957*4882a593Smuzhiyun if (var->xres_virtual < var->xres)
958*4882a593Smuzhiyun var->xres = var->xres_virtual;
959*4882a593Smuzhiyun if (var->xoffset + var->xres > var->xres_virtual)
960*4882a593Smuzhiyun var->xoffset = var->xres_virtual - var->xres;
961*4882a593Smuzhiyun if (var->yoffset + var->yres > var->yres_virtual)
962*4882a593Smuzhiyun var->yoffset = var->yres_virtual - var->yres;
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun return 0;
965*4882a593Smuzhiyun }
966*4882a593Smuzhiyun
967*4882a593Smuzhiyun
savagefb_decode_var(struct fb_var_screeninfo * var,struct savagefb_par * par,struct savage_reg * reg)968*4882a593Smuzhiyun static int savagefb_decode_var(struct fb_var_screeninfo *var,
969*4882a593Smuzhiyun struct savagefb_par *par,
970*4882a593Smuzhiyun struct savage_reg *reg)
971*4882a593Smuzhiyun {
972*4882a593Smuzhiyun struct xtimings timings;
973*4882a593Smuzhiyun int width, dclk, i, j; /*, refresh; */
974*4882a593Smuzhiyun unsigned int m, n, r;
975*4882a593Smuzhiyun unsigned char tmp = 0;
976*4882a593Smuzhiyun unsigned int pixclock = var->pixclock;
977*4882a593Smuzhiyun
978*4882a593Smuzhiyun DBG("savagefb_decode_var");
979*4882a593Smuzhiyun
980*4882a593Smuzhiyun memset(&timings, 0, sizeof(timings));
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun if (!pixclock) pixclock = 10000; /* 10ns = 100MHz */
983*4882a593Smuzhiyun timings.Clock = 1000000000 / pixclock;
984*4882a593Smuzhiyun if (timings.Clock < 1) timings.Clock = 1;
985*4882a593Smuzhiyun timings.dblscan = var->vmode & FB_VMODE_DOUBLE;
986*4882a593Smuzhiyun timings.interlaced = var->vmode & FB_VMODE_INTERLACED;
987*4882a593Smuzhiyun timings.HDisplay = var->xres;
988*4882a593Smuzhiyun timings.HSyncStart = timings.HDisplay + var->right_margin;
989*4882a593Smuzhiyun timings.HSyncEnd = timings.HSyncStart + var->hsync_len;
990*4882a593Smuzhiyun timings.HTotal = timings.HSyncEnd + var->left_margin;
991*4882a593Smuzhiyun timings.VDisplay = var->yres;
992*4882a593Smuzhiyun timings.VSyncStart = timings.VDisplay + var->lower_margin;
993*4882a593Smuzhiyun timings.VSyncEnd = timings.VSyncStart + var->vsync_len;
994*4882a593Smuzhiyun timings.VTotal = timings.VSyncEnd + var->upper_margin;
995*4882a593Smuzhiyun timings.sync = var->sync;
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun
998*4882a593Smuzhiyun par->depth = var->bits_per_pixel;
999*4882a593Smuzhiyun par->vwidth = var->xres_virtual;
1000*4882a593Smuzhiyun
1001*4882a593Smuzhiyun if (var->bits_per_pixel == 16 && par->chip == S3_SAVAGE3D) {
1002*4882a593Smuzhiyun timings.HDisplay *= 2;
1003*4882a593Smuzhiyun timings.HSyncStart *= 2;
1004*4882a593Smuzhiyun timings.HSyncEnd *= 2;
1005*4882a593Smuzhiyun timings.HTotal *= 2;
1006*4882a593Smuzhiyun }
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun /*
1009*4882a593Smuzhiyun * This will allocate the datastructure and initialize all of the
1010*4882a593Smuzhiyun * generic VGA registers.
1011*4882a593Smuzhiyun */
1012*4882a593Smuzhiyun vgaHWInit(var, par, &timings, reg);
1013*4882a593Smuzhiyun
1014*4882a593Smuzhiyun /* We need to set CR67 whether or not we use the BIOS. */
1015*4882a593Smuzhiyun
1016*4882a593Smuzhiyun dclk = timings.Clock;
1017*4882a593Smuzhiyun reg->CR67 = 0x00;
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun switch(var->bits_per_pixel) {
1020*4882a593Smuzhiyun case 8:
1021*4882a593Smuzhiyun if ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))
1022*4882a593Smuzhiyun reg->CR67 = 0x10; /* 8bpp, 2 pixels/clock */
1023*4882a593Smuzhiyun else
1024*4882a593Smuzhiyun reg->CR67 = 0x00; /* 8bpp, 1 pixel/clock */
1025*4882a593Smuzhiyun break;
1026*4882a593Smuzhiyun case 15:
1027*4882a593Smuzhiyun if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
1028*4882a593Smuzhiyun ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
1029*4882a593Smuzhiyun reg->CR67 = 0x30; /* 15bpp, 2 pixel/clock */
1030*4882a593Smuzhiyun else
1031*4882a593Smuzhiyun reg->CR67 = 0x20; /* 15bpp, 1 pixels/clock */
1032*4882a593Smuzhiyun break;
1033*4882a593Smuzhiyun case 16:
1034*4882a593Smuzhiyun if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
1035*4882a593Smuzhiyun ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
1036*4882a593Smuzhiyun reg->CR67 = 0x50; /* 16bpp, 2 pixel/clock */
1037*4882a593Smuzhiyun else
1038*4882a593Smuzhiyun reg->CR67 = 0x40; /* 16bpp, 1 pixels/clock */
1039*4882a593Smuzhiyun break;
1040*4882a593Smuzhiyun case 24:
1041*4882a593Smuzhiyun reg->CR67 = 0x70;
1042*4882a593Smuzhiyun break;
1043*4882a593Smuzhiyun case 32:
1044*4882a593Smuzhiyun reg->CR67 = 0xd0;
1045*4882a593Smuzhiyun break;
1046*4882a593Smuzhiyun }
1047*4882a593Smuzhiyun
1048*4882a593Smuzhiyun /*
1049*4882a593Smuzhiyun * Either BIOS use is disabled, or we failed to find a suitable
1050*4882a593Smuzhiyun * match. Fall back to traditional register-crunching.
1051*4882a593Smuzhiyun */
1052*4882a593Smuzhiyun
1053*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
1054*4882a593Smuzhiyun tmp = vga_in8(0x3d5, par);
1055*4882a593Smuzhiyun if (1 /*FIXME:psav->pci_burst*/)
1056*4882a593Smuzhiyun reg->CR3A = (tmp & 0x7f) | 0x15;
1057*4882a593Smuzhiyun else
1058*4882a593Smuzhiyun reg->CR3A = tmp | 0x95;
1059*4882a593Smuzhiyun
1060*4882a593Smuzhiyun reg->CR53 = 0x00;
1061*4882a593Smuzhiyun reg->CR31 = 0x8c;
1062*4882a593Smuzhiyun reg->CR66 = 0x89;
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun vga_out8(0x3d4, 0x58, par);
1065*4882a593Smuzhiyun reg->CR58 = vga_in8(0x3d5, par) & 0x80;
1066*4882a593Smuzhiyun reg->CR58 |= 0x13;
1067*4882a593Smuzhiyun
1068*4882a593Smuzhiyun reg->SR15 = 0x03 | 0x80;
1069*4882a593Smuzhiyun reg->SR18 = 0x00;
1070*4882a593Smuzhiyun reg->CR43 = reg->CR45 = reg->CR65 = 0x00;
1071*4882a593Smuzhiyun
1072*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
1073*4882a593Smuzhiyun reg->CR40 = vga_in8(0x3d5, par) & ~0x01;
1074*4882a593Smuzhiyun
1075*4882a593Smuzhiyun reg->MMPR0 = 0x010400;
1076*4882a593Smuzhiyun reg->MMPR1 = 0x00;
1077*4882a593Smuzhiyun reg->MMPR2 = 0x0808;
1078*4882a593Smuzhiyun reg->MMPR3 = 0x08080810;
1079*4882a593Smuzhiyun
1080*4882a593Smuzhiyun SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
1081*4882a593Smuzhiyun /* m = 107; n = 4; r = 2; */
1082*4882a593Smuzhiyun
1083*4882a593Smuzhiyun if (par->MCLK <= 0) {
1084*4882a593Smuzhiyun reg->SR10 = 255;
1085*4882a593Smuzhiyun reg->SR11 = 255;
1086*4882a593Smuzhiyun } else {
1087*4882a593Smuzhiyun common_calc_clock(par->MCLK, 1, 1, 31, 0, 3, 135000, 270000,
1088*4882a593Smuzhiyun ®->SR11, ®->SR10);
1089*4882a593Smuzhiyun /* reg->SR10 = 80; // MCLK == 286000 */
1090*4882a593Smuzhiyun /* reg->SR11 = 125; */
1091*4882a593Smuzhiyun }
1092*4882a593Smuzhiyun
1093*4882a593Smuzhiyun reg->SR12 = (r << 6) | (n & 0x3f);
1094*4882a593Smuzhiyun reg->SR13 = m & 0xff;
1095*4882a593Smuzhiyun reg->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
1096*4882a593Smuzhiyun
1097*4882a593Smuzhiyun if (var->bits_per_pixel < 24)
1098*4882a593Smuzhiyun reg->MMPR0 -= 0x8000;
1099*4882a593Smuzhiyun else
1100*4882a593Smuzhiyun reg->MMPR0 -= 0x4000;
1101*4882a593Smuzhiyun
1102*4882a593Smuzhiyun if (timings.interlaced)
1103*4882a593Smuzhiyun reg->CR42 = 0x20;
1104*4882a593Smuzhiyun else
1105*4882a593Smuzhiyun reg->CR42 = 0x00;
1106*4882a593Smuzhiyun
1107*4882a593Smuzhiyun reg->CR34 = 0x10; /* display fifo */
1108*4882a593Smuzhiyun
1109*4882a593Smuzhiyun i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) |
1110*4882a593Smuzhiyun ((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) |
1111*4882a593Smuzhiyun ((((timings.HSyncStart >> 3) - 1) & 0x100) >> 6) |
1112*4882a593Smuzhiyun ((timings.HSyncStart & 0x800) >> 7);
1113*4882a593Smuzhiyun
1114*4882a593Smuzhiyun if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 64)
1115*4882a593Smuzhiyun i |= 0x08;
1116*4882a593Smuzhiyun if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32)
1117*4882a593Smuzhiyun i |= 0x20;
1118*4882a593Smuzhiyun
1119*4882a593Smuzhiyun j = (reg->CRTC[0] + ((i & 0x01) << 8) +
1120*4882a593Smuzhiyun reg->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
1121*4882a593Smuzhiyun
1122*4882a593Smuzhiyun if (j - (reg->CRTC[4] + ((i & 0x10) << 4)) < 4) {
1123*4882a593Smuzhiyun if (reg->CRTC[4] + ((i & 0x10) << 4) + 4 <=
1124*4882a593Smuzhiyun reg->CRTC[0] + ((i & 0x01) << 8))
1125*4882a593Smuzhiyun j = reg->CRTC[4] + ((i & 0x10) << 4) + 4;
1126*4882a593Smuzhiyun else
1127*4882a593Smuzhiyun j = reg->CRTC[0] + ((i & 0x01) << 8) + 1;
1128*4882a593Smuzhiyun }
1129*4882a593Smuzhiyun
1130*4882a593Smuzhiyun reg->CR3B = j & 0xff;
1131*4882a593Smuzhiyun i |= (j & 0x100) >> 2;
1132*4882a593Smuzhiyun reg->CR3C = (reg->CRTC[0] + ((i & 0x01) << 8)) / 2;
1133*4882a593Smuzhiyun reg->CR5D = i;
1134*4882a593Smuzhiyun reg->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) |
1135*4882a593Smuzhiyun (((timings.VDisplay - 1) & 0x400) >> 9) |
1136*4882a593Smuzhiyun (((timings.VSyncStart) & 0x400) >> 8) |
1137*4882a593Smuzhiyun (((timings.VSyncStart) & 0x400) >> 6) | 0x40;
1138*4882a593Smuzhiyun width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3;
1139*4882a593Smuzhiyun reg->CR91 = reg->CRTC[19] = 0xff & width;
1140*4882a593Smuzhiyun reg->CR51 = (0x300 & width) >> 4;
1141*4882a593Smuzhiyun reg->CR90 = 0x80 | (width >> 8);
1142*4882a593Smuzhiyun reg->MiscOutReg |= 0x0c;
1143*4882a593Smuzhiyun
1144*4882a593Smuzhiyun /* Set frame buffer description. */
1145*4882a593Smuzhiyun
1146*4882a593Smuzhiyun if (var->bits_per_pixel <= 8)
1147*4882a593Smuzhiyun reg->CR50 = 0;
1148*4882a593Smuzhiyun else if (var->bits_per_pixel <= 16)
1149*4882a593Smuzhiyun reg->CR50 = 0x10;
1150*4882a593Smuzhiyun else
1151*4882a593Smuzhiyun reg->CR50 = 0x30;
1152*4882a593Smuzhiyun
1153*4882a593Smuzhiyun if (var->xres_virtual <= 640)
1154*4882a593Smuzhiyun reg->CR50 |= 0x40;
1155*4882a593Smuzhiyun else if (var->xres_virtual == 800)
1156*4882a593Smuzhiyun reg->CR50 |= 0x80;
1157*4882a593Smuzhiyun else if (var->xres_virtual == 1024)
1158*4882a593Smuzhiyun reg->CR50 |= 0x00;
1159*4882a593Smuzhiyun else if (var->xres_virtual == 1152)
1160*4882a593Smuzhiyun reg->CR50 |= 0x01;
1161*4882a593Smuzhiyun else if (var->xres_virtual == 1280)
1162*4882a593Smuzhiyun reg->CR50 |= 0xc0;
1163*4882a593Smuzhiyun else if (var->xres_virtual == 1600)
1164*4882a593Smuzhiyun reg->CR50 |= 0x81;
1165*4882a593Smuzhiyun else
1166*4882a593Smuzhiyun reg->CR50 |= 0xc1; /* Use GBD */
1167*4882a593Smuzhiyun
1168*4882a593Smuzhiyun if (par->chip == S3_SAVAGE2000)
1169*4882a593Smuzhiyun reg->CR33 = 0x08;
1170*4882a593Smuzhiyun else
1171*4882a593Smuzhiyun reg->CR33 = 0x20;
1172*4882a593Smuzhiyun
1173*4882a593Smuzhiyun reg->CRTC[0x17] = 0xeb;
1174*4882a593Smuzhiyun
1175*4882a593Smuzhiyun reg->CR67 |= 1;
1176*4882a593Smuzhiyun
1177*4882a593Smuzhiyun vga_out8(0x3d4, 0x36, par);
1178*4882a593Smuzhiyun reg->CR36 = vga_in8(0x3d5, par);
1179*4882a593Smuzhiyun vga_out8(0x3d4, 0x68, par);
1180*4882a593Smuzhiyun reg->CR68 = vga_in8(0x3d5, par);
1181*4882a593Smuzhiyun reg->CR69 = 0;
1182*4882a593Smuzhiyun vga_out8(0x3d4, 0x6f, par);
1183*4882a593Smuzhiyun reg->CR6F = vga_in8(0x3d5, par);
1184*4882a593Smuzhiyun vga_out8(0x3d4, 0x86, par);
1185*4882a593Smuzhiyun reg->CR86 = vga_in8(0x3d5, par);
1186*4882a593Smuzhiyun vga_out8(0x3d4, 0x88, par);
1187*4882a593Smuzhiyun reg->CR88 = vga_in8(0x3d5, par) | 0x08;
1188*4882a593Smuzhiyun vga_out8(0x3d4, 0xb0, par);
1189*4882a593Smuzhiyun reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
1190*4882a593Smuzhiyun
1191*4882a593Smuzhiyun return 0;
1192*4882a593Smuzhiyun }
1193*4882a593Smuzhiyun
1194*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
1195*4882a593Smuzhiyun
1196*4882a593Smuzhiyun /*
1197*4882a593Smuzhiyun * Set a single color register. Return != 0 for invalid regno.
1198*4882a593Smuzhiyun */
savagefb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)1199*4882a593Smuzhiyun static int savagefb_setcolreg(unsigned regno,
1200*4882a593Smuzhiyun unsigned red,
1201*4882a593Smuzhiyun unsigned green,
1202*4882a593Smuzhiyun unsigned blue,
1203*4882a593Smuzhiyun unsigned transp,
1204*4882a593Smuzhiyun struct fb_info *info)
1205*4882a593Smuzhiyun {
1206*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1207*4882a593Smuzhiyun
1208*4882a593Smuzhiyun if (regno >= NR_PALETTE)
1209*4882a593Smuzhiyun return -EINVAL;
1210*4882a593Smuzhiyun
1211*4882a593Smuzhiyun par->palette[regno].red = red;
1212*4882a593Smuzhiyun par->palette[regno].green = green;
1213*4882a593Smuzhiyun par->palette[regno].blue = blue;
1214*4882a593Smuzhiyun par->palette[regno].transp = transp;
1215*4882a593Smuzhiyun
1216*4882a593Smuzhiyun switch (info->var.bits_per_pixel) {
1217*4882a593Smuzhiyun case 8:
1218*4882a593Smuzhiyun vga_out8(0x3c8, regno, par);
1219*4882a593Smuzhiyun
1220*4882a593Smuzhiyun vga_out8(0x3c9, red >> 10, par);
1221*4882a593Smuzhiyun vga_out8(0x3c9, green >> 10, par);
1222*4882a593Smuzhiyun vga_out8(0x3c9, blue >> 10, par);
1223*4882a593Smuzhiyun break;
1224*4882a593Smuzhiyun
1225*4882a593Smuzhiyun case 16:
1226*4882a593Smuzhiyun if (regno < 16)
1227*4882a593Smuzhiyun ((u32 *)info->pseudo_palette)[regno] =
1228*4882a593Smuzhiyun ((red & 0xf800) ) |
1229*4882a593Smuzhiyun ((green & 0xfc00) >> 5) |
1230*4882a593Smuzhiyun ((blue & 0xf800) >> 11);
1231*4882a593Smuzhiyun break;
1232*4882a593Smuzhiyun
1233*4882a593Smuzhiyun case 24:
1234*4882a593Smuzhiyun if (regno < 16)
1235*4882a593Smuzhiyun ((u32 *)info->pseudo_palette)[regno] =
1236*4882a593Smuzhiyun ((red & 0xff00) << 8) |
1237*4882a593Smuzhiyun ((green & 0xff00) ) |
1238*4882a593Smuzhiyun ((blue & 0xff00) >> 8);
1239*4882a593Smuzhiyun break;
1240*4882a593Smuzhiyun case 32:
1241*4882a593Smuzhiyun if (regno < 16)
1242*4882a593Smuzhiyun ((u32 *)info->pseudo_palette)[regno] =
1243*4882a593Smuzhiyun ((transp & 0xff00) << 16) |
1244*4882a593Smuzhiyun ((red & 0xff00) << 8) |
1245*4882a593Smuzhiyun ((green & 0xff00) ) |
1246*4882a593Smuzhiyun ((blue & 0xff00) >> 8);
1247*4882a593Smuzhiyun break;
1248*4882a593Smuzhiyun
1249*4882a593Smuzhiyun default:
1250*4882a593Smuzhiyun return 1;
1251*4882a593Smuzhiyun }
1252*4882a593Smuzhiyun
1253*4882a593Smuzhiyun return 0;
1254*4882a593Smuzhiyun }
1255*4882a593Smuzhiyun
savagefb_set_par_int(struct savagefb_par * par,struct savage_reg * reg)1256*4882a593Smuzhiyun static void savagefb_set_par_int(struct savagefb_par *par, struct savage_reg *reg)
1257*4882a593Smuzhiyun {
1258*4882a593Smuzhiyun unsigned char tmp, cr3a, cr66, cr67;
1259*4882a593Smuzhiyun
1260*4882a593Smuzhiyun DBG("savagefb_set_par_int");
1261*4882a593Smuzhiyun
1262*4882a593Smuzhiyun par->SavageWaitIdle(par);
1263*4882a593Smuzhiyun
1264*4882a593Smuzhiyun vga_out8(0x3c2, 0x23, par);
1265*4882a593Smuzhiyun
1266*4882a593Smuzhiyun vga_out16(0x3d4, 0x4838, par);
1267*4882a593Smuzhiyun vga_out16(0x3d4, 0xa539, par);
1268*4882a593Smuzhiyun vga_out16(0x3c4, 0x0608, par);
1269*4882a593Smuzhiyun
1270*4882a593Smuzhiyun vgaHWProtect(par, 1);
1271*4882a593Smuzhiyun
1272*4882a593Smuzhiyun /*
1273*4882a593Smuzhiyun * Some Savage/MX and /IX systems go nuts when trying to exit the
1274*4882a593Smuzhiyun * server after WindowMaker has displayed a gradient background. I
1275*4882a593Smuzhiyun * haven't been able to find what causes it, but a non-destructive
1276*4882a593Smuzhiyun * switch to mode 3 here seems to eliminate the issue.
1277*4882a593Smuzhiyun */
1278*4882a593Smuzhiyun
1279*4882a593Smuzhiyun VerticalRetraceWait(par);
1280*4882a593Smuzhiyun vga_out8(0x3d4, 0x67, par);
1281*4882a593Smuzhiyun cr67 = vga_in8(0x3d5, par);
1282*4882a593Smuzhiyun vga_out8(0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */
1283*4882a593Smuzhiyun
1284*4882a593Smuzhiyun vga_out8(0x3d4, 0x23, par);
1285*4882a593Smuzhiyun vga_out8(0x3d5, 0x00, par);
1286*4882a593Smuzhiyun vga_out8(0x3d4, 0x26, par);
1287*4882a593Smuzhiyun vga_out8(0x3d5, 0x00, par);
1288*4882a593Smuzhiyun
1289*4882a593Smuzhiyun /* restore extended regs */
1290*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
1291*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR66, par);
1292*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
1293*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR3A, par);
1294*4882a593Smuzhiyun vga_out8(0x3d4, 0x31, par);
1295*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR31, par);
1296*4882a593Smuzhiyun vga_out8(0x3d4, 0x32, par);
1297*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR32, par);
1298*4882a593Smuzhiyun vga_out8(0x3d4, 0x58, par);
1299*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR58, par);
1300*4882a593Smuzhiyun vga_out8(0x3d4, 0x53, par);
1301*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR53 & 0x7f, par);
1302*4882a593Smuzhiyun
1303*4882a593Smuzhiyun vga_out16(0x3c4, 0x0608, par);
1304*4882a593Smuzhiyun
1305*4882a593Smuzhiyun /* Restore DCLK registers. */
1306*4882a593Smuzhiyun
1307*4882a593Smuzhiyun vga_out8(0x3c4, 0x0e, par);
1308*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR0E, par);
1309*4882a593Smuzhiyun vga_out8(0x3c4, 0x0f, par);
1310*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR0F, par);
1311*4882a593Smuzhiyun vga_out8(0x3c4, 0x29, par);
1312*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR29, par);
1313*4882a593Smuzhiyun vga_out8(0x3c4, 0x15, par);
1314*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR15, par);
1315*4882a593Smuzhiyun
1316*4882a593Smuzhiyun /* Restore flat panel expansion registers. */
1317*4882a593Smuzhiyun if (par->chip == S3_SAVAGE_MX) {
1318*4882a593Smuzhiyun int i;
1319*4882a593Smuzhiyun
1320*4882a593Smuzhiyun for (i = 0; i < 8; i++) {
1321*4882a593Smuzhiyun vga_out8(0x3c4, 0x54+i, par);
1322*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR54[i], par);
1323*4882a593Smuzhiyun }
1324*4882a593Smuzhiyun }
1325*4882a593Smuzhiyun
1326*4882a593Smuzhiyun vgaHWRestore (par, reg);
1327*4882a593Smuzhiyun
1328*4882a593Smuzhiyun /* extended mode timing registers */
1329*4882a593Smuzhiyun vga_out8(0x3d4, 0x53, par);
1330*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR53, par);
1331*4882a593Smuzhiyun vga_out8(0x3d4, 0x5d, par);
1332*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR5D, par);
1333*4882a593Smuzhiyun vga_out8(0x3d4, 0x5e, par);
1334*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR5E, par);
1335*4882a593Smuzhiyun vga_out8(0x3d4, 0x3b, par);
1336*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR3B, par);
1337*4882a593Smuzhiyun vga_out8(0x3d4, 0x3c, par);
1338*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR3C, par);
1339*4882a593Smuzhiyun vga_out8(0x3d4, 0x43, par);
1340*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR43, par);
1341*4882a593Smuzhiyun vga_out8(0x3d4, 0x65, par);
1342*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR65, par);
1343*4882a593Smuzhiyun
1344*4882a593Smuzhiyun /* restore the desired video mode with cr67 */
1345*4882a593Smuzhiyun vga_out8(0x3d4, 0x67, par);
1346*4882a593Smuzhiyun /* following part not present in X11 driver */
1347*4882a593Smuzhiyun cr67 = vga_in8(0x3d5, par) & 0xf;
1348*4882a593Smuzhiyun vga_out8(0x3d5, 0x50 | cr67, par);
1349*4882a593Smuzhiyun mdelay(10);
1350*4882a593Smuzhiyun vga_out8(0x3d4, 0x67, par);
1351*4882a593Smuzhiyun /* end of part */
1352*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR67 & ~0x0c, par);
1353*4882a593Smuzhiyun
1354*4882a593Smuzhiyun /* other mode timing and extended regs */
1355*4882a593Smuzhiyun vga_out8(0x3d4, 0x34, par);
1356*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR34, par);
1357*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
1358*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR40, par);
1359*4882a593Smuzhiyun vga_out8(0x3d4, 0x42, par);
1360*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR42, par);
1361*4882a593Smuzhiyun vga_out8(0x3d4, 0x45, par);
1362*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR45, par);
1363*4882a593Smuzhiyun vga_out8(0x3d4, 0x50, par);
1364*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR50, par);
1365*4882a593Smuzhiyun vga_out8(0x3d4, 0x51, par);
1366*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR51, par);
1367*4882a593Smuzhiyun
1368*4882a593Smuzhiyun /* memory timings */
1369*4882a593Smuzhiyun vga_out8(0x3d4, 0x36, par);
1370*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR36, par);
1371*4882a593Smuzhiyun vga_out8(0x3d4, 0x60, par);
1372*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR60, par);
1373*4882a593Smuzhiyun vga_out8(0x3d4, 0x68, par);
1374*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR68, par);
1375*4882a593Smuzhiyun vga_out8(0x3d4, 0x69, par);
1376*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR69, par);
1377*4882a593Smuzhiyun vga_out8(0x3d4, 0x6f, par);
1378*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR6F, par);
1379*4882a593Smuzhiyun
1380*4882a593Smuzhiyun vga_out8(0x3d4, 0x33, par);
1381*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR33, par);
1382*4882a593Smuzhiyun vga_out8(0x3d4, 0x86, par);
1383*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR86, par);
1384*4882a593Smuzhiyun vga_out8(0x3d4, 0x88, par);
1385*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR88, par);
1386*4882a593Smuzhiyun vga_out8(0x3d4, 0x90, par);
1387*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR90, par);
1388*4882a593Smuzhiyun vga_out8(0x3d4, 0x91, par);
1389*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR91, par);
1390*4882a593Smuzhiyun
1391*4882a593Smuzhiyun if (par->chip == S3_SAVAGE4) {
1392*4882a593Smuzhiyun vga_out8(0x3d4, 0xb0, par);
1393*4882a593Smuzhiyun vga_out8(0x3d5, reg->CRB0, par);
1394*4882a593Smuzhiyun }
1395*4882a593Smuzhiyun
1396*4882a593Smuzhiyun vga_out8(0x3d4, 0x32, par);
1397*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR32, par);
1398*4882a593Smuzhiyun
1399*4882a593Smuzhiyun /* unlock extended seq regs */
1400*4882a593Smuzhiyun vga_out8(0x3c4, 0x08, par);
1401*4882a593Smuzhiyun vga_out8(0x3c5, 0x06, par);
1402*4882a593Smuzhiyun
1403*4882a593Smuzhiyun /* Restore extended sequencer regs for MCLK. SR10 == 255 indicates
1404*4882a593Smuzhiyun * that we should leave the default SR10 and SR11 values there.
1405*4882a593Smuzhiyun */
1406*4882a593Smuzhiyun if (reg->SR10 != 255) {
1407*4882a593Smuzhiyun vga_out8(0x3c4, 0x10, par);
1408*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR10, par);
1409*4882a593Smuzhiyun vga_out8(0x3c4, 0x11, par);
1410*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR11, par);
1411*4882a593Smuzhiyun }
1412*4882a593Smuzhiyun
1413*4882a593Smuzhiyun /* restore extended seq regs for dclk */
1414*4882a593Smuzhiyun vga_out8(0x3c4, 0x0e, par);
1415*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR0E, par);
1416*4882a593Smuzhiyun vga_out8(0x3c4, 0x0f, par);
1417*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR0F, par);
1418*4882a593Smuzhiyun vga_out8(0x3c4, 0x12, par);
1419*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR12, par);
1420*4882a593Smuzhiyun vga_out8(0x3c4, 0x13, par);
1421*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR13, par);
1422*4882a593Smuzhiyun vga_out8(0x3c4, 0x29, par);
1423*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR29, par);
1424*4882a593Smuzhiyun vga_out8(0x3c4, 0x18, par);
1425*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR18, par);
1426*4882a593Smuzhiyun
1427*4882a593Smuzhiyun /* load new m, n pll values for dclk & mclk */
1428*4882a593Smuzhiyun vga_out8(0x3c4, 0x15, par);
1429*4882a593Smuzhiyun tmp = vga_in8(0x3c5, par) & ~0x21;
1430*4882a593Smuzhiyun
1431*4882a593Smuzhiyun vga_out8(0x3c5, tmp | 0x03, par);
1432*4882a593Smuzhiyun vga_out8(0x3c5, tmp | 0x23, par);
1433*4882a593Smuzhiyun vga_out8(0x3c5, tmp | 0x03, par);
1434*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR15, par);
1435*4882a593Smuzhiyun udelay(100);
1436*4882a593Smuzhiyun
1437*4882a593Smuzhiyun vga_out8(0x3c4, 0x30, par);
1438*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR30, par);
1439*4882a593Smuzhiyun vga_out8(0x3c4, 0x08, par);
1440*4882a593Smuzhiyun vga_out8(0x3c5, reg->SR08, par);
1441*4882a593Smuzhiyun
1442*4882a593Smuzhiyun /* now write out cr67 in full, possibly starting STREAMS */
1443*4882a593Smuzhiyun VerticalRetraceWait(par);
1444*4882a593Smuzhiyun vga_out8(0x3d4, 0x67, par);
1445*4882a593Smuzhiyun vga_out8(0x3d5, reg->CR67, par);
1446*4882a593Smuzhiyun
1447*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
1448*4882a593Smuzhiyun cr66 = vga_in8(0x3d5, par);
1449*4882a593Smuzhiyun vga_out8(0x3d5, cr66 | 0x80, par);
1450*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
1451*4882a593Smuzhiyun cr3a = vga_in8(0x3d5, par);
1452*4882a593Smuzhiyun vga_out8(0x3d5, cr3a | 0x80, par);
1453*4882a593Smuzhiyun
1454*4882a593Smuzhiyun if (par->chip != S3_SAVAGE_MX) {
1455*4882a593Smuzhiyun VerticalRetraceWait(par);
1456*4882a593Smuzhiyun savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
1457*4882a593Smuzhiyun par->SavageWaitIdle(par);
1458*4882a593Smuzhiyun savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
1459*4882a593Smuzhiyun par->SavageWaitIdle(par);
1460*4882a593Smuzhiyun savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
1461*4882a593Smuzhiyun par->SavageWaitIdle(par);
1462*4882a593Smuzhiyun savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
1463*4882a593Smuzhiyun }
1464*4882a593Smuzhiyun
1465*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
1466*4882a593Smuzhiyun vga_out8(0x3d5, cr66, par);
1467*4882a593Smuzhiyun vga_out8(0x3d4, 0x3a, par);
1468*4882a593Smuzhiyun vga_out8(0x3d5, cr3a, par);
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun SavageSetup2DEngine(par);
1471*4882a593Smuzhiyun vgaHWProtect(par, 0);
1472*4882a593Smuzhiyun }
1473*4882a593Smuzhiyun
savagefb_update_start(struct savagefb_par * par,int base)1474*4882a593Smuzhiyun static void savagefb_update_start(struct savagefb_par *par, int base)
1475*4882a593Smuzhiyun {
1476*4882a593Smuzhiyun /* program the start address registers */
1477*4882a593Smuzhiyun vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par);
1478*4882a593Smuzhiyun vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par);
1479*4882a593Smuzhiyun vga_out8(0x3d4, 0x69, par);
1480*4882a593Smuzhiyun vga_out8(0x3d5, (base & 0x7f0000) >> 16, par);
1481*4882a593Smuzhiyun }
1482*4882a593Smuzhiyun
1483*4882a593Smuzhiyun
savagefb_set_fix(struct fb_info * info)1484*4882a593Smuzhiyun static void savagefb_set_fix(struct fb_info *info)
1485*4882a593Smuzhiyun {
1486*4882a593Smuzhiyun info->fix.line_length = info->var.xres_virtual *
1487*4882a593Smuzhiyun info->var.bits_per_pixel / 8;
1488*4882a593Smuzhiyun
1489*4882a593Smuzhiyun if (info->var.bits_per_pixel == 8) {
1490*4882a593Smuzhiyun info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
1491*4882a593Smuzhiyun info->fix.xpanstep = 4;
1492*4882a593Smuzhiyun } else {
1493*4882a593Smuzhiyun info->fix.visual = FB_VISUAL_TRUECOLOR;
1494*4882a593Smuzhiyun info->fix.xpanstep = 2;
1495*4882a593Smuzhiyun }
1496*4882a593Smuzhiyun
1497*4882a593Smuzhiyun }
1498*4882a593Smuzhiyun
savagefb_set_par(struct fb_info * info)1499*4882a593Smuzhiyun static int savagefb_set_par(struct fb_info *info)
1500*4882a593Smuzhiyun {
1501*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1502*4882a593Smuzhiyun struct fb_var_screeninfo *var = &info->var;
1503*4882a593Smuzhiyun int err;
1504*4882a593Smuzhiyun
1505*4882a593Smuzhiyun DBG("savagefb_set_par");
1506*4882a593Smuzhiyun err = savagefb_decode_var(var, par, &par->state);
1507*4882a593Smuzhiyun if (err)
1508*4882a593Smuzhiyun return err;
1509*4882a593Smuzhiyun
1510*4882a593Smuzhiyun if (par->dacSpeedBpp <= 0) {
1511*4882a593Smuzhiyun if (var->bits_per_pixel > 24)
1512*4882a593Smuzhiyun par->dacSpeedBpp = par->clock[3];
1513*4882a593Smuzhiyun else if (var->bits_per_pixel >= 24)
1514*4882a593Smuzhiyun par->dacSpeedBpp = par->clock[2];
1515*4882a593Smuzhiyun else if ((var->bits_per_pixel > 8) && (var->bits_per_pixel < 24))
1516*4882a593Smuzhiyun par->dacSpeedBpp = par->clock[1];
1517*4882a593Smuzhiyun else if (var->bits_per_pixel <= 8)
1518*4882a593Smuzhiyun par->dacSpeedBpp = par->clock[0];
1519*4882a593Smuzhiyun }
1520*4882a593Smuzhiyun
1521*4882a593Smuzhiyun /* Set ramdac limits */
1522*4882a593Smuzhiyun par->maxClock = par->dacSpeedBpp;
1523*4882a593Smuzhiyun par->minClock = 10000;
1524*4882a593Smuzhiyun
1525*4882a593Smuzhiyun savagefb_set_par_int(par, &par->state);
1526*4882a593Smuzhiyun fb_set_cmap(&info->cmap, info);
1527*4882a593Smuzhiyun savagefb_set_fix(info);
1528*4882a593Smuzhiyun savagefb_set_clip(info);
1529*4882a593Smuzhiyun
1530*4882a593Smuzhiyun SavagePrintRegs(par);
1531*4882a593Smuzhiyun return 0;
1532*4882a593Smuzhiyun }
1533*4882a593Smuzhiyun
1534*4882a593Smuzhiyun /*
1535*4882a593Smuzhiyun * Pan or Wrap the Display
1536*4882a593Smuzhiyun */
savagefb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)1537*4882a593Smuzhiyun static int savagefb_pan_display(struct fb_var_screeninfo *var,
1538*4882a593Smuzhiyun struct fb_info *info)
1539*4882a593Smuzhiyun {
1540*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1541*4882a593Smuzhiyun int base;
1542*4882a593Smuzhiyun
1543*4882a593Smuzhiyun base = (var->yoffset * info->fix.line_length
1544*4882a593Smuzhiyun + (var->xoffset & ~1) * ((info->var.bits_per_pixel+7) / 8)) >> 2;
1545*4882a593Smuzhiyun
1546*4882a593Smuzhiyun savagefb_update_start(par, base);
1547*4882a593Smuzhiyun return 0;
1548*4882a593Smuzhiyun }
1549*4882a593Smuzhiyun
savagefb_blank(int blank,struct fb_info * info)1550*4882a593Smuzhiyun static int savagefb_blank(int blank, struct fb_info *info)
1551*4882a593Smuzhiyun {
1552*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1553*4882a593Smuzhiyun u8 sr8 = 0, srd = 0;
1554*4882a593Smuzhiyun
1555*4882a593Smuzhiyun if (par->display_type == DISP_CRT) {
1556*4882a593Smuzhiyun vga_out8(0x3c4, 0x08, par);
1557*4882a593Smuzhiyun sr8 = vga_in8(0x3c5, par);
1558*4882a593Smuzhiyun sr8 |= 0x06;
1559*4882a593Smuzhiyun vga_out8(0x3c5, sr8, par);
1560*4882a593Smuzhiyun vga_out8(0x3c4, 0x0d, par);
1561*4882a593Smuzhiyun srd = vga_in8(0x3c5, par);
1562*4882a593Smuzhiyun srd &= 0x50;
1563*4882a593Smuzhiyun
1564*4882a593Smuzhiyun switch (blank) {
1565*4882a593Smuzhiyun case FB_BLANK_UNBLANK:
1566*4882a593Smuzhiyun case FB_BLANK_NORMAL:
1567*4882a593Smuzhiyun break;
1568*4882a593Smuzhiyun case FB_BLANK_VSYNC_SUSPEND:
1569*4882a593Smuzhiyun srd |= 0x10;
1570*4882a593Smuzhiyun break;
1571*4882a593Smuzhiyun case FB_BLANK_HSYNC_SUSPEND:
1572*4882a593Smuzhiyun srd |= 0x40;
1573*4882a593Smuzhiyun break;
1574*4882a593Smuzhiyun case FB_BLANK_POWERDOWN:
1575*4882a593Smuzhiyun srd |= 0x50;
1576*4882a593Smuzhiyun break;
1577*4882a593Smuzhiyun }
1578*4882a593Smuzhiyun
1579*4882a593Smuzhiyun vga_out8(0x3c4, 0x0d, par);
1580*4882a593Smuzhiyun vga_out8(0x3c5, srd, par);
1581*4882a593Smuzhiyun }
1582*4882a593Smuzhiyun
1583*4882a593Smuzhiyun if (par->display_type == DISP_LCD ||
1584*4882a593Smuzhiyun par->display_type == DISP_DFP) {
1585*4882a593Smuzhiyun switch(blank) {
1586*4882a593Smuzhiyun case FB_BLANK_UNBLANK:
1587*4882a593Smuzhiyun case FB_BLANK_NORMAL:
1588*4882a593Smuzhiyun vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
1589*4882a593Smuzhiyun vga_out8(0x3c5, vga_in8(0x3c5, par) | 0x10, par);
1590*4882a593Smuzhiyun break;
1591*4882a593Smuzhiyun case FB_BLANK_VSYNC_SUSPEND:
1592*4882a593Smuzhiyun case FB_BLANK_HSYNC_SUSPEND:
1593*4882a593Smuzhiyun case FB_BLANK_POWERDOWN:
1594*4882a593Smuzhiyun vga_out8(0x3c4, 0x31, par); /* SR31 bit 4 - FP enable */
1595*4882a593Smuzhiyun vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x10, par);
1596*4882a593Smuzhiyun break;
1597*4882a593Smuzhiyun }
1598*4882a593Smuzhiyun }
1599*4882a593Smuzhiyun
1600*4882a593Smuzhiyun return (blank == FB_BLANK_NORMAL) ? 1 : 0;
1601*4882a593Smuzhiyun }
1602*4882a593Smuzhiyun
savagefb_open(struct fb_info * info,int user)1603*4882a593Smuzhiyun static int savagefb_open(struct fb_info *info, int user)
1604*4882a593Smuzhiyun {
1605*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1606*4882a593Smuzhiyun
1607*4882a593Smuzhiyun mutex_lock(&par->open_lock);
1608*4882a593Smuzhiyun
1609*4882a593Smuzhiyun if (!par->open_count) {
1610*4882a593Smuzhiyun memset(&par->vgastate, 0, sizeof(par->vgastate));
1611*4882a593Smuzhiyun par->vgastate.flags = VGA_SAVE_CMAP | VGA_SAVE_FONTS |
1612*4882a593Smuzhiyun VGA_SAVE_MODE;
1613*4882a593Smuzhiyun par->vgastate.vgabase = par->mmio.vbase + 0x8000;
1614*4882a593Smuzhiyun save_vga(&par->vgastate);
1615*4882a593Smuzhiyun savage_get_default_par(par, &par->initial);
1616*4882a593Smuzhiyun }
1617*4882a593Smuzhiyun
1618*4882a593Smuzhiyun par->open_count++;
1619*4882a593Smuzhiyun mutex_unlock(&par->open_lock);
1620*4882a593Smuzhiyun return 0;
1621*4882a593Smuzhiyun }
1622*4882a593Smuzhiyun
savagefb_release(struct fb_info * info,int user)1623*4882a593Smuzhiyun static int savagefb_release(struct fb_info *info, int user)
1624*4882a593Smuzhiyun {
1625*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1626*4882a593Smuzhiyun
1627*4882a593Smuzhiyun mutex_lock(&par->open_lock);
1628*4882a593Smuzhiyun
1629*4882a593Smuzhiyun if (par->open_count == 1) {
1630*4882a593Smuzhiyun savage_set_default_par(par, &par->initial);
1631*4882a593Smuzhiyun restore_vga(&par->vgastate);
1632*4882a593Smuzhiyun }
1633*4882a593Smuzhiyun
1634*4882a593Smuzhiyun par->open_count--;
1635*4882a593Smuzhiyun mutex_unlock(&par->open_lock);
1636*4882a593Smuzhiyun return 0;
1637*4882a593Smuzhiyun }
1638*4882a593Smuzhiyun
1639*4882a593Smuzhiyun static const struct fb_ops savagefb_ops = {
1640*4882a593Smuzhiyun .owner = THIS_MODULE,
1641*4882a593Smuzhiyun .fb_open = savagefb_open,
1642*4882a593Smuzhiyun .fb_release = savagefb_release,
1643*4882a593Smuzhiyun .fb_check_var = savagefb_check_var,
1644*4882a593Smuzhiyun .fb_set_par = savagefb_set_par,
1645*4882a593Smuzhiyun .fb_setcolreg = savagefb_setcolreg,
1646*4882a593Smuzhiyun .fb_pan_display = savagefb_pan_display,
1647*4882a593Smuzhiyun .fb_blank = savagefb_blank,
1648*4882a593Smuzhiyun #if defined(CONFIG_FB_SAVAGE_ACCEL)
1649*4882a593Smuzhiyun .fb_fillrect = savagefb_fillrect,
1650*4882a593Smuzhiyun .fb_copyarea = savagefb_copyarea,
1651*4882a593Smuzhiyun .fb_imageblit = savagefb_imageblit,
1652*4882a593Smuzhiyun .fb_sync = savagefb_sync,
1653*4882a593Smuzhiyun #else
1654*4882a593Smuzhiyun .fb_fillrect = cfb_fillrect,
1655*4882a593Smuzhiyun .fb_copyarea = cfb_copyarea,
1656*4882a593Smuzhiyun .fb_imageblit = cfb_imageblit,
1657*4882a593Smuzhiyun #endif
1658*4882a593Smuzhiyun };
1659*4882a593Smuzhiyun
1660*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
1661*4882a593Smuzhiyun
1662*4882a593Smuzhiyun static const struct fb_var_screeninfo savagefb_var800x600x8 = {
1663*4882a593Smuzhiyun .accel_flags = FB_ACCELF_TEXT,
1664*4882a593Smuzhiyun .xres = 800,
1665*4882a593Smuzhiyun .yres = 600,
1666*4882a593Smuzhiyun .xres_virtual = 800,
1667*4882a593Smuzhiyun .yres_virtual = 600,
1668*4882a593Smuzhiyun .bits_per_pixel = 8,
1669*4882a593Smuzhiyun .pixclock = 25000,
1670*4882a593Smuzhiyun .left_margin = 88,
1671*4882a593Smuzhiyun .right_margin = 40,
1672*4882a593Smuzhiyun .upper_margin = 23,
1673*4882a593Smuzhiyun .lower_margin = 1,
1674*4882a593Smuzhiyun .hsync_len = 128,
1675*4882a593Smuzhiyun .vsync_len = 4,
1676*4882a593Smuzhiyun .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
1677*4882a593Smuzhiyun .vmode = FB_VMODE_NONINTERLACED
1678*4882a593Smuzhiyun };
1679*4882a593Smuzhiyun
savage_enable_mmio(struct savagefb_par * par)1680*4882a593Smuzhiyun static void savage_enable_mmio(struct savagefb_par *par)
1681*4882a593Smuzhiyun {
1682*4882a593Smuzhiyun unsigned char val;
1683*4882a593Smuzhiyun
1684*4882a593Smuzhiyun DBG("savage_enable_mmio\n");
1685*4882a593Smuzhiyun
1686*4882a593Smuzhiyun val = vga_in8(0x3c3, par);
1687*4882a593Smuzhiyun vga_out8(0x3c3, val | 0x01, par);
1688*4882a593Smuzhiyun val = vga_in8(0x3cc, par);
1689*4882a593Smuzhiyun vga_out8(0x3c2, val | 0x01, par);
1690*4882a593Smuzhiyun
1691*4882a593Smuzhiyun if (par->chip >= S3_SAVAGE4) {
1692*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
1693*4882a593Smuzhiyun val = vga_in8(0x3d5, par);
1694*4882a593Smuzhiyun vga_out8(0x3d5, val | 1, par);
1695*4882a593Smuzhiyun }
1696*4882a593Smuzhiyun }
1697*4882a593Smuzhiyun
1698*4882a593Smuzhiyun
savage_disable_mmio(struct savagefb_par * par)1699*4882a593Smuzhiyun static void savage_disable_mmio(struct savagefb_par *par)
1700*4882a593Smuzhiyun {
1701*4882a593Smuzhiyun unsigned char val;
1702*4882a593Smuzhiyun
1703*4882a593Smuzhiyun DBG("savage_disable_mmio\n");
1704*4882a593Smuzhiyun
1705*4882a593Smuzhiyun if (par->chip >= S3_SAVAGE4) {
1706*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
1707*4882a593Smuzhiyun val = vga_in8(0x3d5, par);
1708*4882a593Smuzhiyun vga_out8(0x3d5, val | 1, par);
1709*4882a593Smuzhiyun }
1710*4882a593Smuzhiyun }
1711*4882a593Smuzhiyun
1712*4882a593Smuzhiyun
savage_map_mmio(struct fb_info * info)1713*4882a593Smuzhiyun static int savage_map_mmio(struct fb_info *info)
1714*4882a593Smuzhiyun {
1715*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1716*4882a593Smuzhiyun DBG("savage_map_mmio");
1717*4882a593Smuzhiyun
1718*4882a593Smuzhiyun if (S3_SAVAGE3D_SERIES(par->chip))
1719*4882a593Smuzhiyun par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
1720*4882a593Smuzhiyun SAVAGE_NEWMMIO_REGBASE_S3;
1721*4882a593Smuzhiyun else
1722*4882a593Smuzhiyun par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
1723*4882a593Smuzhiyun SAVAGE_NEWMMIO_REGBASE_S4;
1724*4882a593Smuzhiyun
1725*4882a593Smuzhiyun par->mmio.len = SAVAGE_NEWMMIO_REGSIZE;
1726*4882a593Smuzhiyun
1727*4882a593Smuzhiyun par->mmio.vbase = ioremap(par->mmio.pbase, par->mmio.len);
1728*4882a593Smuzhiyun if (!par->mmio.vbase) {
1729*4882a593Smuzhiyun printk("savagefb: unable to map memory mapped IO\n");
1730*4882a593Smuzhiyun return -ENOMEM;
1731*4882a593Smuzhiyun } else
1732*4882a593Smuzhiyun printk(KERN_INFO "savagefb: mapped io at %p\n",
1733*4882a593Smuzhiyun par->mmio.vbase);
1734*4882a593Smuzhiyun
1735*4882a593Smuzhiyun info->fix.mmio_start = par->mmio.pbase;
1736*4882a593Smuzhiyun info->fix.mmio_len = par->mmio.len;
1737*4882a593Smuzhiyun
1738*4882a593Smuzhiyun par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET);
1739*4882a593Smuzhiyun par->bci_ptr = 0;
1740*4882a593Smuzhiyun
1741*4882a593Smuzhiyun savage_enable_mmio(par);
1742*4882a593Smuzhiyun
1743*4882a593Smuzhiyun return 0;
1744*4882a593Smuzhiyun }
1745*4882a593Smuzhiyun
savage_unmap_mmio(struct fb_info * info)1746*4882a593Smuzhiyun static void savage_unmap_mmio(struct fb_info *info)
1747*4882a593Smuzhiyun {
1748*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1749*4882a593Smuzhiyun DBG("savage_unmap_mmio");
1750*4882a593Smuzhiyun
1751*4882a593Smuzhiyun savage_disable_mmio(par);
1752*4882a593Smuzhiyun
1753*4882a593Smuzhiyun if (par->mmio.vbase) {
1754*4882a593Smuzhiyun iounmap(par->mmio.vbase);
1755*4882a593Smuzhiyun par->mmio.vbase = NULL;
1756*4882a593Smuzhiyun }
1757*4882a593Smuzhiyun }
1758*4882a593Smuzhiyun
savage_map_video(struct fb_info * info,int video_len)1759*4882a593Smuzhiyun static int savage_map_video(struct fb_info *info, int video_len)
1760*4882a593Smuzhiyun {
1761*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1762*4882a593Smuzhiyun int resource;
1763*4882a593Smuzhiyun
1764*4882a593Smuzhiyun DBG("savage_map_video");
1765*4882a593Smuzhiyun
1766*4882a593Smuzhiyun if (S3_SAVAGE3D_SERIES(par->chip))
1767*4882a593Smuzhiyun resource = 0;
1768*4882a593Smuzhiyun else
1769*4882a593Smuzhiyun resource = 1;
1770*4882a593Smuzhiyun
1771*4882a593Smuzhiyun par->video.pbase = pci_resource_start(par->pcidev, resource);
1772*4882a593Smuzhiyun par->video.len = video_len;
1773*4882a593Smuzhiyun par->video.vbase = ioremap_wc(par->video.pbase, par->video.len);
1774*4882a593Smuzhiyun
1775*4882a593Smuzhiyun if (!par->video.vbase) {
1776*4882a593Smuzhiyun printk("savagefb: unable to map screen memory\n");
1777*4882a593Smuzhiyun return -ENOMEM;
1778*4882a593Smuzhiyun } else
1779*4882a593Smuzhiyun printk(KERN_INFO "savagefb: mapped framebuffer at %p, "
1780*4882a593Smuzhiyun "pbase == %x\n", par->video.vbase, par->video.pbase);
1781*4882a593Smuzhiyun
1782*4882a593Smuzhiyun info->fix.smem_start = par->video.pbase;
1783*4882a593Smuzhiyun info->fix.smem_len = par->video.len - par->cob_size;
1784*4882a593Smuzhiyun info->screen_base = par->video.vbase;
1785*4882a593Smuzhiyun par->video.wc_cookie = arch_phys_wc_add(par->video.pbase, video_len);
1786*4882a593Smuzhiyun
1787*4882a593Smuzhiyun /* Clear framebuffer, it's all white in memory after boot */
1788*4882a593Smuzhiyun memset_io(par->video.vbase, 0, par->video.len);
1789*4882a593Smuzhiyun
1790*4882a593Smuzhiyun return 0;
1791*4882a593Smuzhiyun }
1792*4882a593Smuzhiyun
savage_unmap_video(struct fb_info * info)1793*4882a593Smuzhiyun static void savage_unmap_video(struct fb_info *info)
1794*4882a593Smuzhiyun {
1795*4882a593Smuzhiyun struct savagefb_par *par = info->par;
1796*4882a593Smuzhiyun
1797*4882a593Smuzhiyun DBG("savage_unmap_video");
1798*4882a593Smuzhiyun
1799*4882a593Smuzhiyun if (par->video.vbase) {
1800*4882a593Smuzhiyun arch_phys_wc_del(par->video.wc_cookie);
1801*4882a593Smuzhiyun iounmap(par->video.vbase);
1802*4882a593Smuzhiyun par->video.vbase = NULL;
1803*4882a593Smuzhiyun info->screen_base = NULL;
1804*4882a593Smuzhiyun }
1805*4882a593Smuzhiyun }
1806*4882a593Smuzhiyun
savage_init_hw(struct savagefb_par * par)1807*4882a593Smuzhiyun static int savage_init_hw(struct savagefb_par *par)
1808*4882a593Smuzhiyun {
1809*4882a593Smuzhiyun unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp;
1810*4882a593Smuzhiyun
1811*4882a593Smuzhiyun static unsigned char RamSavage3D[] = { 8, 4, 4, 2 };
1812*4882a593Smuzhiyun static unsigned char RamSavage4[] = { 2, 4, 8, 12, 16, 32, 64, 32 };
1813*4882a593Smuzhiyun static unsigned char RamSavageMX[] = { 2, 8, 4, 16, 8, 16, 4, 16 };
1814*4882a593Smuzhiyun static unsigned char RamSavageNB[] = { 0, 2, 4, 8, 16, 32, 2, 2 };
1815*4882a593Smuzhiyun int videoRam, videoRambytes, dvi;
1816*4882a593Smuzhiyun
1817*4882a593Smuzhiyun DBG("savage_init_hw");
1818*4882a593Smuzhiyun
1819*4882a593Smuzhiyun /* unprotect CRTC[0-7] */
1820*4882a593Smuzhiyun vga_out8(0x3d4, 0x11, par);
1821*4882a593Smuzhiyun tmp = vga_in8(0x3d5, par);
1822*4882a593Smuzhiyun vga_out8(0x3d5, tmp & 0x7f, par);
1823*4882a593Smuzhiyun
1824*4882a593Smuzhiyun /* unlock extended regs */
1825*4882a593Smuzhiyun vga_out16(0x3d4, 0x4838, par);
1826*4882a593Smuzhiyun vga_out16(0x3d4, 0xa039, par);
1827*4882a593Smuzhiyun vga_out16(0x3c4, 0x0608, par);
1828*4882a593Smuzhiyun
1829*4882a593Smuzhiyun vga_out8(0x3d4, 0x40, par);
1830*4882a593Smuzhiyun tmp = vga_in8(0x3d5, par);
1831*4882a593Smuzhiyun vga_out8(0x3d5, tmp & ~0x01, par);
1832*4882a593Smuzhiyun
1833*4882a593Smuzhiyun /* unlock sys regs */
1834*4882a593Smuzhiyun vga_out8(0x3d4, 0x38, par);
1835*4882a593Smuzhiyun vga_out8(0x3d5, 0x48, par);
1836*4882a593Smuzhiyun
1837*4882a593Smuzhiyun /* Unlock system registers. */
1838*4882a593Smuzhiyun vga_out16(0x3d4, 0x4838, par);
1839*4882a593Smuzhiyun
1840*4882a593Smuzhiyun /* Next go on to detect amount of installed ram */
1841*4882a593Smuzhiyun
1842*4882a593Smuzhiyun vga_out8(0x3d4, 0x36, par); /* for register CR36 (CONFG_REG1), */
1843*4882a593Smuzhiyun config1 = vga_in8(0x3d5, par); /* get amount of vram installed */
1844*4882a593Smuzhiyun
1845*4882a593Smuzhiyun /* Compute the amount of video memory and offscreen memory. */
1846*4882a593Smuzhiyun
1847*4882a593Smuzhiyun switch (par->chip) {
1848*4882a593Smuzhiyun case S3_SAVAGE3D:
1849*4882a593Smuzhiyun videoRam = RamSavage3D[(config1 & 0xC0) >> 6 ] * 1024;
1850*4882a593Smuzhiyun break;
1851*4882a593Smuzhiyun
1852*4882a593Smuzhiyun case S3_SAVAGE4:
1853*4882a593Smuzhiyun /*
1854*4882a593Smuzhiyun * The Savage4 has one ugly special case to consider. On
1855*4882a593Smuzhiyun * systems with 4 banks of 2Mx32 SDRAM, the BIOS says 4MB
1856*4882a593Smuzhiyun * when it really means 8MB. Why do it the same when you
1857*4882a593Smuzhiyun * can do it different...
1858*4882a593Smuzhiyun */
1859*4882a593Smuzhiyun vga_out8(0x3d4, 0x68, par); /* memory control 1 */
1860*4882a593Smuzhiyun if ((vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6))
1861*4882a593Smuzhiyun RamSavage4[1] = 8;
1862*4882a593Smuzhiyun fallthrough;
1863*4882a593Smuzhiyun
1864*4882a593Smuzhiyun case S3_SAVAGE2000:
1865*4882a593Smuzhiyun videoRam = RamSavage4[(config1 & 0xE0) >> 5] * 1024;
1866*4882a593Smuzhiyun break;
1867*4882a593Smuzhiyun
1868*4882a593Smuzhiyun case S3_SAVAGE_MX:
1869*4882a593Smuzhiyun case S3_SUPERSAVAGE:
1870*4882a593Smuzhiyun videoRam = RamSavageMX[(config1 & 0x0E) >> 1] * 1024;
1871*4882a593Smuzhiyun break;
1872*4882a593Smuzhiyun
1873*4882a593Smuzhiyun case S3_PROSAVAGE:
1874*4882a593Smuzhiyun case S3_PROSAVAGEDDR:
1875*4882a593Smuzhiyun case S3_TWISTER:
1876*4882a593Smuzhiyun videoRam = RamSavageNB[(config1 & 0xE0) >> 5] * 1024;
1877*4882a593Smuzhiyun break;
1878*4882a593Smuzhiyun
1879*4882a593Smuzhiyun default:
1880*4882a593Smuzhiyun /* How did we get here? */
1881*4882a593Smuzhiyun videoRam = 0;
1882*4882a593Smuzhiyun break;
1883*4882a593Smuzhiyun }
1884*4882a593Smuzhiyun
1885*4882a593Smuzhiyun videoRambytes = videoRam * 1024;
1886*4882a593Smuzhiyun
1887*4882a593Smuzhiyun printk(KERN_INFO "savagefb: probed videoram: %dk\n", videoRam);
1888*4882a593Smuzhiyun
1889*4882a593Smuzhiyun /* reset graphics engine to avoid memory corruption */
1890*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
1891*4882a593Smuzhiyun cr66 = vga_in8(0x3d5, par);
1892*4882a593Smuzhiyun vga_out8(0x3d5, cr66 | 0x02, par);
1893*4882a593Smuzhiyun usleep_range(10000, 11000);
1894*4882a593Smuzhiyun
1895*4882a593Smuzhiyun vga_out8(0x3d4, 0x66, par);
1896*4882a593Smuzhiyun vga_out8(0x3d5, cr66 & ~0x02, par); /* clear reset flag */
1897*4882a593Smuzhiyun usleep_range(10000, 11000);
1898*4882a593Smuzhiyun
1899*4882a593Smuzhiyun
1900*4882a593Smuzhiyun /*
1901*4882a593Smuzhiyun * reset memory interface, 3D engine, AGP master, PCI master,
1902*4882a593Smuzhiyun * master engine unit, motion compensation/LPB
1903*4882a593Smuzhiyun */
1904*4882a593Smuzhiyun vga_out8(0x3d4, 0x3f, par);
1905*4882a593Smuzhiyun cr3f = vga_in8(0x3d5, par);
1906*4882a593Smuzhiyun vga_out8(0x3d5, cr3f | 0x08, par);
1907*4882a593Smuzhiyun usleep_range(10000, 11000);
1908*4882a593Smuzhiyun
1909*4882a593Smuzhiyun vga_out8(0x3d4, 0x3f, par);
1910*4882a593Smuzhiyun vga_out8(0x3d5, cr3f & ~0x08, par); /* clear reset flags */
1911*4882a593Smuzhiyun usleep_range(10000, 11000);
1912*4882a593Smuzhiyun
1913*4882a593Smuzhiyun /* Savage ramdac speeds */
1914*4882a593Smuzhiyun par->numClocks = 4;
1915*4882a593Smuzhiyun par->clock[0] = 250000;
1916*4882a593Smuzhiyun par->clock[1] = 250000;
1917*4882a593Smuzhiyun par->clock[2] = 220000;
1918*4882a593Smuzhiyun par->clock[3] = 220000;
1919*4882a593Smuzhiyun
1920*4882a593Smuzhiyun /* detect current mclk */
1921*4882a593Smuzhiyun vga_out8(0x3c4, 0x08, par);
1922*4882a593Smuzhiyun sr8 = vga_in8(0x3c5, par);
1923*4882a593Smuzhiyun vga_out8(0x3c5, 0x06, par);
1924*4882a593Smuzhiyun vga_out8(0x3c4, 0x10, par);
1925*4882a593Smuzhiyun n = vga_in8(0x3c5, par);
1926*4882a593Smuzhiyun vga_out8(0x3c4, 0x11, par);
1927*4882a593Smuzhiyun m = vga_in8(0x3c5, par);
1928*4882a593Smuzhiyun vga_out8(0x3c4, 0x08, par);
1929*4882a593Smuzhiyun vga_out8(0x3c5, sr8, par);
1930*4882a593Smuzhiyun m &= 0x7f;
1931*4882a593Smuzhiyun n1 = n & 0x1f;
1932*4882a593Smuzhiyun n2 = (n >> 5) & 0x03;
1933*4882a593Smuzhiyun par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
1934*4882a593Smuzhiyun printk(KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
1935*4882a593Smuzhiyun par->MCLK);
1936*4882a593Smuzhiyun
1937*4882a593Smuzhiyun /* check for DVI/flat panel */
1938*4882a593Smuzhiyun dvi = 0;
1939*4882a593Smuzhiyun
1940*4882a593Smuzhiyun if (par->chip == S3_SAVAGE4) {
1941*4882a593Smuzhiyun unsigned char sr30 = 0x00;
1942*4882a593Smuzhiyun
1943*4882a593Smuzhiyun vga_out8(0x3c4, 0x30, par);
1944*4882a593Smuzhiyun /* clear bit 1 */
1945*4882a593Smuzhiyun vga_out8(0x3c5, vga_in8(0x3c5, par) & ~0x02, par);
1946*4882a593Smuzhiyun sr30 = vga_in8(0x3c5, par);
1947*4882a593Smuzhiyun if (sr30 & 0x02 /*0x04 */) {
1948*4882a593Smuzhiyun dvi = 1;
1949*4882a593Smuzhiyun printk("savagefb: Digital Flat Panel Detected\n");
1950*4882a593Smuzhiyun }
1951*4882a593Smuzhiyun }
1952*4882a593Smuzhiyun
1953*4882a593Smuzhiyun if ((S3_SAVAGE_MOBILE_SERIES(par->chip) ||
1954*4882a593Smuzhiyun S3_MOBILE_TWISTER_SERIES(par->chip)) && !par->crtonly)
1955*4882a593Smuzhiyun par->display_type = DISP_LCD;
1956*4882a593Smuzhiyun else if (dvi || (par->chip == S3_SAVAGE4 && par->dvi))
1957*4882a593Smuzhiyun par->display_type = DISP_DFP;
1958*4882a593Smuzhiyun else
1959*4882a593Smuzhiyun par->display_type = DISP_CRT;
1960*4882a593Smuzhiyun
1961*4882a593Smuzhiyun /* Check LCD panel parrmation */
1962*4882a593Smuzhiyun
1963*4882a593Smuzhiyun if (par->display_type == DISP_LCD) {
1964*4882a593Smuzhiyun unsigned char cr6b = VGArCR(0x6b, par);
1965*4882a593Smuzhiyun
1966*4882a593Smuzhiyun int panelX = (VGArSEQ(0x61, par) +
1967*4882a593Smuzhiyun ((VGArSEQ(0x66, par) & 0x02) << 7) + 1) * 8;
1968*4882a593Smuzhiyun int panelY = (VGArSEQ(0x69, par) +
1969*4882a593Smuzhiyun ((VGArSEQ(0x6e, par) & 0x70) << 4) + 1);
1970*4882a593Smuzhiyun
1971*4882a593Smuzhiyun char * sTechnology = "Unknown";
1972*4882a593Smuzhiyun
1973*4882a593Smuzhiyun /* OK, I admit it. I don't know how to limit the max dot clock
1974*4882a593Smuzhiyun * for LCD panels of various sizes. I thought I copied the
1975*4882a593Smuzhiyun * formula from the BIOS, but many users have parrmed me of
1976*4882a593Smuzhiyun * my folly.
1977*4882a593Smuzhiyun *
1978*4882a593Smuzhiyun * Instead, I'll abandon any attempt to automatically limit the
1979*4882a593Smuzhiyun * clock, and add an LCDClock option to XF86Config. Some day,
1980*4882a593Smuzhiyun * I should come back to this.
1981*4882a593Smuzhiyun */
1982*4882a593Smuzhiyun
1983*4882a593Smuzhiyun enum ACTIVE_DISPLAYS { /* These are the bits in CR6B */
1984*4882a593Smuzhiyun ActiveCRT = 0x01,
1985*4882a593Smuzhiyun ActiveLCD = 0x02,
1986*4882a593Smuzhiyun ActiveTV = 0x04,
1987*4882a593Smuzhiyun ActiveCRT2 = 0x20,
1988*4882a593Smuzhiyun ActiveDUO = 0x80
1989*4882a593Smuzhiyun };
1990*4882a593Smuzhiyun
1991*4882a593Smuzhiyun if ((VGArSEQ(0x39, par) & 0x03) == 0) {
1992*4882a593Smuzhiyun sTechnology = "TFT";
1993*4882a593Smuzhiyun } else if ((VGArSEQ(0x30, par) & 0x01) == 0) {
1994*4882a593Smuzhiyun sTechnology = "DSTN";
1995*4882a593Smuzhiyun } else {
1996*4882a593Smuzhiyun sTechnology = "STN";
1997*4882a593Smuzhiyun }
1998*4882a593Smuzhiyun
1999*4882a593Smuzhiyun printk(KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n",
2000*4882a593Smuzhiyun panelX, panelY, sTechnology,
2001*4882a593Smuzhiyun cr6b & ActiveLCD ? "and active" : "but not active");
2002*4882a593Smuzhiyun
2003*4882a593Smuzhiyun if (cr6b & ActiveLCD) {
2004*4882a593Smuzhiyun /*
2005*4882a593Smuzhiyun * If the LCD is active and panel expansion is enabled,
2006*4882a593Smuzhiyun * we probably want to kill the HW cursor.
2007*4882a593Smuzhiyun */
2008*4882a593Smuzhiyun
2009*4882a593Smuzhiyun printk(KERN_INFO "savagefb: Limiting video mode to "
2010*4882a593Smuzhiyun "%dx%d\n", panelX, panelY);
2011*4882a593Smuzhiyun
2012*4882a593Smuzhiyun par->SavagePanelWidth = panelX;
2013*4882a593Smuzhiyun par->SavagePanelHeight = panelY;
2014*4882a593Smuzhiyun
2015*4882a593Smuzhiyun } else
2016*4882a593Smuzhiyun par->display_type = DISP_CRT;
2017*4882a593Smuzhiyun }
2018*4882a593Smuzhiyun
2019*4882a593Smuzhiyun savage_get_default_par(par, &par->state);
2020*4882a593Smuzhiyun par->save = par->state;
2021*4882a593Smuzhiyun
2022*4882a593Smuzhiyun if (S3_SAVAGE4_SERIES(par->chip)) {
2023*4882a593Smuzhiyun /*
2024*4882a593Smuzhiyun * The Savage4 and ProSavage have COB coherency bugs which
2025*4882a593Smuzhiyun * render the buffer useless. We disable it.
2026*4882a593Smuzhiyun */
2027*4882a593Smuzhiyun par->cob_index = 2;
2028*4882a593Smuzhiyun par->cob_size = 0x8000 << par->cob_index;
2029*4882a593Smuzhiyun par->cob_offset = videoRambytes;
2030*4882a593Smuzhiyun } else {
2031*4882a593Smuzhiyun /* We use 128kB for the COB on all chips. */
2032*4882a593Smuzhiyun
2033*4882a593Smuzhiyun par->cob_index = 7;
2034*4882a593Smuzhiyun par->cob_size = 0x400 << par->cob_index;
2035*4882a593Smuzhiyun par->cob_offset = videoRambytes - par->cob_size;
2036*4882a593Smuzhiyun }
2037*4882a593Smuzhiyun
2038*4882a593Smuzhiyun return videoRambytes;
2039*4882a593Smuzhiyun }
2040*4882a593Smuzhiyun
savage_init_fb_info(struct fb_info * info,struct pci_dev * dev,const struct pci_device_id * id)2041*4882a593Smuzhiyun static int savage_init_fb_info(struct fb_info *info, struct pci_dev *dev,
2042*4882a593Smuzhiyun const struct pci_device_id *id)
2043*4882a593Smuzhiyun {
2044*4882a593Smuzhiyun struct savagefb_par *par = info->par;
2045*4882a593Smuzhiyun int err = 0;
2046*4882a593Smuzhiyun
2047*4882a593Smuzhiyun par->pcidev = dev;
2048*4882a593Smuzhiyun
2049*4882a593Smuzhiyun info->fix.type = FB_TYPE_PACKED_PIXELS;
2050*4882a593Smuzhiyun info->fix.type_aux = 0;
2051*4882a593Smuzhiyun info->fix.ypanstep = 1;
2052*4882a593Smuzhiyun info->fix.ywrapstep = 0;
2053*4882a593Smuzhiyun info->fix.accel = id->driver_data;
2054*4882a593Smuzhiyun
2055*4882a593Smuzhiyun switch (info->fix.accel) {
2056*4882a593Smuzhiyun case FB_ACCEL_SUPERSAVAGE:
2057*4882a593Smuzhiyun par->chip = S3_SUPERSAVAGE;
2058*4882a593Smuzhiyun snprintf(info->fix.id, 16, "SuperSavage");
2059*4882a593Smuzhiyun break;
2060*4882a593Smuzhiyun case FB_ACCEL_SAVAGE4:
2061*4882a593Smuzhiyun par->chip = S3_SAVAGE4;
2062*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage4");
2063*4882a593Smuzhiyun break;
2064*4882a593Smuzhiyun case FB_ACCEL_SAVAGE3D:
2065*4882a593Smuzhiyun par->chip = S3_SAVAGE3D;
2066*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage3D");
2067*4882a593Smuzhiyun break;
2068*4882a593Smuzhiyun case FB_ACCEL_SAVAGE3D_MV:
2069*4882a593Smuzhiyun par->chip = S3_SAVAGE3D;
2070*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage3D-MV");
2071*4882a593Smuzhiyun break;
2072*4882a593Smuzhiyun case FB_ACCEL_SAVAGE2000:
2073*4882a593Smuzhiyun par->chip = S3_SAVAGE2000;
2074*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage2000");
2075*4882a593Smuzhiyun break;
2076*4882a593Smuzhiyun case FB_ACCEL_SAVAGE_MX_MV:
2077*4882a593Smuzhiyun par->chip = S3_SAVAGE_MX;
2078*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage/MX-MV");
2079*4882a593Smuzhiyun break;
2080*4882a593Smuzhiyun case FB_ACCEL_SAVAGE_MX:
2081*4882a593Smuzhiyun par->chip = S3_SAVAGE_MX;
2082*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage/MX");
2083*4882a593Smuzhiyun break;
2084*4882a593Smuzhiyun case FB_ACCEL_SAVAGE_IX_MV:
2085*4882a593Smuzhiyun par->chip = S3_SAVAGE_MX;
2086*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage/IX-MV");
2087*4882a593Smuzhiyun break;
2088*4882a593Smuzhiyun case FB_ACCEL_SAVAGE_IX:
2089*4882a593Smuzhiyun par->chip = S3_SAVAGE_MX;
2090*4882a593Smuzhiyun snprintf(info->fix.id, 16, "Savage/IX");
2091*4882a593Smuzhiyun break;
2092*4882a593Smuzhiyun case FB_ACCEL_PROSAVAGE_PM:
2093*4882a593Smuzhiyun par->chip = S3_PROSAVAGE;
2094*4882a593Smuzhiyun snprintf(info->fix.id, 16, "ProSavagePM");
2095*4882a593Smuzhiyun break;
2096*4882a593Smuzhiyun case FB_ACCEL_PROSAVAGE_KM:
2097*4882a593Smuzhiyun par->chip = S3_PROSAVAGE;
2098*4882a593Smuzhiyun snprintf(info->fix.id, 16, "ProSavageKM");
2099*4882a593Smuzhiyun break;
2100*4882a593Smuzhiyun case FB_ACCEL_S3TWISTER_P:
2101*4882a593Smuzhiyun par->chip = S3_TWISTER;
2102*4882a593Smuzhiyun snprintf(info->fix.id, 16, "TwisterP");
2103*4882a593Smuzhiyun break;
2104*4882a593Smuzhiyun case FB_ACCEL_S3TWISTER_K:
2105*4882a593Smuzhiyun par->chip = S3_TWISTER;
2106*4882a593Smuzhiyun snprintf(info->fix.id, 16, "TwisterK");
2107*4882a593Smuzhiyun break;
2108*4882a593Smuzhiyun case FB_ACCEL_PROSAVAGE_DDR:
2109*4882a593Smuzhiyun par->chip = S3_PROSAVAGEDDR;
2110*4882a593Smuzhiyun snprintf(info->fix.id, 16, "ProSavageDDR");
2111*4882a593Smuzhiyun break;
2112*4882a593Smuzhiyun case FB_ACCEL_PROSAVAGE_DDRK:
2113*4882a593Smuzhiyun par->chip = S3_PROSAVAGEDDR;
2114*4882a593Smuzhiyun snprintf(info->fix.id, 16, "ProSavage8");
2115*4882a593Smuzhiyun break;
2116*4882a593Smuzhiyun }
2117*4882a593Smuzhiyun
2118*4882a593Smuzhiyun if (S3_SAVAGE3D_SERIES(par->chip)) {
2119*4882a593Smuzhiyun par->SavageWaitIdle = savage3D_waitidle;
2120*4882a593Smuzhiyun par->SavageWaitFifo = savage3D_waitfifo;
2121*4882a593Smuzhiyun } else if (S3_SAVAGE4_SERIES(par->chip) ||
2122*4882a593Smuzhiyun S3_SUPERSAVAGE == par->chip) {
2123*4882a593Smuzhiyun par->SavageWaitIdle = savage4_waitidle;
2124*4882a593Smuzhiyun par->SavageWaitFifo = savage4_waitfifo;
2125*4882a593Smuzhiyun } else {
2126*4882a593Smuzhiyun par->SavageWaitIdle = savage2000_waitidle;
2127*4882a593Smuzhiyun par->SavageWaitFifo = savage2000_waitfifo;
2128*4882a593Smuzhiyun }
2129*4882a593Smuzhiyun
2130*4882a593Smuzhiyun info->var.nonstd = 0;
2131*4882a593Smuzhiyun info->var.activate = FB_ACTIVATE_NOW;
2132*4882a593Smuzhiyun info->var.width = -1;
2133*4882a593Smuzhiyun info->var.height = -1;
2134*4882a593Smuzhiyun info->var.accel_flags = 0;
2135*4882a593Smuzhiyun
2136*4882a593Smuzhiyun info->fbops = &savagefb_ops;
2137*4882a593Smuzhiyun info->flags = FBINFO_DEFAULT |
2138*4882a593Smuzhiyun FBINFO_HWACCEL_YPAN |
2139*4882a593Smuzhiyun FBINFO_HWACCEL_XPAN;
2140*4882a593Smuzhiyun
2141*4882a593Smuzhiyun info->pseudo_palette = par->pseudo_palette;
2142*4882a593Smuzhiyun
2143*4882a593Smuzhiyun #if defined(CONFIG_FB_SAVAGE_ACCEL)
2144*4882a593Smuzhiyun /* FIFO size + padding for commands */
2145*4882a593Smuzhiyun info->pixmap.addr = kcalloc(8, 1024, GFP_KERNEL);
2146*4882a593Smuzhiyun
2147*4882a593Smuzhiyun err = -ENOMEM;
2148*4882a593Smuzhiyun if (info->pixmap.addr) {
2149*4882a593Smuzhiyun info->pixmap.size = 8*1024;
2150*4882a593Smuzhiyun info->pixmap.scan_align = 4;
2151*4882a593Smuzhiyun info->pixmap.buf_align = 4;
2152*4882a593Smuzhiyun info->pixmap.access_align = 32;
2153*4882a593Smuzhiyun
2154*4882a593Smuzhiyun err = fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
2155*4882a593Smuzhiyun if (!err)
2156*4882a593Smuzhiyun info->flags |= FBINFO_HWACCEL_COPYAREA |
2157*4882a593Smuzhiyun FBINFO_HWACCEL_FILLRECT |
2158*4882a593Smuzhiyun FBINFO_HWACCEL_IMAGEBLIT;
2159*4882a593Smuzhiyun else
2160*4882a593Smuzhiyun kfree(info->pixmap.addr);
2161*4882a593Smuzhiyun }
2162*4882a593Smuzhiyun #endif
2163*4882a593Smuzhiyun return err;
2164*4882a593Smuzhiyun }
2165*4882a593Smuzhiyun
2166*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
2167*4882a593Smuzhiyun
savagefb_probe(struct pci_dev * dev,const struct pci_device_id * id)2168*4882a593Smuzhiyun static int savagefb_probe(struct pci_dev *dev, const struct pci_device_id *id)
2169*4882a593Smuzhiyun {
2170*4882a593Smuzhiyun struct fb_info *info;
2171*4882a593Smuzhiyun struct savagefb_par *par;
2172*4882a593Smuzhiyun u_int h_sync, v_sync;
2173*4882a593Smuzhiyun int err, lpitch;
2174*4882a593Smuzhiyun int video_len;
2175*4882a593Smuzhiyun
2176*4882a593Smuzhiyun DBG("savagefb_probe");
2177*4882a593Smuzhiyun
2178*4882a593Smuzhiyun info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev);
2179*4882a593Smuzhiyun if (!info)
2180*4882a593Smuzhiyun return -ENOMEM;
2181*4882a593Smuzhiyun par = info->par;
2182*4882a593Smuzhiyun mutex_init(&par->open_lock);
2183*4882a593Smuzhiyun err = pci_enable_device(dev);
2184*4882a593Smuzhiyun if (err)
2185*4882a593Smuzhiyun goto failed_enable;
2186*4882a593Smuzhiyun
2187*4882a593Smuzhiyun if ((err = pci_request_regions(dev, "savagefb"))) {
2188*4882a593Smuzhiyun printk(KERN_ERR "cannot request PCI regions\n");
2189*4882a593Smuzhiyun goto failed_enable;
2190*4882a593Smuzhiyun }
2191*4882a593Smuzhiyun
2192*4882a593Smuzhiyun err = -ENOMEM;
2193*4882a593Smuzhiyun
2194*4882a593Smuzhiyun if ((err = savage_init_fb_info(info, dev, id)))
2195*4882a593Smuzhiyun goto failed_init;
2196*4882a593Smuzhiyun
2197*4882a593Smuzhiyun err = savage_map_mmio(info);
2198*4882a593Smuzhiyun if (err)
2199*4882a593Smuzhiyun goto failed_mmio;
2200*4882a593Smuzhiyun
2201*4882a593Smuzhiyun video_len = savage_init_hw(par);
2202*4882a593Smuzhiyun /* FIXME: can't be negative */
2203*4882a593Smuzhiyun if (video_len < 0) {
2204*4882a593Smuzhiyun err = video_len;
2205*4882a593Smuzhiyun goto failed_mmio;
2206*4882a593Smuzhiyun }
2207*4882a593Smuzhiyun
2208*4882a593Smuzhiyun err = savage_map_video(info, video_len);
2209*4882a593Smuzhiyun if (err)
2210*4882a593Smuzhiyun goto failed_video;
2211*4882a593Smuzhiyun
2212*4882a593Smuzhiyun INIT_LIST_HEAD(&info->modelist);
2213*4882a593Smuzhiyun #if defined(CONFIG_FB_SAVAGE_I2C)
2214*4882a593Smuzhiyun savagefb_create_i2c_busses(info);
2215*4882a593Smuzhiyun savagefb_probe_i2c_connector(info, &par->edid);
2216*4882a593Smuzhiyun fb_edid_to_monspecs(par->edid, &info->monspecs);
2217*4882a593Smuzhiyun kfree(par->edid);
2218*4882a593Smuzhiyun fb_videomode_to_modelist(info->monspecs.modedb,
2219*4882a593Smuzhiyun info->monspecs.modedb_len,
2220*4882a593Smuzhiyun &info->modelist);
2221*4882a593Smuzhiyun #endif
2222*4882a593Smuzhiyun info->var = savagefb_var800x600x8;
2223*4882a593Smuzhiyun /* if a panel was detected, default to a CVT mode instead */
2224*4882a593Smuzhiyun if (par->SavagePanelWidth) {
2225*4882a593Smuzhiyun struct fb_videomode cvt_mode;
2226*4882a593Smuzhiyun
2227*4882a593Smuzhiyun memset(&cvt_mode, 0, sizeof(cvt_mode));
2228*4882a593Smuzhiyun cvt_mode.xres = par->SavagePanelWidth;
2229*4882a593Smuzhiyun cvt_mode.yres = par->SavagePanelHeight;
2230*4882a593Smuzhiyun cvt_mode.refresh = 60;
2231*4882a593Smuzhiyun /* FIXME: if we know there is only the panel
2232*4882a593Smuzhiyun * we can enable reduced blanking as well */
2233*4882a593Smuzhiyun if (fb_find_mode_cvt(&cvt_mode, 0, 0))
2234*4882a593Smuzhiyun printk(KERN_WARNING "No CVT mode found for panel\n");
2235*4882a593Smuzhiyun else if (fb_find_mode(&info->var, info, NULL, NULL, 0,
2236*4882a593Smuzhiyun &cvt_mode, 0) != 3)
2237*4882a593Smuzhiyun info->var = savagefb_var800x600x8;
2238*4882a593Smuzhiyun }
2239*4882a593Smuzhiyun
2240*4882a593Smuzhiyun if (mode_option) {
2241*4882a593Smuzhiyun fb_find_mode(&info->var, info, mode_option,
2242*4882a593Smuzhiyun info->monspecs.modedb, info->monspecs.modedb_len,
2243*4882a593Smuzhiyun NULL, 8);
2244*4882a593Smuzhiyun } else if (info->monspecs.modedb != NULL) {
2245*4882a593Smuzhiyun const struct fb_videomode *mode;
2246*4882a593Smuzhiyun
2247*4882a593Smuzhiyun mode = fb_find_best_display(&info->monspecs, &info->modelist);
2248*4882a593Smuzhiyun savage_update_var(&info->var, mode);
2249*4882a593Smuzhiyun }
2250*4882a593Smuzhiyun
2251*4882a593Smuzhiyun /* maximize virtual vertical length */
2252*4882a593Smuzhiyun lpitch = info->var.xres_virtual*((info->var.bits_per_pixel + 7) >> 3);
2253*4882a593Smuzhiyun info->var.yres_virtual = info->fix.smem_len/lpitch;
2254*4882a593Smuzhiyun
2255*4882a593Smuzhiyun if (info->var.yres_virtual < info->var.yres) {
2256*4882a593Smuzhiyun err = -ENOMEM;
2257*4882a593Smuzhiyun goto failed;
2258*4882a593Smuzhiyun }
2259*4882a593Smuzhiyun
2260*4882a593Smuzhiyun #if defined(CONFIG_FB_SAVAGE_ACCEL)
2261*4882a593Smuzhiyun /*
2262*4882a593Smuzhiyun * The clipping coordinates are masked with 0xFFF, so limit our
2263*4882a593Smuzhiyun * virtual resolutions to these sizes.
2264*4882a593Smuzhiyun */
2265*4882a593Smuzhiyun if (info->var.yres_virtual > 0x1000)
2266*4882a593Smuzhiyun info->var.yres_virtual = 0x1000;
2267*4882a593Smuzhiyun
2268*4882a593Smuzhiyun if (info->var.xres_virtual > 0x1000)
2269*4882a593Smuzhiyun info->var.xres_virtual = 0x1000;
2270*4882a593Smuzhiyun #endif
2271*4882a593Smuzhiyun savagefb_check_var(&info->var, info);
2272*4882a593Smuzhiyun savagefb_set_fix(info);
2273*4882a593Smuzhiyun
2274*4882a593Smuzhiyun /*
2275*4882a593Smuzhiyun * Calculate the hsync and vsync frequencies. Note that
2276*4882a593Smuzhiyun * we split the 1e12 constant up so that we can preserve
2277*4882a593Smuzhiyun * the precision and fit the results into 32-bit registers.
2278*4882a593Smuzhiyun * (1953125000 * 512 = 1e12)
2279*4882a593Smuzhiyun */
2280*4882a593Smuzhiyun h_sync = 1953125000 / info->var.pixclock;
2281*4882a593Smuzhiyun h_sync = h_sync * 512 / (info->var.xres + info->var.left_margin +
2282*4882a593Smuzhiyun info->var.right_margin +
2283*4882a593Smuzhiyun info->var.hsync_len);
2284*4882a593Smuzhiyun v_sync = h_sync / (info->var.yres + info->var.upper_margin +
2285*4882a593Smuzhiyun info->var.lower_margin + info->var.vsync_len);
2286*4882a593Smuzhiyun
2287*4882a593Smuzhiyun printk(KERN_INFO "savagefb v" SAVAGEFB_VERSION ": "
2288*4882a593Smuzhiyun "%dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
2289*4882a593Smuzhiyun info->fix.smem_len >> 10,
2290*4882a593Smuzhiyun info->var.xres, info->var.yres,
2291*4882a593Smuzhiyun h_sync / 1000, h_sync % 1000, v_sync);
2292*4882a593Smuzhiyun
2293*4882a593Smuzhiyun
2294*4882a593Smuzhiyun fb_destroy_modedb(info->monspecs.modedb);
2295*4882a593Smuzhiyun info->monspecs.modedb = NULL;
2296*4882a593Smuzhiyun
2297*4882a593Smuzhiyun err = register_framebuffer(info);
2298*4882a593Smuzhiyun if (err < 0)
2299*4882a593Smuzhiyun goto failed;
2300*4882a593Smuzhiyun
2301*4882a593Smuzhiyun printk(KERN_INFO "fb: S3 %s frame buffer device\n",
2302*4882a593Smuzhiyun info->fix.id);
2303*4882a593Smuzhiyun
2304*4882a593Smuzhiyun /*
2305*4882a593Smuzhiyun * Our driver data
2306*4882a593Smuzhiyun */
2307*4882a593Smuzhiyun pci_set_drvdata(dev, info);
2308*4882a593Smuzhiyun
2309*4882a593Smuzhiyun return 0;
2310*4882a593Smuzhiyun
2311*4882a593Smuzhiyun failed:
2312*4882a593Smuzhiyun #ifdef CONFIG_FB_SAVAGE_I2C
2313*4882a593Smuzhiyun savagefb_delete_i2c_busses(info);
2314*4882a593Smuzhiyun #endif
2315*4882a593Smuzhiyun fb_alloc_cmap(&info->cmap, 0, 0);
2316*4882a593Smuzhiyun savage_unmap_video(info);
2317*4882a593Smuzhiyun failed_video:
2318*4882a593Smuzhiyun savage_unmap_mmio(info);
2319*4882a593Smuzhiyun failed_mmio:
2320*4882a593Smuzhiyun kfree(info->pixmap.addr);
2321*4882a593Smuzhiyun failed_init:
2322*4882a593Smuzhiyun pci_release_regions(dev);
2323*4882a593Smuzhiyun failed_enable:
2324*4882a593Smuzhiyun framebuffer_release(info);
2325*4882a593Smuzhiyun
2326*4882a593Smuzhiyun return err;
2327*4882a593Smuzhiyun }
2328*4882a593Smuzhiyun
savagefb_remove(struct pci_dev * dev)2329*4882a593Smuzhiyun static void savagefb_remove(struct pci_dev *dev)
2330*4882a593Smuzhiyun {
2331*4882a593Smuzhiyun struct fb_info *info = pci_get_drvdata(dev);
2332*4882a593Smuzhiyun
2333*4882a593Smuzhiyun DBG("savagefb_remove");
2334*4882a593Smuzhiyun
2335*4882a593Smuzhiyun if (info) {
2336*4882a593Smuzhiyun unregister_framebuffer(info);
2337*4882a593Smuzhiyun
2338*4882a593Smuzhiyun #ifdef CONFIG_FB_SAVAGE_I2C
2339*4882a593Smuzhiyun savagefb_delete_i2c_busses(info);
2340*4882a593Smuzhiyun #endif
2341*4882a593Smuzhiyun fb_alloc_cmap(&info->cmap, 0, 0);
2342*4882a593Smuzhiyun savage_unmap_video(info);
2343*4882a593Smuzhiyun savage_unmap_mmio(info);
2344*4882a593Smuzhiyun kfree(info->pixmap.addr);
2345*4882a593Smuzhiyun pci_release_regions(dev);
2346*4882a593Smuzhiyun framebuffer_release(info);
2347*4882a593Smuzhiyun }
2348*4882a593Smuzhiyun }
2349*4882a593Smuzhiyun
savagefb_suspend_late(struct device * dev,pm_message_t mesg)2350*4882a593Smuzhiyun static int savagefb_suspend_late(struct device *dev, pm_message_t mesg)
2351*4882a593Smuzhiyun {
2352*4882a593Smuzhiyun struct fb_info *info = dev_get_drvdata(dev);
2353*4882a593Smuzhiyun struct savagefb_par *par = info->par;
2354*4882a593Smuzhiyun
2355*4882a593Smuzhiyun DBG("savagefb_suspend");
2356*4882a593Smuzhiyun
2357*4882a593Smuzhiyun if (mesg.event == PM_EVENT_PRETHAW)
2358*4882a593Smuzhiyun mesg.event = PM_EVENT_FREEZE;
2359*4882a593Smuzhiyun par->pm_state = mesg.event;
2360*4882a593Smuzhiyun dev->power.power_state = mesg;
2361*4882a593Smuzhiyun
2362*4882a593Smuzhiyun /*
2363*4882a593Smuzhiyun * For PM_EVENT_FREEZE, do not power down so the console
2364*4882a593Smuzhiyun * can remain active.
2365*4882a593Smuzhiyun */
2366*4882a593Smuzhiyun if (mesg.event == PM_EVENT_FREEZE)
2367*4882a593Smuzhiyun return 0;
2368*4882a593Smuzhiyun
2369*4882a593Smuzhiyun console_lock();
2370*4882a593Smuzhiyun fb_set_suspend(info, 1);
2371*4882a593Smuzhiyun
2372*4882a593Smuzhiyun if (info->fbops->fb_sync)
2373*4882a593Smuzhiyun info->fbops->fb_sync(info);
2374*4882a593Smuzhiyun
2375*4882a593Smuzhiyun savagefb_blank(FB_BLANK_POWERDOWN, info);
2376*4882a593Smuzhiyun savage_set_default_par(par, &par->save);
2377*4882a593Smuzhiyun savage_disable_mmio(par);
2378*4882a593Smuzhiyun console_unlock();
2379*4882a593Smuzhiyun
2380*4882a593Smuzhiyun return 0;
2381*4882a593Smuzhiyun }
2382*4882a593Smuzhiyun
savagefb_suspend(struct device * dev)2383*4882a593Smuzhiyun static int __maybe_unused savagefb_suspend(struct device *dev)
2384*4882a593Smuzhiyun {
2385*4882a593Smuzhiyun return savagefb_suspend_late(dev, PMSG_SUSPEND);
2386*4882a593Smuzhiyun }
2387*4882a593Smuzhiyun
savagefb_hibernate(struct device * dev)2388*4882a593Smuzhiyun static int __maybe_unused savagefb_hibernate(struct device *dev)
2389*4882a593Smuzhiyun {
2390*4882a593Smuzhiyun return savagefb_suspend_late(dev, PMSG_HIBERNATE);
2391*4882a593Smuzhiyun }
2392*4882a593Smuzhiyun
savagefb_freeze(struct device * dev)2393*4882a593Smuzhiyun static int __maybe_unused savagefb_freeze(struct device *dev)
2394*4882a593Smuzhiyun {
2395*4882a593Smuzhiyun return savagefb_suspend_late(dev, PMSG_FREEZE);
2396*4882a593Smuzhiyun }
2397*4882a593Smuzhiyun
savagefb_resume(struct device * dev)2398*4882a593Smuzhiyun static int __maybe_unused savagefb_resume(struct device *dev)
2399*4882a593Smuzhiyun {
2400*4882a593Smuzhiyun struct fb_info *info = dev_get_drvdata(dev);
2401*4882a593Smuzhiyun struct savagefb_par *par = info->par;
2402*4882a593Smuzhiyun int cur_state = par->pm_state;
2403*4882a593Smuzhiyun
2404*4882a593Smuzhiyun DBG("savage_resume");
2405*4882a593Smuzhiyun
2406*4882a593Smuzhiyun par->pm_state = PM_EVENT_ON;
2407*4882a593Smuzhiyun
2408*4882a593Smuzhiyun /*
2409*4882a593Smuzhiyun * The adapter was not powered down coming back from a
2410*4882a593Smuzhiyun * PM_EVENT_FREEZE.
2411*4882a593Smuzhiyun */
2412*4882a593Smuzhiyun if (cur_state == PM_EVENT_FREEZE)
2413*4882a593Smuzhiyun return 0;
2414*4882a593Smuzhiyun
2415*4882a593Smuzhiyun console_lock();
2416*4882a593Smuzhiyun
2417*4882a593Smuzhiyun savage_enable_mmio(par);
2418*4882a593Smuzhiyun savage_init_hw(par);
2419*4882a593Smuzhiyun savagefb_set_par(info);
2420*4882a593Smuzhiyun fb_set_suspend(info, 0);
2421*4882a593Smuzhiyun savagefb_blank(FB_BLANK_UNBLANK, info);
2422*4882a593Smuzhiyun console_unlock();
2423*4882a593Smuzhiyun
2424*4882a593Smuzhiyun return 0;
2425*4882a593Smuzhiyun }
2426*4882a593Smuzhiyun
2427*4882a593Smuzhiyun static const struct dev_pm_ops savagefb_pm_ops = {
2428*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
2429*4882a593Smuzhiyun .suspend = savagefb_suspend,
2430*4882a593Smuzhiyun .resume = savagefb_resume,
2431*4882a593Smuzhiyun .freeze = savagefb_freeze,
2432*4882a593Smuzhiyun .thaw = savagefb_resume,
2433*4882a593Smuzhiyun .poweroff = savagefb_hibernate,
2434*4882a593Smuzhiyun .restore = savagefb_resume,
2435*4882a593Smuzhiyun #endif
2436*4882a593Smuzhiyun };
2437*4882a593Smuzhiyun
2438*4882a593Smuzhiyun static const struct pci_device_id savagefb_devices[] = {
2439*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX128,
2440*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2441*4882a593Smuzhiyun
2442*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64,
2443*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2444*4882a593Smuzhiyun
2445*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_MX64C,
2446*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2447*4882a593Smuzhiyun
2448*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128SDR,
2449*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2450*4882a593Smuzhiyun
2451*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX128DDR,
2452*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2453*4882a593Smuzhiyun
2454*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64SDR,
2455*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2456*4882a593Smuzhiyun
2457*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IX64DDR,
2458*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2459*4882a593Smuzhiyun
2460*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCSDR,
2461*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2462*4882a593Smuzhiyun
2463*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SUPSAV_IXCDDR,
2464*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SUPERSAVAGE},
2465*4882a593Smuzhiyun
2466*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4,
2467*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE4},
2468*4882a593Smuzhiyun
2469*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D,
2470*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D},
2471*4882a593Smuzhiyun
2472*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE3D_MV,
2473*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE3D_MV},
2474*4882a593Smuzhiyun
2475*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000,
2476*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE2000},
2477*4882a593Smuzhiyun
2478*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX_MV,
2479*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX_MV},
2480*4882a593Smuzhiyun
2481*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_MX,
2482*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_MX},
2483*4882a593Smuzhiyun
2484*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX_MV,
2485*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX_MV},
2486*4882a593Smuzhiyun
2487*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE_IX,
2488*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_SAVAGE_IX},
2489*4882a593Smuzhiyun
2490*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_PM,
2491*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_PM},
2492*4882a593Smuzhiyun
2493*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_KM,
2494*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_KM},
2495*4882a593Smuzhiyun
2496*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_P,
2497*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_P},
2498*4882a593Smuzhiyun
2499*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_S3TWISTER_K,
2500*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_S3TWISTER_K},
2501*4882a593Smuzhiyun
2502*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDR,
2503*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDR},
2504*4882a593Smuzhiyun
2505*4882a593Smuzhiyun {PCI_VENDOR_ID_S3, PCI_CHIP_PROSAVAGE_DDRK,
2506*4882a593Smuzhiyun PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_PROSAVAGE_DDRK},
2507*4882a593Smuzhiyun
2508*4882a593Smuzhiyun {0, 0, 0, 0, 0, 0, 0}
2509*4882a593Smuzhiyun };
2510*4882a593Smuzhiyun
2511*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, savagefb_devices);
2512*4882a593Smuzhiyun
2513*4882a593Smuzhiyun static struct pci_driver savagefb_driver = {
2514*4882a593Smuzhiyun .name = "savagefb",
2515*4882a593Smuzhiyun .id_table = savagefb_devices,
2516*4882a593Smuzhiyun .probe = savagefb_probe,
2517*4882a593Smuzhiyun .driver.pm = &savagefb_pm_ops,
2518*4882a593Smuzhiyun .remove = savagefb_remove,
2519*4882a593Smuzhiyun };
2520*4882a593Smuzhiyun
2521*4882a593Smuzhiyun /* **************************** exit-time only **************************** */
2522*4882a593Smuzhiyun
savage_done(void)2523*4882a593Smuzhiyun static void __exit savage_done(void)
2524*4882a593Smuzhiyun {
2525*4882a593Smuzhiyun DBG("savage_done");
2526*4882a593Smuzhiyun pci_unregister_driver(&savagefb_driver);
2527*4882a593Smuzhiyun }
2528*4882a593Smuzhiyun
2529*4882a593Smuzhiyun
2530*4882a593Smuzhiyun /* ************************* init in-kernel code ************************** */
2531*4882a593Smuzhiyun
savagefb_setup(char * options)2532*4882a593Smuzhiyun static int __init savagefb_setup(char *options)
2533*4882a593Smuzhiyun {
2534*4882a593Smuzhiyun #ifndef MODULE
2535*4882a593Smuzhiyun char *this_opt;
2536*4882a593Smuzhiyun
2537*4882a593Smuzhiyun if (!options || !*options)
2538*4882a593Smuzhiyun return 0;
2539*4882a593Smuzhiyun
2540*4882a593Smuzhiyun while ((this_opt = strsep(&options, ",")) != NULL) {
2541*4882a593Smuzhiyun mode_option = this_opt;
2542*4882a593Smuzhiyun }
2543*4882a593Smuzhiyun #endif /* !MODULE */
2544*4882a593Smuzhiyun return 0;
2545*4882a593Smuzhiyun }
2546*4882a593Smuzhiyun
savagefb_init(void)2547*4882a593Smuzhiyun static int __init savagefb_init(void)
2548*4882a593Smuzhiyun {
2549*4882a593Smuzhiyun char *option;
2550*4882a593Smuzhiyun
2551*4882a593Smuzhiyun DBG("savagefb_init");
2552*4882a593Smuzhiyun
2553*4882a593Smuzhiyun if (fb_get_options("savagefb", &option))
2554*4882a593Smuzhiyun return -ENODEV;
2555*4882a593Smuzhiyun
2556*4882a593Smuzhiyun savagefb_setup(option);
2557*4882a593Smuzhiyun return pci_register_driver(&savagefb_driver);
2558*4882a593Smuzhiyun
2559*4882a593Smuzhiyun }
2560*4882a593Smuzhiyun
2561*4882a593Smuzhiyun module_init(savagefb_init);
2562*4882a593Smuzhiyun module_exit(savage_done);
2563*4882a593Smuzhiyun
2564*4882a593Smuzhiyun module_param(mode_option, charp, 0);
2565*4882a593Smuzhiyun MODULE_PARM_DESC(mode_option, "Specify initial video mode");
2566