blob: 3b4b0f1e06155d796551ea899d6526b5424d195f [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/errno.h>
40#include <linux/string.h>
41#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/slab.h>
43#include <linux/delay.h>
44#include <linux/fb.h>
45#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/pgtable.h>
47
48#ifdef CONFIG_ZORRO
49#include <linux/zorro.h>
50#endif
51#ifdef CONFIG_PCI
52#include <linux/pci.h>
53#endif
54#ifdef CONFIG_AMIGA
55#include <asm/amigahw.h>
56#endif
57#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110058#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070059#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#else
61#define isPReP 0
62#endif
63
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070064#include <video/vga.h>
65#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Linus Torvalds1da177e2005-04-16 15:20:36 -070067/*****************************************************************
68 *
69 * debugging and utility macros
70 *
71 */
72
Linus Torvalds1da177e2005-04-16 15:20:36 -070073/* disable runtime assertions? */
74/* #define CIRRUSFB_NDEBUG */
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076/* debugging assertions */
77#ifndef CIRRUSFB_NDEBUG
78#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070079 if (!(expr)) { \
80 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070081 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#else
84#define assert(expr)
85#endif
86
Krzysztof Helt8503df62007-10-16 01:29:08 -070087#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/*****************************************************************
90 *
91 * chipset information
92 *
93 */
94
95/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070096enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 BT_NONE = 0,
98 BT_SD64,
99 BT_PICCOLO,
100 BT_PICASSO,
101 BT_SPECTRUM,
102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
105 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700106};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108/*
109 * per-board-type information, used for enumerating and abstracting
110 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700111 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 * use direct indexing on this array
113 * NOTE: '__initdata' cannot be used as some of this info
114 * is required at runtime. Maybe separate into an init-only and
115 * a run-time table?
116 */
117static const struct cirrusfb_board_info_rec {
118 char *name; /* ASCII name of chipset */
119 long maxclock[5]; /* maximum video clock */
120 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700121 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
122 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700123 /* construct bit 19 of screen start address */
124 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 /* initial SR07 value, then for each mode */
127 unsigned char sr07;
128 unsigned char sr07_1bpp;
129 unsigned char sr07_1bpp_mux;
130 unsigned char sr07_8bpp;
131 unsigned char sr07_8bpp_mux;
132
133 unsigned char sr1f; /* SR1F VGA initial register value */
134} cirrusfb_board_info[] = {
135 [BT_SD64] = {
136 .name = "CL SD64",
137 .maxclock = {
138 /* guess */
139 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700140 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700142 .init_sr07 = true,
143 .init_sr1f = true,
144 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 .sr07 = 0xF0,
146 .sr07_1bpp = 0xF0,
147 .sr07_8bpp = 0xF1,
148 .sr1f = 0x20
149 },
150 [BT_PICCOLO] = {
151 .name = "CL Piccolo",
152 .maxclock = {
153 /* guess */
154 90000, 90000, 90000, 90000, 90000
155 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700156 .init_sr07 = true,
157 .init_sr1f = true,
158 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 .sr07 = 0x80,
160 .sr07_1bpp = 0x80,
161 .sr07_8bpp = 0x81,
162 .sr1f = 0x22
163 },
164 [BT_PICASSO] = {
165 .name = "CL Picasso",
166 .maxclock = {
167 /* guess */
168 90000, 90000, 90000, 90000, 90000
169 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700170 .init_sr07 = true,
171 .init_sr1f = true,
172 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .sr07 = 0x20,
174 .sr07_1bpp = 0x20,
175 .sr07_8bpp = 0x21,
176 .sr1f = 0x22
177 },
178 [BT_SPECTRUM] = {
179 .name = "CL Spectrum",
180 .maxclock = {
181 /* guess */
182 90000, 90000, 90000, 90000, 90000
183 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700184 .init_sr07 = true,
185 .init_sr1f = true,
186 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 .sr07 = 0x80,
188 .sr07_1bpp = 0x80,
189 .sr07_8bpp = 0x81,
190 .sr1f = 0x22
191 },
192 [BT_PICASSO4] = {
193 .name = "CL Picasso4",
194 .maxclock = {
195 135100, 135100, 85500, 85500, 0
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = false,
199 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x20,
201 .sr07_1bpp = 0x20,
202 .sr07_8bpp = 0x21,
203 .sr1f = 0
204 },
205 [BT_ALPINE] = {
206 .name = "CL Alpine",
207 .maxclock = {
208 /* for the GD5430. GD5446 can do more... */
209 85500, 85500, 50000, 28500, 0
210 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700211 .init_sr07 = true,
212 .init_sr1f = true,
213 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 .sr07 = 0xA0,
215 .sr07_1bpp = 0xA1,
216 .sr07_1bpp_mux = 0xA7,
217 .sr07_8bpp = 0xA1,
218 .sr07_8bpp_mux = 0xA7,
219 .sr1f = 0x1C
220 },
221 [BT_GD5480] = {
222 .name = "CL GD5480",
223 .maxclock = {
224 135100, 200000, 200000, 135100, 135100
225 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700226 .init_sr07 = true,
227 .init_sr1f = true,
228 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 .sr07 = 0x10,
230 .sr07_1bpp = 0x11,
231 .sr07_8bpp = 0x11,
232 .sr1f = 0x1C
233 },
234 [BT_LAGUNA] = {
235 .name = "CL Laguna",
236 .maxclock = {
237 /* guess */
238 135100, 135100, 135100, 135100, 135100,
239 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700240 .init_sr07 = false,
241 .init_sr1f = false,
242 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 }
244};
245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246#ifdef CONFIG_PCI
247#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000248 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700251 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
252 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
253 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
254 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
255 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
256 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
257 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
258 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
259 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
260 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
261 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 { 0, }
263};
264MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
265#undef CHIP
266#endif /* CONFIG_PCI */
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268#ifdef CONFIG_ZORRO
269static const struct zorro_device_id cirrusfb_zorro_table[] = {
270 {
271 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
272 .driver_data = BT_SD64,
273 }, {
274 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
275 .driver_data = BT_PICCOLO,
276 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700277 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 .driver_data = BT_PICASSO,
279 }, {
280 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
281 .driver_data = BT_SPECTRUM,
282 }, {
283 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
284 .driver_data = BT_PICASSO4,
285 },
286 { 0 }
287};
288
289static const struct {
290 zorro_id id2;
291 unsigned long size;
292} cirrusfb_zorro_table2[] = {
293 [BT_SD64] = {
294 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
295 .size = 0x400000
296 },
297 [BT_PICCOLO] = {
298 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
299 .size = 0x200000
300 },
301 [BT_PICASSO] = {
302 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
303 .size = 0x200000
304 },
305 [BT_SPECTRUM] = {
306 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
307 .size = 0x200000
308 },
309 [BT_PICASSO4] = {
310 .id2 = 0,
311 .size = 0x400000
312 }
313};
314#endif /* CONFIG_ZORRO */
315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316struct cirrusfb_regs {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700317 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318};
319
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700321enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700322 CRT,
323 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700324};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700325#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
327/* info about board */
328struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700330 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 unsigned char SFR; /* Shadow of special function register */
332
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 struct cirrusfb_regs currentmode;
334 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700335 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700337 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338};
339
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700340static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700341static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
343/****************************************************************************/
344/**** BEGIN PROTOTYPES ******************************************************/
345
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700347static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700349static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350#endif
351
Krzysztof Helt8503df62007-10-16 01:29:08 -0700352static int cirrusfb_open(struct fb_info *info, int user);
353static int cirrusfb_release(struct fb_info *info, int user);
354static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
355 unsigned blue, unsigned transp,
356 struct fb_info *info);
357static int cirrusfb_check_var(struct fb_var_screeninfo *var,
358 struct fb_info *info);
359static int cirrusfb_set_par(struct fb_info *info);
360static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
361 struct fb_info *info);
362static int cirrusfb_blank(int blank_mode, struct fb_info *info);
363static void cirrusfb_fillrect(struct fb_info *info,
364 const struct fb_fillrect *region);
365static void cirrusfb_copyarea(struct fb_info *info,
366 const struct fb_copyarea *area);
367static void cirrusfb_imageblit(struct fb_info *info,
368 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
370/* function table of the above functions */
371static struct fb_ops cirrusfb_ops = {
372 .owner = THIS_MODULE,
373 .fb_open = cirrusfb_open,
374 .fb_release = cirrusfb_release,
375 .fb_setcolreg = cirrusfb_setcolreg,
376 .fb_check_var = cirrusfb_check_var,
377 .fb_set_par = cirrusfb_set_par,
378 .fb_pan_display = cirrusfb_pan_display,
379 .fb_blank = cirrusfb_blank,
380 .fb_fillrect = cirrusfb_fillrect,
381 .fb_copyarea = cirrusfb_copyarea,
382 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383};
384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700386static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700387static void switch_monitor(struct cirrusfb_info *cinfo, int on);
388static void WGen(const struct cirrusfb_info *cinfo,
389 int regnum, unsigned char val);
390static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
391static void AttrOn(const struct cirrusfb_info *cinfo);
392static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
393static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
394static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
395static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
396 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700398static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
399 unsigned char *red, unsigned char *green,
400 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700402static void cirrusfb_WaitBLT(u8 __iomem *regbase);
403static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
404 u_short curx, u_short cury,
405 u_short destx, u_short desty,
406 u_short width, u_short height,
407 u_short line_length);
408static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
409 u_short x, u_short y,
410 u_short width, u_short height,
411 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700413static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
415#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700416static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
417static void cirrusfb_dbg_print_regs(struct fb_info *info,
418 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700419 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420#endif /* CIRRUSFB_DEBUG */
421
422/*** END PROTOTYPES ********************************************************/
423/*****************************************************************************/
424/*** BEGIN Interface Used by the World ***************************************/
425
Krzysztof Helt8503df62007-10-16 01:29:08 -0700426static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
428/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700429static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
431 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700432 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 return 0;
434}
435
436/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700437static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438{
439 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700440 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 return 0;
442}
443
444/**** END Interface used by the World *************************************/
445/****************************************************************************/
446/**** BEGIN Hardware specific Routines **************************************/
447
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700449static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700451 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700452 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Krzysztof Helt486ff382008-10-15 22:03:42 -0700454 /* Read MCLK value */
455 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700456 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700459 * should divide it by to get VCLK
460 */
461
462 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700463 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700464 return 1;
465 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700466 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700467 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 }
469
Krzysztof Helt486ff382008-10-15 22:03:42 -0700470 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
473static int cirrusfb_check_var(struct fb_var_screeninfo *var,
474 struct fb_info *info)
475{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700476 int yres;
477 /* memory size in pixels */
478 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700481 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 var->red.offset = 0;
483 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700484 var->green = var->red;
485 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 break;
487
488 case 8:
489 var->red.offset = 0;
490 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700491 var->green = var->red;
492 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 break;
494
495 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700496 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 var->red.offset = 2;
498 var->green.offset = -3;
499 var->blue.offset = 8;
500 } else {
501 var->red.offset = 10;
502 var->green.offset = 5;
503 var->blue.offset = 0;
504 }
505 var->red.length = 5;
506 var->green.length = 5;
507 var->blue.length = 5;
508 break;
509
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700511 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 var->red.offset = 8;
513 var->green.offset = 16;
514 var->blue.offset = 24;
515 } else {
516 var->red.offset = 16;
517 var->green.offset = 8;
518 var->blue.offset = 0;
519 }
520 var->red.length = 8;
521 var->green.length = 8;
522 var->blue.length = 8;
523 break;
524
525 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700526 dev_dbg(info->device,
527 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700528 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 /* should never occur */
530 break;
531 }
532
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700533 if (var->xres_virtual < var->xres)
534 var->xres_virtual = var->xres;
535 /* use highest possible virtual resolution */
536 if (var->yres_virtual == -1) {
537 var->yres_virtual = pixels / var->xres_virtual;
538
539 dev_info(info->device,
540 "virtual resolution set to maximum of %dx%d\n",
541 var->xres_virtual, var->yres_virtual);
542 }
543 if (var->yres_virtual < var->yres)
544 var->yres_virtual = var->yres;
545
546 if (var->xres_virtual * var->yres_virtual > pixels) {
547 dev_err(info->device, "mode %dx%dx%d rejected... "
548 "virtual resolution too high to fit into video memory!\n",
549 var->xres_virtual, var->yres_virtual,
550 var->bits_per_pixel);
551 return -EINVAL;
552 }
553
554
555 if (var->xoffset < 0)
556 var->xoffset = 0;
557 if (var->yoffset < 0)
558 var->yoffset = 0;
559
560 /* truncate xoffset and yoffset to maximum if too high */
561 if (var->xoffset > var->xres_virtual - var->xres)
562 var->xoffset = var->xres_virtual - var->xres - 1;
563 if (var->yoffset > var->yres_virtual - var->yres)
564 var->yoffset = var->yres_virtual - var->yres - 1;
565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 var->red.msb_right =
567 var->green.msb_right =
568 var->blue.msb_right =
569 var->transp.offset =
570 var->transp.length =
571 var->transp.msb_right = 0;
572
573 yres = var->yres;
574 if (var->vmode & FB_VMODE_DOUBLE)
575 yres *= 2;
576 else if (var->vmode & FB_VMODE_INTERLACED)
577 yres = (yres + 1) / 2;
578
579 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700580 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700581 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 return -EINVAL;
583 }
584
585 return 0;
586}
587
Krzysztof Helt8503df62007-10-16 01:29:08 -0700588static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700590 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591{
592 long freq;
593 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700594 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Krzysztof Helt8503df62007-10-16 01:29:08 -0700597 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700599 info->fix.line_length = var->xres_virtual / 8;
600 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 break;
602
603 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700604 info->fix.line_length = var->xres_virtual;
605 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 break;
607
608 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700610 info->fix.line_length = var->xres_virtual * maxclockidx;
Krzysztof Helt3b921832008-10-15 22:03:41 -0700611 info->fix.visual = FB_VISUAL_TRUECOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 break;
613
614 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700615 dev_dbg(info->device,
616 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700617 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 /* should never occur */
619 break;
620 }
621
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700622 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
624 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700625 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700627 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
629 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
630 regs->multiplexing = 0;
631
632 /* If the frequency is greater than we can support, we might be able
633 * to use multiplexing for the video mode */
634 if (freq > maxclock) {
635 switch (cinfo->btype) {
636 case BT_ALPINE:
637 case BT_GD5480:
638 regs->multiplexing = 1;
639 break;
640
641 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700642 dev_err(info->device,
643 "Frequency greater than maxclock (%ld kHz)\n",
644 maxclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 return -EINVAL;
646 }
647 }
648#if 0
649 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
650 * the VCLK is double the pixel clock. */
651 switch (var->bits_per_pixel) {
652 case 16:
653 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700654 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700655 /* Xbh has this type of clock for 32-bit */
656 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 break;
658 }
659#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 return 0;
661}
662
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700663static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700665 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700666 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700667
Krzysztof Helt8503df62007-10-16 01:29:08 -0700668 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700669 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
Krzysztof Helt486ff382008-10-15 22:03:42 -0700671 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700672 dev_dbg(info->device, "Set %s as pixclock source.\n",
673 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700674 old1f |= 0x40;
675 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
676 if (div == 2)
677 old1e |= 1;
678
679 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700681 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682}
683
684/*************************************************************************
685 cirrusfb_set_par_foo()
686
687 actually writes the values for a new video mode into the hardware,
688**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700689static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690{
691 struct cirrusfb_info *cinfo = info->par;
692 struct fb_var_screeninfo *var = &info->var;
693 struct cirrusfb_regs regs;
694 u8 __iomem *regbase = cinfo->regbase;
695 unsigned char tmp;
696 int offset = 0, err;
697 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700698 int hdispend, hsyncstart, hsyncend, htotal;
699 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700700 long freq;
701 int nom, den, div;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700703 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700705 dev_dbg(info->device, "pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700707 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700710 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700712 dev_dbg(info->device, "mode change aborted. invalid var.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 return -EINVAL;
714 }
715
716 bi = &cirrusfb_board_info[cinfo->btype];
717
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700718 hsyncstart = var->xres + var->right_margin;
719 hsyncend = hsyncstart + var->hsync_len;
720 htotal = (hsyncend + var->left_margin) / 8 - 5;
721 hdispend = var->xres / 8 - 1;
722 hsyncstart = hsyncstart / 8 + 1;
723 hsyncend = hsyncend / 8 + 1;
724
725 yres = var->yres;
726 vsyncstart = yres + var->lower_margin;
727 vsyncend = vsyncstart + var->vsync_len;
728 vtotal = vsyncend + var->upper_margin;
729 vdispend = yres - 1;
730
731 if (var->vmode & FB_VMODE_DOUBLE) {
732 yres *= 2;
733 vsyncstart *= 2;
734 vsyncend *= 2;
735 vtotal *= 2;
736 } else if (var->vmode & FB_VMODE_INTERLACED) {
737 yres = (yres + 1) / 2;
738 vsyncstart = (vsyncstart + 1) / 2;
739 vsyncend = (vsyncend + 1) / 2;
740 vtotal = (vtotal + 1) / 2;
741 }
742
743 vtotal -= 2;
744 vsyncstart -= 1;
745 vsyncend -= 1;
746
747 if (yres >= 1024) {
748 vtotal /= 2;
749 vsyncstart /= 2;
750 vsyncend /= 2;
751 vdispend /= 2;
752 }
753 if (regs.multiplexing) {
754 htotal /= 2;
755 hsyncstart /= 2;
756 hsyncend /= 2;
757 hdispend /= 2;
758 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700760 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
762 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700763 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700766 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700769 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700770 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Krzysztof Helt8503df62007-10-16 01:29:08 -0700772 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700773 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700774 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700775 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700777 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 tmp = hsyncend % 32;
781 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700783 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700784 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700786 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
789 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700794 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700796 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700798 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700804 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700805 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
807 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700808 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 tmp |= 0x20;
810 if (var->vmode & FB_VMODE_DOUBLE)
811 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700812 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700813 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700816 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700818 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700819 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700821 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700822 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700824 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700825 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700827 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700828 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700830 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700831 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
833 tmp = 0;
834 if (var->vmode & FB_VMODE_INTERLACED)
835 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700836 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700838 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700840 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700842 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 tmp |= 128;
844
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700845 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700846 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700848 freq = PICOS2KHZ(var->pixclock);
849 bestclock(freq, &nom, &den, &div);
850
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700851 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
852 freq, nom, den, div);
853
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 /* set VCLK0 */
855 /* hardware RefClock: 14.31818 MHz */
856 /* formula: VClk = (OSC * N) / (D * (1+P)) */
857 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
858
Krzysztof Helt486ff382008-10-15 22:03:42 -0700859 if (cinfo->btype == BT_ALPINE) {
860 /* if freq is close to mclk or mclk/2 select mclk
861 * as clock source
862 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700863 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700864 if (divMCLK) {
865 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700866 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700867 }
868 }
869 if (nom) {
870 vga_wseq(regbase, CL_SEQRB, nom);
871 tmp = den << 1;
872 if (div != 0)
873 tmp |= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Krzysztof Helt486ff382008-10-15 22:03:42 -0700875 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
876 if ((cinfo->btype == BT_SD64) ||
877 (cinfo->btype == BT_ALPINE) ||
878 (cinfo->btype == BT_GD5480))
879 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700881 dev_dbg(info->device, "CL_SEQR1B: %ld\n", (long) tmp);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700882 vga_wseq(regbase, CL_SEQR1B, tmp);
883 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700885 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700887 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 else
889 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
890 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700891 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
Krzysztof Helt8503df62007-10-16 01:29:08 -0700893/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
894 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
896 /* don't know if it would hurt to also program this if no interlaced */
897 /* mode is used, but I feel better this way.. :-) */
898 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700899 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700901 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Krzysztof Helt8503df62007-10-16 01:29:08 -0700903 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
905 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700906 /* enable display memory & CRTC I/O address for color mode */
907 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
909 tmp |= 0x40;
910 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
911 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Krzysztof Helt8503df62007-10-16 01:29:08 -0700914 /* Screen A Preset Row-Scan register */
915 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
916 /* text cursor on and start line */
917 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
918 /* text cursor end line */
919 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921 /******************************************************
922 *
923 * 1 bpp
924 *
925 */
926
927 /* programming for different color depths */
928 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700929 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700930 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 /* SR07 */
933 switch (cinfo->btype) {
934 case BT_SD64:
935 case BT_PICCOLO:
936 case BT_PICASSO:
937 case BT_SPECTRUM:
938 case BT_PICASSO4:
939 case BT_ALPINE:
940 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700941 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700942 regs.multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 bi->sr07_1bpp_mux : bi->sr07_1bpp);
944 break;
945
946 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700947 vga_wseq(regbase, CL_SEQR7,
948 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 break;
950
951 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700952 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 break;
954 }
955
956 /* Extended Sequencer Mode */
957 switch (cinfo->btype) {
958 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700959 /* setting the SEQRF on SD64 is not necessary
960 * (only during init)
961 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700962 /* MCLK select */
963 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 break;
965
966 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700967 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700968 /* ### ueberall 0x22? */
969 /* ##vorher 1c MCLK select */
970 vga_wseq(regbase, CL_SEQR1F, 0x22);
971 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
972 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 break;
974
975 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700976 /* ##vorher 22 MCLK select */
977 vga_wseq(regbase, CL_SEQR1F, 0x22);
978 /* ## vorher d0 avoid FIFO underruns..? */
979 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 break;
981
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 case BT_PICASSO4:
983 case BT_ALPINE:
984 case BT_GD5480:
985 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 /* do nothing */
987 break;
988
989 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700990 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 break;
992 }
993
Krzysztof Helt8503df62007-10-16 01:29:08 -0700994 /* pixel mask: pass-through for first plane */
995 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 /* hidden dac reg: 1280x1024 */
998 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 /* hidden dac: nothing */
1001 WHDR(cinfo, 0);
1002 /* memory mode: odd/even, ext. memory */
1003 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1004 /* plane mask: only write to first plane */
1005 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 offset = var->xres_virtual / 16;
1007 }
1008
1009 /******************************************************
1010 *
1011 * 8 bpp
1012 *
1013 */
1014
1015 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001016 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 switch (cinfo->btype) {
1018 case BT_SD64:
1019 case BT_PICCOLO:
1020 case BT_PICASSO:
1021 case BT_SPECTRUM:
1022 case BT_PICASSO4:
1023 case BT_ALPINE:
1024 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001025 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 regs.multiplexing ?
1027 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1028 break;
1029
1030 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001031 vga_wseq(regbase, CL_SEQR7,
1032 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 break;
1034
1035 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001036 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 break;
1038 }
1039
1040 switch (cinfo->btype) {
1041 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001042 /* MCLK select */
1043 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 break;
1045
1046 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001049 /* ### vorher 1c MCLK select */
1050 vga_wseq(regbase, CL_SEQR1F, 0x22);
1051 /* Fast Page-Mode writes */
1052 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 break;
1054
1055 case BT_PICASSO4:
1056#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001057 /* ### INCOMPLETE!! */
1058 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001060/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 break;
1062
1063 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 /* We already set SRF and SR1F */
1065 break;
1066
1067 case BT_GD5480:
1068 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 /* do nothing */
1070 break;
1071
1072 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001073 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 break;
1075 }
1076
Krzysztof Helt8503df62007-10-16 01:29:08 -07001077 /* mode register: 256 color mode */
1078 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1079 /* pixel mask: pass-through all planes */
1080 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001082 /* hidden dac reg: 1280x1024 */
1083 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001085 /* hidden dac: nothing */
1086 WHDR(cinfo, 0);
1087 /* memory mode: chain4, ext. memory */
1088 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1089 /* plane mask: enable writing to all 4 planes */
1090 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 offset = var->xres_virtual / 8;
1092 }
1093
1094 /******************************************************
1095 *
1096 * 16 bpp
1097 *
1098 */
1099
1100 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001101 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 switch (cinfo->btype) {
1103 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001104 /* Extended Sequencer Mode: 256c col. mode */
1105 vga_wseq(regbase, CL_SEQR7, 0xf7);
1106 /* MCLK select */
1107 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 break;
1109
1110 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001111 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001112 vga_wseq(regbase, CL_SEQR7, 0x87);
1113 /* Fast Page-Mode writes */
1114 vga_wseq(regbase, CL_SEQRF, 0xb0);
1115 /* MCLK select */
1116 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 break;
1118
1119 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 vga_wseq(regbase, CL_SEQR7, 0x27);
1121 /* Fast Page-Mode writes */
1122 vga_wseq(regbase, CL_SEQRF, 0xb0);
1123 /* MCLK select */
1124 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001128 vga_wseq(regbase, CL_SEQR7, 0x27);
1129/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 break;
1131
1132 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001133 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 break;
1135
1136 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001137 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 /* We already set SRF and SR1F */
1139 break;
1140
1141 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001142 vga_wseq(regbase, CL_SEQR7,
1143 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 break;
1145
1146 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001147 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 break;
1149 }
1150
Krzysztof Helt8503df62007-10-16 01:29:08 -07001151 /* mode register: 256 color mode */
1152 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1153 /* pixel mask: pass-through all planes */
1154 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157#elif defined(CONFIG_ZORRO)
1158 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001159 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001161 /* memory mode: chain4, ext. memory */
1162 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1163 /* plane mask: enable writing to all 4 planes */
1164 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 offset = var->xres_virtual / 4;
1166 }
1167
1168 /******************************************************
1169 *
1170 * 32 bpp
1171 *
1172 */
1173
1174 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001175 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 switch (cinfo->btype) {
1177 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001178 /* Extended Sequencer Mode: 256c col. mode */
1179 vga_wseq(regbase, CL_SEQR7, 0xf9);
1180 /* MCLK select */
1181 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 break;
1183
1184 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001185 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 vga_wseq(regbase, CL_SEQR7, 0x85);
1187 /* Fast Page-Mode writes */
1188 vga_wseq(regbase, CL_SEQRF, 0xb0);
1189 /* MCLK select */
1190 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 break;
1192
1193 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001194 vga_wseq(regbase, CL_SEQR7, 0x25);
1195 /* Fast Page-Mode writes */
1196 vga_wseq(regbase, CL_SEQRF, 0xb0);
1197 /* MCLK select */
1198 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 break;
1200
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001202 vga_wseq(regbase, CL_SEQR7, 0x25);
1203/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 break;
1205
1206 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001207 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 break;
1209
1210 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001211 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 /* We already set SRF and SR1F */
1213 break;
1214
1215 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001216 vga_wseq(regbase, CL_SEQR7,
1217 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 break;
1219
1220 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001221 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 break;
1223 }
1224
Krzysztof Helt8503df62007-10-16 01:29:08 -07001225 /* mode register: 256 color mode */
1226 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1227 /* pixel mask: pass-through all planes */
1228 WGen(cinfo, VGA_PEL_MSK, 0xff);
1229 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1230 WHDR(cinfo, 0xc5);
1231 /* memory mode: chain4, ext. memory */
1232 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1233 /* plane mask: enable writing to all 4 planes */
1234 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 offset = var->xres_virtual / 4;
1236 }
1237
1238 /******************************************************
1239 *
1240 * unknown/unsupported bpp
1241 *
1242 */
1243
Krzysztof Helt8503df62007-10-16 01:29:08 -07001244 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001245 dev_err(info->device,
1246 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248
Krzysztof Helt8503df62007-10-16 01:29:08 -07001249 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 tmp = 0x22;
1251 if (offset & 0x100)
1252 tmp |= 0x10; /* offset overflow bit */
1253
Krzysztof Helt8503df62007-10-16 01:29:08 -07001254 /* screen start addr #16-18, fastpagemode cycles */
1255 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
1257 if (cinfo->btype == BT_SD64 ||
1258 cinfo->btype == BT_PICASSO4 ||
1259 cinfo->btype == BT_ALPINE ||
1260 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001261 /* screen start address bit 19 */
1262 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
Krzysztof Helt8503df62007-10-16 01:29:08 -07001264 /* text cursor location high */
1265 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1266 /* text cursor location low */
1267 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1268 /* underline row scanline = at very bottom */
1269 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270
Krzysztof Helt8503df62007-10-16 01:29:08 -07001271 /* controller mode */
1272 vga_wattr(regbase, VGA_ATC_MODE, 1);
1273 /* overscan (border) color */
1274 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1275 /* color plane enable */
1276 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1277 /* pixel panning */
1278 vga_wattr(regbase, CL_AR33, 0);
1279 /* color select */
1280 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281
1282 /* [ EGS: SetOffset(); ] */
1283 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001284 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
Krzysztof Helt8503df62007-10-16 01:29:08 -07001286 /* set/reset register */
1287 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1288 /* set/reset enable */
1289 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1290 /* color compare */
1291 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1292 /* data rotate */
1293 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1294 /* read map select */
1295 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1296 /* miscellaneous register */
1297 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1298 /* color don't care */
1299 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1300 /* bit mask */
1301 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
Krzysztof Helt8503df62007-10-16 01:29:08 -07001303 /* graphics cursor attributes: nothing special */
1304 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
1306 /* finally, turn on everything - turn off "FullBandwidth" bit */
1307 /* also, set "DotClock%2" bit where requested */
1308 tmp = 0x01;
1309
1310/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1311 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1312 tmp |= 0x08;
1313*/
1314
Krzysztof Helt8503df62007-10-16 01:29:08 -07001315 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001316 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317
1318 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319
1320 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001321 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001324 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325#endif
1326
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 return 0;
1328}
1329
1330/* for some reason incomprehensible to me, cirrusfb requires that you write
1331 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001334 cirrusfb_set_par_foo(info);
1335 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336}
1337
Krzysztof Helt8503df62007-10-16 01:29:08 -07001338static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1339 unsigned blue, unsigned transp,
1340 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
1342 struct cirrusfb_info *cinfo = info->par;
1343
1344 if (regno > 255)
1345 return -EINVAL;
1346
1347 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1348 u32 v;
1349 red >>= (16 - info->var.red.length);
1350 green >>= (16 - info->var.green.length);
1351 blue >>= (16 - info->var.blue.length);
1352
Krzysztof Helt8503df62007-10-16 01:29:08 -07001353 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 return 1;
1355 v = (red << info->var.red.offset) |
1356 (green << info->var.green.offset) |
1357 (blue << info->var.blue.offset);
1358
Krzysztof Helt060b6002007-10-16 01:29:13 -07001359 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 return 0;
1361 }
1362
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 if (info->var.bits_per_pixel == 8)
1364 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365
1366 return 0;
1367
1368}
1369
1370/*************************************************************************
1371 cirrusfb_pan_display()
1372
1373 performs display panning - provided hardware permits this
1374**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001375static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1376 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377{
1378 int xoffset = 0;
1379 int yoffset = 0;
1380 unsigned long base;
1381 unsigned char tmp = 0, tmp2 = 0, xpix;
1382 struct cirrusfb_info *cinfo = info->par;
1383
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001384 dev_dbg(info->device,
1385 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386
1387 /* no range checks for xoffset and yoffset, */
1388 /* as fb_pan_display has already done this */
1389 if (var->vmode & FB_VMODE_YWRAP)
1390 return -EINVAL;
1391
1392 info->var.xoffset = var->xoffset;
1393 info->var.yoffset = var->yoffset;
1394
1395 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1396 yoffset = var->yoffset;
1397
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001398 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399
1400 if (info->var.bits_per_pixel == 1) {
1401 /* base is already correct */
1402 xpix = (unsigned char) (var->xoffset % 8);
1403 } else {
1404 base /= 4;
1405 xpix = (unsigned char) ((xoffset % 4) * 2);
1406 }
1407
Krzysztof Helt8503df62007-10-16 01:29:08 -07001408 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
1410 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001411 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1412 (unsigned char) (base & 0xff));
1413 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1414 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415
1416 /* construct bits 16, 17 and 18 of screen start address */
1417 if (base & 0x10000)
1418 tmp |= 0x01;
1419 if (base & 0x20000)
1420 tmp |= 0x04;
1421 if (base & 0x40000)
1422 tmp |= 0x08;
1423
Krzysztof Helt8503df62007-10-16 01:29:08 -07001424 /* 0xf2 is %11110010, exclude tmp bits */
1425 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1426 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427
1428 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001429 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1430 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431
Krzysztof Helt8503df62007-10-16 01:29:08 -07001432 /* write pixel panning value to AR33; this does not quite work in 8bpp
1433 *
1434 * ### Piccolo..? Will this work?
1435 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001437 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
Krzysztof Helt8503df62007-10-16 01:29:08 -07001439 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
Krzysztof Helt8503df62007-10-16 01:29:08 -07001441 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442}
1443
Krzysztof Helt8503df62007-10-16 01:29:08 -07001444static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445{
1446 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001447 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1448 * then the caller blanks by setting the CLUT (Color Look Up Table)
1449 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1450 * failed due to e.g. a video mode which doesn't support it.
1451 * Implements VESA suspend and powerdown modes on hardware that
1452 * supports disabling hsync/vsync:
1453 * blank_mode == 2: suspend vsync
1454 * blank_mode == 3: suspend hsync
1455 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 */
1457 unsigned char val;
1458 struct cirrusfb_info *cinfo = info->par;
1459 int current_mode = cinfo->blank_mode;
1460
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001461 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 if (info->state != FBINFO_STATE_RUNNING ||
1464 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001465 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 return 0;
1467 }
1468
1469 /* Undo current */
1470 if (current_mode == FB_BLANK_NORMAL ||
1471 current_mode == FB_BLANK_UNBLANK) {
1472 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001473 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1474 /* clear "FullBandwidth" bit */
1475 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 }
1479
1480 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001483 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1484 /* set "FullBandwidth" bit */
1485 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 }
1487
1488 switch (blank_mode) {
1489 case FB_BLANK_UNBLANK:
1490 case FB_BLANK_NORMAL:
1491 break;
1492 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001493 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 break;
1495 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001496 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 break;
1498 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001499 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 break;
1501 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001502 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 return 1;
1504 }
1505
1506 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001507 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509 /* Let fbcon do a soft blank for us */
1510 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1511}
1512/**** END Hardware specific Routines **************************************/
1513/****************************************************************************/
1514/**** BEGIN Internal Routines ***********************************************/
1515
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001516static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001518 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 const struct cirrusfb_board_info_rec *bi;
1520
Krzysztof Helt8503df62007-10-16 01:29:08 -07001521 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
1523 bi = &cirrusfb_board_info[cinfo->btype];
1524
1525 /* reset board globally */
1526 switch (cinfo->btype) {
1527 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001528 WSFR(cinfo, 0x01);
1529 udelay(500);
1530 WSFR(cinfo, 0x51);
1531 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 break;
1533 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534 WSFR2(cinfo, 0xff);
1535 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 break;
1537 case BT_SD64:
1538 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 WSFR(cinfo, 0x1f);
1540 udelay(500);
1541 WSFR(cinfo, 0x4f);
1542 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 break;
1544 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001545 /* disable flickerfixer */
1546 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1547 mdelay(100);
1548 /* from Klaus' NetBSD driver: */
1549 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1550 /* put blitter into 542x compat */
1551 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1552 /* mode */
1553 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 break;
1555
1556 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 /* from Klaus' NetBSD driver: */
1558 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
1560
1561 case BT_ALPINE:
1562 /* Nothing to do to reset the board. */
1563 break;
1564
1565 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001566 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 break;
1568 }
1569
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001570 /* make sure RAM size set by this point */
1571 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
1573 /* the P4 is not fully initialized here; I rely on it having been */
1574 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001575 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
1577 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001578 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1579 WGen(cinfo, CL_POS102, 0x01);
1580 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
1582 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001583 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 /* reset sequencer logic */
1586 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587
Krzysztof Helt8503df62007-10-16 01:29:08 -07001588 /* FullBandwidth (video off) and 8/9 dot clock */
1589 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1590 /* polarity (-/-), disable access to display memory,
1591 * VGA_CRTC_START_HI base address: color
1592 */
1593 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594
Krzysztof Helt8503df62007-10-16 01:29:08 -07001595 /* "magic cookie" - doesn't make any sense to me.. */
1596/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1597 /* unlock all extension registers */
1598 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Krzysztof Helt8503df62007-10-16 01:29:08 -07001600 /* reset blitter */
1601 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603 switch (cinfo->btype) {
1604 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001605 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 break;
1607 case BT_ALPINE:
1608 break;
1609 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001610 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 break;
1612 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001613 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1614 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 break;
1616 }
1617 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001618 /* plane mask: nothing */
1619 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1620 /* character map select: doesn't even matter in gx mode */
1621 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1622 /* memory mode: chain-4, no odd/even, ext. memory */
1623 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
1625 /* controller-internal base address of video memory */
1626 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001627 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628
Krzysztof Helt8503df62007-10-16 01:29:08 -07001629 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1630 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
Krzysztof Helt8503df62007-10-16 01:29:08 -07001632 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1633 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1634 /* graphics cursor Y position (..."... ) */
1635 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1636 /* graphics cursor attributes */
1637 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1638 /* graphics cursor pattern address */
1639 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
1641 /* writing these on a P4 might give problems.. */
1642 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001643 /* configuration readback and ext. color */
1644 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1645 /* signature generator */
1646 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 }
1648
1649 /* MCLK select etc. */
1650 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653 /* Screen A preset row scan: none */
1654 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1655 /* Text cursor start: disable text cursor */
1656 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1657 /* Text cursor end: - */
1658 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1659 /* Screen start address high: 0 */
1660 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1661 /* Screen start address low: 0 */
1662 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1663 /* text cursor location high: 0 */
1664 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1665 /* text cursor location low: 0 */
1666 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
Krzysztof Helt8503df62007-10-16 01:29:08 -07001668 /* Underline Row scanline: - */
1669 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1670 /* mode control: timing enable, byte mode, no compat modes */
1671 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1672 /* Line Compare: not needed */
1673 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 /* ext. display controls: ext.adr. wrap */
1676 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
Krzysztof Helt8503df62007-10-16 01:29:08 -07001678 /* Set/Reset registes: - */
1679 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1680 /* Set/Reset enable: - */
1681 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1682 /* Color Compare: - */
1683 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1684 /* Data Rotate: - */
1685 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1686 /* Read Map Select: - */
1687 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1688 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1689 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1690 /* Miscellaneous: memory map base address, graphics mode */
1691 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1692 /* Color Don't care: involve all planes */
1693 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1694 /* Bit Mask: no mask at all */
1695 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 /* (5434 can't have bit 3 set for bitblt) */
1698 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001700 /* Graphics controller mode extensions: finer granularity,
1701 * 8byte data latches
1702 */
1703 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1706 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1707 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1708 /* Background color byte 1: - */
1709 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1710 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
Krzysztof Helt8503df62007-10-16 01:29:08 -07001712 /* Attribute Controller palette registers: "identity mapping" */
1713 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1714 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1715 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1716 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1717 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1718 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1719 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1720 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1721 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1722 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1723 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1724 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1725 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1726 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1727 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1728 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
Krzysztof Helt8503df62007-10-16 01:29:08 -07001730 /* Attribute Controller mode: graphics mode */
1731 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1732 /* Overscan color reg.: reg. 0 */
1733 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1734 /* Color Plane enable: Enable all 4 planes */
1735 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1736/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1737 /* Color Select: - */
1738 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
Krzysztof Helt8503df62007-10-16 01:29:08 -07001740 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
1742 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001743 /* polarity (-/-), enable display mem,
1744 * VGA_CRTC_START_HI i/o base = color
1745 */
1746 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Krzysztof Helt8503df62007-10-16 01:29:08 -07001748 /* BLT Start/status: Blitter reset */
1749 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1750 /* - " - : "end-of-reset" */
1751 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752
1753 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001754 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 return;
1756}
1757
Krzysztof Helt8503df62007-10-16 01:29:08 -07001758static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759{
1760#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1761 static int IsOn = 0; /* XXX not ok for multiple boards */
1762
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 if (cinfo->btype == BT_PICASSO4)
1764 return; /* nothing to switch */
1765 if (cinfo->btype == BT_ALPINE)
1766 return; /* nothing to switch */
1767 if (cinfo->btype == BT_GD5480)
1768 return; /* nothing to switch */
1769 if (cinfo->btype == BT_PICASSO) {
1770 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001771 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 return;
1773 }
1774 if (on) {
1775 switch (cinfo->btype) {
1776 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001777 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 break;
1779 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001780 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 break;
1782 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001783 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 break;
1785 default: /* do nothing */ break;
1786 }
1787 } else {
1788 switch (cinfo->btype) {
1789 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001790 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 break;
1792 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001793 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 break;
1795 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001798 default: /* do nothing */
1799 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 }
1801 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802#endif /* CONFIG_ZORRO */
1803}
1804
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805/******************************************/
1806/* Linux 2.6-style accelerated functions */
1807/******************************************/
1808
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809static void cirrusfb_fillrect(struct fb_info *info,
1810 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812 struct fb_fillrect modded;
1813 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001814 struct cirrusfb_info *cinfo = info->par;
1815 int m = info->var.bits_per_pixel;
1816 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1817 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818
1819 if (info->state != FBINFO_STATE_RUNNING)
1820 return;
1821 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1822 cfb_fillrect(info, region);
1823 return;
1824 }
1825
1826 vxres = info->var.xres_virtual;
1827 vyres = info->var.yres_virtual;
1828
1829 memcpy(&modded, region, sizeof(struct fb_fillrect));
1830
Krzysztof Helt8503df62007-10-16 01:29:08 -07001831 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 modded.dx >= vxres || modded.dy >= vyres)
1833 return;
1834
Krzysztof Helt8503df62007-10-16 01:29:08 -07001835 if (modded.dx + modded.width > vxres)
1836 modded.width = vxres - modded.dx;
1837 if (modded.dy + modded.height > vyres)
1838 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839
Krzysztof Helt060b6002007-10-16 01:29:13 -07001840 cirrusfb_RectFill(cinfo->regbase,
1841 info->var.bits_per_pixel,
1842 (region->dx * m) / 8, region->dy,
1843 (region->width * m) / 8, region->height,
1844 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001845 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846}
1847
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848static void cirrusfb_copyarea(struct fb_info *info,
1849 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 struct fb_copyarea modded;
1852 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001853 struct cirrusfb_info *cinfo = info->par;
1854 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855
1856 if (info->state != FBINFO_STATE_RUNNING)
1857 return;
1858 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1859 cfb_copyarea(info, area);
1860 return;
1861 }
1862
1863 vxres = info->var.xres_virtual;
1864 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001865 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866
Krzysztof Helt8503df62007-10-16 01:29:08 -07001867 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 modded.sx >= vxres || modded.sy >= vyres ||
1869 modded.dx >= vxres || modded.dy >= vyres)
1870 return;
1871
Krzysztof Helt8503df62007-10-16 01:29:08 -07001872 if (modded.sx + modded.width > vxres)
1873 modded.width = vxres - modded.sx;
1874 if (modded.dx + modded.width > vxres)
1875 modded.width = vxres - modded.dx;
1876 if (modded.sy + modded.height > vyres)
1877 modded.height = vyres - modded.sy;
1878 if (modded.dy + modded.height > vyres)
1879 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880
Krzysztof Helt060b6002007-10-16 01:29:13 -07001881 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1882 (area->sx * m) / 8, area->sy,
1883 (area->dx * m) / 8, area->dy,
1884 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001885 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001886
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887}
1888
Krzysztof Helt8503df62007-10-16 01:29:08 -07001889static void cirrusfb_imageblit(struct fb_info *info,
1890 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891{
1892 struct cirrusfb_info *cinfo = info->par;
1893
Krzysztof Helt8503df62007-10-16 01:29:08 -07001894 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 cfb_imageblit(info, image);
1896}
1897
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898#ifdef CONFIG_PPC_PREP
1899#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1900#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 *display = PREP_VIDEO_BASE;
1904 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905}
1906
1907#endif /* CONFIG_PPC_PREP */
1908
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001910static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911
1912/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1913 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1914 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1915 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001916static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1917 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918{
1919 unsigned long mem;
1920 unsigned char SRF;
1921
Krzysztof Helt8503df62007-10-16 01:29:08 -07001922 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001924 case 0x08:
1925 mem = 512 * 1024;
1926 break;
1927 case 0x10:
1928 mem = 1024 * 1024;
1929 break;
1930 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
1931 * on the 5430.
1932 */
1933 case 0x18:
1934 mem = 2048 * 1024;
1935 break;
1936 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001937 dev_warn(info->device, "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 mem = 1024 * 1024;
1939 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001940 if (SRF & 0x80)
1941 /* If DRAM bank switching is enabled, there must be twice as much
1942 * memory installed. (4MB on the 5434)
1943 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001945
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 return mem;
1948}
1949
Krzysztof Helt8503df62007-10-16 01:29:08 -07001950static void get_pci_addrs(const struct pci_dev *pdev,
1951 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001953 assert(pdev != NULL);
1954 assert(display != NULL);
1955 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957 *display = 0;
1958 *registers = 0;
1959
1960 /* This is a best-guess for now */
1961
1962 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1963 *display = pci_resource_start(pdev, 1);
1964 *registers = pci_resource_start(pdev, 0);
1965 } else {
1966 *display = pci_resource_start(pdev, 0);
1967 *registers = pci_resource_start(pdev, 1);
1968 }
1969
Krzysztof Helt8503df62007-10-16 01:29:08 -07001970 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971}
1972
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001973static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001975 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001977 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978#if 0 /* if system didn't claim this region, we would... */
1979 release_mem_region(0xA0000, 65535);
1980#endif
1981 if (release_io_ports)
1982 release_region(0x3C0, 32);
1983 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984}
1985#endif /* CONFIG_PCI */
1986
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001988static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989{
Al Virod91f5bb2007-10-17 00:27:18 +01001990 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001991 struct zorro_dev *zdev = to_zorro_dev(info->device);
1992
1993 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994
1995 if (cinfo->btype == BT_PICASSO4) {
1996 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001997 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001998 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002000 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002001 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003}
2004#endif /* CONFIG_ZORRO */
2005
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002006static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002008 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 struct fb_var_screeninfo *var = &info->var;
2010
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 info->pseudo_palette = cinfo->pseudo_palette;
2012 info->flags = FBINFO_DEFAULT
2013 | FBINFO_HWACCEL_XPAN
2014 | FBINFO_HWACCEL_YPAN
2015 | FBINFO_HWACCEL_FILLRECT
2016 | FBINFO_HWACCEL_COPYAREA;
2017 if (noaccel)
2018 info->flags |= FBINFO_HWACCEL_DISABLED;
2019 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 if (cinfo->btype == BT_GD5480) {
2021 if (var->bits_per_pixel == 16)
2022 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002023 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 info->screen_base += 2 * MB_;
2025 }
2026
2027 /* Fill fix common fields */
2028 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2029 sizeof(info->fix.id));
2030
2031 /* monochrome: only 1 memory plane */
2032 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002033 info->fix.smem_len = info->screen_size;
2034 if (var->bits_per_pixel == 1)
2035 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 info->fix.xpanstep = 1;
2038 info->fix.ypanstep = 1;
2039 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040
2041 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 info->fix.mmio_len = 0;
2043 info->fix.accel = FB_ACCEL_NONE;
2044
2045 fb_alloc_cmap(&info->cmap, 256, 0);
2046
2047 return 0;
2048}
2049
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002050static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002052 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002054 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 btype = cinfo->btype;
2057
2058 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002059 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002061 /* set all the vital stuff */
2062 cirrusfb_set_fbinfo(info);
2063
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002064 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002066 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2067 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002068 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002069 err = -EINVAL;
2070 goto err_dealloc_cmap;
2071 }
2072
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 info->var.activate = FB_ACTIVATE_NOW;
2074
2075 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2076 if (err < 0) {
2077 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002078 dev_dbg(info->device,
2079 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002080 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 }
2082
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 err = register_framebuffer(info);
2084 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002085 dev_err(info->device,
2086 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087 goto err_dealloc_cmap;
2088 }
2089
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 return 0;
2091
2092err_dealloc_cmap:
2093 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002094 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002095 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 return err;
2097}
2098
Krzysztof Helt8503df62007-10-16 01:29:08 -07002099static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100{
2101 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102
Krzysztof Helt8503df62007-10-16 01:29:08 -07002103 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002104 unregister_framebuffer(info);
2105 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002106 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002107 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002108 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109}
2110
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002112static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2113 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114{
2115 struct cirrusfb_info *cinfo;
2116 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002117 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 unsigned long board_addr, board_size;
2119 int ret;
2120
2121 ret = pci_enable_device(pdev);
2122 if (ret < 0) {
2123 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2124 goto err_out;
2125 }
2126
2127 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2128 if (!info) {
2129 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2130 ret = -ENOMEM;
2131 goto err_disable;
2132 }
2133
2134 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002135 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002137 dev_dbg(info->device,
2138 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
2139 (unsigned long long)pdev->resource[0].start, btype);
2140 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2141 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142
Krzysztof Helt8503df62007-10-16 01:29:08 -07002143 if (isPReP) {
2144 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002146 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002148 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002149 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002151 dev_dbg(info->device,
2152 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002153 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002154 /* FIXME: this forces VGA. alternatives? */
2155 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 }
2157
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002158 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002159 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160
2161 board_size = (btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002162 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163
2164 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002165 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002166 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2167 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 goto err_release_fb;
2169 }
2170#if 0 /* if the system didn't claim this region, we would... */
2171 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002172 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2173 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 ret = -EBUSY;
2175 goto err_release_regions;
2176 }
2177#endif
2178 if (request_region(0x3C0, 32, "cirrusfb"))
2179 release_io_ports = 1;
2180
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002181 info->screen_base = ioremap(board_addr, board_size);
2182 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 ret = -EIO;
2184 goto err_release_legacy;
2185 }
2186
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002187 info->fix.smem_start = board_addr;
2188 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 cinfo->unmap = cirrusfb_pci_unmap;
2190
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002191 dev_info(info->device,
2192 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2193 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 pci_set_drvdata(pdev, info);
2195
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002196 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002197 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002198 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002199 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200
2201err_release_legacy:
2202 if (release_io_ports)
2203 release_region(0x3C0, 32);
2204#if 0
2205 release_mem_region(0xA0000, 65535);
2206err_release_regions:
2207#endif
2208 pci_release_regions(pdev);
2209err_release_fb:
2210 framebuffer_release(info);
2211err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212err_out:
2213 return ret;
2214}
2215
Krzysztof Helt8503df62007-10-16 01:29:08 -07002216static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217{
2218 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219
Krzysztof Helt8503df62007-10-16 01:29:08 -07002220 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221}
2222
2223static struct pci_driver cirrusfb_pci_driver = {
2224 .name = "cirrusfb",
2225 .id_table = cirrusfb_pci_table,
2226 .probe = cirrusfb_pci_register,
2227 .remove = __devexit_p(cirrusfb_pci_unregister),
2228#ifdef CONFIG_PM
2229#if 0
2230 .suspend = cirrusfb_pci_suspend,
2231 .resume = cirrusfb_pci_resume,
2232#endif
2233#endif
2234};
2235#endif /* CONFIG_PCI */
2236
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002238static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2239 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240{
2241 struct cirrusfb_info *cinfo;
2242 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002243 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 struct zorro_dev *z2 = NULL;
2245 unsigned long board_addr, board_size, size;
2246 int ret;
2247
2248 btype = ent->driver_data;
2249 if (cirrusfb_zorro_table2[btype].id2)
2250 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2251 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252
2253 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2254 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002255 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 ret = -ENOMEM;
2257 goto err_out;
2258 }
2259
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002260 dev_info(info->device, "%s board detected\n",
2261 cirrusfb_board_info[btype].name);
2262
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 cinfo->btype = btype;
2265
Al Viro36ea96a2007-10-27 19:46:58 +01002266 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002267 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 board_addr = zorro_resource_start(z);
2270 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002271 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272
2273 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002274 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2275 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 ret = -EBUSY;
2277 goto err_release_fb;
2278 }
2279
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 ret = -EIO;
2281
2282 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002283 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284
2285 /* To be precise, for the P4 this is not the */
2286 /* begin of the board, but the begin of RAM. */
2287 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2288 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002289 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 if (!cinfo->regbase)
2291 goto err_release_region;
2292
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002293 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002294 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002296 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002298 info->fix.smem_start = board_addr + 16777216;
2299 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2300 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 goto err_unmap_regbase;
2302 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002303 dev_info(info->device, " REG at $%lx\n",
2304 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002306 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002308 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002310 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2311 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 goto err_release_region;
2313
2314 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002315 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002316 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002318 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002319 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 }
2321 cinfo->unmap = cirrusfb_zorro_unmap;
2322
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002323 dev_info(info->device,
2324 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2325 board_size / MB_, board_addr);
2326
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 zorro_set_drvdata(z, info);
2328
Al Virod91f5bb2007-10-17 00:27:18 +01002329 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002330 if (ret) {
2331 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002332 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002333 iounmap(cinfo->regbase - 0x600000);
2334 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002335 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002336 }
2337 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338
2339err_unmap_regbase:
2340 /* Parental advisory: explicit hack */
2341 iounmap(cinfo->regbase - 0x600000);
2342err_release_region:
2343 release_region(board_addr, board_size);
2344err_release_fb:
2345 framebuffer_release(info);
2346err_out:
2347 return ret;
2348}
2349
2350void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2351{
2352 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353
Krzysztof Helt8503df62007-10-16 01:29:08 -07002354 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355}
2356
2357static struct zorro_driver cirrusfb_zorro_driver = {
2358 .name = "cirrusfb",
2359 .id_table = cirrusfb_zorro_table,
2360 .probe = cirrusfb_zorro_register,
2361 .remove = __devexit_p(cirrusfb_zorro_unregister),
2362};
2363#endif /* CONFIG_ZORRO */
2364
2365static int __init cirrusfb_init(void)
2366{
2367 int error = 0;
2368
2369#ifndef MODULE
2370 char *option = NULL;
2371
2372 if (fb_get_options("cirrusfb", &option))
2373 return -ENODEV;
2374 cirrusfb_setup(option);
2375#endif
2376
2377#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002378 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379#endif
2380#ifdef CONFIG_PCI
2381 error |= pci_register_driver(&cirrusfb_pci_driver);
2382#endif
2383 return error;
2384}
2385
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002387static int __init cirrusfb_setup(char *options)
2388{
Vlada Pericee119402008-11-19 15:36:45 -08002389 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390
Linus Torvalds1da177e2005-04-16 15:20:36 -07002391 if (!options || !*options)
2392 return 0;
2393
Krzysztof Helt8503df62007-10-16 01:29:08 -07002394 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002395 if (!*this_opt)
2396 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 if (!strcmp(this_opt, "noaccel"))
2399 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002400 else if (!strncmp(this_opt, "mode:", 5))
2401 mode_option = this_opt + 5;
2402 else
2403 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 }
2405 return 0;
2406}
2407#endif
2408
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 /*
2410 * Modularization
2411 */
2412
2413MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2414MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2415MODULE_LICENSE("GPL");
2416
Krzysztof Helt8503df62007-10-16 01:29:08 -07002417static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418{
2419#ifdef CONFIG_PCI
2420 pci_unregister_driver(&cirrusfb_pci_driver);
2421#endif
2422#ifdef CONFIG_ZORRO
2423 zorro_unregister_driver(&cirrusfb_zorro_driver);
2424#endif
2425}
2426
2427module_init(cirrusfb_init);
2428
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002429module_param(mode_option, charp, 0);
2430MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002431module_param(noaccel, bool, 0);
2432MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002433
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434#ifdef MODULE
2435module_exit(cirrusfb_exit);
2436#endif
2437
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438/**********************************************************************/
2439/* about the following functions - I have used the same names for the */
2440/* functions as Markus Wild did in his Retina driver for NetBSD as */
2441/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002442/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443/**********************************************************************/
2444
2445/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002446static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 int regnum, unsigned char val)
2448{
2449 unsigned long regofs = 0;
2450
2451 if (cinfo->btype == BT_PICASSO) {
2452 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002453/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2454 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2456 regofs = 0xfff;
2457 }
2458
Krzysztof Helt8503df62007-10-16 01:29:08 -07002459 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460}
2461
2462/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464{
2465 unsigned long regofs = 0;
2466
2467 if (cinfo->btype == BT_PICASSO) {
2468 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2470 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2472 regofs = 0xfff;
2473 }
2474
Krzysztof Helt8503df62007-10-16 01:29:08 -07002475 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476}
2477
2478/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002479static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002481 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482
Krzysztof Helt8503df62007-10-16 01:29:08 -07002483 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 /* if we're just in "write value" mode, write back the */
2485 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002486 vga_w(cinfo->regbase, VGA_ATT_IW,
2487 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 }
2489 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002490/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2491 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492
2493 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002494 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495}
2496
2497/*** WHDR() - write into the Hidden DAC register ***/
2498/* as the HDR is the only extension register that requires special treatment
2499 * (the other extension registers are accessible just like the "ordinary"
2500 * registers of their functional group) here is a specialized routine for
2501 * accessing the HDR
2502 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002503static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504{
2505 unsigned char dummy;
2506
2507 if (cinfo->btype == BT_PICASSO) {
2508 /* Klaus' hint for correct access to HDR on some boards */
2509 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002510 WGen(cinfo, VGA_PEL_MSK, 0x00);
2511 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 dummy = RGen(cinfo, VGA_PEL_IW);
2514 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 }
2516 /* now do the usual stuff to access the HDR */
2517
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518 dummy = RGen(cinfo, VGA_PEL_MSK);
2519 udelay(200);
2520 dummy = RGen(cinfo, VGA_PEL_MSK);
2521 udelay(200);
2522 dummy = RGen(cinfo, VGA_PEL_MSK);
2523 udelay(200);
2524 dummy = RGen(cinfo, VGA_PEL_MSK);
2525 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527 WGen(cinfo, VGA_PEL_MSK, val);
2528 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
2530 if (cinfo->btype == BT_PICASSO) {
2531 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002532 dummy = RGen(cinfo, VGA_PEL_IW);
2533 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534
2535 /* and at the end, restore the mask value */
2536 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537 WGen(cinfo, VGA_PEL_MSK, 0xff);
2538 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 }
2540}
2541
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544{
2545#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549#endif
2550}
2551
2552/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554{
2555#ifdef CONFIG_ZORRO
2556 /* writing an arbitrary value to this one causes the monitor switcher */
2557 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561#endif
2562}
2563
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002565static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 unsigned char green, unsigned char blue)
2567{
2568 unsigned int data = VGA_PEL_D;
2569
2570 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002571 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572
2573 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2574 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2575 /* but DAC data register IS, at least for Picasso II */
2576 if (cinfo->btype == BT_PICASSO)
2577 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578 vga_w(cinfo->regbase, data, red);
2579 vga_w(cinfo->regbase, data, green);
2580 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582 vga_w(cinfo->regbase, data, blue);
2583 vga_w(cinfo->regbase, data, green);
2584 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 }
2586}
2587
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588#if 0
2589/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591 unsigned char *green, unsigned char *blue)
2592{
2593 unsigned int data = VGA_PEL_D;
2594
Krzysztof Helt8503df62007-10-16 01:29:08 -07002595 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596
2597 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2598 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2599 if (cinfo->btype == BT_PICASSO)
2600 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002601 *red = vga_r(cinfo->regbase, data);
2602 *green = vga_r(cinfo->regbase, data);
2603 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002605 *blue = vga_r(cinfo->regbase, data);
2606 *green = vga_r(cinfo->regbase, data);
2607 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 }
2609}
2610#endif
2611
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612/*******************************************************************
2613 cirrusfb_WaitBLT()
2614
2615 Wait for the BitBLT engine to complete a possible earlier job
2616*********************************************************************/
2617
2618/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002619static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620{
2621 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002622 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623 /* do nothing */ ;
2624}
2625
2626/*******************************************************************
2627 cirrusfb_BitBLT()
2628
2629 perform accelerated "scrolling"
2630********************************************************************/
2631
Krzysztof Helt8503df62007-10-16 01:29:08 -07002632static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2633 u_short curx, u_short cury,
2634 u_short destx, u_short desty,
2635 u_short width, u_short height,
2636 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637{
2638 u_short nwidth, nheight;
2639 u_long nsrc, ndest;
2640 u_char bltmode;
2641
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642 nwidth = width - 1;
2643 nheight = height - 1;
2644
2645 bltmode = 0x00;
2646 /* if source adr < dest addr, do the Blt backwards */
2647 if (cury <= desty) {
2648 if (cury == desty) {
2649 /* if src and dest are on the same line, check x */
2650 if (curx < destx)
2651 bltmode |= 0x01;
2652 } else
2653 bltmode |= 0x01;
2654 }
2655 if (!bltmode) {
2656 /* standard case: forward blitting */
2657 nsrc = (cury * line_length) + curx;
2658 ndest = (desty * line_length) + destx;
2659 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002660 /* this means start addresses are at the end,
2661 * counting backwards
2662 */
2663 nsrc = cury * line_length + curx +
2664 nheight * line_length + nwidth;
2665 ndest = desty * line_length + destx +
2666 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667 }
2668
2669 /*
2670 run-down of registers to be programmed:
2671 destination pitch
2672 source pitch
2673 BLT width/height
2674 source start
2675 destination start
2676 BLT mode
2677 BLT ROP
2678 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2679 start/stop
2680 */
2681
Krzysztof Helt8503df62007-10-16 01:29:08 -07002682 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683
2684 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002685 /* dest pitch low */
2686 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2687 /* dest pitch hi */
2688 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2689 /* source pitch low */
2690 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2691 /* source pitch hi */
2692 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693
2694 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002695 /* BLT width low */
2696 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2697 /* BLT width hi */
2698 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699
2700 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002701 /* BLT height low */
2702 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2703 /* BLT width hi */
2704 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705
2706 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002707 /* BLT dest low */
2708 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2709 /* BLT dest mid */
2710 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2711 /* BLT dest hi */
2712 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713
2714 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002715 /* BLT src low */
2716 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2717 /* BLT src mid */
2718 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2719 /* BLT src hi */
2720 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721
2722 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002723 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724
2725 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002726 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727
2728 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002729 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730}
2731
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732/*******************************************************************
2733 cirrusfb_RectFill()
2734
2735 perform accelerated rectangle fill
2736********************************************************************/
2737
Krzysztof Helt8503df62007-10-16 01:29:08 -07002738static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739 u_short x, u_short y, u_short width, u_short height,
2740 u_char color, u_short line_length)
2741{
2742 u_short nwidth, nheight;
2743 u_long ndest;
2744 u_char op;
2745
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746 nwidth = width - 1;
2747 nheight = height - 1;
2748
2749 ndest = (y * line_length) + x;
2750
Krzysztof Helt8503df62007-10-16 01:29:08 -07002751 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752
2753 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002754 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2755 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2756 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2757 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758
2759 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002760 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2761 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762
2763 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002764 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2765 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766
2767 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002768 /* BLT dest low */
2769 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2770 /* BLT dest mid */
2771 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2772 /* BLT dest hi */
2773 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774
2775 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002776 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2777 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2778 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779
2780 /* This is a ColorExpand Blt, using the */
2781 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002782 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2783 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784
2785 op = 0xc0;
2786 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002787 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2788 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 op = 0x50;
2790 op = 0xd0;
2791 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002792 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2793 vga_wgfx(regbase, CL_GR11, color); /* background color */
2794 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2795 vga_wgfx(regbase, CL_GR13, color); /* background color */
2796 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2797 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 op = 0x50;
2799 op = 0xf0;
2800 }
2801 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002802 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803
2804 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002805 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806
2807 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002808 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809}
2810
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811/**************************************************************************
2812 * bestclock() - determine closest possible clock lower(?) than the
2813 * desired pixel clock
2814 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002815static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002817 int n, d;
2818 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819
Krzysztof Helt8503df62007-10-16 01:29:08 -07002820 assert(nom != NULL);
2821 assert(den != NULL);
2822 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823
2824 *nom = 0;
2825 *den = 0;
2826 *div = 0;
2827
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 if (freq < 8000)
2829 freq = 8000;
2830
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002831 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832
2833 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002834 int s = 0;
2835
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002836 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002838 int temp = d;
2839
2840 if (temp > 31) {
2841 s = 1;
2842 temp >>= 1;
2843 }
2844 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002845 h = h > freq ? h - freq : freq - h;
2846 if (h < diff) {
2847 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002849 *den = temp;
2850 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851 }
2852 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002853 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002855 if (d > 31) {
2856 s = 1;
2857 d >>= 1;
2858 }
2859 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002860 h = h > freq ? h - freq : freq - h;
2861 if (h < diff) {
2862 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002864 *den = d;
2865 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 }
2867 }
2868 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869}
2870
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871/* -------------------------------------------------------------------------
2872 *
2873 * debugging functions
2874 *
2875 * -------------------------------------------------------------------------
2876 */
2877
2878#ifdef CIRRUSFB_DEBUG
2879
2880/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881 * cirrusfb_dbg_print_regs
2882 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2883 * @reg_class: type of registers to read: %CRT, or %SEQ
2884 *
2885 * DESCRIPTION:
2886 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2887 * old-style I/O ports are queried for information, otherwise MMIO is
2888 * used at the given @base address to query the information.
2889 */
2890
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002891static void cirrusfb_dbg_print_regs(struct fb_info *info,
2892 caddr_t regbase,
2893 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894{
2895 va_list list;
2896 unsigned char val = 0;
2897 unsigned reg;
2898 char *name;
2899
Krzysztof Helt8503df62007-10-16 01:29:08 -07002900 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901
Krzysztof Helt8503df62007-10-16 01:29:08 -07002902 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002904 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905
2906 switch (reg_class) {
2907 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002908 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002909 break;
2910 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002911 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912 break;
2913 default:
2914 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002915 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916 break;
2917 }
2918
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002919 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920
Krzysztof Helt8503df62007-10-16 01:29:08 -07002921 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922 }
2923
Krzysztof Helt8503df62007-10-16 01:29:08 -07002924 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002925}
2926
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 * cirrusfb_dbg_reg_dump
2929 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2930 *
2931 * DESCRIPTION:
2932 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2933 * old-style I/O ports are queried for information, otherwise MMIO is
2934 * used at the given @base address to query the information.
2935 */
2936
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002937static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002939 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002941 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942 "CR00", 0x00,
2943 "CR01", 0x01,
2944 "CR02", 0x02,
2945 "CR03", 0x03,
2946 "CR04", 0x04,
2947 "CR05", 0x05,
2948 "CR06", 0x06,
2949 "CR07", 0x07,
2950 "CR08", 0x08,
2951 "CR09", 0x09,
2952 "CR0A", 0x0A,
2953 "CR0B", 0x0B,
2954 "CR0C", 0x0C,
2955 "CR0D", 0x0D,
2956 "CR0E", 0x0E,
2957 "CR0F", 0x0F,
2958 "CR10", 0x10,
2959 "CR11", 0x11,
2960 "CR12", 0x12,
2961 "CR13", 0x13,
2962 "CR14", 0x14,
2963 "CR15", 0x15,
2964 "CR16", 0x16,
2965 "CR17", 0x17,
2966 "CR18", 0x18,
2967 "CR22", 0x22,
2968 "CR24", 0x24,
2969 "CR26", 0x26,
2970 "CR2D", 0x2D,
2971 "CR2E", 0x2E,
2972 "CR2F", 0x2F,
2973 "CR30", 0x30,
2974 "CR31", 0x31,
2975 "CR32", 0x32,
2976 "CR33", 0x33,
2977 "CR34", 0x34,
2978 "CR35", 0x35,
2979 "CR36", 0x36,
2980 "CR37", 0x37,
2981 "CR38", 0x38,
2982 "CR39", 0x39,
2983 "CR3A", 0x3A,
2984 "CR3B", 0x3B,
2985 "CR3C", 0x3C,
2986 "CR3D", 0x3D,
2987 "CR3E", 0x3E,
2988 "CR3F", 0x3F,
2989 NULL);
2990
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002991 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002992
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002993 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002994
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002995 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996 "SR00", 0x00,
2997 "SR01", 0x01,
2998 "SR02", 0x02,
2999 "SR03", 0x03,
3000 "SR04", 0x04,
3001 "SR08", 0x08,
3002 "SR09", 0x09,
3003 "SR0A", 0x0A,
3004 "SR0B", 0x0B,
3005 "SR0D", 0x0D,
3006 "SR10", 0x10,
3007 "SR11", 0x11,
3008 "SR12", 0x12,
3009 "SR13", 0x13,
3010 "SR14", 0x14,
3011 "SR15", 0x15,
3012 "SR16", 0x16,
3013 "SR17", 0x17,
3014 "SR18", 0x18,
3015 "SR19", 0x19,
3016 "SR1A", 0x1A,
3017 "SR1B", 0x1B,
3018 "SR1C", 0x1C,
3019 "SR1D", 0x1D,
3020 "SR1E", 0x1E,
3021 "SR1F", 0x1F,
3022 NULL);
3023
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003024 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003025}
3026
3027#endif /* CIRRUSFB_DEBUG */
3028