blob: 56306658c01ec9e1b1c94218573f99b33890869c [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>
48#include <linux/selection.h>
49#include <asm/pgtable.h>
50
51#ifdef CONFIG_ZORRO
52#include <linux/zorro.h>
53#endif
54#ifdef CONFIG_PCI
55#include <linux/pci.h>
56#endif
57#ifdef CONFIG_AMIGA
58#include <asm/amigahw.h>
59#endif
60#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110061#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070062#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#else
64#define isPReP 0
65#endif
66
67#include "video/vga.h"
68#include "video/cirrus.h"
69
Linus Torvalds1da177e2005-04-16 15:20:36 -070070/*****************************************************************
71 *
72 * debugging and utility macros
73 *
74 */
75
76/* enable debug output? */
77/* #define CIRRUSFB_DEBUG 1 */
78
79/* disable runtime assertions? */
80/* #define CIRRUSFB_NDEBUG */
81
82/* debug output */
83#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -070084#define DPRINTK(fmt, args...) \
85 printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086#else
87#define DPRINTK(fmt, args...)
88#endif
89
90/* debugging assertions */
91#ifndef CIRRUSFB_NDEBUG
92#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070093 if (!(expr)) { \
94 printk("Assertion failed! %s,%s,%s,line=%d\n", \
95 #expr, __FILE__, __FUNCTION__, __LINE__); \
96 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070097#else
98#define assert(expr)
99#endif
100
Krzysztof Helt8503df62007-10-16 01:29:08 -0700101#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102#define KB_ (1024)
103
104#define MAX_NUM_BOARDS 7
105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106/*****************************************************************
107 *
108 * chipset information
109 *
110 */
111
112/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700113enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 BT_NONE = 0,
115 BT_SD64,
116 BT_PICCOLO,
117 BT_PICASSO,
118 BT_SPECTRUM,
119 BT_PICASSO4, /* GD5446 */
120 BT_ALPINE, /* GD543x/4x */
121 BT_GD5480,
122 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700123};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125/*
126 * per-board-type information, used for enumerating and abstracting
127 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700128 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 * use direct indexing on this array
130 * NOTE: '__initdata' cannot be used as some of this info
131 * is required at runtime. Maybe separate into an init-only and
132 * a run-time table?
133 */
134static const struct cirrusfb_board_info_rec {
135 char *name; /* ASCII name of chipset */
136 long maxclock[5]; /* maximum video clock */
137 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700138 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
139 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700140 /* construct bit 19 of screen start address */
141 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
143 /* initial SR07 value, then for each mode */
144 unsigned char sr07;
145 unsigned char sr07_1bpp;
146 unsigned char sr07_1bpp_mux;
147 unsigned char sr07_8bpp;
148 unsigned char sr07_8bpp_mux;
149
150 unsigned char sr1f; /* SR1F VGA initial register value */
151} cirrusfb_board_info[] = {
152 [BT_SD64] = {
153 .name = "CL SD64",
154 .maxclock = {
155 /* guess */
156 /* the SD64/P4 have a higher max. videoclock */
157 140000, 140000, 140000, 140000, 140000,
158 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700159 .init_sr07 = true,
160 .init_sr1f = true,
161 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 .sr07 = 0xF0,
163 .sr07_1bpp = 0xF0,
164 .sr07_8bpp = 0xF1,
165 .sr1f = 0x20
166 },
167 [BT_PICCOLO] = {
168 .name = "CL Piccolo",
169 .maxclock = {
170 /* guess */
171 90000, 90000, 90000, 90000, 90000
172 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700173 .init_sr07 = true,
174 .init_sr1f = true,
175 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 .sr07 = 0x80,
177 .sr07_1bpp = 0x80,
178 .sr07_8bpp = 0x81,
179 .sr1f = 0x22
180 },
181 [BT_PICASSO] = {
182 .name = "CL Picasso",
183 .maxclock = {
184 /* guess */
185 90000, 90000, 90000, 90000, 90000
186 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700187 .init_sr07 = true,
188 .init_sr1f = true,
189 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 .sr07 = 0x20,
191 .sr07_1bpp = 0x20,
192 .sr07_8bpp = 0x21,
193 .sr1f = 0x22
194 },
195 [BT_SPECTRUM] = {
196 .name = "CL Spectrum",
197 .maxclock = {
198 /* guess */
199 90000, 90000, 90000, 90000, 90000
200 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700201 .init_sr07 = true,
202 .init_sr1f = true,
203 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 .sr07 = 0x80,
205 .sr07_1bpp = 0x80,
206 .sr07_8bpp = 0x81,
207 .sr1f = 0x22
208 },
209 [BT_PICASSO4] = {
210 .name = "CL Picasso4",
211 .maxclock = {
212 135100, 135100, 85500, 85500, 0
213 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700214 .init_sr07 = true,
215 .init_sr1f = false,
216 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 .sr07 = 0x20,
218 .sr07_1bpp = 0x20,
219 .sr07_8bpp = 0x21,
220 .sr1f = 0
221 },
222 [BT_ALPINE] = {
223 .name = "CL Alpine",
224 .maxclock = {
225 /* for the GD5430. GD5446 can do more... */
226 85500, 85500, 50000, 28500, 0
227 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700228 .init_sr07 = true,
229 .init_sr1f = true,
230 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 .sr07 = 0xA0,
232 .sr07_1bpp = 0xA1,
233 .sr07_1bpp_mux = 0xA7,
234 .sr07_8bpp = 0xA1,
235 .sr07_8bpp_mux = 0xA7,
236 .sr1f = 0x1C
237 },
238 [BT_GD5480] = {
239 .name = "CL GD5480",
240 .maxclock = {
241 135100, 200000, 200000, 135100, 135100
242 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700243 .init_sr07 = true,
244 .init_sr1f = true,
245 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 .sr07 = 0x10,
247 .sr07_1bpp = 0x11,
248 .sr07_8bpp = 0x11,
249 .sr1f = 0x1C
250 },
251 [BT_LAGUNA] = {
252 .name = "CL Laguna",
253 .maxclock = {
254 /* guess */
255 135100, 135100, 135100, 135100, 135100,
256 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700257 .init_sr07 = false,
258 .init_sr1f = false,
259 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 }
261};
262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263#ifdef CONFIG_PCI
264#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000265 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266
267static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700268 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
271 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
272 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
273 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
274 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
275 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
276 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
277 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
278 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 { 0, }
280};
281MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
282#undef CHIP
283#endif /* CONFIG_PCI */
284
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285#ifdef CONFIG_ZORRO
286static const struct zorro_device_id cirrusfb_zorro_table[] = {
287 {
288 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
289 .driver_data = BT_SD64,
290 }, {
291 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
292 .driver_data = BT_PICCOLO,
293 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700294 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 .driver_data = BT_PICASSO,
296 }, {
297 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
298 .driver_data = BT_SPECTRUM,
299 }, {
300 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
301 .driver_data = BT_PICASSO4,
302 },
303 { 0 }
304};
305
306static const struct {
307 zorro_id id2;
308 unsigned long size;
309} cirrusfb_zorro_table2[] = {
310 [BT_SD64] = {
311 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
312 .size = 0x400000
313 },
314 [BT_PICCOLO] = {
315 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
316 .size = 0x200000
317 },
318 [BT_PICASSO] = {
319 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
320 .size = 0x200000
321 },
322 [BT_SPECTRUM] = {
323 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
324 .size = 0x200000
325 },
326 [BT_PICASSO4] = {
327 .id2 = 0,
328 .size = 0x400000
329 }
330};
331#endif /* CONFIG_ZORRO */
332
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333struct cirrusfb_regs {
334 __u32 line_length; /* in BYTES! */
335 __u32 visual;
336 __u32 type;
337
338 long freq;
339 long nom;
340 long den;
341 long div;
342 long multiplexing;
343 long mclk;
344 long divMCLK;
345
346 long HorizRes; /* The x resolution in pixel */
347 long HorizTotal;
348 long HorizDispEnd;
349 long HorizBlankStart;
350 long HorizBlankEnd;
351 long HorizSyncStart;
352 long HorizSyncEnd;
353
354 long VertRes; /* the physical y resolution in scanlines */
355 long VertTotal;
356 long VertDispEnd;
357 long VertSyncStart;
358 long VertSyncEnd;
359 long VertBlankStart;
360 long VertBlankEnd;
361};
362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700364enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700365 CRT,
366 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700367};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700368#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
370/* info about board */
371struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700373 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 unsigned char SFR; /* Shadow of special function register */
375
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 struct cirrusfb_regs currentmode;
377 int blank_mode;
378
Antonino A. Daplas49d5c7b2005-11-29 19:34:43 -0800379 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381#ifdef CONFIG_ZORRO
382 struct zorro_dev *zdev;
383#endif
384#ifdef CONFIG_PCI
385 struct pci_dev *pdev;
386#endif
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700387 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388};
389
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390static unsigned cirrusfb_def_mode = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700391static int noaccel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
393/*
394 * Predefined Video Modes
395 */
396
397static const struct {
398 const char *name;
399 struct fb_var_screeninfo var;
400} cirrusfb_predefined[] = {
401 {
402 /* autodetect mode */
403 .name = "Autodetect",
404 }, {
405 /* 640x480, 31.25 kHz, 60 Hz, 25 MHz PixClock */
406 .name = "640x480",
407 .var = {
408 .xres = 640,
409 .yres = 480,
410 .xres_virtual = 640,
411 .yres_virtual = 480,
412 .bits_per_pixel = 8,
413 .red = { .length = 8 },
414 .green = { .length = 8 },
415 .blue = { .length = 8 },
416 .width = -1,
417 .height = -1,
418 .pixclock = 40000,
419 .left_margin = 48,
420 .right_margin = 16,
421 .upper_margin = 32,
422 .lower_margin = 8,
423 .hsync_len = 96,
424 .vsync_len = 4,
Krzysztof Helt8503df62007-10-16 01:29:08 -0700425 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 .vmode = FB_VMODE_NONINTERLACED
427 }
428 }, {
429 /* 800x600, 48 kHz, 76 Hz, 50 MHz PixClock */
430 .name = "800x600",
431 .var = {
432 .xres = 800,
433 .yres = 600,
434 .xres_virtual = 800,
435 .yres_virtual = 600,
436 .bits_per_pixel = 8,
437 .red = { .length = 8 },
438 .green = { .length = 8 },
439 .blue = { .length = 8 },
440 .width = -1,
441 .height = -1,
442 .pixclock = 20000,
443 .left_margin = 128,
444 .right_margin = 16,
445 .upper_margin = 24,
446 .lower_margin = 2,
447 .hsync_len = 96,
448 .vsync_len = 6,
449 .vmode = FB_VMODE_NONINTERLACED
450 }
451 }, {
452 /*
453 * Modeline from XF86Config:
454 * Mode "1024x768" 80 1024 1136 1340 1432 768 770 774 805
455 */
456 /* 1024x768, 55.8 kHz, 70 Hz, 80 MHz PixClock */
457 .name = "1024x768",
458 .var = {
459 .xres = 1024,
460 .yres = 768,
461 .xres_virtual = 1024,
462 .yres_virtual = 768,
463 .bits_per_pixel = 8,
464 .red = { .length = 8 },
465 .green = { .length = 8 },
466 .blue = { .length = 8 },
467 .width = -1,
468 .height = -1,
469 .pixclock = 12500,
470 .left_margin = 144,
471 .right_margin = 32,
472 .upper_margin = 30,
473 .lower_margin = 2,
474 .hsync_len = 192,
475 .vsync_len = 6,
476 .vmode = FB_VMODE_NONINTERLACED
477 }
478 }
479};
480
481#define NUM_TOTAL_MODES ARRAY_SIZE(cirrusfb_predefined)
482
483/****************************************************************************/
484/**** BEGIN PROTOTYPES ******************************************************/
485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700487static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700489static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490#endif
491
Krzysztof Helt8503df62007-10-16 01:29:08 -0700492static int cirrusfb_open(struct fb_info *info, int user);
493static int cirrusfb_release(struct fb_info *info, int user);
494static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
495 unsigned blue, unsigned transp,
496 struct fb_info *info);
497static int cirrusfb_check_var(struct fb_var_screeninfo *var,
498 struct fb_info *info);
499static int cirrusfb_set_par(struct fb_info *info);
500static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
501 struct fb_info *info);
502static int cirrusfb_blank(int blank_mode, struct fb_info *info);
503static void cirrusfb_fillrect(struct fb_info *info,
504 const struct fb_fillrect *region);
505static void cirrusfb_copyarea(struct fb_info *info,
506 const struct fb_copyarea *area);
507static void cirrusfb_imageblit(struct fb_info *info,
508 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510/* function table of the above functions */
511static struct fb_ops cirrusfb_ops = {
512 .owner = THIS_MODULE,
513 .fb_open = cirrusfb_open,
514 .fb_release = cirrusfb_release,
515 .fb_setcolreg = cirrusfb_setcolreg,
516 .fb_check_var = cirrusfb_check_var,
517 .fb_set_par = cirrusfb_set_par,
518 .fb_pan_display = cirrusfb_pan_display,
519 .fb_blank = cirrusfb_blank,
520 .fb_fillrect = cirrusfb_fillrect,
521 .fb_copyarea = cirrusfb_copyarea,
522 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523};
524
525/*--- Hardware Specific Routines -------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700526static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 struct cirrusfb_regs *regs,
528 const struct fb_info *info);
529/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700530static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700531static void switch_monitor(struct cirrusfb_info *cinfo, int on);
532static void WGen(const struct cirrusfb_info *cinfo,
533 int regnum, unsigned char val);
534static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
535static void AttrOn(const struct cirrusfb_info *cinfo);
536static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
537static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
538static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
539static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
540 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700542static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
543 unsigned char *red, unsigned char *green,
544 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700546static void cirrusfb_WaitBLT(u8 __iomem *regbase);
547static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
548 u_short curx, u_short cury,
549 u_short destx, u_short desty,
550 u_short width, u_short height,
551 u_short line_length);
552static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
553 u_short x, u_short y,
554 u_short width, u_short height,
555 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Krzysztof Helt8503df62007-10-16 01:29:08 -0700557static void bestclock(long freq, long *best,
558 long *nom, long *den,
559 long *div, long maxfreq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700562static void cirrusfb_dump(void);
563static void cirrusfb_dbg_reg_dump(caddr_t regbase);
564static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700565 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700566static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567#endif /* CIRRUSFB_DEBUG */
568
569/*** END PROTOTYPES ********************************************************/
570/*****************************************************************************/
571/*** BEGIN Interface Used by the World ***************************************/
572
Krzysztof Helt8503df62007-10-16 01:29:08 -0700573static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
575/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700576static int cirrusfb_open(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, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 return 0;
581}
582
583/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700584static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585{
586 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700587 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 return 0;
589}
590
591/**** END Interface used by the World *************************************/
592/****************************************************************************/
593/**** BEGIN Hardware specific Routines **************************************/
594
595/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700596static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597{
598 long mclk;
599
Krzysztof Helt8503df62007-10-16 01:29:08 -0700600 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
603 * Assume a 64-bit data path for now. The formula is:
604 * ((B * PCLK * 2)/W) * 1.2
605 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
606 mclk = ((bpp / 8) * freq * 2) / 4;
607 mclk = (mclk * 12) / 10;
608 if (mclk < 50000)
609 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700610 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
613 mclk = ((mclk * 16) / 14318);
614 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700615 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
617 /* Determine if we should use MCLK instead of VCLK, and if so, what we
618 * should divide it by to get VCLK */
619 switch (freq) {
620 case 24751 ... 25249:
621 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700622 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 break;
624 case 49501 ... 50499:
625 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700626 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 break;
628 default:
629 *div = 0;
630 break;
631 }
632
633 return mclk;
634}
635
636static int cirrusfb_check_var(struct fb_var_screeninfo *var,
637 struct fb_info *info)
638{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 int nom, den; /* translyting from pixels->bytes */
640 int yres, i;
641 static struct { int xres, yres; } modes[] =
642 { { 1600, 1280 },
643 { 1280, 1024 },
644 { 1024, 768 },
645 { 800, 600 },
646 { 640, 480 },
647 { -1, -1 } };
648
649 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700650 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 nom = 4;
652 den = 8;
653 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700654 case 8:
655 case 16:
656 case 24:
657 case 32:
658 nom = var->bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 den = 1;
660 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700662 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
663 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700665 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 return -EINVAL;
667 }
668
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700669 if (var->xres * nom / den * var->yres > info->screen_size) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700670 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
671 "resolution too high to fit into video memory!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700673 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 return -EINVAL;
675 }
676
677 /* use highest possible virtual resolution */
678 if (var->xres_virtual == -1 &&
679 var->yres_virtual == -1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700680 printk(KERN_INFO
681 "cirrusfb: using maximum available virtual resolution\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 for (i = 0; modes[i].xres != -1; i++) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700683 int size = modes[i].xres * nom / den * modes[i].yres;
684 if (size < info->screen_size / 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 break;
686 }
687 if (modes[i].xres == -1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700688 printk(KERN_ERR "cirrusfb: could not find a virtual "
689 "resolution that fits into video memory!!\n");
690 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 return -EINVAL;
692 }
693 var->xres_virtual = modes[i].xres;
694 var->yres_virtual = modes[i].yres;
695
Krzysztof Helt8503df62007-10-16 01:29:08 -0700696 printk(KERN_INFO "cirrusfb: virtual resolution set to "
697 "maximum of %dx%d\n", var->xres_virtual,
698 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 }
700
701 if (var->xres_virtual < var->xres)
702 var->xres_virtual = var->xres;
703 if (var->yres_virtual < var->yres)
704 var->yres_virtual = var->yres;
705
706 if (var->xoffset < 0)
707 var->xoffset = 0;
708 if (var->yoffset < 0)
709 var->yoffset = 0;
710
711 /* truncate xoffset and yoffset to maximum if too high */
712 if (var->xoffset > var->xres_virtual - var->xres)
713 var->xoffset = var->xres_virtual - var->xres - 1;
714 if (var->yoffset > var->yres_virtual - var->yres)
715 var->yoffset = var->yres_virtual - var->yres - 1;
716
717 switch (var->bits_per_pixel) {
718 case 1:
719 var->red.offset = 0;
720 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700721 var->green = var->red;
722 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 break;
724
725 case 8:
726 var->red.offset = 0;
727 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700728 var->green = var->red;
729 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 break;
731
732 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700733 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 var->red.offset = 2;
735 var->green.offset = -3;
736 var->blue.offset = 8;
737 } else {
738 var->red.offset = 10;
739 var->green.offset = 5;
740 var->blue.offset = 0;
741 }
742 var->red.length = 5;
743 var->green.length = 5;
744 var->blue.length = 5;
745 break;
746
747 case 24:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700749 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 var->red.offset = 8;
751 var->green.offset = 16;
752 var->blue.offset = 24;
753 } else {
754 var->red.offset = 16;
755 var->green.offset = 8;
756 var->blue.offset = 0;
757 }
758 var->red.length = 8;
759 var->green.length = 8;
760 var->blue.length = 8;
761 break;
762
763 default:
764 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700765 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 /* should never occur */
767 break;
768 }
769
770 var->red.msb_right =
771 var->green.msb_right =
772 var->blue.msb_right =
773 var->transp.offset =
774 var->transp.length =
775 var->transp.msb_right = 0;
776
777 yres = var->yres;
778 if (var->vmode & FB_VMODE_DOUBLE)
779 yres *= 2;
780 else if (var->vmode & FB_VMODE_INTERLACED)
781 yres = (yres + 1) / 2;
782
783 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700784 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
785 "special treatment required! (TODO)\n");
786 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 return -EINVAL;
788 }
789
790 return 0;
791}
792
Krzysztof Helt8503df62007-10-16 01:29:08 -0700793static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 struct cirrusfb_regs *regs,
795 const struct fb_info *info)
796{
797 long freq;
798 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700799 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 struct cirrusfb_info *cinfo = info->par;
801 int xres, hfront, hsync, hback;
802 int yres, vfront, vsync, vback;
803
Krzysztof Helt8503df62007-10-16 01:29:08 -0700804 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 case 1:
806 regs->line_length = var->xres_virtual / 8;
807 regs->visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 break;
809
810 case 8:
811 regs->line_length = var->xres_virtual;
812 regs->visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 break;
814
815 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 case 24:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 case 32:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700818 regs->line_length = var->xres_virtual * maxclockidx;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 regs->visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 break;
821
822 default:
823 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700824 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 /* should never occur */
826 break;
827 }
828
829 regs->type = FB_TYPE_PACKED_PIXELS;
830
831 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700832 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
836 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
837 regs->multiplexing = 0;
838
839 /* If the frequency is greater than we can support, we might be able
840 * to use multiplexing for the video mode */
841 if (freq > maxclock) {
842 switch (cinfo->btype) {
843 case BT_ALPINE:
844 case BT_GD5480:
845 regs->multiplexing = 1;
846 break;
847
848 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700849 printk(KERN_ERR "cirrusfb: Frequency greater "
850 "than maxclock (%ld kHz)\n", maxclock);
851 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 return -EINVAL;
853 }
854 }
855#if 0
856 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
857 * the VCLK is double the pixel clock. */
858 switch (var->bits_per_pixel) {
859 case 16:
860 case 32:
861 if (regs->HorizRes <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700862 /* Xbh has this type of clock for 32-bit */
863 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 break;
865 }
866#endif
867
Krzysztof Helt8503df62007-10-16 01:29:08 -0700868 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
869 maxclock);
870 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
871 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
873 xres = var->xres;
874 hfront = var->right_margin;
875 hsync = var->hsync_len;
876 hback = var->left_margin;
877
878 yres = var->yres;
879 vfront = var->lower_margin;
880 vsync = var->vsync_len;
881 vback = var->upper_margin;
882
883 if (var->vmode & FB_VMODE_DOUBLE) {
884 yres *= 2;
885 vfront *= 2;
886 vsync *= 2;
887 vback *= 2;
888 } else if (var->vmode & FB_VMODE_INTERLACED) {
889 yres = (yres + 1) / 2;
890 vfront = (vfront + 1) / 2;
891 vsync = (vsync + 1) / 2;
892 vback = (vback + 1) / 2;
893 }
894 regs->HorizRes = xres;
895 regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
896 regs->HorizDispEnd = xres / 8 - 1;
897 regs->HorizBlankStart = xres / 8;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700898 /* does not count with "-5" */
899 regs->HorizBlankEnd = regs->HorizTotal + 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 regs->HorizSyncStart = (xres + hfront) / 8 + 1;
901 regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
902
903 regs->VertRes = yres;
904 regs->VertTotal = yres + vfront + vsync + vback - 2;
905 regs->VertDispEnd = yres - 1;
906 regs->VertBlankStart = yres;
907 regs->VertBlankEnd = regs->VertTotal;
908 regs->VertSyncStart = yres + vfront - 1;
909 regs->VertSyncEnd = yres + vfront + vsync - 1;
910
911 if (regs->VertRes >= 1024) {
912 regs->VertTotal /= 2;
913 regs->VertSyncStart /= 2;
914 regs->VertSyncEnd /= 2;
915 regs->VertDispEnd /= 2;
916 }
917 if (regs->multiplexing) {
918 regs->HorizTotal /= 2;
919 regs->HorizSyncStart /= 2;
920 regs->HorizSyncEnd /= 2;
921 regs->HorizDispEnd /= 2;
922 }
923
924 return 0;
925}
926
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
928 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700930 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 if (div == 2) {
933 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700934 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
935 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
936 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 } else if (div == 1) {
938 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700939 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
940 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
941 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700943 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 }
945}
946
947/*************************************************************************
948 cirrusfb_set_par_foo()
949
950 actually writes the values for a new video mode into the hardware,
951**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953{
954 struct cirrusfb_info *cinfo = info->par;
955 struct fb_var_screeninfo *var = &info->var;
956 struct cirrusfb_regs regs;
957 u8 __iomem *regbase = cinfo->regbase;
958 unsigned char tmp;
959 int offset = 0, err;
960 const struct cirrusfb_board_info_rec *bi;
961
Krzysztof Helt8503df62007-10-16 01:29:08 -0700962 DPRINTK("ENTER\n");
963 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700967 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
969 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700970 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 /* should never happen */
972 DPRINTK("mode change aborted. invalid var.\n");
973 return -EINVAL;
974 }
975
976 bi = &cirrusfb_board_info[cinfo->btype];
977
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980
981 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700982 DPRINTK("CRT0: %ld\n", regs.HorizTotal);
983 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
Krzysztof Helt8503df62007-10-16 01:29:08 -0700985 DPRINTK("CRT1: %ld\n", regs.HorizDispEnd);
986 vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
Krzysztof Helt8503df62007-10-16 01:29:08 -0700988 DPRINTK("CRT2: %ld\n", regs.HorizBlankStart);
989 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
Krzysztof Helt8503df62007-10-16 01:29:08 -0700991 /* + 128: Compatible read */
992 DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);
993 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
994 128 + (regs.HorizBlankEnd % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
Krzysztof Helt8503df62007-10-16 01:29:08 -0700996 DPRINTK("CRT4: %ld\n", regs.HorizSyncStart);
997 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
999 tmp = regs.HorizSyncEnd % 32;
1000 if (regs.HorizBlankEnd & 32)
1001 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001002 DPRINTK("CRT5: %d\n", tmp);
1003 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
Krzysztof Helt8503df62007-10-16 01:29:08 -07001005 DPRINTK("CRT6: %ld\n", regs.VertTotal & 0xff);
1006 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
1008 tmp = 16; /* LineCompare bit #9 */
1009 if (regs.VertTotal & 256)
1010 tmp |= 1;
1011 if (regs.VertDispEnd & 256)
1012 tmp |= 2;
1013 if (regs.VertSyncStart & 256)
1014 tmp |= 4;
1015 if (regs.VertBlankStart & 256)
1016 tmp |= 8;
1017 if (regs.VertTotal & 512)
1018 tmp |= 32;
1019 if (regs.VertDispEnd & 512)
1020 tmp |= 64;
1021 if (regs.VertSyncStart & 512)
1022 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001023 DPRINTK("CRT7: %d\n", tmp);
1024 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
1026 tmp = 0x40; /* LineCompare bit #8 */
1027 if (regs.VertBlankStart & 512)
1028 tmp |= 0x20;
1029 if (var->vmode & FB_VMODE_DOUBLE)
1030 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001031 DPRINTK("CRT9: %d\n", tmp);
1032 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033
Krzysztof Helt8503df62007-10-16 01:29:08 -07001034 DPRINTK("CRT10: %ld\n", regs.VertSyncStart & 0xff);
1035 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, regs.VertSyncStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
Krzysztof Helt8503df62007-10-16 01:29:08 -07001037 DPRINTK("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
1038 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, regs.VertSyncEnd % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039
Krzysztof Helt8503df62007-10-16 01:29:08 -07001040 DPRINTK("CRT12: %ld\n", regs.VertDispEnd & 0xff);
1041 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, regs.VertDispEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042
Krzysztof Helt8503df62007-10-16 01:29:08 -07001043 DPRINTK("CRT15: %ld\n", regs.VertBlankStart & 0xff);
1044 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, regs.VertBlankStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046 DPRINTK("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
1047 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, regs.VertBlankEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Krzysztof Helt8503df62007-10-16 01:29:08 -07001049 DPRINTK("CRT18: 0xff\n");
1050 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
1052 tmp = 0;
1053 if (var->vmode & FB_VMODE_INTERLACED)
1054 tmp |= 1;
1055 if (regs.HorizBlankEnd & 64)
1056 tmp |= 16;
1057 if (regs.HorizBlankEnd & 128)
1058 tmp |= 32;
1059 if (regs.VertBlankEnd & 256)
1060 tmp |= 64;
1061 if (regs.VertBlankEnd & 512)
1062 tmp |= 128;
1063
Krzysztof Helt8503df62007-10-16 01:29:08 -07001064 DPRINTK("CRT1a: %d\n", tmp);
1065 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
1067 /* set VCLK0 */
1068 /* hardware RefClock: 14.31818 MHz */
1069 /* formula: VClk = (OSC * N) / (D * (1+P)) */
1070 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
1071
Krzysztof Helt8503df62007-10-16 01:29:08 -07001072 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 tmp = regs.den << 1;
1074 if (regs.div != 0)
1075 tmp |= 1;
1076
Krzysztof Helt8503df62007-10-16 01:29:08 -07001077 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 if ((cinfo->btype == BT_SD64) ||
1079 (cinfo->btype == BT_ALPINE) ||
1080 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001081 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
Krzysztof Helt8503df62007-10-16 01:29:08 -07001083 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
1084 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
1086 if (regs.VertRes >= 1024)
1087 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001088 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 else
1090 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
1091 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001092 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
Krzysztof Helt8503df62007-10-16 01:29:08 -07001094/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
1095 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
1097 /* don't know if it would hurt to also program this if no interlaced */
1098 /* mode is used, but I feel better this way.. :-) */
1099 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001100 vga_wcrt(regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001102 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103
Krzysztof Helt8503df62007-10-16 01:29:08 -07001104 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
1106 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001107 /* enable display memory & CRTC I/O address for color mode */
1108 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1110 tmp |= 0x40;
1111 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1112 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001113 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114
Krzysztof Helt8503df62007-10-16 01:29:08 -07001115 /* Screen A Preset Row-Scan register */
1116 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
1117 /* text cursor on and start line */
1118 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
1119 /* text cursor end line */
1120 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121
1122 /******************************************************
1123 *
1124 * 1 bpp
1125 *
1126 */
1127
1128 /* programming for different color depths */
1129 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001130 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
1131 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132
1133 /* SR07 */
1134 switch (cinfo->btype) {
1135 case BT_SD64:
1136 case BT_PICCOLO:
1137 case BT_PICASSO:
1138 case BT_SPECTRUM:
1139 case BT_PICASSO4:
1140 case BT_ALPINE:
1141 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001142 DPRINTK(" (for GD54xx)\n");
1143 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 regs.multiplexing ?
1145 bi->sr07_1bpp_mux : bi->sr07_1bpp);
1146 break;
1147
1148 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001149 DPRINTK(" (for GD546x)\n");
1150 vga_wseq(regbase, CL_SEQR7,
1151 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 break;
1153
1154 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 break;
1157 }
1158
1159 /* Extended Sequencer Mode */
1160 switch (cinfo->btype) {
1161 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001162 /* setting the SEQRF on SD64 is not necessary
1163 * (only during init)
1164 */
1165 DPRINTK("(for SD64)\n");
1166 /* MCLK select */
1167 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 break;
1169
1170 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001171 case BT_SPECTRUM:
1172 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001173 /* ### ueberall 0x22? */
1174 /* ##vorher 1c MCLK select */
1175 vga_wseq(regbase, CL_SEQR1F, 0x22);
1176 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1177 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 break;
1179
1180 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001181 DPRINTK("(for Picasso)\n");
1182 /* ##vorher 22 MCLK select */
1183 vga_wseq(regbase, CL_SEQR1F, 0x22);
1184 /* ## vorher d0 avoid FIFO underruns..? */
1185 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 break;
1187
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 case BT_PICASSO4:
1189 case BT_ALPINE:
1190 case BT_GD5480:
1191 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001192 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 /* do nothing */
1194 break;
1195
1196 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001197 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 break;
1199 }
1200
Krzysztof Helt8503df62007-10-16 01:29:08 -07001201 /* pixel mask: pass-through for first plane */
1202 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001204 /* hidden dac reg: 1280x1024 */
1205 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001207 /* hidden dac: nothing */
1208 WHDR(cinfo, 0);
1209 /* memory mode: odd/even, ext. memory */
1210 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1211 /* plane mask: only write to first plane */
1212 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 offset = var->xres_virtual / 16;
1214 }
1215
1216 /******************************************************
1217 *
1218 * 8 bpp
1219 *
1220 */
1221
1222 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001223 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 switch (cinfo->btype) {
1225 case BT_SD64:
1226 case BT_PICCOLO:
1227 case BT_PICASSO:
1228 case BT_SPECTRUM:
1229 case BT_PICASSO4:
1230 case BT_ALPINE:
1231 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001232 DPRINTK(" (for GD54xx)\n");
1233 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 regs.multiplexing ?
1235 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1236 break;
1237
1238 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001239 DPRINTK(" (for GD546x)\n");
1240 vga_wseq(regbase, CL_SEQR7,
1241 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 break;
1243
1244 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001245 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 break;
1247 }
1248
1249 switch (cinfo->btype) {
1250 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001251 /* MCLK select */
1252 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 break;
1254
1255 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001258 /* ### vorher 1c MCLK select */
1259 vga_wseq(regbase, CL_SEQR1F, 0x22);
1260 /* Fast Page-Mode writes */
1261 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 break;
1263
1264 case BT_PICASSO4:
1265#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001266 /* ### INCOMPLETE!! */
1267 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001269/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 break;
1271
1272 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001273 DPRINTK(" (for GD543x)\n");
1274 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 /* We already set SRF and SR1F */
1276 break;
1277
1278 case BT_GD5480:
1279 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001280 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 /* do nothing */
1282 break;
1283
1284 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001285 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 break;
1287 }
1288
Krzysztof Helt8503df62007-10-16 01:29:08 -07001289 /* mode register: 256 color mode */
1290 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1291 /* pixel mask: pass-through all planes */
1292 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001294 /* hidden dac reg: 1280x1024 */
1295 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001297 /* hidden dac: nothing */
1298 WHDR(cinfo, 0);
1299 /* memory mode: chain4, ext. memory */
1300 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1301 /* plane mask: enable writing to all 4 planes */
1302 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 offset = var->xres_virtual / 8;
1304 }
1305
1306 /******************************************************
1307 *
1308 * 16 bpp
1309 *
1310 */
1311
1312 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 switch (cinfo->btype) {
1315 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001316 /* Extended Sequencer Mode: 256c col. mode */
1317 vga_wseq(regbase, CL_SEQR7, 0xf7);
1318 /* MCLK select */
1319 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 break;
1321
1322 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001323 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001324 vga_wseq(regbase, CL_SEQR7, 0x87);
1325 /* Fast Page-Mode writes */
1326 vga_wseq(regbase, CL_SEQRF, 0xb0);
1327 /* MCLK select */
1328 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 break;
1330
1331 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332 vga_wseq(regbase, CL_SEQR7, 0x27);
1333 /* Fast Page-Mode writes */
1334 vga_wseq(regbase, CL_SEQRF, 0xb0);
1335 /* MCLK select */
1336 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 break;
1338
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001340 vga_wseq(regbase, CL_SEQR7, 0x27);
1341/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 break;
1343
1344 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001345 DPRINTK(" (for GD543x)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 if (regs.HorizRes >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001347 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001349 vga_wseq(regbase, CL_SEQR7, 0xa3);
1350 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 break;
1352
1353 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001354 DPRINTK(" (for GD5480)\n");
1355 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 /* We already set SRF and SR1F */
1357 break;
1358
1359 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001360 DPRINTK(" (for GD546x)\n");
1361 vga_wseq(regbase, CL_SEQR7,
1362 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 break;
1364
1365 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 break;
1368 }
1369
Krzysztof Helt8503df62007-10-16 01:29:08 -07001370 /* mode register: 256 color mode */
1371 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1372 /* pixel mask: pass-through all planes */
1373 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001375 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376#elif defined(CONFIG_ZORRO)
1377 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001378 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001380 /* memory mode: chain4, ext. memory */
1381 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1382 /* plane mask: enable writing to all 4 planes */
1383 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 offset = var->xres_virtual / 4;
1385 }
1386
1387 /******************************************************
1388 *
1389 * 32 bpp
1390 *
1391 */
1392
1393 else if (var->bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001394 DPRINTK("cirrusfb: preparing for 24/32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 switch (cinfo->btype) {
1396 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 /* Extended Sequencer Mode: 256c col. mode */
1398 vga_wseq(regbase, CL_SEQR7, 0xf9);
1399 /* MCLK select */
1400 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 break;
1402
1403 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001404 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 vga_wseq(regbase, CL_SEQR7, 0x85);
1406 /* Fast Page-Mode writes */
1407 vga_wseq(regbase, CL_SEQRF, 0xb0);
1408 /* MCLK select */
1409 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 break;
1411
1412 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001413 vga_wseq(regbase, CL_SEQR7, 0x25);
1414 /* Fast Page-Mode writes */
1415 vga_wseq(regbase, CL_SEQRF, 0xb0);
1416 /* MCLK select */
1417 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 break;
1419
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001421 vga_wseq(regbase, CL_SEQR7, 0x25);
1422/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 break;
1424
1425 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001426 DPRINTK(" (for GD543x)\n");
1427 vga_wseq(regbase, CL_SEQR7, 0xa9);
1428 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 break;
1430
1431 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001432 DPRINTK(" (for GD5480)\n");
1433 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 /* We already set SRF and SR1F */
1435 break;
1436
1437 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001438 DPRINTK(" (for GD546x)\n");
1439 vga_wseq(regbase, CL_SEQR7,
1440 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 break;
1442
1443 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001444 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 break;
1446 }
1447
Krzysztof Helt8503df62007-10-16 01:29:08 -07001448 /* mode register: 256 color mode */
1449 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1450 /* pixel mask: pass-through all planes */
1451 WGen(cinfo, VGA_PEL_MSK, 0xff);
1452 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1453 WHDR(cinfo, 0xc5);
1454 /* memory mode: chain4, ext. memory */
1455 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1456 /* plane mask: enable writing to all 4 planes */
1457 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 offset = var->xres_virtual / 4;
1459 }
1460
1461 /******************************************************
1462 *
1463 * unknown/unsupported bpp
1464 *
1465 */
1466
Krzysztof Helt8503df62007-10-16 01:29:08 -07001467 else
1468 printk(KERN_ERR "cirrusfb: What's this?? "
1469 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
Krzysztof Helt8503df62007-10-16 01:29:08 -07001472 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 tmp = 0x22;
1474 if (offset & 0x100)
1475 tmp |= 0x10; /* offset overflow bit */
1476
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 /* screen start addr #16-18, fastpagemode cycles */
1478 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479
1480 if (cinfo->btype == BT_SD64 ||
1481 cinfo->btype == BT_PICASSO4 ||
1482 cinfo->btype == BT_ALPINE ||
1483 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001484 /* screen start address bit 19 */
1485 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
Krzysztof Helt8503df62007-10-16 01:29:08 -07001487 /* text cursor location high */
1488 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1489 /* text cursor location low */
1490 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1491 /* underline row scanline = at very bottom */
1492 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493
Krzysztof Helt8503df62007-10-16 01:29:08 -07001494 /* controller mode */
1495 vga_wattr(regbase, VGA_ATC_MODE, 1);
1496 /* overscan (border) color */
1497 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1498 /* color plane enable */
1499 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1500 /* pixel panning */
1501 vga_wattr(regbase, CL_AR33, 0);
1502 /* color select */
1503 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 /* [ EGS: SetOffset(); ] */
1506 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001507 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
Krzysztof Helt8503df62007-10-16 01:29:08 -07001509 /* set/reset register */
1510 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1511 /* set/reset enable */
1512 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1513 /* color compare */
1514 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1515 /* data rotate */
1516 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1517 /* read map select */
1518 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1519 /* miscellaneous register */
1520 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1521 /* color don't care */
1522 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1523 /* bit mask */
1524 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
Krzysztof Helt8503df62007-10-16 01:29:08 -07001526 /* graphics cursor attributes: nothing special */
1527 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
1529 /* finally, turn on everything - turn off "FullBandwidth" bit */
1530 /* also, set "DotClock%2" bit where requested */
1531 tmp = 0x01;
1532
1533/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1534 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1535 tmp |= 0x08;
1536*/
1537
Krzysztof Helt8503df62007-10-16 01:29:08 -07001538 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1539 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540
1541 cinfo->currentmode = regs;
1542 info->fix.type = regs.type;
1543 info->fix.visual = regs.visual;
1544 info->fix.line_length = regs.line_length;
1545
1546 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001547 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548
1549#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551#endif
1552
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 return 0;
1555}
1556
1557/* for some reason incomprehensible to me, cirrusfb requires that you write
1558 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001559static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001561 cirrusfb_set_par_foo(info);
1562 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563}
1564
Krzysztof Helt8503df62007-10-16 01:29:08 -07001565static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1566 unsigned blue, unsigned transp,
1567 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568{
1569 struct cirrusfb_info *cinfo = info->par;
1570
1571 if (regno > 255)
1572 return -EINVAL;
1573
1574 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1575 u32 v;
1576 red >>= (16 - info->var.red.length);
1577 green >>= (16 - info->var.green.length);
1578 blue >>= (16 - info->var.blue.length);
1579
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 return 1;
1582 v = (red << info->var.red.offset) |
1583 (green << info->var.green.offset) |
1584 (blue << info->var.blue.offset);
1585
Krzysztof Helt060b6002007-10-16 01:29:13 -07001586 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 return 0;
1588 }
1589
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 if (info->var.bits_per_pixel == 8)
1591 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 return 0;
1594
1595}
1596
1597/*************************************************************************
1598 cirrusfb_pan_display()
1599
1600 performs display panning - provided hardware permits this
1601**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001602static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1603 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604{
1605 int xoffset = 0;
1606 int yoffset = 0;
1607 unsigned long base;
1608 unsigned char tmp = 0, tmp2 = 0, xpix;
1609 struct cirrusfb_info *cinfo = info->par;
1610
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 DPRINTK("ENTER\n");
1612 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
1614 /* no range checks for xoffset and yoffset, */
1615 /* as fb_pan_display has already done this */
1616 if (var->vmode & FB_VMODE_YWRAP)
1617 return -EINVAL;
1618
1619 info->var.xoffset = var->xoffset;
1620 info->var.yoffset = var->yoffset;
1621
1622 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1623 yoffset = var->yoffset;
1624
1625 base = yoffset * cinfo->currentmode.line_length + xoffset;
1626
1627 if (info->var.bits_per_pixel == 1) {
1628 /* base is already correct */
1629 xpix = (unsigned char) (var->xoffset % 8);
1630 } else {
1631 base /= 4;
1632 xpix = (unsigned char) ((xoffset % 4) * 2);
1633 }
1634
Krzysztof Helt8503df62007-10-16 01:29:08 -07001635 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636
1637 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001638 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1639 (unsigned char) (base & 0xff));
1640 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1641 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
1643 /* construct bits 16, 17 and 18 of screen start address */
1644 if (base & 0x10000)
1645 tmp |= 0x01;
1646 if (base & 0x20000)
1647 tmp |= 0x04;
1648 if (base & 0x40000)
1649 tmp |= 0x08;
1650
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 /* 0xf2 is %11110010, exclude tmp bits */
1652 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1653 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
1655 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001656 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1657 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 /* write pixel panning value to AR33; this does not quite work in 8bpp
1660 *
1661 * ### Piccolo..? Will this work?
1662 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
Krzysztof Helt8503df62007-10-16 01:29:08 -07001666 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
Krzysztof Helt8503df62007-10-16 01:29:08 -07001668 DPRINTK("EXIT\n");
1669 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670}
1671
Krzysztof Helt8503df62007-10-16 01:29:08 -07001672static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673{
1674 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1676 * then the caller blanks by setting the CLUT (Color Look Up Table)
1677 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1678 * failed due to e.g. a video mode which doesn't support it.
1679 * Implements VESA suspend and powerdown modes on hardware that
1680 * supports disabling hsync/vsync:
1681 * blank_mode == 2: suspend vsync
1682 * blank_mode == 3: suspend hsync
1683 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 */
1685 unsigned char val;
1686 struct cirrusfb_info *cinfo = info->par;
1687 int current_mode = cinfo->blank_mode;
1688
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
1691 if (info->state != FBINFO_STATE_RUNNING ||
1692 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 return 0;
1695 }
1696
1697 /* Undo current */
1698 if (current_mode == FB_BLANK_NORMAL ||
1699 current_mode == FB_BLANK_UNBLANK) {
1700 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1702 /* clear "FullBandwidth" bit */
1703 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 }
1707
1708 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001709 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001711 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1712 /* set "FullBandwidth" bit */
1713 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 }
1715
1716 switch (blank_mode) {
1717 case FB_BLANK_UNBLANK:
1718 case FB_BLANK_NORMAL:
1719 break;
1720 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 break;
1723 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 break;
1726 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001727 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 break;
1729 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001730 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 return 1;
1732 }
1733
1734 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001735 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736
1737 /* Let fbcon do a soft blank for us */
1738 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1739}
1740/**** END Hardware specific Routines **************************************/
1741/****************************************************************************/
1742/**** BEGIN Internal Routines ***********************************************/
1743
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001744static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001746 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 const struct cirrusfb_board_info_rec *bi;
1748
Krzysztof Helt8503df62007-10-16 01:29:08 -07001749 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752
1753 bi = &cirrusfb_board_info[cinfo->btype];
1754
1755 /* reset board globally */
1756 switch (cinfo->btype) {
1757 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001758 WSFR(cinfo, 0x01);
1759 udelay(500);
1760 WSFR(cinfo, 0x51);
1761 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 break;
1763 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001764 WSFR2(cinfo, 0xff);
1765 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 break;
1767 case BT_SD64:
1768 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001769 WSFR(cinfo, 0x1f);
1770 udelay(500);
1771 WSFR(cinfo, 0x4f);
1772 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 break;
1774 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001775 /* disable flickerfixer */
1776 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1777 mdelay(100);
1778 /* from Klaus' NetBSD driver: */
1779 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1780 /* put blitter into 542x compat */
1781 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1782 /* mode */
1783 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 break;
1785
1786 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001787 /* from Klaus' NetBSD driver: */
1788 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 break;
1790
1791 case BT_ALPINE:
1792 /* Nothing to do to reset the board. */
1793 break;
1794
1795 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 break;
1798 }
1799
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001800 /* make sure RAM size set by this point */
1801 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802
1803 /* the P4 is not fully initialized here; I rely on it having been */
1804 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001805 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
1807 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001808 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1809 WGen(cinfo, CL_POS102, 0x01);
1810 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811
1812 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001813 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
Krzysztof Helt8503df62007-10-16 01:29:08 -07001815 /* reset sequencer logic */
1816 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817
Krzysztof Helt8503df62007-10-16 01:29:08 -07001818 /* FullBandwidth (video off) and 8/9 dot clock */
1819 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1820 /* polarity (-/-), disable access to display memory,
1821 * VGA_CRTC_START_HI base address: color
1822 */
1823 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824
Krzysztof Helt8503df62007-10-16 01:29:08 -07001825 /* "magic cookie" - doesn't make any sense to me.. */
1826/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1827 /* unlock all extension registers */
1828 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829
Krzysztof Helt8503df62007-10-16 01:29:08 -07001830 /* reset blitter */
1831 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832
1833 switch (cinfo->btype) {
1834 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001835 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836 break;
1837 case BT_ALPINE:
1838 break;
1839 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001840 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841 break;
1842 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001843 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1844 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 break;
1846 }
1847 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848 /* plane mask: nothing */
1849 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1850 /* character map select: doesn't even matter in gx mode */
1851 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1852 /* memory mode: chain-4, no odd/even, ext. memory */
1853 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854
1855 /* controller-internal base address of video memory */
1856 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001857 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858
Krzysztof Helt8503df62007-10-16 01:29:08 -07001859 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1860 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
Krzysztof Helt8503df62007-10-16 01:29:08 -07001862 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1863 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1864 /* graphics cursor Y position (..."... ) */
1865 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1866 /* graphics cursor attributes */
1867 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1868 /* graphics cursor pattern address */
1869 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
1871 /* writing these on a P4 might give problems.. */
1872 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001873 /* configuration readback and ext. color */
1874 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1875 /* signature generator */
1876 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 }
1878
1879 /* MCLK select etc. */
1880 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001881 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882
Krzysztof Helt8503df62007-10-16 01:29:08 -07001883 /* Screen A preset row scan: none */
1884 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1885 /* Text cursor start: disable text cursor */
1886 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1887 /* Text cursor end: - */
1888 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1889 /* Screen start address high: 0 */
1890 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1891 /* Screen start address low: 0 */
1892 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1893 /* text cursor location high: 0 */
1894 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1895 /* text cursor location low: 0 */
1896 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897
Krzysztof Helt8503df62007-10-16 01:29:08 -07001898 /* Underline Row scanline: - */
1899 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1900 /* mode control: timing enable, byte mode, no compat modes */
1901 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1902 /* Line Compare: not needed */
1903 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001905 /* ext. display controls: ext.adr. wrap */
1906 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907
Krzysztof Helt8503df62007-10-16 01:29:08 -07001908 /* Set/Reset registes: - */
1909 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1910 /* Set/Reset enable: - */
1911 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1912 /* Color Compare: - */
1913 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1914 /* Data Rotate: - */
1915 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1916 /* Read Map Select: - */
1917 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1918 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1919 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1920 /* Miscellaneous: memory map base address, graphics mode */
1921 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1922 /* Color Don't care: involve all planes */
1923 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1924 /* Bit Mask: no mask at all */
1925 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001927 /* (5434 can't have bit 3 set for bitblt) */
1928 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001930 /* Graphics controller mode extensions: finer granularity,
1931 * 8byte data latches
1932 */
1933 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934
Krzysztof Helt8503df62007-10-16 01:29:08 -07001935 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1936 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1937 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1938 /* Background color byte 1: - */
1939 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1940 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941
Krzysztof Helt8503df62007-10-16 01:29:08 -07001942 /* Attribute Controller palette registers: "identity mapping" */
1943 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1944 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1945 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1946 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1947 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1948 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1949 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1950 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1951 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1952 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1953 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1954 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1955 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1956 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1957 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1958 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
Krzysztof Helt8503df62007-10-16 01:29:08 -07001960 /* Attribute Controller mode: graphics mode */
1961 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1962 /* Overscan color reg.: reg. 0 */
1963 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1964 /* Color Plane enable: Enable all 4 planes */
1965 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1966/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1967 /* Color Select: - */
1968 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
Krzysztof Helt8503df62007-10-16 01:29:08 -07001970 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971
1972 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001973 /* polarity (-/-), enable display mem,
1974 * VGA_CRTC_START_HI i/o base = color
1975 */
1976 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977
Krzysztof Helt8503df62007-10-16 01:29:08 -07001978 /* BLT Start/status: Blitter reset */
1979 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1980 /* - " - : "end-of-reset" */
1981 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982
1983 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001984 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985
Krzysztof Helt8503df62007-10-16 01:29:08 -07001986 printk(KERN_DEBUG "cirrusfb: This board has %ld bytes of DRAM memory\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001987 info->screen_size);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001988 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 return;
1990}
1991
Krzysztof Helt8503df62007-10-16 01:29:08 -07001992static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993{
1994#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1995 static int IsOn = 0; /* XXX not ok for multiple boards */
1996
Krzysztof Helt8503df62007-10-16 01:29:08 -07001997 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998
1999 if (cinfo->btype == BT_PICASSO4)
2000 return; /* nothing to switch */
2001 if (cinfo->btype == BT_ALPINE)
2002 return; /* nothing to switch */
2003 if (cinfo->btype == BT_GD5480)
2004 return; /* nothing to switch */
2005 if (cinfo->btype == BT_PICASSO) {
2006 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07002007 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008
Krzysztof Helt8503df62007-10-16 01:29:08 -07002009 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 return;
2011 }
2012 if (on) {
2013 switch (cinfo->btype) {
2014 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002015 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 break;
2017 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002018 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 break;
2020 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002021 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022 break;
2023 default: /* do nothing */ break;
2024 }
2025 } else {
2026 switch (cinfo->btype) {
2027 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002028 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 break;
2030 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002031 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 break;
2033 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002034 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 break;
2036 default: /* do nothing */ break;
2037 }
2038 }
2039
Krzysztof Helt8503df62007-10-16 01:29:08 -07002040 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041#endif /* CONFIG_ZORRO */
2042}
2043
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044/******************************************/
2045/* Linux 2.6-style accelerated functions */
2046/******************************************/
2047
Krzysztof Helt8503df62007-10-16 01:29:08 -07002048static void cirrusfb_fillrect(struct fb_info *info,
2049 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 struct fb_fillrect modded;
2052 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002053 struct cirrusfb_info *cinfo = info->par;
2054 int m = info->var.bits_per_pixel;
2055 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
2056 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057
2058 if (info->state != FBINFO_STATE_RUNNING)
2059 return;
2060 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2061 cfb_fillrect(info, region);
2062 return;
2063 }
2064
2065 vxres = info->var.xres_virtual;
2066 vyres = info->var.yres_virtual;
2067
2068 memcpy(&modded, region, sizeof(struct fb_fillrect));
2069
Krzysztof Helt8503df62007-10-16 01:29:08 -07002070 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 modded.dx >= vxres || modded.dy >= vyres)
2072 return;
2073
Krzysztof Helt8503df62007-10-16 01:29:08 -07002074 if (modded.dx + modded.width > vxres)
2075 modded.width = vxres - modded.dx;
2076 if (modded.dy + modded.height > vyres)
2077 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078
Krzysztof Helt060b6002007-10-16 01:29:13 -07002079 cirrusfb_RectFill(cinfo->regbase,
2080 info->var.bits_per_pixel,
2081 (region->dx * m) / 8, region->dy,
2082 (region->width * m) / 8, region->height,
2083 color,
2084 cinfo->currentmode.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085}
2086
Krzysztof Helt8503df62007-10-16 01:29:08 -07002087static void cirrusfb_copyarea(struct fb_info *info,
2088 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 struct fb_copyarea modded;
2091 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002092 struct cirrusfb_info *cinfo = info->par;
2093 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094
2095 if (info->state != FBINFO_STATE_RUNNING)
2096 return;
2097 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2098 cfb_copyarea(info, area);
2099 return;
2100 }
2101
2102 vxres = info->var.xres_virtual;
2103 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07002104 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105
Krzysztof Helt8503df62007-10-16 01:29:08 -07002106 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 modded.sx >= vxres || modded.sy >= vyres ||
2108 modded.dx >= vxres || modded.dy >= vyres)
2109 return;
2110
Krzysztof Helt8503df62007-10-16 01:29:08 -07002111 if (modded.sx + modded.width > vxres)
2112 modded.width = vxres - modded.sx;
2113 if (modded.dx + modded.width > vxres)
2114 modded.width = vxres - modded.dx;
2115 if (modded.sy + modded.height > vyres)
2116 modded.height = vyres - modded.sy;
2117 if (modded.dy + modded.height > vyres)
2118 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
Krzysztof Helt060b6002007-10-16 01:29:13 -07002120 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
2121 (area->sx * m) / 8, area->sy,
2122 (area->dx * m) / 8, area->dy,
2123 (area->width * m) / 8, area->height,
2124 cinfo->currentmode.line_length);
2125
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126}
2127
Krzysztof Helt8503df62007-10-16 01:29:08 -07002128static void cirrusfb_imageblit(struct fb_info *info,
2129 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130{
2131 struct cirrusfb_info *cinfo = info->par;
2132
Krzysztof Helt8503df62007-10-16 01:29:08 -07002133 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 cfb_imageblit(info, image);
2135}
2136
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137#ifdef CONFIG_PPC_PREP
2138#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
2139#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002140static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002142 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143
2144 *display = PREP_VIDEO_BASE;
2145 *registers = (unsigned long) PREP_IO_BASE;
2146
Krzysztof Helt8503df62007-10-16 01:29:08 -07002147 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148}
2149
2150#endif /* CONFIG_PPC_PREP */
2151
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
2155/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
2156 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
2157 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
2158 * seem to have. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002159static unsigned int cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160{
2161 unsigned long mem;
2162 unsigned char SRF;
2163
Krzysztof Helt8503df62007-10-16 01:29:08 -07002164 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165
Krzysztof Helt8503df62007-10-16 01:29:08 -07002166 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002168 case 0x08:
2169 mem = 512 * 1024;
2170 break;
2171 case 0x10:
2172 mem = 1024 * 1024;
2173 break;
2174 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2175 * on the 5430.
2176 */
2177 case 0x18:
2178 mem = 2048 * 1024;
2179 break;
2180 default:
2181 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 mem = 1024 * 1024;
2183 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002184 if (SRF & 0x80)
2185 /* If DRAM bank switching is enabled, there must be twice as much
2186 * memory installed. (4MB on the 5434)
2187 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002189
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2191
Krzysztof Helt8503df62007-10-16 01:29:08 -07002192 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 return mem;
2194}
2195
Krzysztof Helt8503df62007-10-16 01:29:08 -07002196static void get_pci_addrs(const struct pci_dev *pdev,
2197 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002199 assert(pdev != NULL);
2200 assert(display != NULL);
2201 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202
Krzysztof Helt8503df62007-10-16 01:29:08 -07002203 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204
2205 *display = 0;
2206 *registers = 0;
2207
2208 /* This is a best-guess for now */
2209
2210 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2211 *display = pci_resource_start(pdev, 1);
2212 *registers = pci_resource_start(pdev, 0);
2213 } else {
2214 *display = pci_resource_start(pdev, 0);
2215 *registers = pci_resource_start(pdev, 1);
2216 }
2217
Krzysztof Helt8503df62007-10-16 01:29:08 -07002218 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219
Krzysztof Helt8503df62007-10-16 01:29:08 -07002220 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221}
2222
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002223static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002225 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226 struct pci_dev *pdev = cinfo->pdev;
2227
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002228 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229#if 0 /* if system didn't claim this region, we would... */
2230 release_mem_region(0xA0000, 65535);
2231#endif
2232 if (release_io_ports)
2233 release_region(0x3C0, 32);
2234 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235}
2236#endif /* CONFIG_PCI */
2237
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002239static void __devexit cirrusfb_zorro_unmap(struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240{
2241 zorro_release_device(cinfo->zdev);
2242
2243 if (cinfo->btype == BT_PICASSO4) {
2244 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002245 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002246 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 } else {
2248 if (zorro_resource_start(cinfo->zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002249 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251}
2252#endif /* CONFIG_ZORRO */
2253
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002254static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002256 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 struct fb_var_screeninfo *var = &info->var;
2258
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 info->pseudo_palette = cinfo->pseudo_palette;
2260 info->flags = FBINFO_DEFAULT
2261 | FBINFO_HWACCEL_XPAN
2262 | FBINFO_HWACCEL_YPAN
2263 | FBINFO_HWACCEL_FILLRECT
2264 | FBINFO_HWACCEL_COPYAREA;
2265 if (noaccel)
2266 info->flags |= FBINFO_HWACCEL_DISABLED;
2267 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 if (cinfo->btype == BT_GD5480) {
2269 if (var->bits_per_pixel == 16)
2270 info->screen_base += 1 * MB_;
2271 if (var->bits_per_pixel == 24 || var->bits_per_pixel == 32)
2272 info->screen_base += 2 * MB_;
2273 }
2274
2275 /* Fill fix common fields */
2276 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2277 sizeof(info->fix.id));
2278
2279 /* monochrome: only 1 memory plane */
2280 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002281 info->fix.smem_len = info->screen_size;
2282 if (var->bits_per_pixel == 1)
2283 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 info->fix.type = cinfo->currentmode.type;
2285 info->fix.type_aux = 0;
2286 info->fix.visual = cinfo->currentmode.visual;
2287 info->fix.xpanstep = 1;
2288 info->fix.ypanstep = 1;
2289 info->fix.ywrapstep = 0;
2290 info->fix.line_length = cinfo->currentmode.line_length;
2291
2292 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 info->fix.mmio_len = 0;
2294 info->fix.accel = FB_ACCEL_NONE;
2295
2296 fb_alloc_cmap(&info->cmap, 256, 0);
2297
2298 return 0;
2299}
2300
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002301static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002303 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002305 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306
Krzysztof Helt8503df62007-10-16 01:29:08 -07002307 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308
Krzysztof Helt8503df62007-10-16 01:29:08 -07002309 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2310 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 btype = cinfo->btype;
2313
2314 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002315 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002317 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
2319 /* Make pretend we've set the var so our structures are in a "good" */
2320 /* state, even though we haven't written the mode to the hw yet... */
2321 info->var = cirrusfb_predefined[cirrusfb_def_mode].var;
2322 info->var.activate = FB_ACTIVATE_NOW;
2323
2324 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2325 if (err < 0) {
2326 /* should never happen */
2327 DPRINTK("choking on default var... umm, no good.\n");
2328 goto err_unmap_cirrusfb;
2329 }
2330
2331 /* set all the vital stuff */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002332 cirrusfb_set_fbinfo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333
2334 err = register_framebuffer(info);
2335 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002336 printk(KERN_ERR "cirrusfb: could not register "
2337 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 goto err_dealloc_cmap;
2339 }
2340
Krzysztof Helt8503df62007-10-16 01:29:08 -07002341 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342 return 0;
2343
2344err_dealloc_cmap:
2345 fb_dealloc_cmap(&info->cmap);
2346err_unmap_cirrusfb:
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002347 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002348 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 return err;
2350}
2351
Krzysztof Helt8503df62007-10-16 01:29:08 -07002352static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353{
2354 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002355 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
Krzysztof Helt8503df62007-10-16 01:29:08 -07002357 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358
Krzysztof Helt8503df62007-10-16 01:29:08 -07002359 unregister_framebuffer(info);
2360 fb_dealloc_cmap(&info->cmap);
2361 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002362 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002363 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364
Krzysztof Helt8503df62007-10-16 01:29:08 -07002365 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366}
2367
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002369static int cirrusfb_pci_register(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 const struct pci_device_id *ent)
2371{
2372 struct cirrusfb_info *cinfo;
2373 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002374 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375 unsigned long board_addr, board_size;
2376 int ret;
2377
2378 ret = pci_enable_device(pdev);
2379 if (ret < 0) {
2380 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2381 goto err_out;
2382 }
2383
2384 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2385 if (!info) {
2386 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2387 ret = -ENOMEM;
2388 goto err_disable;
2389 }
2390
2391 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 cinfo->pdev = pdev;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002393 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394
Krzysztof Helt7345de32007-10-16 01:29:11 -07002395 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002397 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398
Krzysztof Helt8503df62007-10-16 01:29:08 -07002399 if (isPReP) {
2400 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002402 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002404 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002405 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002407 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002408 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002409 /* FIXME: this forces VGA. alternatives? */
2410 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411 }
2412
Krzysztof Helt8503df62007-10-16 01:29:08 -07002413 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002414 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415
2416 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002417 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418
2419 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002420 if (ret < 0) {
2421 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2422 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 board_addr);
2424 goto err_release_fb;
2425 }
2426#if 0 /* if the system didn't claim this region, we would... */
2427 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2428 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2429,
2430 0xA0000L);
2431 ret = -EBUSY;
2432 goto err_release_regions;
2433 }
2434#endif
2435 if (request_region(0x3C0, 32, "cirrusfb"))
2436 release_io_ports = 1;
2437
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002438 info->screen_base = ioremap(board_addr, board_size);
2439 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 ret = -EIO;
2441 goto err_release_legacy;
2442 }
2443
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002444 info->fix.smem_start = board_addr;
2445 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446 cinfo->unmap = cirrusfb_pci_unmap;
2447
Krzysztof Helt8503df62007-10-16 01:29:08 -07002448 printk(KERN_INFO " RAM (%lu kB) at 0xx%lx, ",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002449 info->screen_size / KB_, board_addr);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002450 printk(KERN_INFO "Cirrus Logic chipset on PCI bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 pci_set_drvdata(pdev, info);
2452
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002453 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002454 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002455 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002456 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457
2458err_release_legacy:
2459 if (release_io_ports)
2460 release_region(0x3C0, 32);
2461#if 0
2462 release_mem_region(0xA0000, 65535);
2463err_release_regions:
2464#endif
2465 pci_release_regions(pdev);
2466err_release_fb:
2467 framebuffer_release(info);
2468err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469err_out:
2470 return ret;
2471}
2472
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474{
2475 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477
Krzysztof Helt8503df62007-10-16 01:29:08 -07002478 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481}
2482
2483static struct pci_driver cirrusfb_pci_driver = {
2484 .name = "cirrusfb",
2485 .id_table = cirrusfb_pci_table,
2486 .probe = cirrusfb_pci_register,
2487 .remove = __devexit_p(cirrusfb_pci_unregister),
2488#ifdef CONFIG_PM
2489#if 0
2490 .suspend = cirrusfb_pci_suspend,
2491 .resume = cirrusfb_pci_resume,
2492#endif
2493#endif
2494};
2495#endif /* CONFIG_PCI */
2496
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497#ifdef CONFIG_ZORRO
2498static int cirrusfb_zorro_register(struct zorro_dev *z,
2499 const struct zorro_device_id *ent)
2500{
2501 struct cirrusfb_info *cinfo;
2502 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002503 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 struct zorro_dev *z2 = NULL;
2505 unsigned long board_addr, board_size, size;
2506 int ret;
2507
2508 btype = ent->driver_data;
2509 if (cirrusfb_zorro_table2[btype].id2)
2510 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2511 size = cirrusfb_zorro_table2[btype].size;
2512 printk(KERN_INFO "cirrusfb: %s board detected; ",
2513 cirrusfb_board_info[btype].name);
2514
2515 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2516 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002517 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518 ret = -ENOMEM;
2519 goto err_out;
2520 }
2521
2522 cinfo = info->par;
2523 cinfo->info = info;
2524 cinfo->btype = btype;
2525
Krzysztof Helt8503df62007-10-16 01:29:08 -07002526 assert(z > 0);
2527 assert(z2 >= 0);
2528 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
2530 cinfo->zdev = z;
2531 board_addr = zorro_resource_start(z);
2532 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002533 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534
2535 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002536 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2537 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538 board_addr);
2539 ret = -EBUSY;
2540 goto err_release_fb;
2541 }
2542
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544
2545 ret = -EIO;
2546
2547 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549
2550 /* To be precise, for the P4 this is not the */
2551 /* begin of the board, but the begin of RAM. */
2552 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2553 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555 if (!cinfo->regbase)
2556 goto err_release_region;
2557
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2559 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002561 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002563 info->fix.smem_start = board_addr + 16777216;
2564 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2565 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 goto err_unmap_regbase;
2567 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568 printk(KERN_INFO " REG at $%lx\n",
2569 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002571 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002573 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002575 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2576 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 goto err_release_region;
2578
2579 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002580 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002581 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582
Krzysztof Helt8503df62007-10-16 01:29:08 -07002583 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2584 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 }
2586 cinfo->unmap = cirrusfb_zorro_unmap;
2587
Krzysztof Helt8503df62007-10-16 01:29:08 -07002588 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 zorro_set_drvdata(z, info);
2590
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002591 ret = cirrusfb_register(cinfo);
2592 if (ret) {
2593 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002594 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002595 iounmap(cinfo->regbase - 0x600000);
2596 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002597 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002598 }
2599 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600
2601err_unmap_regbase:
2602 /* Parental advisory: explicit hack */
2603 iounmap(cinfo->regbase - 0x600000);
2604err_release_region:
2605 release_region(board_addr, board_size);
2606err_release_fb:
2607 framebuffer_release(info);
2608err_out:
2609 return ret;
2610}
2611
2612void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2613{
2614 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002615 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618
Krzysztof Helt8503df62007-10-16 01:29:08 -07002619 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620}
2621
2622static struct zorro_driver cirrusfb_zorro_driver = {
2623 .name = "cirrusfb",
2624 .id_table = cirrusfb_zorro_table,
2625 .probe = cirrusfb_zorro_register,
2626 .remove = __devexit_p(cirrusfb_zorro_unregister),
2627};
2628#endif /* CONFIG_ZORRO */
2629
2630static int __init cirrusfb_init(void)
2631{
2632 int error = 0;
2633
2634#ifndef MODULE
2635 char *option = NULL;
2636
2637 if (fb_get_options("cirrusfb", &option))
2638 return -ENODEV;
2639 cirrusfb_setup(option);
2640#endif
2641
2642#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002643 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644#endif
2645#ifdef CONFIG_PCI
2646 error |= pci_register_driver(&cirrusfb_pci_driver);
2647#endif
2648 return error;
2649}
2650
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651#ifndef MODULE
2652static int __init cirrusfb_setup(char *options) {
2653 char *this_opt, s[32];
2654 int i;
2655
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657
2658 if (!options || !*options)
2659 return 0;
2660
Krzysztof Helt8503df62007-10-16 01:29:08 -07002661 while ((this_opt = strsep(&options, ",")) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 if (!*this_opt) continue;
2663
2664 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2665
2666 for (i = 0; i < NUM_TOTAL_MODES; i++) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667 sprintf(s, "mode:%s", cirrusfb_predefined[i].name);
2668 if (strcmp(this_opt, s) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 cirrusfb_def_mode = i;
2670 }
2671 if (!strcmp(this_opt, "noaccel"))
2672 noaccel = 1;
2673 }
2674 return 0;
2675}
2676#endif
2677
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 /*
2679 * Modularization
2680 */
2681
2682MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2683MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2684MODULE_LICENSE("GPL");
2685
Krzysztof Helt8503df62007-10-16 01:29:08 -07002686static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687{
2688#ifdef CONFIG_PCI
2689 pci_unregister_driver(&cirrusfb_pci_driver);
2690#endif
2691#ifdef CONFIG_ZORRO
2692 zorro_unregister_driver(&cirrusfb_zorro_driver);
2693#endif
2694}
2695
2696module_init(cirrusfb_init);
2697
2698#ifdef MODULE
2699module_exit(cirrusfb_exit);
2700#endif
2701
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702/**********************************************************************/
2703/* about the following functions - I have used the same names for the */
2704/* functions as Markus Wild did in his Retina driver for NetBSD as */
2705/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707/**********************************************************************/
2708
2709/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002710static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 int regnum, unsigned char val)
2712{
2713 unsigned long regofs = 0;
2714
2715 if (cinfo->btype == BT_PICASSO) {
2716 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002717/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2718 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2720 regofs = 0xfff;
2721 }
2722
Krzysztof Helt8503df62007-10-16 01:29:08 -07002723 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724}
2725
2726/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002727static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728{
2729 unsigned long regofs = 0;
2730
2731 if (cinfo->btype == BT_PICASSO) {
2732 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002733/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2734 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2736 regofs = 0xfff;
2737 }
2738
Krzysztof Helt8503df62007-10-16 01:29:08 -07002739 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740}
2741
2742/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002743static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002745 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746
Krzysztof Helt8503df62007-10-16 01:29:08 -07002747 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748
Krzysztof Helt8503df62007-10-16 01:29:08 -07002749 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 /* if we're just in "write value" mode, write back the */
2751 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002752 vga_w(cinfo->regbase, VGA_ATT_IW,
2753 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 }
2755 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002756/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2757 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758
2759 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002760 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761
Krzysztof Helt8503df62007-10-16 01:29:08 -07002762 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763}
2764
2765/*** WHDR() - write into the Hidden DAC register ***/
2766/* as the HDR is the only extension register that requires special treatment
2767 * (the other extension registers are accessible just like the "ordinary"
2768 * registers of their functional group) here is a specialized routine for
2769 * accessing the HDR
2770 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002771static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772{
2773 unsigned char dummy;
2774
2775 if (cinfo->btype == BT_PICASSO) {
2776 /* Klaus' hint for correct access to HDR on some boards */
2777 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002778 WGen(cinfo, VGA_PEL_MSK, 0x00);
2779 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002781 dummy = RGen(cinfo, VGA_PEL_IW);
2782 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 }
2784 /* now do the usual stuff to access the HDR */
2785
Krzysztof Helt8503df62007-10-16 01:29:08 -07002786 dummy = RGen(cinfo, VGA_PEL_MSK);
2787 udelay(200);
2788 dummy = RGen(cinfo, VGA_PEL_MSK);
2789 udelay(200);
2790 dummy = RGen(cinfo, VGA_PEL_MSK);
2791 udelay(200);
2792 dummy = RGen(cinfo, VGA_PEL_MSK);
2793 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794
Krzysztof Helt8503df62007-10-16 01:29:08 -07002795 WGen(cinfo, VGA_PEL_MSK, val);
2796 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797
2798 if (cinfo->btype == BT_PICASSO) {
2799 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002800 dummy = RGen(cinfo, VGA_PEL_IW);
2801 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802
2803 /* and at the end, restore the mask value */
2804 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002805 WGen(cinfo, VGA_PEL_MSK, 0xff);
2806 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 }
2808}
2809
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002811static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812{
2813#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002814 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002816 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817#endif
2818}
2819
2820/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002821static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822{
2823#ifdef CONFIG_ZORRO
2824 /* writing an arbitrary value to this one causes the monitor switcher */
2825 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002826 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002828 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829#endif
2830}
2831
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002833static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 unsigned char green, unsigned char blue)
2835{
2836 unsigned int data = VGA_PEL_D;
2837
2838 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002839 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840
2841 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2842 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2843 /* but DAC data register IS, at least for Picasso II */
2844 if (cinfo->btype == BT_PICASSO)
2845 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002846 vga_w(cinfo->regbase, data, red);
2847 vga_w(cinfo->regbase, data, green);
2848 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002850 vga_w(cinfo->regbase, data, blue);
2851 vga_w(cinfo->regbase, data, green);
2852 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853 }
2854}
2855
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856#if 0
2857/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002858static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 unsigned char *green, unsigned char *blue)
2860{
2861 unsigned int data = VGA_PEL_D;
2862
Krzysztof Helt8503df62007-10-16 01:29:08 -07002863 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864
2865 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2866 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2867 if (cinfo->btype == BT_PICASSO)
2868 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002869 *red = vga_r(cinfo->regbase, data);
2870 *green = vga_r(cinfo->regbase, data);
2871 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002873 *blue = vga_r(cinfo->regbase, data);
2874 *green = vga_r(cinfo->regbase, data);
2875 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 }
2877}
2878#endif
2879
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880/*******************************************************************
2881 cirrusfb_WaitBLT()
2882
2883 Wait for the BitBLT engine to complete a possible earlier job
2884*********************************************************************/
2885
2886/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002887static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888{
2889 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002890 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891 /* do nothing */ ;
2892}
2893
2894/*******************************************************************
2895 cirrusfb_BitBLT()
2896
2897 perform accelerated "scrolling"
2898********************************************************************/
2899
Krzysztof Helt8503df62007-10-16 01:29:08 -07002900static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2901 u_short curx, u_short cury,
2902 u_short destx, u_short desty,
2903 u_short width, u_short height,
2904 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905{
2906 u_short nwidth, nheight;
2907 u_long nsrc, ndest;
2908 u_char bltmode;
2909
Krzysztof Helt8503df62007-10-16 01:29:08 -07002910 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911
2912 nwidth = width - 1;
2913 nheight = height - 1;
2914
2915 bltmode = 0x00;
2916 /* if source adr < dest addr, do the Blt backwards */
2917 if (cury <= desty) {
2918 if (cury == desty) {
2919 /* if src and dest are on the same line, check x */
2920 if (curx < destx)
2921 bltmode |= 0x01;
2922 } else
2923 bltmode |= 0x01;
2924 }
2925 if (!bltmode) {
2926 /* standard case: forward blitting */
2927 nsrc = (cury * line_length) + curx;
2928 ndest = (desty * line_length) + destx;
2929 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002930 /* this means start addresses are at the end,
2931 * counting backwards
2932 */
2933 nsrc = cury * line_length + curx +
2934 nheight * line_length + nwidth;
2935 ndest = desty * line_length + destx +
2936 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937 }
2938
2939 /*
2940 run-down of registers to be programmed:
2941 destination pitch
2942 source pitch
2943 BLT width/height
2944 source start
2945 destination start
2946 BLT mode
2947 BLT ROP
2948 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2949 start/stop
2950 */
2951
Krzysztof Helt8503df62007-10-16 01:29:08 -07002952 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953
2954 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002955 /* dest pitch low */
2956 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2957 /* dest pitch hi */
2958 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2959 /* source pitch low */
2960 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2961 /* source pitch hi */
2962 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963
2964 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002965 /* BLT width low */
2966 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2967 /* BLT width hi */
2968 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969
2970 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002971 /* BLT height low */
2972 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2973 /* BLT width hi */
2974 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975
2976 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002977 /* BLT dest low */
2978 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2979 /* BLT dest mid */
2980 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2981 /* BLT dest hi */
2982 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983
2984 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002985 /* BLT src low */
2986 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2987 /* BLT src mid */
2988 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2989 /* BLT src hi */
2990 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002991
2992 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002993 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002994
2995 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002996 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002997
2998 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002999 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003000
Krzysztof Helt8503df62007-10-16 01:29:08 -07003001 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002}
3003
Linus Torvalds1da177e2005-04-16 15:20:36 -07003004/*******************************************************************
3005 cirrusfb_RectFill()
3006
3007 perform accelerated rectangle fill
3008********************************************************************/
3009
Krzysztof Helt8503df62007-10-16 01:29:08 -07003010static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003011 u_short x, u_short y, u_short width, u_short height,
3012 u_char color, u_short line_length)
3013{
3014 u_short nwidth, nheight;
3015 u_long ndest;
3016 u_char op;
3017
Krzysztof Helt8503df62007-10-16 01:29:08 -07003018 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019
3020 nwidth = width - 1;
3021 nheight = height - 1;
3022
3023 ndest = (y * line_length) + x;
3024
Krzysztof Helt8503df62007-10-16 01:29:08 -07003025 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003026
3027 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003028 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
3029 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
3030 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
3031 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003032
3033 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003034 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
3035 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036
3037 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003038 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
3039 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003040
3041 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003042 /* BLT dest low */
3043 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
3044 /* BLT dest mid */
3045 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
3046 /* BLT dest hi */
3047 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048
3049 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003050 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
3051 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
3052 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003053
3054 /* This is a ColorExpand Blt, using the */
3055 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003056 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
3057 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058
3059 op = 0xc0;
3060 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003061 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3062 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003063 op = 0x50;
3064 op = 0xd0;
3065 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003066 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3067 vga_wgfx(regbase, CL_GR11, color); /* background color */
3068 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
3069 vga_wgfx(regbase, CL_GR13, color); /* background color */
3070 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
3071 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003072 op = 0x50;
3073 op = 0xf0;
3074 }
3075 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003076 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003077
3078 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003079 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080
3081 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003082 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083
Krzysztof Helt8503df62007-10-16 01:29:08 -07003084 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003085}
3086
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087/**************************************************************************
3088 * bestclock() - determine closest possible clock lower(?) than the
3089 * desired pixel clock
3090 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07003091static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092 long *den, long *div, long maxfreq)
3093{
3094 long n, h, d, f;
3095
Krzysztof Helt8503df62007-10-16 01:29:08 -07003096 assert(best != NULL);
3097 assert(nom != NULL);
3098 assert(den != NULL);
3099 assert(div != NULL);
3100 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003101
3102 *nom = 0;
3103 *den = 0;
3104 *div = 0;
3105
Krzysztof Helt8503df62007-10-16 01:29:08 -07003106 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107
3108 if (freq < 8000)
3109 freq = 8000;
3110
3111 if (freq > maxfreq)
3112 freq = maxfreq;
3113
3114 *best = 0;
3115 f = freq * 10;
3116
3117 for (n = 32; n < 128; n++) {
3118 d = (143181 * n) / f;
3119 if ((d >= 7) && (d <= 63)) {
3120 if (d > 31)
3121 d = (d / 2) * 2;
3122 h = (14318 * n) / d;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003123 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 *best = h;
3125 *nom = n;
3126 if (d < 32) {
3127 *den = d;
3128 *div = 0;
3129 } else {
3130 *den = d / 2;
3131 *div = 1;
3132 }
3133 }
3134 }
3135 d = ((143181 * n) + f - 1) / f;
3136 if ((d >= 7) && (d <= 63)) {
3137 if (d > 31)
3138 d = (d / 2) * 2;
3139 h = (14318 * n) / d;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003140 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 *best = h;
3142 *nom = n;
3143 if (d < 32) {
3144 *den = d;
3145 *div = 0;
3146 } else {
3147 *den = d / 2;
3148 *div = 1;
3149 }
3150 }
3151 }
3152 }
3153
Krzysztof Helt8503df62007-10-16 01:29:08 -07003154 DPRINTK("Best possible values for given frequency:\n");
3155 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
3156 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157
Krzysztof Helt8503df62007-10-16 01:29:08 -07003158 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003159}
3160
Linus Torvalds1da177e2005-04-16 15:20:36 -07003161/* -------------------------------------------------------------------------
3162 *
3163 * debugging functions
3164 *
3165 * -------------------------------------------------------------------------
3166 */
3167
3168#ifdef CIRRUSFB_DEBUG
3169
3170/**
3171 * cirrusfb_dbg_print_byte
3172 * @name: name associated with byte value to be displayed
3173 * @val: byte value to be displayed
3174 *
3175 * DESCRIPTION:
3176 * Display an indented string, along with a hexidecimal byte value, and
3177 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3178 * order.
3179 */
3180
3181static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003182void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003183{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003184 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3185 name, val,
3186 val & 0x80 ? '1' : '0',
3187 val & 0x40 ? '1' : '0',
3188 val & 0x20 ? '1' : '0',
3189 val & 0x10 ? '1' : '0',
3190 val & 0x08 ? '1' : '0',
3191 val & 0x04 ? '1' : '0',
3192 val & 0x02 ? '1' : '0',
3193 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194}
3195
Linus Torvalds1da177e2005-04-16 15:20:36 -07003196/**
3197 * cirrusfb_dbg_print_regs
3198 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3199 * @reg_class: type of registers to read: %CRT, or %SEQ
3200 *
3201 * DESCRIPTION:
3202 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3203 * old-style I/O ports are queried for information, otherwise MMIO is
3204 * used at the given @base address to query the information.
3205 */
3206
3207static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003208void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003209 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003210{
3211 va_list list;
3212 unsigned char val = 0;
3213 unsigned reg;
3214 char *name;
3215
Krzysztof Helt8503df62007-10-16 01:29:08 -07003216 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217
Krzysztof Helt8503df62007-10-16 01:29:08 -07003218 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003220 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221
3222 switch (reg_class) {
3223 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003224 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003225 break;
3226 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003227 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228 break;
3229 default:
3230 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003231 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003232 break;
3233 }
3234
Krzysztof Helt8503df62007-10-16 01:29:08 -07003235 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236
Krzysztof Helt8503df62007-10-16 01:29:08 -07003237 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238 }
3239
Krzysztof Helt8503df62007-10-16 01:29:08 -07003240 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241}
3242
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243/**
3244 * cirrusfb_dump
3245 * @cirrusfbinfo:
3246 *
3247 * DESCRIPTION:
3248 */
3249
Krzysztof Helt8503df62007-10-16 01:29:08 -07003250static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003251{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003252 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003253}
3254
Linus Torvalds1da177e2005-04-16 15:20:36 -07003255/**
3256 * cirrusfb_dbg_reg_dump
3257 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3258 *
3259 * DESCRIPTION:
3260 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3261 * old-style I/O ports are queried for information, otherwise MMIO is
3262 * used at the given @base address to query the information.
3263 */
3264
3265static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003266void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003267{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003268 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003269
Krzysztof Helt8503df62007-10-16 01:29:08 -07003270 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271 "CR00", 0x00,
3272 "CR01", 0x01,
3273 "CR02", 0x02,
3274 "CR03", 0x03,
3275 "CR04", 0x04,
3276 "CR05", 0x05,
3277 "CR06", 0x06,
3278 "CR07", 0x07,
3279 "CR08", 0x08,
3280 "CR09", 0x09,
3281 "CR0A", 0x0A,
3282 "CR0B", 0x0B,
3283 "CR0C", 0x0C,
3284 "CR0D", 0x0D,
3285 "CR0E", 0x0E,
3286 "CR0F", 0x0F,
3287 "CR10", 0x10,
3288 "CR11", 0x11,
3289 "CR12", 0x12,
3290 "CR13", 0x13,
3291 "CR14", 0x14,
3292 "CR15", 0x15,
3293 "CR16", 0x16,
3294 "CR17", 0x17,
3295 "CR18", 0x18,
3296 "CR22", 0x22,
3297 "CR24", 0x24,
3298 "CR26", 0x26,
3299 "CR2D", 0x2D,
3300 "CR2E", 0x2E,
3301 "CR2F", 0x2F,
3302 "CR30", 0x30,
3303 "CR31", 0x31,
3304 "CR32", 0x32,
3305 "CR33", 0x33,
3306 "CR34", 0x34,
3307 "CR35", 0x35,
3308 "CR36", 0x36,
3309 "CR37", 0x37,
3310 "CR38", 0x38,
3311 "CR39", 0x39,
3312 "CR3A", 0x3A,
3313 "CR3B", 0x3B,
3314 "CR3C", 0x3C,
3315 "CR3D", 0x3D,
3316 "CR3E", 0x3E,
3317 "CR3F", 0x3F,
3318 NULL);
3319
Krzysztof Helt8503df62007-10-16 01:29:08 -07003320 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003321
Krzysztof Helt8503df62007-10-16 01:29:08 -07003322 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323
Krzysztof Helt8503df62007-10-16 01:29:08 -07003324 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 "SR00", 0x00,
3326 "SR01", 0x01,
3327 "SR02", 0x02,
3328 "SR03", 0x03,
3329 "SR04", 0x04,
3330 "SR08", 0x08,
3331 "SR09", 0x09,
3332 "SR0A", 0x0A,
3333 "SR0B", 0x0B,
3334 "SR0D", 0x0D,
3335 "SR10", 0x10,
3336 "SR11", 0x11,
3337 "SR12", 0x12,
3338 "SR13", 0x13,
3339 "SR14", 0x14,
3340 "SR15", 0x15,
3341 "SR16", 0x16,
3342 "SR17", 0x17,
3343 "SR18", 0x18,
3344 "SR19", 0x19,
3345 "SR1A", 0x1A,
3346 "SR1B", 0x1B,
3347 "SR1C", 0x1C,
3348 "SR1D", 0x1D,
3349 "SR1E", 0x1E,
3350 "SR1F", 0x1F,
3351 NULL);
3352
Krzysztof Helt8503df62007-10-16 01:29:08 -07003353 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003354}
3355
3356#endif /* CIRRUSFB_DEBUG */
3357