blob: 119e49ed6218d8fe4cdc5ce34894efc900e77886 [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) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700870 tmp = den << 1;
871 if (div != 0)
872 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700873 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
874 if ((cinfo->btype == BT_SD64) ||
875 (cinfo->btype == BT_ALPINE) ||
876 (cinfo->btype == BT_GD5480))
877 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700879 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
880 /* Laguna chipset has reversed clock registers */
881 if (cinfo->btype == BT_LAGUNA) {
882 vga_wseq(regbase, CL_SEQRE, tmp);
883 vga_wseq(regbase, CL_SEQR1E, nom);
884 } else {
885 vga_wseq(regbase, CL_SEQRB, nom);
886 vga_wseq(regbase, CL_SEQR1B, tmp);
887 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700888 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700890 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700892 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 else
894 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
895 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700896 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Krzysztof Helt8503df62007-10-16 01:29:08 -0700898/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
899 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
901 /* don't know if it would hurt to also program this if no interlaced */
902 /* mode is used, but I feel better this way.. :-) */
903 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700904 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700906 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Krzysztof Helt8503df62007-10-16 01:29:08 -0700908 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
910 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700911 /* enable display memory & CRTC I/O address for color mode */
912 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
914 tmp |= 0x40;
915 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
916 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700917 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Krzysztof Helt8503df62007-10-16 01:29:08 -0700919 /* Screen A Preset Row-Scan register */
920 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
921 /* text cursor on and start line */
922 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
923 /* text cursor end line */
924 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
926 /******************************************************
927 *
928 * 1 bpp
929 *
930 */
931
932 /* programming for different color depths */
933 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700934 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700935 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937 /* SR07 */
938 switch (cinfo->btype) {
939 case BT_SD64:
940 case BT_PICCOLO:
941 case BT_PICASSO:
942 case BT_SPECTRUM:
943 case BT_PICASSO4:
944 case BT_ALPINE:
945 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700946 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700947 regs.multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 bi->sr07_1bpp_mux : bi->sr07_1bpp);
949 break;
950
951 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 vga_wseq(regbase, CL_SEQR7,
953 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 break;
955
956 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700957 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 break;
959 }
960
961 /* Extended Sequencer Mode */
962 switch (cinfo->btype) {
963 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700964 /* setting the SEQRF on SD64 is not necessary
965 * (only during init)
966 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700967 /* MCLK select */
968 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 break;
970
971 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700972 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700973 /* ### ueberall 0x22? */
974 /* ##vorher 1c MCLK select */
975 vga_wseq(regbase, CL_SEQR1F, 0x22);
976 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
977 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 break;
979
980 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700981 /* ##vorher 22 MCLK select */
982 vga_wseq(regbase, CL_SEQR1F, 0x22);
983 /* ## vorher d0 avoid FIFO underruns..? */
984 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 break;
986
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 case BT_PICASSO4:
988 case BT_ALPINE:
989 case BT_GD5480:
990 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 /* do nothing */
992 break;
993
994 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700995 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 break;
997 }
998
Krzysztof Helt8503df62007-10-16 01:29:08 -0700999 /* pixel mask: pass-through for first plane */
1000 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001002 /* hidden dac reg: 1280x1024 */
1003 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001005 /* hidden dac: nothing */
1006 WHDR(cinfo, 0);
1007 /* memory mode: odd/even, ext. memory */
1008 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1009 /* plane mask: only write to first plane */
1010 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 offset = var->xres_virtual / 16;
1012 }
1013
1014 /******************************************************
1015 *
1016 * 8 bpp
1017 *
1018 */
1019
1020 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001021 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 switch (cinfo->btype) {
1023 case BT_SD64:
1024 case BT_PICCOLO:
1025 case BT_PICASSO:
1026 case BT_SPECTRUM:
1027 case BT_PICASSO4:
1028 case BT_ALPINE:
1029 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001030 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 regs.multiplexing ?
1032 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1033 break;
1034
1035 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001036 vga_wseq(regbase, CL_SEQR7,
1037 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 break;
1039
1040 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001041 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 break;
1043 }
1044
1045 switch (cinfo->btype) {
1046 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001047 /* MCLK select */
1048 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 break;
1050
1051 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001054 /* ### vorher 1c MCLK select */
1055 vga_wseq(regbase, CL_SEQR1F, 0x22);
1056 /* Fast Page-Mode writes */
1057 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 break;
1059
1060 case BT_PICASSO4:
1061#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001062 /* ### INCOMPLETE!! */
1063 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001065/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 break;
1067
1068 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 /* We already set SRF and SR1F */
1070 break;
1071
1072 case BT_GD5480:
1073 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 /* do nothing */
1075 break;
1076
1077 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001078 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 break;
1080 }
1081
Krzysztof Helt8503df62007-10-16 01:29:08 -07001082 /* mode register: 256 color mode */
1083 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1084 /* pixel mask: pass-through all planes */
1085 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001087 /* hidden dac reg: 1280x1024 */
1088 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001090 /* hidden dac: nothing */
1091 WHDR(cinfo, 0);
1092 /* memory mode: chain4, ext. memory */
1093 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1094 /* plane mask: enable writing to all 4 planes */
1095 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 offset = var->xres_virtual / 8;
1097 }
1098
1099 /******************************************************
1100 *
1101 * 16 bpp
1102 *
1103 */
1104
1105 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001106 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 switch (cinfo->btype) {
1108 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001109 /* Extended Sequencer Mode: 256c col. mode */
1110 vga_wseq(regbase, CL_SEQR7, 0xf7);
1111 /* MCLK select */
1112 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 break;
1114
1115 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001116 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001117 vga_wseq(regbase, CL_SEQR7, 0x87);
1118 /* Fast Page-Mode writes */
1119 vga_wseq(regbase, CL_SEQRF, 0xb0);
1120 /* MCLK select */
1121 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 break;
1123
1124 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001125 vga_wseq(regbase, CL_SEQR7, 0x27);
1126 /* Fast Page-Mode writes */
1127 vga_wseq(regbase, CL_SEQRF, 0xb0);
1128 /* MCLK select */
1129 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 break;
1131
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001133 vga_wseq(regbase, CL_SEQR7, 0x27);
1134/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 break;
1136
1137 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001138 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 break;
1140
1141 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001142 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 /* We already set SRF and SR1F */
1144 break;
1145
1146 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001147 vga_wseq(regbase, CL_SEQR7,
1148 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 break;
1150
1151 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001152 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 break;
1154 }
1155
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 /* mode register: 256 color mode */
1157 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1158 /* pixel mask: pass-through all planes */
1159 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001161 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162#elif defined(CONFIG_ZORRO)
1163 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001164 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001166 /* memory mode: chain4, ext. memory */
1167 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1168 /* plane mask: enable writing to all 4 planes */
1169 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 offset = var->xres_virtual / 4;
1171 }
1172
1173 /******************************************************
1174 *
1175 * 32 bpp
1176 *
1177 */
1178
1179 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001180 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 switch (cinfo->btype) {
1182 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001183 /* Extended Sequencer Mode: 256c col. mode */
1184 vga_wseq(regbase, CL_SEQR7, 0xf9);
1185 /* MCLK select */
1186 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 break;
1188
1189 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001190 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001191 vga_wseq(regbase, CL_SEQR7, 0x85);
1192 /* Fast Page-Mode writes */
1193 vga_wseq(regbase, CL_SEQRF, 0xb0);
1194 /* MCLK select */
1195 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 break;
1197
1198 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001199 vga_wseq(regbase, CL_SEQR7, 0x25);
1200 /* Fast Page-Mode writes */
1201 vga_wseq(regbase, CL_SEQRF, 0xb0);
1202 /* MCLK select */
1203 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 break;
1205
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001207 vga_wseq(regbase, CL_SEQR7, 0x25);
1208/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 break;
1210
1211 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001212 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 break;
1214
1215 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001216 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 /* We already set SRF and SR1F */
1218 break;
1219
1220 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001221 vga_wseq(regbase, CL_SEQR7,
1222 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 break;
1224
1225 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001226 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 break;
1228 }
1229
Krzysztof Helt8503df62007-10-16 01:29:08 -07001230 /* mode register: 256 color mode */
1231 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1232 /* pixel mask: pass-through all planes */
1233 WGen(cinfo, VGA_PEL_MSK, 0xff);
1234 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1235 WHDR(cinfo, 0xc5);
1236 /* memory mode: chain4, ext. memory */
1237 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1238 /* plane mask: enable writing to all 4 planes */
1239 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 offset = var->xres_virtual / 4;
1241 }
1242
1243 /******************************************************
1244 *
1245 * unknown/unsupported bpp
1246 *
1247 */
1248
Krzysztof Helt8503df62007-10-16 01:29:08 -07001249 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001250 dev_err(info->device,
1251 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
Krzysztof Helt8503df62007-10-16 01:29:08 -07001254 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 tmp = 0x22;
1256 if (offset & 0x100)
1257 tmp |= 0x10; /* offset overflow bit */
1258
Krzysztof Helt8503df62007-10-16 01:29:08 -07001259 /* screen start addr #16-18, fastpagemode cycles */
1260 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001262 /* screen start address bit 19 */
1263 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001264 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001266 if (cinfo->btype == BT_LAGUNA ||
1267 cinfo->btype == BT_GD5480) {
1268
1269 tmp = 0;
1270 if ((htotal + 5) & 256)
1271 tmp |= 128;
1272 if (hdispend & 256)
1273 tmp |= 64;
1274 if (hsyncstart & 256)
1275 tmp |= 48;
1276 if (vtotal & 1024)
1277 tmp |= 8;
1278 if (vdispend & 1024)
1279 tmp |= 4;
1280 if (vsyncstart & 1024)
1281 tmp |= 3;
1282
1283 vga_wcrt(regbase, CL_CRT1E, tmp);
1284 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1285 }
1286
1287
Krzysztof Helt8503df62007-10-16 01:29:08 -07001288 /* text cursor location high */
1289 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1290 /* text cursor location low */
1291 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1292 /* underline row scanline = at very bottom */
1293 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294
Krzysztof Helt8503df62007-10-16 01:29:08 -07001295 /* controller mode */
1296 vga_wattr(regbase, VGA_ATC_MODE, 1);
1297 /* overscan (border) color */
1298 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1299 /* color plane enable */
1300 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1301 /* pixel panning */
1302 vga_wattr(regbase, CL_AR33, 0);
1303 /* color select */
1304 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
1306 /* [ EGS: SetOffset(); ] */
1307 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001308 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
Krzysztof Helt8503df62007-10-16 01:29:08 -07001310 /* set/reset register */
1311 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1312 /* set/reset enable */
1313 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1314 /* color compare */
1315 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1316 /* data rotate */
1317 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1318 /* read map select */
1319 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1320 /* miscellaneous register */
1321 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1322 /* color don't care */
1323 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1324 /* bit mask */
1325 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
Krzysztof Helt8503df62007-10-16 01:29:08 -07001327 /* graphics cursor attributes: nothing special */
1328 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 /* finally, turn on everything - turn off "FullBandwidth" bit */
1331 /* also, set "DotClock%2" bit where requested */
1332 tmp = 0x01;
1333
1334/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1335 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1336 tmp |= 0x08;
1337*/
1338
Krzysztof Helt8503df62007-10-16 01:29:08 -07001339 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001340 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341
1342 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
1344 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001345 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
1347#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001348 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349#endif
1350
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 return 0;
1352}
1353
1354/* for some reason incomprehensible to me, cirrusfb requires that you write
1355 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001356static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001358 cirrusfb_set_par_foo(info);
1359 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360}
1361
Krzysztof Helt8503df62007-10-16 01:29:08 -07001362static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1363 unsigned blue, unsigned transp,
1364 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365{
1366 struct cirrusfb_info *cinfo = info->par;
1367
1368 if (regno > 255)
1369 return -EINVAL;
1370
1371 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1372 u32 v;
1373 red >>= (16 - info->var.red.length);
1374 green >>= (16 - info->var.green.length);
1375 blue >>= (16 - info->var.blue.length);
1376
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 return 1;
1379 v = (red << info->var.red.offset) |
1380 (green << info->var.green.offset) |
1381 (blue << info->var.blue.offset);
1382
Krzysztof Helt060b6002007-10-16 01:29:13 -07001383 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 return 0;
1385 }
1386
Krzysztof Helt8503df62007-10-16 01:29:08 -07001387 if (info->var.bits_per_pixel == 8)
1388 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
1390 return 0;
1391
1392}
1393
1394/*************************************************************************
1395 cirrusfb_pan_display()
1396
1397 performs display panning - provided hardware permits this
1398**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001399static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1400 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401{
1402 int xoffset = 0;
1403 int yoffset = 0;
1404 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001405 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 struct cirrusfb_info *cinfo = info->par;
1407
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001408 dev_dbg(info->device,
1409 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410
1411 /* no range checks for xoffset and yoffset, */
1412 /* as fb_pan_display has already done this */
1413 if (var->vmode & FB_VMODE_YWRAP)
1414 return -EINVAL;
1415
1416 info->var.xoffset = var->xoffset;
1417 info->var.yoffset = var->yoffset;
1418
1419 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1420 yoffset = var->yoffset;
1421
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001422 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423
1424 if (info->var.bits_per_pixel == 1) {
1425 /* base is already correct */
1426 xpix = (unsigned char) (var->xoffset % 8);
1427 } else {
1428 base /= 4;
1429 xpix = (unsigned char) ((xoffset % 4) * 2);
1430 }
1431
Krzysztof Helt8503df62007-10-16 01:29:08 -07001432 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
1434 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001435 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1436 (unsigned char) (base & 0xff));
1437 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1438 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001440 /* 0xf2 is %11110010, exclude tmp bits */
1441 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 /* construct bits 16, 17 and 18 of screen start address */
1443 if (base & 0x10000)
1444 tmp |= 0x01;
1445 if (base & 0x20000)
1446 tmp |= 0x04;
1447 if (base & 0x40000)
1448 tmp |= 0x08;
1449
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001450 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451
1452 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001453 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1454 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
Krzysztof Helt8503df62007-10-16 01:29:08 -07001456 /* write pixel panning value to AR33; this does not quite work in 8bpp
1457 *
1458 * ### Piccolo..? Will this work?
1459 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001461 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
Krzysztof Helt8503df62007-10-16 01:29:08 -07001463 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
Krzysztof Helt8503df62007-10-16 01:29:08 -07001465 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466}
1467
Krzysztof Helt8503df62007-10-16 01:29:08 -07001468static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469{
1470 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001471 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1472 * then the caller blanks by setting the CLUT (Color Look Up Table)
1473 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1474 * failed due to e.g. a video mode which doesn't support it.
1475 * Implements VESA suspend and powerdown modes on hardware that
1476 * supports disabling hsync/vsync:
1477 * blank_mode == 2: suspend vsync
1478 * blank_mode == 3: suspend hsync
1479 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 */
1481 unsigned char val;
1482 struct cirrusfb_info *cinfo = info->par;
1483 int current_mode = cinfo->blank_mode;
1484
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001485 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487 if (info->state != FBINFO_STATE_RUNNING ||
1488 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001489 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 return 0;
1491 }
1492
1493 /* Undo current */
1494 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001495 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001496 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001497 val = 0;
1498 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001499 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001500 val = 0x20;
1501
1502 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1503 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 switch (blank_mode) {
1506 case FB_BLANK_UNBLANK:
1507 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001508 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 break;
1510 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001511 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 break;
1513 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001514 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 break;
1516 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001517 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 break;
1519 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001520 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 return 1;
1522 }
1523
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001524 vga_wgfx(cinfo->regbase, CL_GRE, val);
1525
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001527 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
1529 /* Let fbcon do a soft blank for us */
1530 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1531}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001532
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533/**** END Hardware specific Routines **************************************/
1534/****************************************************************************/
1535/**** BEGIN Internal Routines ***********************************************/
1536
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001537static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001539 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 const struct cirrusfb_board_info_rec *bi;
1541
Krzysztof Helt8503df62007-10-16 01:29:08 -07001542 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543
1544 bi = &cirrusfb_board_info[cinfo->btype];
1545
1546 /* reset board globally */
1547 switch (cinfo->btype) {
1548 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001549 WSFR(cinfo, 0x01);
1550 udelay(500);
1551 WSFR(cinfo, 0x51);
1552 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 break;
1554 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 WSFR2(cinfo, 0xff);
1556 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 break;
1558 case BT_SD64:
1559 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001560 WSFR(cinfo, 0x1f);
1561 udelay(500);
1562 WSFR(cinfo, 0x4f);
1563 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 break;
1565 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001566 /* disable flickerfixer */
1567 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1568 mdelay(100);
1569 /* from Klaus' NetBSD driver: */
1570 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1571 /* put blitter into 542x compat */
1572 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1573 /* mode */
1574 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 break;
1576
1577 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001578 /* from Klaus' NetBSD driver: */
1579 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 break;
1581
1582 case BT_ALPINE:
1583 /* Nothing to do to reset the board. */
1584 break;
1585
1586 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001587 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 break;
1589 }
1590
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001591 /* make sure RAM size set by this point */
1592 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593
1594 /* the P4 is not fully initialized here; I rely on it having been */
1595 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001596 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
1598 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001599 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1600 WGen(cinfo, CL_POS102, 0x01);
1601 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001604 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
Krzysztof Helt8503df62007-10-16 01:29:08 -07001606 /* reset sequencer logic */
1607 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608
Krzysztof Helt8503df62007-10-16 01:29:08 -07001609 /* FullBandwidth (video off) and 8/9 dot clock */
1610 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1611 /* polarity (-/-), disable access to display memory,
1612 * VGA_CRTC_START_HI base address: color
1613 */
1614 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 /* "magic cookie" - doesn't make any sense to me.. */
1617/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1618 /* unlock all extension registers */
1619 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
Krzysztof Helt8503df62007-10-16 01:29:08 -07001621 /* reset blitter */
1622 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623
1624 switch (cinfo->btype) {
1625 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001626 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 break;
1628 case BT_ALPINE:
1629 break;
1630 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001631 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 break;
1633 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001634 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1635 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 break;
1637 }
1638 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001639 /* plane mask: nothing */
1640 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1641 /* character map select: doesn't even matter in gx mode */
1642 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1643 /* memory mode: chain-4, no odd/even, ext. memory */
1644 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
1646 /* controller-internal base address of video memory */
1647 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001648 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1651 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1654 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1655 /* graphics cursor Y position (..."... ) */
1656 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1657 /* graphics cursor attributes */
1658 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1659 /* graphics cursor pattern address */
1660 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661
1662 /* writing these on a P4 might give problems.. */
1663 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 /* configuration readback and ext. color */
1665 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1666 /* signature generator */
1667 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 }
1669
1670 /* MCLK select etc. */
1671 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001672 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
Krzysztof Helt8503df62007-10-16 01:29:08 -07001674 /* Screen A preset row scan: none */
1675 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1676 /* Text cursor start: disable text cursor */
1677 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1678 /* Text cursor end: - */
1679 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1680 /* Screen start address high: 0 */
1681 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1682 /* Screen start address low: 0 */
1683 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1684 /* text cursor location high: 0 */
1685 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1686 /* text cursor location low: 0 */
1687 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 /* Underline Row scanline: - */
1690 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1691 /* mode control: timing enable, byte mode, no compat modes */
1692 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1693 /* Line Compare: not needed */
1694 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001696 /* ext. display controls: ext.adr. wrap */
1697 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698
Krzysztof Helt8503df62007-10-16 01:29:08 -07001699 /* Set/Reset registes: - */
1700 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1701 /* Set/Reset enable: - */
1702 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1703 /* Color Compare: - */
1704 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1705 /* Data Rotate: - */
1706 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1707 /* Read Map Select: - */
1708 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1709 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1710 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1711 /* Miscellaneous: memory map base address, graphics mode */
1712 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1713 /* Color Don't care: involve all planes */
1714 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1715 /* Bit Mask: no mask at all */
1716 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001718 /* (5434 can't have bit 3 set for bitblt) */
1719 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 /* Graphics controller mode extensions: finer granularity,
1722 * 8byte data latches
1723 */
1724 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725
Krzysztof Helt8503df62007-10-16 01:29:08 -07001726 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1727 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1728 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1729 /* Background color byte 1: - */
1730 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1731 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732
Krzysztof Helt8503df62007-10-16 01:29:08 -07001733 /* Attribute Controller palette registers: "identity mapping" */
1734 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1735 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1736 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1737 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1738 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1739 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1740 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1741 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1742 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1743 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1744 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1745 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1746 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1747 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1748 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1749 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751 /* Attribute Controller mode: graphics mode */
1752 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1753 /* Overscan color reg.: reg. 0 */
1754 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1755 /* Color Plane enable: Enable all 4 planes */
1756 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1757/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1758 /* Color Select: - */
1759 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760
Krzysztof Helt8503df62007-10-16 01:29:08 -07001761 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762
1763 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001764 /* polarity (-/-), enable display mem,
1765 * VGA_CRTC_START_HI i/o base = color
1766 */
1767 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768
Krzysztof Helt8503df62007-10-16 01:29:08 -07001769 /* BLT Start/status: Blitter reset */
1770 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1771 /* - " - : "end-of-reset" */
1772 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773
1774 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001775 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 return;
1777}
1778
Krzysztof Helt8503df62007-10-16 01:29:08 -07001779static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780{
1781#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1782 static int IsOn = 0; /* XXX not ok for multiple boards */
1783
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 if (cinfo->btype == BT_PICASSO4)
1785 return; /* nothing to switch */
1786 if (cinfo->btype == BT_ALPINE)
1787 return; /* nothing to switch */
1788 if (cinfo->btype == BT_GD5480)
1789 return; /* nothing to switch */
1790 if (cinfo->btype == BT_PICASSO) {
1791 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001792 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 return;
1794 }
1795 if (on) {
1796 switch (cinfo->btype) {
1797 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001798 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 break;
1800 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001801 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 break;
1803 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001804 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 break;
1806 default: /* do nothing */ break;
1807 }
1808 } else {
1809 switch (cinfo->btype) {
1810 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001811 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812 break;
1813 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001814 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 break;
1816 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001817 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001819 default: /* do nothing */
1820 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 }
1822 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823#endif /* CONFIG_ZORRO */
1824}
1825
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826/******************************************/
1827/* Linux 2.6-style accelerated functions */
1828/******************************************/
1829
Krzysztof Helt8503df62007-10-16 01:29:08 -07001830static void cirrusfb_fillrect(struct fb_info *info,
1831 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 struct fb_fillrect modded;
1834 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001835 struct cirrusfb_info *cinfo = info->par;
1836 int m = info->var.bits_per_pixel;
1837 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1838 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839
1840 if (info->state != FBINFO_STATE_RUNNING)
1841 return;
1842 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1843 cfb_fillrect(info, region);
1844 return;
1845 }
1846
1847 vxres = info->var.xres_virtual;
1848 vyres = info->var.yres_virtual;
1849
1850 memcpy(&modded, region, sizeof(struct fb_fillrect));
1851
Krzysztof Helt8503df62007-10-16 01:29:08 -07001852 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 modded.dx >= vxres || modded.dy >= vyres)
1854 return;
1855
Krzysztof Helt8503df62007-10-16 01:29:08 -07001856 if (modded.dx + modded.width > vxres)
1857 modded.width = vxres - modded.dx;
1858 if (modded.dy + modded.height > vyres)
1859 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
Krzysztof Helt060b6002007-10-16 01:29:13 -07001861 cirrusfb_RectFill(cinfo->regbase,
1862 info->var.bits_per_pixel,
1863 (region->dx * m) / 8, region->dy,
1864 (region->width * m) / 8, region->height,
1865 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001866 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867}
1868
Krzysztof Helt8503df62007-10-16 01:29:08 -07001869static void cirrusfb_copyarea(struct fb_info *info,
1870 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 struct fb_copyarea modded;
1873 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001874 struct cirrusfb_info *cinfo = info->par;
1875 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876
1877 if (info->state != FBINFO_STATE_RUNNING)
1878 return;
1879 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1880 cfb_copyarea(info, area);
1881 return;
1882 }
1883
1884 vxres = info->var.xres_virtual;
1885 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001886 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887
Krzysztof Helt8503df62007-10-16 01:29:08 -07001888 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 modded.sx >= vxres || modded.sy >= vyres ||
1890 modded.dx >= vxres || modded.dy >= vyres)
1891 return;
1892
Krzysztof Helt8503df62007-10-16 01:29:08 -07001893 if (modded.sx + modded.width > vxres)
1894 modded.width = vxres - modded.sx;
1895 if (modded.dx + modded.width > vxres)
1896 modded.width = vxres - modded.dx;
1897 if (modded.sy + modded.height > vyres)
1898 modded.height = vyres - modded.sy;
1899 if (modded.dy + modded.height > vyres)
1900 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901
Krzysztof Helt060b6002007-10-16 01:29:13 -07001902 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1903 (area->sx * m) / 8, area->sy,
1904 (area->dx * m) / 8, area->dy,
1905 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001906 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001907
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908}
1909
Krzysztof Helt8503df62007-10-16 01:29:08 -07001910static void cirrusfb_imageblit(struct fb_info *info,
1911 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912{
1913 struct cirrusfb_info *cinfo = info->par;
1914
Krzysztof Helt8503df62007-10-16 01:29:08 -07001915 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 cfb_imageblit(info, image);
1917}
1918
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919#ifdef CONFIG_PPC_PREP
1920#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1921#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001922static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 *display = PREP_VIDEO_BASE;
1925 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926}
1927
1928#endif /* CONFIG_PPC_PREP */
1929
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001931static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932
1933/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1934 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1935 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1936 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001937static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1938 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
1940 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001941 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001943 if (cinfo->btype == BT_LAGUNA) {
1944 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1945
1946 mem = ((SR14 & 7) + 1) << 20;
1947 } else {
1948 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1949 switch ((SRF & 0x18)) {
1950 case 0x08:
1951 mem = 512 * 1024;
1952 break;
1953 case 0x10:
1954 mem = 1024 * 1024;
1955 break;
1956 /* 64-bit DRAM data bus width; assume 2MB.
1957 * Also indicates 2MB memory on the 5430.
1958 */
1959 case 0x18:
1960 mem = 2048 * 1024;
1961 break;
1962 default:
1963 dev_warn(info->device, "Unknown memory size!\n");
1964 mem = 1024 * 1024;
1965 }
1966 /* If DRAM bank switching is enabled, there must be
1967 * twice as much memory installed. (4MB on the 5434)
1968 */
1969 if (SRF & 0x80)
1970 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001972
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 return mem;
1975}
1976
Krzysztof Helt8503df62007-10-16 01:29:08 -07001977static void get_pci_addrs(const struct pci_dev *pdev,
1978 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001980 assert(pdev != NULL);
1981 assert(display != NULL);
1982 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 *display = 0;
1985 *registers = 0;
1986
1987 /* This is a best-guess for now */
1988
1989 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1990 *display = pci_resource_start(pdev, 1);
1991 *registers = pci_resource_start(pdev, 0);
1992 } else {
1993 *display = pci_resource_start(pdev, 0);
1994 *registers = pci_resource_start(pdev, 1);
1995 }
1996
Krzysztof Helt8503df62007-10-16 01:29:08 -07001997 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998}
1999
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002000static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002002 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002004 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005#if 0 /* if system didn't claim this region, we would... */
2006 release_mem_region(0xA0000, 65535);
2007#endif
2008 if (release_io_ports)
2009 release_region(0x3C0, 32);
2010 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011}
2012#endif /* CONFIG_PCI */
2013
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00002015static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016{
Al Virod91f5bb2007-10-17 00:27:18 +01002017 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002018 struct zorro_dev *zdev = to_zorro_dev(info->device);
2019
2020 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021
2022 if (cinfo->btype == BT_PICASSO4) {
2023 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002024 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002025 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002027 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002028 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030}
2031#endif /* CONFIG_ZORRO */
2032
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002033static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002035 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 struct fb_var_screeninfo *var = &info->var;
2037
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038 info->pseudo_palette = cinfo->pseudo_palette;
2039 info->flags = FBINFO_DEFAULT
2040 | FBINFO_HWACCEL_XPAN
2041 | FBINFO_HWACCEL_YPAN
2042 | FBINFO_HWACCEL_FILLRECT
2043 | FBINFO_HWACCEL_COPYAREA;
2044 if (noaccel)
2045 info->flags |= FBINFO_HWACCEL_DISABLED;
2046 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 if (cinfo->btype == BT_GD5480) {
2048 if (var->bits_per_pixel == 16)
2049 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002050 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 info->screen_base += 2 * MB_;
2052 }
2053
2054 /* Fill fix common fields */
2055 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2056 sizeof(info->fix.id));
2057
2058 /* monochrome: only 1 memory plane */
2059 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002060 info->fix.smem_len = info->screen_size;
2061 if (var->bits_per_pixel == 1)
2062 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 info->fix.xpanstep = 1;
2065 info->fix.ypanstep = 1;
2066 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067
2068 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 info->fix.mmio_len = 0;
2070 info->fix.accel = FB_ACCEL_NONE;
2071
2072 fb_alloc_cmap(&info->cmap, 256, 0);
2073
2074 return 0;
2075}
2076
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002077static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002079 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002081 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 btype = cinfo->btype;
2084
2085 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002086 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002088 /* set all the vital stuff */
2089 cirrusfb_set_fbinfo(info);
2090
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002091 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002093 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2094 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002095 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002096 err = -EINVAL;
2097 goto err_dealloc_cmap;
2098 }
2099
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 info->var.activate = FB_ACTIVATE_NOW;
2101
2102 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2103 if (err < 0) {
2104 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002105 dev_dbg(info->device,
2106 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002107 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 }
2109
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 err = register_framebuffer(info);
2111 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002112 dev_err(info->device,
2113 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 goto err_dealloc_cmap;
2115 }
2116
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 return 0;
2118
2119err_dealloc_cmap:
2120 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002121 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002122 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 return err;
2124}
2125
Krzysztof Helt8503df62007-10-16 01:29:08 -07002126static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127{
2128 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129
Krzysztof Helt8503df62007-10-16 01:29:08 -07002130 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002131 unregister_framebuffer(info);
2132 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002133 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002134 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002135 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136}
2137
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002139static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2140 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141{
2142 struct cirrusfb_info *cinfo;
2143 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002144 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 unsigned long board_addr, board_size;
2146 int ret;
2147
2148 ret = pci_enable_device(pdev);
2149 if (ret < 0) {
2150 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2151 goto err_out;
2152 }
2153
2154 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2155 if (!info) {
2156 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2157 ret = -ENOMEM;
2158 goto err_disable;
2159 }
2160
2161 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002162 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002164 dev_dbg(info->device,
2165 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
2166 (unsigned long long)pdev->resource[0].start, btype);
2167 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2168 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169
Krzysztof Helt8503df62007-10-16 01:29:08 -07002170 if (isPReP) {
2171 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002173 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002175 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002176 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002178 dev_dbg(info->device,
2179 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002180 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002181 /* FIXME: this forces VGA. alternatives? */
2182 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 }
2184
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002185 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002186 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187
2188 board_size = (btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002189 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190
2191 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002192 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002193 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2194 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 goto err_release_fb;
2196 }
2197#if 0 /* if the system didn't claim this region, we would... */
2198 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002199 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2200 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 ret = -EBUSY;
2202 goto err_release_regions;
2203 }
2204#endif
2205 if (request_region(0x3C0, 32, "cirrusfb"))
2206 release_io_ports = 1;
2207
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002208 info->screen_base = ioremap(board_addr, board_size);
2209 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210 ret = -EIO;
2211 goto err_release_legacy;
2212 }
2213
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002214 info->fix.smem_start = board_addr;
2215 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216 cinfo->unmap = cirrusfb_pci_unmap;
2217
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002218 dev_info(info->device,
2219 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2220 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 pci_set_drvdata(pdev, info);
2222
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002223 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002224 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002225 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002226 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227
2228err_release_legacy:
2229 if (release_io_ports)
2230 release_region(0x3C0, 32);
2231#if 0
2232 release_mem_region(0xA0000, 65535);
2233err_release_regions:
2234#endif
2235 pci_release_regions(pdev);
2236err_release_fb:
2237 framebuffer_release(info);
2238err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239err_out:
2240 return ret;
2241}
2242
Krzysztof Helt8503df62007-10-16 01:29:08 -07002243static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244{
2245 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246
Krzysztof Helt8503df62007-10-16 01:29:08 -07002247 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248}
2249
2250static struct pci_driver cirrusfb_pci_driver = {
2251 .name = "cirrusfb",
2252 .id_table = cirrusfb_pci_table,
2253 .probe = cirrusfb_pci_register,
2254 .remove = __devexit_p(cirrusfb_pci_unregister),
2255#ifdef CONFIG_PM
2256#if 0
2257 .suspend = cirrusfb_pci_suspend,
2258 .resume = cirrusfb_pci_resume,
2259#endif
2260#endif
2261};
2262#endif /* CONFIG_PCI */
2263
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002265static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2266 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267{
2268 struct cirrusfb_info *cinfo;
2269 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002270 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 struct zorro_dev *z2 = NULL;
2272 unsigned long board_addr, board_size, size;
2273 int ret;
2274
2275 btype = ent->driver_data;
2276 if (cirrusfb_zorro_table2[btype].id2)
2277 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2278 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
2280 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2281 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002282 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 ret = -ENOMEM;
2284 goto err_out;
2285 }
2286
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002287 dev_info(info->device, "%s board detected\n",
2288 cirrusfb_board_info[btype].name);
2289
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 cinfo->btype = btype;
2292
Al Viro36ea96a2007-10-27 19:46:58 +01002293 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002294 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 board_addr = zorro_resource_start(z);
2297 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002298 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299
2300 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002301 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2302 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 ret = -EBUSY;
2304 goto err_release_fb;
2305 }
2306
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 ret = -EIO;
2308
2309 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002310 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311
2312 /* To be precise, for the P4 this is not the */
2313 /* begin of the board, but the begin of RAM. */
2314 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2315 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002316 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 if (!cinfo->regbase)
2318 goto err_release_region;
2319
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002320 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002321 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002323 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002325 info->fix.smem_start = board_addr + 16777216;
2326 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2327 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 goto err_unmap_regbase;
2329 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002330 dev_info(info->device, " REG at $%lx\n",
2331 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002333 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002335 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002337 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2338 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339 goto err_release_region;
2340
2341 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002342 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002343 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002345 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002346 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347 }
2348 cinfo->unmap = cirrusfb_zorro_unmap;
2349
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002350 dev_info(info->device,
2351 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2352 board_size / MB_, board_addr);
2353
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 zorro_set_drvdata(z, info);
2355
Al Virod91f5bb2007-10-17 00:27:18 +01002356 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002357 if (ret) {
2358 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002359 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002360 iounmap(cinfo->regbase - 0x600000);
2361 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002362 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002363 }
2364 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365
2366err_unmap_regbase:
2367 /* Parental advisory: explicit hack */
2368 iounmap(cinfo->regbase - 0x600000);
2369err_release_region:
2370 release_region(board_addr, board_size);
2371err_release_fb:
2372 framebuffer_release(info);
2373err_out:
2374 return ret;
2375}
2376
2377void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2378{
2379 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380
Krzysztof Helt8503df62007-10-16 01:29:08 -07002381 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382}
2383
2384static struct zorro_driver cirrusfb_zorro_driver = {
2385 .name = "cirrusfb",
2386 .id_table = cirrusfb_zorro_table,
2387 .probe = cirrusfb_zorro_register,
2388 .remove = __devexit_p(cirrusfb_zorro_unregister),
2389};
2390#endif /* CONFIG_ZORRO */
2391
2392static int __init cirrusfb_init(void)
2393{
2394 int error = 0;
2395
2396#ifndef MODULE
2397 char *option = NULL;
2398
2399 if (fb_get_options("cirrusfb", &option))
2400 return -ENODEV;
2401 cirrusfb_setup(option);
2402#endif
2403
2404#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002405 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406#endif
2407#ifdef CONFIG_PCI
2408 error |= pci_register_driver(&cirrusfb_pci_driver);
2409#endif
2410 return error;
2411}
2412
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002414static int __init cirrusfb_setup(char *options)
2415{
Vlada Pericee119402008-11-19 15:36:45 -08002416 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418 if (!options || !*options)
2419 return 0;
2420
Krzysztof Helt8503df62007-10-16 01:29:08 -07002421 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002422 if (!*this_opt)
2423 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425 if (!strcmp(this_opt, "noaccel"))
2426 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002427 else if (!strncmp(this_opt, "mode:", 5))
2428 mode_option = this_opt + 5;
2429 else
2430 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 }
2432 return 0;
2433}
2434#endif
2435
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 /*
2437 * Modularization
2438 */
2439
2440MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2441MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2442MODULE_LICENSE("GPL");
2443
Krzysztof Helt8503df62007-10-16 01:29:08 -07002444static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445{
2446#ifdef CONFIG_PCI
2447 pci_unregister_driver(&cirrusfb_pci_driver);
2448#endif
2449#ifdef CONFIG_ZORRO
2450 zorro_unregister_driver(&cirrusfb_zorro_driver);
2451#endif
2452}
2453
2454module_init(cirrusfb_init);
2455
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002456module_param(mode_option, charp, 0);
2457MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002458module_param(noaccel, bool, 0);
2459MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002460
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461#ifdef MODULE
2462module_exit(cirrusfb_exit);
2463#endif
2464
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465/**********************************************************************/
2466/* about the following functions - I have used the same names for the */
2467/* functions as Markus Wild did in his Retina driver for NetBSD as */
2468/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470/**********************************************************************/
2471
2472/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 int regnum, unsigned char val)
2475{
2476 unsigned long regofs = 0;
2477
2478 if (cinfo->btype == BT_PICASSO) {
2479 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2481 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2483 regofs = 0xfff;
2484 }
2485
Krzysztof Helt8503df62007-10-16 01:29:08 -07002486 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487}
2488
2489/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002490static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491{
2492 unsigned long regofs = 0;
2493
2494 if (cinfo->btype == BT_PICASSO) {
2495 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2497 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2499 regofs = 0xfff;
2500 }
2501
Krzysztof Helt8503df62007-10-16 01:29:08 -07002502 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503}
2504
2505/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002506static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509
Krzysztof Helt8503df62007-10-16 01:29:08 -07002510 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511 /* if we're just in "write value" mode, write back the */
2512 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 vga_w(cinfo->regbase, VGA_ATT_IW,
2514 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 }
2516 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002517/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2518 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519
2520 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522}
2523
2524/*** WHDR() - write into the Hidden DAC register ***/
2525/* as the HDR is the only extension register that requires special treatment
2526 * (the other extension registers are accessible just like the "ordinary"
2527 * registers of their functional group) here is a specialized routine for
2528 * accessing the HDR
2529 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002530static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531{
2532 unsigned char dummy;
2533
2534 if (cinfo->btype == BT_PICASSO) {
2535 /* Klaus' hint for correct access to HDR on some boards */
2536 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537 WGen(cinfo, VGA_PEL_MSK, 0x00);
2538 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540 dummy = RGen(cinfo, VGA_PEL_IW);
2541 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 }
2543 /* now do the usual stuff to access the HDR */
2544
Krzysztof Helt8503df62007-10-16 01:29:08 -07002545 dummy = RGen(cinfo, VGA_PEL_MSK);
2546 udelay(200);
2547 dummy = RGen(cinfo, VGA_PEL_MSK);
2548 udelay(200);
2549 dummy = RGen(cinfo, VGA_PEL_MSK);
2550 udelay(200);
2551 dummy = RGen(cinfo, VGA_PEL_MSK);
2552 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554 WGen(cinfo, VGA_PEL_MSK, val);
2555 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556
2557 if (cinfo->btype == BT_PICASSO) {
2558 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002559 dummy = RGen(cinfo, VGA_PEL_IW);
2560 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561
2562 /* and at the end, restore the mask value */
2563 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002564 WGen(cinfo, VGA_PEL_MSK, 0xff);
2565 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 }
2567}
2568
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002570static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571{
2572#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002575 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576#endif
2577}
2578
2579/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002580static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581{
2582#ifdef CONFIG_ZORRO
2583 /* writing an arbitrary value to this one causes the monitor switcher */
2584 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002585 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002587 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588#endif
2589}
2590
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002592static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 unsigned char green, unsigned char blue)
2594{
2595 unsigned int data = VGA_PEL_D;
2596
2597 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002598 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599
2600 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2601 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2602 /* but DAC data register IS, at least for Picasso II */
2603 if (cinfo->btype == BT_PICASSO)
2604 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002605 vga_w(cinfo->regbase, data, red);
2606 vga_w(cinfo->regbase, data, green);
2607 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609 vga_w(cinfo->regbase, data, blue);
2610 vga_w(cinfo->regbase, data, green);
2611 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612 }
2613}
2614
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615#if 0
2616/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618 unsigned char *green, unsigned char *blue)
2619{
2620 unsigned int data = VGA_PEL_D;
2621
Krzysztof Helt8503df62007-10-16 01:29:08 -07002622 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623
2624 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2625 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2626 if (cinfo->btype == BT_PICASSO)
2627 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002628 *red = vga_r(cinfo->regbase, data);
2629 *green = vga_r(cinfo->regbase, data);
2630 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002632 *blue = vga_r(cinfo->regbase, data);
2633 *green = vga_r(cinfo->regbase, data);
2634 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635 }
2636}
2637#endif
2638
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639/*******************************************************************
2640 cirrusfb_WaitBLT()
2641
2642 Wait for the BitBLT engine to complete a possible earlier job
2643*********************************************************************/
2644
2645/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002646static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647{
2648 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002649 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650 /* do nothing */ ;
2651}
2652
2653/*******************************************************************
2654 cirrusfb_BitBLT()
2655
2656 perform accelerated "scrolling"
2657********************************************************************/
2658
Krzysztof Helt8503df62007-10-16 01:29:08 -07002659static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2660 u_short curx, u_short cury,
2661 u_short destx, u_short desty,
2662 u_short width, u_short height,
2663 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664{
2665 u_short nwidth, nheight;
2666 u_long nsrc, ndest;
2667 u_char bltmode;
2668
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 nwidth = width - 1;
2670 nheight = height - 1;
2671
2672 bltmode = 0x00;
2673 /* if source adr < dest addr, do the Blt backwards */
2674 if (cury <= desty) {
2675 if (cury == desty) {
2676 /* if src and dest are on the same line, check x */
2677 if (curx < destx)
2678 bltmode |= 0x01;
2679 } else
2680 bltmode |= 0x01;
2681 }
2682 if (!bltmode) {
2683 /* standard case: forward blitting */
2684 nsrc = (cury * line_length) + curx;
2685 ndest = (desty * line_length) + destx;
2686 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002687 /* this means start addresses are at the end,
2688 * counting backwards
2689 */
2690 nsrc = cury * line_length + curx +
2691 nheight * line_length + nwidth;
2692 ndest = desty * line_length + destx +
2693 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 }
2695
2696 /*
2697 run-down of registers to be programmed:
2698 destination pitch
2699 source pitch
2700 BLT width/height
2701 source start
2702 destination start
2703 BLT mode
2704 BLT ROP
2705 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2706 start/stop
2707 */
2708
Krzysztof Helt8503df62007-10-16 01:29:08 -07002709 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710
2711 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002712 /* dest pitch low */
2713 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2714 /* dest pitch hi */
2715 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2716 /* source pitch low */
2717 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2718 /* source pitch hi */
2719 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720
2721 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002722 /* BLT width low */
2723 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2724 /* BLT width hi */
2725 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726
2727 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002728 /* BLT height low */
2729 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2730 /* BLT width hi */
2731 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732
2733 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002734 /* BLT dest low */
2735 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2736 /* BLT dest mid */
2737 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2738 /* BLT dest hi */
2739 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740
2741 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002742 /* BLT src low */
2743 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2744 /* BLT src mid */
2745 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2746 /* BLT src hi */
2747 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748
2749 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002750 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751
2752 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002753 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754
2755 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002756 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757}
2758
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759/*******************************************************************
2760 cirrusfb_RectFill()
2761
2762 perform accelerated rectangle fill
2763********************************************************************/
2764
Krzysztof Helt8503df62007-10-16 01:29:08 -07002765static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 u_short x, u_short y, u_short width, u_short height,
2767 u_char color, u_short line_length)
2768{
2769 u_short nwidth, nheight;
2770 u_long ndest;
2771 u_char op;
2772
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 nwidth = width - 1;
2774 nheight = height - 1;
2775
2776 ndest = (y * line_length) + x;
2777
Krzysztof Helt8503df62007-10-16 01:29:08 -07002778 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779
2780 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002781 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2782 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2783 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2784 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785
2786 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002787 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2788 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789
2790 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002791 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2792 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793
2794 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002795 /* BLT dest low */
2796 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2797 /* BLT dest mid */
2798 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2799 /* BLT dest hi */
2800 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801
2802 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002803 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2804 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2805 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806
2807 /* This is a ColorExpand Blt, using the */
2808 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002809 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2810 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811
2812 op = 0xc0;
2813 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002814 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2815 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 op = 0x50;
2817 op = 0xd0;
2818 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002819 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2820 vga_wgfx(regbase, CL_GR11, color); /* background color */
2821 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2822 vga_wgfx(regbase, CL_GR13, color); /* background color */
2823 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2824 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 op = 0x50;
2826 op = 0xf0;
2827 }
2828 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002829 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830
2831 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002832 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833
2834 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002835 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836}
2837
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838/**************************************************************************
2839 * bestclock() - determine closest possible clock lower(?) than the
2840 * desired pixel clock
2841 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002842static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002844 int n, d;
2845 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847 assert(nom != NULL);
2848 assert(den != NULL);
2849 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850
2851 *nom = 0;
2852 *den = 0;
2853 *div = 0;
2854
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 if (freq < 8000)
2856 freq = 8000;
2857
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002858 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859
2860 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002861 int s = 0;
2862
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002863 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002865 int temp = d;
2866
2867 if (temp > 31) {
2868 s = 1;
2869 temp >>= 1;
2870 }
2871 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002872 h = h > freq ? h - freq : freq - h;
2873 if (h < diff) {
2874 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002876 *den = temp;
2877 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878 }
2879 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002880 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002882 if (d > 31) {
2883 s = 1;
2884 d >>= 1;
2885 }
2886 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002887 h = h > freq ? h - freq : freq - h;
2888 if (h < diff) {
2889 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002891 *den = d;
2892 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 }
2894 }
2895 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896}
2897
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898/* -------------------------------------------------------------------------
2899 *
2900 * debugging functions
2901 *
2902 * -------------------------------------------------------------------------
2903 */
2904
2905#ifdef CIRRUSFB_DEBUG
2906
2907/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908 * cirrusfb_dbg_print_regs
2909 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2910 * @reg_class: type of registers to read: %CRT, or %SEQ
2911 *
2912 * DESCRIPTION:
2913 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2914 * old-style I/O ports are queried for information, otherwise MMIO is
2915 * used at the given @base address to query the information.
2916 */
2917
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002918static void cirrusfb_dbg_print_regs(struct fb_info *info,
2919 caddr_t regbase,
2920 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921{
2922 va_list list;
2923 unsigned char val = 0;
2924 unsigned reg;
2925 char *name;
2926
Krzysztof Helt8503df62007-10-16 01:29:08 -07002927 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928
Krzysztof Helt8503df62007-10-16 01:29:08 -07002929 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002931 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932
2933 switch (reg_class) {
2934 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002935 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936 break;
2937 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002938 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 break;
2940 default:
2941 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002942 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 break;
2944 }
2945
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002946 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947
Krzysztof Helt8503df62007-10-16 01:29:08 -07002948 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 }
2950
Krzysztof Helt8503df62007-10-16 01:29:08 -07002951 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952}
2953
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002955 * cirrusfb_dbg_reg_dump
2956 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2957 *
2958 * DESCRIPTION:
2959 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2960 * old-style I/O ports are queried for information, otherwise MMIO is
2961 * used at the given @base address to query the information.
2962 */
2963
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002964static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002965{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002966 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002968 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969 "CR00", 0x00,
2970 "CR01", 0x01,
2971 "CR02", 0x02,
2972 "CR03", 0x03,
2973 "CR04", 0x04,
2974 "CR05", 0x05,
2975 "CR06", 0x06,
2976 "CR07", 0x07,
2977 "CR08", 0x08,
2978 "CR09", 0x09,
2979 "CR0A", 0x0A,
2980 "CR0B", 0x0B,
2981 "CR0C", 0x0C,
2982 "CR0D", 0x0D,
2983 "CR0E", 0x0E,
2984 "CR0F", 0x0F,
2985 "CR10", 0x10,
2986 "CR11", 0x11,
2987 "CR12", 0x12,
2988 "CR13", 0x13,
2989 "CR14", 0x14,
2990 "CR15", 0x15,
2991 "CR16", 0x16,
2992 "CR17", 0x17,
2993 "CR18", 0x18,
2994 "CR22", 0x22,
2995 "CR24", 0x24,
2996 "CR26", 0x26,
2997 "CR2D", 0x2D,
2998 "CR2E", 0x2E,
2999 "CR2F", 0x2F,
3000 "CR30", 0x30,
3001 "CR31", 0x31,
3002 "CR32", 0x32,
3003 "CR33", 0x33,
3004 "CR34", 0x34,
3005 "CR35", 0x35,
3006 "CR36", 0x36,
3007 "CR37", 0x37,
3008 "CR38", 0x38,
3009 "CR39", 0x39,
3010 "CR3A", 0x3A,
3011 "CR3B", 0x3B,
3012 "CR3C", 0x3C,
3013 "CR3D", 0x3D,
3014 "CR3E", 0x3E,
3015 "CR3F", 0x3F,
3016 NULL);
3017
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003018 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003020 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003022 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003023 "SR00", 0x00,
3024 "SR01", 0x01,
3025 "SR02", 0x02,
3026 "SR03", 0x03,
3027 "SR04", 0x04,
3028 "SR08", 0x08,
3029 "SR09", 0x09,
3030 "SR0A", 0x0A,
3031 "SR0B", 0x0B,
3032 "SR0D", 0x0D,
3033 "SR10", 0x10,
3034 "SR11", 0x11,
3035 "SR12", 0x12,
3036 "SR13", 0x13,
3037 "SR14", 0x14,
3038 "SR15", 0x15,
3039 "SR16", 0x16,
3040 "SR17", 0x17,
3041 "SR18", 0x18,
3042 "SR19", 0x19,
3043 "SR1A", 0x1A,
3044 "SR1B", 0x1B,
3045 "SR1C", 0x1C,
3046 "SR1D", 0x1D,
3047 "SR1E", 0x1E,
3048 "SR1F", 0x1F,
3049 NULL);
3050
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003051 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052}
3053
3054#endif /* CIRRUSFB_DEBUG */
3055