blob: e9a2661669eb556c77b578ef586f28b32a005979 [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;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700522 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
524 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700525 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 var->red.offset = 0;
527 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700528 var->green = var->red;
529 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 break;
531
532 case 8:
533 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700534 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700535 var->green = var->red;
536 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 break;
538
539 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700540 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 var->red.offset = 2;
542 var->green.offset = -3;
543 var->blue.offset = 8;
544 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700545 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 var->green.offset = 5;
547 var->blue.offset = 0;
548 }
549 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700550 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 var->blue.length = 5;
552 break;
553
Krzysztof Helt7cade312009-03-31 15:25:13 -0700554 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700555 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700556 var->red.offset = 0;
557 var->green.offset = 8;
558 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 } else {
560 var->red.offset = 16;
561 var->green.offset = 8;
562 var->blue.offset = 0;
563 }
564 var->red.length = 8;
565 var->green.length = 8;
566 var->blue.length = 8;
567 break;
568
569 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700570 dev_dbg(info->device,
571 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700572 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 /* should never occur */
574 break;
575 }
576
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700577 if (var->xres_virtual < var->xres)
578 var->xres_virtual = var->xres;
579 /* use highest possible virtual resolution */
580 if (var->yres_virtual == -1) {
581 var->yres_virtual = pixels / var->xres_virtual;
582
583 dev_info(info->device,
584 "virtual resolution set to maximum of %dx%d\n",
585 var->xres_virtual, var->yres_virtual);
586 }
587 if (var->yres_virtual < var->yres)
588 var->yres_virtual = var->yres;
589
590 if (var->xres_virtual * var->yres_virtual > pixels) {
591 dev_err(info->device, "mode %dx%dx%d rejected... "
592 "virtual resolution too high to fit into video memory!\n",
593 var->xres_virtual, var->yres_virtual,
594 var->bits_per_pixel);
595 return -EINVAL;
596 }
597
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700598 if (var->xoffset < 0)
599 var->xoffset = 0;
600 if (var->yoffset < 0)
601 var->yoffset = 0;
602
603 /* truncate xoffset and yoffset to maximum if too high */
604 if (var->xoffset > var->xres_virtual - var->xres)
605 var->xoffset = var->xres_virtual - var->xres - 1;
606 if (var->yoffset > var->yres_virtual - var->yres)
607 var->yoffset = var->yres_virtual - var->yres - 1;
608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 var->red.msb_right =
610 var->green.msb_right =
611 var->blue.msb_right =
612 var->transp.offset =
613 var->transp.length =
614 var->transp.msb_right = 0;
615
616 yres = var->yres;
617 if (var->vmode & FB_VMODE_DOUBLE)
618 yres *= 2;
619 else if (var->vmode & FB_VMODE_INTERLACED)
620 yres = (yres + 1) / 2;
621
622 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700623 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700624 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 return -EINVAL;
626 }
627
Krzysztof Helt99a45842009-03-31 15:25:09 -0700628 if (cirrusfb_check_pixclock(var, info))
629 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700631 if (!is_laguna(cinfo))
632 var->accel_flags = FB_ACCELF_TEXT;
633
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 return 0;
635}
636
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700637static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700639 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700640 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700641
Krzysztof Helt8503df62007-10-16 01:29:08 -0700642 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700643 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Krzysztof Helt486ff382008-10-15 22:03:42 -0700645 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700646 dev_dbg(info->device, "Set %s as pixclock source.\n",
647 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700648 old1f |= 0x40;
649 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
650 if (div == 2)
651 old1e |= 1;
652
653 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700655 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656}
657
658/*************************************************************************
659 cirrusfb_set_par_foo()
660
661 actually writes the values for a new video mode into the hardware,
662**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700663static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
665 struct cirrusfb_info *cinfo = info->par;
666 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 u8 __iomem *regbase = cinfo->regbase;
668 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700669 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700671 int hdispend, hsyncstart, hsyncend, htotal;
672 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700673 long freq;
674 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700675 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700677 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700679
680 switch (var->bits_per_pixel) {
681 case 1:
682 info->fix.line_length = var->xres_virtual / 8;
683 info->fix.visual = FB_VISUAL_MONO10;
684 break;
685
686 case 8:
687 info->fix.line_length = var->xres_virtual;
688 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
689 break;
690
691 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700692 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700693 info->fix.line_length = var->xres_virtual *
694 var->bits_per_pixel >> 3;
695 info->fix.visual = FB_VISUAL_TRUECOLOR;
696 break;
697 }
698 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700700 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 bi = &cirrusfb_board_info[cinfo->btype];
703
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700704 hsyncstart = var->xres + var->right_margin;
705 hsyncend = hsyncstart + var->hsync_len;
706 htotal = (hsyncend + var->left_margin) / 8 - 5;
707 hdispend = var->xres / 8 - 1;
708 hsyncstart = hsyncstart / 8 + 1;
709 hsyncend = hsyncend / 8 + 1;
710
711 yres = var->yres;
712 vsyncstart = yres + var->lower_margin;
713 vsyncend = vsyncstart + var->vsync_len;
714 vtotal = vsyncend + var->upper_margin;
715 vdispend = yres - 1;
716
717 if (var->vmode & FB_VMODE_DOUBLE) {
718 yres *= 2;
719 vsyncstart *= 2;
720 vsyncend *= 2;
721 vtotal *= 2;
722 } else if (var->vmode & FB_VMODE_INTERLACED) {
723 yres = (yres + 1) / 2;
724 vsyncstart = (vsyncstart + 1) / 2;
725 vsyncend = (vsyncend + 1) / 2;
726 vtotal = (vtotal + 1) / 2;
727 }
728
729 vtotal -= 2;
730 vsyncstart -= 1;
731 vsyncend -= 1;
732
733 if (yres >= 1024) {
734 vtotal /= 2;
735 vsyncstart /= 2;
736 vsyncend /= 2;
737 vdispend /= 2;
738 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700739 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700740 htotal /= 2;
741 hsyncstart /= 2;
742 hsyncend /= 2;
743 hdispend /= 2;
744 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700746 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
748 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700749 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700750 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700752 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700753 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700755 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700756 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Krzysztof Helt8503df62007-10-16 01:29:08 -0700758 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700759 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700760 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700761 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700763 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700766 tmp = hsyncend % 32;
767 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700769 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700770 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700772 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700773 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774
775 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700776 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700782 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700788 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700790 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700791 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700794 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 tmp |= 0x20;
796 if (var->vmode & FB_VMODE_DOUBLE)
797 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700798 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700799 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700801 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700804 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700805 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700807 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700808 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700810 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700811 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700813 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700814 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700816 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700817 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
819 tmp = 0;
820 if (var->vmode & FB_VMODE_INTERLACED)
821 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700822 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700826 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700828 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 tmp |= 128;
830
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700831 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700832 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700834 freq = PICOS2KHZ(var->pixclock);
Krzysztof Helt7cade312009-03-31 15:25:13 -0700835 if (cinfo->btype == BT_ALPINE && var->bits_per_pixel == 24)
836 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700837 if (cinfo->multiplexing)
838 freq /= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700839
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700840 bestclock(freq, &nom, &den, &div);
841
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700842 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
843 freq, nom, den, div);
844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 /* set VCLK0 */
846 /* hardware RefClock: 14.31818 MHz */
847 /* formula: VClk = (OSC * N) / (D * (1+P)) */
848 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
849
Krzysztof Helt527410f2009-03-31 15:25:13 -0700850 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700851 /* if freq is close to mclk or mclk/2 select mclk
852 * as clock source
853 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700854 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700855 if (divMCLK) {
856 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700857 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700858 }
859 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700860 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700861 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
862 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
863 unsigned short tile_control;
864
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700865 if (cinfo->btype == BT_LAGUNAB) {
866 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
867 tile_control &= ~0x80;
868 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
869 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700870
871 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
872 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
873 control = fb_readw(cinfo->laguna_mmio + 0x402);
874 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
875 control &= ~0x6800;
876 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700877 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700878 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700879 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700880 tmp = den << 1;
881 if (div != 0)
882 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700883 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
884 if ((cinfo->btype == BT_SD64) ||
885 (cinfo->btype == BT_ALPINE) ||
886 (cinfo->btype == BT_GD5480))
887 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700889 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
890 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700891 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700892 vga_wseq(regbase, CL_SEQRE, tmp);
893 vga_wseq(regbase, CL_SEQR1E, nom);
894 } else {
895 vga_wseq(regbase, CL_SEQRB, nom);
896 vga_wseq(regbase, CL_SEQR1B, tmp);
897 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700898 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700900 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700902 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 else
904 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
905 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700906 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 /* don't know if it would hurt to also program this if no interlaced */
909 /* mode is used, but I feel better this way.. :-) */
910 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700911 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700913 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 /* enable display memory & CRTC I/O address for color mode */
917 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
919 tmp |= 0x40;
920 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
921 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700922 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700923 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700924 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
Krzysztof Helt8503df62007-10-16 01:29:08 -0700926 /* text cursor on and start line */
927 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
928 /* text cursor end line */
929 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
931 /******************************************************
932 *
933 * 1 bpp
934 *
935 */
936
937 /* programming for different color depths */
938 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700939 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700940 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
942 /* SR07 */
943 switch (cinfo->btype) {
944 case BT_SD64:
945 case BT_PICCOLO:
946 case BT_PICASSO:
947 case BT_SPECTRUM:
948 case BT_PICASSO4:
949 case BT_ALPINE:
950 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700951 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700952 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 bi->sr07_1bpp_mux : bi->sr07_1bpp);
954 break;
955
956 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700957 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700958 vga_wseq(regbase, CL_SEQR7,
959 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 break;
961
962 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700963 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 break;
965 }
966
967 /* Extended Sequencer Mode */
968 switch (cinfo->btype) {
969 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700970 /* setting the SEQRF on SD64 is not necessary
971 * (only during init)
972 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700973 /* MCLK select */
974 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 break;
976
977 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700978 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 /* ### ueberall 0x22? */
980 /* ##vorher 1c MCLK select */
981 vga_wseq(regbase, CL_SEQR1F, 0x22);
982 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
983 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 break;
985
986 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700987 /* ##vorher 22 MCLK select */
988 vga_wseq(regbase, CL_SEQR1F, 0x22);
989 /* ## vorher d0 avoid FIFO underruns..? */
990 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 break;
992
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 case BT_PICASSO4:
994 case BT_ALPINE:
995 case BT_GD5480:
996 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700997 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 /* do nothing */
999 break;
1000
1001 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001002 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 break;
1004 }
1005
Krzysztof Helt8503df62007-10-16 01:29:08 -07001006 /* pixel mask: pass-through for first plane */
1007 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001008 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001009 /* hidden dac reg: 1280x1024 */
1010 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001012 /* hidden dac: nothing */
1013 WHDR(cinfo, 0);
1014 /* memory mode: odd/even, ext. memory */
1015 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1016 /* plane mask: only write to first plane */
1017 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 }
1019
1020 /******************************************************
1021 *
1022 * 8 bpp
1023 *
1024 */
1025
1026 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001027 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 switch (cinfo->btype) {
1029 case BT_SD64:
1030 case BT_PICCOLO:
1031 case BT_PICASSO:
1032 case BT_SPECTRUM:
1033 case BT_PICASSO4:
1034 case BT_ALPINE:
1035 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001036 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001037 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1039 break;
1040
1041 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001042 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001043 vga_wseq(regbase, CL_SEQR7,
1044 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001045 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 break;
1047
1048 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001049 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 break;
1051 }
1052
1053 switch (cinfo->btype) {
1054 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001055 /* MCLK select */
1056 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 break;
1058
1059 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001062 /* ### vorher 1c MCLK select */
1063 vga_wseq(regbase, CL_SEQR1F, 0x22);
1064 /* Fast Page-Mode writes */
1065 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 break;
1067
1068 case BT_PICASSO4:
1069#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* ### INCOMPLETE!! */
1071 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 /* We already set SRF and SR1F */
1075 break;
1076
1077 case BT_GD5480:
1078 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001079 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 /* do nothing */
1081 break;
1082
1083 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001084 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 break;
1086 }
1087
Krzysztof Helt8503df62007-10-16 01:29:08 -07001088 /* mode register: 256 color mode */
1089 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001090 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001091 /* hidden dac reg: 1280x1024 */
1092 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001094 /* hidden dac: nothing */
1095 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 }
1097
1098 /******************************************************
1099 *
1100 * 16 bpp
1101 *
1102 */
1103
1104 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001105 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 switch (cinfo->btype) {
1107 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108 /* Extended Sequencer Mode: 256c col. mode */
1109 vga_wseq(regbase, CL_SEQR7, 0xf7);
1110 /* MCLK select */
1111 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 break;
1113
1114 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001115 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001116 vga_wseq(regbase, CL_SEQR7, 0x87);
1117 /* Fast Page-Mode writes */
1118 vga_wseq(regbase, CL_SEQRF, 0xb0);
1119 /* MCLK select */
1120 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 break;
1122
1123 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001124 vga_wseq(regbase, CL_SEQR7, 0x27);
1125 /* Fast Page-Mode writes */
1126 vga_wseq(regbase, CL_SEQRF, 0xb0);
1127 /* MCLK select */
1128 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 break;
1130
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001133 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 break;
1135
1136 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001137 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 /* We already set SRF and SR1F */
1139 break;
1140
1141 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001142 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001143 vga_wseq(regbase, CL_SEQR7,
1144 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001145 control |= 0x2000;
1146 format |= 0x1400;
1147 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 break;
1149
1150 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001151 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 break;
1153 }
1154
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 /* mode register: 256 color mode */
1156 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001158 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159#elif defined(CONFIG_ZORRO)
1160 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001161 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 }
1164
1165 /******************************************************
1166 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001167 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 *
1169 */
1170
Krzysztof Helt7cade312009-03-31 15:25:13 -07001171 else if (var->bits_per_pixel == 24) {
1172 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 switch (cinfo->btype) {
1174 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001175 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001176 vga_wseq(regbase, CL_SEQR7, 0xf5);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001177 /* MCLK select */
1178 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 break;
1180
1181 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001182 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001183 vga_wseq(regbase, CL_SEQR7, 0x85);
1184 /* Fast Page-Mode writes */
1185 vga_wseq(regbase, CL_SEQRF, 0xb0);
1186 /* MCLK select */
1187 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 break;
1189
1190 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001191 vga_wseq(regbase, CL_SEQR7, 0x25);
1192 /* Fast Page-Mode writes */
1193 vga_wseq(regbase, CL_SEQRF, 0xb0);
1194 /* MCLK select */
1195 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 break;
1197
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 case BT_ALPINE:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001200 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 break;
1202
1203 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001204 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 /* We already set SRF and SR1F */
1206 break;
1207
1208 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001209 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 vga_wseq(regbase, CL_SEQR7,
1211 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001212 control |= 0x4000;
1213 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001214 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 break;
1216
1217 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001218 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 break;
1220 }
1221
Krzysztof Helt8503df62007-10-16 01:29:08 -07001222 /* mode register: 256 color mode */
1223 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001224 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1225 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 }
1227
1228 /******************************************************
1229 *
1230 * unknown/unsupported bpp
1231 *
1232 */
1233
Krzysztof Helt8503df62007-10-16 01:29:08 -07001234 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001235 dev_err(info->device,
1236 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
Krzysztof Helt6683e012009-03-31 15:25:06 -07001239 pitch = info->fix.line_length >> 3;
1240 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001242 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 tmp |= 0x10; /* offset overflow bit */
1244
Krzysztof Helt8503df62007-10-16 01:29:08 -07001245 /* screen start addr #16-18, fastpagemode cycles */
1246 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001248 /* screen start address bit 19 */
1249 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001250 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001252 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001253 tmp = 0;
1254 if ((htotal + 5) & 256)
1255 tmp |= 128;
1256 if (hdispend & 256)
1257 tmp |= 64;
1258 if (hsyncstart & 256)
1259 tmp |= 48;
1260 if (vtotal & 1024)
1261 tmp |= 8;
1262 if (vdispend & 1024)
1263 tmp |= 4;
1264 if (vsyncstart & 1024)
1265 tmp |= 3;
1266
1267 vga_wcrt(regbase, CL_CRT1E, tmp);
1268 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1269 }
1270
Krzysztof Helt8503df62007-10-16 01:29:08 -07001271 /* pixel panning */
1272 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
1274 /* [ EGS: SetOffset(); ] */
1275 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001276 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001278 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001279 /* no tiles */
1280 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1281 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1282 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1283 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 /* finally, turn on everything - turn off "FullBandwidth" bit */
1285 /* also, set "DotClock%2" bit where requested */
1286 tmp = 0x01;
1287
1288/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1289 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1290 tmp |= 0x08;
1291*/
1292
Krzysztof Helt8503df62007-10-16 01:29:08 -07001293 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001294 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001297 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298#endif
1299
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 return 0;
1301}
1302
1303/* for some reason incomprehensible to me, cirrusfb requires that you write
1304 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001305static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001307 cirrusfb_set_par_foo(info);
1308 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309}
1310
Krzysztof Helt8503df62007-10-16 01:29:08 -07001311static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1312 unsigned blue, unsigned transp,
1313 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314{
1315 struct cirrusfb_info *cinfo = info->par;
1316
1317 if (regno > 255)
1318 return -EINVAL;
1319
1320 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1321 u32 v;
1322 red >>= (16 - info->var.red.length);
1323 green >>= (16 - info->var.green.length);
1324 blue >>= (16 - info->var.blue.length);
1325
Krzysztof Helt8503df62007-10-16 01:29:08 -07001326 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 return 1;
1328 v = (red << info->var.red.offset) |
1329 (green << info->var.green.offset) |
1330 (blue << info->var.blue.offset);
1331
Krzysztof Helt060b6002007-10-16 01:29:13 -07001332 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 return 0;
1334 }
1335
Krzysztof Helt8503df62007-10-16 01:29:08 -07001336 if (info->var.bits_per_pixel == 8)
1337 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
1339 return 0;
1340
1341}
1342
1343/*************************************************************************
1344 cirrusfb_pan_display()
1345
1346 performs display panning - provided hardware permits this
1347**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001348static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1349 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001351 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001353 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 struct cirrusfb_info *cinfo = info->par;
1355
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001356 dev_dbg(info->device,
1357 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358
1359 /* no range checks for xoffset and yoffset, */
1360 /* as fb_pan_display has already done this */
1361 if (var->vmode & FB_VMODE_YWRAP)
1362 return -EINVAL;
1363
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365
Krzysztof Helt99a45842009-03-31 15:25:09 -07001366 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
1368 if (info->var.bits_per_pixel == 1) {
1369 /* base is already correct */
1370 xpix = (unsigned char) (var->xoffset % 8);
1371 } else {
1372 base /= 4;
1373 xpix = (unsigned char) ((xoffset % 4) * 2);
1374 }
1375
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001376 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001377 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
1379 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001380 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1381 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001383 /* 0xf2 is %11110010, exclude tmp bits */
1384 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 /* construct bits 16, 17 and 18 of screen start address */
1386 if (base & 0x10000)
1387 tmp |= 0x01;
1388 if (base & 0x20000)
1389 tmp |= 0x04;
1390 if (base & 0x40000)
1391 tmp |= 0x08;
1392
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001393 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
1395 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001396 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001397 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1398 if (is_laguna(cinfo))
1399 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1400 else
1401 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001402 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1403 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 /* write pixel panning value to AR33; this does not quite work in 8bpp
1406 *
1407 * ### Piccolo..? Will this work?
1408 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001410 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411
Krzysztof Helt8503df62007-10-16 01:29:08 -07001412 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413}
1414
Krzysztof Helt8503df62007-10-16 01:29:08 -07001415static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416{
1417 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001418 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1419 * then the caller blanks by setting the CLUT (Color Look Up Table)
1420 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1421 * failed due to e.g. a video mode which doesn't support it.
1422 * Implements VESA suspend and powerdown modes on hardware that
1423 * supports disabling hsync/vsync:
1424 * blank_mode == 2: suspend vsync
1425 * blank_mode == 3: suspend hsync
1426 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 */
1428 unsigned char val;
1429 struct cirrusfb_info *cinfo = info->par;
1430 int current_mode = cinfo->blank_mode;
1431
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001432 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
1434 if (info->state != FBINFO_STATE_RUNNING ||
1435 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001436 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 return 0;
1438 }
1439
1440 /* Undo current */
1441 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001442 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001443 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001444 val = 0;
1445 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001446 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001447 val = 0x20;
1448
1449 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1450 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451
1452 switch (blank_mode) {
1453 case FB_BLANK_UNBLANK:
1454 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001455 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 break;
1457 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001458 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 break;
1460 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001461 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 break;
1463 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001464 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 break;
1466 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001467 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 return 1;
1469 }
1470
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001471 vga_wgfx(cinfo->regbase, CL_GRE, val);
1472
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001474 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475
1476 /* Let fbcon do a soft blank for us */
1477 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1478}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001479
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480/**** END Hardware specific Routines **************************************/
1481/****************************************************************************/
1482/**** BEGIN Internal Routines ***********************************************/
1483
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001484static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001486 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 const struct cirrusfb_board_info_rec *bi;
1488
Krzysztof Helt8503df62007-10-16 01:29:08 -07001489 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
1491 bi = &cirrusfb_board_info[cinfo->btype];
1492
1493 /* reset board globally */
1494 switch (cinfo->btype) {
1495 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001496 WSFR(cinfo, 0x01);
1497 udelay(500);
1498 WSFR(cinfo, 0x51);
1499 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 break;
1501 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001502 WSFR2(cinfo, 0xff);
1503 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 break;
1505 case BT_SD64:
1506 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001507 WSFR(cinfo, 0x1f);
1508 udelay(500);
1509 WSFR(cinfo, 0x4f);
1510 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 break;
1512 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 /* disable flickerfixer */
1514 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1515 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001516 /* mode */
1517 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001518 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001519 /* from Klaus' NetBSD driver: */
1520 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001521 case BT_ALPINE: /* fall through */
1522 /* put blitter into 542x compat */
1523 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 break;
1525
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001526 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001527 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 /* Nothing to do to reset the board. */
1529 break;
1530
1531 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001532 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 break;
1534 }
1535
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001536 /* make sure RAM size set by this point */
1537 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
1539 /* the P4 is not fully initialized here; I rely on it having been */
1540 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001541 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542
1543 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001544 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1545 WGen(cinfo, CL_POS102, 0x01);
1546 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
1548 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001549 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
Krzysztof Helt8503df62007-10-16 01:29:08 -07001551 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001552 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Krzysztof Helt8503df62007-10-16 01:29:08 -07001554 /* FullBandwidth (video off) and 8/9 dot clock */
1555 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 /* "magic cookie" - doesn't make any sense to me.. */
1558/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1559 /* unlock all extension registers */
1560 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 switch (cinfo->btype) {
1563 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001564 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 break;
1566 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001567 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001568 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 break;
1570 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 break;
1573 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001574 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1575 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 break;
1577 }
1578 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001579 /* plane mask: nothing */
1580 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1581 /* character map select: doesn't even matter in gx mode */
1582 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001583 /* memory mode: chain4, ext. memory */
1584 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585
1586 /* controller-internal base address of video memory */
1587 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001588 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1591 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
Krzysztof Helt8503df62007-10-16 01:29:08 -07001593 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1594 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1595 /* graphics cursor Y position (..."... ) */
1596 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1597 /* graphics cursor attributes */
1598 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1599 /* graphics cursor pattern address */
1600 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601
1602 /* writing these on a P4 might give problems.. */
1603 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001604 /* configuration readback and ext. color */
1605 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1606 /* signature generator */
1607 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 }
1609
1610 /* MCLK select etc. */
1611 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001612 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Krzysztof Helt8503df62007-10-16 01:29:08 -07001614 /* Screen A preset row scan: none */
1615 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1616 /* Text cursor start: disable text cursor */
1617 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1618 /* Text cursor end: - */
1619 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001620 /* text cursor location high: 0 */
1621 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1622 /* text cursor location low: 0 */
1623 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
Krzysztof Helt8503df62007-10-16 01:29:08 -07001625 /* Underline Row scanline: - */
1626 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001628 /* ext. display controls: ext.adr. wrap */
1629 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630
Krzysztof Helt8503df62007-10-16 01:29:08 -07001631 /* Set/Reset registes: - */
1632 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1633 /* Set/Reset enable: - */
1634 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1635 /* Color Compare: - */
1636 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1637 /* Data Rotate: - */
1638 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1639 /* Read Map Select: - */
1640 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1641 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1642 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1643 /* Miscellaneous: memory map base address, graphics mode */
1644 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1645 /* Color Don't care: involve all planes */
1646 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1647 /* Bit Mask: no mask at all */
1648 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001649
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001650 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 /* (5434 can't have bit 3 set for bitblt) */
1652 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001654 /* Graphics controller mode extensions: finer granularity,
1655 * 8byte data latches
1656 */
1657 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1660 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1661 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1662 /* Background color byte 1: - */
1663 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1664 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
Krzysztof Helt8503df62007-10-16 01:29:08 -07001666 /* Attribute Controller palette registers: "identity mapping" */
1667 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1668 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1669 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1670 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1671 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1672 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1673 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1674 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1675 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1676 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1677 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1678 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1679 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1680 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1681 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1682 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 /* Attribute Controller mode: graphics mode */
1685 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1686 /* Overscan color reg.: reg. 0 */
1687 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1688 /* Color Plane enable: Enable all 4 planes */
1689 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001690 /* Color Select: - */
1691 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694
Krzysztof Helt8503df62007-10-16 01:29:08 -07001695 /* BLT Start/status: Blitter reset */
1696 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1697 /* - " - : "end-of-reset" */
1698 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699
1700 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 return;
1703}
1704
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706{
1707#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1708 static int IsOn = 0; /* XXX not ok for multiple boards */
1709
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 if (cinfo->btype == BT_PICASSO4)
1711 return; /* nothing to switch */
1712 if (cinfo->btype == BT_ALPINE)
1713 return; /* nothing to switch */
1714 if (cinfo->btype == BT_GD5480)
1715 return; /* nothing to switch */
1716 if (cinfo->btype == BT_PICASSO) {
1717 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001718 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719 return;
1720 }
1721 if (on) {
1722 switch (cinfo->btype) {
1723 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 break;
1726 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001727 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 break;
1729 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001730 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 break;
1732 default: /* do nothing */ break;
1733 }
1734 } else {
1735 switch (cinfo->btype) {
1736 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001737 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 break;
1739 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001740 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 break;
1742 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001743 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001745 default: /* do nothing */
1746 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 }
1748 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749#endif /* CONFIG_ZORRO */
1750}
1751
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752/******************************************/
1753/* Linux 2.6-style accelerated functions */
1754/******************************************/
1755
Krzysztof Helt8343c892009-03-31 15:25:11 -07001756static int cirrusfb_sync(struct fb_info *info)
1757{
1758 struct cirrusfb_info *cinfo = info->par;
1759
1760 if (!is_laguna(cinfo)) {
1761 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1762 cpu_relax();
1763 }
1764 return 0;
1765}
1766
Krzysztof Helt8503df62007-10-16 01:29:08 -07001767static void cirrusfb_fillrect(struct fb_info *info,
1768 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 struct fb_fillrect modded;
1771 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001772 struct cirrusfb_info *cinfo = info->par;
1773 int m = info->var.bits_per_pixel;
1774 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1775 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776
1777 if (info->state != FBINFO_STATE_RUNNING)
1778 return;
1779 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1780 cfb_fillrect(info, region);
1781 return;
1782 }
1783
1784 vxres = info->var.xres_virtual;
1785 vyres = info->var.yres_virtual;
1786
1787 memcpy(&modded, region, sizeof(struct fb_fillrect));
1788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 modded.dx >= vxres || modded.dy >= vyres)
1791 return;
1792
Krzysztof Helt8503df62007-10-16 01:29:08 -07001793 if (modded.dx + modded.width > vxres)
1794 modded.width = vxres - modded.dx;
1795 if (modded.dy + modded.height > vyres)
1796 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797
Krzysztof Helt060b6002007-10-16 01:29:13 -07001798 cirrusfb_RectFill(cinfo->regbase,
1799 info->var.bits_per_pixel,
1800 (region->dx * m) / 8, region->dy,
1801 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001802 color, color,
1803 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804}
1805
Krzysztof Helt8503df62007-10-16 01:29:08 -07001806static void cirrusfb_copyarea(struct fb_info *info,
1807 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 struct fb_copyarea modded;
1810 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001811 struct cirrusfb_info *cinfo = info->par;
1812 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813
1814 if (info->state != FBINFO_STATE_RUNNING)
1815 return;
1816 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1817 cfb_copyarea(info, area);
1818 return;
1819 }
1820
1821 vxres = info->var.xres_virtual;
1822 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001823 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824
Krzysztof Helt8503df62007-10-16 01:29:08 -07001825 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 modded.sx >= vxres || modded.sy >= vyres ||
1827 modded.dx >= vxres || modded.dy >= vyres)
1828 return;
1829
Krzysztof Helt8503df62007-10-16 01:29:08 -07001830 if (modded.sx + modded.width > vxres)
1831 modded.width = vxres - modded.sx;
1832 if (modded.dx + modded.width > vxres)
1833 modded.width = vxres - modded.dx;
1834 if (modded.sy + modded.height > vyres)
1835 modded.height = vyres - modded.sy;
1836 if (modded.dy + modded.height > vyres)
1837 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838
Krzysztof Helt060b6002007-10-16 01:29:13 -07001839 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1840 (area->sx * m) / 8, area->sy,
1841 (area->dx * m) / 8, area->dy,
1842 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001843 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001844
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845}
1846
Krzysztof Helt8503df62007-10-16 01:29:08 -07001847static void cirrusfb_imageblit(struct fb_info *info,
1848 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849{
1850 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001851 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852
Krzysztof Helt9e848062009-03-31 15:25:11 -07001853 if (info->state != FBINFO_STATE_RUNNING)
1854 return;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001855 /* Alpine acceleration does not work at 24bpp ?!? */
1856 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1 ||
1857 (cinfo->btype == BT_ALPINE && op == 0xc))
Krzysztof Helt9e848062009-03-31 15:25:11 -07001858 cfb_imageblit(info, image);
1859 else {
1860 unsigned size = ((image->width + 7) >> 3) * image->height;
1861 int m = info->var.bits_per_pixel;
1862 u32 fg, bg;
1863
1864 if (info->var.bits_per_pixel == 8) {
1865 fg = image->fg_color;
1866 bg = image->bg_color;
1867 } else {
1868 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1869 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1870 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001871 if (info->var.bits_per_pixel == 24) {
1872 /* clear background first */
1873 cirrusfb_RectFill(cinfo->regbase,
1874 info->var.bits_per_pixel,
1875 (image->dx * m) / 8, image->dy,
1876 (image->width * m) / 8,
1877 image->height,
1878 bg, bg,
1879 info->fix.line_length, 0x40);
1880 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001881 cirrusfb_RectFill(cinfo->regbase,
1882 info->var.bits_per_pixel,
1883 (image->dx * m) / 8, image->dy,
1884 (image->width * m) / 8, image->height,
1885 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001886 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001887 memcpy(info->screen_base, image->data, size);
1888 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889}
1890
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891#ifdef CONFIG_PPC_PREP
1892#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1893#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001894static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 *display = PREP_VIDEO_BASE;
1897 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898}
1899
1900#endif /* CONFIG_PPC_PREP */
1901
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001903static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904
1905/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1906 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1907 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1908 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001909static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1910 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911{
1912 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001913 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001915 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001916 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1917
1918 mem = ((SR14 & 7) + 1) << 20;
1919 } else {
1920 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1921 switch ((SRF & 0x18)) {
1922 case 0x08:
1923 mem = 512 * 1024;
1924 break;
1925 case 0x10:
1926 mem = 1024 * 1024;
1927 break;
1928 /* 64-bit DRAM data bus width; assume 2MB.
1929 * Also indicates 2MB memory on the 5430.
1930 */
1931 case 0x18:
1932 mem = 2048 * 1024;
1933 break;
1934 default:
1935 dev_warn(info->device, "Unknown memory size!\n");
1936 mem = 1024 * 1024;
1937 }
1938 /* If DRAM bank switching is enabled, there must be
1939 * twice as much memory installed. (4MB on the 5434)
1940 */
1941 if (SRF & 0x80)
1942 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001944
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 return mem;
1947}
1948
Krzysztof Helt8503df62007-10-16 01:29:08 -07001949static void get_pci_addrs(const struct pci_dev *pdev,
1950 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001952 assert(pdev != NULL);
1953 assert(display != NULL);
1954 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 *display = 0;
1957 *registers = 0;
1958
1959 /* This is a best-guess for now */
1960
1961 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1962 *display = pci_resource_start(pdev, 1);
1963 *registers = pci_resource_start(pdev, 0);
1964 } else {
1965 *display = pci_resource_start(pdev, 0);
1966 *registers = pci_resource_start(pdev, 1);
1967 }
1968
Krzysztof Helt8503df62007-10-16 01:29:08 -07001969 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970}
1971
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001972static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001974 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001975 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001977 if (cinfo->laguna_mmio == NULL)
1978 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001979 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980#if 0 /* if system didn't claim this region, we would... */
1981 release_mem_region(0xA0000, 65535);
1982#endif
1983 if (release_io_ports)
1984 release_region(0x3C0, 32);
1985 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986}
1987#endif /* CONFIG_PCI */
1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001990static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991{
Al Virod91f5bb2007-10-17 00:27:18 +01001992 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001993 struct zorro_dev *zdev = to_zorro_dev(info->device);
1994
1995 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996
1997 if (cinfo->btype == BT_PICASSO4) {
1998 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001999 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002000 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002002 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002003 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005}
2006#endif /* CONFIG_ZORRO */
2007
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002008/* function table of the above functions */
2009static struct fb_ops cirrusfb_ops = {
2010 .owner = THIS_MODULE,
2011 .fb_open = cirrusfb_open,
2012 .fb_release = cirrusfb_release,
2013 .fb_setcolreg = cirrusfb_setcolreg,
2014 .fb_check_var = cirrusfb_check_var,
2015 .fb_set_par = cirrusfb_set_par,
2016 .fb_pan_display = cirrusfb_pan_display,
2017 .fb_blank = cirrusfb_blank,
2018 .fb_fillrect = cirrusfb_fillrect,
2019 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07002020 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002021 .fb_imageblit = cirrusfb_imageblit,
2022};
2023
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002024static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002026 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 struct fb_var_screeninfo *var = &info->var;
2028
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 info->pseudo_palette = cinfo->pseudo_palette;
2030 info->flags = FBINFO_DEFAULT
2031 | FBINFO_HWACCEL_XPAN
2032 | FBINFO_HWACCEL_YPAN
2033 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002034 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002036 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002038 info->fix.accel = FB_ACCEL_NONE;
2039 } else
2040 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
2041
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002043
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044 if (cinfo->btype == BT_GD5480) {
2045 if (var->bits_per_pixel == 16)
2046 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002047 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 info->screen_base += 2 * MB_;
2049 }
2050
2051 /* Fill fix common fields */
2052 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2053 sizeof(info->fix.id));
2054
2055 /* monochrome: only 1 memory plane */
2056 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002057 info->fix.smem_len = info->screen_size;
2058 if (var->bits_per_pixel == 1)
2059 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 info->fix.xpanstep = 1;
2062 info->fix.ypanstep = 1;
2063 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064
2065 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067
2068 fb_alloc_cmap(&info->cmap, 256, 0);
2069
2070 return 0;
2071}
2072
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002073static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002075 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
2078 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002079 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002081 /* set all the vital stuff */
2082 cirrusfb_set_fbinfo(info);
2083
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002084 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002086 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2087 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002088 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002089 err = -EINVAL;
2090 goto err_dealloc_cmap;
2091 }
2092
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 info->var.activate = FB_ACTIVATE_NOW;
2094
Krzysztof Helt99a45842009-03-31 15:25:09 -07002095 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 if (err < 0) {
2097 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002098 dev_dbg(info->device,
2099 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002100 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 }
2102
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 err = register_framebuffer(info);
2104 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002105 dev_err(info->device,
2106 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 goto err_dealloc_cmap;
2108 }
2109
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 return 0;
2111
2112err_dealloc_cmap:
2113 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 return err;
2115}
2116
Krzysztof Helt8503df62007-10-16 01:29:08 -07002117static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118{
2119 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120
Krzysztof Helt8503df62007-10-16 01:29:08 -07002121 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002122 unregister_framebuffer(info);
2123 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002124 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002125 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002126 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127}
2128
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002130static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2131 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132{
2133 struct cirrusfb_info *cinfo;
2134 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 unsigned long board_addr, board_size;
2136 int ret;
2137
2138 ret = pci_enable_device(pdev);
2139 if (ret < 0) {
2140 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2141 goto err_out;
2142 }
2143
2144 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2145 if (!info) {
2146 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2147 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002148 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 }
2150
2151 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002152 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002154 dev_dbg(info->device,
2155 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002156 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002157 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2158 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159
Krzysztof Helt8503df62007-10-16 01:29:08 -07002160 if (isPReP) {
2161 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002163 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002165 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002166 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002168 dev_dbg(info->device,
2169 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002170 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002171 /* FIXME: this forces VGA. alternatives? */
2172 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002173 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 }
2175
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002176 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002177 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002179 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002180 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181
2182 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002183 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002184 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2185 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186 goto err_release_fb;
2187 }
2188#if 0 /* if the system didn't claim this region, we would... */
2189 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002190 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2191 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 ret = -EBUSY;
2193 goto err_release_regions;
2194 }
2195#endif
2196 if (request_region(0x3C0, 32, "cirrusfb"))
2197 release_io_ports = 1;
2198
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002199 info->screen_base = ioremap(board_addr, board_size);
2200 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 ret = -EIO;
2202 goto err_release_legacy;
2203 }
2204
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002205 info->fix.smem_start = board_addr;
2206 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 cinfo->unmap = cirrusfb_pci_unmap;
2208
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002209 dev_info(info->device,
2210 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2211 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 pci_set_drvdata(pdev, info);
2213
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002214 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002215 if (!ret)
2216 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002218 pci_set_drvdata(pdev, NULL);
2219 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220err_release_legacy:
2221 if (release_io_ports)
2222 release_region(0x3C0, 32);
2223#if 0
2224 release_mem_region(0xA0000, 65535);
2225err_release_regions:
2226#endif
2227 pci_release_regions(pdev);
2228err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002229 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002230 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232err_out:
2233 return ret;
2234}
2235
Krzysztof Helt8503df62007-10-16 01:29:08 -07002236static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237{
2238 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239
Krzysztof Helt8503df62007-10-16 01:29:08 -07002240 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241}
2242
2243static struct pci_driver cirrusfb_pci_driver = {
2244 .name = "cirrusfb",
2245 .id_table = cirrusfb_pci_table,
2246 .probe = cirrusfb_pci_register,
2247 .remove = __devexit_p(cirrusfb_pci_unregister),
2248#ifdef CONFIG_PM
2249#if 0
2250 .suspend = cirrusfb_pci_suspend,
2251 .resume = cirrusfb_pci_resume,
2252#endif
2253#endif
2254};
2255#endif /* CONFIG_PCI */
2256
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002258static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2259 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260{
2261 struct cirrusfb_info *cinfo;
2262 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002263 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 struct zorro_dev *z2 = NULL;
2265 unsigned long board_addr, board_size, size;
2266 int ret;
2267
2268 btype = ent->driver_data;
2269 if (cirrusfb_zorro_table2[btype].id2)
2270 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2271 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272
2273 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2274 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002275 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 ret = -ENOMEM;
2277 goto err_out;
2278 }
2279
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002280 dev_info(info->device, "%s board detected\n",
2281 cirrusfb_board_info[btype].name);
2282
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 cinfo->btype = btype;
2285
Al Viro36ea96a2007-10-27 19:46:58 +01002286 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002287 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 board_addr = zorro_resource_start(z);
2290 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002291 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292
2293 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002294 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2295 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 ret = -EBUSY;
2297 goto err_release_fb;
2298 }
2299
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 ret = -EIO;
2301
2302 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002303 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304
2305 /* To be precise, for the P4 this is not the */
2306 /* begin of the board, but the begin of RAM. */
2307 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2308 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002309 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 if (!cinfo->regbase)
2311 goto err_release_region;
2312
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002313 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002314 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002316 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002318 info->fix.smem_start = board_addr + 16777216;
2319 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2320 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 goto err_unmap_regbase;
2322 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002323 dev_info(info->device, " REG at $%lx\n",
2324 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002326 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002328 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002330 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2331 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332 goto err_release_region;
2333
2334 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002335 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002336 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002338 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002339 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 }
2341 cinfo->unmap = cirrusfb_zorro_unmap;
2342
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002343 dev_info(info->device,
2344 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2345 board_size / MB_, board_addr);
2346
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347 zorro_set_drvdata(z, info);
2348
Al Virod91f5bb2007-10-17 00:27:18 +01002349 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002350 if (!ret)
2351 return 0;
2352
2353 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2354 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
2356err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002357 if (btype == BT_PICASSO4)
2358 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359err_release_region:
2360 release_region(board_addr, board_size);
2361err_release_fb:
2362 framebuffer_release(info);
2363err_out:
2364 return ret;
2365}
2366
2367void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2368{
2369 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370
Krzysztof Helt8503df62007-10-16 01:29:08 -07002371 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372}
2373
2374static struct zorro_driver cirrusfb_zorro_driver = {
2375 .name = "cirrusfb",
2376 .id_table = cirrusfb_zorro_table,
2377 .probe = cirrusfb_zorro_register,
2378 .remove = __devexit_p(cirrusfb_zorro_unregister),
2379};
2380#endif /* CONFIG_ZORRO */
2381
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002383static int __init cirrusfb_setup(char *options)
2384{
Vlada Pericee119402008-11-19 15:36:45 -08002385 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 if (!options || !*options)
2388 return 0;
2389
Krzysztof Helt8503df62007-10-16 01:29:08 -07002390 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002391 if (!*this_opt)
2392 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 if (!strcmp(this_opt, "noaccel"))
2395 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002396 else if (!strncmp(this_opt, "mode:", 5))
2397 mode_option = this_opt + 5;
2398 else
2399 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 }
2401 return 0;
2402}
2403#endif
2404
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 /*
2406 * Modularization
2407 */
2408
2409MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2410MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2411MODULE_LICENSE("GPL");
2412
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002413static int __init cirrusfb_init(void)
2414{
2415 int error = 0;
2416
2417#ifndef MODULE
2418 char *option = NULL;
2419
2420 if (fb_get_options("cirrusfb", &option))
2421 return -ENODEV;
2422 cirrusfb_setup(option);
2423#endif
2424
2425#ifdef CONFIG_ZORRO
2426 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2427#endif
2428#ifdef CONFIG_PCI
2429 error |= pci_register_driver(&cirrusfb_pci_driver);
2430#endif
2431 return error;
2432}
2433
Krzysztof Helt8503df62007-10-16 01:29:08 -07002434static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435{
2436#ifdef CONFIG_PCI
2437 pci_unregister_driver(&cirrusfb_pci_driver);
2438#endif
2439#ifdef CONFIG_ZORRO
2440 zorro_unregister_driver(&cirrusfb_zorro_driver);
2441#endif
2442}
2443
2444module_init(cirrusfb_init);
2445
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002446module_param(mode_option, charp, 0);
2447MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002448module_param(noaccel, bool, 0);
2449MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002450
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451#ifdef MODULE
2452module_exit(cirrusfb_exit);
2453#endif
2454
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455/**********************************************************************/
2456/* about the following functions - I have used the same names for the */
2457/* functions as Markus Wild did in his Retina driver for NetBSD as */
2458/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002459/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460/**********************************************************************/
2461
2462/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 int regnum, unsigned char val)
2465{
2466 unsigned long regofs = 0;
2467
2468 if (cinfo->btype == BT_PICASSO) {
2469 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002470/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2471 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2473 regofs = 0xfff;
2474 }
2475
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477}
2478
2479/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481{
2482 unsigned long regofs = 0;
2483
2484 if (cinfo->btype == BT_PICASSO) {
2485 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002486/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2487 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2489 regofs = 0xfff;
2490 }
2491
Krzysztof Helt8503df62007-10-16 01:29:08 -07002492 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493}
2494
2495/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002498 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499
Krzysztof Helt8503df62007-10-16 01:29:08 -07002500 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 /* if we're just in "write value" mode, write back the */
2502 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002503 vga_w(cinfo->regbase, VGA_ATT_IW,
2504 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 }
2506 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002507/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2508 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509
2510 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002511 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512}
2513
2514/*** WHDR() - write into the Hidden DAC register ***/
2515/* as the HDR is the only extension register that requires special treatment
2516 * (the other extension registers are accessible just like the "ordinary"
2517 * registers of their functional group) here is a specialized routine for
2518 * accessing the HDR
2519 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521{
2522 unsigned char dummy;
2523
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002524 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002525 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 if (cinfo->btype == BT_PICASSO) {
2527 /* Klaus' hint for correct access to HDR on some boards */
2528 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002529 WGen(cinfo, VGA_PEL_MSK, 0x00);
2530 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002532 dummy = RGen(cinfo, VGA_PEL_IW);
2533 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 }
2535 /* now do the usual stuff to access the HDR */
2536
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537 dummy = RGen(cinfo, VGA_PEL_MSK);
2538 udelay(200);
2539 dummy = RGen(cinfo, VGA_PEL_MSK);
2540 udelay(200);
2541 dummy = RGen(cinfo, VGA_PEL_MSK);
2542 udelay(200);
2543 dummy = RGen(cinfo, VGA_PEL_MSK);
2544 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 WGen(cinfo, VGA_PEL_MSK, val);
2547 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548
2549 if (cinfo->btype == BT_PICASSO) {
2550 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002551 dummy = RGen(cinfo, VGA_PEL_IW);
2552 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553
2554 /* and at the end, restore the mask value */
2555 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002556 WGen(cinfo, VGA_PEL_MSK, 0xff);
2557 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 }
2559}
2560
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002562static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563{
2564#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002565 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002567 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568#endif
2569}
2570
2571/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573{
2574#ifdef CONFIG_ZORRO
2575 /* writing an arbitrary value to this one causes the monitor switcher */
2576 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002577 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580#endif
2581}
2582
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002584static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 unsigned char green, unsigned char blue)
2586{
2587 unsigned int data = VGA_PEL_D;
2588
2589 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591
2592 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002593 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002594 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595 /* but DAC data register IS, at least for Picasso II */
2596 if (cinfo->btype == BT_PICASSO)
2597 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002598 vga_w(cinfo->regbase, data, red);
2599 vga_w(cinfo->regbase, data, green);
2600 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002602 vga_w(cinfo->regbase, data, blue);
2603 vga_w(cinfo->regbase, data, green);
2604 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605 }
2606}
2607
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608#if 0
2609/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002610static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 unsigned char *green, unsigned char *blue)
2612{
2613 unsigned int data = VGA_PEL_D;
2614
Krzysztof Helt8503df62007-10-16 01:29:08 -07002615 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616
2617 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2618 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2619 if (cinfo->btype == BT_PICASSO)
2620 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002621 *red = vga_r(cinfo->regbase, data);
2622 *green = vga_r(cinfo->regbase, data);
2623 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 *blue = vga_r(cinfo->regbase, data);
2626 *green = vga_r(cinfo->regbase, data);
2627 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628 }
2629}
2630#endif
2631
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632/*******************************************************************
2633 cirrusfb_WaitBLT()
2634
2635 Wait for the BitBLT engine to complete a possible earlier job
2636*********************************************************************/
2637
2638/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002639static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002641 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002642 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643}
2644
2645/*******************************************************************
2646 cirrusfb_BitBLT()
2647
2648 perform accelerated "scrolling"
2649********************************************************************/
2650
Krzysztof Helt8343c892009-03-31 15:25:11 -07002651static void cirrusfb_set_blitter(u8 __iomem *regbase,
2652 u_short nwidth, u_short nheight,
2653 u_long nsrc, u_long ndest,
2654 u_short bltmode, u_short line_length)
2655
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002658 /* dest pitch low */
2659 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2660 /* dest pitch hi */
2661 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2662 /* source pitch low */
2663 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2664 /* source pitch hi */
2665 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666
2667 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002668 /* BLT width low */
2669 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2670 /* BLT width hi */
2671 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672
2673 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002674 /* BLT height low */
2675 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2676 /* BLT width hi */
2677 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678
2679 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002680 /* BLT dest low */
2681 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2682 /* BLT dest mid */
2683 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2684 /* BLT dest hi */
2685 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686
2687 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002688 /* BLT src low */
2689 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2690 /* BLT src mid */
2691 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2692 /* BLT src hi */
2693 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
2695 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002696 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697
2698 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002699 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700
2701 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002702 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002703}
2704
2705/*******************************************************************
2706 cirrusfb_BitBLT()
2707
2708 perform accelerated "scrolling"
2709********************************************************************/
2710
2711static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2712 u_short curx, u_short cury,
2713 u_short destx, u_short desty,
2714 u_short width, u_short height,
2715 u_short line_length)
2716{
2717 u_short nwidth = width - 1;
2718 u_short nheight = height - 1;
2719 u_long nsrc, ndest;
2720 u_char bltmode;
2721
2722 bltmode = 0x00;
2723 /* if source adr < dest addr, do the Blt backwards */
2724 if (cury <= desty) {
2725 if (cury == desty) {
2726 /* if src and dest are on the same line, check x */
2727 if (curx < destx)
2728 bltmode |= 0x01;
2729 } else
2730 bltmode |= 0x01;
2731 }
2732 /* standard case: forward blitting */
2733 nsrc = (cury * line_length) + curx;
2734 ndest = (desty * line_length) + destx;
2735 if (bltmode) {
2736 /* this means start addresses are at the end,
2737 * counting backwards
2738 */
2739 nsrc += nheight * line_length + nwidth;
2740 ndest += nheight * line_length + nwidth;
2741 }
2742
2743 cirrusfb_WaitBLT(regbase);
2744
2745 cirrusfb_set_blitter(regbase, nwidth, nheight,
2746 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747}
2748
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749/*******************************************************************
2750 cirrusfb_RectFill()
2751
2752 perform accelerated rectangle fill
2753********************************************************************/
2754
Krzysztof Helt8503df62007-10-16 01:29:08 -07002755static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002757 u32 fg_color, u32 bg_color, u_short line_length,
2758 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002760 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 u_char op;
2762
Krzysztof Helt8503df62007-10-16 01:29:08 -07002763 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 /* This is a ColorExpand Blt, using the */
2766 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002767 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2768 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769
Krzysztof Helt9e848062009-03-31 15:25:11 -07002770 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002771 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002772 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2773 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2774 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002775 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002776 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002777 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2778 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002779 op = 0xa0;
2780 }
2781 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002782 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2783 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2784 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002786 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002787 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788}
2789
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790/**************************************************************************
2791 * bestclock() - determine closest possible clock lower(?) than the
2792 * desired pixel clock
2793 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002794static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002796 int n, d;
2797 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798
Krzysztof Helt8503df62007-10-16 01:29:08 -07002799 assert(nom != NULL);
2800 assert(den != NULL);
2801 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802
2803 *nom = 0;
2804 *den = 0;
2805 *div = 0;
2806
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 if (freq < 8000)
2808 freq = 8000;
2809
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002810 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811
2812 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002813 int s = 0;
2814
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002815 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002817 int temp = d;
2818
2819 if (temp > 31) {
2820 s = 1;
2821 temp >>= 1;
2822 }
2823 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002824 h = h > freq ? h - freq : freq - h;
2825 if (h < diff) {
2826 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002828 *den = temp;
2829 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 }
2831 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002832 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002834 if (d > 31) {
2835 s = 1;
2836 d >>= 1;
2837 }
2838 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002839 h = h > freq ? h - freq : freq - h;
2840 if (h < diff) {
2841 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002843 *den = d;
2844 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 }
2846 }
2847 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848}
2849
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850/* -------------------------------------------------------------------------
2851 *
2852 * debugging functions
2853 *
2854 * -------------------------------------------------------------------------
2855 */
2856
2857#ifdef CIRRUSFB_DEBUG
2858
2859/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860 * cirrusfb_dbg_print_regs
2861 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2862 * @reg_class: type of registers to read: %CRT, or %SEQ
2863 *
2864 * DESCRIPTION:
2865 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2866 * old-style I/O ports are queried for information, otherwise MMIO is
2867 * used at the given @base address to query the information.
2868 */
2869
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002870static void cirrusfb_dbg_print_regs(struct fb_info *info,
2871 caddr_t regbase,
2872 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873{
2874 va_list list;
2875 unsigned char val = 0;
2876 unsigned reg;
2877 char *name;
2878
Krzysztof Helt8503df62007-10-16 01:29:08 -07002879 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880
Krzysztof Helt8503df62007-10-16 01:29:08 -07002881 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002883 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884
2885 switch (reg_class) {
2886 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002887 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 break;
2889 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002890 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891 break;
2892 default:
2893 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002894 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895 break;
2896 }
2897
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002898 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899
Krzysztof Helt8503df62007-10-16 01:29:08 -07002900 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002901 }
2902
Krzysztof Helt8503df62007-10-16 01:29:08 -07002903 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002904}
2905
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 * cirrusfb_dbg_reg_dump
2908 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2909 *
2910 * DESCRIPTION:
2911 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2912 * old-style I/O ports are queried for information, otherwise MMIO is
2913 * used at the given @base address to query the information.
2914 */
2915
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002916static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002918 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002920 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 "CR00", 0x00,
2922 "CR01", 0x01,
2923 "CR02", 0x02,
2924 "CR03", 0x03,
2925 "CR04", 0x04,
2926 "CR05", 0x05,
2927 "CR06", 0x06,
2928 "CR07", 0x07,
2929 "CR08", 0x08,
2930 "CR09", 0x09,
2931 "CR0A", 0x0A,
2932 "CR0B", 0x0B,
2933 "CR0C", 0x0C,
2934 "CR0D", 0x0D,
2935 "CR0E", 0x0E,
2936 "CR0F", 0x0F,
2937 "CR10", 0x10,
2938 "CR11", 0x11,
2939 "CR12", 0x12,
2940 "CR13", 0x13,
2941 "CR14", 0x14,
2942 "CR15", 0x15,
2943 "CR16", 0x16,
2944 "CR17", 0x17,
2945 "CR18", 0x18,
2946 "CR22", 0x22,
2947 "CR24", 0x24,
2948 "CR26", 0x26,
2949 "CR2D", 0x2D,
2950 "CR2E", 0x2E,
2951 "CR2F", 0x2F,
2952 "CR30", 0x30,
2953 "CR31", 0x31,
2954 "CR32", 0x32,
2955 "CR33", 0x33,
2956 "CR34", 0x34,
2957 "CR35", 0x35,
2958 "CR36", 0x36,
2959 "CR37", 0x37,
2960 "CR38", 0x38,
2961 "CR39", 0x39,
2962 "CR3A", 0x3A,
2963 "CR3B", 0x3B,
2964 "CR3C", 0x3C,
2965 "CR3D", 0x3D,
2966 "CR3E", 0x3E,
2967 "CR3F", 0x3F,
2968 NULL);
2969
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002970 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002972 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002974 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975 "SR00", 0x00,
2976 "SR01", 0x01,
2977 "SR02", 0x02,
2978 "SR03", 0x03,
2979 "SR04", 0x04,
2980 "SR08", 0x08,
2981 "SR09", 0x09,
2982 "SR0A", 0x0A,
2983 "SR0B", 0x0B,
2984 "SR0D", 0x0D,
2985 "SR10", 0x10,
2986 "SR11", 0x11,
2987 "SR12", 0x12,
2988 "SR13", 0x13,
2989 "SR14", 0x14,
2990 "SR15", 0x15,
2991 "SR16", 0x16,
2992 "SR17", 0x17,
2993 "SR18", 0x18,
2994 "SR19", 0x19,
2995 "SR1A", 0x1A,
2996 "SR1B", 0x1B,
2997 "SR1C", 0x1C,
2998 "SR1D", 0x1D,
2999 "SR1E", 0x1E,
3000 "SR1F", 0x1F,
3001 NULL);
3002
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003003 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003004}
3005
3006#endif /* CIRRUSFB_DEBUG */
3007