blob: 573bd456e5abe11ba5601bb12e649318e7f70288 [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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337};
338
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700340enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700341 CRT,
342 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700343};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700344#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
346/* info about board */
347struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700349 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 unsigned char SFR; /* Shadow of special function register */
351
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 struct cirrusfb_regs currentmode;
353 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700354 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700356 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357};
358
Krzysztof Helt8503df62007-10-16 01:29:08 -0700359static int noaccel;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700360static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362/****************************************************************************/
363/**** BEGIN PROTOTYPES ******************************************************/
364
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700366static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700368static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369#endif
370
Krzysztof Helt8503df62007-10-16 01:29:08 -0700371static int cirrusfb_open(struct fb_info *info, int user);
372static int cirrusfb_release(struct fb_info *info, int user);
373static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
374 unsigned blue, unsigned transp,
375 struct fb_info *info);
376static int cirrusfb_check_var(struct fb_var_screeninfo *var,
377 struct fb_info *info);
378static int cirrusfb_set_par(struct fb_info *info);
379static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
380 struct fb_info *info);
381static int cirrusfb_blank(int blank_mode, struct fb_info *info);
382static void cirrusfb_fillrect(struct fb_info *info,
383 const struct fb_fillrect *region);
384static void cirrusfb_copyarea(struct fb_info *info,
385 const struct fb_copyarea *area);
386static void cirrusfb_imageblit(struct fb_info *info,
387 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
389/* function table of the above functions */
390static struct fb_ops cirrusfb_ops = {
391 .owner = THIS_MODULE,
392 .fb_open = cirrusfb_open,
393 .fb_release = cirrusfb_release,
394 .fb_setcolreg = cirrusfb_setcolreg,
395 .fb_check_var = cirrusfb_check_var,
396 .fb_set_par = cirrusfb_set_par,
397 .fb_pan_display = cirrusfb_pan_display,
398 .fb_blank = cirrusfb_blank,
399 .fb_fillrect = cirrusfb_fillrect,
400 .fb_copyarea = cirrusfb_copyarea,
401 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402};
403
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700405static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700406static void switch_monitor(struct cirrusfb_info *cinfo, int on);
407static void WGen(const struct cirrusfb_info *cinfo,
408 int regnum, unsigned char val);
409static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
410static void AttrOn(const struct cirrusfb_info *cinfo);
411static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
412static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
413static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
414static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
415 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700417static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
418 unsigned char *red, unsigned char *green,
419 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421static void cirrusfb_WaitBLT(u8 __iomem *regbase);
422static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
423 u_short curx, u_short cury,
424 u_short destx, u_short desty,
425 u_short width, u_short height,
426 u_short line_length);
427static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
428 u_short x, u_short y,
429 u_short width, u_short height,
430 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Krzysztof Helt8503df62007-10-16 01:29:08 -0700432static void bestclock(long freq, long *best,
433 long *nom, long *den,
434 long *div, long maxfreq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700437static void cirrusfb_dump(void);
438static void cirrusfb_dbg_reg_dump(caddr_t regbase);
439static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700440 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700441static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442#endif /* CIRRUSFB_DEBUG */
443
444/*** END PROTOTYPES ********************************************************/
445/*****************************************************************************/
446/*** BEGIN Interface Used by the World ***************************************/
447
Krzysztof Helt8503df62007-10-16 01:29:08 -0700448static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
450/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700451static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
453 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700454 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 return 0;
456}
457
458/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700459static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460{
461 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700462 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 return 0;
464}
465
466/**** END Interface used by the World *************************************/
467/****************************************************************************/
468/**** BEGIN Hardware specific Routines **************************************/
469
470/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700471static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472{
473 long mclk;
474
Krzysztof Helt8503df62007-10-16 01:29:08 -0700475 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
478 * Assume a 64-bit data path for now. The formula is:
479 * ((B * PCLK * 2)/W) * 1.2
480 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
481 mclk = ((bpp / 8) * freq * 2) / 4;
482 mclk = (mclk * 12) / 10;
483 if (mclk < 50000)
484 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700485 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
487 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
488 mclk = ((mclk * 16) / 14318);
489 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700490 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
492 /* Determine if we should use MCLK instead of VCLK, and if so, what we
493 * should divide it by to get VCLK */
494 switch (freq) {
495 case 24751 ... 25249:
496 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700497 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 break;
499 case 49501 ... 50499:
500 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700501 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 break;
503 default:
504 *div = 0;
505 break;
506 }
507
508 return mclk;
509}
510
511static int cirrusfb_check_var(struct fb_var_screeninfo *var,
512 struct fb_info *info)
513{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700514 int yres;
515 /* memory size in pixels */
516 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
518 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700519 case 1:
Krzysztof Helt09a29102008-09-02 14:35:51 -0700520 pixels /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700522 case 8:
523 case 16:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700524 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700527 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
528 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700530 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 return -EINVAL;
532 }
533
Krzysztof Helt09a29102008-09-02 14:35:51 -0700534 if (var->xres_virtual < var->xres)
535 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700537 if (var->yres_virtual == -1) {
538 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Krzysztof Helt8503df62007-10-16 01:29:08 -0700540 printk(KERN_INFO "cirrusfb: virtual resolution set to "
541 "maximum of %dx%d\n", var->xres_virtual,
542 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 if (var->yres_virtual < var->yres)
545 var->yres_virtual = var->yres;
546
Krzysztof Helt09a29102008-09-02 14:35:51 -0700547 if (var->xres_virtual * var->yres_virtual > pixels) {
548 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
549 "virtual resolution too high to fit into video memory!\n",
550 var->xres_virtual, var->yres_virtual,
551 var->bits_per_pixel);
552 DPRINTK("EXIT - EINVAL error\n");
553 return -EINVAL;
554 }
555
556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 if (var->xoffset < 0)
558 var->xoffset = 0;
559 if (var->yoffset < 0)
560 var->yoffset = 0;
561
562 /* truncate xoffset and yoffset to maximum if too high */
563 if (var->xoffset > var->xres_virtual - var->xres)
564 var->xoffset = var->xres_virtual - var->xres - 1;
565 if (var->yoffset > var->yres_virtual - var->yres)
566 var->yoffset = var->yres_virtual - var->yres - 1;
567
568 switch (var->bits_per_pixel) {
569 case 1:
570 var->red.offset = 0;
571 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700572 var->green = var->red;
573 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 break;
575
576 case 8:
577 var->red.offset = 0;
578 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700579 var->green = var->red;
580 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 break;
582
583 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700584 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 var->red.offset = 2;
586 var->green.offset = -3;
587 var->blue.offset = 8;
588 } else {
589 var->red.offset = 10;
590 var->green.offset = 5;
591 var->blue.offset = 0;
592 }
593 var->red.length = 5;
594 var->green.length = 5;
595 var->blue.length = 5;
596 break;
597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700599 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 var->red.offset = 8;
601 var->green.offset = 16;
602 var->blue.offset = 24;
603 } else {
604 var->red.offset = 16;
605 var->green.offset = 8;
606 var->blue.offset = 0;
607 }
608 var->red.length = 8;
609 var->green.length = 8;
610 var->blue.length = 8;
611 break;
612
613 default:
614 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700615 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 /* should never occur */
617 break;
618 }
619
620 var->red.msb_right =
621 var->green.msb_right =
622 var->blue.msb_right =
623 var->transp.offset =
624 var->transp.length =
625 var->transp.msb_right = 0;
626
627 yres = var->yres;
628 if (var->vmode & FB_VMODE_DOUBLE)
629 yres *= 2;
630 else if (var->vmode & FB_VMODE_INTERLACED)
631 yres = (yres + 1) / 2;
632
633 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700634 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
635 "special treatment required! (TODO)\n");
636 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 return -EINVAL;
638 }
639
640 return 0;
641}
642
Krzysztof Helt8503df62007-10-16 01:29:08 -0700643static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700645 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646{
647 long freq;
648 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700649 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Krzysztof Helt8503df62007-10-16 01:29:08 -0700652 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700654 info->fix.line_length = var->xres_virtual / 8;
655 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 break;
657
658 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700659 info->fix.line_length = var->xres_virtual;
660 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 break;
662
663 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700665 info->fix.line_length = var->xres_virtual * maxclockidx;
666 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 break;
668
669 default:
670 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700671 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 /* should never occur */
673 break;
674 }
675
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700676 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
678 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700679 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Krzysztof Helt8503df62007-10-16 01:29:08 -0700681 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
683 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
684 regs->multiplexing = 0;
685
686 /* If the frequency is greater than we can support, we might be able
687 * to use multiplexing for the video mode */
688 if (freq > maxclock) {
689 switch (cinfo->btype) {
690 case BT_ALPINE:
691 case BT_GD5480:
692 regs->multiplexing = 1;
693 break;
694
695 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700696 printk(KERN_ERR "cirrusfb: Frequency greater "
697 "than maxclock (%ld kHz)\n", maxclock);
698 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 return -EINVAL;
700 }
701 }
702#if 0
703 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
704 * the VCLK is double the pixel clock. */
705 switch (var->bits_per_pixel) {
706 case 16:
707 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700708 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700709 /* Xbh has this type of clock for 32-bit */
710 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 break;
712 }
713#endif
714
Krzysztof Helt8503df62007-10-16 01:29:08 -0700715 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
716 maxclock);
717 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
718 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 return 0;
721}
722
Krzysztof Helt8503df62007-10-16 01:29:08 -0700723static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
724 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700726 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
728 if (div == 2) {
729 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700730 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
731 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
732 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 } else if (div == 1) {
734 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700735 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
736 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
737 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700739 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 }
741}
742
743/*************************************************************************
744 cirrusfb_set_par_foo()
745
746 actually writes the values for a new video mode into the hardware,
747**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700748static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749{
750 struct cirrusfb_info *cinfo = info->par;
751 struct fb_var_screeninfo *var = &info->var;
752 struct cirrusfb_regs regs;
753 u8 __iomem *regbase = cinfo->regbase;
754 unsigned char tmp;
755 int offset = 0, err;
756 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700757 int hdispend, hsyncstart, hsyncend, htotal;
758 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Krzysztof Helt8503df62007-10-16 01:29:08 -0700760 DPRINTK("ENTER\n");
761 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700763 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700765 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
767 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700768 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 /* should never happen */
770 DPRINTK("mode change aborted. invalid var.\n");
771 return -EINVAL;
772 }
773
774 bi = &cirrusfb_board_info[cinfo->btype];
775
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700776 hsyncstart = var->xres + var->right_margin;
777 hsyncend = hsyncstart + var->hsync_len;
778 htotal = (hsyncend + var->left_margin) / 8 - 5;
779 hdispend = var->xres / 8 - 1;
780 hsyncstart = hsyncstart / 8 + 1;
781 hsyncend = hsyncend / 8 + 1;
782
783 yres = var->yres;
784 vsyncstart = yres + var->lower_margin;
785 vsyncend = vsyncstart + var->vsync_len;
786 vtotal = vsyncend + var->upper_margin;
787 vdispend = yres - 1;
788
789 if (var->vmode & FB_VMODE_DOUBLE) {
790 yres *= 2;
791 vsyncstart *= 2;
792 vsyncend *= 2;
793 vtotal *= 2;
794 } else if (var->vmode & FB_VMODE_INTERLACED) {
795 yres = (yres + 1) / 2;
796 vsyncstart = (vsyncstart + 1) / 2;
797 vsyncend = (vsyncend + 1) / 2;
798 vtotal = (vtotal + 1) / 2;
799 }
800
801 vtotal -= 2;
802 vsyncstart -= 1;
803 vsyncend -= 1;
804
805 if (yres >= 1024) {
806 vtotal /= 2;
807 vsyncstart /= 2;
808 vsyncend /= 2;
809 vdispend /= 2;
810 }
811 if (regs.multiplexing) {
812 htotal /= 2;
813 hsyncstart /= 2;
814 hsyncend /= 2;
815 hdispend /= 2;
816 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700818 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
820 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700821 DPRINTK("CRT0: %d\n", htotal);
822 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 DPRINTK("CRT1: %d\n", hdispend);
825 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700827 DPRINTK("CRT2: %d\n", var->xres / 8);
828 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Krzysztof Helt8503df62007-10-16 01:29:08 -0700830 /* + 128: Compatible read */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 DPRINTK("CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700832 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700833 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700835 DPRINTK("CRT4: %d\n", hsyncstart);
836 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700838 tmp = hsyncend % 32;
839 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700841 DPRINTK("CRT5: %d\n", tmp);
842 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700844 DPRINTK("CRT6: %d\n", vtotal & 0xff);
845 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
847 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700848 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700850 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700852 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700854 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700856 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700858 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700860 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700862 DPRINTK("CRT7: %d\n", tmp);
863 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
865 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700866 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 tmp |= 0x20;
868 if (var->vmode & FB_VMODE_DOUBLE)
869 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700870 DPRINTK("CRT9: %d\n", tmp);
871 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700873 DPRINTK("CRT10: %d\n", vsyncstart & 0xff);
874 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700876 DPRINTK("CRT11: 64+32+%d\n", vsyncend % 16);
877 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700879 DPRINTK("CRT12: %d\n", vdispend & 0xff);
880 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700882 DPRINTK("CRT15: %d\n", (vdispend + 1) & 0xff);
883 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700885 DPRINTK("CRT16: %d\n", vtotal & 0xff);
886 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Krzysztof Helt8503df62007-10-16 01:29:08 -0700888 DPRINTK("CRT18: 0xff\n");
889 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
891 tmp = 0;
892 if (var->vmode & FB_VMODE_INTERLACED)
893 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700894 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700896 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700898 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700900 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 tmp |= 128;
902
Krzysztof Helt8503df62007-10-16 01:29:08 -0700903 DPRINTK("CRT1a: %d\n", tmp);
904 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
906 /* set VCLK0 */
907 /* hardware RefClock: 14.31818 MHz */
908 /* formula: VClk = (OSC * N) / (D * (1+P)) */
909 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
910
Krzysztof Helt8503df62007-10-16 01:29:08 -0700911 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 tmp = regs.den << 1;
913 if (regs.div != 0)
914 tmp |= 1;
915
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 if ((cinfo->btype == BT_SD64) ||
918 (cinfo->btype == BT_ALPINE) ||
919 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -0700920 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921
Krzysztof Helt8503df62007-10-16 01:29:08 -0700922 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
923 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700925 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 else
929 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
930 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Krzysztof Helt8503df62007-10-16 01:29:08 -0700933/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
934 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936 /* don't know if it would hurt to also program this if no interlaced */
937 /* mode is used, but I feel better this way.. :-) */
938 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700939 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700941 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
Krzysztof Helt8503df62007-10-16 01:29:08 -0700943 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
945 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700946 /* enable display memory & CRTC I/O address for color mode */
947 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
949 tmp |= 0x40;
950 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
951 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 /* Screen A Preset Row-Scan register */
955 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
956 /* text cursor on and start line */
957 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
958 /* text cursor end line */
959 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 /******************************************************
962 *
963 * 1 bpp
964 *
965 */
966
967 /* programming for different color depths */
968 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700969 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
970 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971
972 /* SR07 */
973 switch (cinfo->btype) {
974 case BT_SD64:
975 case BT_PICCOLO:
976 case BT_PICASSO:
977 case BT_SPECTRUM:
978 case BT_PICASSO4:
979 case BT_ALPINE:
980 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700981 DPRINTK(" (for GD54xx)\n");
982 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 regs.multiplexing ?
984 bi->sr07_1bpp_mux : bi->sr07_1bpp);
985 break;
986
987 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700988 DPRINTK(" (for GD546x)\n");
989 vga_wseq(regbase, CL_SEQR7,
990 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 break;
992
993 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700994 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 break;
996 }
997
998 /* Extended Sequencer Mode */
999 switch (cinfo->btype) {
1000 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001001 /* setting the SEQRF on SD64 is not necessary
1002 * (only during init)
1003 */
1004 DPRINTK("(for SD64)\n");
1005 /* MCLK select */
1006 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 break;
1008
1009 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001010 case BT_SPECTRUM:
1011 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001012 /* ### ueberall 0x22? */
1013 /* ##vorher 1c MCLK select */
1014 vga_wseq(regbase, CL_SEQR1F, 0x22);
1015 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1016 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 break;
1018
1019 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001020 DPRINTK("(for Picasso)\n");
1021 /* ##vorher 22 MCLK select */
1022 vga_wseq(regbase, CL_SEQR1F, 0x22);
1023 /* ## vorher d0 avoid FIFO underruns..? */
1024 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 break;
1026
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 case BT_PICASSO4:
1028 case BT_ALPINE:
1029 case BT_GD5480:
1030 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001031 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 /* do nothing */
1033 break;
1034
1035 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001036 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 break;
1038 }
1039
Krzysztof Helt8503df62007-10-16 01:29:08 -07001040 /* pixel mask: pass-through for first plane */
1041 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001043 /* hidden dac reg: 1280x1024 */
1044 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046 /* hidden dac: nothing */
1047 WHDR(cinfo, 0);
1048 /* memory mode: odd/even, ext. memory */
1049 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1050 /* plane mask: only write to first plane */
1051 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 offset = var->xres_virtual / 16;
1053 }
1054
1055 /******************************************************
1056 *
1057 * 8 bpp
1058 *
1059 */
1060
1061 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001062 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 switch (cinfo->btype) {
1064 case BT_SD64:
1065 case BT_PICCOLO:
1066 case BT_PICASSO:
1067 case BT_SPECTRUM:
1068 case BT_PICASSO4:
1069 case BT_ALPINE:
1070 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001071 DPRINTK(" (for GD54xx)\n");
1072 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 regs.multiplexing ?
1074 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1075 break;
1076
1077 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001078 DPRINTK(" (for GD546x)\n");
1079 vga_wseq(regbase, CL_SEQR7,
1080 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 break;
1082
1083 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001084 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 break;
1086 }
1087
1088 switch (cinfo->btype) {
1089 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001090 /* MCLK select */
1091 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 break;
1093
1094 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001097 /* ### vorher 1c MCLK select */
1098 vga_wseq(regbase, CL_SEQR1F, 0x22);
1099 /* Fast Page-Mode writes */
1100 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 break;
1102
1103 case BT_PICASSO4:
1104#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001105 /* ### INCOMPLETE!! */
1106 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 break;
1110
1111 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001112 DPRINTK(" (for GD543x)\n");
1113 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 /* We already set SRF and SR1F */
1115 break;
1116
1117 case BT_GD5480:
1118 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001119 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 /* do nothing */
1121 break;
1122
1123 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001124 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126 }
1127
Krzysztof Helt8503df62007-10-16 01:29:08 -07001128 /* mode register: 256 color mode */
1129 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1130 /* pixel mask: pass-through all planes */
1131 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001133 /* hidden dac reg: 1280x1024 */
1134 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001136 /* hidden dac: nothing */
1137 WHDR(cinfo, 0);
1138 /* memory mode: chain4, ext. memory */
1139 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1140 /* plane mask: enable writing to all 4 planes */
1141 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 offset = var->xres_virtual / 8;
1143 }
1144
1145 /******************************************************
1146 *
1147 * 16 bpp
1148 *
1149 */
1150
1151 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001152 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 switch (cinfo->btype) {
1154 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 /* Extended Sequencer Mode: 256c col. mode */
1156 vga_wseq(regbase, CL_SEQR7, 0xf7);
1157 /* MCLK select */
1158 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
1160
1161 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001162 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001163 vga_wseq(regbase, CL_SEQR7, 0x87);
1164 /* Fast Page-Mode writes */
1165 vga_wseq(regbase, CL_SEQRF, 0xb0);
1166 /* MCLK select */
1167 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 break;
1169
1170 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001171 vga_wseq(regbase, CL_SEQR7, 0x27);
1172 /* Fast Page-Mode writes */
1173 vga_wseq(regbase, CL_SEQRF, 0xb0);
1174 /* MCLK select */
1175 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 break;
1177
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001179 vga_wseq(regbase, CL_SEQR7, 0x27);
1180/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 break;
1182
1183 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 DPRINTK(" (for GD543x)\n");
Krzysztof Helt9a85cf52008-10-15 22:03:39 -07001185 if (var->xres >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001188 vga_wseq(regbase, CL_SEQR7, 0xa3);
1189 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 break;
1191
1192 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001193 DPRINTK(" (for GD5480)\n");
1194 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 /* We already set SRF and SR1F */
1196 break;
1197
1198 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001199 DPRINTK(" (for GD546x)\n");
1200 vga_wseq(regbase, CL_SEQR7,
1201 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 break;
1203
1204 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001205 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 break;
1207 }
1208
Krzysztof Helt8503df62007-10-16 01:29:08 -07001209 /* mode register: 256 color mode */
1210 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1211 /* pixel mask: pass-through all planes */
1212 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001214 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215#elif defined(CONFIG_ZORRO)
1216 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001217 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001219 /* memory mode: chain4, ext. memory */
1220 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1221 /* plane mask: enable writing to all 4 planes */
1222 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 offset = var->xres_virtual / 4;
1224 }
1225
1226 /******************************************************
1227 *
1228 * 32 bpp
1229 *
1230 */
1231
1232 else if (var->bits_per_pixel == 32) {
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001233 DPRINTK("cirrusfb: preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 switch (cinfo->btype) {
1235 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001236 /* Extended Sequencer Mode: 256c col. mode */
1237 vga_wseq(regbase, CL_SEQR7, 0xf9);
1238 /* MCLK select */
1239 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 break;
1241
1242 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001243 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001244 vga_wseq(regbase, CL_SEQR7, 0x85);
1245 /* Fast Page-Mode writes */
1246 vga_wseq(regbase, CL_SEQRF, 0xb0);
1247 /* MCLK select */
1248 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 break;
1250
1251 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001252 vga_wseq(regbase, CL_SEQR7, 0x25);
1253 /* Fast Page-Mode writes */
1254 vga_wseq(regbase, CL_SEQRF, 0xb0);
1255 /* MCLK select */
1256 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 break;
1258
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001260 vga_wseq(regbase, CL_SEQR7, 0x25);
1261/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 break;
1263
1264 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001265 DPRINTK(" (for GD543x)\n");
1266 vga_wseq(regbase, CL_SEQR7, 0xa9);
1267 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 break;
1269
1270 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001271 DPRINTK(" (for GD5480)\n");
1272 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 /* We already set SRF and SR1F */
1274 break;
1275
1276 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001277 DPRINTK(" (for GD546x)\n");
1278 vga_wseq(regbase, CL_SEQR7,
1279 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 break;
1281
1282 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001283 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 break;
1285 }
1286
Krzysztof Helt8503df62007-10-16 01:29:08 -07001287 /* mode register: 256 color mode */
1288 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1289 /* pixel mask: pass-through all planes */
1290 WGen(cinfo, VGA_PEL_MSK, 0xff);
1291 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1292 WHDR(cinfo, 0xc5);
1293 /* memory mode: chain4, ext. memory */
1294 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1295 /* plane mask: enable writing to all 4 planes */
1296 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 offset = var->xres_virtual / 4;
1298 }
1299
1300 /******************************************************
1301 *
1302 * unknown/unsupported bpp
1303 *
1304 */
1305
Krzysztof Helt8503df62007-10-16 01:29:08 -07001306 else
1307 printk(KERN_ERR "cirrusfb: What's this?? "
1308 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Krzysztof Helt8503df62007-10-16 01:29:08 -07001311 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 tmp = 0x22;
1313 if (offset & 0x100)
1314 tmp |= 0x10; /* offset overflow bit */
1315
Krzysztof Helt8503df62007-10-16 01:29:08 -07001316 /* screen start addr #16-18, fastpagemode cycles */
1317 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318
1319 if (cinfo->btype == BT_SD64 ||
1320 cinfo->btype == BT_PICASSO4 ||
1321 cinfo->btype == BT_ALPINE ||
1322 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001323 /* screen start address bit 19 */
1324 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325
Krzysztof Helt8503df62007-10-16 01:29:08 -07001326 /* text cursor location high */
1327 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1328 /* text cursor location low */
1329 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1330 /* underline row scanline = at very bottom */
1331 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
Krzysztof Helt8503df62007-10-16 01:29:08 -07001333 /* controller mode */
1334 vga_wattr(regbase, VGA_ATC_MODE, 1);
1335 /* overscan (border) color */
1336 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1337 /* color plane enable */
1338 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1339 /* pixel panning */
1340 vga_wattr(regbase, CL_AR33, 0);
1341 /* color select */
1342 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
1344 /* [ EGS: SetOffset(); ] */
1345 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001346 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347
Krzysztof Helt8503df62007-10-16 01:29:08 -07001348 /* set/reset register */
1349 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1350 /* set/reset enable */
1351 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1352 /* color compare */
1353 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1354 /* data rotate */
1355 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1356 /* read map select */
1357 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1358 /* miscellaneous register */
1359 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1360 /* color don't care */
1361 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1362 /* bit mask */
1363 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
Krzysztof Helt8503df62007-10-16 01:29:08 -07001365 /* graphics cursor attributes: nothing special */
1366 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
1368 /* finally, turn on everything - turn off "FullBandwidth" bit */
1369 /* also, set "DotClock%2" bit where requested */
1370 tmp = 0x01;
1371
1372/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1373 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1374 tmp |= 0x08;
1375*/
1376
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1378 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
1380 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381
1382 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001383 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
1385#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001386 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387#endif
1388
Krzysztof Helt8503df62007-10-16 01:29:08 -07001389 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 return 0;
1391}
1392
1393/* for some reason incomprehensible to me, cirrusfb requires that you write
1394 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001395static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 cirrusfb_set_par_foo(info);
1398 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399}
1400
Krzysztof Helt8503df62007-10-16 01:29:08 -07001401static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1402 unsigned blue, unsigned transp,
1403 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404{
1405 struct cirrusfb_info *cinfo = info->par;
1406
1407 if (regno > 255)
1408 return -EINVAL;
1409
1410 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1411 u32 v;
1412 red >>= (16 - info->var.red.length);
1413 green >>= (16 - info->var.green.length);
1414 blue >>= (16 - info->var.blue.length);
1415
Krzysztof Helt8503df62007-10-16 01:29:08 -07001416 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 return 1;
1418 v = (red << info->var.red.offset) |
1419 (green << info->var.green.offset) |
1420 (blue << info->var.blue.offset);
1421
Krzysztof Helt060b6002007-10-16 01:29:13 -07001422 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 return 0;
1424 }
1425
Krzysztof Helt8503df62007-10-16 01:29:08 -07001426 if (info->var.bits_per_pixel == 8)
1427 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
1429 return 0;
1430
1431}
1432
1433/*************************************************************************
1434 cirrusfb_pan_display()
1435
1436 performs display panning - provided hardware permits this
1437**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001438static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1439 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440{
1441 int xoffset = 0;
1442 int yoffset = 0;
1443 unsigned long base;
1444 unsigned char tmp = 0, tmp2 = 0, xpix;
1445 struct cirrusfb_info *cinfo = info->par;
1446
Krzysztof Helt8503df62007-10-16 01:29:08 -07001447 DPRINTK("ENTER\n");
1448 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449
1450 /* no range checks for xoffset and yoffset, */
1451 /* as fb_pan_display has already done this */
1452 if (var->vmode & FB_VMODE_YWRAP)
1453 return -EINVAL;
1454
1455 info->var.xoffset = var->xoffset;
1456 info->var.yoffset = var->yoffset;
1457
1458 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1459 yoffset = var->yoffset;
1460
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001461 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 if (info->var.bits_per_pixel == 1) {
1464 /* base is already correct */
1465 xpix = (unsigned char) (var->xoffset % 8);
1466 } else {
1467 base /= 4;
1468 xpix = (unsigned char) ((xoffset % 4) * 2);
1469 }
1470
Krzysztof Helt8503df62007-10-16 01:29:08 -07001471 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472
1473 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001474 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1475 (unsigned char) (base & 0xff));
1476 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1477 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478
1479 /* construct bits 16, 17 and 18 of screen start address */
1480 if (base & 0x10000)
1481 tmp |= 0x01;
1482 if (base & 0x20000)
1483 tmp |= 0x04;
1484 if (base & 0x40000)
1485 tmp |= 0x08;
1486
Krzysztof Helt8503df62007-10-16 01:29:08 -07001487 /* 0xf2 is %11110010, exclude tmp bits */
1488 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1489 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
1491 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001492 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1493 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 /* write pixel panning value to AR33; this does not quite work in 8bpp
1496 *
1497 * ### Piccolo..? Will this work?
1498 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001500 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
Krzysztof Helt8503df62007-10-16 01:29:08 -07001502 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 DPRINTK("EXIT\n");
1505 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506}
1507
Krzysztof Helt8503df62007-10-16 01:29:08 -07001508static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509{
1510 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001511 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1512 * then the caller blanks by setting the CLUT (Color Look Up Table)
1513 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1514 * failed due to e.g. a video mode which doesn't support it.
1515 * Implements VESA suspend and powerdown modes on hardware that
1516 * supports disabling hsync/vsync:
1517 * blank_mode == 2: suspend vsync
1518 * blank_mode == 3: suspend hsync
1519 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 */
1521 unsigned char val;
1522 struct cirrusfb_info *cinfo = info->par;
1523 int current_mode = cinfo->blank_mode;
1524
Krzysztof Helt8503df62007-10-16 01:29:08 -07001525 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526
1527 if (info->state != FBINFO_STATE_RUNNING ||
1528 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001529 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 return 0;
1531 }
1532
1533 /* Undo current */
1534 if (current_mode == FB_BLANK_NORMAL ||
1535 current_mode == FB_BLANK_UNBLANK) {
1536 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001537 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1538 /* clear "FullBandwidth" bit */
1539 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001541 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 }
1543
1544 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001545 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001547 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1548 /* set "FullBandwidth" bit */
1549 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 }
1551
1552 switch (blank_mode) {
1553 case FB_BLANK_UNBLANK:
1554 case FB_BLANK_NORMAL:
1555 break;
1556 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 break;
1559 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001560 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 break;
1562 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001563 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 break;
1565 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001566 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 return 1;
1568 }
1569
1570 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
1573 /* Let fbcon do a soft blank for us */
1574 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1575}
1576/**** END Hardware specific Routines **************************************/
1577/****************************************************************************/
1578/**** BEGIN Internal Routines ***********************************************/
1579
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001580static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001582 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 const struct cirrusfb_board_info_rec *bi;
1584
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
1589 bi = &cirrusfb_board_info[cinfo->btype];
1590
1591 /* reset board globally */
1592 switch (cinfo->btype) {
1593 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001594 WSFR(cinfo, 0x01);
1595 udelay(500);
1596 WSFR(cinfo, 0x51);
1597 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 break;
1599 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001600 WSFR2(cinfo, 0xff);
1601 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 break;
1603 case BT_SD64:
1604 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001605 WSFR(cinfo, 0x1f);
1606 udelay(500);
1607 WSFR(cinfo, 0x4f);
1608 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 break;
1610 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 /* disable flickerfixer */
1612 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1613 mdelay(100);
1614 /* from Klaus' NetBSD driver: */
1615 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1616 /* put blitter into 542x compat */
1617 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1618 /* mode */
1619 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 break;
1621
1622 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 /* from Klaus' NetBSD driver: */
1624 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 break;
1626
1627 case BT_ALPINE:
1628 /* Nothing to do to reset the board. */
1629 break;
1630
1631 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001632 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 break;
1634 }
1635
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001636 /* make sure RAM size set by this point */
1637 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
1639 /* the P4 is not fully initialized here; I rely on it having been */
1640 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
1643 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001644 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1645 WGen(cinfo, CL_POS102, 0x01);
1646 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
1648 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001649 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 /* reset sequencer logic */
1652 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
Krzysztof Helt8503df62007-10-16 01:29:08 -07001654 /* FullBandwidth (video off) and 8/9 dot clock */
1655 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1656 /* polarity (-/-), disable access to display memory,
1657 * VGA_CRTC_START_HI base address: color
1658 */
1659 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
Krzysztof Helt8503df62007-10-16 01:29:08 -07001661 /* "magic cookie" - doesn't make any sense to me.. */
1662/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1663 /* unlock all extension registers */
1664 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
Krzysztof Helt8503df62007-10-16 01:29:08 -07001666 /* reset blitter */
1667 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
1669 switch (cinfo->btype) {
1670 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001671 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 break;
1673 case BT_ALPINE:
1674 break;
1675 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001676 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 break;
1678 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001679 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1680 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 break;
1682 }
1683 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 /* plane mask: nothing */
1685 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1686 /* character map select: doesn't even matter in gx mode */
1687 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1688 /* memory mode: chain-4, no odd/even, ext. memory */
1689 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
1691 /* controller-internal base address of video memory */
1692 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694
Krzysztof Helt8503df62007-10-16 01:29:08 -07001695 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1696 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697
Krzysztof Helt8503df62007-10-16 01:29:08 -07001698 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1699 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1700 /* graphics cursor Y position (..."... ) */
1701 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1702 /* graphics cursor attributes */
1703 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1704 /* graphics cursor pattern address */
1705 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706
1707 /* writing these on a P4 might give problems.. */
1708 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001709 /* configuration readback and ext. color */
1710 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1711 /* signature generator */
1712 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 }
1714
1715 /* MCLK select etc. */
1716 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001717 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718
Krzysztof Helt8503df62007-10-16 01:29:08 -07001719 /* Screen A preset row scan: none */
1720 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1721 /* Text cursor start: disable text cursor */
1722 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1723 /* Text cursor end: - */
1724 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1725 /* Screen start address high: 0 */
1726 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1727 /* Screen start address low: 0 */
1728 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1729 /* text cursor location high: 0 */
1730 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1731 /* text cursor location low: 0 */
1732 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733
Krzysztof Helt8503df62007-10-16 01:29:08 -07001734 /* Underline Row scanline: - */
1735 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1736 /* mode control: timing enable, byte mode, no compat modes */
1737 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1738 /* Line Compare: not needed */
1739 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001741 /* ext. display controls: ext.adr. wrap */
1742 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743
Krzysztof Helt8503df62007-10-16 01:29:08 -07001744 /* Set/Reset registes: - */
1745 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1746 /* Set/Reset enable: - */
1747 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1748 /* Color Compare: - */
1749 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1750 /* Data Rotate: - */
1751 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1752 /* Read Map Select: - */
1753 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1754 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1755 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1756 /* Miscellaneous: memory map base address, graphics mode */
1757 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1758 /* Color Don't care: involve all planes */
1759 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1760 /* Bit Mask: no mask at all */
1761 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763 /* (5434 can't have bit 3 set for bitblt) */
1764 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001766 /* Graphics controller mode extensions: finer granularity,
1767 * 8byte data latches
1768 */
1769 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
Krzysztof Helt8503df62007-10-16 01:29:08 -07001771 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1772 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1773 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1774 /* Background color byte 1: - */
1775 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1776 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Krzysztof Helt8503df62007-10-16 01:29:08 -07001778 /* Attribute Controller palette registers: "identity mapping" */
1779 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1780 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1781 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1782 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1783 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1784 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1785 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1786 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1787 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1788 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1789 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1790 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1791 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1792 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1793 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1794 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 /* Attribute Controller mode: graphics mode */
1797 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1798 /* Overscan color reg.: reg. 0 */
1799 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1800 /* Color Plane enable: Enable all 4 planes */
1801 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1802/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1803 /* Color Select: - */
1804 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805
Krzysztof Helt8503df62007-10-16 01:29:08 -07001806 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
1808 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809 /* polarity (-/-), enable display mem,
1810 * VGA_CRTC_START_HI i/o base = color
1811 */
1812 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813
Krzysztof Helt8503df62007-10-16 01:29:08 -07001814 /* BLT Start/status: Blitter reset */
1815 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1816 /* - " - : "end-of-reset" */
1817 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818
1819 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001820 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
Krzysztof Helt8503df62007-10-16 01:29:08 -07001822 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 return;
1824}
1825
Krzysztof Helt8503df62007-10-16 01:29:08 -07001826static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827{
1828#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1829 static int IsOn = 0; /* XXX not ok for multiple boards */
1830
Krzysztof Helt8503df62007-10-16 01:29:08 -07001831 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832
1833 if (cinfo->btype == BT_PICASSO4)
1834 return; /* nothing to switch */
1835 if (cinfo->btype == BT_ALPINE)
1836 return; /* nothing to switch */
1837 if (cinfo->btype == BT_GD5480)
1838 return; /* nothing to switch */
1839 if (cinfo->btype == BT_PICASSO) {
1840 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001841 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842
Krzysztof Helt8503df62007-10-16 01:29:08 -07001843 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 return;
1845 }
1846 if (on) {
1847 switch (cinfo->btype) {
1848 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001849 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850 break;
1851 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001852 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 break;
1854 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001855 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 break;
1857 default: /* do nothing */ break;
1858 }
1859 } else {
1860 switch (cinfo->btype) {
1861 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001862 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863 break;
1864 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001865 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866 break;
1867 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001868 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 break;
1870 default: /* do nothing */ break;
1871 }
1872 }
1873
Krzysztof Helt8503df62007-10-16 01:29:08 -07001874 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875#endif /* CONFIG_ZORRO */
1876}
1877
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878/******************************************/
1879/* Linux 2.6-style accelerated functions */
1880/******************************************/
1881
Krzysztof Helt8503df62007-10-16 01:29:08 -07001882static void cirrusfb_fillrect(struct fb_info *info,
1883 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 struct fb_fillrect modded;
1886 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001887 struct cirrusfb_info *cinfo = info->par;
1888 int m = info->var.bits_per_pixel;
1889 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1890 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891
1892 if (info->state != FBINFO_STATE_RUNNING)
1893 return;
1894 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1895 cfb_fillrect(info, region);
1896 return;
1897 }
1898
1899 vxres = info->var.xres_virtual;
1900 vyres = info->var.yres_virtual;
1901
1902 memcpy(&modded, region, sizeof(struct fb_fillrect));
1903
Krzysztof Helt8503df62007-10-16 01:29:08 -07001904 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 modded.dx >= vxres || modded.dy >= vyres)
1906 return;
1907
Krzysztof Helt8503df62007-10-16 01:29:08 -07001908 if (modded.dx + modded.width > vxres)
1909 modded.width = vxres - modded.dx;
1910 if (modded.dy + modded.height > vyres)
1911 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912
Krzysztof Helt060b6002007-10-16 01:29:13 -07001913 cirrusfb_RectFill(cinfo->regbase,
1914 info->var.bits_per_pixel,
1915 (region->dx * m) / 8, region->dy,
1916 (region->width * m) / 8, region->height,
1917 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001918 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919}
1920
Krzysztof Helt8503df62007-10-16 01:29:08 -07001921static void cirrusfb_copyarea(struct fb_info *info,
1922 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 struct fb_copyarea modded;
1925 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001926 struct cirrusfb_info *cinfo = info->par;
1927 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928
1929 if (info->state != FBINFO_STATE_RUNNING)
1930 return;
1931 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1932 cfb_copyarea(info, area);
1933 return;
1934 }
1935
1936 vxres = info->var.xres_virtual;
1937 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001938 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939
Krzysztof Helt8503df62007-10-16 01:29:08 -07001940 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 modded.sx >= vxres || modded.sy >= vyres ||
1942 modded.dx >= vxres || modded.dy >= vyres)
1943 return;
1944
Krzysztof Helt8503df62007-10-16 01:29:08 -07001945 if (modded.sx + modded.width > vxres)
1946 modded.width = vxres - modded.sx;
1947 if (modded.dx + modded.width > vxres)
1948 modded.width = vxres - modded.dx;
1949 if (modded.sy + modded.height > vyres)
1950 modded.height = vyres - modded.sy;
1951 if (modded.dy + modded.height > vyres)
1952 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953
Krzysztof Helt060b6002007-10-16 01:29:13 -07001954 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1955 (area->sx * m) / 8, area->sy,
1956 (area->dx * m) / 8, area->dy,
1957 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001958 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001959
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960}
1961
Krzysztof Helt8503df62007-10-16 01:29:08 -07001962static void cirrusfb_imageblit(struct fb_info *info,
1963 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964{
1965 struct cirrusfb_info *cinfo = info->par;
1966
Krzysztof Helt8503df62007-10-16 01:29:08 -07001967 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968 cfb_imageblit(info, image);
1969}
1970
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971#ifdef CONFIG_PPC_PREP
1972#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1973#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001974static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001976 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977
1978 *display = PREP_VIDEO_BASE;
1979 *registers = (unsigned long) PREP_IO_BASE;
1980
Krzysztof Helt8503df62007-10-16 01:29:08 -07001981 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982}
1983
1984#endif /* CONFIG_PPC_PREP */
1985
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001987static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988
1989/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1990 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1991 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1992 * seem to have. */
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001993static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994{
1995 unsigned long mem;
1996 unsigned char SRF;
1997
Krzysztof Helt8503df62007-10-16 01:29:08 -07001998 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999
Krzysztof Helt8503df62007-10-16 01:29:08 -07002000 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002002 case 0x08:
2003 mem = 512 * 1024;
2004 break;
2005 case 0x10:
2006 mem = 1024 * 1024;
2007 break;
2008 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2009 * on the 5430.
2010 */
2011 case 0x18:
2012 mem = 2048 * 1024;
2013 break;
2014 default:
2015 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 mem = 1024 * 1024;
2017 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002018 if (SRF & 0x80)
2019 /* If DRAM bank switching is enabled, there must be twice as much
2020 * memory installed. (4MB on the 5434)
2021 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002023
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2025
Krzysztof Helt8503df62007-10-16 01:29:08 -07002026 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 return mem;
2028}
2029
Krzysztof Helt8503df62007-10-16 01:29:08 -07002030static void get_pci_addrs(const struct pci_dev *pdev,
2031 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002033 assert(pdev != NULL);
2034 assert(display != NULL);
2035 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036
Krzysztof Helt8503df62007-10-16 01:29:08 -07002037 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038
2039 *display = 0;
2040 *registers = 0;
2041
2042 /* This is a best-guess for now */
2043
2044 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2045 *display = pci_resource_start(pdev, 1);
2046 *registers = pci_resource_start(pdev, 0);
2047 } else {
2048 *display = pci_resource_start(pdev, 0);
2049 *registers = pci_resource_start(pdev, 1);
2050 }
2051
Krzysztof Helt8503df62007-10-16 01:29:08 -07002052 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053
Krzysztof Helt8503df62007-10-16 01:29:08 -07002054 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055}
2056
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002057static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002059 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002061 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062#if 0 /* if system didn't claim this region, we would... */
2063 release_mem_region(0xA0000, 65535);
2064#endif
2065 if (release_io_ports)
2066 release_region(0x3C0, 32);
2067 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068}
2069#endif /* CONFIG_PCI */
2070
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002072static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073{
Al Virod91f5bb2007-10-17 00:27:18 +01002074 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002075 struct zorro_dev *zdev = to_zorro_dev(info->device);
2076
2077 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078
2079 if (cinfo->btype == BT_PICASSO4) {
2080 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002081 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002082 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002084 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002085 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087}
2088#endif /* CONFIG_ZORRO */
2089
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002090static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002092 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 struct fb_var_screeninfo *var = &info->var;
2094
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 info->pseudo_palette = cinfo->pseudo_palette;
2096 info->flags = FBINFO_DEFAULT
2097 | FBINFO_HWACCEL_XPAN
2098 | FBINFO_HWACCEL_YPAN
2099 | FBINFO_HWACCEL_FILLRECT
2100 | FBINFO_HWACCEL_COPYAREA;
2101 if (noaccel)
2102 info->flags |= FBINFO_HWACCEL_DISABLED;
2103 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 if (cinfo->btype == BT_GD5480) {
2105 if (var->bits_per_pixel == 16)
2106 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002107 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 info->screen_base += 2 * MB_;
2109 }
2110
2111 /* Fill fix common fields */
2112 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2113 sizeof(info->fix.id));
2114
2115 /* monochrome: only 1 memory plane */
2116 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002117 info->fix.smem_len = info->screen_size;
2118 if (var->bits_per_pixel == 1)
2119 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 info->fix.xpanstep = 1;
2122 info->fix.ypanstep = 1;
2123 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124
2125 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 info->fix.mmio_len = 0;
2127 info->fix.accel = FB_ACCEL_NONE;
2128
2129 fb_alloc_cmap(&info->cmap, 256, 0);
2130
2131 return 0;
2132}
2133
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002134static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002136 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002138 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139
Krzysztof Helt8503df62007-10-16 01:29:08 -07002140 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141
Krzysztof Helt8503df62007-10-16 01:29:08 -07002142 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2143 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 btype = cinfo->btype;
2146
2147 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002148 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002150 /* set all the vital stuff */
2151 cirrusfb_set_fbinfo(info);
2152
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002153 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002155 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2156 if (!err) {
2157 DPRINTK("wrong initial video mode\n");
2158 err = -EINVAL;
2159 goto err_dealloc_cmap;
2160 }
2161
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 info->var.activate = FB_ACTIVATE_NOW;
2163
2164 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2165 if (err < 0) {
2166 /* should never happen */
2167 DPRINTK("choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002168 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 }
2170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 err = register_framebuffer(info);
2172 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002173 printk(KERN_ERR "cirrusfb: could not register "
2174 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 goto err_dealloc_cmap;
2176 }
2177
Krzysztof Helt8503df62007-10-16 01:29:08 -07002178 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 return 0;
2180
2181err_dealloc_cmap:
2182 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002183 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002184 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 return err;
2186}
2187
Krzysztof Helt8503df62007-10-16 01:29:08 -07002188static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189{
2190 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002191 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192
Krzysztof Helt8503df62007-10-16 01:29:08 -07002193 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194
Krzysztof Helt8503df62007-10-16 01:29:08 -07002195 unregister_framebuffer(info);
2196 fb_dealloc_cmap(&info->cmap);
2197 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002198 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002199 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200
Krzysztof Helt8503df62007-10-16 01:29:08 -07002201 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202}
2203
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002205static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2206 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207{
2208 struct cirrusfb_info *cinfo;
2209 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002210 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 unsigned long board_addr, board_size;
2212 int ret;
2213
2214 ret = pci_enable_device(pdev);
2215 if (ret < 0) {
2216 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2217 goto err_out;
2218 }
2219
2220 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2221 if (!info) {
2222 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2223 ret = -ENOMEM;
2224 goto err_disable;
2225 }
2226
2227 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002228 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229
Krzysztof Helt7345de32007-10-16 01:29:11 -07002230 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002232 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233
Krzysztof Helt8503df62007-10-16 01:29:08 -07002234 if (isPReP) {
2235 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002237 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002239 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002240 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002242 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002243 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002244 /* FIXME: this forces VGA. alternatives? */
2245 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 }
2247
Krzysztof Helt8503df62007-10-16 01:29:08 -07002248 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002249 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
2251 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002252 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253
2254 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002255 if (ret < 0) {
2256 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2257 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 board_addr);
2259 goto err_release_fb;
2260 }
2261#if 0 /* if the system didn't claim this region, we would... */
2262 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2263 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2264,
2265 0xA0000L);
2266 ret = -EBUSY;
2267 goto err_release_regions;
2268 }
2269#endif
2270 if (request_region(0x3C0, 32, "cirrusfb"))
2271 release_io_ports = 1;
2272
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002273 info->screen_base = ioremap(board_addr, board_size);
2274 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 ret = -EIO;
2276 goto err_release_legacy;
2277 }
2278
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002279 info->fix.smem_start = board_addr;
2280 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 cinfo->unmap = cirrusfb_pci_unmap;
2282
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002283 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2284 "Logic chipset on PCI bus\n",
2285 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 pci_set_drvdata(pdev, info);
2287
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002288 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002289 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002290 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002291 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292
2293err_release_legacy:
2294 if (release_io_ports)
2295 release_region(0x3C0, 32);
2296#if 0
2297 release_mem_region(0xA0000, 65535);
2298err_release_regions:
2299#endif
2300 pci_release_regions(pdev);
2301err_release_fb:
2302 framebuffer_release(info);
2303err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304err_out:
2305 return ret;
2306}
2307
Krzysztof Helt8503df62007-10-16 01:29:08 -07002308static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309{
2310 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002311 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312
Krzysztof Helt8503df62007-10-16 01:29:08 -07002313 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314
Krzysztof Helt8503df62007-10-16 01:29:08 -07002315 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316}
2317
2318static struct pci_driver cirrusfb_pci_driver = {
2319 .name = "cirrusfb",
2320 .id_table = cirrusfb_pci_table,
2321 .probe = cirrusfb_pci_register,
2322 .remove = __devexit_p(cirrusfb_pci_unregister),
2323#ifdef CONFIG_PM
2324#if 0
2325 .suspend = cirrusfb_pci_suspend,
2326 .resume = cirrusfb_pci_resume,
2327#endif
2328#endif
2329};
2330#endif /* CONFIG_PCI */
2331
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002333static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2334 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335{
2336 struct cirrusfb_info *cinfo;
2337 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002338 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339 struct zorro_dev *z2 = NULL;
2340 unsigned long board_addr, board_size, size;
2341 int ret;
2342
2343 btype = ent->driver_data;
2344 if (cirrusfb_zorro_table2[btype].id2)
2345 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2346 size = cirrusfb_zorro_table2[btype].size;
2347 printk(KERN_INFO "cirrusfb: %s board detected; ",
2348 cirrusfb_board_info[btype].name);
2349
2350 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2351 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002352 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 ret = -ENOMEM;
2354 goto err_out;
2355 }
2356
2357 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 cinfo->btype = btype;
2359
Al Viro36ea96a2007-10-27 19:46:58 +01002360 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002361 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363 board_addr = zorro_resource_start(z);
2364 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002365 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366
2367 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002368 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2369 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 board_addr);
2371 ret = -EBUSY;
2372 goto err_release_fb;
2373 }
2374
Krzysztof Helt8503df62007-10-16 01:29:08 -07002375 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376
2377 ret = -EIO;
2378
2379 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002380 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381
2382 /* To be precise, for the P4 this is not the */
2383 /* begin of the board, but the begin of RAM. */
2384 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2385 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002386 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 if (!cinfo->regbase)
2388 goto err_release_region;
2389
Krzysztof Helt8503df62007-10-16 01:29:08 -07002390 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2391 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002393 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002395 info->fix.smem_start = board_addr + 16777216;
2396 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2397 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 goto err_unmap_regbase;
2399 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002400 printk(KERN_INFO " REG at $%lx\n",
2401 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002403 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002405 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002407 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2408 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 goto err_release_region;
2410
2411 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002412 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002413 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414
Krzysztof Helt8503df62007-10-16 01:29:08 -07002415 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2416 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 }
2418 cinfo->unmap = cirrusfb_zorro_unmap;
2419
Krzysztof Helt8503df62007-10-16 01:29:08 -07002420 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 zorro_set_drvdata(z, info);
2422
Al Virod91f5bb2007-10-17 00:27:18 +01002423 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002424 if (ret) {
2425 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002426 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002427 iounmap(cinfo->regbase - 0x600000);
2428 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002429 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002430 }
2431 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432
2433err_unmap_regbase:
2434 /* Parental advisory: explicit hack */
2435 iounmap(cinfo->regbase - 0x600000);
2436err_release_region:
2437 release_region(board_addr, board_size);
2438err_release_fb:
2439 framebuffer_release(info);
2440err_out:
2441 return ret;
2442}
2443
2444void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2445{
2446 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002447 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448
Krzysztof Helt8503df62007-10-16 01:29:08 -07002449 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450
Krzysztof Helt8503df62007-10-16 01:29:08 -07002451 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452}
2453
2454static struct zorro_driver cirrusfb_zorro_driver = {
2455 .name = "cirrusfb",
2456 .id_table = cirrusfb_zorro_table,
2457 .probe = cirrusfb_zorro_register,
2458 .remove = __devexit_p(cirrusfb_zorro_unregister),
2459};
2460#endif /* CONFIG_ZORRO */
2461
2462static int __init cirrusfb_init(void)
2463{
2464 int error = 0;
2465
2466#ifndef MODULE
2467 char *option = NULL;
2468
2469 if (fb_get_options("cirrusfb", &option))
2470 return -ENODEV;
2471 cirrusfb_setup(option);
2472#endif
2473
2474#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002475 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476#endif
2477#ifdef CONFIG_PCI
2478 error |= pci_register_driver(&cirrusfb_pci_driver);
2479#endif
2480 return error;
2481}
2482
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483#ifndef MODULE
2484static int __init cirrusfb_setup(char *options) {
2485 char *this_opt, s[32];
2486 int i;
2487
Krzysztof Helt8503df62007-10-16 01:29:08 -07002488 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489
2490 if (!options || !*options)
2491 return 0;
2492
Krzysztof Helt8503df62007-10-16 01:29:08 -07002493 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002494 if (!*this_opt)
2495 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496
2497 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2498
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 if (!strcmp(this_opt, "noaccel"))
2500 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002501 else if (!strncmp(this_opt, "mode:", 5))
2502 mode_option = this_opt + 5;
2503 else
2504 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 }
2506 return 0;
2507}
2508#endif
2509
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 /*
2511 * Modularization
2512 */
2513
2514MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2515MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2516MODULE_LICENSE("GPL");
2517
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519{
2520#ifdef CONFIG_PCI
2521 pci_unregister_driver(&cirrusfb_pci_driver);
2522#endif
2523#ifdef CONFIG_ZORRO
2524 zorro_unregister_driver(&cirrusfb_zorro_driver);
2525#endif
2526}
2527
2528module_init(cirrusfb_init);
2529
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002530module_param(mode_option, charp, 0);
2531MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
2532
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533#ifdef MODULE
2534module_exit(cirrusfb_exit);
2535#endif
2536
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537/**********************************************************************/
2538/* about the following functions - I have used the same names for the */
2539/* functions as Markus Wild did in his Retina driver for NetBSD as */
2540/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542/**********************************************************************/
2543
2544/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002545static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 int regnum, unsigned char val)
2547{
2548 unsigned long regofs = 0;
2549
2550 if (cinfo->btype == BT_PICASSO) {
2551 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002552/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2553 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2555 regofs = 0xfff;
2556 }
2557
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559}
2560
2561/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002562static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563{
2564 unsigned long regofs = 0;
2565
2566 if (cinfo->btype == BT_PICASSO) {
2567 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2569 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2571 regofs = 0xfff;
2572 }
2573
Krzysztof Helt8503df62007-10-16 01:29:08 -07002574 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575}
2576
2577/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002580 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583
Krzysztof Helt8503df62007-10-16 01:29:08 -07002584 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 /* if we're just in "write value" mode, write back the */
2586 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002587 vga_w(cinfo->regbase, VGA_ATT_IW,
2588 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 }
2590 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002591/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2592 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593
2594 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002595 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596
Krzysztof Helt8503df62007-10-16 01:29:08 -07002597 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598}
2599
2600/*** WHDR() - write into the Hidden DAC register ***/
2601/* as the HDR is the only extension register that requires special treatment
2602 * (the other extension registers are accessible just like the "ordinary"
2603 * registers of their functional group) here is a specialized routine for
2604 * accessing the HDR
2605 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002606static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607{
2608 unsigned char dummy;
2609
2610 if (cinfo->btype == BT_PICASSO) {
2611 /* Klaus' hint for correct access to HDR on some boards */
2612 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002613 WGen(cinfo, VGA_PEL_MSK, 0x00);
2614 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002616 dummy = RGen(cinfo, VGA_PEL_IW);
2617 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618 }
2619 /* now do the usual stuff to access the HDR */
2620
Krzysztof Helt8503df62007-10-16 01:29:08 -07002621 dummy = RGen(cinfo, VGA_PEL_MSK);
2622 udelay(200);
2623 dummy = RGen(cinfo, VGA_PEL_MSK);
2624 udelay(200);
2625 dummy = RGen(cinfo, VGA_PEL_MSK);
2626 udelay(200);
2627 dummy = RGen(cinfo, VGA_PEL_MSK);
2628 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629
Krzysztof Helt8503df62007-10-16 01:29:08 -07002630 WGen(cinfo, VGA_PEL_MSK, val);
2631 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632
2633 if (cinfo->btype == BT_PICASSO) {
2634 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002635 dummy = RGen(cinfo, VGA_PEL_IW);
2636 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
2638 /* and at the end, restore the mask value */
2639 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002640 WGen(cinfo, VGA_PEL_MSK, 0xff);
2641 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642 }
2643}
2644
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002646static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647{
2648#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002649 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002651 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652#endif
2653}
2654
2655/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657{
2658#ifdef CONFIG_ZORRO
2659 /* writing an arbitrary value to this one causes the monitor switcher */
2660 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002661 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664#endif
2665}
2666
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002668static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 unsigned char green, unsigned char blue)
2670{
2671 unsigned int data = VGA_PEL_D;
2672
2673 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002674 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675
2676 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2677 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2678 /* but DAC data register IS, at least for Picasso II */
2679 if (cinfo->btype == BT_PICASSO)
2680 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002681 vga_w(cinfo->regbase, data, red);
2682 vga_w(cinfo->regbase, data, green);
2683 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002685 vga_w(cinfo->regbase, data, blue);
2686 vga_w(cinfo->regbase, data, green);
2687 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688 }
2689}
2690
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691#if 0
2692/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002693static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 unsigned char *green, unsigned char *blue)
2695{
2696 unsigned int data = VGA_PEL_D;
2697
Krzysztof Helt8503df62007-10-16 01:29:08 -07002698 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699
2700 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2701 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2702 if (cinfo->btype == BT_PICASSO)
2703 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002704 *red = vga_r(cinfo->regbase, data);
2705 *green = vga_r(cinfo->regbase, data);
2706 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002708 *blue = vga_r(cinfo->regbase, data);
2709 *green = vga_r(cinfo->regbase, data);
2710 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 }
2712}
2713#endif
2714
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715/*******************************************************************
2716 cirrusfb_WaitBLT()
2717
2718 Wait for the BitBLT engine to complete a possible earlier job
2719*********************************************************************/
2720
2721/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002722static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723{
2724 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002725 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726 /* do nothing */ ;
2727}
2728
2729/*******************************************************************
2730 cirrusfb_BitBLT()
2731
2732 perform accelerated "scrolling"
2733********************************************************************/
2734
Krzysztof Helt8503df62007-10-16 01:29:08 -07002735static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2736 u_short curx, u_short cury,
2737 u_short destx, u_short desty,
2738 u_short width, u_short height,
2739 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740{
2741 u_short nwidth, nheight;
2742 u_long nsrc, ndest;
2743 u_char bltmode;
2744
Krzysztof Helt8503df62007-10-16 01:29:08 -07002745 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746
2747 nwidth = width - 1;
2748 nheight = height - 1;
2749
2750 bltmode = 0x00;
2751 /* if source adr < dest addr, do the Blt backwards */
2752 if (cury <= desty) {
2753 if (cury == desty) {
2754 /* if src and dest are on the same line, check x */
2755 if (curx < destx)
2756 bltmode |= 0x01;
2757 } else
2758 bltmode |= 0x01;
2759 }
2760 if (!bltmode) {
2761 /* standard case: forward blitting */
2762 nsrc = (cury * line_length) + curx;
2763 ndest = (desty * line_length) + destx;
2764 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002765 /* this means start addresses are at the end,
2766 * counting backwards
2767 */
2768 nsrc = cury * line_length + curx +
2769 nheight * line_length + nwidth;
2770 ndest = desty * line_length + destx +
2771 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 }
2773
2774 /*
2775 run-down of registers to be programmed:
2776 destination pitch
2777 source pitch
2778 BLT width/height
2779 source start
2780 destination start
2781 BLT mode
2782 BLT ROP
2783 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2784 start/stop
2785 */
2786
Krzysztof Helt8503df62007-10-16 01:29:08 -07002787 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788
2789 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002790 /* dest pitch low */
2791 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2792 /* dest pitch hi */
2793 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2794 /* source pitch low */
2795 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2796 /* source pitch hi */
2797 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798
2799 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002800 /* BLT width low */
2801 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2802 /* BLT width hi */
2803 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804
2805 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002806 /* BLT height low */
2807 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2808 /* BLT width hi */
2809 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810
2811 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002812 /* BLT dest low */
2813 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2814 /* BLT dest mid */
2815 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2816 /* BLT dest hi */
2817 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818
2819 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002820 /* BLT src low */
2821 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2822 /* BLT src mid */
2823 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2824 /* BLT src hi */
2825 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826
2827 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002828 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829
2830 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002831 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832
2833 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002834 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835
Krzysztof Helt8503df62007-10-16 01:29:08 -07002836 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837}
2838
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839/*******************************************************************
2840 cirrusfb_RectFill()
2841
2842 perform accelerated rectangle fill
2843********************************************************************/
2844
Krzysztof Helt8503df62007-10-16 01:29:08 -07002845static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846 u_short x, u_short y, u_short width, u_short height,
2847 u_char color, u_short line_length)
2848{
2849 u_short nwidth, nheight;
2850 u_long ndest;
2851 u_char op;
2852
Krzysztof Helt8503df62007-10-16 01:29:08 -07002853 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854
2855 nwidth = width - 1;
2856 nheight = height - 1;
2857
2858 ndest = (y * line_length) + x;
2859
Krzysztof Helt8503df62007-10-16 01:29:08 -07002860 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861
2862 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002863 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2864 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2865 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2866 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867
2868 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002869 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2870 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871
2872 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002873 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2874 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875
2876 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002877 /* BLT dest low */
2878 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2879 /* BLT dest mid */
2880 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2881 /* BLT dest hi */
2882 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883
2884 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002885 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2886 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2887 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888
2889 /* This is a ColorExpand Blt, using the */
2890 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002891 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2892 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893
2894 op = 0xc0;
2895 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002896 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2897 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898 op = 0x50;
2899 op = 0xd0;
2900 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002901 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2902 vga_wgfx(regbase, CL_GR11, color); /* background color */
2903 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2904 vga_wgfx(regbase, CL_GR13, color); /* background color */
2905 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2906 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 op = 0x50;
2908 op = 0xf0;
2909 }
2910 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002911 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912
2913 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002914 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915
2916 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002917 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918
Krzysztof Helt8503df62007-10-16 01:29:08 -07002919 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920}
2921
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922/**************************************************************************
2923 * bestclock() - determine closest possible clock lower(?) than the
2924 * desired pixel clock
2925 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002926static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927 long *den, long *div, long maxfreq)
2928{
2929 long n, h, d, f;
2930
Krzysztof Helt8503df62007-10-16 01:29:08 -07002931 assert(best != NULL);
2932 assert(nom != NULL);
2933 assert(den != NULL);
2934 assert(div != NULL);
2935 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936
2937 *nom = 0;
2938 *den = 0;
2939 *div = 0;
2940
Krzysztof Helt8503df62007-10-16 01:29:08 -07002941 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942
2943 if (freq < 8000)
2944 freq = 8000;
2945
2946 if (freq > maxfreq)
2947 freq = maxfreq;
2948
2949 *best = 0;
2950 f = freq * 10;
2951
2952 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002953 int s = 0;
2954
Linus Torvalds1da177e2005-04-16 15:20:36 -07002955 d = (143181 * n) / f;
2956 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002957 int temp = d;
2958
2959 if (temp > 31) {
2960 s = 1;
2961 temp >>= 1;
2962 }
2963 h = ((14318 * n) / temp) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002964 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002965 *best = h;
2966 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002967 *den = temp;
2968 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969 }
2970 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002971 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002973 if (d > 31) {
2974 s = 1;
2975 d >>= 1;
2976 }
2977 h = ((14318 * n) / d) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002978 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 *best = h;
2980 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002981 *den = d;
2982 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983 }
2984 }
2985 }
2986
Krzysztof Helt8503df62007-10-16 01:29:08 -07002987 DPRINTK("Best possible values for given frequency:\n");
2988 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
2989 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990
Krzysztof Helt8503df62007-10-16 01:29:08 -07002991 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002992}
2993
Linus Torvalds1da177e2005-04-16 15:20:36 -07002994/* -------------------------------------------------------------------------
2995 *
2996 * debugging functions
2997 *
2998 * -------------------------------------------------------------------------
2999 */
3000
3001#ifdef CIRRUSFB_DEBUG
3002
3003/**
3004 * cirrusfb_dbg_print_byte
3005 * @name: name associated with byte value to be displayed
3006 * @val: byte value to be displayed
3007 *
3008 * DESCRIPTION:
3009 * Display an indented string, along with a hexidecimal byte value, and
3010 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3011 * order.
3012 */
3013
3014static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003015void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003016{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003017 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3018 name, val,
3019 val & 0x80 ? '1' : '0',
3020 val & 0x40 ? '1' : '0',
3021 val & 0x20 ? '1' : '0',
3022 val & 0x10 ? '1' : '0',
3023 val & 0x08 ? '1' : '0',
3024 val & 0x04 ? '1' : '0',
3025 val & 0x02 ? '1' : '0',
3026 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003027}
3028
Linus Torvalds1da177e2005-04-16 15:20:36 -07003029/**
3030 * cirrusfb_dbg_print_regs
3031 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3032 * @reg_class: type of registers to read: %CRT, or %SEQ
3033 *
3034 * DESCRIPTION:
3035 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3036 * old-style I/O ports are queried for information, otherwise MMIO is
3037 * used at the given @base address to query the information.
3038 */
3039
3040static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003041void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003042 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043{
3044 va_list list;
3045 unsigned char val = 0;
3046 unsigned reg;
3047 char *name;
3048
Krzysztof Helt8503df62007-10-16 01:29:08 -07003049 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003050
Krzysztof Helt8503df62007-10-16 01:29:08 -07003051 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003053 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003054
3055 switch (reg_class) {
3056 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003057 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058 break;
3059 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003060 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061 break;
3062 default:
3063 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003064 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003065 break;
3066 }
3067
Krzysztof Helt8503df62007-10-16 01:29:08 -07003068 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003069
Krzysztof Helt8503df62007-10-16 01:29:08 -07003070 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003071 }
3072
Krzysztof Helt8503df62007-10-16 01:29:08 -07003073 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003074}
3075
Linus Torvalds1da177e2005-04-16 15:20:36 -07003076/**
3077 * cirrusfb_dump
3078 * @cirrusfbinfo:
3079 *
3080 * DESCRIPTION:
3081 */
3082
Krzysztof Helt8503df62007-10-16 01:29:08 -07003083static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003085 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003086}
3087
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088/**
3089 * cirrusfb_dbg_reg_dump
3090 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3091 *
3092 * DESCRIPTION:
3093 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3094 * old-style I/O ports are queried for information, otherwise MMIO is
3095 * used at the given @base address to query the information.
3096 */
3097
3098static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003099void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003101 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003102
Krzysztof Helt8503df62007-10-16 01:29:08 -07003103 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104 "CR00", 0x00,
3105 "CR01", 0x01,
3106 "CR02", 0x02,
3107 "CR03", 0x03,
3108 "CR04", 0x04,
3109 "CR05", 0x05,
3110 "CR06", 0x06,
3111 "CR07", 0x07,
3112 "CR08", 0x08,
3113 "CR09", 0x09,
3114 "CR0A", 0x0A,
3115 "CR0B", 0x0B,
3116 "CR0C", 0x0C,
3117 "CR0D", 0x0D,
3118 "CR0E", 0x0E,
3119 "CR0F", 0x0F,
3120 "CR10", 0x10,
3121 "CR11", 0x11,
3122 "CR12", 0x12,
3123 "CR13", 0x13,
3124 "CR14", 0x14,
3125 "CR15", 0x15,
3126 "CR16", 0x16,
3127 "CR17", 0x17,
3128 "CR18", 0x18,
3129 "CR22", 0x22,
3130 "CR24", 0x24,
3131 "CR26", 0x26,
3132 "CR2D", 0x2D,
3133 "CR2E", 0x2E,
3134 "CR2F", 0x2F,
3135 "CR30", 0x30,
3136 "CR31", 0x31,
3137 "CR32", 0x32,
3138 "CR33", 0x33,
3139 "CR34", 0x34,
3140 "CR35", 0x35,
3141 "CR36", 0x36,
3142 "CR37", 0x37,
3143 "CR38", 0x38,
3144 "CR39", 0x39,
3145 "CR3A", 0x3A,
3146 "CR3B", 0x3B,
3147 "CR3C", 0x3C,
3148 "CR3D", 0x3D,
3149 "CR3E", 0x3E,
3150 "CR3F", 0x3F,
3151 NULL);
3152
Krzysztof Helt8503df62007-10-16 01:29:08 -07003153 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154
Krzysztof Helt8503df62007-10-16 01:29:08 -07003155 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003156
Krzysztof Helt8503df62007-10-16 01:29:08 -07003157 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003158 "SR00", 0x00,
3159 "SR01", 0x01,
3160 "SR02", 0x02,
3161 "SR03", 0x03,
3162 "SR04", 0x04,
3163 "SR08", 0x08,
3164 "SR09", 0x09,
3165 "SR0A", 0x0A,
3166 "SR0B", 0x0B,
3167 "SR0D", 0x0D,
3168 "SR10", 0x10,
3169 "SR11", 0x11,
3170 "SR12", 0x12,
3171 "SR13", 0x13,
3172 "SR14", 0x14,
3173 "SR15", 0x15,
3174 "SR16", 0x16,
3175 "SR17", 0x17,
3176 "SR18", 0x18,
3177 "SR19", 0x19,
3178 "SR1A", 0x1A,
3179 "SR1B", 0x1B,
3180 "SR1C", 0x1C,
3181 "SR1D", 0x1D,
3182 "SR1E", 0x1E,
3183 "SR1F", 0x1F,
3184 NULL);
3185
Krzysztof Helt8503df62007-10-16 01:29:08 -07003186 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003187}
3188
3189#endif /* CIRRUSFB_DEBUG */
3190