blob: bab713b63a0c41db3830d278cab21567fb0bea6a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/errno.h>
40#include <linux/string.h>
41#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/slab.h>
43#include <linux/delay.h>
44#include <linux/fb.h>
45#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/pgtable.h>
47
48#ifdef CONFIG_ZORRO
49#include <linux/zorro.h>
50#endif
51#ifdef CONFIG_PCI
52#include <linux/pci.h>
53#endif
54#ifdef CONFIG_AMIGA
55#include <asm/amigahw.h>
56#endif
57#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110058#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070059#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#else
61#define isPReP 0
62#endif
63
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070064#include <video/vga.h>
65#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Linus Torvalds1da177e2005-04-16 15:20:36 -070067/*****************************************************************
68 *
69 * debugging and utility macros
70 *
71 */
72
Linus Torvalds1da177e2005-04-16 15:20:36 -070073/* disable runtime assertions? */
74/* #define CIRRUSFB_NDEBUG */
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076/* debugging assertions */
77#ifndef CIRRUSFB_NDEBUG
78#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070079 if (!(expr)) { \
80 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070081 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#else
84#define assert(expr)
85#endif
86
Krzysztof Helt8503df62007-10-16 01:29:08 -070087#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/*****************************************************************
90 *
91 * chipset information
92 *
93 */
94
95/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070096enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 BT_NONE = 0,
98 BT_SD64,
99 BT_PICCOLO,
100 BT_PICASSO,
101 BT_SPECTRUM,
102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
105 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700106};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108/*
109 * per-board-type information, used for enumerating and abstracting
110 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700111 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 * use direct indexing on this array
113 * NOTE: '__initdata' cannot be used as some of this info
114 * is required at runtime. Maybe separate into an init-only and
115 * a run-time table?
116 */
117static const struct cirrusfb_board_info_rec {
118 char *name; /* ASCII name of chipset */
119 long maxclock[5]; /* maximum video clock */
120 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700121 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
122 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700123 /* construct bit 19 of screen start address */
124 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 /* initial SR07 value, then for each mode */
127 unsigned char sr07;
128 unsigned char sr07_1bpp;
129 unsigned char sr07_1bpp_mux;
130 unsigned char sr07_8bpp;
131 unsigned char sr07_8bpp_mux;
132
133 unsigned char sr1f; /* SR1F VGA initial register value */
134} cirrusfb_board_info[] = {
135 [BT_SD64] = {
136 .name = "CL SD64",
137 .maxclock = {
138 /* guess */
139 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700140 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700142 .init_sr07 = true,
143 .init_sr1f = true,
144 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 .sr07 = 0xF0,
146 .sr07_1bpp = 0xF0,
147 .sr07_8bpp = 0xF1,
148 .sr1f = 0x20
149 },
150 [BT_PICCOLO] = {
151 .name = "CL Piccolo",
152 .maxclock = {
153 /* guess */
154 90000, 90000, 90000, 90000, 90000
155 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700156 .init_sr07 = true,
157 .init_sr1f = true,
158 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 .sr07 = 0x80,
160 .sr07_1bpp = 0x80,
161 .sr07_8bpp = 0x81,
162 .sr1f = 0x22
163 },
164 [BT_PICASSO] = {
165 .name = "CL Picasso",
166 .maxclock = {
167 /* guess */
168 90000, 90000, 90000, 90000, 90000
169 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700170 .init_sr07 = true,
171 .init_sr1f = true,
172 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .sr07 = 0x20,
174 .sr07_1bpp = 0x20,
175 .sr07_8bpp = 0x21,
176 .sr1f = 0x22
177 },
178 [BT_SPECTRUM] = {
179 .name = "CL Spectrum",
180 .maxclock = {
181 /* guess */
182 90000, 90000, 90000, 90000, 90000
183 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700184 .init_sr07 = true,
185 .init_sr1f = true,
186 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 .sr07 = 0x80,
188 .sr07_1bpp = 0x80,
189 .sr07_8bpp = 0x81,
190 .sr1f = 0x22
191 },
192 [BT_PICASSO4] = {
193 .name = "CL Picasso4",
194 .maxclock = {
195 135100, 135100, 85500, 85500, 0
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = false,
199 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x20,
201 .sr07_1bpp = 0x20,
202 .sr07_8bpp = 0x21,
203 .sr1f = 0
204 },
205 [BT_ALPINE] = {
206 .name = "CL Alpine",
207 .maxclock = {
208 /* for the GD5430. GD5446 can do more... */
209 85500, 85500, 50000, 28500, 0
210 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700211 .init_sr07 = true,
212 .init_sr1f = true,
213 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 .sr07 = 0xA0,
215 .sr07_1bpp = 0xA1,
216 .sr07_1bpp_mux = 0xA7,
217 .sr07_8bpp = 0xA1,
218 .sr07_8bpp_mux = 0xA7,
219 .sr1f = 0x1C
220 },
221 [BT_GD5480] = {
222 .name = "CL GD5480",
223 .maxclock = {
224 135100, 200000, 200000, 135100, 135100
225 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700226 .init_sr07 = true,
227 .init_sr1f = true,
228 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 .sr07 = 0x10,
230 .sr07_1bpp = 0x11,
231 .sr07_8bpp = 0x11,
232 .sr1f = 0x1C
233 },
234 [BT_LAGUNA] = {
235 .name = "CL Laguna",
236 .maxclock = {
237 /* guess */
238 135100, 135100, 135100, 135100, 135100,
239 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700240 .init_sr07 = false,
241 .init_sr1f = false,
242 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 }
244};
245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246#ifdef CONFIG_PCI
247#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000248 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700251 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
252 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
253 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
254 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
255 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
256 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
257 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
258 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
259 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
260 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
261 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 { 0, }
263};
264MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
265#undef CHIP
266#endif /* CONFIG_PCI */
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268#ifdef CONFIG_ZORRO
269static const struct zorro_device_id cirrusfb_zorro_table[] = {
270 {
271 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
272 .driver_data = BT_SD64,
273 }, {
274 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
275 .driver_data = BT_PICCOLO,
276 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700277 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 .driver_data = BT_PICASSO,
279 }, {
280 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
281 .driver_data = BT_SPECTRUM,
282 }, {
283 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
284 .driver_data = BT_PICASSO4,
285 },
286 { 0 }
287};
288
289static const struct {
290 zorro_id id2;
291 unsigned long size;
292} cirrusfb_zorro_table2[] = {
293 [BT_SD64] = {
294 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
295 .size = 0x400000
296 },
297 [BT_PICCOLO] = {
298 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
299 .size = 0x200000
300 },
301 [BT_PICASSO] = {
302 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
303 .size = 0x200000
304 },
305 [BT_SPECTRUM] = {
306 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
307 .size = 0x200000
308 },
309 [BT_PICASSO4] = {
310 .id2 = 0,
311 .size = 0x400000
312 }
313};
314#endif /* CONFIG_ZORRO */
315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700317enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700318 CRT,
319 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700320};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700321#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323/* info about board */
324struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700326 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700327 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 unsigned char SFR; /* Shadow of special function register */
329
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700330 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700332 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700334 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335};
336
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700337static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700338static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340/****************************************************************************/
341/**** BEGIN PROTOTYPES ******************************************************/
342
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700344static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
345 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700348static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700349static void switch_monitor(struct cirrusfb_info *cinfo, int on);
350static void WGen(const struct cirrusfb_info *cinfo,
351 int regnum, unsigned char val);
352static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
353static void AttrOn(const struct cirrusfb_info *cinfo);
354static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
355static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
356static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
357static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
358 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
361 unsigned char *red, unsigned char *green,
362 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700364static void cirrusfb_WaitBLT(u8 __iomem *regbase);
365static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
366 u_short curx, u_short cury,
367 u_short destx, u_short desty,
368 u_short width, u_short height,
369 u_short line_length);
370static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
371 u_short x, u_short y,
372 u_short width, u_short height,
373 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700375static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700378static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
379static void cirrusfb_dbg_print_regs(struct fb_info *info,
380 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700381 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382#endif /* CIRRUSFB_DEBUG */
383
384/*** END PROTOTYPES ********************************************************/
385/*****************************************************************************/
386/*** BEGIN Interface Used by the World ***************************************/
387
Krzysztof Helt8503df62007-10-16 01:29:08 -0700388static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
390/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700391static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392{
393 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700394 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 return 0;
396}
397
398/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700399static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
401 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700402 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 return 0;
404}
405
406/**** END Interface used by the World *************************************/
407/****************************************************************************/
408/**** BEGIN Hardware specific Routines **************************************/
409
Krzysztof Helt486ff382008-10-15 22:03:42 -0700410/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700411static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700413 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700414 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
Krzysztof Helt486ff382008-10-15 22:03:42 -0700416 /* Read MCLK value */
417 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700418 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700421 * should divide it by to get VCLK
422 */
423
424 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700425 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700426 return 1;
427 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700428 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700429 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 }
431
Krzysztof Helt486ff382008-10-15 22:03:42 -0700432 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433}
434
Krzysztof Helt99a45842009-03-31 15:25:09 -0700435static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
436 struct fb_info *info)
437{
438 long freq;
439 long maxclock;
440 struct cirrusfb_info *cinfo = info->par;
441 unsigned maxclockidx = var->bits_per_pixel >> 3;
442
443 /* convert from ps to kHz */
444 freq = PICOS2KHZ(var->pixclock);
445
446 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
447
448 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
449 cinfo->multiplexing = 0;
450
451 /* If the frequency is greater than we can support, we might be able
452 * to use multiplexing for the video mode */
453 if (freq > maxclock) {
454 switch (cinfo->btype) {
455 case BT_ALPINE:
456 case BT_GD5480:
457 cinfo->multiplexing = 1;
458 break;
459
460 default:
461 dev_err(info->device,
462 "Frequency greater than maxclock (%ld kHz)\n",
463 maxclock);
464 return -EINVAL;
465 }
466 }
467#if 0
468 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
469 * the VCLK is double the pixel clock. */
470 switch (var->bits_per_pixel) {
471 case 16:
472 case 32:
473 if (var->xres <= 800)
474 /* Xbh has this type of clock for 32-bit */
475 freq /= 2;
476 break;
477 }
478#endif
479 return 0;
480}
481
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482static int cirrusfb_check_var(struct fb_var_screeninfo *var,
483 struct fb_info *info)
484{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700485 int yres;
486 /* memory size in pixels */
487 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
489 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700490 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 var->red.offset = 0;
492 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700493 var->green = var->red;
494 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 break;
496
497 case 8:
498 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700499 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700500 var->green = var->red;
501 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 break;
503
504 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700505 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 var->red.offset = 2;
507 var->green.offset = -3;
508 var->blue.offset = 8;
509 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700510 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 var->green.offset = 5;
512 var->blue.offset = 0;
513 }
514 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700515 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 var->blue.length = 5;
517 break;
518
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700520 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 var->red.offset = 8;
522 var->green.offset = 16;
523 var->blue.offset = 24;
524 } else {
525 var->red.offset = 16;
526 var->green.offset = 8;
527 var->blue.offset = 0;
528 }
529 var->red.length = 8;
530 var->green.length = 8;
531 var->blue.length = 8;
532 break;
533
534 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700535 dev_dbg(info->device,
536 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700537 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 /* should never occur */
539 break;
540 }
541
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700542 if (var->xres_virtual < var->xres)
543 var->xres_virtual = var->xres;
544 /* use highest possible virtual resolution */
545 if (var->yres_virtual == -1) {
546 var->yres_virtual = pixels / var->xres_virtual;
547
548 dev_info(info->device,
549 "virtual resolution set to maximum of %dx%d\n",
550 var->xres_virtual, var->yres_virtual);
551 }
552 if (var->yres_virtual < var->yres)
553 var->yres_virtual = var->yres;
554
555 if (var->xres_virtual * var->yres_virtual > pixels) {
556 dev_err(info->device, "mode %dx%dx%d rejected... "
557 "virtual resolution too high to fit into video memory!\n",
558 var->xres_virtual, var->yres_virtual,
559 var->bits_per_pixel);
560 return -EINVAL;
561 }
562
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700563 if (var->xoffset < 0)
564 var->xoffset = 0;
565 if (var->yoffset < 0)
566 var->yoffset = 0;
567
568 /* truncate xoffset and yoffset to maximum if too high */
569 if (var->xoffset > var->xres_virtual - var->xres)
570 var->xoffset = var->xres_virtual - var->xres - 1;
571 if (var->yoffset > var->yres_virtual - var->yres)
572 var->yoffset = var->yres_virtual - var->yres - 1;
573
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 var->red.msb_right =
575 var->green.msb_right =
576 var->blue.msb_right =
577 var->transp.offset =
578 var->transp.length =
579 var->transp.msb_right = 0;
580
581 yres = var->yres;
582 if (var->vmode & FB_VMODE_DOUBLE)
583 yres *= 2;
584 else if (var->vmode & FB_VMODE_INTERLACED)
585 yres = (yres + 1) / 2;
586
587 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700588 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700589 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 return -EINVAL;
591 }
592
Krzysztof Helt99a45842009-03-31 15:25:09 -0700593 if (cirrusfb_check_pixclock(var, info))
594 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 return 0;
597}
598
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700599static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700601 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700602 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700603
Krzysztof Helt8503df62007-10-16 01:29:08 -0700604 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700605 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Krzysztof Helt486ff382008-10-15 22:03:42 -0700607 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700608 dev_dbg(info->device, "Set %s as pixclock source.\n",
609 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700610 old1f |= 0x40;
611 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
612 if (div == 2)
613 old1e |= 1;
614
615 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700617 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618}
619
620/*************************************************************************
621 cirrusfb_set_par_foo()
622
623 actually writes the values for a new video mode into the hardware,
624**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700625static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626{
627 struct cirrusfb_info *cinfo = info->par;
628 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 u8 __iomem *regbase = cinfo->regbase;
630 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700631 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700633 int hdispend, hsyncstart, hsyncend, htotal;
634 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700635 long freq;
636 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700637 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700639 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700641
642 switch (var->bits_per_pixel) {
643 case 1:
644 info->fix.line_length = var->xres_virtual / 8;
645 info->fix.visual = FB_VISUAL_MONO10;
646 break;
647
648 case 8:
649 info->fix.line_length = var->xres_virtual;
650 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
651 break;
652
653 case 16:
654 case 32:
655 info->fix.line_length = var->xres_virtual *
656 var->bits_per_pixel >> 3;
657 info->fix.visual = FB_VISUAL_TRUECOLOR;
658 break;
659 }
660 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700662 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 bi = &cirrusfb_board_info[cinfo->btype];
665
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700666 hsyncstart = var->xres + var->right_margin;
667 hsyncend = hsyncstart + var->hsync_len;
668 htotal = (hsyncend + var->left_margin) / 8 - 5;
669 hdispend = var->xres / 8 - 1;
670 hsyncstart = hsyncstart / 8 + 1;
671 hsyncend = hsyncend / 8 + 1;
672
673 yres = var->yres;
674 vsyncstart = yres + var->lower_margin;
675 vsyncend = vsyncstart + var->vsync_len;
676 vtotal = vsyncend + var->upper_margin;
677 vdispend = yres - 1;
678
679 if (var->vmode & FB_VMODE_DOUBLE) {
680 yres *= 2;
681 vsyncstart *= 2;
682 vsyncend *= 2;
683 vtotal *= 2;
684 } else if (var->vmode & FB_VMODE_INTERLACED) {
685 yres = (yres + 1) / 2;
686 vsyncstart = (vsyncstart + 1) / 2;
687 vsyncend = (vsyncend + 1) / 2;
688 vtotal = (vtotal + 1) / 2;
689 }
690
691 vtotal -= 2;
692 vsyncstart -= 1;
693 vsyncend -= 1;
694
695 if (yres >= 1024) {
696 vtotal /= 2;
697 vsyncstart /= 2;
698 vsyncend /= 2;
699 vdispend /= 2;
700 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700701 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700702 htotal /= 2;
703 hsyncstart /= 2;
704 hsyncend /= 2;
705 hdispend /= 2;
706 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700708 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
710 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700711 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700712 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700714 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700715 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700717 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700718 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719
Krzysztof Helt8503df62007-10-16 01:29:08 -0700720 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700721 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700722 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700723 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700725 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700726 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700728 tmp = hsyncend % 32;
729 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700731 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700732 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700734 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700735 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
737 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700738 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700740 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700742 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700744 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700746 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700748 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700750 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700752 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700753 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
755 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700756 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 tmp |= 0x20;
758 if (var->vmode & FB_VMODE_DOUBLE)
759 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700760 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700761 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700763 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700766 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700769 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700770 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700772 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700773 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700775 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700776 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700778 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700779 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
781 tmp = 0;
782 if (var->vmode & FB_VMODE_INTERLACED)
783 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700788 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 tmp |= 128;
792
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700793 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700794 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700796 freq = PICOS2KHZ(var->pixclock);
797 bestclock(freq, &nom, &den, &div);
798
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700799 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
800 freq, nom, den, div);
801
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 /* set VCLK0 */
803 /* hardware RefClock: 14.31818 MHz */
804 /* formula: VClk = (OSC * N) / (D * (1+P)) */
805 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
806
Krzysztof Helt486ff382008-10-15 22:03:42 -0700807 if (cinfo->btype == BT_ALPINE) {
808 /* if freq is close to mclk or mclk/2 select mclk
809 * as clock source
810 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700811 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700812 if (divMCLK) {
813 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700814 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700815 }
816 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700817 if (cinfo->btype == BT_LAGUNA) {
818 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
819 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
820 unsigned short tile_control;
821
822 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
823 fb_writew(tile_control & ~0x80, cinfo->laguna_mmio + 0x2c4);
824
825 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
826 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
827 control = fb_readw(cinfo->laguna_mmio + 0x402);
828 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
829 control &= ~0x6800;
830 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700831 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700832 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700833 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700834 tmp = den << 1;
835 if (div != 0)
836 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700837 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
838 if ((cinfo->btype == BT_SD64) ||
839 (cinfo->btype == BT_ALPINE) ||
840 (cinfo->btype == BT_GD5480))
841 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700843 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
844 /* Laguna chipset has reversed clock registers */
845 if (cinfo->btype == BT_LAGUNA) {
846 vga_wseq(regbase, CL_SEQRE, tmp);
847 vga_wseq(regbase, CL_SEQR1E, nom);
848 } else {
849 vga_wseq(regbase, CL_SEQRB, nom);
850 vga_wseq(regbase, CL_SEQR1B, tmp);
851 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700852 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700854 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700856 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 else
858 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
859 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700860 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 /* don't know if it would hurt to also program this if no interlaced */
863 /* mode is used, but I feel better this way.. :-) */
864 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700865 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700867 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700870 /* enable display memory & CRTC I/O address for color mode */
871 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
873 tmp |= 0x40;
874 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
875 tmp |= 0x80;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700876 if (cinfo->btype == BT_LAGUNA)
877 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700878 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
Krzysztof Helt8503df62007-10-16 01:29:08 -0700880 /* text cursor on and start line */
881 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
882 /* text cursor end line */
883 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 /******************************************************
886 *
887 * 1 bpp
888 *
889 */
890
891 /* programming for different color depths */
892 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700893 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700894 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
896 /* SR07 */
897 switch (cinfo->btype) {
898 case BT_SD64:
899 case BT_PICCOLO:
900 case BT_PICASSO:
901 case BT_SPECTRUM:
902 case BT_PICASSO4:
903 case BT_ALPINE:
904 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700905 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700906 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 bi->sr07_1bpp_mux : bi->sr07_1bpp);
908 break;
909
910 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700911 vga_wseq(regbase, CL_SEQR7,
912 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 break;
914
915 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700916 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 break;
918 }
919
920 /* Extended Sequencer Mode */
921 switch (cinfo->btype) {
922 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700923 /* setting the SEQRF on SD64 is not necessary
924 * (only during init)
925 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700926 /* MCLK select */
927 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 break;
929
930 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700931 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700932 /* ### ueberall 0x22? */
933 /* ##vorher 1c MCLK select */
934 vga_wseq(regbase, CL_SEQR1F, 0x22);
935 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
936 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 break;
938
939 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700940 /* ##vorher 22 MCLK select */
941 vga_wseq(regbase, CL_SEQR1F, 0x22);
942 /* ## vorher d0 avoid FIFO underruns..? */
943 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 break;
945
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 case BT_PICASSO4:
947 case BT_ALPINE:
948 case BT_GD5480:
949 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 /* do nothing */
951 break;
952
953 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700954 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 break;
956 }
957
Krzysztof Helt8503df62007-10-16 01:29:08 -0700958 /* pixel mask: pass-through for first plane */
959 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700960 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700961 /* hidden dac reg: 1280x1024 */
962 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700964 /* hidden dac: nothing */
965 WHDR(cinfo, 0);
966 /* memory mode: odd/even, ext. memory */
967 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
968 /* plane mask: only write to first plane */
969 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 }
971
972 /******************************************************
973 *
974 * 8 bpp
975 *
976 */
977
978 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700979 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 switch (cinfo->btype) {
981 case BT_SD64:
982 case BT_PICCOLO:
983 case BT_PICASSO:
984 case BT_SPECTRUM:
985 case BT_PICASSO4:
986 case BT_ALPINE:
987 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700988 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700989 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 bi->sr07_8bpp_mux : bi->sr07_8bpp);
991 break;
992
993 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700994 vga_wseq(regbase, CL_SEQR7,
995 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700996 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 break;
998
999 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001000 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 break;
1002 }
1003
1004 switch (cinfo->btype) {
1005 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001006 /* MCLK select */
1007 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 break;
1009
1010 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001013 /* ### vorher 1c MCLK select */
1014 vga_wseq(regbase, CL_SEQR1F, 0x22);
1015 /* Fast Page-Mode writes */
1016 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 break;
1018
1019 case BT_PICASSO4:
1020#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001021 /* ### INCOMPLETE!! */
1022 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001024/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 break;
1026
1027 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 /* We already set SRF and SR1F */
1029 break;
1030
1031 case BT_GD5480:
1032 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 /* do nothing */
1034 break;
1035
1036 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001037 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 break;
1039 }
1040
Krzysztof Helt8503df62007-10-16 01:29:08 -07001041 /* mode register: 256 color mode */
1042 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001043 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001044 /* hidden dac reg: 1280x1024 */
1045 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001047 /* hidden dac: nothing */
1048 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 }
1050
1051 /******************************************************
1052 *
1053 * 16 bpp
1054 *
1055 */
1056
1057 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001058 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 switch (cinfo->btype) {
1060 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001061 /* Extended Sequencer Mode: 256c col. mode */
1062 vga_wseq(regbase, CL_SEQR7, 0xf7);
1063 /* MCLK select */
1064 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 break;
1066
1067 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001068 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001069 vga_wseq(regbase, CL_SEQR7, 0x87);
1070 /* Fast Page-Mode writes */
1071 vga_wseq(regbase, CL_SEQRF, 0xb0);
1072 /* MCLK select */
1073 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 break;
1075
1076 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001077 vga_wseq(regbase, CL_SEQR7, 0x27);
1078 /* Fast Page-Mode writes */
1079 vga_wseq(regbase, CL_SEQRF, 0xb0);
1080 /* MCLK select */
1081 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 break;
1083
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001085 vga_wseq(regbase, CL_SEQR7, 0x27);
1086/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 break;
1088
1089 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001090 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 break;
1092
1093 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001094 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 /* We already set SRF and SR1F */
1096 break;
1097
1098 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001099 vga_wseq(regbase, CL_SEQR7,
1100 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001101 control |= 0x2000;
1102 format |= 0x1400;
1103 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 break;
1105
1106 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001107 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 break;
1109 }
1110
Krzysztof Helt8503df62007-10-16 01:29:08 -07001111 /* mode register: 256 color mode */
1112 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001114 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115#elif defined(CONFIG_ZORRO)
1116 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001117 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 }
1120
1121 /******************************************************
1122 *
1123 * 32 bpp
1124 *
1125 */
1126
1127 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001128 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 switch (cinfo->btype) {
1130 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001131 /* Extended Sequencer Mode: 256c col. mode */
1132 vga_wseq(regbase, CL_SEQR7, 0xf9);
1133 /* MCLK select */
1134 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 break;
1136
1137 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001138 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001139 vga_wseq(regbase, CL_SEQR7, 0x85);
1140 /* Fast Page-Mode writes */
1141 vga_wseq(regbase, CL_SEQRF, 0xb0);
1142 /* MCLK select */
1143 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 break;
1145
1146 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001147 vga_wseq(regbase, CL_SEQR7, 0x25);
1148 /* Fast Page-Mode writes */
1149 vga_wseq(regbase, CL_SEQRF, 0xb0);
1150 /* MCLK select */
1151 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 break;
1153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 vga_wseq(regbase, CL_SEQR7, 0x25);
1156/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 break;
1158
1159 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001160 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 break;
1162
1163 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001164 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 /* We already set SRF and SR1F */
1166 break;
1167
1168 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001169 vga_wseq(regbase, CL_SEQR7,
1170 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001171 control |= 0x6000;
1172 format |= 0x3400;
1173 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 break;
1175
1176 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001177 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 break;
1179 }
1180
Krzysztof Helt8503df62007-10-16 01:29:08 -07001181 /* mode register: 256 color mode */
1182 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001183 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1184 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 }
1186
1187 /******************************************************
1188 *
1189 * unknown/unsupported bpp
1190 *
1191 */
1192
Krzysztof Helt8503df62007-10-16 01:29:08 -07001193 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001194 dev_err(info->device,
1195 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197
Krzysztof Helt6683e012009-03-31 15:25:06 -07001198 pitch = info->fix.line_length >> 3;
1199 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001201 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 tmp |= 0x10; /* offset overflow bit */
1203
Krzysztof Helt8503df62007-10-16 01:29:08 -07001204 /* screen start addr #16-18, fastpagemode cycles */
1205 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001207 /* screen start address bit 19 */
1208 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001209 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001211 if (cinfo->btype == BT_LAGUNA) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001212 tmp = 0;
1213 if ((htotal + 5) & 256)
1214 tmp |= 128;
1215 if (hdispend & 256)
1216 tmp |= 64;
1217 if (hsyncstart & 256)
1218 tmp |= 48;
1219 if (vtotal & 1024)
1220 tmp |= 8;
1221 if (vdispend & 1024)
1222 tmp |= 4;
1223 if (vsyncstart & 1024)
1224 tmp |= 3;
1225
1226 vga_wcrt(regbase, CL_CRT1E, tmp);
1227 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1228 }
1229
Krzysztof Helt8503df62007-10-16 01:29:08 -07001230 /* pixel panning */
1231 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
1233 /* [ EGS: SetOffset(); ] */
1234 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001237 if (cinfo->btype == BT_LAGUNA) {
1238 /* no tiles */
1239 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1240 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1241 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1242 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 /* finally, turn on everything - turn off "FullBandwidth" bit */
1244 /* also, set "DotClock%2" bit where requested */
1245 tmp = 0x01;
1246
1247/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1248 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1249 tmp |= 0x08;
1250*/
1251
Krzysztof Helt8503df62007-10-16 01:29:08 -07001252 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001253 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001256 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257#endif
1258
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 return 0;
1260}
1261
1262/* for some reason incomprehensible to me, cirrusfb requires that you write
1263 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001264static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001266 cirrusfb_set_par_foo(info);
1267 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268}
1269
Krzysztof Helt8503df62007-10-16 01:29:08 -07001270static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1271 unsigned blue, unsigned transp,
1272 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273{
1274 struct cirrusfb_info *cinfo = info->par;
1275
1276 if (regno > 255)
1277 return -EINVAL;
1278
1279 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1280 u32 v;
1281 red >>= (16 - info->var.red.length);
1282 green >>= (16 - info->var.green.length);
1283 blue >>= (16 - info->var.blue.length);
1284
Krzysztof Helt8503df62007-10-16 01:29:08 -07001285 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 return 1;
1287 v = (red << info->var.red.offset) |
1288 (green << info->var.green.offset) |
1289 (blue << info->var.blue.offset);
1290
Krzysztof Helt060b6002007-10-16 01:29:13 -07001291 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 return 0;
1293 }
1294
Krzysztof Helt8503df62007-10-16 01:29:08 -07001295 if (info->var.bits_per_pixel == 8)
1296 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297
1298 return 0;
1299
1300}
1301
1302/*************************************************************************
1303 cirrusfb_pan_display()
1304
1305 performs display panning - provided hardware permits this
1306**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001307static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1308 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001310 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001312 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 struct cirrusfb_info *cinfo = info->par;
1314
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001315 dev_dbg(info->device,
1316 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317
1318 /* no range checks for xoffset and yoffset, */
1319 /* as fb_pan_display has already done this */
1320 if (var->vmode & FB_VMODE_YWRAP)
1321 return -EINVAL;
1322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324
Krzysztof Helt99a45842009-03-31 15:25:09 -07001325 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
1327 if (info->var.bits_per_pixel == 1) {
1328 /* base is already correct */
1329 xpix = (unsigned char) (var->xoffset % 8);
1330 } else {
1331 base /= 4;
1332 xpix = (unsigned char) ((xoffset % 4) * 2);
1333 }
1334
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001335 if (cinfo->btype != BT_LAGUNA)
1336 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001339 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1340 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001342 /* 0xf2 is %11110010, exclude tmp bits */
1343 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 /* construct bits 16, 17 and 18 of screen start address */
1345 if (base & 0x10000)
1346 tmp |= 0x01;
1347 if (base & 0x20000)
1348 tmp |= 0x04;
1349 if (base & 0x40000)
1350 tmp |= 0x08;
1351
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001352 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353
1354 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001355 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
1356 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D) & ~0x80;
1357 tmp |= (base >> 12) & 0x80;
1358 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1359 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Krzysztof Helt8503df62007-10-16 01:29:08 -07001361 /* write pixel panning value to AR33; this does not quite work in 8bpp
1362 *
1363 * ### Piccolo..? Will this work?
1364 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001368 if (cinfo->btype != BT_LAGUNA)
1369 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Krzysztof Helt8503df62007-10-16 01:29:08 -07001371 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372}
1373
Krzysztof Helt8503df62007-10-16 01:29:08 -07001374static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375{
1376 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1378 * then the caller blanks by setting the CLUT (Color Look Up Table)
1379 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1380 * failed due to e.g. a video mode which doesn't support it.
1381 * Implements VESA suspend and powerdown modes on hardware that
1382 * supports disabling hsync/vsync:
1383 * blank_mode == 2: suspend vsync
1384 * blank_mode == 3: suspend hsync
1385 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 */
1387 unsigned char val;
1388 struct cirrusfb_info *cinfo = info->par;
1389 int current_mode = cinfo->blank_mode;
1390
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001391 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
1393 if (info->state != FBINFO_STATE_RUNNING ||
1394 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001395 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 return 0;
1397 }
1398
1399 /* Undo current */
1400 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001401 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001402 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001403 val = 0;
1404 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001406 val = 0x20;
1407
1408 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1409 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410
1411 switch (blank_mode) {
1412 case FB_BLANK_UNBLANK:
1413 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001414 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 break;
1416 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001417 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 break;
1419 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001420 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 break;
1422 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001423 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 break;
1425 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001426 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 return 1;
1428 }
1429
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001430 vga_wgfx(cinfo->regbase, CL_GRE, val);
1431
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001433 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
1435 /* Let fbcon do a soft blank for us */
1436 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1437}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001438
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439/**** END Hardware specific Routines **************************************/
1440/****************************************************************************/
1441/**** BEGIN Internal Routines ***********************************************/
1442
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001443static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001445 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 const struct cirrusfb_board_info_rec *bi;
1447
Krzysztof Helt8503df62007-10-16 01:29:08 -07001448 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449
1450 bi = &cirrusfb_board_info[cinfo->btype];
1451
1452 /* reset board globally */
1453 switch (cinfo->btype) {
1454 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001455 WSFR(cinfo, 0x01);
1456 udelay(500);
1457 WSFR(cinfo, 0x51);
1458 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 break;
1460 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001461 WSFR2(cinfo, 0xff);
1462 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 break;
1464 case BT_SD64:
1465 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001466 WSFR(cinfo, 0x1f);
1467 udelay(500);
1468 WSFR(cinfo, 0x4f);
1469 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 break;
1471 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001472 /* disable flickerfixer */
1473 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1474 mdelay(100);
1475 /* from Klaus' NetBSD driver: */
1476 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1477 /* put blitter into 542x compat */
1478 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1479 /* mode */
1480 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 break;
1482
1483 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001484 /* from Klaus' NetBSD driver: */
1485 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 break;
1487
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001488 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 case BT_ALPINE:
1490 /* Nothing to do to reset the board. */
1491 break;
1492
1493 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001494 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 break;
1496 }
1497
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001498 /* make sure RAM size set by this point */
1499 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
1501 /* the P4 is not fully initialized here; I rely on it having been */
1502 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001503 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001506 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1507 WGen(cinfo, CL_POS102, 0x01);
1508 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509
1510 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001511 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001514 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
Krzysztof Helt8503df62007-10-16 01:29:08 -07001516 /* FullBandwidth (video off) and 8/9 dot clock */
1517 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
Krzysztof Helt8503df62007-10-16 01:29:08 -07001519 /* "magic cookie" - doesn't make any sense to me.. */
1520/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1521 /* unlock all extension registers */
1522 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
Krzysztof Helt8503df62007-10-16 01:29:08 -07001524 /* reset blitter */
1525 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526
1527 switch (cinfo->btype) {
1528 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001529 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 break;
1531 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001532 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 break;
1534 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001535 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 break;
1537 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001538 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1539 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 break;
1541 }
1542 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 /* plane mask: nothing */
1544 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1545 /* character map select: doesn't even matter in gx mode */
1546 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001547 /* memory mode: chain4, ext. memory */
1548 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
1550 /* controller-internal base address of video memory */
1551 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001552 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Krzysztof Helt8503df62007-10-16 01:29:08 -07001554 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1555 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1558 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1559 /* graphics cursor Y position (..."... ) */
1560 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1561 /* graphics cursor attributes */
1562 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1563 /* graphics cursor pattern address */
1564 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
1566 /* writing these on a P4 might give problems.. */
1567 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001568 /* configuration readback and ext. color */
1569 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1570 /* signature generator */
1571 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 }
1573
1574 /* MCLK select etc. */
1575 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001576 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
Krzysztof Helt8503df62007-10-16 01:29:08 -07001578 /* Screen A preset row scan: none */
1579 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1580 /* Text cursor start: disable text cursor */
1581 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1582 /* Text cursor end: - */
1583 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001584 /* text cursor location high: 0 */
1585 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1586 /* text cursor location low: 0 */
1587 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
Krzysztof Helt8503df62007-10-16 01:29:08 -07001589 /* Underline Row scanline: - */
1590 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001592 /* ext. display controls: ext.adr. wrap */
1593 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594
Krzysztof Helt8503df62007-10-16 01:29:08 -07001595 /* Set/Reset registes: - */
1596 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1597 /* Set/Reset enable: - */
1598 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1599 /* Color Compare: - */
1600 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1601 /* Data Rotate: - */
1602 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1603 /* Read Map Select: - */
1604 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1605 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1606 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1607 /* Miscellaneous: memory map base address, graphics mode */
1608 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1609 /* Color Don't care: involve all planes */
1610 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1611 /* Bit Mask: no mask at all */
1612 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001613
1614 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_LAGUNA)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001615 /* (5434 can't have bit 3 set for bitblt) */
1616 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001618 /* Graphics controller mode extensions: finer granularity,
1619 * 8byte data latches
1620 */
1621 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1624 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1625 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1626 /* Background color byte 1: - */
1627 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1628 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
Krzysztof Helt8503df62007-10-16 01:29:08 -07001630 /* Attribute Controller palette registers: "identity mapping" */
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1644 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1645 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1646 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
Krzysztof Helt8503df62007-10-16 01:29:08 -07001648 /* Attribute Controller mode: graphics mode */
1649 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1650 /* Overscan color reg.: reg. 0 */
1651 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1652 /* Color Plane enable: Enable all 4 planes */
1653 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001654 /* Color Select: - */
1655 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 /* BLT Start/status: Blitter reset */
1660 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1661 /* - " - : "end-of-reset" */
1662 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
1664 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001665 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 return;
1667}
1668
Krzysztof Helt8503df62007-10-16 01:29:08 -07001669static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
1671#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1672 static int IsOn = 0; /* XXX not ok for multiple boards */
1673
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 if (cinfo->btype == BT_PICASSO4)
1675 return; /* nothing to switch */
1676 if (cinfo->btype == BT_ALPINE)
1677 return; /* nothing to switch */
1678 if (cinfo->btype == BT_GD5480)
1679 return; /* nothing to switch */
1680 if (cinfo->btype == BT_PICASSO) {
1681 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001682 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 return;
1684 }
1685 if (on) {
1686 switch (cinfo->btype) {
1687 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001688 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 break;
1690 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 break;
1693 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 break;
1696 default: /* do nothing */ break;
1697 }
1698 } else {
1699 switch (cinfo->btype) {
1700 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 break;
1703 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001704 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 break;
1706 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001707 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001709 default: /* do nothing */
1710 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 }
1712 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713#endif /* CONFIG_ZORRO */
1714}
1715
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716/******************************************/
1717/* Linux 2.6-style accelerated functions */
1718/******************************************/
1719
Krzysztof Helt8503df62007-10-16 01:29:08 -07001720static void cirrusfb_fillrect(struct fb_info *info,
1721 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723 struct fb_fillrect modded;
1724 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001725 struct cirrusfb_info *cinfo = info->par;
1726 int m = info->var.bits_per_pixel;
1727 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1728 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
1730 if (info->state != FBINFO_STATE_RUNNING)
1731 return;
1732 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1733 cfb_fillrect(info, region);
1734 return;
1735 }
1736
1737 vxres = info->var.xres_virtual;
1738 vyres = info->var.yres_virtual;
1739
1740 memcpy(&modded, region, sizeof(struct fb_fillrect));
1741
Krzysztof Helt8503df62007-10-16 01:29:08 -07001742 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 modded.dx >= vxres || modded.dy >= vyres)
1744 return;
1745
Krzysztof Helt8503df62007-10-16 01:29:08 -07001746 if (modded.dx + modded.width > vxres)
1747 modded.width = vxres - modded.dx;
1748 if (modded.dy + modded.height > vyres)
1749 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750
Krzysztof Helt060b6002007-10-16 01:29:13 -07001751 cirrusfb_RectFill(cinfo->regbase,
1752 info->var.bits_per_pixel,
1753 (region->dx * m) / 8, region->dy,
1754 (region->width * m) / 8, region->height,
1755 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001756 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757}
1758
Krzysztof Helt8503df62007-10-16 01:29:08 -07001759static void cirrusfb_copyarea(struct fb_info *info,
1760 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 struct fb_copyarea modded;
1763 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001764 struct cirrusfb_info *cinfo = info->par;
1765 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766
1767 if (info->state != FBINFO_STATE_RUNNING)
1768 return;
1769 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1770 cfb_copyarea(info, area);
1771 return;
1772 }
1773
1774 vxres = info->var.xres_virtual;
1775 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001776 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Krzysztof Helt8503df62007-10-16 01:29:08 -07001778 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 modded.sx >= vxres || modded.sy >= vyres ||
1780 modded.dx >= vxres || modded.dy >= vyres)
1781 return;
1782
Krzysztof Helt8503df62007-10-16 01:29:08 -07001783 if (modded.sx + modded.width > vxres)
1784 modded.width = vxres - modded.sx;
1785 if (modded.dx + modded.width > vxres)
1786 modded.width = vxres - modded.dx;
1787 if (modded.sy + modded.height > vyres)
1788 modded.height = vyres - modded.sy;
1789 if (modded.dy + modded.height > vyres)
1790 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
Krzysztof Helt060b6002007-10-16 01:29:13 -07001792 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1793 (area->sx * m) / 8, area->sy,
1794 (area->dx * m) / 8, area->dy,
1795 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001796 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001797
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798}
1799
Krzysztof Helt8503df62007-10-16 01:29:08 -07001800static void cirrusfb_imageblit(struct fb_info *info,
1801 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802{
1803 struct cirrusfb_info *cinfo = info->par;
1804
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001805 if (cinfo->btype != BT_LAGUNA)
1806 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807 cfb_imageblit(info, image);
1808}
1809
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810#ifdef CONFIG_PPC_PREP
1811#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1812#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001813static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 *display = PREP_VIDEO_BASE;
1816 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817}
1818
1819#endif /* CONFIG_PPC_PREP */
1820
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001822static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823
1824/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1825 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1826 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1827 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001828static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1829 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830{
1831 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001832 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001834 if (cinfo->btype == BT_LAGUNA) {
1835 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1836
1837 mem = ((SR14 & 7) + 1) << 20;
1838 } else {
1839 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1840 switch ((SRF & 0x18)) {
1841 case 0x08:
1842 mem = 512 * 1024;
1843 break;
1844 case 0x10:
1845 mem = 1024 * 1024;
1846 break;
1847 /* 64-bit DRAM data bus width; assume 2MB.
1848 * Also indicates 2MB memory on the 5430.
1849 */
1850 case 0x18:
1851 mem = 2048 * 1024;
1852 break;
1853 default:
1854 dev_warn(info->device, "Unknown memory size!\n");
1855 mem = 1024 * 1024;
1856 }
1857 /* If DRAM bank switching is enabled, there must be
1858 * twice as much memory installed. (4MB on the 5434)
1859 */
1860 if (SRF & 0x80)
1861 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001863
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 return mem;
1866}
1867
Krzysztof Helt8503df62007-10-16 01:29:08 -07001868static void get_pci_addrs(const struct pci_dev *pdev,
1869 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001871 assert(pdev != NULL);
1872 assert(display != NULL);
1873 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875 *display = 0;
1876 *registers = 0;
1877
1878 /* This is a best-guess for now */
1879
1880 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1881 *display = pci_resource_start(pdev, 1);
1882 *registers = pci_resource_start(pdev, 0);
1883 } else {
1884 *display = pci_resource_start(pdev, 0);
1885 *registers = pci_resource_start(pdev, 1);
1886 }
1887
Krzysztof Helt8503df62007-10-16 01:29:08 -07001888 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889}
1890
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001891static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001893 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001894 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001896 if (cinfo->laguna_mmio == NULL)
1897 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001898 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899#if 0 /* if system didn't claim this region, we would... */
1900 release_mem_region(0xA0000, 65535);
1901#endif
1902 if (release_io_ports)
1903 release_region(0x3C0, 32);
1904 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905}
1906#endif /* CONFIG_PCI */
1907
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001909static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910{
Al Virod91f5bb2007-10-17 00:27:18 +01001911 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001912 struct zorro_dev *zdev = to_zorro_dev(info->device);
1913
1914 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915
1916 if (cinfo->btype == BT_PICASSO4) {
1917 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001918 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001919 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001921 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001922 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924}
1925#endif /* CONFIG_ZORRO */
1926
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001927/* function table of the above functions */
1928static struct fb_ops cirrusfb_ops = {
1929 .owner = THIS_MODULE,
1930 .fb_open = cirrusfb_open,
1931 .fb_release = cirrusfb_release,
1932 .fb_setcolreg = cirrusfb_setcolreg,
1933 .fb_check_var = cirrusfb_check_var,
1934 .fb_set_par = cirrusfb_set_par,
1935 .fb_pan_display = cirrusfb_pan_display,
1936 .fb_blank = cirrusfb_blank,
1937 .fb_fillrect = cirrusfb_fillrect,
1938 .fb_copyarea = cirrusfb_copyarea,
1939 .fb_imageblit = cirrusfb_imageblit,
1940};
1941
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001942static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001944 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 struct fb_var_screeninfo *var = &info->var;
1946
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 info->pseudo_palette = cinfo->pseudo_palette;
1948 info->flags = FBINFO_DEFAULT
1949 | FBINFO_HWACCEL_XPAN
1950 | FBINFO_HWACCEL_YPAN
1951 | FBINFO_HWACCEL_FILLRECT
1952 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001953 if (noaccel || cinfo->btype == BT_LAGUNA)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 info->flags |= FBINFO_HWACCEL_DISABLED;
1955 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 if (cinfo->btype == BT_GD5480) {
1957 if (var->bits_per_pixel == 16)
1958 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001959 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 info->screen_base += 2 * MB_;
1961 }
1962
1963 /* Fill fix common fields */
1964 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
1965 sizeof(info->fix.id));
1966
1967 /* monochrome: only 1 memory plane */
1968 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001969 info->fix.smem_len = info->screen_size;
1970 if (var->bits_per_pixel == 1)
1971 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 info->fix.xpanstep = 1;
1974 info->fix.ypanstep = 1;
1975 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976
1977 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 info->fix.mmio_len = 0;
1979 info->fix.accel = FB_ACCEL_NONE;
1980
1981 fb_alloc_cmap(&info->cmap, 256, 0);
1982
1983 return 0;
1984}
1985
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001986static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001988 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990
1991 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001992 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993
Krzysztof Helta1d35a72008-10-15 22:03:38 -07001994 /* set all the vital stuff */
1995 cirrusfb_set_fbinfo(info);
1996
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001997 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998
Krzysztof Helta1d35a72008-10-15 22:03:38 -07001999 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2000 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002001 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002002 err = -EINVAL;
2003 goto err_dealloc_cmap;
2004 }
2005
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 info->var.activate = FB_ACTIVATE_NOW;
2007
Krzysztof Helt99a45842009-03-31 15:25:09 -07002008 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 if (err < 0) {
2010 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002011 dev_dbg(info->device,
2012 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002013 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 }
2015
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 err = register_framebuffer(info);
2017 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002018 dev_err(info->device,
2019 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 goto err_dealloc_cmap;
2021 }
2022
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 return 0;
2024
2025err_dealloc_cmap:
2026 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002027 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002028 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 return err;
2030}
2031
Krzysztof Helt8503df62007-10-16 01:29:08 -07002032static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033{
2034 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035
Krzysztof Helt8503df62007-10-16 01:29:08 -07002036 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002037 unregister_framebuffer(info);
2038 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002039 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002040 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002041 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042}
2043
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002045static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2046 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047{
2048 struct cirrusfb_info *cinfo;
2049 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 unsigned long board_addr, board_size;
2051 int ret;
2052
2053 ret = pci_enable_device(pdev);
2054 if (ret < 0) {
2055 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2056 goto err_out;
2057 }
2058
2059 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2060 if (!info) {
2061 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2062 ret = -ENOMEM;
2063 goto err_disable;
2064 }
2065
2066 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002067 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002069 dev_dbg(info->device,
2070 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002071 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002072 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2073 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074
Krzysztof Helt8503df62007-10-16 01:29:08 -07002075 if (isPReP) {
2076 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002078 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002080 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002081 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002083 dev_dbg(info->device,
2084 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002085 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002086 /* FIXME: this forces VGA. alternatives? */
2087 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002088 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 }
2090
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002091 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002092 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002094 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002095 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096
2097 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002098 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002099 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2100 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 goto err_release_fb;
2102 }
2103#if 0 /* if the system didn't claim this region, we would... */
2104 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002105 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2106 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 ret = -EBUSY;
2108 goto err_release_regions;
2109 }
2110#endif
2111 if (request_region(0x3C0, 32, "cirrusfb"))
2112 release_io_ports = 1;
2113
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002114 info->screen_base = ioremap(board_addr, board_size);
2115 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 ret = -EIO;
2117 goto err_release_legacy;
2118 }
2119
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002120 info->fix.smem_start = board_addr;
2121 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 cinfo->unmap = cirrusfb_pci_unmap;
2123
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002124 dev_info(info->device,
2125 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2126 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 pci_set_drvdata(pdev, info);
2128
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002129 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002130 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002131 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002132 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133
2134err_release_legacy:
2135 if (release_io_ports)
2136 release_region(0x3C0, 32);
2137#if 0
2138 release_mem_region(0xA0000, 65535);
2139err_release_regions:
2140#endif
2141 pci_release_regions(pdev);
2142err_release_fb:
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002143 if (cinfo->laguna_mmio == NULL)
2144 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 framebuffer_release(info);
2146err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147err_out:
2148 return ret;
2149}
2150
Krzysztof Helt8503df62007-10-16 01:29:08 -07002151static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152{
2153 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
Krzysztof Helt8503df62007-10-16 01:29:08 -07002155 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156}
2157
2158static struct pci_driver cirrusfb_pci_driver = {
2159 .name = "cirrusfb",
2160 .id_table = cirrusfb_pci_table,
2161 .probe = cirrusfb_pci_register,
2162 .remove = __devexit_p(cirrusfb_pci_unregister),
2163#ifdef CONFIG_PM
2164#if 0
2165 .suspend = cirrusfb_pci_suspend,
2166 .resume = cirrusfb_pci_resume,
2167#endif
2168#endif
2169};
2170#endif /* CONFIG_PCI */
2171
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002173static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2174 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175{
2176 struct cirrusfb_info *cinfo;
2177 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002178 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 struct zorro_dev *z2 = NULL;
2180 unsigned long board_addr, board_size, size;
2181 int ret;
2182
2183 btype = ent->driver_data;
2184 if (cirrusfb_zorro_table2[btype].id2)
2185 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2186 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187
2188 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2189 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002190 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 ret = -ENOMEM;
2192 goto err_out;
2193 }
2194
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002195 dev_info(info->device, "%s board detected\n",
2196 cirrusfb_board_info[btype].name);
2197
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 cinfo->btype = btype;
2200
Al Viro36ea96a2007-10-27 19:46:58 +01002201 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002202 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204 board_addr = zorro_resource_start(z);
2205 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002206 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207
2208 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002209 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2210 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 ret = -EBUSY;
2212 goto err_release_fb;
2213 }
2214
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 ret = -EIO;
2216
2217 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002218 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219
2220 /* To be precise, for the P4 this is not the */
2221 /* begin of the board, but the begin of RAM. */
2222 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2223 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002224 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225 if (!cinfo->regbase)
2226 goto err_release_region;
2227
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002228 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002229 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002231 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002233 info->fix.smem_start = board_addr + 16777216;
2234 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2235 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 goto err_unmap_regbase;
2237 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002238 dev_info(info->device, " REG at $%lx\n",
2239 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002241 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002243 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002245 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2246 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 goto err_release_region;
2248
2249 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002250 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002251 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002253 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002254 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 }
2256 cinfo->unmap = cirrusfb_zorro_unmap;
2257
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002258 dev_info(info->device,
2259 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2260 board_size / MB_, board_addr);
2261
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 zorro_set_drvdata(z, info);
2263
Al Virod91f5bb2007-10-17 00:27:18 +01002264 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002265 if (ret) {
2266 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002267 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002268 iounmap(cinfo->regbase - 0x600000);
2269 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002270 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002271 }
2272 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273
2274err_unmap_regbase:
2275 /* Parental advisory: explicit hack */
2276 iounmap(cinfo->regbase - 0x600000);
2277err_release_region:
2278 release_region(board_addr, board_size);
2279err_release_fb:
2280 framebuffer_release(info);
2281err_out:
2282 return ret;
2283}
2284
2285void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2286{
2287 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288
Krzysztof Helt8503df62007-10-16 01:29:08 -07002289 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290}
2291
2292static struct zorro_driver cirrusfb_zorro_driver = {
2293 .name = "cirrusfb",
2294 .id_table = cirrusfb_zorro_table,
2295 .probe = cirrusfb_zorro_register,
2296 .remove = __devexit_p(cirrusfb_zorro_unregister),
2297};
2298#endif /* CONFIG_ZORRO */
2299
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002301static int __init cirrusfb_setup(char *options)
2302{
Vlada Pericee119402008-11-19 15:36:45 -08002303 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 if (!options || !*options)
2306 return 0;
2307
Krzysztof Helt8503df62007-10-16 01:29:08 -07002308 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002309 if (!*this_opt)
2310 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 if (!strcmp(this_opt, "noaccel"))
2313 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002314 else if (!strncmp(this_opt, "mode:", 5))
2315 mode_option = this_opt + 5;
2316 else
2317 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 }
2319 return 0;
2320}
2321#endif
2322
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 /*
2324 * Modularization
2325 */
2326
2327MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2328MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2329MODULE_LICENSE("GPL");
2330
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002331static int __init cirrusfb_init(void)
2332{
2333 int error = 0;
2334
2335#ifndef MODULE
2336 char *option = NULL;
2337
2338 if (fb_get_options("cirrusfb", &option))
2339 return -ENODEV;
2340 cirrusfb_setup(option);
2341#endif
2342
2343#ifdef CONFIG_ZORRO
2344 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2345#endif
2346#ifdef CONFIG_PCI
2347 error |= pci_register_driver(&cirrusfb_pci_driver);
2348#endif
2349 return error;
2350}
2351
Krzysztof Helt8503df62007-10-16 01:29:08 -07002352static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353{
2354#ifdef CONFIG_PCI
2355 pci_unregister_driver(&cirrusfb_pci_driver);
2356#endif
2357#ifdef CONFIG_ZORRO
2358 zorro_unregister_driver(&cirrusfb_zorro_driver);
2359#endif
2360}
2361
2362module_init(cirrusfb_init);
2363
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002364module_param(mode_option, charp, 0);
2365MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002366module_param(noaccel, bool, 0);
2367MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002368
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369#ifdef MODULE
2370module_exit(cirrusfb_exit);
2371#endif
2372
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373/**********************************************************************/
2374/* about the following functions - I have used the same names for the */
2375/* functions as Markus Wild did in his Retina driver for NetBSD as */
2376/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002377/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378/**********************************************************************/
2379
2380/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002381static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382 int regnum, unsigned char val)
2383{
2384 unsigned long regofs = 0;
2385
2386 if (cinfo->btype == BT_PICASSO) {
2387 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002388/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2389 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2391 regofs = 0xfff;
2392 }
2393
Krzysztof Helt8503df62007-10-16 01:29:08 -07002394 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395}
2396
2397/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002398static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399{
2400 unsigned long regofs = 0;
2401
2402 if (cinfo->btype == BT_PICASSO) {
2403 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002404/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2405 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2407 regofs = 0xfff;
2408 }
2409
Krzysztof Helt8503df62007-10-16 01:29:08 -07002410 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411}
2412
2413/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002414static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002416 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417
Krzysztof Helt8503df62007-10-16 01:29:08 -07002418 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419 /* if we're just in "write value" mode, write back the */
2420 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002421 vga_w(cinfo->regbase, VGA_ATT_IW,
2422 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 }
2424 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002425/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2426 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427
2428 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002429 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430}
2431
2432/*** WHDR() - write into the Hidden DAC register ***/
2433/* as the HDR is the only extension register that requires special treatment
2434 * (the other extension registers are accessible just like the "ordinary"
2435 * registers of their functional group) here is a specialized routine for
2436 * accessing the HDR
2437 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002438static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439{
2440 unsigned char dummy;
2441
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002442 if (cinfo->btype == BT_LAGUNA)
2443 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444 if (cinfo->btype == BT_PICASSO) {
2445 /* Klaus' hint for correct access to HDR on some boards */
2446 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002447 WGen(cinfo, VGA_PEL_MSK, 0x00);
2448 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002450 dummy = RGen(cinfo, VGA_PEL_IW);
2451 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 }
2453 /* now do the usual stuff to access the HDR */
2454
Krzysztof Helt8503df62007-10-16 01:29:08 -07002455 dummy = RGen(cinfo, VGA_PEL_MSK);
2456 udelay(200);
2457 dummy = RGen(cinfo, VGA_PEL_MSK);
2458 udelay(200);
2459 dummy = RGen(cinfo, VGA_PEL_MSK);
2460 udelay(200);
2461 dummy = RGen(cinfo, VGA_PEL_MSK);
2462 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464 WGen(cinfo, VGA_PEL_MSK, val);
2465 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466
2467 if (cinfo->btype == BT_PICASSO) {
2468 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469 dummy = RGen(cinfo, VGA_PEL_IW);
2470 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471
2472 /* and at the end, restore the mask value */
2473 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002474 WGen(cinfo, VGA_PEL_MSK, 0xff);
2475 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476 }
2477}
2478
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481{
2482#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002483 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002485 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486#endif
2487}
2488
2489/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002490static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491{
2492#ifdef CONFIG_ZORRO
2493 /* writing an arbitrary value to this one causes the monitor switcher */
2494 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002495 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002497 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498#endif
2499}
2500
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002502static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 unsigned char green, unsigned char blue)
2504{
2505 unsigned int data = VGA_PEL_D;
2506
2507 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509
2510 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002511 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
2512 cinfo->btype == BT_LAGUNA) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513 /* but DAC data register IS, at least for Picasso II */
2514 if (cinfo->btype == BT_PICASSO)
2515 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002516 vga_w(cinfo->regbase, data, red);
2517 vga_w(cinfo->regbase, data, green);
2518 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520 vga_w(cinfo->regbase, data, blue);
2521 vga_w(cinfo->regbase, data, green);
2522 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 }
2524}
2525
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526#if 0
2527/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002528static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 unsigned char *green, unsigned char *blue)
2530{
2531 unsigned int data = VGA_PEL_D;
2532
Krzysztof Helt8503df62007-10-16 01:29:08 -07002533 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534
2535 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2536 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2537 if (cinfo->btype == BT_PICASSO)
2538 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002539 *red = vga_r(cinfo->regbase, data);
2540 *green = vga_r(cinfo->regbase, data);
2541 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543 *blue = vga_r(cinfo->regbase, data);
2544 *green = vga_r(cinfo->regbase, data);
2545 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 }
2547}
2548#endif
2549
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550/*******************************************************************
2551 cirrusfb_WaitBLT()
2552
2553 Wait for the BitBLT engine to complete a possible earlier job
2554*********************************************************************/
2555
2556/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002557static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558{
2559 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002561 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562}
2563
2564/*******************************************************************
2565 cirrusfb_BitBLT()
2566
2567 perform accelerated "scrolling"
2568********************************************************************/
2569
Krzysztof Helt8503df62007-10-16 01:29:08 -07002570static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2571 u_short curx, u_short cury,
2572 u_short destx, u_short desty,
2573 u_short width, u_short height,
2574 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575{
2576 u_short nwidth, nheight;
2577 u_long nsrc, ndest;
2578 u_char bltmode;
2579
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 nwidth = width - 1;
2581 nheight = height - 1;
2582
2583 bltmode = 0x00;
2584 /* if source adr < dest addr, do the Blt backwards */
2585 if (cury <= desty) {
2586 if (cury == desty) {
2587 /* if src and dest are on the same line, check x */
2588 if (curx < destx)
2589 bltmode |= 0x01;
2590 } else
2591 bltmode |= 0x01;
2592 }
2593 if (!bltmode) {
2594 /* standard case: forward blitting */
2595 nsrc = (cury * line_length) + curx;
2596 ndest = (desty * line_length) + destx;
2597 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002598 /* this means start addresses are at the end,
2599 * counting backwards
2600 */
2601 nsrc = cury * line_length + curx +
2602 nheight * line_length + nwidth;
2603 ndest = desty * line_length + destx +
2604 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605 }
2606
2607 /*
2608 run-down of registers to be programmed:
2609 destination pitch
2610 source pitch
2611 BLT width/height
2612 source start
2613 destination start
2614 BLT mode
2615 BLT ROP
2616 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2617 start/stop
2618 */
2619
Krzysztof Helt8503df62007-10-16 01:29:08 -07002620 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621
2622 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002623 /* dest pitch low */
2624 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2625 /* dest pitch hi */
2626 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2627 /* source pitch low */
2628 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2629 /* source pitch hi */
2630 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631
2632 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002633 /* BLT width low */
2634 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2635 /* BLT width hi */
2636 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
2638 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002639 /* BLT height low */
2640 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2641 /* BLT width hi */
2642 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643
2644 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002645 /* BLT dest low */
2646 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2647 /* BLT dest mid */
2648 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2649 /* BLT dest hi */
2650 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651
2652 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002653 /* BLT src low */
2654 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2655 /* BLT src mid */
2656 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2657 /* BLT src hi */
2658 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
2660 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002661 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662
2663 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002664 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665
2666 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668}
2669
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670/*******************************************************************
2671 cirrusfb_RectFill()
2672
2673 perform accelerated rectangle fill
2674********************************************************************/
2675
Krzysztof Helt8503df62007-10-16 01:29:08 -07002676static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 u_short x, u_short y, u_short width, u_short height,
2678 u_char color, u_short line_length)
2679{
2680 u_short nwidth, nheight;
2681 u_long ndest;
2682 u_char op;
2683
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684 nwidth = width - 1;
2685 nheight = height - 1;
2686
2687 ndest = (y * line_length) + x;
2688
Krzysztof Helt8503df62007-10-16 01:29:08 -07002689 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002692 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2693 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2694 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2695 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696
2697 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002698 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2699 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700
2701 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002702 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2703 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704
2705 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706 /* BLT dest low */
2707 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2708 /* BLT dest mid */
2709 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2710 /* BLT dest hi */
2711 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712
2713 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002714 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2715 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2716 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717
2718 /* This is a ColorExpand Blt, using the */
2719 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002720 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2721 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722
2723 op = 0xc0;
2724 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002725 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2726 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727 op = 0x50;
2728 op = 0xd0;
2729 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002730 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2731 vga_wgfx(regbase, CL_GR11, color); /* background color */
2732 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2733 vga_wgfx(regbase, CL_GR13, color); /* background color */
2734 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2735 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 op = 0x50;
2737 op = 0xf0;
2738 }
2739 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002740 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741
2742 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002743 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744
2745 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002746 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747}
2748
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749/**************************************************************************
2750 * bestclock() - determine closest possible clock lower(?) than the
2751 * desired pixel clock
2752 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002753static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002755 int n, d;
2756 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757
Krzysztof Helt8503df62007-10-16 01:29:08 -07002758 assert(nom != NULL);
2759 assert(den != NULL);
2760 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761
2762 *nom = 0;
2763 *den = 0;
2764 *div = 0;
2765
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 if (freq < 8000)
2767 freq = 8000;
2768
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002769 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770
2771 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002772 int s = 0;
2773
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002774 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002776 int temp = d;
2777
2778 if (temp > 31) {
2779 s = 1;
2780 temp >>= 1;
2781 }
2782 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002783 h = h > freq ? h - freq : freq - h;
2784 if (h < diff) {
2785 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002787 *den = temp;
2788 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 }
2790 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002791 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002793 if (d > 31) {
2794 s = 1;
2795 d >>= 1;
2796 }
2797 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002798 h = h > freq ? h - freq : freq - h;
2799 if (h < diff) {
2800 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002802 *den = d;
2803 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 }
2805 }
2806 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807}
2808
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809/* -------------------------------------------------------------------------
2810 *
2811 * debugging functions
2812 *
2813 * -------------------------------------------------------------------------
2814 */
2815
2816#ifdef CIRRUSFB_DEBUG
2817
2818/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 * cirrusfb_dbg_print_regs
2820 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2821 * @reg_class: type of registers to read: %CRT, or %SEQ
2822 *
2823 * DESCRIPTION:
2824 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2825 * old-style I/O ports are queried for information, otherwise MMIO is
2826 * used at the given @base address to query the information.
2827 */
2828
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002829static void cirrusfb_dbg_print_regs(struct fb_info *info,
2830 caddr_t regbase,
2831 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832{
2833 va_list list;
2834 unsigned char val = 0;
2835 unsigned reg;
2836 char *name;
2837
Krzysztof Helt8503df62007-10-16 01:29:08 -07002838 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839
Krzysztof Helt8503df62007-10-16 01:29:08 -07002840 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002842 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843
2844 switch (reg_class) {
2845 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002846 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 break;
2848 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002849 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 break;
2851 default:
2852 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002853 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 break;
2855 }
2856
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002857 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858
Krzysztof Helt8503df62007-10-16 01:29:08 -07002859 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860 }
2861
Krzysztof Helt8503df62007-10-16 01:29:08 -07002862 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863}
2864
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 * cirrusfb_dbg_reg_dump
2867 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2868 *
2869 * DESCRIPTION:
2870 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2871 * old-style I/O ports are queried for information, otherwise MMIO is
2872 * used at the given @base address to query the information.
2873 */
2874
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002875static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002877 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002879 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 "CR00", 0x00,
2881 "CR01", 0x01,
2882 "CR02", 0x02,
2883 "CR03", 0x03,
2884 "CR04", 0x04,
2885 "CR05", 0x05,
2886 "CR06", 0x06,
2887 "CR07", 0x07,
2888 "CR08", 0x08,
2889 "CR09", 0x09,
2890 "CR0A", 0x0A,
2891 "CR0B", 0x0B,
2892 "CR0C", 0x0C,
2893 "CR0D", 0x0D,
2894 "CR0E", 0x0E,
2895 "CR0F", 0x0F,
2896 "CR10", 0x10,
2897 "CR11", 0x11,
2898 "CR12", 0x12,
2899 "CR13", 0x13,
2900 "CR14", 0x14,
2901 "CR15", 0x15,
2902 "CR16", 0x16,
2903 "CR17", 0x17,
2904 "CR18", 0x18,
2905 "CR22", 0x22,
2906 "CR24", 0x24,
2907 "CR26", 0x26,
2908 "CR2D", 0x2D,
2909 "CR2E", 0x2E,
2910 "CR2F", 0x2F,
2911 "CR30", 0x30,
2912 "CR31", 0x31,
2913 "CR32", 0x32,
2914 "CR33", 0x33,
2915 "CR34", 0x34,
2916 "CR35", 0x35,
2917 "CR36", 0x36,
2918 "CR37", 0x37,
2919 "CR38", 0x38,
2920 "CR39", 0x39,
2921 "CR3A", 0x3A,
2922 "CR3B", 0x3B,
2923 "CR3C", 0x3C,
2924 "CR3D", 0x3D,
2925 "CR3E", 0x3E,
2926 "CR3F", 0x3F,
2927 NULL);
2928
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002929 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002931 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002933 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002934 "SR00", 0x00,
2935 "SR01", 0x01,
2936 "SR02", 0x02,
2937 "SR03", 0x03,
2938 "SR04", 0x04,
2939 "SR08", 0x08,
2940 "SR09", 0x09,
2941 "SR0A", 0x0A,
2942 "SR0B", 0x0B,
2943 "SR0D", 0x0D,
2944 "SR10", 0x10,
2945 "SR11", 0x11,
2946 "SR12", 0x12,
2947 "SR13", 0x13,
2948 "SR14", 0x14,
2949 "SR15", 0x15,
2950 "SR16", 0x16,
2951 "SR17", 0x17,
2952 "SR18", 0x18,
2953 "SR19", 0x19,
2954 "SR1A", 0x1A,
2955 "SR1B", 0x1B,
2956 "SR1C", 0x1C,
2957 "SR1D", 0x1D,
2958 "SR1E", 0x1E,
2959 "SR1F", 0x1F,
2960 NULL);
2961
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002962 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963}
2964
2965#endif /* CIRRUSFB_DEBUG */
2966