blob: 4c16b68a9c5acd6cc0321cfbab6dbe5ec88cd262 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
37#define CIRRUSFB_VERSION "2.0-pre2"
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/module.h>
40#include <linux/kernel.h>
41#include <linux/errno.h>
42#include <linux/string.h>
43#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/slab.h>
45#include <linux/delay.h>
46#include <linux/fb.h>
47#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <asm/pgtable.h>
49
50#ifdef CONFIG_ZORRO
51#include <linux/zorro.h>
52#endif
53#ifdef CONFIG_PCI
54#include <linux/pci.h>
55#endif
56#ifdef CONFIG_AMIGA
57#include <asm/amigahw.h>
58#endif
59#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110060#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070061#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#else
63#define isPReP 0
64#endif
65
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070066#include <video/vga.h>
67#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/*****************************************************************
70 *
71 * debugging and utility macros
72 *
73 */
74
75/* enable debug output? */
76/* #define CIRRUSFB_DEBUG 1 */
77
78/* disable runtime assertions? */
79/* #define CIRRUSFB_NDEBUG */
80
81/* debug output */
82#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -070083#define DPRINTK(fmt, args...) \
Harvey Harrison5ae12172008-04-28 02:15:47 -070084 printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#else
86#define DPRINTK(fmt, args...)
87#endif
88
89/* debugging assertions */
90#ifndef CIRRUSFB_NDEBUG
91#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070092 if (!(expr)) { \
93 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070094 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070095 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070096#else
97#define assert(expr)
98#endif
99
Krzysztof Helt8503df62007-10-16 01:29:08 -0700100#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*****************************************************************
103 *
104 * chipset information
105 *
106 */
107
108/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700109enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 BT_NONE = 0,
111 BT_SD64,
112 BT_PICCOLO,
113 BT_PICASSO,
114 BT_SPECTRUM,
115 BT_PICASSO4, /* GD5446 */
116 BT_ALPINE, /* GD543x/4x */
117 BT_GD5480,
118 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700119};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * per-board-type information, used for enumerating and abstracting
123 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700124 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 * use direct indexing on this array
126 * NOTE: '__initdata' cannot be used as some of this info
127 * is required at runtime. Maybe separate into an init-only and
128 * a run-time table?
129 */
130static const struct cirrusfb_board_info_rec {
131 char *name; /* ASCII name of chipset */
132 long maxclock[5]; /* maximum video clock */
133 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700134 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
135 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700136 /* construct bit 19 of screen start address */
137 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139 /* initial SR07 value, then for each mode */
140 unsigned char sr07;
141 unsigned char sr07_1bpp;
142 unsigned char sr07_1bpp_mux;
143 unsigned char sr07_8bpp;
144 unsigned char sr07_8bpp_mux;
145
146 unsigned char sr1f; /* SR1F VGA initial register value */
147} cirrusfb_board_info[] = {
148 [BT_SD64] = {
149 .name = "CL SD64",
150 .maxclock = {
151 /* guess */
152 /* the SD64/P4 have a higher max. videoclock */
153 140000, 140000, 140000, 140000, 140000,
154 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700155 .init_sr07 = true,
156 .init_sr1f = true,
157 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 .sr07 = 0xF0,
159 .sr07_1bpp = 0xF0,
160 .sr07_8bpp = 0xF1,
161 .sr1f = 0x20
162 },
163 [BT_PICCOLO] = {
164 .name = "CL Piccolo",
165 .maxclock = {
166 /* guess */
167 90000, 90000, 90000, 90000, 90000
168 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700169 .init_sr07 = true,
170 .init_sr1f = true,
171 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .sr07 = 0x80,
173 .sr07_1bpp = 0x80,
174 .sr07_8bpp = 0x81,
175 .sr1f = 0x22
176 },
177 [BT_PICASSO] = {
178 .name = "CL Picasso",
179 .maxclock = {
180 /* guess */
181 90000, 90000, 90000, 90000, 90000
182 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700183 .init_sr07 = true,
184 .init_sr1f = true,
185 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 .sr07 = 0x20,
187 .sr07_1bpp = 0x20,
188 .sr07_8bpp = 0x21,
189 .sr1f = 0x22
190 },
191 [BT_SPECTRUM] = {
192 .name = "CL Spectrum",
193 .maxclock = {
194 /* guess */
195 90000, 90000, 90000, 90000, 90000
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = true,
199 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x80,
201 .sr07_1bpp = 0x80,
202 .sr07_8bpp = 0x81,
203 .sr1f = 0x22
204 },
205 [BT_PICASSO4] = {
206 .name = "CL Picasso4",
207 .maxclock = {
208 135100, 135100, 85500, 85500, 0
209 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700210 .init_sr07 = true,
211 .init_sr1f = false,
212 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 .sr07 = 0x20,
214 .sr07_1bpp = 0x20,
215 .sr07_8bpp = 0x21,
216 .sr1f = 0
217 },
218 [BT_ALPINE] = {
219 .name = "CL Alpine",
220 .maxclock = {
221 /* for the GD5430. GD5446 can do more... */
222 85500, 85500, 50000, 28500, 0
223 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700224 .init_sr07 = true,
225 .init_sr1f = true,
226 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .sr07 = 0xA0,
228 .sr07_1bpp = 0xA1,
229 .sr07_1bpp_mux = 0xA7,
230 .sr07_8bpp = 0xA1,
231 .sr07_8bpp_mux = 0xA7,
232 .sr1f = 0x1C
233 },
234 [BT_GD5480] = {
235 .name = "CL GD5480",
236 .maxclock = {
237 135100, 200000, 200000, 135100, 135100
238 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700239 .init_sr07 = true,
240 .init_sr1f = true,
241 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 .sr07 = 0x10,
243 .sr07_1bpp = 0x11,
244 .sr07_8bpp = 0x11,
245 .sr1f = 0x1C
246 },
247 [BT_LAGUNA] = {
248 .name = "CL Laguna",
249 .maxclock = {
250 /* guess */
251 135100, 135100, 135100, 135100, 135100,
252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329struct cirrusfb_regs {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 long freq;
331 long nom;
332 long den;
333 long div;
334 long multiplexing;
335 long mclk;
336 long divMCLK;
337
338 long HorizRes; /* The x resolution in pixel */
339 long HorizTotal;
340 long HorizDispEnd;
341 long HorizBlankStart;
342 long HorizBlankEnd;
343 long HorizSyncStart;
344 long HorizSyncEnd;
345
346 long VertRes; /* the physical y resolution in scanlines */
347 long VertTotal;
348 long VertDispEnd;
349 long VertSyncStart;
350 long VertSyncEnd;
351 long VertBlankStart;
352 long VertBlankEnd;
353};
354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700356enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700357 CRT,
358 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700359};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362/* info about board */
363struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700365 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 unsigned char SFR; /* Shadow of special function register */
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 struct cirrusfb_regs currentmode;
369 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700370 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700372 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373};
374
Krzysztof Helt8503df62007-10-16 01:29:08 -0700375static int noaccel;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700376static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378/****************************************************************************/
379/**** BEGIN PROTOTYPES ******************************************************/
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700382static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700384static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385#endif
386
Krzysztof Helt8503df62007-10-16 01:29:08 -0700387static int cirrusfb_open(struct fb_info *info, int user);
388static int cirrusfb_release(struct fb_info *info, int user);
389static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
390 unsigned blue, unsigned transp,
391 struct fb_info *info);
392static int cirrusfb_check_var(struct fb_var_screeninfo *var,
393 struct fb_info *info);
394static int cirrusfb_set_par(struct fb_info *info);
395static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
396 struct fb_info *info);
397static int cirrusfb_blank(int blank_mode, struct fb_info *info);
398static void cirrusfb_fillrect(struct fb_info *info,
399 const struct fb_fillrect *region);
400static void cirrusfb_copyarea(struct fb_info *info,
401 const struct fb_copyarea *area);
402static void cirrusfb_imageblit(struct fb_info *info,
403 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405/* function table of the above functions */
406static struct fb_ops cirrusfb_ops = {
407 .owner = THIS_MODULE,
408 .fb_open = cirrusfb_open,
409 .fb_release = cirrusfb_release,
410 .fb_setcolreg = cirrusfb_setcolreg,
411 .fb_check_var = cirrusfb_check_var,
412 .fb_set_par = cirrusfb_set_par,
413 .fb_pan_display = cirrusfb_pan_display,
414 .fb_blank = cirrusfb_blank,
415 .fb_fillrect = cirrusfb_fillrect,
416 .fb_copyarea = cirrusfb_copyarea,
417 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418};
419
420/*--- Hardware Specific Routines -------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700423 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700425static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700426static void switch_monitor(struct cirrusfb_info *cinfo, int on);
427static void WGen(const struct cirrusfb_info *cinfo,
428 int regnum, unsigned char val);
429static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
430static void AttrOn(const struct cirrusfb_info *cinfo);
431static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
432static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
433static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
434static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
435 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700437static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
438 unsigned char *red, unsigned char *green,
439 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700441static void cirrusfb_WaitBLT(u8 __iomem *regbase);
442static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
443 u_short curx, u_short cury,
444 u_short destx, u_short desty,
445 u_short width, u_short height,
446 u_short line_length);
447static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
448 u_short x, u_short y,
449 u_short width, u_short height,
450 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
Krzysztof Helt8503df62007-10-16 01:29:08 -0700452static void bestclock(long freq, long *best,
453 long *nom, long *den,
454 long *div, long maxfreq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
456#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700457static void cirrusfb_dump(void);
458static void cirrusfb_dbg_reg_dump(caddr_t regbase);
459static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700460 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700461static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462#endif /* CIRRUSFB_DEBUG */
463
464/*** END PROTOTYPES ********************************************************/
465/*****************************************************************************/
466/*** BEGIN Interface Used by the World ***************************************/
467
Krzysztof Helt8503df62007-10-16 01:29:08 -0700468static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
470/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700471static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
473 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700474 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 return 0;
476}
477
478/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700479static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
481 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700482 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 return 0;
484}
485
486/**** END Interface used by the World *************************************/
487/****************************************************************************/
488/**** BEGIN Hardware specific Routines **************************************/
489
490/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700491static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
493 long mclk;
494
Krzysztof Helt8503df62007-10-16 01:29:08 -0700495 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
497 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
498 * Assume a 64-bit data path for now. The formula is:
499 * ((B * PCLK * 2)/W) * 1.2
500 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
501 mclk = ((bpp / 8) * freq * 2) / 4;
502 mclk = (mclk * 12) / 10;
503 if (mclk < 50000)
504 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700505 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
507 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
508 mclk = ((mclk * 16) / 14318);
509 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700510 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 /* Determine if we should use MCLK instead of VCLK, and if so, what we
513 * should divide it by to get VCLK */
514 switch (freq) {
515 case 24751 ... 25249:
516 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700517 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 break;
519 case 49501 ... 50499:
520 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700521 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 break;
523 default:
524 *div = 0;
525 break;
526 }
527
528 return mclk;
529}
530
531static int cirrusfb_check_var(struct fb_var_screeninfo *var,
532 struct fb_info *info)
533{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700534 int yres;
535 /* memory size in pixels */
536 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
538 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700539 case 1:
Krzysztof Helt09a29102008-09-02 14:35:51 -0700540 pixels /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700542 case 8:
543 case 16:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700544 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700547 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
548 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700550 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 return -EINVAL;
552 }
553
Krzysztof Helt09a29102008-09-02 14:35:51 -0700554 if (var->xres_virtual < var->xres)
555 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700557 if (var->yres_virtual == -1) {
558 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
Krzysztof Helt8503df62007-10-16 01:29:08 -0700560 printk(KERN_INFO "cirrusfb: virtual resolution set to "
561 "maximum of %dx%d\n", var->xres_virtual,
562 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 if (var->yres_virtual < var->yres)
565 var->yres_virtual = var->yres;
566
Krzysztof Helt09a29102008-09-02 14:35:51 -0700567 if (var->xres_virtual * var->yres_virtual > pixels) {
568 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
569 "virtual resolution too high to fit into video memory!\n",
570 var->xres_virtual, var->yres_virtual,
571 var->bits_per_pixel);
572 DPRINTK("EXIT - EINVAL error\n");
573 return -EINVAL;
574 }
575
576
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 if (var->xoffset < 0)
578 var->xoffset = 0;
579 if (var->yoffset < 0)
580 var->yoffset = 0;
581
582 /* truncate xoffset and yoffset to maximum if too high */
583 if (var->xoffset > var->xres_virtual - var->xres)
584 var->xoffset = var->xres_virtual - var->xres - 1;
585 if (var->yoffset > var->yres_virtual - var->yres)
586 var->yoffset = var->yres_virtual - var->yres - 1;
587
588 switch (var->bits_per_pixel) {
589 case 1:
590 var->red.offset = 0;
591 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700592 var->green = var->red;
593 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 break;
595
596 case 8:
597 var->red.offset = 0;
598 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700599 var->green = var->red;
600 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 break;
602
603 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700604 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 var->red.offset = 2;
606 var->green.offset = -3;
607 var->blue.offset = 8;
608 } else {
609 var->red.offset = 10;
610 var->green.offset = 5;
611 var->blue.offset = 0;
612 }
613 var->red.length = 5;
614 var->green.length = 5;
615 var->blue.length = 5;
616 break;
617
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700619 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 var->red.offset = 8;
621 var->green.offset = 16;
622 var->blue.offset = 24;
623 } else {
624 var->red.offset = 16;
625 var->green.offset = 8;
626 var->blue.offset = 0;
627 }
628 var->red.length = 8;
629 var->green.length = 8;
630 var->blue.length = 8;
631 break;
632
633 default:
634 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700635 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 /* should never occur */
637 break;
638 }
639
640 var->red.msb_right =
641 var->green.msb_right =
642 var->blue.msb_right =
643 var->transp.offset =
644 var->transp.length =
645 var->transp.msb_right = 0;
646
647 yres = var->yres;
648 if (var->vmode & FB_VMODE_DOUBLE)
649 yres *= 2;
650 else if (var->vmode & FB_VMODE_INTERLACED)
651 yres = (yres + 1) / 2;
652
653 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700654 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
655 "special treatment required! (TODO)\n");
656 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 return -EINVAL;
658 }
659
660 return 0;
661}
662
Krzysztof Helt8503df62007-10-16 01:29:08 -0700663static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700665 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666{
667 long freq;
668 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700669 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 struct cirrusfb_info *cinfo = info->par;
671 int xres, hfront, hsync, hback;
672 int yres, vfront, vsync, vback;
673
Krzysztof Helt8503df62007-10-16 01:29:08 -0700674 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700676 info->fix.line_length = var->xres_virtual / 8;
677 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 break;
679
680 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700681 info->fix.line_length = var->xres_virtual;
682 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 break;
684
685 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700687 info->fix.line_length = var->xres_virtual * maxclockidx;
688 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 break;
690
691 default:
692 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700693 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 /* should never occur */
695 break;
696 }
697
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700698 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
700 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700701 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
Krzysztof Helt8503df62007-10-16 01:29:08 -0700703 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
706 regs->multiplexing = 0;
707
708 /* If the frequency is greater than we can support, we might be able
709 * to use multiplexing for the video mode */
710 if (freq > maxclock) {
711 switch (cinfo->btype) {
712 case BT_ALPINE:
713 case BT_GD5480:
714 regs->multiplexing = 1;
715 break;
716
717 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700718 printk(KERN_ERR "cirrusfb: Frequency greater "
719 "than maxclock (%ld kHz)\n", maxclock);
720 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 return -EINVAL;
722 }
723 }
724#if 0
725 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
726 * the VCLK is double the pixel clock. */
727 switch (var->bits_per_pixel) {
728 case 16:
729 case 32:
730 if (regs->HorizRes <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700731 /* Xbh has this type of clock for 32-bit */
732 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 break;
734 }
735#endif
736
Krzysztof Helt8503df62007-10-16 01:29:08 -0700737 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
738 maxclock);
739 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
740 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
742 xres = var->xres;
743 hfront = var->right_margin;
744 hsync = var->hsync_len;
745 hback = var->left_margin;
746
747 yres = var->yres;
748 vfront = var->lower_margin;
749 vsync = var->vsync_len;
750 vback = var->upper_margin;
751
752 if (var->vmode & FB_VMODE_DOUBLE) {
753 yres *= 2;
754 vfront *= 2;
755 vsync *= 2;
756 vback *= 2;
757 } else if (var->vmode & FB_VMODE_INTERLACED) {
758 yres = (yres + 1) / 2;
759 vfront = (vfront + 1) / 2;
760 vsync = (vsync + 1) / 2;
761 vback = (vback + 1) / 2;
762 }
763 regs->HorizRes = xres;
764 regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
765 regs->HorizDispEnd = xres / 8 - 1;
766 regs->HorizBlankStart = xres / 8;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700767 /* does not count with "-5" */
768 regs->HorizBlankEnd = regs->HorizTotal + 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 regs->HorizSyncStart = (xres + hfront) / 8 + 1;
770 regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
771
772 regs->VertRes = yres;
773 regs->VertTotal = yres + vfront + vsync + vback - 2;
774 regs->VertDispEnd = yres - 1;
775 regs->VertBlankStart = yres;
776 regs->VertBlankEnd = regs->VertTotal;
777 regs->VertSyncStart = yres + vfront - 1;
778 regs->VertSyncEnd = yres + vfront + vsync - 1;
779
780 if (regs->VertRes >= 1024) {
781 regs->VertTotal /= 2;
782 regs->VertSyncStart /= 2;
783 regs->VertSyncEnd /= 2;
784 regs->VertDispEnd /= 2;
785 }
786 if (regs->multiplexing) {
787 regs->HorizTotal /= 2;
788 regs->HorizSyncStart /= 2;
789 regs->HorizSyncEnd /= 2;
790 regs->HorizDispEnd /= 2;
791 }
792
793 return 0;
794}
795
Krzysztof Helt8503df62007-10-16 01:29:08 -0700796static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
797 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700799 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 if (div == 2) {
802 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700803 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
804 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
805 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 } else if (div == 1) {
807 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700808 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
809 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
810 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700812 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 }
814}
815
816/*************************************************************************
817 cirrusfb_set_par_foo()
818
819 actually writes the values for a new video mode into the hardware,
820**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700821static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822{
823 struct cirrusfb_info *cinfo = info->par;
824 struct fb_var_screeninfo *var = &info->var;
825 struct cirrusfb_regs regs;
826 u8 __iomem *regbase = cinfo->regbase;
827 unsigned char tmp;
828 int offset = 0, err;
829 const struct cirrusfb_board_info_rec *bi;
830
Krzysztof Helt8503df62007-10-16 01:29:08 -0700831 DPRINTK("ENTER\n");
832 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700836 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
838 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700839 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 /* should never happen */
841 DPRINTK("mode change aborted. invalid var.\n");
842 return -EINVAL;
843 }
844
845 bi = &cirrusfb_board_info[cinfo->btype];
846
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700848 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
850 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700851 DPRINTK("CRT0: %ld\n", regs.HorizTotal);
852 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Krzysztof Helt8503df62007-10-16 01:29:08 -0700854 DPRINTK("CRT1: %ld\n", regs.HorizDispEnd);
855 vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Krzysztof Helt8503df62007-10-16 01:29:08 -0700857 DPRINTK("CRT2: %ld\n", regs.HorizBlankStart);
858 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859
Krzysztof Helt8503df62007-10-16 01:29:08 -0700860 /* + 128: Compatible read */
861 DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);
862 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
863 128 + (regs.HorizBlankEnd % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Krzysztof Helt8503df62007-10-16 01:29:08 -0700865 DPRINTK("CRT4: %ld\n", regs.HorizSyncStart);
866 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
868 tmp = regs.HorizSyncEnd % 32;
869 if (regs.HorizBlankEnd & 32)
870 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700871 DPRINTK("CRT5: %d\n", tmp);
872 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
Krzysztof Helt8503df62007-10-16 01:29:08 -0700874 DPRINTK("CRT6: %ld\n", regs.VertTotal & 0xff);
875 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
877 tmp = 16; /* LineCompare bit #9 */
878 if (regs.VertTotal & 256)
879 tmp |= 1;
880 if (regs.VertDispEnd & 256)
881 tmp |= 2;
882 if (regs.VertSyncStart & 256)
883 tmp |= 4;
884 if (regs.VertBlankStart & 256)
885 tmp |= 8;
886 if (regs.VertTotal & 512)
887 tmp |= 32;
888 if (regs.VertDispEnd & 512)
889 tmp |= 64;
890 if (regs.VertSyncStart & 512)
891 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700892 DPRINTK("CRT7: %d\n", tmp);
893 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895 tmp = 0x40; /* LineCompare bit #8 */
896 if (regs.VertBlankStart & 512)
897 tmp |= 0x20;
898 if (var->vmode & FB_VMODE_DOUBLE)
899 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700900 DPRINTK("CRT9: %d\n", tmp);
901 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Krzysztof Helt8503df62007-10-16 01:29:08 -0700903 DPRINTK("CRT10: %ld\n", regs.VertSyncStart & 0xff);
904 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, regs.VertSyncStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
Krzysztof Helt8503df62007-10-16 01:29:08 -0700906 DPRINTK("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
907 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, regs.VertSyncEnd % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
Krzysztof Helt8503df62007-10-16 01:29:08 -0700909 DPRINTK("CRT12: %ld\n", regs.VertDispEnd & 0xff);
910 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, regs.VertDispEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 DPRINTK("CRT15: %ld\n", regs.VertBlankStart & 0xff);
913 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, regs.VertBlankStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Krzysztof Helt8503df62007-10-16 01:29:08 -0700915 DPRINTK("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
916 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, regs.VertBlankEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Krzysztof Helt8503df62007-10-16 01:29:08 -0700918 DPRINTK("CRT18: 0xff\n");
919 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921 tmp = 0;
922 if (var->vmode & FB_VMODE_INTERLACED)
923 tmp |= 1;
924 if (regs.HorizBlankEnd & 64)
925 tmp |= 16;
926 if (regs.HorizBlankEnd & 128)
927 tmp |= 32;
928 if (regs.VertBlankEnd & 256)
929 tmp |= 64;
930 if (regs.VertBlankEnd & 512)
931 tmp |= 128;
932
Krzysztof Helt8503df62007-10-16 01:29:08 -0700933 DPRINTK("CRT1a: %d\n", tmp);
934 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936 /* set VCLK0 */
937 /* hardware RefClock: 14.31818 MHz */
938 /* formula: VClk = (OSC * N) / (D * (1+P)) */
939 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
940
Krzysztof Helt8503df62007-10-16 01:29:08 -0700941 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 tmp = regs.den << 1;
943 if (regs.div != 0)
944 tmp |= 1;
945
Krzysztof Helt8503df62007-10-16 01:29:08 -0700946 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 if ((cinfo->btype == BT_SD64) ||
948 (cinfo->btype == BT_ALPINE) ||
949 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -0700950 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
953 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955 if (regs.VertRes >= 1024)
956 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700957 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 else
959 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
960 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700961 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962
Krzysztof Helt8503df62007-10-16 01:29:08 -0700963/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
964 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
966 /* don't know if it would hurt to also program this if no interlaced */
967 /* mode is used, but I feel better this way.. :-) */
968 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700969 vga_wcrt(regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700971 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
Krzysztof Helt8503df62007-10-16 01:29:08 -0700973 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974
975 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700976 /* enable display memory & CRTC I/O address for color mode */
977 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
979 tmp |= 0x40;
980 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
981 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700982 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983
Krzysztof Helt8503df62007-10-16 01:29:08 -0700984 /* Screen A Preset Row-Scan register */
985 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
986 /* text cursor on and start line */
987 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
988 /* text cursor end line */
989 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
991 /******************************************************
992 *
993 * 1 bpp
994 *
995 */
996
997 /* programming for different color depths */
998 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700999 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
1000 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001
1002 /* SR07 */
1003 switch (cinfo->btype) {
1004 case BT_SD64:
1005 case BT_PICCOLO:
1006 case BT_PICASSO:
1007 case BT_SPECTRUM:
1008 case BT_PICASSO4:
1009 case BT_ALPINE:
1010 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001011 DPRINTK(" (for GD54xx)\n");
1012 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 regs.multiplexing ?
1014 bi->sr07_1bpp_mux : bi->sr07_1bpp);
1015 break;
1016
1017 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001018 DPRINTK(" (for GD546x)\n");
1019 vga_wseq(regbase, CL_SEQR7,
1020 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 break;
1022
1023 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001024 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 break;
1026 }
1027
1028 /* Extended Sequencer Mode */
1029 switch (cinfo->btype) {
1030 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001031 /* setting the SEQRF on SD64 is not necessary
1032 * (only during init)
1033 */
1034 DPRINTK("(for SD64)\n");
1035 /* MCLK select */
1036 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 break;
1038
1039 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001040 case BT_SPECTRUM:
1041 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001042 /* ### ueberall 0x22? */
1043 /* ##vorher 1c MCLK select */
1044 vga_wseq(regbase, CL_SEQR1F, 0x22);
1045 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1046 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 break;
1048
1049 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001050 DPRINTK("(for Picasso)\n");
1051 /* ##vorher 22 MCLK select */
1052 vga_wseq(regbase, CL_SEQR1F, 0x22);
1053 /* ## vorher d0 avoid FIFO underruns..? */
1054 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 break;
1056
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 case BT_PICASSO4:
1058 case BT_ALPINE:
1059 case BT_GD5480:
1060 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001061 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 /* do nothing */
1063 break;
1064
1065 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001066 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 break;
1068 }
1069
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* pixel mask: pass-through for first plane */
1071 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 /* hidden dac reg: 1280x1024 */
1074 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001076 /* hidden dac: nothing */
1077 WHDR(cinfo, 0);
1078 /* memory mode: odd/even, ext. memory */
1079 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1080 /* plane mask: only write to first plane */
1081 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 offset = var->xres_virtual / 16;
1083 }
1084
1085 /******************************************************
1086 *
1087 * 8 bpp
1088 *
1089 */
1090
1091 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001092 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 switch (cinfo->btype) {
1094 case BT_SD64:
1095 case BT_PICCOLO:
1096 case BT_PICASSO:
1097 case BT_SPECTRUM:
1098 case BT_PICASSO4:
1099 case BT_ALPINE:
1100 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001101 DPRINTK(" (for GD54xx)\n");
1102 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 regs.multiplexing ?
1104 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1105 break;
1106
1107 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108 DPRINTK(" (for GD546x)\n");
1109 vga_wseq(regbase, CL_SEQR7,
1110 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 break;
1112
1113 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001114 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 break;
1116 }
1117
1118 switch (cinfo->btype) {
1119 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 /* MCLK select */
1121 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 break;
1123
1124 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001127 /* ### vorher 1c MCLK select */
1128 vga_wseq(regbase, CL_SEQR1F, 0x22);
1129 /* Fast Page-Mode writes */
1130 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 break;
1132
1133 case BT_PICASSO4:
1134#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001135 /* ### INCOMPLETE!! */
1136 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001138/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 break;
1140
1141 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001142 DPRINTK(" (for GD543x)\n");
1143 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 /* We already set SRF and SR1F */
1145 break;
1146
1147 case BT_GD5480:
1148 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001149 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 /* do nothing */
1151 break;
1152
1153 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001154 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 break;
1156 }
1157
Krzysztof Helt8503df62007-10-16 01:29:08 -07001158 /* mode register: 256 color mode */
1159 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1160 /* pixel mask: pass-through all planes */
1161 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001163 /* hidden dac reg: 1280x1024 */
1164 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001166 /* hidden dac: nothing */
1167 WHDR(cinfo, 0);
1168 /* memory mode: chain4, ext. memory */
1169 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1170 /* plane mask: enable writing to all 4 planes */
1171 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 offset = var->xres_virtual / 8;
1173 }
1174
1175 /******************************************************
1176 *
1177 * 16 bpp
1178 *
1179 */
1180
1181 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001182 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 switch (cinfo->btype) {
1184 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001185 /* Extended Sequencer Mode: 256c col. mode */
1186 vga_wseq(regbase, CL_SEQR7, 0xf7);
1187 /* MCLK select */
1188 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 break;
1190
1191 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001192 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001193 vga_wseq(regbase, CL_SEQR7, 0x87);
1194 /* Fast Page-Mode writes */
1195 vga_wseq(regbase, CL_SEQRF, 0xb0);
1196 /* MCLK select */
1197 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 break;
1199
1200 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001201 vga_wseq(regbase, CL_SEQR7, 0x27);
1202 /* Fast Page-Mode writes */
1203 vga_wseq(regbase, CL_SEQRF, 0xb0);
1204 /* MCLK select */
1205 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 break;
1207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001209 vga_wseq(regbase, CL_SEQR7, 0x27);
1210/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 break;
1212
1213 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001214 DPRINTK(" (for GD543x)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 if (regs.HorizRes >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001216 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001218 vga_wseq(regbase, CL_SEQR7, 0xa3);
1219 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 break;
1221
1222 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001223 DPRINTK(" (for GD5480)\n");
1224 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 /* We already set SRF and SR1F */
1226 break;
1227
1228 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001229 DPRINTK(" (for GD546x)\n");
1230 vga_wseq(regbase, CL_SEQR7,
1231 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 break;
1233
1234 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 break;
1237 }
1238
Krzysztof Helt8503df62007-10-16 01:29:08 -07001239 /* mode register: 256 color mode */
1240 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1241 /* pixel mask: pass-through all planes */
1242 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001244 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245#elif defined(CONFIG_ZORRO)
1246 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001247 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001249 /* memory mode: chain4, ext. memory */
1250 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1251 /* plane mask: enable writing to all 4 planes */
1252 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 offset = var->xres_virtual / 4;
1254 }
1255
1256 /******************************************************
1257 *
1258 * 32 bpp
1259 *
1260 */
1261
1262 else if (var->bits_per_pixel == 32) {
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001263 DPRINTK("cirrusfb: preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 switch (cinfo->btype) {
1265 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001266 /* Extended Sequencer Mode: 256c col. mode */
1267 vga_wseq(regbase, CL_SEQR7, 0xf9);
1268 /* MCLK select */
1269 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 break;
1271
1272 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001273 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001274 vga_wseq(regbase, CL_SEQR7, 0x85);
1275 /* Fast Page-Mode writes */
1276 vga_wseq(regbase, CL_SEQRF, 0xb0);
1277 /* MCLK select */
1278 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 break;
1280
1281 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001282 vga_wseq(regbase, CL_SEQR7, 0x25);
1283 /* Fast Page-Mode writes */
1284 vga_wseq(regbase, CL_SEQRF, 0xb0);
1285 /* MCLK select */
1286 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 break;
1288
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290 vga_wseq(regbase, CL_SEQR7, 0x25);
1291/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 break;
1293
1294 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001295 DPRINTK(" (for GD543x)\n");
1296 vga_wseq(regbase, CL_SEQR7, 0xa9);
1297 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 break;
1299
1300 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001301 DPRINTK(" (for GD5480)\n");
1302 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 /* We already set SRF and SR1F */
1304 break;
1305
1306 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001307 DPRINTK(" (for GD546x)\n");
1308 vga_wseq(regbase, CL_SEQR7,
1309 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 break;
1311
1312 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 break;
1315 }
1316
Krzysztof Helt8503df62007-10-16 01:29:08 -07001317 /* mode register: 256 color mode */
1318 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1319 /* pixel mask: pass-through all planes */
1320 WGen(cinfo, VGA_PEL_MSK, 0xff);
1321 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1322 WHDR(cinfo, 0xc5);
1323 /* memory mode: chain4, ext. memory */
1324 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1325 /* plane mask: enable writing to all 4 planes */
1326 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 offset = var->xres_virtual / 4;
1328 }
1329
1330 /******************************************************
1331 *
1332 * unknown/unsupported bpp
1333 *
1334 */
1335
Krzysztof Helt8503df62007-10-16 01:29:08 -07001336 else
1337 printk(KERN_ERR "cirrusfb: What's this?? "
1338 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340
Krzysztof Helt8503df62007-10-16 01:29:08 -07001341 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 tmp = 0x22;
1343 if (offset & 0x100)
1344 tmp |= 0x10; /* offset overflow bit */
1345
Krzysztof Helt8503df62007-10-16 01:29:08 -07001346 /* screen start addr #16-18, fastpagemode cycles */
1347 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
1349 if (cinfo->btype == BT_SD64 ||
1350 cinfo->btype == BT_PICASSO4 ||
1351 cinfo->btype == BT_ALPINE ||
1352 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001353 /* screen start address bit 19 */
1354 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
Krzysztof Helt8503df62007-10-16 01:29:08 -07001356 /* text cursor location high */
1357 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1358 /* text cursor location low */
1359 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1360 /* underline row scanline = at very bottom */
1361 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 /* controller mode */
1364 vga_wattr(regbase, VGA_ATC_MODE, 1);
1365 /* overscan (border) color */
1366 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1367 /* color plane enable */
1368 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1369 /* pixel panning */
1370 vga_wattr(regbase, CL_AR33, 0);
1371 /* color select */
1372 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 /* [ EGS: SetOffset(); ] */
1375 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001376 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Krzysztof Helt8503df62007-10-16 01:29:08 -07001378 /* set/reset register */
1379 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1380 /* set/reset enable */
1381 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1382 /* color compare */
1383 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1384 /* data rotate */
1385 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1386 /* read map select */
1387 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1388 /* miscellaneous register */
1389 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1390 /* color don't care */
1391 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1392 /* bit mask */
1393 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
Krzysztof Helt8503df62007-10-16 01:29:08 -07001395 /* graphics cursor attributes: nothing special */
1396 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397
1398 /* finally, turn on everything - turn off "FullBandwidth" bit */
1399 /* also, set "DotClock%2" bit where requested */
1400 tmp = 0x01;
1401
1402/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1403 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1404 tmp |= 0x08;
1405*/
1406
Krzysztof Helt8503df62007-10-16 01:29:08 -07001407 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1408 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
1410 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411
1412 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001413 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
1415#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001416 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417#endif
1418
Krzysztof Helt8503df62007-10-16 01:29:08 -07001419 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 return 0;
1421}
1422
1423/* for some reason incomprehensible to me, cirrusfb requires that you write
1424 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001425static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001427 cirrusfb_set_par_foo(info);
1428 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429}
1430
Krzysztof Helt8503df62007-10-16 01:29:08 -07001431static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1432 unsigned blue, unsigned transp,
1433 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434{
1435 struct cirrusfb_info *cinfo = info->par;
1436
1437 if (regno > 255)
1438 return -EINVAL;
1439
1440 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1441 u32 v;
1442 red >>= (16 - info->var.red.length);
1443 green >>= (16 - info->var.green.length);
1444 blue >>= (16 - info->var.blue.length);
1445
Krzysztof Helt8503df62007-10-16 01:29:08 -07001446 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 return 1;
1448 v = (red << info->var.red.offset) |
1449 (green << info->var.green.offset) |
1450 (blue << info->var.blue.offset);
1451
Krzysztof Helt060b6002007-10-16 01:29:13 -07001452 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 return 0;
1454 }
1455
Krzysztof Helt8503df62007-10-16 01:29:08 -07001456 if (info->var.bits_per_pixel == 8)
1457 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458
1459 return 0;
1460
1461}
1462
1463/*************************************************************************
1464 cirrusfb_pan_display()
1465
1466 performs display panning - provided hardware permits this
1467**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001468static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1469 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470{
1471 int xoffset = 0;
1472 int yoffset = 0;
1473 unsigned long base;
1474 unsigned char tmp = 0, tmp2 = 0, xpix;
1475 struct cirrusfb_info *cinfo = info->par;
1476
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 DPRINTK("ENTER\n");
1478 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479
1480 /* no range checks for xoffset and yoffset, */
1481 /* as fb_pan_display has already done this */
1482 if (var->vmode & FB_VMODE_YWRAP)
1483 return -EINVAL;
1484
1485 info->var.xoffset = var->xoffset;
1486 info->var.yoffset = var->yoffset;
1487
1488 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1489 yoffset = var->yoffset;
1490
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001491 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
1493 if (info->var.bits_per_pixel == 1) {
1494 /* base is already correct */
1495 xpix = (unsigned char) (var->xoffset % 8);
1496 } else {
1497 base /= 4;
1498 xpix = (unsigned char) ((xoffset % 4) * 2);
1499 }
1500
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1505 (unsigned char) (base & 0xff));
1506 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1507 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509 /* construct bits 16, 17 and 18 of screen start address */
1510 if (base & 0x10000)
1511 tmp |= 0x01;
1512 if (base & 0x20000)
1513 tmp |= 0x04;
1514 if (base & 0x40000)
1515 tmp |= 0x08;
1516
Krzysztof Helt8503df62007-10-16 01:29:08 -07001517 /* 0xf2 is %11110010, exclude tmp bits */
1518 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1519 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
1521 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001522 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1523 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Krzysztof Helt8503df62007-10-16 01:29:08 -07001525 /* write pixel panning value to AR33; this does not quite work in 8bpp
1526 *
1527 * ### Piccolo..? Will this work?
1528 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001530 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
Krzysztof Helt8503df62007-10-16 01:29:08 -07001532 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534 DPRINTK("EXIT\n");
1535 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536}
1537
Krzysztof Helt8503df62007-10-16 01:29:08 -07001538static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539{
1540 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001541 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1542 * then the caller blanks by setting the CLUT (Color Look Up Table)
1543 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1544 * failed due to e.g. a video mode which doesn't support it.
1545 * Implements VESA suspend and powerdown modes on hardware that
1546 * supports disabling hsync/vsync:
1547 * blank_mode == 2: suspend vsync
1548 * blank_mode == 3: suspend hsync
1549 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 */
1551 unsigned char val;
1552 struct cirrusfb_info *cinfo = info->par;
1553 int current_mode = cinfo->blank_mode;
1554
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
1557 if (info->state != FBINFO_STATE_RUNNING ||
1558 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001559 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 return 0;
1561 }
1562
1563 /* Undo current */
1564 if (current_mode == FB_BLANK_NORMAL ||
1565 current_mode == FB_BLANK_UNBLANK) {
1566 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001567 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1568 /* clear "FullBandwidth" bit */
1569 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 }
1573
1574 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001575 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001577 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1578 /* set "FullBandwidth" bit */
1579 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 }
1581
1582 switch (blank_mode) {
1583 case FB_BLANK_UNBLANK:
1584 case FB_BLANK_NORMAL:
1585 break;
1586 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 break;
1589 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 break;
1592 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001593 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 break;
1595 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001596 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 return 1;
1598 }
1599
1600 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001601 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603 /* Let fbcon do a soft blank for us */
1604 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1605}
1606/**** END Hardware specific Routines **************************************/
1607/****************************************************************************/
1608/**** BEGIN Internal Routines ***********************************************/
1609
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001610static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001612 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 const struct cirrusfb_board_info_rec *bi;
1614
Krzysztof Helt8503df62007-10-16 01:29:08 -07001615 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
Krzysztof Helt8503df62007-10-16 01:29:08 -07001617 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
1619 bi = &cirrusfb_board_info[cinfo->btype];
1620
1621 /* reset board globally */
1622 switch (cinfo->btype) {
1623 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001624 WSFR(cinfo, 0x01);
1625 udelay(500);
1626 WSFR(cinfo, 0x51);
1627 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 break;
1629 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001630 WSFR2(cinfo, 0xff);
1631 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 break;
1633 case BT_SD64:
1634 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001635 WSFR(cinfo, 0x1f);
1636 udelay(500);
1637 WSFR(cinfo, 0x4f);
1638 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 break;
1640 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 /* disable flickerfixer */
1642 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1643 mdelay(100);
1644 /* from Klaus' NetBSD driver: */
1645 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1646 /* put blitter into 542x compat */
1647 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1648 /* mode */
1649 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 break;
1651
1652 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653 /* from Klaus' NetBSD driver: */
1654 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 break;
1656
1657 case BT_ALPINE:
1658 /* Nothing to do to reset the board. */
1659 break;
1660
1661 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001662 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 break;
1664 }
1665
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001666 /* make sure RAM size set by this point */
1667 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
1669 /* the P4 is not fully initialized here; I rely on it having been */
1670 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001671 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
1673 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001674 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1675 WGen(cinfo, CL_POS102, 0x01);
1676 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
1678 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001679 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680
Krzysztof Helt8503df62007-10-16 01:29:08 -07001681 /* reset sequencer logic */
1682 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 /* FullBandwidth (video off) and 8/9 dot clock */
1685 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1686 /* polarity (-/-), disable access to display memory,
1687 * VGA_CRTC_START_HI base address: color
1688 */
1689 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 /* "magic cookie" - doesn't make any sense to me.. */
1692/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1693 /* unlock all extension registers */
1694 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
Krzysztof Helt8503df62007-10-16 01:29:08 -07001696 /* reset blitter */
1697 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698
1699 switch (cinfo->btype) {
1700 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 break;
1703 case BT_ALPINE:
1704 break;
1705 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001706 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 break;
1708 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001709 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1710 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 break;
1712 }
1713 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001714 /* plane mask: nothing */
1715 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1716 /* character map select: doesn't even matter in gx mode */
1717 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1718 /* memory mode: chain-4, no odd/even, ext. memory */
1719 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720
1721 /* controller-internal base address of video memory */
1722 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001723 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724
Krzysztof Helt8503df62007-10-16 01:29:08 -07001725 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1726 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727
Krzysztof Helt8503df62007-10-16 01:29:08 -07001728 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1729 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1730 /* graphics cursor Y position (..."... ) */
1731 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1732 /* graphics cursor attributes */
1733 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1734 /* graphics cursor pattern address */
1735 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736
1737 /* writing these on a P4 might give problems.. */
1738 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001739 /* configuration readback and ext. color */
1740 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1741 /* signature generator */
1742 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 }
1744
1745 /* MCLK select etc. */
1746 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001747 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748
Krzysztof Helt8503df62007-10-16 01:29:08 -07001749 /* Screen A preset row scan: none */
1750 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1751 /* Text cursor start: disable text cursor */
1752 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1753 /* Text cursor end: - */
1754 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1755 /* Screen start address high: 0 */
1756 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1757 /* Screen start address low: 0 */
1758 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1759 /* text cursor location high: 0 */
1760 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1761 /* text cursor location low: 0 */
1762 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763
Krzysztof Helt8503df62007-10-16 01:29:08 -07001764 /* Underline Row scanline: - */
1765 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1766 /* mode control: timing enable, byte mode, no compat modes */
1767 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1768 /* Line Compare: not needed */
1769 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001771 /* ext. display controls: ext.adr. wrap */
1772 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773
Krzysztof Helt8503df62007-10-16 01:29:08 -07001774 /* Set/Reset registes: - */
1775 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1776 /* Set/Reset enable: - */
1777 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1778 /* Color Compare: - */
1779 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1780 /* Data Rotate: - */
1781 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1782 /* Read Map Select: - */
1783 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1784 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1785 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1786 /* Miscellaneous: memory map base address, graphics mode */
1787 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1788 /* Color Don't care: involve all planes */
1789 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1790 /* Bit Mask: no mask at all */
1791 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001793 /* (5434 can't have bit 3 set for bitblt) */
1794 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 /* Graphics controller mode extensions: finer granularity,
1797 * 8byte data latches
1798 */
1799 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
Krzysztof Helt8503df62007-10-16 01:29:08 -07001801 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1802 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1803 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1804 /* Background color byte 1: - */
1805 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1806 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Krzysztof Helt8503df62007-10-16 01:29:08 -07001808 /* Attribute Controller palette registers: "identity mapping" */
1809 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1810 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1811 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1812 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1813 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1814 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1815 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1816 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1817 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1818 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1819 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1820 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1821 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1822 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1823 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1824 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
Krzysztof Helt8503df62007-10-16 01:29:08 -07001826 /* Attribute Controller mode: graphics mode */
1827 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1828 /* Overscan color reg.: reg. 0 */
1829 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1830 /* Color Plane enable: Enable all 4 planes */
1831 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1832/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1833 /* Color Select: - */
1834 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835
Krzysztof Helt8503df62007-10-16 01:29:08 -07001836 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
1838 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001839 /* polarity (-/-), enable display mem,
1840 * VGA_CRTC_START_HI i/o base = color
1841 */
1842 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
Krzysztof Helt8503df62007-10-16 01:29:08 -07001844 /* BLT Start/status: Blitter reset */
1845 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1846 /* - " - : "end-of-reset" */
1847 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848
1849 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001850 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851
Krzysztof Helt8503df62007-10-16 01:29:08 -07001852 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 return;
1854}
1855
Krzysztof Helt8503df62007-10-16 01:29:08 -07001856static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857{
1858#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1859 static int IsOn = 0; /* XXX not ok for multiple boards */
1860
Krzysztof Helt8503df62007-10-16 01:29:08 -07001861 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862
1863 if (cinfo->btype == BT_PICASSO4)
1864 return; /* nothing to switch */
1865 if (cinfo->btype == BT_ALPINE)
1866 return; /* nothing to switch */
1867 if (cinfo->btype == BT_GD5480)
1868 return; /* nothing to switch */
1869 if (cinfo->btype == BT_PICASSO) {
1870 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001871 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872
Krzysztof Helt8503df62007-10-16 01:29:08 -07001873 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 return;
1875 }
1876 if (on) {
1877 switch (cinfo->btype) {
1878 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001879 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 break;
1881 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001882 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 break;
1884 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001885 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886 break;
1887 default: /* do nothing */ break;
1888 }
1889 } else {
1890 switch (cinfo->btype) {
1891 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001892 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 break;
1894 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001895 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 break;
1897 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001898 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 break;
1900 default: /* do nothing */ break;
1901 }
1902 }
1903
Krzysztof Helt8503df62007-10-16 01:29:08 -07001904 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905#endif /* CONFIG_ZORRO */
1906}
1907
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908/******************************************/
1909/* Linux 2.6-style accelerated functions */
1910/******************************************/
1911
Krzysztof Helt8503df62007-10-16 01:29:08 -07001912static void cirrusfb_fillrect(struct fb_info *info,
1913 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 struct fb_fillrect modded;
1916 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001917 struct cirrusfb_info *cinfo = info->par;
1918 int m = info->var.bits_per_pixel;
1919 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1920 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921
1922 if (info->state != FBINFO_STATE_RUNNING)
1923 return;
1924 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1925 cfb_fillrect(info, region);
1926 return;
1927 }
1928
1929 vxres = info->var.xres_virtual;
1930 vyres = info->var.yres_virtual;
1931
1932 memcpy(&modded, region, sizeof(struct fb_fillrect));
1933
Krzysztof Helt8503df62007-10-16 01:29:08 -07001934 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 modded.dx >= vxres || modded.dy >= vyres)
1936 return;
1937
Krzysztof Helt8503df62007-10-16 01:29:08 -07001938 if (modded.dx + modded.width > vxres)
1939 modded.width = vxres - modded.dx;
1940 if (modded.dy + modded.height > vyres)
1941 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942
Krzysztof Helt060b6002007-10-16 01:29:13 -07001943 cirrusfb_RectFill(cinfo->regbase,
1944 info->var.bits_per_pixel,
1945 (region->dx * m) / 8, region->dy,
1946 (region->width * m) / 8, region->height,
1947 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001948 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949}
1950
Krzysztof Helt8503df62007-10-16 01:29:08 -07001951static void cirrusfb_copyarea(struct fb_info *info,
1952 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 struct fb_copyarea modded;
1955 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001956 struct cirrusfb_info *cinfo = info->par;
1957 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958
1959 if (info->state != FBINFO_STATE_RUNNING)
1960 return;
1961 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1962 cfb_copyarea(info, area);
1963 return;
1964 }
1965
1966 vxres = info->var.xres_virtual;
1967 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001968 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
Krzysztof Helt8503df62007-10-16 01:29:08 -07001970 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 modded.sx >= vxres || modded.sy >= vyres ||
1972 modded.dx >= vxres || modded.dy >= vyres)
1973 return;
1974
Krzysztof Helt8503df62007-10-16 01:29:08 -07001975 if (modded.sx + modded.width > vxres)
1976 modded.width = vxres - modded.sx;
1977 if (modded.dx + modded.width > vxres)
1978 modded.width = vxres - modded.dx;
1979 if (modded.sy + modded.height > vyres)
1980 modded.height = vyres - modded.sy;
1981 if (modded.dy + modded.height > vyres)
1982 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
Krzysztof Helt060b6002007-10-16 01:29:13 -07001984 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1985 (area->sx * m) / 8, area->sy,
1986 (area->dx * m) / 8, area->dy,
1987 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001988 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001989
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990}
1991
Krzysztof Helt8503df62007-10-16 01:29:08 -07001992static void cirrusfb_imageblit(struct fb_info *info,
1993 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994{
1995 struct cirrusfb_info *cinfo = info->par;
1996
Krzysztof Helt8503df62007-10-16 01:29:08 -07001997 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 cfb_imageblit(info, image);
1999}
2000
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001#ifdef CONFIG_PPC_PREP
2002#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
2003#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002004static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002006 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007
2008 *display = PREP_VIDEO_BASE;
2009 *registers = (unsigned long) PREP_IO_BASE;
2010
Krzysztof Helt8503df62007-10-16 01:29:08 -07002011 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012}
2013
2014#endif /* CONFIG_PPC_PREP */
2015
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002017static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018
2019/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
2020 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
2021 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
2022 * seem to have. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002023static unsigned int cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024{
2025 unsigned long mem;
2026 unsigned char SRF;
2027
Krzysztof Helt8503df62007-10-16 01:29:08 -07002028 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
Krzysztof Helt8503df62007-10-16 01:29:08 -07002030 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002032 case 0x08:
2033 mem = 512 * 1024;
2034 break;
2035 case 0x10:
2036 mem = 1024 * 1024;
2037 break;
2038 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2039 * on the 5430.
2040 */
2041 case 0x18:
2042 mem = 2048 * 1024;
2043 break;
2044 default:
2045 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 mem = 1024 * 1024;
2047 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002048 if (SRF & 0x80)
2049 /* If DRAM bank switching is enabled, there must be twice as much
2050 * memory installed. (4MB on the 5434)
2051 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002053
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2055
Krzysztof Helt8503df62007-10-16 01:29:08 -07002056 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 return mem;
2058}
2059
Krzysztof Helt8503df62007-10-16 01:29:08 -07002060static void get_pci_addrs(const struct pci_dev *pdev,
2061 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002063 assert(pdev != NULL);
2064 assert(display != NULL);
2065 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066
Krzysztof Helt8503df62007-10-16 01:29:08 -07002067 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068
2069 *display = 0;
2070 *registers = 0;
2071
2072 /* This is a best-guess for now */
2073
2074 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2075 *display = pci_resource_start(pdev, 1);
2076 *registers = pci_resource_start(pdev, 0);
2077 } else {
2078 *display = pci_resource_start(pdev, 0);
2079 *registers = pci_resource_start(pdev, 1);
2080 }
2081
Krzysztof Helt8503df62007-10-16 01:29:08 -07002082 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
Krzysztof Helt8503df62007-10-16 01:29:08 -07002084 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085}
2086
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002087static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002089 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002091 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092#if 0 /* if system didn't claim this region, we would... */
2093 release_mem_region(0xA0000, 65535);
2094#endif
2095 if (release_io_ports)
2096 release_region(0x3C0, 32);
2097 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098}
2099#endif /* CONFIG_PCI */
2100
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002102static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103{
Al Virod91f5bb2007-10-17 00:27:18 +01002104 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002105 struct zorro_dev *zdev = to_zorro_dev(info->device);
2106
2107 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108
2109 if (cinfo->btype == BT_PICASSO4) {
2110 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002111 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002112 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002114 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002115 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117}
2118#endif /* CONFIG_ZORRO */
2119
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002120static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002122 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 struct fb_var_screeninfo *var = &info->var;
2124
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 info->pseudo_palette = cinfo->pseudo_palette;
2126 info->flags = FBINFO_DEFAULT
2127 | FBINFO_HWACCEL_XPAN
2128 | FBINFO_HWACCEL_YPAN
2129 | FBINFO_HWACCEL_FILLRECT
2130 | FBINFO_HWACCEL_COPYAREA;
2131 if (noaccel)
2132 info->flags |= FBINFO_HWACCEL_DISABLED;
2133 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 if (cinfo->btype == BT_GD5480) {
2135 if (var->bits_per_pixel == 16)
2136 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002137 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 info->screen_base += 2 * MB_;
2139 }
2140
2141 /* Fill fix common fields */
2142 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2143 sizeof(info->fix.id));
2144
2145 /* monochrome: only 1 memory plane */
2146 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002147 info->fix.smem_len = info->screen_size;
2148 if (var->bits_per_pixel == 1)
2149 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 info->fix.xpanstep = 1;
2152 info->fix.ypanstep = 1;
2153 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
2155 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 info->fix.mmio_len = 0;
2157 info->fix.accel = FB_ACCEL_NONE;
2158
2159 fb_alloc_cmap(&info->cmap, 256, 0);
2160
2161 return 0;
2162}
2163
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002164static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002166 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002168 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169
Krzysztof Helt8503df62007-10-16 01:29:08 -07002170 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171
Krzysztof Helt8503df62007-10-16 01:29:08 -07002172 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2173 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 btype = cinfo->btype;
2176
2177 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002178 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002180 /* set all the vital stuff */
2181 cirrusfb_set_fbinfo(info);
2182
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002183 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002185 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2186 if (!err) {
2187 DPRINTK("wrong initial video mode\n");
2188 err = -EINVAL;
2189 goto err_dealloc_cmap;
2190 }
2191
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 info->var.activate = FB_ACTIVATE_NOW;
2193
2194 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2195 if (err < 0) {
2196 /* should never happen */
2197 DPRINTK("choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002198 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 }
2200
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 err = register_framebuffer(info);
2202 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002203 printk(KERN_ERR "cirrusfb: could not register "
2204 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 goto err_dealloc_cmap;
2206 }
2207
Krzysztof Helt8503df62007-10-16 01:29:08 -07002208 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 return 0;
2210
2211err_dealloc_cmap:
2212 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002213 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002214 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 return err;
2216}
2217
Krzysztof Helt8503df62007-10-16 01:29:08 -07002218static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219{
2220 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002221 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222
Krzysztof Helt8503df62007-10-16 01:29:08 -07002223 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224
Krzysztof Helt8503df62007-10-16 01:29:08 -07002225 unregister_framebuffer(info);
2226 fb_dealloc_cmap(&info->cmap);
2227 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002228 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002229 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230
Krzysztof Helt8503df62007-10-16 01:29:08 -07002231 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232}
2233
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002235static int cirrusfb_pci_register(struct pci_dev *pdev,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 const struct pci_device_id *ent)
2237{
2238 struct cirrusfb_info *cinfo;
2239 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002240 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 unsigned long board_addr, board_size;
2242 int ret;
2243
2244 ret = pci_enable_device(pdev);
2245 if (ret < 0) {
2246 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2247 goto err_out;
2248 }
2249
2250 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2251 if (!info) {
2252 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2253 ret = -ENOMEM;
2254 goto err_disable;
2255 }
2256
2257 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002258 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259
Krzysztof Helt7345de32007-10-16 01:29:11 -07002260 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002262 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263
Krzysztof Helt8503df62007-10-16 01:29:08 -07002264 if (isPReP) {
2265 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002267 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002269 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002270 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002272 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002273 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002274 /* FIXME: this forces VGA. alternatives? */
2275 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 }
2277
Krzysztof Helt8503df62007-10-16 01:29:08 -07002278 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002279 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
2281 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002282 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283
2284 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002285 if (ret < 0) {
2286 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2287 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288 board_addr);
2289 goto err_release_fb;
2290 }
2291#if 0 /* if the system didn't claim this region, we would... */
2292 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2293 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2294,
2295 0xA0000L);
2296 ret = -EBUSY;
2297 goto err_release_regions;
2298 }
2299#endif
2300 if (request_region(0x3C0, 32, "cirrusfb"))
2301 release_io_ports = 1;
2302
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002303 info->screen_base = ioremap(board_addr, board_size);
2304 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 ret = -EIO;
2306 goto err_release_legacy;
2307 }
2308
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002309 info->fix.smem_start = board_addr;
2310 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311 cinfo->unmap = cirrusfb_pci_unmap;
2312
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002313 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2314 "Logic chipset on PCI bus\n",
2315 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316 pci_set_drvdata(pdev, info);
2317
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002318 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002319 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002320 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002321 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322
2323err_release_legacy:
2324 if (release_io_ports)
2325 release_region(0x3C0, 32);
2326#if 0
2327 release_mem_region(0xA0000, 65535);
2328err_release_regions:
2329#endif
2330 pci_release_regions(pdev);
2331err_release_fb:
2332 framebuffer_release(info);
2333err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334err_out:
2335 return ret;
2336}
2337
Krzysztof Helt8503df62007-10-16 01:29:08 -07002338static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339{
2340 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002341 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342
Krzysztof Helt8503df62007-10-16 01:29:08 -07002343 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344
Krzysztof Helt8503df62007-10-16 01:29:08 -07002345 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346}
2347
2348static struct pci_driver cirrusfb_pci_driver = {
2349 .name = "cirrusfb",
2350 .id_table = cirrusfb_pci_table,
2351 .probe = cirrusfb_pci_register,
2352 .remove = __devexit_p(cirrusfb_pci_unregister),
2353#ifdef CONFIG_PM
2354#if 0
2355 .suspend = cirrusfb_pci_suspend,
2356 .resume = cirrusfb_pci_resume,
2357#endif
2358#endif
2359};
2360#endif /* CONFIG_PCI */
2361
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362#ifdef CONFIG_ZORRO
2363static int cirrusfb_zorro_register(struct zorro_dev *z,
2364 const struct zorro_device_id *ent)
2365{
2366 struct cirrusfb_info *cinfo;
2367 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002368 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369 struct zorro_dev *z2 = NULL;
2370 unsigned long board_addr, board_size, size;
2371 int ret;
2372
2373 btype = ent->driver_data;
2374 if (cirrusfb_zorro_table2[btype].id2)
2375 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2376 size = cirrusfb_zorro_table2[btype].size;
2377 printk(KERN_INFO "cirrusfb: %s board detected; ",
2378 cirrusfb_board_info[btype].name);
2379
2380 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2381 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002382 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383 ret = -ENOMEM;
2384 goto err_out;
2385 }
2386
2387 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 cinfo->btype = btype;
2389
Al Viro36ea96a2007-10-27 19:46:58 +01002390 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002391 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 board_addr = zorro_resource_start(z);
2394 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002395 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396
2397 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002398 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2399 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 board_addr);
2401 ret = -EBUSY;
2402 goto err_release_fb;
2403 }
2404
Krzysztof Helt8503df62007-10-16 01:29:08 -07002405 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406
2407 ret = -EIO;
2408
2409 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002410 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411
2412 /* To be precise, for the P4 this is not the */
2413 /* begin of the board, but the begin of RAM. */
2414 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2415 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002416 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 if (!cinfo->regbase)
2418 goto err_release_region;
2419
Krzysztof Helt8503df62007-10-16 01:29:08 -07002420 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2421 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002423 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002425 info->fix.smem_start = board_addr + 16777216;
2426 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2427 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 goto err_unmap_regbase;
2429 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002430 printk(KERN_INFO " REG at $%lx\n",
2431 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002433 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002435 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002437 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2438 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439 goto err_release_region;
2440
2441 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002442 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002443 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444
Krzysztof Helt8503df62007-10-16 01:29:08 -07002445 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2446 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 }
2448 cinfo->unmap = cirrusfb_zorro_unmap;
2449
Krzysztof Helt8503df62007-10-16 01:29:08 -07002450 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 zorro_set_drvdata(z, info);
2452
Al Virod91f5bb2007-10-17 00:27:18 +01002453 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002454 if (ret) {
2455 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002456 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002457 iounmap(cinfo->regbase - 0x600000);
2458 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002459 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002460 }
2461 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462
2463err_unmap_regbase:
2464 /* Parental advisory: explicit hack */
2465 iounmap(cinfo->regbase - 0x600000);
2466err_release_region:
2467 release_region(board_addr, board_size);
2468err_release_fb:
2469 framebuffer_release(info);
2470err_out:
2471 return ret;
2472}
2473
2474void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2475{
2476 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002477 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478
Krzysztof Helt8503df62007-10-16 01:29:08 -07002479 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480
Krzysztof Helt8503df62007-10-16 01:29:08 -07002481 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482}
2483
2484static struct zorro_driver cirrusfb_zorro_driver = {
2485 .name = "cirrusfb",
2486 .id_table = cirrusfb_zorro_table,
2487 .probe = cirrusfb_zorro_register,
2488 .remove = __devexit_p(cirrusfb_zorro_unregister),
2489};
2490#endif /* CONFIG_ZORRO */
2491
2492static int __init cirrusfb_init(void)
2493{
2494 int error = 0;
2495
2496#ifndef MODULE
2497 char *option = NULL;
2498
2499 if (fb_get_options("cirrusfb", &option))
2500 return -ENODEV;
2501 cirrusfb_setup(option);
2502#endif
2503
2504#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002505 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506#endif
2507#ifdef CONFIG_PCI
2508 error |= pci_register_driver(&cirrusfb_pci_driver);
2509#endif
2510 return error;
2511}
2512
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513#ifndef MODULE
2514static int __init cirrusfb_setup(char *options) {
2515 char *this_opt, s[32];
2516 int i;
2517
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519
2520 if (!options || !*options)
2521 return 0;
2522
Krzysztof Helt8503df62007-10-16 01:29:08 -07002523 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002524 if (!*this_opt)
2525 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526
2527 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2528
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 if (!strcmp(this_opt, "noaccel"))
2530 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002531 else if (!strncmp(this_opt, "mode:", 5))
2532 mode_option = this_opt + 5;
2533 else
2534 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535 }
2536 return 0;
2537}
2538#endif
2539
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540 /*
2541 * Modularization
2542 */
2543
2544MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2545MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2546MODULE_LICENSE("GPL");
2547
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549{
2550#ifdef CONFIG_PCI
2551 pci_unregister_driver(&cirrusfb_pci_driver);
2552#endif
2553#ifdef CONFIG_ZORRO
2554 zorro_unregister_driver(&cirrusfb_zorro_driver);
2555#endif
2556}
2557
2558module_init(cirrusfb_init);
2559
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002560module_param(mode_option, charp, 0);
2561MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
2562
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563#ifdef MODULE
2564module_exit(cirrusfb_exit);
2565#endif
2566
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567/**********************************************************************/
2568/* about the following functions - I have used the same names for the */
2569/* functions as Markus Wild did in his Retina driver for NetBSD as */
2570/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002571/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572/**********************************************************************/
2573
2574/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002575static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 int regnum, unsigned char val)
2577{
2578 unsigned long regofs = 0;
2579
2580 if (cinfo->btype == BT_PICASSO) {
2581 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2583 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2585 regofs = 0xfff;
2586 }
2587
Krzysztof Helt8503df62007-10-16 01:29:08 -07002588 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589}
2590
2591/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002592static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593{
2594 unsigned long regofs = 0;
2595
2596 if (cinfo->btype == BT_PICASSO) {
2597 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002598/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2599 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2601 regofs = 0xfff;
2602 }
2603
Krzysztof Helt8503df62007-10-16 01:29:08 -07002604 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605}
2606
2607/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002608static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002610 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611
Krzysztof Helt8503df62007-10-16 01:29:08 -07002612 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613
Krzysztof Helt8503df62007-10-16 01:29:08 -07002614 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 /* if we're just in "write value" mode, write back the */
2616 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617 vga_w(cinfo->regbase, VGA_ATT_IW,
2618 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619 }
2620 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002621/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2622 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623
2624 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
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
2630/*** WHDR() - write into the Hidden DAC register ***/
2631/* as the HDR is the only extension register that requires special treatment
2632 * (the other extension registers are accessible just like the "ordinary"
2633 * registers of their functional group) here is a specialized routine for
2634 * accessing the HDR
2635 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002636static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637{
2638 unsigned char dummy;
2639
2640 if (cinfo->btype == BT_PICASSO) {
2641 /* Klaus' hint for correct access to HDR on some boards */
2642 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002643 WGen(cinfo, VGA_PEL_MSK, 0x00);
2644 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002646 dummy = RGen(cinfo, VGA_PEL_IW);
2647 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648 }
2649 /* now do the usual stuff to access the HDR */
2650
Krzysztof Helt8503df62007-10-16 01:29:08 -07002651 dummy = RGen(cinfo, VGA_PEL_MSK);
2652 udelay(200);
2653 dummy = RGen(cinfo, VGA_PEL_MSK);
2654 udelay(200);
2655 dummy = RGen(cinfo, VGA_PEL_MSK);
2656 udelay(200);
2657 dummy = RGen(cinfo, VGA_PEL_MSK);
2658 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
Krzysztof Helt8503df62007-10-16 01:29:08 -07002660 WGen(cinfo, VGA_PEL_MSK, val);
2661 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662
2663 if (cinfo->btype == BT_PICASSO) {
2664 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002665 dummy = RGen(cinfo, VGA_PEL_IW);
2666 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667
2668 /* and at the end, restore the mask value */
2669 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002670 WGen(cinfo, VGA_PEL_MSK, 0xff);
2671 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672 }
2673}
2674
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002676static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677{
2678#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002679 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002681 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682#endif
2683}
2684
2685/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002686static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687{
2688#ifdef CONFIG_ZORRO
2689 /* writing an arbitrary value to this one causes the monitor switcher */
2690 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002691 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002693 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694#endif
2695}
2696
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002698static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699 unsigned char green, unsigned char blue)
2700{
2701 unsigned int data = VGA_PEL_D;
2702
2703 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002704 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705
2706 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2707 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2708 /* but DAC data register IS, at least for Picasso II */
2709 if (cinfo->btype == BT_PICASSO)
2710 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002711 vga_w(cinfo->regbase, data, red);
2712 vga_w(cinfo->regbase, data, green);
2713 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002715 vga_w(cinfo->regbase, data, blue);
2716 vga_w(cinfo->regbase, data, green);
2717 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718 }
2719}
2720
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721#if 0
2722/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002723static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724 unsigned char *green, unsigned char *blue)
2725{
2726 unsigned int data = VGA_PEL_D;
2727
Krzysztof Helt8503df62007-10-16 01:29:08 -07002728 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729
2730 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2731 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2732 if (cinfo->btype == BT_PICASSO)
2733 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002734 *red = vga_r(cinfo->regbase, data);
2735 *green = vga_r(cinfo->regbase, data);
2736 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002738 *blue = vga_r(cinfo->regbase, data);
2739 *green = vga_r(cinfo->regbase, data);
2740 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741 }
2742}
2743#endif
2744
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745/*******************************************************************
2746 cirrusfb_WaitBLT()
2747
2748 Wait for the BitBLT engine to complete a possible earlier job
2749*********************************************************************/
2750
2751/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002752static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753{
2754 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002755 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 /* do nothing */ ;
2757}
2758
2759/*******************************************************************
2760 cirrusfb_BitBLT()
2761
2762 perform accelerated "scrolling"
2763********************************************************************/
2764
Krzysztof Helt8503df62007-10-16 01:29:08 -07002765static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2766 u_short curx, u_short cury,
2767 u_short destx, u_short desty,
2768 u_short width, u_short height,
2769 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770{
2771 u_short nwidth, nheight;
2772 u_long nsrc, ndest;
2773 u_char bltmode;
2774
Krzysztof Helt8503df62007-10-16 01:29:08 -07002775 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776
2777 nwidth = width - 1;
2778 nheight = height - 1;
2779
2780 bltmode = 0x00;
2781 /* if source adr < dest addr, do the Blt backwards */
2782 if (cury <= desty) {
2783 if (cury == desty) {
2784 /* if src and dest are on the same line, check x */
2785 if (curx < destx)
2786 bltmode |= 0x01;
2787 } else
2788 bltmode |= 0x01;
2789 }
2790 if (!bltmode) {
2791 /* standard case: forward blitting */
2792 nsrc = (cury * line_length) + curx;
2793 ndest = (desty * line_length) + destx;
2794 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002795 /* this means start addresses are at the end,
2796 * counting backwards
2797 */
2798 nsrc = cury * line_length + curx +
2799 nheight * line_length + nwidth;
2800 ndest = desty * line_length + destx +
2801 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 }
2803
2804 /*
2805 run-down of registers to be programmed:
2806 destination pitch
2807 source pitch
2808 BLT width/height
2809 source start
2810 destination start
2811 BLT mode
2812 BLT ROP
2813 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2814 start/stop
2815 */
2816
Krzysztof Helt8503df62007-10-16 01:29:08 -07002817 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818
2819 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002820 /* dest pitch low */
2821 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2822 /* dest pitch hi */
2823 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2824 /* source pitch low */
2825 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2826 /* source pitch hi */
2827 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828
2829 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002830 /* BLT width low */
2831 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2832 /* BLT width hi */
2833 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834
2835 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002836 /* BLT height low */
2837 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2838 /* BLT width hi */
2839 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840
2841 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002842 /* BLT dest low */
2843 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2844 /* BLT dest mid */
2845 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2846 /* BLT dest hi */
2847 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848
2849 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002850 /* BLT src low */
2851 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2852 /* BLT src mid */
2853 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2854 /* BLT src hi */
2855 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856
2857 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002858 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859
2860 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002861 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862
2863 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002864 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865
Krzysztof Helt8503df62007-10-16 01:29:08 -07002866 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867}
2868
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869/*******************************************************************
2870 cirrusfb_RectFill()
2871
2872 perform accelerated rectangle fill
2873********************************************************************/
2874
Krzysztof Helt8503df62007-10-16 01:29:08 -07002875static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 u_short x, u_short y, u_short width, u_short height,
2877 u_char color, u_short line_length)
2878{
2879 u_short nwidth, nheight;
2880 u_long ndest;
2881 u_char op;
2882
Krzysztof Helt8503df62007-10-16 01:29:08 -07002883 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884
2885 nwidth = width - 1;
2886 nheight = height - 1;
2887
2888 ndest = (y * line_length) + x;
2889
Krzysztof Helt8503df62007-10-16 01:29:08 -07002890 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891
2892 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002893 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2894 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2895 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2896 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897
2898 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002899 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2900 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901
2902 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002903 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2904 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905
2906 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002907 /* BLT dest low */
2908 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2909 /* BLT dest mid */
2910 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2911 /* BLT dest hi */
2912 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913
2914 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002915 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2916 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2917 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918
2919 /* This is a ColorExpand Blt, using the */
2920 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002921 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2922 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923
2924 op = 0xc0;
2925 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002926 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2927 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 op = 0x50;
2929 op = 0xd0;
2930 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002931 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2932 vga_wgfx(regbase, CL_GR11, color); /* background color */
2933 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2934 vga_wgfx(regbase, CL_GR13, color); /* background color */
2935 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2936 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937 op = 0x50;
2938 op = 0xf0;
2939 }
2940 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002941 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942
2943 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002944 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945
2946 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002947 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948
Krzysztof Helt8503df62007-10-16 01:29:08 -07002949 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950}
2951
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952/**************************************************************************
2953 * bestclock() - determine closest possible clock lower(?) than the
2954 * desired pixel clock
2955 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002956static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 long *den, long *div, long maxfreq)
2958{
2959 long n, h, d, f;
2960
Krzysztof Helt8503df62007-10-16 01:29:08 -07002961 assert(best != NULL);
2962 assert(nom != NULL);
2963 assert(den != NULL);
2964 assert(div != NULL);
2965 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966
2967 *nom = 0;
2968 *den = 0;
2969 *div = 0;
2970
Krzysztof Helt8503df62007-10-16 01:29:08 -07002971 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972
2973 if (freq < 8000)
2974 freq = 8000;
2975
2976 if (freq > maxfreq)
2977 freq = maxfreq;
2978
2979 *best = 0;
2980 f = freq * 10;
2981
2982 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002983 int s = 0;
2984
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985 d = (143181 * n) / f;
2986 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002987 int temp = d;
2988
2989 if (temp > 31) {
2990 s = 1;
2991 temp >>= 1;
2992 }
2993 h = ((14318 * n) / temp) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002994 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002995 *best = h;
2996 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002997 *den = temp;
2998 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002999 }
3000 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07003001 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07003003 if (d > 31) {
3004 s = 1;
3005 d >>= 1;
3006 }
3007 h = ((14318 * n) / d) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003008 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003009 *best = h;
3010 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07003011 *den = d;
3012 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003013 }
3014 }
3015 }
3016
Krzysztof Helt8503df62007-10-16 01:29:08 -07003017 DPRINTK("Best possible values for given frequency:\n");
3018 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
3019 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020
Krzysztof Helt8503df62007-10-16 01:29:08 -07003021 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003022}
3023
Linus Torvalds1da177e2005-04-16 15:20:36 -07003024/* -------------------------------------------------------------------------
3025 *
3026 * debugging functions
3027 *
3028 * -------------------------------------------------------------------------
3029 */
3030
3031#ifdef CIRRUSFB_DEBUG
3032
3033/**
3034 * cirrusfb_dbg_print_byte
3035 * @name: name associated with byte value to be displayed
3036 * @val: byte value to be displayed
3037 *
3038 * DESCRIPTION:
3039 * Display an indented string, along with a hexidecimal byte value, and
3040 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3041 * order.
3042 */
3043
3044static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003045void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003046{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003047 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3048 name, val,
3049 val & 0x80 ? '1' : '0',
3050 val & 0x40 ? '1' : '0',
3051 val & 0x20 ? '1' : '0',
3052 val & 0x10 ? '1' : '0',
3053 val & 0x08 ? '1' : '0',
3054 val & 0x04 ? '1' : '0',
3055 val & 0x02 ? '1' : '0',
3056 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057}
3058
Linus Torvalds1da177e2005-04-16 15:20:36 -07003059/**
3060 * cirrusfb_dbg_print_regs
3061 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3062 * @reg_class: type of registers to read: %CRT, or %SEQ
3063 *
3064 * DESCRIPTION:
3065 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3066 * old-style I/O ports are queried for information, otherwise MMIO is
3067 * used at the given @base address to query the information.
3068 */
3069
3070static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003071void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003072 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003073{
3074 va_list list;
3075 unsigned char val = 0;
3076 unsigned reg;
3077 char *name;
3078
Krzysztof Helt8503df62007-10-16 01:29:08 -07003079 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080
Krzysztof Helt8503df62007-10-16 01:29:08 -07003081 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003082 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003083 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084
3085 switch (reg_class) {
3086 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003087 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088 break;
3089 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003090 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 break;
3092 default:
3093 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003094 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003095 break;
3096 }
3097
Krzysztof Helt8503df62007-10-16 01:29:08 -07003098 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003099
Krzysztof Helt8503df62007-10-16 01:29:08 -07003100 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003101 }
3102
Krzysztof Helt8503df62007-10-16 01:29:08 -07003103 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104}
3105
Linus Torvalds1da177e2005-04-16 15:20:36 -07003106/**
3107 * cirrusfb_dump
3108 * @cirrusfbinfo:
3109 *
3110 * DESCRIPTION:
3111 */
3112
Krzysztof Helt8503df62007-10-16 01:29:08 -07003113static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003115 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116}
3117
Linus Torvalds1da177e2005-04-16 15:20:36 -07003118/**
3119 * cirrusfb_dbg_reg_dump
3120 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3121 *
3122 * DESCRIPTION:
3123 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3124 * old-style I/O ports are queried for information, otherwise MMIO is
3125 * used at the given @base address to query the information.
3126 */
3127
3128static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003129void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003130{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003131 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003132
Krzysztof Helt8503df62007-10-16 01:29:08 -07003133 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134 "CR00", 0x00,
3135 "CR01", 0x01,
3136 "CR02", 0x02,
3137 "CR03", 0x03,
3138 "CR04", 0x04,
3139 "CR05", 0x05,
3140 "CR06", 0x06,
3141 "CR07", 0x07,
3142 "CR08", 0x08,
3143 "CR09", 0x09,
3144 "CR0A", 0x0A,
3145 "CR0B", 0x0B,
3146 "CR0C", 0x0C,
3147 "CR0D", 0x0D,
3148 "CR0E", 0x0E,
3149 "CR0F", 0x0F,
3150 "CR10", 0x10,
3151 "CR11", 0x11,
3152 "CR12", 0x12,
3153 "CR13", 0x13,
3154 "CR14", 0x14,
3155 "CR15", 0x15,
3156 "CR16", 0x16,
3157 "CR17", 0x17,
3158 "CR18", 0x18,
3159 "CR22", 0x22,
3160 "CR24", 0x24,
3161 "CR26", 0x26,
3162 "CR2D", 0x2D,
3163 "CR2E", 0x2E,
3164 "CR2F", 0x2F,
3165 "CR30", 0x30,
3166 "CR31", 0x31,
3167 "CR32", 0x32,
3168 "CR33", 0x33,
3169 "CR34", 0x34,
3170 "CR35", 0x35,
3171 "CR36", 0x36,
3172 "CR37", 0x37,
3173 "CR38", 0x38,
3174 "CR39", 0x39,
3175 "CR3A", 0x3A,
3176 "CR3B", 0x3B,
3177 "CR3C", 0x3C,
3178 "CR3D", 0x3D,
3179 "CR3E", 0x3E,
3180 "CR3F", 0x3F,
3181 NULL);
3182
Krzysztof Helt8503df62007-10-16 01:29:08 -07003183 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003184
Krzysztof Helt8503df62007-10-16 01:29:08 -07003185 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186
Krzysztof Helt8503df62007-10-16 01:29:08 -07003187 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 "SR00", 0x00,
3189 "SR01", 0x01,
3190 "SR02", 0x02,
3191 "SR03", 0x03,
3192 "SR04", 0x04,
3193 "SR08", 0x08,
3194 "SR09", 0x09,
3195 "SR0A", 0x0A,
3196 "SR0B", 0x0B,
3197 "SR0D", 0x0D,
3198 "SR10", 0x10,
3199 "SR11", 0x11,
3200 "SR12", 0x12,
3201 "SR13", 0x13,
3202 "SR14", 0x14,
3203 "SR15", 0x15,
3204 "SR16", 0x16,
3205 "SR17", 0x17,
3206 "SR18", 0x18,
3207 "SR19", 0x19,
3208 "SR1A", 0x1A,
3209 "SR1B", 0x1B,
3210 "SR1C", 0x1C,
3211 "SR1D", 0x1D,
3212 "SR1E", 0x1E,
3213 "SR1F", 0x1F,
3214 NULL);
3215
Krzysztof Helt8503df62007-10-16 01:29:08 -07003216 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217}
3218
3219#endif /* CIRRUSFB_DEBUG */
3220