blob: 4888ff69f802e643892e253e5de42453908a25f1 [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:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700641 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700644 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
645 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700647 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 return -EINVAL;
649 }
650
Krzysztof Helt09a29102008-09-02 14:35:51 -0700651 if (var->xres_virtual < var->xres)
652 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700654 if (var->yres_virtual == -1) {
655 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
Krzysztof Helt8503df62007-10-16 01:29:08 -0700657 printk(KERN_INFO "cirrusfb: virtual resolution set to "
658 "maximum of %dx%d\n", var->xres_virtual,
659 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 if (var->yres_virtual < var->yres)
662 var->yres_virtual = var->yres;
663
Krzysztof Helt09a29102008-09-02 14:35:51 -0700664 if (var->xres_virtual * var->yres_virtual > pixels) {
665 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
666 "virtual resolution too high to fit into video memory!\n",
667 var->xres_virtual, var->yres_virtual,
668 var->bits_per_pixel);
669 DPRINTK("EXIT - EINVAL error\n");
670 return -EINVAL;
671 }
672
673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 if (var->xoffset < 0)
675 var->xoffset = 0;
676 if (var->yoffset < 0)
677 var->yoffset = 0;
678
679 /* truncate xoffset and yoffset to maximum if too high */
680 if (var->xoffset > var->xres_virtual - var->xres)
681 var->xoffset = var->xres_virtual - var->xres - 1;
682 if (var->yoffset > var->yres_virtual - var->yres)
683 var->yoffset = var->yres_virtual - var->yres - 1;
684
685 switch (var->bits_per_pixel) {
686 case 1:
687 var->red.offset = 0;
688 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700689 var->green = var->red;
690 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 break;
692
693 case 8:
694 var->red.offset = 0;
695 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700696 var->green = var->red;
697 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 break;
699
700 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700701 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 var->red.offset = 2;
703 var->green.offset = -3;
704 var->blue.offset = 8;
705 } else {
706 var->red.offset = 10;
707 var->green.offset = 5;
708 var->blue.offset = 0;
709 }
710 var->red.length = 5;
711 var->green.length = 5;
712 var->blue.length = 5;
713 break;
714
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700716 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 var->red.offset = 8;
718 var->green.offset = 16;
719 var->blue.offset = 24;
720 } else {
721 var->red.offset = 16;
722 var->green.offset = 8;
723 var->blue.offset = 0;
724 }
725 var->red.length = 8;
726 var->green.length = 8;
727 var->blue.length = 8;
728 break;
729
730 default:
731 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700732 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 /* should never occur */
734 break;
735 }
736
737 var->red.msb_right =
738 var->green.msb_right =
739 var->blue.msb_right =
740 var->transp.offset =
741 var->transp.length =
742 var->transp.msb_right = 0;
743
744 yres = var->yres;
745 if (var->vmode & FB_VMODE_DOUBLE)
746 yres *= 2;
747 else if (var->vmode & FB_VMODE_INTERLACED)
748 yres = (yres + 1) / 2;
749
750 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700751 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
752 "special treatment required! (TODO)\n");
753 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 return -EINVAL;
755 }
756
757 return 0;
758}
759
Krzysztof Helt8503df62007-10-16 01:29:08 -0700760static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700762 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763{
764 long freq;
765 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700766 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 struct cirrusfb_info *cinfo = info->par;
768 int xres, hfront, hsync, hback;
769 int yres, vfront, vsync, vback;
770
Krzysztof Helt8503df62007-10-16 01:29:08 -0700771 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700773 info->fix.line_length = var->xres_virtual / 8;
774 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 break;
776
777 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700778 info->fix.line_length = var->xres_virtual;
779 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 break;
781
782 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700784 info->fix.line_length = var->xres_virtual * maxclockidx;
785 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 break;
787
788 default:
789 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700790 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 /* should never occur */
792 break;
793 }
794
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700795 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700798 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Krzysztof Helt8503df62007-10-16 01:29:08 -0700800 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
802 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
803 regs->multiplexing = 0;
804
805 /* If the frequency is greater than we can support, we might be able
806 * to use multiplexing for the video mode */
807 if (freq > maxclock) {
808 switch (cinfo->btype) {
809 case BT_ALPINE:
810 case BT_GD5480:
811 regs->multiplexing = 1;
812 break;
813
814 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700815 printk(KERN_ERR "cirrusfb: Frequency greater "
816 "than maxclock (%ld kHz)\n", maxclock);
817 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 return -EINVAL;
819 }
820 }
821#if 0
822 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
823 * the VCLK is double the pixel clock. */
824 switch (var->bits_per_pixel) {
825 case 16:
826 case 32:
827 if (regs->HorizRes <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700828 /* Xbh has this type of clock for 32-bit */
829 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 break;
831 }
832#endif
833
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
835 maxclock);
836 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
837 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
839 xres = var->xres;
840 hfront = var->right_margin;
841 hsync = var->hsync_len;
842 hback = var->left_margin;
843
844 yres = var->yres;
845 vfront = var->lower_margin;
846 vsync = var->vsync_len;
847 vback = var->upper_margin;
848
849 if (var->vmode & FB_VMODE_DOUBLE) {
850 yres *= 2;
851 vfront *= 2;
852 vsync *= 2;
853 vback *= 2;
854 } else if (var->vmode & FB_VMODE_INTERLACED) {
855 yres = (yres + 1) / 2;
856 vfront = (vfront + 1) / 2;
857 vsync = (vsync + 1) / 2;
858 vback = (vback + 1) / 2;
859 }
860 regs->HorizRes = xres;
861 regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
862 regs->HorizDispEnd = xres / 8 - 1;
863 regs->HorizBlankStart = xres / 8;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700864 /* does not count with "-5" */
865 regs->HorizBlankEnd = regs->HorizTotal + 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 regs->HorizSyncStart = (xres + hfront) / 8 + 1;
867 regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
868
869 regs->VertRes = yres;
870 regs->VertTotal = yres + vfront + vsync + vback - 2;
871 regs->VertDispEnd = yres - 1;
872 regs->VertBlankStart = yres;
873 regs->VertBlankEnd = regs->VertTotal;
874 regs->VertSyncStart = yres + vfront - 1;
875 regs->VertSyncEnd = yres + vfront + vsync - 1;
876
877 if (regs->VertRes >= 1024) {
878 regs->VertTotal /= 2;
879 regs->VertSyncStart /= 2;
880 regs->VertSyncEnd /= 2;
881 regs->VertDispEnd /= 2;
882 }
883 if (regs->multiplexing) {
884 regs->HorizTotal /= 2;
885 regs->HorizSyncStart /= 2;
886 regs->HorizSyncEnd /= 2;
887 regs->HorizDispEnd /= 2;
888 }
889
890 return 0;
891}
892
Krzysztof Helt8503df62007-10-16 01:29:08 -0700893static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
894 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700896 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
898 if (div == 2) {
899 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700900 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
901 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
902 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 } else if (div == 1) {
904 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700905 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
906 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
907 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700909 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 }
911}
912
913/*************************************************************************
914 cirrusfb_set_par_foo()
915
916 actually writes the values for a new video mode into the hardware,
917**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700918static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919{
920 struct cirrusfb_info *cinfo = info->par;
921 struct fb_var_screeninfo *var = &info->var;
922 struct cirrusfb_regs regs;
923 u8 __iomem *regbase = cinfo->regbase;
924 unsigned char tmp;
925 int offset = 0, err;
926 const struct cirrusfb_board_info_rec *bi;
927
Krzysztof Helt8503df62007-10-16 01:29:08 -0700928 DPRINTK("ENTER\n");
929 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700933 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
935 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700936 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 /* should never happen */
938 DPRINTK("mode change aborted. invalid var.\n");
939 return -EINVAL;
940 }
941
942 bi = &cirrusfb_board_info[cinfo->btype];
943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700945 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700948 DPRINTK("CRT0: %ld\n", regs.HorizTotal);
949 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
Krzysztof Helt8503df62007-10-16 01:29:08 -0700951 DPRINTK("CRT1: %ld\n", regs.HorizDispEnd);
952 vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 DPRINTK("CRT2: %ld\n", regs.HorizBlankStart);
955 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
Krzysztof Helt8503df62007-10-16 01:29:08 -0700957 /* + 128: Compatible read */
958 DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);
959 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
960 128 + (regs.HorizBlankEnd % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
Krzysztof Helt8503df62007-10-16 01:29:08 -0700962 DPRINTK("CRT4: %ld\n", regs.HorizSyncStart);
963 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
965 tmp = regs.HorizSyncEnd % 32;
966 if (regs.HorizBlankEnd & 32)
967 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700968 DPRINTK("CRT5: %d\n", tmp);
969 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
Krzysztof Helt8503df62007-10-16 01:29:08 -0700971 DPRINTK("CRT6: %ld\n", regs.VertTotal & 0xff);
972 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973
974 tmp = 16; /* LineCompare bit #9 */
975 if (regs.VertTotal & 256)
976 tmp |= 1;
977 if (regs.VertDispEnd & 256)
978 tmp |= 2;
979 if (regs.VertSyncStart & 256)
980 tmp |= 4;
981 if (regs.VertBlankStart & 256)
982 tmp |= 8;
983 if (regs.VertTotal & 512)
984 tmp |= 32;
985 if (regs.VertDispEnd & 512)
986 tmp |= 64;
987 if (regs.VertSyncStart & 512)
988 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700989 DPRINTK("CRT7: %d\n", tmp);
990 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
992 tmp = 0x40; /* LineCompare bit #8 */
993 if (regs.VertBlankStart & 512)
994 tmp |= 0x20;
995 if (var->vmode & FB_VMODE_DOUBLE)
996 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 DPRINTK("CRT9: %d\n", tmp);
998 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 DPRINTK("CRT10: %ld\n", regs.VertSyncStart & 0xff);
1001 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, regs.VertSyncStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
Krzysztof Helt8503df62007-10-16 01:29:08 -07001003 DPRINTK("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
1004 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, regs.VertSyncEnd % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005
Krzysztof Helt8503df62007-10-16 01:29:08 -07001006 DPRINTK("CRT12: %ld\n", regs.VertDispEnd & 0xff);
1007 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, regs.VertDispEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Krzysztof Helt8503df62007-10-16 01:29:08 -07001009 DPRINTK("CRT15: %ld\n", regs.VertBlankStart & 0xff);
1010 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, regs.VertBlankStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
Krzysztof Helt8503df62007-10-16 01:29:08 -07001012 DPRINTK("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
1013 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, regs.VertBlankEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
Krzysztof Helt8503df62007-10-16 01:29:08 -07001015 DPRINTK("CRT18: 0xff\n");
1016 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
1018 tmp = 0;
1019 if (var->vmode & FB_VMODE_INTERLACED)
1020 tmp |= 1;
1021 if (regs.HorizBlankEnd & 64)
1022 tmp |= 16;
1023 if (regs.HorizBlankEnd & 128)
1024 tmp |= 32;
1025 if (regs.VertBlankEnd & 256)
1026 tmp |= 64;
1027 if (regs.VertBlankEnd & 512)
1028 tmp |= 128;
1029
Krzysztof Helt8503df62007-10-16 01:29:08 -07001030 DPRINTK("CRT1a: %d\n", tmp);
1031 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
1033 /* set VCLK0 */
1034 /* hardware RefClock: 14.31818 MHz */
1035 /* formula: VClk = (OSC * N) / (D * (1+P)) */
1036 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
1037
Krzysztof Helt8503df62007-10-16 01:29:08 -07001038 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 tmp = regs.den << 1;
1040 if (regs.div != 0)
1041 tmp |= 1;
1042
Krzysztof Helt8503df62007-10-16 01:29:08 -07001043 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 if ((cinfo->btype == BT_SD64) ||
1045 (cinfo->btype == BT_ALPINE) ||
1046 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001047 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Krzysztof Helt8503df62007-10-16 01:29:08 -07001049 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
1050 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
1052 if (regs.VertRes >= 1024)
1053 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001054 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 else
1056 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
1057 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001058 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
Krzysztof Helt8503df62007-10-16 01:29:08 -07001060/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
1061 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062
1063 /* don't know if it would hurt to also program this if no interlaced */
1064 /* mode is used, but I feel better this way.. :-) */
1065 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001066 vga_wcrt(regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001068 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
1072 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 /* enable display memory & CRTC I/O address for color mode */
1074 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1076 tmp |= 0x40;
1077 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1078 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001079 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
Krzysztof Helt8503df62007-10-16 01:29:08 -07001081 /* Screen A Preset Row-Scan register */
1082 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
1083 /* text cursor on and start line */
1084 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
1085 /* text cursor end line */
1086 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
1088 /******************************************************
1089 *
1090 * 1 bpp
1091 *
1092 */
1093
1094 /* programming for different color depths */
1095 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001096 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
1097 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
1099 /* SR07 */
1100 switch (cinfo->btype) {
1101 case BT_SD64:
1102 case BT_PICCOLO:
1103 case BT_PICASSO:
1104 case BT_SPECTRUM:
1105 case BT_PICASSO4:
1106 case BT_ALPINE:
1107 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108 DPRINTK(" (for GD54xx)\n");
1109 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 regs.multiplexing ?
1111 bi->sr07_1bpp_mux : bi->sr07_1bpp);
1112 break;
1113
1114 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001115 DPRINTK(" (for GD546x)\n");
1116 vga_wseq(regbase, CL_SEQR7,
1117 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 break;
1119
1120 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001121 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 break;
1123 }
1124
1125 /* Extended Sequencer Mode */
1126 switch (cinfo->btype) {
1127 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001128 /* setting the SEQRF on SD64 is not necessary
1129 * (only during init)
1130 */
1131 DPRINTK("(for SD64)\n");
1132 /* MCLK select */
1133 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 break;
1135
1136 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001137 case BT_SPECTRUM:
1138 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001139 /* ### ueberall 0x22? */
1140 /* ##vorher 1c MCLK select */
1141 vga_wseq(regbase, CL_SEQR1F, 0x22);
1142 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1143 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 break;
1145
1146 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001147 DPRINTK("(for Picasso)\n");
1148 /* ##vorher 22 MCLK select */
1149 vga_wseq(regbase, CL_SEQR1F, 0x22);
1150 /* ## vorher d0 avoid FIFO underruns..? */
1151 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 break;
1153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 case BT_PICASSO4:
1155 case BT_ALPINE:
1156 case BT_GD5480:
1157 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001158 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 /* do nothing */
1160 break;
1161
1162 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001163 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 break;
1165 }
1166
Krzysztof Helt8503df62007-10-16 01:29:08 -07001167 /* pixel mask: pass-through for first plane */
1168 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001170 /* hidden dac reg: 1280x1024 */
1171 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001173 /* hidden dac: nothing */
1174 WHDR(cinfo, 0);
1175 /* memory mode: odd/even, ext. memory */
1176 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1177 /* plane mask: only write to first plane */
1178 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 offset = var->xres_virtual / 16;
1180 }
1181
1182 /******************************************************
1183 *
1184 * 8 bpp
1185 *
1186 */
1187
1188 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 switch (cinfo->btype) {
1191 case BT_SD64:
1192 case BT_PICCOLO:
1193 case BT_PICASSO:
1194 case BT_SPECTRUM:
1195 case BT_PICASSO4:
1196 case BT_ALPINE:
1197 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001198 DPRINTK(" (for GD54xx)\n");
1199 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 regs.multiplexing ?
1201 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1202 break;
1203
1204 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001205 DPRINTK(" (for GD546x)\n");
1206 vga_wseq(regbase, CL_SEQR7,
1207 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 break;
1209
1210 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001211 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 break;
1213 }
1214
1215 switch (cinfo->btype) {
1216 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001217 /* MCLK select */
1218 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 break;
1220
1221 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001224 /* ### vorher 1c MCLK select */
1225 vga_wseq(regbase, CL_SEQR1F, 0x22);
1226 /* Fast Page-Mode writes */
1227 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 break;
1229
1230 case BT_PICASSO4:
1231#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001232 /* ### INCOMPLETE!! */
1233 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 break;
1237
1238 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001239 DPRINTK(" (for GD543x)\n");
1240 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 /* We already set SRF and SR1F */
1242 break;
1243
1244 case BT_GD5480:
1245 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001246 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 /* do nothing */
1248 break;
1249
1250 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001251 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 break;
1253 }
1254
Krzysztof Helt8503df62007-10-16 01:29:08 -07001255 /* mode register: 256 color mode */
1256 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1257 /* pixel mask: pass-through all planes */
1258 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001260 /* hidden dac reg: 1280x1024 */
1261 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001263 /* hidden dac: nothing */
1264 WHDR(cinfo, 0);
1265 /* memory mode: chain4, ext. memory */
1266 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1267 /* plane mask: enable writing to all 4 planes */
1268 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 offset = var->xres_virtual / 8;
1270 }
1271
1272 /******************************************************
1273 *
1274 * 16 bpp
1275 *
1276 */
1277
1278 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001279 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 switch (cinfo->btype) {
1281 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001282 /* Extended Sequencer Mode: 256c col. mode */
1283 vga_wseq(regbase, CL_SEQR7, 0xf7);
1284 /* MCLK select */
1285 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 break;
1287
1288 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001289 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290 vga_wseq(regbase, CL_SEQR7, 0x87);
1291 /* Fast Page-Mode writes */
1292 vga_wseq(regbase, CL_SEQRF, 0xb0);
1293 /* MCLK select */
1294 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 break;
1296
1297 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001298 vga_wseq(regbase, CL_SEQR7, 0x27);
1299 /* Fast Page-Mode writes */
1300 vga_wseq(regbase, CL_SEQRF, 0xb0);
1301 /* MCLK select */
1302 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 break;
1304
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001306 vga_wseq(regbase, CL_SEQR7, 0x27);
1307/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 break;
1309
1310 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001311 DPRINTK(" (for GD543x)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 if (regs.HorizRes >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001315 vga_wseq(regbase, CL_SEQR7, 0xa3);
1316 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 break;
1318
1319 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001320 DPRINTK(" (for GD5480)\n");
1321 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 /* We already set SRF and SR1F */
1323 break;
1324
1325 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001326 DPRINTK(" (for GD546x)\n");
1327 vga_wseq(regbase, CL_SEQR7,
1328 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 break;
1330
1331 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 break;
1334 }
1335
Krzysztof Helt8503df62007-10-16 01:29:08 -07001336 /* mode register: 256 color mode */
1337 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1338 /* pixel mask: pass-through all planes */
1339 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001341 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342#elif defined(CONFIG_ZORRO)
1343 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001344 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001346 /* memory mode: chain4, ext. memory */
1347 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1348 /* plane mask: enable writing to all 4 planes */
1349 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 offset = var->xres_virtual / 4;
1351 }
1352
1353 /******************************************************
1354 *
1355 * 32 bpp
1356 *
1357 */
1358
1359 else if (var->bits_per_pixel == 32) {
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001360 DPRINTK("cirrusfb: preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 switch (cinfo->btype) {
1362 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 /* Extended Sequencer Mode: 256c col. mode */
1364 vga_wseq(regbase, CL_SEQR7, 0xf9);
1365 /* MCLK select */
1366 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 break;
1368
1369 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001370 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001371 vga_wseq(regbase, CL_SEQR7, 0x85);
1372 /* Fast Page-Mode writes */
1373 vga_wseq(regbase, CL_SEQRF, 0xb0);
1374 /* MCLK select */
1375 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 break;
1377
1378 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001379 vga_wseq(regbase, CL_SEQR7, 0x25);
1380 /* Fast Page-Mode writes */
1381 vga_wseq(regbase, CL_SEQRF, 0xb0);
1382 /* MCLK select */
1383 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 break;
1385
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001387 vga_wseq(regbase, CL_SEQR7, 0x25);
1388/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 break;
1390
1391 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001392 DPRINTK(" (for GD543x)\n");
1393 vga_wseq(regbase, CL_SEQR7, 0xa9);
1394 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 break;
1396
1397 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001398 DPRINTK(" (for GD5480)\n");
1399 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 /* We already set SRF and SR1F */
1401 break;
1402
1403 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001404 DPRINTK(" (for GD546x)\n");
1405 vga_wseq(regbase, CL_SEQR7,
1406 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 break;
1408
1409 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001410 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 break;
1412 }
1413
Krzysztof Helt8503df62007-10-16 01:29:08 -07001414 /* mode register: 256 color mode */
1415 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1416 /* pixel mask: pass-through all planes */
1417 WGen(cinfo, VGA_PEL_MSK, 0xff);
1418 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1419 WHDR(cinfo, 0xc5);
1420 /* memory mode: chain4, ext. memory */
1421 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1422 /* plane mask: enable writing to all 4 planes */
1423 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 offset = var->xres_virtual / 4;
1425 }
1426
1427 /******************************************************
1428 *
1429 * unknown/unsupported bpp
1430 *
1431 */
1432
Krzysztof Helt8503df62007-10-16 01:29:08 -07001433 else
1434 printk(KERN_ERR "cirrusfb: What's this?? "
1435 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437
Krzysztof Helt8503df62007-10-16 01:29:08 -07001438 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 tmp = 0x22;
1440 if (offset & 0x100)
1441 tmp |= 0x10; /* offset overflow bit */
1442
Krzysztof Helt8503df62007-10-16 01:29:08 -07001443 /* screen start addr #16-18, fastpagemode cycles */
1444 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445
1446 if (cinfo->btype == BT_SD64 ||
1447 cinfo->btype == BT_PICASSO4 ||
1448 cinfo->btype == BT_ALPINE ||
1449 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001450 /* screen start address bit 19 */
1451 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
Krzysztof Helt8503df62007-10-16 01:29:08 -07001453 /* text cursor location high */
1454 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1455 /* text cursor location low */
1456 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1457 /* underline row scanline = at very bottom */
1458 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459
Krzysztof Helt8503df62007-10-16 01:29:08 -07001460 /* controller mode */
1461 vga_wattr(regbase, VGA_ATC_MODE, 1);
1462 /* overscan (border) color */
1463 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1464 /* color plane enable */
1465 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1466 /* pixel panning */
1467 vga_wattr(regbase, CL_AR33, 0);
1468 /* color select */
1469 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470
1471 /* [ EGS: SetOffset(); ] */
1472 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001473 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
Krzysztof Helt8503df62007-10-16 01:29:08 -07001475 /* set/reset register */
1476 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1477 /* set/reset enable */
1478 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1479 /* color compare */
1480 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1481 /* data rotate */
1482 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1483 /* read map select */
1484 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1485 /* miscellaneous register */
1486 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1487 /* color don't care */
1488 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1489 /* bit mask */
1490 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491
Krzysztof Helt8503df62007-10-16 01:29:08 -07001492 /* graphics cursor attributes: nothing special */
1493 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
1495 /* finally, turn on everything - turn off "FullBandwidth" bit */
1496 /* also, set "DotClock%2" bit where requested */
1497 tmp = 0x01;
1498
1499/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1500 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1501 tmp |= 0x08;
1502*/
1503
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1505 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
1507 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001510 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
1512#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514#endif
1515
Krzysztof Helt8503df62007-10-16 01:29:08 -07001516 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 return 0;
1518}
1519
1520/* for some reason incomprehensible to me, cirrusfb requires that you write
1521 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001522static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001524 cirrusfb_set_par_foo(info);
1525 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526}
1527
Krzysztof Helt8503df62007-10-16 01:29:08 -07001528static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1529 unsigned blue, unsigned transp,
1530 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531{
1532 struct cirrusfb_info *cinfo = info->par;
1533
1534 if (regno > 255)
1535 return -EINVAL;
1536
1537 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1538 u32 v;
1539 red >>= (16 - info->var.red.length);
1540 green >>= (16 - info->var.green.length);
1541 blue >>= (16 - info->var.blue.length);
1542
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 return 1;
1545 v = (red << info->var.red.offset) |
1546 (green << info->var.green.offset) |
1547 (blue << info->var.blue.offset);
1548
Krzysztof Helt060b6002007-10-16 01:29:13 -07001549 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 return 0;
1551 }
1552
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 if (info->var.bits_per_pixel == 8)
1554 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555
1556 return 0;
1557
1558}
1559
1560/*************************************************************************
1561 cirrusfb_pan_display()
1562
1563 performs display panning - provided hardware permits this
1564**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001565static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1566 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567{
1568 int xoffset = 0;
1569 int yoffset = 0;
1570 unsigned long base;
1571 unsigned char tmp = 0, tmp2 = 0, xpix;
1572 struct cirrusfb_info *cinfo = info->par;
1573
Krzysztof Helt8503df62007-10-16 01:29:08 -07001574 DPRINTK("ENTER\n");
1575 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
1577 /* no range checks for xoffset and yoffset, */
1578 /* as fb_pan_display has already done this */
1579 if (var->vmode & FB_VMODE_YWRAP)
1580 return -EINVAL;
1581
1582 info->var.xoffset = var->xoffset;
1583 info->var.yoffset = var->yoffset;
1584
1585 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1586 yoffset = var->yoffset;
1587
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001588 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
1590 if (info->var.bits_per_pixel == 1) {
1591 /* base is already correct */
1592 xpix = (unsigned char) (var->xoffset % 8);
1593 } else {
1594 base /= 4;
1595 xpix = (unsigned char) ((xoffset % 4) * 2);
1596 }
1597
Krzysztof Helt8503df62007-10-16 01:29:08 -07001598 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
1600 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001601 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1602 (unsigned char) (base & 0xff));
1603 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1604 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
1606 /* construct bits 16, 17 and 18 of screen start address */
1607 if (base & 0x10000)
1608 tmp |= 0x01;
1609 if (base & 0x20000)
1610 tmp |= 0x04;
1611 if (base & 0x40000)
1612 tmp |= 0x08;
1613
Krzysztof Helt8503df62007-10-16 01:29:08 -07001614 /* 0xf2 is %11110010, exclude tmp bits */
1615 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1616 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617
1618 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001619 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1620 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
Krzysztof Helt8503df62007-10-16 01:29:08 -07001622 /* write pixel panning value to AR33; this does not quite work in 8bpp
1623 *
1624 * ### Piccolo..? Will this work?
1625 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001627 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628
Krzysztof Helt8503df62007-10-16 01:29:08 -07001629 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630
Krzysztof Helt8503df62007-10-16 01:29:08 -07001631 DPRINTK("EXIT\n");
1632 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633}
1634
Krzysztof Helt8503df62007-10-16 01:29:08 -07001635static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636{
1637 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001638 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1639 * then the caller blanks by setting the CLUT (Color Look Up Table)
1640 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1641 * failed due to e.g. a video mode which doesn't support it.
1642 * Implements VESA suspend and powerdown modes on hardware that
1643 * supports disabling hsync/vsync:
1644 * blank_mode == 2: suspend vsync
1645 * blank_mode == 3: suspend hsync
1646 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 */
1648 unsigned char val;
1649 struct cirrusfb_info *cinfo = info->par;
1650 int current_mode = cinfo->blank_mode;
1651
Krzysztof Helt8503df62007-10-16 01:29:08 -07001652 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654 if (info->state != FBINFO_STATE_RUNNING ||
1655 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 return 0;
1658 }
1659
1660 /* Undo current */
1661 if (current_mode == FB_BLANK_NORMAL ||
1662 current_mode == FB_BLANK_UNBLANK) {
1663 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1665 /* clear "FullBandwidth" bit */
1666 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001668 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 }
1670
1671 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001672 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001674 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1675 /* set "FullBandwidth" bit */
1676 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 }
1678
1679 switch (blank_mode) {
1680 case FB_BLANK_UNBLANK:
1681 case FB_BLANK_NORMAL:
1682 break;
1683 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 break;
1686 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 break;
1689 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001690 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 break;
1692 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 return 1;
1695 }
1696
1697 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001698 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699
1700 /* Let fbcon do a soft blank for us */
1701 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1702}
1703/**** END Hardware specific Routines **************************************/
1704/****************************************************************************/
1705/**** BEGIN Internal Routines ***********************************************/
1706
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001707static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001709 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 const struct cirrusfb_board_info_rec *bi;
1711
Krzysztof Helt8503df62007-10-16 01:29:08 -07001712 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713
Krzysztof Helt8503df62007-10-16 01:29:08 -07001714 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715
1716 bi = &cirrusfb_board_info[cinfo->btype];
1717
1718 /* reset board globally */
1719 switch (cinfo->btype) {
1720 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 WSFR(cinfo, 0x01);
1722 udelay(500);
1723 WSFR(cinfo, 0x51);
1724 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 break;
1726 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001727 WSFR2(cinfo, 0xff);
1728 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 break;
1730 case BT_SD64:
1731 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001732 WSFR(cinfo, 0x1f);
1733 udelay(500);
1734 WSFR(cinfo, 0x4f);
1735 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 break;
1737 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001738 /* disable flickerfixer */
1739 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1740 mdelay(100);
1741 /* from Klaus' NetBSD driver: */
1742 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1743 /* put blitter into 542x compat */
1744 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1745 /* mode */
1746 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 break;
1748
1749 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750 /* from Klaus' NetBSD driver: */
1751 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 break;
1753
1754 case BT_ALPINE:
1755 /* Nothing to do to reset the board. */
1756 break;
1757
1758 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001759 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 break;
1761 }
1762
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001763 /* make sure RAM size set by this point */
1764 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765
1766 /* the P4 is not fully initialized here; I rely on it having been */
1767 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001768 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769
1770 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001771 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1772 WGen(cinfo, CL_POS102, 0x01);
1773 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774
1775 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001776 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Krzysztof Helt8503df62007-10-16 01:29:08 -07001778 /* reset sequencer logic */
1779 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780
Krzysztof Helt8503df62007-10-16 01:29:08 -07001781 /* FullBandwidth (video off) and 8/9 dot clock */
1782 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1783 /* polarity (-/-), disable access to display memory,
1784 * VGA_CRTC_START_HI base address: color
1785 */
1786 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
Krzysztof Helt8503df62007-10-16 01:29:08 -07001788 /* "magic cookie" - doesn't make any sense to me.. */
1789/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1790 /* unlock all extension registers */
1791 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
Krzysztof Helt8503df62007-10-16 01:29:08 -07001793 /* reset blitter */
1794 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
1796 switch (cinfo->btype) {
1797 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001798 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 break;
1800 case BT_ALPINE:
1801 break;
1802 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001803 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 break;
1805 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001806 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1807 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808 break;
1809 }
1810 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001811 /* plane mask: nothing */
1812 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1813 /* character map select: doesn't even matter in gx mode */
1814 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1815 /* memory mode: chain-4, no odd/even, ext. memory */
1816 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817
1818 /* controller-internal base address of video memory */
1819 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001820 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
Krzysztof Helt8503df62007-10-16 01:29:08 -07001822 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1823 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824
Krzysztof Helt8503df62007-10-16 01:29:08 -07001825 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1826 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1827 /* graphics cursor Y position (..."... ) */
1828 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1829 /* graphics cursor attributes */
1830 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1831 /* graphics cursor pattern address */
1832 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833
1834 /* writing these on a P4 might give problems.. */
1835 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001836 /* configuration readback and ext. color */
1837 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1838 /* signature generator */
1839 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 }
1841
1842 /* MCLK select etc. */
1843 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001844 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845
Krzysztof Helt8503df62007-10-16 01:29:08 -07001846 /* Screen A preset row scan: none */
1847 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1848 /* Text cursor start: disable text cursor */
1849 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1850 /* Text cursor end: - */
1851 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1852 /* Screen start address high: 0 */
1853 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1854 /* Screen start address low: 0 */
1855 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1856 /* text cursor location high: 0 */
1857 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1858 /* text cursor location low: 0 */
1859 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
Krzysztof Helt8503df62007-10-16 01:29:08 -07001861 /* Underline Row scanline: - */
1862 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1863 /* mode control: timing enable, byte mode, no compat modes */
1864 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1865 /* Line Compare: not needed */
1866 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001868 /* ext. display controls: ext.adr. wrap */
1869 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
Krzysztof Helt8503df62007-10-16 01:29:08 -07001871 /* Set/Reset registes: - */
1872 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1873 /* Set/Reset enable: - */
1874 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1875 /* Color Compare: - */
1876 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1877 /* Data Rotate: - */
1878 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1879 /* Read Map Select: - */
1880 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1881 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1882 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1883 /* Miscellaneous: memory map base address, graphics mode */
1884 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1885 /* Color Don't care: involve all planes */
1886 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1887 /* Bit Mask: no mask at all */
1888 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001890 /* (5434 can't have bit 3 set for bitblt) */
1891 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001893 /* Graphics controller mode extensions: finer granularity,
1894 * 8byte data latches
1895 */
1896 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897
Krzysztof Helt8503df62007-10-16 01:29:08 -07001898 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1899 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1900 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1901 /* Background color byte 1: - */
1902 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1903 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904
Krzysztof Helt8503df62007-10-16 01:29:08 -07001905 /* Attribute Controller palette registers: "identity mapping" */
1906 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1907 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1908 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1909 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1910 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1911 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1912 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1913 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1914 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1915 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1916 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1917 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1918 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1919 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1920 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1921 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922
Krzysztof Helt8503df62007-10-16 01:29:08 -07001923 /* Attribute Controller mode: graphics mode */
1924 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1925 /* Overscan color reg.: reg. 0 */
1926 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1927 /* Color Plane enable: Enable all 4 planes */
1928 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1929/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1930 /* Color Select: - */
1931 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932
Krzysztof Helt8503df62007-10-16 01:29:08 -07001933 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934
1935 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001936 /* polarity (-/-), enable display mem,
1937 * VGA_CRTC_START_HI i/o base = color
1938 */
1939 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940
Krzysztof Helt8503df62007-10-16 01:29:08 -07001941 /* BLT Start/status: Blitter reset */
1942 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1943 /* - " - : "end-of-reset" */
1944 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945
1946 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001947 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948
Krzysztof Helt8503df62007-10-16 01:29:08 -07001949 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 return;
1951}
1952
Krzysztof Helt8503df62007-10-16 01:29:08 -07001953static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954{
1955#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1956 static int IsOn = 0; /* XXX not ok for multiple boards */
1957
Krzysztof Helt8503df62007-10-16 01:29:08 -07001958 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
1960 if (cinfo->btype == BT_PICASSO4)
1961 return; /* nothing to switch */
1962 if (cinfo->btype == BT_ALPINE)
1963 return; /* nothing to switch */
1964 if (cinfo->btype == BT_GD5480)
1965 return; /* nothing to switch */
1966 if (cinfo->btype == BT_PICASSO) {
1967 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001968 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
Krzysztof Helt8503df62007-10-16 01:29:08 -07001970 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 return;
1972 }
1973 if (on) {
1974 switch (cinfo->btype) {
1975 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001976 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 break;
1978 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001979 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 break;
1981 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001982 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 break;
1984 default: /* do nothing */ break;
1985 }
1986 } else {
1987 switch (cinfo->btype) {
1988 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001989 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 break;
1991 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001992 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 break;
1994 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001995 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 break;
1997 default: /* do nothing */ break;
1998 }
1999 }
2000
Krzysztof Helt8503df62007-10-16 01:29:08 -07002001 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002#endif /* CONFIG_ZORRO */
2003}
2004
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005/******************************************/
2006/* Linux 2.6-style accelerated functions */
2007/******************************************/
2008
Krzysztof Helt8503df62007-10-16 01:29:08 -07002009static void cirrusfb_fillrect(struct fb_info *info,
2010 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 struct fb_fillrect modded;
2013 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002014 struct cirrusfb_info *cinfo = info->par;
2015 int m = info->var.bits_per_pixel;
2016 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
2017 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018
2019 if (info->state != FBINFO_STATE_RUNNING)
2020 return;
2021 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2022 cfb_fillrect(info, region);
2023 return;
2024 }
2025
2026 vxres = info->var.xres_virtual;
2027 vyres = info->var.yres_virtual;
2028
2029 memcpy(&modded, region, sizeof(struct fb_fillrect));
2030
Krzysztof Helt8503df62007-10-16 01:29:08 -07002031 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 modded.dx >= vxres || modded.dy >= vyres)
2033 return;
2034
Krzysztof Helt8503df62007-10-16 01:29:08 -07002035 if (modded.dx + modded.width > vxres)
2036 modded.width = vxres - modded.dx;
2037 if (modded.dy + modded.height > vyres)
2038 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039
Krzysztof Helt060b6002007-10-16 01:29:13 -07002040 cirrusfb_RectFill(cinfo->regbase,
2041 info->var.bits_per_pixel,
2042 (region->dx * m) / 8, region->dy,
2043 (region->width * m) / 8, region->height,
2044 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07002045 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046}
2047
Krzysztof Helt8503df62007-10-16 01:29:08 -07002048static void cirrusfb_copyarea(struct fb_info *info,
2049 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 struct fb_copyarea modded;
2052 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002053 struct cirrusfb_info *cinfo = info->par;
2054 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055
2056 if (info->state != FBINFO_STATE_RUNNING)
2057 return;
2058 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2059 cfb_copyarea(info, area);
2060 return;
2061 }
2062
2063 vxres = info->var.xres_virtual;
2064 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002065 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066
Krzysztof Helt8503df62007-10-16 01:29:08 -07002067 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 modded.sx >= vxres || modded.sy >= vyres ||
2069 modded.dx >= vxres || modded.dy >= vyres)
2070 return;
2071
Krzysztof Helt8503df62007-10-16 01:29:08 -07002072 if (modded.sx + modded.width > vxres)
2073 modded.width = vxres - modded.sx;
2074 if (modded.dx + modded.width > vxres)
2075 modded.width = vxres - modded.dx;
2076 if (modded.sy + modded.height > vyres)
2077 modded.height = vyres - modded.sy;
2078 if (modded.dy + modded.height > vyres)
2079 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080
Krzysztof Helt060b6002007-10-16 01:29:13 -07002081 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
2082 (area->sx * m) / 8, area->sy,
2083 (area->dx * m) / 8, area->dy,
2084 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07002085 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002086
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087}
2088
Krzysztof Helt8503df62007-10-16 01:29:08 -07002089static void cirrusfb_imageblit(struct fb_info *info,
2090 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091{
2092 struct cirrusfb_info *cinfo = info->par;
2093
Krzysztof Helt8503df62007-10-16 01:29:08 -07002094 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 cfb_imageblit(info, image);
2096}
2097
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098#ifdef CONFIG_PPC_PREP
2099#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
2100#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002101static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002103 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104
2105 *display = PREP_VIDEO_BASE;
2106 *registers = (unsigned long) PREP_IO_BASE;
2107
Krzysztof Helt8503df62007-10-16 01:29:08 -07002108 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109}
2110
2111#endif /* CONFIG_PPC_PREP */
2112
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002114static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115
2116/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
2117 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
2118 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
2119 * seem to have. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002120static unsigned int cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121{
2122 unsigned long mem;
2123 unsigned char SRF;
2124
Krzysztof Helt8503df62007-10-16 01:29:08 -07002125 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126
Krzysztof Helt8503df62007-10-16 01:29:08 -07002127 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002129 case 0x08:
2130 mem = 512 * 1024;
2131 break;
2132 case 0x10:
2133 mem = 1024 * 1024;
2134 break;
2135 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2136 * on the 5430.
2137 */
2138 case 0x18:
2139 mem = 2048 * 1024;
2140 break;
2141 default:
2142 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 mem = 1024 * 1024;
2144 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002145 if (SRF & 0x80)
2146 /* If DRAM bank switching is enabled, there must be twice as much
2147 * memory installed. (4MB on the 5434)
2148 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002150
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2152
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 return mem;
2155}
2156
Krzysztof Helt8503df62007-10-16 01:29:08 -07002157static void get_pci_addrs(const struct pci_dev *pdev,
2158 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002160 assert(pdev != NULL);
2161 assert(display != NULL);
2162 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163
Krzysztof Helt8503df62007-10-16 01:29:08 -07002164 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165
2166 *display = 0;
2167 *registers = 0;
2168
2169 /* This is a best-guess for now */
2170
2171 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2172 *display = pci_resource_start(pdev, 1);
2173 *registers = pci_resource_start(pdev, 0);
2174 } else {
2175 *display = pci_resource_start(pdev, 0);
2176 *registers = pci_resource_start(pdev, 1);
2177 }
2178
Krzysztof Helt8503df62007-10-16 01:29:08 -07002179 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
Krzysztof Helt8503df62007-10-16 01:29:08 -07002181 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182}
2183
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002184static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002186 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 struct pci_dev *pdev = cinfo->pdev;
2188
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002189 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190#if 0 /* if system didn't claim this region, we would... */
2191 release_mem_region(0xA0000, 65535);
2192#endif
2193 if (release_io_ports)
2194 release_region(0x3C0, 32);
2195 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196}
2197#endif /* CONFIG_PCI */
2198
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002200static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201{
Al Virod91f5bb2007-10-17 00:27:18 +01002202 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 zorro_release_device(cinfo->zdev);
2204
2205 if (cinfo->btype == BT_PICASSO4) {
2206 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002207 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002208 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 } else {
2210 if (zorro_resource_start(cinfo->zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002211 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213}
2214#endif /* CONFIG_ZORRO */
2215
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002216static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002218 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219 struct fb_var_screeninfo *var = &info->var;
2220
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 info->pseudo_palette = cinfo->pseudo_palette;
2222 info->flags = FBINFO_DEFAULT
2223 | FBINFO_HWACCEL_XPAN
2224 | FBINFO_HWACCEL_YPAN
2225 | FBINFO_HWACCEL_FILLRECT
2226 | FBINFO_HWACCEL_COPYAREA;
2227 if (noaccel)
2228 info->flags |= FBINFO_HWACCEL_DISABLED;
2229 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 if (cinfo->btype == BT_GD5480) {
2231 if (var->bits_per_pixel == 16)
2232 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002233 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 info->screen_base += 2 * MB_;
2235 }
2236
2237 /* Fill fix common fields */
2238 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2239 sizeof(info->fix.id));
2240
2241 /* monochrome: only 1 memory plane */
2242 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002243 info->fix.smem_len = info->screen_size;
2244 if (var->bits_per_pixel == 1)
2245 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 info->fix.xpanstep = 1;
2248 info->fix.ypanstep = 1;
2249 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
2251 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 info->fix.mmio_len = 0;
2253 info->fix.accel = FB_ACCEL_NONE;
2254
2255 fb_alloc_cmap(&info->cmap, 256, 0);
2256
2257 return 0;
2258}
2259
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002260static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002262 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002264 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265
Krzysztof Helt8503df62007-10-16 01:29:08 -07002266 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267
Krzysztof Helt8503df62007-10-16 01:29:08 -07002268 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2269 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 btype = cinfo->btype;
2272
2273 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002274 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002276 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277
2278 /* Make pretend we've set the var so our structures are in a "good" */
2279 /* state, even though we haven't written the mode to the hw yet... */
2280 info->var = cirrusfb_predefined[cirrusfb_def_mode].var;
2281 info->var.activate = FB_ACTIVATE_NOW;
2282
2283 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2284 if (err < 0) {
2285 /* should never happen */
2286 DPRINTK("choking on default var... umm, no good.\n");
2287 goto err_unmap_cirrusfb;
2288 }
2289
2290 /* set all the vital stuff */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002291 cirrusfb_set_fbinfo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292
2293 err = register_framebuffer(info);
2294 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002295 printk(KERN_ERR "cirrusfb: could not register "
2296 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 goto err_dealloc_cmap;
2298 }
2299
Krzysztof Helt8503df62007-10-16 01:29:08 -07002300 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 return 0;
2302
2303err_dealloc_cmap:
2304 fb_dealloc_cmap(&info->cmap);
2305err_unmap_cirrusfb:
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002306 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002307 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 return err;
2309}
2310
Krzysztof Helt8503df62007-10-16 01:29:08 -07002311static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312{
2313 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002314 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315
Krzysztof Helt8503df62007-10-16 01:29:08 -07002316 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
Krzysztof Helt8503df62007-10-16 01:29:08 -07002318 unregister_framebuffer(info);
2319 fb_dealloc_cmap(&info->cmap);
2320 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002321 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002322 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323
Krzysztof Helt8503df62007-10-16 01:29:08 -07002324 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325}
2326
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002328static int cirrusfb_pci_register(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 const struct pci_device_id *ent)
2330{
2331 struct cirrusfb_info *cinfo;
2332 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002333 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334 unsigned long board_addr, board_size;
2335 int ret;
2336
2337 ret = pci_enable_device(pdev);
2338 if (ret < 0) {
2339 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2340 goto err_out;
2341 }
2342
2343 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2344 if (!info) {
2345 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2346 ret = -ENOMEM;
2347 goto err_disable;
2348 }
2349
2350 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 cinfo->pdev = pdev;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002352 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353
Krzysztof Helt7345de32007-10-16 01:29:11 -07002354 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002356 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357
Krzysztof Helt8503df62007-10-16 01:29:08 -07002358 if (isPReP) {
2359 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002361 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002363 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002364 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002366 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002367 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002368 /* FIXME: this forces VGA. alternatives? */
2369 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 }
2371
Krzysztof Helt8503df62007-10-16 01:29:08 -07002372 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002373 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374
2375 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002376 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
2378 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002379 if (ret < 0) {
2380 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2381 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382 board_addr);
2383 goto err_release_fb;
2384 }
2385#if 0 /* if the system didn't claim this region, we would... */
2386 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2387 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2388,
2389 0xA0000L);
2390 ret = -EBUSY;
2391 goto err_release_regions;
2392 }
2393#endif
2394 if (request_region(0x3C0, 32, "cirrusfb"))
2395 release_io_ports = 1;
2396
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002397 info->screen_base = ioremap(board_addr, board_size);
2398 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 ret = -EIO;
2400 goto err_release_legacy;
2401 }
2402
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002403 info->fix.smem_start = board_addr;
2404 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 cinfo->unmap = cirrusfb_pci_unmap;
2406
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002407 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2408 "Logic chipset on PCI bus\n",
2409 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410 pci_set_drvdata(pdev, info);
2411
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002412 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002413 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002414 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002415 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416
2417err_release_legacy:
2418 if (release_io_ports)
2419 release_region(0x3C0, 32);
2420#if 0
2421 release_mem_region(0xA0000, 65535);
2422err_release_regions:
2423#endif
2424 pci_release_regions(pdev);
2425err_release_fb:
2426 framebuffer_release(info);
2427err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428err_out:
2429 return ret;
2430}
2431
Krzysztof Helt8503df62007-10-16 01:29:08 -07002432static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433{
2434 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002435 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436
Krzysztof Helt8503df62007-10-16 01:29:08 -07002437 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438
Krzysztof Helt8503df62007-10-16 01:29:08 -07002439 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440}
2441
2442static struct pci_driver cirrusfb_pci_driver = {
2443 .name = "cirrusfb",
2444 .id_table = cirrusfb_pci_table,
2445 .probe = cirrusfb_pci_register,
2446 .remove = __devexit_p(cirrusfb_pci_unregister),
2447#ifdef CONFIG_PM
2448#if 0
2449 .suspend = cirrusfb_pci_suspend,
2450 .resume = cirrusfb_pci_resume,
2451#endif
2452#endif
2453};
2454#endif /* CONFIG_PCI */
2455
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456#ifdef CONFIG_ZORRO
2457static int cirrusfb_zorro_register(struct zorro_dev *z,
2458 const struct zorro_device_id *ent)
2459{
2460 struct cirrusfb_info *cinfo;
2461 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002462 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 struct zorro_dev *z2 = NULL;
2464 unsigned long board_addr, board_size, size;
2465 int ret;
2466
2467 btype = ent->driver_data;
2468 if (cirrusfb_zorro_table2[btype].id2)
2469 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2470 size = cirrusfb_zorro_table2[btype].size;
2471 printk(KERN_INFO "cirrusfb: %s board detected; ",
2472 cirrusfb_board_info[btype].name);
2473
2474 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2475 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 ret = -ENOMEM;
2478 goto err_out;
2479 }
2480
2481 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 cinfo->btype = btype;
2483
Al Viro36ea96a2007-10-27 19:46:58 +01002484 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002485 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486
2487 cinfo->zdev = z;
2488 board_addr = zorro_resource_start(z);
2489 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002490 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491
2492 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002493 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2494 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 board_addr);
2496 ret = -EBUSY;
2497 goto err_release_fb;
2498 }
2499
Krzysztof Helt8503df62007-10-16 01:29:08 -07002500 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501
2502 ret = -EIO;
2503
2504 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002505 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506
2507 /* To be precise, for the P4 this is not the */
2508 /* begin of the board, but the begin of RAM. */
2509 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2510 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002511 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512 if (!cinfo->regbase)
2513 goto err_release_region;
2514
Krzysztof Helt8503df62007-10-16 01:29:08 -07002515 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2516 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002518 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002520 info->fix.smem_start = board_addr + 16777216;
2521 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2522 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 goto err_unmap_regbase;
2524 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002525 printk(KERN_INFO " REG at $%lx\n",
2526 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002528 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002530 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002532 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2533 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 goto err_release_region;
2535
2536 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002538 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2541 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 }
2543 cinfo->unmap = cirrusfb_zorro_unmap;
2544
Krzysztof Helt8503df62007-10-16 01:29:08 -07002545 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 zorro_set_drvdata(z, info);
2547
Al Virod91f5bb2007-10-17 00:27:18 +01002548 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002549 if (ret) {
2550 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002551 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002552 iounmap(cinfo->regbase - 0x600000);
2553 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002554 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002555 }
2556 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557
2558err_unmap_regbase:
2559 /* Parental advisory: explicit hack */
2560 iounmap(cinfo->regbase - 0x600000);
2561err_release_region:
2562 release_region(board_addr, board_size);
2563err_release_fb:
2564 framebuffer_release(info);
2565err_out:
2566 return ret;
2567}
2568
2569void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2570{
2571 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573
Krzysztof Helt8503df62007-10-16 01:29:08 -07002574 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
Krzysztof Helt8503df62007-10-16 01:29:08 -07002576 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577}
2578
2579static struct zorro_driver cirrusfb_zorro_driver = {
2580 .name = "cirrusfb",
2581 .id_table = cirrusfb_zorro_table,
2582 .probe = cirrusfb_zorro_register,
2583 .remove = __devexit_p(cirrusfb_zorro_unregister),
2584};
2585#endif /* CONFIG_ZORRO */
2586
2587static int __init cirrusfb_init(void)
2588{
2589 int error = 0;
2590
2591#ifndef MODULE
2592 char *option = NULL;
2593
2594 if (fb_get_options("cirrusfb", &option))
2595 return -ENODEV;
2596 cirrusfb_setup(option);
2597#endif
2598
2599#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002600 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601#endif
2602#ifdef CONFIG_PCI
2603 error |= pci_register_driver(&cirrusfb_pci_driver);
2604#endif
2605 return error;
2606}
2607
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608#ifndef MODULE
2609static int __init cirrusfb_setup(char *options) {
2610 char *this_opt, s[32];
2611 int i;
2612
Krzysztof Helt8503df62007-10-16 01:29:08 -07002613 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614
2615 if (!options || !*options)
2616 return 0;
2617
Krzysztof Helt8503df62007-10-16 01:29:08 -07002618 while ((this_opt = strsep(&options, ",")) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619 if (!*this_opt) continue;
2620
2621 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2622
2623 for (i = 0; i < NUM_TOTAL_MODES; i++) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002624 sprintf(s, "mode:%s", cirrusfb_predefined[i].name);
2625 if (strcmp(this_opt, s) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626 cirrusfb_def_mode = i;
2627 }
2628 if (!strcmp(this_opt, "noaccel"))
2629 noaccel = 1;
2630 }
2631 return 0;
2632}
2633#endif
2634
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635 /*
2636 * Modularization
2637 */
2638
2639MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2640MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2641MODULE_LICENSE("GPL");
2642
Krzysztof Helt8503df62007-10-16 01:29:08 -07002643static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644{
2645#ifdef CONFIG_PCI
2646 pci_unregister_driver(&cirrusfb_pci_driver);
2647#endif
2648#ifdef CONFIG_ZORRO
2649 zorro_unregister_driver(&cirrusfb_zorro_driver);
2650#endif
2651}
2652
2653module_init(cirrusfb_init);
2654
2655#ifdef MODULE
2656module_exit(cirrusfb_exit);
2657#endif
2658
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659/**********************************************************************/
2660/* about the following functions - I have used the same names for the */
2661/* functions as Markus Wild did in his Retina driver for NetBSD as */
2662/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664/**********************************************************************/
2665
2666/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668 int regnum, unsigned char val)
2669{
2670 unsigned long regofs = 0;
2671
2672 if (cinfo->btype == BT_PICASSO) {
2673 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002674/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2675 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2677 regofs = 0xfff;
2678 }
2679
Krzysztof Helt8503df62007-10-16 01:29:08 -07002680 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681}
2682
2683/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002684static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685{
2686 unsigned long regofs = 0;
2687
2688 if (cinfo->btype == BT_PICASSO) {
2689 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002690/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2691 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2693 regofs = 0xfff;
2694 }
2695
Krzysztof Helt8503df62007-10-16 01:29:08 -07002696 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697}
2698
2699/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002700static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002702 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703
Krzysztof Helt8503df62007-10-16 01:29:08 -07002704 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 /* if we're just in "write value" mode, write back the */
2708 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002709 vga_w(cinfo->regbase, VGA_ATT_IW,
2710 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 }
2712 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002713/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2714 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715
2716 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002717 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718
Krzysztof Helt8503df62007-10-16 01:29:08 -07002719 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720}
2721
2722/*** WHDR() - write into the Hidden DAC register ***/
2723/* as the HDR is the only extension register that requires special treatment
2724 * (the other extension registers are accessible just like the "ordinary"
2725 * registers of their functional group) here is a specialized routine for
2726 * accessing the HDR
2727 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002728static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729{
2730 unsigned char dummy;
2731
2732 if (cinfo->btype == BT_PICASSO) {
2733 /* Klaus' hint for correct access to HDR on some boards */
2734 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002735 WGen(cinfo, VGA_PEL_MSK, 0x00);
2736 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002738 dummy = RGen(cinfo, VGA_PEL_IW);
2739 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740 }
2741 /* now do the usual stuff to access the HDR */
2742
Krzysztof Helt8503df62007-10-16 01:29:08 -07002743 dummy = RGen(cinfo, VGA_PEL_MSK);
2744 udelay(200);
2745 dummy = RGen(cinfo, VGA_PEL_MSK);
2746 udelay(200);
2747 dummy = RGen(cinfo, VGA_PEL_MSK);
2748 udelay(200);
2749 dummy = RGen(cinfo, VGA_PEL_MSK);
2750 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751
Krzysztof Helt8503df62007-10-16 01:29:08 -07002752 WGen(cinfo, VGA_PEL_MSK, val);
2753 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754
2755 if (cinfo->btype == BT_PICASSO) {
2756 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002757 dummy = RGen(cinfo, VGA_PEL_IW);
2758 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759
2760 /* and at the end, restore the mask value */
2761 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002762 WGen(cinfo, VGA_PEL_MSK, 0xff);
2763 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764 }
2765}
2766
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002768static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769{
2770#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002771 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002773 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774#endif
2775}
2776
2777/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002778static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779{
2780#ifdef CONFIG_ZORRO
2781 /* writing an arbitrary value to this one causes the monitor switcher */
2782 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002783 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002785 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786#endif
2787}
2788
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002790static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 unsigned char green, unsigned char blue)
2792{
2793 unsigned int data = VGA_PEL_D;
2794
2795 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002796 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797
2798 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2799 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2800 /* but DAC data register IS, at least for Picasso II */
2801 if (cinfo->btype == BT_PICASSO)
2802 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002803 vga_w(cinfo->regbase, data, red);
2804 vga_w(cinfo->regbase, data, green);
2805 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002807 vga_w(cinfo->regbase, data, blue);
2808 vga_w(cinfo->regbase, data, green);
2809 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 }
2811}
2812
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813#if 0
2814/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002815static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 unsigned char *green, unsigned char *blue)
2817{
2818 unsigned int data = VGA_PEL_D;
2819
Krzysztof Helt8503df62007-10-16 01:29:08 -07002820 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821
2822 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2823 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2824 if (cinfo->btype == BT_PICASSO)
2825 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002826 *red = vga_r(cinfo->regbase, data);
2827 *green = vga_r(cinfo->regbase, data);
2828 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002830 *blue = vga_r(cinfo->regbase, data);
2831 *green = vga_r(cinfo->regbase, data);
2832 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 }
2834}
2835#endif
2836
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837/*******************************************************************
2838 cirrusfb_WaitBLT()
2839
2840 Wait for the BitBLT engine to complete a possible earlier job
2841*********************************************************************/
2842
2843/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002844static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845{
2846 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 /* do nothing */ ;
2849}
2850
2851/*******************************************************************
2852 cirrusfb_BitBLT()
2853
2854 perform accelerated "scrolling"
2855********************************************************************/
2856
Krzysztof Helt8503df62007-10-16 01:29:08 -07002857static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2858 u_short curx, u_short cury,
2859 u_short destx, u_short desty,
2860 u_short width, u_short height,
2861 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862{
2863 u_short nwidth, nheight;
2864 u_long nsrc, ndest;
2865 u_char bltmode;
2866
Krzysztof Helt8503df62007-10-16 01:29:08 -07002867 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868
2869 nwidth = width - 1;
2870 nheight = height - 1;
2871
2872 bltmode = 0x00;
2873 /* if source adr < dest addr, do the Blt backwards */
2874 if (cury <= desty) {
2875 if (cury == desty) {
2876 /* if src and dest are on the same line, check x */
2877 if (curx < destx)
2878 bltmode |= 0x01;
2879 } else
2880 bltmode |= 0x01;
2881 }
2882 if (!bltmode) {
2883 /* standard case: forward blitting */
2884 nsrc = (cury * line_length) + curx;
2885 ndest = (desty * line_length) + destx;
2886 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002887 /* this means start addresses are at the end,
2888 * counting backwards
2889 */
2890 nsrc = cury * line_length + curx +
2891 nheight * line_length + nwidth;
2892 ndest = desty * line_length + destx +
2893 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 }
2895
2896 /*
2897 run-down of registers to be programmed:
2898 destination pitch
2899 source pitch
2900 BLT width/height
2901 source start
2902 destination start
2903 BLT mode
2904 BLT ROP
2905 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2906 start/stop
2907 */
2908
Krzysztof Helt8503df62007-10-16 01:29:08 -07002909 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910
2911 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002912 /* dest pitch low */
2913 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2914 /* dest pitch hi */
2915 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2916 /* source pitch low */
2917 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2918 /* source pitch hi */
2919 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920
2921 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002922 /* BLT width low */
2923 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2924 /* BLT width hi */
2925 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926
2927 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002928 /* BLT height low */
2929 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2930 /* BLT width hi */
2931 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932
2933 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002934 /* BLT dest low */
2935 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2936 /* BLT dest mid */
2937 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2938 /* BLT dest hi */
2939 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940
2941 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002942 /* BLT src low */
2943 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2944 /* BLT src mid */
2945 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2946 /* BLT src hi */
2947 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948
2949 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002950 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951
2952 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002953 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954
2955 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002956 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957
Krzysztof Helt8503df62007-10-16 01:29:08 -07002958 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002959}
2960
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961/*******************************************************************
2962 cirrusfb_RectFill()
2963
2964 perform accelerated rectangle fill
2965********************************************************************/
2966
Krzysztof Helt8503df62007-10-16 01:29:08 -07002967static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 u_short x, u_short y, u_short width, u_short height,
2969 u_char color, u_short line_length)
2970{
2971 u_short nwidth, nheight;
2972 u_long ndest;
2973 u_char op;
2974
Krzysztof Helt8503df62007-10-16 01:29:08 -07002975 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002976
2977 nwidth = width - 1;
2978 nheight = height - 1;
2979
2980 ndest = (y * line_length) + x;
2981
Krzysztof Helt8503df62007-10-16 01:29:08 -07002982 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983
2984 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002985 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2986 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2987 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2988 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989
2990 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002991 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2992 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002993
2994 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002995 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2996 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002997
2998 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002999 /* BLT dest low */
3000 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
3001 /* BLT dest mid */
3002 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
3003 /* BLT dest hi */
3004 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005
3006 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003007 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
3008 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
3009 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003010
3011 /* This is a ColorExpand Blt, using the */
3012 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003013 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
3014 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003015
3016 op = 0xc0;
3017 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003018 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3019 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020 op = 0x50;
3021 op = 0xd0;
3022 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003023 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3024 vga_wgfx(regbase, CL_GR11, color); /* background color */
3025 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
3026 vga_wgfx(regbase, CL_GR13, color); /* background color */
3027 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
3028 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003029 op = 0x50;
3030 op = 0xf0;
3031 }
3032 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003033 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034
3035 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003036 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003037
3038 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003039 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040
Krzysztof Helt8503df62007-10-16 01:29:08 -07003041 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003042}
3043
Linus Torvalds1da177e2005-04-16 15:20:36 -07003044/**************************************************************************
3045 * bestclock() - determine closest possible clock lower(?) than the
3046 * desired pixel clock
3047 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07003048static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049 long *den, long *div, long maxfreq)
3050{
3051 long n, h, d, f;
3052
Krzysztof Helt8503df62007-10-16 01:29:08 -07003053 assert(best != NULL);
3054 assert(nom != NULL);
3055 assert(den != NULL);
3056 assert(div != NULL);
3057 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058
3059 *nom = 0;
3060 *den = 0;
3061 *div = 0;
3062
Krzysztof Helt8503df62007-10-16 01:29:08 -07003063 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003064
3065 if (freq < 8000)
3066 freq = 8000;
3067
3068 if (freq > maxfreq)
3069 freq = maxfreq;
3070
3071 *best = 0;
3072 f = freq * 10;
3073
3074 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07003075 int s = 0;
3076
Linus Torvalds1da177e2005-04-16 15:20:36 -07003077 d = (143181 * n) / f;
3078 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07003079 int temp = d;
3080
3081 if (temp > 31) {
3082 s = 1;
3083 temp >>= 1;
3084 }
3085 h = ((14318 * n) / temp) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003086 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087 *best = h;
3088 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07003089 *den = temp;
3090 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 }
3092 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07003093 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003094 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07003095 if (d > 31) {
3096 s = 1;
3097 d >>= 1;
3098 }
3099 h = ((14318 * n) / d) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003100 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003101 *best = h;
3102 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07003103 *den = d;
3104 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003105 }
3106 }
3107 }
3108
Krzysztof Helt8503df62007-10-16 01:29:08 -07003109 DPRINTK("Best possible values for given frequency:\n");
3110 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
3111 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003112
Krzysztof Helt8503df62007-10-16 01:29:08 -07003113 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114}
3115
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116/* -------------------------------------------------------------------------
3117 *
3118 * debugging functions
3119 *
3120 * -------------------------------------------------------------------------
3121 */
3122
3123#ifdef CIRRUSFB_DEBUG
3124
3125/**
3126 * cirrusfb_dbg_print_byte
3127 * @name: name associated with byte value to be displayed
3128 * @val: byte value to be displayed
3129 *
3130 * DESCRIPTION:
3131 * Display an indented string, along with a hexidecimal byte value, and
3132 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3133 * order.
3134 */
3135
3136static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003137void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003138{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003139 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3140 name, val,
3141 val & 0x80 ? '1' : '0',
3142 val & 0x40 ? '1' : '0',
3143 val & 0x20 ? '1' : '0',
3144 val & 0x10 ? '1' : '0',
3145 val & 0x08 ? '1' : '0',
3146 val & 0x04 ? '1' : '0',
3147 val & 0x02 ? '1' : '0',
3148 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149}
3150
Linus Torvalds1da177e2005-04-16 15:20:36 -07003151/**
3152 * cirrusfb_dbg_print_regs
3153 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3154 * @reg_class: type of registers to read: %CRT, or %SEQ
3155 *
3156 * DESCRIPTION:
3157 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3158 * old-style I/O ports are queried for information, otherwise MMIO is
3159 * used at the given @base address to query the information.
3160 */
3161
3162static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003163void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003164 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165{
3166 va_list list;
3167 unsigned char val = 0;
3168 unsigned reg;
3169 char *name;
3170
Krzysztof Helt8503df62007-10-16 01:29:08 -07003171 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003172
Krzysztof Helt8503df62007-10-16 01:29:08 -07003173 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003174 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003175 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003176
3177 switch (reg_class) {
3178 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003179 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003180 break;
3181 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003182 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003183 break;
3184 default:
3185 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003186 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187 break;
3188 }
3189
Krzysztof Helt8503df62007-10-16 01:29:08 -07003190 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003191
Krzysztof Helt8503df62007-10-16 01:29:08 -07003192 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003193 }
3194
Krzysztof Helt8503df62007-10-16 01:29:08 -07003195 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003196}
3197
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198/**
3199 * cirrusfb_dump
3200 * @cirrusfbinfo:
3201 *
3202 * DESCRIPTION:
3203 */
3204
Krzysztof Helt8503df62007-10-16 01:29:08 -07003205static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003207 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003208}
3209
Linus Torvalds1da177e2005-04-16 15:20:36 -07003210/**
3211 * cirrusfb_dbg_reg_dump
3212 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3213 *
3214 * DESCRIPTION:
3215 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3216 * old-style I/O ports are queried for information, otherwise MMIO is
3217 * used at the given @base address to query the information.
3218 */
3219
3220static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003221void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003222{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003223 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224
Krzysztof Helt8503df62007-10-16 01:29:08 -07003225 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 "CR00", 0x00,
3227 "CR01", 0x01,
3228 "CR02", 0x02,
3229 "CR03", 0x03,
3230 "CR04", 0x04,
3231 "CR05", 0x05,
3232 "CR06", 0x06,
3233 "CR07", 0x07,
3234 "CR08", 0x08,
3235 "CR09", 0x09,
3236 "CR0A", 0x0A,
3237 "CR0B", 0x0B,
3238 "CR0C", 0x0C,
3239 "CR0D", 0x0D,
3240 "CR0E", 0x0E,
3241 "CR0F", 0x0F,
3242 "CR10", 0x10,
3243 "CR11", 0x11,
3244 "CR12", 0x12,
3245 "CR13", 0x13,
3246 "CR14", 0x14,
3247 "CR15", 0x15,
3248 "CR16", 0x16,
3249 "CR17", 0x17,
3250 "CR18", 0x18,
3251 "CR22", 0x22,
3252 "CR24", 0x24,
3253 "CR26", 0x26,
3254 "CR2D", 0x2D,
3255 "CR2E", 0x2E,
3256 "CR2F", 0x2F,
3257 "CR30", 0x30,
3258 "CR31", 0x31,
3259 "CR32", 0x32,
3260 "CR33", 0x33,
3261 "CR34", 0x34,
3262 "CR35", 0x35,
3263 "CR36", 0x36,
3264 "CR37", 0x37,
3265 "CR38", 0x38,
3266 "CR39", 0x39,
3267 "CR3A", 0x3A,
3268 "CR3B", 0x3B,
3269 "CR3C", 0x3C,
3270 "CR3D", 0x3D,
3271 "CR3E", 0x3E,
3272 "CR3F", 0x3F,
3273 NULL);
3274
Krzysztof Helt8503df62007-10-16 01:29:08 -07003275 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003276
Krzysztof Helt8503df62007-10-16 01:29:08 -07003277 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003278
Krzysztof Helt8503df62007-10-16 01:29:08 -07003279 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003280 "SR00", 0x00,
3281 "SR01", 0x01,
3282 "SR02", 0x02,
3283 "SR03", 0x03,
3284 "SR04", 0x04,
3285 "SR08", 0x08,
3286 "SR09", 0x09,
3287 "SR0A", 0x0A,
3288 "SR0B", 0x0B,
3289 "SR0D", 0x0D,
3290 "SR10", 0x10,
3291 "SR11", 0x11,
3292 "SR12", 0x12,
3293 "SR13", 0x13,
3294 "SR14", 0x14,
3295 "SR15", 0x15,
3296 "SR16", 0x16,
3297 "SR17", 0x17,
3298 "SR18", 0x18,
3299 "SR19", 0x19,
3300 "SR1A", 0x1A,
3301 "SR1B", 0x1B,
3302 "SR1C", 0x1C,
3303 "SR1D", 0x1D,
3304 "SR1E", 0x1E,
3305 "SR1F", 0x1F,
3306 NULL);
3307
Krzysztof Helt8503df62007-10-16 01:29:08 -07003308 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309}
3310
3311#endif /* CIRRUSFB_DEBUG */
3312