1 | /*-*- linux-c -*- |
2 | * linux/drivers/video/i810_main.c -- Intel 810 frame buffer device |
3 | * |
4 | * Copyright (C) 2001 Antonino Daplas<adaplas@pol.net> |
5 | * All Rights Reserved |
6 | * |
7 | * Contributors: |
8 | * Michael Vogt <mvogt@acm.org> - added support for Intel 815 chipsets |
9 | * and enabling the power-on state of |
10 | * external VGA connectors for |
11 | * secondary displays |
12 | * |
13 | * Fredrik Andersson <krueger@shell.linux.se> - alpha testing of |
14 | * the VESA GTF |
15 | * |
16 | * Brad Corrion <bcorrion@web-co.com> - alpha testing of customized |
17 | * timings support |
18 | * |
19 | * The code framework is a modification of vfb.c by Geert Uytterhoeven. |
20 | * DotClock and PLL calculations are partly based on i810_driver.c |
21 | * in xfree86 v4.0.3 by Precision Insight. |
22 | * Watermark calculation and tables are based on i810_wmark.c |
23 | * in xfre86 v4.0.3 by Precision Insight. Slight modifications |
24 | * only to allow for integer operations instead of floating point. |
25 | * |
26 | * This file is subject to the terms and conditions of the GNU General Public |
27 | * License. See the file COPYING in the main directory of this archive for |
28 | * more details. |
29 | */ |
30 | |
31 | #include <linux/aperture.h> |
32 | #include <linux/module.h> |
33 | #include <linux/kernel.h> |
34 | #include <linux/errno.h> |
35 | #include <linux/string.h> |
36 | #include <linux/mm.h> |
37 | #include <linux/slab.h> |
38 | #include <linux/fb.h> |
39 | #include <linux/init.h> |
40 | #include <linux/pci.h> |
41 | #include <linux/pci_ids.h> |
42 | #include <linux/resource.h> |
43 | #include <linux/unistd.h> |
44 | #include <linux/console.h> |
45 | #include <linux/io.h> |
46 | |
47 | #include <asm/io.h> |
48 | #include <asm/div64.h> |
49 | #include <asm/page.h> |
50 | |
51 | #include "i810_regs.h" |
52 | #include "i810.h" |
53 | #include "i810_main.h" |
54 | |
55 | /* |
56 | * voffset - framebuffer offset in MiB from aperture start address. In order for |
57 | * the driver to work with X, we must try to use memory holes left untouched by X. The |
58 | * following table lists where X's different surfaces start at. |
59 | * |
60 | * --------------------------------------------- |
61 | * : : 64 MiB : 32 MiB : |
62 | * ---------------------------------------------- |
63 | * : FrontBuffer : 0 : 0 : |
64 | * : DepthBuffer : 48 : 16 : |
65 | * : BackBuffer : 56 : 24 : |
66 | * ---------------------------------------------- |
67 | * |
68 | * So for chipsets with 64 MiB Aperture sizes, 32 MiB for v_offset is okay, allowing up to |
69 | * 15 + 1 MiB of Framebuffer memory. For 32 MiB Aperture sizes, a v_offset of 8 MiB should |
70 | * work, allowing 7 + 1 MiB of Framebuffer memory. |
71 | * Note, the size of the hole may change depending on how much memory you allocate to X, |
72 | * and how the memory is split up between these surfaces. |
73 | * |
74 | * Note: Anytime the DepthBuffer or FrontBuffer is overlapped, X would still run but with |
75 | * DRI disabled. But if the Frontbuffer is overlapped, X will fail to load. |
76 | * |
77 | * Experiment with v_offset to find out which works best for you. |
78 | */ |
79 | static u32 v_offset_default; /* For 32 MiB Aper size, 8 should be the default */ |
80 | static u32 voffset; |
81 | |
82 | static int i810fb_cursor(struct fb_info *info, struct fb_cursor *cursor); |
83 | static int i810fb_init_pci(struct pci_dev *dev, |
84 | const struct pci_device_id *entry); |
85 | static void i810fb_remove_pci(struct pci_dev *dev); |
86 | static int i810fb_resume(struct pci_dev *dev); |
87 | static int i810fb_suspend(struct pci_dev *dev, pm_message_t state); |
88 | |
89 | /* Chipset Specific Functions */ |
90 | static int i810fb_set_par (struct fb_info *info); |
91 | static int i810fb_getcolreg (u8 regno, u8 *red, u8 *green, u8 *blue, |
92 | u8 *transp, struct fb_info *info); |
93 | static int i810fb_setcolreg (unsigned regno, unsigned red, unsigned green, unsigned blue, |
94 | unsigned transp, struct fb_info *info); |
95 | static int i810fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); |
96 | static int i810fb_blank (int blank_mode, struct fb_info *info); |
97 | |
98 | /* Initialization */ |
99 | static void i810fb_release_resource (struct fb_info *info, struct i810fb_par *par); |
100 | |
101 | /* PCI */ |
102 | static const char * const i810_pci_list[] = { |
103 | "Intel(R) 810 Framebuffer Device" , |
104 | "Intel(R) 810-DC100 Framebuffer Device" , |
105 | "Intel(R) 810E Framebuffer Device" , |
106 | "Intel(R) 815 (Internal Graphics 100Mhz FSB) Framebuffer Device" , |
107 | "Intel(R) 815 (Internal Graphics only) Framebuffer Device" , |
108 | "Intel(R) 815 (Internal Graphics with AGP) Framebuffer Device" |
109 | }; |
110 | |
111 | static const struct pci_device_id i810fb_pci_tbl[] = { |
112 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1, |
113 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, |
114 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3, |
115 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, |
116 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG, |
117 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 }, |
118 | /* mvo: added i815 PCI-ID */ |
119 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_100, |
120 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 }, |
121 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_NOAGP, |
122 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, |
123 | { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC, |
124 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, |
125 | { 0 }, |
126 | }; |
127 | |
128 | static struct pci_driver i810fb_driver = { |
129 | .name = "i810fb" , |
130 | .id_table = i810fb_pci_tbl, |
131 | .probe = i810fb_init_pci, |
132 | .remove = i810fb_remove_pci, |
133 | .suspend = i810fb_suspend, |
134 | .resume = i810fb_resume, |
135 | }; |
136 | |
137 | static char *mode_option = NULL; |
138 | static int vram = 4; |
139 | static int bpp = 8; |
140 | static bool mtrr; |
141 | static bool accel; |
142 | static int hsync1; |
143 | static int hsync2; |
144 | static int vsync1; |
145 | static int vsync2; |
146 | static int xres; |
147 | static int yres; |
148 | static int vyres; |
149 | static bool sync; |
150 | static bool extvga; |
151 | static bool dcolor; |
152 | static bool ddc3; |
153 | |
154 | /*------------------------------------------------------------*/ |
155 | |
156 | /************************************************************** |
157 | * Hardware Low Level Routines * |
158 | **************************************************************/ |
159 | |
160 | /** |
161 | * i810_screen_off - turns off/on display |
162 | * @mmio: address of register space |
163 | * @mode: on or off |
164 | * |
165 | * DESCRIPTION: |
166 | * Blanks/unblanks the display |
167 | */ |
168 | static void i810_screen_off(u8 __iomem *mmio, u8 mode) |
169 | { |
170 | u32 count = WAIT_COUNT; |
171 | u8 val; |
172 | |
173 | i810_writeb(SR_INDEX, mmio, SR01); |
174 | val = i810_readb(SR_DATA, mmio); |
175 | val = (mode == OFF) ? val | SCR_OFF : |
176 | val & ~SCR_OFF; |
177 | |
178 | while((i810_readw(DISP_SL, mmio) & 0xFFF) && count--); |
179 | i810_writeb(SR_INDEX, mmio, SR01); |
180 | i810_writeb(SR_DATA, mmio, val); |
181 | } |
182 | |
183 | /** |
184 | * i810_dram_off - turns off/on dram refresh |
185 | * @mmio: address of register space |
186 | * @mode: on or off |
187 | * |
188 | * DESCRIPTION: |
189 | * Turns off DRAM refresh. Must be off for only 2 vsyncs |
190 | * before data becomes corrupt |
191 | */ |
192 | static void i810_dram_off(u8 __iomem *mmio, u8 mode) |
193 | { |
194 | u8 val; |
195 | |
196 | val = i810_readb(DRAMCH, mmio); |
197 | val &= DRAM_OFF; |
198 | val = (mode == OFF) ? val : val | DRAM_ON; |
199 | i810_writeb(DRAMCH, mmio, val); |
200 | } |
201 | |
202 | /** |
203 | * i810_protect_regs - allows rw/ro mode of certain VGA registers |
204 | * @mmio: address of register space |
205 | * @mode: protect/unprotect |
206 | * |
207 | * DESCRIPTION: |
208 | * The IBM VGA standard allows protection of certain VGA registers. |
209 | * This will protect or unprotect them. |
210 | */ |
211 | static void i810_protect_regs(u8 __iomem *mmio, int mode) |
212 | { |
213 | u8 reg; |
214 | |
215 | i810_writeb(CR_INDEX_CGA, mmio, CR11); |
216 | reg = i810_readb(CR_DATA_CGA, mmio); |
217 | reg = (mode == OFF) ? reg & ~0x80 : |
218 | reg | 0x80; |
219 | |
220 | i810_writeb(CR_INDEX_CGA, mmio, CR11); |
221 | i810_writeb(CR_DATA_CGA, mmio, reg); |
222 | } |
223 | |
224 | /** |
225 | * i810_load_pll - loads values for the hardware PLL clock |
226 | * @par: pointer to i810fb_par structure |
227 | * |
228 | * DESCRIPTION: |
229 | * Loads the P, M, and N registers. |
230 | */ |
231 | static void i810_load_pll(struct i810fb_par *par) |
232 | { |
233 | u32 tmp1, tmp2; |
234 | u8 __iomem *mmio = par->mmio_start_virtual; |
235 | |
236 | tmp1 = par->regs.M | par->regs.N << 16; |
237 | tmp2 = i810_readl(DCLK_2D, mmio); |
238 | tmp2 &= ~MN_MASK; |
239 | i810_writel(DCLK_2D, mmio, tmp1 | tmp2); |
240 | |
241 | tmp1 = par->regs.P; |
242 | tmp2 = i810_readl(DCLK_0DS, mmio); |
243 | tmp2 &= ~(P_OR << 16); |
244 | i810_writel(DCLK_0DS, mmio, (tmp1 << 16) | tmp2); |
245 | |
246 | i810_writeb(MSR_WRITE, mmio, par->regs.msr | 0xC8 | 1); |
247 | |
248 | } |
249 | |
250 | /** |
251 | * i810_load_vga - load standard VGA registers |
252 | * @par: pointer to i810fb_par structure |
253 | * |
254 | * DESCRIPTION: |
255 | * Load values to VGA registers |
256 | */ |
257 | static void i810_load_vga(struct i810fb_par *par) |
258 | { |
259 | u8 __iomem *mmio = par->mmio_start_virtual; |
260 | |
261 | /* interlace */ |
262 | i810_writeb(CR_INDEX_CGA, mmio, CR70); |
263 | i810_writeb(CR_DATA_CGA, mmio, par->interlace); |
264 | |
265 | i810_writeb(CR_INDEX_CGA, mmio, CR00); |
266 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr00); |
267 | i810_writeb(CR_INDEX_CGA, mmio, CR01); |
268 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr01); |
269 | i810_writeb(CR_INDEX_CGA, mmio, CR02); |
270 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr02); |
271 | i810_writeb(CR_INDEX_CGA, mmio, CR03); |
272 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr03); |
273 | i810_writeb(CR_INDEX_CGA, mmio, CR04); |
274 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr04); |
275 | i810_writeb(CR_INDEX_CGA, mmio, CR05); |
276 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr05); |
277 | i810_writeb(CR_INDEX_CGA, mmio, CR06); |
278 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr06); |
279 | i810_writeb(CR_INDEX_CGA, mmio, CR09); |
280 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr09); |
281 | i810_writeb(CR_INDEX_CGA, mmio, CR10); |
282 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr10); |
283 | i810_writeb(CR_INDEX_CGA, mmio, CR11); |
284 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr11); |
285 | i810_writeb(CR_INDEX_CGA, mmio, CR12); |
286 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr12); |
287 | i810_writeb(CR_INDEX_CGA, mmio, CR15); |
288 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr15); |
289 | i810_writeb(CR_INDEX_CGA, mmio, CR16); |
290 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr16); |
291 | } |
292 | |
293 | /** |
294 | * i810_load_vgax - load extended VGA registers |
295 | * @par: pointer to i810fb_par structure |
296 | * |
297 | * DESCRIPTION: |
298 | * Load values to extended VGA registers |
299 | */ |
300 | static void i810_load_vgax(struct i810fb_par *par) |
301 | { |
302 | u8 __iomem *mmio = par->mmio_start_virtual; |
303 | |
304 | i810_writeb(CR_INDEX_CGA, mmio, CR30); |
305 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr30); |
306 | i810_writeb(CR_INDEX_CGA, mmio, CR31); |
307 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr31); |
308 | i810_writeb(CR_INDEX_CGA, mmio, CR32); |
309 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr32); |
310 | i810_writeb(CR_INDEX_CGA, mmio, CR33); |
311 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr33); |
312 | i810_writeb(CR_INDEX_CGA, mmio, CR35); |
313 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr35); |
314 | i810_writeb(CR_INDEX_CGA, mmio, CR39); |
315 | i810_writeb(CR_DATA_CGA, mmio, par->regs.cr39); |
316 | } |
317 | |
318 | /** |
319 | * i810_load_2d - load grahics registers |
320 | * @par: pointer to i810fb_par structure |
321 | * |
322 | * DESCRIPTION: |
323 | * Load values to graphics registers |
324 | */ |
325 | static void i810_load_2d(struct i810fb_par *par) |
326 | { |
327 | u32 tmp; |
328 | u8 tmp8; |
329 | u8 __iomem *mmio = par->mmio_start_virtual; |
330 | |
331 | i810_writel(FW_BLC, mmio, par->watermark); |
332 | tmp = i810_readl(PIXCONF, mmio); |
333 | tmp |= 1 | 1 << 20; |
334 | i810_writel(PIXCONF, mmio, tmp); |
335 | |
336 | i810_writel(OVRACT, mmio, par->ovract); |
337 | |
338 | i810_writeb(GR_INDEX, mmio, GR10); |
339 | tmp8 = i810_readb(GR_DATA, mmio); |
340 | tmp8 |= 2; |
341 | i810_writeb(GR_INDEX, mmio, GR10); |
342 | i810_writeb(GR_DATA, mmio, tmp8); |
343 | } |
344 | |
345 | /** |
346 | * i810_hires - enables high resolution mode |
347 | * @mmio: address of register space |
348 | */ |
349 | static void i810_hires(u8 __iomem *mmio) |
350 | { |
351 | u8 val; |
352 | |
353 | i810_writeb(CR_INDEX_CGA, mmio, CR80); |
354 | val = i810_readb(CR_DATA_CGA, mmio); |
355 | i810_writeb(CR_INDEX_CGA, mmio, CR80); |
356 | i810_writeb(CR_DATA_CGA, mmio, val | 1); |
357 | /* Stop LCD displays from flickering */ |
358 | i810_writel(MEM_MODE, mmio, i810_readl(MEM_MODE, mmio) | 4); |
359 | } |
360 | |
361 | /** |
362 | * i810_load_pitch - loads the characters per line of the display |
363 | * @par: pointer to i810fb_par structure |
364 | * |
365 | * DESCRIPTION: |
366 | * Loads the characters per line |
367 | */ |
368 | static void i810_load_pitch(struct i810fb_par *par) |
369 | { |
370 | u32 tmp, pitch; |
371 | u8 val; |
372 | u8 __iomem *mmio = par->mmio_start_virtual; |
373 | |
374 | pitch = par->pitch >> 3; |
375 | i810_writeb(SR_INDEX, mmio, SR01); |
376 | val = i810_readb(SR_DATA, mmio); |
377 | val &= 0xE0; |
378 | val |= 1 | 1 << 2; |
379 | i810_writeb(SR_INDEX, mmio, SR01); |
380 | i810_writeb(SR_DATA, mmio, val); |
381 | |
382 | tmp = pitch & 0xFF; |
383 | i810_writeb(CR_INDEX_CGA, mmio, CR13); |
384 | i810_writeb(CR_DATA_CGA, mmio, (u8) tmp); |
385 | |
386 | tmp = pitch >> 8; |
387 | i810_writeb(CR_INDEX_CGA, mmio, CR41); |
388 | val = i810_readb(CR_DATA_CGA, mmio) & ~0x0F; |
389 | i810_writeb(CR_INDEX_CGA, mmio, CR41); |
390 | i810_writeb(CR_DATA_CGA, mmio, (u8) tmp | val); |
391 | } |
392 | |
393 | /** |
394 | * i810_load_color - loads the color depth of the display |
395 | * @par: pointer to i810fb_par structure |
396 | * |
397 | * DESCRIPTION: |
398 | * Loads the color depth of the display and the graphics engine |
399 | */ |
400 | static void i810_load_color(struct i810fb_par *par) |
401 | { |
402 | u8 __iomem *mmio = par->mmio_start_virtual; |
403 | u32 reg1; |
404 | u16 reg2; |
405 | |
406 | reg1 = i810_readl(PIXCONF, mmio) & ~(0xF0000 | 1 << 27); |
407 | reg2 = i810_readw(BLTCNTL, mmio) & ~0x30; |
408 | |
409 | reg1 |= 0x8000 | par->pixconf; |
410 | reg2 |= par->bltcntl; |
411 | i810_writel(PIXCONF, mmio, reg1); |
412 | i810_writew(BLTCNTL, mmio, reg2); |
413 | } |
414 | |
415 | /** |
416 | * i810_load_regs - loads all registers for the mode |
417 | * @par: pointer to i810fb_par structure |
418 | * |
419 | * DESCRIPTION: |
420 | * Loads registers |
421 | */ |
422 | static void i810_load_regs(struct i810fb_par *par) |
423 | { |
424 | u8 __iomem *mmio = par->mmio_start_virtual; |
425 | |
426 | i810_screen_off(mmio, OFF); |
427 | i810_protect_regs(mmio, OFF); |
428 | i810_dram_off(mmio, OFF); |
429 | i810_load_pll(par); |
430 | i810_load_vga(par); |
431 | i810_load_vgax(par); |
432 | i810_dram_off(mmio, ON); |
433 | i810_load_2d(par); |
434 | i810_hires(mmio); |
435 | i810_screen_off(mmio, ON); |
436 | i810_protect_regs(mmio, ON); |
437 | i810_load_color(par); |
438 | i810_load_pitch(par); |
439 | } |
440 | |
441 | static void i810_write_dac(u8 regno, u8 red, u8 green, u8 blue, |
442 | u8 __iomem *mmio) |
443 | { |
444 | i810_writeb(CLUT_INDEX_WRITE, mmio, regno); |
445 | i810_writeb(CLUT_DATA, mmio, red); |
446 | i810_writeb(CLUT_DATA, mmio, green); |
447 | i810_writeb(CLUT_DATA, mmio, blue); |
448 | } |
449 | |
450 | static void i810_read_dac(u8 regno, u8 *red, u8 *green, u8 *blue, |
451 | u8 __iomem *mmio) |
452 | { |
453 | i810_writeb(CLUT_INDEX_READ, mmio, regno); |
454 | *red = i810_readb(CLUT_DATA, mmio); |
455 | *green = i810_readb(CLUT_DATA, mmio); |
456 | *blue = i810_readb(CLUT_DATA, mmio); |
457 | } |
458 | |
459 | /************************************************************ |
460 | * VGA State Restore * |
461 | ************************************************************/ |
462 | static void i810_restore_pll(struct i810fb_par *par) |
463 | { |
464 | u32 tmp1, tmp2; |
465 | u8 __iomem *mmio = par->mmio_start_virtual; |
466 | |
467 | tmp1 = par->hw_state.dclk_2d; |
468 | tmp2 = i810_readl(DCLK_2D, mmio); |
469 | tmp1 &= ~MN_MASK; |
470 | tmp2 &= MN_MASK; |
471 | i810_writel(DCLK_2D, mmio, tmp1 | tmp2); |
472 | |
473 | tmp1 = par->hw_state.dclk_1d; |
474 | tmp2 = i810_readl(DCLK_1D, mmio); |
475 | tmp1 &= ~MN_MASK; |
476 | tmp2 &= MN_MASK; |
477 | i810_writel(DCLK_1D, mmio, tmp1 | tmp2); |
478 | |
479 | i810_writel(DCLK_0DS, mmio, par->hw_state.dclk_0ds); |
480 | } |
481 | |
482 | static void i810_restore_dac(struct i810fb_par *par) |
483 | { |
484 | u32 tmp1, tmp2; |
485 | u8 __iomem *mmio = par->mmio_start_virtual; |
486 | |
487 | tmp1 = par->hw_state.pixconf; |
488 | tmp2 = i810_readl(PIXCONF, mmio); |
489 | tmp1 &= DAC_BIT; |
490 | tmp2 &= ~DAC_BIT; |
491 | i810_writel(PIXCONF, mmio, tmp1 | tmp2); |
492 | } |
493 | |
494 | static void i810_restore_vgax(struct i810fb_par *par) |
495 | { |
496 | u8 i, j; |
497 | u8 __iomem *mmio = par->mmio_start_virtual; |
498 | |
499 | for (i = 0; i < 4; i++) { |
500 | i810_writeb(CR_INDEX_CGA, mmio, CR30+i); |
501 | i810_writeb(CR_DATA_CGA, mmio, *(&(par->hw_state.cr30) + i)); |
502 | } |
503 | i810_writeb(CR_INDEX_CGA, mmio, CR35); |
504 | i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr35); |
505 | i810_writeb(CR_INDEX_CGA, mmio, CR39); |
506 | i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr39); |
507 | i810_writeb(CR_INDEX_CGA, mmio, CR41); |
508 | i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr39); |
509 | |
510 | /*restore interlace*/ |
511 | i810_writeb(CR_INDEX_CGA, mmio, CR70); |
512 | i = par->hw_state.cr70; |
513 | i &= INTERLACE_BIT; |
514 | j = i810_readb(CR_DATA_CGA, mmio); |
515 | i810_writeb(CR_INDEX_CGA, mmio, CR70); |
516 | i810_writeb(CR_DATA_CGA, mmio, j | i); |
517 | |
518 | i810_writeb(CR_INDEX_CGA, mmio, CR80); |
519 | i810_writeb(CR_DATA_CGA, mmio, par->hw_state.cr80); |
520 | i810_writeb(MSR_WRITE, mmio, par->hw_state.msr); |
521 | i810_writeb(SR_INDEX, mmio, SR01); |
522 | i = (par->hw_state.sr01) & ~0xE0 ; |
523 | j = i810_readb(SR_DATA, mmio) & 0xE0; |
524 | i810_writeb(SR_INDEX, mmio, SR01); |
525 | i810_writeb(SR_DATA, mmio, i | j); |
526 | } |
527 | |
528 | static void i810_restore_vga(struct i810fb_par *par) |
529 | { |
530 | u8 i; |
531 | u8 __iomem *mmio = par->mmio_start_virtual; |
532 | |
533 | for (i = 0; i < 10; i++) { |
534 | i810_writeb(CR_INDEX_CGA, mmio, CR00 + i); |
535 | i810_writeb(CR_DATA_CGA, mmio, *((&par->hw_state.cr00) + i)); |
536 | } |
537 | for (i = 0; i < 8; i++) { |
538 | i810_writeb(CR_INDEX_CGA, mmio, CR10 + i); |
539 | i810_writeb(CR_DATA_CGA, mmio, *((&par->hw_state.cr10) + i)); |
540 | } |
541 | } |
542 | |
543 | static void i810_restore_addr_map(struct i810fb_par *par) |
544 | { |
545 | u8 tmp; |
546 | u8 __iomem *mmio = par->mmio_start_virtual; |
547 | |
548 | i810_writeb(GR_INDEX, mmio, GR10); |
549 | tmp = i810_readb(GR_DATA, mmio); |
550 | tmp &= ADDR_MAP_MASK; |
551 | tmp |= par->hw_state.gr10; |
552 | i810_writeb(GR_INDEX, mmio, GR10); |
553 | i810_writeb(GR_DATA, mmio, tmp); |
554 | } |
555 | |
556 | static void i810_restore_2d(struct i810fb_par *par) |
557 | { |
558 | u32 tmp_long; |
559 | u16 tmp_word; |
560 | u8 __iomem *mmio = par->mmio_start_virtual; |
561 | |
562 | tmp_word = i810_readw(BLTCNTL, mmio); |
563 | tmp_word &= ~(3 << 4); |
564 | tmp_word |= par->hw_state.bltcntl; |
565 | i810_writew(BLTCNTL, mmio, tmp_word); |
566 | |
567 | i810_dram_off(mmio, OFF); |
568 | i810_writel(PIXCONF, mmio, par->hw_state.pixconf); |
569 | i810_dram_off(mmio, ON); |
570 | |
571 | tmp_word = i810_readw(HWSTAM, mmio); |
572 | tmp_word &= 3 << 13; |
573 | tmp_word |= par->hw_state.hwstam; |
574 | i810_writew(HWSTAM, mmio, tmp_word); |
575 | |
576 | tmp_long = i810_readl(FW_BLC, mmio); |
577 | tmp_long &= FW_BLC_MASK; |
578 | tmp_long |= par->hw_state.fw_blc; |
579 | i810_writel(FW_BLC, mmio, tmp_long); |
580 | |
581 | i810_writel(HWS_PGA, mmio, par->hw_state.hws_pga); |
582 | i810_writew(IER, mmio, par->hw_state.ier); |
583 | i810_writew(IMR, mmio, par->hw_state.imr); |
584 | i810_writel(DPLYSTAS, mmio, par->hw_state.dplystas); |
585 | } |
586 | |
587 | static void i810_restore_vga_state(struct i810fb_par *par) |
588 | { |
589 | u8 __iomem *mmio = par->mmio_start_virtual; |
590 | |
591 | i810_screen_off(mmio, OFF); |
592 | i810_protect_regs(mmio, OFF); |
593 | i810_dram_off(mmio, OFF); |
594 | i810_restore_pll(par); |
595 | i810_restore_dac(par); |
596 | i810_restore_vga(par); |
597 | i810_restore_vgax(par); |
598 | i810_restore_addr_map(par); |
599 | i810_dram_off(mmio, ON); |
600 | i810_restore_2d(par); |
601 | i810_screen_off(mmio, ON); |
602 | i810_protect_regs(mmio, ON); |
603 | } |
604 | |
605 | /*********************************************************************** |
606 | * VGA State Save * |
607 | ***********************************************************************/ |
608 | |
609 | static void i810_save_vgax(struct i810fb_par *par) |
610 | { |
611 | u8 i; |
612 | u8 __iomem *mmio = par->mmio_start_virtual; |
613 | |
614 | for (i = 0; i < 4; i++) { |
615 | i810_writeb(CR_INDEX_CGA, mmio, CR30 + i); |
616 | *(&(par->hw_state.cr30) + i) = i810_readb(CR_DATA_CGA, mmio); |
617 | } |
618 | i810_writeb(CR_INDEX_CGA, mmio, CR35); |
619 | par->hw_state.cr35 = i810_readb(CR_DATA_CGA, mmio); |
620 | i810_writeb(CR_INDEX_CGA, mmio, CR39); |
621 | par->hw_state.cr39 = i810_readb(CR_DATA_CGA, mmio); |
622 | i810_writeb(CR_INDEX_CGA, mmio, CR41); |
623 | par->hw_state.cr41 = i810_readb(CR_DATA_CGA, mmio); |
624 | i810_writeb(CR_INDEX_CGA, mmio, CR70); |
625 | par->hw_state.cr70 = i810_readb(CR_DATA_CGA, mmio); |
626 | par->hw_state.msr = i810_readb(MSR_READ, mmio); |
627 | i810_writeb(CR_INDEX_CGA, mmio, CR80); |
628 | par->hw_state.cr80 = i810_readb(CR_DATA_CGA, mmio); |
629 | i810_writeb(SR_INDEX, mmio, SR01); |
630 | par->hw_state.sr01 = i810_readb(SR_DATA, mmio); |
631 | } |
632 | |
633 | static void i810_save_vga(struct i810fb_par *par) |
634 | { |
635 | u8 i; |
636 | u8 __iomem *mmio = par->mmio_start_virtual; |
637 | |
638 | for (i = 0; i < 10; i++) { |
639 | i810_writeb(CR_INDEX_CGA, mmio, CR00 + i); |
640 | *((&par->hw_state.cr00) + i) = i810_readb(CR_DATA_CGA, mmio); |
641 | } |
642 | for (i = 0; i < 8; i++) { |
643 | i810_writeb(CR_INDEX_CGA, mmio, CR10 + i); |
644 | *((&par->hw_state.cr10) + i) = i810_readb(CR_DATA_CGA, mmio); |
645 | } |
646 | } |
647 | |
648 | static void i810_save_2d(struct i810fb_par *par) |
649 | { |
650 | u8 __iomem *mmio = par->mmio_start_virtual; |
651 | |
652 | par->hw_state.dclk_2d = i810_readl(DCLK_2D, mmio); |
653 | par->hw_state.dclk_1d = i810_readl(DCLK_1D, mmio); |
654 | par->hw_state.dclk_0ds = i810_readl(DCLK_0DS, mmio); |
655 | par->hw_state.pixconf = i810_readl(PIXCONF, mmio); |
656 | par->hw_state.fw_blc = i810_readl(FW_BLC, mmio); |
657 | par->hw_state.bltcntl = i810_readw(BLTCNTL, mmio); |
658 | par->hw_state.hwstam = i810_readw(HWSTAM, mmio); |
659 | par->hw_state.hws_pga = i810_readl(HWS_PGA, mmio); |
660 | par->hw_state.ier = i810_readw(IER, mmio); |
661 | par->hw_state.imr = i810_readw(IMR, mmio); |
662 | par->hw_state.dplystas = i810_readl(DPLYSTAS, mmio); |
663 | } |
664 | |
665 | static void i810_save_vga_state(struct i810fb_par *par) |
666 | { |
667 | i810_save_vga(par); |
668 | i810_save_vgax(par); |
669 | i810_save_2d(par); |
670 | } |
671 | |
672 | /************************************************************ |
673 | * Helpers * |
674 | ************************************************************/ |
675 | /** |
676 | * get_line_length - calculates buffer pitch in bytes |
677 | * @par: pointer to i810fb_par structure |
678 | * @xres_virtual: virtual resolution of the frame |
679 | * @bpp: bits per pixel |
680 | * |
681 | * DESCRIPTION: |
682 | * Calculates buffer pitch in bytes. |
683 | */ |
684 | static u32 get_line_length(struct i810fb_par *par, int xres_virtual, int bpp) |
685 | { |
686 | u32 length; |
687 | |
688 | length = xres_virtual*bpp; |
689 | length = (length+31)&-32; |
690 | length >>= 3; |
691 | return length; |
692 | } |
693 | |
694 | /** |
695 | * i810_calc_dclk - calculates the P, M, and N values of a pixelclock value |
696 | * @freq: target pixelclock in picoseconds |
697 | * @m: where to write M register |
698 | * @n: where to write N register |
699 | * @p: where to write P register |
700 | * |
701 | * DESCRIPTION: |
702 | * Based on the formula Freq_actual = (4*M*Freq_ref)/(N^P) |
703 | * Repeatedly computes the Freq until the actual Freq is equal to |
704 | * the target Freq or until the loop count is zero. In the latter |
705 | * case, the actual frequency nearest the target will be used. |
706 | */ |
707 | static void i810_calc_dclk(u32 freq, u32 *m, u32 *n, u32 *p) |
708 | { |
709 | u32 m_reg, n_reg, p_divisor, n_target_max; |
710 | u32 m_target, n_target, p_target, n_best, m_best, mod; |
711 | u32 f_out, target_freq, diff = 0, mod_min, diff_min; |
712 | |
713 | diff_min = mod_min = 0xFFFFFFFF; |
714 | n_best = m_best = m_target = f_out = 0; |
715 | |
716 | target_freq = freq; |
717 | n_target_max = 30; |
718 | |
719 | /* |
720 | * find P such that target freq is 16x reference freq (Hz). |
721 | */ |
722 | p_divisor = 1; |
723 | p_target = 0; |
724 | while(!((1000000 * p_divisor)/(16 * 24 * target_freq)) && |
725 | p_divisor <= 32) { |
726 | p_divisor <<= 1; |
727 | p_target++; |
728 | } |
729 | |
730 | n_reg = m_reg = n_target = 3; |
731 | while (diff_min && mod_min && (n_target < n_target_max)) { |
732 | f_out = (p_divisor * n_reg * 1000000)/(4 * 24 * m_reg); |
733 | mod = (p_divisor * n_reg * 1000000) % (4 * 24 * m_reg); |
734 | m_target = m_reg; |
735 | n_target = n_reg; |
736 | if (f_out <= target_freq) { |
737 | n_reg++; |
738 | diff = target_freq - f_out; |
739 | } else { |
740 | m_reg++; |
741 | diff = f_out - target_freq; |
742 | } |
743 | |
744 | if (diff_min > diff) { |
745 | diff_min = diff; |
746 | n_best = n_target; |
747 | m_best = m_target; |
748 | } |
749 | |
750 | if (!diff && mod_min > mod) { |
751 | mod_min = mod; |
752 | n_best = n_target; |
753 | m_best = m_target; |
754 | } |
755 | } |
756 | if (m) *m = (m_best - 2) & 0x3FF; |
757 | if (n) *n = (n_best - 2) & 0x3FF; |
758 | if (p) *p = (p_target << 4); |
759 | } |
760 | |
761 | /************************************************************* |
762 | * Hardware Cursor Routines * |
763 | *************************************************************/ |
764 | |
765 | /** |
766 | * i810_enable_cursor - show or hide the hardware cursor |
767 | * @mmio: address of register space |
768 | * @mode: show (1) or hide (0) |
769 | * |
770 | * Description: |
771 | * Shows or hides the hardware cursor |
772 | */ |
773 | static void i810_enable_cursor(u8 __iomem *mmio, int mode) |
774 | { |
775 | u32 temp; |
776 | |
777 | temp = i810_readl(PIXCONF, mmio); |
778 | temp = (mode == ON) ? temp | CURSOR_ENABLE_MASK : |
779 | temp & ~CURSOR_ENABLE_MASK; |
780 | |
781 | i810_writel(PIXCONF, mmio, temp); |
782 | } |
783 | |
784 | static void i810_reset_cursor_image(struct i810fb_par *par) |
785 | { |
786 | u8 __iomem *addr = par->cursor_heap.virtual; |
787 | int i, j; |
788 | |
789 | for (i = 64; i--; ) { |
790 | for (j = 0; j < 8; j++) { |
791 | i810_writeb(j, addr, 0xff); |
792 | i810_writeb(j+8, addr, 0x00); |
793 | } |
794 | addr +=16; |
795 | } |
796 | } |
797 | |
798 | static void i810_load_cursor_image(int width, int height, u8 *data, |
799 | struct i810fb_par *par) |
800 | { |
801 | u8 __iomem *addr = par->cursor_heap.virtual; |
802 | int i, j, w = width/8; |
803 | int mod = width % 8, t_mask, d_mask; |
804 | |
805 | t_mask = 0xff >> mod; |
806 | d_mask = ~(0xff >> mod); |
807 | for (i = height; i--; ) { |
808 | for (j = 0; j < w; j++) { |
809 | i810_writeb(j+0, addr, 0x00); |
810 | i810_writeb(j+8, addr, *data++); |
811 | } |
812 | if (mod) { |
813 | i810_writeb(j+0, addr, t_mask); |
814 | i810_writeb(j+8, addr, *data++ & d_mask); |
815 | } |
816 | addr += 16; |
817 | } |
818 | } |
819 | |
820 | static void i810_load_cursor_colors(int fg, int bg, struct fb_info *info) |
821 | { |
822 | struct i810fb_par *par = info->par; |
823 | u8 __iomem *mmio = par->mmio_start_virtual; |
824 | u8 red, green, blue, trans, temp; |
825 | |
826 | i810fb_getcolreg(regno: bg, red: &red, green: &green, blue: &blue, transp: &trans, info); |
827 | |
828 | temp = i810_readb(PIXCONF1, mmio); |
829 | i810_writeb(PIXCONF1, mmio, temp | EXTENDED_PALETTE); |
830 | |
831 | i810_write_dac(regno: 4, red, green, blue, mmio); |
832 | |
833 | i810_writeb(PIXCONF1, mmio, temp); |
834 | |
835 | i810fb_getcolreg(regno: fg, red: &red, green: &green, blue: &blue, transp: &trans, info); |
836 | temp = i810_readb(PIXCONF1, mmio); |
837 | i810_writeb(PIXCONF1, mmio, temp | EXTENDED_PALETTE); |
838 | |
839 | i810_write_dac(regno: 5, red, green, blue, mmio); |
840 | |
841 | i810_writeb(PIXCONF1, mmio, temp); |
842 | } |
843 | |
844 | /** |
845 | * i810_init_cursor - initializes the cursor |
846 | * @par: pointer to i810fb_par structure |
847 | * |
848 | * DESCRIPTION: |
849 | * Initializes the cursor registers |
850 | */ |
851 | static void i810_init_cursor(struct i810fb_par *par) |
852 | { |
853 | u8 __iomem *mmio = par->mmio_start_virtual; |
854 | |
855 | i810_enable_cursor(mmio, OFF); |
856 | i810_writel(CURBASE, mmio, par->cursor_heap.physical); |
857 | i810_writew(CURCNTR, mmio, COORD_ACTIVE | CURSOR_MODE_64_XOR); |
858 | } |
859 | |
860 | /********************************************************************* |
861 | * Framebuffer hook helpers * |
862 | *********************************************************************/ |
863 | /** |
864 | * i810_round_off - Round off values to capability of hardware |
865 | * @var: pointer to fb_var_screeninfo structure |
866 | * |
867 | * DESCRIPTION: |
868 | * @var contains user-defined information for the mode to be set. |
869 | * This will try modify those values to ones nearest the |
870 | * capability of the hardware |
871 | */ |
872 | static void i810_round_off(struct fb_var_screeninfo *var) |
873 | { |
874 | u32 xres, yres, vxres, vyres; |
875 | |
876 | /* |
877 | * Presently supports only these configurations |
878 | */ |
879 | |
880 | xres = var->xres; |
881 | yres = var->yres; |
882 | vxres = var->xres_virtual; |
883 | vyres = var->yres_virtual; |
884 | |
885 | var->bits_per_pixel += 7; |
886 | var->bits_per_pixel &= ~7; |
887 | |
888 | if (var->bits_per_pixel < 8) |
889 | var->bits_per_pixel = 8; |
890 | if (var->bits_per_pixel > 32) |
891 | var->bits_per_pixel = 32; |
892 | |
893 | round_off_xres(xres: &xres); |
894 | if (xres < 40) |
895 | xres = 40; |
896 | if (xres > 2048) |
897 | xres = 2048; |
898 | xres = (xres + 7) & ~7; |
899 | |
900 | if (vxres < xres) |
901 | vxres = xres; |
902 | |
903 | round_off_yres(xres: &xres, yres: &yres); |
904 | if (yres < 1) |
905 | yres = 1; |
906 | if (yres >= 2048) |
907 | yres = 2048; |
908 | |
909 | if (vyres < yres) |
910 | vyres = yres; |
911 | |
912 | if (var->bits_per_pixel == 32) |
913 | var->accel_flags = 0; |
914 | |
915 | /* round of horizontal timings to nearest 8 pixels */ |
916 | var->left_margin = (var->left_margin + 4) & ~7; |
917 | var->right_margin = (var->right_margin + 4) & ~7; |
918 | var->hsync_len = (var->hsync_len + 4) & ~7; |
919 | |
920 | if (var->vmode & FB_VMODE_INTERLACED) { |
921 | if (!((yres + var->upper_margin + var->vsync_len + |
922 | var->lower_margin) & 1)) |
923 | var->upper_margin++; |
924 | } |
925 | |
926 | var->xres = xres; |
927 | var->yres = yres; |
928 | var->xres_virtual = vxres; |
929 | var->yres_virtual = vyres; |
930 | } |
931 | |
932 | /** |
933 | * set_color_bitfields - sets rgba fields |
934 | * @var: pointer to fb_var_screeninfo |
935 | * |
936 | * DESCRIPTION: |
937 | * The length, offset and ordering for each color field |
938 | * (red, green, blue) will be set as specified |
939 | * by the hardware |
940 | */ |
941 | static void set_color_bitfields(struct fb_var_screeninfo *var) |
942 | { |
943 | switch (var->bits_per_pixel) { |
944 | case 8: |
945 | var->red.offset = 0; |
946 | var->red.length = 8; |
947 | var->green.offset = 0; |
948 | var->green.length = 8; |
949 | var->blue.offset = 0; |
950 | var->blue.length = 8; |
951 | var->transp.offset = 0; |
952 | var->transp.length = 0; |
953 | break; |
954 | case 16: |
955 | var->green.length = (var->green.length == 5) ? 5 : 6; |
956 | var->red.length = 5; |
957 | var->blue.length = 5; |
958 | var->transp.length = 6 - var->green.length; |
959 | var->blue.offset = 0; |
960 | var->green.offset = 5; |
961 | var->red.offset = 5 + var->green.length; |
962 | var->transp.offset = (5 + var->red.offset) & 15; |
963 | break; |
964 | case 24: /* RGB 888 */ |
965 | case 32: /* RGBA 8888 */ |
966 | var->red.offset = 16; |
967 | var->red.length = 8; |
968 | var->green.offset = 8; |
969 | var->green.length = 8; |
970 | var->blue.offset = 0; |
971 | var->blue.length = 8; |
972 | var->transp.length = var->bits_per_pixel - 24; |
973 | var->transp.offset = (var->transp.length) ? 24 : 0; |
974 | break; |
975 | } |
976 | var->red.msb_right = 0; |
977 | var->green.msb_right = 0; |
978 | var->blue.msb_right = 0; |
979 | var->transp.msb_right = 0; |
980 | } |
981 | |
982 | /** |
983 | * i810_check_params - check if contents in var are valid |
984 | * @var: pointer to fb_var_screeninfo |
985 | * @info: pointer to fb_info |
986 | * |
987 | * DESCRIPTION: |
988 | * This will check if the framebuffer size is sufficient |
989 | * for the current mode and if the user's monitor has the |
990 | * required specifications to display the current mode. |
991 | */ |
992 | static int i810_check_params(struct fb_var_screeninfo *var, |
993 | struct fb_info *info) |
994 | { |
995 | struct i810fb_par *par = info->par; |
996 | int line_length, vidmem, mode_valid = 0, retval = 0; |
997 | u32 vyres = var->yres_virtual, vxres = var->xres_virtual; |
998 | |
999 | /* |
1000 | * Memory limit |
1001 | */ |
1002 | line_length = get_line_length(par, xres_virtual: vxres, bpp: var->bits_per_pixel); |
1003 | vidmem = line_length*vyres; |
1004 | |
1005 | if (vidmem > par->fb.size) { |
1006 | vyres = par->fb.size/line_length; |
1007 | if (vyres < var->yres) { |
1008 | vyres = info->var.yres; |
1009 | vxres = par->fb.size/vyres; |
1010 | vxres /= var->bits_per_pixel >> 3; |
1011 | line_length = get_line_length(par, xres_virtual: vxres, |
1012 | bpp: var->bits_per_pixel); |
1013 | vidmem = line_length * info->var.yres; |
1014 | if (vxres < var->xres) { |
1015 | printk("i810fb: required video memory, " |
1016 | "%d bytes, for %dx%d-%d (virtual) " |
1017 | "is out of range\n" , |
1018 | vidmem, vxres, vyres, |
1019 | var->bits_per_pixel); |
1020 | return -ENOMEM; |
1021 | } |
1022 | } |
1023 | } |
1024 | |
1025 | var->xres_virtual = vxres; |
1026 | var->yres_virtual = vyres; |
1027 | |
1028 | /* |
1029 | * Monitor limit |
1030 | */ |
1031 | switch (var->bits_per_pixel) { |
1032 | case 8: |
1033 | info->monspecs.dclkmax = 234000000; |
1034 | break; |
1035 | case 16: |
1036 | info->monspecs.dclkmax = 229000000; |
1037 | break; |
1038 | case 24: |
1039 | case 32: |
1040 | info->monspecs.dclkmax = 204000000; |
1041 | break; |
1042 | } |
1043 | |
1044 | info->monspecs.dclkmin = 15000000; |
1045 | |
1046 | if (!fb_validate_mode(var, info)) |
1047 | mode_valid = 1; |
1048 | |
1049 | #ifdef CONFIG_FB_I810_I2C |
1050 | if (!mode_valid && info->monspecs.gtf && |
1051 | !fb_get_mode(FB_MAXTIMINGS, 0, var, info)) |
1052 | mode_valid = 1; |
1053 | |
1054 | if (!mode_valid && info->monspecs.modedb_len) { |
1055 | const struct fb_videomode *mode; |
1056 | |
1057 | mode = fb_find_best_mode(var, &info->modelist); |
1058 | if (mode) { |
1059 | fb_videomode_to_var(var, mode); |
1060 | mode_valid = 1; |
1061 | } |
1062 | } |
1063 | #endif |
1064 | if (!mode_valid && info->monspecs.modedb_len == 0) { |
1065 | if (fb_get_mode(FB_MAXTIMINGS, val: 0, var, info)) { |
1066 | int default_sync = (info->monspecs.hfmin-HFMIN) |
1067 | |(info->monspecs.hfmax-HFMAX) |
1068 | |(info->monspecs.vfmin-VFMIN) |
1069 | |(info->monspecs.vfmax-VFMAX); |
1070 | printk("i810fb: invalid video mode%s\n" , |
1071 | default_sync ? "" : ". Specifying " |
1072 | "vsyncN/hsyncN parameters may help" ); |
1073 | retval = -EINVAL; |
1074 | } |
1075 | } |
1076 | |
1077 | return retval; |
1078 | } |
1079 | |
1080 | /** |
1081 | * encode_fix - fill up fb_fix_screeninfo structure |
1082 | * @fix: pointer to fb_fix_screeninfo |
1083 | * @info: pointer to fb_info |
1084 | * |
1085 | * DESCRIPTION: |
1086 | * This will set up parameters that are unmodifiable by the user. |
1087 | */ |
1088 | static int encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info) |
1089 | { |
1090 | struct i810fb_par *par = info->par; |
1091 | |
1092 | memset(fix, 0, sizeof(struct fb_fix_screeninfo)); |
1093 | |
1094 | strcpy(p: fix->id, q: "I810" ); |
1095 | mutex_lock(&info->mm_lock); |
1096 | fix->smem_start = par->fb.physical; |
1097 | fix->smem_len = par->fb.size; |
1098 | mutex_unlock(lock: &info->mm_lock); |
1099 | fix->type = FB_TYPE_PACKED_PIXELS; |
1100 | fix->type_aux = 0; |
1101 | fix->xpanstep = 8; |
1102 | fix->ypanstep = 1; |
1103 | |
1104 | switch (info->var.bits_per_pixel) { |
1105 | case 8: |
1106 | fix->visual = FB_VISUAL_PSEUDOCOLOR; |
1107 | break; |
1108 | case 16: |
1109 | case 24: |
1110 | case 32: |
1111 | if (info->var.nonstd) |
1112 | fix->visual = FB_VISUAL_DIRECTCOLOR; |
1113 | else |
1114 | fix->visual = FB_VISUAL_TRUECOLOR; |
1115 | break; |
1116 | default: |
1117 | return -EINVAL; |
1118 | } |
1119 | fix->ywrapstep = 0; |
1120 | fix->line_length = par->pitch; |
1121 | fix->mmio_start = par->mmio_start_phys; |
1122 | fix->mmio_len = MMIO_SIZE; |
1123 | fix->accel = FB_ACCEL_I810; |
1124 | |
1125 | return 0; |
1126 | } |
1127 | |
1128 | /** |
1129 | * decode_var - modify par according to contents of var |
1130 | * @var: pointer to fb_var_screeninfo |
1131 | * @par: pointer to i810fb_par |
1132 | * |
1133 | * DESCRIPTION: |
1134 | * Based on the contents of @var, @par will be dynamically filled up. |
1135 | * @par contains all information necessary to modify the hardware. |
1136 | */ |
1137 | static void decode_var(const struct fb_var_screeninfo *var, |
1138 | struct i810fb_par *par) |
1139 | { |
1140 | u32 xres, yres, vxres, vyres; |
1141 | |
1142 | xres = var->xres; |
1143 | yres = var->yres; |
1144 | vxres = var->xres_virtual; |
1145 | vyres = var->yres_virtual; |
1146 | |
1147 | switch (var->bits_per_pixel) { |
1148 | case 8: |
1149 | par->pixconf = PIXCONF8; |
1150 | par->bltcntl = 0; |
1151 | par->depth = 1; |
1152 | par->blit_bpp = BPP8; |
1153 | break; |
1154 | case 16: |
1155 | if (var->green.length == 5) |
1156 | par->pixconf = PIXCONF15; |
1157 | else |
1158 | par->pixconf = PIXCONF16; |
1159 | par->bltcntl = 16; |
1160 | par->depth = 2; |
1161 | par->blit_bpp = BPP16; |
1162 | break; |
1163 | case 24: |
1164 | par->pixconf = PIXCONF24; |
1165 | par->bltcntl = 32; |
1166 | par->depth = 3; |
1167 | par->blit_bpp = BPP24; |
1168 | break; |
1169 | case 32: |
1170 | par->pixconf = PIXCONF32; |
1171 | par->bltcntl = 0; |
1172 | par->depth = 4; |
1173 | par->blit_bpp = 3 << 24; |
1174 | break; |
1175 | } |
1176 | if (var->nonstd && var->bits_per_pixel != 8) |
1177 | par->pixconf |= 1 << 27; |
1178 | |
1179 | i810_calc_dclk(freq: var->pixclock, m: &par->regs.M, |
1180 | n: &par->regs.N, p: &par->regs.P); |
1181 | i810fb_encode_registers(var, par, xres, yres); |
1182 | |
1183 | par->watermark = i810_get_watermark(var, par); |
1184 | par->pitch = get_line_length(par, xres_virtual: vxres, bpp: var->bits_per_pixel); |
1185 | } |
1186 | |
1187 | /** |
1188 | * i810fb_getcolreg - gets red, green and blue values of the hardware DAC |
1189 | * @regno: DAC index |
1190 | * @red: red |
1191 | * @green: green |
1192 | * @blue: blue |
1193 | * @transp: transparency (alpha) |
1194 | * @info: pointer to fb_info |
1195 | * |
1196 | * DESCRIPTION: |
1197 | * Gets the red, green and blue values of the hardware DAC as pointed by @regno |
1198 | * and writes them to @red, @green and @blue respectively |
1199 | */ |
1200 | static int i810fb_getcolreg(u8 regno, u8 *red, u8 *green, u8 *blue, |
1201 | u8 *transp, struct fb_info *info) |
1202 | { |
1203 | struct i810fb_par *par = info->par; |
1204 | u8 __iomem *mmio = par->mmio_start_virtual; |
1205 | u8 temp; |
1206 | |
1207 | if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { |
1208 | if ((info->var.green.length == 5 && regno > 31) || |
1209 | (info->var.green.length == 6 && regno > 63)) |
1210 | return 1; |
1211 | } |
1212 | |
1213 | temp = i810_readb(PIXCONF1, mmio); |
1214 | i810_writeb(PIXCONF1, mmio, temp & ~EXTENDED_PALETTE); |
1215 | |
1216 | if (info->fix.visual == FB_VISUAL_DIRECTCOLOR && |
1217 | info->var.green.length == 5) |
1218 | i810_read_dac(regno: regno * 8, red, green, blue, mmio); |
1219 | |
1220 | else if (info->fix.visual == FB_VISUAL_DIRECTCOLOR && |
1221 | info->var.green.length == 6) { |
1222 | u8 tmp; |
1223 | |
1224 | i810_read_dac(regno: regno * 8, red, green: &tmp, blue, mmio); |
1225 | i810_read_dac(regno: regno * 4, red: &tmp, green, blue: &tmp, mmio); |
1226 | } |
1227 | else |
1228 | i810_read_dac(regno, red, green, blue, mmio); |
1229 | |
1230 | *transp = 0; |
1231 | i810_writeb(PIXCONF1, mmio, temp); |
1232 | |
1233 | return 0; |
1234 | } |
1235 | |
1236 | /****************************************************************** |
1237 | * Framebuffer device-specific hooks * |
1238 | ******************************************************************/ |
1239 | |
1240 | static int i810fb_open(struct fb_info *info, int user) |
1241 | { |
1242 | struct i810fb_par *par = info->par; |
1243 | |
1244 | mutex_lock(&par->open_lock); |
1245 | if (par->use_count == 0) { |
1246 | memset(&par->state, 0, sizeof(struct vgastate)); |
1247 | par->state.flags = VGA_SAVE_CMAP; |
1248 | par->state.vgabase = par->mmio_start_virtual; |
1249 | save_vga(state: &par->state); |
1250 | |
1251 | i810_save_vga_state(par); |
1252 | } |
1253 | |
1254 | par->use_count++; |
1255 | mutex_unlock(lock: &par->open_lock); |
1256 | |
1257 | return 0; |
1258 | } |
1259 | |
1260 | static int i810fb_release(struct fb_info *info, int user) |
1261 | { |
1262 | struct i810fb_par *par = info->par; |
1263 | |
1264 | mutex_lock(&par->open_lock); |
1265 | if (par->use_count == 0) { |
1266 | mutex_unlock(lock: &par->open_lock); |
1267 | return -EINVAL; |
1268 | } |
1269 | |
1270 | if (par->use_count == 1) { |
1271 | i810_restore_vga_state(par); |
1272 | restore_vga(state: &par->state); |
1273 | } |
1274 | |
1275 | par->use_count--; |
1276 | mutex_unlock(lock: &par->open_lock); |
1277 | |
1278 | return 0; |
1279 | } |
1280 | |
1281 | |
1282 | static int i810fb_setcolreg(unsigned regno, unsigned red, unsigned green, |
1283 | unsigned blue, unsigned transp, |
1284 | struct fb_info *info) |
1285 | { |
1286 | struct i810fb_par *par = info->par; |
1287 | u8 __iomem *mmio = par->mmio_start_virtual; |
1288 | u8 temp; |
1289 | int i; |
1290 | |
1291 | if (regno > 255) return 1; |
1292 | |
1293 | if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { |
1294 | if ((info->var.green.length == 5 && regno > 31) || |
1295 | (info->var.green.length == 6 && regno > 63)) |
1296 | return 1; |
1297 | } |
1298 | |
1299 | if (info->var.grayscale) |
1300 | red = green = blue = (19595 * red + 38470 * green + |
1301 | 7471 * blue) >> 16; |
1302 | |
1303 | temp = i810_readb(PIXCONF1, mmio); |
1304 | i810_writeb(PIXCONF1, mmio, temp & ~EXTENDED_PALETTE); |
1305 | |
1306 | if (info->fix.visual == FB_VISUAL_DIRECTCOLOR && |
1307 | info->var.green.length == 5) { |
1308 | for (i = 0; i < 8; i++) |
1309 | i810_write_dac(regno: (u8) (regno * 8) + i, red: (u8) red, |
1310 | green: (u8) green, blue: (u8) blue, mmio); |
1311 | } else if (info->fix.visual == FB_VISUAL_DIRECTCOLOR && |
1312 | info->var.green.length == 6) { |
1313 | u8 r, g, b; |
1314 | |
1315 | if (regno < 32) { |
1316 | for (i = 0; i < 8; i++) |
1317 | i810_write_dac(regno: (u8) (regno * 8) + i, |
1318 | red: (u8) red, green: (u8) green, |
1319 | blue: (u8) blue, mmio); |
1320 | } |
1321 | i810_read_dac(regno: (u8) (regno*4), red: &r, green: &g, blue: &b, mmio); |
1322 | for (i = 0; i < 4; i++) |
1323 | i810_write_dac(regno: (u8) (regno*4) + i, red: r, green: (u8) green, |
1324 | blue: b, mmio); |
1325 | } else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) { |
1326 | i810_write_dac(regno: (u8) regno, red: (u8) red, green: (u8) green, |
1327 | blue: (u8) blue, mmio); |
1328 | } |
1329 | |
1330 | i810_writeb(PIXCONF1, mmio, temp); |
1331 | |
1332 | if (regno < 16) { |
1333 | switch (info->var.bits_per_pixel) { |
1334 | case 16: |
1335 | if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) { |
1336 | if (info->var.green.length == 5) |
1337 | ((u32 *)info->pseudo_palette)[regno] = |
1338 | (regno << 10) | (regno << 5) | |
1339 | regno; |
1340 | else |
1341 | ((u32 *)info->pseudo_palette)[regno] = |
1342 | (regno << 11) | (regno << 5) | |
1343 | regno; |
1344 | } else { |
1345 | if (info->var.green.length == 5) { |
1346 | /* RGB 555 */ |
1347 | ((u32 *)info->pseudo_palette)[regno] = |
1348 | ((red & 0xf800) >> 1) | |
1349 | ((green & 0xf800) >> 6) | |
1350 | ((blue & 0xf800) >> 11); |
1351 | } else { |
1352 | /* RGB 565 */ |
1353 | ((u32 *)info->pseudo_palette)[regno] = |
1354 | (red & 0xf800) | |
1355 | ((green & 0xf800) >> 5) | |
1356 | ((blue & 0xf800) >> 11); |
1357 | } |
1358 | } |
1359 | break; |
1360 | case 24: /* RGB 888 */ |
1361 | case 32: /* RGBA 8888 */ |
1362 | if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) |
1363 | ((u32 *)info->pseudo_palette)[regno] = |
1364 | (regno << 16) | (regno << 8) | |
1365 | regno; |
1366 | else |
1367 | ((u32 *)info->pseudo_palette)[regno] = |
1368 | ((red & 0xff00) << 8) | |
1369 | (green & 0xff00) | |
1370 | ((blue & 0xff00) >> 8); |
1371 | break; |
1372 | } |
1373 | } |
1374 | return 0; |
1375 | } |
1376 | |
1377 | static int i810fb_pan_display(struct fb_var_screeninfo *var, |
1378 | struct fb_info *info) |
1379 | { |
1380 | struct i810fb_par *par = info->par; |
1381 | u32 total; |
1382 | |
1383 | total = var->xoffset * par->depth + |
1384 | var->yoffset * info->fix.line_length; |
1385 | i810fb_load_front(offset: total, info); |
1386 | |
1387 | return 0; |
1388 | } |
1389 | |
1390 | static int i810fb_blank (int blank_mode, struct fb_info *info) |
1391 | { |
1392 | struct i810fb_par *par = info->par; |
1393 | u8 __iomem *mmio = par->mmio_start_virtual; |
1394 | int mode = 0, pwr, scr_off = 0; |
1395 | |
1396 | pwr = i810_readl(PWR_CLKC, mmio); |
1397 | |
1398 | switch (blank_mode) { |
1399 | case FB_BLANK_UNBLANK: |
1400 | mode = POWERON; |
1401 | pwr |= 1; |
1402 | scr_off = ON; |
1403 | break; |
1404 | case FB_BLANK_NORMAL: |
1405 | mode = POWERON; |
1406 | pwr |= 1; |
1407 | scr_off = OFF; |
1408 | break; |
1409 | case FB_BLANK_VSYNC_SUSPEND: |
1410 | mode = STANDBY; |
1411 | pwr |= 1; |
1412 | scr_off = OFF; |
1413 | break; |
1414 | case FB_BLANK_HSYNC_SUSPEND: |
1415 | mode = SUSPEND; |
1416 | pwr |= 1; |
1417 | scr_off = OFF; |
1418 | break; |
1419 | case FB_BLANK_POWERDOWN: |
1420 | mode = POWERDOWN; |
1421 | pwr &= ~1; |
1422 | scr_off = OFF; |
1423 | break; |
1424 | default: |
1425 | return -EINVAL; |
1426 | } |
1427 | |
1428 | i810_screen_off(mmio, mode: scr_off); |
1429 | i810_writel(HVSYNC, mmio, mode); |
1430 | i810_writel(PWR_CLKC, mmio, pwr); |
1431 | |
1432 | return 0; |
1433 | } |
1434 | |
1435 | static int i810fb_set_par(struct fb_info *info) |
1436 | { |
1437 | struct i810fb_par *par = info->par; |
1438 | |
1439 | decode_var(var: &info->var, par); |
1440 | i810_load_regs(par); |
1441 | i810_init_cursor(par); |
1442 | encode_fix(fix: &info->fix, info); |
1443 | |
1444 | if (info->var.accel_flags && !(par->dev_flags & LOCKUP)) { |
1445 | info->flags = FBINFO_HWACCEL_YPAN | |
1446 | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | |
1447 | FBINFO_HWACCEL_IMAGEBLIT; |
1448 | info->pixmap.scan_align = 2; |
1449 | } else { |
1450 | info->pixmap.scan_align = 1; |
1451 | info->flags = FBINFO_HWACCEL_YPAN; |
1452 | } |
1453 | return 0; |
1454 | } |
1455 | |
1456 | static int i810fb_check_var(struct fb_var_screeninfo *var, |
1457 | struct fb_info *info) |
1458 | { |
1459 | int err; |
1460 | |
1461 | if (IS_DVT) { |
1462 | var->vmode &= ~FB_VMODE_MASK; |
1463 | var->vmode |= FB_VMODE_NONINTERLACED; |
1464 | } |
1465 | if (var->vmode & FB_VMODE_DOUBLE) { |
1466 | var->vmode &= ~FB_VMODE_MASK; |
1467 | var->vmode |= FB_VMODE_NONINTERLACED; |
1468 | } |
1469 | |
1470 | i810_round_off(var); |
1471 | if ((err = i810_check_params(var, info))) |
1472 | return err; |
1473 | |
1474 | i810fb_fill_var_timings(var); |
1475 | set_color_bitfields(var); |
1476 | return 0; |
1477 | } |
1478 | |
1479 | static int i810fb_cursor(struct fb_info *info, struct fb_cursor *cursor) |
1480 | { |
1481 | struct i810fb_par *par = info->par; |
1482 | u8 __iomem *mmio = par->mmio_start_virtual; |
1483 | |
1484 | if (par->dev_flags & LOCKUP) |
1485 | return -ENXIO; |
1486 | |
1487 | if (cursor->image.width > 64 || cursor->image.height > 64) |
1488 | return -ENXIO; |
1489 | |
1490 | if ((i810_readl(CURBASE, mmio) & 0xf) != par->cursor_heap.physical) { |
1491 | i810_init_cursor(par); |
1492 | cursor->set |= FB_CUR_SETALL; |
1493 | } |
1494 | |
1495 | i810_enable_cursor(mmio, OFF); |
1496 | |
1497 | if (cursor->set & FB_CUR_SETPOS) { |
1498 | u32 tmp; |
1499 | |
1500 | tmp = (cursor->image.dx - info->var.xoffset) & 0xffff; |
1501 | tmp |= (cursor->image.dy - info->var.yoffset) << 16; |
1502 | i810_writel(CURPOS, mmio, tmp); |
1503 | } |
1504 | |
1505 | if (cursor->set & FB_CUR_SETSIZE) |
1506 | i810_reset_cursor_image(par); |
1507 | |
1508 | if (cursor->set & FB_CUR_SETCMAP) |
1509 | i810_load_cursor_colors(fg: cursor->image.fg_color, |
1510 | bg: cursor->image.bg_color, |
1511 | info); |
1512 | |
1513 | if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) { |
1514 | int size = ((cursor->image.width + 7) >> 3) * |
1515 | cursor->image.height; |
1516 | int i; |
1517 | u8 *data = kmalloc(size: 64 * 8, GFP_ATOMIC); |
1518 | |
1519 | if (data == NULL) |
1520 | return -ENOMEM; |
1521 | |
1522 | switch (cursor->rop) { |
1523 | case ROP_XOR: |
1524 | for (i = 0; i < size; i++) |
1525 | data[i] = cursor->image.data[i] ^ cursor->mask[i]; |
1526 | break; |
1527 | case ROP_COPY: |
1528 | default: |
1529 | for (i = 0; i < size; i++) |
1530 | data[i] = cursor->image.data[i] & cursor->mask[i]; |
1531 | break; |
1532 | } |
1533 | |
1534 | i810_load_cursor_image(width: cursor->image.width, |
1535 | height: cursor->image.height, data, |
1536 | par); |
1537 | kfree(objp: data); |
1538 | } |
1539 | |
1540 | if (cursor->enable) |
1541 | i810_enable_cursor(mmio, ON); |
1542 | |
1543 | return 0; |
1544 | } |
1545 | |
1546 | static const struct fb_ops i810fb_ops = { |
1547 | .owner = THIS_MODULE, |
1548 | .fb_open = i810fb_open, |
1549 | .fb_release = i810fb_release, |
1550 | __FB_DEFAULT_IOMEM_OPS_RDWR, |
1551 | .fb_check_var = i810fb_check_var, |
1552 | .fb_set_par = i810fb_set_par, |
1553 | .fb_setcolreg = i810fb_setcolreg, |
1554 | .fb_blank = i810fb_blank, |
1555 | .fb_pan_display = i810fb_pan_display, |
1556 | .fb_fillrect = i810fb_fillrect, |
1557 | .fb_copyarea = i810fb_copyarea, |
1558 | .fb_imageblit = i810fb_imageblit, |
1559 | .fb_cursor = i810fb_cursor, |
1560 | .fb_sync = i810fb_sync, |
1561 | __FB_DEFAULT_IOMEM_OPS_MMAP, |
1562 | }; |
1563 | |
1564 | /*********************************************************************** |
1565 | * Power Management * |
1566 | ***********************************************************************/ |
1567 | static int i810fb_suspend(struct pci_dev *dev, pm_message_t mesg) |
1568 | { |
1569 | struct fb_info *info = pci_get_drvdata(pdev: dev); |
1570 | struct i810fb_par *par = info->par; |
1571 | |
1572 | par->cur_state = mesg.event; |
1573 | |
1574 | switch (mesg.event) { |
1575 | case PM_EVENT_FREEZE: |
1576 | case PM_EVENT_PRETHAW: |
1577 | dev->dev.power.power_state = mesg; |
1578 | return 0; |
1579 | } |
1580 | |
1581 | console_lock(); |
1582 | fb_set_suspend(info, state: 1); |
1583 | |
1584 | if (info->fbops->fb_sync) |
1585 | info->fbops->fb_sync(info); |
1586 | |
1587 | i810fb_blank(blank_mode: FB_BLANK_POWERDOWN, info); |
1588 | agp_unbind_memory(par->i810_gtt.i810_fb_memory); |
1589 | agp_unbind_memory(par->i810_gtt.i810_cursor_memory); |
1590 | |
1591 | pci_save_state(dev); |
1592 | pci_disable_device(dev); |
1593 | pci_set_power_state(dev, state: pci_choose_state(dev, state: mesg)); |
1594 | console_unlock(); |
1595 | |
1596 | return 0; |
1597 | } |
1598 | |
1599 | static int i810fb_resume(struct pci_dev *dev) |
1600 | { |
1601 | struct fb_info *info = pci_get_drvdata(pdev: dev); |
1602 | struct i810fb_par *par = info->par; |
1603 | int cur_state = par->cur_state; |
1604 | |
1605 | par->cur_state = PM_EVENT_ON; |
1606 | |
1607 | if (cur_state == PM_EVENT_FREEZE) { |
1608 | pci_set_power_state(dev, PCI_D0); |
1609 | return 0; |
1610 | } |
1611 | |
1612 | console_lock(); |
1613 | pci_set_power_state(dev, PCI_D0); |
1614 | pci_restore_state(dev); |
1615 | |
1616 | if (pci_enable_device(dev)) |
1617 | goto fail; |
1618 | |
1619 | pci_set_master(dev); |
1620 | agp_bind_memory(par->i810_gtt.i810_fb_memory, |
1621 | par->fb.offset); |
1622 | agp_bind_memory(par->i810_gtt.i810_cursor_memory, |
1623 | par->cursor_heap.offset); |
1624 | i810fb_set_par(info); |
1625 | fb_set_suspend (info, state: 0); |
1626 | info->fbops->fb_blank(VESA_NO_BLANKING, info); |
1627 | fail: |
1628 | console_unlock(); |
1629 | return 0; |
1630 | } |
1631 | /*********************************************************************** |
1632 | * AGP resource allocation * |
1633 | ***********************************************************************/ |
1634 | |
1635 | static void i810_fix_pointers(struct i810fb_par *par) |
1636 | { |
1637 | par->fb.physical = par->aperture.physical+(par->fb.offset << 12); |
1638 | par->fb.virtual = par->aperture.virtual+(par->fb.offset << 12); |
1639 | par->iring.physical = par->aperture.physical + |
1640 | (par->iring.offset << 12); |
1641 | par->iring.virtual = par->aperture.virtual + |
1642 | (par->iring.offset << 12); |
1643 | par->cursor_heap.virtual = par->aperture.virtual+ |
1644 | (par->cursor_heap.offset << 12); |
1645 | } |
1646 | |
1647 | static void i810_fix_offsets(struct i810fb_par *par) |
1648 | { |
1649 | if (vram + 1 > par->aperture.size >> 20) |
1650 | vram = (par->aperture.size >> 20) - 1; |
1651 | if (v_offset_default > (par->aperture.size >> 20)) |
1652 | v_offset_default = (par->aperture.size >> 20); |
1653 | if (vram + v_offset_default + 1 > par->aperture.size >> 20) |
1654 | v_offset_default = (par->aperture.size >> 20) - (vram + 1); |
1655 | |
1656 | par->fb.size = vram << 20; |
1657 | par->fb.offset = v_offset_default << 20; |
1658 | par->fb.offset >>= 12; |
1659 | |
1660 | par->iring.offset = par->fb.offset + (par->fb.size >> 12); |
1661 | par->iring.size = RINGBUFFER_SIZE; |
1662 | |
1663 | par->cursor_heap.offset = par->iring.offset + (RINGBUFFER_SIZE >> 12); |
1664 | par->cursor_heap.size = 4096; |
1665 | } |
1666 | |
1667 | static int i810_alloc_agp_mem(struct fb_info *info) |
1668 | { |
1669 | struct i810fb_par *par = info->par; |
1670 | int size; |
1671 | struct agp_bridge_data *bridge; |
1672 | |
1673 | i810_fix_offsets(par); |
1674 | size = par->fb.size + par->iring.size; |
1675 | |
1676 | if (!(bridge = agp_backend_acquire(par->dev))) { |
1677 | printk("i810fb_alloc_fbmem: cannot acquire agpgart\n" ); |
1678 | return -ENODEV; |
1679 | } |
1680 | if (!(par->i810_gtt.i810_fb_memory = |
1681 | agp_allocate_memory(bridge, size >> 12, AGP_NORMAL_MEMORY))) { |
1682 | printk("i810fb_alloc_fbmem: can't allocate framebuffer " |
1683 | "memory\n" ); |
1684 | agp_backend_release(bridge); |
1685 | return -ENOMEM; |
1686 | } |
1687 | if (agp_bind_memory(par->i810_gtt.i810_fb_memory, |
1688 | par->fb.offset)) { |
1689 | printk("i810fb_alloc_fbmem: can't bind framebuffer memory\n" ); |
1690 | agp_backend_release(bridge); |
1691 | return -EBUSY; |
1692 | } |
1693 | |
1694 | if (!(par->i810_gtt.i810_cursor_memory = |
1695 | agp_allocate_memory(bridge, par->cursor_heap.size >> 12, |
1696 | AGP_PHYSICAL_MEMORY))) { |
1697 | printk("i810fb_alloc_cursormem: can't allocate " |
1698 | "cursor memory\n" ); |
1699 | agp_backend_release(bridge); |
1700 | return -ENOMEM; |
1701 | } |
1702 | if (agp_bind_memory(par->i810_gtt.i810_cursor_memory, |
1703 | par->cursor_heap.offset)) { |
1704 | printk("i810fb_alloc_cursormem: cannot bind cursor memory\n" ); |
1705 | agp_backend_release(bridge); |
1706 | return -EBUSY; |
1707 | } |
1708 | |
1709 | par->cursor_heap.physical = par->i810_gtt.i810_cursor_memory->physical; |
1710 | |
1711 | i810_fix_pointers(par); |
1712 | |
1713 | agp_backend_release(bridge); |
1714 | |
1715 | return 0; |
1716 | } |
1717 | |
1718 | /*************************************************************** |
1719 | * Initialization * |
1720 | ***************************************************************/ |
1721 | |
1722 | /** |
1723 | * i810_init_monspecs |
1724 | * @info: pointer to device specific info structure |
1725 | * |
1726 | * DESCRIPTION: |
1727 | * Sets the user monitor's horizontal and vertical |
1728 | * frequency limits |
1729 | */ |
1730 | static void i810_init_monspecs(struct fb_info *info) |
1731 | { |
1732 | if (!hsync1) |
1733 | hsync1 = HFMIN; |
1734 | if (!hsync2) |
1735 | hsync2 = HFMAX; |
1736 | if (!info->monspecs.hfmax) |
1737 | info->monspecs.hfmax = hsync2; |
1738 | if (!info->monspecs.hfmin) |
1739 | info->monspecs.hfmin = hsync1; |
1740 | if (hsync2 < hsync1) |
1741 | info->monspecs.hfmin = hsync2; |
1742 | |
1743 | if (!vsync1) |
1744 | vsync1 = VFMIN; |
1745 | if (!vsync2) |
1746 | vsync2 = VFMAX; |
1747 | if (IS_DVT && vsync1 < 60) |
1748 | vsync1 = 60; |
1749 | if (!info->monspecs.vfmax) |
1750 | info->monspecs.vfmax = vsync2; |
1751 | if (!info->monspecs.vfmin) |
1752 | info->monspecs.vfmin = vsync1; |
1753 | if (vsync2 < vsync1) |
1754 | info->monspecs.vfmin = vsync2; |
1755 | } |
1756 | |
1757 | /** |
1758 | * i810_init_defaults - initializes default values to use |
1759 | * @par: pointer to i810fb_par structure |
1760 | * @info: pointer to current fb_info structure |
1761 | */ |
1762 | static void i810_init_defaults(struct i810fb_par *par, struct fb_info *info) |
1763 | { |
1764 | mutex_init(&par->open_lock); |
1765 | |
1766 | if (voffset) |
1767 | v_offset_default = voffset; |
1768 | else if (par->aperture.size > 32 * 1024 * 1024) |
1769 | v_offset_default = 16; |
1770 | else |
1771 | v_offset_default = 8; |
1772 | |
1773 | if (!vram) |
1774 | vram = 1; |
1775 | |
1776 | if (accel) |
1777 | par->dev_flags |= HAS_ACCELERATION; |
1778 | |
1779 | if (sync) |
1780 | par->dev_flags |= ALWAYS_SYNC; |
1781 | |
1782 | par->ddc_num = (ddc3 ? 3 : 2); |
1783 | |
1784 | if (bpp < 8) |
1785 | bpp = 8; |
1786 | |
1787 | par->i810fb_ops = i810fb_ops; |
1788 | |
1789 | if (xres) |
1790 | info->var.xres = xres; |
1791 | else |
1792 | info->var.xres = 640; |
1793 | |
1794 | if (yres) |
1795 | info->var.yres = yres; |
1796 | else |
1797 | info->var.yres = 480; |
1798 | |
1799 | if (!vyres) |
1800 | vyres = (vram << 20)/(info->var.xres*bpp >> 3); |
1801 | |
1802 | info->var.yres_virtual = vyres; |
1803 | info->var.bits_per_pixel = bpp; |
1804 | |
1805 | if (dcolor) |
1806 | info->var.nonstd = 1; |
1807 | |
1808 | if (par->dev_flags & HAS_ACCELERATION) |
1809 | info->var.accel_flags = 1; |
1810 | |
1811 | i810_init_monspecs(info); |
1812 | } |
1813 | |
1814 | /** |
1815 | * i810_init_device - initialize device |
1816 | * @par: pointer to i810fb_par structure |
1817 | */ |
1818 | static void i810_init_device(struct i810fb_par *par) |
1819 | { |
1820 | u8 reg; |
1821 | u8 __iomem *mmio = par->mmio_start_virtual; |
1822 | |
1823 | if (mtrr) |
1824 | par->wc_cookie= arch_phys_wc_add(base: (u32) par->aperture.physical, |
1825 | size: par->aperture.size); |
1826 | |
1827 | i810_init_cursor(par); |
1828 | |
1829 | /* mvo: enable external vga-connector (for laptops) */ |
1830 | if (extvga) { |
1831 | i810_writel(HVSYNC, mmio, 0); |
1832 | i810_writel(PWR_CLKC, mmio, 3); |
1833 | } |
1834 | |
1835 | pci_read_config_byte(dev: par->dev, where: 0x50, val: ®); |
1836 | reg &= FREQ_MASK; |
1837 | par->mem_freq = (reg) ? 133 : 100; |
1838 | |
1839 | } |
1840 | |
1841 | static int i810_allocate_pci_resource(struct i810fb_par *par, |
1842 | const struct pci_device_id *entry) |
1843 | { |
1844 | int err; |
1845 | |
1846 | if ((err = pci_enable_device(dev: par->dev))) { |
1847 | printk("i810fb_init: cannot enable device\n" ); |
1848 | return err; |
1849 | } |
1850 | par->res_flags |= PCI_DEVICE_ENABLED; |
1851 | |
1852 | if (pci_resource_len(par->dev, 0) > 512 * 1024) { |
1853 | par->aperture.physical = pci_resource_start(par->dev, 0); |
1854 | par->aperture.size = pci_resource_len(par->dev, 0); |
1855 | par->mmio_start_phys = pci_resource_start(par->dev, 1); |
1856 | } else { |
1857 | par->aperture.physical = pci_resource_start(par->dev, 1); |
1858 | par->aperture.size = pci_resource_len(par->dev, 1); |
1859 | par->mmio_start_phys = pci_resource_start(par->dev, 0); |
1860 | } |
1861 | if (!par->aperture.size) { |
1862 | printk("i810fb_init: device is disabled\n" ); |
1863 | return -ENOMEM; |
1864 | } |
1865 | |
1866 | if (!request_mem_region(par->aperture.physical, |
1867 | par->aperture.size, |
1868 | i810_pci_list[entry->driver_data])) { |
1869 | printk("i810fb_init: cannot request framebuffer region\n" ); |
1870 | return -ENODEV; |
1871 | } |
1872 | par->res_flags |= FRAMEBUFFER_REQ; |
1873 | |
1874 | par->aperture.virtual = ioremap_wc(offset: par->aperture.physical, |
1875 | size: par->aperture.size); |
1876 | if (!par->aperture.virtual) { |
1877 | printk("i810fb_init: cannot remap framebuffer region\n" ); |
1878 | return -ENODEV; |
1879 | } |
1880 | |
1881 | if (!request_mem_region(par->mmio_start_phys, |
1882 | MMIO_SIZE, |
1883 | i810_pci_list[entry->driver_data])) { |
1884 | printk("i810fb_init: cannot request mmio region\n" ); |
1885 | return -ENODEV; |
1886 | } |
1887 | par->res_flags |= MMIO_REQ; |
1888 | |
1889 | par->mmio_start_virtual = ioremap(offset: par->mmio_start_phys, |
1890 | MMIO_SIZE); |
1891 | if (!par->mmio_start_virtual) { |
1892 | printk("i810fb_init: cannot remap mmio region\n" ); |
1893 | return -ENODEV; |
1894 | } |
1895 | |
1896 | return 0; |
1897 | } |
1898 | |
1899 | static void i810fb_find_init_mode(struct fb_info *info) |
1900 | { |
1901 | struct fb_videomode mode; |
1902 | struct fb_var_screeninfo var; |
1903 | struct fb_monspecs *specs = &info->monspecs; |
1904 | int found = 0; |
1905 | #ifdef CONFIG_FB_I810_I2C |
1906 | int i; |
1907 | int err = 1; |
1908 | struct i810fb_par *par = info->par; |
1909 | #endif |
1910 | |
1911 | INIT_LIST_HEAD(list: &info->modelist); |
1912 | memset(&mode, 0, sizeof(struct fb_videomode)); |
1913 | var = info->var; |
1914 | #ifdef CONFIG_FB_I810_I2C |
1915 | i810_create_i2c_busses(par); |
1916 | |
1917 | for (i = 0; i < par->ddc_num + 1; i++) { |
1918 | err = i810_probe_i2c_connector(info, &par->edid, i); |
1919 | if (!err) |
1920 | break; |
1921 | } |
1922 | |
1923 | if (!err) |
1924 | printk("i810fb_init_pci: DDC probe successful\n" ); |
1925 | |
1926 | fb_edid_to_monspecs(par->edid, specs); |
1927 | |
1928 | if (specs->modedb == NULL) |
1929 | printk("i810fb_init_pci: Unable to get Mode Database\n" ); |
1930 | |
1931 | fb_videomode_to_modelist(specs->modedb, specs->modedb_len, |
1932 | &info->modelist); |
1933 | if (specs->modedb != NULL) { |
1934 | const struct fb_videomode *m; |
1935 | |
1936 | if (xres && yres) { |
1937 | if ((m = fb_find_best_mode(&var, &info->modelist))) { |
1938 | mode = *m; |
1939 | found = 1; |
1940 | } |
1941 | } |
1942 | |
1943 | if (!found) { |
1944 | m = fb_find_best_display(&info->monspecs, &info->modelist); |
1945 | mode = *m; |
1946 | found = 1; |
1947 | } |
1948 | |
1949 | fb_videomode_to_var(&var, &mode); |
1950 | } |
1951 | #endif |
1952 | if (mode_option) |
1953 | fb_find_mode(var: &var, info, mode_option, db: specs->modedb, |
1954 | dbsize: specs->modedb_len, default_mode: (found) ? &mode : NULL, |
1955 | default_bpp: info->var.bits_per_pixel); |
1956 | |
1957 | info->var = var; |
1958 | fb_destroy_modedb(modedb: specs->modedb); |
1959 | specs->modedb = NULL; |
1960 | } |
1961 | |
1962 | #ifndef MODULE |
1963 | static int i810fb_setup(char *options) |
1964 | { |
1965 | char *this_opt, *suffix = NULL; |
1966 | |
1967 | if (!options || !*options) |
1968 | return 0; |
1969 | |
1970 | while ((this_opt = strsep(&options, "," )) != NULL) { |
1971 | if (!strncmp(this_opt, "mtrr" , 4)) |
1972 | mtrr = true; |
1973 | else if (!strncmp(this_opt, "accel" , 5)) |
1974 | accel = true; |
1975 | else if (!strncmp(this_opt, "extvga" , 6)) |
1976 | extvga = true; |
1977 | else if (!strncmp(this_opt, "sync" , 4)) |
1978 | sync = true; |
1979 | else if (!strncmp(this_opt, "vram:" , 5)) |
1980 | vram = (simple_strtoul(this_opt+5, NULL, 0)); |
1981 | else if (!strncmp(this_opt, "voffset:" , 8)) |
1982 | voffset = (simple_strtoul(this_opt+8, NULL, 0)); |
1983 | else if (!strncmp(this_opt, "xres:" , 5)) |
1984 | xres = simple_strtoul(this_opt+5, NULL, 0); |
1985 | else if (!strncmp(this_opt, "yres:" , 5)) |
1986 | yres = simple_strtoul(this_opt+5, NULL, 0); |
1987 | else if (!strncmp(this_opt, "vyres:" , 6)) |
1988 | vyres = simple_strtoul(this_opt+6, NULL, 0); |
1989 | else if (!strncmp(this_opt, "bpp:" , 4)) |
1990 | bpp = simple_strtoul(this_opt+4, NULL, 0); |
1991 | else if (!strncmp(this_opt, "hsync1:" , 7)) { |
1992 | hsync1 = simple_strtoul(this_opt+7, &suffix, 0); |
1993 | if (strncmp(suffix, "H" , 1)) |
1994 | hsync1 *= 1000; |
1995 | } else if (!strncmp(this_opt, "hsync2:" , 7)) { |
1996 | hsync2 = simple_strtoul(this_opt+7, &suffix, 0); |
1997 | if (strncmp(suffix, "H" , 1)) |
1998 | hsync2 *= 1000; |
1999 | } else if (!strncmp(this_opt, "vsync1:" , 7)) |
2000 | vsync1 = simple_strtoul(this_opt+7, NULL, 0); |
2001 | else if (!strncmp(this_opt, "vsync2:" , 7)) |
2002 | vsync2 = simple_strtoul(this_opt+7, NULL, 0); |
2003 | else if (!strncmp(this_opt, "dcolor" , 6)) |
2004 | dcolor = true; |
2005 | else if (!strncmp(this_opt, "ddc3" , 4)) |
2006 | ddc3 = true; |
2007 | else |
2008 | mode_option = this_opt; |
2009 | } |
2010 | return 0; |
2011 | } |
2012 | #endif |
2013 | |
2014 | static int i810fb_init_pci(struct pci_dev *dev, |
2015 | const struct pci_device_id *entry) |
2016 | { |
2017 | struct fb_info *info; |
2018 | struct i810fb_par *par = NULL; |
2019 | struct fb_videomode mode; |
2020 | int err = -1, vfreq, hfreq, pixclock; |
2021 | |
2022 | err = aperture_remove_conflicting_pci_devices(pdev: dev, name: "i810fb" ); |
2023 | if (err) |
2024 | return err; |
2025 | |
2026 | info = framebuffer_alloc(size: sizeof(struct i810fb_par), dev: &dev->dev); |
2027 | if (!info) |
2028 | return -ENOMEM; |
2029 | |
2030 | par = info->par; |
2031 | par->dev = dev; |
2032 | |
2033 | if (!(info->pixmap.addr = kzalloc(size: 8*1024, GFP_KERNEL))) { |
2034 | i810fb_release_resource(info, par); |
2035 | return -ENOMEM; |
2036 | } |
2037 | info->pixmap.size = 8*1024; |
2038 | info->pixmap.buf_align = 8; |
2039 | info->pixmap.access_align = 32; |
2040 | info->pixmap.flags = FB_PIXMAP_SYSTEM; |
2041 | |
2042 | if ((err = i810_allocate_pci_resource(par, entry))) { |
2043 | i810fb_release_resource(info, par); |
2044 | return err; |
2045 | } |
2046 | |
2047 | i810_init_defaults(par, info); |
2048 | |
2049 | if ((err = i810_alloc_agp_mem(info))) { |
2050 | i810fb_release_resource(info, par); |
2051 | return err; |
2052 | } |
2053 | |
2054 | i810_init_device(par); |
2055 | |
2056 | info->screen_base = par->fb.virtual; |
2057 | info->fbops = &par->i810fb_ops; |
2058 | info->pseudo_palette = par->pseudo_palette; |
2059 | fb_alloc_cmap(cmap: &info->cmap, len: 256, transp: 0); |
2060 | i810fb_find_init_mode(info); |
2061 | |
2062 | if ((err = info->fbops->fb_check_var(&info->var, info))) { |
2063 | i810fb_release_resource(info, par); |
2064 | return err; |
2065 | } |
2066 | |
2067 | fb_var_to_videomode(mode: &mode, var: &info->var); |
2068 | fb_add_videomode(mode: &mode, head: &info->modelist); |
2069 | |
2070 | i810fb_init_ringbuffer(info); |
2071 | err = register_framebuffer(fb_info: info); |
2072 | |
2073 | if (err < 0) { |
2074 | i810fb_release_resource(info, par); |
2075 | printk("i810fb_init: cannot register framebuffer device\n" ); |
2076 | return err; |
2077 | } |
2078 | |
2079 | pci_set_drvdata(pdev: dev, data: info); |
2080 | pixclock = 1000000000/(info->var.pixclock); |
2081 | pixclock *= 1000; |
2082 | hfreq = pixclock/(info->var.xres + info->var.left_margin + |
2083 | info->var.hsync_len + info->var.right_margin); |
2084 | vfreq = hfreq/(info->var.yres + info->var.upper_margin + |
2085 | info->var.vsync_len + info->var.lower_margin); |
2086 | |
2087 | printk("I810FB: fb%d : %s v%d.%d.%d%s\n" |
2088 | "I810FB: Video RAM : %dK\n" |
2089 | "I810FB: Monitor : H: %d-%d KHz V: %d-%d Hz\n" |
2090 | "I810FB: Mode : %dx%d-%dbpp@%dHz\n" , |
2091 | info->node, |
2092 | i810_pci_list[entry->driver_data], |
2093 | VERSION_MAJOR, VERSION_MINOR, VERSION_TEENIE, BRANCH_VERSION, |
2094 | (int) par->fb.size>>10, info->monspecs.hfmin/1000, |
2095 | info->monspecs.hfmax/1000, info->monspecs.vfmin, |
2096 | info->monspecs.vfmax, info->var.xres, |
2097 | info->var.yres, info->var.bits_per_pixel, vfreq); |
2098 | return 0; |
2099 | } |
2100 | |
2101 | /*************************************************************** |
2102 | * De-initialization * |
2103 | ***************************************************************/ |
2104 | |
2105 | static void i810fb_release_resource(struct fb_info *info, |
2106 | struct i810fb_par *par) |
2107 | { |
2108 | struct gtt_data *gtt = &par->i810_gtt; |
2109 | arch_phys_wc_del(handle: par->wc_cookie); |
2110 | |
2111 | i810_delete_i2c_busses(par); |
2112 | |
2113 | if (par->i810_gtt.i810_cursor_memory) |
2114 | agp_free_memory(gtt->i810_cursor_memory); |
2115 | if (par->i810_gtt.i810_fb_memory) |
2116 | agp_free_memory(gtt->i810_fb_memory); |
2117 | |
2118 | if (par->mmio_start_virtual) |
2119 | iounmap(addr: par->mmio_start_virtual); |
2120 | if (par->aperture.virtual) |
2121 | iounmap(addr: par->aperture.virtual); |
2122 | kfree(objp: par->edid); |
2123 | if (par->res_flags & FRAMEBUFFER_REQ) |
2124 | release_mem_region(par->aperture.physical, |
2125 | par->aperture.size); |
2126 | if (par->res_flags & MMIO_REQ) |
2127 | release_mem_region(par->mmio_start_phys, MMIO_SIZE); |
2128 | |
2129 | framebuffer_release(info); |
2130 | |
2131 | } |
2132 | |
2133 | static void i810fb_remove_pci(struct pci_dev *dev) |
2134 | { |
2135 | struct fb_info *info = pci_get_drvdata(pdev: dev); |
2136 | struct i810fb_par *par = info->par; |
2137 | |
2138 | unregister_framebuffer(fb_info: info); |
2139 | i810fb_release_resource(info, par); |
2140 | printk("cleanup_module: unloaded i810 framebuffer device\n" ); |
2141 | } |
2142 | |
2143 | #ifndef MODULE |
2144 | static int i810fb_init(void) |
2145 | { |
2146 | char *option = NULL; |
2147 | |
2148 | if (fb_modesetting_disabled(drvname: "i810fb" )) |
2149 | return -ENODEV; |
2150 | |
2151 | if (fb_get_options(name: "i810fb" , option: &option)) |
2152 | return -ENODEV; |
2153 | i810fb_setup(options: option); |
2154 | |
2155 | return pci_register_driver(&i810fb_driver); |
2156 | } |
2157 | #endif |
2158 | |
2159 | /********************************************************************* |
2160 | * Modularization * |
2161 | *********************************************************************/ |
2162 | |
2163 | #ifdef MODULE |
2164 | |
2165 | static int i810fb_init(void) |
2166 | { |
2167 | if (fb_modesetting_disabled("i810fb" )) |
2168 | return -ENODEV; |
2169 | |
2170 | hsync1 *= 1000; |
2171 | hsync2 *= 1000; |
2172 | |
2173 | return pci_register_driver(&i810fb_driver); |
2174 | } |
2175 | |
2176 | module_param(vram, int, 0); |
2177 | MODULE_PARM_DESC(vram, "System RAM to allocate to framebuffer in MiB" |
2178 | " (default=4)" ); |
2179 | module_param(voffset, int, 0); |
2180 | MODULE_PARM_DESC(voffset, "at what offset to place start of framebuffer " |
2181 | "memory (0 to maximum aperture size), in MiB (default = 48)" ); |
2182 | module_param(bpp, int, 0); |
2183 | MODULE_PARM_DESC(bpp, "Color depth for display in bits per pixel" |
2184 | " (default = 8)" ); |
2185 | module_param(xres, int, 0); |
2186 | MODULE_PARM_DESC(xres, "Horizontal resolution in pixels (default = 640)" ); |
2187 | module_param(yres, int, 0); |
2188 | MODULE_PARM_DESC(yres, "Vertical resolution in scanlines (default = 480)" ); |
2189 | module_param(vyres,int, 0); |
2190 | MODULE_PARM_DESC(vyres, "Virtual vertical resolution in scanlines" |
2191 | " (default = 480)" ); |
2192 | module_param(hsync1, int, 0); |
2193 | MODULE_PARM_DESC(hsync1, "Minimum horizontal frequency of monitor in KHz" |
2194 | " (default = 29)" ); |
2195 | module_param(hsync2, int, 0); |
2196 | MODULE_PARM_DESC(hsync2, "Maximum horizontal frequency of monitor in KHz" |
2197 | " (default = 30)" ); |
2198 | module_param(vsync1, int, 0); |
2199 | MODULE_PARM_DESC(vsync1, "Minimum vertical frequency of monitor in Hz" |
2200 | " (default = 50)" ); |
2201 | module_param(vsync2, int, 0); |
2202 | MODULE_PARM_DESC(vsync2, "Maximum vertical frequency of monitor in Hz" |
2203 | " (default = 60)" ); |
2204 | module_param(accel, bool, 0); |
2205 | MODULE_PARM_DESC(accel, "Use Acceleration (BLIT) engine (default = 0)" ); |
2206 | module_param(mtrr, bool, 0); |
2207 | MODULE_PARM_DESC(mtrr, "Use MTRR (default = 0)" ); |
2208 | module_param(extvga, bool, 0); |
2209 | MODULE_PARM_DESC(extvga, "Enable external VGA connector (default = 0)" ); |
2210 | module_param(sync, bool, 0); |
2211 | MODULE_PARM_DESC(sync, "wait for accel engine to finish drawing" |
2212 | " (default = 0)" ); |
2213 | module_param(dcolor, bool, 0); |
2214 | MODULE_PARM_DESC(dcolor, "use DirectColor visuals" |
2215 | " (default = 0 = TrueColor)" ); |
2216 | module_param(ddc3, bool, 0); |
2217 | MODULE_PARM_DESC(ddc3, "Probe DDC bus 3 (default = 0 = no)" ); |
2218 | module_param(mode_option, charp, 0); |
2219 | MODULE_PARM_DESC(mode_option, "Specify initial video mode" ); |
2220 | |
2221 | MODULE_AUTHOR("Tony A. Daplas" ); |
2222 | MODULE_DESCRIPTION("Framebuffer device for the Intel 810/815 and" |
2223 | " compatible cards" ); |
2224 | MODULE_LICENSE("GPL" ); |
2225 | |
2226 | static void __exit i810fb_exit(void) |
2227 | { |
2228 | pci_unregister_driver(&i810fb_driver); |
2229 | } |
2230 | module_exit(i810fb_exit); |
2231 | |
2232 | #endif /* MODULE */ |
2233 | |
2234 | module_init(i810fb_init); |
2235 | |