blob: c3dbbe6e3acf42f9353fe6d79e6eee929acc3560 [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 */
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200290 zorro_id ramid2; /* Zorro ID of optional second RAM device */
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200291};
292
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800293static const struct zorrocl zcl_sd64 = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200294 .type = BT_SD64,
295 .ramid = ZORRO_PROD_HELFRICH_SD64_RAM,
296};
297
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800298static const struct zorrocl zcl_piccolo = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200299 .type = BT_PICCOLO,
300 .ramid = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
301};
302
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800303static const struct zorrocl zcl_picasso = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200304 .type = BT_PICASSO,
305 .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
306};
307
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800308static const struct zorrocl zcl_spectrum = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200309 .type = BT_SPECTRUM,
310 .ramid = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
311};
312
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800313static const struct zorrocl zcl_picasso4_z3 = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200314 .type = BT_PICASSO4,
315 .regoffset = 0x00600000,
316 .ramsize = 4 * MB_,
Geert Uytterhoevene78bb882011-10-20 13:42:25 +0200317 .ramoffset = 0x01000000, /* 0x02000000 for 64 MiB boards */
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200318};
319
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800320static const struct zorrocl zcl_picasso4_z2 = {
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200321 .type = BT_PICASSO4,
322 .regoffset = 0x10000,
323 .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM1,
324 .ramid2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM2,
325};
326
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200327
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800328static const struct zorro_device_id cirrusfb_zorro_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200330 .id = ZORRO_PROD_HELFRICH_SD64_REG,
331 .driver_data = (unsigned long)&zcl_sd64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200333 .id = ZORRO_PROD_HELFRICH_PICCOLO_REG,
334 .driver_data = (unsigned long)&zcl_piccolo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200336 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
337 .driver_data = (unsigned long)&zcl_picasso,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200339 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
340 .driver_data = (unsigned long)&zcl_spectrum,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 }, {
342 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200343 .driver_data = (unsigned long)&zcl_picasso4_z3,
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200344 }, {
345 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG,
346 .driver_data = (unsigned long)&zcl_picasso4_z2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 },
348 { 0 }
349};
Geert Uytterhoevenbf54a2b2008-11-18 21:13:53 +0100350MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351#endif /* CONFIG_ZORRO */
352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700354enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700355 CRT,
356 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700357};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700358#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
360/* info about board */
361struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700363 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700364 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 unsigned char SFR; /* Shadow of special function register */
366
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700367 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700368 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700370 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700372 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373};
374
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800375static bool noaccel;
376static char *mode_option = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378/****************************************************************************/
379/**** BEGIN PROTOTYPES ******************************************************/
380
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700382static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
383 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700386static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700387static void switch_monitor(struct cirrusfb_info *cinfo, int on);
388static void WGen(const struct cirrusfb_info *cinfo,
389 int regnum, unsigned char val);
390static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
391static void AttrOn(const struct cirrusfb_info *cinfo);
392static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
393static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
394static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
395static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
396 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700398static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
399 unsigned char *red, unsigned char *green,
400 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700402static void cirrusfb_WaitBLT(u8 __iomem *regbase);
403static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
404 u_short curx, u_short cury,
405 u_short destx, u_short desty,
406 u_short width, u_short height,
407 u_short line_length);
408static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
409 u_short x, u_short y,
410 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700411 u32 fg_color, u32 bg_color,
412 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700414static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
416#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700417static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
418static void cirrusfb_dbg_print_regs(struct fb_info *info,
419 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700420 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421#endif /* CIRRUSFB_DEBUG */
422
423/*** END PROTOTYPES ********************************************************/
424/*****************************************************************************/
425/*** BEGIN Interface Used by the World ***************************************/
426
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700427static inline int is_laguna(const struct cirrusfb_info *cinfo)
428{
429 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
430}
431
Krzysztof Helt8503df62007-10-16 01:29:08 -0700432static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700435static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436{
437 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700438 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 return 0;
440}
441
442/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700443static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444{
445 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700446 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 return 0;
448}
449
450/**** END Interface used by the World *************************************/
451/****************************************************************************/
452/**** BEGIN Hardware specific Routines **************************************/
453
Krzysztof Helt486ff382008-10-15 22:03:42 -0700454/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700455static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700457 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700458 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
Krzysztof Helt486ff382008-10-15 22:03:42 -0700460 /* Read MCLK value */
461 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700462 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
464 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700465 * should divide it by to get VCLK
466 */
467
468 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700469 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700470 return 1;
471 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700472 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700473 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 }
475
Krzysztof Helt486ff382008-10-15 22:03:42 -0700476 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477}
478
Krzysztof Helt99a45842009-03-31 15:25:09 -0700479static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
480 struct fb_info *info)
481{
482 long freq;
483 long maxclock;
484 struct cirrusfb_info *cinfo = info->par;
485 unsigned maxclockidx = var->bits_per_pixel >> 3;
486
487 /* convert from ps to kHz */
488 freq = PICOS2KHZ(var->pixclock);
489
490 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
491
492 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
493 cinfo->multiplexing = 0;
494
495 /* If the frequency is greater than we can support, we might be able
496 * to use multiplexing for the video mode */
497 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700498 dev_err(info->device,
499 "Frequency greater than maxclock (%ld kHz)\n",
500 maxclock);
501 return -EINVAL;
502 }
503 /*
504 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
505 * pixel clock
506 */
507 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700508 switch (cinfo->btype) {
509 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700510 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700511 case BT_PICASSO4:
512 if (freq > 85500)
513 cinfo->multiplexing = 1;
514 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700515 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700516 if (freq > 135100)
517 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700518 break;
519
520 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700521 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700522 }
523 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700524
525 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700526 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700527 cinfo->doubleVCLK = 0;
528 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
529 var->bits_per_pixel == 16) {
530 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700531 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700532
Krzysztof Helt99a45842009-03-31 15:25:09 -0700533 return 0;
534}
535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536static int cirrusfb_check_var(struct fb_var_screeninfo *var,
537 struct fb_info *info)
538{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700539 int yres;
540 /* memory size in pixels */
541 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700542 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
544 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700545 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 var->red.offset = 0;
547 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700548 var->green = var->red;
549 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 break;
551
552 case 8:
553 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700554 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700555 var->green = var->red;
556 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 break;
558
559 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700560 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 var->red.offset = 2;
562 var->green.offset = -3;
563 var->blue.offset = 8;
564 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700565 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 var->green.offset = 5;
567 var->blue.offset = 0;
568 }
569 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700570 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 var->blue.length = 5;
572 break;
573
Krzysztof Helt7cade312009-03-31 15:25:13 -0700574 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700575 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700576 var->red.offset = 0;
577 var->green.offset = 8;
578 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 } else {
580 var->red.offset = 16;
581 var->green.offset = 8;
582 var->blue.offset = 0;
583 }
584 var->red.length = 8;
585 var->green.length = 8;
586 var->blue.length = 8;
587 break;
588
589 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700590 dev_dbg(info->device,
591 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Krzysztof Helt0efb2a02009-04-13 14:39:55 -0700592 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 }
594
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700595 if (var->xres_virtual < var->xres)
596 var->xres_virtual = var->xres;
597 /* use highest possible virtual resolution */
598 if (var->yres_virtual == -1) {
599 var->yres_virtual = pixels / var->xres_virtual;
600
601 dev_info(info->device,
602 "virtual resolution set to maximum of %dx%d\n",
603 var->xres_virtual, var->yres_virtual);
604 }
605 if (var->yres_virtual < var->yres)
606 var->yres_virtual = var->yres;
607
608 if (var->xres_virtual * var->yres_virtual > pixels) {
609 dev_err(info->device, "mode %dx%dx%d rejected... "
610 "virtual resolution too high to fit into video memory!\n",
611 var->xres_virtual, var->yres_virtual,
612 var->bits_per_pixel);
613 return -EINVAL;
614 }
615
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700616 if (var->xoffset < 0)
617 var->xoffset = 0;
618 if (var->yoffset < 0)
619 var->yoffset = 0;
620
621 /* truncate xoffset and yoffset to maximum if too high */
622 if (var->xoffset > var->xres_virtual - var->xres)
623 var->xoffset = var->xres_virtual - var->xres - 1;
624 if (var->yoffset > var->yres_virtual - var->yres)
625 var->yoffset = var->yres_virtual - var->yres - 1;
626
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 var->red.msb_right =
628 var->green.msb_right =
629 var->blue.msb_right =
630 var->transp.offset =
631 var->transp.length =
632 var->transp.msb_right = 0;
633
634 yres = var->yres;
635 if (var->vmode & FB_VMODE_DOUBLE)
636 yres *= 2;
637 else if (var->vmode & FB_VMODE_INTERLACED)
638 yres = (yres + 1) / 2;
639
640 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700641 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700642 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 return -EINVAL;
644 }
645
Krzysztof Helt99a45842009-03-31 15:25:09 -0700646 if (cirrusfb_check_pixclock(var, info))
647 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700649 if (!is_laguna(cinfo))
650 var->accel_flags = FB_ACCELF_TEXT;
651
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 return 0;
653}
654
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700655static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700657 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700658 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700659
Krzysztof Helt8503df62007-10-16 01:29:08 -0700660 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700661 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662
Krzysztof Helt486ff382008-10-15 22:03:42 -0700663 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700664 dev_dbg(info->device, "Set %s as pixclock source.\n",
665 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700666 old1f |= 0x40;
667 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
668 if (div == 2)
669 old1e |= 1;
670
671 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700673 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674}
675
676/*************************************************************************
677 cirrusfb_set_par_foo()
678
679 actually writes the values for a new video mode into the hardware,
680**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700681static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682{
683 struct cirrusfb_info *cinfo = info->par;
684 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 u8 __iomem *regbase = cinfo->regbase;
686 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700687 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700689 int hdispend, hsyncstart, hsyncend, htotal;
690 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700691 long freq;
692 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700693 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700695 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700697
698 switch (var->bits_per_pixel) {
699 case 1:
700 info->fix.line_length = var->xres_virtual / 8;
701 info->fix.visual = FB_VISUAL_MONO10;
702 break;
703
704 case 8:
705 info->fix.line_length = var->xres_virtual;
706 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
707 break;
708
709 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700710 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700711 info->fix.line_length = var->xres_virtual *
712 var->bits_per_pixel >> 3;
713 info->fix.visual = FB_VISUAL_TRUECOLOR;
714 break;
715 }
716 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700718 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 bi = &cirrusfb_board_info[cinfo->btype];
721
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700722 hsyncstart = var->xres + var->right_margin;
723 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700724 htotal = (hsyncend + var->left_margin) / 8;
725 hdispend = var->xres / 8;
726 hsyncstart = hsyncstart / 8;
727 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700728
Krzysztof Helt8636a922009-03-31 15:25:17 -0700729 vdispend = var->yres;
730 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700731 vsyncend = vsyncstart + var->vsync_len;
732 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700733
734 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700735 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700736 vsyncstart *= 2;
737 vsyncend *= 2;
738 vtotal *= 2;
739 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700740 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700741 vsyncstart = (vsyncstart + 1) / 2;
742 vsyncend = (vsyncend + 1) / 2;
743 vtotal = (vtotal + 1) / 2;
744 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700745 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700746 if (yres >= 1024) {
747 vtotal /= 2;
748 vsyncstart /= 2;
749 vsyncend /= 2;
750 vdispend /= 2;
751 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700752
753 vdispend -= 1;
754 vsyncstart -= 1;
755 vsyncend -= 1;
756 vtotal -= 2;
757
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700758 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700759 htotal /= 2;
760 hsyncstart /= 2;
761 hsyncend /= 2;
762 hdispend /= 2;
763 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700764
765 htotal -= 5;
766 hdispend -= 1;
767 hsyncstart += 1;
768 hsyncend += 1;
769
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700771 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
773 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700774 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700775 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700777 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700780 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Krzysztof Helt8503df62007-10-16 01:29:08 -0700783 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700784 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700785 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700791 tmp = hsyncend % 32;
792 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700794 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700795 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700797 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700798 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700801 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700805 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700811 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700813 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700816 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700819 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 tmp |= 0x20;
821 if (var->vmode & FB_VMODE_DOUBLE)
822 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700823 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700824 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700826 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700827 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700829 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700830 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700832 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700833 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700835 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700836 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700838 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700839 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700841 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700842 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
844 tmp = 0;
845 if (var->vmode & FB_VMODE_INTERLACED)
846 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700847 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700849 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700851 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700853 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 tmp |= 128;
855
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700856 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700857 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700859 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700860 if (var->bits_per_pixel == 24)
861 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
862 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700863 if (cinfo->multiplexing)
864 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700865 if (cinfo->doubleVCLK)
866 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700867
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700868 bestclock(freq, &nom, &den, &div);
869
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700870 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
871 freq, nom, den, div);
872
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 /* set VCLK0 */
874 /* hardware RefClock: 14.31818 MHz */
875 /* formula: VClk = (OSC * N) / (D * (1+P)) */
876 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
877
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700878 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
879 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700880 /* if freq is close to mclk or mclk/2 select mclk
881 * as clock source
882 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700883 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700884 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700885 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700886 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700887 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700888 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700889 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
890 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
891 unsigned short tile_control;
892
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700893 if (cinfo->btype == BT_LAGUNAB) {
894 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
895 tile_control &= ~0x80;
896 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
897 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700898
899 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
900 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
901 control = fb_readw(cinfo->laguna_mmio + 0x402);
902 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
903 control &= ~0x6800;
904 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700905 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700906 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700907 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700908 tmp = den << 1;
909 if (div != 0)
910 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700911 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
912 if ((cinfo->btype == BT_SD64) ||
913 (cinfo->btype == BT_ALPINE) ||
914 (cinfo->btype == BT_GD5480))
915 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700917 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700918 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700919 vga_wseq(regbase, CL_SEQRE, tmp);
920 vga_wseq(regbase, CL_SEQR1E, nom);
921 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700922 vga_wseq(regbase, CL_SEQRE, nom);
923 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700924 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700925 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700927 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 else
931 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
932 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700933 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 /* don't know if it would hurt to also program this if no interlaced */
936 /* mode is used, but I feel better this way.. :-) */
937 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700938 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700940 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700942 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700943 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700944 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
946 tmp |= 0x40;
947 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
948 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700949 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
Krzysztof Helt8503df62007-10-16 01:29:08 -0700951 /* text cursor on and start line */
952 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
953 /* text cursor end line */
954 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956 /******************************************************
957 *
958 * 1 bpp
959 *
960 */
961
962 /* programming for different color depths */
963 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700964 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
967 /* SR07 */
968 switch (cinfo->btype) {
969 case BT_SD64:
970 case BT_PICCOLO:
971 case BT_PICASSO:
972 case BT_SPECTRUM:
973 case BT_PICASSO4:
974 case BT_ALPINE:
975 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700976 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700977 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 bi->sr07_1bpp_mux : bi->sr07_1bpp);
979 break;
980
981 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700982 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700983 vga_wseq(regbase, CL_SEQR7,
984 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 break;
986
987 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700988 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 break;
990 }
991
992 /* Extended Sequencer Mode */
993 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
995 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700996 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
998 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 break;
1000
1001 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001002 /* ## vorher d0 avoid FIFO underruns..? */
1003 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 break;
1005
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001006 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 case BT_PICASSO4:
1008 case BT_ALPINE:
1009 case BT_GD5480:
1010 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001011 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 /* do nothing */
1013 break;
1014
1015 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001016 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 break;
1018 }
1019
Krzysztof Helt8503df62007-10-16 01:29:08 -07001020 /* pixel mask: pass-through for first plane */
1021 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001022 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001023 /* hidden dac reg: 1280x1024 */
1024 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001026 /* hidden dac: nothing */
1027 WHDR(cinfo, 0);
1028 /* memory mode: odd/even, ext. memory */
1029 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1030 /* plane mask: only write to first plane */
1031 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 }
1033
1034 /******************************************************
1035 *
1036 * 8 bpp
1037 *
1038 */
1039
1040 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001041 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 switch (cinfo->btype) {
1043 case BT_SD64:
1044 case BT_PICCOLO:
1045 case BT_PICASSO:
1046 case BT_SPECTRUM:
1047 case BT_PICASSO4:
1048 case BT_ALPINE:
1049 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001050 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001051 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1053 break;
1054
1055 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001056 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001057 vga_wseq(regbase, CL_SEQR7,
1058 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001059 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 break;
1061
1062 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001063 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 break;
1065 }
1066
1067 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001071 /* Fast Page-Mode writes */
1072 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 break;
1074
1075 case BT_PICASSO4:
1076#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001077 /* ### INCOMPLETE!! */
1078 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001081 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 case BT_GD5480:
1083 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001084 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 /* do nothing */
1086 break;
1087
1088 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001089 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 break;
1091 }
1092
Krzysztof Helt8503df62007-10-16 01:29:08 -07001093 /* mode register: 256 color mode */
1094 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001095 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001096 /* hidden dac reg: 1280x1024 */
1097 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001099 /* hidden dac: nothing */
1100 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 }
1102
1103 /******************************************************
1104 *
1105 * 16 bpp
1106 *
1107 */
1108
1109 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001110 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001113 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001114 vga_wseq(regbase, CL_SEQR7, 0x87);
1115 /* Fast Page-Mode writes */
1116 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 break;
1118
1119 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 vga_wseq(regbase, CL_SEQR7, 0x27);
1121 /* Fast Page-Mode writes */
1122 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 break;
1124
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001125 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001128 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001129 vga_wseq(regbase, CL_SEQR7,
1130 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 break;
1132
1133 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001134 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 /* We already set SRF and SR1F */
1136 break;
1137
1138 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001139 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001140 vga_wseq(regbase, CL_SEQR7,
1141 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001142 control |= 0x2000;
1143 format |= 0x1400;
1144 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 break;
1146
1147 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001148 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 break;
1150 }
1151
Krzysztof Helt8503df62007-10-16 01:29:08 -07001152 /* mode register: 256 color mode */
1153 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001155 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156#elif defined(CONFIG_ZORRO)
1157 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001158 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161
1162 /******************************************************
1163 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001164 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 *
1166 */
1167
Krzysztof Helt7cade312009-03-31 15:25:13 -07001168 else if (var->bits_per_pixel == 24) {
1169 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001172 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001173 vga_wseq(regbase, CL_SEQR7, 0x85);
1174 /* Fast Page-Mode writes */
1175 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 break;
1177
1178 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001179 vga_wseq(regbase, CL_SEQR7, 0x25);
1180 /* Fast Page-Mode writes */
1181 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 break;
1183
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001184 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001187 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001188 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 break;
1190
1191 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001192 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 /* We already set SRF and SR1F */
1194 break;
1195
1196 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001197 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001198 vga_wseq(regbase, CL_SEQR7,
1199 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001200 control |= 0x4000;
1201 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001202 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 break;
1204
1205 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001206 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 break;
1208 }
1209
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 /* mode register: 256 color mode */
1211 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001212 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1213 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 }
1215
1216 /******************************************************
1217 *
1218 * unknown/unsupported bpp
1219 *
1220 */
1221
Krzysztof Helt8503df62007-10-16 01:29:08 -07001222 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001223 dev_err(info->device,
1224 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
Krzysztof Helt6683e012009-03-31 15:25:06 -07001227 pitch = info->fix.line_length >> 3;
1228 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001230 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 tmp |= 0x10; /* offset overflow bit */
1232
Krzysztof Helt8503df62007-10-16 01:29:08 -07001233 /* screen start addr #16-18, fastpagemode cycles */
1234 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001236 /* screen start address bit 19 */
1237 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001238 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001240 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001241 tmp = 0;
1242 if ((htotal + 5) & 256)
1243 tmp |= 128;
1244 if (hdispend & 256)
1245 tmp |= 64;
1246 if (hsyncstart & 256)
1247 tmp |= 48;
1248 if (vtotal & 1024)
1249 tmp |= 8;
1250 if (vdispend & 1024)
1251 tmp |= 4;
1252 if (vsyncstart & 1024)
1253 tmp |= 3;
1254
1255 vga_wcrt(regbase, CL_CRT1E, tmp);
1256 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1257 }
1258
Krzysztof Helt8503df62007-10-16 01:29:08 -07001259 /* pixel panning */
1260 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
1262 /* [ EGS: SetOffset(); ] */
1263 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001264 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001266 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001267 /* no tiles */
1268 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1269 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1270 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1271 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 /* finally, turn on everything - turn off "FullBandwidth" bit */
1273 /* also, set "DotClock%2" bit where requested */
1274 tmp = 0x01;
1275
1276/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1277 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1278 tmp |= 0x08;
1279*/
1280
Krzysztof Helt8503df62007-10-16 01:29:08 -07001281 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001282 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001285 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286#endif
1287
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 return 0;
1289}
1290
1291/* for some reason incomprehensible to me, cirrusfb requires that you write
1292 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001293static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001295 cirrusfb_set_par_foo(info);
1296 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297}
1298
Krzysztof Helt8503df62007-10-16 01:29:08 -07001299static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1300 unsigned blue, unsigned transp,
1301 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302{
1303 struct cirrusfb_info *cinfo = info->par;
1304
1305 if (regno > 255)
1306 return -EINVAL;
1307
1308 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1309 u32 v;
1310 red >>= (16 - info->var.red.length);
1311 green >>= (16 - info->var.green.length);
1312 blue >>= (16 - info->var.blue.length);
1313
Krzysztof Helt8503df62007-10-16 01:29:08 -07001314 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 return 1;
1316 v = (red << info->var.red.offset) |
1317 (green << info->var.green.offset) |
1318 (blue << info->var.blue.offset);
1319
Krzysztof Helt060b6002007-10-16 01:29:13 -07001320 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 return 0;
1322 }
1323
Krzysztof Helt8503df62007-10-16 01:29:08 -07001324 if (info->var.bits_per_pixel == 8)
1325 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
1327 return 0;
1328
1329}
1330
1331/*************************************************************************
1332 cirrusfb_pan_display()
1333
1334 performs display panning - provided hardware permits this
1335**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001336static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1337 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001339 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001341 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 struct cirrusfb_info *cinfo = info->par;
1343
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 /* no range checks for xoffset and yoffset, */
1345 /* as fb_pan_display has already done this */
1346 if (var->vmode & FB_VMODE_YWRAP)
1347 return -EINVAL;
1348
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
Krzysztof Helt99a45842009-03-31 15:25:09 -07001351 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
1353 if (info->var.bits_per_pixel == 1) {
1354 /* base is already correct */
1355 xpix = (unsigned char) (var->xoffset % 8);
1356 } else {
1357 base /= 4;
1358 xpix = (unsigned char) ((xoffset % 4) * 2);
1359 }
1360
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001361 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001362 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001365 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1366 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001368 /* 0xf2 is %11110010, exclude tmp bits */
1369 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 /* construct bits 16, 17 and 18 of screen start address */
1371 if (base & 0x10000)
1372 tmp |= 0x01;
1373 if (base & 0x20000)
1374 tmp |= 0x04;
1375 if (base & 0x40000)
1376 tmp |= 0x08;
1377
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001378 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
1380 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001381 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001382 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1383 if (is_laguna(cinfo))
1384 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1385 else
1386 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001387 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1388 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 /* write pixel panning value to AR33; this does not quite work in 8bpp
1391 *
1392 * ### Piccolo..? Will this work?
1393 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001395 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398}
1399
Krzysztof Helt8503df62007-10-16 01:29:08 -07001400static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401{
1402 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001403 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1404 * then the caller blanks by setting the CLUT (Color Look Up Table)
1405 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1406 * failed due to e.g. a video mode which doesn't support it.
1407 * Implements VESA suspend and powerdown modes on hardware that
1408 * supports disabling hsync/vsync:
1409 * blank_mode == 2: suspend vsync
1410 * blank_mode == 3: suspend hsync
1411 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 */
1413 unsigned char val;
1414 struct cirrusfb_info *cinfo = info->par;
1415 int current_mode = cinfo->blank_mode;
1416
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001417 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
1419 if (info->state != FBINFO_STATE_RUNNING ||
1420 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001421 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 return 0;
1423 }
1424
1425 /* Undo current */
1426 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001427 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001428 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001429 val = 0;
1430 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001431 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001432 val = 0x20;
1433
1434 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1435 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
1437 switch (blank_mode) {
1438 case FB_BLANK_UNBLANK:
1439 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001440 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 break;
1442 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001443 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 break;
1445 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001446 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 break;
1448 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001449 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 break;
1451 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001452 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 return 1;
1454 }
1455
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001456 vga_wgfx(cinfo->regbase, CL_GRE, val);
1457
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001459 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460
1461 /* Let fbcon do a soft blank for us */
1462 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1463}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001464
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465/**** END Hardware specific Routines **************************************/
1466/****************************************************************************/
1467/**** BEGIN Internal Routines ***********************************************/
1468
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001469static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001471 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 const struct cirrusfb_board_info_rec *bi;
1473
Krzysztof Helt8503df62007-10-16 01:29:08 -07001474 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
1476 bi = &cirrusfb_board_info[cinfo->btype];
1477
1478 /* reset board globally */
1479 switch (cinfo->btype) {
1480 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 WSFR(cinfo, 0x01);
1482 udelay(500);
1483 WSFR(cinfo, 0x51);
1484 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 break;
1486 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001487 WSFR2(cinfo, 0xff);
1488 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 break;
1490 case BT_SD64:
1491 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001492 WSFR(cinfo, 0x1f);
1493 udelay(500);
1494 WSFR(cinfo, 0x4f);
1495 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 break;
1497 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001498 /* disable flickerfixer */
1499 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1500 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 /* mode */
1502 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001503 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 /* from Klaus' NetBSD driver: */
1505 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001506 case BT_ALPINE: /* fall through */
1507 /* put blitter into 542x compat */
1508 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 break;
1510
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001511 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001512 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 /* Nothing to do to reset the board. */
1514 break;
1515
1516 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001517 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 break;
1519 }
1520
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001521 /* make sure RAM size set by this point */
1522 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
1524 /* the P4 is not fully initialized here; I rely on it having been */
1525 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001526 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
1528 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001529 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1530 WGen(cinfo, CL_POS102, 0x01);
1531 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
1533 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535
Krzysztof Helt8503df62007-10-16 01:29:08 -07001536 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001537 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 /* FullBandwidth (video off) and 8/9 dot clock */
1540 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
Krzysztof Helt8503df62007-10-16 01:29:08 -07001542 /* "magic cookie" - doesn't make any sense to me.. */
1543/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1544 /* unlock all extension registers */
1545 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 switch (cinfo->btype) {
1548 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001549 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 break;
1551 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001552 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001553 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 break;
1555 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001556#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001558#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
1560 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001561 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1562 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 break;
1564 }
1565 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001566 /* plane mask: nothing */
1567 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1568 /* character map select: doesn't even matter in gx mode */
1569 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001570 /* memory mode: chain4, ext. memory */
1571 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
1573 /* controller-internal base address of video memory */
1574 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001575 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
Krzysztof Helt8503df62007-10-16 01:29:08 -07001577 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1578 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1581 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1582 /* graphics cursor Y position (..."... ) */
1583 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1584 /* graphics cursor attributes */
1585 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1586 /* graphics cursor pattern address */
1587 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
1589 /* writing these on a P4 might give problems.. */
1590 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001591 /* configuration readback and ext. color */
1592 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1593 /* signature generator */
1594 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 }
1596
Krzysztof Helt8503df62007-10-16 01:29:08 -07001597 /* Screen A preset row scan: none */
1598 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1599 /* Text cursor start: disable text cursor */
1600 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1601 /* Text cursor end: - */
1602 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001603 /* text cursor location high: 0 */
1604 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1605 /* text cursor location low: 0 */
1606 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
Krzysztof Helt8503df62007-10-16 01:29:08 -07001608 /* Underline Row scanline: - */
1609 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 /* ext. display controls: ext.adr. wrap */
1612 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Masanari Iidaff0c2642012-07-22 00:23:15 +09001614 /* Set/Reset registers: - */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001615 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1616 /* Set/Reset enable: - */
1617 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1618 /* Color Compare: - */
1619 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1620 /* Data Rotate: - */
1621 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1622 /* Read Map Select: - */
1623 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1624 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1625 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1626 /* Miscellaneous: memory map base address, graphics mode */
1627 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1628 /* Color Don't care: involve all planes */
1629 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1630 /* Bit Mask: no mask at all */
1631 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001632
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001633 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1634 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001635 /* (5434 can't have bit 3 set for bitblt) */
1636 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001638 /* Graphics controller mode extensions: finer granularity,
1639 * 8byte data latches
1640 */
1641 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
Krzysztof Helt8503df62007-10-16 01:29:08 -07001643 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1644 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1645 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1646 /* Background color byte 1: - */
1647 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1648 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 /* Attribute Controller palette registers: "identity mapping" */
1651 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1652 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1653 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1654 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1655 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1656 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1657 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1658 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1659 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1660 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1661 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1662 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1663 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1664 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1665 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1666 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
Krzysztof Helt8503df62007-10-16 01:29:08 -07001668 /* Attribute Controller mode: graphics mode */
1669 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1670 /* Overscan color reg.: reg. 0 */
1671 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1672 /* Color Plane enable: Enable all 4 planes */
1673 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001674 /* Color Select: - */
1675 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676
Krzysztof Helt8503df62007-10-16 01:29:08 -07001677 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678
Krzysztof Helt8503df62007-10-16 01:29:08 -07001679 /* BLT Start/status: Blitter reset */
1680 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1681 /* - " - : "end-of-reset" */
1682 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
1684 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001685 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 return;
1687}
1688
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690{
1691#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1692 static int IsOn = 0; /* XXX not ok for multiple boards */
1693
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 if (cinfo->btype == BT_PICASSO4)
1695 return; /* nothing to switch */
1696 if (cinfo->btype == BT_ALPINE)
1697 return; /* nothing to switch */
1698 if (cinfo->btype == BT_GD5480)
1699 return; /* nothing to switch */
1700 if (cinfo->btype == BT_PICASSO) {
1701 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 return;
1704 }
1705 if (on) {
1706 switch (cinfo->btype) {
1707 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001708 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 break;
1710 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001711 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 break;
1713 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001714 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 break;
1716 default: /* do nothing */ break;
1717 }
1718 } else {
1719 switch (cinfo->btype) {
1720 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 break;
1723 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 break;
1726 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001727 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001729 default: /* do nothing */
1730 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 }
1732 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733#endif /* CONFIG_ZORRO */
1734}
1735
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736/******************************************/
1737/* Linux 2.6-style accelerated functions */
1738/******************************************/
1739
Krzysztof Helt8343c892009-03-31 15:25:11 -07001740static int cirrusfb_sync(struct fb_info *info)
1741{
1742 struct cirrusfb_info *cinfo = info->par;
1743
1744 if (!is_laguna(cinfo)) {
1745 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1746 cpu_relax();
1747 }
1748 return 0;
1749}
1750
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751static void cirrusfb_fillrect(struct fb_info *info,
1752 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 struct fb_fillrect modded;
1755 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001756 struct cirrusfb_info *cinfo = info->par;
1757 int m = info->var.bits_per_pixel;
1758 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1759 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760
1761 if (info->state != FBINFO_STATE_RUNNING)
1762 return;
1763 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1764 cfb_fillrect(info, region);
1765 return;
1766 }
1767
1768 vxres = info->var.xres_virtual;
1769 vyres = info->var.yres_virtual;
1770
1771 memcpy(&modded, region, sizeof(struct fb_fillrect));
1772
Krzysztof Helt8503df62007-10-16 01:29:08 -07001773 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774 modded.dx >= vxres || modded.dy >= vyres)
1775 return;
1776
Krzysztof Helt8503df62007-10-16 01:29:08 -07001777 if (modded.dx + modded.width > vxres)
1778 modded.width = vxres - modded.dx;
1779 if (modded.dy + modded.height > vyres)
1780 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
Krzysztof Helt060b6002007-10-16 01:29:13 -07001782 cirrusfb_RectFill(cinfo->regbase,
1783 info->var.bits_per_pixel,
1784 (region->dx * m) / 8, region->dy,
1785 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001786 color, color,
1787 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788}
1789
Krzysztof Helt8503df62007-10-16 01:29:08 -07001790static void cirrusfb_copyarea(struct fb_info *info,
1791 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 struct fb_copyarea modded;
1794 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001795 struct cirrusfb_info *cinfo = info->par;
1796 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
1798 if (info->state != FBINFO_STATE_RUNNING)
1799 return;
1800 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1801 cfb_copyarea(info, area);
1802 return;
1803 }
1804
1805 vxres = info->var.xres_virtual;
1806 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001807 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810 modded.sx >= vxres || modded.sy >= vyres ||
1811 modded.dx >= vxres || modded.dy >= vyres)
1812 return;
1813
Krzysztof Helt8503df62007-10-16 01:29:08 -07001814 if (modded.sx + modded.width > vxres)
1815 modded.width = vxres - modded.sx;
1816 if (modded.dx + modded.width > vxres)
1817 modded.width = vxres - modded.dx;
1818 if (modded.sy + modded.height > vyres)
1819 modded.height = vyres - modded.sy;
1820 if (modded.dy + modded.height > vyres)
1821 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822
Krzysztof Helt060b6002007-10-16 01:29:13 -07001823 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1824 (area->sx * m) / 8, area->sy,
1825 (area->dx * m) / 8, area->dy,
1826 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001827 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001828
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829}
1830
Krzysztof Helt8503df62007-10-16 01:29:08 -07001831static void cirrusfb_imageblit(struct fb_info *info,
1832 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833{
1834 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001835 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836
Krzysztof Helt9e848062009-03-31 15:25:11 -07001837 if (info->state != FBINFO_STATE_RUNNING)
1838 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001839 /* Alpine/SD64 does not work at 24bpp ??? */
1840 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1841 cfb_imageblit(info, image);
1842 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1843 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001844 cfb_imageblit(info, image);
1845 else {
1846 unsigned size = ((image->width + 7) >> 3) * image->height;
1847 int m = info->var.bits_per_pixel;
1848 u32 fg, bg;
1849
1850 if (info->var.bits_per_pixel == 8) {
1851 fg = image->fg_color;
1852 bg = image->bg_color;
1853 } else {
1854 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1855 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1856 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001857 if (info->var.bits_per_pixel == 24) {
1858 /* clear background first */
1859 cirrusfb_RectFill(cinfo->regbase,
1860 info->var.bits_per_pixel,
1861 (image->dx * m) / 8, image->dy,
1862 (image->width * m) / 8,
1863 image->height,
1864 bg, bg,
1865 info->fix.line_length, 0x40);
1866 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001867 cirrusfb_RectFill(cinfo->regbase,
1868 info->var.bits_per_pixel,
1869 (image->dx * m) / 8, image->dy,
1870 (image->width * m) / 8, image->height,
1871 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001872 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001873 memcpy(info->screen_base, image->data, size);
1874 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875}
1876
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877#ifdef CONFIG_PPC_PREP
1878#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1879#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001880static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 *display = PREP_VIDEO_BASE;
1883 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884}
1885
1886#endif /* CONFIG_PPC_PREP */
1887
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001889static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890
1891/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1892 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1893 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1894 * seem to have. */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001895static unsigned int cirrusfb_get_memsize(struct fb_info *info,
1896 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897{
1898 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001899 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001901 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001902 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1903
1904 mem = ((SR14 & 7) + 1) << 20;
1905 } else {
1906 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1907 switch ((SRF & 0x18)) {
1908 case 0x08:
1909 mem = 512 * 1024;
1910 break;
1911 case 0x10:
1912 mem = 1024 * 1024;
1913 break;
1914 /* 64-bit DRAM data bus width; assume 2MB.
1915 * Also indicates 2MB memory on the 5430.
1916 */
1917 case 0x18:
1918 mem = 2048 * 1024;
1919 break;
1920 default:
1921 dev_warn(info->device, "Unknown memory size!\n");
1922 mem = 1024 * 1024;
1923 }
1924 /* If DRAM bank switching is enabled, there must be
1925 * twice as much memory installed. (4MB on the 5434)
1926 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001927 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001928 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001930
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 return mem;
1933}
1934
Krzysztof Helt8503df62007-10-16 01:29:08 -07001935static void get_pci_addrs(const struct pci_dev *pdev,
1936 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001938 assert(pdev != NULL);
1939 assert(display != NULL);
1940 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 *display = 0;
1943 *registers = 0;
1944
1945 /* This is a best-guess for now */
1946
1947 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1948 *display = pci_resource_start(pdev, 1);
1949 *registers = pci_resource_start(pdev, 0);
1950 } else {
1951 *display = pci_resource_start(pdev, 0);
1952 *registers = pci_resource_start(pdev, 1);
1953 }
1954
Krzysztof Helt8503df62007-10-16 01:29:08 -07001955 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956}
1957
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001958static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001960 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001961 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001963 if (cinfo->laguna_mmio == NULL)
1964 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001965 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966#if 0 /* if system didn't claim this region, we would... */
1967 release_mem_region(0xA0000, 65535);
1968#endif
1969 if (release_io_ports)
1970 release_region(0x3C0, 32);
1971 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972}
1973#endif /* CONFIG_PCI */
1974
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001976static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977{
Al Virod91f5bb2007-10-17 00:27:18 +01001978 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001979 struct zorro_dev *zdev = to_zorro_dev(info->device);
1980
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001981 if (info->fix.smem_start > 16 * MB_)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001982 iounmap(info->screen_base);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001983 if (info->fix.mmio_start > 16 * MB_)
1984 iounmap(cinfo->regbase);
1985
1986 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987}
1988#endif /* CONFIG_ZORRO */
1989
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001990/* function table of the above functions */
1991static struct fb_ops cirrusfb_ops = {
1992 .owner = THIS_MODULE,
1993 .fb_open = cirrusfb_open,
1994 .fb_release = cirrusfb_release,
1995 .fb_setcolreg = cirrusfb_setcolreg,
1996 .fb_check_var = cirrusfb_check_var,
1997 .fb_set_par = cirrusfb_set_par,
1998 .fb_pan_display = cirrusfb_pan_display,
1999 .fb_blank = cirrusfb_blank,
2000 .fb_fillrect = cirrusfb_fillrect,
2001 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07002002 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002003 .fb_imageblit = cirrusfb_imageblit,
2004};
2005
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002006static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002008 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 struct fb_var_screeninfo *var = &info->var;
2010
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 info->pseudo_palette = cinfo->pseudo_palette;
2012 info->flags = FBINFO_DEFAULT
2013 | FBINFO_HWACCEL_XPAN
2014 | FBINFO_HWACCEL_YPAN
2015 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002016 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002018 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002020 info->fix.accel = FB_ACCEL_NONE;
2021 } else
2022 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
2023
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002025
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 if (cinfo->btype == BT_GD5480) {
2027 if (var->bits_per_pixel == 16)
2028 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002029 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 info->screen_base += 2 * MB_;
2031 }
2032
2033 /* Fill fix common fields */
2034 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2035 sizeof(info->fix.id));
2036
2037 /* monochrome: only 1 memory plane */
2038 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002039 info->fix.smem_len = info->screen_size;
2040 if (var->bits_per_pixel == 1)
2041 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 info->fix.xpanstep = 1;
2044 info->fix.ypanstep = 1;
2045 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046
2047 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049
2050 fb_alloc_cmap(&info->cmap, 256, 0);
2051
2052 return 0;
2053}
2054
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002055static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002057 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059
2060 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002061 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002063 /* set all the vital stuff */
2064 cirrusfb_set_fbinfo(info);
2065
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002066 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002068 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2069 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002070 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002071 err = -EINVAL;
2072 goto err_dealloc_cmap;
2073 }
2074
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 info->var.activate = FB_ACTIVATE_NOW;
2076
Krzysztof Helt99a45842009-03-31 15:25:09 -07002077 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 if (err < 0) {
2079 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002080 dev_dbg(info->device,
2081 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002082 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 }
2084
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 err = register_framebuffer(info);
2086 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002087 dev_err(info->device,
2088 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 goto err_dealloc_cmap;
2090 }
2091
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 return 0;
2093
2094err_dealloc_cmap:
2095 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 return err;
2097}
2098
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002099static void cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100{
2101 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102
Krzysztof Helt8503df62007-10-16 01:29:08 -07002103 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002104 unregister_framebuffer(info);
2105 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002106 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002107 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002108 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109}
2110
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111#ifdef CONFIG_PCI
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002112static int cirrusfb_pci_register(struct pci_dev *pdev,
2113 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114{
2115 struct cirrusfb_info *cinfo;
2116 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 unsigned long board_addr, board_size;
2118 int ret;
2119
2120 ret = pci_enable_device(pdev);
2121 if (ret < 0) {
2122 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2123 goto err_out;
2124 }
2125
2126 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2127 if (!info) {
2128 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2129 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002130 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 }
2132
2133 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002134 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002136 dev_dbg(info->device,
2137 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002138 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002139 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2140 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141
Krzysztof Helt8503df62007-10-16 01:29:08 -07002142 if (isPReP) {
2143 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002145 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002147 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002148 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002150 dev_dbg(info->device,
2151 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002152 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153 /* FIXME: this forces VGA. alternatives? */
2154 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002155 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 }
2157
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002158 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002159 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002161 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002162 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163
2164 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002165 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002166 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2167 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 goto err_release_fb;
2169 }
2170#if 0 /* if the system didn't claim this region, we would... */
2171 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002172 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2173 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 ret = -EBUSY;
2175 goto err_release_regions;
2176 }
2177#endif
2178 if (request_region(0x3C0, 32, "cirrusfb"))
2179 release_io_ports = 1;
2180
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002181 info->screen_base = ioremap(board_addr, board_size);
2182 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 ret = -EIO;
2184 goto err_release_legacy;
2185 }
2186
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002187 info->fix.smem_start = board_addr;
2188 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 cinfo->unmap = cirrusfb_pci_unmap;
2190
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002191 dev_info(info->device,
2192 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2193 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 pci_set_drvdata(pdev, info);
2195
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002196 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002197 if (!ret)
2198 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002200 pci_set_drvdata(pdev, NULL);
2201 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202err_release_legacy:
2203 if (release_io_ports)
2204 release_region(0x3C0, 32);
2205#if 0
2206 release_mem_region(0xA0000, 65535);
2207err_release_regions:
2208#endif
2209 pci_release_regions(pdev);
2210err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002211 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002212 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214err_out:
2215 return ret;
2216}
2217
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002218static void cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219{
2220 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221
Krzysztof Helt8503df62007-10-16 01:29:08 -07002222 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223}
2224
2225static struct pci_driver cirrusfb_pci_driver = {
2226 .name = "cirrusfb",
2227 .id_table = cirrusfb_pci_table,
2228 .probe = cirrusfb_pci_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002229 .remove = cirrusfb_pci_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230#ifdef CONFIG_PM
2231#if 0
2232 .suspend = cirrusfb_pci_suspend,
2233 .resume = cirrusfb_pci_resume,
2234#endif
2235#endif
2236};
2237#endif /* CONFIG_PCI */
2238
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239#ifdef CONFIG_ZORRO
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002240static int cirrusfb_zorro_register(struct zorro_dev *z,
2241 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 struct fb_info *info;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002244 int error;
2245 const struct zorrocl *zcl;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002246 enum cirrus_board btype;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002247 unsigned long regbase, ramsize, rambase;
2248 struct cirrusfb_info *cinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
2250 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2251 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002252 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002253 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 }
2255
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002256 zcl = (const struct zorrocl *)ent->driver_data;
2257 btype = zcl->type;
2258 regbase = zorro_resource_start(z) + zcl->regoffset;
2259 ramsize = zcl->ramsize;
2260 if (ramsize) {
2261 rambase = zorro_resource_start(z) + zcl->ramoffset;
Geert Uytterhoevene78bb882011-10-20 13:42:25 +02002262 if (zorro_resource_len(z) == 64 * MB_) {
2263 /* Quirk for 64 MiB Picasso IV */
2264 rambase += zcl->ramoffset;
2265 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002266 } else {
2267 struct zorro_dev *ram = zorro_find_device(zcl->ramid, NULL);
2268 if (!ram || !zorro_resource_len(ram)) {
2269 dev_err(info->device, "No video RAM found\n");
2270 error = -ENODEV;
2271 goto err_release_fb;
2272 }
2273 rambase = zorro_resource_start(ram);
2274 ramsize = zorro_resource_len(ram);
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +02002275 if (zcl->ramid2 &&
2276 (ram = zorro_find_device(zcl->ramid2, NULL))) {
2277 if (zorro_resource_start(ram) != rambase + ramsize) {
2278 dev_warn(info->device,
2279 "Skipping non-contiguous RAM at %pR\n",
2280 &ram->resource);
2281 } else {
2282 ramsize += zorro_resource_len(ram);
2283 }
2284 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002285 }
2286
2287 dev_info(info->device,
2288 "%s board detected, REG at 0x%lx, %lu MiB RAM at 0x%lx\n",
2289 cirrusfb_board_info[btype].name, regbase, ramsize / MB_,
2290 rambase);
2291
2292 if (!zorro_request_device(z, "cirrusfb")) {
2293 dev_err(info->device, "Cannot reserve %pR\n", &z->resource);
2294 error = -EBUSY;
2295 goto err_release_fb;
2296 }
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002297
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 cinfo->btype = btype;
2300
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002301 info->fix.mmio_start = regbase;
2302 cinfo->regbase = regbase > 16 * MB_ ? ioremap(regbase, 64 * 1024)
2303 : (caddr_t)ZTWO_VADDR(regbase);
2304 if (!cinfo->regbase) {
2305 dev_err(info->device, "Cannot map registers\n");
2306 error = -EIO;
2307 goto err_release_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 }
2309
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002310 info->fix.smem_start = rambase;
2311 info->screen_size = ramsize;
2312 info->screen_base = rambase > 16 * MB_ ? ioremap(rambase, ramsize)
2313 : (caddr_t)ZTWO_VADDR(rambase);
2314 if (!info->screen_base) {
2315 dev_err(info->device, "Cannot map video RAM\n");
2316 error = -EIO;
2317 goto err_unmap_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002319
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 cinfo->unmap = cirrusfb_zorro_unmap;
2321
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002322 dev_info(info->device,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002323 "Cirrus Logic chipset on Zorro bus, RAM (%lu MiB) at 0x%lx\n",
2324 ramsize / MB_, rambase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002326 /* MCLK select etc. */
2327 if (cirrusfb_board_info[btype].init_sr1f)
2328 vga_wseq(cinfo->regbase, CL_SEQR1F,
2329 cirrusfb_board_info[btype].sr1f);
2330
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002331 error = cirrusfb_register(info);
2332 if (error) {
2333 dev_err(info->device, "Failed to register device, error %d\n",
2334 error);
2335 goto err_unmap_ram;
2336 }
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002337
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002338 zorro_set_drvdata(z, info);
2339 return 0;
2340
2341err_unmap_ram:
2342 if (rambase > 16 * MB_)
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002343 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002345err_unmap_reg:
2346 if (regbase > 16 * MB_)
2347 iounmap(cinfo->regbase);
2348err_release_dev:
2349 zorro_release_device(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350err_release_fb:
2351 framebuffer_release(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002352 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353}
2354
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002355void cirrusfb_zorro_unregister(struct zorro_dev *z)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356{
2357 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358
Krzysztof Helt8503df62007-10-16 01:29:08 -07002359 cirrusfb_cleanup(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002360 zorro_set_drvdata(z, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361}
2362
2363static struct zorro_driver cirrusfb_zorro_driver = {
2364 .name = "cirrusfb",
2365 .id_table = cirrusfb_zorro_table,
2366 .probe = cirrusfb_zorro_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002367 .remove = cirrusfb_zorro_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368};
2369#endif /* CONFIG_ZORRO */
2370
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002372static int __init cirrusfb_setup(char *options)
2373{
Vlada Pericee119402008-11-19 15:36:45 -08002374 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 if (!options || !*options)
2377 return 0;
2378
Krzysztof Helt8503df62007-10-16 01:29:08 -07002379 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002380 if (!*this_opt)
2381 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383 if (!strcmp(this_opt, "noaccel"))
2384 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002385 else if (!strncmp(this_opt, "mode:", 5))
2386 mode_option = this_opt + 5;
2387 else
2388 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 }
2390 return 0;
2391}
2392#endif
2393
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 /*
2395 * Modularization
2396 */
2397
2398MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2399MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2400MODULE_LICENSE("GPL");
2401
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002402static int __init cirrusfb_init(void)
2403{
2404 int error = 0;
2405
2406#ifndef MODULE
2407 char *option = NULL;
2408
2409 if (fb_get_options("cirrusfb", &option))
2410 return -ENODEV;
2411 cirrusfb_setup(option);
2412#endif
2413
2414#ifdef CONFIG_ZORRO
2415 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2416#endif
2417#ifdef CONFIG_PCI
2418 error |= pci_register_driver(&cirrusfb_pci_driver);
2419#endif
2420 return error;
2421}
2422
Krzysztof Helt8503df62007-10-16 01:29:08 -07002423static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424{
2425#ifdef CONFIG_PCI
2426 pci_unregister_driver(&cirrusfb_pci_driver);
2427#endif
2428#ifdef CONFIG_ZORRO
2429 zorro_unregister_driver(&cirrusfb_zorro_driver);
2430#endif
2431}
2432
2433module_init(cirrusfb_init);
2434
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002435module_param(mode_option, charp, 0);
2436MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002437module_param(noaccel, bool, 0);
2438MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002439
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440#ifdef MODULE
2441module_exit(cirrusfb_exit);
2442#endif
2443
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444/**********************************************************************/
2445/* about the following functions - I have used the same names for the */
2446/* functions as Markus Wild did in his Retina driver for NetBSD as */
2447/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002448/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449/**********************************************************************/
2450
2451/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002452static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453 int regnum, unsigned char val)
2454{
2455 unsigned long regofs = 0;
2456
2457 if (cinfo->btype == BT_PICASSO) {
2458 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002459/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2460 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2462 regofs = 0xfff;
2463 }
2464
Krzysztof Helt8503df62007-10-16 01:29:08 -07002465 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466}
2467
2468/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470{
2471 unsigned long regofs = 0;
2472
2473 if (cinfo->btype == BT_PICASSO) {
2474 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002475/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2476 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2478 regofs = 0xfff;
2479 }
2480
Krzysztof Helt8503df62007-10-16 01:29:08 -07002481 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482}
2483
2484/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002485static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002487 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488
Krzysztof Helt8503df62007-10-16 01:29:08 -07002489 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 /* if we're just in "write value" mode, write back the */
2491 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002492 vga_w(cinfo->regbase, VGA_ATT_IW,
2493 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 }
2495 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2497 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498
2499 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002500 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501}
2502
2503/*** WHDR() - write into the Hidden DAC register ***/
2504/* as the HDR is the only extension register that requires special treatment
2505 * (the other extension registers are accessible just like the "ordinary"
2506 * registers of their functional group) here is a specialized routine for
2507 * accessing the HDR
2508 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002509static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510{
2511 unsigned char dummy;
2512
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002513 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002514 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 if (cinfo->btype == BT_PICASSO) {
2516 /* Klaus' hint for correct access to HDR on some boards */
2517 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518 WGen(cinfo, VGA_PEL_MSK, 0x00);
2519 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 dummy = RGen(cinfo, VGA_PEL_IW);
2522 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 }
2524 /* now do the usual stuff to access the HDR */
2525
Krzysztof Helt8503df62007-10-16 01:29:08 -07002526 dummy = RGen(cinfo, VGA_PEL_MSK);
2527 udelay(200);
2528 dummy = RGen(cinfo, VGA_PEL_MSK);
2529 udelay(200);
2530 dummy = RGen(cinfo, VGA_PEL_MSK);
2531 udelay(200);
2532 dummy = RGen(cinfo, VGA_PEL_MSK);
2533 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534
Krzysztof Helt8503df62007-10-16 01:29:08 -07002535 WGen(cinfo, VGA_PEL_MSK, val);
2536 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537
2538 if (cinfo->btype == BT_PICASSO) {
2539 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540 dummy = RGen(cinfo, VGA_PEL_IW);
2541 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542
2543 /* and at the end, restore the mask value */
2544 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002545 WGen(cinfo, VGA_PEL_MSK, 0xff);
2546 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 }
2548}
2549
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002551static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552{
2553#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002556 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557#endif
2558}
2559
2560/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002561static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562{
2563#ifdef CONFIG_ZORRO
2564 /* writing an arbitrary value to this one causes the monitor switcher */
2565 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569#endif
2570}
2571
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 unsigned char green, unsigned char blue)
2575{
2576 unsigned int data = VGA_PEL_D;
2577
2578 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580
2581 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002582 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002583 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584 /* but DAC data register IS, at least for Picasso II */
2585 if (cinfo->btype == BT_PICASSO)
2586 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002587 vga_w(cinfo->regbase, data, red);
2588 vga_w(cinfo->regbase, data, green);
2589 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002591 vga_w(cinfo->regbase, data, blue);
2592 vga_w(cinfo->regbase, data, green);
2593 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 }
2595}
2596
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597#if 0
2598/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002599static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 unsigned char *green, unsigned char *blue)
2601{
2602 unsigned int data = VGA_PEL_D;
2603
Krzysztof Helt8503df62007-10-16 01:29:08 -07002604 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605
2606 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2607 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2608 if (cinfo->btype == BT_PICASSO)
2609 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002610 *red = vga_r(cinfo->regbase, data);
2611 *green = vga_r(cinfo->regbase, data);
2612 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002614 *blue = vga_r(cinfo->regbase, data);
2615 *green = vga_r(cinfo->regbase, data);
2616 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617 }
2618}
2619#endif
2620
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621/*******************************************************************
2622 cirrusfb_WaitBLT()
2623
2624 Wait for the BitBLT engine to complete a possible earlier job
2625*********************************************************************/
2626
2627/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002628static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002630 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002631 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632}
2633
2634/*******************************************************************
2635 cirrusfb_BitBLT()
2636
2637 perform accelerated "scrolling"
2638********************************************************************/
2639
Krzysztof Helt8343c892009-03-31 15:25:11 -07002640static void cirrusfb_set_blitter(u8 __iomem *regbase,
2641 u_short nwidth, u_short nheight,
2642 u_long nsrc, u_long ndest,
2643 u_short bltmode, u_short line_length)
2644
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002647 /* dest pitch low */
2648 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2649 /* dest pitch hi */
2650 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2651 /* source pitch low */
2652 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2653 /* source pitch hi */
2654 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655
2656 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002657 /* BLT width low */
2658 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2659 /* BLT width hi */
2660 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661
2662 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663 /* BLT height low */
2664 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2665 /* BLT width hi */
2666 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667
2668 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002669 /* BLT dest low */
2670 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2671 /* BLT dest mid */
2672 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2673 /* BLT dest hi */
2674 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675
2676 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002677 /* BLT src low */
2678 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2679 /* BLT src mid */
2680 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2681 /* BLT src hi */
2682 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683
2684 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002685 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686
2687 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002688 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689
2690 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002691 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002692}
2693
2694/*******************************************************************
2695 cirrusfb_BitBLT()
2696
2697 perform accelerated "scrolling"
2698********************************************************************/
2699
2700static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2701 u_short curx, u_short cury,
2702 u_short destx, u_short desty,
2703 u_short width, u_short height,
2704 u_short line_length)
2705{
2706 u_short nwidth = width - 1;
2707 u_short nheight = height - 1;
2708 u_long nsrc, ndest;
2709 u_char bltmode;
2710
2711 bltmode = 0x00;
2712 /* if source adr < dest addr, do the Blt backwards */
2713 if (cury <= desty) {
2714 if (cury == desty) {
2715 /* if src and dest are on the same line, check x */
2716 if (curx < destx)
2717 bltmode |= 0x01;
2718 } else
2719 bltmode |= 0x01;
2720 }
2721 /* standard case: forward blitting */
2722 nsrc = (cury * line_length) + curx;
2723 ndest = (desty * line_length) + destx;
2724 if (bltmode) {
2725 /* this means start addresses are at the end,
2726 * counting backwards
2727 */
2728 nsrc += nheight * line_length + nwidth;
2729 ndest += nheight * line_length + nwidth;
2730 }
2731
2732 cirrusfb_WaitBLT(regbase);
2733
2734 cirrusfb_set_blitter(regbase, nwidth, nheight,
2735 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736}
2737
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738/*******************************************************************
2739 cirrusfb_RectFill()
2740
2741 perform accelerated rectangle fill
2742********************************************************************/
2743
Krzysztof Helt8503df62007-10-16 01:29:08 -07002744static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002746 u32 fg_color, u32 bg_color, u_short line_length,
2747 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002749 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 u_char op;
2751
Krzysztof Helt8503df62007-10-16 01:29:08 -07002752 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 /* This is a ColorExpand Blt, using the */
2755 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002756 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2757 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758
Krzysztof Helt9e848062009-03-31 15:25:11 -07002759 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002760 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002761 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2762 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2763 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002764 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002765 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002766 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2767 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002768 op = 0xa0;
2769 }
2770 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002771 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2772 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2773 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002775 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002776 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777}
2778
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779/**************************************************************************
2780 * bestclock() - determine closest possible clock lower(?) than the
2781 * desired pixel clock
2782 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002783static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002785 int n, d;
2786 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787
Krzysztof Helt8503df62007-10-16 01:29:08 -07002788 assert(nom != NULL);
2789 assert(den != NULL);
2790 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791
2792 *nom = 0;
2793 *den = 0;
2794 *div = 0;
2795
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 if (freq < 8000)
2797 freq = 8000;
2798
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002799 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800
2801 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002802 int s = 0;
2803
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002804 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002806 int temp = d;
2807
2808 if (temp > 31) {
2809 s = 1;
2810 temp >>= 1;
2811 }
2812 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002813 h = h > freq ? h - freq : freq - h;
2814 if (h < diff) {
2815 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002817 *den = temp;
2818 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 }
2820 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002821 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002823 if (d > 31) {
2824 s = 1;
2825 d >>= 1;
2826 }
2827 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002828 h = h > freq ? h - freq : freq - h;
2829 if (h < diff) {
2830 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002832 *den = d;
2833 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 }
2835 }
2836 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837}
2838
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839/* -------------------------------------------------------------------------
2840 *
2841 * debugging functions
2842 *
2843 * -------------------------------------------------------------------------
2844 */
2845
2846#ifdef CIRRUSFB_DEBUG
2847
2848/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 * cirrusfb_dbg_print_regs
2850 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2851 * @reg_class: type of registers to read: %CRT, or %SEQ
2852 *
2853 * DESCRIPTION:
2854 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2855 * old-style I/O ports are queried for information, otherwise MMIO is
2856 * used at the given @base address to query the information.
2857 */
2858
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002859static void cirrusfb_dbg_print_regs(struct fb_info *info,
2860 caddr_t regbase,
2861 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862{
2863 va_list list;
2864 unsigned char val = 0;
2865 unsigned reg;
2866 char *name;
2867
Krzysztof Helt8503df62007-10-16 01:29:08 -07002868 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869
Krzysztof Helt8503df62007-10-16 01:29:08 -07002870 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002872 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873
2874 switch (reg_class) {
2875 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002876 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877 break;
2878 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002879 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 break;
2881 default:
2882 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002883 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884 break;
2885 }
2886
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002887 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888
Krzysztof Helt8503df62007-10-16 01:29:08 -07002889 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 }
2891
Krzysztof Helt8503df62007-10-16 01:29:08 -07002892 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893}
2894
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 * cirrusfb_dbg_reg_dump
2897 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2898 *
2899 * DESCRIPTION:
2900 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2901 * old-style I/O ports are queried for information, otherwise MMIO is
2902 * used at the given @base address to query the information.
2903 */
2904
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002905static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002907 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002909 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910 "CR00", 0x00,
2911 "CR01", 0x01,
2912 "CR02", 0x02,
2913 "CR03", 0x03,
2914 "CR04", 0x04,
2915 "CR05", 0x05,
2916 "CR06", 0x06,
2917 "CR07", 0x07,
2918 "CR08", 0x08,
2919 "CR09", 0x09,
2920 "CR0A", 0x0A,
2921 "CR0B", 0x0B,
2922 "CR0C", 0x0C,
2923 "CR0D", 0x0D,
2924 "CR0E", 0x0E,
2925 "CR0F", 0x0F,
2926 "CR10", 0x10,
2927 "CR11", 0x11,
2928 "CR12", 0x12,
2929 "CR13", 0x13,
2930 "CR14", 0x14,
2931 "CR15", 0x15,
2932 "CR16", 0x16,
2933 "CR17", 0x17,
2934 "CR18", 0x18,
2935 "CR22", 0x22,
2936 "CR24", 0x24,
2937 "CR26", 0x26,
2938 "CR2D", 0x2D,
2939 "CR2E", 0x2E,
2940 "CR2F", 0x2F,
2941 "CR30", 0x30,
2942 "CR31", 0x31,
2943 "CR32", 0x32,
2944 "CR33", 0x33,
2945 "CR34", 0x34,
2946 "CR35", 0x35,
2947 "CR36", 0x36,
2948 "CR37", 0x37,
2949 "CR38", 0x38,
2950 "CR39", 0x39,
2951 "CR3A", 0x3A,
2952 "CR3B", 0x3B,
2953 "CR3C", 0x3C,
2954 "CR3D", 0x3D,
2955 "CR3E", 0x3E,
2956 "CR3F", 0x3F,
2957 NULL);
2958
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002959 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002961 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002963 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964 "SR00", 0x00,
2965 "SR01", 0x01,
2966 "SR02", 0x02,
2967 "SR03", 0x03,
2968 "SR04", 0x04,
2969 "SR08", 0x08,
2970 "SR09", 0x09,
2971 "SR0A", 0x0A,
2972 "SR0B", 0x0B,
2973 "SR0D", 0x0D,
2974 "SR10", 0x10,
2975 "SR11", 0x11,
2976 "SR12", 0x12,
2977 "SR13", 0x13,
2978 "SR14", 0x14,
2979 "SR15", 0x15,
2980 "SR16", 0x16,
2981 "SR17", 0x17,
2982 "SR18", 0x18,
2983 "SR19", 0x19,
2984 "SR1A", 0x1A,
2985 "SR1B", 0x1B,
2986 "SR1C", 0x1C,
2987 "SR1D", 0x1D,
2988 "SR1E", 0x1E,
2989 "SR1F", 0x1F,
2990 NULL);
2991
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002992 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002993}
2994
2995#endif /* CIRRUSFB_DEBUG */
2996