blob: dfd12a2dfe72a7dbc66ad0cdd7443129e41d6997 [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...) \
Harvey Harrison5ae12172008-04-28 02:15:47 -070084 printk(KERN_DEBUG "%s: " fmt, __func__ , ## 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", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070094 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070095 }
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{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700631 int yres;
632 /* memory size in pixels */
633 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
635 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700636 case 1:
Krzysztof Helt09a29102008-09-02 14:35:51 -0700637 pixels /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700639 case 8:
640 case 16:
641 case 24:
642 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700645 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
646 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700648 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 return -EINVAL;
650 }
651
Krzysztof Helt09a29102008-09-02 14:35:51 -0700652 if (var->xres_virtual < var->xres)
653 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700655 if (var->yres_virtual == -1) {
656 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
Krzysztof Helt8503df62007-10-16 01:29:08 -0700658 printk(KERN_INFO "cirrusfb: virtual resolution set to "
659 "maximum of %dx%d\n", var->xres_virtual,
660 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 if (var->yres_virtual < var->yres)
663 var->yres_virtual = var->yres;
664
Krzysztof Helt09a29102008-09-02 14:35:51 -0700665 if (var->xres_virtual * var->yres_virtual > pixels) {
666 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
667 "virtual resolution too high to fit into video memory!\n",
668 var->xres_virtual, var->yres_virtual,
669 var->bits_per_pixel);
670 DPRINTK("EXIT - EINVAL error\n");
671 return -EINVAL;
672 }
673
674
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 if (var->xoffset < 0)
676 var->xoffset = 0;
677 if (var->yoffset < 0)
678 var->yoffset = 0;
679
680 /* truncate xoffset and yoffset to maximum if too high */
681 if (var->xoffset > var->xres_virtual - var->xres)
682 var->xoffset = var->xres_virtual - var->xres - 1;
683 if (var->yoffset > var->yres_virtual - var->yres)
684 var->yoffset = var->yres_virtual - var->yres - 1;
685
686 switch (var->bits_per_pixel) {
687 case 1:
688 var->red.offset = 0;
689 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700690 var->green = var->red;
691 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 break;
693
694 case 8:
695 var->red.offset = 0;
696 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700697 var->green = var->red;
698 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 break;
700
701 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700702 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 var->red.offset = 2;
704 var->green.offset = -3;
705 var->blue.offset = 8;
706 } else {
707 var->red.offset = 10;
708 var->green.offset = 5;
709 var->blue.offset = 0;
710 }
711 var->red.length = 5;
712 var->green.length = 5;
713 var->blue.length = 5;
714 break;
715
716 case 24:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700718 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 var->red.offset = 8;
720 var->green.offset = 16;
721 var->blue.offset = 24;
722 } else {
723 var->red.offset = 16;
724 var->green.offset = 8;
725 var->blue.offset = 0;
726 }
727 var->red.length = 8;
728 var->green.length = 8;
729 var->blue.length = 8;
730 break;
731
732 default:
733 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700734 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 /* should never occur */
736 break;
737 }
738
739 var->red.msb_right =
740 var->green.msb_right =
741 var->blue.msb_right =
742 var->transp.offset =
743 var->transp.length =
744 var->transp.msb_right = 0;
745
746 yres = var->yres;
747 if (var->vmode & FB_VMODE_DOUBLE)
748 yres *= 2;
749 else if (var->vmode & FB_VMODE_INTERLACED)
750 yres = (yres + 1) / 2;
751
752 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700753 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
754 "special treatment required! (TODO)\n");
755 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 return -EINVAL;
757 }
758
759 return 0;
760}
761
Krzysztof Helt8503df62007-10-16 01:29:08 -0700762static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700764 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765{
766 long freq;
767 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700768 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 struct cirrusfb_info *cinfo = info->par;
770 int xres, hfront, hsync, hback;
771 int yres, vfront, vsync, vback;
772
Krzysztof Helt8503df62007-10-16 01:29:08 -0700773 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700775 info->fix.line_length = var->xres_virtual / 8;
776 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 break;
778
779 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700780 info->fix.line_length = var->xres_virtual;
781 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 break;
783
784 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 case 24:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700787 info->fix.line_length = var->xres_virtual * maxclockidx;
788 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 break;
790
791 default:
792 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700793 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 /* should never occur */
795 break;
796 }
797
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700798 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700801 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Krzysztof Helt8503df62007-10-16 01:29:08 -0700803 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
805 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
806 regs->multiplexing = 0;
807
808 /* If the frequency is greater than we can support, we might be able
809 * to use multiplexing for the video mode */
810 if (freq > maxclock) {
811 switch (cinfo->btype) {
812 case BT_ALPINE:
813 case BT_GD5480:
814 regs->multiplexing = 1;
815 break;
816
817 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700818 printk(KERN_ERR "cirrusfb: Frequency greater "
819 "than maxclock (%ld kHz)\n", maxclock);
820 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 return -EINVAL;
822 }
823 }
824#if 0
825 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
826 * the VCLK is double the pixel clock. */
827 switch (var->bits_per_pixel) {
828 case 16:
829 case 32:
830 if (regs->HorizRes <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700831 /* Xbh has this type of clock for 32-bit */
832 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 break;
834 }
835#endif
836
Krzysztof Helt8503df62007-10-16 01:29:08 -0700837 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
838 maxclock);
839 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
840 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 xres = var->xres;
843 hfront = var->right_margin;
844 hsync = var->hsync_len;
845 hback = var->left_margin;
846
847 yres = var->yres;
848 vfront = var->lower_margin;
849 vsync = var->vsync_len;
850 vback = var->upper_margin;
851
852 if (var->vmode & FB_VMODE_DOUBLE) {
853 yres *= 2;
854 vfront *= 2;
855 vsync *= 2;
856 vback *= 2;
857 } else if (var->vmode & FB_VMODE_INTERLACED) {
858 yres = (yres + 1) / 2;
859 vfront = (vfront + 1) / 2;
860 vsync = (vsync + 1) / 2;
861 vback = (vback + 1) / 2;
862 }
863 regs->HorizRes = xres;
864 regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
865 regs->HorizDispEnd = xres / 8 - 1;
866 regs->HorizBlankStart = xres / 8;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700867 /* does not count with "-5" */
868 regs->HorizBlankEnd = regs->HorizTotal + 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 regs->HorizSyncStart = (xres + hfront) / 8 + 1;
870 regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
871
872 regs->VertRes = yres;
873 regs->VertTotal = yres + vfront + vsync + vback - 2;
874 regs->VertDispEnd = yres - 1;
875 regs->VertBlankStart = yres;
876 regs->VertBlankEnd = regs->VertTotal;
877 regs->VertSyncStart = yres + vfront - 1;
878 regs->VertSyncEnd = yres + vfront + vsync - 1;
879
880 if (regs->VertRes >= 1024) {
881 regs->VertTotal /= 2;
882 regs->VertSyncStart /= 2;
883 regs->VertSyncEnd /= 2;
884 regs->VertDispEnd /= 2;
885 }
886 if (regs->multiplexing) {
887 regs->HorizTotal /= 2;
888 regs->HorizSyncStart /= 2;
889 regs->HorizSyncEnd /= 2;
890 regs->HorizDispEnd /= 2;
891 }
892
893 return 0;
894}
895
Krzysztof Helt8503df62007-10-16 01:29:08 -0700896static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
897 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700899 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 if (div == 2) {
902 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700903 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
904 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
905 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 } else if (div == 1) {
907 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700908 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
909 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
910 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 }
914}
915
916/*************************************************************************
917 cirrusfb_set_par_foo()
918
919 actually writes the values for a new video mode into the hardware,
920**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700921static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922{
923 struct cirrusfb_info *cinfo = info->par;
924 struct fb_var_screeninfo *var = &info->var;
925 struct cirrusfb_regs regs;
926 u8 __iomem *regbase = cinfo->regbase;
927 unsigned char tmp;
928 int offset = 0, err;
929 const struct cirrusfb_board_info_rec *bi;
930
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 DPRINTK("ENTER\n");
932 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700934 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700936 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700939 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 /* should never happen */
941 DPRINTK("mode change aborted. invalid var.\n");
942 return -EINVAL;
943 }
944
945 bi = &cirrusfb_board_info[cinfo->btype];
946
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700948 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
950 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700951 DPRINTK("CRT0: %ld\n", regs.HorizTotal);
952 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 DPRINTK("CRT1: %ld\n", regs.HorizDispEnd);
955 vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
Krzysztof Helt8503df62007-10-16 01:29:08 -0700957 DPRINTK("CRT2: %ld\n", regs.HorizBlankStart);
958 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
Krzysztof Helt8503df62007-10-16 01:29:08 -0700960 /* + 128: Compatible read */
961 DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);
962 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
963 128 + (regs.HorizBlankEnd % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 DPRINTK("CRT4: %ld\n", regs.HorizSyncStart);
966 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
968 tmp = regs.HorizSyncEnd % 32;
969 if (regs.HorizBlankEnd & 32)
970 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700971 DPRINTK("CRT5: %d\n", tmp);
972 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
Krzysztof Helt8503df62007-10-16 01:29:08 -0700974 DPRINTK("CRT6: %ld\n", regs.VertTotal & 0xff);
975 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
977 tmp = 16; /* LineCompare bit #9 */
978 if (regs.VertTotal & 256)
979 tmp |= 1;
980 if (regs.VertDispEnd & 256)
981 tmp |= 2;
982 if (regs.VertSyncStart & 256)
983 tmp |= 4;
984 if (regs.VertBlankStart & 256)
985 tmp |= 8;
986 if (regs.VertTotal & 512)
987 tmp |= 32;
988 if (regs.VertDispEnd & 512)
989 tmp |= 64;
990 if (regs.VertSyncStart & 512)
991 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700992 DPRINTK("CRT7: %d\n", tmp);
993 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
995 tmp = 0x40; /* LineCompare bit #8 */
996 if (regs.VertBlankStart & 512)
997 tmp |= 0x20;
998 if (var->vmode & FB_VMODE_DOUBLE)
999 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 DPRINTK("CRT9: %d\n", tmp);
1001 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
Krzysztof Helt8503df62007-10-16 01:29:08 -07001003 DPRINTK("CRT10: %ld\n", regs.VertSyncStart & 0xff);
1004 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, regs.VertSyncStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Krzysztof Helt8503df62007-10-16 01:29:08 -07001006 DPRINTK("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
1007 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, regs.VertSyncEnd % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Krzysztof Helt8503df62007-10-16 01:29:08 -07001009 DPRINTK("CRT12: %ld\n", regs.VertDispEnd & 0xff);
1010 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, regs.VertDispEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
Krzysztof Helt8503df62007-10-16 01:29:08 -07001012 DPRINTK("CRT15: %ld\n", regs.VertBlankStart & 0xff);
1013 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, regs.VertBlankStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
Krzysztof Helt8503df62007-10-16 01:29:08 -07001015 DPRINTK("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
1016 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, regs.VertBlankEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
Krzysztof Helt8503df62007-10-16 01:29:08 -07001018 DPRINTK("CRT18: 0xff\n");
1019 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
1021 tmp = 0;
1022 if (var->vmode & FB_VMODE_INTERLACED)
1023 tmp |= 1;
1024 if (regs.HorizBlankEnd & 64)
1025 tmp |= 16;
1026 if (regs.HorizBlankEnd & 128)
1027 tmp |= 32;
1028 if (regs.VertBlankEnd & 256)
1029 tmp |= 64;
1030 if (regs.VertBlankEnd & 512)
1031 tmp |= 128;
1032
Krzysztof Helt8503df62007-10-16 01:29:08 -07001033 DPRINTK("CRT1a: %d\n", tmp);
1034 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
1036 /* set VCLK0 */
1037 /* hardware RefClock: 14.31818 MHz */
1038 /* formula: VClk = (OSC * N) / (D * (1+P)) */
1039 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
1040
Krzysztof Helt8503df62007-10-16 01:29:08 -07001041 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 tmp = regs.den << 1;
1043 if (regs.div != 0)
1044 tmp |= 1;
1045
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 if ((cinfo->btype == BT_SD64) ||
1048 (cinfo->btype == BT_ALPINE) ||
1049 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001050 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
Krzysztof Helt8503df62007-10-16 01:29:08 -07001052 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
1053 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054
1055 if (regs.VertRes >= 1024)
1056 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001057 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 else
1059 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
1060 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001061 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062
Krzysztof Helt8503df62007-10-16 01:29:08 -07001063/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
1064 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
1066 /* don't know if it would hurt to also program this if no interlaced */
1067 /* mode is used, but I feel better this way.. :-) */
1068 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001069 vga_wcrt(regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001071 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
1075 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001076 /* enable display memory & CRTC I/O address for color mode */
1077 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1079 tmp |= 0x40;
1080 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1081 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001082 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083
Krzysztof Helt8503df62007-10-16 01:29:08 -07001084 /* Screen A Preset Row-Scan register */
1085 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
1086 /* text cursor on and start line */
1087 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
1088 /* text cursor end line */
1089 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
1091 /******************************************************
1092 *
1093 * 1 bpp
1094 *
1095 */
1096
1097 /* programming for different color depths */
1098 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001099 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
1100 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101
1102 /* SR07 */
1103 switch (cinfo->btype) {
1104 case BT_SD64:
1105 case BT_PICCOLO:
1106 case BT_PICASSO:
1107 case BT_SPECTRUM:
1108 case BT_PICASSO4:
1109 case BT_ALPINE:
1110 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001111 DPRINTK(" (for GD54xx)\n");
1112 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 regs.multiplexing ?
1114 bi->sr07_1bpp_mux : bi->sr07_1bpp);
1115 break;
1116
1117 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001118 DPRINTK(" (for GD546x)\n");
1119 vga_wseq(regbase, CL_SEQR7,
1120 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 break;
1122
1123 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001124 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126 }
1127
1128 /* Extended Sequencer Mode */
1129 switch (cinfo->btype) {
1130 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001131 /* setting the SEQRF on SD64 is not necessary
1132 * (only during init)
1133 */
1134 DPRINTK("(for SD64)\n");
1135 /* MCLK select */
1136 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 break;
1138
1139 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001140 case BT_SPECTRUM:
1141 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001142 /* ### ueberall 0x22? */
1143 /* ##vorher 1c MCLK select */
1144 vga_wseq(regbase, CL_SEQR1F, 0x22);
1145 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1146 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 break;
1148
1149 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001150 DPRINTK("(for Picasso)\n");
1151 /* ##vorher 22 MCLK select */
1152 vga_wseq(regbase, CL_SEQR1F, 0x22);
1153 /* ## vorher d0 avoid FIFO underruns..? */
1154 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 break;
1156
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 case BT_PICASSO4:
1158 case BT_ALPINE:
1159 case BT_GD5480:
1160 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001161 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 /* do nothing */
1163 break;
1164
1165 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001166 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 break;
1168 }
1169
Krzysztof Helt8503df62007-10-16 01:29:08 -07001170 /* pixel mask: pass-through for first plane */
1171 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001173 /* hidden dac reg: 1280x1024 */
1174 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001176 /* hidden dac: nothing */
1177 WHDR(cinfo, 0);
1178 /* memory mode: odd/even, ext. memory */
1179 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1180 /* plane mask: only write to first plane */
1181 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 offset = var->xres_virtual / 16;
1183 }
1184
1185 /******************************************************
1186 *
1187 * 8 bpp
1188 *
1189 */
1190
1191 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001192 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 switch (cinfo->btype) {
1194 case BT_SD64:
1195 case BT_PICCOLO:
1196 case BT_PICASSO:
1197 case BT_SPECTRUM:
1198 case BT_PICASSO4:
1199 case BT_ALPINE:
1200 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001201 DPRINTK(" (for GD54xx)\n");
1202 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 regs.multiplexing ?
1204 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1205 break;
1206
1207 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001208 DPRINTK(" (for GD546x)\n");
1209 vga_wseq(regbase, CL_SEQR7,
1210 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 break;
1212
1213 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001214 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 break;
1216 }
1217
1218 switch (cinfo->btype) {
1219 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001220 /* MCLK select */
1221 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 break;
1223
1224 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001227 /* ### vorher 1c MCLK select */
1228 vga_wseq(regbase, CL_SEQR1F, 0x22);
1229 /* Fast Page-Mode writes */
1230 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 break;
1232
1233 case BT_PICASSO4:
1234#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235 /* ### INCOMPLETE!! */
1236 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001238/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 break;
1240
1241 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001242 DPRINTK(" (for GD543x)\n");
1243 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 /* We already set SRF and SR1F */
1245 break;
1246
1247 case BT_GD5480:
1248 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001249 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 /* do nothing */
1251 break;
1252
1253 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001254 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 break;
1256 }
1257
Krzysztof Helt8503df62007-10-16 01:29:08 -07001258 /* mode register: 256 color mode */
1259 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1260 /* pixel mask: pass-through all planes */
1261 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001263 /* hidden dac reg: 1280x1024 */
1264 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001266 /* hidden dac: nothing */
1267 WHDR(cinfo, 0);
1268 /* memory mode: chain4, ext. memory */
1269 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1270 /* plane mask: enable writing to all 4 planes */
1271 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 offset = var->xres_virtual / 8;
1273 }
1274
1275 /******************************************************
1276 *
1277 * 16 bpp
1278 *
1279 */
1280
1281 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001282 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 switch (cinfo->btype) {
1284 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001285 /* Extended Sequencer Mode: 256c col. mode */
1286 vga_wseq(regbase, CL_SEQR7, 0xf7);
1287 /* MCLK select */
1288 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 break;
1290
1291 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001292 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001293 vga_wseq(regbase, CL_SEQR7, 0x87);
1294 /* Fast Page-Mode writes */
1295 vga_wseq(regbase, CL_SEQRF, 0xb0);
1296 /* MCLK select */
1297 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 break;
1299
1300 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001301 vga_wseq(regbase, CL_SEQR7, 0x27);
1302 /* Fast Page-Mode writes */
1303 vga_wseq(regbase, CL_SEQRF, 0xb0);
1304 /* MCLK select */
1305 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 break;
1307
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001309 vga_wseq(regbase, CL_SEQR7, 0x27);
1310/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 break;
1312
1313 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001314 DPRINTK(" (for GD543x)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 if (regs.HorizRes >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001316 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001318 vga_wseq(regbase, CL_SEQR7, 0xa3);
1319 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 break;
1321
1322 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001323 DPRINTK(" (for GD5480)\n");
1324 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 /* We already set SRF and SR1F */
1326 break;
1327
1328 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001329 DPRINTK(" (for GD546x)\n");
1330 vga_wseq(regbase, CL_SEQR7,
1331 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 break;
1333
1334 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001335 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 break;
1337 }
1338
Krzysztof Helt8503df62007-10-16 01:29:08 -07001339 /* mode register: 256 color mode */
1340 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1341 /* pixel mask: pass-through all planes */
1342 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001344 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345#elif defined(CONFIG_ZORRO)
1346 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001347 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001349 /* memory mode: chain4, ext. memory */
1350 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1351 /* plane mask: enable writing to all 4 planes */
1352 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 offset = var->xres_virtual / 4;
1354 }
1355
1356 /******************************************************
1357 *
1358 * 32 bpp
1359 *
1360 */
1361
1362 else if (var->bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 DPRINTK("cirrusfb: preparing for 24/32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 switch (cinfo->btype) {
1365 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 /* Extended Sequencer Mode: 256c col. mode */
1367 vga_wseq(regbase, CL_SEQR7, 0xf9);
1368 /* MCLK select */
1369 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 break;
1371
1372 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001373 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001374 vga_wseq(regbase, CL_SEQR7, 0x85);
1375 /* Fast Page-Mode writes */
1376 vga_wseq(regbase, CL_SEQRF, 0xb0);
1377 /* MCLK select */
1378 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 break;
1380
1381 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001382 vga_wseq(regbase, CL_SEQR7, 0x25);
1383 /* Fast Page-Mode writes */
1384 vga_wseq(regbase, CL_SEQRF, 0xb0);
1385 /* MCLK select */
1386 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 break;
1388
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 vga_wseq(regbase, CL_SEQR7, 0x25);
1391/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 break;
1393
1394 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001395 DPRINTK(" (for GD543x)\n");
1396 vga_wseq(regbase, CL_SEQR7, 0xa9);
1397 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 break;
1399
1400 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001401 DPRINTK(" (for GD5480)\n");
1402 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 /* We already set SRF and SR1F */
1404 break;
1405
1406 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001407 DPRINTK(" (for GD546x)\n");
1408 vga_wseq(regbase, CL_SEQR7,
1409 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 break;
1411
1412 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001413 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 break;
1415 }
1416
Krzysztof Helt8503df62007-10-16 01:29:08 -07001417 /* mode register: 256 color mode */
1418 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1419 /* pixel mask: pass-through all planes */
1420 WGen(cinfo, VGA_PEL_MSK, 0xff);
1421 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1422 WHDR(cinfo, 0xc5);
1423 /* memory mode: chain4, ext. memory */
1424 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1425 /* plane mask: enable writing to all 4 planes */
1426 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 offset = var->xres_virtual / 4;
1428 }
1429
1430 /******************************************************
1431 *
1432 * unknown/unsupported bpp
1433 *
1434 */
1435
Krzysztof Helt8503df62007-10-16 01:29:08 -07001436 else
1437 printk(KERN_ERR "cirrusfb: What's this?? "
1438 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
Krzysztof Helt8503df62007-10-16 01:29:08 -07001441 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 tmp = 0x22;
1443 if (offset & 0x100)
1444 tmp |= 0x10; /* offset overflow bit */
1445
Krzysztof Helt8503df62007-10-16 01:29:08 -07001446 /* screen start addr #16-18, fastpagemode cycles */
1447 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
1449 if (cinfo->btype == BT_SD64 ||
1450 cinfo->btype == BT_PICASSO4 ||
1451 cinfo->btype == BT_ALPINE ||
1452 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001453 /* screen start address bit 19 */
1454 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
Krzysztof Helt8503df62007-10-16 01:29:08 -07001456 /* text cursor location high */
1457 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1458 /* text cursor location low */
1459 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1460 /* underline row scanline = at very bottom */
1461 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
Krzysztof Helt8503df62007-10-16 01:29:08 -07001463 /* controller mode */
1464 vga_wattr(regbase, VGA_ATC_MODE, 1);
1465 /* overscan (border) color */
1466 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1467 /* color plane enable */
1468 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1469 /* pixel panning */
1470 vga_wattr(regbase, CL_AR33, 0);
1471 /* color select */
1472 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473
1474 /* [ EGS: SetOffset(); ] */
1475 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001476 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477
Krzysztof Helt8503df62007-10-16 01:29:08 -07001478 /* set/reset register */
1479 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1480 /* set/reset enable */
1481 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1482 /* color compare */
1483 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1484 /* data rotate */
1485 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1486 /* read map select */
1487 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1488 /* miscellaneous register */
1489 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1490 /* color don't care */
1491 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1492 /* bit mask */
1493 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 /* graphics cursor attributes: nothing special */
1496 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
1498 /* finally, turn on everything - turn off "FullBandwidth" bit */
1499 /* also, set "DotClock%2" bit where requested */
1500 tmp = 0x01;
1501
1502/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1503 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1504 tmp |= 0x08;
1505*/
1506
Krzysztof Helt8503df62007-10-16 01:29:08 -07001507 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1508 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509
1510 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
1512 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
1515#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001516 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517#endif
1518
Krzysztof Helt8503df62007-10-16 01:29:08 -07001519 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 return 0;
1521}
1522
1523/* for some reason incomprehensible to me, cirrusfb requires that you write
1524 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001525static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001527 cirrusfb_set_par_foo(info);
1528 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529}
1530
Krzysztof Helt8503df62007-10-16 01:29:08 -07001531static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1532 unsigned blue, unsigned transp,
1533 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534{
1535 struct cirrusfb_info *cinfo = info->par;
1536
1537 if (regno > 255)
1538 return -EINVAL;
1539
1540 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1541 u32 v;
1542 red >>= (16 - info->var.red.length);
1543 green >>= (16 - info->var.green.length);
1544 blue >>= (16 - info->var.blue.length);
1545
Krzysztof Helt8503df62007-10-16 01:29:08 -07001546 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 return 1;
1548 v = (red << info->var.red.offset) |
1549 (green << info->var.green.offset) |
1550 (blue << info->var.blue.offset);
1551
Krzysztof Helt060b6002007-10-16 01:29:13 -07001552 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 return 0;
1554 }
1555
Krzysztof Helt8503df62007-10-16 01:29:08 -07001556 if (info->var.bits_per_pixel == 8)
1557 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
1559 return 0;
1560
1561}
1562
1563/*************************************************************************
1564 cirrusfb_pan_display()
1565
1566 performs display panning - provided hardware permits this
1567**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001568static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1569 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570{
1571 int xoffset = 0;
1572 int yoffset = 0;
1573 unsigned long base;
1574 unsigned char tmp = 0, tmp2 = 0, xpix;
1575 struct cirrusfb_info *cinfo = info->par;
1576
Krzysztof Helt8503df62007-10-16 01:29:08 -07001577 DPRINTK("ENTER\n");
1578 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
1580 /* no range checks for xoffset and yoffset, */
1581 /* as fb_pan_display has already done this */
1582 if (var->vmode & FB_VMODE_YWRAP)
1583 return -EINVAL;
1584
1585 info->var.xoffset = var->xoffset;
1586 info->var.yoffset = var->yoffset;
1587
1588 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1589 yoffset = var->yoffset;
1590
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001591 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 if (info->var.bits_per_pixel == 1) {
1594 /* base is already correct */
1595 xpix = (unsigned char) (var->xoffset % 8);
1596 } else {
1597 base /= 4;
1598 xpix = (unsigned char) ((xoffset % 4) * 2);
1599 }
1600
Krzysztof Helt8503df62007-10-16 01:29:08 -07001601 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001604 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1605 (unsigned char) (base & 0xff));
1606 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1607 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608
1609 /* construct bits 16, 17 and 18 of screen start address */
1610 if (base & 0x10000)
1611 tmp |= 0x01;
1612 if (base & 0x20000)
1613 tmp |= 0x04;
1614 if (base & 0x40000)
1615 tmp |= 0x08;
1616
Krzysztof Helt8503df62007-10-16 01:29:08 -07001617 /* 0xf2 is %11110010, exclude tmp bits */
1618 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1619 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
1621 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001622 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1623 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
Krzysztof Helt8503df62007-10-16 01:29:08 -07001625 /* write pixel panning value to AR33; this does not quite work in 8bpp
1626 *
1627 * ### Piccolo..? Will this work?
1628 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001630 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
Krzysztof Helt8503df62007-10-16 01:29:08 -07001632 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633
Krzysztof Helt8503df62007-10-16 01:29:08 -07001634 DPRINTK("EXIT\n");
1635 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636}
1637
Krzysztof Helt8503df62007-10-16 01:29:08 -07001638static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639{
1640 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1642 * then the caller blanks by setting the CLUT (Color Look Up Table)
1643 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1644 * failed due to e.g. a video mode which doesn't support it.
1645 * Implements VESA suspend and powerdown modes on hardware that
1646 * supports disabling hsync/vsync:
1647 * blank_mode == 2: suspend vsync
1648 * blank_mode == 3: suspend hsync
1649 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 */
1651 unsigned char val;
1652 struct cirrusfb_info *cinfo = info->par;
1653 int current_mode = cinfo->blank_mode;
1654
Krzysztof Helt8503df62007-10-16 01:29:08 -07001655 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
1657 if (info->state != FBINFO_STATE_RUNNING ||
1658 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 return 0;
1661 }
1662
1663 /* Undo current */
1664 if (current_mode == FB_BLANK_NORMAL ||
1665 current_mode == FB_BLANK_UNBLANK) {
1666 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1668 /* clear "FullBandwidth" bit */
1669 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001671 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 }
1673
1674 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001677 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1678 /* set "FullBandwidth" bit */
1679 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 }
1681
1682 switch (blank_mode) {
1683 case FB_BLANK_UNBLANK:
1684 case FB_BLANK_NORMAL:
1685 break;
1686 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 break;
1689 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001690 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 break;
1692 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 break;
1695 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001696 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 return 1;
1698 }
1699
1700 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702
1703 /* Let fbcon do a soft blank for us */
1704 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1705}
1706/**** END Hardware specific Routines **************************************/
1707/****************************************************************************/
1708/**** BEGIN Internal Routines ***********************************************/
1709
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001710static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001712 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 const struct cirrusfb_board_info_rec *bi;
1714
Krzysztof Helt8503df62007-10-16 01:29:08 -07001715 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
Krzysztof Helt8503df62007-10-16 01:29:08 -07001717 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718
1719 bi = &cirrusfb_board_info[cinfo->btype];
1720
1721 /* reset board globally */
1722 switch (cinfo->btype) {
1723 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 WSFR(cinfo, 0x01);
1725 udelay(500);
1726 WSFR(cinfo, 0x51);
1727 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 break;
1729 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001730 WSFR2(cinfo, 0xff);
1731 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 break;
1733 case BT_SD64:
1734 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001735 WSFR(cinfo, 0x1f);
1736 udelay(500);
1737 WSFR(cinfo, 0x4f);
1738 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 break;
1740 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001741 /* disable flickerfixer */
1742 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1743 mdelay(100);
1744 /* from Klaus' NetBSD driver: */
1745 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1746 /* put blitter into 542x compat */
1747 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1748 /* mode */
1749 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750 break;
1751
1752 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001753 /* from Klaus' NetBSD driver: */
1754 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 break;
1756
1757 case BT_ALPINE:
1758 /* Nothing to do to reset the board. */
1759 break;
1760
1761 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001762 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 break;
1764 }
1765
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001766 /* make sure RAM size set by this point */
1767 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768
1769 /* the P4 is not fully initialized here; I rely on it having been */
1770 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001771 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
1773 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001774 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1775 WGen(cinfo, CL_POS102, 0x01);
1776 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
1778 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001779 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780
Krzysztof Helt8503df62007-10-16 01:29:08 -07001781 /* reset sequencer logic */
1782 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783
Krzysztof Helt8503df62007-10-16 01:29:08 -07001784 /* FullBandwidth (video off) and 8/9 dot clock */
1785 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1786 /* polarity (-/-), disable access to display memory,
1787 * VGA_CRTC_START_HI base address: color
1788 */
1789 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
Krzysztof Helt8503df62007-10-16 01:29:08 -07001791 /* "magic cookie" - doesn't make any sense to me.. */
1792/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1793 /* unlock all extension registers */
1794 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 /* reset blitter */
1797 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
1799 switch (cinfo->btype) {
1800 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001801 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 break;
1803 case BT_ALPINE:
1804 break;
1805 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001806 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 break;
1808 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1810 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811 break;
1812 }
1813 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001814 /* plane mask: nothing */
1815 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1816 /* character map select: doesn't even matter in gx mode */
1817 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1818 /* memory mode: chain-4, no odd/even, ext. memory */
1819 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
1821 /* controller-internal base address of video memory */
1822 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001823 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824
Krzysztof Helt8503df62007-10-16 01:29:08 -07001825 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1826 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827
Krzysztof Helt8503df62007-10-16 01:29:08 -07001828 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1829 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1830 /* graphics cursor Y position (..."... ) */
1831 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1832 /* graphics cursor attributes */
1833 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1834 /* graphics cursor pattern address */
1835 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836
1837 /* writing these on a P4 might give problems.. */
1838 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001839 /* configuration readback and ext. color */
1840 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1841 /* signature generator */
1842 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 }
1844
1845 /* MCLK select etc. */
1846 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001847 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848
Krzysztof Helt8503df62007-10-16 01:29:08 -07001849 /* Screen A preset row scan: none */
1850 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1851 /* Text cursor start: disable text cursor */
1852 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1853 /* Text cursor end: - */
1854 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1855 /* Screen start address high: 0 */
1856 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1857 /* Screen start address low: 0 */
1858 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1859 /* text cursor location high: 0 */
1860 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1861 /* text cursor location low: 0 */
1862 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863
Krzysztof Helt8503df62007-10-16 01:29:08 -07001864 /* Underline Row scanline: - */
1865 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1866 /* mode control: timing enable, byte mode, no compat modes */
1867 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1868 /* Line Compare: not needed */
1869 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001871 /* ext. display controls: ext.adr. wrap */
1872 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873
Krzysztof Helt8503df62007-10-16 01:29:08 -07001874 /* Set/Reset registes: - */
1875 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1876 /* Set/Reset enable: - */
1877 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1878 /* Color Compare: - */
1879 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1880 /* Data Rotate: - */
1881 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1882 /* Read Map Select: - */
1883 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1884 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1885 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1886 /* Miscellaneous: memory map base address, graphics mode */
1887 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1888 /* Color Don't care: involve all planes */
1889 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1890 /* Bit Mask: no mask at all */
1891 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001893 /* (5434 can't have bit 3 set for bitblt) */
1894 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001896 /* Graphics controller mode extensions: finer granularity,
1897 * 8byte data latches
1898 */
1899 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1902 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1903 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1904 /* Background color byte 1: - */
1905 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1906 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907
Krzysztof Helt8503df62007-10-16 01:29:08 -07001908 /* Attribute Controller palette registers: "identity mapping" */
1909 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1910 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1911 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1912 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1913 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1914 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1915 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1916 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1917 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1918 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1919 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1920 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1921 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1922 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1923 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1924 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925
Krzysztof Helt8503df62007-10-16 01:29:08 -07001926 /* Attribute Controller mode: graphics mode */
1927 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1928 /* Overscan color reg.: reg. 0 */
1929 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1930 /* Color Plane enable: Enable all 4 planes */
1931 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1932/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1933 /* Color Select: - */
1934 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935
Krzysztof Helt8503df62007-10-16 01:29:08 -07001936 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937
1938 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001939 /* polarity (-/-), enable display mem,
1940 * VGA_CRTC_START_HI i/o base = color
1941 */
1942 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943
Krzysztof Helt8503df62007-10-16 01:29:08 -07001944 /* BLT Start/status: Blitter reset */
1945 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1946 /* - " - : "end-of-reset" */
1947 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948
1949 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001950 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951
Krzysztof Helt8503df62007-10-16 01:29:08 -07001952 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 return;
1954}
1955
Krzysztof Helt8503df62007-10-16 01:29:08 -07001956static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957{
1958#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1959 static int IsOn = 0; /* XXX not ok for multiple boards */
1960
Krzysztof Helt8503df62007-10-16 01:29:08 -07001961 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
1963 if (cinfo->btype == BT_PICASSO4)
1964 return; /* nothing to switch */
1965 if (cinfo->btype == BT_ALPINE)
1966 return; /* nothing to switch */
1967 if (cinfo->btype == BT_GD5480)
1968 return; /* nothing to switch */
1969 if (cinfo->btype == BT_PICASSO) {
1970 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001971 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972
Krzysztof Helt8503df62007-10-16 01:29:08 -07001973 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 return;
1975 }
1976 if (on) {
1977 switch (cinfo->btype) {
1978 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001979 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 break;
1981 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001982 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 break;
1984 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001985 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 break;
1987 default: /* do nothing */ break;
1988 }
1989 } else {
1990 switch (cinfo->btype) {
1991 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001992 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 break;
1994 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001995 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 break;
1997 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001998 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 break;
2000 default: /* do nothing */ break;
2001 }
2002 }
2003
Krzysztof Helt8503df62007-10-16 01:29:08 -07002004 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005#endif /* CONFIG_ZORRO */
2006}
2007
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008/******************************************/
2009/* Linux 2.6-style accelerated functions */
2010/******************************************/
2011
Krzysztof Helt8503df62007-10-16 01:29:08 -07002012static void cirrusfb_fillrect(struct fb_info *info,
2013 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 struct fb_fillrect modded;
2016 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002017 struct cirrusfb_info *cinfo = info->par;
2018 int m = info->var.bits_per_pixel;
2019 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
2020 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021
2022 if (info->state != FBINFO_STATE_RUNNING)
2023 return;
2024 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2025 cfb_fillrect(info, region);
2026 return;
2027 }
2028
2029 vxres = info->var.xres_virtual;
2030 vyres = info->var.yres_virtual;
2031
2032 memcpy(&modded, region, sizeof(struct fb_fillrect));
2033
Krzysztof Helt8503df62007-10-16 01:29:08 -07002034 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 modded.dx >= vxres || modded.dy >= vyres)
2036 return;
2037
Krzysztof Helt8503df62007-10-16 01:29:08 -07002038 if (modded.dx + modded.width > vxres)
2039 modded.width = vxres - modded.dx;
2040 if (modded.dy + modded.height > vyres)
2041 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042
Krzysztof Helt060b6002007-10-16 01:29:13 -07002043 cirrusfb_RectFill(cinfo->regbase,
2044 info->var.bits_per_pixel,
2045 (region->dx * m) / 8, region->dy,
2046 (region->width * m) / 8, region->height,
2047 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07002048 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049}
2050
Krzysztof Helt8503df62007-10-16 01:29:08 -07002051static void cirrusfb_copyarea(struct fb_info *info,
2052 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 struct fb_copyarea modded;
2055 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002056 struct cirrusfb_info *cinfo = info->par;
2057 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058
2059 if (info->state != FBINFO_STATE_RUNNING)
2060 return;
2061 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2062 cfb_copyarea(info, area);
2063 return;
2064 }
2065
2066 vxres = info->var.xres_virtual;
2067 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002068 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069
Krzysztof Helt8503df62007-10-16 01:29:08 -07002070 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 modded.sx >= vxres || modded.sy >= vyres ||
2072 modded.dx >= vxres || modded.dy >= vyres)
2073 return;
2074
Krzysztof Helt8503df62007-10-16 01:29:08 -07002075 if (modded.sx + modded.width > vxres)
2076 modded.width = vxres - modded.sx;
2077 if (modded.dx + modded.width > vxres)
2078 modded.width = vxres - modded.dx;
2079 if (modded.sy + modded.height > vyres)
2080 modded.height = vyres - modded.sy;
2081 if (modded.dy + modded.height > vyres)
2082 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
Krzysztof Helt060b6002007-10-16 01:29:13 -07002084 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
2085 (area->sx * m) / 8, area->sy,
2086 (area->dx * m) / 8, area->dy,
2087 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07002088 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002089
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090}
2091
Krzysztof Helt8503df62007-10-16 01:29:08 -07002092static void cirrusfb_imageblit(struct fb_info *info,
2093 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094{
2095 struct cirrusfb_info *cinfo = info->par;
2096
Krzysztof Helt8503df62007-10-16 01:29:08 -07002097 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 cfb_imageblit(info, image);
2099}
2100
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101#ifdef CONFIG_PPC_PREP
2102#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
2103#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002104static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002106 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107
2108 *display = PREP_VIDEO_BASE;
2109 *registers = (unsigned long) PREP_IO_BASE;
2110
Krzysztof Helt8503df62007-10-16 01:29:08 -07002111 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112}
2113
2114#endif /* CONFIG_PPC_PREP */
2115
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002117static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118
2119/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
2120 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
2121 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
2122 * seem to have. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002123static unsigned int cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124{
2125 unsigned long mem;
2126 unsigned char SRF;
2127
Krzysztof Helt8503df62007-10-16 01:29:08 -07002128 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129
Krzysztof Helt8503df62007-10-16 01:29:08 -07002130 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002132 case 0x08:
2133 mem = 512 * 1024;
2134 break;
2135 case 0x10:
2136 mem = 1024 * 1024;
2137 break;
2138 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2139 * on the 5430.
2140 */
2141 case 0x18:
2142 mem = 2048 * 1024;
2143 break;
2144 default:
2145 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 mem = 1024 * 1024;
2147 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002148 if (SRF & 0x80)
2149 /* If DRAM bank switching is enabled, there must be twice as much
2150 * memory installed. (4MB on the 5434)
2151 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2155
Krzysztof Helt8503df62007-10-16 01:29:08 -07002156 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 return mem;
2158}
2159
Krzysztof Helt8503df62007-10-16 01:29:08 -07002160static void get_pci_addrs(const struct pci_dev *pdev,
2161 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002163 assert(pdev != NULL);
2164 assert(display != NULL);
2165 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166
Krzysztof Helt8503df62007-10-16 01:29:08 -07002167 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168
2169 *display = 0;
2170 *registers = 0;
2171
2172 /* This is a best-guess for now */
2173
2174 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2175 *display = pci_resource_start(pdev, 1);
2176 *registers = pci_resource_start(pdev, 0);
2177 } else {
2178 *display = pci_resource_start(pdev, 0);
2179 *registers = pci_resource_start(pdev, 1);
2180 }
2181
Krzysztof Helt8503df62007-10-16 01:29:08 -07002182 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183
Krzysztof Helt8503df62007-10-16 01:29:08 -07002184 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185}
2186
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002187static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002189 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 struct pci_dev *pdev = cinfo->pdev;
2191
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002192 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193#if 0 /* if system didn't claim this region, we would... */
2194 release_mem_region(0xA0000, 65535);
2195#endif
2196 if (release_io_ports)
2197 release_region(0x3C0, 32);
2198 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199}
2200#endif /* CONFIG_PCI */
2201
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002203static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204{
Al Virod91f5bb2007-10-17 00:27:18 +01002205 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 zorro_release_device(cinfo->zdev);
2207
2208 if (cinfo->btype == BT_PICASSO4) {
2209 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002210 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002211 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 } else {
2213 if (zorro_resource_start(cinfo->zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002214 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216}
2217#endif /* CONFIG_ZORRO */
2218
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002219static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002221 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 struct fb_var_screeninfo *var = &info->var;
2223
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 info->pseudo_palette = cinfo->pseudo_palette;
2225 info->flags = FBINFO_DEFAULT
2226 | FBINFO_HWACCEL_XPAN
2227 | FBINFO_HWACCEL_YPAN
2228 | FBINFO_HWACCEL_FILLRECT
2229 | FBINFO_HWACCEL_COPYAREA;
2230 if (noaccel)
2231 info->flags |= FBINFO_HWACCEL_DISABLED;
2232 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 if (cinfo->btype == BT_GD5480) {
2234 if (var->bits_per_pixel == 16)
2235 info->screen_base += 1 * MB_;
2236 if (var->bits_per_pixel == 24 || var->bits_per_pixel == 32)
2237 info->screen_base += 2 * MB_;
2238 }
2239
2240 /* Fill fix common fields */
2241 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2242 sizeof(info->fix.id));
2243
2244 /* monochrome: only 1 memory plane */
2245 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002246 info->fix.smem_len = info->screen_size;
2247 if (var->bits_per_pixel == 1)
2248 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 info->fix.xpanstep = 1;
2251 info->fix.ypanstep = 1;
2252 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253
2254 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 info->fix.mmio_len = 0;
2256 info->fix.accel = FB_ACCEL_NONE;
2257
2258 fb_alloc_cmap(&info->cmap, 256, 0);
2259
2260 return 0;
2261}
2262
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002263static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002265 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002267 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268
Krzysztof Helt8503df62007-10-16 01:29:08 -07002269 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270
Krzysztof Helt8503df62007-10-16 01:29:08 -07002271 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2272 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 btype = cinfo->btype;
2275
2276 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002277 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002279 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
2281 /* Make pretend we've set the var so our structures are in a "good" */
2282 /* state, even though we haven't written the mode to the hw yet... */
2283 info->var = cirrusfb_predefined[cirrusfb_def_mode].var;
2284 info->var.activate = FB_ACTIVATE_NOW;
2285
2286 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2287 if (err < 0) {
2288 /* should never happen */
2289 DPRINTK("choking on default var... umm, no good.\n");
2290 goto err_unmap_cirrusfb;
2291 }
2292
2293 /* set all the vital stuff */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002294 cirrusfb_set_fbinfo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
2296 err = register_framebuffer(info);
2297 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002298 printk(KERN_ERR "cirrusfb: could not register "
2299 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 goto err_dealloc_cmap;
2301 }
2302
Krzysztof Helt8503df62007-10-16 01:29:08 -07002303 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 return 0;
2305
2306err_dealloc_cmap:
2307 fb_dealloc_cmap(&info->cmap);
2308err_unmap_cirrusfb:
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002309 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002310 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311 return err;
2312}
2313
Krzysztof Helt8503df62007-10-16 01:29:08 -07002314static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315{
2316 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002317 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
Krzysztof Helt8503df62007-10-16 01:29:08 -07002319 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320
Krzysztof Helt8503df62007-10-16 01:29:08 -07002321 unregister_framebuffer(info);
2322 fb_dealloc_cmap(&info->cmap);
2323 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002324 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002325 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326
Krzysztof Helt8503df62007-10-16 01:29:08 -07002327 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328}
2329
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002331static int cirrusfb_pci_register(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332 const struct pci_device_id *ent)
2333{
2334 struct cirrusfb_info *cinfo;
2335 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002336 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337 unsigned long board_addr, board_size;
2338 int ret;
2339
2340 ret = pci_enable_device(pdev);
2341 if (ret < 0) {
2342 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2343 goto err_out;
2344 }
2345
2346 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2347 if (!info) {
2348 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2349 ret = -ENOMEM;
2350 goto err_disable;
2351 }
2352
2353 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 cinfo->pdev = pdev;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002355 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
Krzysztof Helt7345de32007-10-16 01:29:11 -07002357 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002359 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360
Krzysztof Helt8503df62007-10-16 01:29:08 -07002361 if (isPReP) {
2362 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002364 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002366 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002367 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002369 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002370 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002371 /* FIXME: this forces VGA. alternatives? */
2372 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373 }
2374
Krzysztof Helt8503df62007-10-16 01:29:08 -07002375 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002376 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
2378 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002379 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380
2381 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002382 if (ret < 0) {
2383 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2384 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 board_addr);
2386 goto err_release_fb;
2387 }
2388#if 0 /* if the system didn't claim this region, we would... */
2389 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2390 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2391,
2392 0xA0000L);
2393 ret = -EBUSY;
2394 goto err_release_regions;
2395 }
2396#endif
2397 if (request_region(0x3C0, 32, "cirrusfb"))
2398 release_io_ports = 1;
2399
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002400 info->screen_base = ioremap(board_addr, board_size);
2401 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 ret = -EIO;
2403 goto err_release_legacy;
2404 }
2405
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002406 info->fix.smem_start = board_addr;
2407 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408 cinfo->unmap = cirrusfb_pci_unmap;
2409
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002410 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2411 "Logic chipset on PCI bus\n",
2412 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 pci_set_drvdata(pdev, info);
2414
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002415 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002416 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002417 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002418 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419
2420err_release_legacy:
2421 if (release_io_ports)
2422 release_region(0x3C0, 32);
2423#if 0
2424 release_mem_region(0xA0000, 65535);
2425err_release_regions:
2426#endif
2427 pci_release_regions(pdev);
2428err_release_fb:
2429 framebuffer_release(info);
2430err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431err_out:
2432 return ret;
2433}
2434
Krzysztof Helt8503df62007-10-16 01:29:08 -07002435static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436{
2437 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002438 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441
Krzysztof Helt8503df62007-10-16 01:29:08 -07002442 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443}
2444
2445static struct pci_driver cirrusfb_pci_driver = {
2446 .name = "cirrusfb",
2447 .id_table = cirrusfb_pci_table,
2448 .probe = cirrusfb_pci_register,
2449 .remove = __devexit_p(cirrusfb_pci_unregister),
2450#ifdef CONFIG_PM
2451#if 0
2452 .suspend = cirrusfb_pci_suspend,
2453 .resume = cirrusfb_pci_resume,
2454#endif
2455#endif
2456};
2457#endif /* CONFIG_PCI */
2458
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459#ifdef CONFIG_ZORRO
2460static int cirrusfb_zorro_register(struct zorro_dev *z,
2461 const struct zorro_device_id *ent)
2462{
2463 struct cirrusfb_info *cinfo;
2464 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002465 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 struct zorro_dev *z2 = NULL;
2467 unsigned long board_addr, board_size, size;
2468 int ret;
2469
2470 btype = ent->driver_data;
2471 if (cirrusfb_zorro_table2[btype].id2)
2472 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2473 size = cirrusfb_zorro_table2[btype].size;
2474 printk(KERN_INFO "cirrusfb: %s board detected; ",
2475 cirrusfb_board_info[btype].name);
2476
2477 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2478 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002479 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480 ret = -ENOMEM;
2481 goto err_out;
2482 }
2483
2484 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 cinfo->btype = btype;
2486
Al Viro36ea96a2007-10-27 19:46:58 +01002487 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002488 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489
2490 cinfo->zdev = z;
2491 board_addr = zorro_resource_start(z);
2492 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002493 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494
2495 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2497 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 board_addr);
2499 ret = -EBUSY;
2500 goto err_release_fb;
2501 }
2502
Krzysztof Helt8503df62007-10-16 01:29:08 -07002503 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504
2505 ret = -EIO;
2506
2507 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509
2510 /* To be precise, for the P4 this is not the */
2511 /* begin of the board, but the begin of RAM. */
2512 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2513 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002514 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 if (!cinfo->regbase)
2516 goto err_release_region;
2517
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2519 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002521 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002523 info->fix.smem_start = board_addr + 16777216;
2524 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2525 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 goto err_unmap_regbase;
2527 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002528 printk(KERN_INFO " REG at $%lx\n",
2529 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002531 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002533 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002535 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2536 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537 goto err_release_region;
2538
2539 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002541 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542
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 }
2546 cinfo->unmap = cirrusfb_zorro_unmap;
2547
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 zorro_set_drvdata(z, info);
2550
Al Virod91f5bb2007-10-17 00:27:18 +01002551 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002552 if (ret) {
2553 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002554 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002555 iounmap(cinfo->regbase - 0x600000);
2556 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002557 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002558 }
2559 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560
2561err_unmap_regbase:
2562 /* Parental advisory: explicit hack */
2563 iounmap(cinfo->regbase - 0x600000);
2564err_release_region:
2565 release_region(board_addr, board_size);
2566err_release_fb:
2567 framebuffer_release(info);
2568err_out:
2569 return ret;
2570}
2571
2572void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2573{
2574 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002575 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576
Krzysztof Helt8503df62007-10-16 01:29:08 -07002577 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580}
2581
2582static struct zorro_driver cirrusfb_zorro_driver = {
2583 .name = "cirrusfb",
2584 .id_table = cirrusfb_zorro_table,
2585 .probe = cirrusfb_zorro_register,
2586 .remove = __devexit_p(cirrusfb_zorro_unregister),
2587};
2588#endif /* CONFIG_ZORRO */
2589
2590static int __init cirrusfb_init(void)
2591{
2592 int error = 0;
2593
2594#ifndef MODULE
2595 char *option = NULL;
2596
2597 if (fb_get_options("cirrusfb", &option))
2598 return -ENODEV;
2599 cirrusfb_setup(option);
2600#endif
2601
2602#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002603 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604#endif
2605#ifdef CONFIG_PCI
2606 error |= pci_register_driver(&cirrusfb_pci_driver);
2607#endif
2608 return error;
2609}
2610
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611#ifndef MODULE
2612static int __init cirrusfb_setup(char *options) {
2613 char *this_opt, s[32];
2614 int i;
2615
Krzysztof Helt8503df62007-10-16 01:29:08 -07002616 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617
2618 if (!options || !*options)
2619 return 0;
2620
Krzysztof Helt8503df62007-10-16 01:29:08 -07002621 while ((this_opt = strsep(&options, ",")) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 if (!*this_opt) continue;
2623
2624 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2625
2626 for (i = 0; i < NUM_TOTAL_MODES; i++) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002627 sprintf(s, "mode:%s", cirrusfb_predefined[i].name);
2628 if (strcmp(this_opt, s) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 cirrusfb_def_mode = i;
2630 }
2631 if (!strcmp(this_opt, "noaccel"))
2632 noaccel = 1;
2633 }
2634 return 0;
2635}
2636#endif
2637
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638 /*
2639 * Modularization
2640 */
2641
2642MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2643MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2644MODULE_LICENSE("GPL");
2645
Krzysztof Helt8503df62007-10-16 01:29:08 -07002646static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647{
2648#ifdef CONFIG_PCI
2649 pci_unregister_driver(&cirrusfb_pci_driver);
2650#endif
2651#ifdef CONFIG_ZORRO
2652 zorro_unregister_driver(&cirrusfb_zorro_driver);
2653#endif
2654}
2655
2656module_init(cirrusfb_init);
2657
2658#ifdef MODULE
2659module_exit(cirrusfb_exit);
2660#endif
2661
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662/**********************************************************************/
2663/* about the following functions - I have used the same names for the */
2664/* functions as Markus Wild did in his Retina driver for NetBSD as */
2665/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002666/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667/**********************************************************************/
2668
2669/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002670static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 int regnum, unsigned char val)
2672{
2673 unsigned long regofs = 0;
2674
2675 if (cinfo->btype == BT_PICASSO) {
2676 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002677/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2678 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2680 regofs = 0xfff;
2681 }
2682
Krzysztof Helt8503df62007-10-16 01:29:08 -07002683 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684}
2685
2686/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002687static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688{
2689 unsigned long regofs = 0;
2690
2691 if (cinfo->btype == BT_PICASSO) {
2692 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002693/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2694 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2696 regofs = 0xfff;
2697 }
2698
Krzysztof Helt8503df62007-10-16 01:29:08 -07002699 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700}
2701
2702/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002703static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002705 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706
Krzysztof Helt8503df62007-10-16 01:29:08 -07002707 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708
Krzysztof Helt8503df62007-10-16 01:29:08 -07002709 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710 /* if we're just in "write value" mode, write back the */
2711 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002712 vga_w(cinfo->regbase, VGA_ATT_IW,
2713 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 }
2715 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002716/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2717 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718
2719 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002720 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721
Krzysztof Helt8503df62007-10-16 01:29:08 -07002722 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723}
2724
2725/*** WHDR() - write into the Hidden DAC register ***/
2726/* as the HDR is the only extension register that requires special treatment
2727 * (the other extension registers are accessible just like the "ordinary"
2728 * registers of their functional group) here is a specialized routine for
2729 * accessing the HDR
2730 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002731static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732{
2733 unsigned char dummy;
2734
2735 if (cinfo->btype == BT_PICASSO) {
2736 /* Klaus' hint for correct access to HDR on some boards */
2737 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002738 WGen(cinfo, VGA_PEL_MSK, 0x00);
2739 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002741 dummy = RGen(cinfo, VGA_PEL_IW);
2742 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743 }
2744 /* now do the usual stuff to access the HDR */
2745
Krzysztof Helt8503df62007-10-16 01:29:08 -07002746 dummy = RGen(cinfo, VGA_PEL_MSK);
2747 udelay(200);
2748 dummy = RGen(cinfo, VGA_PEL_MSK);
2749 udelay(200);
2750 dummy = RGen(cinfo, VGA_PEL_MSK);
2751 udelay(200);
2752 dummy = RGen(cinfo, VGA_PEL_MSK);
2753 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754
Krzysztof Helt8503df62007-10-16 01:29:08 -07002755 WGen(cinfo, VGA_PEL_MSK, val);
2756 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757
2758 if (cinfo->btype == BT_PICASSO) {
2759 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002760 dummy = RGen(cinfo, VGA_PEL_IW);
2761 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762
2763 /* and at the end, restore the mask value */
2764 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002765 WGen(cinfo, VGA_PEL_MSK, 0xff);
2766 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 }
2768}
2769
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002771static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772{
2773#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002774 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002776 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777#endif
2778}
2779
2780/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002781static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782{
2783#ifdef CONFIG_ZORRO
2784 /* writing an arbitrary value to this one causes the monitor switcher */
2785 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002786 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002788 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789#endif
2790}
2791
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002793static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 unsigned char green, unsigned char blue)
2795{
2796 unsigned int data = VGA_PEL_D;
2797
2798 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002799 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800
2801 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2802 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2803 /* but DAC data register IS, at least for Picasso II */
2804 if (cinfo->btype == BT_PICASSO)
2805 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002806 vga_w(cinfo->regbase, data, red);
2807 vga_w(cinfo->regbase, data, green);
2808 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002810 vga_w(cinfo->regbase, data, blue);
2811 vga_w(cinfo->regbase, data, green);
2812 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 }
2814}
2815
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816#if 0
2817/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002818static void RClut(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
Krzysztof Helt8503df62007-10-16 01:29:08 -07002823 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824
2825 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2826 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2827 if (cinfo->btype == BT_PICASSO)
2828 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002829 *red = vga_r(cinfo->regbase, data);
2830 *green = vga_r(cinfo->regbase, data);
2831 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002833 *blue = vga_r(cinfo->regbase, data);
2834 *green = vga_r(cinfo->regbase, data);
2835 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 }
2837}
2838#endif
2839
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840/*******************************************************************
2841 cirrusfb_WaitBLT()
2842
2843 Wait for the BitBLT engine to complete a possible earlier job
2844*********************************************************************/
2845
2846/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848{
2849 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002850 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851 /* do nothing */ ;
2852}
2853
2854/*******************************************************************
2855 cirrusfb_BitBLT()
2856
2857 perform accelerated "scrolling"
2858********************************************************************/
2859
Krzysztof Helt8503df62007-10-16 01:29:08 -07002860static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2861 u_short curx, u_short cury,
2862 u_short destx, u_short desty,
2863 u_short width, u_short height,
2864 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865{
2866 u_short nwidth, nheight;
2867 u_long nsrc, ndest;
2868 u_char bltmode;
2869
Krzysztof Helt8503df62007-10-16 01:29:08 -07002870 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871
2872 nwidth = width - 1;
2873 nheight = height - 1;
2874
2875 bltmode = 0x00;
2876 /* if source adr < dest addr, do the Blt backwards */
2877 if (cury <= desty) {
2878 if (cury == desty) {
2879 /* if src and dest are on the same line, check x */
2880 if (curx < destx)
2881 bltmode |= 0x01;
2882 } else
2883 bltmode |= 0x01;
2884 }
2885 if (!bltmode) {
2886 /* standard case: forward blitting */
2887 nsrc = (cury * line_length) + curx;
2888 ndest = (desty * line_length) + destx;
2889 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002890 /* this means start addresses are at the end,
2891 * counting backwards
2892 */
2893 nsrc = cury * line_length + curx +
2894 nheight * line_length + nwidth;
2895 ndest = desty * line_length + destx +
2896 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 }
2898
2899 /*
2900 run-down of registers to be programmed:
2901 destination pitch
2902 source pitch
2903 BLT width/height
2904 source start
2905 destination start
2906 BLT mode
2907 BLT ROP
2908 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2909 start/stop
2910 */
2911
Krzysztof Helt8503df62007-10-16 01:29:08 -07002912 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913
2914 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002915 /* dest pitch low */
2916 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2917 /* dest pitch hi */
2918 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2919 /* source pitch low */
2920 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2921 /* source pitch hi */
2922 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923
2924 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002925 /* BLT width low */
2926 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2927 /* BLT width hi */
2928 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002929
2930 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002931 /* BLT height low */
2932 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2933 /* BLT width hi */
2934 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935
2936 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002937 /* BLT dest low */
2938 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2939 /* BLT dest mid */
2940 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2941 /* BLT dest hi */
2942 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943
2944 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002945 /* BLT src low */
2946 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2947 /* BLT src mid */
2948 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2949 /* BLT src hi */
2950 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951
2952 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002953 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954
2955 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002956 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957
2958 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002959 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960
Krzysztof Helt8503df62007-10-16 01:29:08 -07002961 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962}
2963
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964/*******************************************************************
2965 cirrusfb_RectFill()
2966
2967 perform accelerated rectangle fill
2968********************************************************************/
2969
Krzysztof Helt8503df62007-10-16 01:29:08 -07002970static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 u_short x, u_short y, u_short width, u_short height,
2972 u_char color, u_short line_length)
2973{
2974 u_short nwidth, nheight;
2975 u_long ndest;
2976 u_char op;
2977
Krzysztof Helt8503df62007-10-16 01:29:08 -07002978 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979
2980 nwidth = width - 1;
2981 nheight = height - 1;
2982
2983 ndest = (y * line_length) + x;
2984
Krzysztof Helt8503df62007-10-16 01:29:08 -07002985 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002986
2987 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002988 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2989 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2990 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2991 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002992
2993 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002994 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2995 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996
2997 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002998 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2999 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003000
3001 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003002 /* BLT dest low */
3003 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
3004 /* BLT dest mid */
3005 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
3006 /* BLT dest hi */
3007 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008
3009 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003010 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
3011 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
3012 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003013
3014 /* This is a ColorExpand Blt, using the */
3015 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003016 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
3017 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003018
3019 op = 0xc0;
3020 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003021 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3022 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003023 op = 0x50;
3024 op = 0xd0;
3025 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003026 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3027 vga_wgfx(regbase, CL_GR11, color); /* background color */
3028 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
3029 vga_wgfx(regbase, CL_GR13, color); /* background color */
3030 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
3031 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003032 op = 0x50;
3033 op = 0xf0;
3034 }
3035 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003036 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003037
3038 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003039 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040
3041 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003042 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043
Krzysztof Helt8503df62007-10-16 01:29:08 -07003044 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003045}
3046
Linus Torvalds1da177e2005-04-16 15:20:36 -07003047/**************************************************************************
3048 * bestclock() - determine closest possible clock lower(?) than the
3049 * desired pixel clock
3050 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07003051static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052 long *den, long *div, long maxfreq)
3053{
3054 long n, h, d, f;
3055
Krzysztof Helt8503df62007-10-16 01:29:08 -07003056 assert(best != NULL);
3057 assert(nom != NULL);
3058 assert(den != NULL);
3059 assert(div != NULL);
3060 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061
3062 *nom = 0;
3063 *den = 0;
3064 *div = 0;
3065
Krzysztof Helt8503df62007-10-16 01:29:08 -07003066 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003067
3068 if (freq < 8000)
3069 freq = 8000;
3070
3071 if (freq > maxfreq)
3072 freq = maxfreq;
3073
3074 *best = 0;
3075 f = freq * 10;
3076
3077 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07003078 int s = 0;
3079
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080 d = (143181 * n) / f;
3081 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07003082 int temp = d;
3083
3084 if (temp > 31) {
3085 s = 1;
3086 temp >>= 1;
3087 }
3088 h = ((14318 * n) / temp) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003089 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090 *best = h;
3091 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07003092 *den = temp;
3093 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003094 }
3095 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07003096 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07003098 if (d > 31) {
3099 s = 1;
3100 d >>= 1;
3101 }
3102 h = ((14318 * n) / d) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003103 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104 *best = h;
3105 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07003106 *den = d;
3107 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003108 }
3109 }
3110 }
3111
Krzysztof Helt8503df62007-10-16 01:29:08 -07003112 DPRINTK("Best possible values for given frequency:\n");
3113 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
3114 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003115
Krzysztof Helt8503df62007-10-16 01:29:08 -07003116 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117}
3118
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119/* -------------------------------------------------------------------------
3120 *
3121 * debugging functions
3122 *
3123 * -------------------------------------------------------------------------
3124 */
3125
3126#ifdef CIRRUSFB_DEBUG
3127
3128/**
3129 * cirrusfb_dbg_print_byte
3130 * @name: name associated with byte value to be displayed
3131 * @val: byte value to be displayed
3132 *
3133 * DESCRIPTION:
3134 * Display an indented string, along with a hexidecimal byte value, and
3135 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3136 * order.
3137 */
3138
3139static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003140void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003142 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3143 name, val,
3144 val & 0x80 ? '1' : '0',
3145 val & 0x40 ? '1' : '0',
3146 val & 0x20 ? '1' : '0',
3147 val & 0x10 ? '1' : '0',
3148 val & 0x08 ? '1' : '0',
3149 val & 0x04 ? '1' : '0',
3150 val & 0x02 ? '1' : '0',
3151 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152}
3153
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154/**
3155 * cirrusfb_dbg_print_regs
3156 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3157 * @reg_class: type of registers to read: %CRT, or %SEQ
3158 *
3159 * DESCRIPTION:
3160 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3161 * old-style I/O ports are queried for information, otherwise MMIO is
3162 * used at the given @base address to query the information.
3163 */
3164
3165static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003166void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003167 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003168{
3169 va_list list;
3170 unsigned char val = 0;
3171 unsigned reg;
3172 char *name;
3173
Krzysztof Helt8503df62007-10-16 01:29:08 -07003174 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003175
Krzysztof Helt8503df62007-10-16 01:29:08 -07003176 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003178 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179
3180 switch (reg_class) {
3181 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003182 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003183 break;
3184 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003185 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186 break;
3187 default:
3188 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003189 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003190 break;
3191 }
3192
Krzysztof Helt8503df62007-10-16 01:29:08 -07003193 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194
Krzysztof Helt8503df62007-10-16 01:29:08 -07003195 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003196 }
3197
Krzysztof Helt8503df62007-10-16 01:29:08 -07003198 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199}
3200
Linus Torvalds1da177e2005-04-16 15:20:36 -07003201/**
3202 * cirrusfb_dump
3203 * @cirrusfbinfo:
3204 *
3205 * DESCRIPTION:
3206 */
3207
Krzysztof Helt8503df62007-10-16 01:29:08 -07003208static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003210 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211}
3212
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213/**
3214 * cirrusfb_dbg_reg_dump
3215 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3216 *
3217 * DESCRIPTION:
3218 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3219 * old-style I/O ports are queried for information, otherwise MMIO is
3220 * used at the given @base address to query the information.
3221 */
3222
3223static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003224void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003225{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003226 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003227
Krzysztof Helt8503df62007-10-16 01:29:08 -07003228 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003229 "CR00", 0x00,
3230 "CR01", 0x01,
3231 "CR02", 0x02,
3232 "CR03", 0x03,
3233 "CR04", 0x04,
3234 "CR05", 0x05,
3235 "CR06", 0x06,
3236 "CR07", 0x07,
3237 "CR08", 0x08,
3238 "CR09", 0x09,
3239 "CR0A", 0x0A,
3240 "CR0B", 0x0B,
3241 "CR0C", 0x0C,
3242 "CR0D", 0x0D,
3243 "CR0E", 0x0E,
3244 "CR0F", 0x0F,
3245 "CR10", 0x10,
3246 "CR11", 0x11,
3247 "CR12", 0x12,
3248 "CR13", 0x13,
3249 "CR14", 0x14,
3250 "CR15", 0x15,
3251 "CR16", 0x16,
3252 "CR17", 0x17,
3253 "CR18", 0x18,
3254 "CR22", 0x22,
3255 "CR24", 0x24,
3256 "CR26", 0x26,
3257 "CR2D", 0x2D,
3258 "CR2E", 0x2E,
3259 "CR2F", 0x2F,
3260 "CR30", 0x30,
3261 "CR31", 0x31,
3262 "CR32", 0x32,
3263 "CR33", 0x33,
3264 "CR34", 0x34,
3265 "CR35", 0x35,
3266 "CR36", 0x36,
3267 "CR37", 0x37,
3268 "CR38", 0x38,
3269 "CR39", 0x39,
3270 "CR3A", 0x3A,
3271 "CR3B", 0x3B,
3272 "CR3C", 0x3C,
3273 "CR3D", 0x3D,
3274 "CR3E", 0x3E,
3275 "CR3F", 0x3F,
3276 NULL);
3277
Krzysztof Helt8503df62007-10-16 01:29:08 -07003278 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003279
Krzysztof Helt8503df62007-10-16 01:29:08 -07003280 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003281
Krzysztof Helt8503df62007-10-16 01:29:08 -07003282 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003283 "SR00", 0x00,
3284 "SR01", 0x01,
3285 "SR02", 0x02,
3286 "SR03", 0x03,
3287 "SR04", 0x04,
3288 "SR08", 0x08,
3289 "SR09", 0x09,
3290 "SR0A", 0x0A,
3291 "SR0B", 0x0B,
3292 "SR0D", 0x0D,
3293 "SR10", 0x10,
3294 "SR11", 0x11,
3295 "SR12", 0x12,
3296 "SR13", 0x13,
3297 "SR14", 0x14,
3298 "SR15", 0x15,
3299 "SR16", 0x16,
3300 "SR17", 0x17,
3301 "SR18", 0x18,
3302 "SR19", 0x19,
3303 "SR1A", 0x1A,
3304 "SR1B", 0x1B,
3305 "SR1C", 0x1C,
3306 "SR1D", 0x1D,
3307 "SR1E", 0x1E,
3308 "SR1F", 0x1F,
3309 NULL);
3310
Krzysztof Helt8503df62007-10-16 01:29:08 -07003311 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003312}
3313
3314#endif /* CIRRUSFB_DEBUG */
3315