blob: f7e2d5add83172239d2a805c500d2e6fcb353ec0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
37#define CIRRUSFB_VERSION "2.0-pre2"
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/module.h>
40#include <linux/kernel.h>
41#include <linux/errno.h>
42#include <linux/string.h>
43#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/slab.h>
45#include <linux/delay.h>
46#include <linux/fb.h>
47#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <asm/pgtable.h>
49
50#ifdef CONFIG_ZORRO
51#include <linux/zorro.h>
52#endif
53#ifdef CONFIG_PCI
54#include <linux/pci.h>
55#endif
56#ifdef CONFIG_AMIGA
57#include <asm/amigahw.h>
58#endif
59#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110060#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070061#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#else
63#define isPReP 0
64#endif
65
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070066#include <video/vga.h>
67#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/*****************************************************************
70 *
71 * debugging and utility macros
72 *
73 */
74
75/* enable debug output? */
76/* #define CIRRUSFB_DEBUG 1 */
77
78/* disable runtime assertions? */
79/* #define CIRRUSFB_NDEBUG */
80
81/* debug output */
82#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -070083#define DPRINTK(fmt, args...) \
84 printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#else
86#define DPRINTK(fmt, args...)
87#endif
88
89/* debugging assertions */
90#ifndef CIRRUSFB_NDEBUG
91#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070092 if (!(expr)) { \
93 printk("Assertion failed! %s,%s,%s,line=%d\n", \
94 #expr, __FILE__, __FUNCTION__, __LINE__); \
95 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070096#else
97#define assert(expr)
98#endif
99
Krzysztof Helt8503df62007-10-16 01:29:08 -0700100#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*****************************************************************
103 *
104 * chipset information
105 *
106 */
107
108/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700109enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 BT_NONE = 0,
111 BT_SD64,
112 BT_PICCOLO,
113 BT_PICASSO,
114 BT_SPECTRUM,
115 BT_PICASSO4, /* GD5446 */
116 BT_ALPINE, /* GD543x/4x */
117 BT_GD5480,
118 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700119};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * per-board-type information, used for enumerating and abstracting
123 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700124 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 * use direct indexing on this array
126 * NOTE: '__initdata' cannot be used as some of this info
127 * is required at runtime. Maybe separate into an init-only and
128 * a run-time table?
129 */
130static const struct cirrusfb_board_info_rec {
131 char *name; /* ASCII name of chipset */
132 long maxclock[5]; /* maximum video clock */
133 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700134 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
135 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700136 /* construct bit 19 of screen start address */
137 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139 /* initial SR07 value, then for each mode */
140 unsigned char sr07;
141 unsigned char sr07_1bpp;
142 unsigned char sr07_1bpp_mux;
143 unsigned char sr07_8bpp;
144 unsigned char sr07_8bpp_mux;
145
146 unsigned char sr1f; /* SR1F VGA initial register value */
147} cirrusfb_board_info[] = {
148 [BT_SD64] = {
149 .name = "CL SD64",
150 .maxclock = {
151 /* guess */
152 /* the SD64/P4 have a higher max. videoclock */
153 140000, 140000, 140000, 140000, 140000,
154 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700155 .init_sr07 = true,
156 .init_sr1f = true,
157 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 .sr07 = 0xF0,
159 .sr07_1bpp = 0xF0,
160 .sr07_8bpp = 0xF1,
161 .sr1f = 0x20
162 },
163 [BT_PICCOLO] = {
164 .name = "CL Piccolo",
165 .maxclock = {
166 /* guess */
167 90000, 90000, 90000, 90000, 90000
168 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700169 .init_sr07 = true,
170 .init_sr1f = true,
171 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .sr07 = 0x80,
173 .sr07_1bpp = 0x80,
174 .sr07_8bpp = 0x81,
175 .sr1f = 0x22
176 },
177 [BT_PICASSO] = {
178 .name = "CL Picasso",
179 .maxclock = {
180 /* guess */
181 90000, 90000, 90000, 90000, 90000
182 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700183 .init_sr07 = true,
184 .init_sr1f = true,
185 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 .sr07 = 0x20,
187 .sr07_1bpp = 0x20,
188 .sr07_8bpp = 0x21,
189 .sr1f = 0x22
190 },
191 [BT_SPECTRUM] = {
192 .name = "CL Spectrum",
193 .maxclock = {
194 /* guess */
195 90000, 90000, 90000, 90000, 90000
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = true,
199 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x80,
201 .sr07_1bpp = 0x80,
202 .sr07_8bpp = 0x81,
203 .sr1f = 0x22
204 },
205 [BT_PICASSO4] = {
206 .name = "CL Picasso4",
207 .maxclock = {
208 135100, 135100, 85500, 85500, 0
209 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700210 .init_sr07 = true,
211 .init_sr1f = false,
212 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 .sr07 = 0x20,
214 .sr07_1bpp = 0x20,
215 .sr07_8bpp = 0x21,
216 .sr1f = 0
217 },
218 [BT_ALPINE] = {
219 .name = "CL Alpine",
220 .maxclock = {
221 /* for the GD5430. GD5446 can do more... */
222 85500, 85500, 50000, 28500, 0
223 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700224 .init_sr07 = true,
225 .init_sr1f = true,
226 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .sr07 = 0xA0,
228 .sr07_1bpp = 0xA1,
229 .sr07_1bpp_mux = 0xA7,
230 .sr07_8bpp = 0xA1,
231 .sr07_8bpp_mux = 0xA7,
232 .sr1f = 0x1C
233 },
234 [BT_GD5480] = {
235 .name = "CL GD5480",
236 .maxclock = {
237 135100, 200000, 200000, 135100, 135100
238 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700239 .init_sr07 = true,
240 .init_sr1f = true,
241 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 .sr07 = 0x10,
243 .sr07_1bpp = 0x11,
244 .sr07_8bpp = 0x11,
245 .sr1f = 0x1C
246 },
247 [BT_LAGUNA] = {
248 .name = "CL Laguna",
249 .maxclock = {
250 /* guess */
251 135100, 135100, 135100, 135100, 135100,
252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329struct cirrusfb_regs {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 long freq;
331 long nom;
332 long den;
333 long div;
334 long multiplexing;
335 long mclk;
336 long divMCLK;
337
338 long HorizRes; /* The x resolution in pixel */
339 long HorizTotal;
340 long HorizDispEnd;
341 long HorizBlankStart;
342 long HorizBlankEnd;
343 long HorizSyncStart;
344 long HorizSyncEnd;
345
346 long VertRes; /* the physical y resolution in scanlines */
347 long VertTotal;
348 long VertDispEnd;
349 long VertSyncStart;
350 long VertSyncEnd;
351 long VertBlankStart;
352 long VertBlankEnd;
353};
354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700356enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700357 CRT,
358 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700359};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362/* info about board */
363struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700365 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 unsigned char SFR; /* Shadow of special function register */
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 struct cirrusfb_regs currentmode;
369 int blank_mode;
370
Antonino A. Daplas49d5c7b2005-11-29 19:34:43 -0800371 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373#ifdef CONFIG_ZORRO
374 struct zorro_dev *zdev;
375#endif
376#ifdef CONFIG_PCI
377 struct pci_dev *pdev;
378#endif
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700379 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380};
381
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382static unsigned cirrusfb_def_mode = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700383static int noaccel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385/*
386 * Predefined Video Modes
387 */
388
389static const struct {
390 const char *name;
391 struct fb_var_screeninfo var;
392} cirrusfb_predefined[] = {
393 {
394 /* autodetect mode */
395 .name = "Autodetect",
396 }, {
397 /* 640x480, 31.25 kHz, 60 Hz, 25 MHz PixClock */
398 .name = "640x480",
399 .var = {
400 .xres = 640,
401 .yres = 480,
402 .xres_virtual = 640,
403 .yres_virtual = 480,
404 .bits_per_pixel = 8,
405 .red = { .length = 8 },
406 .green = { .length = 8 },
407 .blue = { .length = 8 },
408 .width = -1,
409 .height = -1,
410 .pixclock = 40000,
411 .left_margin = 48,
412 .right_margin = 16,
413 .upper_margin = 32,
414 .lower_margin = 8,
415 .hsync_len = 96,
416 .vsync_len = 4,
Krzysztof Helt8503df62007-10-16 01:29:08 -0700417 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 .vmode = FB_VMODE_NONINTERLACED
419 }
420 }, {
421 /* 800x600, 48 kHz, 76 Hz, 50 MHz PixClock */
422 .name = "800x600",
423 .var = {
424 .xres = 800,
425 .yres = 600,
426 .xres_virtual = 800,
427 .yres_virtual = 600,
428 .bits_per_pixel = 8,
429 .red = { .length = 8 },
430 .green = { .length = 8 },
431 .blue = { .length = 8 },
432 .width = -1,
433 .height = -1,
434 .pixclock = 20000,
435 .left_margin = 128,
436 .right_margin = 16,
437 .upper_margin = 24,
438 .lower_margin = 2,
439 .hsync_len = 96,
440 .vsync_len = 6,
441 .vmode = FB_VMODE_NONINTERLACED
442 }
443 }, {
444 /*
445 * Modeline from XF86Config:
446 * Mode "1024x768" 80 1024 1136 1340 1432 768 770 774 805
447 */
448 /* 1024x768, 55.8 kHz, 70 Hz, 80 MHz PixClock */
449 .name = "1024x768",
450 .var = {
451 .xres = 1024,
452 .yres = 768,
453 .xres_virtual = 1024,
454 .yres_virtual = 768,
455 .bits_per_pixel = 8,
456 .red = { .length = 8 },
457 .green = { .length = 8 },
458 .blue = { .length = 8 },
459 .width = -1,
460 .height = -1,
461 .pixclock = 12500,
462 .left_margin = 144,
463 .right_margin = 32,
464 .upper_margin = 30,
465 .lower_margin = 2,
466 .hsync_len = 192,
467 .vsync_len = 6,
468 .vmode = FB_VMODE_NONINTERLACED
469 }
470 }
471};
472
473#define NUM_TOTAL_MODES ARRAY_SIZE(cirrusfb_predefined)
474
475/****************************************************************************/
476/**** BEGIN PROTOTYPES ******************************************************/
477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700479static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700481static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482#endif
483
Krzysztof Helt8503df62007-10-16 01:29:08 -0700484static int cirrusfb_open(struct fb_info *info, int user);
485static int cirrusfb_release(struct fb_info *info, int user);
486static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
487 unsigned blue, unsigned transp,
488 struct fb_info *info);
489static int cirrusfb_check_var(struct fb_var_screeninfo *var,
490 struct fb_info *info);
491static int cirrusfb_set_par(struct fb_info *info);
492static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
493 struct fb_info *info);
494static int cirrusfb_blank(int blank_mode, struct fb_info *info);
495static void cirrusfb_fillrect(struct fb_info *info,
496 const struct fb_fillrect *region);
497static void cirrusfb_copyarea(struct fb_info *info,
498 const struct fb_copyarea *area);
499static void cirrusfb_imageblit(struct fb_info *info,
500 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
502/* function table of the above functions */
503static struct fb_ops cirrusfb_ops = {
504 .owner = THIS_MODULE,
505 .fb_open = cirrusfb_open,
506 .fb_release = cirrusfb_release,
507 .fb_setcolreg = cirrusfb_setcolreg,
508 .fb_check_var = cirrusfb_check_var,
509 .fb_set_par = cirrusfb_set_par,
510 .fb_pan_display = cirrusfb_pan_display,
511 .fb_blank = cirrusfb_blank,
512 .fb_fillrect = cirrusfb_fillrect,
513 .fb_copyarea = cirrusfb_copyarea,
514 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515};
516
517/*--- Hardware Specific Routines -------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700518static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700520 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700522static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700523static void switch_monitor(struct cirrusfb_info *cinfo, int on);
524static void WGen(const struct cirrusfb_info *cinfo,
525 int regnum, unsigned char val);
526static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
527static void AttrOn(const struct cirrusfb_info *cinfo);
528static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
529static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
530static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
531static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
532 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700534static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
535 unsigned char *red, unsigned char *green,
536 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700538static void cirrusfb_WaitBLT(u8 __iomem *regbase);
539static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
540 u_short curx, u_short cury,
541 u_short destx, u_short desty,
542 u_short width, u_short height,
543 u_short line_length);
544static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
545 u_short x, u_short y,
546 u_short width, u_short height,
547 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548
Krzysztof Helt8503df62007-10-16 01:29:08 -0700549static void bestclock(long freq, long *best,
550 long *nom, long *den,
551 long *div, long maxfreq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
553#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700554static void cirrusfb_dump(void);
555static void cirrusfb_dbg_reg_dump(caddr_t regbase);
556static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700557 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700558static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559#endif /* CIRRUSFB_DEBUG */
560
561/*** END PROTOTYPES ********************************************************/
562/*****************************************************************************/
563/*** BEGIN Interface Used by the World ***************************************/
564
Krzysztof Helt8503df62007-10-16 01:29:08 -0700565static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
567/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700568static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
570 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700571 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 return 0;
573}
574
575/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700576static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
578 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700579 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 return 0;
581}
582
583/**** END Interface used by the World *************************************/
584/****************************************************************************/
585/**** BEGIN Hardware specific Routines **************************************/
586
587/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700588static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
590 long mclk;
591
Krzysztof Helt8503df62007-10-16 01:29:08 -0700592 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
594 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
595 * Assume a 64-bit data path for now. The formula is:
596 * ((B * PCLK * 2)/W) * 1.2
597 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
598 mclk = ((bpp / 8) * freq * 2) / 4;
599 mclk = (mclk * 12) / 10;
600 if (mclk < 50000)
601 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700602 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
604 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
605 mclk = ((mclk * 16) / 14318);
606 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700607 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
609 /* Determine if we should use MCLK instead of VCLK, and if so, what we
610 * should divide it by to get VCLK */
611 switch (freq) {
612 case 24751 ... 25249:
613 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700614 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 break;
616 case 49501 ... 50499:
617 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700618 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 break;
620 default:
621 *div = 0;
622 break;
623 }
624
625 return mclk;
626}
627
628static int cirrusfb_check_var(struct fb_var_screeninfo *var,
629 struct fb_info *info)
630{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 int nom, den; /* translyting from pixels->bytes */
632 int yres, i;
633 static struct { int xres, yres; } modes[] =
634 { { 1600, 1280 },
635 { 1280, 1024 },
636 { 1024, 768 },
637 { 800, 600 },
638 { 640, 480 },
639 { -1, -1 } };
640
641 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700642 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 nom = 4;
644 den = 8;
645 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700646 case 8:
647 case 16:
648 case 24:
649 case 32:
650 nom = var->bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 den = 1;
652 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700654 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
655 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700657 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 return -EINVAL;
659 }
660
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700661 if (var->xres * nom / den * var->yres > info->screen_size) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700662 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
663 "resolution too high to fit into video memory!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700665 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 return -EINVAL;
667 }
668
669 /* use highest possible virtual resolution */
670 if (var->xres_virtual == -1 &&
671 var->yres_virtual == -1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700672 printk(KERN_INFO
673 "cirrusfb: using maximum available virtual resolution\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 for (i = 0; modes[i].xres != -1; i++) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700675 int size = modes[i].xres * nom / den * modes[i].yres;
676 if (size < info->screen_size / 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 break;
678 }
679 if (modes[i].xres == -1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700680 printk(KERN_ERR "cirrusfb: could not find a virtual "
681 "resolution that fits into video memory!!\n");
682 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 return -EINVAL;
684 }
685 var->xres_virtual = modes[i].xres;
686 var->yres_virtual = modes[i].yres;
687
Krzysztof Helt8503df62007-10-16 01:29:08 -0700688 printk(KERN_INFO "cirrusfb: virtual resolution set to "
689 "maximum of %dx%d\n", var->xres_virtual,
690 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 }
692
693 if (var->xres_virtual < var->xres)
694 var->xres_virtual = var->xres;
695 if (var->yres_virtual < var->yres)
696 var->yres_virtual = var->yres;
697
698 if (var->xoffset < 0)
699 var->xoffset = 0;
700 if (var->yoffset < 0)
701 var->yoffset = 0;
702
703 /* truncate xoffset and yoffset to maximum if too high */
704 if (var->xoffset > var->xres_virtual - var->xres)
705 var->xoffset = var->xres_virtual - var->xres - 1;
706 if (var->yoffset > var->yres_virtual - var->yres)
707 var->yoffset = var->yres_virtual - var->yres - 1;
708
709 switch (var->bits_per_pixel) {
710 case 1:
711 var->red.offset = 0;
712 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700713 var->green = var->red;
714 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 break;
716
717 case 8:
718 var->red.offset = 0;
719 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700720 var->green = var->red;
721 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 break;
723
724 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700725 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 var->red.offset = 2;
727 var->green.offset = -3;
728 var->blue.offset = 8;
729 } else {
730 var->red.offset = 10;
731 var->green.offset = 5;
732 var->blue.offset = 0;
733 }
734 var->red.length = 5;
735 var->green.length = 5;
736 var->blue.length = 5;
737 break;
738
739 case 24:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700741 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 var->red.offset = 8;
743 var->green.offset = 16;
744 var->blue.offset = 24;
745 } else {
746 var->red.offset = 16;
747 var->green.offset = 8;
748 var->blue.offset = 0;
749 }
750 var->red.length = 8;
751 var->green.length = 8;
752 var->blue.length = 8;
753 break;
754
755 default:
756 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700757 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 /* should never occur */
759 break;
760 }
761
762 var->red.msb_right =
763 var->green.msb_right =
764 var->blue.msb_right =
765 var->transp.offset =
766 var->transp.length =
767 var->transp.msb_right = 0;
768
769 yres = var->yres;
770 if (var->vmode & FB_VMODE_DOUBLE)
771 yres *= 2;
772 else if (var->vmode & FB_VMODE_INTERLACED)
773 yres = (yres + 1) / 2;
774
775 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700776 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
777 "special treatment required! (TODO)\n");
778 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 return -EINVAL;
780 }
781
782 return 0;
783}
784
Krzysztof Helt8503df62007-10-16 01:29:08 -0700785static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700787 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
789 long freq;
790 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700791 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 struct cirrusfb_info *cinfo = info->par;
793 int xres, hfront, hsync, hback;
794 int yres, vfront, vsync, vback;
795
Krzysztof Helt8503df62007-10-16 01:29:08 -0700796 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700798 info->fix.line_length = var->xres_virtual / 8;
799 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 break;
801
802 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700803 info->fix.line_length = var->xres_virtual;
804 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 break;
806
807 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 case 24:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700810 info->fix.line_length = var->xres_virtual * maxclockidx;
811 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 break;
813
814 default:
815 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700816 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 /* should never occur */
818 break;
819 }
820
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700821 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
823 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700824 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Krzysztof Helt8503df62007-10-16 01:29:08 -0700826 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
828 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
829 regs->multiplexing = 0;
830
831 /* If the frequency is greater than we can support, we might be able
832 * to use multiplexing for the video mode */
833 if (freq > maxclock) {
834 switch (cinfo->btype) {
835 case BT_ALPINE:
836 case BT_GD5480:
837 regs->multiplexing = 1;
838 break;
839
840 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700841 printk(KERN_ERR "cirrusfb: Frequency greater "
842 "than maxclock (%ld kHz)\n", maxclock);
843 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 return -EINVAL;
845 }
846 }
847#if 0
848 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
849 * the VCLK is double the pixel clock. */
850 switch (var->bits_per_pixel) {
851 case 16:
852 case 32:
853 if (regs->HorizRes <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700854 /* Xbh has this type of clock for 32-bit */
855 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 break;
857 }
858#endif
859
Krzysztof Helt8503df62007-10-16 01:29:08 -0700860 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
861 maxclock);
862 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
863 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
865 xres = var->xres;
866 hfront = var->right_margin;
867 hsync = var->hsync_len;
868 hback = var->left_margin;
869
870 yres = var->yres;
871 vfront = var->lower_margin;
872 vsync = var->vsync_len;
873 vback = var->upper_margin;
874
875 if (var->vmode & FB_VMODE_DOUBLE) {
876 yres *= 2;
877 vfront *= 2;
878 vsync *= 2;
879 vback *= 2;
880 } else if (var->vmode & FB_VMODE_INTERLACED) {
881 yres = (yres + 1) / 2;
882 vfront = (vfront + 1) / 2;
883 vsync = (vsync + 1) / 2;
884 vback = (vback + 1) / 2;
885 }
886 regs->HorizRes = xres;
887 regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
888 regs->HorizDispEnd = xres / 8 - 1;
889 regs->HorizBlankStart = xres / 8;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700890 /* does not count with "-5" */
891 regs->HorizBlankEnd = regs->HorizTotal + 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 regs->HorizSyncStart = (xres + hfront) / 8 + 1;
893 regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
894
895 regs->VertRes = yres;
896 regs->VertTotal = yres + vfront + vsync + vback - 2;
897 regs->VertDispEnd = yres - 1;
898 regs->VertBlankStart = yres;
899 regs->VertBlankEnd = regs->VertTotal;
900 regs->VertSyncStart = yres + vfront - 1;
901 regs->VertSyncEnd = yres + vfront + vsync - 1;
902
903 if (regs->VertRes >= 1024) {
904 regs->VertTotal /= 2;
905 regs->VertSyncStart /= 2;
906 regs->VertSyncEnd /= 2;
907 regs->VertDispEnd /= 2;
908 }
909 if (regs->multiplexing) {
910 regs->HorizTotal /= 2;
911 regs->HorizSyncStart /= 2;
912 regs->HorizSyncEnd /= 2;
913 regs->HorizDispEnd /= 2;
914 }
915
916 return 0;
917}
918
Krzysztof Helt8503df62007-10-16 01:29:08 -0700919static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
920 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700922 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
924 if (div == 2) {
925 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700926 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
927 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
928 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 } else if (div == 1) {
930 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
932 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
933 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700935 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 }
937}
938
939/*************************************************************************
940 cirrusfb_set_par_foo()
941
942 actually writes the values for a new video mode into the hardware,
943**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700944static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945{
946 struct cirrusfb_info *cinfo = info->par;
947 struct fb_var_screeninfo *var = &info->var;
948 struct cirrusfb_regs regs;
949 u8 __iomem *regbase = cinfo->regbase;
950 unsigned char tmp;
951 int offset = 0, err;
952 const struct cirrusfb_board_info_rec *bi;
953
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 DPRINTK("ENTER\n");
955 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700957 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700959 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700962 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 /* should never happen */
964 DPRINTK("mode change aborted. invalid var.\n");
965 return -EINVAL;
966 }
967
968 bi = &cirrusfb_board_info[cinfo->btype];
969
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700971 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
973 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700974 DPRINTK("CRT0: %ld\n", regs.HorizTotal);
975 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
Krzysztof Helt8503df62007-10-16 01:29:08 -0700977 DPRINTK("CRT1: %ld\n", regs.HorizDispEnd);
978 vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
Krzysztof Helt8503df62007-10-16 01:29:08 -0700980 DPRINTK("CRT2: %ld\n", regs.HorizBlankStart);
981 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
Krzysztof Helt8503df62007-10-16 01:29:08 -0700983 /* + 128: Compatible read */
984 DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);
985 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
986 128 + (regs.HorizBlankEnd % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
Krzysztof Helt8503df62007-10-16 01:29:08 -0700988 DPRINTK("CRT4: %ld\n", regs.HorizSyncStart);
989 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
991 tmp = regs.HorizSyncEnd % 32;
992 if (regs.HorizBlankEnd & 32)
993 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700994 DPRINTK("CRT5: %d\n", tmp);
995 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 DPRINTK("CRT6: %ld\n", regs.VertTotal & 0xff);
998 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
1000 tmp = 16; /* LineCompare bit #9 */
1001 if (regs.VertTotal & 256)
1002 tmp |= 1;
1003 if (regs.VertDispEnd & 256)
1004 tmp |= 2;
1005 if (regs.VertSyncStart & 256)
1006 tmp |= 4;
1007 if (regs.VertBlankStart & 256)
1008 tmp |= 8;
1009 if (regs.VertTotal & 512)
1010 tmp |= 32;
1011 if (regs.VertDispEnd & 512)
1012 tmp |= 64;
1013 if (regs.VertSyncStart & 512)
1014 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001015 DPRINTK("CRT7: %d\n", tmp);
1016 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
1018 tmp = 0x40; /* LineCompare bit #8 */
1019 if (regs.VertBlankStart & 512)
1020 tmp |= 0x20;
1021 if (var->vmode & FB_VMODE_DOUBLE)
1022 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001023 DPRINTK("CRT9: %d\n", tmp);
1024 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
Krzysztof Helt8503df62007-10-16 01:29:08 -07001026 DPRINTK("CRT10: %ld\n", regs.VertSyncStart & 0xff);
1027 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, regs.VertSyncStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028
Krzysztof Helt8503df62007-10-16 01:29:08 -07001029 DPRINTK("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
1030 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, regs.VertSyncEnd % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031
Krzysztof Helt8503df62007-10-16 01:29:08 -07001032 DPRINTK("CRT12: %ld\n", regs.VertDispEnd & 0xff);
1033 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, regs.VertDispEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034
Krzysztof Helt8503df62007-10-16 01:29:08 -07001035 DPRINTK("CRT15: %ld\n", regs.VertBlankStart & 0xff);
1036 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, regs.VertBlankStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037
Krzysztof Helt8503df62007-10-16 01:29:08 -07001038 DPRINTK("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
1039 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, regs.VertBlankEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
Krzysztof Helt8503df62007-10-16 01:29:08 -07001041 DPRINTK("CRT18: 0xff\n");
1042 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043
1044 tmp = 0;
1045 if (var->vmode & FB_VMODE_INTERLACED)
1046 tmp |= 1;
1047 if (regs.HorizBlankEnd & 64)
1048 tmp |= 16;
1049 if (regs.HorizBlankEnd & 128)
1050 tmp |= 32;
1051 if (regs.VertBlankEnd & 256)
1052 tmp |= 64;
1053 if (regs.VertBlankEnd & 512)
1054 tmp |= 128;
1055
Krzysztof Helt8503df62007-10-16 01:29:08 -07001056 DPRINTK("CRT1a: %d\n", tmp);
1057 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
1059 /* set VCLK0 */
1060 /* hardware RefClock: 14.31818 MHz */
1061 /* formula: VClk = (OSC * N) / (D * (1+P)) */
1062 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
1063
Krzysztof Helt8503df62007-10-16 01:29:08 -07001064 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 tmp = regs.den << 1;
1066 if (regs.div != 0)
1067 tmp |= 1;
1068
Krzysztof Helt8503df62007-10-16 01:29:08 -07001069 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 if ((cinfo->btype == BT_SD64) ||
1071 (cinfo->btype == BT_ALPINE) ||
1072 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
Krzysztof Helt8503df62007-10-16 01:29:08 -07001075 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
1076 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
1078 if (regs.VertRes >= 1024)
1079 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001080 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 else
1082 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
1083 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001084 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
Krzysztof Helt8503df62007-10-16 01:29:08 -07001086/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
1087 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088
1089 /* don't know if it would hurt to also program this if no interlaced */
1090 /* mode is used, but I feel better this way.. :-) */
1091 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001092 vga_wcrt(regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001094 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095
Krzysztof Helt8503df62007-10-16 01:29:08 -07001096 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097
1098 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001099 /* enable display memory & CRTC I/O address for color mode */
1100 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1102 tmp |= 0x40;
1103 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1104 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001105 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106
Krzysztof Helt8503df62007-10-16 01:29:08 -07001107 /* Screen A Preset Row-Scan register */
1108 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
1109 /* text cursor on and start line */
1110 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
1111 /* text cursor end line */
1112 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
1114 /******************************************************
1115 *
1116 * 1 bpp
1117 *
1118 */
1119
1120 /* programming for different color depths */
1121 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001122 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
1123 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124
1125 /* SR07 */
1126 switch (cinfo->btype) {
1127 case BT_SD64:
1128 case BT_PICCOLO:
1129 case BT_PICASSO:
1130 case BT_SPECTRUM:
1131 case BT_PICASSO4:
1132 case BT_ALPINE:
1133 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001134 DPRINTK(" (for GD54xx)\n");
1135 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 regs.multiplexing ?
1137 bi->sr07_1bpp_mux : bi->sr07_1bpp);
1138 break;
1139
1140 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001141 DPRINTK(" (for GD546x)\n");
1142 vga_wseq(regbase, CL_SEQR7,
1143 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 break;
1145
1146 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001147 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 break;
1149 }
1150
1151 /* Extended Sequencer Mode */
1152 switch (cinfo->btype) {
1153 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001154 /* setting the SEQRF on SD64 is not necessary
1155 * (only during init)
1156 */
1157 DPRINTK("(for SD64)\n");
1158 /* MCLK select */
1159 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 break;
1161
1162 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001163 case BT_SPECTRUM:
1164 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001165 /* ### ueberall 0x22? */
1166 /* ##vorher 1c MCLK select */
1167 vga_wseq(regbase, CL_SEQR1F, 0x22);
1168 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1169 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 break;
1171
1172 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001173 DPRINTK("(for Picasso)\n");
1174 /* ##vorher 22 MCLK select */
1175 vga_wseq(regbase, CL_SEQR1F, 0x22);
1176 /* ## vorher d0 avoid FIFO underruns..? */
1177 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 break;
1179
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 case BT_PICASSO4:
1181 case BT_ALPINE:
1182 case BT_GD5480:
1183 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 /* do nothing */
1186 break;
1187
1188 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 break;
1191 }
1192
Krzysztof Helt8503df62007-10-16 01:29:08 -07001193 /* pixel mask: pass-through for first plane */
1194 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001196 /* hidden dac reg: 1280x1024 */
1197 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001199 /* hidden dac: nothing */
1200 WHDR(cinfo, 0);
1201 /* memory mode: odd/even, ext. memory */
1202 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1203 /* plane mask: only write to first plane */
1204 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 offset = var->xres_virtual / 16;
1206 }
1207
1208 /******************************************************
1209 *
1210 * 8 bpp
1211 *
1212 */
1213
1214 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001215 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 switch (cinfo->btype) {
1217 case BT_SD64:
1218 case BT_PICCOLO:
1219 case BT_PICASSO:
1220 case BT_SPECTRUM:
1221 case BT_PICASSO4:
1222 case BT_ALPINE:
1223 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001224 DPRINTK(" (for GD54xx)\n");
1225 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 regs.multiplexing ?
1227 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1228 break;
1229
1230 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001231 DPRINTK(" (for GD546x)\n");
1232 vga_wseq(regbase, CL_SEQR7,
1233 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 break;
1235
1236 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001237 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 break;
1239 }
1240
1241 switch (cinfo->btype) {
1242 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001243 /* MCLK select */
1244 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 break;
1246
1247 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001250 /* ### vorher 1c MCLK select */
1251 vga_wseq(regbase, CL_SEQR1F, 0x22);
1252 /* Fast Page-Mode writes */
1253 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 break;
1255
1256 case BT_PICASSO4:
1257#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001258 /* ### INCOMPLETE!! */
1259 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001261/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 break;
1263
1264 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001265 DPRINTK(" (for GD543x)\n");
1266 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 /* We already set SRF and SR1F */
1268 break;
1269
1270 case BT_GD5480:
1271 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001272 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 /* do nothing */
1274 break;
1275
1276 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001277 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 break;
1279 }
1280
Krzysztof Helt8503df62007-10-16 01:29:08 -07001281 /* mode register: 256 color mode */
1282 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1283 /* pixel mask: pass-through all planes */
1284 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001286 /* hidden dac reg: 1280x1024 */
1287 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001289 /* hidden dac: nothing */
1290 WHDR(cinfo, 0);
1291 /* memory mode: chain4, ext. memory */
1292 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1293 /* plane mask: enable writing to all 4 planes */
1294 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 offset = var->xres_virtual / 8;
1296 }
1297
1298 /******************************************************
1299 *
1300 * 16 bpp
1301 *
1302 */
1303
1304 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001305 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 switch (cinfo->btype) {
1307 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001308 /* Extended Sequencer Mode: 256c col. mode */
1309 vga_wseq(regbase, CL_SEQR7, 0xf7);
1310 /* MCLK select */
1311 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 break;
1313
1314 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001315 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001316 vga_wseq(regbase, CL_SEQR7, 0x87);
1317 /* Fast Page-Mode writes */
1318 vga_wseq(regbase, CL_SEQRF, 0xb0);
1319 /* MCLK select */
1320 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 break;
1322
1323 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001324 vga_wseq(regbase, CL_SEQR7, 0x27);
1325 /* Fast Page-Mode writes */
1326 vga_wseq(regbase, CL_SEQRF, 0xb0);
1327 /* MCLK select */
1328 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 break;
1330
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332 vga_wseq(regbase, CL_SEQR7, 0x27);
1333/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 break;
1335
1336 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001337 DPRINTK(" (for GD543x)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 if (regs.HorizRes >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001339 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001341 vga_wseq(regbase, CL_SEQR7, 0xa3);
1342 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 break;
1344
1345 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001346 DPRINTK(" (for GD5480)\n");
1347 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 /* We already set SRF and SR1F */
1349 break;
1350
1351 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001352 DPRINTK(" (for GD546x)\n");
1353 vga_wseq(regbase, CL_SEQR7,
1354 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 break;
1356
1357 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001358 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 break;
1360 }
1361
Krzysztof Helt8503df62007-10-16 01:29:08 -07001362 /* mode register: 256 color mode */
1363 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1364 /* pixel mask: pass-through all planes */
1365 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001367 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368#elif defined(CONFIG_ZORRO)
1369 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001370 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001372 /* memory mode: chain4, ext. memory */
1373 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1374 /* plane mask: enable writing to all 4 planes */
1375 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 offset = var->xres_virtual / 4;
1377 }
1378
1379 /******************************************************
1380 *
1381 * 32 bpp
1382 *
1383 */
1384
1385 else if (var->bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001386 DPRINTK("cirrusfb: preparing for 24/32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 switch (cinfo->btype) {
1388 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001389 /* Extended Sequencer Mode: 256c col. mode */
1390 vga_wseq(regbase, CL_SEQR7, 0xf9);
1391 /* MCLK select */
1392 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 break;
1394
1395 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001396 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 vga_wseq(regbase, CL_SEQR7, 0x85);
1398 /* Fast Page-Mode writes */
1399 vga_wseq(regbase, CL_SEQRF, 0xb0);
1400 /* MCLK select */
1401 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 break;
1403
1404 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 vga_wseq(regbase, CL_SEQR7, 0x25);
1406 /* Fast Page-Mode writes */
1407 vga_wseq(regbase, CL_SEQRF, 0xb0);
1408 /* MCLK select */
1409 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 break;
1411
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001413 vga_wseq(regbase, CL_SEQR7, 0x25);
1414/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 break;
1416
1417 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001418 DPRINTK(" (for GD543x)\n");
1419 vga_wseq(regbase, CL_SEQR7, 0xa9);
1420 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 break;
1422
1423 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001424 DPRINTK(" (for GD5480)\n");
1425 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 /* We already set SRF and SR1F */
1427 break;
1428
1429 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001430 DPRINTK(" (for GD546x)\n");
1431 vga_wseq(regbase, CL_SEQR7,
1432 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 break;
1434
1435 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001436 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 break;
1438 }
1439
Krzysztof Helt8503df62007-10-16 01:29:08 -07001440 /* mode register: 256 color mode */
1441 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1442 /* pixel mask: pass-through all planes */
1443 WGen(cinfo, VGA_PEL_MSK, 0xff);
1444 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1445 WHDR(cinfo, 0xc5);
1446 /* memory mode: chain4, ext. memory */
1447 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1448 /* plane mask: enable writing to all 4 planes */
1449 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 offset = var->xres_virtual / 4;
1451 }
1452
1453 /******************************************************
1454 *
1455 * unknown/unsupported bpp
1456 *
1457 */
1458
Krzysztof Helt8503df62007-10-16 01:29:08 -07001459 else
1460 printk(KERN_ERR "cirrusfb: What's this?? "
1461 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
Krzysztof Helt8503df62007-10-16 01:29:08 -07001464 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 tmp = 0x22;
1466 if (offset & 0x100)
1467 tmp |= 0x10; /* offset overflow bit */
1468
Krzysztof Helt8503df62007-10-16 01:29:08 -07001469 /* screen start addr #16-18, fastpagemode cycles */
1470 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472 if (cinfo->btype == BT_SD64 ||
1473 cinfo->btype == BT_PICASSO4 ||
1474 cinfo->btype == BT_ALPINE ||
1475 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001476 /* screen start address bit 19 */
1477 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478
Krzysztof Helt8503df62007-10-16 01:29:08 -07001479 /* text cursor location high */
1480 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1481 /* text cursor location low */
1482 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1483 /* underline row scanline = at very bottom */
1484 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
Krzysztof Helt8503df62007-10-16 01:29:08 -07001486 /* controller mode */
1487 vga_wattr(regbase, VGA_ATC_MODE, 1);
1488 /* overscan (border) color */
1489 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1490 /* color plane enable */
1491 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1492 /* pixel panning */
1493 vga_wattr(regbase, CL_AR33, 0);
1494 /* color select */
1495 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
1497 /* [ EGS: SetOffset(); ] */
1498 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001499 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 /* set/reset register */
1502 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1503 /* set/reset enable */
1504 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1505 /* color compare */
1506 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1507 /* data rotate */
1508 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1509 /* read map select */
1510 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1511 /* miscellaneous register */
1512 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1513 /* color don't care */
1514 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1515 /* bit mask */
1516 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
Krzysztof Helt8503df62007-10-16 01:29:08 -07001518 /* graphics cursor attributes: nothing special */
1519 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
1521 /* finally, turn on everything - turn off "FullBandwidth" bit */
1522 /* also, set "DotClock%2" bit where requested */
1523 tmp = 0x01;
1524
1525/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1526 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1527 tmp |= 0x08;
1528*/
1529
Krzysztof Helt8503df62007-10-16 01:29:08 -07001530 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1531 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
1533 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
1535 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001536 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537
1538#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540#endif
1541
Krzysztof Helt8503df62007-10-16 01:29:08 -07001542 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 return 0;
1544}
1545
1546/* for some reason incomprehensible to me, cirrusfb requires that you write
1547 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001548static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 cirrusfb_set_par_foo(info);
1551 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552}
1553
Krzysztof Helt8503df62007-10-16 01:29:08 -07001554static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1555 unsigned blue, unsigned transp,
1556 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557{
1558 struct cirrusfb_info *cinfo = info->par;
1559
1560 if (regno > 255)
1561 return -EINVAL;
1562
1563 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1564 u32 v;
1565 red >>= (16 - info->var.red.length);
1566 green >>= (16 - info->var.green.length);
1567 blue >>= (16 - info->var.blue.length);
1568
Krzysztof Helt8503df62007-10-16 01:29:08 -07001569 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 return 1;
1571 v = (red << info->var.red.offset) |
1572 (green << info->var.green.offset) |
1573 (blue << info->var.blue.offset);
1574
Krzysztof Helt060b6002007-10-16 01:29:13 -07001575 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 return 0;
1577 }
1578
Krzysztof Helt8503df62007-10-16 01:29:08 -07001579 if (info->var.bits_per_pixel == 8)
1580 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
1582 return 0;
1583
1584}
1585
1586/*************************************************************************
1587 cirrusfb_pan_display()
1588
1589 performs display panning - provided hardware permits this
1590**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001591static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1592 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593{
1594 int xoffset = 0;
1595 int yoffset = 0;
1596 unsigned long base;
1597 unsigned char tmp = 0, tmp2 = 0, xpix;
1598 struct cirrusfb_info *cinfo = info->par;
1599
Krzysztof Helt8503df62007-10-16 01:29:08 -07001600 DPRINTK("ENTER\n");
1601 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603 /* no range checks for xoffset and yoffset, */
1604 /* as fb_pan_display has already done this */
1605 if (var->vmode & FB_VMODE_YWRAP)
1606 return -EINVAL;
1607
1608 info->var.xoffset = var->xoffset;
1609 info->var.yoffset = var->yoffset;
1610
1611 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1612 yoffset = var->yoffset;
1613
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001614 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
1616 if (info->var.bits_per_pixel == 1) {
1617 /* base is already correct */
1618 xpix = (unsigned char) (var->xoffset % 8);
1619 } else {
1620 base /= 4;
1621 xpix = (unsigned char) ((xoffset % 4) * 2);
1622 }
1623
Krzysztof Helt8503df62007-10-16 01:29:08 -07001624 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625
1626 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001627 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1628 (unsigned char) (base & 0xff));
1629 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1630 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
1632 /* construct bits 16, 17 and 18 of screen start address */
1633 if (base & 0x10000)
1634 tmp |= 0x01;
1635 if (base & 0x20000)
1636 tmp |= 0x04;
1637 if (base & 0x40000)
1638 tmp |= 0x08;
1639
Krzysztof Helt8503df62007-10-16 01:29:08 -07001640 /* 0xf2 is %11110010, exclude tmp bits */
1641 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1642 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
1644 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001645 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1646 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
Krzysztof Helt8503df62007-10-16 01:29:08 -07001648 /* write pixel panning value to AR33; this does not quite work in 8bpp
1649 *
1650 * ### Piccolo..? Will this work?
1651 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
Krzysztof Helt8503df62007-10-16 01:29:08 -07001655 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 DPRINTK("EXIT\n");
1658 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659}
1660
Krzysztof Helt8503df62007-10-16 01:29:08 -07001661static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662{
1663 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1665 * then the caller blanks by setting the CLUT (Color Look Up Table)
1666 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1667 * failed due to e.g. a video mode which doesn't support it.
1668 * Implements VESA suspend and powerdown modes on hardware that
1669 * supports disabling hsync/vsync:
1670 * blank_mode == 2: suspend vsync
1671 * blank_mode == 3: suspend hsync
1672 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 */
1674 unsigned char val;
1675 struct cirrusfb_info *cinfo = info->par;
1676 int current_mode = cinfo->blank_mode;
1677
Krzysztof Helt8503df62007-10-16 01:29:08 -07001678 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
1680 if (info->state != FBINFO_STATE_RUNNING ||
1681 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001682 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 return 0;
1684 }
1685
1686 /* Undo current */
1687 if (current_mode == FB_BLANK_NORMAL ||
1688 current_mode == FB_BLANK_UNBLANK) {
1689 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001690 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1691 /* clear "FullBandwidth" bit */
1692 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 }
1696
1697 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001698 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001700 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1701 /* set "FullBandwidth" bit */
1702 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 }
1704
1705 switch (blank_mode) {
1706 case FB_BLANK_UNBLANK:
1707 case FB_BLANK_NORMAL:
1708 break;
1709 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001710 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 break;
1712 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001713 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 break;
1715 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001716 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 break;
1718 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001719 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 return 1;
1721 }
1722
1723 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725
1726 /* Let fbcon do a soft blank for us */
1727 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1728}
1729/**** END Hardware specific Routines **************************************/
1730/****************************************************************************/
1731/**** BEGIN Internal Routines ***********************************************/
1732
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001733static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001735 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 const struct cirrusfb_board_info_rec *bi;
1737
Krzysztof Helt8503df62007-10-16 01:29:08 -07001738 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
Krzysztof Helt8503df62007-10-16 01:29:08 -07001740 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
1742 bi = &cirrusfb_board_info[cinfo->btype];
1743
1744 /* reset board globally */
1745 switch (cinfo->btype) {
1746 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001747 WSFR(cinfo, 0x01);
1748 udelay(500);
1749 WSFR(cinfo, 0x51);
1750 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 break;
1752 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001753 WSFR2(cinfo, 0xff);
1754 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 break;
1756 case BT_SD64:
1757 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001758 WSFR(cinfo, 0x1f);
1759 udelay(500);
1760 WSFR(cinfo, 0x4f);
1761 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 break;
1763 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001764 /* disable flickerfixer */
1765 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1766 mdelay(100);
1767 /* from Klaus' NetBSD driver: */
1768 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1769 /* put blitter into 542x compat */
1770 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1771 /* mode */
1772 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 break;
1774
1775 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001776 /* from Klaus' NetBSD driver: */
1777 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 break;
1779
1780 case BT_ALPINE:
1781 /* Nothing to do to reset the board. */
1782 break;
1783
1784 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001785 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 break;
1787 }
1788
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001789 /* make sure RAM size set by this point */
1790 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
1792 /* the P4 is not fully initialized here; I rely on it having been */
1793 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001794 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
1796 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001797 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1798 WGen(cinfo, CL_POS102, 0x01);
1799 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
1801 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001802 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
Krzysztof Helt8503df62007-10-16 01:29:08 -07001804 /* reset sequencer logic */
1805 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
Krzysztof Helt8503df62007-10-16 01:29:08 -07001807 /* FullBandwidth (video off) and 8/9 dot clock */
1808 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1809 /* polarity (-/-), disable access to display memory,
1810 * VGA_CRTC_START_HI base address: color
1811 */
1812 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813
Krzysztof Helt8503df62007-10-16 01:29:08 -07001814 /* "magic cookie" - doesn't make any sense to me.. */
1815/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1816 /* unlock all extension registers */
1817 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818
Krzysztof Helt8503df62007-10-16 01:29:08 -07001819 /* reset blitter */
1820 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
1822 switch (cinfo->btype) {
1823 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001824 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 break;
1826 case BT_ALPINE:
1827 break;
1828 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001829 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830 break;
1831 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001832 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1833 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834 break;
1835 }
1836 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001837 /* plane mask: nothing */
1838 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1839 /* character map select: doesn't even matter in gx mode */
1840 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1841 /* memory mode: chain-4, no odd/even, ext. memory */
1842 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
1844 /* controller-internal base address of video memory */
1845 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001846 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1849 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850
Krzysztof Helt8503df62007-10-16 01:29:08 -07001851 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1852 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1853 /* graphics cursor Y position (..."... ) */
1854 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1855 /* graphics cursor attributes */
1856 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1857 /* graphics cursor pattern address */
1858 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859
1860 /* writing these on a P4 might give problems.. */
1861 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001862 /* configuration readback and ext. color */
1863 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1864 /* signature generator */
1865 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 }
1867
1868 /* MCLK select etc. */
1869 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001870 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
Krzysztof Helt8503df62007-10-16 01:29:08 -07001872 /* Screen A preset row scan: none */
1873 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1874 /* Text cursor start: disable text cursor */
1875 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1876 /* Text cursor end: - */
1877 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1878 /* Screen start address high: 0 */
1879 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1880 /* Screen start address low: 0 */
1881 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1882 /* text cursor location high: 0 */
1883 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1884 /* text cursor location low: 0 */
1885 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886
Krzysztof Helt8503df62007-10-16 01:29:08 -07001887 /* Underline Row scanline: - */
1888 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1889 /* mode control: timing enable, byte mode, no compat modes */
1890 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1891 /* Line Compare: not needed */
1892 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001894 /* ext. display controls: ext.adr. wrap */
1895 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
Krzysztof Helt8503df62007-10-16 01:29:08 -07001897 /* Set/Reset registes: - */
1898 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1899 /* Set/Reset enable: - */
1900 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1901 /* Color Compare: - */
1902 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1903 /* Data Rotate: - */
1904 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1905 /* Read Map Select: - */
1906 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1907 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1908 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1909 /* Miscellaneous: memory map base address, graphics mode */
1910 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1911 /* Color Don't care: involve all planes */
1912 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1913 /* Bit Mask: no mask at all */
1914 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001916 /* (5434 can't have bit 3 set for bitblt) */
1917 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001919 /* Graphics controller mode extensions: finer granularity,
1920 * 8byte data latches
1921 */
1922 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923
Krzysztof Helt8503df62007-10-16 01:29:08 -07001924 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1925 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1926 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1927 /* Background color byte 1: - */
1928 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1929 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
Krzysztof Helt8503df62007-10-16 01:29:08 -07001931 /* Attribute Controller palette registers: "identity mapping" */
1932 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1933 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1934 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1935 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1936 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1937 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1938 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1939 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1940 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1941 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1942 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1943 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1944 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1945 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1946 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1947 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948
Krzysztof Helt8503df62007-10-16 01:29:08 -07001949 /* Attribute Controller mode: graphics mode */
1950 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1951 /* Overscan color reg.: reg. 0 */
1952 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1953 /* Color Plane enable: Enable all 4 planes */
1954 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1955/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1956 /* Color Select: - */
1957 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958
Krzysztof Helt8503df62007-10-16 01:29:08 -07001959 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960
1961 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001962 /* polarity (-/-), enable display mem,
1963 * VGA_CRTC_START_HI i/o base = color
1964 */
1965 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966
Krzysztof Helt8503df62007-10-16 01:29:08 -07001967 /* BLT Start/status: Blitter reset */
1968 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1969 /* - " - : "end-of-reset" */
1970 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971
1972 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001973 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974
Krzysztof Helt8503df62007-10-16 01:29:08 -07001975 printk(KERN_DEBUG "cirrusfb: This board has %ld bytes of DRAM memory\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001976 info->screen_size);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001977 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 return;
1979}
1980
Krzysztof Helt8503df62007-10-16 01:29:08 -07001981static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982{
1983#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1984 static int IsOn = 0; /* XXX not ok for multiple boards */
1985
Krzysztof Helt8503df62007-10-16 01:29:08 -07001986 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987
1988 if (cinfo->btype == BT_PICASSO4)
1989 return; /* nothing to switch */
1990 if (cinfo->btype == BT_ALPINE)
1991 return; /* nothing to switch */
1992 if (cinfo->btype == BT_GD5480)
1993 return; /* nothing to switch */
1994 if (cinfo->btype == BT_PICASSO) {
1995 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001996 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997
Krzysztof Helt8503df62007-10-16 01:29:08 -07001998 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 return;
2000 }
2001 if (on) {
2002 switch (cinfo->btype) {
2003 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002004 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 break;
2006 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002007 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 break;
2009 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002010 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 break;
2012 default: /* do nothing */ break;
2013 }
2014 } else {
2015 switch (cinfo->btype) {
2016 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002017 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 break;
2019 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002020 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 break;
2022 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002023 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 break;
2025 default: /* do nothing */ break;
2026 }
2027 }
2028
Krzysztof Helt8503df62007-10-16 01:29:08 -07002029 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030#endif /* CONFIG_ZORRO */
2031}
2032
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033/******************************************/
2034/* Linux 2.6-style accelerated functions */
2035/******************************************/
2036
Krzysztof Helt8503df62007-10-16 01:29:08 -07002037static void cirrusfb_fillrect(struct fb_info *info,
2038 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 struct fb_fillrect modded;
2041 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002042 struct cirrusfb_info *cinfo = info->par;
2043 int m = info->var.bits_per_pixel;
2044 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
2045 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046
2047 if (info->state != FBINFO_STATE_RUNNING)
2048 return;
2049 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2050 cfb_fillrect(info, region);
2051 return;
2052 }
2053
2054 vxres = info->var.xres_virtual;
2055 vyres = info->var.yres_virtual;
2056
2057 memcpy(&modded, region, sizeof(struct fb_fillrect));
2058
Krzysztof Helt8503df62007-10-16 01:29:08 -07002059 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 modded.dx >= vxres || modded.dy >= vyres)
2061 return;
2062
Krzysztof Helt8503df62007-10-16 01:29:08 -07002063 if (modded.dx + modded.width > vxres)
2064 modded.width = vxres - modded.dx;
2065 if (modded.dy + modded.height > vyres)
2066 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067
Krzysztof Helt060b6002007-10-16 01:29:13 -07002068 cirrusfb_RectFill(cinfo->regbase,
2069 info->var.bits_per_pixel,
2070 (region->dx * m) / 8, region->dy,
2071 (region->width * m) / 8, region->height,
2072 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07002073 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074}
2075
Krzysztof Helt8503df62007-10-16 01:29:08 -07002076static void cirrusfb_copyarea(struct fb_info *info,
2077 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 struct fb_copyarea modded;
2080 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002081 struct cirrusfb_info *cinfo = info->par;
2082 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
2084 if (info->state != FBINFO_STATE_RUNNING)
2085 return;
2086 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2087 cfb_copyarea(info, area);
2088 return;
2089 }
2090
2091 vxres = info->var.xres_virtual;
2092 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002093 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094
Krzysztof Helt8503df62007-10-16 01:29:08 -07002095 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 modded.sx >= vxres || modded.sy >= vyres ||
2097 modded.dx >= vxres || modded.dy >= vyres)
2098 return;
2099
Krzysztof Helt8503df62007-10-16 01:29:08 -07002100 if (modded.sx + modded.width > vxres)
2101 modded.width = vxres - modded.sx;
2102 if (modded.dx + modded.width > vxres)
2103 modded.width = vxres - modded.dx;
2104 if (modded.sy + modded.height > vyres)
2105 modded.height = vyres - modded.sy;
2106 if (modded.dy + modded.height > vyres)
2107 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108
Krzysztof Helt060b6002007-10-16 01:29:13 -07002109 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
2110 (area->sx * m) / 8, area->sy,
2111 (area->dx * m) / 8, area->dy,
2112 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07002113 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002114
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115}
2116
Krzysztof Helt8503df62007-10-16 01:29:08 -07002117static void cirrusfb_imageblit(struct fb_info *info,
2118 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119{
2120 struct cirrusfb_info *cinfo = info->par;
2121
Krzysztof Helt8503df62007-10-16 01:29:08 -07002122 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 cfb_imageblit(info, image);
2124}
2125
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126#ifdef CONFIG_PPC_PREP
2127#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
2128#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002129static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002131 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132
2133 *display = PREP_VIDEO_BASE;
2134 *registers = (unsigned long) PREP_IO_BASE;
2135
Krzysztof Helt8503df62007-10-16 01:29:08 -07002136 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137}
2138
2139#endif /* CONFIG_PPC_PREP */
2140
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002142static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143
2144/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
2145 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
2146 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
2147 * seem to have. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002148static unsigned int cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149{
2150 unsigned long mem;
2151 unsigned char SRF;
2152
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
Krzysztof Helt8503df62007-10-16 01:29:08 -07002155 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002157 case 0x08:
2158 mem = 512 * 1024;
2159 break;
2160 case 0x10:
2161 mem = 1024 * 1024;
2162 break;
2163 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2164 * on the 5430.
2165 */
2166 case 0x18:
2167 mem = 2048 * 1024;
2168 break;
2169 default:
2170 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 mem = 1024 * 1024;
2172 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002173 if (SRF & 0x80)
2174 /* If DRAM bank switching is enabled, there must be twice as much
2175 * memory installed. (4MB on the 5434)
2176 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002178
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2180
Krzysztof Helt8503df62007-10-16 01:29:08 -07002181 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 return mem;
2183}
2184
Krzysztof Helt8503df62007-10-16 01:29:08 -07002185static void get_pci_addrs(const struct pci_dev *pdev,
2186 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002188 assert(pdev != NULL);
2189 assert(display != NULL);
2190 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191
Krzysztof Helt8503df62007-10-16 01:29:08 -07002192 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193
2194 *display = 0;
2195 *registers = 0;
2196
2197 /* This is a best-guess for now */
2198
2199 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2200 *display = pci_resource_start(pdev, 1);
2201 *registers = pci_resource_start(pdev, 0);
2202 } else {
2203 *display = pci_resource_start(pdev, 0);
2204 *registers = pci_resource_start(pdev, 1);
2205 }
2206
Krzysztof Helt8503df62007-10-16 01:29:08 -07002207 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208
Krzysztof Helt8503df62007-10-16 01:29:08 -07002209 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210}
2211
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002212static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002214 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 struct pci_dev *pdev = cinfo->pdev;
2216
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002217 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218#if 0 /* if system didn't claim this region, we would... */
2219 release_mem_region(0xA0000, 65535);
2220#endif
2221 if (release_io_ports)
2222 release_region(0x3C0, 32);
2223 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224}
2225#endif /* CONFIG_PCI */
2226
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002228static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229{
Al Virod91f5bb2007-10-17 00:27:18 +01002230 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 zorro_release_device(cinfo->zdev);
2232
2233 if (cinfo->btype == BT_PICASSO4) {
2234 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002235 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002236 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 } else {
2238 if (zorro_resource_start(cinfo->zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002239 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241}
2242#endif /* CONFIG_ZORRO */
2243
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002244static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002246 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 struct fb_var_screeninfo *var = &info->var;
2248
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 info->pseudo_palette = cinfo->pseudo_palette;
2250 info->flags = FBINFO_DEFAULT
2251 | FBINFO_HWACCEL_XPAN
2252 | FBINFO_HWACCEL_YPAN
2253 | FBINFO_HWACCEL_FILLRECT
2254 | FBINFO_HWACCEL_COPYAREA;
2255 if (noaccel)
2256 info->flags |= FBINFO_HWACCEL_DISABLED;
2257 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 if (cinfo->btype == BT_GD5480) {
2259 if (var->bits_per_pixel == 16)
2260 info->screen_base += 1 * MB_;
2261 if (var->bits_per_pixel == 24 || var->bits_per_pixel == 32)
2262 info->screen_base += 2 * MB_;
2263 }
2264
2265 /* Fill fix common fields */
2266 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2267 sizeof(info->fix.id));
2268
2269 /* monochrome: only 1 memory plane */
2270 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002271 info->fix.smem_len = info->screen_size;
2272 if (var->bits_per_pixel == 1)
2273 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 info->fix.xpanstep = 1;
2276 info->fix.ypanstep = 1;
2277 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
2279 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 info->fix.mmio_len = 0;
2281 info->fix.accel = FB_ACCEL_NONE;
2282
2283 fb_alloc_cmap(&info->cmap, 256, 0);
2284
2285 return 0;
2286}
2287
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002288static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002290 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002292 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293
Krzysztof Helt8503df62007-10-16 01:29:08 -07002294 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
Krzysztof Helt8503df62007-10-16 01:29:08 -07002296 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2297 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 btype = cinfo->btype;
2300
2301 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002302 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002304 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305
2306 /* Make pretend we've set the var so our structures are in a "good" */
2307 /* state, even though we haven't written the mode to the hw yet... */
2308 info->var = cirrusfb_predefined[cirrusfb_def_mode].var;
2309 info->var.activate = FB_ACTIVATE_NOW;
2310
2311 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2312 if (err < 0) {
2313 /* should never happen */
2314 DPRINTK("choking on default var... umm, no good.\n");
2315 goto err_unmap_cirrusfb;
2316 }
2317
2318 /* set all the vital stuff */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002319 cirrusfb_set_fbinfo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320
2321 err = register_framebuffer(info);
2322 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002323 printk(KERN_ERR "cirrusfb: could not register "
2324 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 goto err_dealloc_cmap;
2326 }
2327
Krzysztof Helt8503df62007-10-16 01:29:08 -07002328 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 return 0;
2330
2331err_dealloc_cmap:
2332 fb_dealloc_cmap(&info->cmap);
2333err_unmap_cirrusfb:
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002334 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002335 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 return err;
2337}
2338
Krzysztof Helt8503df62007-10-16 01:29:08 -07002339static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340{
2341 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002342 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343
Krzysztof Helt8503df62007-10-16 01:29:08 -07002344 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345
Krzysztof Helt8503df62007-10-16 01:29:08 -07002346 unregister_framebuffer(info);
2347 fb_dealloc_cmap(&info->cmap);
2348 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002349 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002350 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351
Krzysztof Helt8503df62007-10-16 01:29:08 -07002352 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353}
2354
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002356static int cirrusfb_pci_register(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357 const struct pci_device_id *ent)
2358{
2359 struct cirrusfb_info *cinfo;
2360 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002361 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362 unsigned long board_addr, board_size;
2363 int ret;
2364
2365 ret = pci_enable_device(pdev);
2366 if (ret < 0) {
2367 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2368 goto err_out;
2369 }
2370
2371 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2372 if (!info) {
2373 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2374 ret = -ENOMEM;
2375 goto err_disable;
2376 }
2377
2378 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 cinfo->pdev = pdev;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002380 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381
Krzysztof Helt7345de32007-10-16 01:29:11 -07002382 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002384 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385
Krzysztof Helt8503df62007-10-16 01:29:08 -07002386 if (isPReP) {
2387 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002389 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002391 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002392 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002394 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002395 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002396 /* FIXME: this forces VGA. alternatives? */
2397 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 }
2399
Krzysztof Helt8503df62007-10-16 01:29:08 -07002400 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002401 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402
2403 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002404 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405
2406 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002407 if (ret < 0) {
2408 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2409 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410 board_addr);
2411 goto err_release_fb;
2412 }
2413#if 0 /* if the system didn't claim this region, we would... */
2414 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2415 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2416,
2417 0xA0000L);
2418 ret = -EBUSY;
2419 goto err_release_regions;
2420 }
2421#endif
2422 if (request_region(0x3C0, 32, "cirrusfb"))
2423 release_io_ports = 1;
2424
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002425 info->screen_base = ioremap(board_addr, board_size);
2426 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 ret = -EIO;
2428 goto err_release_legacy;
2429 }
2430
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002431 info->fix.smem_start = board_addr;
2432 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433 cinfo->unmap = cirrusfb_pci_unmap;
2434
Krzysztof Helt8503df62007-10-16 01:29:08 -07002435 printk(KERN_INFO " RAM (%lu kB) at 0xx%lx, ",
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07002436 info->screen_size >> 10, board_addr);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002437 printk(KERN_INFO "Cirrus Logic chipset on PCI bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438 pci_set_drvdata(pdev, info);
2439
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002440 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002441 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002442 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002443 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444
2445err_release_legacy:
2446 if (release_io_ports)
2447 release_region(0x3C0, 32);
2448#if 0
2449 release_mem_region(0xA0000, 65535);
2450err_release_regions:
2451#endif
2452 pci_release_regions(pdev);
2453err_release_fb:
2454 framebuffer_release(info);
2455err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456err_out:
2457 return ret;
2458}
2459
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461{
2462 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464
Krzysztof Helt8503df62007-10-16 01:29:08 -07002465 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466
Krzysztof Helt8503df62007-10-16 01:29:08 -07002467 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468}
2469
2470static struct pci_driver cirrusfb_pci_driver = {
2471 .name = "cirrusfb",
2472 .id_table = cirrusfb_pci_table,
2473 .probe = cirrusfb_pci_register,
2474 .remove = __devexit_p(cirrusfb_pci_unregister),
2475#ifdef CONFIG_PM
2476#if 0
2477 .suspend = cirrusfb_pci_suspend,
2478 .resume = cirrusfb_pci_resume,
2479#endif
2480#endif
2481};
2482#endif /* CONFIG_PCI */
2483
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484#ifdef CONFIG_ZORRO
2485static int cirrusfb_zorro_register(struct zorro_dev *z,
2486 const struct zorro_device_id *ent)
2487{
2488 struct cirrusfb_info *cinfo;
2489 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002490 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 struct zorro_dev *z2 = NULL;
2492 unsigned long board_addr, board_size, size;
2493 int ret;
2494
2495 btype = ent->driver_data;
2496 if (cirrusfb_zorro_table2[btype].id2)
2497 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2498 size = cirrusfb_zorro_table2[btype].size;
2499 printk(KERN_INFO "cirrusfb: %s board detected; ",
2500 cirrusfb_board_info[btype].name);
2501
2502 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2503 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002504 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 ret = -ENOMEM;
2506 goto err_out;
2507 }
2508
2509 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 cinfo->btype = btype;
2511
Al Viro36ea96a2007-10-27 19:46:58 +01002512 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514
2515 cinfo->zdev = z;
2516 board_addr = zorro_resource_start(z);
2517 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002518 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519
2520 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2522 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 board_addr);
2524 ret = -EBUSY;
2525 goto err_release_fb;
2526 }
2527
Krzysztof Helt8503df62007-10-16 01:29:08 -07002528 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
2530 ret = -EIO;
2531
2532 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002533 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534
2535 /* To be precise, for the P4 this is not the */
2536 /* begin of the board, but the begin of RAM. */
2537 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2538 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002539 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540 if (!cinfo->regbase)
2541 goto err_release_region;
2542
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2544 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002546 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002548 info->fix.smem_start = board_addr + 16777216;
2549 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2550 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 goto err_unmap_regbase;
2552 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553 printk(KERN_INFO " REG at $%lx\n",
2554 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002556 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002558 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002560 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2561 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 goto err_release_region;
2563
2564 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002565 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002566 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2569 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 }
2571 cinfo->unmap = cirrusfb_zorro_unmap;
2572
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 zorro_set_drvdata(z, info);
2575
Al Virod91f5bb2007-10-17 00:27:18 +01002576 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002577 if (ret) {
2578 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002579 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002580 iounmap(cinfo->regbase - 0x600000);
2581 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002582 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002583 }
2584 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585
2586err_unmap_regbase:
2587 /* Parental advisory: explicit hack */
2588 iounmap(cinfo->regbase - 0x600000);
2589err_release_region:
2590 release_region(board_addr, board_size);
2591err_release_fb:
2592 framebuffer_release(info);
2593err_out:
2594 return ret;
2595}
2596
2597void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2598{
2599 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002600 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601
Krzysztof Helt8503df62007-10-16 01:29:08 -07002602 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603
Krzysztof Helt8503df62007-10-16 01:29:08 -07002604 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605}
2606
2607static struct zorro_driver cirrusfb_zorro_driver = {
2608 .name = "cirrusfb",
2609 .id_table = cirrusfb_zorro_table,
2610 .probe = cirrusfb_zorro_register,
2611 .remove = __devexit_p(cirrusfb_zorro_unregister),
2612};
2613#endif /* CONFIG_ZORRO */
2614
2615static int __init cirrusfb_init(void)
2616{
2617 int error = 0;
2618
2619#ifndef MODULE
2620 char *option = NULL;
2621
2622 if (fb_get_options("cirrusfb", &option))
2623 return -ENODEV;
2624 cirrusfb_setup(option);
2625#endif
2626
2627#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002628 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629#endif
2630#ifdef CONFIG_PCI
2631 error |= pci_register_driver(&cirrusfb_pci_driver);
2632#endif
2633 return error;
2634}
2635
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636#ifndef MODULE
2637static int __init cirrusfb_setup(char *options) {
2638 char *this_opt, s[32];
2639 int i;
2640
Krzysztof Helt8503df62007-10-16 01:29:08 -07002641 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642
2643 if (!options || !*options)
2644 return 0;
2645
Krzysztof Helt8503df62007-10-16 01:29:08 -07002646 while ((this_opt = strsep(&options, ",")) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 if (!*this_opt) continue;
2648
2649 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2650
2651 for (i = 0; i < NUM_TOTAL_MODES; i++) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002652 sprintf(s, "mode:%s", cirrusfb_predefined[i].name);
2653 if (strcmp(this_opt, s) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654 cirrusfb_def_mode = i;
2655 }
2656 if (!strcmp(this_opt, "noaccel"))
2657 noaccel = 1;
2658 }
2659 return 0;
2660}
2661#endif
2662
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 /*
2664 * Modularization
2665 */
2666
2667MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2668MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2669MODULE_LICENSE("GPL");
2670
Krzysztof Helt8503df62007-10-16 01:29:08 -07002671static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672{
2673#ifdef CONFIG_PCI
2674 pci_unregister_driver(&cirrusfb_pci_driver);
2675#endif
2676#ifdef CONFIG_ZORRO
2677 zorro_unregister_driver(&cirrusfb_zorro_driver);
2678#endif
2679}
2680
2681module_init(cirrusfb_init);
2682
2683#ifdef MODULE
2684module_exit(cirrusfb_exit);
2685#endif
2686
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687/**********************************************************************/
2688/* about the following functions - I have used the same names for the */
2689/* functions as Markus Wild did in his Retina driver for NetBSD as */
2690/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002691/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692/**********************************************************************/
2693
2694/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002695static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 int regnum, unsigned char val)
2697{
2698 unsigned long regofs = 0;
2699
2700 if (cinfo->btype == BT_PICASSO) {
2701 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002702/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2703 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2705 regofs = 0xfff;
2706 }
2707
Krzysztof Helt8503df62007-10-16 01:29:08 -07002708 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709}
2710
2711/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002712static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713{
2714 unsigned long regofs = 0;
2715
2716 if (cinfo->btype == BT_PICASSO) {
2717 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002718/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2719 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2721 regofs = 0xfff;
2722 }
2723
Krzysztof Helt8503df62007-10-16 01:29:08 -07002724 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725}
2726
2727/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002728static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002730 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731
Krzysztof Helt8503df62007-10-16 01:29:08 -07002732 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
Krzysztof Helt8503df62007-10-16 01:29:08 -07002734 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 /* if we're just in "write value" mode, write back the */
2736 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002737 vga_w(cinfo->regbase, VGA_ATT_IW,
2738 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739 }
2740 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002741/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2742 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743
2744 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002745 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746
Krzysztof Helt8503df62007-10-16 01:29:08 -07002747 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748}
2749
2750/*** WHDR() - write into the Hidden DAC register ***/
2751/* as the HDR is the only extension register that requires special treatment
2752 * (the other extension registers are accessible just like the "ordinary"
2753 * registers of their functional group) here is a specialized routine for
2754 * accessing the HDR
2755 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002756static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757{
2758 unsigned char dummy;
2759
2760 if (cinfo->btype == BT_PICASSO) {
2761 /* Klaus' hint for correct access to HDR on some boards */
2762 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002763 WGen(cinfo, VGA_PEL_MSK, 0x00);
2764 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002766 dummy = RGen(cinfo, VGA_PEL_IW);
2767 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 }
2769 /* now do the usual stuff to access the HDR */
2770
Krzysztof Helt8503df62007-10-16 01:29:08 -07002771 dummy = RGen(cinfo, VGA_PEL_MSK);
2772 udelay(200);
2773 dummy = RGen(cinfo, VGA_PEL_MSK);
2774 udelay(200);
2775 dummy = RGen(cinfo, VGA_PEL_MSK);
2776 udelay(200);
2777 dummy = RGen(cinfo, VGA_PEL_MSK);
2778 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779
Krzysztof Helt8503df62007-10-16 01:29:08 -07002780 WGen(cinfo, VGA_PEL_MSK, val);
2781 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782
2783 if (cinfo->btype == BT_PICASSO) {
2784 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002785 dummy = RGen(cinfo, VGA_PEL_IW);
2786 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787
2788 /* and at the end, restore the mask value */
2789 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002790 WGen(cinfo, VGA_PEL_MSK, 0xff);
2791 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 }
2793}
2794
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002796static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797{
2798#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002799 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002801 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802#endif
2803}
2804
2805/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002806static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807{
2808#ifdef CONFIG_ZORRO
2809 /* writing an arbitrary value to this one causes the monitor switcher */
2810 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002811 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002813 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814#endif
2815}
2816
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002818static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 unsigned char green, unsigned char blue)
2820{
2821 unsigned int data = VGA_PEL_D;
2822
2823 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002824 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825
2826 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2827 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2828 /* but DAC data register IS, at least for Picasso II */
2829 if (cinfo->btype == BT_PICASSO)
2830 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002831 vga_w(cinfo->regbase, data, red);
2832 vga_w(cinfo->regbase, data, green);
2833 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002835 vga_w(cinfo->regbase, data, blue);
2836 vga_w(cinfo->regbase, data, green);
2837 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 }
2839}
2840
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841#if 0
2842/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002843static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844 unsigned char *green, unsigned char *blue)
2845{
2846 unsigned int data = VGA_PEL_D;
2847
Krzysztof Helt8503df62007-10-16 01:29:08 -07002848 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849
2850 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2851 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2852 if (cinfo->btype == BT_PICASSO)
2853 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002854 *red = vga_r(cinfo->regbase, data);
2855 *green = vga_r(cinfo->regbase, data);
2856 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002858 *blue = vga_r(cinfo->regbase, data);
2859 *green = vga_r(cinfo->regbase, data);
2860 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 }
2862}
2863#endif
2864
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865/*******************************************************************
2866 cirrusfb_WaitBLT()
2867
2868 Wait for the BitBLT engine to complete a possible earlier job
2869*********************************************************************/
2870
2871/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002872static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873{
2874 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002875 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 /* do nothing */ ;
2877}
2878
2879/*******************************************************************
2880 cirrusfb_BitBLT()
2881
2882 perform accelerated "scrolling"
2883********************************************************************/
2884
Krzysztof Helt8503df62007-10-16 01:29:08 -07002885static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2886 u_short curx, u_short cury,
2887 u_short destx, u_short desty,
2888 u_short width, u_short height,
2889 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890{
2891 u_short nwidth, nheight;
2892 u_long nsrc, ndest;
2893 u_char bltmode;
2894
Krzysztof Helt8503df62007-10-16 01:29:08 -07002895 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896
2897 nwidth = width - 1;
2898 nheight = height - 1;
2899
2900 bltmode = 0x00;
2901 /* if source adr < dest addr, do the Blt backwards */
2902 if (cury <= desty) {
2903 if (cury == desty) {
2904 /* if src and dest are on the same line, check x */
2905 if (curx < destx)
2906 bltmode |= 0x01;
2907 } else
2908 bltmode |= 0x01;
2909 }
2910 if (!bltmode) {
2911 /* standard case: forward blitting */
2912 nsrc = (cury * line_length) + curx;
2913 ndest = (desty * line_length) + destx;
2914 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002915 /* this means start addresses are at the end,
2916 * counting backwards
2917 */
2918 nsrc = cury * line_length + curx +
2919 nheight * line_length + nwidth;
2920 ndest = desty * line_length + destx +
2921 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922 }
2923
2924 /*
2925 run-down of registers to be programmed:
2926 destination pitch
2927 source pitch
2928 BLT width/height
2929 source start
2930 destination start
2931 BLT mode
2932 BLT ROP
2933 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2934 start/stop
2935 */
2936
Krzysztof Helt8503df62007-10-16 01:29:08 -07002937 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938
2939 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002940 /* dest pitch low */
2941 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2942 /* dest pitch hi */
2943 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2944 /* source pitch low */
2945 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2946 /* source pitch hi */
2947 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948
2949 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002950 /* BLT width low */
2951 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2952 /* BLT width hi */
2953 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954
2955 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002956 /* BLT height low */
2957 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2958 /* BLT width hi */
2959 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960
2961 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002962 /* BLT dest low */
2963 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2964 /* BLT dest mid */
2965 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2966 /* BLT dest hi */
2967 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968
2969 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002970 /* BLT src low */
2971 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2972 /* BLT src mid */
2973 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2974 /* BLT src hi */
2975 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002976
2977 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002978 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979
2980 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002981 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002982
2983 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002984 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985
Krzysztof Helt8503df62007-10-16 01:29:08 -07002986 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002987}
2988
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989/*******************************************************************
2990 cirrusfb_RectFill()
2991
2992 perform accelerated rectangle fill
2993********************************************************************/
2994
Krzysztof Helt8503df62007-10-16 01:29:08 -07002995static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996 u_short x, u_short y, u_short width, u_short height,
2997 u_char color, u_short line_length)
2998{
2999 u_short nwidth, nheight;
3000 u_long ndest;
3001 u_char op;
3002
Krzysztof Helt8503df62007-10-16 01:29:08 -07003003 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003004
3005 nwidth = width - 1;
3006 nheight = height - 1;
3007
3008 ndest = (y * line_length) + x;
3009
Krzysztof Helt8503df62007-10-16 01:29:08 -07003010 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003011
3012 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003013 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
3014 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
3015 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
3016 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003017
3018 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003019 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
3020 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021
3022 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003023 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
3024 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003025
3026 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003027 /* BLT dest low */
3028 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
3029 /* BLT dest mid */
3030 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
3031 /* BLT dest hi */
3032 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003033
3034 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003035 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
3036 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
3037 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038
3039 /* This is a ColorExpand Blt, using the */
3040 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003041 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
3042 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043
3044 op = 0xc0;
3045 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003046 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3047 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048 op = 0x50;
3049 op = 0xd0;
3050 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003051 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3052 vga_wgfx(regbase, CL_GR11, color); /* background color */
3053 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
3054 vga_wgfx(regbase, CL_GR13, color); /* background color */
3055 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
3056 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057 op = 0x50;
3058 op = 0xf0;
3059 }
3060 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003061 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003062
3063 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003064 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003065
3066 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003067 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003068
Krzysztof Helt8503df62007-10-16 01:29:08 -07003069 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003070}
3071
Linus Torvalds1da177e2005-04-16 15:20:36 -07003072/**************************************************************************
3073 * bestclock() - determine closest possible clock lower(?) than the
3074 * desired pixel clock
3075 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07003076static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003077 long *den, long *div, long maxfreq)
3078{
3079 long n, h, d, f;
3080
Krzysztof Helt8503df62007-10-16 01:29:08 -07003081 assert(best != NULL);
3082 assert(nom != NULL);
3083 assert(den != NULL);
3084 assert(div != NULL);
3085 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086
3087 *nom = 0;
3088 *den = 0;
3089 *div = 0;
3090
Krzysztof Helt8503df62007-10-16 01:29:08 -07003091 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092
3093 if (freq < 8000)
3094 freq = 8000;
3095
3096 if (freq > maxfreq)
3097 freq = maxfreq;
3098
3099 *best = 0;
3100 f = freq * 10;
3101
3102 for (n = 32; n < 128; n++) {
3103 d = (143181 * n) / f;
3104 if ((d >= 7) && (d <= 63)) {
3105 if (d > 31)
3106 d = (d / 2) * 2;
3107 h = (14318 * n) / d;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003108 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003109 *best = h;
3110 *nom = n;
3111 if (d < 32) {
3112 *den = d;
3113 *div = 0;
3114 } else {
3115 *den = d / 2;
3116 *div = 1;
3117 }
3118 }
3119 }
3120 d = ((143181 * n) + f - 1) / f;
3121 if ((d >= 7) && (d <= 63)) {
3122 if (d > 31)
3123 d = (d / 2) * 2;
3124 h = (14318 * n) / d;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003125 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126 *best = h;
3127 *nom = n;
3128 if (d < 32) {
3129 *den = d;
3130 *div = 0;
3131 } else {
3132 *den = d / 2;
3133 *div = 1;
3134 }
3135 }
3136 }
3137 }
3138
Krzysztof Helt8503df62007-10-16 01:29:08 -07003139 DPRINTK("Best possible values for given frequency:\n");
3140 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
3141 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003142
Krzysztof Helt8503df62007-10-16 01:29:08 -07003143 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144}
3145
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146/* -------------------------------------------------------------------------
3147 *
3148 * debugging functions
3149 *
3150 * -------------------------------------------------------------------------
3151 */
3152
3153#ifdef CIRRUSFB_DEBUG
3154
3155/**
3156 * cirrusfb_dbg_print_byte
3157 * @name: name associated with byte value to be displayed
3158 * @val: byte value to be displayed
3159 *
3160 * DESCRIPTION:
3161 * Display an indented string, along with a hexidecimal byte value, and
3162 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3163 * order.
3164 */
3165
3166static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003167void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003168{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003169 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3170 name, val,
3171 val & 0x80 ? '1' : '0',
3172 val & 0x40 ? '1' : '0',
3173 val & 0x20 ? '1' : '0',
3174 val & 0x10 ? '1' : '0',
3175 val & 0x08 ? '1' : '0',
3176 val & 0x04 ? '1' : '0',
3177 val & 0x02 ? '1' : '0',
3178 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179}
3180
Linus Torvalds1da177e2005-04-16 15:20:36 -07003181/**
3182 * cirrusfb_dbg_print_regs
3183 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3184 * @reg_class: type of registers to read: %CRT, or %SEQ
3185 *
3186 * DESCRIPTION:
3187 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3188 * old-style I/O ports are queried for information, otherwise MMIO is
3189 * used at the given @base address to query the information.
3190 */
3191
3192static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003193void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003194 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195{
3196 va_list list;
3197 unsigned char val = 0;
3198 unsigned reg;
3199 char *name;
3200
Krzysztof Helt8503df62007-10-16 01:29:08 -07003201 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003202
Krzysztof Helt8503df62007-10-16 01:29:08 -07003203 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003204 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003205 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206
3207 switch (reg_class) {
3208 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003209 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003210 break;
3211 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003212 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213 break;
3214 default:
3215 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003216 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217 break;
3218 }
3219
Krzysztof Helt8503df62007-10-16 01:29:08 -07003220 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221
Krzysztof Helt8503df62007-10-16 01:29:08 -07003222 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003223 }
3224
Krzysztof Helt8503df62007-10-16 01:29:08 -07003225 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226}
3227
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228/**
3229 * cirrusfb_dump
3230 * @cirrusfbinfo:
3231 *
3232 * DESCRIPTION:
3233 */
3234
Krzysztof Helt8503df62007-10-16 01:29:08 -07003235static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003237 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238}
3239
Linus Torvalds1da177e2005-04-16 15:20:36 -07003240/**
3241 * cirrusfb_dbg_reg_dump
3242 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3243 *
3244 * DESCRIPTION:
3245 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3246 * old-style I/O ports are queried for information, otherwise MMIO is
3247 * used at the given @base address to query the information.
3248 */
3249
3250static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003251void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003252{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003253 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003254
Krzysztof Helt8503df62007-10-16 01:29:08 -07003255 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003256 "CR00", 0x00,
3257 "CR01", 0x01,
3258 "CR02", 0x02,
3259 "CR03", 0x03,
3260 "CR04", 0x04,
3261 "CR05", 0x05,
3262 "CR06", 0x06,
3263 "CR07", 0x07,
3264 "CR08", 0x08,
3265 "CR09", 0x09,
3266 "CR0A", 0x0A,
3267 "CR0B", 0x0B,
3268 "CR0C", 0x0C,
3269 "CR0D", 0x0D,
3270 "CR0E", 0x0E,
3271 "CR0F", 0x0F,
3272 "CR10", 0x10,
3273 "CR11", 0x11,
3274 "CR12", 0x12,
3275 "CR13", 0x13,
3276 "CR14", 0x14,
3277 "CR15", 0x15,
3278 "CR16", 0x16,
3279 "CR17", 0x17,
3280 "CR18", 0x18,
3281 "CR22", 0x22,
3282 "CR24", 0x24,
3283 "CR26", 0x26,
3284 "CR2D", 0x2D,
3285 "CR2E", 0x2E,
3286 "CR2F", 0x2F,
3287 "CR30", 0x30,
3288 "CR31", 0x31,
3289 "CR32", 0x32,
3290 "CR33", 0x33,
3291 "CR34", 0x34,
3292 "CR35", 0x35,
3293 "CR36", 0x36,
3294 "CR37", 0x37,
3295 "CR38", 0x38,
3296 "CR39", 0x39,
3297 "CR3A", 0x3A,
3298 "CR3B", 0x3B,
3299 "CR3C", 0x3C,
3300 "CR3D", 0x3D,
3301 "CR3E", 0x3E,
3302 "CR3F", 0x3F,
3303 NULL);
3304
Krzysztof Helt8503df62007-10-16 01:29:08 -07003305 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003306
Krzysztof Helt8503df62007-10-16 01:29:08 -07003307 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003308
Krzysztof Helt8503df62007-10-16 01:29:08 -07003309 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003310 "SR00", 0x00,
3311 "SR01", 0x01,
3312 "SR02", 0x02,
3313 "SR03", 0x03,
3314 "SR04", 0x04,
3315 "SR08", 0x08,
3316 "SR09", 0x09,
3317 "SR0A", 0x0A,
3318 "SR0B", 0x0B,
3319 "SR0D", 0x0D,
3320 "SR10", 0x10,
3321 "SR11", 0x11,
3322 "SR12", 0x12,
3323 "SR13", 0x13,
3324 "SR14", 0x14,
3325 "SR15", 0x15,
3326 "SR16", 0x16,
3327 "SR17", 0x17,
3328 "SR18", 0x18,
3329 "SR19", 0x19,
3330 "SR1A", 0x1A,
3331 "SR1B", 0x1B,
3332 "SR1C", 0x1C,
3333 "SR1D", 0x1D,
3334 "SR1E", 0x1E,
3335 "SR1F", 0x1F,
3336 NULL);
3337
Krzysztof Helt8503df62007-10-16 01:29:08 -07003338 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003339}
3340
3341#endif /* CIRRUSFB_DEBUG */
3342