blob: 4e34a6817435a55706ca0a26eadd178a3e61fc3e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
37#define CIRRUSFB_VERSION "2.0-pre2"
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/module.h>
40#include <linux/kernel.h>
41#include <linux/errno.h>
42#include <linux/string.h>
43#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/slab.h>
45#include <linux/delay.h>
46#include <linux/fb.h>
47#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <asm/pgtable.h>
49
50#ifdef CONFIG_ZORRO
51#include <linux/zorro.h>
52#endif
53#ifdef CONFIG_PCI
54#include <linux/pci.h>
55#endif
56#ifdef CONFIG_AMIGA
57#include <asm/amigahw.h>
58#endif
59#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110060#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070061#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#else
63#define isPReP 0
64#endif
65
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070066#include <video/vga.h>
67#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/*****************************************************************
70 *
71 * debugging and utility macros
72 *
73 */
74
75/* enable debug output? */
76/* #define CIRRUSFB_DEBUG 1 */
77
78/* disable runtime assertions? */
79/* #define CIRRUSFB_NDEBUG */
80
81/* debug output */
82#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -070083#define DPRINTK(fmt, args...) \
Harvey Harrison5ae12172008-04-28 02:15:47 -070084 printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#else
86#define DPRINTK(fmt, args...)
87#endif
88
89/* debugging assertions */
90#ifndef CIRRUSFB_NDEBUG
91#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070092 if (!(expr)) { \
93 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070094 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070095 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070096#else
97#define assert(expr)
98#endif
99
Krzysztof Helt8503df62007-10-16 01:29:08 -0700100#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*****************************************************************
103 *
104 * chipset information
105 *
106 */
107
108/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700109enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 BT_NONE = 0,
111 BT_SD64,
112 BT_PICCOLO,
113 BT_PICASSO,
114 BT_SPECTRUM,
115 BT_PICASSO4, /* GD5446 */
116 BT_ALPINE, /* GD543x/4x */
117 BT_GD5480,
118 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700119};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * per-board-type information, used for enumerating and abstracting
123 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700124 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 * use direct indexing on this array
126 * NOTE: '__initdata' cannot be used as some of this info
127 * is required at runtime. Maybe separate into an init-only and
128 * a run-time table?
129 */
130static const struct cirrusfb_board_info_rec {
131 char *name; /* ASCII name of chipset */
132 long maxclock[5]; /* maximum video clock */
133 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700134 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
135 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700136 /* construct bit 19 of screen start address */
137 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139 /* initial SR07 value, then for each mode */
140 unsigned char sr07;
141 unsigned char sr07_1bpp;
142 unsigned char sr07_1bpp_mux;
143 unsigned char sr07_8bpp;
144 unsigned char sr07_8bpp_mux;
145
146 unsigned char sr1f; /* SR1F VGA initial register value */
147} cirrusfb_board_info[] = {
148 [BT_SD64] = {
149 .name = "CL SD64",
150 .maxclock = {
151 /* guess */
152 /* the SD64/P4 have a higher max. videoclock */
153 140000, 140000, 140000, 140000, 140000,
154 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700155 .init_sr07 = true,
156 .init_sr1f = true,
157 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 .sr07 = 0xF0,
159 .sr07_1bpp = 0xF0,
160 .sr07_8bpp = 0xF1,
161 .sr1f = 0x20
162 },
163 [BT_PICCOLO] = {
164 .name = "CL Piccolo",
165 .maxclock = {
166 /* guess */
167 90000, 90000, 90000, 90000, 90000
168 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700169 .init_sr07 = true,
170 .init_sr1f = true,
171 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .sr07 = 0x80,
173 .sr07_1bpp = 0x80,
174 .sr07_8bpp = 0x81,
175 .sr1f = 0x22
176 },
177 [BT_PICASSO] = {
178 .name = "CL Picasso",
179 .maxclock = {
180 /* guess */
181 90000, 90000, 90000, 90000, 90000
182 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700183 .init_sr07 = true,
184 .init_sr1f = true,
185 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 .sr07 = 0x20,
187 .sr07_1bpp = 0x20,
188 .sr07_8bpp = 0x21,
189 .sr1f = 0x22
190 },
191 [BT_SPECTRUM] = {
192 .name = "CL Spectrum",
193 .maxclock = {
194 /* guess */
195 90000, 90000, 90000, 90000, 90000
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = true,
199 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x80,
201 .sr07_1bpp = 0x80,
202 .sr07_8bpp = 0x81,
203 .sr1f = 0x22
204 },
205 [BT_PICASSO4] = {
206 .name = "CL Picasso4",
207 .maxclock = {
208 135100, 135100, 85500, 85500, 0
209 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700210 .init_sr07 = true,
211 .init_sr1f = false,
212 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 .sr07 = 0x20,
214 .sr07_1bpp = 0x20,
215 .sr07_8bpp = 0x21,
216 .sr1f = 0
217 },
218 [BT_ALPINE] = {
219 .name = "CL Alpine",
220 .maxclock = {
221 /* for the GD5430. GD5446 can do more... */
222 85500, 85500, 50000, 28500, 0
223 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700224 .init_sr07 = true,
225 .init_sr1f = true,
226 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .sr07 = 0xA0,
228 .sr07_1bpp = 0xA1,
229 .sr07_1bpp_mux = 0xA7,
230 .sr07_8bpp = 0xA1,
231 .sr07_8bpp_mux = 0xA7,
232 .sr1f = 0x1C
233 },
234 [BT_GD5480] = {
235 .name = "CL GD5480",
236 .maxclock = {
237 135100, 200000, 200000, 135100, 135100
238 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700239 .init_sr07 = true,
240 .init_sr1f = true,
241 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 .sr07 = 0x10,
243 .sr07_1bpp = 0x11,
244 .sr07_8bpp = 0x11,
245 .sr1f = 0x1C
246 },
247 [BT_LAGUNA] = {
248 .name = "CL Laguna",
249 .maxclock = {
250 /* guess */
251 135100, 135100, 135100, 135100, 135100,
252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329struct cirrusfb_regs {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 long freq;
331 long nom;
332 long den;
333 long div;
334 long multiplexing;
335 long mclk;
336 long divMCLK;
337
338 long HorizRes; /* The x resolution in pixel */
339 long HorizTotal;
340 long HorizDispEnd;
341 long HorizBlankStart;
342 long HorizBlankEnd;
343 long HorizSyncStart;
344 long HorizSyncEnd;
345
346 long VertRes; /* the physical y resolution in scanlines */
347 long VertTotal;
348 long VertDispEnd;
349 long VertSyncStart;
350 long VertSyncEnd;
351 long VertBlankStart;
352 long VertBlankEnd;
353};
354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700356enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700357 CRT,
358 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700359};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362/* info about board */
363struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700365 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 unsigned char SFR; /* Shadow of special function register */
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 struct cirrusfb_regs currentmode;
369 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700370 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700372 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373};
374
Krzysztof Helt8503df62007-10-16 01:29:08 -0700375static int noaccel;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700376static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378/****************************************************************************/
379/**** BEGIN PROTOTYPES ******************************************************/
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700382static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700384static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385#endif
386
Krzysztof Helt8503df62007-10-16 01:29:08 -0700387static int cirrusfb_open(struct fb_info *info, int user);
388static int cirrusfb_release(struct fb_info *info, int user);
389static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
390 unsigned blue, unsigned transp,
391 struct fb_info *info);
392static int cirrusfb_check_var(struct fb_var_screeninfo *var,
393 struct fb_info *info);
394static int cirrusfb_set_par(struct fb_info *info);
395static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
396 struct fb_info *info);
397static int cirrusfb_blank(int blank_mode, struct fb_info *info);
398static void cirrusfb_fillrect(struct fb_info *info,
399 const struct fb_fillrect *region);
400static void cirrusfb_copyarea(struct fb_info *info,
401 const struct fb_copyarea *area);
402static void cirrusfb_imageblit(struct fb_info *info,
403 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405/* function table of the above functions */
406static struct fb_ops cirrusfb_ops = {
407 .owner = THIS_MODULE,
408 .fb_open = cirrusfb_open,
409 .fb_release = cirrusfb_release,
410 .fb_setcolreg = cirrusfb_setcolreg,
411 .fb_check_var = cirrusfb_check_var,
412 .fb_set_par = cirrusfb_set_par,
413 .fb_pan_display = cirrusfb_pan_display,
414 .fb_blank = cirrusfb_blank,
415 .fb_fillrect = cirrusfb_fillrect,
416 .fb_copyarea = cirrusfb_copyarea,
417 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418};
419
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700421static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700422static void switch_monitor(struct cirrusfb_info *cinfo, int on);
423static void WGen(const struct cirrusfb_info *cinfo,
424 int regnum, unsigned char val);
425static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
426static void AttrOn(const struct cirrusfb_info *cinfo);
427static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
428static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
429static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
430static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
431 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700433static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
434 unsigned char *red, unsigned char *green,
435 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700437static void cirrusfb_WaitBLT(u8 __iomem *regbase);
438static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
439 u_short curx, u_short cury,
440 u_short destx, u_short desty,
441 u_short width, u_short height,
442 u_short line_length);
443static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
444 u_short x, u_short y,
445 u_short width, u_short height,
446 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
Krzysztof Helt8503df62007-10-16 01:29:08 -0700448static void bestclock(long freq, long *best,
449 long *nom, long *den,
450 long *div, long maxfreq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451
452#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700453static void cirrusfb_dump(void);
454static void cirrusfb_dbg_reg_dump(caddr_t regbase);
455static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700456 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700457static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458#endif /* CIRRUSFB_DEBUG */
459
460/*** END PROTOTYPES ********************************************************/
461/*****************************************************************************/
462/*** BEGIN Interface Used by the World ***************************************/
463
Krzysztof Helt8503df62007-10-16 01:29:08 -0700464static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
466/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700467static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
469 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700470 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 return 0;
472}
473
474/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700475static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
477 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700478 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 return 0;
480}
481
482/**** END Interface used by the World *************************************/
483/****************************************************************************/
484/**** BEGIN Hardware specific Routines **************************************/
485
486/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700487static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488{
489 long mclk;
490
Krzysztof Helt8503df62007-10-16 01:29:08 -0700491 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
493 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
494 * Assume a 64-bit data path for now. The formula is:
495 * ((B * PCLK * 2)/W) * 1.2
496 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
497 mclk = ((bpp / 8) * freq * 2) / 4;
498 mclk = (mclk * 12) / 10;
499 if (mclk < 50000)
500 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700501 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
504 mclk = ((mclk * 16) / 14318);
505 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700506 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 /* Determine if we should use MCLK instead of VCLK, and if so, what we
509 * should divide it by to get VCLK */
510 switch (freq) {
511 case 24751 ... 25249:
512 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700513 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 break;
515 case 49501 ... 50499:
516 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700517 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 break;
519 default:
520 *div = 0;
521 break;
522 }
523
524 return mclk;
525}
526
527static int cirrusfb_check_var(struct fb_var_screeninfo *var,
528 struct fb_info *info)
529{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700530 int yres;
531 /* memory size in pixels */
532 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
534 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700535 case 1:
Krzysztof Helt09a29102008-09-02 14:35:51 -0700536 pixels /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700538 case 8:
539 case 16:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700540 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700543 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
544 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700546 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 return -EINVAL;
548 }
549
Krzysztof Helt09a29102008-09-02 14:35:51 -0700550 if (var->xres_virtual < var->xres)
551 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700553 if (var->yres_virtual == -1) {
554 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Krzysztof Helt8503df62007-10-16 01:29:08 -0700556 printk(KERN_INFO "cirrusfb: virtual resolution set to "
557 "maximum of %dx%d\n", var->xres_virtual,
558 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 if (var->yres_virtual < var->yres)
561 var->yres_virtual = var->yres;
562
Krzysztof Helt09a29102008-09-02 14:35:51 -0700563 if (var->xres_virtual * var->yres_virtual > pixels) {
564 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
565 "virtual resolution too high to fit into video memory!\n",
566 var->xres_virtual, var->yres_virtual,
567 var->bits_per_pixel);
568 DPRINTK("EXIT - EINVAL error\n");
569 return -EINVAL;
570 }
571
572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 if (var->xoffset < 0)
574 var->xoffset = 0;
575 if (var->yoffset < 0)
576 var->yoffset = 0;
577
578 /* truncate xoffset and yoffset to maximum if too high */
579 if (var->xoffset > var->xres_virtual - var->xres)
580 var->xoffset = var->xres_virtual - var->xres - 1;
581 if (var->yoffset > var->yres_virtual - var->yres)
582 var->yoffset = var->yres_virtual - var->yres - 1;
583
584 switch (var->bits_per_pixel) {
585 case 1:
586 var->red.offset = 0;
587 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700588 var->green = var->red;
589 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 break;
591
592 case 8:
593 var->red.offset = 0;
594 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700595 var->green = var->red;
596 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 break;
598
599 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700600 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 var->red.offset = 2;
602 var->green.offset = -3;
603 var->blue.offset = 8;
604 } else {
605 var->red.offset = 10;
606 var->green.offset = 5;
607 var->blue.offset = 0;
608 }
609 var->red.length = 5;
610 var->green.length = 5;
611 var->blue.length = 5;
612 break;
613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700615 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 var->red.offset = 8;
617 var->green.offset = 16;
618 var->blue.offset = 24;
619 } else {
620 var->red.offset = 16;
621 var->green.offset = 8;
622 var->blue.offset = 0;
623 }
624 var->red.length = 8;
625 var->green.length = 8;
626 var->blue.length = 8;
627 break;
628
629 default:
630 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700631 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 /* should never occur */
633 break;
634 }
635
636 var->red.msb_right =
637 var->green.msb_right =
638 var->blue.msb_right =
639 var->transp.offset =
640 var->transp.length =
641 var->transp.msb_right = 0;
642
643 yres = var->yres;
644 if (var->vmode & FB_VMODE_DOUBLE)
645 yres *= 2;
646 else if (var->vmode & FB_VMODE_INTERLACED)
647 yres = (yres + 1) / 2;
648
649 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700650 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
651 "special treatment required! (TODO)\n");
652 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 return -EINVAL;
654 }
655
656 return 0;
657}
658
Krzysztof Helt8503df62007-10-16 01:29:08 -0700659static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700661 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
663 long freq;
664 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700665 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 struct cirrusfb_info *cinfo = info->par;
667 int xres, hfront, hsync, hback;
668 int yres, vfront, vsync, vback;
669
Krzysztof Helt8503df62007-10-16 01:29:08 -0700670 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700672 info->fix.line_length = var->xres_virtual / 8;
673 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 break;
675
676 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700677 info->fix.line_length = var->xres_virtual;
678 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 break;
680
681 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700683 info->fix.line_length = var->xres_virtual * maxclockidx;
684 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 break;
686
687 default:
688 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700689 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 /* should never occur */
691 break;
692 }
693
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700694 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700697 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Krzysztof Helt8503df62007-10-16 01:29:08 -0700699 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
701 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
702 regs->multiplexing = 0;
703
704 /* If the frequency is greater than we can support, we might be able
705 * to use multiplexing for the video mode */
706 if (freq > maxclock) {
707 switch (cinfo->btype) {
708 case BT_ALPINE:
709 case BT_GD5480:
710 regs->multiplexing = 1;
711 break;
712
713 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700714 printk(KERN_ERR "cirrusfb: Frequency greater "
715 "than maxclock (%ld kHz)\n", maxclock);
716 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 return -EINVAL;
718 }
719 }
720#if 0
721 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
722 * the VCLK is double the pixel clock. */
723 switch (var->bits_per_pixel) {
724 case 16:
725 case 32:
726 if (regs->HorizRes <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700727 /* Xbh has this type of clock for 32-bit */
728 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 break;
730 }
731#endif
732
Krzysztof Helt8503df62007-10-16 01:29:08 -0700733 bestclock(freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
734 maxclock);
735 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
736 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
738 xres = var->xres;
739 hfront = var->right_margin;
740 hsync = var->hsync_len;
741 hback = var->left_margin;
742
743 yres = var->yres;
744 vfront = var->lower_margin;
745 vsync = var->vsync_len;
746 vback = var->upper_margin;
747
748 if (var->vmode & FB_VMODE_DOUBLE) {
749 yres *= 2;
750 vfront *= 2;
751 vsync *= 2;
752 vback *= 2;
753 } else if (var->vmode & FB_VMODE_INTERLACED) {
754 yres = (yres + 1) / 2;
755 vfront = (vfront + 1) / 2;
756 vsync = (vsync + 1) / 2;
757 vback = (vback + 1) / 2;
758 }
759 regs->HorizRes = xres;
760 regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
761 regs->HorizDispEnd = xres / 8 - 1;
762 regs->HorizBlankStart = xres / 8;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700763 /* does not count with "-5" */
764 regs->HorizBlankEnd = regs->HorizTotal + 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 regs->HorizSyncStart = (xres + hfront) / 8 + 1;
766 regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
767
768 regs->VertRes = yres;
769 regs->VertTotal = yres + vfront + vsync + vback - 2;
770 regs->VertDispEnd = yres - 1;
771 regs->VertBlankStart = yres;
772 regs->VertBlankEnd = regs->VertTotal;
773 regs->VertSyncStart = yres + vfront - 1;
774 regs->VertSyncEnd = yres + vfront + vsync - 1;
775
776 if (regs->VertRes >= 1024) {
777 regs->VertTotal /= 2;
778 regs->VertSyncStart /= 2;
779 regs->VertSyncEnd /= 2;
780 regs->VertDispEnd /= 2;
781 }
782 if (regs->multiplexing) {
783 regs->HorizTotal /= 2;
784 regs->HorizSyncStart /= 2;
785 regs->HorizSyncEnd /= 2;
786 regs->HorizDispEnd /= 2;
787 }
788
789 return 0;
790}
791
Krzysztof Helt8503df62007-10-16 01:29:08 -0700792static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
793 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700795 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797 if (div == 2) {
798 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700799 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
800 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
801 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 } else if (div == 1) {
803 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700804 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
805 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
806 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700808 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 }
810}
811
812/*************************************************************************
813 cirrusfb_set_par_foo()
814
815 actually writes the values for a new video mode into the hardware,
816**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700817static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818{
819 struct cirrusfb_info *cinfo = info->par;
820 struct fb_var_screeninfo *var = &info->var;
821 struct cirrusfb_regs regs;
822 u8 __iomem *regbase = cinfo->regbase;
823 unsigned char tmp;
824 int offset = 0, err;
825 const struct cirrusfb_board_info_rec *bi;
826
Krzysztof Helt8503df62007-10-16 01:29:08 -0700827 DPRINTK("ENTER\n");
828 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700830 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700832 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
834 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700835 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 /* should never happen */
837 DPRINTK("mode change aborted. invalid var.\n");
838 return -EINVAL;
839 }
840
841 bi = &cirrusfb_board_info[cinfo->btype];
842
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700844 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
846 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700847 DPRINTK("CRT0: %ld\n", regs.HorizTotal);
848 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
Krzysztof Helt8503df62007-10-16 01:29:08 -0700850 DPRINTK("CRT1: %ld\n", regs.HorizDispEnd);
851 vga_wcrt(regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
Krzysztof Helt8503df62007-10-16 01:29:08 -0700853 DPRINTK("CRT2: %ld\n", regs.HorizBlankStart);
854 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Krzysztof Helt8503df62007-10-16 01:29:08 -0700856 /* + 128: Compatible read */
857 DPRINTK("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32);
858 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
859 128 + (regs.HorizBlankEnd % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Krzysztof Helt8503df62007-10-16 01:29:08 -0700861 DPRINTK("CRT4: %ld\n", regs.HorizSyncStart);
862 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
864 tmp = regs.HorizSyncEnd % 32;
865 if (regs.HorizBlankEnd & 32)
866 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700867 DPRINTK("CRT5: %d\n", tmp);
868 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
Krzysztof Helt8503df62007-10-16 01:29:08 -0700870 DPRINTK("CRT6: %ld\n", regs.VertTotal & 0xff);
871 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
873 tmp = 16; /* LineCompare bit #9 */
874 if (regs.VertTotal & 256)
875 tmp |= 1;
876 if (regs.VertDispEnd & 256)
877 tmp |= 2;
878 if (regs.VertSyncStart & 256)
879 tmp |= 4;
880 if (regs.VertBlankStart & 256)
881 tmp |= 8;
882 if (regs.VertTotal & 512)
883 tmp |= 32;
884 if (regs.VertDispEnd & 512)
885 tmp |= 64;
886 if (regs.VertSyncStart & 512)
887 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700888 DPRINTK("CRT7: %d\n", tmp);
889 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
891 tmp = 0x40; /* LineCompare bit #8 */
892 if (regs.VertBlankStart & 512)
893 tmp |= 0x20;
894 if (var->vmode & FB_VMODE_DOUBLE)
895 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700896 DPRINTK("CRT9: %d\n", tmp);
897 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Helt8503df62007-10-16 01:29:08 -0700899 DPRINTK("CRT10: %ld\n", regs.VertSyncStart & 0xff);
900 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, regs.VertSyncStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901
Krzysztof Helt8503df62007-10-16 01:29:08 -0700902 DPRINTK("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
903 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, regs.VertSyncEnd % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
Krzysztof Helt8503df62007-10-16 01:29:08 -0700905 DPRINTK("CRT12: %ld\n", regs.VertDispEnd & 0xff);
906 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, regs.VertDispEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Krzysztof Helt8503df62007-10-16 01:29:08 -0700908 DPRINTK("CRT15: %ld\n", regs.VertBlankStart & 0xff);
909 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, regs.VertBlankStart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Krzysztof Helt8503df62007-10-16 01:29:08 -0700911 DPRINTK("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
912 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, regs.VertBlankEnd & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Krzysztof Helt8503df62007-10-16 01:29:08 -0700914 DPRINTK("CRT18: 0xff\n");
915 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
917 tmp = 0;
918 if (var->vmode & FB_VMODE_INTERLACED)
919 tmp |= 1;
920 if (regs.HorizBlankEnd & 64)
921 tmp |= 16;
922 if (regs.HorizBlankEnd & 128)
923 tmp |= 32;
924 if (regs.VertBlankEnd & 256)
925 tmp |= 64;
926 if (regs.VertBlankEnd & 512)
927 tmp |= 128;
928
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929 DPRINTK("CRT1a: %d\n", tmp);
930 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 /* set VCLK0 */
933 /* hardware RefClock: 14.31818 MHz */
934 /* formula: VClk = (OSC * N) / (D * (1+P)) */
935 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
936
Krzysztof Helt8503df62007-10-16 01:29:08 -0700937 vga_wseq(regbase, CL_SEQRB, regs.nom);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 tmp = regs.den << 1;
939 if (regs.div != 0)
940 tmp |= 1;
941
Krzysztof Helt8503df62007-10-16 01:29:08 -0700942 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 if ((cinfo->btype == BT_SD64) ||
944 (cinfo->btype == BT_ALPINE) ||
945 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -0700946 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947
Krzysztof Helt8503df62007-10-16 01:29:08 -0700948 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
949 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
951 if (regs.VertRes >= 1024)
952 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700953 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 else
955 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
956 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700957 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Krzysztof Helt8503df62007-10-16 01:29:08 -0700959/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
960 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
962 /* don't know if it would hurt to also program this if no interlaced */
963 /* mode is used, but I feel better this way.. :-) */
964 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 vga_wcrt(regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700967 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Krzysztof Helt8503df62007-10-16 01:29:08 -0700969 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
971 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700972 /* enable display memory & CRTC I/O address for color mode */
973 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
975 tmp |= 0x40;
976 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
977 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700978 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
Krzysztof Helt8503df62007-10-16 01:29:08 -0700980 /* Screen A Preset Row-Scan register */
981 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
982 /* text cursor on and start line */
983 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
984 /* text cursor end line */
985 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
987 /******************************************************
988 *
989 * 1 bpp
990 *
991 */
992
993 /* programming for different color depths */
994 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700995 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
996 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
998 /* SR07 */
999 switch (cinfo->btype) {
1000 case BT_SD64:
1001 case BT_PICCOLO:
1002 case BT_PICASSO:
1003 case BT_SPECTRUM:
1004 case BT_PICASSO4:
1005 case BT_ALPINE:
1006 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001007 DPRINTK(" (for GD54xx)\n");
1008 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 regs.multiplexing ?
1010 bi->sr07_1bpp_mux : bi->sr07_1bpp);
1011 break;
1012
1013 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001014 DPRINTK(" (for GD546x)\n");
1015 vga_wseq(regbase, CL_SEQR7,
1016 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 break;
1018
1019 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001020 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 break;
1022 }
1023
1024 /* Extended Sequencer Mode */
1025 switch (cinfo->btype) {
1026 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001027 /* setting the SEQRF on SD64 is not necessary
1028 * (only during init)
1029 */
1030 DPRINTK("(for SD64)\n");
1031 /* MCLK select */
1032 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 break;
1034
1035 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001036 case BT_SPECTRUM:
1037 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001038 /* ### ueberall 0x22? */
1039 /* ##vorher 1c MCLK select */
1040 vga_wseq(regbase, CL_SEQR1F, 0x22);
1041 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1042 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 break;
1044
1045 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046 DPRINTK("(for Picasso)\n");
1047 /* ##vorher 22 MCLK select */
1048 vga_wseq(regbase, CL_SEQR1F, 0x22);
1049 /* ## vorher d0 avoid FIFO underruns..? */
1050 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 break;
1052
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 case BT_PICASSO4:
1054 case BT_ALPINE:
1055 case BT_GD5480:
1056 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001057 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 /* do nothing */
1059 break;
1060
1061 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001062 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 break;
1064 }
1065
Krzysztof Helt8503df62007-10-16 01:29:08 -07001066 /* pixel mask: pass-through for first plane */
1067 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001069 /* hidden dac reg: 1280x1024 */
1070 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001072 /* hidden dac: nothing */
1073 WHDR(cinfo, 0);
1074 /* memory mode: odd/even, ext. memory */
1075 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1076 /* plane mask: only write to first plane */
1077 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 offset = var->xres_virtual / 16;
1079 }
1080
1081 /******************************************************
1082 *
1083 * 8 bpp
1084 *
1085 */
1086
1087 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001088 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 switch (cinfo->btype) {
1090 case BT_SD64:
1091 case BT_PICCOLO:
1092 case BT_PICASSO:
1093 case BT_SPECTRUM:
1094 case BT_PICASSO4:
1095 case BT_ALPINE:
1096 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001097 DPRINTK(" (for GD54xx)\n");
1098 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 regs.multiplexing ?
1100 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1101 break;
1102
1103 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001104 DPRINTK(" (for GD546x)\n");
1105 vga_wseq(regbase, CL_SEQR7,
1106 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 break;
1108
1109 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001110 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 break;
1112 }
1113
1114 switch (cinfo->btype) {
1115 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001116 /* MCLK select */
1117 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 break;
1119
1120 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001123 /* ### vorher 1c MCLK select */
1124 vga_wseq(regbase, CL_SEQR1F, 0x22);
1125 /* Fast Page-Mode writes */
1126 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 break;
1128
1129 case BT_PICASSO4:
1130#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001131 /* ### INCOMPLETE!! */
1132 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001134/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 break;
1136
1137 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001138 DPRINTK(" (for GD543x)\n");
1139 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 /* We already set SRF and SR1F */
1141 break;
1142
1143 case BT_GD5480:
1144 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001145 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 /* do nothing */
1147 break;
1148
1149 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001150 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 break;
1152 }
1153
Krzysztof Helt8503df62007-10-16 01:29:08 -07001154 /* mode register: 256 color mode */
1155 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1156 /* pixel mask: pass-through all planes */
1157 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001159 /* hidden dac reg: 1280x1024 */
1160 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001162 /* hidden dac: nothing */
1163 WHDR(cinfo, 0);
1164 /* memory mode: chain4, ext. memory */
1165 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1166 /* plane mask: enable writing to all 4 planes */
1167 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 offset = var->xres_virtual / 8;
1169 }
1170
1171 /******************************************************
1172 *
1173 * 16 bpp
1174 *
1175 */
1176
1177 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001178 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 switch (cinfo->btype) {
1180 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001181 /* Extended Sequencer Mode: 256c col. mode */
1182 vga_wseq(regbase, CL_SEQR7, 0xf7);
1183 /* MCLK select */
1184 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 break;
1186
1187 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001188 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 vga_wseq(regbase, CL_SEQR7, 0x87);
1190 /* Fast Page-Mode writes */
1191 vga_wseq(regbase, CL_SEQRF, 0xb0);
1192 /* MCLK select */
1193 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 break;
1195
1196 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001197 vga_wseq(regbase, CL_SEQR7, 0x27);
1198 /* Fast Page-Mode writes */
1199 vga_wseq(regbase, CL_SEQRF, 0xb0);
1200 /* MCLK select */
1201 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 break;
1203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001205 vga_wseq(regbase, CL_SEQR7, 0x27);
1206/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 break;
1208
1209 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 DPRINTK(" (for GD543x)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 if (regs.HorizRes >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001212 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001214 vga_wseq(regbase, CL_SEQR7, 0xa3);
1215 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 break;
1217
1218 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001219 DPRINTK(" (for GD5480)\n");
1220 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 /* We already set SRF and SR1F */
1222 break;
1223
1224 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001225 DPRINTK(" (for GD546x)\n");
1226 vga_wseq(regbase, CL_SEQR7,
1227 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 break;
1229
1230 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001231 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 break;
1233 }
1234
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235 /* mode register: 256 color mode */
1236 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1237 /* pixel mask: pass-through all planes */
1238 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001240 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241#elif defined(CONFIG_ZORRO)
1242 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001243 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001245 /* memory mode: chain4, ext. memory */
1246 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1247 /* plane mask: enable writing to all 4 planes */
1248 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 offset = var->xres_virtual / 4;
1250 }
1251
1252 /******************************************************
1253 *
1254 * 32 bpp
1255 *
1256 */
1257
1258 else if (var->bits_per_pixel == 32) {
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001259 DPRINTK("cirrusfb: preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 switch (cinfo->btype) {
1261 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001262 /* Extended Sequencer Mode: 256c col. mode */
1263 vga_wseq(regbase, CL_SEQR7, 0xf9);
1264 /* MCLK select */
1265 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 break;
1267
1268 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001269 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001270 vga_wseq(regbase, CL_SEQR7, 0x85);
1271 /* Fast Page-Mode writes */
1272 vga_wseq(regbase, CL_SEQRF, 0xb0);
1273 /* MCLK select */
1274 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 break;
1276
1277 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001278 vga_wseq(regbase, CL_SEQR7, 0x25);
1279 /* Fast Page-Mode writes */
1280 vga_wseq(regbase, CL_SEQRF, 0xb0);
1281 /* MCLK select */
1282 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 break;
1284
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001286 vga_wseq(regbase, CL_SEQR7, 0x25);
1287/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 break;
1289
1290 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 DPRINTK(" (for GD543x)\n");
1292 vga_wseq(regbase, CL_SEQR7, 0xa9);
1293 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 break;
1295
1296 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001297 DPRINTK(" (for GD5480)\n");
1298 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 /* We already set SRF and SR1F */
1300 break;
1301
1302 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001303 DPRINTK(" (for GD546x)\n");
1304 vga_wseq(regbase, CL_SEQR7,
1305 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 break;
1307
1308 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001309 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 break;
1311 }
1312
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313 /* mode register: 256 color mode */
1314 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1315 /* pixel mask: pass-through all planes */
1316 WGen(cinfo, VGA_PEL_MSK, 0xff);
1317 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1318 WHDR(cinfo, 0xc5);
1319 /* memory mode: chain4, ext. memory */
1320 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1321 /* plane mask: enable writing to all 4 planes */
1322 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 offset = var->xres_virtual / 4;
1324 }
1325
1326 /******************************************************
1327 *
1328 * unknown/unsupported bpp
1329 *
1330 */
1331
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332 else
1333 printk(KERN_ERR "cirrusfb: What's this?? "
1334 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
Krzysztof Helt8503df62007-10-16 01:29:08 -07001337 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 tmp = 0x22;
1339 if (offset & 0x100)
1340 tmp |= 0x10; /* offset overflow bit */
1341
Krzysztof Helt8503df62007-10-16 01:29:08 -07001342 /* screen start addr #16-18, fastpagemode cycles */
1343 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
1345 if (cinfo->btype == BT_SD64 ||
1346 cinfo->btype == BT_PICASSO4 ||
1347 cinfo->btype == BT_ALPINE ||
1348 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001349 /* screen start address bit 19 */
1350 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351
Krzysztof Helt8503df62007-10-16 01:29:08 -07001352 /* text cursor location high */
1353 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1354 /* text cursor location low */
1355 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1356 /* underline row scanline = at very bottom */
1357 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358
Krzysztof Helt8503df62007-10-16 01:29:08 -07001359 /* controller mode */
1360 vga_wattr(regbase, VGA_ATC_MODE, 1);
1361 /* overscan (border) color */
1362 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1363 /* color plane enable */
1364 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1365 /* pixel panning */
1366 vga_wattr(regbase, CL_AR33, 0);
1367 /* color select */
1368 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
1370 /* [ EGS: SetOffset(); ] */
1371 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001372 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
Krzysztof Helt8503df62007-10-16 01:29:08 -07001374 /* set/reset register */
1375 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1376 /* set/reset enable */
1377 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1378 /* color compare */
1379 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1380 /* data rotate */
1381 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1382 /* read map select */
1383 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1384 /* miscellaneous register */
1385 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1386 /* color don't care */
1387 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1388 /* bit mask */
1389 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
Krzysztof Helt8503df62007-10-16 01:29:08 -07001391 /* graphics cursor attributes: nothing special */
1392 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
1394 /* finally, turn on everything - turn off "FullBandwidth" bit */
1395 /* also, set "DotClock%2" bit where requested */
1396 tmp = 0x01;
1397
1398/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1399 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1400 tmp |= 0x08;
1401*/
1402
Krzysztof Helt8503df62007-10-16 01:29:08 -07001403 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1404 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405
1406 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
1408 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001409 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410
1411#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001412 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413#endif
1414
Krzysztof Helt8503df62007-10-16 01:29:08 -07001415 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 return 0;
1417}
1418
1419/* for some reason incomprehensible to me, cirrusfb requires that you write
1420 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001421static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001423 cirrusfb_set_par_foo(info);
1424 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425}
1426
Krzysztof Helt8503df62007-10-16 01:29:08 -07001427static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1428 unsigned blue, unsigned transp,
1429 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430{
1431 struct cirrusfb_info *cinfo = info->par;
1432
1433 if (regno > 255)
1434 return -EINVAL;
1435
1436 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1437 u32 v;
1438 red >>= (16 - info->var.red.length);
1439 green >>= (16 - info->var.green.length);
1440 blue >>= (16 - info->var.blue.length);
1441
Krzysztof Helt8503df62007-10-16 01:29:08 -07001442 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 return 1;
1444 v = (red << info->var.red.offset) |
1445 (green << info->var.green.offset) |
1446 (blue << info->var.blue.offset);
1447
Krzysztof Helt060b6002007-10-16 01:29:13 -07001448 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 return 0;
1450 }
1451
Krzysztof Helt8503df62007-10-16 01:29:08 -07001452 if (info->var.bits_per_pixel == 8)
1453 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
1455 return 0;
1456
1457}
1458
1459/*************************************************************************
1460 cirrusfb_pan_display()
1461
1462 performs display panning - provided hardware permits this
1463**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001464static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1465 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466{
1467 int xoffset = 0;
1468 int yoffset = 0;
1469 unsigned long base;
1470 unsigned char tmp = 0, tmp2 = 0, xpix;
1471 struct cirrusfb_info *cinfo = info->par;
1472
Krzysztof Helt8503df62007-10-16 01:29:08 -07001473 DPRINTK("ENTER\n");
1474 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
1476 /* no range checks for xoffset and yoffset, */
1477 /* as fb_pan_display has already done this */
1478 if (var->vmode & FB_VMODE_YWRAP)
1479 return -EINVAL;
1480
1481 info->var.xoffset = var->xoffset;
1482 info->var.yoffset = var->yoffset;
1483
1484 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1485 yoffset = var->yoffset;
1486
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001487 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
1489 if (info->var.bits_per_pixel == 1) {
1490 /* base is already correct */
1491 xpix = (unsigned char) (var->xoffset % 8);
1492 } else {
1493 base /= 4;
1494 xpix = (unsigned char) ((xoffset % 4) * 2);
1495 }
1496
Krzysztof Helt8503df62007-10-16 01:29:08 -07001497 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498
1499 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001500 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1501 (unsigned char) (base & 0xff));
1502 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1503 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 /* construct bits 16, 17 and 18 of screen start address */
1506 if (base & 0x10000)
1507 tmp |= 0x01;
1508 if (base & 0x20000)
1509 tmp |= 0x04;
1510 if (base & 0x40000)
1511 tmp |= 0x08;
1512
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 /* 0xf2 is %11110010, exclude tmp bits */
1514 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1515 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
1517 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001518 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1519 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
Krzysztof Helt8503df62007-10-16 01:29:08 -07001521 /* write pixel panning value to AR33; this does not quite work in 8bpp
1522 *
1523 * ### Piccolo..? Will this work?
1524 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001526 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
Krzysztof Helt8503df62007-10-16 01:29:08 -07001528 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
Krzysztof Helt8503df62007-10-16 01:29:08 -07001530 DPRINTK("EXIT\n");
1531 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532}
1533
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535{
1536 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001537 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1538 * then the caller blanks by setting the CLUT (Color Look Up Table)
1539 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1540 * failed due to e.g. a video mode which doesn't support it.
1541 * Implements VESA suspend and powerdown modes on hardware that
1542 * supports disabling hsync/vsync:
1543 * blank_mode == 2: suspend vsync
1544 * blank_mode == 3: suspend hsync
1545 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 */
1547 unsigned char val;
1548 struct cirrusfb_info *cinfo = info->par;
1549 int current_mode = cinfo->blank_mode;
1550
Krzysztof Helt8503df62007-10-16 01:29:08 -07001551 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
1553 if (info->state != FBINFO_STATE_RUNNING ||
1554 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 return 0;
1557 }
1558
1559 /* Undo current */
1560 if (current_mode == FB_BLANK_NORMAL ||
1561 current_mode == FB_BLANK_UNBLANK) {
1562 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001563 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1564 /* clear "FullBandwidth" bit */
1565 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001567 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 }
1569
1570 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001573 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1574 /* set "FullBandwidth" bit */
1575 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 }
1577
1578 switch (blank_mode) {
1579 case FB_BLANK_UNBLANK:
1580 case FB_BLANK_NORMAL:
1581 break;
1582 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001583 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 break;
1585 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001586 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 break;
1588 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001589 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 break;
1591 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001592 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 return 1;
1594 }
1595
1596 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001597 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
1599 /* Let fbcon do a soft blank for us */
1600 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1601}
1602/**** END Hardware specific Routines **************************************/
1603/****************************************************************************/
1604/**** BEGIN Internal Routines ***********************************************/
1605
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001606static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001608 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 const struct cirrusfb_board_info_rec *bi;
1610
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612
Krzysztof Helt8503df62007-10-16 01:29:08 -07001613 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614
1615 bi = &cirrusfb_board_info[cinfo->btype];
1616
1617 /* reset board globally */
1618 switch (cinfo->btype) {
1619 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001620 WSFR(cinfo, 0x01);
1621 udelay(500);
1622 WSFR(cinfo, 0x51);
1623 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 break;
1625 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001626 WSFR2(cinfo, 0xff);
1627 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 break;
1629 case BT_SD64:
1630 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001631 WSFR(cinfo, 0x1f);
1632 udelay(500);
1633 WSFR(cinfo, 0x4f);
1634 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 break;
1636 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001637 /* disable flickerfixer */
1638 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1639 mdelay(100);
1640 /* from Klaus' NetBSD driver: */
1641 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1642 /* put blitter into 542x compat */
1643 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1644 /* mode */
1645 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 break;
1647
1648 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001649 /* from Klaus' NetBSD driver: */
1650 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 break;
1652
1653 case BT_ALPINE:
1654 /* Nothing to do to reset the board. */
1655 break;
1656
1657 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001658 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 break;
1660 }
1661
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001662 /* make sure RAM size set by this point */
1663 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664
1665 /* the P4 is not fully initialized here; I rely on it having been */
1666 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668
1669 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001670 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1671 WGen(cinfo, CL_POS102, 0x01);
1672 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
1674 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676
Krzysztof Helt8503df62007-10-16 01:29:08 -07001677 /* reset sequencer logic */
1678 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 /* FullBandwidth (video off) and 8/9 dot clock */
1681 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1682 /* polarity (-/-), disable access to display memory,
1683 * VGA_CRTC_START_HI base address: color
1684 */
1685 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 /* "magic cookie" - doesn't make any sense to me.. */
1688/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1689 /* unlock all extension registers */
1690 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691
Krzysztof Helt8503df62007-10-16 01:29:08 -07001692 /* reset blitter */
1693 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694
1695 switch (cinfo->btype) {
1696 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 break;
1699 case BT_ALPINE:
1700 break;
1701 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 break;
1704 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1706 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 break;
1708 }
1709 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001710 /* plane mask: nothing */
1711 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1712 /* character map select: doesn't even matter in gx mode */
1713 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1714 /* memory mode: chain-4, no odd/even, ext. memory */
1715 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
1717 /* controller-internal base address of video memory */
1718 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001719 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1722 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1725 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1726 /* graphics cursor Y position (..."... ) */
1727 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1728 /* graphics cursor attributes */
1729 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1730 /* graphics cursor pattern address */
1731 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732
1733 /* writing these on a P4 might give problems.. */
1734 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001735 /* configuration readback and ext. color */
1736 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1737 /* signature generator */
1738 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 }
1740
1741 /* MCLK select etc. */
1742 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001743 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744
Krzysztof Helt8503df62007-10-16 01:29:08 -07001745 /* Screen A preset row scan: none */
1746 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1747 /* Text cursor start: disable text cursor */
1748 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1749 /* Text cursor end: - */
1750 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1751 /* Screen start address high: 0 */
1752 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1753 /* Screen start address low: 0 */
1754 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1755 /* text cursor location high: 0 */
1756 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1757 /* text cursor location low: 0 */
1758 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759
Krzysztof Helt8503df62007-10-16 01:29:08 -07001760 /* Underline Row scanline: - */
1761 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1762 /* mode control: timing enable, byte mode, no compat modes */
1763 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1764 /* Line Compare: not needed */
1765 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001767 /* ext. display controls: ext.adr. wrap */
1768 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769
Krzysztof Helt8503df62007-10-16 01:29:08 -07001770 /* Set/Reset registes: - */
1771 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1772 /* Set/Reset enable: - */
1773 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1774 /* Color Compare: - */
1775 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1776 /* Data Rotate: - */
1777 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1778 /* Read Map Select: - */
1779 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1780 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1781 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1782 /* Miscellaneous: memory map base address, graphics mode */
1783 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1784 /* Color Don't care: involve all planes */
1785 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1786 /* Bit Mask: no mask at all */
1787 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 /* (5434 can't have bit 3 set for bitblt) */
1790 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001792 /* Graphics controller mode extensions: finer granularity,
1793 * 8byte data latches
1794 */
1795 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796
Krzysztof Helt8503df62007-10-16 01:29:08 -07001797 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1798 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1799 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1800 /* Background color byte 1: - */
1801 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1802 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
Krzysztof Helt8503df62007-10-16 01:29:08 -07001804 /* Attribute Controller palette registers: "identity mapping" */
1805 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1806 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1807 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1808 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1809 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1810 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1811 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1812 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1813 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1814 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1815 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1816 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1817 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1818 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1819 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1820 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
Krzysztof Helt8503df62007-10-16 01:29:08 -07001822 /* Attribute Controller mode: graphics mode */
1823 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1824 /* Overscan color reg.: reg. 0 */
1825 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1826 /* Color Plane enable: Enable all 4 planes */
1827 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1828/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1829 /* Color Select: - */
1830 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831
Krzysztof Helt8503df62007-10-16 01:29:08 -07001832 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833
1834 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001835 /* polarity (-/-), enable display mem,
1836 * VGA_CRTC_START_HI i/o base = color
1837 */
1838 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839
Krzysztof Helt8503df62007-10-16 01:29:08 -07001840 /* BLT Start/status: Blitter reset */
1841 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1842 /* - " - : "end-of-reset" */
1843 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844
1845 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001846 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 return;
1850}
1851
Krzysztof Helt8503df62007-10-16 01:29:08 -07001852static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853{
1854#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1855 static int IsOn = 0; /* XXX not ok for multiple boards */
1856
Krzysztof Helt8503df62007-10-16 01:29:08 -07001857 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858
1859 if (cinfo->btype == BT_PICASSO4)
1860 return; /* nothing to switch */
1861 if (cinfo->btype == BT_ALPINE)
1862 return; /* nothing to switch */
1863 if (cinfo->btype == BT_GD5480)
1864 return; /* nothing to switch */
1865 if (cinfo->btype == BT_PICASSO) {
1866 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001867 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868
Krzysztof Helt8503df62007-10-16 01:29:08 -07001869 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870 return;
1871 }
1872 if (on) {
1873 switch (cinfo->btype) {
1874 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001875 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 break;
1877 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001878 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 break;
1880 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001881 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 break;
1883 default: /* do nothing */ break;
1884 }
1885 } else {
1886 switch (cinfo->btype) {
1887 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001888 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 break;
1890 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001891 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 break;
1893 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001894 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 break;
1896 default: /* do nothing */ break;
1897 }
1898 }
1899
Krzysztof Helt8503df62007-10-16 01:29:08 -07001900 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901#endif /* CONFIG_ZORRO */
1902}
1903
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904/******************************************/
1905/* Linux 2.6-style accelerated functions */
1906/******************************************/
1907
Krzysztof Helt8503df62007-10-16 01:29:08 -07001908static void cirrusfb_fillrect(struct fb_info *info,
1909 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 struct fb_fillrect modded;
1912 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001913 struct cirrusfb_info *cinfo = info->par;
1914 int m = info->var.bits_per_pixel;
1915 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1916 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917
1918 if (info->state != FBINFO_STATE_RUNNING)
1919 return;
1920 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1921 cfb_fillrect(info, region);
1922 return;
1923 }
1924
1925 vxres = info->var.xres_virtual;
1926 vyres = info->var.yres_virtual;
1927
1928 memcpy(&modded, region, sizeof(struct fb_fillrect));
1929
Krzysztof Helt8503df62007-10-16 01:29:08 -07001930 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 modded.dx >= vxres || modded.dy >= vyres)
1932 return;
1933
Krzysztof Helt8503df62007-10-16 01:29:08 -07001934 if (modded.dx + modded.width > vxres)
1935 modded.width = vxres - modded.dx;
1936 if (modded.dy + modded.height > vyres)
1937 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938
Krzysztof Helt060b6002007-10-16 01:29:13 -07001939 cirrusfb_RectFill(cinfo->regbase,
1940 info->var.bits_per_pixel,
1941 (region->dx * m) / 8, region->dy,
1942 (region->width * m) / 8, region->height,
1943 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001944 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945}
1946
Krzysztof Helt8503df62007-10-16 01:29:08 -07001947static void cirrusfb_copyarea(struct fb_info *info,
1948 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 struct fb_copyarea modded;
1951 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001952 struct cirrusfb_info *cinfo = info->par;
1953 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954
1955 if (info->state != FBINFO_STATE_RUNNING)
1956 return;
1957 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1958 cfb_copyarea(info, area);
1959 return;
1960 }
1961
1962 vxres = info->var.xres_virtual;
1963 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001964 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965
Krzysztof Helt8503df62007-10-16 01:29:08 -07001966 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 modded.sx >= vxres || modded.sy >= vyres ||
1968 modded.dx >= vxres || modded.dy >= vyres)
1969 return;
1970
Krzysztof Helt8503df62007-10-16 01:29:08 -07001971 if (modded.sx + modded.width > vxres)
1972 modded.width = vxres - modded.sx;
1973 if (modded.dx + modded.width > vxres)
1974 modded.width = vxres - modded.dx;
1975 if (modded.sy + modded.height > vyres)
1976 modded.height = vyres - modded.sy;
1977 if (modded.dy + modded.height > vyres)
1978 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979
Krzysztof Helt060b6002007-10-16 01:29:13 -07001980 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1981 (area->sx * m) / 8, area->sy,
1982 (area->dx * m) / 8, area->dy,
1983 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001984 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001985
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986}
1987
Krzysztof Helt8503df62007-10-16 01:29:08 -07001988static void cirrusfb_imageblit(struct fb_info *info,
1989 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990{
1991 struct cirrusfb_info *cinfo = info->par;
1992
Krzysztof Helt8503df62007-10-16 01:29:08 -07001993 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 cfb_imageblit(info, image);
1995}
1996
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997#ifdef CONFIG_PPC_PREP
1998#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1999#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07002000static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002002 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003
2004 *display = PREP_VIDEO_BASE;
2005 *registers = (unsigned long) PREP_IO_BASE;
2006
Krzysztof Helt8503df62007-10-16 01:29:08 -07002007 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008}
2009
2010#endif /* CONFIG_PPC_PREP */
2011
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07002013static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014
2015/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
2016 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
2017 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
2018 * seem to have. */
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002019static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020{
2021 unsigned long mem;
2022 unsigned char SRF;
2023
Krzysztof Helt8503df62007-10-16 01:29:08 -07002024 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025
Krzysztof Helt8503df62007-10-16 01:29:08 -07002026 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002028 case 0x08:
2029 mem = 512 * 1024;
2030 break;
2031 case 0x10:
2032 mem = 1024 * 1024;
2033 break;
2034 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2035 * on the 5430.
2036 */
2037 case 0x18:
2038 mem = 2048 * 1024;
2039 break;
2040 default:
2041 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 mem = 1024 * 1024;
2043 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002044 if (SRF & 0x80)
2045 /* If DRAM bank switching is enabled, there must be twice as much
2046 * memory installed. (4MB on the 5434)
2047 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002049
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2051
Krzysztof Helt8503df62007-10-16 01:29:08 -07002052 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 return mem;
2054}
2055
Krzysztof Helt8503df62007-10-16 01:29:08 -07002056static void get_pci_addrs(const struct pci_dev *pdev,
2057 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002059 assert(pdev != NULL);
2060 assert(display != NULL);
2061 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062
Krzysztof Helt8503df62007-10-16 01:29:08 -07002063 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064
2065 *display = 0;
2066 *registers = 0;
2067
2068 /* This is a best-guess for now */
2069
2070 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2071 *display = pci_resource_start(pdev, 1);
2072 *registers = pci_resource_start(pdev, 0);
2073 } else {
2074 *display = pci_resource_start(pdev, 0);
2075 *registers = pci_resource_start(pdev, 1);
2076 }
2077
Krzysztof Helt8503df62007-10-16 01:29:08 -07002078 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079
Krzysztof Helt8503df62007-10-16 01:29:08 -07002080 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081}
2082
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002083static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002085 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002087 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088#if 0 /* if system didn't claim this region, we would... */
2089 release_mem_region(0xA0000, 65535);
2090#endif
2091 if (release_io_ports)
2092 release_region(0x3C0, 32);
2093 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094}
2095#endif /* CONFIG_PCI */
2096
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002098static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099{
Al Virod91f5bb2007-10-17 00:27:18 +01002100 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002101 struct zorro_dev *zdev = to_zorro_dev(info->device);
2102
2103 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104
2105 if (cinfo->btype == BT_PICASSO4) {
2106 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002107 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002108 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002110 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002111 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113}
2114#endif /* CONFIG_ZORRO */
2115
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002116static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002118 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 struct fb_var_screeninfo *var = &info->var;
2120
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 info->pseudo_palette = cinfo->pseudo_palette;
2122 info->flags = FBINFO_DEFAULT
2123 | FBINFO_HWACCEL_XPAN
2124 | FBINFO_HWACCEL_YPAN
2125 | FBINFO_HWACCEL_FILLRECT
2126 | FBINFO_HWACCEL_COPYAREA;
2127 if (noaccel)
2128 info->flags |= FBINFO_HWACCEL_DISABLED;
2129 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 if (cinfo->btype == BT_GD5480) {
2131 if (var->bits_per_pixel == 16)
2132 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002133 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 info->screen_base += 2 * MB_;
2135 }
2136
2137 /* Fill fix common fields */
2138 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2139 sizeof(info->fix.id));
2140
2141 /* monochrome: only 1 memory plane */
2142 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002143 info->fix.smem_len = info->screen_size;
2144 if (var->bits_per_pixel == 1)
2145 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 info->fix.xpanstep = 1;
2148 info->fix.ypanstep = 1;
2149 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150
2151 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 info->fix.mmio_len = 0;
2153 info->fix.accel = FB_ACCEL_NONE;
2154
2155 fb_alloc_cmap(&info->cmap, 256, 0);
2156
2157 return 0;
2158}
2159
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002160static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002162 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002164 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165
Krzysztof Helt8503df62007-10-16 01:29:08 -07002166 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167
Krzysztof Helt8503df62007-10-16 01:29:08 -07002168 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2169 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 btype = cinfo->btype;
2172
2173 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002174 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002176 /* set all the vital stuff */
2177 cirrusfb_set_fbinfo(info);
2178
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002179 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002181 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2182 if (!err) {
2183 DPRINTK("wrong initial video mode\n");
2184 err = -EINVAL;
2185 goto err_dealloc_cmap;
2186 }
2187
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 info->var.activate = FB_ACTIVATE_NOW;
2189
2190 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2191 if (err < 0) {
2192 /* should never happen */
2193 DPRINTK("choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002194 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 }
2196
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197 err = register_framebuffer(info);
2198 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002199 printk(KERN_ERR "cirrusfb: could not register "
2200 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 goto err_dealloc_cmap;
2202 }
2203
Krzysztof Helt8503df62007-10-16 01:29:08 -07002204 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 return 0;
2206
2207err_dealloc_cmap:
2208 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002209 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002210 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 return err;
2212}
2213
Krzysztof Helt8503df62007-10-16 01:29:08 -07002214static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215{
2216 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002217 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218
Krzysztof Helt8503df62007-10-16 01:29:08 -07002219 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220
Krzysztof Helt8503df62007-10-16 01:29:08 -07002221 unregister_framebuffer(info);
2222 fb_dealloc_cmap(&info->cmap);
2223 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002224 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002225 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226
Krzysztof Helt8503df62007-10-16 01:29:08 -07002227 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228}
2229
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002231static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2232 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233{
2234 struct cirrusfb_info *cinfo;
2235 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002236 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 unsigned long board_addr, board_size;
2238 int ret;
2239
2240 ret = pci_enable_device(pdev);
2241 if (ret < 0) {
2242 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2243 goto err_out;
2244 }
2245
2246 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2247 if (!info) {
2248 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2249 ret = -ENOMEM;
2250 goto err_disable;
2251 }
2252
2253 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002254 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255
Krzysztof Helt7345de32007-10-16 01:29:11 -07002256 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002258 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259
Krzysztof Helt8503df62007-10-16 01:29:08 -07002260 if (isPReP) {
2261 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002263 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002265 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002266 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002268 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002269 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002270 /* FIXME: this forces VGA. alternatives? */
2271 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 }
2273
Krzysztof Helt8503df62007-10-16 01:29:08 -07002274 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002275 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276
2277 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002278 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
2280 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002281 if (ret < 0) {
2282 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2283 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 board_addr);
2285 goto err_release_fb;
2286 }
2287#if 0 /* if the system didn't claim this region, we would... */
2288 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2289 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2290,
2291 0xA0000L);
2292 ret = -EBUSY;
2293 goto err_release_regions;
2294 }
2295#endif
2296 if (request_region(0x3C0, 32, "cirrusfb"))
2297 release_io_ports = 1;
2298
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002299 info->screen_base = ioremap(board_addr, board_size);
2300 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 ret = -EIO;
2302 goto err_release_legacy;
2303 }
2304
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002305 info->fix.smem_start = board_addr;
2306 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 cinfo->unmap = cirrusfb_pci_unmap;
2308
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002309 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2310 "Logic chipset on PCI bus\n",
2311 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 pci_set_drvdata(pdev, info);
2313
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002314 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002315 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002316 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002317 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
2319err_release_legacy:
2320 if (release_io_ports)
2321 release_region(0x3C0, 32);
2322#if 0
2323 release_mem_region(0xA0000, 65535);
2324err_release_regions:
2325#endif
2326 pci_release_regions(pdev);
2327err_release_fb:
2328 framebuffer_release(info);
2329err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330err_out:
2331 return ret;
2332}
2333
Krzysztof Helt8503df62007-10-16 01:29:08 -07002334static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335{
2336 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002337 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338
Krzysztof Helt8503df62007-10-16 01:29:08 -07002339 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340
Krzysztof Helt8503df62007-10-16 01:29:08 -07002341 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342}
2343
2344static struct pci_driver cirrusfb_pci_driver = {
2345 .name = "cirrusfb",
2346 .id_table = cirrusfb_pci_table,
2347 .probe = cirrusfb_pci_register,
2348 .remove = __devexit_p(cirrusfb_pci_unregister),
2349#ifdef CONFIG_PM
2350#if 0
2351 .suspend = cirrusfb_pci_suspend,
2352 .resume = cirrusfb_pci_resume,
2353#endif
2354#endif
2355};
2356#endif /* CONFIG_PCI */
2357
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002359static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2360 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361{
2362 struct cirrusfb_info *cinfo;
2363 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002364 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 struct zorro_dev *z2 = NULL;
2366 unsigned long board_addr, board_size, size;
2367 int ret;
2368
2369 btype = ent->driver_data;
2370 if (cirrusfb_zorro_table2[btype].id2)
2371 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2372 size = cirrusfb_zorro_table2[btype].size;
2373 printk(KERN_INFO "cirrusfb: %s board detected; ",
2374 cirrusfb_board_info[btype].name);
2375
2376 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2377 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002378 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 ret = -ENOMEM;
2380 goto err_out;
2381 }
2382
2383 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384 cinfo->btype = btype;
2385
Al Viro36ea96a2007-10-27 19:46:58 +01002386 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002387 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 board_addr = zorro_resource_start(z);
2390 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002391 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392
2393 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002394 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2395 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 board_addr);
2397 ret = -EBUSY;
2398 goto err_release_fb;
2399 }
2400
Krzysztof Helt8503df62007-10-16 01:29:08 -07002401 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402
2403 ret = -EIO;
2404
2405 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002406 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407
2408 /* To be precise, for the P4 this is not the */
2409 /* begin of the board, but the begin of RAM. */
2410 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2411 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002412 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 if (!cinfo->regbase)
2414 goto err_release_region;
2415
Krzysztof Helt8503df62007-10-16 01:29:08 -07002416 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2417 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002419 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002421 info->fix.smem_start = board_addr + 16777216;
2422 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2423 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424 goto err_unmap_regbase;
2425 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002426 printk(KERN_INFO " REG at $%lx\n",
2427 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002429 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002431 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002433 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2434 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 goto err_release_region;
2436
2437 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002438 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002439 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440
Krzysztof Helt8503df62007-10-16 01:29:08 -07002441 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2442 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 }
2444 cinfo->unmap = cirrusfb_zorro_unmap;
2445
Krzysztof Helt8503df62007-10-16 01:29:08 -07002446 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 zorro_set_drvdata(z, info);
2448
Al Virod91f5bb2007-10-17 00:27:18 +01002449 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002450 if (ret) {
2451 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002452 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002453 iounmap(cinfo->regbase - 0x600000);
2454 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002455 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002456 }
2457 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458
2459err_unmap_regbase:
2460 /* Parental advisory: explicit hack */
2461 iounmap(cinfo->regbase - 0x600000);
2462err_release_region:
2463 release_region(board_addr, board_size);
2464err_release_fb:
2465 framebuffer_release(info);
2466err_out:
2467 return ret;
2468}
2469
2470void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2471{
2472 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474
Krzysztof Helt8503df62007-10-16 01:29:08 -07002475 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476
Krzysztof Helt8503df62007-10-16 01:29:08 -07002477 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478}
2479
2480static struct zorro_driver cirrusfb_zorro_driver = {
2481 .name = "cirrusfb",
2482 .id_table = cirrusfb_zorro_table,
2483 .probe = cirrusfb_zorro_register,
2484 .remove = __devexit_p(cirrusfb_zorro_unregister),
2485};
2486#endif /* CONFIG_ZORRO */
2487
2488static int __init cirrusfb_init(void)
2489{
2490 int error = 0;
2491
2492#ifndef MODULE
2493 char *option = NULL;
2494
2495 if (fb_get_options("cirrusfb", &option))
2496 return -ENODEV;
2497 cirrusfb_setup(option);
2498#endif
2499
2500#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002501 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502#endif
2503#ifdef CONFIG_PCI
2504 error |= pci_register_driver(&cirrusfb_pci_driver);
2505#endif
2506 return error;
2507}
2508
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509#ifndef MODULE
2510static int __init cirrusfb_setup(char *options) {
2511 char *this_opt, s[32];
2512 int i;
2513
Krzysztof Helt8503df62007-10-16 01:29:08 -07002514 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515
2516 if (!options || !*options)
2517 return 0;
2518
Krzysztof Helt8503df62007-10-16 01:29:08 -07002519 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002520 if (!*this_opt)
2521 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522
2523 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2524
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 if (!strcmp(this_opt, "noaccel"))
2526 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002527 else if (!strncmp(this_opt, "mode:", 5))
2528 mode_option = this_opt + 5;
2529 else
2530 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 }
2532 return 0;
2533}
2534#endif
2535
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 /*
2537 * Modularization
2538 */
2539
2540MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2541MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2542MODULE_LICENSE("GPL");
2543
Krzysztof Helt8503df62007-10-16 01:29:08 -07002544static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545{
2546#ifdef CONFIG_PCI
2547 pci_unregister_driver(&cirrusfb_pci_driver);
2548#endif
2549#ifdef CONFIG_ZORRO
2550 zorro_unregister_driver(&cirrusfb_zorro_driver);
2551#endif
2552}
2553
2554module_init(cirrusfb_init);
2555
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002556module_param(mode_option, charp, 0);
2557MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
2558
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559#ifdef MODULE
2560module_exit(cirrusfb_exit);
2561#endif
2562
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563/**********************************************************************/
2564/* about the following functions - I have used the same names for the */
2565/* functions as Markus Wild did in his Retina driver for NetBSD as */
2566/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002567/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568/**********************************************************************/
2569
2570/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002571static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 int regnum, unsigned char val)
2573{
2574 unsigned long regofs = 0;
2575
2576 if (cinfo->btype == BT_PICASSO) {
2577 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2579 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2581 regofs = 0xfff;
2582 }
2583
Krzysztof Helt8503df62007-10-16 01:29:08 -07002584 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585}
2586
2587/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002588static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589{
2590 unsigned long regofs = 0;
2591
2592 if (cinfo->btype == BT_PICASSO) {
2593 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002594/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2595 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2597 regofs = 0xfff;
2598 }
2599
Krzysztof Helt8503df62007-10-16 01:29:08 -07002600 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601}
2602
2603/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002604static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002606 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607
Krzysztof Helt8503df62007-10-16 01:29:08 -07002608 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609
Krzysztof Helt8503df62007-10-16 01:29:08 -07002610 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 /* if we're just in "write value" mode, write back the */
2612 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002613 vga_w(cinfo->regbase, VGA_ATT_IW,
2614 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 }
2616 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2618 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619
2620 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002621 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622
Krzysztof Helt8503df62007-10-16 01:29:08 -07002623 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624}
2625
2626/*** WHDR() - write into the Hidden DAC register ***/
2627/* as the HDR is the only extension register that requires special treatment
2628 * (the other extension registers are accessible just like the "ordinary"
2629 * registers of their functional group) here is a specialized routine for
2630 * accessing the HDR
2631 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002632static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633{
2634 unsigned char dummy;
2635
2636 if (cinfo->btype == BT_PICASSO) {
2637 /* Klaus' hint for correct access to HDR on some boards */
2638 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002639 WGen(cinfo, VGA_PEL_MSK, 0x00);
2640 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002642 dummy = RGen(cinfo, VGA_PEL_IW);
2643 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 }
2645 /* now do the usual stuff to access the HDR */
2646
Krzysztof Helt8503df62007-10-16 01:29:08 -07002647 dummy = RGen(cinfo, VGA_PEL_MSK);
2648 udelay(200);
2649 dummy = RGen(cinfo, VGA_PEL_MSK);
2650 udelay(200);
2651 dummy = RGen(cinfo, VGA_PEL_MSK);
2652 udelay(200);
2653 dummy = RGen(cinfo, VGA_PEL_MSK);
2654 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656 WGen(cinfo, VGA_PEL_MSK, val);
2657 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658
2659 if (cinfo->btype == BT_PICASSO) {
2660 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002661 dummy = RGen(cinfo, VGA_PEL_IW);
2662 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663
2664 /* and at the end, restore the mask value */
2665 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002666 WGen(cinfo, VGA_PEL_MSK, 0xff);
2667 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668 }
2669}
2670
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002672static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673{
2674#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002675 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002677 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678#endif
2679}
2680
2681/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002682static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683{
2684#ifdef CONFIG_ZORRO
2685 /* writing an arbitrary value to this one causes the monitor switcher */
2686 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002687 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002689 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690#endif
2691}
2692
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002694static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 unsigned char green, unsigned char blue)
2696{
2697 unsigned int data = VGA_PEL_D;
2698
2699 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002700 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701
2702 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2703 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2704 /* but DAC data register IS, at least for Picasso II */
2705 if (cinfo->btype == BT_PICASSO)
2706 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002707 vga_w(cinfo->regbase, data, red);
2708 vga_w(cinfo->regbase, data, green);
2709 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002711 vga_w(cinfo->regbase, data, blue);
2712 vga_w(cinfo->regbase, data, green);
2713 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 }
2715}
2716
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717#if 0
2718/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002719static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720 unsigned char *green, unsigned char *blue)
2721{
2722 unsigned int data = VGA_PEL_D;
2723
Krzysztof Helt8503df62007-10-16 01:29:08 -07002724 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725
2726 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2727 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2728 if (cinfo->btype == BT_PICASSO)
2729 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002730 *red = vga_r(cinfo->regbase, data);
2731 *green = vga_r(cinfo->regbase, data);
2732 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002734 *blue = vga_r(cinfo->regbase, data);
2735 *green = vga_r(cinfo->regbase, data);
2736 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 }
2738}
2739#endif
2740
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741/*******************************************************************
2742 cirrusfb_WaitBLT()
2743
2744 Wait for the BitBLT engine to complete a possible earlier job
2745*********************************************************************/
2746
2747/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002748static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749{
2750 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002751 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752 /* do nothing */ ;
2753}
2754
2755/*******************************************************************
2756 cirrusfb_BitBLT()
2757
2758 perform accelerated "scrolling"
2759********************************************************************/
2760
Krzysztof Helt8503df62007-10-16 01:29:08 -07002761static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2762 u_short curx, u_short cury,
2763 u_short destx, u_short desty,
2764 u_short width, u_short height,
2765 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766{
2767 u_short nwidth, nheight;
2768 u_long nsrc, ndest;
2769 u_char bltmode;
2770
Krzysztof Helt8503df62007-10-16 01:29:08 -07002771 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772
2773 nwidth = width - 1;
2774 nheight = height - 1;
2775
2776 bltmode = 0x00;
2777 /* if source adr < dest addr, do the Blt backwards */
2778 if (cury <= desty) {
2779 if (cury == desty) {
2780 /* if src and dest are on the same line, check x */
2781 if (curx < destx)
2782 bltmode |= 0x01;
2783 } else
2784 bltmode |= 0x01;
2785 }
2786 if (!bltmode) {
2787 /* standard case: forward blitting */
2788 nsrc = (cury * line_length) + curx;
2789 ndest = (desty * line_length) + destx;
2790 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002791 /* this means start addresses are at the end,
2792 * counting backwards
2793 */
2794 nsrc = cury * line_length + curx +
2795 nheight * line_length + nwidth;
2796 ndest = desty * line_length + destx +
2797 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 }
2799
2800 /*
2801 run-down of registers to be programmed:
2802 destination pitch
2803 source pitch
2804 BLT width/height
2805 source start
2806 destination start
2807 BLT mode
2808 BLT ROP
2809 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2810 start/stop
2811 */
2812
Krzysztof Helt8503df62007-10-16 01:29:08 -07002813 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814
2815 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002816 /* dest pitch low */
2817 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2818 /* dest pitch hi */
2819 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2820 /* source pitch low */
2821 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2822 /* source pitch hi */
2823 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824
2825 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002826 /* BLT width low */
2827 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2828 /* BLT width hi */
2829 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830
2831 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002832 /* BLT height low */
2833 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2834 /* BLT width hi */
2835 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836
2837 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002838 /* BLT dest low */
2839 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2840 /* BLT dest mid */
2841 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2842 /* BLT dest hi */
2843 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844
2845 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002846 /* BLT src low */
2847 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2848 /* BLT src mid */
2849 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2850 /* BLT src hi */
2851 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852
2853 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002854 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855
2856 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002857 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858
2859 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002860 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861
Krzysztof Helt8503df62007-10-16 01:29:08 -07002862 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863}
2864
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865/*******************************************************************
2866 cirrusfb_RectFill()
2867
2868 perform accelerated rectangle fill
2869********************************************************************/
2870
Krzysztof Helt8503df62007-10-16 01:29:08 -07002871static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 u_short x, u_short y, u_short width, u_short height,
2873 u_char color, u_short line_length)
2874{
2875 u_short nwidth, nheight;
2876 u_long ndest;
2877 u_char op;
2878
Krzysztof Helt8503df62007-10-16 01:29:08 -07002879 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880
2881 nwidth = width - 1;
2882 nheight = height - 1;
2883
2884 ndest = (y * line_length) + x;
2885
Krzysztof Helt8503df62007-10-16 01:29:08 -07002886 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887
2888 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002889 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2890 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2891 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2892 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893
2894 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002895 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2896 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897
2898 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002899 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2900 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901
2902 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002903 /* BLT dest low */
2904 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2905 /* BLT dest mid */
2906 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2907 /* BLT dest hi */
2908 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002909
2910 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002911 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2912 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2913 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914
2915 /* This is a ColorExpand Blt, using the */
2916 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002917 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2918 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919
2920 op = 0xc0;
2921 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002922 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2923 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924 op = 0x50;
2925 op = 0xd0;
2926 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002927 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2928 vga_wgfx(regbase, CL_GR11, color); /* background color */
2929 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2930 vga_wgfx(regbase, CL_GR13, color); /* background color */
2931 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2932 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933 op = 0x50;
2934 op = 0xf0;
2935 }
2936 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002937 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938
2939 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002940 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002941
2942 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002943 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944
Krzysztof Helt8503df62007-10-16 01:29:08 -07002945 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946}
2947
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948/**************************************************************************
2949 * bestclock() - determine closest possible clock lower(?) than the
2950 * desired pixel clock
2951 **************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002952static void bestclock(long freq, long *best, long *nom,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953 long *den, long *div, long maxfreq)
2954{
2955 long n, h, d, f;
2956
Krzysztof Helt8503df62007-10-16 01:29:08 -07002957 assert(best != NULL);
2958 assert(nom != NULL);
2959 assert(den != NULL);
2960 assert(div != NULL);
2961 assert(maxfreq > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962
2963 *nom = 0;
2964 *den = 0;
2965 *div = 0;
2966
Krzysztof Helt8503df62007-10-16 01:29:08 -07002967 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968
2969 if (freq < 8000)
2970 freq = 8000;
2971
2972 if (freq > maxfreq)
2973 freq = maxfreq;
2974
2975 *best = 0;
2976 f = freq * 10;
2977
2978 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002979 int s = 0;
2980
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981 d = (143181 * n) / f;
2982 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002983 int temp = d;
2984
2985 if (temp > 31) {
2986 s = 1;
2987 temp >>= 1;
2988 }
2989 h = ((14318 * n) / temp) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002990 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002991 *best = h;
2992 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002993 *den = temp;
2994 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002995 }
2996 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002997 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002998 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002999 if (d > 31) {
3000 s = 1;
3001 d >>= 1;
3002 }
3003 h = ((14318 * n) / d) >> s;
Krzysztof Helt8503df62007-10-16 01:29:08 -07003004 if (abs(h - freq) < abs(*best - freq)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005 *best = h;
3006 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07003007 *den = d;
3008 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003009 }
3010 }
3011 }
3012
Krzysztof Helt8503df62007-10-16 01:29:08 -07003013 DPRINTK("Best possible values for given frequency:\n");
3014 DPRINTK(" best: %ld kHz nom: %ld den: %ld div: %ld\n",
3015 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003016
Krzysztof Helt8503df62007-10-16 01:29:08 -07003017 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003018}
3019
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020/* -------------------------------------------------------------------------
3021 *
3022 * debugging functions
3023 *
3024 * -------------------------------------------------------------------------
3025 */
3026
3027#ifdef CIRRUSFB_DEBUG
3028
3029/**
3030 * cirrusfb_dbg_print_byte
3031 * @name: name associated with byte value to be displayed
3032 * @val: byte value to be displayed
3033 *
3034 * DESCRIPTION:
3035 * Display an indented string, along with a hexidecimal byte value, and
3036 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3037 * order.
3038 */
3039
3040static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003041void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003042{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003043 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3044 name, val,
3045 val & 0x80 ? '1' : '0',
3046 val & 0x40 ? '1' : '0',
3047 val & 0x20 ? '1' : '0',
3048 val & 0x10 ? '1' : '0',
3049 val & 0x08 ? '1' : '0',
3050 val & 0x04 ? '1' : '0',
3051 val & 0x02 ? '1' : '0',
3052 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003053}
3054
Linus Torvalds1da177e2005-04-16 15:20:36 -07003055/**
3056 * cirrusfb_dbg_print_regs
3057 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3058 * @reg_class: type of registers to read: %CRT, or %SEQ
3059 *
3060 * DESCRIPTION:
3061 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3062 * old-style I/O ports are queried for information, otherwise MMIO is
3063 * used at the given @base address to query the information.
3064 */
3065
3066static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003067void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003068 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003069{
3070 va_list list;
3071 unsigned char val = 0;
3072 unsigned reg;
3073 char *name;
3074
Krzysztof Helt8503df62007-10-16 01:29:08 -07003075 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003076
Krzysztof Helt8503df62007-10-16 01:29:08 -07003077 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003079 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080
3081 switch (reg_class) {
3082 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003083 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 break;
3085 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003086 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087 break;
3088 default:
3089 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003090 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 break;
3092 }
3093
Krzysztof Helt8503df62007-10-16 01:29:08 -07003094 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003095
Krzysztof Helt8503df62007-10-16 01:29:08 -07003096 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097 }
3098
Krzysztof Helt8503df62007-10-16 01:29:08 -07003099 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003100}
3101
Linus Torvalds1da177e2005-04-16 15:20:36 -07003102/**
3103 * cirrusfb_dump
3104 * @cirrusfbinfo:
3105 *
3106 * DESCRIPTION:
3107 */
3108
Krzysztof Helt8503df62007-10-16 01:29:08 -07003109static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003110{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003111 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003112}
3113
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114/**
3115 * cirrusfb_dbg_reg_dump
3116 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3117 *
3118 * DESCRIPTION:
3119 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3120 * old-style I/O ports are queried for information, otherwise MMIO is
3121 * used at the given @base address to query the information.
3122 */
3123
3124static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003125void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003126{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003127 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003128
Krzysztof Helt8503df62007-10-16 01:29:08 -07003129 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003130 "CR00", 0x00,
3131 "CR01", 0x01,
3132 "CR02", 0x02,
3133 "CR03", 0x03,
3134 "CR04", 0x04,
3135 "CR05", 0x05,
3136 "CR06", 0x06,
3137 "CR07", 0x07,
3138 "CR08", 0x08,
3139 "CR09", 0x09,
3140 "CR0A", 0x0A,
3141 "CR0B", 0x0B,
3142 "CR0C", 0x0C,
3143 "CR0D", 0x0D,
3144 "CR0E", 0x0E,
3145 "CR0F", 0x0F,
3146 "CR10", 0x10,
3147 "CR11", 0x11,
3148 "CR12", 0x12,
3149 "CR13", 0x13,
3150 "CR14", 0x14,
3151 "CR15", 0x15,
3152 "CR16", 0x16,
3153 "CR17", 0x17,
3154 "CR18", 0x18,
3155 "CR22", 0x22,
3156 "CR24", 0x24,
3157 "CR26", 0x26,
3158 "CR2D", 0x2D,
3159 "CR2E", 0x2E,
3160 "CR2F", 0x2F,
3161 "CR30", 0x30,
3162 "CR31", 0x31,
3163 "CR32", 0x32,
3164 "CR33", 0x33,
3165 "CR34", 0x34,
3166 "CR35", 0x35,
3167 "CR36", 0x36,
3168 "CR37", 0x37,
3169 "CR38", 0x38,
3170 "CR39", 0x39,
3171 "CR3A", 0x3A,
3172 "CR3B", 0x3B,
3173 "CR3C", 0x3C,
3174 "CR3D", 0x3D,
3175 "CR3E", 0x3E,
3176 "CR3F", 0x3F,
3177 NULL);
3178
Krzysztof Helt8503df62007-10-16 01:29:08 -07003179 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003180
Krzysztof Helt8503df62007-10-16 01:29:08 -07003181 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003182
Krzysztof Helt8503df62007-10-16 01:29:08 -07003183 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003184 "SR00", 0x00,
3185 "SR01", 0x01,
3186 "SR02", 0x02,
3187 "SR03", 0x03,
3188 "SR04", 0x04,
3189 "SR08", 0x08,
3190 "SR09", 0x09,
3191 "SR0A", 0x0A,
3192 "SR0B", 0x0B,
3193 "SR0D", 0x0D,
3194 "SR10", 0x10,
3195 "SR11", 0x11,
3196 "SR12", 0x12,
3197 "SR13", 0x13,
3198 "SR14", 0x14,
3199 "SR15", 0x15,
3200 "SR16", 0x16,
3201 "SR17", 0x17,
3202 "SR18", 0x18,
3203 "SR19", 0x19,
3204 "SR1A", 0x1A,
3205 "SR1B", 0x1B,
3206 "SR1C", 0x1C,
3207 "SR1D", 0x1D,
3208 "SR1E", 0x1E,
3209 "SR1F", 0x1F,
3210 NULL);
3211
Krzysztof Helt8503df62007-10-16 01:29:08 -07003212 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213}
3214
3215#endif /* CIRRUSFB_DEBUG */
3216