blob: 12f45520e5d38df6836c1a1db159e10d40c3691d [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
435static int cirrusfb_check_var(struct fb_var_screeninfo *var,
436 struct fb_info *info)
437{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700438 int yres;
439 /* memory size in pixels */
440 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700443 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 var->red.offset = 0;
445 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700446 var->green = var->red;
447 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 break;
449
450 case 8:
451 var->red.offset = 0;
452 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700453 var->green = var->red;
454 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 break;
456
457 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700458 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 var->red.offset = 2;
460 var->green.offset = -3;
461 var->blue.offset = 8;
462 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700463 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 var->green.offset = 5;
465 var->blue.offset = 0;
466 }
467 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700468 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 var->blue.length = 5;
470 break;
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700473 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 var->red.offset = 8;
475 var->green.offset = 16;
476 var->blue.offset = 24;
477 } else {
478 var->red.offset = 16;
479 var->green.offset = 8;
480 var->blue.offset = 0;
481 }
482 var->red.length = 8;
483 var->green.length = 8;
484 var->blue.length = 8;
485 break;
486
487 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700488 dev_dbg(info->device,
489 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700490 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 /* should never occur */
492 break;
493 }
494
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700495 if (var->xres_virtual < var->xres)
496 var->xres_virtual = var->xres;
497 /* use highest possible virtual resolution */
498 if (var->yres_virtual == -1) {
499 var->yres_virtual = pixels / var->xres_virtual;
500
501 dev_info(info->device,
502 "virtual resolution set to maximum of %dx%d\n",
503 var->xres_virtual, var->yres_virtual);
504 }
505 if (var->yres_virtual < var->yres)
506 var->yres_virtual = var->yres;
507
508 if (var->xres_virtual * var->yres_virtual > pixels) {
509 dev_err(info->device, "mode %dx%dx%d rejected... "
510 "virtual resolution too high to fit into video memory!\n",
511 var->xres_virtual, var->yres_virtual,
512 var->bits_per_pixel);
513 return -EINVAL;
514 }
515
516
517 if (var->xoffset < 0)
518 var->xoffset = 0;
519 if (var->yoffset < 0)
520 var->yoffset = 0;
521
522 /* truncate xoffset and yoffset to maximum if too high */
523 if (var->xoffset > var->xres_virtual - var->xres)
524 var->xoffset = var->xres_virtual - var->xres - 1;
525 if (var->yoffset > var->yres_virtual - var->yres)
526 var->yoffset = var->yres_virtual - var->yres - 1;
527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 var->red.msb_right =
529 var->green.msb_right =
530 var->blue.msb_right =
531 var->transp.offset =
532 var->transp.length =
533 var->transp.msb_right = 0;
534
535 yres = var->yres;
536 if (var->vmode & FB_VMODE_DOUBLE)
537 yres *= 2;
538 else if (var->vmode & FB_VMODE_INTERLACED)
539 yres = (yres + 1) / 2;
540
541 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700542 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700543 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 return -EINVAL;
545 }
546
547 return 0;
548}
549
Krzysztof Helt8503df62007-10-16 01:29:08 -0700550static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700551 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
553 long freq;
554 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700555 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
Krzysztof Helt8503df62007-10-16 01:29:08 -0700558 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700560 info->fix.line_length = var->xres_virtual / 8;
561 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 break;
563
564 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700565 info->fix.line_length = var->xres_virtual;
566 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 break;
568
569 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700571 info->fix.line_length = var->xres_virtual * maxclockidx;
Krzysztof Helt3b921832008-10-15 22:03:41 -0700572 info->fix.visual = FB_VISUAL_TRUECOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 break;
574
575 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700576 dev_dbg(info->device,
577 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700578 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 /* should never occur */
580 break;
581 }
582
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700583 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
585 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700586 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700588 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700591 cinfo->multiplexing = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
593 /* If the frequency is greater than we can support, we might be able
594 * to use multiplexing for the video mode */
595 if (freq > maxclock) {
596 switch (cinfo->btype) {
597 case BT_ALPINE:
598 case BT_GD5480:
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700599 cinfo->multiplexing = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 break;
601
602 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700603 dev_err(info->device,
604 "Frequency greater than maxclock (%ld kHz)\n",
605 maxclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 return -EINVAL;
607 }
608 }
609#if 0
610 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
611 * the VCLK is double the pixel clock. */
612 switch (var->bits_per_pixel) {
613 case 16:
614 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700615 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700616 /* Xbh has this type of clock for 32-bit */
617 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 break;
619 }
620#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return 0;
622}
623
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700624static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700626 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700627 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700628
Krzysztof Helt8503df62007-10-16 01:29:08 -0700629 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700630 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
Krzysztof Helt486ff382008-10-15 22:03:42 -0700632 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700633 dev_dbg(info->device, "Set %s as pixclock source.\n",
634 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700635 old1f |= 0x40;
636 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
637 if (div == 2)
638 old1e |= 1;
639
640 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700642 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643}
644
645/*************************************************************************
646 cirrusfb_set_par_foo()
647
648 actually writes the values for a new video mode into the hardware,
649**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700650static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
652 struct cirrusfb_info *cinfo = info->par;
653 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 u8 __iomem *regbase = cinfo->regbase;
655 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700656 int err;
657 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700659 int hdispend, hsyncstart, hsyncend, htotal;
660 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700661 long freq;
662 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700663 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700665 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700667 dev_dbg(info->device, "pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700669 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700671 err = cirrusfb_decode_var(var, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700672 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700674 dev_dbg(info->device, "mode change aborted. invalid var.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 return -EINVAL;
676 }
677
678 bi = &cirrusfb_board_info[cinfo->btype];
679
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700680 hsyncstart = var->xres + var->right_margin;
681 hsyncend = hsyncstart + var->hsync_len;
682 htotal = (hsyncend + var->left_margin) / 8 - 5;
683 hdispend = var->xres / 8 - 1;
684 hsyncstart = hsyncstart / 8 + 1;
685 hsyncend = hsyncend / 8 + 1;
686
687 yres = var->yres;
688 vsyncstart = yres + var->lower_margin;
689 vsyncend = vsyncstart + var->vsync_len;
690 vtotal = vsyncend + var->upper_margin;
691 vdispend = yres - 1;
692
693 if (var->vmode & FB_VMODE_DOUBLE) {
694 yres *= 2;
695 vsyncstart *= 2;
696 vsyncend *= 2;
697 vtotal *= 2;
698 } else if (var->vmode & FB_VMODE_INTERLACED) {
699 yres = (yres + 1) / 2;
700 vsyncstart = (vsyncstart + 1) / 2;
701 vsyncend = (vsyncend + 1) / 2;
702 vtotal = (vtotal + 1) / 2;
703 }
704
705 vtotal -= 2;
706 vsyncstart -= 1;
707 vsyncend -= 1;
708
709 if (yres >= 1024) {
710 vtotal /= 2;
711 vsyncstart /= 2;
712 vsyncend /= 2;
713 vdispend /= 2;
714 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700715 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700716 htotal /= 2;
717 hsyncstart /= 2;
718 hsyncend /= 2;
719 hdispend /= 2;
720 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700722 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700725 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700726 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700728 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700729 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700731 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700732 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Krzysztof Helt8503df62007-10-16 01:29:08 -0700734 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700735 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700736 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700737 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700739 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700740 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700742 tmp = hsyncend % 32;
743 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700745 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700746 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700748 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700749 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700752 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700756 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700758 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700760 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700766 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700767 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
769 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700770 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 tmp |= 0x20;
772 if (var->vmode & FB_VMODE_DOUBLE)
773 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700774 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700775 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700777 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700780 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700783 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700786 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700789 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700792 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700793 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795 tmp = 0;
796 if (var->vmode & FB_VMODE_INTERLACED)
797 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700798 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 tmp |= 128;
806
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700807 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700808 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700810 freq = PICOS2KHZ(var->pixclock);
811 bestclock(freq, &nom, &den, &div);
812
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700813 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
814 freq, nom, den, div);
815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 /* set VCLK0 */
817 /* hardware RefClock: 14.31818 MHz */
818 /* formula: VClk = (OSC * N) / (D * (1+P)) */
819 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
820
Krzysztof Helt486ff382008-10-15 22:03:42 -0700821 if (cinfo->btype == BT_ALPINE) {
822 /* if freq is close to mclk or mclk/2 select mclk
823 * as clock source
824 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700825 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700826 if (divMCLK) {
827 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700828 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700829 }
830 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700831 if (cinfo->btype == BT_LAGUNA) {
832 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
833 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
834 unsigned short tile_control;
835
836 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
837 fb_writew(tile_control & ~0x80, cinfo->laguna_mmio + 0x2c4);
838
839 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
840 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
841 control = fb_readw(cinfo->laguna_mmio + 0x402);
842 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
843 control &= ~0x6800;
844 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700845 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700846 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700847 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700848 tmp = den << 1;
849 if (div != 0)
850 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700851 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
852 if ((cinfo->btype == BT_SD64) ||
853 (cinfo->btype == BT_ALPINE) ||
854 (cinfo->btype == BT_GD5480))
855 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700857 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
858 /* Laguna chipset has reversed clock registers */
859 if (cinfo->btype == BT_LAGUNA) {
860 vga_wseq(regbase, CL_SEQRE, tmp);
861 vga_wseq(regbase, CL_SEQR1E, nom);
862 } else {
863 vga_wseq(regbase, CL_SEQRB, nom);
864 vga_wseq(regbase, CL_SEQR1B, tmp);
865 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700866 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700868 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700870 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 else
872 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
873 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700874 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
Krzysztof Helt8503df62007-10-16 01:29:08 -0700876/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
877 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
879 /* don't know if it would hurt to also program this if no interlaced */
880 /* mode is used, but I feel better this way.. :-) */
881 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700882 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700884 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
Krzysztof Helt8503df62007-10-16 01:29:08 -0700886 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
888 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700889 /* enable display memory & CRTC I/O address for color mode */
890 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
892 tmp |= 0x40;
893 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
894 tmp |= 0x80;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700895 if (cinfo->btype == BT_LAGUNA)
896 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700897 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Helt8503df62007-10-16 01:29:08 -0700899 /* Screen A Preset Row-Scan register */
900 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
901 /* text cursor on and start line */
902 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
903 /* text cursor end line */
904 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
906 /******************************************************
907 *
908 * 1 bpp
909 *
910 */
911
912 /* programming for different color depths */
913 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700914 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700915 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
917 /* SR07 */
918 switch (cinfo->btype) {
919 case BT_SD64:
920 case BT_PICCOLO:
921 case BT_PICASSO:
922 case BT_SPECTRUM:
923 case BT_PICASSO4:
924 case BT_ALPINE:
925 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700926 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700927 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 bi->sr07_1bpp_mux : bi->sr07_1bpp);
929 break;
930
931 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700932 vga_wseq(regbase, CL_SEQR7,
933 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 break;
935
936 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700937 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 break;
939 }
940
941 /* Extended Sequencer Mode */
942 switch (cinfo->btype) {
943 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700944 /* setting the SEQRF on SD64 is not necessary
945 * (only during init)
946 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700947 /* MCLK select */
948 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 break;
950
951 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700952 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700953 /* ### ueberall 0x22? */
954 /* ##vorher 1c MCLK select */
955 vga_wseq(regbase, CL_SEQR1F, 0x22);
956 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
957 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 break;
959
960 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700961 /* ##vorher 22 MCLK select */
962 vga_wseq(regbase, CL_SEQR1F, 0x22);
963 /* ## vorher d0 avoid FIFO underruns..? */
964 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 break;
966
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 case BT_PICASSO4:
968 case BT_ALPINE:
969 case BT_GD5480:
970 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 /* do nothing */
972 break;
973
974 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700975 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 break;
977 }
978
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 /* pixel mask: pass-through for first plane */
980 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700981 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700982 /* hidden dac reg: 1280x1024 */
983 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700985 /* hidden dac: nothing */
986 WHDR(cinfo, 0);
987 /* memory mode: odd/even, ext. memory */
988 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
989 /* plane mask: only write to first plane */
990 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 }
992
993 /******************************************************
994 *
995 * 8 bpp
996 *
997 */
998
999 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001000 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 switch (cinfo->btype) {
1002 case BT_SD64:
1003 case BT_PICCOLO:
1004 case BT_PICASSO:
1005 case BT_SPECTRUM:
1006 case BT_PICASSO4:
1007 case BT_ALPINE:
1008 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001009 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001010 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1012 break;
1013
1014 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001015 vga_wseq(regbase, CL_SEQR7,
1016 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001017 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 break;
1019
1020 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001021 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 break;
1023 }
1024
1025 switch (cinfo->btype) {
1026 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001027 /* MCLK select */
1028 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 break;
1030
1031 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001034 /* ### vorher 1c MCLK select */
1035 vga_wseq(regbase, CL_SEQR1F, 0x22);
1036 /* Fast Page-Mode writes */
1037 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 break;
1039
1040 case BT_PICASSO4:
1041#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001042 /* ### INCOMPLETE!! */
1043 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001045/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 break;
1047
1048 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 /* We already set SRF and SR1F */
1050 break;
1051
1052 case BT_GD5480:
1053 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 /* do nothing */
1055 break;
1056
1057 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001058 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 break;
1060 }
1061
Krzysztof Helt8503df62007-10-16 01:29:08 -07001062 /* mode register: 256 color mode */
1063 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001064 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001065 /* hidden dac reg: 1280x1024 */
1066 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001068 /* hidden dac: nothing */
1069 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 }
1071
1072 /******************************************************
1073 *
1074 * 16 bpp
1075 *
1076 */
1077
1078 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001079 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 switch (cinfo->btype) {
1081 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001082 /* Extended Sequencer Mode: 256c col. mode */
1083 vga_wseq(regbase, CL_SEQR7, 0xf7);
1084 /* MCLK select */
1085 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 break;
1087
1088 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001089 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001090 vga_wseq(regbase, CL_SEQR7, 0x87);
1091 /* Fast Page-Mode writes */
1092 vga_wseq(regbase, CL_SEQRF, 0xb0);
1093 /* MCLK select */
1094 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 break;
1096
1097 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001098 vga_wseq(regbase, CL_SEQR7, 0x27);
1099 /* Fast Page-Mode writes */
1100 vga_wseq(regbase, CL_SEQRF, 0xb0);
1101 /* MCLK select */
1102 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 break;
1104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001106 vga_wseq(regbase, CL_SEQR7, 0x27);
1107/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 break;
1109
1110 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001111 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 break;
1113
1114 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001115 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 /* We already set SRF and SR1F */
1117 break;
1118
1119 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 vga_wseq(regbase, CL_SEQR7,
1121 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001122 control |= 0x2000;
1123 format |= 0x1400;
1124 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126
1127 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001128 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 break;
1130 }
1131
Krzysztof Helt8503df62007-10-16 01:29:08 -07001132 /* mode register: 256 color mode */
1133 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001135 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#elif defined(CONFIG_ZORRO)
1137 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001138 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 }
1141
1142 /******************************************************
1143 *
1144 * 32 bpp
1145 *
1146 */
1147
1148 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001149 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 switch (cinfo->btype) {
1151 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001152 /* Extended Sequencer Mode: 256c col. mode */
1153 vga_wseq(regbase, CL_SEQR7, 0xf9);
1154 /* MCLK select */
1155 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 break;
1157
1158 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001159 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001160 vga_wseq(regbase, CL_SEQR7, 0x85);
1161 /* Fast Page-Mode writes */
1162 vga_wseq(regbase, CL_SEQRF, 0xb0);
1163 /* MCLK select */
1164 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 break;
1166
1167 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001168 vga_wseq(regbase, CL_SEQR7, 0x25);
1169 /* Fast Page-Mode writes */
1170 vga_wseq(regbase, CL_SEQRF, 0xb0);
1171 /* MCLK select */
1172 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 break;
1174
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001176 vga_wseq(regbase, CL_SEQR7, 0x25);
1177/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 break;
1179
1180 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001181 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 break;
1183
1184 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001185 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 /* We already set SRF and SR1F */
1187 break;
1188
1189 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001190 vga_wseq(regbase, CL_SEQR7,
1191 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001192 control |= 0x6000;
1193 format |= 0x3400;
1194 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 break;
1196
1197 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001198 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 break;
1200 }
1201
Krzysztof Helt8503df62007-10-16 01:29:08 -07001202 /* mode register: 256 color mode */
1203 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001204 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1205 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 }
1207
1208 /******************************************************
1209 *
1210 * unknown/unsupported bpp
1211 *
1212 */
1213
Krzysztof Helt8503df62007-10-16 01:29:08 -07001214 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001215 dev_err(info->device,
1216 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
Krzysztof Helt6683e012009-03-31 15:25:06 -07001219 pitch = info->fix.line_length >> 3;
1220 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001222 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 tmp |= 0x10; /* offset overflow bit */
1224
Krzysztof Helt8503df62007-10-16 01:29:08 -07001225 /* screen start addr #16-18, fastpagemode cycles */
1226 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001228 /* screen start address bit 19 */
1229 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001230 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001232 if (cinfo->btype == BT_LAGUNA) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001233 tmp = 0;
1234 if ((htotal + 5) & 256)
1235 tmp |= 128;
1236 if (hdispend & 256)
1237 tmp |= 64;
1238 if (hsyncstart & 256)
1239 tmp |= 48;
1240 if (vtotal & 1024)
1241 tmp |= 8;
1242 if (vdispend & 1024)
1243 tmp |= 4;
1244 if (vsyncstart & 1024)
1245 tmp |= 3;
1246
1247 vga_wcrt(regbase, CL_CRT1E, tmp);
1248 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1249 }
1250
1251
Krzysztof Helt8503df62007-10-16 01:29:08 -07001252 /* pixel panning */
1253 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254
1255 /* [ EGS: SetOffset(); ] */
1256 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001257 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001259 if (cinfo->btype == BT_LAGUNA) {
1260 /* no tiles */
1261 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1262 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1263 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1264 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 /* finally, turn on everything - turn off "FullBandwidth" bit */
1266 /* also, set "DotClock%2" bit where requested */
1267 tmp = 0x01;
1268
1269/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1270 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1271 tmp |= 0x08;
1272*/
1273
Krzysztof Helt8503df62007-10-16 01:29:08 -07001274 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001275 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001278 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
1280#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001281 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282#endif
1283
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 return 0;
1285}
1286
1287/* for some reason incomprehensible to me, cirrusfb requires that you write
1288 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001289static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 cirrusfb_set_par_foo(info);
1292 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293}
1294
Krzysztof Helt8503df62007-10-16 01:29:08 -07001295static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1296 unsigned blue, unsigned transp,
1297 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298{
1299 struct cirrusfb_info *cinfo = info->par;
1300
1301 if (regno > 255)
1302 return -EINVAL;
1303
1304 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1305 u32 v;
1306 red >>= (16 - info->var.red.length);
1307 green >>= (16 - info->var.green.length);
1308 blue >>= (16 - info->var.blue.length);
1309
Krzysztof Helt8503df62007-10-16 01:29:08 -07001310 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 return 1;
1312 v = (red << info->var.red.offset) |
1313 (green << info->var.green.offset) |
1314 (blue << info->var.blue.offset);
1315
Krzysztof Helt060b6002007-10-16 01:29:13 -07001316 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 return 0;
1318 }
1319
Krzysztof Helt8503df62007-10-16 01:29:08 -07001320 if (info->var.bits_per_pixel == 8)
1321 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323 return 0;
1324
1325}
1326
1327/*************************************************************************
1328 cirrusfb_pan_display()
1329
1330 performs display panning - provided hardware permits this
1331**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1333 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334{
1335 int xoffset = 0;
1336 int yoffset = 0;
1337 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001338 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 struct cirrusfb_info *cinfo = info->par;
1340
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001341 dev_dbg(info->device,
1342 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
1344 /* no range checks for xoffset and yoffset, */
1345 /* as fb_pan_display has already done this */
1346 if (var->vmode & FB_VMODE_YWRAP)
1347 return -EINVAL;
1348
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1350 yoffset = var->yoffset;
1351
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001352 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353
1354 if (info->var.bits_per_pixel == 1) {
1355 /* base is already correct */
1356 xpix = (unsigned char) (var->xoffset % 8);
1357 } else {
1358 base /= 4;
1359 xpix = (unsigned char) ((xoffset % 4) * 2);
1360 }
1361
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001362 if (cinfo->btype != BT_LAGUNA)
1363 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1367 (unsigned char) (base & 0xff));
1368 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1369 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001371 /* 0xf2 is %11110010, exclude tmp bits */
1372 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 /* construct bits 16, 17 and 18 of screen start address */
1374 if (base & 0x10000)
1375 tmp |= 0x01;
1376 if (base & 0x20000)
1377 tmp |= 0x04;
1378 if (base & 0x40000)
1379 tmp |= 0x08;
1380
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001381 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
1383 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001384 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
1385 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D) & ~0x80;
1386 tmp |= (base >> 12) & 0x80;
1387 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1388 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 /* write pixel panning value to AR33; this does not quite work in 8bpp
1391 *
1392 * ### Piccolo..? Will this work?
1393 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001395 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001397 if (cinfo->btype != BT_LAGUNA)
1398 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399
Krzysztof Helt8503df62007-10-16 01:29:08 -07001400 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401}
1402
Krzysztof Helt8503df62007-10-16 01:29:08 -07001403static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404{
1405 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001406 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1407 * then the caller blanks by setting the CLUT (Color Look Up Table)
1408 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1409 * failed due to e.g. a video mode which doesn't support it.
1410 * Implements VESA suspend and powerdown modes on hardware that
1411 * supports disabling hsync/vsync:
1412 * blank_mode == 2: suspend vsync
1413 * blank_mode == 3: suspend hsync
1414 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 */
1416 unsigned char val;
1417 struct cirrusfb_info *cinfo = info->par;
1418 int current_mode = cinfo->blank_mode;
1419
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001420 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
1422 if (info->state != FBINFO_STATE_RUNNING ||
1423 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001424 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 return 0;
1426 }
1427
1428 /* Undo current */
1429 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001430 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001431 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001432 val = 0;
1433 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001434 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001435 val = 0x20;
1436
1437 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1438 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
1440 switch (blank_mode) {
1441 case FB_BLANK_UNBLANK:
1442 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001443 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 break;
1445 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001446 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 break;
1448 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001449 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 break;
1451 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001452 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 break;
1454 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001455 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 return 1;
1457 }
1458
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001459 vga_wgfx(cinfo->regbase, CL_GRE, val);
1460
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001462 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
1464 /* Let fbcon do a soft blank for us */
1465 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1466}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001467
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468/**** END Hardware specific Routines **************************************/
1469/****************************************************************************/
1470/**** BEGIN Internal Routines ***********************************************/
1471
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001472static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001474 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 const struct cirrusfb_board_info_rec *bi;
1476
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478
1479 bi = &cirrusfb_board_info[cinfo->btype];
1480
1481 /* reset board globally */
1482 switch (cinfo->btype) {
1483 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001484 WSFR(cinfo, 0x01);
1485 udelay(500);
1486 WSFR(cinfo, 0x51);
1487 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 break;
1489 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001490 WSFR2(cinfo, 0xff);
1491 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 break;
1493 case BT_SD64:
1494 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 WSFR(cinfo, 0x1f);
1496 udelay(500);
1497 WSFR(cinfo, 0x4f);
1498 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 break;
1500 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 /* disable flickerfixer */
1502 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1503 mdelay(100);
1504 /* from Klaus' NetBSD driver: */
1505 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1506 /* put blitter into 542x compat */
1507 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1508 /* mode */
1509 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 break;
1511
1512 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 /* from Klaus' NetBSD driver: */
1514 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 break;
1516
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001517 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 case BT_ALPINE:
1519 /* Nothing to do to reset the board. */
1520 break;
1521
1522 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001523 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 break;
1525 }
1526
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001527 /* make sure RAM size set by this point */
1528 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
1530 /* the P4 is not fully initialized here; I rely on it having been */
1531 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001532 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
1534 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001535 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1536 WGen(cinfo, CL_POS102, 0x01);
1537 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
1539 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
Krzysztof Helt8503df62007-10-16 01:29:08 -07001542 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001543 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
Krzysztof Helt8503df62007-10-16 01:29:08 -07001545 /* FullBandwidth (video off) and 8/9 dot clock */
1546 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1547 /* polarity (-/-), disable access to display memory,
1548 * VGA_CRTC_START_HI base address: color
1549 */
1550 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
Krzysztof Helt8503df62007-10-16 01:29:08 -07001552 /* "magic cookie" - doesn't make any sense to me.. */
1553/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1554 /* unlock all extension registers */
1555 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 /* reset blitter */
1558 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 switch (cinfo->btype) {
1561 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001562 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 break;
1564 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001565 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 break;
1567 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001568 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 break;
1570 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1572 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 break;
1574 }
1575 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001576 /* plane mask: nothing */
1577 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1578 /* character map select: doesn't even matter in gx mode */
1579 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001580 /* memory mode: chain4, ext. memory */
1581 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
1583 /* controller-internal base address of video memory */
1584 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1588 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1591 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1592 /* graphics cursor Y position (..."... ) */
1593 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1594 /* graphics cursor attributes */
1595 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1596 /* graphics cursor pattern address */
1597 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
1599 /* writing these on a P4 might give problems.. */
1600 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001601 /* configuration readback and ext. color */
1602 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1603 /* signature generator */
1604 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 }
1606
1607 /* MCLK select etc. */
1608 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001609 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 /* Screen A preset row scan: none */
1612 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1613 /* Text cursor start: disable text cursor */
1614 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1615 /* Text cursor end: - */
1616 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1617 /* Screen start address high: 0 */
1618 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1619 /* Screen start address low: 0 */
1620 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1621 /* text cursor location high: 0 */
1622 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1623 /* text cursor location low: 0 */
1624 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625
Krzysztof Helt8503df62007-10-16 01:29:08 -07001626 /* Underline Row scanline: - */
1627 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1628 /* mode control: timing enable, byte mode, no compat modes */
1629 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1630 /* Line Compare: not needed */
1631 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001633 /* ext. display controls: ext.adr. wrap */
1634 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635
Krzysztof Helt8503df62007-10-16 01:29:08 -07001636 /* Set/Reset registes: - */
1637 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1638 /* Set/Reset enable: - */
1639 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1640 /* Color Compare: - */
1641 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1642 /* Data Rotate: - */
1643 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1644 /* Read Map Select: - */
1645 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1646 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1647 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1648 /* Miscellaneous: memory map base address, graphics mode */
1649 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1650 /* Color Don't care: involve all planes */
1651 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1652 /* Bit Mask: no mask at all */
1653 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001654
1655 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_LAGUNA)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 /* (5434 can't have bit 3 set for bitblt) */
1657 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 /* Graphics controller mode extensions: finer granularity,
1660 * 8byte data latches
1661 */
1662 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1665 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1666 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1667 /* Background color byte 1: - */
1668 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1669 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
Krzysztof Helt8503df62007-10-16 01:29:08 -07001671 /* Attribute Controller palette registers: "identity mapping" */
1672 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1673 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1674 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1675 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1676 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1677 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1678 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1679 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1680 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1681 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1682 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1683 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1684 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1685 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1686 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1687 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 /* Attribute Controller mode: graphics mode */
1690 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1691 /* Overscan color reg.: reg. 0 */
1692 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1693 /* Color Plane enable: Enable all 4 planes */
1694 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001695 /* Color Select: - */
1696 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697
Krzysztof Helt8503df62007-10-16 01:29:08 -07001698 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699
1700 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 /* polarity (-/-), enable display mem,
1702 * VGA_CRTC_START_HI i/o base = color
1703 */
1704 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Krzysztof Helt8503df62007-10-16 01:29:08 -07001706 /* BLT Start/status: Blitter reset */
1707 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1708 /* - " - : "end-of-reset" */
1709 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710
1711 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001712 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 return;
1714}
1715
Krzysztof Helt8503df62007-10-16 01:29:08 -07001716static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717{
1718#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1719 static int IsOn = 0; /* XXX not ok for multiple boards */
1720
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 if (cinfo->btype == BT_PICASSO4)
1722 return; /* nothing to switch */
1723 if (cinfo->btype == BT_ALPINE)
1724 return; /* nothing to switch */
1725 if (cinfo->btype == BT_GD5480)
1726 return; /* nothing to switch */
1727 if (cinfo->btype == BT_PICASSO) {
1728 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001729 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 return;
1731 }
1732 if (on) {
1733 switch (cinfo->btype) {
1734 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001735 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 break;
1737 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001738 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 break;
1740 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001741 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 break;
1743 default: /* do nothing */ break;
1744 }
1745 } else {
1746 switch (cinfo->btype) {
1747 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001748 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 break;
1750 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 break;
1753 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001754 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001756 default: /* do nothing */
1757 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 }
1759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760#endif /* CONFIG_ZORRO */
1761}
1762
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763/******************************************/
1764/* Linux 2.6-style accelerated functions */
1765/******************************************/
1766
Krzysztof Helt8503df62007-10-16 01:29:08 -07001767static void cirrusfb_fillrect(struct fb_info *info,
1768 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 struct fb_fillrect modded;
1771 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001772 struct cirrusfb_info *cinfo = info->par;
1773 int m = info->var.bits_per_pixel;
1774 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1775 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776
1777 if (info->state != FBINFO_STATE_RUNNING)
1778 return;
1779 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1780 cfb_fillrect(info, region);
1781 return;
1782 }
1783
1784 vxres = info->var.xres_virtual;
1785 vyres = info->var.yres_virtual;
1786
1787 memcpy(&modded, region, sizeof(struct fb_fillrect));
1788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 modded.dx >= vxres || modded.dy >= vyres)
1791 return;
1792
Krzysztof Helt8503df62007-10-16 01:29:08 -07001793 if (modded.dx + modded.width > vxres)
1794 modded.width = vxres - modded.dx;
1795 if (modded.dy + modded.height > vyres)
1796 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
Krzysztof Helt060b6002007-10-16 01:29:13 -07001798 cirrusfb_RectFill(cinfo->regbase,
1799 info->var.bits_per_pixel,
1800 (region->dx * m) / 8, region->dy,
1801 (region->width * m) / 8, region->height,
1802 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001803 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804}
1805
Krzysztof Helt8503df62007-10-16 01:29:08 -07001806static void cirrusfb_copyarea(struct fb_info *info,
1807 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 struct fb_copyarea modded;
1810 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001811 struct cirrusfb_info *cinfo = info->par;
1812 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813
1814 if (info->state != FBINFO_STATE_RUNNING)
1815 return;
1816 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1817 cfb_copyarea(info, area);
1818 return;
1819 }
1820
1821 vxres = info->var.xres_virtual;
1822 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001823 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824
Krzysztof Helt8503df62007-10-16 01:29:08 -07001825 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 modded.sx >= vxres || modded.sy >= vyres ||
1827 modded.dx >= vxres || modded.dy >= vyres)
1828 return;
1829
Krzysztof Helt8503df62007-10-16 01:29:08 -07001830 if (modded.sx + modded.width > vxres)
1831 modded.width = vxres - modded.sx;
1832 if (modded.dx + modded.width > vxres)
1833 modded.width = vxres - modded.dx;
1834 if (modded.sy + modded.height > vyres)
1835 modded.height = vyres - modded.sy;
1836 if (modded.dy + modded.height > vyres)
1837 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838
Krzysztof Helt060b6002007-10-16 01:29:13 -07001839 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1840 (area->sx * m) / 8, area->sy,
1841 (area->dx * m) / 8, area->dy,
1842 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001843 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001844
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845}
1846
Krzysztof Helt8503df62007-10-16 01:29:08 -07001847static void cirrusfb_imageblit(struct fb_info *info,
1848 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849{
1850 struct cirrusfb_info *cinfo = info->par;
1851
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001852 if (cinfo->btype != BT_LAGUNA)
1853 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854 cfb_imageblit(info, image);
1855}
1856
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857#ifdef CONFIG_PPC_PREP
1858#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1859#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001860static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 *display = PREP_VIDEO_BASE;
1863 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864}
1865
1866#endif /* CONFIG_PPC_PREP */
1867
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001869static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
1871/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1872 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1873 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1874 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001875static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1876 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877{
1878 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001879 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001881 if (cinfo->btype == BT_LAGUNA) {
1882 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1883
1884 mem = ((SR14 & 7) + 1) << 20;
1885 } else {
1886 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1887 switch ((SRF & 0x18)) {
1888 case 0x08:
1889 mem = 512 * 1024;
1890 break;
1891 case 0x10:
1892 mem = 1024 * 1024;
1893 break;
1894 /* 64-bit DRAM data bus width; assume 2MB.
1895 * Also indicates 2MB memory on the 5430.
1896 */
1897 case 0x18:
1898 mem = 2048 * 1024;
1899 break;
1900 default:
1901 dev_warn(info->device, "Unknown memory size!\n");
1902 mem = 1024 * 1024;
1903 }
1904 /* If DRAM bank switching is enabled, there must be
1905 * twice as much memory installed. (4MB on the 5434)
1906 */
1907 if (SRF & 0x80)
1908 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001910
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 return mem;
1913}
1914
Krzysztof Helt8503df62007-10-16 01:29:08 -07001915static void get_pci_addrs(const struct pci_dev *pdev,
1916 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001918 assert(pdev != NULL);
1919 assert(display != NULL);
1920 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 *display = 0;
1923 *registers = 0;
1924
1925 /* This is a best-guess for now */
1926
1927 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1928 *display = pci_resource_start(pdev, 1);
1929 *registers = pci_resource_start(pdev, 0);
1930 } else {
1931 *display = pci_resource_start(pdev, 0);
1932 *registers = pci_resource_start(pdev, 1);
1933 }
1934
Krzysztof Helt8503df62007-10-16 01:29:08 -07001935 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936}
1937
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001938static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001940 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001941 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001943 if (cinfo->laguna_mmio == NULL)
1944 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001945 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946#if 0 /* if system didn't claim this region, we would... */
1947 release_mem_region(0xA0000, 65535);
1948#endif
1949 if (release_io_ports)
1950 release_region(0x3C0, 32);
1951 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952}
1953#endif /* CONFIG_PCI */
1954
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001956static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957{
Al Virod91f5bb2007-10-17 00:27:18 +01001958 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001959 struct zorro_dev *zdev = to_zorro_dev(info->device);
1960
1961 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
1963 if (cinfo->btype == BT_PICASSO4) {
1964 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001965 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001966 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001968 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001969 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971}
1972#endif /* CONFIG_ZORRO */
1973
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001974/* function table of the above functions */
1975static struct fb_ops cirrusfb_ops = {
1976 .owner = THIS_MODULE,
1977 .fb_open = cirrusfb_open,
1978 .fb_release = cirrusfb_release,
1979 .fb_setcolreg = cirrusfb_setcolreg,
1980 .fb_check_var = cirrusfb_check_var,
1981 .fb_set_par = cirrusfb_set_par,
1982 .fb_pan_display = cirrusfb_pan_display,
1983 .fb_blank = cirrusfb_blank,
1984 .fb_fillrect = cirrusfb_fillrect,
1985 .fb_copyarea = cirrusfb_copyarea,
1986 .fb_imageblit = cirrusfb_imageblit,
1987};
1988
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001989static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001991 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 struct fb_var_screeninfo *var = &info->var;
1993
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 info->pseudo_palette = cinfo->pseudo_palette;
1995 info->flags = FBINFO_DEFAULT
1996 | FBINFO_HWACCEL_XPAN
1997 | FBINFO_HWACCEL_YPAN
1998 | FBINFO_HWACCEL_FILLRECT
1999 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002000 if (noaccel || cinfo->btype == BT_LAGUNA)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 info->flags |= FBINFO_HWACCEL_DISABLED;
2002 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 if (cinfo->btype == BT_GD5480) {
2004 if (var->bits_per_pixel == 16)
2005 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002006 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 info->screen_base += 2 * MB_;
2008 }
2009
2010 /* Fill fix common fields */
2011 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2012 sizeof(info->fix.id));
2013
2014 /* monochrome: only 1 memory plane */
2015 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002016 info->fix.smem_len = info->screen_size;
2017 if (var->bits_per_pixel == 1)
2018 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 info->fix.xpanstep = 1;
2021 info->fix.ypanstep = 1;
2022 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023
2024 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 info->fix.mmio_len = 0;
2026 info->fix.accel = FB_ACCEL_NONE;
2027
2028 fb_alloc_cmap(&info->cmap, 256, 0);
2029
2030 return 0;
2031}
2032
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002033static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002035 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037
2038 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002039 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002041 /* set all the vital stuff */
2042 cirrusfb_set_fbinfo(info);
2043
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002044 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002046 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2047 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002048 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002049 err = -EINVAL;
2050 goto err_dealloc_cmap;
2051 }
2052
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 info->var.activate = FB_ACTIVATE_NOW;
2054
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002055 err = cirrusfb_decode_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 if (err < 0) {
2057 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002058 dev_dbg(info->device,
2059 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002060 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 }
2062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 err = register_framebuffer(info);
2064 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002065 dev_err(info->device,
2066 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 goto err_dealloc_cmap;
2068 }
2069
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 return 0;
2071
2072err_dealloc_cmap:
2073 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002074 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002075 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 return err;
2077}
2078
Krzysztof Helt8503df62007-10-16 01:29:08 -07002079static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080{
2081 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082
Krzysztof Helt8503df62007-10-16 01:29:08 -07002083 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002084 unregister_framebuffer(info);
2085 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002086 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002087 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002088 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089}
2090
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002092static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2093 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094{
2095 struct cirrusfb_info *cinfo;
2096 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 unsigned long board_addr, board_size;
2098 int ret;
2099
2100 ret = pci_enable_device(pdev);
2101 if (ret < 0) {
2102 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2103 goto err_out;
2104 }
2105
2106 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2107 if (!info) {
2108 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2109 ret = -ENOMEM;
2110 goto err_disable;
2111 }
2112
2113 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002114 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002116 dev_dbg(info->device,
2117 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002118 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002119 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2120 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121
Krzysztof Helt8503df62007-10-16 01:29:08 -07002122 if (isPReP) {
2123 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002125 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002127 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002128 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002130 dev_dbg(info->device,
2131 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002132 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002133 /* FIXME: this forces VGA. alternatives? */
2134 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002135 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 }
2137
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002138 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002139 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002141 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002142 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143
2144 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002145 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002146 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2147 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 goto err_release_fb;
2149 }
2150#if 0 /* if the system didn't claim this region, we would... */
2151 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002152 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2153 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154 ret = -EBUSY;
2155 goto err_release_regions;
2156 }
2157#endif
2158 if (request_region(0x3C0, 32, "cirrusfb"))
2159 release_io_ports = 1;
2160
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002161 info->screen_base = ioremap(board_addr, board_size);
2162 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 ret = -EIO;
2164 goto err_release_legacy;
2165 }
2166
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002167 info->fix.smem_start = board_addr;
2168 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 cinfo->unmap = cirrusfb_pci_unmap;
2170
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002171 dev_info(info->device,
2172 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2173 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 pci_set_drvdata(pdev, info);
2175
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002176 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002177 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002178 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002179 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
2181err_release_legacy:
2182 if (release_io_ports)
2183 release_region(0x3C0, 32);
2184#if 0
2185 release_mem_region(0xA0000, 65535);
2186err_release_regions:
2187#endif
2188 pci_release_regions(pdev);
2189err_release_fb:
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002190 if (cinfo->laguna_mmio == NULL)
2191 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 framebuffer_release(info);
2193err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194err_out:
2195 return ret;
2196}
2197
Krzysztof Helt8503df62007-10-16 01:29:08 -07002198static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199{
2200 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201
Krzysztof Helt8503df62007-10-16 01:29:08 -07002202 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203}
2204
2205static struct pci_driver cirrusfb_pci_driver = {
2206 .name = "cirrusfb",
2207 .id_table = cirrusfb_pci_table,
2208 .probe = cirrusfb_pci_register,
2209 .remove = __devexit_p(cirrusfb_pci_unregister),
2210#ifdef CONFIG_PM
2211#if 0
2212 .suspend = cirrusfb_pci_suspend,
2213 .resume = cirrusfb_pci_resume,
2214#endif
2215#endif
2216};
2217#endif /* CONFIG_PCI */
2218
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002220static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2221 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222{
2223 struct cirrusfb_info *cinfo;
2224 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002225 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226 struct zorro_dev *z2 = NULL;
2227 unsigned long board_addr, board_size, size;
2228 int ret;
2229
2230 btype = ent->driver_data;
2231 if (cirrusfb_zorro_table2[btype].id2)
2232 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2233 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234
2235 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2236 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002237 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 ret = -ENOMEM;
2239 goto err_out;
2240 }
2241
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002242 dev_info(info->device, "%s board detected\n",
2243 cirrusfb_board_info[btype].name);
2244
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 cinfo->btype = btype;
2247
Al Viro36ea96a2007-10-27 19:46:58 +01002248 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002249 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 board_addr = zorro_resource_start(z);
2252 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002253 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254
2255 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002256 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2257 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 ret = -EBUSY;
2259 goto err_release_fb;
2260 }
2261
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 ret = -EIO;
2263
2264 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002265 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266
2267 /* To be precise, for the P4 this is not the */
2268 /* begin of the board, but the begin of RAM. */
2269 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2270 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002271 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 if (!cinfo->regbase)
2273 goto err_release_region;
2274
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002275 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002276 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002278 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002280 info->fix.smem_start = board_addr + 16777216;
2281 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2282 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 goto err_unmap_regbase;
2284 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002285 dev_info(info->device, " REG at $%lx\n",
2286 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002288 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002290 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002292 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2293 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 goto err_release_region;
2295
2296 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002297 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002298 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002300 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002301 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 }
2303 cinfo->unmap = cirrusfb_zorro_unmap;
2304
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002305 dev_info(info->device,
2306 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2307 board_size / MB_, board_addr);
2308
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309 zorro_set_drvdata(z, info);
2310
Al Virod91f5bb2007-10-17 00:27:18 +01002311 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002312 if (ret) {
2313 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002314 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002315 iounmap(cinfo->regbase - 0x600000);
2316 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002317 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002318 }
2319 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320
2321err_unmap_regbase:
2322 /* Parental advisory: explicit hack */
2323 iounmap(cinfo->regbase - 0x600000);
2324err_release_region:
2325 release_region(board_addr, board_size);
2326err_release_fb:
2327 framebuffer_release(info);
2328err_out:
2329 return ret;
2330}
2331
2332void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2333{
2334 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335
Krzysztof Helt8503df62007-10-16 01:29:08 -07002336 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337}
2338
2339static struct zorro_driver cirrusfb_zorro_driver = {
2340 .name = "cirrusfb",
2341 .id_table = cirrusfb_zorro_table,
2342 .probe = cirrusfb_zorro_register,
2343 .remove = __devexit_p(cirrusfb_zorro_unregister),
2344};
2345#endif /* CONFIG_ZORRO */
2346
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002348static int __init cirrusfb_setup(char *options)
2349{
Vlada Pericee119402008-11-19 15:36:45 -08002350 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352 if (!options || !*options)
2353 return 0;
2354
Krzysztof Helt8503df62007-10-16 01:29:08 -07002355 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002356 if (!*this_opt)
2357 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359 if (!strcmp(this_opt, "noaccel"))
2360 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002361 else if (!strncmp(this_opt, "mode:", 5))
2362 mode_option = this_opt + 5;
2363 else
2364 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 }
2366 return 0;
2367}
2368#endif
2369
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 /*
2371 * Modularization
2372 */
2373
2374MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2375MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2376MODULE_LICENSE("GPL");
2377
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002378static int __init cirrusfb_init(void)
2379{
2380 int error = 0;
2381
2382#ifndef MODULE
2383 char *option = NULL;
2384
2385 if (fb_get_options("cirrusfb", &option))
2386 return -ENODEV;
2387 cirrusfb_setup(option);
2388#endif
2389
2390#ifdef CONFIG_ZORRO
2391 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2392#endif
2393#ifdef CONFIG_PCI
2394 error |= pci_register_driver(&cirrusfb_pci_driver);
2395#endif
2396 return error;
2397}
2398
Krzysztof Helt8503df62007-10-16 01:29:08 -07002399static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400{
2401#ifdef CONFIG_PCI
2402 pci_unregister_driver(&cirrusfb_pci_driver);
2403#endif
2404#ifdef CONFIG_ZORRO
2405 zorro_unregister_driver(&cirrusfb_zorro_driver);
2406#endif
2407}
2408
2409module_init(cirrusfb_init);
2410
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002411module_param(mode_option, charp, 0);
2412MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002413module_param(noaccel, bool, 0);
2414MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002415
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416#ifdef MODULE
2417module_exit(cirrusfb_exit);
2418#endif
2419
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420/**********************************************************************/
2421/* about the following functions - I have used the same names for the */
2422/* functions as Markus Wild did in his Retina driver for NetBSD as */
2423/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002424/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425/**********************************************************************/
2426
2427/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002428static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429 int regnum, unsigned char val)
2430{
2431 unsigned long regofs = 0;
2432
2433 if (cinfo->btype == BT_PICASSO) {
2434 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002435/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2436 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2438 regofs = 0xfff;
2439 }
2440
Krzysztof Helt8503df62007-10-16 01:29:08 -07002441 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442}
2443
2444/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002445static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446{
2447 unsigned long regofs = 0;
2448
2449 if (cinfo->btype == BT_PICASSO) {
2450 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002451/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2452 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2454 regofs = 0xfff;
2455 }
2456
Krzysztof Helt8503df62007-10-16 01:29:08 -07002457 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458}
2459
2460/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002461static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464
Krzysztof Helt8503df62007-10-16 01:29:08 -07002465 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 /* if we're just in "write value" mode, write back the */
2467 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002468 vga_w(cinfo->regbase, VGA_ATT_IW,
2469 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 }
2471 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002472/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2473 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474
2475 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477}
2478
2479/*** WHDR() - write into the Hidden DAC register ***/
2480/* as the HDR is the only extension register that requires special treatment
2481 * (the other extension registers are accessible just like the "ordinary"
2482 * registers of their functional group) here is a specialized routine for
2483 * accessing the HDR
2484 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002485static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486{
2487 unsigned char dummy;
2488
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002489 if (cinfo->btype == BT_LAGUNA)
2490 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 if (cinfo->btype == BT_PICASSO) {
2492 /* Klaus' hint for correct access to HDR on some boards */
2493 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002494 WGen(cinfo, VGA_PEL_MSK, 0x00);
2495 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002497 dummy = RGen(cinfo, VGA_PEL_IW);
2498 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 }
2500 /* now do the usual stuff to access the HDR */
2501
Krzysztof Helt8503df62007-10-16 01:29:08 -07002502 dummy = RGen(cinfo, VGA_PEL_MSK);
2503 udelay(200);
2504 dummy = RGen(cinfo, VGA_PEL_MSK);
2505 udelay(200);
2506 dummy = RGen(cinfo, VGA_PEL_MSK);
2507 udelay(200);
2508 dummy = RGen(cinfo, VGA_PEL_MSK);
2509 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510
Krzysztof Helt8503df62007-10-16 01:29:08 -07002511 WGen(cinfo, VGA_PEL_MSK, val);
2512 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513
2514 if (cinfo->btype == BT_PICASSO) {
2515 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002516 dummy = RGen(cinfo, VGA_PEL_IW);
2517 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518
2519 /* and at the end, restore the mask value */
2520 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 WGen(cinfo, VGA_PEL_MSK, 0xff);
2522 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 }
2524}
2525
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528{
2529#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002530 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002532 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533#endif
2534}
2535
2536/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538{
2539#ifdef CONFIG_ZORRO
2540 /* writing an arbitrary value to this one causes the monitor switcher */
2541 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002542 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002544 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545#endif
2546}
2547
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002549static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 unsigned char green, unsigned char blue)
2551{
2552 unsigned int data = VGA_PEL_D;
2553
2554 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002555 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556
2557 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002558 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
2559 cinfo->btype == BT_LAGUNA) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560 /* but DAC data register IS, at least for Picasso II */
2561 if (cinfo->btype == BT_PICASSO)
2562 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002563 vga_w(cinfo->regbase, data, red);
2564 vga_w(cinfo->regbase, data, green);
2565 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002567 vga_w(cinfo->regbase, data, blue);
2568 vga_w(cinfo->regbase, data, green);
2569 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 }
2571}
2572
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573#if 0
2574/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002575static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 unsigned char *green, unsigned char *blue)
2577{
2578 unsigned int data = VGA_PEL_D;
2579
Krzysztof Helt8503df62007-10-16 01:29:08 -07002580 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581
2582 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2583 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2584 if (cinfo->btype == BT_PICASSO)
2585 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002586 *red = vga_r(cinfo->regbase, data);
2587 *green = vga_r(cinfo->regbase, data);
2588 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590 *blue = vga_r(cinfo->regbase, data);
2591 *green = vga_r(cinfo->regbase, data);
2592 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 }
2594}
2595#endif
2596
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597/*******************************************************************
2598 cirrusfb_WaitBLT()
2599
2600 Wait for the BitBLT engine to complete a possible earlier job
2601*********************************************************************/
2602
2603/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002604static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605{
2606 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002607 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002608 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609}
2610
2611/*******************************************************************
2612 cirrusfb_BitBLT()
2613
2614 perform accelerated "scrolling"
2615********************************************************************/
2616
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2618 u_short curx, u_short cury,
2619 u_short destx, u_short desty,
2620 u_short width, u_short height,
2621 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622{
2623 u_short nwidth, nheight;
2624 u_long nsrc, ndest;
2625 u_char bltmode;
2626
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627 nwidth = width - 1;
2628 nheight = height - 1;
2629
2630 bltmode = 0x00;
2631 /* if source adr < dest addr, do the Blt backwards */
2632 if (cury <= desty) {
2633 if (cury == desty) {
2634 /* if src and dest are on the same line, check x */
2635 if (curx < destx)
2636 bltmode |= 0x01;
2637 } else
2638 bltmode |= 0x01;
2639 }
2640 if (!bltmode) {
2641 /* standard case: forward blitting */
2642 nsrc = (cury * line_length) + curx;
2643 ndest = (desty * line_length) + destx;
2644 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002645 /* this means start addresses are at the end,
2646 * counting backwards
2647 */
2648 nsrc = cury * line_length + curx +
2649 nheight * line_length + nwidth;
2650 ndest = desty * line_length + destx +
2651 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652 }
2653
2654 /*
2655 run-down of registers to be programmed:
2656 destination pitch
2657 source pitch
2658 BLT width/height
2659 source start
2660 destination start
2661 BLT mode
2662 BLT ROP
2663 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2664 start/stop
2665 */
2666
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668
2669 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002670 /* dest pitch low */
2671 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2672 /* dest pitch hi */
2673 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2674 /* source pitch low */
2675 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2676 /* source pitch hi */
2677 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678
2679 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002680 /* BLT width low */
2681 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2682 /* BLT width hi */
2683 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684
2685 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002686 /* BLT height low */
2687 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2688 /* BLT width hi */
2689 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002692 /* BLT dest low */
2693 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2694 /* BLT dest mid */
2695 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2696 /* BLT dest hi */
2697 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698
2699 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002700 /* BLT src low */
2701 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2702 /* BLT src mid */
2703 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2704 /* BLT src hi */
2705 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706
2707 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002708 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709
2710 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002711 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712
2713 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002714 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715}
2716
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717/*******************************************************************
2718 cirrusfb_RectFill()
2719
2720 perform accelerated rectangle fill
2721********************************************************************/
2722
Krzysztof Helt8503df62007-10-16 01:29:08 -07002723static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724 u_short x, u_short y, u_short width, u_short height,
2725 u_char color, u_short line_length)
2726{
2727 u_short nwidth, nheight;
2728 u_long ndest;
2729 u_char op;
2730
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731 nwidth = width - 1;
2732 nheight = height - 1;
2733
2734 ndest = (y * line_length) + x;
2735
Krzysztof Helt8503df62007-10-16 01:29:08 -07002736 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737
2738 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002739 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2740 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2741 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2742 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743
2744 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002745 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2746 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747
2748 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002749 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2750 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751
2752 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002753 /* BLT dest low */
2754 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2755 /* BLT dest mid */
2756 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2757 /* BLT dest hi */
2758 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759
2760 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002761 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2762 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2763 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764
2765 /* This is a ColorExpand Blt, using the */
2766 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002767 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2768 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769
2770 op = 0xc0;
2771 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002772 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2773 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 op = 0x50;
2775 op = 0xd0;
2776 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002777 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2778 vga_wgfx(regbase, CL_GR11, color); /* background color */
2779 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2780 vga_wgfx(regbase, CL_GR13, color); /* background color */
2781 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2782 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 op = 0x50;
2784 op = 0xf0;
2785 }
2786 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002787 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788
2789 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002790 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791
2792 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002793 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794}
2795
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796/**************************************************************************
2797 * bestclock() - determine closest possible clock lower(?) than the
2798 * desired pixel clock
2799 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002800static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002802 int n, d;
2803 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804
Krzysztof Helt8503df62007-10-16 01:29:08 -07002805 assert(nom != NULL);
2806 assert(den != NULL);
2807 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808
2809 *nom = 0;
2810 *den = 0;
2811 *div = 0;
2812
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 if (freq < 8000)
2814 freq = 8000;
2815
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002816 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817
2818 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002819 int s = 0;
2820
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002821 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002823 int temp = d;
2824
2825 if (temp > 31) {
2826 s = 1;
2827 temp >>= 1;
2828 }
2829 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002830 h = h > freq ? h - freq : freq - h;
2831 if (h < diff) {
2832 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002834 *den = temp;
2835 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 }
2837 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002838 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002840 if (d > 31) {
2841 s = 1;
2842 d >>= 1;
2843 }
2844 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002845 h = h > freq ? h - freq : freq - h;
2846 if (h < diff) {
2847 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002849 *den = d;
2850 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851 }
2852 }
2853 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854}
2855
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856/* -------------------------------------------------------------------------
2857 *
2858 * debugging functions
2859 *
2860 * -------------------------------------------------------------------------
2861 */
2862
2863#ifdef CIRRUSFB_DEBUG
2864
2865/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 * cirrusfb_dbg_print_regs
2867 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2868 * @reg_class: type of registers to read: %CRT, or %SEQ
2869 *
2870 * DESCRIPTION:
2871 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2872 * old-style I/O ports are queried for information, otherwise MMIO is
2873 * used at the given @base address to query the information.
2874 */
2875
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002876static void cirrusfb_dbg_print_regs(struct fb_info *info,
2877 caddr_t regbase,
2878 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002879{
2880 va_list list;
2881 unsigned char val = 0;
2882 unsigned reg;
2883 char *name;
2884
Krzysztof Helt8503df62007-10-16 01:29:08 -07002885 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886
Krzysztof Helt8503df62007-10-16 01:29:08 -07002887 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002889 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890
2891 switch (reg_class) {
2892 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002893 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 break;
2895 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002896 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 break;
2898 default:
2899 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002900 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901 break;
2902 }
2903
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002904 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905
Krzysztof Helt8503df62007-10-16 01:29:08 -07002906 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 }
2908
Krzysztof Helt8503df62007-10-16 01:29:08 -07002909 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910}
2911
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913 * cirrusfb_dbg_reg_dump
2914 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2915 *
2916 * DESCRIPTION:
2917 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2918 * old-style I/O ports are queried for information, otherwise MMIO is
2919 * used at the given @base address to query the information.
2920 */
2921
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002922static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002924 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002925
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002926 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927 "CR00", 0x00,
2928 "CR01", 0x01,
2929 "CR02", 0x02,
2930 "CR03", 0x03,
2931 "CR04", 0x04,
2932 "CR05", 0x05,
2933 "CR06", 0x06,
2934 "CR07", 0x07,
2935 "CR08", 0x08,
2936 "CR09", 0x09,
2937 "CR0A", 0x0A,
2938 "CR0B", 0x0B,
2939 "CR0C", 0x0C,
2940 "CR0D", 0x0D,
2941 "CR0E", 0x0E,
2942 "CR0F", 0x0F,
2943 "CR10", 0x10,
2944 "CR11", 0x11,
2945 "CR12", 0x12,
2946 "CR13", 0x13,
2947 "CR14", 0x14,
2948 "CR15", 0x15,
2949 "CR16", 0x16,
2950 "CR17", 0x17,
2951 "CR18", 0x18,
2952 "CR22", 0x22,
2953 "CR24", 0x24,
2954 "CR26", 0x26,
2955 "CR2D", 0x2D,
2956 "CR2E", 0x2E,
2957 "CR2F", 0x2F,
2958 "CR30", 0x30,
2959 "CR31", 0x31,
2960 "CR32", 0x32,
2961 "CR33", 0x33,
2962 "CR34", 0x34,
2963 "CR35", 0x35,
2964 "CR36", 0x36,
2965 "CR37", 0x37,
2966 "CR38", 0x38,
2967 "CR39", 0x39,
2968 "CR3A", 0x3A,
2969 "CR3B", 0x3B,
2970 "CR3C", 0x3C,
2971 "CR3D", 0x3D,
2972 "CR3E", 0x3E,
2973 "CR3F", 0x3F,
2974 NULL);
2975
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002976 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002978 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002980 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981 "SR00", 0x00,
2982 "SR01", 0x01,
2983 "SR02", 0x02,
2984 "SR03", 0x03,
2985 "SR04", 0x04,
2986 "SR08", 0x08,
2987 "SR09", 0x09,
2988 "SR0A", 0x0A,
2989 "SR0B", 0x0B,
2990 "SR0D", 0x0D,
2991 "SR10", 0x10,
2992 "SR11", 0x11,
2993 "SR12", 0x12,
2994 "SR13", 0x13,
2995 "SR14", 0x14,
2996 "SR15", 0x15,
2997 "SR16", 0x16,
2998 "SR17", 0x17,
2999 "SR18", 0x18,
3000 "SR19", 0x19,
3001 "SR1A", 0x1A,
3002 "SR1B", 0x1B,
3003 "SR1C", 0x1C,
3004 "SR1D", 0x1D,
3005 "SR1E", 0x1E,
3006 "SR1F", 0x1F,
3007 NULL);
3008
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003009 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003010}
3011
3012#endif /* CIRRUSFB_DEBUG */
3013