blob: dd09bae910f0c415f689929a5316cc80cd4dc2ac [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
1262 if (cinfo->btype == BT_SD64 ||
1263 cinfo->btype == BT_PICASSO4 ||
1264 cinfo->btype == BT_ALPINE ||
1265 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001266 /* screen start address bit 19 */
1267 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
Krzysztof Helt8503df62007-10-16 01:29:08 -07001269 /* text cursor location high */
1270 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1271 /* text cursor location low */
1272 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1273 /* underline row scanline = at very bottom */
1274 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Krzysztof Helt8503df62007-10-16 01:29:08 -07001276 /* controller mode */
1277 vga_wattr(regbase, VGA_ATC_MODE, 1);
1278 /* overscan (border) color */
1279 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1280 /* color plane enable */
1281 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1282 /* pixel panning */
1283 vga_wattr(regbase, CL_AR33, 0);
1284 /* color select */
1285 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
1287 /* [ EGS: SetOffset(); ] */
1288 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001289 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 /* set/reset register */
1292 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1293 /* set/reset enable */
1294 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1295 /* color compare */
1296 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1297 /* data rotate */
1298 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1299 /* read map select */
1300 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1301 /* miscellaneous register */
1302 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1303 /* color don't care */
1304 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1305 /* bit mask */
1306 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307
Krzysztof Helt8503df62007-10-16 01:29:08 -07001308 /* graphics cursor attributes: nothing special */
1309 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
1311 /* finally, turn on everything - turn off "FullBandwidth" bit */
1312 /* also, set "DotClock%2" bit where requested */
1313 tmp = 0x01;
1314
1315/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1316 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1317 tmp |= 0x08;
1318*/
1319
Krzysztof Helt8503df62007-10-16 01:29:08 -07001320 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001321 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324
1325 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001326 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
1328#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001329 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330#endif
1331
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 return 0;
1333}
1334
1335/* for some reason incomprehensible to me, cirrusfb requires that you write
1336 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001337static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001339 cirrusfb_set_par_foo(info);
1340 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341}
1342
Krzysztof Helt8503df62007-10-16 01:29:08 -07001343static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1344 unsigned blue, unsigned transp,
1345 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346{
1347 struct cirrusfb_info *cinfo = info->par;
1348
1349 if (regno > 255)
1350 return -EINVAL;
1351
1352 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1353 u32 v;
1354 red >>= (16 - info->var.red.length);
1355 green >>= (16 - info->var.green.length);
1356 blue >>= (16 - info->var.blue.length);
1357
Krzysztof Helt8503df62007-10-16 01:29:08 -07001358 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 return 1;
1360 v = (red << info->var.red.offset) |
1361 (green << info->var.green.offset) |
1362 (blue << info->var.blue.offset);
1363
Krzysztof Helt060b6002007-10-16 01:29:13 -07001364 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 return 0;
1366 }
1367
Krzysztof Helt8503df62007-10-16 01:29:08 -07001368 if (info->var.bits_per_pixel == 8)
1369 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
1371 return 0;
1372
1373}
1374
1375/*************************************************************************
1376 cirrusfb_pan_display()
1377
1378 performs display panning - provided hardware permits this
1379**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001380static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1381 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382{
1383 int xoffset = 0;
1384 int yoffset = 0;
1385 unsigned long base;
1386 unsigned char tmp = 0, tmp2 = 0, xpix;
1387 struct cirrusfb_info *cinfo = info->par;
1388
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001389 dev_dbg(info->device,
1390 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391
1392 /* no range checks for xoffset and yoffset, */
1393 /* as fb_pan_display has already done this */
1394 if (var->vmode & FB_VMODE_YWRAP)
1395 return -EINVAL;
1396
1397 info->var.xoffset = var->xoffset;
1398 info->var.yoffset = var->yoffset;
1399
1400 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1401 yoffset = var->yoffset;
1402
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001403 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
1405 if (info->var.bits_per_pixel == 1) {
1406 /* base is already correct */
1407 xpix = (unsigned char) (var->xoffset % 8);
1408 } else {
1409 base /= 4;
1410 xpix = (unsigned char) ((xoffset % 4) * 2);
1411 }
1412
Krzysztof Helt8503df62007-10-16 01:29:08 -07001413 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
1415 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001416 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1417 (unsigned char) (base & 0xff));
1418 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1419 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
1421 /* construct bits 16, 17 and 18 of screen start address */
1422 if (base & 0x10000)
1423 tmp |= 0x01;
1424 if (base & 0x20000)
1425 tmp |= 0x04;
1426 if (base & 0x40000)
1427 tmp |= 0x08;
1428
Krzysztof Helt8503df62007-10-16 01:29:08 -07001429 /* 0xf2 is %11110010, exclude tmp bits */
1430 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1431 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
1433 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001434 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1435 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
Krzysztof Helt8503df62007-10-16 01:29:08 -07001437 /* write pixel panning value to AR33; this does not quite work in 8bpp
1438 *
1439 * ### Piccolo..? Will this work?
1440 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001442 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443
Krzysztof Helt8503df62007-10-16 01:29:08 -07001444 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445
Krzysztof Helt8503df62007-10-16 01:29:08 -07001446 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447}
1448
Krzysztof Helt8503df62007-10-16 01:29:08 -07001449static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450{
1451 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001452 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1453 * then the caller blanks by setting the CLUT (Color Look Up Table)
1454 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1455 * failed due to e.g. a video mode which doesn't support it.
1456 * Implements VESA suspend and powerdown modes on hardware that
1457 * supports disabling hsync/vsync:
1458 * blank_mode == 2: suspend vsync
1459 * blank_mode == 3: suspend hsync
1460 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 */
1462 unsigned char val;
1463 struct cirrusfb_info *cinfo = info->par;
1464 int current_mode = cinfo->blank_mode;
1465
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001466 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467
1468 if (info->state != FBINFO_STATE_RUNNING ||
1469 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001470 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 return 0;
1472 }
1473
1474 /* Undo current */
1475 if (current_mode == FB_BLANK_NORMAL ||
1476 current_mode == FB_BLANK_UNBLANK) {
1477 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001478 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1479 /* clear "FullBandwidth" bit */
1480 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001482 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 }
1484
1485 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001486 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001488 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1489 /* set "FullBandwidth" bit */
1490 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 }
1492
1493 switch (blank_mode) {
1494 case FB_BLANK_UNBLANK:
1495 case FB_BLANK_NORMAL:
1496 break;
1497 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001498 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 break;
1500 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 break;
1503 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 break;
1506 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001507 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 return 1;
1509 }
1510
1511 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001512 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
1514 /* Let fbcon do a soft blank for us */
1515 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1516}
1517/**** END Hardware specific Routines **************************************/
1518/****************************************************************************/
1519/**** BEGIN Internal Routines ***********************************************/
1520
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001521static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001523 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 const struct cirrusfb_board_info_rec *bi;
1525
Krzysztof Helt8503df62007-10-16 01:29:08 -07001526 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
1528 bi = &cirrusfb_board_info[cinfo->btype];
1529
1530 /* reset board globally */
1531 switch (cinfo->btype) {
1532 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001533 WSFR(cinfo, 0x01);
1534 udelay(500);
1535 WSFR(cinfo, 0x51);
1536 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 break;
1538 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 WSFR2(cinfo, 0xff);
1540 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 break;
1542 case BT_SD64:
1543 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001544 WSFR(cinfo, 0x1f);
1545 udelay(500);
1546 WSFR(cinfo, 0x4f);
1547 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 break;
1549 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 /* disable flickerfixer */
1551 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1552 mdelay(100);
1553 /* from Klaus' NetBSD driver: */
1554 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1555 /* put blitter into 542x compat */
1556 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1557 /* mode */
1558 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
1560
1561 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001562 /* from Klaus' NetBSD driver: */
1563 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 break;
1565
1566 case BT_ALPINE:
1567 /* Nothing to do to reset the board. */
1568 break;
1569
1570 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001571 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 break;
1573 }
1574
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001575 /* make sure RAM size set by this point */
1576 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
1578 /* the P4 is not fully initialized here; I rely on it having been */
1579 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
1582 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001583 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1584 WGen(cinfo, CL_POS102, 0x01);
1585 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
1587 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001588 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 /* reset sequencer logic */
1591 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
Krzysztof Helt8503df62007-10-16 01:29:08 -07001593 /* FullBandwidth (video off) and 8/9 dot clock */
1594 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1595 /* polarity (-/-), disable access to display memory,
1596 * VGA_CRTC_START_HI base address: color
1597 */
1598 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599
Krzysztof Helt8503df62007-10-16 01:29:08 -07001600 /* "magic cookie" - doesn't make any sense to me.. */
1601/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1602 /* unlock all extension registers */
1603 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Krzysztof Helt8503df62007-10-16 01:29:08 -07001605 /* reset blitter */
1606 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
1608 switch (cinfo->btype) {
1609 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001610 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 break;
1612 case BT_ALPINE:
1613 break;
1614 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001615 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 break;
1617 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001618 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1619 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 break;
1621 }
1622 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 /* plane mask: nothing */
1624 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1625 /* character map select: doesn't even matter in gx mode */
1626 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1627 /* memory mode: chain-4, no odd/even, ext. memory */
1628 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
1630 /* controller-internal base address of video memory */
1631 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001632 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633
Krzysztof Helt8503df62007-10-16 01:29:08 -07001634 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1635 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636
Krzysztof Helt8503df62007-10-16 01:29:08 -07001637 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1638 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1639 /* graphics cursor Y position (..."... ) */
1640 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1641 /* graphics cursor attributes */
1642 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1643 /* graphics cursor pattern address */
1644 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
1646 /* writing these on a P4 might give problems.. */
1647 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001648 /* configuration readback and ext. color */
1649 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1650 /* signature generator */
1651 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 }
1653
1654 /* MCLK select etc. */
1655 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
Krzysztof Helt8503df62007-10-16 01:29:08 -07001658 /* Screen A preset row scan: none */
1659 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1660 /* Text cursor start: disable text cursor */
1661 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1662 /* Text cursor end: - */
1663 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1664 /* Screen start address high: 0 */
1665 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1666 /* Screen start address low: 0 */
1667 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1668 /* text cursor location high: 0 */
1669 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1670 /* text cursor location low: 0 */
1671 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
Krzysztof Helt8503df62007-10-16 01:29:08 -07001673 /* Underline Row scanline: - */
1674 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1675 /* mode control: timing enable, byte mode, no compat modes */
1676 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1677 /* Line Compare: not needed */
1678 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 /* ext. display controls: ext.adr. wrap */
1681 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682
Krzysztof Helt8503df62007-10-16 01:29:08 -07001683 /* Set/Reset registes: - */
1684 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1685 /* Set/Reset enable: - */
1686 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1687 /* Color Compare: - */
1688 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1689 /* Data Rotate: - */
1690 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1691 /* Read Map Select: - */
1692 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1693 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1694 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1695 /* Miscellaneous: memory map base address, graphics mode */
1696 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1697 /* Color Don't care: involve all planes */
1698 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1699 /* Bit Mask: no mask at all */
1700 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 /* (5434 can't have bit 3 set for bitblt) */
1703 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705 /* Graphics controller mode extensions: finer granularity,
1706 * 8byte data latches
1707 */
1708 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709
Krzysztof Helt8503df62007-10-16 01:29:08 -07001710 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1711 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1712 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1713 /* Background color byte 1: - */
1714 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1715 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
Krzysztof Helt8503df62007-10-16 01:29:08 -07001717 /* Attribute Controller palette registers: "identity mapping" */
1718 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1719 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1720 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1721 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1722 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1723 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1724 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1725 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1726 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1727 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1728 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1729 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1730 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1731 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1732 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1733 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734
Krzysztof Helt8503df62007-10-16 01:29:08 -07001735 /* Attribute Controller mode: graphics mode */
1736 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1737 /* Overscan color reg.: reg. 0 */
1738 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1739 /* Color Plane enable: Enable all 4 planes */
1740 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1741/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1742 /* Color Select: - */
1743 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744
Krzysztof Helt8503df62007-10-16 01:29:08 -07001745 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746
1747 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001748 /* polarity (-/-), enable display mem,
1749 * VGA_CRTC_START_HI i/o base = color
1750 */
1751 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752
Krzysztof Helt8503df62007-10-16 01:29:08 -07001753 /* BLT Start/status: Blitter reset */
1754 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1755 /* - " - : "end-of-reset" */
1756 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757
1758 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001759 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 return;
1761}
1762
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764{
1765#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1766 static int IsOn = 0; /* XXX not ok for multiple boards */
1767
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 if (cinfo->btype == BT_PICASSO4)
1769 return; /* nothing to switch */
1770 if (cinfo->btype == BT_ALPINE)
1771 return; /* nothing to switch */
1772 if (cinfo->btype == BT_GD5480)
1773 return; /* nothing to switch */
1774 if (cinfo->btype == BT_PICASSO) {
1775 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001776 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777 return;
1778 }
1779 if (on) {
1780 switch (cinfo->btype) {
1781 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001782 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 break;
1784 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001785 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 break;
1787 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001788 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 break;
1790 default: /* do nothing */ break;
1791 }
1792 } else {
1793 switch (cinfo->btype) {
1794 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001795 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 break;
1797 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001798 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 break;
1800 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001801 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001803 default: /* do nothing */
1804 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 }
1806 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807#endif /* CONFIG_ZORRO */
1808}
1809
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810/******************************************/
1811/* Linux 2.6-style accelerated functions */
1812/******************************************/
1813
Krzysztof Helt8503df62007-10-16 01:29:08 -07001814static void cirrusfb_fillrect(struct fb_info *info,
1815 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817 struct fb_fillrect modded;
1818 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001819 struct cirrusfb_info *cinfo = info->par;
1820 int m = info->var.bits_per_pixel;
1821 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1822 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823
1824 if (info->state != FBINFO_STATE_RUNNING)
1825 return;
1826 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1827 cfb_fillrect(info, region);
1828 return;
1829 }
1830
1831 vxres = info->var.xres_virtual;
1832 vyres = info->var.yres_virtual;
1833
1834 memcpy(&modded, region, sizeof(struct fb_fillrect));
1835
Krzysztof Helt8503df62007-10-16 01:29:08 -07001836 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 modded.dx >= vxres || modded.dy >= vyres)
1838 return;
1839
Krzysztof Helt8503df62007-10-16 01:29:08 -07001840 if (modded.dx + modded.width > vxres)
1841 modded.width = vxres - modded.dx;
1842 if (modded.dy + modded.height > vyres)
1843 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844
Krzysztof Helt060b6002007-10-16 01:29:13 -07001845 cirrusfb_RectFill(cinfo->regbase,
1846 info->var.bits_per_pixel,
1847 (region->dx * m) / 8, region->dy,
1848 (region->width * m) / 8, region->height,
1849 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001850 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851}
1852
Krzysztof Helt8503df62007-10-16 01:29:08 -07001853static void cirrusfb_copyarea(struct fb_info *info,
1854 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 struct fb_copyarea modded;
1857 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001858 struct cirrusfb_info *cinfo = info->par;
1859 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
1861 if (info->state != FBINFO_STATE_RUNNING)
1862 return;
1863 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1864 cfb_copyarea(info, area);
1865 return;
1866 }
1867
1868 vxres = info->var.xres_virtual;
1869 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001870 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
Krzysztof Helt8503df62007-10-16 01:29:08 -07001872 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 modded.sx >= vxres || modded.sy >= vyres ||
1874 modded.dx >= vxres || modded.dy >= vyres)
1875 return;
1876
Krzysztof Helt8503df62007-10-16 01:29:08 -07001877 if (modded.sx + modded.width > vxres)
1878 modded.width = vxres - modded.sx;
1879 if (modded.dx + modded.width > vxres)
1880 modded.width = vxres - modded.dx;
1881 if (modded.sy + modded.height > vyres)
1882 modded.height = vyres - modded.sy;
1883 if (modded.dy + modded.height > vyres)
1884 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885
Krzysztof Helt060b6002007-10-16 01:29:13 -07001886 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1887 (area->sx * m) / 8, area->sy,
1888 (area->dx * m) / 8, area->dy,
1889 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001890 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001891
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892}
1893
Krzysztof Helt8503df62007-10-16 01:29:08 -07001894static void cirrusfb_imageblit(struct fb_info *info,
1895 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896{
1897 struct cirrusfb_info *cinfo = info->par;
1898
Krzysztof Helt8503df62007-10-16 01:29:08 -07001899 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 cfb_imageblit(info, image);
1901}
1902
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903#ifdef CONFIG_PPC_PREP
1904#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1905#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001906static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 *display = PREP_VIDEO_BASE;
1909 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910}
1911
1912#endif /* CONFIG_PPC_PREP */
1913
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001915static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916
1917/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1918 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1919 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1920 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001921static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1922 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923{
1924 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001925 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001927 if (cinfo->btype == BT_LAGUNA) {
1928 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1929
1930 mem = ((SR14 & 7) + 1) << 20;
1931 } else {
1932 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1933 switch ((SRF & 0x18)) {
1934 case 0x08:
1935 mem = 512 * 1024;
1936 break;
1937 case 0x10:
1938 mem = 1024 * 1024;
1939 break;
1940 /* 64-bit DRAM data bus width; assume 2MB.
1941 * Also indicates 2MB memory on the 5430.
1942 */
1943 case 0x18:
1944 mem = 2048 * 1024;
1945 break;
1946 default:
1947 dev_warn(info->device, "Unknown memory size!\n");
1948 mem = 1024 * 1024;
1949 }
1950 /* If DRAM bank switching is enabled, there must be
1951 * twice as much memory installed. (4MB on the 5434)
1952 */
1953 if (SRF & 0x80)
1954 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001956
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 return mem;
1959}
1960
Krzysztof Helt8503df62007-10-16 01:29:08 -07001961static void get_pci_addrs(const struct pci_dev *pdev,
1962 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001964 assert(pdev != NULL);
1965 assert(display != NULL);
1966 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968 *display = 0;
1969 *registers = 0;
1970
1971 /* This is a best-guess for now */
1972
1973 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1974 *display = pci_resource_start(pdev, 1);
1975 *registers = pci_resource_start(pdev, 0);
1976 } else {
1977 *display = pci_resource_start(pdev, 0);
1978 *registers = pci_resource_start(pdev, 1);
1979 }
1980
Krzysztof Helt8503df62007-10-16 01:29:08 -07001981 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982}
1983
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001984static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001986 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001988 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989#if 0 /* if system didn't claim this region, we would... */
1990 release_mem_region(0xA0000, 65535);
1991#endif
1992 if (release_io_ports)
1993 release_region(0x3C0, 32);
1994 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995}
1996#endif /* CONFIG_PCI */
1997
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001999static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000{
Al Virod91f5bb2007-10-17 00:27:18 +01002001 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002002 struct zorro_dev *zdev = to_zorro_dev(info->device);
2003
2004 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005
2006 if (cinfo->btype == BT_PICASSO4) {
2007 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002008 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002009 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002011 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002012 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014}
2015#endif /* CONFIG_ZORRO */
2016
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002017static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002019 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 struct fb_var_screeninfo *var = &info->var;
2021
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022 info->pseudo_palette = cinfo->pseudo_palette;
2023 info->flags = FBINFO_DEFAULT
2024 | FBINFO_HWACCEL_XPAN
2025 | FBINFO_HWACCEL_YPAN
2026 | FBINFO_HWACCEL_FILLRECT
2027 | FBINFO_HWACCEL_COPYAREA;
2028 if (noaccel)
2029 info->flags |= FBINFO_HWACCEL_DISABLED;
2030 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 if (cinfo->btype == BT_GD5480) {
2032 if (var->bits_per_pixel == 16)
2033 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002034 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 info->screen_base += 2 * MB_;
2036 }
2037
2038 /* Fill fix common fields */
2039 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2040 sizeof(info->fix.id));
2041
2042 /* monochrome: only 1 memory plane */
2043 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002044 info->fix.smem_len = info->screen_size;
2045 if (var->bits_per_pixel == 1)
2046 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 info->fix.xpanstep = 1;
2049 info->fix.ypanstep = 1;
2050 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051
2052 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 info->fix.mmio_len = 0;
2054 info->fix.accel = FB_ACCEL_NONE;
2055
2056 fb_alloc_cmap(&info->cmap, 256, 0);
2057
2058 return 0;
2059}
2060
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002061static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002063 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002065 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 btype = cinfo->btype;
2068
2069 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002070 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002072 /* set all the vital stuff */
2073 cirrusfb_set_fbinfo(info);
2074
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002075 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002077 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2078 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002079 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002080 err = -EINVAL;
2081 goto err_dealloc_cmap;
2082 }
2083
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 info->var.activate = FB_ACTIVATE_NOW;
2085
2086 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2087 if (err < 0) {
2088 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002089 dev_dbg(info->device,
2090 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002091 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 }
2093
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 err = register_framebuffer(info);
2095 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002096 dev_err(info->device,
2097 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 goto err_dealloc_cmap;
2099 }
2100
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 return 0;
2102
2103err_dealloc_cmap:
2104 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002105 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002106 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 return err;
2108}
2109
Krzysztof Helt8503df62007-10-16 01:29:08 -07002110static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111{
2112 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113
Krzysztof Helt8503df62007-10-16 01:29:08 -07002114 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002115 unregister_framebuffer(info);
2116 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002117 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002118 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002119 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120}
2121
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002123static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2124 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125{
2126 struct cirrusfb_info *cinfo;
2127 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002128 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 unsigned long board_addr, board_size;
2130 int ret;
2131
2132 ret = pci_enable_device(pdev);
2133 if (ret < 0) {
2134 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2135 goto err_out;
2136 }
2137
2138 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2139 if (!info) {
2140 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2141 ret = -ENOMEM;
2142 goto err_disable;
2143 }
2144
2145 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002146 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002148 dev_dbg(info->device,
2149 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
2150 (unsigned long long)pdev->resource[0].start, btype);
2151 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2152 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153
Krzysztof Helt8503df62007-10-16 01:29:08 -07002154 if (isPReP) {
2155 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002157 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002159 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002160 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002162 dev_dbg(info->device,
2163 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002164 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002165 /* FIXME: this forces VGA. alternatives? */
2166 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 }
2168
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002169 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002170 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171
2172 board_size = (btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002173 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174
2175 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002176 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002177 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2178 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 goto err_release_fb;
2180 }
2181#if 0 /* if the system didn't claim this region, we would... */
2182 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002183 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2184 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 ret = -EBUSY;
2186 goto err_release_regions;
2187 }
2188#endif
2189 if (request_region(0x3C0, 32, "cirrusfb"))
2190 release_io_ports = 1;
2191
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002192 info->screen_base = ioremap(board_addr, board_size);
2193 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 ret = -EIO;
2195 goto err_release_legacy;
2196 }
2197
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002198 info->fix.smem_start = board_addr;
2199 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 cinfo->unmap = cirrusfb_pci_unmap;
2201
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002202 dev_info(info->device,
2203 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2204 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 pci_set_drvdata(pdev, info);
2206
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002207 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002208 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002209 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002210 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211
2212err_release_legacy:
2213 if (release_io_ports)
2214 release_region(0x3C0, 32);
2215#if 0
2216 release_mem_region(0xA0000, 65535);
2217err_release_regions:
2218#endif
2219 pci_release_regions(pdev);
2220err_release_fb:
2221 framebuffer_release(info);
2222err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223err_out:
2224 return ret;
2225}
2226
Krzysztof Helt8503df62007-10-16 01:29:08 -07002227static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228{
2229 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230
Krzysztof Helt8503df62007-10-16 01:29:08 -07002231 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232}
2233
2234static struct pci_driver cirrusfb_pci_driver = {
2235 .name = "cirrusfb",
2236 .id_table = cirrusfb_pci_table,
2237 .probe = cirrusfb_pci_register,
2238 .remove = __devexit_p(cirrusfb_pci_unregister),
2239#ifdef CONFIG_PM
2240#if 0
2241 .suspend = cirrusfb_pci_suspend,
2242 .resume = cirrusfb_pci_resume,
2243#endif
2244#endif
2245};
2246#endif /* CONFIG_PCI */
2247
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002249static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2250 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251{
2252 struct cirrusfb_info *cinfo;
2253 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002254 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 struct zorro_dev *z2 = NULL;
2256 unsigned long board_addr, board_size, size;
2257 int ret;
2258
2259 btype = ent->driver_data;
2260 if (cirrusfb_zorro_table2[btype].id2)
2261 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2262 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263
2264 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2265 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002266 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 ret = -ENOMEM;
2268 goto err_out;
2269 }
2270
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002271 dev_info(info->device, "%s board detected\n",
2272 cirrusfb_board_info[btype].name);
2273
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 cinfo->btype = btype;
2276
Al Viro36ea96a2007-10-27 19:46:58 +01002277 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002278 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 board_addr = zorro_resource_start(z);
2281 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002282 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283
2284 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002285 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2286 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 ret = -EBUSY;
2288 goto err_release_fb;
2289 }
2290
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 ret = -EIO;
2292
2293 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002294 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
2296 /* To be precise, for the P4 this is not the */
2297 /* begin of the board, but the begin of RAM. */
2298 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2299 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002300 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 if (!cinfo->regbase)
2302 goto err_release_region;
2303
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002304 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002305 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002307 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002309 info->fix.smem_start = board_addr + 16777216;
2310 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2311 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 goto err_unmap_regbase;
2313 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002314 dev_info(info->device, " REG at $%lx\n",
2315 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002317 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002319 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002321 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2322 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 goto err_release_region;
2324
2325 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002326 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002327 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002329 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002330 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 }
2332 cinfo->unmap = cirrusfb_zorro_unmap;
2333
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002334 dev_info(info->device,
2335 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2336 board_size / MB_, board_addr);
2337
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 zorro_set_drvdata(z, info);
2339
Al Virod91f5bb2007-10-17 00:27:18 +01002340 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002341 if (ret) {
2342 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002343 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002344 iounmap(cinfo->regbase - 0x600000);
2345 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002346 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002347 }
2348 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349
2350err_unmap_regbase:
2351 /* Parental advisory: explicit hack */
2352 iounmap(cinfo->regbase - 0x600000);
2353err_release_region:
2354 release_region(board_addr, board_size);
2355err_release_fb:
2356 framebuffer_release(info);
2357err_out:
2358 return ret;
2359}
2360
2361void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2362{
2363 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364
Krzysztof Helt8503df62007-10-16 01:29:08 -07002365 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366}
2367
2368static struct zorro_driver cirrusfb_zorro_driver = {
2369 .name = "cirrusfb",
2370 .id_table = cirrusfb_zorro_table,
2371 .probe = cirrusfb_zorro_register,
2372 .remove = __devexit_p(cirrusfb_zorro_unregister),
2373};
2374#endif /* CONFIG_ZORRO */
2375
2376static int __init cirrusfb_init(void)
2377{
2378 int error = 0;
2379
2380#ifndef MODULE
2381 char *option = NULL;
2382
2383 if (fb_get_options("cirrusfb", &option))
2384 return -ENODEV;
2385 cirrusfb_setup(option);
2386#endif
2387
2388#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002389 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390#endif
2391#ifdef CONFIG_PCI
2392 error |= pci_register_driver(&cirrusfb_pci_driver);
2393#endif
2394 return error;
2395}
2396
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002398static int __init cirrusfb_setup(char *options)
2399{
Vlada Pericee119402008-11-19 15:36:45 -08002400 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 if (!options || !*options)
2403 return 0;
2404
Krzysztof Helt8503df62007-10-16 01:29:08 -07002405 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002406 if (!*this_opt)
2407 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 if (!strcmp(this_opt, "noaccel"))
2410 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002411 else if (!strncmp(this_opt, "mode:", 5))
2412 mode_option = this_opt + 5;
2413 else
2414 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 }
2416 return 0;
2417}
2418#endif
2419
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 /*
2421 * Modularization
2422 */
2423
2424MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2425MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2426MODULE_LICENSE("GPL");
2427
Krzysztof Helt8503df62007-10-16 01:29:08 -07002428static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429{
2430#ifdef CONFIG_PCI
2431 pci_unregister_driver(&cirrusfb_pci_driver);
2432#endif
2433#ifdef CONFIG_ZORRO
2434 zorro_unregister_driver(&cirrusfb_zorro_driver);
2435#endif
2436}
2437
2438module_init(cirrusfb_init);
2439
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002440module_param(mode_option, charp, 0);
2441MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002442module_param(noaccel, bool, 0);
2443MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002444
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445#ifdef MODULE
2446module_exit(cirrusfb_exit);
2447#endif
2448
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449/**********************************************************************/
2450/* about the following functions - I have used the same names for the */
2451/* functions as Markus Wild did in his Retina driver for NetBSD as */
2452/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002453/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454/**********************************************************************/
2455
2456/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002457static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 int regnum, unsigned char val)
2459{
2460 unsigned long regofs = 0;
2461
2462 if (cinfo->btype == BT_PICASSO) {
2463 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2465 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2467 regofs = 0xfff;
2468 }
2469
Krzysztof Helt8503df62007-10-16 01:29:08 -07002470 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471}
2472
2473/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002474static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475{
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 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487}
2488
2489/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002490static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002492 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493
Krzysztof Helt8503df62007-10-16 01:29:08 -07002494 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 /* if we're just in "write value" mode, write back the */
2496 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002497 vga_w(cinfo->regbase, VGA_ATT_IW,
2498 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 }
2500 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002501/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2502 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503
2504 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002505 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506}
2507
2508/*** WHDR() - write into the Hidden DAC register ***/
2509/* as the HDR is the only extension register that requires special treatment
2510 * (the other extension registers are accessible just like the "ordinary"
2511 * registers of their functional group) here is a specialized routine for
2512 * accessing the HDR
2513 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002514static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515{
2516 unsigned char dummy;
2517
2518 if (cinfo->btype == BT_PICASSO) {
2519 /* Klaus' hint for correct access to HDR on some boards */
2520 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 WGen(cinfo, VGA_PEL_MSK, 0x00);
2522 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002524 dummy = RGen(cinfo, VGA_PEL_IW);
2525 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 }
2527 /* now do the usual stuff to access the HDR */
2528
Krzysztof Helt8503df62007-10-16 01:29:08 -07002529 dummy = RGen(cinfo, VGA_PEL_MSK);
2530 udelay(200);
2531 dummy = RGen(cinfo, VGA_PEL_MSK);
2532 udelay(200);
2533 dummy = RGen(cinfo, VGA_PEL_MSK);
2534 udelay(200);
2535 dummy = RGen(cinfo, VGA_PEL_MSK);
2536 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537
Krzysztof Helt8503df62007-10-16 01:29:08 -07002538 WGen(cinfo, VGA_PEL_MSK, val);
2539 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540
2541 if (cinfo->btype == BT_PICASSO) {
2542 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543 dummy = RGen(cinfo, VGA_PEL_IW);
2544 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545
2546 /* and at the end, restore the mask value */
2547 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 WGen(cinfo, VGA_PEL_MSK, 0xff);
2549 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 }
2551}
2552
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555{
2556#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002557 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002559 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560#endif
2561}
2562
2563/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002564static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565{
2566#ifdef CONFIG_ZORRO
2567 /* writing an arbitrary value to this one causes the monitor switcher */
2568 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002569 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002571 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572#endif
2573}
2574
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002576static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 unsigned char green, unsigned char blue)
2578{
2579 unsigned int data = VGA_PEL_D;
2580
2581 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583
2584 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2585 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2586 /* but DAC data register IS, at least for Picasso II */
2587 if (cinfo->btype == BT_PICASSO)
2588 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002589 vga_w(cinfo->regbase, data, red);
2590 vga_w(cinfo->regbase, data, green);
2591 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002593 vga_w(cinfo->regbase, data, blue);
2594 vga_w(cinfo->regbase, data, green);
2595 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 }
2597}
2598
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599#if 0
2600/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002601static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 unsigned char *green, unsigned char *blue)
2603{
2604 unsigned int data = VGA_PEL_D;
2605
Krzysztof Helt8503df62007-10-16 01:29:08 -07002606 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607
2608 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2609 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2610 if (cinfo->btype == BT_PICASSO)
2611 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002612 *red = vga_r(cinfo->regbase, data);
2613 *green = vga_r(cinfo->regbase, data);
2614 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002616 *blue = vga_r(cinfo->regbase, data);
2617 *green = vga_r(cinfo->regbase, data);
2618 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619 }
2620}
2621#endif
2622
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623/*******************************************************************
2624 cirrusfb_WaitBLT()
2625
2626 Wait for the BitBLT engine to complete a possible earlier job
2627*********************************************************************/
2628
2629/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002630static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631{
2632 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002633 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634 /* do nothing */ ;
2635}
2636
2637/*******************************************************************
2638 cirrusfb_BitBLT()
2639
2640 perform accelerated "scrolling"
2641********************************************************************/
2642
Krzysztof Helt8503df62007-10-16 01:29:08 -07002643static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2644 u_short curx, u_short cury,
2645 u_short destx, u_short desty,
2646 u_short width, u_short height,
2647 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648{
2649 u_short nwidth, nheight;
2650 u_long nsrc, ndest;
2651 u_char bltmode;
2652
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653 nwidth = width - 1;
2654 nheight = height - 1;
2655
2656 bltmode = 0x00;
2657 /* if source adr < dest addr, do the Blt backwards */
2658 if (cury <= desty) {
2659 if (cury == desty) {
2660 /* if src and dest are on the same line, check x */
2661 if (curx < destx)
2662 bltmode |= 0x01;
2663 } else
2664 bltmode |= 0x01;
2665 }
2666 if (!bltmode) {
2667 /* standard case: forward blitting */
2668 nsrc = (cury * line_length) + curx;
2669 ndest = (desty * line_length) + destx;
2670 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002671 /* this means start addresses are at the end,
2672 * counting backwards
2673 */
2674 nsrc = cury * line_length + curx +
2675 nheight * line_length + nwidth;
2676 ndest = desty * line_length + destx +
2677 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 }
2679
2680 /*
2681 run-down of registers to be programmed:
2682 destination pitch
2683 source pitch
2684 BLT width/height
2685 source start
2686 destination start
2687 BLT mode
2688 BLT ROP
2689 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2690 start/stop
2691 */
2692
Krzysztof Helt8503df62007-10-16 01:29:08 -07002693 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
2695 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002696 /* dest pitch low */
2697 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2698 /* dest pitch hi */
2699 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2700 /* source pitch low */
2701 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2702 /* source pitch hi */
2703 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704
2705 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706 /* BLT width low */
2707 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2708 /* BLT width hi */
2709 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710
2711 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002712 /* BLT height low */
2713 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2714 /* BLT width hi */
2715 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716
2717 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002718 /* BLT dest low */
2719 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2720 /* BLT dest mid */
2721 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2722 /* BLT dest hi */
2723 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724
2725 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002726 /* BLT src low */
2727 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2728 /* BLT src mid */
2729 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2730 /* BLT src hi */
2731 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732
2733 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002734 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735
2736 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002737 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738
2739 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002740 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741}
2742
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743/*******************************************************************
2744 cirrusfb_RectFill()
2745
2746 perform accelerated rectangle fill
2747********************************************************************/
2748
Krzysztof Helt8503df62007-10-16 01:29:08 -07002749static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 u_short x, u_short y, u_short width, u_short height,
2751 u_char color, u_short line_length)
2752{
2753 u_short nwidth, nheight;
2754 u_long ndest;
2755 u_char op;
2756
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 nwidth = width - 1;
2758 nheight = height - 1;
2759
2760 ndest = (y * line_length) + x;
2761
Krzysztof Helt8503df62007-10-16 01:29:08 -07002762 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763
2764 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002765 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2766 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2767 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2768 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769
2770 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002771 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2772 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773
2774 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002775 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2776 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777
2778 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002779 /* BLT dest low */
2780 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2781 /* BLT dest mid */
2782 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2783 /* BLT dest hi */
2784 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785
2786 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002787 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2788 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2789 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790
2791 /* This is a ColorExpand Blt, using the */
2792 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002793 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2794 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795
2796 op = 0xc0;
2797 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002798 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2799 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 op = 0x50;
2801 op = 0xd0;
2802 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002803 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2804 vga_wgfx(regbase, CL_GR11, color); /* background color */
2805 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2806 vga_wgfx(regbase, CL_GR13, color); /* background color */
2807 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2808 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 op = 0x50;
2810 op = 0xf0;
2811 }
2812 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002813 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814
2815 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002816 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817
2818 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002819 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820}
2821
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822/**************************************************************************
2823 * bestclock() - determine closest possible clock lower(?) than the
2824 * desired pixel clock
2825 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002826static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002828 int n, d;
2829 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830
Krzysztof Helt8503df62007-10-16 01:29:08 -07002831 assert(nom != NULL);
2832 assert(den != NULL);
2833 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834
2835 *nom = 0;
2836 *den = 0;
2837 *div = 0;
2838
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 if (freq < 8000)
2840 freq = 8000;
2841
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002842 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843
2844 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002845 int s = 0;
2846
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002847 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002849 int temp = d;
2850
2851 if (temp > 31) {
2852 s = 1;
2853 temp >>= 1;
2854 }
2855 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002856 h = h > freq ? h - freq : freq - h;
2857 if (h < diff) {
2858 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002860 *den = temp;
2861 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862 }
2863 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002864 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002866 if (d > 31) {
2867 s = 1;
2868 d >>= 1;
2869 }
2870 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002871 h = h > freq ? h - freq : freq - h;
2872 if (h < diff) {
2873 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002875 *den = d;
2876 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877 }
2878 }
2879 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880}
2881
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882/* -------------------------------------------------------------------------
2883 *
2884 * debugging functions
2885 *
2886 * -------------------------------------------------------------------------
2887 */
2888
2889#ifdef CIRRUSFB_DEBUG
2890
2891/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 * cirrusfb_dbg_print_regs
2893 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2894 * @reg_class: type of registers to read: %CRT, or %SEQ
2895 *
2896 * DESCRIPTION:
2897 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2898 * old-style I/O ports are queried for information, otherwise MMIO is
2899 * used at the given @base address to query the information.
2900 */
2901
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002902static void cirrusfb_dbg_print_regs(struct fb_info *info,
2903 caddr_t regbase,
2904 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905{
2906 va_list list;
2907 unsigned char val = 0;
2908 unsigned reg;
2909 char *name;
2910
Krzysztof Helt8503df62007-10-16 01:29:08 -07002911 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912
Krzysztof Helt8503df62007-10-16 01:29:08 -07002913 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002915 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916
2917 switch (reg_class) {
2918 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002919 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920 break;
2921 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002922 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923 break;
2924 default:
2925 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002926 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927 break;
2928 }
2929
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002930 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002931
Krzysztof Helt8503df62007-10-16 01:29:08 -07002932 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933 }
2934
Krzysztof Helt8503df62007-10-16 01:29:08 -07002935 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936}
2937
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 * cirrusfb_dbg_reg_dump
2940 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2941 *
2942 * DESCRIPTION:
2943 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2944 * old-style I/O ports are queried for information, otherwise MMIO is
2945 * used at the given @base address to query the information.
2946 */
2947
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002948static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002950 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002952 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953 "CR00", 0x00,
2954 "CR01", 0x01,
2955 "CR02", 0x02,
2956 "CR03", 0x03,
2957 "CR04", 0x04,
2958 "CR05", 0x05,
2959 "CR06", 0x06,
2960 "CR07", 0x07,
2961 "CR08", 0x08,
2962 "CR09", 0x09,
2963 "CR0A", 0x0A,
2964 "CR0B", 0x0B,
2965 "CR0C", 0x0C,
2966 "CR0D", 0x0D,
2967 "CR0E", 0x0E,
2968 "CR0F", 0x0F,
2969 "CR10", 0x10,
2970 "CR11", 0x11,
2971 "CR12", 0x12,
2972 "CR13", 0x13,
2973 "CR14", 0x14,
2974 "CR15", 0x15,
2975 "CR16", 0x16,
2976 "CR17", 0x17,
2977 "CR18", 0x18,
2978 "CR22", 0x22,
2979 "CR24", 0x24,
2980 "CR26", 0x26,
2981 "CR2D", 0x2D,
2982 "CR2E", 0x2E,
2983 "CR2F", 0x2F,
2984 "CR30", 0x30,
2985 "CR31", 0x31,
2986 "CR32", 0x32,
2987 "CR33", 0x33,
2988 "CR34", 0x34,
2989 "CR35", 0x35,
2990 "CR36", 0x36,
2991 "CR37", 0x37,
2992 "CR38", 0x38,
2993 "CR39", 0x39,
2994 "CR3A", 0x3A,
2995 "CR3B", 0x3B,
2996 "CR3C", 0x3C,
2997 "CR3D", 0x3D,
2998 "CR3E", 0x3E,
2999 "CR3F", 0x3F,
3000 NULL);
3001
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003002 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003003
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003004 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003006 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003007 "SR00", 0x00,
3008 "SR01", 0x01,
3009 "SR02", 0x02,
3010 "SR03", 0x03,
3011 "SR04", 0x04,
3012 "SR08", 0x08,
3013 "SR09", 0x09,
3014 "SR0A", 0x0A,
3015 "SR0B", 0x0B,
3016 "SR0D", 0x0D,
3017 "SR10", 0x10,
3018 "SR11", 0x11,
3019 "SR12", 0x12,
3020 "SR13", 0x13,
3021 "SR14", 0x14,
3022 "SR15", 0x15,
3023 "SR16", 0x16,
3024 "SR17", 0x17,
3025 "SR18", 0x18,
3026 "SR19", 0x19,
3027 "SR1A", 0x1A,
3028 "SR1B", 0x1B,
3029 "SR1C", 0x1C,
3030 "SR1D", 0x1D,
3031 "SR1E", 0x1E,
3032 "SR1F", 0x1F,
3033 NULL);
3034
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003035 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036}
3037
3038#endif /* CIRRUSFB_DEBUG */
3039