Commit | Line | Data |
---|---|---|
a268422d OZ |
1 | /* |
2 | * linux/drivers/video/s3fb.c -- Frame buffer device driver for S3 Trio/Virge | |
3 | * | |
4 | * Copyright (c) 2006-2007 Ondrej Zajicek <santiago@crfreenet.org> | |
5 | * | |
6 | * This file is subject to the terms and conditions of the GNU General Public | |
7 | * License. See the file COPYING in the main directory of this archive for | |
8 | * more details. | |
9 | * | |
10 | * Code is based on David Boucher's viafb (http://davesdomain.org.uk/viafb/) | |
11 | * which is based on the code of neofb. | |
12 | */ | |
13 | ||
a268422d OZ |
14 | #include <linux/module.h> |
15 | #include <linux/kernel.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/string.h> | |
18 | #include <linux/mm.h> | |
19 | #include <linux/tty.h> | |
a268422d OZ |
20 | #include <linux/delay.h> |
21 | #include <linux/fb.h> | |
22 | #include <linux/svga.h> | |
23 | #include <linux/init.h> | |
24 | #include <linux/pci.h> | |
ac751efa | 25 | #include <linux/console.h> /* Why should fb driver call console functions? because console_lock() */ |
a268422d OZ |
26 | #include <video/vga.h> |
27 | ||
86c0f043 OZ |
28 | #include <linux/i2c.h> |
29 | #include <linux/i2c-algo-bit.h> | |
30 | ||
a268422d OZ |
31 | struct s3fb_info { |
32 | int chip, rev, mclk_freq; | |
4edcd2ab | 33 | int wc_cookie; |
a268422d OZ |
34 | struct vgastate state; |
35 | struct mutex open_lock; | |
36 | unsigned int ref_count; | |
37 | u32 pseudo_palette[16]; | |
86c0f043 OZ |
38 | #ifdef CONFIG_FB_S3_DDC |
39 | u8 __iomem *mmio; | |
40 | bool ddc_registered; | |
41 | struct i2c_adapter ddc_adapter; | |
42 | struct i2c_algo_bit_data ddc_algo; | |
43 | #endif | |
a268422d OZ |
44 | }; |
45 | ||
46 | ||
47 | /* ------------------------------------------------------------------------- */ | |
48 | ||
49 | static const struct svga_fb_format s3fb_formats[] = { | |
50 | { 0, {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0}, 0, | |
51 | FB_TYPE_TEXT, FB_AUX_TEXT_SVGA_STEP4, FB_VISUAL_PSEUDOCOLOR, 8, 16}, | |
c26d7b29 | 52 | { 4, {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, 0, |
a268422d | 53 | FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 8, 16}, |
c26d7b29 | 54 | { 4, {0, 4, 0}, {0, 4, 0}, {0, 4, 0}, {0, 0, 0}, 1, |
a268422d | 55 | FB_TYPE_INTERLEAVED_PLANES, 1, FB_VISUAL_PSEUDOCOLOR, 8, 16}, |
c26d7b29 | 56 | { 8, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, |
a268422d OZ |
57 | FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_PSEUDOCOLOR, 4, 8}, |
58 | {16, {10, 5, 0}, {5, 5, 0}, {0, 5, 0}, {0, 0, 0}, 0, | |
59 | FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 2, 4}, | |
60 | {16, {11, 5, 0}, {5, 6, 0}, {0, 5, 0}, {0, 0, 0}, 0, | |
61 | FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 2, 4}, | |
62 | {24, {16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, | |
63 | FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 1, 2}, | |
64 | {32, {16, 8, 0}, {8, 8, 0}, {0, 8, 0}, {0, 0, 0}, 0, | |
65 | FB_TYPE_PACKED_PIXELS, 0, FB_VISUAL_TRUECOLOR, 1, 2}, | |
66 | SVGA_FORMAT_END | |
67 | }; | |
68 | ||
69 | ||
70 | static const struct svga_pll s3_pll = {3, 129, 3, 33, 0, 3, | |
249bdbbf | 71 | 35000, 240000, 14318}; |
5694f9ce OZ |
72 | static const struct svga_pll s3_trio3d_pll = {3, 129, 3, 31, 0, 4, |
73 | 230000, 460000, 14318}; | |
a268422d OZ |
74 | |
75 | static const int s3_memsizes[] = {4096, 0, 3072, 8192, 2048, 6144, 1024, 512}; | |
76 | ||
77 | static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64", "S3 Trio64V+", | |
78 | "S3 Trio64UV+", "S3 Trio64V2/DX", "S3 Trio64V2/GX", | |
94e948e6 | 79 | "S3 Plato/PX", "S3 Aurora64V+", "S3 Virge", |
a268422d | 80 | "S3 Virge/VX", "S3 Virge/DX", "S3 Virge/GX", |
94e948e6 | 81 | "S3 Virge/GX2", "S3 Virge/GX2+", "", |
5694f9ce | 82 | "S3 Trio3D/1X", "S3 Trio3D/2X", "S3 Trio3D/2X", |
6fcdbc0c | 83 | "S3 Trio3D", "S3 Virge/MX"}; |
a268422d OZ |
84 | |
85 | #define CHIP_UNKNOWN 0x00 | |
86 | #define CHIP_732_TRIO32 0x01 | |
87 | #define CHIP_764_TRIO64 0x02 | |
88 | #define CHIP_765_TRIO64VP 0x03 | |
89 | #define CHIP_767_TRIO64UVP 0x04 | |
90 | #define CHIP_775_TRIO64V2_DX 0x05 | |
91 | #define CHIP_785_TRIO64V2_GX 0x06 | |
92 | #define CHIP_551_PLATO_PX 0x07 | |
93 | #define CHIP_M65_AURORA64VP 0x08 | |
94 | #define CHIP_325_VIRGE 0x09 | |
95 | #define CHIP_988_VIRGE_VX 0x0A | |
96 | #define CHIP_375_VIRGE_DX 0x0B | |
97 | #define CHIP_385_VIRGE_GX 0x0C | |
94e948e6 OZ |
98 | #define CHIP_357_VIRGE_GX2 0x0D |
99 | #define CHIP_359_VIRGE_GX2P 0x0E | |
9966c4fe OZ |
100 | #define CHIP_360_TRIO3D_1X 0x10 |
101 | #define CHIP_362_TRIO3D_2X 0x11 | |
102 | #define CHIP_368_TRIO3D_2X 0x12 | |
5694f9ce | 103 | #define CHIP_365_TRIO3D 0x13 |
6fcdbc0c | 104 | #define CHIP_260_VIRGE_MX 0x14 |
a268422d OZ |
105 | |
106 | #define CHIP_XXX_TRIO 0x80 | |
107 | #define CHIP_XXX_TRIO64V2_DXGX 0x81 | |
108 | #define CHIP_XXX_VIRGE_DXGX 0x82 | |
9966c4fe | 109 | #define CHIP_36X_TRIO3D_1X_2X 0x83 |
a268422d OZ |
110 | |
111 | #define CHIP_UNDECIDED_FLAG 0x80 | |
112 | #define CHIP_MASK 0xFF | |
113 | ||
86c0f043 OZ |
114 | #define MMIO_OFFSET 0x1000000 |
115 | #define MMIO_SIZE 0x10000 | |
116 | ||
a268422d OZ |
117 | /* CRT timing register sets */ |
118 | ||
119 | static const struct vga_regset s3_h_total_regs[] = {{0x00, 0, 7}, {0x5D, 0, 0}, VGA_REGSET_END}; | |
120 | static const struct vga_regset s3_h_display_regs[] = {{0x01, 0, 7}, {0x5D, 1, 1}, VGA_REGSET_END}; | |
121 | static const struct vga_regset s3_h_blank_start_regs[] = {{0x02, 0, 7}, {0x5D, 2, 2}, VGA_REGSET_END}; | |
122 | static const struct vga_regset s3_h_blank_end_regs[] = {{0x03, 0, 4}, {0x05, 7, 7}, VGA_REGSET_END}; | |
123 | static const struct vga_regset s3_h_sync_start_regs[] = {{0x04, 0, 7}, {0x5D, 4, 4}, VGA_REGSET_END}; | |
124 | static const struct vga_regset s3_h_sync_end_regs[] = {{0x05, 0, 4}, VGA_REGSET_END}; | |
125 | ||
126 | static const struct vga_regset s3_v_total_regs[] = {{0x06, 0, 7}, {0x07, 0, 0}, {0x07, 5, 5}, {0x5E, 0, 0}, VGA_REGSET_END}; | |
127 | static const struct vga_regset s3_v_display_regs[] = {{0x12, 0, 7}, {0x07, 1, 1}, {0x07, 6, 6}, {0x5E, 1, 1}, VGA_REGSET_END}; | |
128 | static const struct vga_regset s3_v_blank_start_regs[] = {{0x15, 0, 7}, {0x07, 3, 3}, {0x09, 5, 5}, {0x5E, 2, 2}, VGA_REGSET_END}; | |
129 | static const struct vga_regset s3_v_blank_end_regs[] = {{0x16, 0, 7}, VGA_REGSET_END}; | |
130 | static const struct vga_regset s3_v_sync_start_regs[] = {{0x10, 0, 7}, {0x07, 2, 2}, {0x07, 7, 7}, {0x5E, 4, 4}, VGA_REGSET_END}; | |
131 | static const struct vga_regset s3_v_sync_end_regs[] = {{0x11, 0, 3}, VGA_REGSET_END}; | |
132 | ||
133 | static const struct vga_regset s3_line_compare_regs[] = {{0x18, 0, 7}, {0x07, 4, 4}, {0x09, 6, 6}, {0x5E, 6, 6}, VGA_REGSET_END}; | |
7fe029df | 134 | static const struct vga_regset s3_start_address_regs[] = {{0x0d, 0, 7}, {0x0c, 0, 7}, {0x69, 0, 4}, VGA_REGSET_END}; |
a268422d OZ |
135 | static const struct vga_regset s3_offset_regs[] = {{0x13, 0, 7}, {0x51, 4, 5}, VGA_REGSET_END}; /* set 0x43 bit 2 to 0 */ |
136 | ||
cb11c048 OZ |
137 | static const struct vga_regset s3_dtpc_regs[] = {{0x3B, 0, 7}, {0x5D, 6, 6}, VGA_REGSET_END}; |
138 | ||
a268422d OZ |
139 | static const struct svga_timing_regs s3_timing_regs = { |
140 | s3_h_total_regs, s3_h_display_regs, s3_h_blank_start_regs, | |
141 | s3_h_blank_end_regs, s3_h_sync_start_regs, s3_h_sync_end_regs, | |
142 | s3_v_total_regs, s3_v_display_regs, s3_v_blank_start_regs, | |
143 | s3_v_blank_end_regs, s3_v_sync_start_regs, s3_v_sync_end_regs, | |
144 | }; | |
145 | ||
146 | ||
147 | /* ------------------------------------------------------------------------- */ | |
148 | ||
149 | /* Module parameters */ | |
150 | ||
151 | ||
48c68c4f | 152 | static char *mode_option; |
48c68c4f | 153 | static int mtrr = 1; |
a268422d OZ |
154 | static int fasttext = 1; |
155 | ||
156 | ||
157 | MODULE_AUTHOR("(c) 2006-2007 Ondrej Zajicek <santiago@crfreenet.org>"); | |
158 | MODULE_LICENSE("GPL"); | |
159 | MODULE_DESCRIPTION("fbdev driver for S3 Trio/Virge"); | |
160 | ||
a8140543 KH |
161 | module_param(mode_option, charp, 0444); |
162 | MODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)"); | |
163 | module_param_named(mode, mode_option, charp, 0444); | |
164 | MODULE_PARM_DESC(mode, "Default video mode ('640x480-8@60', etc) (deprecated)"); | |
a268422d OZ |
165 | module_param(mtrr, int, 0444); |
166 | MODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)"); | |
a268422d OZ |
167 | |
168 | module_param(fasttext, int, 0644); | |
169 | MODULE_PARM_DESC(fasttext, "Enable S3 fast text mode (1=enable, 0=disable, default=1)"); | |
170 | ||
171 | ||
86c0f043 OZ |
172 | /* ------------------------------------------------------------------------- */ |
173 | ||
174 | #ifdef CONFIG_FB_S3_DDC | |
175 | ||
176 | #define DDC_REG 0xaa /* Trio 3D/1X/2X */ | |
177 | #define DDC_MMIO_REG 0xff20 /* all other chips */ | |
178 | #define DDC_SCL_OUT (1 << 0) | |
179 | #define DDC_SDA_OUT (1 << 1) | |
180 | #define DDC_SCL_IN (1 << 2) | |
181 | #define DDC_SDA_IN (1 << 3) | |
182 | #define DDC_DRIVE_EN (1 << 4) | |
183 | ||
184 | static bool s3fb_ddc_needs_mmio(int chip) | |
185 | { | |
186 | return !(chip == CHIP_360_TRIO3D_1X || | |
187 | chip == CHIP_362_TRIO3D_2X || | |
188 | chip == CHIP_368_TRIO3D_2X); | |
189 | } | |
190 | ||
191 | static u8 s3fb_ddc_read(struct s3fb_info *par) | |
192 | { | |
193 | if (s3fb_ddc_needs_mmio(par->chip)) | |
194 | return readb(par->mmio + DDC_MMIO_REG); | |
195 | else | |
196 | return vga_rcrt(par->state.vgabase, DDC_REG); | |
197 | } | |
198 | ||
199 | static void s3fb_ddc_write(struct s3fb_info *par, u8 val) | |
200 | { | |
201 | if (s3fb_ddc_needs_mmio(par->chip)) | |
202 | writeb(val, par->mmio + DDC_MMIO_REG); | |
203 | else | |
204 | vga_wcrt(par->state.vgabase, DDC_REG, val); | |
205 | } | |
206 | ||
207 | static void s3fb_ddc_setscl(void *data, int val) | |
208 | { | |
209 | struct s3fb_info *par = data; | |
210 | unsigned char reg; | |
211 | ||
212 | reg = s3fb_ddc_read(par) | DDC_DRIVE_EN; | |
213 | if (val) | |
214 | reg |= DDC_SCL_OUT; | |
215 | else | |
216 | reg &= ~DDC_SCL_OUT; | |
217 | s3fb_ddc_write(par, reg); | |
218 | } | |
219 | ||
220 | static void s3fb_ddc_setsda(void *data, int val) | |
221 | { | |
222 | struct s3fb_info *par = data; | |
223 | unsigned char reg; | |
224 | ||
225 | reg = s3fb_ddc_read(par) | DDC_DRIVE_EN; | |
226 | if (val) | |
227 | reg |= DDC_SDA_OUT; | |
228 | else | |
229 | reg &= ~DDC_SDA_OUT; | |
230 | s3fb_ddc_write(par, reg); | |
231 | } | |
232 | ||
233 | static int s3fb_ddc_getscl(void *data) | |
234 | { | |
235 | struct s3fb_info *par = data; | |
236 | ||
237 | return !!(s3fb_ddc_read(par) & DDC_SCL_IN); | |
238 | } | |
239 | ||
240 | static int s3fb_ddc_getsda(void *data) | |
241 | { | |
242 | struct s3fb_info *par = data; | |
243 | ||
244 | return !!(s3fb_ddc_read(par) & DDC_SDA_IN); | |
245 | } | |
246 | ||
48c68c4f | 247 | static int s3fb_setup_ddc_bus(struct fb_info *info) |
86c0f043 OZ |
248 | { |
249 | struct s3fb_info *par = info->par; | |
250 | ||
251 | strlcpy(par->ddc_adapter.name, info->fix.id, | |
252 | sizeof(par->ddc_adapter.name)); | |
253 | par->ddc_adapter.owner = THIS_MODULE; | |
254 | par->ddc_adapter.class = I2C_CLASS_DDC; | |
255 | par->ddc_adapter.algo_data = &par->ddc_algo; | |
256 | par->ddc_adapter.dev.parent = info->device; | |
257 | par->ddc_algo.setsda = s3fb_ddc_setsda; | |
258 | par->ddc_algo.setscl = s3fb_ddc_setscl; | |
259 | par->ddc_algo.getsda = s3fb_ddc_getsda; | |
260 | par->ddc_algo.getscl = s3fb_ddc_getscl; | |
261 | par->ddc_algo.udelay = 10; | |
262 | par->ddc_algo.timeout = 20; | |
263 | par->ddc_algo.data = par; | |
264 | ||
265 | i2c_set_adapdata(&par->ddc_adapter, par); | |
266 | ||
267 | /* | |
268 | * some Virge cards have external MUX to switch chip I2C bus between | |
269 | * DDC and extension pins - switch it do DDC | |
270 | */ | |
271 | /* vga_wseq(par->state.vgabase, 0x08, 0x06); - not needed, already unlocked */ | |
272 | if (par->chip == CHIP_357_VIRGE_GX2 || | |
6fcdbc0c OZ |
273 | par->chip == CHIP_359_VIRGE_GX2P || |
274 | par->chip == CHIP_260_VIRGE_MX) | |
86c0f043 OZ |
275 | svga_wseq_mask(par->state.vgabase, 0x0d, 0x01, 0x03); |
276 | else | |
277 | svga_wseq_mask(par->state.vgabase, 0x0d, 0x00, 0x03); | |
278 | /* some Virge need this or the DDC is ignored */ | |
279 | svga_wcrt_mask(par->state.vgabase, 0x5c, 0x03, 0x03); | |
280 | ||
281 | return i2c_bit_add_bus(&par->ddc_adapter); | |
282 | } | |
283 | #endif /* CONFIG_FB_S3_DDC */ | |
284 | ||
285 | ||
a268422d OZ |
286 | /* ------------------------------------------------------------------------- */ |
287 | ||
288 | /* Set font in S3 fast text mode */ | |
289 | ||
290 | static void s3fb_settile_fast(struct fb_info *info, struct fb_tilemap *map) | |
291 | { | |
292 | const u8 *font = map->data; | |
75814d87 | 293 | u8 __iomem *fb = (u8 __iomem *) info->screen_base; |
a268422d OZ |
294 | int i, c; |
295 | ||
296 | if ((map->width != 8) || (map->height != 16) || | |
297 | (map->depth != 1) || (map->length != 256)) { | |
31b6780c JP |
298 | fb_err(info, "unsupported font parameters: width %d, height %d, depth %d, length %d\n", |
299 | map->width, map->height, map->depth, map->length); | |
a268422d OZ |
300 | return; |
301 | } | |
302 | ||
303 | fb += 2; | |
304 | for (i = 0; i < map->height; i++) { | |
305 | for (c = 0; c < map->length; c++) { | |
75814d87 | 306 | fb_writeb(font[c * map->height + i], fb + c * 4); |
a268422d OZ |
307 | } |
308 | fb += 1024; | |
309 | } | |
310 | } | |
311 | ||
55db0923 DM |
312 | static void s3fb_tilecursor(struct fb_info *info, struct fb_tilecursor *cursor) |
313 | { | |
314 | struct s3fb_info *par = info->par; | |
315 | ||
316 | svga_tilecursor(par->state.vgabase, info, cursor); | |
317 | } | |
318 | ||
a268422d OZ |
319 | static struct fb_tile_ops s3fb_tile_ops = { |
320 | .fb_settile = svga_settile, | |
321 | .fb_tilecopy = svga_tilecopy, | |
322 | .fb_tilefill = svga_tilefill, | |
323 | .fb_tileblit = svga_tileblit, | |
55db0923 | 324 | .fb_tilecursor = s3fb_tilecursor, |
34ed25f5 | 325 | .fb_get_tilemax = svga_get_tilemax, |
a268422d OZ |
326 | }; |
327 | ||
328 | static struct fb_tile_ops s3fb_fast_tile_ops = { | |
329 | .fb_settile = s3fb_settile_fast, | |
330 | .fb_tilecopy = svga_tilecopy, | |
331 | .fb_tilefill = svga_tilefill, | |
332 | .fb_tileblit = svga_tileblit, | |
55db0923 | 333 | .fb_tilecursor = s3fb_tilecursor, |
34ed25f5 | 334 | .fb_get_tilemax = svga_get_tilemax, |
a268422d OZ |
335 | }; |
336 | ||
337 | ||
338 | /* ------------------------------------------------------------------------- */ | |
339 | ||
340 | /* image data is MSB-first, fb structure is MSB-first too */ | |
341 | static inline u32 expand_color(u32 c) | |
342 | { | |
343 | return ((c & 1) | ((c & 2) << 7) | ((c & 4) << 14) | ((c & 8) << 21)) * 0xFF; | |
344 | } | |
345 | ||
346 | /* s3fb_iplan_imageblit silently assumes that almost everything is 8-pixel aligned */ | |
347 | static void s3fb_iplan_imageblit(struct fb_info *info, const struct fb_image *image) | |
348 | { | |
349 | u32 fg = expand_color(image->fg_color); | |
350 | u32 bg = expand_color(image->bg_color); | |
351 | const u8 *src1, *src; | |
352 | u8 __iomem *dst1; | |
353 | u32 __iomem *dst; | |
354 | u32 val; | |
355 | int x, y; | |
356 | ||
357 | src1 = image->data; | |
358 | dst1 = info->screen_base + (image->dy * info->fix.line_length) | |
359 | + ((image->dx / 8) * 4); | |
360 | ||
361 | for (y = 0; y < image->height; y++) { | |
362 | src = src1; | |
363 | dst = (u32 __iomem *) dst1; | |
364 | for (x = 0; x < image->width; x += 8) { | |
365 | val = *(src++) * 0x01010101; | |
366 | val = (val & fg) | (~val & bg); | |
367 | fb_writel(val, dst++); | |
368 | } | |
369 | src1 += image->width / 8; | |
370 | dst1 += info->fix.line_length; | |
371 | } | |
372 | ||
373 | } | |
374 | ||
375 | /* s3fb_iplan_fillrect silently assumes that almost everything is 8-pixel aligned */ | |
376 | static void s3fb_iplan_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | |
377 | { | |
378 | u32 fg = expand_color(rect->color); | |
379 | u8 __iomem *dst1; | |
380 | u32 __iomem *dst; | |
381 | int x, y; | |
382 | ||
383 | dst1 = info->screen_base + (rect->dy * info->fix.line_length) | |
384 | + ((rect->dx / 8) * 4); | |
385 | ||
386 | for (y = 0; y < rect->height; y++) { | |
387 | dst = (u32 __iomem *) dst1; | |
388 | for (x = 0; x < rect->width; x += 8) { | |
389 | fb_writel(fg, dst++); | |
390 | } | |
391 | dst1 += info->fix.line_length; | |
392 | } | |
393 | } | |
394 | ||
395 | ||
396 | /* image data is MSB-first, fb structure is high-nibble-in-low-byte-first */ | |
397 | static inline u32 expand_pixel(u32 c) | |
398 | { | |
399 | return (((c & 1) << 24) | ((c & 2) << 27) | ((c & 4) << 14) | ((c & 8) << 17) | | |
400 | ((c & 16) << 4) | ((c & 32) << 7) | ((c & 64) >> 6) | ((c & 128) >> 3)) * 0xF; | |
401 | } | |
402 | ||
403 | /* s3fb_cfb4_imageblit silently assumes that almost everything is 8-pixel aligned */ | |
404 | static void s3fb_cfb4_imageblit(struct fb_info *info, const struct fb_image *image) | |
405 | { | |
406 | u32 fg = image->fg_color * 0x11111111; | |
407 | u32 bg = image->bg_color * 0x11111111; | |
408 | const u8 *src1, *src; | |
409 | u8 __iomem *dst1; | |
410 | u32 __iomem *dst; | |
411 | u32 val; | |
412 | int x, y; | |
413 | ||
414 | src1 = image->data; | |
415 | dst1 = info->screen_base + (image->dy * info->fix.line_length) | |
416 | + ((image->dx / 8) * 4); | |
417 | ||
418 | for (y = 0; y < image->height; y++) { | |
419 | src = src1; | |
420 | dst = (u32 __iomem *) dst1; | |
421 | for (x = 0; x < image->width; x += 8) { | |
422 | val = expand_pixel(*(src++)); | |
423 | val = (val & fg) | (~val & bg); | |
424 | fb_writel(val, dst++); | |
425 | } | |
426 | src1 += image->width / 8; | |
427 | dst1 += info->fix.line_length; | |
428 | } | |
429 | } | |
430 | ||
431 | static void s3fb_imageblit(struct fb_info *info, const struct fb_image *image) | |
432 | { | |
433 | if ((info->var.bits_per_pixel == 4) && (image->depth == 1) | |
434 | && ((image->width % 8) == 0) && ((image->dx % 8) == 0)) { | |
435 | if (info->fix.type == FB_TYPE_INTERLEAVED_PLANES) | |
436 | s3fb_iplan_imageblit(info, image); | |
437 | else | |
438 | s3fb_cfb4_imageblit(info, image); | |
439 | } else | |
440 | cfb_imageblit(info, image); | |
441 | } | |
442 | ||
443 | static void s3fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | |
444 | { | |
445 | if ((info->var.bits_per_pixel == 4) | |
446 | && ((rect->width % 8) == 0) && ((rect->dx % 8) == 0) | |
447 | && (info->fix.type == FB_TYPE_INTERLEAVED_PLANES)) | |
448 | s3fb_iplan_fillrect(info, rect); | |
449 | else | |
450 | cfb_fillrect(info, rect); | |
451 | } | |
452 | ||
453 | ||
454 | ||
455 | /* ------------------------------------------------------------------------- */ | |
456 | ||
457 | ||
458 | static void s3_set_pixclock(struct fb_info *info, u32 pixclock) | |
459 | { | |
9966c4fe | 460 | struct s3fb_info *par = info->par; |
a268422d OZ |
461 | u16 m, n, r; |
462 | u8 regval; | |
249bdbbf | 463 | int rv; |
a268422d | 464 | |
5694f9ce OZ |
465 | rv = svga_compute_pll((par->chip == CHIP_365_TRIO3D) ? &s3_trio3d_pll : &s3_pll, |
466 | 1000000000 / pixclock, &m, &n, &r, info->node); | |
249bdbbf | 467 | if (rv < 0) { |
31b6780c | 468 | fb_err(info, "cannot set requested pixclock, keeping old value\n"); |
249bdbbf OZ |
469 | return; |
470 | } | |
a268422d OZ |
471 | |
472 | /* Set VGA misc register */ | |
f8645933 DM |
473 | regval = vga_r(par->state.vgabase, VGA_MIS_R); |
474 | vga_w(par->state.vgabase, VGA_MIS_W, regval | VGA_MIS_ENB_PLL_LOAD); | |
a268422d OZ |
475 | |
476 | /* Set S3 clock registers */ | |
94e948e6 OZ |
477 | if (par->chip == CHIP_357_VIRGE_GX2 || |
478 | par->chip == CHIP_359_VIRGE_GX2P || | |
479 | par->chip == CHIP_360_TRIO3D_1X || | |
9966c4fe | 480 | par->chip == CHIP_362_TRIO3D_2X || |
6fcdbc0c OZ |
481 | par->chip == CHIP_368_TRIO3D_2X || |
482 | par->chip == CHIP_260_VIRGE_MX) { | |
f8645933 DM |
483 | vga_wseq(par->state.vgabase, 0x12, (n - 2) | ((r & 3) << 6)); /* n and two bits of r */ |
484 | vga_wseq(par->state.vgabase, 0x29, r >> 2); /* remaining highest bit of r */ | |
9966c4fe | 485 | } else |
f8645933 DM |
486 | vga_wseq(par->state.vgabase, 0x12, (n - 2) | (r << 5)); |
487 | vga_wseq(par->state.vgabase, 0x13, m - 2); | |
a268422d OZ |
488 | |
489 | udelay(1000); | |
490 | ||
491 | /* Activate clock - write 0, 1, 0 to seq/15 bit 5 */ | |
f8645933 DM |
492 | regval = vga_rseq (par->state.vgabase, 0x15); /* | 0x80; */ |
493 | vga_wseq(par->state.vgabase, 0x15, regval & ~(1<<5)); | |
494 | vga_wseq(par->state.vgabase, 0x15, regval | (1<<5)); | |
495 | vga_wseq(par->state.vgabase, 0x15, regval & ~(1<<5)); | |
a268422d OZ |
496 | } |
497 | ||
498 | ||
499 | /* Open framebuffer */ | |
500 | ||
501 | static int s3fb_open(struct fb_info *info, int user) | |
502 | { | |
503 | struct s3fb_info *par = info->par; | |
504 | ||
505 | mutex_lock(&(par->open_lock)); | |
506 | if (par->ref_count == 0) { | |
3ff259f2 DM |
507 | void __iomem *vgabase = par->state.vgabase; |
508 | ||
a268422d | 509 | memset(&(par->state), 0, sizeof(struct vgastate)); |
3ff259f2 | 510 | par->state.vgabase = vgabase; |
a268422d OZ |
511 | par->state.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS | VGA_SAVE_CMAP; |
512 | par->state.num_crtc = 0x70; | |
513 | par->state.num_seq = 0x20; | |
514 | save_vga(&(par->state)); | |
515 | } | |
516 | ||
517 | par->ref_count++; | |
518 | mutex_unlock(&(par->open_lock)); | |
519 | ||
520 | return 0; | |
521 | } | |
522 | ||
523 | /* Close framebuffer */ | |
524 | ||
525 | static int s3fb_release(struct fb_info *info, int user) | |
526 | { | |
527 | struct s3fb_info *par = info->par; | |
528 | ||
529 | mutex_lock(&(par->open_lock)); | |
530 | if (par->ref_count == 0) { | |
531 | mutex_unlock(&(par->open_lock)); | |
532 | return -EINVAL; | |
533 | } | |
534 | ||
535 | if (par->ref_count == 1) | |
536 | restore_vga(&(par->state)); | |
537 | ||
538 | par->ref_count--; | |
539 | mutex_unlock(&(par->open_lock)); | |
540 | ||
541 | return 0; | |
542 | } | |
543 | ||
544 | /* Validate passed in var */ | |
545 | ||
546 | static int s3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | |
547 | { | |
548 | struct s3fb_info *par = info->par; | |
549 | int rv, mem, step; | |
c3ca34f9 | 550 | u16 m, n, r; |
a268422d OZ |
551 | |
552 | /* Find appropriate format */ | |
553 | rv = svga_match_format (s3fb_formats, var, NULL); | |
d4b766a0 OZ |
554 | |
555 | /* 32bpp mode is not supported on VIRGE VX, | |
556 | 24bpp is not supported on others */ | |
557 | if ((par->chip == CHIP_988_VIRGE_VX) ? (rv == 7) : (rv == 6)) | |
558 | rv = -EINVAL; | |
559 | ||
560 | if (rv < 0) { | |
31b6780c | 561 | fb_err(info, "unsupported mode requested\n"); |
a268422d OZ |
562 | return rv; |
563 | } | |
564 | ||
565 | /* Do not allow to have real resoulution larger than virtual */ | |
566 | if (var->xres > var->xres_virtual) | |
567 | var->xres_virtual = var->xres; | |
568 | ||
569 | if (var->yres > var->yres_virtual) | |
570 | var->yres_virtual = var->yres; | |
571 | ||
572 | /* Round up xres_virtual to have proper alignment of lines */ | |
573 | step = s3fb_formats[rv].xresstep - 1; | |
574 | var->xres_virtual = (var->xres_virtual+step) & ~step; | |
575 | ||
576 | /* Check whether have enough memory */ | |
577 | mem = ((var->bits_per_pixel * var->xres_virtual) >> 3) * var->yres_virtual; | |
c3ca34f9 | 578 | if (mem > info->screen_size) { |
31b6780c JP |
579 | fb_err(info, "not enough framebuffer memory (%d kB requested , %u kB available)\n", |
580 | mem >> 10, (unsigned int) (info->screen_size >> 10)); | |
a268422d OZ |
581 | return -EINVAL; |
582 | } | |
583 | ||
584 | rv = svga_check_timings (&s3_timing_regs, var, info->node); | |
c3ca34f9 | 585 | if (rv < 0) { |
31b6780c | 586 | fb_err(info, "invalid timings requested\n"); |
a268422d OZ |
587 | return rv; |
588 | } | |
589 | ||
c3ca34f9 KH |
590 | rv = svga_compute_pll(&s3_pll, PICOS2KHZ(var->pixclock), &m, &n, &r, |
591 | info->node); | |
592 | if (rv < 0) { | |
31b6780c | 593 | fb_err(info, "invalid pixclock value requested\n"); |
c3ca34f9 KH |
594 | return rv; |
595 | } | |
596 | ||
a268422d OZ |
597 | return 0; |
598 | } | |
599 | ||
600 | /* Set video mode from par */ | |
601 | ||
602 | static int s3fb_set_par(struct fb_info *info) | |
603 | { | |
604 | struct s3fb_info *par = info->par; | |
9966c4fe | 605 | u32 value, mode, hmul, offset_value, screen_size, multiplex, dbytes; |
a268422d | 606 | u32 bpp = info->var.bits_per_pixel; |
cb11c048 | 607 | u32 htotal, hsstart; |
a268422d OZ |
608 | |
609 | if (bpp != 0) { | |
610 | info->fix.ypanstep = 1; | |
611 | info->fix.line_length = (info->var.xres_virtual * bpp) / 8; | |
612 | ||
613 | info->flags &= ~FBINFO_MISC_TILEBLITTING; | |
614 | info->tileops = NULL; | |
615 | ||
34ed25f5 OZ |
616 | /* in 4bpp supports 8p wide tiles only, any tiles otherwise */ |
617 | info->pixmap.blit_x = (bpp == 4) ? (1 << (8 - 1)) : (~(u32)0); | |
8db51668 | 618 | info->pixmap.blit_y = ~(u32)0; |
34ed25f5 | 619 | |
a268422d OZ |
620 | offset_value = (info->var.xres_virtual * bpp) / 64; |
621 | screen_size = info->var.yres_virtual * info->fix.line_length; | |
622 | } else { | |
623 | info->fix.ypanstep = 16; | |
624 | info->fix.line_length = 0; | |
625 | ||
626 | info->flags |= FBINFO_MISC_TILEBLITTING; | |
627 | info->tileops = fasttext ? &s3fb_fast_tile_ops : &s3fb_tile_ops; | |
34ed25f5 | 628 | |
8db51668 AD |
629 | /* supports 8x16 tiles only */ |
630 | info->pixmap.blit_x = 1 << (8 - 1); | |
631 | info->pixmap.blit_y = 1 << (16 - 1); | |
a268422d OZ |
632 | |
633 | offset_value = info->var.xres_virtual / 16; | |
634 | screen_size = (info->var.xres_virtual * info->var.yres_virtual) / 64; | |
635 | } | |
636 | ||
637 | info->var.xoffset = 0; | |
638 | info->var.yoffset = 0; | |
639 | info->var.activate = FB_ACTIVATE_NOW; | |
640 | ||
641 | /* Unlock registers */ | |
f8645933 DM |
642 | vga_wcrt(par->state.vgabase, 0x38, 0x48); |
643 | vga_wcrt(par->state.vgabase, 0x39, 0xA5); | |
644 | vga_wseq(par->state.vgabase, 0x08, 0x06); | |
ea770789 | 645 | svga_wcrt_mask(par->state.vgabase, 0x11, 0x00, 0x80); |
a268422d OZ |
646 | |
647 | /* Blank screen and turn off sync */ | |
d907ec04 | 648 | svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); |
ea770789 | 649 | svga_wcrt_mask(par->state.vgabase, 0x17, 0x00, 0x80); |
a268422d OZ |
650 | |
651 | /* Set default values */ | |
e2fade2c | 652 | svga_set_default_gfx_regs(par->state.vgabase); |
f51a14dd | 653 | svga_set_default_atc_regs(par->state.vgabase); |
a4ade839 | 654 | svga_set_default_seq_regs(par->state.vgabase); |
1d28fcad | 655 | svga_set_default_crt_regs(par->state.vgabase); |
21da386d DM |
656 | svga_wcrt_multi(par->state.vgabase, s3_line_compare_regs, 0xFFFFFFFF); |
657 | svga_wcrt_multi(par->state.vgabase, s3_start_address_regs, 0); | |
a268422d OZ |
658 | |
659 | /* S3 specific initialization */ | |
ea770789 DM |
660 | svga_wcrt_mask(par->state.vgabase, 0x58, 0x10, 0x10); /* enable linear framebuffer */ |
661 | svga_wcrt_mask(par->state.vgabase, 0x31, 0x08, 0x08); /* enable sequencer access to framebuffer above 256 kB */ | |
a268422d | 662 | |
ea770789 DM |
663 | /* svga_wcrt_mask(par->state.vgabase, 0x33, 0x08, 0x08); */ /* DDR ? */ |
664 | /* svga_wcrt_mask(par->state.vgabase, 0x43, 0x01, 0x01); */ /* DDR ? */ | |
665 | svga_wcrt_mask(par->state.vgabase, 0x33, 0x00, 0x08); /* no DDR ? */ | |
666 | svga_wcrt_mask(par->state.vgabase, 0x43, 0x00, 0x01); /* no DDR ? */ | |
a268422d | 667 | |
ea770789 | 668 | svga_wcrt_mask(par->state.vgabase, 0x5D, 0x00, 0x28); /* Clear strange HSlen bits */ |
a268422d | 669 | |
ea770789 | 670 | /* svga_wcrt_mask(par->state.vgabase, 0x58, 0x03, 0x03); */ |
a268422d | 671 | |
ea770789 DM |
672 | /* svga_wcrt_mask(par->state.vgabase, 0x53, 0x12, 0x13); */ /* enable MMIO */ |
673 | /* svga_wcrt_mask(par->state.vgabase, 0x40, 0x08, 0x08); */ /* enable write buffer */ | |
a268422d OZ |
674 | |
675 | ||
676 | /* Set the offset register */ | |
31b6780c | 677 | fb_dbg(info, "offset register : %d\n", offset_value); |
21da386d | 678 | svga_wcrt_multi(par->state.vgabase, s3_offset_regs, offset_value); |
a268422d | 679 | |
94e948e6 OZ |
680 | if (par->chip != CHIP_357_VIRGE_GX2 && |
681 | par->chip != CHIP_359_VIRGE_GX2P && | |
682 | par->chip != CHIP_360_TRIO3D_1X && | |
9966c4fe | 683 | par->chip != CHIP_362_TRIO3D_2X && |
6fcdbc0c OZ |
684 | par->chip != CHIP_368_TRIO3D_2X && |
685 | par->chip != CHIP_260_VIRGE_MX) { | |
f8645933 DM |
686 | vga_wcrt(par->state.vgabase, 0x54, 0x18); /* M parameter */ |
687 | vga_wcrt(par->state.vgabase, 0x60, 0xff); /* N parameter */ | |
688 | vga_wcrt(par->state.vgabase, 0x61, 0xff); /* L parameter */ | |
689 | vga_wcrt(par->state.vgabase, 0x62, 0xff); /* L parameter */ | |
9966c4fe | 690 | } |
a268422d | 691 | |
f8645933 | 692 | vga_wcrt(par->state.vgabase, 0x3A, 0x35); |
f6b0cc47 | 693 | svga_wattr(par->state.vgabase, 0x33, 0x00); |
a268422d OZ |
694 | |
695 | if (info->var.vmode & FB_VMODE_DOUBLE) | |
ea770789 | 696 | svga_wcrt_mask(par->state.vgabase, 0x09, 0x80, 0x80); |
a268422d | 697 | else |
ea770789 | 698 | svga_wcrt_mask(par->state.vgabase, 0x09, 0x00, 0x80); |
a268422d OZ |
699 | |
700 | if (info->var.vmode & FB_VMODE_INTERLACED) | |
ea770789 | 701 | svga_wcrt_mask(par->state.vgabase, 0x42, 0x20, 0x20); |
a268422d | 702 | else |
ea770789 | 703 | svga_wcrt_mask(par->state.vgabase, 0x42, 0x00, 0x20); |
a268422d OZ |
704 | |
705 | /* Disable hardware graphics cursor */ | |
ea770789 | 706 | svga_wcrt_mask(par->state.vgabase, 0x45, 0x00, 0x01); |
a268422d | 707 | /* Disable Streams engine */ |
ea770789 | 708 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0x0C); |
a268422d OZ |
709 | |
710 | mode = svga_match_format(s3fb_formats, &(info->var), &(info->fix)); | |
711 | ||
712 | /* S3 virge DX hack */ | |
713 | if (par->chip == CHIP_375_VIRGE_DX) { | |
f8645933 DM |
714 | vga_wcrt(par->state.vgabase, 0x86, 0x80); |
715 | vga_wcrt(par->state.vgabase, 0x90, 0x00); | |
a268422d OZ |
716 | } |
717 | ||
718 | /* S3 virge VX hack */ | |
719 | if (par->chip == CHIP_988_VIRGE_VX) { | |
f8645933 DM |
720 | vga_wcrt(par->state.vgabase, 0x50, 0x00); |
721 | vga_wcrt(par->state.vgabase, 0x67, 0x50); | |
66cde97d | 722 | msleep(10); /* screen remains blank sometimes without this */ |
f8645933 DM |
723 | vga_wcrt(par->state.vgabase, 0x63, (mode <= 2) ? 0x90 : 0x09); |
724 | vga_wcrt(par->state.vgabase, 0x66, 0x90); | |
a268422d OZ |
725 | } |
726 | ||
94e948e6 OZ |
727 | if (par->chip == CHIP_357_VIRGE_GX2 || |
728 | par->chip == CHIP_359_VIRGE_GX2P || | |
729 | par->chip == CHIP_360_TRIO3D_1X || | |
9966c4fe | 730 | par->chip == CHIP_362_TRIO3D_2X || |
5694f9ce | 731 | par->chip == CHIP_368_TRIO3D_2X || |
cb11c048 OZ |
732 | par->chip == CHIP_365_TRIO3D || |
733 | par->chip == CHIP_375_VIRGE_DX || | |
6fcdbc0c OZ |
734 | par->chip == CHIP_385_VIRGE_GX || |
735 | par->chip == CHIP_260_VIRGE_MX) { | |
9966c4fe | 736 | dbytes = info->var.xres * ((bpp+7)/8); |
f8645933 DM |
737 | vga_wcrt(par->state.vgabase, 0x91, (dbytes + 7) / 8); |
738 | vga_wcrt(par->state.vgabase, 0x90, (((dbytes + 7) / 8) >> 8) | 0x80); | |
9966c4fe | 739 | |
f8645933 | 740 | vga_wcrt(par->state.vgabase, 0x66, 0x81); |
9966c4fe OZ |
741 | } |
742 | ||
94e948e6 | 743 | if (par->chip == CHIP_357_VIRGE_GX2 || |
cb11c048 OZ |
744 | par->chip == CHIP_359_VIRGE_GX2P || |
745 | par->chip == CHIP_360_TRIO3D_1X || | |
746 | par->chip == CHIP_362_TRIO3D_2X || | |
6fcdbc0c OZ |
747 | par->chip == CHIP_368_TRIO3D_2X || |
748 | par->chip == CHIP_260_VIRGE_MX) | |
cb11c048 OZ |
749 | vga_wcrt(par->state.vgabase, 0x34, 0x00); |
750 | else /* enable Data Transfer Position Control (DTPC) */ | |
751 | vga_wcrt(par->state.vgabase, 0x34, 0x10); | |
752 | ||
ea770789 | 753 | svga_wcrt_mask(par->state.vgabase, 0x31, 0x00, 0x40); |
a268422d OZ |
754 | multiplex = 0; |
755 | hmul = 1; | |
756 | ||
757 | /* Set mode-specific register values */ | |
758 | switch (mode) { | |
759 | case 0: | |
31b6780c | 760 | fb_dbg(info, "text mode\n"); |
9c96394b | 761 | svga_set_textmode_vga_regs(par->state.vgabase); |
a268422d OZ |
762 | |
763 | /* Set additional registers like in 8-bit mode */ | |
ea770789 DM |
764 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); |
765 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0); | |
a268422d OZ |
766 | |
767 | /* Disable enhanced mode */ | |
ea770789 | 768 | svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30); |
a268422d OZ |
769 | |
770 | if (fasttext) { | |
31b6780c | 771 | fb_dbg(info, "high speed text mode set\n"); |
ea770789 | 772 | svga_wcrt_mask(par->state.vgabase, 0x31, 0x40, 0x40); |
a268422d OZ |
773 | } |
774 | break; | |
775 | case 1: | |
31b6780c | 776 | fb_dbg(info, "4 bit pseudocolor\n"); |
f8645933 | 777 | vga_wgfx(par->state.vgabase, VGA_GFX_MODE, 0x40); |
a268422d OZ |
778 | |
779 | /* Set additional registers like in 8-bit mode */ | |
ea770789 DM |
780 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); |
781 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0); | |
a268422d OZ |
782 | |
783 | /* disable enhanced mode */ | |
ea770789 | 784 | svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30); |
a268422d OZ |
785 | break; |
786 | case 2: | |
31b6780c | 787 | fb_dbg(info, "4 bit pseudocolor, planar\n"); |
a268422d OZ |
788 | |
789 | /* Set additional registers like in 8-bit mode */ | |
ea770789 DM |
790 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); |
791 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0); | |
a268422d OZ |
792 | |
793 | /* disable enhanced mode */ | |
ea770789 | 794 | svga_wcrt_mask(par->state.vgabase, 0x3A, 0x00, 0x30); |
a268422d OZ |
795 | break; |
796 | case 3: | |
31b6780c | 797 | fb_dbg(info, "8 bit pseudocolor\n"); |
ea770789 | 798 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x00, 0x30); |
9966c4fe | 799 | if (info->var.pixclock > 20000 || |
94e948e6 OZ |
800 | par->chip == CHIP_357_VIRGE_GX2 || |
801 | par->chip == CHIP_359_VIRGE_GX2P || | |
9966c4fe OZ |
802 | par->chip == CHIP_360_TRIO3D_1X || |
803 | par->chip == CHIP_362_TRIO3D_2X || | |
6fcdbc0c OZ |
804 | par->chip == CHIP_368_TRIO3D_2X || |
805 | par->chip == CHIP_260_VIRGE_MX) | |
ea770789 | 806 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0); |
9966c4fe | 807 | else { |
ea770789 | 808 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x10, 0xF0); |
a268422d OZ |
809 | multiplex = 1; |
810 | } | |
811 | break; | |
812 | case 4: | |
31b6780c | 813 | fb_dbg(info, "5/5/5 truecolor\n"); |
a268422d OZ |
814 | if (par->chip == CHIP_988_VIRGE_VX) { |
815 | if (info->var.pixclock > 20000) | |
ea770789 | 816 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x20, 0xF0); |
a268422d | 817 | else |
ea770789 | 818 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x30, 0xF0); |
3827d10e OZ |
819 | } else if (par->chip == CHIP_365_TRIO3D) { |
820 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x10, 0x30); | |
821 | if (info->var.pixclock > 8695) { | |
822 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x30, 0xF0); | |
823 | hmul = 2; | |
824 | } else { | |
825 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x20, 0xF0); | |
826 | multiplex = 1; | |
827 | } | |
a268422d | 828 | } else { |
ea770789 DM |
829 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x10, 0x30); |
830 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x30, 0xF0); | |
94e948e6 OZ |
831 | if (par->chip != CHIP_357_VIRGE_GX2 && |
832 | par->chip != CHIP_359_VIRGE_GX2P && | |
833 | par->chip != CHIP_360_TRIO3D_1X && | |
9966c4fe | 834 | par->chip != CHIP_362_TRIO3D_2X && |
6fcdbc0c OZ |
835 | par->chip != CHIP_368_TRIO3D_2X && |
836 | par->chip != CHIP_260_VIRGE_MX) | |
9966c4fe | 837 | hmul = 2; |
a268422d OZ |
838 | } |
839 | break; | |
840 | case 5: | |
31b6780c | 841 | fb_dbg(info, "5/6/5 truecolor\n"); |
a268422d OZ |
842 | if (par->chip == CHIP_988_VIRGE_VX) { |
843 | if (info->var.pixclock > 20000) | |
ea770789 | 844 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x40, 0xF0); |
a268422d | 845 | else |
ea770789 | 846 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x50, 0xF0); |
3827d10e OZ |
847 | } else if (par->chip == CHIP_365_TRIO3D) { |
848 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x10, 0x30); | |
849 | if (info->var.pixclock > 8695) { | |
850 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x50, 0xF0); | |
851 | hmul = 2; | |
852 | } else { | |
853 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x40, 0xF0); | |
854 | multiplex = 1; | |
855 | } | |
a268422d | 856 | } else { |
ea770789 DM |
857 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x10, 0x30); |
858 | svga_wcrt_mask(par->state.vgabase, 0x67, 0x50, 0xF0); | |
94e948e6 OZ |
859 | if (par->chip != CHIP_357_VIRGE_GX2 && |
860 | par->chip != CHIP_359_VIRGE_GX2P && | |
861 | par->chip != CHIP_360_TRIO3D_1X && | |
9966c4fe | 862 | par->chip != CHIP_362_TRIO3D_2X && |
6fcdbc0c OZ |
863 | par->chip != CHIP_368_TRIO3D_2X && |
864 | par->chip != CHIP_260_VIRGE_MX) | |
9966c4fe | 865 | hmul = 2; |
a268422d OZ |
866 | } |
867 | break; | |
868 | case 6: | |
869 | /* VIRGE VX case */ | |
31b6780c | 870 | fb_dbg(info, "8/8/8 truecolor\n"); |
ea770789 | 871 | svga_wcrt_mask(par->state.vgabase, 0x67, 0xD0, 0xF0); |
a268422d OZ |
872 | break; |
873 | case 7: | |
31b6780c | 874 | fb_dbg(info, "8/8/8/8 truecolor\n"); |
ea770789 DM |
875 | svga_wcrt_mask(par->state.vgabase, 0x50, 0x30, 0x30); |
876 | svga_wcrt_mask(par->state.vgabase, 0x67, 0xD0, 0xF0); | |
a268422d OZ |
877 | break; |
878 | default: | |
31b6780c | 879 | fb_err(info, "unsupported mode - bug\n"); |
a268422d OZ |
880 | return -EINVAL; |
881 | } | |
882 | ||
883 | if (par->chip != CHIP_988_VIRGE_VX) { | |
d907ec04 DM |
884 | svga_wseq_mask(par->state.vgabase, 0x15, multiplex ? 0x10 : 0x00, 0x10); |
885 | svga_wseq_mask(par->state.vgabase, 0x18, multiplex ? 0x80 : 0x00, 0x80); | |
a268422d OZ |
886 | } |
887 | ||
888 | s3_set_pixclock(info, info->var.pixclock); | |
38d2620e | 889 | svga_set_timings(par->state.vgabase, &s3_timing_regs, &(info->var), hmul, 1, |
a268422d OZ |
890 | (info->var.vmode & FB_VMODE_DOUBLE) ? 2 : 1, |
891 | (info->var.vmode & FB_VMODE_INTERLACED) ? 2 : 1, | |
892 | hmul, info->node); | |
893 | ||
894 | /* Set interlaced mode start/end register */ | |
cb11c048 OZ |
895 | htotal = info->var.xres + info->var.left_margin + info->var.right_margin + info->var.hsync_len; |
896 | htotal = ((htotal * hmul) / 8) - 5; | |
897 | vga_wcrt(par->state.vgabase, 0x3C, (htotal + 1) / 2); | |
898 | ||
899 | /* Set Data Transfer Position */ | |
900 | hsstart = ((info->var.xres + info->var.right_margin) * hmul) / 8; | |
66cde97d OZ |
901 | /* + 2 is needed for Virge/VX, does no harm on other cards */ |
902 | value = clamp((htotal + hsstart + 1) / 2 + 2, hsstart + 4, htotal + 1); | |
cb11c048 | 903 | svga_wcrt_multi(par->state.vgabase, s3_dtpc_regs, value); |
a268422d | 904 | |
75814d87 | 905 | memset_io(info->screen_base, 0x00, screen_size); |
a268422d | 906 | /* Device and screen back on */ |
ea770789 | 907 | svga_wcrt_mask(par->state.vgabase, 0x17, 0x80, 0x80); |
d907ec04 | 908 | svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20); |
a268422d OZ |
909 | |
910 | return 0; | |
911 | } | |
912 | ||
913 | /* Set a colour register */ | |
914 | ||
915 | static int s3fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | |
916 | u_int transp, struct fb_info *fb) | |
917 | { | |
918 | switch (fb->var.bits_per_pixel) { | |
919 | case 0: | |
920 | case 4: | |
921 | if (regno >= 16) | |
922 | return -EINVAL; | |
923 | ||
924 | if ((fb->var.bits_per_pixel == 4) && | |
925 | (fb->var.nonstd == 0)) { | |
926 | outb(0xF0, VGA_PEL_MSK); | |
927 | outb(regno*16, VGA_PEL_IW); | |
928 | } else { | |
929 | outb(0x0F, VGA_PEL_MSK); | |
930 | outb(regno, VGA_PEL_IW); | |
931 | } | |
932 | outb(red >> 10, VGA_PEL_D); | |
933 | outb(green >> 10, VGA_PEL_D); | |
934 | outb(blue >> 10, VGA_PEL_D); | |
935 | break; | |
936 | case 8: | |
937 | if (regno >= 256) | |
938 | return -EINVAL; | |
939 | ||
940 | outb(0xFF, VGA_PEL_MSK); | |
941 | outb(regno, VGA_PEL_IW); | |
942 | outb(red >> 10, VGA_PEL_D); | |
943 | outb(green >> 10, VGA_PEL_D); | |
944 | outb(blue >> 10, VGA_PEL_D); | |
945 | break; | |
946 | case 16: | |
947 | if (regno >= 16) | |
249bdbbf | 948 | return 0; |
a268422d OZ |
949 | |
950 | if (fb->var.green.length == 5) | |
951 | ((u32*)fb->pseudo_palette)[regno] = ((red & 0xF800) >> 1) | | |
952 | ((green & 0xF800) >> 6) | ((blue & 0xF800) >> 11); | |
953 | else if (fb->var.green.length == 6) | |
954 | ((u32*)fb->pseudo_palette)[regno] = (red & 0xF800) | | |
955 | ((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11); | |
956 | else return -EINVAL; | |
957 | break; | |
958 | case 24: | |
959 | case 32: | |
960 | if (regno >= 16) | |
249bdbbf | 961 | return 0; |
a268422d | 962 | |
249bdbbf | 963 | ((u32*)fb->pseudo_palette)[regno] = ((red & 0xFF00) << 8) | |
a268422d OZ |
964 | (green & 0xFF00) | ((blue & 0xFF00) >> 8); |
965 | break; | |
966 | default: | |
967 | return -EINVAL; | |
968 | } | |
969 | ||
970 | return 0; | |
971 | } | |
972 | ||
973 | ||
974 | /* Set the display blanking state */ | |
975 | ||
976 | static int s3fb_blank(int blank_mode, struct fb_info *info) | |
977 | { | |
d907ec04 DM |
978 | struct s3fb_info *par = info->par; |
979 | ||
a268422d OZ |
980 | switch (blank_mode) { |
981 | case FB_BLANK_UNBLANK: | |
31b6780c | 982 | fb_dbg(info, "unblank\n"); |
ea770789 | 983 | svga_wcrt_mask(par->state.vgabase, 0x56, 0x00, 0x06); |
d907ec04 | 984 | svga_wseq_mask(par->state.vgabase, 0x01, 0x00, 0x20); |
a268422d OZ |
985 | break; |
986 | case FB_BLANK_NORMAL: | |
31b6780c | 987 | fb_dbg(info, "blank\n"); |
ea770789 | 988 | svga_wcrt_mask(par->state.vgabase, 0x56, 0x00, 0x06); |
d907ec04 | 989 | svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); |
a268422d OZ |
990 | break; |
991 | case FB_BLANK_HSYNC_SUSPEND: | |
31b6780c | 992 | fb_dbg(info, "hsync\n"); |
ea770789 | 993 | svga_wcrt_mask(par->state.vgabase, 0x56, 0x02, 0x06); |
d907ec04 | 994 | svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); |
a268422d OZ |
995 | break; |
996 | case FB_BLANK_VSYNC_SUSPEND: | |
31b6780c | 997 | fb_dbg(info, "vsync\n"); |
ea770789 | 998 | svga_wcrt_mask(par->state.vgabase, 0x56, 0x04, 0x06); |
d907ec04 | 999 | svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); |
a268422d OZ |
1000 | break; |
1001 | case FB_BLANK_POWERDOWN: | |
31b6780c | 1002 | fb_dbg(info, "sync down\n"); |
ea770789 | 1003 | svga_wcrt_mask(par->state.vgabase, 0x56, 0x06, 0x06); |
d907ec04 | 1004 | svga_wseq_mask(par->state.vgabase, 0x01, 0x20, 0x20); |
a268422d OZ |
1005 | break; |
1006 | } | |
1007 | ||
1008 | return 0; | |
1009 | } | |
1010 | ||
1011 | ||
1012 | /* Pan the display */ | |
1013 | ||
21da386d DM |
1014 | static int s3fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) |
1015 | { | |
1016 | struct s3fb_info *par = info->par; | |
a268422d OZ |
1017 | unsigned int offset; |
1018 | ||
a268422d | 1019 | /* Calculate the offset */ |
a4aadc93 LP |
1020 | if (info->var.bits_per_pixel == 0) { |
1021 | offset = (var->yoffset / 16) * (info->var.xres_virtual / 2) | |
1022 | + (var->xoffset / 2); | |
a268422d OZ |
1023 | offset = offset >> 2; |
1024 | } else { | |
1025 | offset = (var->yoffset * info->fix.line_length) + | |
a4aadc93 | 1026 | (var->xoffset * info->var.bits_per_pixel / 8); |
a268422d OZ |
1027 | offset = offset >> 2; |
1028 | } | |
1029 | ||
1030 | /* Set the offset */ | |
21da386d | 1031 | svga_wcrt_multi(par->state.vgabase, s3_start_address_regs, offset); |
a268422d OZ |
1032 | |
1033 | return 0; | |
1034 | } | |
1035 | ||
1036 | /* ------------------------------------------------------------------------- */ | |
1037 | ||
1038 | /* Frame buffer operations */ | |
1039 | ||
1040 | static struct fb_ops s3fb_ops = { | |
1041 | .owner = THIS_MODULE, | |
1042 | .fb_open = s3fb_open, | |
1043 | .fb_release = s3fb_release, | |
1044 | .fb_check_var = s3fb_check_var, | |
1045 | .fb_set_par = s3fb_set_par, | |
1046 | .fb_setcolreg = s3fb_setcolreg, | |
1047 | .fb_blank = s3fb_blank, | |
1048 | .fb_pan_display = s3fb_pan_display, | |
1049 | .fb_fillrect = s3fb_fillrect, | |
1050 | .fb_copyarea = cfb_copyarea, | |
1051 | .fb_imageblit = s3fb_imageblit, | |
5a87ede9 | 1052 | .fb_get_caps = svga_get_caps, |
a268422d OZ |
1053 | }; |
1054 | ||
1055 | /* ------------------------------------------------------------------------- */ | |
1056 | ||
48c68c4f | 1057 | static int s3_identification(struct s3fb_info *par) |
a268422d | 1058 | { |
f8645933 DM |
1059 | int chip = par->chip; |
1060 | ||
a268422d | 1061 | if (chip == CHIP_XXX_TRIO) { |
f8645933 DM |
1062 | u8 cr30 = vga_rcrt(par->state.vgabase, 0x30); |
1063 | u8 cr2e = vga_rcrt(par->state.vgabase, 0x2e); | |
1064 | u8 cr2f = vga_rcrt(par->state.vgabase, 0x2f); | |
a268422d OZ |
1065 | |
1066 | if ((cr30 == 0xE0) || (cr30 == 0xE1)) { | |
1067 | if (cr2e == 0x10) | |
1068 | return CHIP_732_TRIO32; | |
1069 | if (cr2e == 0x11) { | |
1070 | if (! (cr2f & 0x40)) | |
1071 | return CHIP_764_TRIO64; | |
1072 | else | |
1073 | return CHIP_765_TRIO64VP; | |
1074 | } | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | if (chip == CHIP_XXX_TRIO64V2_DXGX) { | |
f8645933 | 1079 | u8 cr6f = vga_rcrt(par->state.vgabase, 0x6f); |
a268422d OZ |
1080 | |
1081 | if (! (cr6f & 0x01)) | |
1082 | return CHIP_775_TRIO64V2_DX; | |
1083 | else | |
1084 | return CHIP_785_TRIO64V2_GX; | |
1085 | } | |
1086 | ||
1087 | if (chip == CHIP_XXX_VIRGE_DXGX) { | |
f8645933 | 1088 | u8 cr6f = vga_rcrt(par->state.vgabase, 0x6f); |
a268422d OZ |
1089 | |
1090 | if (! (cr6f & 0x01)) | |
1091 | return CHIP_375_VIRGE_DX; | |
1092 | else | |
1093 | return CHIP_385_VIRGE_GX; | |
1094 | } | |
1095 | ||
9966c4fe | 1096 | if (chip == CHIP_36X_TRIO3D_1X_2X) { |
f8645933 | 1097 | switch (vga_rcrt(par->state.vgabase, 0x2f)) { |
9966c4fe OZ |
1098 | case 0x00: |
1099 | return CHIP_360_TRIO3D_1X; | |
1100 | case 0x01: | |
1101 | return CHIP_362_TRIO3D_2X; | |
1102 | case 0x02: | |
1103 | return CHIP_368_TRIO3D_2X; | |
1104 | } | |
1105 | } | |
1106 | ||
a268422d OZ |
1107 | return CHIP_UNKNOWN; |
1108 | } | |
1109 | ||
1110 | ||
1111 | /* PCI probe */ | |
1112 | ||
48c68c4f | 1113 | static int s3_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) |
a268422d | 1114 | { |
94c322c3 DM |
1115 | struct pci_bus_region bus_reg; |
1116 | struct resource vga_res; | |
a268422d OZ |
1117 | struct fb_info *info; |
1118 | struct s3fb_info *par; | |
1119 | int rc; | |
1120 | u8 regval, cr38, cr39; | |
86c0f043 | 1121 | bool found = false; |
a268422d OZ |
1122 | |
1123 | /* Ignore secondary VGA device because there is no VGA arbitration */ | |
1124 | if (! svga_primary_device(dev)) { | |
1125 | dev_info(&(dev->dev), "ignoring secondary device\n"); | |
1126 | return -ENODEV; | |
1127 | } | |
1128 | ||
1129 | /* Allocate and fill driver data structure */ | |
20e061fb | 1130 | info = framebuffer_alloc(sizeof(struct s3fb_info), &(dev->dev)); |
a268422d OZ |
1131 | if (!info) { |
1132 | dev_err(&(dev->dev), "cannot allocate memory\n"); | |
1133 | return -ENOMEM; | |
1134 | } | |
1135 | ||
1136 | par = info->par; | |
1137 | mutex_init(&par->open_lock); | |
1138 | ||
1139 | info->flags = FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN; | |
1140 | info->fbops = &s3fb_ops; | |
1141 | ||
1142 | /* Prepare PCI device */ | |
1143 | rc = pci_enable_device(dev); | |
1144 | if (rc < 0) { | |
594a8819 | 1145 | dev_err(info->device, "cannot enable PCI device\n"); |
a268422d OZ |
1146 | goto err_enable_device; |
1147 | } | |
1148 | ||
1149 | rc = pci_request_regions(dev, "s3fb"); | |
1150 | if (rc < 0) { | |
594a8819 | 1151 | dev_err(info->device, "cannot reserve framebuffer region\n"); |
a268422d OZ |
1152 | goto err_request_regions; |
1153 | } | |
1154 | ||
1155 | ||
1156 | info->fix.smem_start = pci_resource_start(dev, 0); | |
1157 | info->fix.smem_len = pci_resource_len(dev, 0); | |
1158 | ||
1159 | /* Map physical IO memory address into kernel space */ | |
4edcd2ab | 1160 | info->screen_base = pci_iomap_wc(dev, 0, 0); |
a268422d OZ |
1161 | if (! info->screen_base) { |
1162 | rc = -ENOMEM; | |
594a8819 | 1163 | dev_err(info->device, "iomap for framebuffer failed\n"); |
a268422d OZ |
1164 | goto err_iomap; |
1165 | } | |
1166 | ||
94c322c3 DM |
1167 | bus_reg.start = 0; |
1168 | bus_reg.end = 64 * 1024; | |
1169 | ||
1170 | vga_res.flags = IORESOURCE_IO; | |
1171 | ||
fc279850 | 1172 | pcibios_bus_to_resource(dev->bus, &vga_res, &bus_reg); |
94c322c3 | 1173 | |
c051af31 | 1174 | par->state.vgabase = (void __iomem *) (unsigned long) vga_res.start; |
94c322c3 | 1175 | |
a268422d | 1176 | /* Unlock regs */ |
f8645933 DM |
1177 | cr38 = vga_rcrt(par->state.vgabase, 0x38); |
1178 | cr39 = vga_rcrt(par->state.vgabase, 0x39); | |
1179 | vga_wseq(par->state.vgabase, 0x08, 0x06); | |
1180 | vga_wcrt(par->state.vgabase, 0x38, 0x48); | |
1181 | vga_wcrt(par->state.vgabase, 0x39, 0xA5); | |
a268422d | 1182 | |
9966c4fe | 1183 | /* Identify chip type */ |
a268422d | 1184 | par->chip = id->driver_data & CHIP_MASK; |
f8645933 | 1185 | par->rev = vga_rcrt(par->state.vgabase, 0x2f); |
a268422d | 1186 | if (par->chip & CHIP_UNDECIDED_FLAG) |
f8645933 | 1187 | par->chip = s3_identification(par); |
a268422d | 1188 | |
9966c4fe OZ |
1189 | /* Find how many physical memory there is on card */ |
1190 | /* 0x36 register is accessible even if other registers are locked */ | |
f8645933 | 1191 | regval = vga_rcrt(par->state.vgabase, 0x36); |
9966c4fe OZ |
1192 | if (par->chip == CHIP_360_TRIO3D_1X || |
1193 | par->chip == CHIP_362_TRIO3D_2X || | |
5694f9ce OZ |
1194 | par->chip == CHIP_368_TRIO3D_2X || |
1195 | par->chip == CHIP_365_TRIO3D) { | |
9966c4fe OZ |
1196 | switch ((regval & 0xE0) >> 5) { |
1197 | case 0: /* 8MB -- only 4MB usable for display */ | |
1198 | case 1: /* 4MB with 32-bit bus */ | |
1199 | case 2: /* 4MB */ | |
1200 | info->screen_size = 4 << 20; | |
1201 | break; | |
5694f9ce | 1202 | case 4: /* 2MB on 365 Trio3D */ |
9966c4fe OZ |
1203 | case 6: /* 2MB */ |
1204 | info->screen_size = 2 << 20; | |
1205 | break; | |
1206 | } | |
94e948e6 | 1207 | } else if (par->chip == CHIP_357_VIRGE_GX2 || |
6fcdbc0c OZ |
1208 | par->chip == CHIP_359_VIRGE_GX2P || |
1209 | par->chip == CHIP_260_VIRGE_MX) { | |
94e948e6 OZ |
1210 | switch ((regval & 0xC0) >> 6) { |
1211 | case 1: /* 4MB */ | |
1212 | info->screen_size = 4 << 20; | |
1213 | break; | |
1214 | case 3: /* 2MB */ | |
1215 | info->screen_size = 2 << 20; | |
1216 | break; | |
1217 | } | |
66cde97d OZ |
1218 | } else if (par->chip == CHIP_988_VIRGE_VX) { |
1219 | switch ((regval & 0x60) >> 5) { | |
1220 | case 0: /* 2MB */ | |
1221 | info->screen_size = 2 << 20; | |
1222 | break; | |
1223 | case 1: /* 4MB */ | |
1224 | info->screen_size = 4 << 20; | |
1225 | break; | |
1226 | case 2: /* 6MB */ | |
1227 | info->screen_size = 6 << 20; | |
1228 | break; | |
1229 | case 3: /* 8MB */ | |
1230 | info->screen_size = 8 << 20; | |
1231 | break; | |
1232 | } | |
1233 | /* off-screen memory */ | |
1234 | regval = vga_rcrt(par->state.vgabase, 0x37); | |
1235 | switch ((regval & 0x60) >> 5) { | |
1236 | case 1: /* 4MB */ | |
1237 | info->screen_size -= 4 << 20; | |
1238 | break; | |
1239 | case 2: /* 2MB */ | |
1240 | info->screen_size -= 2 << 20; | |
1241 | break; | |
1242 | } | |
9966c4fe OZ |
1243 | } else |
1244 | info->screen_size = s3_memsizes[regval >> 5] << 10; | |
1245 | info->fix.smem_len = info->screen_size; | |
1246 | ||
a268422d | 1247 | /* Find MCLK frequency */ |
f8645933 DM |
1248 | regval = vga_rseq(par->state.vgabase, 0x10); |
1249 | par->mclk_freq = ((vga_rseq(par->state.vgabase, 0x11) + 2) * 14318) / ((regval & 0x1F) + 2); | |
a268422d OZ |
1250 | par->mclk_freq = par->mclk_freq >> (regval >> 5); |
1251 | ||
1252 | /* Restore locks */ | |
f8645933 DM |
1253 | vga_wcrt(par->state.vgabase, 0x38, cr38); |
1254 | vga_wcrt(par->state.vgabase, 0x39, cr39); | |
a268422d OZ |
1255 | |
1256 | strcpy(info->fix.id, s3_names [par->chip]); | |
1257 | info->fix.mmio_start = 0; | |
1258 | info->fix.mmio_len = 0; | |
1259 | info->fix.type = FB_TYPE_PACKED_PIXELS; | |
1260 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | |
1261 | info->fix.ypanstep = 0; | |
1262 | info->fix.accel = FB_ACCEL_NONE; | |
1263 | info->pseudo_palette = (void*) (par->pseudo_palette); | |
86c0f043 OZ |
1264 | info->var.bits_per_pixel = 8; |
1265 | ||
1266 | #ifdef CONFIG_FB_S3_DDC | |
1267 | /* Enable MMIO if needed */ | |
1268 | if (s3fb_ddc_needs_mmio(par->chip)) { | |
1269 | par->mmio = ioremap(info->fix.smem_start + MMIO_OFFSET, MMIO_SIZE); | |
1270 | if (par->mmio) | |
1271 | svga_wcrt_mask(par->state.vgabase, 0x53, 0x08, 0x08); /* enable MMIO */ | |
1272 | else | |
1273 | dev_err(info->device, "unable to map MMIO at 0x%lx, disabling DDC", | |
1274 | info->fix.smem_start + MMIO_OFFSET); | |
1275 | } | |
1276 | if (!s3fb_ddc_needs_mmio(par->chip) || par->mmio) | |
1277 | if (s3fb_setup_ddc_bus(info) == 0) { | |
1278 | u8 *edid = fb_ddc_read(&par->ddc_adapter); | |
1279 | par->ddc_registered = true; | |
1280 | if (edid) { | |
1281 | fb_edid_to_monspecs(edid, &info->monspecs); | |
1282 | kfree(edid); | |
1283 | if (!info->monspecs.modedb) | |
1284 | dev_err(info->device, "error getting mode database\n"); | |
1285 | else { | |
1286 | const struct fb_videomode *m; | |
1287 | ||
1288 | fb_videomode_to_modelist(info->monspecs.modedb, | |
1289 | info->monspecs.modedb_len, | |
1290 | &info->modelist); | |
1291 | m = fb_find_best_display(&info->monspecs, &info->modelist); | |
1292 | if (m) { | |
1293 | fb_videomode_to_var(&info->var, m); | |
1294 | /* fill all other info->var's fields */ | |
1295 | if (s3fb_check_var(&info->var, info) == 0) | |
1296 | found = true; | |
1297 | } | |
1298 | } | |
1299 | } | |
1300 | } | |
1301 | #endif | |
1302 | if (!mode_option && !found) | |
1303 | mode_option = "640x480-8@60"; | |
a268422d OZ |
1304 | |
1305 | /* Prepare startup mode */ | |
86c0f043 OZ |
1306 | if (mode_option) { |
1307 | rc = fb_find_mode(&info->var, info, mode_option, | |
1308 | info->monspecs.modedb, info->monspecs.modedb_len, | |
1309 | NULL, info->var.bits_per_pixel); | |
1310 | if (!rc || rc == 4) { | |
1311 | rc = -EINVAL; | |
1312 | dev_err(info->device, "mode %s not found\n", mode_option); | |
1313 | fb_destroy_modedb(info->monspecs.modedb); | |
1314 | info->monspecs.modedb = NULL; | |
1315 | goto err_find_mode; | |
1316 | } | |
1317 | } | |
1318 | ||
1319 | fb_destroy_modedb(info->monspecs.modedb); | |
1320 | info->monspecs.modedb = NULL; | |
1321 | ||
1322 | /* maximize virtual vertical size for fast scrolling */ | |
1323 | info->var.yres_virtual = info->fix.smem_len * 8 / | |
1324 | (info->var.bits_per_pixel * info->var.xres_virtual); | |
1325 | if (info->var.yres_virtual < info->var.yres) { | |
1326 | dev_err(info->device, "virtual vertical size smaller than real\n"); | |
a2a6fc5f | 1327 | rc = -EINVAL; |
99d054d8 OZ |
1328 | goto err_find_mode; |
1329 | } | |
1330 | ||
a268422d OZ |
1331 | rc = fb_alloc_cmap(&info->cmap, 256, 0); |
1332 | if (rc < 0) { | |
594a8819 | 1333 | dev_err(info->device, "cannot allocate colormap\n"); |
a268422d OZ |
1334 | goto err_alloc_cmap; |
1335 | } | |
1336 | ||
1337 | rc = register_framebuffer(info); | |
1338 | if (rc < 0) { | |
594a8819 | 1339 | dev_err(info->device, "cannot register framebuffer\n"); |
a268422d OZ |
1340 | goto err_reg_fb; |
1341 | } | |
1342 | ||
31b6780c JP |
1343 | fb_info(info, "%s on %s, %d MB RAM, %d MHz MCLK\n", |
1344 | info->fix.id, pci_name(dev), | |
1345 | info->fix.smem_len >> 20, (par->mclk_freq + 500) / 1000); | |
a268422d OZ |
1346 | |
1347 | if (par->chip == CHIP_UNKNOWN) | |
31b6780c JP |
1348 | fb_info(info, "unknown chip, CR2D=%x, CR2E=%x, CRT2F=%x, CRT30=%x\n", |
1349 | vga_rcrt(par->state.vgabase, 0x2d), | |
1350 | vga_rcrt(par->state.vgabase, 0x2e), | |
1351 | vga_rcrt(par->state.vgabase, 0x2f), | |
1352 | vga_rcrt(par->state.vgabase, 0x30)); | |
a268422d OZ |
1353 | |
1354 | /* Record a reference to the driver data */ | |
1355 | pci_set_drvdata(dev, info); | |
1356 | ||
4edcd2ab LR |
1357 | if (mtrr) |
1358 | par->wc_cookie = arch_phys_wc_add(info->fix.smem_start, | |
1359 | info->fix.smem_len); | |
a268422d OZ |
1360 | |
1361 | return 0; | |
1362 | ||
1363 | /* Error handling */ | |
1364 | err_reg_fb: | |
1365 | fb_dealloc_cmap(&info->cmap); | |
1366 | err_alloc_cmap: | |
1367 | err_find_mode: | |
86c0f043 OZ |
1368 | #ifdef CONFIG_FB_S3_DDC |
1369 | if (par->ddc_registered) | |
1370 | i2c_del_adapter(&par->ddc_adapter); | |
1371 | if (par->mmio) | |
1372 | iounmap(par->mmio); | |
1373 | #endif | |
a268422d OZ |
1374 | pci_iounmap(dev, info->screen_base); |
1375 | err_iomap: | |
1376 | pci_release_regions(dev); | |
1377 | err_request_regions: | |
1378 | /* pci_disable_device(dev); */ | |
1379 | err_enable_device: | |
1380 | framebuffer_release(info); | |
1381 | return rc; | |
1382 | } | |
1383 | ||
1384 | ||
1385 | /* PCI remove */ | |
1386 | ||
48c68c4f | 1387 | static void s3_pci_remove(struct pci_dev *dev) |
a268422d OZ |
1388 | { |
1389 | struct fb_info *info = pci_get_drvdata(dev); | |
0c641bff | 1390 | struct s3fb_info __maybe_unused *par; |
a268422d OZ |
1391 | |
1392 | if (info) { | |
0c641bff | 1393 | par = info->par; |
4edcd2ab | 1394 | arch_phys_wc_del(par->wc_cookie); |
a268422d OZ |
1395 | unregister_framebuffer(info); |
1396 | fb_dealloc_cmap(&info->cmap); | |
1397 | ||
86c0f043 OZ |
1398 | #ifdef CONFIG_FB_S3_DDC |
1399 | if (par->ddc_registered) | |
1400 | i2c_del_adapter(&par->ddc_adapter); | |
1401 | if (par->mmio) | |
1402 | iounmap(par->mmio); | |
1403 | #endif | |
1404 | ||
a268422d OZ |
1405 | pci_iounmap(dev, info->screen_base); |
1406 | pci_release_regions(dev); | |
1407 | /* pci_disable_device(dev); */ | |
1408 | ||
a268422d OZ |
1409 | framebuffer_release(info); |
1410 | } | |
1411 | } | |
1412 | ||
1413 | /* PCI suspend */ | |
1414 | ||
1415 | static int s3_pci_suspend(struct pci_dev* dev, pm_message_t state) | |
1416 | { | |
1417 | struct fb_info *info = pci_get_drvdata(dev); | |
1418 | struct s3fb_info *par = info->par; | |
1419 | ||
594a8819 | 1420 | dev_info(info->device, "suspend\n"); |
a268422d | 1421 | |
ac751efa | 1422 | console_lock(); |
a268422d OZ |
1423 | mutex_lock(&(par->open_lock)); |
1424 | ||
1425 | if ((state.event == PM_EVENT_FREEZE) || (par->ref_count == 0)) { | |
1426 | mutex_unlock(&(par->open_lock)); | |
ac751efa | 1427 | console_unlock(); |
a268422d OZ |
1428 | return 0; |
1429 | } | |
1430 | ||
1431 | fb_set_suspend(info, 1); | |
1432 | ||
1433 | pci_save_state(dev); | |
1434 | pci_disable_device(dev); | |
1435 | pci_set_power_state(dev, pci_choose_state(dev, state)); | |
1436 | ||
1437 | mutex_unlock(&(par->open_lock)); | |
ac751efa | 1438 | console_unlock(); |
a268422d OZ |
1439 | |
1440 | return 0; | |
1441 | } | |
1442 | ||
1443 | ||
1444 | /* PCI resume */ | |
1445 | ||
1446 | static int s3_pci_resume(struct pci_dev* dev) | |
1447 | { | |
1448 | struct fb_info *info = pci_get_drvdata(dev); | |
1449 | struct s3fb_info *par = info->par; | |
6314db41 | 1450 | int err; |
a268422d | 1451 | |
594a8819 | 1452 | dev_info(info->device, "resume\n"); |
a268422d | 1453 | |
ac751efa | 1454 | console_lock(); |
a268422d OZ |
1455 | mutex_lock(&(par->open_lock)); |
1456 | ||
1457 | if (par->ref_count == 0) { | |
1458 | mutex_unlock(&(par->open_lock)); | |
ac751efa | 1459 | console_unlock(); |
a268422d OZ |
1460 | return 0; |
1461 | } | |
1462 | ||
1463 | pci_set_power_state(dev, PCI_D0); | |
1464 | pci_restore_state(dev); | |
6314db41 RD |
1465 | err = pci_enable_device(dev); |
1466 | if (err) { | |
1467 | mutex_unlock(&(par->open_lock)); | |
ac751efa | 1468 | console_unlock(); |
594a8819 | 1469 | dev_err(info->device, "error %d enabling device for resume\n", err); |
6314db41 RD |
1470 | return err; |
1471 | } | |
a268422d OZ |
1472 | pci_set_master(dev); |
1473 | ||
1474 | s3fb_set_par(info); | |
1475 | fb_set_suspend(info, 0); | |
1476 | ||
1477 | mutex_unlock(&(par->open_lock)); | |
ac751efa | 1478 | console_unlock(); |
a268422d OZ |
1479 | |
1480 | return 0; | |
1481 | } | |
1482 | ||
1483 | ||
1484 | /* List of boards that we are trying to support */ | |
1485 | ||
48c68c4f | 1486 | static struct pci_device_id s3_devices[] = { |
a268422d OZ |
1487 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8810), .driver_data = CHIP_XXX_TRIO}, |
1488 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8811), .driver_data = CHIP_XXX_TRIO}, | |
1489 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8812), .driver_data = CHIP_M65_AURORA64VP}, | |
1490 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8814), .driver_data = CHIP_767_TRIO64UVP}, | |
1491 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8901), .driver_data = CHIP_XXX_TRIO64V2_DXGX}, | |
1492 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8902), .driver_data = CHIP_551_PLATO_PX}, | |
1493 | ||
1494 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x5631), .driver_data = CHIP_325_VIRGE}, | |
1495 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x883D), .driver_data = CHIP_988_VIRGE_VX}, | |
1496 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A01), .driver_data = CHIP_XXX_VIRGE_DXGX}, | |
94e948e6 OZ |
1497 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A10), .driver_data = CHIP_357_VIRGE_GX2}, |
1498 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A11), .driver_data = CHIP_359_VIRGE_GX2P}, | |
a268422d | 1499 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A12), .driver_data = CHIP_359_VIRGE_GX2P}, |
9966c4fe | 1500 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A13), .driver_data = CHIP_36X_TRIO3D_1X_2X}, |
5694f9ce | 1501 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8904), .driver_data = CHIP_365_TRIO3D}, |
6fcdbc0c | 1502 | {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8C01), .driver_data = CHIP_260_VIRGE_MX}, |
a268422d OZ |
1503 | |
1504 | {0, 0, 0, 0, 0, 0, 0} | |
1505 | }; | |
1506 | ||
1507 | ||
1508 | MODULE_DEVICE_TABLE(pci, s3_devices); | |
1509 | ||
1510 | static struct pci_driver s3fb_pci_driver = { | |
1511 | .name = "s3fb", | |
1512 | .id_table = s3_devices, | |
1513 | .probe = s3_pci_probe, | |
48c68c4f | 1514 | .remove = s3_pci_remove, |
a268422d OZ |
1515 | .suspend = s3_pci_suspend, |
1516 | .resume = s3_pci_resume, | |
1517 | }; | |
1518 | ||
75e1b6a8 | 1519 | /* Parse user specified options */ |
a268422d OZ |
1520 | |
1521 | #ifndef MODULE | |
1522 | static int __init s3fb_setup(char *options) | |
1523 | { | |
1524 | char *opt; | |
1525 | ||
1526 | if (!options || !*options) | |
1527 | return 0; | |
1528 | ||
1529 | while ((opt = strsep(&options, ",")) != NULL) { | |
1530 | ||
1531 | if (!*opt) | |
1532 | continue; | |
62fa4dc7 | 1533 | else if (!strncmp(opt, "mtrr:", 5)) |
a268422d | 1534 | mtrr = simple_strtoul(opt + 5, NULL, 0); |
62fa4dc7 OZ |
1535 | else if (!strncmp(opt, "fasttext:", 9)) |
1536 | fasttext = simple_strtoul(opt + 9, NULL, 0); | |
a268422d | 1537 | else |
a8140543 | 1538 | mode_option = opt; |
a268422d OZ |
1539 | } |
1540 | ||
1541 | return 0; | |
1542 | } | |
1543 | #endif | |
1544 | ||
1545 | /* Cleanup */ | |
1546 | ||
1547 | static void __exit s3fb_cleanup(void) | |
1548 | { | |
1549 | pr_debug("s3fb: cleaning up\n"); | |
1550 | pci_unregister_driver(&s3fb_pci_driver); | |
1551 | } | |
1552 | ||
1553 | /* Driver Initialisation */ | |
1554 | ||
1555 | static int __init s3fb_init(void) | |
1556 | { | |
1557 | ||
1558 | #ifndef MODULE | |
1559 | char *option = NULL; | |
1560 | ||
1561 | if (fb_get_options("s3fb", &option)) | |
1562 | return -ENODEV; | |
1563 | s3fb_setup(option); | |
1564 | #endif | |
1565 | ||
1566 | pr_debug("s3fb: initializing\n"); | |
1567 | return pci_register_driver(&s3fb_pci_driver); | |
1568 | } | |
1569 | ||
1570 | /* ------------------------------------------------------------------------- */ | |
1571 | ||
1572 | /* Modularization */ | |
1573 | ||
1574 | module_init(s3fb_init); | |
1575 | module_exit(s3fb_cleanup); |