blob: 357139ab7a0f181f3e0394661fad843b3fa9f53e [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/delay.h>
43#include <linux/fb.h>
44#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#include <asm/pgtable.h>
46
47#ifdef CONFIG_ZORRO
48#include <linux/zorro.h>
49#endif
50#ifdef CONFIG_PCI
51#include <linux/pci.h>
52#endif
53#ifdef CONFIG_AMIGA
54#include <asm/amigahw.h>
55#endif
56#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110057#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070058#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#else
60#define isPReP 0
61#endif
62
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070063#include <video/vga.h>
64#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Linus Torvalds1da177e2005-04-16 15:20:36 -070066/*****************************************************************
67 *
68 * debugging and utility macros
69 *
70 */
71
Linus Torvalds1da177e2005-04-16 15:20:36 -070072/* disable runtime assertions? */
73/* #define CIRRUSFB_NDEBUG */
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075/* debugging assertions */
76#ifndef CIRRUSFB_NDEBUG
77#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070078 if (!(expr)) { \
79 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070080 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070081 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070082#else
83#define assert(expr)
84#endif
85
Krzysztof Helt8503df62007-10-16 01:29:08 -070086#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Linus Torvalds1da177e2005-04-16 15:20:36 -070088/*****************************************************************
89 *
90 * chipset information
91 *
92 */
93
94/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070095enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 BT_NONE = 0,
Krzysztof Helt7cade312009-03-31 15:25:13 -070097 BT_SD64, /* GD5434 */
98 BT_PICCOLO, /* GD5426 */
99 BT_PICASSO, /* GD5426 or GD5428 */
100 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 BT_PICASSO4, /* GD5446 */
102 BT_ALPINE, /* GD543x/4x */
103 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700104 BT_LAGUNA, /* GD5462/64 */
105 BT_LAGUNAB, /* GD5465 */
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,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700147 .sr07_1bpp_mux = 0xF6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 .sr07_8bpp = 0xF1,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700149 .sr07_8bpp_mux = 0xF7,
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700150 .sr1f = 0x1E
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 },
152 [BT_PICCOLO] = {
153 .name = "CL Piccolo",
154 .maxclock = {
155 /* guess */
156 90000, 90000, 90000, 90000, 90000
157 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700158 .init_sr07 = true,
159 .init_sr1f = true,
160 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 .sr07 = 0x80,
162 .sr07_1bpp = 0x80,
163 .sr07_8bpp = 0x81,
164 .sr1f = 0x22
165 },
166 [BT_PICASSO] = {
167 .name = "CL Picasso",
168 .maxclock = {
169 /* guess */
170 90000, 90000, 90000, 90000, 90000
171 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700172 .init_sr07 = true,
173 .init_sr1f = true,
174 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 .sr07 = 0x20,
176 .sr07_1bpp = 0x20,
177 .sr07_8bpp = 0x21,
178 .sr1f = 0x22
179 },
180 [BT_SPECTRUM] = {
181 .name = "CL Spectrum",
182 .maxclock = {
183 /* guess */
184 90000, 90000, 90000, 90000, 90000
185 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700186 .init_sr07 = true,
187 .init_sr1f = true,
188 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 .sr07 = 0x80,
190 .sr07_1bpp = 0x80,
191 .sr07_8bpp = 0x81,
192 .sr1f = 0x22
193 },
194 [BT_PICASSO4] = {
195 .name = "CL Picasso4",
196 .maxclock = {
197 135100, 135100, 85500, 85500, 0
198 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700199 .init_sr07 = true,
200 .init_sr1f = false,
201 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700202 .sr07 = 0xA0,
203 .sr07_1bpp = 0xA0,
204 .sr07_1bpp_mux = 0xA6,
205 .sr07_8bpp = 0xA1,
206 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 .sr1f = 0
208 },
209 [BT_ALPINE] = {
210 .name = "CL Alpine",
211 .maxclock = {
212 /* for the GD5430. GD5446 can do more... */
213 85500, 85500, 50000, 28500, 0
214 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700215 .init_sr07 = true,
216 .init_sr1f = true,
217 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700219 .sr07_1bpp = 0xA0,
220 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 .sr07_8bpp = 0xA1,
222 .sr07_8bpp_mux = 0xA7,
223 .sr1f = 0x1C
224 },
225 [BT_GD5480] = {
226 .name = "CL GD5480",
227 .maxclock = {
228 135100, 200000, 200000, 135100, 135100
229 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700230 .init_sr07 = true,
231 .init_sr1f = true,
232 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 .sr07 = 0x10,
234 .sr07_1bpp = 0x11,
235 .sr07_8bpp = 0x11,
236 .sr1f = 0x1C
237 },
238 [BT_LAGUNA] = {
239 .name = "CL Laguna",
240 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700241 /* taken from X11 code */
242 170000, 170000, 170000, 170000, 135100,
243 },
244 .init_sr07 = false,
245 .init_sr1f = false,
246 .scrn_start_bit19 = true,
247 },
248 [BT_LAGUNAB] = {
249 .name = "CL Laguna AGP",
250 .maxclock = {
251 /* taken from X11 code */
252 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700254 .init_sr07 = false,
255 .init_sr1f = false,
256 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 }
258};
259
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260#ifdef CONFIG_PCI
261#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000262 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700265 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_SD64),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64),
Krzysztof Helt8503df62007-10-16 01:29:08 -0700268 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
269 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
271 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700275 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 { 0, }
277};
278MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
279#undef CHIP
280#endif /* CONFIG_PCI */
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282#ifdef CONFIG_ZORRO
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200283struct zorrocl {
284 enum cirrus_board type; /* Board type */
285 u32 regoffset; /* Offset of registers in first Zorro device */
286 u32 ramsize; /* Size of video RAM in first Zorro device */
287 /* If zero, use autoprobe on RAM device */
288 u32 ramoffset; /* Offset of video RAM in first Zorro device */
289 zorro_id ramid; /* Zorro ID of RAM device */
290};
291
292static const struct zorrocl zcl_sd64 __devinitconst = {
293 .type = BT_SD64,
294 .ramid = ZORRO_PROD_HELFRICH_SD64_RAM,
295};
296
297static const struct zorrocl zcl_piccolo __devinitconst = {
298 .type = BT_PICCOLO,
299 .ramid = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
300};
301
302static const struct zorrocl zcl_picasso __devinitconst = {
303 .type = BT_PICASSO,
304 .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
305};
306
307static const struct zorrocl zcl_spectrum __devinitconst = {
308 .type = BT_SPECTRUM,
309 .ramid = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
310};
311
312static const struct zorrocl zcl_picasso4_z3 __devinitconst = {
313 .type = BT_PICASSO4,
314 .regoffset = 0x00600000,
315 .ramsize = 4 * MB_,
316 .ramoffset = 0x01000000,
317};
318
319
320static const struct zorro_device_id cirrusfb_zorro_table[] __devinitconst = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200322 .id = ZORRO_PROD_HELFRICH_SD64_REG,
323 .driver_data = (unsigned long)&zcl_sd64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200325 .id = ZORRO_PROD_HELFRICH_PICCOLO_REG,
326 .driver_data = (unsigned long)&zcl_piccolo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200328 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
329 .driver_data = (unsigned long)&zcl_picasso,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200331 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
332 .driver_data = (unsigned long)&zcl_spectrum,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 }, {
334 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200335 .driver_data = (unsigned long)&zcl_picasso4_z3,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 },
337 { 0 }
338};
Geert Uytterhoevenbf54a2b2008-11-18 21:13:53 +0100339MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340#endif /* CONFIG_ZORRO */
341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700343enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700344 CRT,
345 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700346};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700347#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349/* info about board */
350struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700352 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700353 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 unsigned char SFR; /* Shadow of special function register */
355
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700356 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700357 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700359 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700361 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362};
363
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700364static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700365static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367/****************************************************************************/
368/**** BEGIN PROTOTYPES ******************************************************/
369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700371static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
372 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700375static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700376static void switch_monitor(struct cirrusfb_info *cinfo, int on);
377static void WGen(const struct cirrusfb_info *cinfo,
378 int regnum, unsigned char val);
379static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
380static void AttrOn(const struct cirrusfb_info *cinfo);
381static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
382static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
383static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
384static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
385 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700387static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
388 unsigned char *red, unsigned char *green,
389 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700391static void cirrusfb_WaitBLT(u8 __iomem *regbase);
392static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
393 u_short curx, u_short cury,
394 u_short destx, u_short desty,
395 u_short width, u_short height,
396 u_short line_length);
397static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
398 u_short x, u_short y,
399 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700400 u32 fg_color, u32 bg_color,
401 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700403static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700406static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
407static void cirrusfb_dbg_print_regs(struct fb_info *info,
408 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700409 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410#endif /* CIRRUSFB_DEBUG */
411
412/*** END PROTOTYPES ********************************************************/
413/*****************************************************************************/
414/*** BEGIN Interface Used by the World ***************************************/
415
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700416static inline int is_laguna(const struct cirrusfb_info *cinfo)
417{
418 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
419}
420
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
423/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700424static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425{
426 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700427 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 return 0;
429}
430
431/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700432static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433{
434 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700435 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 return 0;
437}
438
439/**** END Interface used by the World *************************************/
440/****************************************************************************/
441/**** BEGIN Hardware specific Routines **************************************/
442
Krzysztof Helt486ff382008-10-15 22:03:42 -0700443/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700444static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700446 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700447 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448
Krzysztof Helt486ff382008-10-15 22:03:42 -0700449 /* Read MCLK value */
450 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700451 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
453 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700454 * should divide it by to get VCLK
455 */
456
457 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700458 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700459 return 1;
460 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700461 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700462 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 }
464
Krzysztof Helt486ff382008-10-15 22:03:42 -0700465 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466}
467
Krzysztof Helt99a45842009-03-31 15:25:09 -0700468static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
469 struct fb_info *info)
470{
471 long freq;
472 long maxclock;
473 struct cirrusfb_info *cinfo = info->par;
474 unsigned maxclockidx = var->bits_per_pixel >> 3;
475
476 /* convert from ps to kHz */
477 freq = PICOS2KHZ(var->pixclock);
478
479 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
480
481 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
482 cinfo->multiplexing = 0;
483
484 /* If the frequency is greater than we can support, we might be able
485 * to use multiplexing for the video mode */
486 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700487 dev_err(info->device,
488 "Frequency greater than maxclock (%ld kHz)\n",
489 maxclock);
490 return -EINVAL;
491 }
492 /*
493 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
494 * pixel clock
495 */
496 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700497 switch (cinfo->btype) {
498 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700499 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700500 case BT_PICASSO4:
501 if (freq > 85500)
502 cinfo->multiplexing = 1;
503 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700504 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700505 if (freq > 135100)
506 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700507 break;
508
509 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700510 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700511 }
512 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700513
514 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700515 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700516 cinfo->doubleVCLK = 0;
517 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
518 var->bits_per_pixel == 16) {
519 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700520 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700521
Krzysztof Helt99a45842009-03-31 15:25:09 -0700522 return 0;
523}
524
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525static int cirrusfb_check_var(struct fb_var_screeninfo *var,
526 struct fb_info *info)
527{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700528 int yres;
529 /* memory size in pixels */
530 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700531 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
533 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700534 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 var->red.offset = 0;
536 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700537 var->green = var->red;
538 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 break;
540
541 case 8:
542 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700543 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700544 var->green = var->red;
545 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 break;
547
548 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700549 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 var->red.offset = 2;
551 var->green.offset = -3;
552 var->blue.offset = 8;
553 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700554 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 var->green.offset = 5;
556 var->blue.offset = 0;
557 }
558 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700559 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 var->blue.length = 5;
561 break;
562
Krzysztof Helt7cade312009-03-31 15:25:13 -0700563 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700564 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700565 var->red.offset = 0;
566 var->green.offset = 8;
567 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 } else {
569 var->red.offset = 16;
570 var->green.offset = 8;
571 var->blue.offset = 0;
572 }
573 var->red.length = 8;
574 var->green.length = 8;
575 var->blue.length = 8;
576 break;
577
578 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700579 dev_dbg(info->device,
580 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Krzysztof Helt0efb2a02009-04-13 14:39:55 -0700581 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 }
583
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700584 if (var->xres_virtual < var->xres)
585 var->xres_virtual = var->xres;
586 /* use highest possible virtual resolution */
587 if (var->yres_virtual == -1) {
588 var->yres_virtual = pixels / var->xres_virtual;
589
590 dev_info(info->device,
591 "virtual resolution set to maximum of %dx%d\n",
592 var->xres_virtual, var->yres_virtual);
593 }
594 if (var->yres_virtual < var->yres)
595 var->yres_virtual = var->yres;
596
597 if (var->xres_virtual * var->yres_virtual > pixels) {
598 dev_err(info->device, "mode %dx%dx%d rejected... "
599 "virtual resolution too high to fit into video memory!\n",
600 var->xres_virtual, var->yres_virtual,
601 var->bits_per_pixel);
602 return -EINVAL;
603 }
604
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700605 if (var->xoffset < 0)
606 var->xoffset = 0;
607 if (var->yoffset < 0)
608 var->yoffset = 0;
609
610 /* truncate xoffset and yoffset to maximum if too high */
611 if (var->xoffset > var->xres_virtual - var->xres)
612 var->xoffset = var->xres_virtual - var->xres - 1;
613 if (var->yoffset > var->yres_virtual - var->yres)
614 var->yoffset = var->yres_virtual - var->yres - 1;
615
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 var->red.msb_right =
617 var->green.msb_right =
618 var->blue.msb_right =
619 var->transp.offset =
620 var->transp.length =
621 var->transp.msb_right = 0;
622
623 yres = var->yres;
624 if (var->vmode & FB_VMODE_DOUBLE)
625 yres *= 2;
626 else if (var->vmode & FB_VMODE_INTERLACED)
627 yres = (yres + 1) / 2;
628
629 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700630 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700631 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 return -EINVAL;
633 }
634
Krzysztof Helt99a45842009-03-31 15:25:09 -0700635 if (cirrusfb_check_pixclock(var, info))
636 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700638 if (!is_laguna(cinfo))
639 var->accel_flags = FB_ACCELF_TEXT;
640
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 return 0;
642}
643
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700644static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700646 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700647 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700648
Krzysztof Helt8503df62007-10-16 01:29:08 -0700649 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700650 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Krzysztof Helt486ff382008-10-15 22:03:42 -0700652 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700653 dev_dbg(info->device, "Set %s as pixclock source.\n",
654 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700655 old1f |= 0x40;
656 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
657 if (div == 2)
658 old1e |= 1;
659
660 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700662 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663}
664
665/*************************************************************************
666 cirrusfb_set_par_foo()
667
668 actually writes the values for a new video mode into the hardware,
669**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700670static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671{
672 struct cirrusfb_info *cinfo = info->par;
673 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 u8 __iomem *regbase = cinfo->regbase;
675 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700676 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700678 int hdispend, hsyncstart, hsyncend, htotal;
679 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700680 long freq;
681 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700682 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700684 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700686
687 switch (var->bits_per_pixel) {
688 case 1:
689 info->fix.line_length = var->xres_virtual / 8;
690 info->fix.visual = FB_VISUAL_MONO10;
691 break;
692
693 case 8:
694 info->fix.line_length = var->xres_virtual;
695 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
696 break;
697
698 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700699 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700700 info->fix.line_length = var->xres_virtual *
701 var->bits_per_pixel >> 3;
702 info->fix.visual = FB_VISUAL_TRUECOLOR;
703 break;
704 }
705 info->fix.type = FB_TYPE_PACKED_PIXELS;
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 bi = &cirrusfb_board_info[cinfo->btype];
710
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700711 hsyncstart = var->xres + var->right_margin;
712 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700713 htotal = (hsyncend + var->left_margin) / 8;
714 hdispend = var->xres / 8;
715 hsyncstart = hsyncstart / 8;
716 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700717
Krzysztof Helt8636a922009-03-31 15:25:17 -0700718 vdispend = var->yres;
719 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700720 vsyncend = vsyncstart + var->vsync_len;
721 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700722
723 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700724 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700725 vsyncstart *= 2;
726 vsyncend *= 2;
727 vtotal *= 2;
728 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700729 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700730 vsyncstart = (vsyncstart + 1) / 2;
731 vsyncend = (vsyncend + 1) / 2;
732 vtotal = (vtotal + 1) / 2;
733 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700734 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700735 if (yres >= 1024) {
736 vtotal /= 2;
737 vsyncstart /= 2;
738 vsyncend /= 2;
739 vdispend /= 2;
740 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700741
742 vdispend -= 1;
743 vsyncstart -= 1;
744 vsyncend -= 1;
745 vtotal -= 2;
746
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700747 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700748 htotal /= 2;
749 hsyncstart /= 2;
750 hsyncend /= 2;
751 hdispend /= 2;
752 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700753
754 htotal -= 5;
755 hdispend -= 1;
756 hsyncstart += 1;
757 hsyncend += 1;
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);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700849 if (var->bits_per_pixel == 24)
850 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
851 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700852 if (cinfo->multiplexing)
853 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700854 if (cinfo->doubleVCLK)
855 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700856
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700857 bestclock(freq, &nom, &den, &div);
858
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700859 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
860 freq, nom, den, div);
861
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 /* set VCLK0 */
863 /* hardware RefClock: 14.31818 MHz */
864 /* formula: VClk = (OSC * N) / (D * (1+P)) */
865 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
866
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700867 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
868 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700869 /* if freq is close to mclk or mclk/2 select mclk
870 * as clock source
871 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700872 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700873 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700874 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700875 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700876 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700877 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700878 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
879 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
880 unsigned short tile_control;
881
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700882 if (cinfo->btype == BT_LAGUNAB) {
883 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
884 tile_control &= ~0x80;
885 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
886 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700887
888 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
889 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
890 control = fb_readw(cinfo->laguna_mmio + 0x402);
891 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
892 control &= ~0x6800;
893 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700894 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700895 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700896 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700897 tmp = den << 1;
898 if (div != 0)
899 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700900 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
901 if ((cinfo->btype == BT_SD64) ||
902 (cinfo->btype == BT_ALPINE) ||
903 (cinfo->btype == BT_GD5480))
904 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700906 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700907 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700908 vga_wseq(regbase, CL_SEQRE, tmp);
909 vga_wseq(regbase, CL_SEQR1E, nom);
910 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700911 vga_wseq(regbase, CL_SEQRE, nom);
912 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700913 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700914 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700916 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700918 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 else
920 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
921 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700922 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 /* don't know if it would hurt to also program this if no interlaced */
925 /* mode is used, but I feel better this way.. :-) */
926 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700927 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700931 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700932 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700933 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
935 tmp |= 0x40;
936 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
937 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700938 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939
Krzysztof Helt8503df62007-10-16 01:29:08 -0700940 /* text cursor on and start line */
941 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
942 /* text cursor end line */
943 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
945 /******************************************************
946 *
947 * 1 bpp
948 *
949 */
950
951 /* programming for different color depths */
952 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700953 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956 /* SR07 */
957 switch (cinfo->btype) {
958 case BT_SD64:
959 case BT_PICCOLO:
960 case BT_PICASSO:
961 case BT_SPECTRUM:
962 case BT_PICASSO4:
963 case BT_ALPINE:
964 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700966 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 bi->sr07_1bpp_mux : bi->sr07_1bpp);
968 break;
969
970 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700971 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700972 vga_wseq(regbase, CL_SEQR7,
973 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 break;
975
976 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700977 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 break;
979 }
980
981 /* Extended Sequencer Mode */
982 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983
984 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700985 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700986 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
987 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 break;
989
990 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700991 /* ## vorher d0 avoid FIFO underruns..? */
992 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 break;
994
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700995 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 case BT_PICASSO4:
997 case BT_ALPINE:
998 case BT_GD5480:
999 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001000 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 /* do nothing */
1002 break;
1003
1004 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001005 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 break;
1007 }
1008
Krzysztof Helt8503df62007-10-16 01:29:08 -07001009 /* pixel mask: pass-through for first plane */
1010 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001011 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001012 /* hidden dac reg: 1280x1024 */
1013 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001015 /* hidden dac: nothing */
1016 WHDR(cinfo, 0);
1017 /* memory mode: odd/even, ext. memory */
1018 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1019 /* plane mask: only write to first plane */
1020 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 }
1022
1023 /******************************************************
1024 *
1025 * 8 bpp
1026 *
1027 */
1028
1029 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001030 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 switch (cinfo->btype) {
1032 case BT_SD64:
1033 case BT_PICCOLO:
1034 case BT_PICASSO:
1035 case BT_SPECTRUM:
1036 case BT_PICASSO4:
1037 case BT_ALPINE:
1038 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001039 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001040 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1042 break;
1043
1044 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001045 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046 vga_wseq(regbase, CL_SEQR7,
1047 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001048 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 break;
1050
1051 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001052 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 break;
1054 }
1055
1056 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001060 /* Fast Page-Mode writes */
1061 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 break;
1063
1064 case BT_PICASSO4:
1065#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001066 /* ### INCOMPLETE!! */
1067 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001070 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 case BT_GD5480:
1072 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001073 case BT_LAGUNAB:
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);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001084 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001085 /* hidden dac reg: 1280x1024 */
1086 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001088 /* hidden dac: nothing */
1089 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 }
1091
1092 /******************************************************
1093 *
1094 * 16 bpp
1095 *
1096 */
1097
1098 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001099 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001102 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001103 vga_wseq(regbase, CL_SEQR7, 0x87);
1104 /* Fast Page-Mode writes */
1105 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 break;
1107
1108 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001109 vga_wseq(regbase, CL_SEQR7, 0x27);
1110 /* Fast Page-Mode writes */
1111 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 break;
1113
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001114 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001117 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001118 vga_wseq(regbase, CL_SEQR7,
1119 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 break;
1121
1122 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001123 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 /* We already set SRF and SR1F */
1125 break;
1126
1127 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001128 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001129 vga_wseq(regbase, CL_SEQR7,
1130 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001131 control |= 0x2000;
1132 format |= 0x1400;
1133 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 break;
1135
1136 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001137 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 break;
1139 }
1140
Krzysztof Helt8503df62007-10-16 01:29:08 -07001141 /* mode register: 256 color mode */
1142 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001144 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145#elif defined(CONFIG_ZORRO)
1146 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001147 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 }
1150
1151 /******************************************************
1152 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001153 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 *
1155 */
1156
Krzysztof Helt7cade312009-03-31 15:25:13 -07001157 else if (var->bits_per_pixel == 24) {
1158 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001161 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001162 vga_wseq(regbase, CL_SEQR7, 0x85);
1163 /* Fast Page-Mode writes */
1164 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 break;
1166
1167 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001168 vga_wseq(regbase, CL_SEQR7, 0x25);
1169 /* Fast Page-Mode writes */
1170 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 break;
1172
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001173 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001176 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001177 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 break;
1179
1180 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001181 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 /* We already set SRF and SR1F */
1183 break;
1184
1185 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001186 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001187 vga_wseq(regbase, CL_SEQR7,
1188 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001189 control |= 0x4000;
1190 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001191 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 break;
1193
1194 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001195 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 break;
1197 }
1198
Krzysztof Helt8503df62007-10-16 01:29:08 -07001199 /* mode register: 256 color mode */
1200 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001201 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1202 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 }
1204
1205 /******************************************************
1206 *
1207 * unknown/unsupported bpp
1208 *
1209 */
1210
Krzysztof Helt8503df62007-10-16 01:29:08 -07001211 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001212 dev_err(info->device,
1213 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
Krzysztof Helt6683e012009-03-31 15:25:06 -07001216 pitch = info->fix.line_length >> 3;
1217 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001219 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 tmp |= 0x10; /* offset overflow bit */
1221
Krzysztof Helt8503df62007-10-16 01:29:08 -07001222 /* screen start addr #16-18, fastpagemode cycles */
1223 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001225 /* screen start address bit 19 */
1226 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001227 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001229 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001230 tmp = 0;
1231 if ((htotal + 5) & 256)
1232 tmp |= 128;
1233 if (hdispend & 256)
1234 tmp |= 64;
1235 if (hsyncstart & 256)
1236 tmp |= 48;
1237 if (vtotal & 1024)
1238 tmp |= 8;
1239 if (vdispend & 1024)
1240 tmp |= 4;
1241 if (vsyncstart & 1024)
1242 tmp |= 3;
1243
1244 vga_wcrt(regbase, CL_CRT1E, tmp);
1245 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1246 }
1247
Krzysztof Helt8503df62007-10-16 01:29:08 -07001248 /* pixel panning */
1249 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250
1251 /* [ EGS: SetOffset(); ] */
1252 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001253 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001255 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001256 /* no tiles */
1257 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1258 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1259 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1260 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 /* finally, turn on everything - turn off "FullBandwidth" bit */
1262 /* also, set "DotClock%2" bit where requested */
1263 tmp = 0x01;
1264
1265/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1266 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1267 tmp |= 0x08;
1268*/
1269
Krzysztof Helt8503df62007-10-16 01:29:08 -07001270 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001271 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001274 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275#endif
1276
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 return 0;
1278}
1279
1280/* for some reason incomprehensible to me, cirrusfb requires that you write
1281 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001282static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001284 cirrusfb_set_par_foo(info);
1285 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286}
1287
Krzysztof Helt8503df62007-10-16 01:29:08 -07001288static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1289 unsigned blue, unsigned transp,
1290 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291{
1292 struct cirrusfb_info *cinfo = info->par;
1293
1294 if (regno > 255)
1295 return -EINVAL;
1296
1297 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1298 u32 v;
1299 red >>= (16 - info->var.red.length);
1300 green >>= (16 - info->var.green.length);
1301 blue >>= (16 - info->var.blue.length);
1302
Krzysztof Helt8503df62007-10-16 01:29:08 -07001303 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 return 1;
1305 v = (red << info->var.red.offset) |
1306 (green << info->var.green.offset) |
1307 (blue << info->var.blue.offset);
1308
Krzysztof Helt060b6002007-10-16 01:29:13 -07001309 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 return 0;
1311 }
1312
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313 if (info->var.bits_per_pixel == 8)
1314 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315
1316 return 0;
1317
1318}
1319
1320/*************************************************************************
1321 cirrusfb_pan_display()
1322
1323 performs display panning - provided hardware permits this
1324**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001325static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1326 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001328 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001330 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 struct cirrusfb_info *cinfo = info->par;
1332
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 /* no range checks for xoffset and yoffset, */
1334 /* as fb_pan_display has already done this */
1335 if (var->vmode & FB_VMODE_YWRAP)
1336 return -EINVAL;
1337
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
Krzysztof Helt99a45842009-03-31 15:25:09 -07001340 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341
1342 if (info->var.bits_per_pixel == 1) {
1343 /* base is already correct */
1344 xpix = (unsigned char) (var->xoffset % 8);
1345 } else {
1346 base /= 4;
1347 xpix = (unsigned char) ((xoffset % 4) * 2);
1348 }
1349
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001350 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001351 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
1353 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001354 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1355 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001357 /* 0xf2 is %11110010, exclude tmp bits */
1358 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 /* construct bits 16, 17 and 18 of screen start address */
1360 if (base & 0x10000)
1361 tmp |= 0x01;
1362 if (base & 0x20000)
1363 tmp |= 0x04;
1364 if (base & 0x40000)
1365 tmp |= 0x08;
1366
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001367 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
1369 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001370 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001371 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1372 if (is_laguna(cinfo))
1373 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1374 else
1375 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001376 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1377 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
Krzysztof Helt8503df62007-10-16 01:29:08 -07001379 /* write pixel panning value to AR33; this does not quite work in 8bpp
1380 *
1381 * ### Piccolo..? Will this work?
1382 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001384 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
Krzysztof Helt8503df62007-10-16 01:29:08 -07001386 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387}
1388
Krzysztof Helt8503df62007-10-16 01:29:08 -07001389static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390{
1391 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001392 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1393 * then the caller blanks by setting the CLUT (Color Look Up Table)
1394 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1395 * failed due to e.g. a video mode which doesn't support it.
1396 * Implements VESA suspend and powerdown modes on hardware that
1397 * supports disabling hsync/vsync:
1398 * blank_mode == 2: suspend vsync
1399 * blank_mode == 3: suspend hsync
1400 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 */
1402 unsigned char val;
1403 struct cirrusfb_info *cinfo = info->par;
1404 int current_mode = cinfo->blank_mode;
1405
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001406 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
1408 if (info->state != FBINFO_STATE_RUNNING ||
1409 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001410 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 return 0;
1412 }
1413
1414 /* Undo current */
1415 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001416 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001417 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001418 val = 0;
1419 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001420 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001421 val = 0x20;
1422
1423 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1424 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425
1426 switch (blank_mode) {
1427 case FB_BLANK_UNBLANK:
1428 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001429 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 break;
1431 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001432 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 break;
1434 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001435 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 break;
1437 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001438 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 break;
1440 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001441 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 return 1;
1443 }
1444
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001445 vga_wgfx(cinfo->regbase, CL_GRE, val);
1446
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001448 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449
1450 /* Let fbcon do a soft blank for us */
1451 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1452}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001453
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454/**** END Hardware specific Routines **************************************/
1455/****************************************************************************/
1456/**** BEGIN Internal Routines ***********************************************/
1457
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001458static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001460 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 const struct cirrusfb_board_info_rec *bi;
1462
Krzysztof Helt8503df62007-10-16 01:29:08 -07001463 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
1465 bi = &cirrusfb_board_info[cinfo->btype];
1466
1467 /* reset board globally */
1468 switch (cinfo->btype) {
1469 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001470 WSFR(cinfo, 0x01);
1471 udelay(500);
1472 WSFR(cinfo, 0x51);
1473 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 break;
1475 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001476 WSFR2(cinfo, 0xff);
1477 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 break;
1479 case BT_SD64:
1480 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 WSFR(cinfo, 0x1f);
1482 udelay(500);
1483 WSFR(cinfo, 0x4f);
1484 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 break;
1486 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001487 /* disable flickerfixer */
1488 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1489 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001490 /* mode */
1491 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001492 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001493 /* from Klaus' NetBSD driver: */
1494 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001495 case BT_ALPINE: /* fall through */
1496 /* put blitter into 542x compat */
1497 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 break;
1499
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001500 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001501 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 /* Nothing to do to reset the board. */
1503 break;
1504
1505 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001506 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 break;
1508 }
1509
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001510 /* make sure RAM size set by this point */
1511 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
1513 /* the P4 is not fully initialized here; I rely on it having been */
1514 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001515 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
1517 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001518 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1519 WGen(cinfo, CL_POS102, 0x01);
1520 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
1522 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001523 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Krzysztof Helt8503df62007-10-16 01:29:08 -07001525 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001526 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
Krzysztof Helt8503df62007-10-16 01:29:08 -07001528 /* FullBandwidth (video off) and 8/9 dot clock */
1529 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530
Krzysztof Helt8503df62007-10-16 01:29:08 -07001531 /* "magic cookie" - doesn't make any sense to me.. */
1532/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1533 /* unlock all extension registers */
1534 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 switch (cinfo->btype) {
1537 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001538 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 break;
1540 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001541 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001542 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 break;
1544 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001545#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001546 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001547#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 break;
1549 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1551 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 break;
1553 }
1554 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 /* plane mask: nothing */
1556 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1557 /* character map select: doesn't even matter in gx mode */
1558 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001559 /* memory mode: chain4, ext. memory */
1560 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561
1562 /* controller-internal base address of video memory */
1563 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001564 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
Krzysztof Helt8503df62007-10-16 01:29:08 -07001566 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1567 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
Krzysztof Helt8503df62007-10-16 01:29:08 -07001569 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1570 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1571 /* graphics cursor Y position (..."... ) */
1572 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1573 /* graphics cursor attributes */
1574 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1575 /* graphics cursor pattern address */
1576 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
1578 /* writing these on a P4 might give problems.. */
1579 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 /* configuration readback and ext. color */
1581 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1582 /* signature generator */
1583 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 }
1585
Krzysztof Helt8503df62007-10-16 01:29:08 -07001586 /* Screen A preset row scan: none */
1587 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1588 /* Text cursor start: disable text cursor */
1589 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1590 /* Text cursor end: - */
1591 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001592 /* text cursor location high: 0 */
1593 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1594 /* text cursor location low: 0 */
1595 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596
Krzysztof Helt8503df62007-10-16 01:29:08 -07001597 /* Underline Row scanline: - */
1598 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001600 /* ext. display controls: ext.adr. wrap */
1601 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Krzysztof Helt8503df62007-10-16 01:29:08 -07001603 /* Set/Reset registes: - */
1604 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1605 /* Set/Reset enable: - */
1606 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1607 /* Color Compare: - */
1608 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1609 /* Data Rotate: - */
1610 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1611 /* Read Map Select: - */
1612 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1613 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1614 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1615 /* Miscellaneous: memory map base address, graphics mode */
1616 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1617 /* Color Don't care: involve all planes */
1618 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1619 /* Bit Mask: no mask at all */
1620 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001621
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001622 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1623 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001624 /* (5434 can't have bit 3 set for bitblt) */
1625 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001627 /* Graphics controller mode extensions: finer granularity,
1628 * 8byte data latches
1629 */
1630 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
Krzysztof Helt8503df62007-10-16 01:29:08 -07001632 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1633 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1634 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1635 /* Background color byte 1: - */
1636 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1637 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
Krzysztof Helt8503df62007-10-16 01:29:08 -07001639 /* Attribute Controller palette registers: "identity mapping" */
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1644 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1645 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1646 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1647 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1648 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1649 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1650 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1651 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1652 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1653 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1654 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1655 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 /* Attribute Controller mode: graphics mode */
1658 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1659 /* Overscan color reg.: reg. 0 */
1660 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1661 /* Color Plane enable: Enable all 4 planes */
1662 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001663 /* Color Select: - */
1664 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
Krzysztof Helt8503df62007-10-16 01:29:08 -07001666 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
Krzysztof Helt8503df62007-10-16 01:29:08 -07001668 /* BLT Start/status: Blitter reset */
1669 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1670 /* - " - : "end-of-reset" */
1671 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
1673 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001674 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 return;
1676}
1677
Krzysztof Helt8503df62007-10-16 01:29:08 -07001678static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679{
1680#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1681 static int IsOn = 0; /* XXX not ok for multiple boards */
1682
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 if (cinfo->btype == BT_PICASSO4)
1684 return; /* nothing to switch */
1685 if (cinfo->btype == BT_ALPINE)
1686 return; /* nothing to switch */
1687 if (cinfo->btype == BT_GD5480)
1688 return; /* nothing to switch */
1689 if (cinfo->btype == BT_PICASSO) {
1690 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 return;
1693 }
1694 if (on) {
1695 switch (cinfo->btype) {
1696 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 break;
1699 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001700 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 break;
1702 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001703 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 break;
1705 default: /* do nothing */ break;
1706 }
1707 } else {
1708 switch (cinfo->btype) {
1709 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001710 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 break;
1712 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001713 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 break;
1715 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001716 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001718 default: /* do nothing */
1719 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 }
1721 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722#endif /* CONFIG_ZORRO */
1723}
1724
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725/******************************************/
1726/* Linux 2.6-style accelerated functions */
1727/******************************************/
1728
Krzysztof Helt8343c892009-03-31 15:25:11 -07001729static int cirrusfb_sync(struct fb_info *info)
1730{
1731 struct cirrusfb_info *cinfo = info->par;
1732
1733 if (!is_laguna(cinfo)) {
1734 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1735 cpu_relax();
1736 }
1737 return 0;
1738}
1739
Krzysztof Helt8503df62007-10-16 01:29:08 -07001740static void cirrusfb_fillrect(struct fb_info *info,
1741 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 struct fb_fillrect modded;
1744 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001745 struct cirrusfb_info *cinfo = info->par;
1746 int m = info->var.bits_per_pixel;
1747 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1748 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749
1750 if (info->state != FBINFO_STATE_RUNNING)
1751 return;
1752 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1753 cfb_fillrect(info, region);
1754 return;
1755 }
1756
1757 vxres = info->var.xres_virtual;
1758 vyres = info->var.yres_virtual;
1759
1760 memcpy(&modded, region, sizeof(struct fb_fillrect));
1761
Krzysztof Helt8503df62007-10-16 01:29:08 -07001762 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 modded.dx >= vxres || modded.dy >= vyres)
1764 return;
1765
Krzysztof Helt8503df62007-10-16 01:29:08 -07001766 if (modded.dx + modded.width > vxres)
1767 modded.width = vxres - modded.dx;
1768 if (modded.dy + modded.height > vyres)
1769 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
Krzysztof Helt060b6002007-10-16 01:29:13 -07001771 cirrusfb_RectFill(cinfo->regbase,
1772 info->var.bits_per_pixel,
1773 (region->dx * m) / 8, region->dy,
1774 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001775 color, color,
1776 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777}
1778
Krzysztof Helt8503df62007-10-16 01:29:08 -07001779static void cirrusfb_copyarea(struct fb_info *info,
1780 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 struct fb_copyarea modded;
1783 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001784 struct cirrusfb_info *cinfo = info->par;
1785 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786
1787 if (info->state != FBINFO_STATE_RUNNING)
1788 return;
1789 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1790 cfb_copyarea(info, area);
1791 return;
1792 }
1793
1794 vxres = info->var.xres_virtual;
1795 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001796 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
Krzysztof Helt8503df62007-10-16 01:29:08 -07001798 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 modded.sx >= vxres || modded.sy >= vyres ||
1800 modded.dx >= vxres || modded.dy >= vyres)
1801 return;
1802
Krzysztof Helt8503df62007-10-16 01:29:08 -07001803 if (modded.sx + modded.width > vxres)
1804 modded.width = vxres - modded.sx;
1805 if (modded.dx + modded.width > vxres)
1806 modded.width = vxres - modded.dx;
1807 if (modded.sy + modded.height > vyres)
1808 modded.height = vyres - modded.sy;
1809 if (modded.dy + modded.height > vyres)
1810 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811
Krzysztof Helt060b6002007-10-16 01:29:13 -07001812 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1813 (area->sx * m) / 8, area->sy,
1814 (area->dx * m) / 8, area->dy,
1815 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001816 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001817
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818}
1819
Krzysztof Helt8503df62007-10-16 01:29:08 -07001820static void cirrusfb_imageblit(struct fb_info *info,
1821 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822{
1823 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001824 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
Krzysztof Helt9e848062009-03-31 15:25:11 -07001826 if (info->state != FBINFO_STATE_RUNNING)
1827 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001828 /* Alpine/SD64 does not work at 24bpp ??? */
1829 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1830 cfb_imageblit(info, image);
1831 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1832 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001833 cfb_imageblit(info, image);
1834 else {
1835 unsigned size = ((image->width + 7) >> 3) * image->height;
1836 int m = info->var.bits_per_pixel;
1837 u32 fg, bg;
1838
1839 if (info->var.bits_per_pixel == 8) {
1840 fg = image->fg_color;
1841 bg = image->bg_color;
1842 } else {
1843 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1844 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1845 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001846 if (info->var.bits_per_pixel == 24) {
1847 /* clear background first */
1848 cirrusfb_RectFill(cinfo->regbase,
1849 info->var.bits_per_pixel,
1850 (image->dx * m) / 8, image->dy,
1851 (image->width * m) / 8,
1852 image->height,
1853 bg, bg,
1854 info->fix.line_length, 0x40);
1855 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001856 cirrusfb_RectFill(cinfo->regbase,
1857 info->var.bits_per_pixel,
1858 (image->dx * m) / 8, image->dy,
1859 (image->width * m) / 8, image->height,
1860 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001861 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001862 memcpy(info->screen_base, image->data, size);
1863 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864}
1865
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866#ifdef CONFIG_PPC_PREP
1867#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1868#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001869static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 *display = PREP_VIDEO_BASE;
1872 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873}
1874
1875#endif /* CONFIG_PPC_PREP */
1876
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001878static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879
1880/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1881 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1882 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1883 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001884static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1885 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886{
1887 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001888 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001890 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001891 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1892
1893 mem = ((SR14 & 7) + 1) << 20;
1894 } else {
1895 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1896 switch ((SRF & 0x18)) {
1897 case 0x08:
1898 mem = 512 * 1024;
1899 break;
1900 case 0x10:
1901 mem = 1024 * 1024;
1902 break;
1903 /* 64-bit DRAM data bus width; assume 2MB.
1904 * Also indicates 2MB memory on the 5430.
1905 */
1906 case 0x18:
1907 mem = 2048 * 1024;
1908 break;
1909 default:
1910 dev_warn(info->device, "Unknown memory size!\n");
1911 mem = 1024 * 1024;
1912 }
1913 /* If DRAM bank switching is enabled, there must be
1914 * twice as much memory installed. (4MB on the 5434)
1915 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001916 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001917 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001919
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921 return mem;
1922}
1923
Krzysztof Helt8503df62007-10-16 01:29:08 -07001924static void get_pci_addrs(const struct pci_dev *pdev,
1925 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001927 assert(pdev != NULL);
1928 assert(display != NULL);
1929 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 *display = 0;
1932 *registers = 0;
1933
1934 /* This is a best-guess for now */
1935
1936 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1937 *display = pci_resource_start(pdev, 1);
1938 *registers = pci_resource_start(pdev, 0);
1939 } else {
1940 *display = pci_resource_start(pdev, 0);
1941 *registers = pci_resource_start(pdev, 1);
1942 }
1943
Krzysztof Helt8503df62007-10-16 01:29:08 -07001944 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945}
1946
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001947static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001949 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001950 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001952 if (cinfo->laguna_mmio == NULL)
1953 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001954 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955#if 0 /* if system didn't claim this region, we would... */
1956 release_mem_region(0xA0000, 65535);
1957#endif
1958 if (release_io_ports)
1959 release_region(0x3C0, 32);
1960 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961}
1962#endif /* CONFIG_PCI */
1963
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001965static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966{
Al Virod91f5bb2007-10-17 00:27:18 +01001967 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001968 struct zorro_dev *zdev = to_zorro_dev(info->device);
1969
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001970 if (info->fix.smem_start > 16 * MB_)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001971 iounmap(info->screen_base);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001972 if (info->fix.mmio_start > 16 * MB_)
1973 iounmap(cinfo->regbase);
1974
1975 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976}
1977#endif /* CONFIG_ZORRO */
1978
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001979/* function table of the above functions */
1980static struct fb_ops cirrusfb_ops = {
1981 .owner = THIS_MODULE,
1982 .fb_open = cirrusfb_open,
1983 .fb_release = cirrusfb_release,
1984 .fb_setcolreg = cirrusfb_setcolreg,
1985 .fb_check_var = cirrusfb_check_var,
1986 .fb_set_par = cirrusfb_set_par,
1987 .fb_pan_display = cirrusfb_pan_display,
1988 .fb_blank = cirrusfb_blank,
1989 .fb_fillrect = cirrusfb_fillrect,
1990 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001991 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001992 .fb_imageblit = cirrusfb_imageblit,
1993};
1994
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001995static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001997 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 struct fb_var_screeninfo *var = &info->var;
1999
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 info->pseudo_palette = cinfo->pseudo_palette;
2001 info->flags = FBINFO_DEFAULT
2002 | FBINFO_HWACCEL_XPAN
2003 | FBINFO_HWACCEL_YPAN
2004 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002005 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002007 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002009 info->fix.accel = FB_ACCEL_NONE;
2010 } else
2011 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
2012
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002014
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 if (cinfo->btype == BT_GD5480) {
2016 if (var->bits_per_pixel == 16)
2017 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002018 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 info->screen_base += 2 * MB_;
2020 }
2021
2022 /* Fill fix common fields */
2023 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2024 sizeof(info->fix.id));
2025
2026 /* monochrome: only 1 memory plane */
2027 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002028 info->fix.smem_len = info->screen_size;
2029 if (var->bits_per_pixel == 1)
2030 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 info->fix.xpanstep = 1;
2033 info->fix.ypanstep = 1;
2034 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035
2036 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038
2039 fb_alloc_cmap(&info->cmap, 256, 0);
2040
2041 return 0;
2042}
2043
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002044static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002046 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048
2049 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002050 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002052 /* set all the vital stuff */
2053 cirrusfb_set_fbinfo(info);
2054
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002055 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002057 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2058 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002059 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002060 err = -EINVAL;
2061 goto err_dealloc_cmap;
2062 }
2063
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 info->var.activate = FB_ACTIVATE_NOW;
2065
Krzysztof Helt99a45842009-03-31 15:25:09 -07002066 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 if (err < 0) {
2068 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002069 dev_dbg(info->device,
2070 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002071 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 }
2073
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 err = register_framebuffer(info);
2075 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002076 dev_err(info->device,
2077 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 goto err_dealloc_cmap;
2079 }
2080
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 return 0;
2082
2083err_dealloc_cmap:
2084 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 return err;
2086}
2087
Krzysztof Helt8503df62007-10-16 01:29:08 -07002088static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089{
2090 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091
Krzysztof Helt8503df62007-10-16 01:29:08 -07002092 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002093 unregister_framebuffer(info);
2094 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002095 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002096 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002097 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098}
2099
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002101static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2102 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103{
2104 struct cirrusfb_info *cinfo;
2105 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 unsigned long board_addr, board_size;
2107 int ret;
2108
2109 ret = pci_enable_device(pdev);
2110 if (ret < 0) {
2111 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2112 goto err_out;
2113 }
2114
2115 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2116 if (!info) {
2117 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2118 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002119 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120 }
2121
2122 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002123 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002125 dev_dbg(info->device,
2126 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002127 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002128 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2129 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130
Krzysztof Helt8503df62007-10-16 01:29:08 -07002131 if (isPReP) {
2132 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002134 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002136 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002137 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002139 dev_dbg(info->device,
2140 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002141 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002142 /* FIXME: this forces VGA. alternatives? */
2143 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002144 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 }
2146
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002147 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002148 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002150 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002151 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152
2153 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002154 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002155 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2156 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 goto err_release_fb;
2158 }
2159#if 0 /* if the system didn't claim this region, we would... */
2160 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002161 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2162 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 ret = -EBUSY;
2164 goto err_release_regions;
2165 }
2166#endif
2167 if (request_region(0x3C0, 32, "cirrusfb"))
2168 release_io_ports = 1;
2169
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002170 info->screen_base = ioremap(board_addr, board_size);
2171 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172 ret = -EIO;
2173 goto err_release_legacy;
2174 }
2175
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002176 info->fix.smem_start = board_addr;
2177 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 cinfo->unmap = cirrusfb_pci_unmap;
2179
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002180 dev_info(info->device,
2181 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2182 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 pci_set_drvdata(pdev, info);
2184
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002185 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002186 if (!ret)
2187 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002189 pci_set_drvdata(pdev, NULL);
2190 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191err_release_legacy:
2192 if (release_io_ports)
2193 release_region(0x3C0, 32);
2194#if 0
2195 release_mem_region(0xA0000, 65535);
2196err_release_regions:
2197#endif
2198 pci_release_regions(pdev);
2199err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002200 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002201 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203err_out:
2204 return ret;
2205}
2206
Krzysztof Helt8503df62007-10-16 01:29:08 -07002207static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208{
2209 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210
Krzysztof Helt8503df62007-10-16 01:29:08 -07002211 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212}
2213
2214static struct pci_driver cirrusfb_pci_driver = {
2215 .name = "cirrusfb",
2216 .id_table = cirrusfb_pci_table,
2217 .probe = cirrusfb_pci_register,
2218 .remove = __devexit_p(cirrusfb_pci_unregister),
2219#ifdef CONFIG_PM
2220#if 0
2221 .suspend = cirrusfb_pci_suspend,
2222 .resume = cirrusfb_pci_resume,
2223#endif
2224#endif
2225};
2226#endif /* CONFIG_PCI */
2227
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002229static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2230 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 struct fb_info *info;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002233 int error;
2234 const struct zorrocl *zcl;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002235 enum cirrus_board btype;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002236 unsigned long regbase, ramsize, rambase;
2237 struct cirrusfb_info *cinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238
2239 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2240 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002241 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002242 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 }
2244
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002245 zcl = (const struct zorrocl *)ent->driver_data;
2246 btype = zcl->type;
2247 regbase = zorro_resource_start(z) + zcl->regoffset;
2248 ramsize = zcl->ramsize;
2249 if (ramsize) {
2250 rambase = zorro_resource_start(z) + zcl->ramoffset;
2251 } else {
2252 struct zorro_dev *ram = zorro_find_device(zcl->ramid, NULL);
2253 if (!ram || !zorro_resource_len(ram)) {
2254 dev_err(info->device, "No video RAM found\n");
2255 error = -ENODEV;
2256 goto err_release_fb;
2257 }
2258 rambase = zorro_resource_start(ram);
2259 ramsize = zorro_resource_len(ram);
2260 }
2261
2262 dev_info(info->device,
2263 "%s board detected, REG at 0x%lx, %lu MiB RAM at 0x%lx\n",
2264 cirrusfb_board_info[btype].name, regbase, ramsize / MB_,
2265 rambase);
2266
2267 if (!zorro_request_device(z, "cirrusfb")) {
2268 dev_err(info->device, "Cannot reserve %pR\n", &z->resource);
2269 error = -EBUSY;
2270 goto err_release_fb;
2271 }
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002272
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 cinfo->btype = btype;
2275
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002276 info->fix.mmio_start = regbase;
2277 cinfo->regbase = regbase > 16 * MB_ ? ioremap(regbase, 64 * 1024)
2278 : (caddr_t)ZTWO_VADDR(regbase);
2279 if (!cinfo->regbase) {
2280 dev_err(info->device, "Cannot map registers\n");
2281 error = -EIO;
2282 goto err_release_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 }
2284
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002285 info->fix.smem_start = rambase;
2286 info->screen_size = ramsize;
2287 info->screen_base = rambase > 16 * MB_ ? ioremap(rambase, ramsize)
2288 : (caddr_t)ZTWO_VADDR(rambase);
2289 if (!info->screen_base) {
2290 dev_err(info->device, "Cannot map video RAM\n");
2291 error = -EIO;
2292 goto err_unmap_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002294
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 cinfo->unmap = cirrusfb_zorro_unmap;
2296
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002297 dev_info(info->device,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002298 "Cirrus Logic chipset on Zorro bus, RAM (%lu MiB) at 0x%lx\n",
2299 ramsize / MB_, rambase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002301 /* MCLK select etc. */
2302 if (cirrusfb_board_info[btype].init_sr1f)
2303 vga_wseq(cinfo->regbase, CL_SEQR1F,
2304 cirrusfb_board_info[btype].sr1f);
2305
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002306 error = cirrusfb_register(info);
2307 if (error) {
2308 dev_err(info->device, "Failed to register device, error %d\n",
2309 error);
2310 goto err_unmap_ram;
2311 }
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002312
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002313 zorro_set_drvdata(z, info);
2314 return 0;
2315
2316err_unmap_ram:
2317 if (rambase > 16 * MB_)
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002318 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002320err_unmap_reg:
2321 if (regbase > 16 * MB_)
2322 iounmap(cinfo->regbase);
2323err_release_dev:
2324 zorro_release_device(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325err_release_fb:
2326 framebuffer_release(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002327 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328}
2329
2330void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2331{
2332 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333
Krzysztof Helt8503df62007-10-16 01:29:08 -07002334 cirrusfb_cleanup(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002335 zorro_set_drvdata(z, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336}
2337
2338static struct zorro_driver cirrusfb_zorro_driver = {
2339 .name = "cirrusfb",
2340 .id_table = cirrusfb_zorro_table,
2341 .probe = cirrusfb_zorro_register,
2342 .remove = __devexit_p(cirrusfb_zorro_unregister),
2343};
2344#endif /* CONFIG_ZORRO */
2345
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002347static int __init cirrusfb_setup(char *options)
2348{
Vlada Pericee119402008-11-19 15:36:45 -08002349 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 if (!options || !*options)
2352 return 0;
2353
Krzysztof Helt8503df62007-10-16 01:29:08 -07002354 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002355 if (!*this_opt)
2356 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 if (!strcmp(this_opt, "noaccel"))
2359 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002360 else if (!strncmp(this_opt, "mode:", 5))
2361 mode_option = this_opt + 5;
2362 else
2363 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364 }
2365 return 0;
2366}
2367#endif
2368
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369 /*
2370 * Modularization
2371 */
2372
2373MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2374MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2375MODULE_LICENSE("GPL");
2376
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002377static int __init cirrusfb_init(void)
2378{
2379 int error = 0;
2380
2381#ifndef MODULE
2382 char *option = NULL;
2383
2384 if (fb_get_options("cirrusfb", &option))
2385 return -ENODEV;
2386 cirrusfb_setup(option);
2387#endif
2388
2389#ifdef CONFIG_ZORRO
2390 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2391#endif
2392#ifdef CONFIG_PCI
2393 error |= pci_register_driver(&cirrusfb_pci_driver);
2394#endif
2395 return error;
2396}
2397
Krzysztof Helt8503df62007-10-16 01:29:08 -07002398static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399{
2400#ifdef CONFIG_PCI
2401 pci_unregister_driver(&cirrusfb_pci_driver);
2402#endif
2403#ifdef CONFIG_ZORRO
2404 zorro_unregister_driver(&cirrusfb_zorro_driver);
2405#endif
2406}
2407
2408module_init(cirrusfb_init);
2409
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002410module_param(mode_option, charp, 0);
2411MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002412module_param(noaccel, bool, 0);
2413MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002414
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415#ifdef MODULE
2416module_exit(cirrusfb_exit);
2417#endif
2418
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419/**********************************************************************/
2420/* about the following functions - I have used the same names for the */
2421/* functions as Markus Wild did in his Retina driver for NetBSD as */
2422/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002423/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424/**********************************************************************/
2425
2426/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002427static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 int regnum, unsigned char val)
2429{
2430 unsigned long regofs = 0;
2431
2432 if (cinfo->btype == BT_PICASSO) {
2433 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002434/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2435 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2437 regofs = 0xfff;
2438 }
2439
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441}
2442
2443/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002444static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445{
2446 unsigned long regofs = 0;
2447
2448 if (cinfo->btype == BT_PICASSO) {
2449 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002450/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2451 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2453 regofs = 0xfff;
2454 }
2455
Krzysztof Helt8503df62007-10-16 01:29:08 -07002456 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457}
2458
2459/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002462 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465 /* if we're just in "write value" mode, write back the */
2466 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002467 vga_w(cinfo->regbase, VGA_ATT_IW,
2468 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 }
2470 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002471/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2472 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473
2474 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002475 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476}
2477
2478/*** WHDR() - write into the Hidden DAC register ***/
2479/* as the HDR is the only extension register that requires special treatment
2480 * (the other extension registers are accessible just like the "ordinary"
2481 * registers of their functional group) here is a specialized routine for
2482 * accessing the HDR
2483 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002484static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485{
2486 unsigned char dummy;
2487
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002488 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002489 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 if (cinfo->btype == BT_PICASSO) {
2491 /* Klaus' hint for correct access to HDR on some boards */
2492 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002493 WGen(cinfo, VGA_PEL_MSK, 0x00);
2494 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496 dummy = RGen(cinfo, VGA_PEL_IW);
2497 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 }
2499 /* now do the usual stuff to access the HDR */
2500
Krzysztof Helt8503df62007-10-16 01:29:08 -07002501 dummy = RGen(cinfo, VGA_PEL_MSK);
2502 udelay(200);
2503 dummy = RGen(cinfo, VGA_PEL_MSK);
2504 udelay(200);
2505 dummy = RGen(cinfo, VGA_PEL_MSK);
2506 udelay(200);
2507 dummy = RGen(cinfo, VGA_PEL_MSK);
2508 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509
Krzysztof Helt8503df62007-10-16 01:29:08 -07002510 WGen(cinfo, VGA_PEL_MSK, val);
2511 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512
2513 if (cinfo->btype == BT_PICASSO) {
2514 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002515 dummy = RGen(cinfo, VGA_PEL_IW);
2516 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517
2518 /* and at the end, restore the mask value */
2519 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520 WGen(cinfo, VGA_PEL_MSK, 0xff);
2521 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 }
2523}
2524
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002526static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527{
2528#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002529 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002531 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532#endif
2533}
2534
2535/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002536static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537{
2538#ifdef CONFIG_ZORRO
2539 /* writing an arbitrary value to this one causes the monitor switcher */
2540 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544#endif
2545}
2546
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 unsigned char green, unsigned char blue)
2550{
2551 unsigned int data = VGA_PEL_D;
2552
2553 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555
2556 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002557 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002558 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 /* but DAC data register IS, at least for Picasso II */
2560 if (cinfo->btype == BT_PICASSO)
2561 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002562 vga_w(cinfo->regbase, data, red);
2563 vga_w(cinfo->regbase, data, green);
2564 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566 vga_w(cinfo->regbase, data, blue);
2567 vga_w(cinfo->regbase, data, green);
2568 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 }
2570}
2571
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572#if 0
2573/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002574static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 unsigned char *green, unsigned char *blue)
2576{
2577 unsigned int data = VGA_PEL_D;
2578
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580
2581 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2582 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2583 if (cinfo->btype == BT_PICASSO)
2584 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002585 *red = vga_r(cinfo->regbase, data);
2586 *green = vga_r(cinfo->regbase, data);
2587 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002589 *blue = vga_r(cinfo->regbase, data);
2590 *green = vga_r(cinfo->regbase, data);
2591 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592 }
2593}
2594#endif
2595
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596/*******************************************************************
2597 cirrusfb_WaitBLT()
2598
2599 Wait for the BitBLT engine to complete a possible earlier job
2600*********************************************************************/
2601
2602/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002603static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002605 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002606 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607}
2608
2609/*******************************************************************
2610 cirrusfb_BitBLT()
2611
2612 perform accelerated "scrolling"
2613********************************************************************/
2614
Krzysztof Helt8343c892009-03-31 15:25:11 -07002615static void cirrusfb_set_blitter(u8 __iomem *regbase,
2616 u_short nwidth, u_short nheight,
2617 u_long nsrc, u_long ndest,
2618 u_short bltmode, u_short line_length)
2619
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002622 /* dest pitch low */
2623 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2624 /* dest pitch hi */
2625 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2626 /* source pitch low */
2627 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2628 /* source pitch hi */
2629 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630
2631 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002632 /* BLT width low */
2633 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2634 /* BLT width hi */
2635 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636
2637 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002638 /* BLT height low */
2639 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2640 /* BLT width hi */
2641 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642
2643 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002644 /* BLT dest low */
2645 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2646 /* BLT dest mid */
2647 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2648 /* BLT dest hi */
2649 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650
2651 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002652 /* BLT src low */
2653 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2654 /* BLT src mid */
2655 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2656 /* BLT src hi */
2657 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658
2659 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002660 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661
2662 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664
2665 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002666 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002667}
2668
2669/*******************************************************************
2670 cirrusfb_BitBLT()
2671
2672 perform accelerated "scrolling"
2673********************************************************************/
2674
2675static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2676 u_short curx, u_short cury,
2677 u_short destx, u_short desty,
2678 u_short width, u_short height,
2679 u_short line_length)
2680{
2681 u_short nwidth = width - 1;
2682 u_short nheight = height - 1;
2683 u_long nsrc, ndest;
2684 u_char bltmode;
2685
2686 bltmode = 0x00;
2687 /* if source adr < dest addr, do the Blt backwards */
2688 if (cury <= desty) {
2689 if (cury == desty) {
2690 /* if src and dest are on the same line, check x */
2691 if (curx < destx)
2692 bltmode |= 0x01;
2693 } else
2694 bltmode |= 0x01;
2695 }
2696 /* standard case: forward blitting */
2697 nsrc = (cury * line_length) + curx;
2698 ndest = (desty * line_length) + destx;
2699 if (bltmode) {
2700 /* this means start addresses are at the end,
2701 * counting backwards
2702 */
2703 nsrc += nheight * line_length + nwidth;
2704 ndest += nheight * line_length + nwidth;
2705 }
2706
2707 cirrusfb_WaitBLT(regbase);
2708
2709 cirrusfb_set_blitter(regbase, nwidth, nheight,
2710 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711}
2712
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713/*******************************************************************
2714 cirrusfb_RectFill()
2715
2716 perform accelerated rectangle fill
2717********************************************************************/
2718
Krzysztof Helt8503df62007-10-16 01:29:08 -07002719static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002721 u32 fg_color, u32 bg_color, u_short line_length,
2722 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002724 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725 u_char op;
2726
Krzysztof Helt8503df62007-10-16 01:29:08 -07002727 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729 /* This is a ColorExpand Blt, using the */
2730 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002731 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2732 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
Krzysztof Helt9e848062009-03-31 15:25:11 -07002734 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002735 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002736 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2737 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2738 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002739 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002740 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002741 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2742 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002743 op = 0xa0;
2744 }
2745 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002746 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2747 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2748 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002750 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002751 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752}
2753
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754/**************************************************************************
2755 * bestclock() - determine closest possible clock lower(?) than the
2756 * desired pixel clock
2757 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002758static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002760 int n, d;
2761 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762
Krzysztof Helt8503df62007-10-16 01:29:08 -07002763 assert(nom != NULL);
2764 assert(den != NULL);
2765 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766
2767 *nom = 0;
2768 *den = 0;
2769 *div = 0;
2770
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 if (freq < 8000)
2772 freq = 8000;
2773
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002774 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775
2776 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002777 int s = 0;
2778
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002779 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002781 int temp = d;
2782
2783 if (temp > 31) {
2784 s = 1;
2785 temp >>= 1;
2786 }
2787 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002788 h = h > freq ? h - freq : freq - h;
2789 if (h < diff) {
2790 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002792 *den = temp;
2793 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 }
2795 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002796 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002798 if (d > 31) {
2799 s = 1;
2800 d >>= 1;
2801 }
2802 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002803 h = h > freq ? h - freq : freq - h;
2804 if (h < diff) {
2805 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002807 *den = d;
2808 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 }
2810 }
2811 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812}
2813
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814/* -------------------------------------------------------------------------
2815 *
2816 * debugging functions
2817 *
2818 * -------------------------------------------------------------------------
2819 */
2820
2821#ifdef CIRRUSFB_DEBUG
2822
2823/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 * cirrusfb_dbg_print_regs
2825 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2826 * @reg_class: type of registers to read: %CRT, or %SEQ
2827 *
2828 * DESCRIPTION:
2829 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2830 * old-style I/O ports are queried for information, otherwise MMIO is
2831 * used at the given @base address to query the information.
2832 */
2833
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002834static void cirrusfb_dbg_print_regs(struct fb_info *info,
2835 caddr_t regbase,
2836 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837{
2838 va_list list;
2839 unsigned char val = 0;
2840 unsigned reg;
2841 char *name;
2842
Krzysztof Helt8503df62007-10-16 01:29:08 -07002843 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844
Krzysztof Helt8503df62007-10-16 01:29:08 -07002845 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848
2849 switch (reg_class) {
2850 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002851 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 break;
2853 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002854 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 break;
2856 default:
2857 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002858 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 break;
2860 }
2861
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002862 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863
Krzysztof Helt8503df62007-10-16 01:29:08 -07002864 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 }
2866
Krzysztof Helt8503df62007-10-16 01:29:08 -07002867 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868}
2869
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 * cirrusfb_dbg_reg_dump
2872 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2873 *
2874 * DESCRIPTION:
2875 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2876 * old-style I/O ports are queried for information, otherwise MMIO is
2877 * used at the given @base address to query the information.
2878 */
2879
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002880static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002882 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002884 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885 "CR00", 0x00,
2886 "CR01", 0x01,
2887 "CR02", 0x02,
2888 "CR03", 0x03,
2889 "CR04", 0x04,
2890 "CR05", 0x05,
2891 "CR06", 0x06,
2892 "CR07", 0x07,
2893 "CR08", 0x08,
2894 "CR09", 0x09,
2895 "CR0A", 0x0A,
2896 "CR0B", 0x0B,
2897 "CR0C", 0x0C,
2898 "CR0D", 0x0D,
2899 "CR0E", 0x0E,
2900 "CR0F", 0x0F,
2901 "CR10", 0x10,
2902 "CR11", 0x11,
2903 "CR12", 0x12,
2904 "CR13", 0x13,
2905 "CR14", 0x14,
2906 "CR15", 0x15,
2907 "CR16", 0x16,
2908 "CR17", 0x17,
2909 "CR18", 0x18,
2910 "CR22", 0x22,
2911 "CR24", 0x24,
2912 "CR26", 0x26,
2913 "CR2D", 0x2D,
2914 "CR2E", 0x2E,
2915 "CR2F", 0x2F,
2916 "CR30", 0x30,
2917 "CR31", 0x31,
2918 "CR32", 0x32,
2919 "CR33", 0x33,
2920 "CR34", 0x34,
2921 "CR35", 0x35,
2922 "CR36", 0x36,
2923 "CR37", 0x37,
2924 "CR38", 0x38,
2925 "CR39", 0x39,
2926 "CR3A", 0x3A,
2927 "CR3B", 0x3B,
2928 "CR3C", 0x3C,
2929 "CR3D", 0x3D,
2930 "CR3E", 0x3E,
2931 "CR3F", 0x3F,
2932 NULL);
2933
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002934 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002936 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002938 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 "SR00", 0x00,
2940 "SR01", 0x01,
2941 "SR02", 0x02,
2942 "SR03", 0x03,
2943 "SR04", 0x04,
2944 "SR08", 0x08,
2945 "SR09", 0x09,
2946 "SR0A", 0x0A,
2947 "SR0B", 0x0B,
2948 "SR0D", 0x0D,
2949 "SR10", 0x10,
2950 "SR11", 0x11,
2951 "SR12", 0x12,
2952 "SR13", 0x13,
2953 "SR14", 0x14,
2954 "SR15", 0x15,
2955 "SR16", 0x16,
2956 "SR17", 0x17,
2957 "SR18", 0x18,
2958 "SR19", 0x19,
2959 "SR1A", 0x1A,
2960 "SR1B", 0x1B,
2961 "SR1C", 0x1C,
2962 "SR1D", 0x1D,
2963 "SR1E", 0x1E,
2964 "SR1F", 0x1F,
2965 NULL);
2966
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002967 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968}
2969
2970#endif /* CIRRUSFB_DEBUG */
2971