blob: 15e2e6bfcbffb1794d77fdd9ca03dd2474d9ddfb [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,
Krzysztof Helt7cade312009-03-31 15:25:13 -070098 BT_SD64, /* GD5434 */
99 BT_PICCOLO, /* GD5426 */
100 BT_PICASSO, /* GD5426 or GD5428 */
101 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700105 BT_LAGUNA, /* GD5462/64 */
106 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700107};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109/*
110 * per-board-type information, used for enumerating and abstracting
111 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700112 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 * use direct indexing on this array
114 * NOTE: '__initdata' cannot be used as some of this info
115 * is required at runtime. Maybe separate into an init-only and
116 * a run-time table?
117 */
118static const struct cirrusfb_board_info_rec {
119 char *name; /* ASCII name of chipset */
120 long maxclock[5]; /* maximum video clock */
121 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700122 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
123 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700124 /* construct bit 19 of screen start address */
125 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* initial SR07 value, then for each mode */
128 unsigned char sr07;
129 unsigned char sr07_1bpp;
130 unsigned char sr07_1bpp_mux;
131 unsigned char sr07_8bpp;
132 unsigned char sr07_8bpp_mux;
133
134 unsigned char sr1f; /* SR1F VGA initial register value */
135} cirrusfb_board_info[] = {
136 [BT_SD64] = {
137 .name = "CL SD64",
138 .maxclock = {
139 /* guess */
140 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700141 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700143 .init_sr07 = true,
144 .init_sr1f = true,
145 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .sr07 = 0xF0,
147 .sr07_1bpp = 0xF0,
148 .sr07_8bpp = 0xF1,
149 .sr1f = 0x20
150 },
151 [BT_PICCOLO] = {
152 .name = "CL Piccolo",
153 .maxclock = {
154 /* guess */
155 90000, 90000, 90000, 90000, 90000
156 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700157 .init_sr07 = true,
158 .init_sr1f = true,
159 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 .sr07 = 0x80,
161 .sr07_1bpp = 0x80,
162 .sr07_8bpp = 0x81,
163 .sr1f = 0x22
164 },
165 [BT_PICASSO] = {
166 .name = "CL Picasso",
167 .maxclock = {
168 /* guess */
169 90000, 90000, 90000, 90000, 90000
170 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700171 .init_sr07 = true,
172 .init_sr1f = true,
173 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 .sr07 = 0x20,
175 .sr07_1bpp = 0x20,
176 .sr07_8bpp = 0x21,
177 .sr1f = 0x22
178 },
179 [BT_SPECTRUM] = {
180 .name = "CL Spectrum",
181 .maxclock = {
182 /* guess */
183 90000, 90000, 90000, 90000, 90000
184 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700185 .init_sr07 = true,
186 .init_sr1f = true,
187 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 .sr07 = 0x80,
189 .sr07_1bpp = 0x80,
190 .sr07_8bpp = 0x81,
191 .sr1f = 0x22
192 },
193 [BT_PICASSO4] = {
194 .name = "CL Picasso4",
195 .maxclock = {
196 135100, 135100, 85500, 85500, 0
197 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700198 .init_sr07 = true,
199 .init_sr1f = false,
200 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700201 .sr07 = 0xA0,
202 .sr07_1bpp = 0xA0,
203 .sr07_1bpp_mux = 0xA6,
204 .sr07_8bpp = 0xA1,
205 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 .sr1f = 0
207 },
208 [BT_ALPINE] = {
209 .name = "CL Alpine",
210 .maxclock = {
211 /* for the GD5430. GD5446 can do more... */
212 85500, 85500, 50000, 28500, 0
213 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700214 .init_sr07 = true,
215 .init_sr1f = true,
216 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700218 .sr07_1bpp = 0xA0,
219 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 .sr07_8bpp = 0xA1,
221 .sr07_8bpp_mux = 0xA7,
222 .sr1f = 0x1C
223 },
224 [BT_GD5480] = {
225 .name = "CL GD5480",
226 .maxclock = {
227 135100, 200000, 200000, 135100, 135100
228 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700229 .init_sr07 = true,
230 .init_sr1f = true,
231 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 .sr07 = 0x10,
233 .sr07_1bpp = 0x11,
234 .sr07_8bpp = 0x11,
235 .sr1f = 0x1C
236 },
237 [BT_LAGUNA] = {
238 .name = "CL Laguna",
239 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700240 /* taken from X11 code */
241 170000, 170000, 170000, 170000, 135100,
242 },
243 .init_sr07 = false,
244 .init_sr1f = false,
245 .scrn_start_bit19 = true,
246 },
247 [BT_LAGUNAB] = {
248 .name = "CL Laguna AGP",
249 .maxclock = {
250 /* taken from X11 code */
251 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700330enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700331 CRT,
332 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700333};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700334#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336/* info about board */
337struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700339 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700340 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 unsigned char SFR; /* Shadow of special function register */
342
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700343 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700345 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700347 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348};
349
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700350static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700351static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353/****************************************************************************/
354/**** BEGIN PROTOTYPES ******************************************************/
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700357static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
358 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700361static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700362static void switch_monitor(struct cirrusfb_info *cinfo, int on);
363static void WGen(const struct cirrusfb_info *cinfo,
364 int regnum, unsigned char val);
365static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
366static void AttrOn(const struct cirrusfb_info *cinfo);
367static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
368static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
369static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
370static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
371 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700373static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
374 unsigned char *red, unsigned char *green,
375 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700377static void cirrusfb_WaitBLT(u8 __iomem *regbase);
378static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
379 u_short curx, u_short cury,
380 u_short destx, u_short desty,
381 u_short width, u_short height,
382 u_short line_length);
383static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
384 u_short x, u_short y,
385 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700386 u32 fg_color, u32 bg_color,
387 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700389static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700392static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
393static void cirrusfb_dbg_print_regs(struct fb_info *info,
394 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700395 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396#endif /* CIRRUSFB_DEBUG */
397
398/*** END PROTOTYPES ********************************************************/
399/*****************************************************************************/
400/*** BEGIN Interface Used by the World ***************************************/
401
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700402static inline int is_laguna(const struct cirrusfb_info *cinfo)
403{
404 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
405}
406
Krzysztof Helt8503df62007-10-16 01:29:08 -0700407static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
409/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 return 0;
415}
416
417/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700418static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 return 0;
423}
424
425/**** END Interface used by the World *************************************/
426/****************************************************************************/
427/**** BEGIN Hardware specific Routines **************************************/
428
Krzysztof Helt486ff382008-10-15 22:03:42 -0700429/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700430static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700432 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700433 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Krzysztof Helt486ff382008-10-15 22:03:42 -0700435 /* Read MCLK value */
436 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700437 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700440 * should divide it by to get VCLK
441 */
442
443 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700444 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700445 return 1;
446 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700447 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
450
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452}
453
Krzysztof Helt99a45842009-03-31 15:25:09 -0700454static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
455 struct fb_info *info)
456{
457 long freq;
458 long maxclock;
459 struct cirrusfb_info *cinfo = info->par;
460 unsigned maxclockidx = var->bits_per_pixel >> 3;
461
462 /* convert from ps to kHz */
463 freq = PICOS2KHZ(var->pixclock);
464
465 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
466
467 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
468 cinfo->multiplexing = 0;
469
470 /* If the frequency is greater than we can support, we might be able
471 * to use multiplexing for the video mode */
472 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700473 dev_err(info->device,
474 "Frequency greater than maxclock (%ld kHz)\n",
475 maxclock);
476 return -EINVAL;
477 }
478 /*
479 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
480 * pixel clock
481 */
482 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700483 switch (cinfo->btype) {
484 case BT_ALPINE:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700485 case BT_PICASSO4:
486 if (freq > 85500)
487 cinfo->multiplexing = 1;
488 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700489 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700490 if (freq > 135100)
491 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700492 break;
493
494 default:
495 dev_err(info->device,
496 "Frequency greater than maxclock (%ld kHz)\n",
497 maxclock);
498 return -EINVAL;
499 }
500 }
501#if 0
502 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
503 * the VCLK is double the pixel clock. */
504 switch (var->bits_per_pixel) {
505 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700506 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700507 if (var->xres <= 800)
508 /* Xbh has this type of clock for 32-bit */
509 freq /= 2;
510 break;
511 }
512#endif
513 return 0;
514}
515
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516static int cirrusfb_check_var(struct fb_var_screeninfo *var,
517 struct fb_info *info)
518{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700519 int yres;
520 /* memory size in pixels */
521 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522
523 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700524 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 var->red.offset = 0;
526 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700527 var->green = var->red;
528 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 break;
530
531 case 8:
532 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700533 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700534 var->green = var->red;
535 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 break;
537
538 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700539 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 var->red.offset = 2;
541 var->green.offset = -3;
542 var->blue.offset = 8;
543 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700544 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 var->green.offset = 5;
546 var->blue.offset = 0;
547 }
548 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700549 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 var->blue.length = 5;
551 break;
552
Krzysztof Helt7cade312009-03-31 15:25:13 -0700553 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700554 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700555 var->red.offset = 0;
556 var->green.offset = 8;
557 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 } else {
559 var->red.offset = 16;
560 var->green.offset = 8;
561 var->blue.offset = 0;
562 }
563 var->red.length = 8;
564 var->green.length = 8;
565 var->blue.length = 8;
566 break;
567
568 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700569 dev_dbg(info->device,
570 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700571 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 /* should never occur */
573 break;
574 }
575
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700576 if (var->xres_virtual < var->xres)
577 var->xres_virtual = var->xres;
578 /* use highest possible virtual resolution */
579 if (var->yres_virtual == -1) {
580 var->yres_virtual = pixels / var->xres_virtual;
581
582 dev_info(info->device,
583 "virtual resolution set to maximum of %dx%d\n",
584 var->xres_virtual, var->yres_virtual);
585 }
586 if (var->yres_virtual < var->yres)
587 var->yres_virtual = var->yres;
588
589 if (var->xres_virtual * var->yres_virtual > pixels) {
590 dev_err(info->device, "mode %dx%dx%d rejected... "
591 "virtual resolution too high to fit into video memory!\n",
592 var->xres_virtual, var->yres_virtual,
593 var->bits_per_pixel);
594 return -EINVAL;
595 }
596
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700597 if (var->xoffset < 0)
598 var->xoffset = 0;
599 if (var->yoffset < 0)
600 var->yoffset = 0;
601
602 /* truncate xoffset and yoffset to maximum if too high */
603 if (var->xoffset > var->xres_virtual - var->xres)
604 var->xoffset = var->xres_virtual - var->xres - 1;
605 if (var->yoffset > var->yres_virtual - var->yres)
606 var->yoffset = var->yres_virtual - var->yres - 1;
607
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 var->red.msb_right =
609 var->green.msb_right =
610 var->blue.msb_right =
611 var->transp.offset =
612 var->transp.length =
613 var->transp.msb_right = 0;
614
615 yres = var->yres;
616 if (var->vmode & FB_VMODE_DOUBLE)
617 yres *= 2;
618 else if (var->vmode & FB_VMODE_INTERLACED)
619 yres = (yres + 1) / 2;
620
621 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700622 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700623 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 return -EINVAL;
625 }
626
Krzysztof Helt99a45842009-03-31 15:25:09 -0700627 if (cirrusfb_check_pixclock(var, info))
628 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return 0;
631}
632
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700633static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700635 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700636 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700637
Krzysztof Helt8503df62007-10-16 01:29:08 -0700638 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700639 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Krzysztof Helt486ff382008-10-15 22:03:42 -0700641 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700642 dev_dbg(info->device, "Set %s as pixclock source.\n",
643 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700644 old1f |= 0x40;
645 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
646 if (div == 2)
647 old1e |= 1;
648
649 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700651 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652}
653
654/*************************************************************************
655 cirrusfb_set_par_foo()
656
657 actually writes the values for a new video mode into the hardware,
658**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700659static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
661 struct cirrusfb_info *cinfo = info->par;
662 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 u8 __iomem *regbase = cinfo->regbase;
664 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700665 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700667 int hdispend, hsyncstart, hsyncend, htotal;
668 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700669 long freq;
670 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700671 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700673 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700675
676 switch (var->bits_per_pixel) {
677 case 1:
678 info->fix.line_length = var->xres_virtual / 8;
679 info->fix.visual = FB_VISUAL_MONO10;
680 break;
681
682 case 8:
683 info->fix.line_length = var->xres_virtual;
684 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
685 break;
686
687 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700688 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700689 info->fix.line_length = var->xres_virtual *
690 var->bits_per_pixel >> 3;
691 info->fix.visual = FB_VISUAL_TRUECOLOR;
692 break;
693 }
694 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700696 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 bi = &cirrusfb_board_info[cinfo->btype];
699
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700700 hsyncstart = var->xres + var->right_margin;
701 hsyncend = hsyncstart + var->hsync_len;
702 htotal = (hsyncend + var->left_margin) / 8 - 5;
703 hdispend = var->xres / 8 - 1;
704 hsyncstart = hsyncstart / 8 + 1;
705 hsyncend = hsyncend / 8 + 1;
706
707 yres = var->yres;
708 vsyncstart = yres + var->lower_margin;
709 vsyncend = vsyncstart + var->vsync_len;
710 vtotal = vsyncend + var->upper_margin;
711 vdispend = yres - 1;
712
713 if (var->vmode & FB_VMODE_DOUBLE) {
714 yres *= 2;
715 vsyncstart *= 2;
716 vsyncend *= 2;
717 vtotal *= 2;
718 } else if (var->vmode & FB_VMODE_INTERLACED) {
719 yres = (yres + 1) / 2;
720 vsyncstart = (vsyncstart + 1) / 2;
721 vsyncend = (vsyncend + 1) / 2;
722 vtotal = (vtotal + 1) / 2;
723 }
724
725 vtotal -= 2;
726 vsyncstart -= 1;
727 vsyncend -= 1;
728
729 if (yres >= 1024) {
730 vtotal /= 2;
731 vsyncstart /= 2;
732 vsyncend /= 2;
733 vdispend /= 2;
734 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700735 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700736 htotal /= 2;
737 hsyncstart /= 2;
738 hsyncend /= 2;
739 hdispend /= 2;
740 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700742 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700745 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700746 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700748 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700749 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700751 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700752 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Krzysztof Helt8503df62007-10-16 01:29:08 -0700754 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700755 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700756 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700757 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700759 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700760 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 tmp = hsyncend % 32;
763 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700765 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700766 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700768 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
771 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700772 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700774 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700776 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700782 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700786 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700787 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
789 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 tmp |= 0x20;
792 if (var->vmode & FB_VMODE_DOUBLE)
793 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700794 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700795 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700797 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700798 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700800 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700801 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700803 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700806 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700809 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700810 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700812 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700813 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 tmp = 0;
816 if (var->vmode & FB_VMODE_INTERLACED)
817 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700818 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700820 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700822 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 tmp |= 128;
826
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700827 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700828 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700830 freq = PICOS2KHZ(var->pixclock);
Krzysztof Helt7cade312009-03-31 15:25:13 -0700831 if (cinfo->btype == BT_ALPINE && var->bits_per_pixel == 24)
832 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700833 if (cinfo->multiplexing)
834 freq /= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700835
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700836 bestclock(freq, &nom, &den, &div);
837
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700838 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
839 freq, nom, den, div);
840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 /* set VCLK0 */
842 /* hardware RefClock: 14.31818 MHz */
843 /* formula: VClk = (OSC * N) / (D * (1+P)) */
844 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
845
Krzysztof Helt527410f2009-03-31 15:25:13 -0700846 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700847 /* if freq is close to mclk or mclk/2 select mclk
848 * as clock source
849 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700850 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700851 if (divMCLK) {
852 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700853 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700854 }
855 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700856 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700857 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
858 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
859 unsigned short tile_control;
860
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700861 if (cinfo->btype == BT_LAGUNAB) {
862 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
863 tile_control &= ~0x80;
864 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
865 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700866
867 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
868 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
869 control = fb_readw(cinfo->laguna_mmio + 0x402);
870 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
871 control &= ~0x6800;
872 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700873 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700874 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700875 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700876 tmp = den << 1;
877 if (div != 0)
878 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700879 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
880 if ((cinfo->btype == BT_SD64) ||
881 (cinfo->btype == BT_ALPINE) ||
882 (cinfo->btype == BT_GD5480))
883 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700885 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
886 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700887 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700888 vga_wseq(regbase, CL_SEQRE, tmp);
889 vga_wseq(regbase, CL_SEQR1E, nom);
890 } else {
891 vga_wseq(regbase, CL_SEQRB, nom);
892 vga_wseq(regbase, CL_SEQR1B, tmp);
893 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700894 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700896 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700898 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 else
900 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
901 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700902 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 /* don't know if it would hurt to also program this if no interlaced */
905 /* mode is used, but I feel better this way.. :-) */
906 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700907 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700909 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 /* enable display memory & CRTC I/O address for color mode */
913 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
915 tmp |= 0x40;
916 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
917 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700918 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700919 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700920 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921
Krzysztof Helt8503df62007-10-16 01:29:08 -0700922 /* text cursor on and start line */
923 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
924 /* text cursor end line */
925 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
927 /******************************************************
928 *
929 * 1 bpp
930 *
931 */
932
933 /* programming for different color depths */
934 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700935 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700936 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 /* SR07 */
939 switch (cinfo->btype) {
940 case BT_SD64:
941 case BT_PICCOLO:
942 case BT_PICASSO:
943 case BT_SPECTRUM:
944 case BT_PICASSO4:
945 case BT_ALPINE:
946 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700947 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700948 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 bi->sr07_1bpp_mux : bi->sr07_1bpp);
950 break;
951
952 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700953 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 vga_wseq(regbase, CL_SEQR7,
955 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 break;
957
958 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700959 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 break;
961 }
962
963 /* Extended Sequencer Mode */
964 switch (cinfo->btype) {
965 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700966 /* setting the SEQRF on SD64 is not necessary
967 * (only during init)
968 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700969 /* MCLK select */
970 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 break;
972
973 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700974 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700975 /* ### ueberall 0x22? */
976 /* ##vorher 1c MCLK select */
977 vga_wseq(regbase, CL_SEQR1F, 0x22);
978 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
979 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 break;
981
982 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700983 /* ##vorher 22 MCLK select */
984 vga_wseq(regbase, CL_SEQR1F, 0x22);
985 /* ## vorher d0 avoid FIFO underruns..? */
986 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 break;
988
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 case BT_PICASSO4:
990 case BT_ALPINE:
991 case BT_GD5480:
992 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700993 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 /* do nothing */
995 break;
996
997 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700998 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 break;
1000 }
1001
Krzysztof Helt8503df62007-10-16 01:29:08 -07001002 /* pixel mask: pass-through for first plane */
1003 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001004 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001005 /* hidden dac reg: 1280x1024 */
1006 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001008 /* hidden dac: nothing */
1009 WHDR(cinfo, 0);
1010 /* memory mode: odd/even, ext. memory */
1011 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1012 /* plane mask: only write to first plane */
1013 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 }
1015
1016 /******************************************************
1017 *
1018 * 8 bpp
1019 *
1020 */
1021
1022 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001023 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 switch (cinfo->btype) {
1025 case BT_SD64:
1026 case BT_PICCOLO:
1027 case BT_PICASSO:
1028 case BT_SPECTRUM:
1029 case BT_PICASSO4:
1030 case BT_ALPINE:
1031 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001032 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001033 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1035 break;
1036
1037 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001038 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001039 vga_wseq(regbase, CL_SEQR7,
1040 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001041 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 break;
1043
1044 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001045 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 break;
1047 }
1048
1049 switch (cinfo->btype) {
1050 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001051 /* MCLK select */
1052 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 break;
1054
1055 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001058 /* ### vorher 1c MCLK select */
1059 vga_wseq(regbase, CL_SEQR1F, 0x22);
1060 /* Fast Page-Mode writes */
1061 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 break;
1063
1064 case BT_PICASSO4:
1065#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001066 /* ### INCOMPLETE!! */
1067 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 /* We already set SRF and SR1F */
1071 break;
1072
1073 case BT_GD5480:
1074 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001075 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 /* do nothing */
1077 break;
1078
1079 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001080 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 break;
1082 }
1083
Krzysztof Helt8503df62007-10-16 01:29:08 -07001084 /* mode register: 256 color mode */
1085 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001086 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001087 /* hidden dac reg: 1280x1024 */
1088 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001090 /* hidden dac: nothing */
1091 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 }
1093
1094 /******************************************************
1095 *
1096 * 16 bpp
1097 *
1098 */
1099
1100 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001101 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 switch (cinfo->btype) {
1103 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001104 /* Extended Sequencer Mode: 256c col. mode */
1105 vga_wseq(regbase, CL_SEQR7, 0xf7);
1106 /* MCLK select */
1107 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 break;
1109
1110 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001111 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001112 vga_wseq(regbase, CL_SEQR7, 0x87);
1113 /* Fast Page-Mode writes */
1114 vga_wseq(regbase, CL_SEQRF, 0xb0);
1115 /* MCLK select */
1116 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 break;
1118
1119 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 vga_wseq(regbase, CL_SEQR7, 0x27);
1121 /* Fast Page-Mode writes */
1122 vga_wseq(regbase, CL_SEQRF, 0xb0);
1123 /* MCLK select */
1124 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001129 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 break;
1131
1132 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001133 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 /* We already set SRF and SR1F */
1135 break;
1136
1137 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001138 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001139 vga_wseq(regbase, CL_SEQR7,
1140 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001141 control |= 0x2000;
1142 format |= 0x1400;
1143 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 break;
1145
1146 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001147 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 break;
1149 }
1150
Krzysztof Helt8503df62007-10-16 01:29:08 -07001151 /* mode register: 256 color mode */
1152 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001154 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155#elif defined(CONFIG_ZORRO)
1156 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001157 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 }
1160
1161 /******************************************************
1162 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001163 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 *
1165 */
1166
Krzysztof Helt7cade312009-03-31 15:25:13 -07001167 else if (var->bits_per_pixel == 24) {
1168 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 switch (cinfo->btype) {
1170 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001171 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001172 vga_wseq(regbase, CL_SEQR7, 0xf5);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001173 /* MCLK select */
1174 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 break;
1176
1177 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001178 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001179 vga_wseq(regbase, CL_SEQR7, 0x85);
1180 /* Fast Page-Mode writes */
1181 vga_wseq(regbase, CL_SEQRF, 0xb0);
1182 /* MCLK select */
1183 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 break;
1185
1186 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001187 vga_wseq(regbase, CL_SEQR7, 0x25);
1188 /* Fast Page-Mode writes */
1189 vga_wseq(regbase, CL_SEQRF, 0xb0);
1190 /* MCLK select */
1191 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 break;
1193
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 case BT_ALPINE:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001196 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 break;
1198
1199 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001200 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 /* We already set SRF and SR1F */
1202 break;
1203
1204 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001205 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001206 vga_wseq(regbase, CL_SEQR7,
1207 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001208 control |= 0x4000;
1209 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001210 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 break;
1212
1213 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001214 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 break;
1216 }
1217
Krzysztof Helt8503df62007-10-16 01:29:08 -07001218 /* mode register: 256 color mode */
1219 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001220 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1221 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 }
1223
1224 /******************************************************
1225 *
1226 * unknown/unsupported bpp
1227 *
1228 */
1229
Krzysztof Helt8503df62007-10-16 01:29:08 -07001230 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001231 dev_err(info->device,
1232 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234
Krzysztof Helt6683e012009-03-31 15:25:06 -07001235 pitch = info->fix.line_length >> 3;
1236 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001238 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 tmp |= 0x10; /* offset overflow bit */
1240
Krzysztof Helt8503df62007-10-16 01:29:08 -07001241 /* screen start addr #16-18, fastpagemode cycles */
1242 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001244 /* screen start address bit 19 */
1245 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001246 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001248 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001249 tmp = 0;
1250 if ((htotal + 5) & 256)
1251 tmp |= 128;
1252 if (hdispend & 256)
1253 tmp |= 64;
1254 if (hsyncstart & 256)
1255 tmp |= 48;
1256 if (vtotal & 1024)
1257 tmp |= 8;
1258 if (vdispend & 1024)
1259 tmp |= 4;
1260 if (vsyncstart & 1024)
1261 tmp |= 3;
1262
1263 vga_wcrt(regbase, CL_CRT1E, tmp);
1264 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1265 }
1266
Krzysztof Helt8503df62007-10-16 01:29:08 -07001267 /* pixel panning */
1268 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269
1270 /* [ EGS: SetOffset(); ] */
1271 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001272 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001274 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001275 /* no tiles */
1276 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1277 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1278 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1279 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 /* finally, turn on everything - turn off "FullBandwidth" bit */
1281 /* also, set "DotClock%2" bit where requested */
1282 tmp = 0x01;
1283
1284/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1285 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1286 tmp |= 0x08;
1287*/
1288
Krzysztof Helt8503df62007-10-16 01:29:08 -07001289 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001290 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001293 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294#endif
1295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 return 0;
1297}
1298
1299/* for some reason incomprehensible to me, cirrusfb requires that you write
1300 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001301static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001303 cirrusfb_set_par_foo(info);
1304 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305}
1306
Krzysztof Helt8503df62007-10-16 01:29:08 -07001307static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1308 unsigned blue, unsigned transp,
1309 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310{
1311 struct cirrusfb_info *cinfo = info->par;
1312
1313 if (regno > 255)
1314 return -EINVAL;
1315
1316 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1317 u32 v;
1318 red >>= (16 - info->var.red.length);
1319 green >>= (16 - info->var.green.length);
1320 blue >>= (16 - info->var.blue.length);
1321
Krzysztof Helt8503df62007-10-16 01:29:08 -07001322 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 return 1;
1324 v = (red << info->var.red.offset) |
1325 (green << info->var.green.offset) |
1326 (blue << info->var.blue.offset);
1327
Krzysztof Helt060b6002007-10-16 01:29:13 -07001328 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 return 0;
1330 }
1331
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332 if (info->var.bits_per_pixel == 8)
1333 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
1335 return 0;
1336
1337}
1338
1339/*************************************************************************
1340 cirrusfb_pan_display()
1341
1342 performs display panning - provided hardware permits this
1343**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001344static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1345 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001347 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001349 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 struct cirrusfb_info *cinfo = info->par;
1351
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001352 dev_dbg(info->device,
1353 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
1355 /* no range checks for xoffset and yoffset, */
1356 /* as fb_pan_display has already done this */
1357 if (var->vmode & FB_VMODE_YWRAP)
1358 return -EINVAL;
1359
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
Krzysztof Helt99a45842009-03-31 15:25:09 -07001362 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 if (info->var.bits_per_pixel == 1) {
1365 /* base is already correct */
1366 xpix = (unsigned char) (var->xoffset % 8);
1367 } else {
1368 base /= 4;
1369 xpix = (unsigned char) ((xoffset % 4) * 2);
1370 }
1371
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001372 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001373 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
1375 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001376 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1377 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001379 /* 0xf2 is %11110010, exclude tmp bits */
1380 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 /* construct bits 16, 17 and 18 of screen start address */
1382 if (base & 0x10000)
1383 tmp |= 0x01;
1384 if (base & 0x20000)
1385 tmp |= 0x04;
1386 if (base & 0x40000)
1387 tmp |= 0x08;
1388
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001389 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
1391 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001392 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001393 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1394 if (is_laguna(cinfo))
1395 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1396 else
1397 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001398 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1399 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400
Krzysztof Helt8503df62007-10-16 01:29:08 -07001401 /* write pixel panning value to AR33; this does not quite work in 8bpp
1402 *
1403 * ### Piccolo..? Will this work?
1404 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001406 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
Krzysztof Helt8503df62007-10-16 01:29:08 -07001408 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409}
1410
Krzysztof Helt8503df62007-10-16 01:29:08 -07001411static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412{
1413 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001414 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1415 * then the caller blanks by setting the CLUT (Color Look Up Table)
1416 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1417 * failed due to e.g. a video mode which doesn't support it.
1418 * Implements VESA suspend and powerdown modes on hardware that
1419 * supports disabling hsync/vsync:
1420 * blank_mode == 2: suspend vsync
1421 * blank_mode == 3: suspend hsync
1422 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 */
1424 unsigned char val;
1425 struct cirrusfb_info *cinfo = info->par;
1426 int current_mode = cinfo->blank_mode;
1427
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001428 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429
1430 if (info->state != FBINFO_STATE_RUNNING ||
1431 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001432 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 return 0;
1434 }
1435
1436 /* Undo current */
1437 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001438 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001439 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001440 val = 0;
1441 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001442 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001443 val = 0x20;
1444
1445 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1446 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
1448 switch (blank_mode) {
1449 case FB_BLANK_UNBLANK:
1450 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001451 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 break;
1453 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001454 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 break;
1456 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001457 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 break;
1459 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001460 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 break;
1462 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001463 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 return 1;
1465 }
1466
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001467 vga_wgfx(cinfo->regbase, CL_GRE, val);
1468
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001470 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472 /* Let fbcon do a soft blank for us */
1473 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1474}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001475
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476/**** END Hardware specific Routines **************************************/
1477/****************************************************************************/
1478/**** BEGIN Internal Routines ***********************************************/
1479
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001480static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001482 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 const struct cirrusfb_board_info_rec *bi;
1484
Krzysztof Helt8503df62007-10-16 01:29:08 -07001485 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487 bi = &cirrusfb_board_info[cinfo->btype];
1488
1489 /* reset board globally */
1490 switch (cinfo->btype) {
1491 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001492 WSFR(cinfo, 0x01);
1493 udelay(500);
1494 WSFR(cinfo, 0x51);
1495 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 break;
1497 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001498 WSFR2(cinfo, 0xff);
1499 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 break;
1501 case BT_SD64:
1502 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001503 WSFR(cinfo, 0x1f);
1504 udelay(500);
1505 WSFR(cinfo, 0x4f);
1506 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 break;
1508 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001509 /* disable flickerfixer */
1510 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1511 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001512 /* mode */
1513 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001514 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001515 /* from Klaus' NetBSD driver: */
1516 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001517 case BT_ALPINE: /* fall through */
1518 /* put blitter into 542x compat */
1519 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 break;
1521
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001522 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001523 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 /* Nothing to do to reset the board. */
1525 break;
1526
1527 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001528 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 break;
1530 }
1531
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001532 /* make sure RAM size set by this point */
1533 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
1535 /* the P4 is not fully initialized here; I rely on it having been */
1536 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001537 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
1539 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1541 WGen(cinfo, CL_POS102, 0x01);
1542 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543
1544 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001545 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
Krzysztof Helt8503df62007-10-16 01:29:08 -07001547 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001548 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 /* FullBandwidth (video off) and 8/9 dot clock */
1551 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 /* "magic cookie" - doesn't make any sense to me.. */
1554/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1555 /* unlock all extension registers */
1556 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 switch (cinfo->btype) {
1559 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001560 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 break;
1562 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001563 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001564 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 break;
1566 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001567 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 break;
1569 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001570 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1571 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 break;
1573 }
1574 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001575 /* plane mask: nothing */
1576 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1577 /* character map select: doesn't even matter in gx mode */
1578 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001579 /* memory mode: chain4, ext. memory */
1580 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
1582 /* controller-internal base address of video memory */
1583 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001584 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585
Krzysztof Helt8503df62007-10-16 01:29:08 -07001586 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1587 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588
Krzysztof Helt8503df62007-10-16 01:29:08 -07001589 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1590 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1591 /* graphics cursor Y position (..."... ) */
1592 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1593 /* graphics cursor attributes */
1594 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1595 /* graphics cursor pattern address */
1596 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
1598 /* writing these on a P4 might give problems.. */
1599 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001600 /* configuration readback and ext. color */
1601 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1602 /* signature generator */
1603 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 }
1605
1606 /* MCLK select etc. */
1607 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001608 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
Krzysztof Helt8503df62007-10-16 01:29:08 -07001610 /* Screen A preset row scan: none */
1611 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1612 /* Text cursor start: disable text cursor */
1613 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1614 /* Text cursor end: - */
1615 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 /* text cursor location high: 0 */
1617 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1618 /* text cursor location low: 0 */
1619 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
Krzysztof Helt8503df62007-10-16 01:29:08 -07001621 /* Underline Row scanline: - */
1622 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001624 /* ext. display controls: ext.adr. wrap */
1625 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
Krzysztof Helt8503df62007-10-16 01:29:08 -07001627 /* Set/Reset registes: - */
1628 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1629 /* Set/Reset enable: - */
1630 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1631 /* Color Compare: - */
1632 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1633 /* Data Rotate: - */
1634 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1635 /* Read Map Select: - */
1636 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1637 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1638 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1639 /* Miscellaneous: memory map base address, graphics mode */
1640 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1641 /* Color Don't care: involve all planes */
1642 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1643 /* Bit Mask: no mask at all */
1644 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001645
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001646 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001647 /* (5434 can't have bit 3 set for bitblt) */
1648 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 /* Graphics controller mode extensions: finer granularity,
1651 * 8byte data latches
1652 */
1653 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
Krzysztof Helt8503df62007-10-16 01:29:08 -07001655 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1656 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1657 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1658 /* Background color byte 1: - */
1659 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1660 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661
Krzysztof Helt8503df62007-10-16 01:29:08 -07001662 /* Attribute Controller palette registers: "identity mapping" */
1663 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1664 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1665 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1666 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1667 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1668 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1669 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1670 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1671 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1672 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1673 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1674 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1675 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1676 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1677 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1678 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 /* Attribute Controller mode: graphics mode */
1681 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1682 /* Overscan color reg.: reg. 0 */
1683 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1684 /* Color Plane enable: Enable all 4 planes */
1685 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001686 /* Color Select: - */
1687 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 /* BLT Start/status: Blitter reset */
1692 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1693 /* - " - : "end-of-reset" */
1694 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 return;
1699}
1700
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702{
1703#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1704 static int IsOn = 0; /* XXX not ok for multiple boards */
1705
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 if (cinfo->btype == BT_PICASSO4)
1707 return; /* nothing to switch */
1708 if (cinfo->btype == BT_ALPINE)
1709 return; /* nothing to switch */
1710 if (cinfo->btype == BT_GD5480)
1711 return; /* nothing to switch */
1712 if (cinfo->btype == BT_PICASSO) {
1713 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001714 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 return;
1716 }
1717 if (on) {
1718 switch (cinfo->btype) {
1719 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001720 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 break;
1722 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001723 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 break;
1725 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001726 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 break;
1728 default: /* do nothing */ break;
1729 }
1730 } else {
1731 switch (cinfo->btype) {
1732 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001733 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 break;
1735 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001736 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 break;
1738 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001739 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001741 default: /* do nothing */
1742 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 }
1744 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745#endif /* CONFIG_ZORRO */
1746}
1747
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748/******************************************/
1749/* Linux 2.6-style accelerated functions */
1750/******************************************/
1751
Krzysztof Helt8343c892009-03-31 15:25:11 -07001752static int cirrusfb_sync(struct fb_info *info)
1753{
1754 struct cirrusfb_info *cinfo = info->par;
1755
1756 if (!is_laguna(cinfo)) {
1757 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1758 cpu_relax();
1759 }
1760 return 0;
1761}
1762
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763static void cirrusfb_fillrect(struct fb_info *info,
1764 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 struct fb_fillrect modded;
1767 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001768 struct cirrusfb_info *cinfo = info->par;
1769 int m = info->var.bits_per_pixel;
1770 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1771 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
1773 if (info->state != FBINFO_STATE_RUNNING)
1774 return;
1775 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1776 cfb_fillrect(info, region);
1777 return;
1778 }
1779
1780 vxres = info->var.xres_virtual;
1781 vyres = info->var.yres_virtual;
1782
1783 memcpy(&modded, region, sizeof(struct fb_fillrect));
1784
Krzysztof Helt8503df62007-10-16 01:29:08 -07001785 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 modded.dx >= vxres || modded.dy >= vyres)
1787 return;
1788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 if (modded.dx + modded.width > vxres)
1790 modded.width = vxres - modded.dx;
1791 if (modded.dy + modded.height > vyres)
1792 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793
Krzysztof Helt060b6002007-10-16 01:29:13 -07001794 cirrusfb_RectFill(cinfo->regbase,
1795 info->var.bits_per_pixel,
1796 (region->dx * m) / 8, region->dy,
1797 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001798 color, color,
1799 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800}
1801
Krzysztof Helt8503df62007-10-16 01:29:08 -07001802static void cirrusfb_copyarea(struct fb_info *info,
1803 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 struct fb_copyarea modded;
1806 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001807 struct cirrusfb_info *cinfo = info->par;
1808 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
1810 if (info->state != FBINFO_STATE_RUNNING)
1811 return;
1812 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1813 cfb_copyarea(info, area);
1814 return;
1815 }
1816
1817 vxres = info->var.xres_virtual;
1818 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001819 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
Krzysztof Helt8503df62007-10-16 01:29:08 -07001821 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 modded.sx >= vxres || modded.sy >= vyres ||
1823 modded.dx >= vxres || modded.dy >= vyres)
1824 return;
1825
Krzysztof Helt8503df62007-10-16 01:29:08 -07001826 if (modded.sx + modded.width > vxres)
1827 modded.width = vxres - modded.sx;
1828 if (modded.dx + modded.width > vxres)
1829 modded.width = vxres - modded.dx;
1830 if (modded.sy + modded.height > vyres)
1831 modded.height = vyres - modded.sy;
1832 if (modded.dy + modded.height > vyres)
1833 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834
Krzysztof Helt060b6002007-10-16 01:29:13 -07001835 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1836 (area->sx * m) / 8, area->sy,
1837 (area->dx * m) / 8, area->dy,
1838 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001839 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001840
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841}
1842
Krzysztof Helt8503df62007-10-16 01:29:08 -07001843static void cirrusfb_imageblit(struct fb_info *info,
1844 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845{
1846 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001847 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848
Krzysztof Helt9e848062009-03-31 15:25:11 -07001849 if (info->state != FBINFO_STATE_RUNNING)
1850 return;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001851 /* Alpine acceleration does not work at 24bpp ?!? */
1852 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1 ||
1853 (cinfo->btype == BT_ALPINE && op == 0xc))
Krzysztof Helt9e848062009-03-31 15:25:11 -07001854 cfb_imageblit(info, image);
1855 else {
1856 unsigned size = ((image->width + 7) >> 3) * image->height;
1857 int m = info->var.bits_per_pixel;
1858 u32 fg, bg;
1859
1860 if (info->var.bits_per_pixel == 8) {
1861 fg = image->fg_color;
1862 bg = image->bg_color;
1863 } else {
1864 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1865 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1866 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001867 if (info->var.bits_per_pixel == 24) {
1868 /* clear background first */
1869 cirrusfb_RectFill(cinfo->regbase,
1870 info->var.bits_per_pixel,
1871 (image->dx * m) / 8, image->dy,
1872 (image->width * m) / 8,
1873 image->height,
1874 bg, bg,
1875 info->fix.line_length, 0x40);
1876 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001877 cirrusfb_RectFill(cinfo->regbase,
1878 info->var.bits_per_pixel,
1879 (image->dx * m) / 8, image->dy,
1880 (image->width * m) / 8, image->height,
1881 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001882 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001883 memcpy(info->screen_base, image->data, size);
1884 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885}
1886
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887#ifdef CONFIG_PPC_PREP
1888#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1889#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001890static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 *display = PREP_VIDEO_BASE;
1893 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894}
1895
1896#endif /* CONFIG_PPC_PREP */
1897
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001899static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900
1901/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1902 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1903 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1904 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001905static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1906 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907{
1908 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001909 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001911 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001912 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1913
1914 mem = ((SR14 & 7) + 1) << 20;
1915 } else {
1916 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1917 switch ((SRF & 0x18)) {
1918 case 0x08:
1919 mem = 512 * 1024;
1920 break;
1921 case 0x10:
1922 mem = 1024 * 1024;
1923 break;
1924 /* 64-bit DRAM data bus width; assume 2MB.
1925 * Also indicates 2MB memory on the 5430.
1926 */
1927 case 0x18:
1928 mem = 2048 * 1024;
1929 break;
1930 default:
1931 dev_warn(info->device, "Unknown memory size!\n");
1932 mem = 1024 * 1024;
1933 }
1934 /* If DRAM bank switching is enabled, there must be
1935 * twice as much memory installed. (4MB on the 5434)
1936 */
1937 if (SRF & 0x80)
1938 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001940
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 return mem;
1943}
1944
Krzysztof Helt8503df62007-10-16 01:29:08 -07001945static void get_pci_addrs(const struct pci_dev *pdev,
1946 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001948 assert(pdev != NULL);
1949 assert(display != NULL);
1950 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 *display = 0;
1953 *registers = 0;
1954
1955 /* This is a best-guess for now */
1956
1957 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1958 *display = pci_resource_start(pdev, 1);
1959 *registers = pci_resource_start(pdev, 0);
1960 } else {
1961 *display = pci_resource_start(pdev, 0);
1962 *registers = pci_resource_start(pdev, 1);
1963 }
1964
Krzysztof Helt8503df62007-10-16 01:29:08 -07001965 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966}
1967
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001968static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001970 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001971 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001973 if (cinfo->laguna_mmio == NULL)
1974 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001975 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976#if 0 /* if system didn't claim this region, we would... */
1977 release_mem_region(0xA0000, 65535);
1978#endif
1979 if (release_io_ports)
1980 release_region(0x3C0, 32);
1981 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982}
1983#endif /* CONFIG_PCI */
1984
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001986static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987{
Al Virod91f5bb2007-10-17 00:27:18 +01001988 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001989 struct zorro_dev *zdev = to_zorro_dev(info->device);
1990
1991 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992
1993 if (cinfo->btype == BT_PICASSO4) {
1994 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001995 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001996 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001998 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001999 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001}
2002#endif /* CONFIG_ZORRO */
2003
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002004/* function table of the above functions */
2005static struct fb_ops cirrusfb_ops = {
2006 .owner = THIS_MODULE,
2007 .fb_open = cirrusfb_open,
2008 .fb_release = cirrusfb_release,
2009 .fb_setcolreg = cirrusfb_setcolreg,
2010 .fb_check_var = cirrusfb_check_var,
2011 .fb_set_par = cirrusfb_set_par,
2012 .fb_pan_display = cirrusfb_pan_display,
2013 .fb_blank = cirrusfb_blank,
2014 .fb_fillrect = cirrusfb_fillrect,
2015 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07002016 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002017 .fb_imageblit = cirrusfb_imageblit,
2018};
2019
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002020static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002022 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 struct fb_var_screeninfo *var = &info->var;
2024
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 info->pseudo_palette = cinfo->pseudo_palette;
2026 info->flags = FBINFO_DEFAULT
2027 | FBINFO_HWACCEL_XPAN
2028 | FBINFO_HWACCEL_YPAN
2029 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002030 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002032 if (noaccel || is_laguna(cinfo))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 info->flags |= FBINFO_HWACCEL_DISABLED;
2034 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002035
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 if (cinfo->btype == BT_GD5480) {
2037 if (var->bits_per_pixel == 16)
2038 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002039 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 info->screen_base += 2 * MB_;
2041 }
2042
2043 /* Fill fix common fields */
2044 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2045 sizeof(info->fix.id));
2046
2047 /* monochrome: only 1 memory plane */
2048 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002049 info->fix.smem_len = info->screen_size;
2050 if (var->bits_per_pixel == 1)
2051 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 info->fix.xpanstep = 1;
2054 info->fix.ypanstep = 1;
2055 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056
2057 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 info->fix.mmio_len = 0;
2059 info->fix.accel = FB_ACCEL_NONE;
2060
2061 fb_alloc_cmap(&info->cmap, 256, 0);
2062
2063 return 0;
2064}
2065
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002066static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002068 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070
2071 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002072 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002074 /* set all the vital stuff */
2075 cirrusfb_set_fbinfo(info);
2076
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002077 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002079 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2080 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002081 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002082 err = -EINVAL;
2083 goto err_dealloc_cmap;
2084 }
2085
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 info->var.activate = FB_ACTIVATE_NOW;
2087
Krzysztof Helt99a45842009-03-31 15:25:09 -07002088 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 if (err < 0) {
2090 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002091 dev_dbg(info->device,
2092 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002093 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 }
2095
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 err = register_framebuffer(info);
2097 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002098 dev_err(info->device,
2099 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 goto err_dealloc_cmap;
2101 }
2102
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 return 0;
2104
2105err_dealloc_cmap:
2106 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 return err;
2108}
2109
Krzysztof Helt8503df62007-10-16 01:29:08 -07002110static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111{
2112 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113
Krzysztof Helt8503df62007-10-16 01:29:08 -07002114 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002115 unregister_framebuffer(info);
2116 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002117 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002118 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002119 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120}
2121
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002123static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2124 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125{
2126 struct cirrusfb_info *cinfo;
2127 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 unsigned long board_addr, board_size;
2129 int ret;
2130
2131 ret = pci_enable_device(pdev);
2132 if (ret < 0) {
2133 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2134 goto err_out;
2135 }
2136
2137 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2138 if (!info) {
2139 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2140 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002141 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 }
2143
2144 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002145 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002147 dev_dbg(info->device,
2148 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002149 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002150 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2151 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153 if (isPReP) {
2154 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002156 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002158 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002159 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002161 dev_dbg(info->device,
2162 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002163 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002164 /* FIXME: this forces VGA. alternatives? */
2165 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002166 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 }
2168
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002169 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002170 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002172 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002173 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174
2175 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002176 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002177 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2178 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 goto err_release_fb;
2180 }
2181#if 0 /* if the system didn't claim this region, we would... */
2182 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002183 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2184 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 ret = -EBUSY;
2186 goto err_release_regions;
2187 }
2188#endif
2189 if (request_region(0x3C0, 32, "cirrusfb"))
2190 release_io_ports = 1;
2191
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002192 info->screen_base = ioremap(board_addr, board_size);
2193 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 ret = -EIO;
2195 goto err_release_legacy;
2196 }
2197
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002198 info->fix.smem_start = board_addr;
2199 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 cinfo->unmap = cirrusfb_pci_unmap;
2201
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002202 dev_info(info->device,
2203 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2204 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 pci_set_drvdata(pdev, info);
2206
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002207 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002208 if (!ret)
2209 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002211 pci_set_drvdata(pdev, NULL);
2212 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213err_release_legacy:
2214 if (release_io_ports)
2215 release_region(0x3C0, 32);
2216#if 0
2217 release_mem_region(0xA0000, 65535);
2218err_release_regions:
2219#endif
2220 pci_release_regions(pdev);
2221err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002222 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002223 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225err_out:
2226 return ret;
2227}
2228
Krzysztof Helt8503df62007-10-16 01:29:08 -07002229static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230{
2231 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232
Krzysztof Helt8503df62007-10-16 01:29:08 -07002233 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234}
2235
2236static struct pci_driver cirrusfb_pci_driver = {
2237 .name = "cirrusfb",
2238 .id_table = cirrusfb_pci_table,
2239 .probe = cirrusfb_pci_register,
2240 .remove = __devexit_p(cirrusfb_pci_unregister),
2241#ifdef CONFIG_PM
2242#if 0
2243 .suspend = cirrusfb_pci_suspend,
2244 .resume = cirrusfb_pci_resume,
2245#endif
2246#endif
2247};
2248#endif /* CONFIG_PCI */
2249
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002251static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2252 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253{
2254 struct cirrusfb_info *cinfo;
2255 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002256 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 struct zorro_dev *z2 = NULL;
2258 unsigned long board_addr, board_size, size;
2259 int ret;
2260
2261 btype = ent->driver_data;
2262 if (cirrusfb_zorro_table2[btype].id2)
2263 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2264 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265
2266 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2267 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002268 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 ret = -ENOMEM;
2270 goto err_out;
2271 }
2272
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002273 dev_info(info->device, "%s board detected\n",
2274 cirrusfb_board_info[btype].name);
2275
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 cinfo->btype = btype;
2278
Al Viro36ea96a2007-10-27 19:46:58 +01002279 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002280 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282 board_addr = zorro_resource_start(z);
2283 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002284 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285
2286 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002287 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2288 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 ret = -EBUSY;
2290 goto err_release_fb;
2291 }
2292
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 ret = -EIO;
2294
2295 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002296 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297
2298 /* To be precise, for the P4 this is not the */
2299 /* begin of the board, but the begin of RAM. */
2300 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2301 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002302 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 if (!cinfo->regbase)
2304 goto err_release_region;
2305
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002306 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002307 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002309 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002311 info->fix.smem_start = board_addr + 16777216;
2312 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2313 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314 goto err_unmap_regbase;
2315 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002316 dev_info(info->device, " REG at $%lx\n",
2317 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002319 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002321 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002323 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2324 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325 goto err_release_region;
2326
2327 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002328 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002329 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002331 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002332 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 }
2334 cinfo->unmap = cirrusfb_zorro_unmap;
2335
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002336 dev_info(info->device,
2337 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2338 board_size / MB_, board_addr);
2339
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 zorro_set_drvdata(z, info);
2341
Al Virod91f5bb2007-10-17 00:27:18 +01002342 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002343 if (!ret)
2344 return 0;
2345
2346 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2347 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348
2349err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002350 if (btype == BT_PICASSO4)
2351 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352err_release_region:
2353 release_region(board_addr, board_size);
2354err_release_fb:
2355 framebuffer_release(info);
2356err_out:
2357 return ret;
2358}
2359
2360void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2361{
2362 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363
Krzysztof Helt8503df62007-10-16 01:29:08 -07002364 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365}
2366
2367static struct zorro_driver cirrusfb_zorro_driver = {
2368 .name = "cirrusfb",
2369 .id_table = cirrusfb_zorro_table,
2370 .probe = cirrusfb_zorro_register,
2371 .remove = __devexit_p(cirrusfb_zorro_unregister),
2372};
2373#endif /* CONFIG_ZORRO */
2374
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002376static int __init cirrusfb_setup(char *options)
2377{
Vlada Pericee119402008-11-19 15:36:45 -08002378 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380 if (!options || !*options)
2381 return 0;
2382
Krzysztof Helt8503df62007-10-16 01:29:08 -07002383 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002384 if (!*this_opt)
2385 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 if (!strcmp(this_opt, "noaccel"))
2388 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002389 else if (!strncmp(this_opt, "mode:", 5))
2390 mode_option = this_opt + 5;
2391 else
2392 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 }
2394 return 0;
2395}
2396#endif
2397
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 /*
2399 * Modularization
2400 */
2401
2402MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2403MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2404MODULE_LICENSE("GPL");
2405
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002406static int __init cirrusfb_init(void)
2407{
2408 int error = 0;
2409
2410#ifndef MODULE
2411 char *option = NULL;
2412
2413 if (fb_get_options("cirrusfb", &option))
2414 return -ENODEV;
2415 cirrusfb_setup(option);
2416#endif
2417
2418#ifdef CONFIG_ZORRO
2419 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2420#endif
2421#ifdef CONFIG_PCI
2422 error |= pci_register_driver(&cirrusfb_pci_driver);
2423#endif
2424 return error;
2425}
2426
Krzysztof Helt8503df62007-10-16 01:29:08 -07002427static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428{
2429#ifdef CONFIG_PCI
2430 pci_unregister_driver(&cirrusfb_pci_driver);
2431#endif
2432#ifdef CONFIG_ZORRO
2433 zorro_unregister_driver(&cirrusfb_zorro_driver);
2434#endif
2435}
2436
2437module_init(cirrusfb_init);
2438
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002439module_param(mode_option, charp, 0);
2440MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002441module_param(noaccel, bool, 0);
2442MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002443
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444#ifdef MODULE
2445module_exit(cirrusfb_exit);
2446#endif
2447
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448/**********************************************************************/
2449/* about the following functions - I have used the same names for the */
2450/* functions as Markus Wild did in his Retina driver for NetBSD as */
2451/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002452/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453/**********************************************************************/
2454
2455/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002456static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457 int regnum, unsigned char val)
2458{
2459 unsigned long regofs = 0;
2460
2461 if (cinfo->btype == BT_PICASSO) {
2462 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2464 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2466 regofs = 0xfff;
2467 }
2468
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470}
2471
2472/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474{
2475 unsigned long regofs = 0;
2476
2477 if (cinfo->btype == BT_PICASSO) {
2478 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002479/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2480 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2482 regofs = 0xfff;
2483 }
2484
Krzysztof Helt8503df62007-10-16 01:29:08 -07002485 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486}
2487
2488/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002489static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002491 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492
Krzysztof Helt8503df62007-10-16 01:29:08 -07002493 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 /* if we're just in "write value" mode, write back the */
2495 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496 vga_w(cinfo->regbase, VGA_ATT_IW,
2497 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 }
2499 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002500/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2501 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502
2503 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002504 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505}
2506
2507/*** WHDR() - write into the Hidden DAC register ***/
2508/* as the HDR is the only extension register that requires special treatment
2509 * (the other extension registers are accessible just like the "ordinary"
2510 * registers of their functional group) here is a specialized routine for
2511 * accessing the HDR
2512 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514{
2515 unsigned char dummy;
2516
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002517 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002518 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519 if (cinfo->btype == BT_PICASSO) {
2520 /* Klaus' hint for correct access to HDR on some boards */
2521 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002522 WGen(cinfo, VGA_PEL_MSK, 0x00);
2523 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002525 dummy = RGen(cinfo, VGA_PEL_IW);
2526 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527 }
2528 /* now do the usual stuff to access the HDR */
2529
Krzysztof Helt8503df62007-10-16 01:29:08 -07002530 dummy = RGen(cinfo, VGA_PEL_MSK);
2531 udelay(200);
2532 dummy = RGen(cinfo, VGA_PEL_MSK);
2533 udelay(200);
2534 dummy = RGen(cinfo, VGA_PEL_MSK);
2535 udelay(200);
2536 dummy = RGen(cinfo, VGA_PEL_MSK);
2537 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538
Krzysztof Helt8503df62007-10-16 01:29:08 -07002539 WGen(cinfo, VGA_PEL_MSK, val);
2540 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541
2542 if (cinfo->btype == BT_PICASSO) {
2543 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002544 dummy = RGen(cinfo, VGA_PEL_IW);
2545 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546
2547 /* and at the end, restore the mask value */
2548 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002549 WGen(cinfo, VGA_PEL_MSK, 0xff);
2550 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 }
2552}
2553
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002555static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556{
2557#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561#endif
2562}
2563
2564/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002565static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566{
2567#ifdef CONFIG_ZORRO
2568 /* writing an arbitrary value to this one causes the monitor switcher */
2569 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002570 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573#endif
2574}
2575
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002577static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 unsigned char green, unsigned char blue)
2579{
2580 unsigned int data = VGA_PEL_D;
2581
2582 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002583 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584
2585 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002586 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002587 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 /* but DAC data register IS, at least for Picasso II */
2589 if (cinfo->btype == BT_PICASSO)
2590 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002591 vga_w(cinfo->regbase, data, red);
2592 vga_w(cinfo->regbase, data, green);
2593 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002595 vga_w(cinfo->regbase, data, blue);
2596 vga_w(cinfo->regbase, data, green);
2597 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598 }
2599}
2600
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601#if 0
2602/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002603static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 unsigned char *green, unsigned char *blue)
2605{
2606 unsigned int data = VGA_PEL_D;
2607
Krzysztof Helt8503df62007-10-16 01:29:08 -07002608 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609
2610 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2611 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2612 if (cinfo->btype == BT_PICASSO)
2613 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002614 *red = vga_r(cinfo->regbase, data);
2615 *green = vga_r(cinfo->regbase, data);
2616 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002618 *blue = vga_r(cinfo->regbase, data);
2619 *green = vga_r(cinfo->regbase, data);
2620 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 }
2622}
2623#endif
2624
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625/*******************************************************************
2626 cirrusfb_WaitBLT()
2627
2628 Wait for the BitBLT engine to complete a possible earlier job
2629*********************************************************************/
2630
2631/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002632static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002634 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002635 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636}
2637
2638/*******************************************************************
2639 cirrusfb_BitBLT()
2640
2641 perform accelerated "scrolling"
2642********************************************************************/
2643
Krzysztof Helt8343c892009-03-31 15:25:11 -07002644static void cirrusfb_set_blitter(u8 __iomem *regbase,
2645 u_short nwidth, u_short nheight,
2646 u_long nsrc, u_long ndest,
2647 u_short bltmode, u_short line_length)
2648
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002651 /* dest pitch low */
2652 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2653 /* dest pitch hi */
2654 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2655 /* source pitch low */
2656 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2657 /* source pitch hi */
2658 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
2660 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002661 /* BLT width low */
2662 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2663 /* BLT width hi */
2664 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665
2666 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667 /* BLT height low */
2668 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2669 /* BLT width hi */
2670 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671
2672 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002673 /* BLT dest low */
2674 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2675 /* BLT dest mid */
2676 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2677 /* BLT dest hi */
2678 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679
2680 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002681 /* BLT src low */
2682 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2683 /* BLT src mid */
2684 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2685 /* BLT src hi */
2686 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687
2688 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002689 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002692 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693
2694 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002695 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002696}
2697
2698/*******************************************************************
2699 cirrusfb_BitBLT()
2700
2701 perform accelerated "scrolling"
2702********************************************************************/
2703
2704static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2705 u_short curx, u_short cury,
2706 u_short destx, u_short desty,
2707 u_short width, u_short height,
2708 u_short line_length)
2709{
2710 u_short nwidth = width - 1;
2711 u_short nheight = height - 1;
2712 u_long nsrc, ndest;
2713 u_char bltmode;
2714
2715 bltmode = 0x00;
2716 /* if source adr < dest addr, do the Blt backwards */
2717 if (cury <= desty) {
2718 if (cury == desty) {
2719 /* if src and dest are on the same line, check x */
2720 if (curx < destx)
2721 bltmode |= 0x01;
2722 } else
2723 bltmode |= 0x01;
2724 }
2725 /* standard case: forward blitting */
2726 nsrc = (cury * line_length) + curx;
2727 ndest = (desty * line_length) + destx;
2728 if (bltmode) {
2729 /* this means start addresses are at the end,
2730 * counting backwards
2731 */
2732 nsrc += nheight * line_length + nwidth;
2733 ndest += nheight * line_length + nwidth;
2734 }
2735
2736 cirrusfb_WaitBLT(regbase);
2737
2738 cirrusfb_set_blitter(regbase, nwidth, nheight,
2739 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740}
2741
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742/*******************************************************************
2743 cirrusfb_RectFill()
2744
2745 perform accelerated rectangle fill
2746********************************************************************/
2747
Krzysztof Helt8503df62007-10-16 01:29:08 -07002748static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002750 u32 fg_color, u32 bg_color, u_short line_length,
2751 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002753 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 u_char op;
2755
Krzysztof Helt8503df62007-10-16 01:29:08 -07002756 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 /* This is a ColorExpand Blt, using the */
2759 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002760 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2761 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762
Krzysztof Helt9e848062009-03-31 15:25:11 -07002763 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002764 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002765 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2766 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2767 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002768 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002769 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002770 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2771 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002772 op = 0xa0;
2773 }
2774 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002775 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2776 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2777 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002779 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002780 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781}
2782
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783/**************************************************************************
2784 * bestclock() - determine closest possible clock lower(?) than the
2785 * desired pixel clock
2786 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002787static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002789 int n, d;
2790 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791
Krzysztof Helt8503df62007-10-16 01:29:08 -07002792 assert(nom != NULL);
2793 assert(den != NULL);
2794 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795
2796 *nom = 0;
2797 *den = 0;
2798 *div = 0;
2799
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 if (freq < 8000)
2801 freq = 8000;
2802
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002803 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804
2805 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002806 int s = 0;
2807
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002808 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002810 int temp = d;
2811
2812 if (temp > 31) {
2813 s = 1;
2814 temp >>= 1;
2815 }
2816 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002817 h = h > freq ? h - freq : freq - h;
2818 if (h < diff) {
2819 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002821 *den = temp;
2822 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 }
2824 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002825 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002827 if (d > 31) {
2828 s = 1;
2829 d >>= 1;
2830 }
2831 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002832 h = h > freq ? h - freq : freq - h;
2833 if (h < diff) {
2834 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002836 *den = d;
2837 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 }
2839 }
2840 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841}
2842
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843/* -------------------------------------------------------------------------
2844 *
2845 * debugging functions
2846 *
2847 * -------------------------------------------------------------------------
2848 */
2849
2850#ifdef CIRRUSFB_DEBUG
2851
2852/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853 * cirrusfb_dbg_print_regs
2854 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2855 * @reg_class: type of registers to read: %CRT, or %SEQ
2856 *
2857 * DESCRIPTION:
2858 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2859 * old-style I/O ports are queried for information, otherwise MMIO is
2860 * used at the given @base address to query the information.
2861 */
2862
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002863static void cirrusfb_dbg_print_regs(struct fb_info *info,
2864 caddr_t regbase,
2865 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866{
2867 va_list list;
2868 unsigned char val = 0;
2869 unsigned reg;
2870 char *name;
2871
Krzysztof Helt8503df62007-10-16 01:29:08 -07002872 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873
Krzysztof Helt8503df62007-10-16 01:29:08 -07002874 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002876 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877
2878 switch (reg_class) {
2879 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002880 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881 break;
2882 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002883 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884 break;
2885 default:
2886 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002887 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 break;
2889 }
2890
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002891 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892
Krzysztof Helt8503df62007-10-16 01:29:08 -07002893 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 }
2895
Krzysztof Helt8503df62007-10-16 01:29:08 -07002896 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897}
2898
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 * cirrusfb_dbg_reg_dump
2901 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2902 *
2903 * DESCRIPTION:
2904 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2905 * old-style I/O ports are queried for information, otherwise MMIO is
2906 * used at the given @base address to query the information.
2907 */
2908
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002909static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002911 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002913 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 "CR00", 0x00,
2915 "CR01", 0x01,
2916 "CR02", 0x02,
2917 "CR03", 0x03,
2918 "CR04", 0x04,
2919 "CR05", 0x05,
2920 "CR06", 0x06,
2921 "CR07", 0x07,
2922 "CR08", 0x08,
2923 "CR09", 0x09,
2924 "CR0A", 0x0A,
2925 "CR0B", 0x0B,
2926 "CR0C", 0x0C,
2927 "CR0D", 0x0D,
2928 "CR0E", 0x0E,
2929 "CR0F", 0x0F,
2930 "CR10", 0x10,
2931 "CR11", 0x11,
2932 "CR12", 0x12,
2933 "CR13", 0x13,
2934 "CR14", 0x14,
2935 "CR15", 0x15,
2936 "CR16", 0x16,
2937 "CR17", 0x17,
2938 "CR18", 0x18,
2939 "CR22", 0x22,
2940 "CR24", 0x24,
2941 "CR26", 0x26,
2942 "CR2D", 0x2D,
2943 "CR2E", 0x2E,
2944 "CR2F", 0x2F,
2945 "CR30", 0x30,
2946 "CR31", 0x31,
2947 "CR32", 0x32,
2948 "CR33", 0x33,
2949 "CR34", 0x34,
2950 "CR35", 0x35,
2951 "CR36", 0x36,
2952 "CR37", 0x37,
2953 "CR38", 0x38,
2954 "CR39", 0x39,
2955 "CR3A", 0x3A,
2956 "CR3B", 0x3B,
2957 "CR3C", 0x3C,
2958 "CR3D", 0x3D,
2959 "CR3E", 0x3E,
2960 "CR3F", 0x3F,
2961 NULL);
2962
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002963 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002965 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002967 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 "SR00", 0x00,
2969 "SR01", 0x01,
2970 "SR02", 0x02,
2971 "SR03", 0x03,
2972 "SR04", 0x04,
2973 "SR08", 0x08,
2974 "SR09", 0x09,
2975 "SR0A", 0x0A,
2976 "SR0B", 0x0B,
2977 "SR0D", 0x0D,
2978 "SR10", 0x10,
2979 "SR11", 0x11,
2980 "SR12", 0x12,
2981 "SR13", 0x13,
2982 "SR14", 0x14,
2983 "SR15", 0x15,
2984 "SR16", 0x16,
2985 "SR17", 0x17,
2986 "SR18", 0x18,
2987 "SR19", 0x19,
2988 "SR1A", 0x1A,
2989 "SR1B", 0x1B,
2990 "SR1C", 0x1C,
2991 "SR1D", 0x1D,
2992 "SR1E", 0x1E,
2993 "SR1F", 0x1F,
2994 NULL);
2995
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002996 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002997}
2998
2999#endif /* CIRRUSFB_DEBUG */
3000