blob: 0b9fe74630f1e2848b3d86f3d27622a31a520890 [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 */
113typedef enum {
114 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 */
123} cirrusfb_board_t;
124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125/*
126 * per-board-type information, used for enumerating and abstracting
127 * chip-specific information
128 * NOTE: MUST be in the same order as cirrusfb_board_t in order to
129 * 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
364typedef enum {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700365 CRT,
366 SEQ
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367} cirrusfb_dbg_reg_class_t;
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 {
372 struct fb_info *info;
373
374 u8 __iomem *fbmem;
375 u8 __iomem *regbase;
376 u8 __iomem *mem;
377 unsigned long size;
378 cirrusfb_board_t btype;
379 unsigned char SFR; /* Shadow of special function register */
380
381 unsigned long fbmem_phys;
382 unsigned long fbregs_phys;
383
384 struct cirrusfb_regs currentmode;
385 int blank_mode;
386
Antonino A. Daplas49d5c7b2005-11-29 19:34:43 -0800387 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 struct { u8 red, green, blue, pad; } palette[256];
389
390#ifdef CONFIG_ZORRO
391 struct zorro_dev *zdev;
392#endif
393#ifdef CONFIG_PCI
394 struct pci_dev *pdev;
395#endif
396 void (*unmap)(struct cirrusfb_info *cinfo);
397};
398
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399static unsigned cirrusfb_def_mode = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700400static int noaccel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401
402/*
403 * Predefined Video Modes
404 */
405
406static const struct {
407 const char *name;
408 struct fb_var_screeninfo var;
409} cirrusfb_predefined[] = {
410 {
411 /* autodetect mode */
412 .name = "Autodetect",
413 }, {
414 /* 640x480, 31.25 kHz, 60 Hz, 25 MHz PixClock */
415 .name = "640x480",
416 .var = {
417 .xres = 640,
418 .yres = 480,
419 .xres_virtual = 640,
420 .yres_virtual = 480,
421 .bits_per_pixel = 8,
422 .red = { .length = 8 },
423 .green = { .length = 8 },
424 .blue = { .length = 8 },
425 .width = -1,
426 .height = -1,
427 .pixclock = 40000,
428 .left_margin = 48,
429 .right_margin = 16,
430 .upper_margin = 32,
431 .lower_margin = 8,
432 .hsync_len = 96,
433 .vsync_len = 4,
Krzysztof Helt8503df62007-10-16 01:29:08 -0700434 .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 .vmode = FB_VMODE_NONINTERLACED
436 }
437 }, {
438 /* 800x600, 48 kHz, 76 Hz, 50 MHz PixClock */
439 .name = "800x600",
440 .var = {
441 .xres = 800,
442 .yres = 600,
443 .xres_virtual = 800,
444 .yres_virtual = 600,
445 .bits_per_pixel = 8,
446 .red = { .length = 8 },
447 .green = { .length = 8 },
448 .blue = { .length = 8 },
449 .width = -1,
450 .height = -1,
451 .pixclock = 20000,
452 .left_margin = 128,
453 .right_margin = 16,
454 .upper_margin = 24,
455 .lower_margin = 2,
456 .hsync_len = 96,
457 .vsync_len = 6,
458 .vmode = FB_VMODE_NONINTERLACED
459 }
460 }, {
461 /*
462 * Modeline from XF86Config:
463 * Mode "1024x768" 80 1024 1136 1340 1432 768 770 774 805
464 */
465 /* 1024x768, 55.8 kHz, 70 Hz, 80 MHz PixClock */
466 .name = "1024x768",
467 .var = {
468 .xres = 1024,
469 .yres = 768,
470 .xres_virtual = 1024,
471 .yres_virtual = 768,
472 .bits_per_pixel = 8,
473 .red = { .length = 8 },
474 .green = { .length = 8 },
475 .blue = { .length = 8 },
476 .width = -1,
477 .height = -1,
478 .pixclock = 12500,
479 .left_margin = 144,
480 .right_margin = 32,
481 .upper_margin = 30,
482 .lower_margin = 2,
483 .hsync_len = 192,
484 .vsync_len = 6,
485 .vmode = FB_VMODE_NONINTERLACED
486 }
487 }
488};
489
490#define NUM_TOTAL_MODES ARRAY_SIZE(cirrusfb_predefined)
491
492/****************************************************************************/
493/**** BEGIN PROTOTYPES ******************************************************/
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700496static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700498static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499#endif
500
Krzysztof Helt8503df62007-10-16 01:29:08 -0700501static int cirrusfb_open(struct fb_info *info, int user);
502static int cirrusfb_release(struct fb_info *info, int user);
503static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
504 unsigned blue, unsigned transp,
505 struct fb_info *info);
506static int cirrusfb_check_var(struct fb_var_screeninfo *var,
507 struct fb_info *info);
508static int cirrusfb_set_par(struct fb_info *info);
509static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
510 struct fb_info *info);
511static int cirrusfb_blank(int blank_mode, struct fb_info *info);
512static void cirrusfb_fillrect(struct fb_info *info,
513 const struct fb_fillrect *region);
514static void cirrusfb_copyarea(struct fb_info *info,
515 const struct fb_copyarea *area);
516static void cirrusfb_imageblit(struct fb_info *info,
517 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
519/* function table of the above functions */
520static struct fb_ops cirrusfb_ops = {
521 .owner = THIS_MODULE,
522 .fb_open = cirrusfb_open,
523 .fb_release = cirrusfb_release,
524 .fb_setcolreg = cirrusfb_setcolreg,
525 .fb_check_var = cirrusfb_check_var,
526 .fb_set_par = cirrusfb_set_par,
527 .fb_pan_display = cirrusfb_pan_display,
528 .fb_blank = cirrusfb_blank,
529 .fb_fillrect = cirrusfb_fillrect,
530 .fb_copyarea = cirrusfb_copyarea,
531 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532};
533
534/*--- Hardware Specific Routines -------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700535static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 struct cirrusfb_regs *regs,
537 const struct fb_info *info);
538/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700539static void init_vgachip(struct cirrusfb_info *cinfo);
540static void switch_monitor(struct cirrusfb_info *cinfo, int on);
541static void WGen(const struct cirrusfb_info *cinfo,
542 int regnum, unsigned char val);
543static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
544static void AttrOn(const struct cirrusfb_info *cinfo);
545static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
546static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
547static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
548static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
549 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700551static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
552 unsigned char *red, unsigned char *green,
553 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700555static void cirrusfb_WaitBLT(u8 __iomem *regbase);
556static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
557 u_short curx, u_short cury,
558 u_short destx, u_short desty,
559 u_short width, u_short height,
560 u_short line_length);
561static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
562 u_short x, u_short y,
563 u_short width, u_short height,
564 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Krzysztof Helt8503df62007-10-16 01:29:08 -0700566static void bestclock(long freq, long *best,
567 long *nom, long *den,
568 long *div, long maxfreq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
570#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700571static void cirrusfb_dump(void);
572static void cirrusfb_dbg_reg_dump(caddr_t regbase);
573static void cirrusfb_dbg_print_regs(caddr_t regbase,
574 cirrusfb_dbg_reg_class_t reg_class, ...);
575static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576#endif /* CIRRUSFB_DEBUG */
577
578/*** END PROTOTYPES ********************************************************/
579/*****************************************************************************/
580/*** BEGIN Interface Used by the World ***************************************/
581
Krzysztof Helt8503df62007-10-16 01:29:08 -0700582static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
584/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700585static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586{
587 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700588 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 return 0;
590}
591
592/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700593static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
595 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700596 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 return 0;
598}
599
600/**** END Interface used by the World *************************************/
601/****************************************************************************/
602/**** BEGIN Hardware specific Routines **************************************/
603
604/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700605static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606{
607 long mclk;
608
Krzysztof Helt8503df62007-10-16 01:29:08 -0700609 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
611 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
612 * Assume a 64-bit data path for now. The formula is:
613 * ((B * PCLK * 2)/W) * 1.2
614 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
615 mclk = ((bpp / 8) * freq * 2) / 4;
616 mclk = (mclk * 12) / 10;
617 if (mclk < 50000)
618 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700619 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620
621 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
622 mclk = ((mclk * 16) / 14318);
623 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700624 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
626 /* Determine if we should use MCLK instead of VCLK, and if so, what we
627 * should divide it by to get VCLK */
628 switch (freq) {
629 case 24751 ... 25249:
630 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700631 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 break;
633 case 49501 ... 50499:
634 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700635 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 break;
637 default:
638 *div = 0;
639 break;
640 }
641
642 return mclk;
643}
644
645static int cirrusfb_check_var(struct fb_var_screeninfo *var,
646 struct fb_info *info)
647{
648 struct cirrusfb_info *cinfo = info->par;
649 int nom, den; /* translyting from pixels->bytes */
650 int yres, i;
651 static struct { int xres, yres; } modes[] =
652 { { 1600, 1280 },
653 { 1280, 1024 },
654 { 1024, 768 },
655 { 800, 600 },
656 { 640, 480 },
657 { -1, -1 } };
658
659 switch (var->bits_per_pixel) {
660 case 0 ... 1:
661 var->bits_per_pixel = 1;
662 nom = 4;
663 den = 8;
664 break; /* 8 pixel per byte, only 1/4th of mem usable */
665 case 2 ... 8:
666 var->bits_per_pixel = 8;
667 nom = 1;
668 den = 1;
669 break; /* 1 pixel == 1 byte */
670 case 9 ... 16:
671 var->bits_per_pixel = 16;
672 nom = 2;
673 den = 1;
674 break; /* 2 bytes per pixel */
675 case 17 ... 24:
676 var->bits_per_pixel = 24;
677 nom = 3;
678 den = 1;
679 break; /* 3 bytes per pixel */
680 case 25 ... 32:
681 var->bits_per_pixel = 32;
682 nom = 4;
683 den = 1;
684 break; /* 4 bytes per pixel */
685 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700686 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
687 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700689 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return -EINVAL;
691 }
692
693 if (var->xres * nom / den * var->yres > cinfo->size) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700694 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
695 "resolution too high to fit into video memory!\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700697 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 return -EINVAL;
699 }
700
701 /* use highest possible virtual resolution */
702 if (var->xres_virtual == -1 &&
703 var->yres_virtual == -1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700704 printk(KERN_INFO
705 "cirrusfb: using maximum available virtual resolution\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 for (i = 0; modes[i].xres != -1; i++) {
707 if (modes[i].xres * nom / den * modes[i].yres < cinfo->size / 2)
708 break;
709 }
710 if (modes[i].xres == -1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700711 printk(KERN_ERR "cirrusfb: could not find a virtual "
712 "resolution that fits into video memory!!\n");
713 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 return -EINVAL;
715 }
716 var->xres_virtual = modes[i].xres;
717 var->yres_virtual = modes[i].yres;
718
Krzysztof Helt8503df62007-10-16 01:29:08 -0700719 printk(KERN_INFO "cirrusfb: virtual resolution set to "
720 "maximum of %dx%d\n", var->xres_virtual,
721 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 }
723
724 if (var->xres_virtual < var->xres)
725 var->xres_virtual = var->xres;
726 if (var->yres_virtual < var->yres)
727 var->yres_virtual = var->yres;
728
729 if (var->xoffset < 0)
730 var->xoffset = 0;
731 if (var->yoffset < 0)
732 var->yoffset = 0;
733
734 /* truncate xoffset and yoffset to maximum if too high */
735 if (var->xoffset > var->xres_virtual - var->xres)
736 var->xoffset = var->xres_virtual - var->xres - 1;
737 if (var->yoffset > var->yres_virtual - var->yres)
738 var->yoffset = var->yres_virtual - var->yres - 1;
739
740 switch (var->bits_per_pixel) {
741 case 1:
742 var->red.offset = 0;
743 var->red.length = 1;
744 var->green.offset = 0;
745 var->green.length = 1;
746 var->blue.offset = 0;
747 var->blue.length = 1;
748 break;
749
750 case 8:
751 var->red.offset = 0;
752 var->red.length = 6;
753 var->green.offset = 0;
754 var->green.length = 6;
755 var->blue.offset = 0;
756 var->blue.length = 6;
757 break;
758
759 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700760 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 var->red.offset = 2;
762 var->green.offset = -3;
763 var->blue.offset = 8;
764 } else {
765 var->red.offset = 10;
766 var->green.offset = 5;
767 var->blue.offset = 0;
768 }
769 var->red.length = 5;
770 var->green.length = 5;
771 var->blue.length = 5;
772 break;
773
774 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700775 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 var->red.offset = 8;
777 var->green.offset = 16;
778 var->blue.offset = 24;
779 } else {
780 var->red.offset = 16;
781 var->green.offset = 8;
782 var->blue.offset = 0;
783 }
784 var->red.length = 8;
785 var->green.length = 8;
786 var->blue.length = 8;
787 break;
788
789 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700790 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 var->red.offset = 8;
792 var->green.offset = 16;
793 var->blue.offset = 24;
794 } else {
795 var->red.offset = 16;
796 var->green.offset = 8;
797 var->blue.offset = 0;
798 }
799 var->red.length = 8;
800 var->green.length = 8;
801 var->blue.length = 8;
802 break;
803
804 default:
805 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700806 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 /* should never occur */
808 break;
809 }
810
811 var->red.msb_right =
812 var->green.msb_right =
813 var->blue.msb_right =
814 var->transp.offset =
815 var->transp.length =
816 var->transp.msb_right = 0;
817
818 yres = var->yres;
819 if (var->vmode & FB_VMODE_DOUBLE)
820 yres *= 2;
821 else if (var->vmode & FB_VMODE_INTERLACED)
822 yres = (yres + 1) / 2;
823
824 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700825 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
826 "special treatment required! (TODO)\n");
827 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 return -EINVAL;
829 }
830
831 return 0;
832}
833
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 struct cirrusfb_regs *regs,
836 const struct fb_info *info)
837{
838 long freq;
839 long maxclock;
840 int maxclockidx = 0;
841 struct cirrusfb_info *cinfo = info->par;
842 int xres, hfront, hsync, hback;
843 int yres, vfront, vsync, vback;
844
Krzysztof Helt8503df62007-10-16 01:29:08 -0700845 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 case 1:
847 regs->line_length = var->xres_virtual / 8;
848 regs->visual = FB_VISUAL_MONO10;
849 maxclockidx = 0;
850 break;
851
852 case 8:
853 regs->line_length = var->xres_virtual;
854 regs->visual = FB_VISUAL_PSEUDOCOLOR;
855 maxclockidx = 1;
856 break;
857
858 case 16:
859 regs->line_length = var->xres_virtual * 2;
860 regs->visual = FB_VISUAL_DIRECTCOLOR;
861 maxclockidx = 2;
862 break;
863
864 case 24:
865 regs->line_length = var->xres_virtual * 3;
866 regs->visual = FB_VISUAL_DIRECTCOLOR;
867 maxclockidx = 3;
868 break;
869
870 case 32:
871 regs->line_length = var->xres_virtual * 4;
872 regs->visual = FB_VISUAL_DIRECTCOLOR;
873 maxclockidx = 4;
874 break;
875
876 default:
877 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700878 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 /* should never occur */
880 break;
881 }
882
883 regs->type = FB_TYPE_PACKED_PIXELS;
884
885 /* convert from ps to kHz */
886 freq = 1000000000 / var->pixclock;
887
Krzysztof Helt8503df62007-10-16 01:29:08 -0700888 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
890 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
891 regs->multiplexing = 0;
892
893 /* If the frequency is greater than we can support, we might be able
894 * to use multiplexing for the video mode */
895 if (freq > maxclock) {
896 switch (cinfo->btype) {
897 case BT_ALPINE:
898 case BT_GD5480:
899 regs->multiplexing = 1;
900 break;
901
902 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700903 printk(KERN_ERR "cirrusfb: Frequency greater "
904 "than maxclock (%ld kHz)\n", maxclock);
905 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 return -EINVAL;
907 }
908 }
909#if 0
910 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
911 * the VCLK is double the pixel clock. */
912 switch (var->bits_per_pixel) {
913 case 16:
914 case 32:
915 if (regs->HorizRes <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 /* Xbh has this type of clock for 32-bit */
917 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 break;
919 }
920#endif
921
Krzysztof Helt8503df62007-10-16 01:29:08 -0700922 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
923 maxclock);
924 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
925 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
927 xres = var->xres;
928 hfront = var->right_margin;
929 hsync = var->hsync_len;
930 hback = var->left_margin;
931
932 yres = var->yres;
933 vfront = var->lower_margin;
934 vsync = var->vsync_len;
935 vback = var->upper_margin;
936
937 if (var->vmode & FB_VMODE_DOUBLE) {
938 yres *= 2;
939 vfront *= 2;
940 vsync *= 2;
941 vback *= 2;
942 } else if (var->vmode & FB_VMODE_INTERLACED) {
943 yres = (yres + 1) / 2;
944 vfront = (vfront + 1) / 2;
945 vsync = (vsync + 1) / 2;
946 vback = (vback + 1) / 2;
947 }
948 regs->HorizRes = xres;
949 regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
950 regs->HorizDispEnd = xres / 8 - 1;
951 regs->HorizBlankStart = xres / 8;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 /* does not count with "-5" */
953 regs->HorizBlankEnd = regs->HorizTotal + 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 regs->HorizSyncStart = (xres + hfront) / 8 + 1;
955 regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
956
957 regs->VertRes = yres;
958 regs->VertTotal = yres + vfront + vsync + vback - 2;
959 regs->VertDispEnd = yres - 1;
960 regs->VertBlankStart = yres;
961 regs->VertBlankEnd = regs->VertTotal;
962 regs->VertSyncStart = yres + vfront - 1;
963 regs->VertSyncEnd = yres + vfront + vsync - 1;
964
965 if (regs->VertRes >= 1024) {
966 regs->VertTotal /= 2;
967 regs->VertSyncStart /= 2;
968 regs->VertSyncEnd /= 2;
969 regs->VertDispEnd /= 2;
970 }
971 if (regs->multiplexing) {
972 regs->HorizTotal /= 2;
973 regs->HorizSyncStart /= 2;
974 regs->HorizSyncEnd /= 2;
975 regs->HorizDispEnd /= 2;
976 }
977
978 return 0;
979}
980
Krzysztof Helt8503df62007-10-16 01:29:08 -0700981static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
982 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700984 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
986 if (div == 2) {
987 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700988 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
989 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
990 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 } else if (div == 1) {
992 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700993 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
994 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
995 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 }
999}
1000
1001/*************************************************************************
1002 cirrusfb_set_par_foo()
1003
1004 actually writes the values for a new video mode into the hardware,
1005**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001006static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007{
1008 struct cirrusfb_info *cinfo = info->par;
1009 struct fb_var_screeninfo *var = &info->var;
1010 struct cirrusfb_regs regs;
1011 u8 __iomem *regbase = cinfo->regbase;
1012 unsigned char tmp;
1013 int offset = 0, err;
1014 const struct cirrusfb_board_info_rec *bi;
1015
Krzysztof Helt8503df62007-10-16 01:29:08 -07001016 DPRINTK("ENTER\n");
1017 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001019 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
Krzysztof Helt8503df62007-10-16 01:29:08 -07001021 init_vgachip(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022
1023 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001024 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 /* should never happen */
1026 DPRINTK("mode change aborted. invalid var.\n");
1027 return -EINVAL;
1028 }
1029
1030 bi = &cirrusfb_board_info[cinfo->btype];
1031
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001033 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034
1035 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001036 DPRINTK("CRT0: %ld\n", regs.HorizTotal);
1037 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
Krzysztof Helt8503df62007-10-16 01:29:08 -07001039 DPRINTK("CRT1: %ld\n", regs.HorizDispEnd);
1040 vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041
Krzysztof Helt8503df62007-10-16 01:29:08 -07001042 DPRINTK("CRT2: %ld\n", regs.HorizBlankStart);
1043 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Krzysztof Helt8503df62007-10-16 01:29:08 -07001045 /* + 128: Compatible read */
1046 DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);
1047 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
1048 128 + (regs.HorizBlankEnd % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
Krzysztof Helt8503df62007-10-16 01:29:08 -07001050 DPRINTK("CRT4: %ld\n", regs.HorizSyncStart);
1051 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052
1053 tmp = regs.HorizSyncEnd % 32;
1054 if (regs.HorizBlankEnd & 32)
1055 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001056 DPRINTK("CRT5: %d\n", tmp);
1057 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Krzysztof Helt8503df62007-10-16 01:29:08 -07001059 DPRINTK("CRT6: %ld\n", regs.VertTotal & 0xff);
1060 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
1062 tmp = 16; /* LineCompare bit #9 */
1063 if (regs.VertTotal & 256)
1064 tmp |= 1;
1065 if (regs.VertDispEnd & 256)
1066 tmp |= 2;
1067 if (regs.VertSyncStart & 256)
1068 tmp |= 4;
1069 if (regs.VertBlankStart & 256)
1070 tmp |= 8;
1071 if (regs.VertTotal & 512)
1072 tmp |= 32;
1073 if (regs.VertDispEnd & 512)
1074 tmp |= 64;
1075 if (regs.VertSyncStart & 512)
1076 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001077 DPRINTK("CRT7: %d\n", tmp);
1078 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079
1080 tmp = 0x40; /* LineCompare bit #8 */
1081 if (regs.VertBlankStart & 512)
1082 tmp |= 0x20;
1083 if (var->vmode & FB_VMODE_DOUBLE)
1084 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001085 DPRINTK("CRT9: %d\n", tmp);
1086 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087
Krzysztof Helt8503df62007-10-16 01:29:08 -07001088 DPRINTK("CRT10: %ld\n", regs.VertSyncStart & 0xff);
1089 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, regs.VertSyncStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
Krzysztof Helt8503df62007-10-16 01:29:08 -07001091 DPRINTK("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
1092 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, regs.VertSyncEnd % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
Krzysztof Helt8503df62007-10-16 01:29:08 -07001094 DPRINTK("CRT12: %ld\n", regs.VertDispEnd & 0xff);
1095 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, regs.VertDispEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096
Krzysztof Helt8503df62007-10-16 01:29:08 -07001097 DPRINTK("CRT15: %ld\n", regs.VertBlankStart & 0xff);
1098 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, regs.VertBlankStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099
Krzysztof Helt8503df62007-10-16 01:29:08 -07001100 DPRINTK("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
1101 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, regs.VertBlankEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102
Krzysztof Helt8503df62007-10-16 01:29:08 -07001103 DPRINTK("CRT18: 0xff\n");
1104 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
1106 tmp = 0;
1107 if (var->vmode & FB_VMODE_INTERLACED)
1108 tmp |= 1;
1109 if (regs.HorizBlankEnd & 64)
1110 tmp |= 16;
1111 if (regs.HorizBlankEnd & 128)
1112 tmp |= 32;
1113 if (regs.VertBlankEnd & 256)
1114 tmp |= 64;
1115 if (regs.VertBlankEnd & 512)
1116 tmp |= 128;
1117
Krzysztof Helt8503df62007-10-16 01:29:08 -07001118 DPRINTK("CRT1a: %d\n", tmp);
1119 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120
1121 /* set VCLK0 */
1122 /* hardware RefClock: 14.31818 MHz */
1123 /* formula: VClk = (OSC * N) / (D * (1+P)) */
1124 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
1125
Krzysztof Helt8503df62007-10-16 01:29:08 -07001126 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 tmp = regs.den << 1;
1128 if (regs.div != 0)
1129 tmp |= 1;
1130
Krzysztof Helt8503df62007-10-16 01:29:08 -07001131 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 if ((cinfo->btype == BT_SD64) ||
1133 (cinfo->btype == BT_ALPINE) ||
1134 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001135 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
Krzysztof Helt8503df62007-10-16 01:29:08 -07001137 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
1138 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
1140 if (regs.VertRes >= 1024)
1141 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001142 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 else
1144 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
1145 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001146 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
Krzysztof Helt8503df62007-10-16 01:29:08 -07001148/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
1149 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
1151 /* don't know if it would hurt to also program this if no interlaced */
1152 /* mode is used, but I feel better this way.. :-) */
1153 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001154 vga_wcrt(regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
Krzysztof Helt8503df62007-10-16 01:29:08 -07001158 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159
1160 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001161 /* enable display memory & CRTC I/O address for color mode */
1162 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1164 tmp |= 0x40;
1165 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1166 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001167 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
Krzysztof Helt8503df62007-10-16 01:29:08 -07001169 /* Screen A Preset Row-Scan register */
1170 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
1171 /* text cursor on and start line */
1172 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
1173 /* text cursor end line */
1174 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
1176 /******************************************************
1177 *
1178 * 1 bpp
1179 *
1180 */
1181
1182 /* programming for different color depths */
1183 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
1185 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186
1187 /* SR07 */
1188 switch (cinfo->btype) {
1189 case BT_SD64:
1190 case BT_PICCOLO:
1191 case BT_PICASSO:
1192 case BT_SPECTRUM:
1193 case BT_PICASSO4:
1194 case BT_ALPINE:
1195 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001196 DPRINTK(" (for GD54xx)\n");
1197 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 regs.multiplexing ?
1199 bi->sr07_1bpp_mux : bi->sr07_1bpp);
1200 break;
1201
1202 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001203 DPRINTK(" (for GD546x)\n");
1204 vga_wseq(regbase, CL_SEQR7,
1205 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 break;
1207
1208 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001209 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 break;
1211 }
1212
1213 /* Extended Sequencer Mode */
1214 switch (cinfo->btype) {
1215 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001216 /* setting the SEQRF on SD64 is not necessary
1217 * (only during init)
1218 */
1219 DPRINTK("(for SD64)\n");
1220 /* MCLK select */
1221 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 break;
1223
1224 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001225 DPRINTK("(for Piccolo)\n");
1226 /* ### ueberall 0x22? */
1227 /* ##vorher 1c MCLK select */
1228 vga_wseq(regbase, CL_SEQR1F, 0x22);
1229 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1230 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 break;
1232
1233 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001234 DPRINTK("(for Picasso)\n");
1235 /* ##vorher 22 MCLK select */
1236 vga_wseq(regbase, CL_SEQR1F, 0x22);
1237 /* ## vorher d0 avoid FIFO underruns..? */
1238 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 break;
1240
1241 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001242 DPRINTK("(for Spectrum)\n");
1243 /* ### ueberall 0x22? */
1244 /* ##vorher 1c MCLK select */
1245 vga_wseq(regbase, CL_SEQR1F, 0x22);
1246 /* evtl d0? avoid FIFO underruns..? */
1247 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 break;
1249
1250 case BT_PICASSO4:
1251 case BT_ALPINE:
1252 case BT_GD5480:
1253 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001254 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 /* do nothing */
1256 break;
1257
1258 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001259 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 break;
1261 }
1262
Krzysztof Helt8503df62007-10-16 01:29:08 -07001263 /* pixel mask: pass-through for first plane */
1264 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001266 /* hidden dac reg: 1280x1024 */
1267 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001269 /* hidden dac: nothing */
1270 WHDR(cinfo, 0);
1271 /* memory mode: odd/even, ext. memory */
1272 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1273 /* plane mask: only write to first plane */
1274 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 offset = var->xres_virtual / 16;
1276 }
1277
1278 /******************************************************
1279 *
1280 * 8 bpp
1281 *
1282 */
1283
1284 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001285 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 switch (cinfo->btype) {
1287 case BT_SD64:
1288 case BT_PICCOLO:
1289 case BT_PICASSO:
1290 case BT_SPECTRUM:
1291 case BT_PICASSO4:
1292 case BT_ALPINE:
1293 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001294 DPRINTK(" (for GD54xx)\n");
1295 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 regs.multiplexing ?
1297 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1298 break;
1299
1300 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001301 DPRINTK(" (for GD546x)\n");
1302 vga_wseq(regbase, CL_SEQR7,
1303 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 break;
1305
1306 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001307 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 break;
1309 }
1310
1311 switch (cinfo->btype) {
1312 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313 /* MCLK select */
1314 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 break;
1316
1317 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001318 /* ### vorher 1c MCLK select */
1319 vga_wseq(regbase, CL_SEQR1F, 0x22);
1320 /* Fast Page-Mode writes */
1321 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 break;
1323
1324 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001325 /* ### vorher 1c MCLK select */
1326 vga_wseq(regbase, CL_SEQR1F, 0x22);
1327 /* Fast Page-Mode writes */
1328 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 break;
1330
1331 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332 /* ### vorher 1c MCLK select */
1333 vga_wseq(regbase, CL_SEQR1F, 0x22);
1334 /* Fast Page-Mode writes */
1335 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 break;
1337
1338 case BT_PICASSO4:
1339#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001340 /* ### INCOMPLETE!! */
1341 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001343/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 break;
1345
1346 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001347 DPRINTK(" (for GD543x)\n");
1348 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 /* We already set SRF and SR1F */
1350 break;
1351
1352 case BT_GD5480:
1353 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001354 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 /* do nothing */
1356 break;
1357
1358 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001359 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 break;
1361 }
1362
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 /* mode register: 256 color mode */
1364 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1365 /* pixel mask: pass-through all planes */
1366 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001368 /* hidden dac reg: 1280x1024 */
1369 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001371 /* hidden dac: nothing */
1372 WHDR(cinfo, 0);
1373 /* memory mode: chain4, ext. memory */
1374 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1375 /* plane mask: enable writing to all 4 planes */
1376 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 offset = var->xres_virtual / 8;
1378 }
1379
1380 /******************************************************
1381 *
1382 * 16 bpp
1383 *
1384 */
1385
1386 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001387 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 switch (cinfo->btype) {
1389 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 /* Extended Sequencer Mode: 256c col. mode */
1391 vga_wseq(regbase, CL_SEQR7, 0xf7);
1392 /* MCLK select */
1393 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 break;
1395
1396 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 vga_wseq(regbase, CL_SEQR7, 0x87);
1398 /* Fast Page-Mode writes */
1399 vga_wseq(regbase, CL_SEQRF, 0xb0);
1400 /* MCLK select */
1401 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 break;
1403
1404 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 vga_wseq(regbase, CL_SEQR7, 0x27);
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_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001413 vga_wseq(regbase, CL_SEQR7, 0x87);
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
1420 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001421 vga_wseq(regbase, CL_SEQR7, 0x27);
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");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 if (regs.HorizRes >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001428 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001430 vga_wseq(regbase, CL_SEQR7, 0xa3);
1431 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 break;
1433
1434 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001435 DPRINTK(" (for GD5480)\n");
1436 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 /* We already set SRF and SR1F */
1438 break;
1439
1440 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001441 DPRINTK(" (for GD546x)\n");
1442 vga_wseq(regbase, CL_SEQR7,
1443 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 break;
1445
1446 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001447 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 break;
1449 }
1450
Krzysztof Helt8503df62007-10-16 01:29:08 -07001451 /* mode register: 256 color mode */
1452 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1453 /* pixel mask: pass-through all planes */
1454 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001456 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457#elif defined(CONFIG_ZORRO)
1458 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001459 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001461 /* memory mode: chain4, ext. memory */
1462 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1463 /* plane mask: enable writing to all 4 planes */
1464 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 offset = var->xres_virtual / 4;
1466 }
1467
1468 /******************************************************
1469 *
1470 * 32 bpp
1471 *
1472 */
1473
1474 else if (var->bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001475 DPRINTK("cirrusfb: preparing for 24/32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 switch (cinfo->btype) {
1477 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001478 /* Extended Sequencer Mode: 256c col. mode */
1479 vga_wseq(regbase, CL_SEQR7, 0xf9);
1480 /* MCLK select */
1481 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 break;
1483
1484 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001485 vga_wseq(regbase, CL_SEQR7, 0x85);
1486 /* Fast Page-Mode writes */
1487 vga_wseq(regbase, CL_SEQRF, 0xb0);
1488 /* MCLK select */
1489 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 break;
1491
1492 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001493 vga_wseq(regbase, CL_SEQR7, 0x25);
1494 /* Fast Page-Mode writes */
1495 vga_wseq(regbase, CL_SEQRF, 0xb0);
1496 /* MCLK select */
1497 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 break;
1499
1500 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 vga_wseq(regbase, CL_SEQR7, 0x85);
1502 /* Fast Page-Mode writes */
1503 vga_wseq(regbase, CL_SEQRF, 0xb0);
1504 /* MCLK select */
1505 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 break;
1507
1508 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001509 vga_wseq(regbase, CL_SEQR7, 0x25);
1510/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 break;
1512
1513 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001514 DPRINTK(" (for GD543x)\n");
1515 vga_wseq(regbase, CL_SEQR7, 0xa9);
1516 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 break;
1518
1519 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001520 DPRINTK(" (for GD5480)\n");
1521 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 /* We already set SRF and SR1F */
1523 break;
1524
1525 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001526 DPRINTK(" (for GD546x)\n");
1527 vga_wseq(regbase, CL_SEQR7,
1528 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 break;
1530
1531 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001532 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 break;
1534 }
1535
Krzysztof Helt8503df62007-10-16 01:29:08 -07001536 /* mode register: 256 color mode */
1537 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1538 /* pixel mask: pass-through all planes */
1539 WGen(cinfo, VGA_PEL_MSK, 0xff);
1540 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1541 WHDR(cinfo, 0xc5);
1542 /* memory mode: chain4, ext. memory */
1543 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1544 /* plane mask: enable writing to all 4 planes */
1545 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 offset = var->xres_virtual / 4;
1547 }
1548
1549 /******************************************************
1550 *
1551 * unknown/unsupported bpp
1552 *
1553 */
1554
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 else
1556 printk(KERN_ERR "cirrusfb: What's this?? "
1557 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
Krzysztof Helt8503df62007-10-16 01:29:08 -07001560 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 tmp = 0x22;
1562 if (offset & 0x100)
1563 tmp |= 0x10; /* offset overflow bit */
1564
Krzysztof Helt8503df62007-10-16 01:29:08 -07001565 /* screen start addr #16-18, fastpagemode cycles */
1566 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
1568 if (cinfo->btype == BT_SD64 ||
1569 cinfo->btype == BT_PICASSO4 ||
1570 cinfo->btype == BT_ALPINE ||
1571 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001572 /* screen start address bit 19 */
1573 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574
Krzysztof Helt8503df62007-10-16 01:29:08 -07001575 /* text cursor location high */
1576 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1577 /* text cursor location low */
1578 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1579 /* underline row scanline = at very bottom */
1580 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
Krzysztof Helt8503df62007-10-16 01:29:08 -07001582 /* controller mode */
1583 vga_wattr(regbase, VGA_ATC_MODE, 1);
1584 /* overscan (border) color */
1585 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1586 /* color plane enable */
1587 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1588 /* pixel panning */
1589 vga_wattr(regbase, CL_AR33, 0);
1590 /* color select */
1591 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 /* [ EGS: SetOffset(); ] */
1594 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001595 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596
Krzysztof Helt8503df62007-10-16 01:29:08 -07001597 /* set/reset register */
1598 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1599 /* set/reset enable */
1600 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1601 /* color compare */
1602 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1603 /* data rotate */
1604 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1605 /* read map select */
1606 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1607 /* miscellaneous register */
1608 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1609 /* color don't care */
1610 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1611 /* bit mask */
1612 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Krzysztof Helt8503df62007-10-16 01:29:08 -07001614 /* graphics cursor attributes: nothing special */
1615 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
1617 /* finally, turn on everything - turn off "FullBandwidth" bit */
1618 /* also, set "DotClock%2" bit where requested */
1619 tmp = 0x01;
1620
1621/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1622 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1623 tmp |= 0x08;
1624*/
1625
Krzysztof Helt8503df62007-10-16 01:29:08 -07001626 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1627 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628
1629 cinfo->currentmode = regs;
1630 info->fix.type = regs.type;
1631 info->fix.visual = regs.visual;
1632 info->fix.line_length = regs.line_length;
1633
1634 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001635 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636
1637#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001638 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639#endif
1640
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 return 0;
1643}
1644
1645/* for some reason incomprehensible to me, cirrusfb requires that you write
1646 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001647static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001649 cirrusfb_set_par_foo(info);
1650 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651}
1652
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1654 unsigned blue, unsigned transp,
1655 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656{
1657 struct cirrusfb_info *cinfo = info->par;
1658
1659 if (regno > 255)
1660 return -EINVAL;
1661
1662 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1663 u32 v;
1664 red >>= (16 - info->var.red.length);
1665 green >>= (16 - info->var.green.length);
1666 blue >>= (16 - info->var.blue.length);
1667
Krzysztof Helt8503df62007-10-16 01:29:08 -07001668 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 return 1;
1670 v = (red << info->var.red.offset) |
1671 (green << info->var.green.offset) |
1672 (blue << info->var.blue.offset);
1673
1674 switch (info->var.bits_per_pixel) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 case 8:
1676 cinfo->pseudo_palette[regno] = v;
1677 break;
1678 case 16:
1679 cinfo->pseudo_palette[regno] = v;
1680 break;
1681 case 24:
1682 case 32:
1683 cinfo->pseudo_palette[regno] = v;
1684 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 }
1686 return 0;
1687 }
1688
1689 cinfo->palette[regno].red = red;
1690 cinfo->palette[regno].green = green;
1691 cinfo->palette[regno].blue = blue;
1692
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 if (info->var.bits_per_pixel == 8)
1694 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 return 0;
1697
1698}
1699
1700/*************************************************************************
1701 cirrusfb_pan_display()
1702
1703 performs display panning - provided hardware permits this
1704**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1706 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707{
1708 int xoffset = 0;
1709 int yoffset = 0;
1710 unsigned long base;
1711 unsigned char tmp = 0, tmp2 = 0, xpix;
1712 struct cirrusfb_info *cinfo = info->par;
1713
Krzysztof Helt8503df62007-10-16 01:29:08 -07001714 DPRINTK("ENTER\n");
1715 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
1717 /* no range checks for xoffset and yoffset, */
1718 /* as fb_pan_display has already done this */
1719 if (var->vmode & FB_VMODE_YWRAP)
1720 return -EINVAL;
1721
1722 info->var.xoffset = var->xoffset;
1723 info->var.yoffset = var->yoffset;
1724
1725 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1726 yoffset = var->yoffset;
1727
1728 base = yoffset * cinfo->currentmode.line_length + xoffset;
1729
1730 if (info->var.bits_per_pixel == 1) {
1731 /* base is already correct */
1732 xpix = (unsigned char) (var->xoffset % 8);
1733 } else {
1734 base /= 4;
1735 xpix = (unsigned char) ((xoffset % 4) * 2);
1736 }
1737
Krzysztof Helt8503df62007-10-16 01:29:08 -07001738 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
1740 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001741 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1742 (unsigned char) (base & 0xff));
1743 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1744 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745
1746 /* construct bits 16, 17 and 18 of screen start address */
1747 if (base & 0x10000)
1748 tmp |= 0x01;
1749 if (base & 0x20000)
1750 tmp |= 0x04;
1751 if (base & 0x40000)
1752 tmp |= 0x08;
1753
Krzysztof Helt8503df62007-10-16 01:29:08 -07001754 /* 0xf2 is %11110010, exclude tmp bits */
1755 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1756 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757
1758 /* construct bit 19 of screen start address */
1759 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
1760 tmp2 = 0;
1761 if (base & 0x80000)
1762 tmp2 = 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 }
1765
Krzysztof Helt8503df62007-10-16 01:29:08 -07001766 /* write pixel panning value to AR33; this does not quite work in 8bpp
1767 *
1768 * ### Piccolo..? Will this work?
1769 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001771 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
Krzysztof Helt8503df62007-10-16 01:29:08 -07001773 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774
Krzysztof Helt8503df62007-10-16 01:29:08 -07001775 DPRINTK("EXIT\n");
1776 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777}
1778
Krzysztof Helt8503df62007-10-16 01:29:08 -07001779static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780{
1781 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001782 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1783 * then the caller blanks by setting the CLUT (Color Look Up Table)
1784 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1785 * failed due to e.g. a video mode which doesn't support it.
1786 * Implements VESA suspend and powerdown modes on hardware that
1787 * supports disabling hsync/vsync:
1788 * blank_mode == 2: suspend vsync
1789 * blank_mode == 3: suspend hsync
1790 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 */
1792 unsigned char val;
1793 struct cirrusfb_info *cinfo = info->par;
1794 int current_mode = cinfo->blank_mode;
1795
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
1798 if (info->state != FBINFO_STATE_RUNNING ||
1799 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001800 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 return 0;
1802 }
1803
1804 /* Undo current */
1805 if (current_mode == FB_BLANK_NORMAL ||
1806 current_mode == FB_BLANK_UNBLANK) {
1807 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001808 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1809 /* clear "FullBandwidth" bit */
1810 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001812 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813 }
1814
1815 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001816 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001818 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1819 /* set "FullBandwidth" bit */
1820 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 }
1822
1823 switch (blank_mode) {
1824 case FB_BLANK_UNBLANK:
1825 case FB_BLANK_NORMAL:
1826 break;
1827 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001828 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 break;
1830 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001831 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 break;
1833 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001834 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 break;
1836 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001837 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838 return 1;
1839 }
1840
1841 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001842 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
1844 /* Let fbcon do a soft blank for us */
1845 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1846}
1847/**** END Hardware specific Routines **************************************/
1848/****************************************************************************/
1849/**** BEGIN Internal Routines ***********************************************/
1850
Krzysztof Helt8503df62007-10-16 01:29:08 -07001851static void init_vgachip(struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852{
1853 const struct cirrusfb_board_info_rec *bi;
1854
Krzysztof Helt8503df62007-10-16 01:29:08 -07001855 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856
Krzysztof Helt8503df62007-10-16 01:29:08 -07001857 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858
1859 bi = &cirrusfb_board_info[cinfo->btype];
1860
1861 /* reset board globally */
1862 switch (cinfo->btype) {
1863 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001864 WSFR(cinfo, 0x01);
1865 udelay(500);
1866 WSFR(cinfo, 0x51);
1867 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 break;
1869 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001870 WSFR2(cinfo, 0xff);
1871 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 break;
1873 case BT_SD64:
1874 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001875 WSFR(cinfo, 0x1f);
1876 udelay(500);
1877 WSFR(cinfo, 0x4f);
1878 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 break;
1880 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001881 /* disable flickerfixer */
1882 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1883 mdelay(100);
1884 /* from Klaus' NetBSD driver: */
1885 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1886 /* put blitter into 542x compat */
1887 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1888 /* mode */
1889 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 break;
1891
1892 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001893 /* from Klaus' NetBSD driver: */
1894 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 break;
1896
1897 case BT_ALPINE:
1898 /* Nothing to do to reset the board. */
1899 break;
1900
1901 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001902 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 break;
1904 }
1905
Krzysztof Helt8503df62007-10-16 01:29:08 -07001906 assert(cinfo->size > 0); /* make sure RAM size set by this point */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907
1908 /* the P4 is not fully initialized here; I rely on it having been */
1909 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001910 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911
1912 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001913 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1914 WGen(cinfo, CL_POS102, 0x01);
1915 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916
1917 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001918 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919
Krzysztof Helt8503df62007-10-16 01:29:08 -07001920 /* reset sequencer logic */
1921 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922
Krzysztof Helt8503df62007-10-16 01:29:08 -07001923 /* FullBandwidth (video off) and 8/9 dot clock */
1924 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1925 /* polarity (-/-), disable access to display memory,
1926 * VGA_CRTC_START_HI base address: color
1927 */
1928 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929
Krzysztof Helt8503df62007-10-16 01:29:08 -07001930 /* "magic cookie" - doesn't make any sense to me.. */
1931/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1932 /* unlock all extension registers */
1933 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934
Krzysztof Helt8503df62007-10-16 01:29:08 -07001935 /* reset blitter */
1936 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937
1938 switch (cinfo->btype) {
1939 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001940 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 break;
1942 case BT_ALPINE:
1943 break;
1944 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001945 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 break;
1947 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001948 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1949 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 break;
1951 }
1952 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001953 /* plane mask: nothing */
1954 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1955 /* character map select: doesn't even matter in gx mode */
1956 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1957 /* memory mode: chain-4, no odd/even, ext. memory */
1958 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
1960 /* controller-internal base address of video memory */
1961 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001962 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963
Krzysztof Helt8503df62007-10-16 01:29:08 -07001964 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1965 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966
Krzysztof Helt8503df62007-10-16 01:29:08 -07001967 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1968 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1969 /* graphics cursor Y position (..."... ) */
1970 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1971 /* graphics cursor attributes */
1972 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1973 /* graphics cursor pattern address */
1974 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975
1976 /* writing these on a P4 might give problems.. */
1977 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001978 /* configuration readback and ext. color */
1979 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1980 /* signature generator */
1981 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 }
1983
1984 /* MCLK select etc. */
1985 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001986 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987
Krzysztof Helt8503df62007-10-16 01:29:08 -07001988 /* Screen A preset row scan: none */
1989 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1990 /* Text cursor start: disable text cursor */
1991 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1992 /* Text cursor end: - */
1993 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1994 /* Screen start address high: 0 */
1995 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1996 /* Screen start address low: 0 */
1997 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1998 /* text cursor location high: 0 */
1999 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
2000 /* text cursor location low: 0 */
2001 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002
Krzysztof Helt8503df62007-10-16 01:29:08 -07002003 /* Underline Row scanline: - */
2004 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
2005 /* mode control: timing enable, byte mode, no compat modes */
2006 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
2007 /* Line Compare: not needed */
2008 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002010 /* ext. display controls: ext.adr. wrap */
2011 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012
Krzysztof Helt8503df62007-10-16 01:29:08 -07002013 /* Set/Reset registes: - */
2014 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
2015 /* Set/Reset enable: - */
2016 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
2017 /* Color Compare: - */
2018 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
2019 /* Data Rotate: - */
2020 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
2021 /* Read Map Select: - */
2022 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
2023 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
2024 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
2025 /* Miscellaneous: memory map base address, graphics mode */
2026 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
2027 /* Color Don't care: involve all planes */
2028 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
2029 /* Bit Mask: no mask at all */
2030 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002032 /* (5434 can't have bit 3 set for bitblt) */
2033 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07002035 /* Graphics controller mode extensions: finer granularity,
2036 * 8byte data latches
2037 */
2038 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039
Krzysztof Helt8503df62007-10-16 01:29:08 -07002040 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
2041 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
2042 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
2043 /* Background color byte 1: - */
2044 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
2045 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046
Krzysztof Helt8503df62007-10-16 01:29:08 -07002047 /* Attribute Controller palette registers: "identity mapping" */
2048 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
2049 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
2050 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
2051 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
2052 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
2053 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
2054 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
2055 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
2056 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
2057 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
2058 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
2059 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
2060 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
2061 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
2062 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
2063 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064
Krzysztof Helt8503df62007-10-16 01:29:08 -07002065 /* Attribute Controller mode: graphics mode */
2066 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
2067 /* Overscan color reg.: reg. 0 */
2068 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
2069 /* Color Plane enable: Enable all 4 planes */
2070 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
2071/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
2072 /* Color Select: - */
2073 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074
Krzysztof Helt8503df62007-10-16 01:29:08 -07002075 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076
2077 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002078 /* polarity (-/-), enable display mem,
2079 * VGA_CRTC_START_HI i/o base = color
2080 */
2081 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082
Krzysztof Helt8503df62007-10-16 01:29:08 -07002083 /* BLT Start/status: Blitter reset */
2084 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
2085 /* - " - : "end-of-reset" */
2086 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
2088 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002089 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090
Krzysztof Helt8503df62007-10-16 01:29:08 -07002091 printk(KERN_DEBUG "cirrusfb: This board has %ld bytes of DRAM memory\n",
2092 cinfo->size);
2093 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 return;
2095}
2096
Krzysztof Helt8503df62007-10-16 01:29:08 -07002097static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098{
2099#ifdef CONFIG_ZORRO /* only works on Zorro boards */
2100 static int IsOn = 0; /* XXX not ok for multiple boards */
2101
Krzysztof Helt8503df62007-10-16 01:29:08 -07002102 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103
2104 if (cinfo->btype == BT_PICASSO4)
2105 return; /* nothing to switch */
2106 if (cinfo->btype == BT_ALPINE)
2107 return; /* nothing to switch */
2108 if (cinfo->btype == BT_GD5480)
2109 return; /* nothing to switch */
2110 if (cinfo->btype == BT_PICASSO) {
2111 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07002112 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113
Krzysztof Helt8503df62007-10-16 01:29:08 -07002114 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 return;
2116 }
2117 if (on) {
2118 switch (cinfo->btype) {
2119 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002120 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 break;
2122 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002123 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 break;
2125 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002126 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 break;
2128 default: /* do nothing */ break;
2129 }
2130 } else {
2131 switch (cinfo->btype) {
2132 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002133 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 break;
2135 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002136 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 break;
2138 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002139 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 break;
2141 default: /* do nothing */ break;
2142 }
2143 }
2144
Krzysztof Helt8503df62007-10-16 01:29:08 -07002145 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146#endif /* CONFIG_ZORRO */
2147}
2148
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149/******************************************/
2150/* Linux 2.6-style accelerated functions */
2151/******************************************/
2152
2153static void cirrusfb_prim_fillrect(struct cirrusfb_info *cinfo,
2154 const struct fb_fillrect *region)
2155{
2156 int m; /* bytes per pixel */
Antonino A. Daplas49d5c7b2005-11-29 19:34:43 -08002157 u32 color = (cinfo->info->fix.visual == FB_VISUAL_TRUECOLOR) ?
2158 cinfo->pseudo_palette[region->color] : region->color;
2159
Krzysztof Helt8503df62007-10-16 01:29:08 -07002160 if (cinfo->info->var.bits_per_pixel == 1) {
2161 cirrusfb_RectFill(cinfo->regbase,
2162 cinfo->info->var.bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 region->dx / 8, region->dy,
2164 region->width / 8, region->height,
Antonino A. Daplas49d5c7b2005-11-29 19:34:43 -08002165 color,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166 cinfo->currentmode.line_length);
2167 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002168 m = (cinfo->info->var.bits_per_pixel + 7) / 8;
2169 cirrusfb_RectFill(cinfo->regbase,
2170 cinfo->info->var.bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 region->dx * m, region->dy,
2172 region->width * m, region->height,
Antonino A. Daplas49d5c7b2005-11-29 19:34:43 -08002173 color,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 cinfo->currentmode.line_length);
2175 }
2176 return;
2177}
2178
Krzysztof Helt8503df62007-10-16 01:29:08 -07002179static void cirrusfb_fillrect(struct fb_info *info,
2180 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181{
2182 struct cirrusfb_info *cinfo = info->par;
2183 struct fb_fillrect modded;
2184 int vxres, vyres;
2185
2186 if (info->state != FBINFO_STATE_RUNNING)
2187 return;
2188 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2189 cfb_fillrect(info, region);
2190 return;
2191 }
2192
2193 vxres = info->var.xres_virtual;
2194 vyres = info->var.yres_virtual;
2195
2196 memcpy(&modded, region, sizeof(struct fb_fillrect));
2197
Krzysztof Helt8503df62007-10-16 01:29:08 -07002198 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 modded.dx >= vxres || modded.dy >= vyres)
2200 return;
2201
Krzysztof Helt8503df62007-10-16 01:29:08 -07002202 if (modded.dx + modded.width > vxres)
2203 modded.width = vxres - modded.dx;
2204 if (modded.dy + modded.height > vyres)
2205 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206
2207 cirrusfb_prim_fillrect(cinfo, &modded);
2208}
2209
2210static void cirrusfb_prim_copyarea(struct cirrusfb_info *cinfo,
2211 const struct fb_copyarea *area)
2212{
2213 int m; /* bytes per pixel */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002214 if (cinfo->info->var.bits_per_pixel == 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 cirrusfb_BitBLT(cinfo->regbase, cinfo->info->var.bits_per_pixel,
2216 area->sx / 8, area->sy,
2217 area->dx / 8, area->dy,
2218 area->width / 8, area->height,
2219 cinfo->currentmode.line_length);
2220 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002221 m = (cinfo->info->var.bits_per_pixel + 7) / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 cirrusfb_BitBLT(cinfo->regbase, cinfo->info->var.bits_per_pixel,
2223 area->sx * m, area->sy,
2224 area->dx * m, area->dy,
2225 area->width * m, area->height,
2226 cinfo->currentmode.line_length);
2227 }
2228 return;
2229}
2230
Krzysztof Helt8503df62007-10-16 01:29:08 -07002231static void cirrusfb_copyarea(struct fb_info *info,
2232 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233{
2234 struct cirrusfb_info *cinfo = info->par;
2235 struct fb_copyarea modded;
2236 u32 vxres, vyres;
2237 modded.sx = area->sx;
2238 modded.sy = area->sy;
2239 modded.dx = area->dx;
2240 modded.dy = area->dy;
2241 modded.width = area->width;
2242 modded.height = area->height;
2243
2244 if (info->state != FBINFO_STATE_RUNNING)
2245 return;
2246 if (info->flags & FBINFO_HWACCEL_DISABLED) {
2247 cfb_copyarea(info, area);
2248 return;
2249 }
2250
2251 vxres = info->var.xres_virtual;
2252 vyres = info->var.yres_virtual;
2253
Krzysztof Helt8503df62007-10-16 01:29:08 -07002254 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 modded.sx >= vxres || modded.sy >= vyres ||
2256 modded.dx >= vxres || modded.dy >= vyres)
2257 return;
2258
Krzysztof Helt8503df62007-10-16 01:29:08 -07002259 if (modded.sx + modded.width > vxres)
2260 modded.width = vxres - modded.sx;
2261 if (modded.dx + modded.width > vxres)
2262 modded.width = vxres - modded.dx;
2263 if (modded.sy + modded.height > vyres)
2264 modded.height = vyres - modded.sy;
2265 if (modded.dy + modded.height > vyres)
2266 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267
2268 cirrusfb_prim_copyarea(cinfo, &modded);
2269}
2270
Krzysztof Helt8503df62007-10-16 01:29:08 -07002271static void cirrusfb_imageblit(struct fb_info *info,
2272 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273{
2274 struct cirrusfb_info *cinfo = info->par;
2275
Krzysztof Helt8503df62007-10-16 01:29:08 -07002276 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 cfb_imageblit(info, image);
2278}
2279
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280#ifdef CONFIG_PPC_PREP
2281#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
2282#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002283static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002285 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286
2287 *display = PREP_VIDEO_BASE;
2288 *registers = (unsigned long) PREP_IO_BASE;
2289
Krzysztof Helt8503df62007-10-16 01:29:08 -07002290 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291}
2292
2293#endif /* CONFIG_PPC_PREP */
2294
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002296static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297
2298/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
2299 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
2300 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
2301 * seem to have. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002302static unsigned int cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303{
2304 unsigned long mem;
2305 unsigned char SRF;
2306
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 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002311 case 0x08:
2312 mem = 512 * 1024;
2313 break;
2314 case 0x10:
2315 mem = 1024 * 1024;
2316 break;
2317 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2318 * on the 5430.
2319 */
2320 case 0x18:
2321 mem = 2048 * 1024;
2322 break;
2323 default:
2324 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 mem = 1024 * 1024;
2326 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002327 if (SRF & 0x80)
2328 /* If DRAM bank switching is enabled, there must be twice as much
2329 * memory installed. (4MB on the 5434)
2330 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002332
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2334
Krzysztof Helt8503df62007-10-16 01:29:08 -07002335 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 return mem;
2337}
2338
Krzysztof Helt8503df62007-10-16 01:29:08 -07002339static void get_pci_addrs(const struct pci_dev *pdev,
2340 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002342 assert(pdev != NULL);
2343 assert(display != NULL);
2344 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345
Krzysztof Helt8503df62007-10-16 01:29:08 -07002346 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347
2348 *display = 0;
2349 *registers = 0;
2350
2351 /* This is a best-guess for now */
2352
2353 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2354 *display = pci_resource_start(pdev, 1);
2355 *registers = pci_resource_start(pdev, 0);
2356 } else {
2357 *display = pci_resource_start(pdev, 0);
2358 *registers = pci_resource_start(pdev, 1);
2359 }
2360
Krzysztof Helt8503df62007-10-16 01:29:08 -07002361 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362
Krzysztof Helt8503df62007-10-16 01:29:08 -07002363 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364}
2365
Krzysztof Helt8503df62007-10-16 01:29:08 -07002366static void cirrusfb_pci_unmap(struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367{
2368 struct pci_dev *pdev = cinfo->pdev;
2369
2370 iounmap(cinfo->fbmem);
2371#if 0 /* if system didn't claim this region, we would... */
2372 release_mem_region(0xA0000, 65535);
2373#endif
2374 if (release_io_ports)
2375 release_region(0x3C0, 32);
2376 pci_release_regions(pdev);
2377 framebuffer_release(cinfo->info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378}
2379#endif /* CONFIG_PCI */
2380
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002382static void __devexit cirrusfb_zorro_unmap(struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383{
2384 zorro_release_device(cinfo->zdev);
2385
2386 if (cinfo->btype == BT_PICASSO4) {
2387 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002388 iounmap((void *)cinfo->regbase);
2389 iounmap((void *)cinfo->fbmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 } else {
2391 if (zorro_resource_start(cinfo->zdev) > 0x01000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002392 iounmap((void *)cinfo->fbmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 }
2394 framebuffer_release(cinfo->info);
2395}
2396#endif /* CONFIG_ZORRO */
2397
2398static int cirrusfb_set_fbinfo(struct cirrusfb_info *cinfo)
2399{
2400 struct fb_info *info = cinfo->info;
2401 struct fb_var_screeninfo *var = &info->var;
2402
2403 info->par = cinfo;
2404 info->pseudo_palette = cinfo->pseudo_palette;
2405 info->flags = FBINFO_DEFAULT
2406 | FBINFO_HWACCEL_XPAN
2407 | FBINFO_HWACCEL_YPAN
2408 | FBINFO_HWACCEL_FILLRECT
2409 | FBINFO_HWACCEL_COPYAREA;
2410 if (noaccel)
2411 info->flags |= FBINFO_HWACCEL_DISABLED;
2412 info->fbops = &cirrusfb_ops;
2413 info->screen_base = cinfo->fbmem;
2414 if (cinfo->btype == BT_GD5480) {
2415 if (var->bits_per_pixel == 16)
2416 info->screen_base += 1 * MB_;
2417 if (var->bits_per_pixel == 24 || var->bits_per_pixel == 32)
2418 info->screen_base += 2 * MB_;
2419 }
2420
2421 /* Fill fix common fields */
2422 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2423 sizeof(info->fix.id));
2424
2425 /* monochrome: only 1 memory plane */
2426 /* 8 bit and above: Use whole memory area */
2427 info->fix.smem_start = cinfo->fbmem_phys;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002428 info->fix.smem_len =
2429 (var->bits_per_pixel == 1) ? cinfo->size / 4 : cinfo->size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430 info->fix.type = cinfo->currentmode.type;
2431 info->fix.type_aux = 0;
2432 info->fix.visual = cinfo->currentmode.visual;
2433 info->fix.xpanstep = 1;
2434 info->fix.ypanstep = 1;
2435 info->fix.ywrapstep = 0;
2436 info->fix.line_length = cinfo->currentmode.line_length;
2437
2438 /* FIXME: map region at 0xB8000 if available, fill in here */
2439 info->fix.mmio_start = cinfo->fbregs_phys;
2440 info->fix.mmio_len = 0;
2441 info->fix.accel = FB_ACCEL_NONE;
2442
2443 fb_alloc_cmap(&info->cmap, 256, 0);
2444
2445 return 0;
2446}
2447
2448static int cirrusfb_register(struct cirrusfb_info *cinfo)
2449{
2450 struct fb_info *info;
2451 int err;
2452 cirrusfb_board_t btype;
2453
Krzysztof Helt8503df62007-10-16 01:29:08 -07002454 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455
Krzysztof Helt8503df62007-10-16 01:29:08 -07002456 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2457 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458
2459 info = cinfo->info;
2460 btype = cinfo->btype;
2461
2462 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464
Krzysztof Helt8503df62007-10-16 01:29:08 -07002465 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", cinfo->fbmem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466
2467 /* Make pretend we've set the var so our structures are in a "good" */
2468 /* state, even though we haven't written the mode to the hw yet... */
2469 info->var = cirrusfb_predefined[cirrusfb_def_mode].var;
2470 info->var.activate = FB_ACTIVATE_NOW;
2471
2472 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2473 if (err < 0) {
2474 /* should never happen */
2475 DPRINTK("choking on default var... umm, no good.\n");
2476 goto err_unmap_cirrusfb;
2477 }
2478
2479 /* set all the vital stuff */
2480 cirrusfb_set_fbinfo(cinfo);
2481
2482 err = register_framebuffer(info);
2483 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002484 printk(KERN_ERR "cirrusfb: could not register "
2485 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486 goto err_dealloc_cmap;
2487 }
2488
Krzysztof Helt8503df62007-10-16 01:29:08 -07002489 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 return 0;
2491
2492err_dealloc_cmap:
2493 fb_dealloc_cmap(&info->cmap);
2494err_unmap_cirrusfb:
2495 cinfo->unmap(cinfo);
2496 return err;
2497}
2498
Krzysztof Helt8503df62007-10-16 01:29:08 -07002499static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500{
2501 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002502 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503
Krzysztof Helt8503df62007-10-16 01:29:08 -07002504 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505
Krzysztof Helt8503df62007-10-16 01:29:08 -07002506 unregister_framebuffer(info);
2507 fb_dealloc_cmap(&info->cmap);
2508 printk("Framebuffer unregistered\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509 cinfo->unmap(cinfo);
2510
Krzysztof Helt8503df62007-10-16 01:29:08 -07002511 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512}
2513
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002515static int cirrusfb_pci_register(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516 const struct pci_device_id *ent)
2517{
2518 struct cirrusfb_info *cinfo;
2519 struct fb_info *info;
2520 cirrusfb_board_t btype;
2521 unsigned long board_addr, board_size;
2522 int ret;
2523
2524 ret = pci_enable_device(pdev);
2525 if (ret < 0) {
2526 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2527 goto err_out;
2528 }
2529
2530 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2531 if (!info) {
2532 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2533 ret = -ENOMEM;
2534 goto err_disable;
2535 }
2536
2537 cinfo = info->par;
2538 cinfo->info = info;
2539 cinfo->pdev = pdev;
2540 cinfo->btype = btype = (cirrusfb_board_t) ent->driver_data;
2541
Krzysztof Helt8503df62007-10-16 01:29:08 -07002542 DPRINTK(" Found PCI device, base address 0 is 0x%lx, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 pdev->resource[0].start, btype);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002544 DPRINTK(" base address 1 is 0x%lx\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 if (isPReP) {
2547 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548#ifdef CONFIG_PPC_PREP
Krzysztof Helt8503df62007-10-16 01:29:08 -07002549 get_prep_addrs(&board_addr, &cinfo->fbregs_phys);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002551 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 cinfo->regbase = (char __iomem *) cinfo->fbregs_phys;
2553 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
2555 get_pci_addrs(pdev, &board_addr, &cinfo->fbregs_phys);
2556 /* FIXME: this forces VGA. alternatives? */
2557 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 }
2559
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
2561 board_addr, cinfo->fbregs_phys);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562
2563 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002564 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565
2566 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002567 if (ret < 0) {
2568 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2569 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 board_addr);
2571 goto err_release_fb;
2572 }
2573#if 0 /* if the system didn't claim this region, we would... */
2574 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2575 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2576,
2577 0xA0000L);
2578 ret = -EBUSY;
2579 goto err_release_regions;
2580 }
2581#endif
2582 if (request_region(0x3C0, 32, "cirrusfb"))
2583 release_io_ports = 1;
2584
2585 cinfo->fbmem = ioremap(board_addr, board_size);
2586 if (!cinfo->fbmem) {
2587 ret = -EIO;
2588 goto err_release_legacy;
2589 }
2590
2591 cinfo->fbmem_phys = board_addr;
2592 cinfo->size = board_size;
2593 cinfo->unmap = cirrusfb_pci_unmap;
2594
Krzysztof Helt8503df62007-10-16 01:29:08 -07002595 printk(KERN_INFO " RAM (%lu kB) at 0xx%lx, ",
2596 cinfo->size / KB_, board_addr);
2597 printk(KERN_INFO "Cirrus Logic chipset on PCI bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598 pci_set_drvdata(pdev, info);
2599
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002600 ret = cirrusfb_register(cinfo);
2601 if (ret)
2602 iounmap(cinfo->fbmem);
2603 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604
2605err_release_legacy:
2606 if (release_io_ports)
2607 release_region(0x3C0, 32);
2608#if 0
2609 release_mem_region(0xA0000, 65535);
2610err_release_regions:
2611#endif
2612 pci_release_regions(pdev);
2613err_release_fb:
2614 framebuffer_release(info);
2615err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616err_out:
2617 return ret;
2618}
2619
Krzysztof Helt8503df62007-10-16 01:29:08 -07002620static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621{
2622 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002623 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626
Krzysztof Helt8503df62007-10-16 01:29:08 -07002627 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628}
2629
2630static struct pci_driver cirrusfb_pci_driver = {
2631 .name = "cirrusfb",
2632 .id_table = cirrusfb_pci_table,
2633 .probe = cirrusfb_pci_register,
2634 .remove = __devexit_p(cirrusfb_pci_unregister),
2635#ifdef CONFIG_PM
2636#if 0
2637 .suspend = cirrusfb_pci_suspend,
2638 .resume = cirrusfb_pci_resume,
2639#endif
2640#endif
2641};
2642#endif /* CONFIG_PCI */
2643
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644#ifdef CONFIG_ZORRO
2645static int cirrusfb_zorro_register(struct zorro_dev *z,
2646 const struct zorro_device_id *ent)
2647{
2648 struct cirrusfb_info *cinfo;
2649 struct fb_info *info;
2650 cirrusfb_board_t btype;
2651 struct zorro_dev *z2 = NULL;
2652 unsigned long board_addr, board_size, size;
2653 int ret;
2654
2655 btype = ent->driver_data;
2656 if (cirrusfb_zorro_table2[btype].id2)
2657 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2658 size = cirrusfb_zorro_table2[btype].size;
2659 printk(KERN_INFO "cirrusfb: %s board detected; ",
2660 cirrusfb_board_info[btype].name);
2661
2662 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2663 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002664 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 ret = -ENOMEM;
2666 goto err_out;
2667 }
2668
2669 cinfo = info->par;
2670 cinfo->info = info;
2671 cinfo->btype = btype;
2672
Krzysztof Helt8503df62007-10-16 01:29:08 -07002673 assert(z > 0);
2674 assert(z2 >= 0);
2675 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676
2677 cinfo->zdev = z;
2678 board_addr = zorro_resource_start(z);
2679 board_size = zorro_resource_len(z);
2680 cinfo->size = size;
2681
2682 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002683 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2684 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685 board_addr);
2686 ret = -EBUSY;
2687 goto err_release_fb;
2688 }
2689
Krzysztof Helt8503df62007-10-16 01:29:08 -07002690 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691
2692 ret = -EIO;
2693
2694 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002695 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696
2697 /* To be precise, for the P4 this is not the */
2698 /* begin of the board, but the begin of RAM. */
2699 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2700 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002701 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 if (!cinfo->regbase)
2703 goto err_release_region;
2704
Krzysztof Helt8503df62007-10-16 01:29:08 -07002705 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2706 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 cinfo->regbase += 0x600000;
2708 cinfo->fbregs_phys = board_addr + 0x600000;
2709
2710 cinfo->fbmem_phys = board_addr + 16777216;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002711 cinfo->fbmem = ioremap(cinfo->fbmem_phys, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 if (!cinfo->fbmem)
2713 goto err_unmap_regbase;
2714 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002715 printk(KERN_INFO " REG at $%lx\n",
2716 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717
2718 cinfo->fbmem_phys = board_addr;
2719 if (board_addr > 0x01000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002720 cinfo->fbmem = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07002722 cinfo->fbmem = (caddr_t) ZTWO_VADDR(board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 if (!cinfo->fbmem)
2724 goto err_release_region;
2725
2726 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002727 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728 cinfo->fbregs_phys = z2->resource.start;
2729
Krzysztof Helt8503df62007-10-16 01:29:08 -07002730 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2731 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732 }
2733 cinfo->unmap = cirrusfb_zorro_unmap;
2734
Krzysztof Helt8503df62007-10-16 01:29:08 -07002735 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 zorro_set_drvdata(z, info);
2737
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002738 ret = cirrusfb_register(cinfo);
2739 if (ret) {
2740 if (btype == BT_PICASSO4) {
2741 iounmap(cinfo->fbmem);
2742 iounmap(cinfo->regbase - 0x600000);
2743 } else if (board_addr > 0x01000000)
2744 iounmap(cinfo->fbmem);
2745 }
2746 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747
2748err_unmap_regbase:
2749 /* Parental advisory: explicit hack */
2750 iounmap(cinfo->regbase - 0x600000);
2751err_release_region:
2752 release_region(board_addr, board_size);
2753err_release_fb:
2754 framebuffer_release(info);
2755err_out:
2756 return ret;
2757}
2758
2759void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2760{
2761 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002762 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763
Krzysztof Helt8503df62007-10-16 01:29:08 -07002764 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765
Krzysztof Helt8503df62007-10-16 01:29:08 -07002766 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767}
2768
2769static struct zorro_driver cirrusfb_zorro_driver = {
2770 .name = "cirrusfb",
2771 .id_table = cirrusfb_zorro_table,
2772 .probe = cirrusfb_zorro_register,
2773 .remove = __devexit_p(cirrusfb_zorro_unregister),
2774};
2775#endif /* CONFIG_ZORRO */
2776
2777static int __init cirrusfb_init(void)
2778{
2779 int error = 0;
2780
2781#ifndef MODULE
2782 char *option = NULL;
2783
2784 if (fb_get_options("cirrusfb", &option))
2785 return -ENODEV;
2786 cirrusfb_setup(option);
2787#endif
2788
2789#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002790 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791#endif
2792#ifdef CONFIG_PCI
2793 error |= pci_register_driver(&cirrusfb_pci_driver);
2794#endif
2795 return error;
2796}
2797
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798#ifndef MODULE
2799static int __init cirrusfb_setup(char *options) {
2800 char *this_opt, s[32];
2801 int i;
2802
Krzysztof Helt8503df62007-10-16 01:29:08 -07002803 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804
2805 if (!options || !*options)
2806 return 0;
2807
Krzysztof Helt8503df62007-10-16 01:29:08 -07002808 while ((this_opt = strsep(&options, ",")) != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 if (!*this_opt) continue;
2810
2811 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2812
2813 for (i = 0; i < NUM_TOTAL_MODES; i++) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002814 sprintf(s, "mode:%s", cirrusfb_predefined[i].name);
2815 if (strcmp(this_opt, s) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 cirrusfb_def_mode = i;
2817 }
2818 if (!strcmp(this_opt, "noaccel"))
2819 noaccel = 1;
2820 }
2821 return 0;
2822}
2823#endif
2824
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 /*
2826 * Modularization
2827 */
2828
2829MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2830MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2831MODULE_LICENSE("GPL");
2832
Krzysztof Helt8503df62007-10-16 01:29:08 -07002833static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834{
2835#ifdef CONFIG_PCI
2836 pci_unregister_driver(&cirrusfb_pci_driver);
2837#endif
2838#ifdef CONFIG_ZORRO
2839 zorro_unregister_driver(&cirrusfb_zorro_driver);
2840#endif
2841}
2842
2843module_init(cirrusfb_init);
2844
2845#ifdef MODULE
2846module_exit(cirrusfb_exit);
2847#endif
2848
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849/**********************************************************************/
2850/* about the following functions - I have used the same names for the */
2851/* functions as Markus Wild did in his Retina driver for NetBSD as */
2852/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002853/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854/**********************************************************************/
2855
2856/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002857static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 int regnum, unsigned char val)
2859{
2860 unsigned long regofs = 0;
2861
2862 if (cinfo->btype == BT_PICASSO) {
2863 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002864/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2865 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2867 regofs = 0xfff;
2868 }
2869
Krzysztof Helt8503df62007-10-16 01:29:08 -07002870 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871}
2872
2873/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002874static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875{
2876 unsigned long regofs = 0;
2877
2878 if (cinfo->btype == BT_PICASSO) {
2879 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002880/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2881 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2883 regofs = 0xfff;
2884 }
2885
Krzysztof Helt8503df62007-10-16 01:29:08 -07002886 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887}
2888
2889/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002890static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002892 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893
Krzysztof Helt8503df62007-10-16 01:29:08 -07002894 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895
Krzysztof Helt8503df62007-10-16 01:29:08 -07002896 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 /* if we're just in "write value" mode, write back the */
2898 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002899 vga_w(cinfo->regbase, VGA_ATT_IW,
2900 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901 }
2902 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002903/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2904 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905
2906 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002907 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908
Krzysztof Helt8503df62007-10-16 01:29:08 -07002909 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910}
2911
2912/*** WHDR() - write into the Hidden DAC register ***/
2913/* as the HDR is the only extension register that requires special treatment
2914 * (the other extension registers are accessible just like the "ordinary"
2915 * registers of their functional group) here is a specialized routine for
2916 * accessing the HDR
2917 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002918static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919{
2920 unsigned char dummy;
2921
2922 if (cinfo->btype == BT_PICASSO) {
2923 /* Klaus' hint for correct access to HDR on some boards */
2924 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002925 WGen(cinfo, VGA_PEL_MSK, 0x00);
2926 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002928 dummy = RGen(cinfo, VGA_PEL_IW);
2929 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930 }
2931 /* now do the usual stuff to access the HDR */
2932
Krzysztof Helt8503df62007-10-16 01:29:08 -07002933 dummy = RGen(cinfo, VGA_PEL_MSK);
2934 udelay(200);
2935 dummy = RGen(cinfo, VGA_PEL_MSK);
2936 udelay(200);
2937 dummy = RGen(cinfo, VGA_PEL_MSK);
2938 udelay(200);
2939 dummy = RGen(cinfo, VGA_PEL_MSK);
2940 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002941
Krzysztof Helt8503df62007-10-16 01:29:08 -07002942 WGen(cinfo, VGA_PEL_MSK, val);
2943 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944
2945 if (cinfo->btype == BT_PICASSO) {
2946 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002947 dummy = RGen(cinfo, VGA_PEL_IW);
2948 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949
2950 /* and at the end, restore the mask value */
2951 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002952 WGen(cinfo, VGA_PEL_MSK, 0xff);
2953 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954 }
2955}
2956
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002958static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002959{
2960#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002961 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002963 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964#endif
2965}
2966
2967/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002968static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969{
2970#ifdef CONFIG_ZORRO
2971 /* writing an arbitrary value to this one causes the monitor switcher */
2972 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002973 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002974 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002975 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002976#endif
2977}
2978
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002980static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981 unsigned char green, unsigned char blue)
2982{
2983 unsigned int data = VGA_PEL_D;
2984
2985 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002986 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002987
2988 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2989 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2990 /* but DAC data register IS, at least for Picasso II */
2991 if (cinfo->btype == BT_PICASSO)
2992 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002993 vga_w(cinfo->regbase, data, red);
2994 vga_w(cinfo->regbase, data, green);
2995 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002997 vga_w(cinfo->regbase, data, blue);
2998 vga_w(cinfo->regbase, data, green);
2999 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003000 }
3001}
3002
Linus Torvalds1da177e2005-04-16 15:20:36 -07003003#if 0
3004/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07003005static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003006 unsigned char *green, unsigned char *blue)
3007{
3008 unsigned int data = VGA_PEL_D;
3009
Krzysztof Helt8503df62007-10-16 01:29:08 -07003010 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003011
3012 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
3013 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
3014 if (cinfo->btype == BT_PICASSO)
3015 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003016 *red = vga_r(cinfo->regbase, data);
3017 *green = vga_r(cinfo->regbase, data);
3018 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003020 *blue = vga_r(cinfo->regbase, data);
3021 *green = vga_r(cinfo->regbase, data);
3022 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003023 }
3024}
3025#endif
3026
Linus Torvalds1da177e2005-04-16 15:20:36 -07003027/*******************************************************************
3028 cirrusfb_WaitBLT()
3029
3030 Wait for the BitBLT engine to complete a possible earlier job
3031*********************************************************************/
3032
3033/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003034static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003035{
3036 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003037 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003038 /* do nothing */ ;
3039}
3040
3041/*******************************************************************
3042 cirrusfb_BitBLT()
3043
3044 perform accelerated "scrolling"
3045********************************************************************/
3046
Krzysztof Helt8503df62007-10-16 01:29:08 -07003047static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
3048 u_short curx, u_short cury,
3049 u_short destx, u_short desty,
3050 u_short width, u_short height,
3051 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052{
3053 u_short nwidth, nheight;
3054 u_long nsrc, ndest;
3055 u_char bltmode;
3056
Krzysztof Helt8503df62007-10-16 01:29:08 -07003057 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058
3059 nwidth = width - 1;
3060 nheight = height - 1;
3061
3062 bltmode = 0x00;
3063 /* if source adr < dest addr, do the Blt backwards */
3064 if (cury <= desty) {
3065 if (cury == desty) {
3066 /* if src and dest are on the same line, check x */
3067 if (curx < destx)
3068 bltmode |= 0x01;
3069 } else
3070 bltmode |= 0x01;
3071 }
3072 if (!bltmode) {
3073 /* standard case: forward blitting */
3074 nsrc = (cury * line_length) + curx;
3075 ndest = (desty * line_length) + destx;
3076 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003077 /* this means start addresses are at the end,
3078 * counting backwards
3079 */
3080 nsrc = cury * line_length + curx +
3081 nheight * line_length + nwidth;
3082 ndest = desty * line_length + destx +
3083 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 }
3085
3086 /*
3087 run-down of registers to be programmed:
3088 destination pitch
3089 source pitch
3090 BLT width/height
3091 source start
3092 destination start
3093 BLT mode
3094 BLT ROP
3095 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
3096 start/stop
3097 */
3098
Krzysztof Helt8503df62007-10-16 01:29:08 -07003099 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100
3101 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003102 /* dest pitch low */
3103 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
3104 /* dest pitch hi */
3105 vga_wgfx(regbase, CL_GR25, line_length >> 8);
3106 /* source pitch low */
3107 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
3108 /* source pitch hi */
3109 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003110
3111 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003112 /* BLT width low */
3113 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
3114 /* BLT width hi */
3115 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116
3117 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003118 /* BLT height low */
3119 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
3120 /* BLT width hi */
3121 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122
3123 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003124 /* BLT dest low */
3125 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
3126 /* BLT dest mid */
3127 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
3128 /* BLT dest hi */
3129 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003130
3131 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003132 /* BLT src low */
3133 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
3134 /* BLT src mid */
3135 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
3136 /* BLT src hi */
3137 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003138
3139 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003140 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141
3142 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003143 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144
3145 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003146 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147
Krzysztof Helt8503df62007-10-16 01:29:08 -07003148 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149}
3150
Linus Torvalds1da177e2005-04-16 15:20:36 -07003151/*******************************************************************
3152 cirrusfb_RectFill()
3153
3154 perform accelerated rectangle fill
3155********************************************************************/
3156
Krzysztof Helt8503df62007-10-16 01:29:08 -07003157static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158 u_short x, u_short y, u_short width, u_short height,
3159 u_char color, u_short line_length)
3160{
3161 u_short nwidth, nheight;
3162 u_long ndest;
3163 u_char op;
3164
Krzysztof Helt8503df62007-10-16 01:29:08 -07003165 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003166
3167 nwidth = width - 1;
3168 nheight = height - 1;
3169
3170 ndest = (y * line_length) + x;
3171
Krzysztof Helt8503df62007-10-16 01:29:08 -07003172 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173
3174 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003175 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
3176 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
3177 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
3178 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179
3180 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003181 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
3182 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003183
3184 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003185 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
3186 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187
3188 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003189 /* BLT dest low */
3190 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
3191 /* BLT dest mid */
3192 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
3193 /* BLT dest hi */
3194 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195
3196 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003197 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
3198 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
3199 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003200
3201 /* This is a ColorExpand Blt, using the */
3202 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003203 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
3204 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003205
3206 op = 0xc0;
3207 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003208 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3209 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003210 op = 0x50;
3211 op = 0xd0;
3212 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003213 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
3214 vga_wgfx(regbase, CL_GR11, color); /* background color */
3215 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
3216 vga_wgfx(regbase, CL_GR13, color); /* background color */
3217 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
3218 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 op = 0x50;
3220 op = 0xf0;
3221 }
3222 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003223 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003224
3225 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003226 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003227
3228 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07003229 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003230
Krzysztof Helt8503df62007-10-16 01:29:08 -07003231 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003232}
3233
Linus Torvalds1da177e2005-04-16 15:20:36 -07003234/**************************************************************************
3235 * bestclock() - determine closest possible clock lower(?) than the
3236 * desired pixel clock
3237 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07003238static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003239 long *den, long *div, long maxfreq)
3240{
3241 long n, h, d, f;
3242
Krzysztof Helt8503df62007-10-16 01:29:08 -07003243 assert(best != NULL);
3244 assert(nom != NULL);
3245 assert(den != NULL);
3246 assert(div != NULL);
3247 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003248
3249 *nom = 0;
3250 *den = 0;
3251 *div = 0;
3252
Krzysztof Helt8503df62007-10-16 01:29:08 -07003253 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003254
3255 if (freq < 8000)
3256 freq = 8000;
3257
3258 if (freq > maxfreq)
3259 freq = maxfreq;
3260
3261 *best = 0;
3262 f = freq * 10;
3263
3264 for (n = 32; n < 128; n++) {
3265 d = (143181 * n) / f;
3266 if ((d >= 7) && (d <= 63)) {
3267 if (d > 31)
3268 d = (d / 2) * 2;
3269 h = (14318 * n) / d;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003270 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271 *best = h;
3272 *nom = n;
3273 if (d < 32) {
3274 *den = d;
3275 *div = 0;
3276 } else {
3277 *den = d / 2;
3278 *div = 1;
3279 }
3280 }
3281 }
3282 d = ((143181 * n) + f - 1) / f;
3283 if ((d >= 7) && (d <= 63)) {
3284 if (d > 31)
3285 d = (d / 2) * 2;
3286 h = (14318 * n) / d;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003287 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003288 *best = h;
3289 *nom = n;
3290 if (d < 32) {
3291 *den = d;
3292 *div = 0;
3293 } else {
3294 *den = d / 2;
3295 *div = 1;
3296 }
3297 }
3298 }
3299 }
3300
Krzysztof Helt8503df62007-10-16 01:29:08 -07003301 DPRINTK("Best possible values for given frequency:\n");
3302 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
3303 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003304
Krzysztof Helt8503df62007-10-16 01:29:08 -07003305 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003306}
3307
Linus Torvalds1da177e2005-04-16 15:20:36 -07003308/* -------------------------------------------------------------------------
3309 *
3310 * debugging functions
3311 *
3312 * -------------------------------------------------------------------------
3313 */
3314
3315#ifdef CIRRUSFB_DEBUG
3316
3317/**
3318 * cirrusfb_dbg_print_byte
3319 * @name: name associated with byte value to be displayed
3320 * @val: byte value to be displayed
3321 *
3322 * DESCRIPTION:
3323 * Display an indented string, along with a hexidecimal byte value, and
3324 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3325 * order.
3326 */
3327
3328static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003329void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003330{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003331 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3332 name, val,
3333 val & 0x80 ? '1' : '0',
3334 val & 0x40 ? '1' : '0',
3335 val & 0x20 ? '1' : '0',
3336 val & 0x10 ? '1' : '0',
3337 val & 0x08 ? '1' : '0',
3338 val & 0x04 ? '1' : '0',
3339 val & 0x02 ? '1' : '0',
3340 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003341}
3342
Linus Torvalds1da177e2005-04-16 15:20:36 -07003343/**
3344 * cirrusfb_dbg_print_regs
3345 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3346 * @reg_class: type of registers to read: %CRT, or %SEQ
3347 *
3348 * DESCRIPTION:
3349 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3350 * old-style I/O ports are queried for information, otherwise MMIO is
3351 * used at the given @base address to query the information.
3352 */
3353
3354static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003355void cirrusfb_dbg_print_regs(caddr_t regbase,
3356 cirrusfb_dbg_reg_class_t reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003357{
3358 va_list list;
3359 unsigned char val = 0;
3360 unsigned reg;
3361 char *name;
3362
Krzysztof Helt8503df62007-10-16 01:29:08 -07003363 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003364
Krzysztof Helt8503df62007-10-16 01:29:08 -07003365 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003366 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003367 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003368
3369 switch (reg_class) {
3370 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003371 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003372 break;
3373 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003374 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003375 break;
3376 default:
3377 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003378 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003379 break;
3380 }
3381
Krzysztof Helt8503df62007-10-16 01:29:08 -07003382 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003383
Krzysztof Helt8503df62007-10-16 01:29:08 -07003384 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003385 }
3386
Krzysztof Helt8503df62007-10-16 01:29:08 -07003387 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388}
3389
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390/**
3391 * cirrusfb_dump
3392 * @cirrusfbinfo:
3393 *
3394 * DESCRIPTION:
3395 */
3396
Krzysztof Helt8503df62007-10-16 01:29:08 -07003397static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003398{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003399 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400}
3401
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402/**
3403 * cirrusfb_dbg_reg_dump
3404 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3405 *
3406 * DESCRIPTION:
3407 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3408 * old-style I/O ports are queried for information, otherwise MMIO is
3409 * used at the given @base address to query the information.
3410 */
3411
3412static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003413void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003414{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003415 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003416
Krzysztof Helt8503df62007-10-16 01:29:08 -07003417 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418 "CR00", 0x00,
3419 "CR01", 0x01,
3420 "CR02", 0x02,
3421 "CR03", 0x03,
3422 "CR04", 0x04,
3423 "CR05", 0x05,
3424 "CR06", 0x06,
3425 "CR07", 0x07,
3426 "CR08", 0x08,
3427 "CR09", 0x09,
3428 "CR0A", 0x0A,
3429 "CR0B", 0x0B,
3430 "CR0C", 0x0C,
3431 "CR0D", 0x0D,
3432 "CR0E", 0x0E,
3433 "CR0F", 0x0F,
3434 "CR10", 0x10,
3435 "CR11", 0x11,
3436 "CR12", 0x12,
3437 "CR13", 0x13,
3438 "CR14", 0x14,
3439 "CR15", 0x15,
3440 "CR16", 0x16,
3441 "CR17", 0x17,
3442 "CR18", 0x18,
3443 "CR22", 0x22,
3444 "CR24", 0x24,
3445 "CR26", 0x26,
3446 "CR2D", 0x2D,
3447 "CR2E", 0x2E,
3448 "CR2F", 0x2F,
3449 "CR30", 0x30,
3450 "CR31", 0x31,
3451 "CR32", 0x32,
3452 "CR33", 0x33,
3453 "CR34", 0x34,
3454 "CR35", 0x35,
3455 "CR36", 0x36,
3456 "CR37", 0x37,
3457 "CR38", 0x38,
3458 "CR39", 0x39,
3459 "CR3A", 0x3A,
3460 "CR3B", 0x3B,
3461 "CR3C", 0x3C,
3462 "CR3D", 0x3D,
3463 "CR3E", 0x3E,
3464 "CR3F", 0x3F,
3465 NULL);
3466
Krzysztof Helt8503df62007-10-16 01:29:08 -07003467 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003468
Krzysztof Helt8503df62007-10-16 01:29:08 -07003469 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003470
Krzysztof Helt8503df62007-10-16 01:29:08 -07003471 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003472 "SR00", 0x00,
3473 "SR01", 0x01,
3474 "SR02", 0x02,
3475 "SR03", 0x03,
3476 "SR04", 0x04,
3477 "SR08", 0x08,
3478 "SR09", 0x09,
3479 "SR0A", 0x0A,
3480 "SR0B", 0x0B,
3481 "SR0D", 0x0D,
3482 "SR10", 0x10,
3483 "SR11", 0x11,
3484 "SR12", 0x12,
3485 "SR13", 0x13,
3486 "SR14", 0x14,
3487 "SR15", 0x15,
3488 "SR16", 0x16,
3489 "SR17", 0x17,
3490 "SR18", 0x18,
3491 "SR19", 0x19,
3492 "SR1A", 0x1A,
3493 "SR1B", 0x1B,
3494 "SR1C", 0x1C,
3495 "SR1D", 0x1D,
3496 "SR1E", 0x1E,
3497 "SR1F", 0x1F,
3498 NULL);
3499
Krzysztof Helt8503df62007-10-16 01:29:08 -07003500 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003501}
3502
3503#endif /* CIRRUSFB_DEBUG */
3504