blob: 6df7c54db0a3470b1150d1eb8f5089967628eb73 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/errno.h>
40#include <linux/string.h>
41#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/delay.h>
43#include <linux/fb.h>
44#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#include <asm/pgtable.h>
46
47#ifdef CONFIG_ZORRO
48#include <linux/zorro.h>
49#endif
50#ifdef CONFIG_PCI
51#include <linux/pci.h>
52#endif
53#ifdef CONFIG_AMIGA
54#include <asm/amigahw.h>
55#endif
56#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110057#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070058#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#else
60#define isPReP 0
61#endif
62
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070063#include <video/vga.h>
64#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Linus Torvalds1da177e2005-04-16 15:20:36 -070066/*****************************************************************
67 *
68 * debugging and utility macros
69 *
70 */
71
Linus Torvalds1da177e2005-04-16 15:20:36 -070072/* disable runtime assertions? */
73/* #define CIRRUSFB_NDEBUG */
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075/* debugging assertions */
76#ifndef CIRRUSFB_NDEBUG
77#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070078 if (!(expr)) { \
79 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070080 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070081 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070082#else
83#define assert(expr)
84#endif
85
Krzysztof Helt8503df62007-10-16 01:29:08 -070086#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Linus Torvalds1da177e2005-04-16 15:20:36 -070088/*****************************************************************
89 *
90 * chipset information
91 *
92 */
93
94/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070095enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 BT_NONE = 0,
Krzysztof Helt7cade312009-03-31 15:25:13 -070097 BT_SD64, /* GD5434 */
98 BT_PICCOLO, /* GD5426 */
99 BT_PICASSO, /* GD5426 or GD5428 */
100 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 BT_PICASSO4, /* GD5446 */
102 BT_ALPINE, /* GD543x/4x */
103 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700104 BT_LAGUNA, /* GD5462/64 */
105 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700106};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108/*
109 * per-board-type information, used for enumerating and abstracting
110 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700111 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 * use direct indexing on this array
113 * NOTE: '__initdata' cannot be used as some of this info
114 * is required at runtime. Maybe separate into an init-only and
115 * a run-time table?
116 */
117static const struct cirrusfb_board_info_rec {
118 char *name; /* ASCII name of chipset */
119 long maxclock[5]; /* maximum video clock */
120 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700121 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
122 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700123 /* construct bit 19 of screen start address */
124 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 /* initial SR07 value, then for each mode */
127 unsigned char sr07;
128 unsigned char sr07_1bpp;
129 unsigned char sr07_1bpp_mux;
130 unsigned char sr07_8bpp;
131 unsigned char sr07_8bpp_mux;
132
133 unsigned char sr1f; /* SR1F VGA initial register value */
134} cirrusfb_board_info[] = {
135 [BT_SD64] = {
136 .name = "CL SD64",
137 .maxclock = {
138 /* guess */
139 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700140 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700142 .init_sr07 = true,
143 .init_sr1f = true,
144 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 .sr07 = 0xF0,
146 .sr07_1bpp = 0xF0,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700147 .sr07_1bpp_mux = 0xF6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 .sr07_8bpp = 0xF1,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700149 .sr07_8bpp_mux = 0xF7,
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700150 .sr1f = 0x1E
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 },
152 [BT_PICCOLO] = {
153 .name = "CL Piccolo",
154 .maxclock = {
155 /* guess */
156 90000, 90000, 90000, 90000, 90000
157 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700158 .init_sr07 = true,
159 .init_sr1f = true,
160 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 .sr07 = 0x80,
162 .sr07_1bpp = 0x80,
163 .sr07_8bpp = 0x81,
164 .sr1f = 0x22
165 },
166 [BT_PICASSO] = {
167 .name = "CL Picasso",
168 .maxclock = {
169 /* guess */
170 90000, 90000, 90000, 90000, 90000
171 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700172 .init_sr07 = true,
173 .init_sr1f = true,
174 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 .sr07 = 0x20,
176 .sr07_1bpp = 0x20,
177 .sr07_8bpp = 0x21,
178 .sr1f = 0x22
179 },
180 [BT_SPECTRUM] = {
181 .name = "CL Spectrum",
182 .maxclock = {
183 /* guess */
184 90000, 90000, 90000, 90000, 90000
185 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700186 .init_sr07 = true,
187 .init_sr1f = true,
188 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 .sr07 = 0x80,
190 .sr07_1bpp = 0x80,
191 .sr07_8bpp = 0x81,
192 .sr1f = 0x22
193 },
194 [BT_PICASSO4] = {
195 .name = "CL Picasso4",
196 .maxclock = {
197 135100, 135100, 85500, 85500, 0
198 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700199 .init_sr07 = true,
200 .init_sr1f = false,
201 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700202 .sr07 = 0xA0,
203 .sr07_1bpp = 0xA0,
204 .sr07_1bpp_mux = 0xA6,
205 .sr07_8bpp = 0xA1,
206 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 .sr1f = 0
208 },
209 [BT_ALPINE] = {
210 .name = "CL Alpine",
211 .maxclock = {
212 /* for the GD5430. GD5446 can do more... */
213 85500, 85500, 50000, 28500, 0
214 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700215 .init_sr07 = true,
216 .init_sr1f = true,
217 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700219 .sr07_1bpp = 0xA0,
220 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 .sr07_8bpp = 0xA1,
222 .sr07_8bpp_mux = 0xA7,
223 .sr1f = 0x1C
224 },
225 [BT_GD5480] = {
226 .name = "CL GD5480",
227 .maxclock = {
228 135100, 200000, 200000, 135100, 135100
229 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700230 .init_sr07 = true,
231 .init_sr1f = true,
232 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 .sr07 = 0x10,
234 .sr07_1bpp = 0x11,
235 .sr07_8bpp = 0x11,
236 .sr1f = 0x1C
237 },
238 [BT_LAGUNA] = {
239 .name = "CL Laguna",
240 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700241 /* taken from X11 code */
242 170000, 170000, 170000, 170000, 135100,
243 },
244 .init_sr07 = false,
245 .init_sr1f = false,
246 .scrn_start_bit19 = true,
247 },
248 [BT_LAGUNAB] = {
249 .name = "CL Laguna AGP",
250 .maxclock = {
251 /* taken from X11 code */
252 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700254 .init_sr07 = false,
255 .init_sr1f = false,
256 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 }
258};
259
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260#ifdef CONFIG_PCI
261#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000262 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700265 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_SD64),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64),
Krzysztof Helt8503df62007-10-16 01:29:08 -0700268 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
269 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
271 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700275 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 { 0, }
277};
278MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
279#undef CHIP
280#endif /* CONFIG_PCI */
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282#ifdef CONFIG_ZORRO
283static const struct zorro_device_id cirrusfb_zorro_table[] = {
284 {
285 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
286 .driver_data = BT_SD64,
287 }, {
288 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
289 .driver_data = BT_PICCOLO,
290 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700291 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 .driver_data = BT_PICASSO,
293 }, {
294 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
295 .driver_data = BT_SPECTRUM,
296 }, {
297 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
298 .driver_data = BT_PICASSO4,
299 },
300 { 0 }
301};
Geert Uytterhoevenbf54a2b2008-11-18 21:13:53 +0100302MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
304static const struct {
305 zorro_id id2;
306 unsigned long size;
307} cirrusfb_zorro_table2[] = {
308 [BT_SD64] = {
309 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
310 .size = 0x400000
311 },
312 [BT_PICCOLO] = {
313 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
314 .size = 0x200000
315 },
316 [BT_PICASSO] = {
317 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
318 .size = 0x200000
319 },
320 [BT_SPECTRUM] = {
321 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
322 .size = 0x200000
323 },
324 [BT_PICASSO4] = {
325 .id2 = 0,
326 .size = 0x400000
327 }
328};
329#endif /* CONFIG_ZORRO */
330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700332enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700333 CRT,
334 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700335};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700336#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338/* info about board */
339struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700341 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700342 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 unsigned char SFR; /* Shadow of special function register */
344
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700345 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700346 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700348 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700350 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351};
352
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700353static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700354static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
356/****************************************************************************/
357/**** BEGIN PROTOTYPES ******************************************************/
358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
361 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700364static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700365static void switch_monitor(struct cirrusfb_info *cinfo, int on);
366static void WGen(const struct cirrusfb_info *cinfo,
367 int regnum, unsigned char val);
368static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
369static void AttrOn(const struct cirrusfb_info *cinfo);
370static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
371static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
372static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
373static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
374 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700376static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
377 unsigned char *red, unsigned char *green,
378 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700380static void cirrusfb_WaitBLT(u8 __iomem *regbase);
381static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
382 u_short curx, u_short cury,
383 u_short destx, u_short desty,
384 u_short width, u_short height,
385 u_short line_length);
386static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
387 u_short x, u_short y,
388 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700389 u32 fg_color, u32 bg_color,
390 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700392static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
394#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700395static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
396static void cirrusfb_dbg_print_regs(struct fb_info *info,
397 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700398 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399#endif /* CIRRUSFB_DEBUG */
400
401/*** END PROTOTYPES ********************************************************/
402/*****************************************************************************/
403/*** BEGIN Interface Used by the World ***************************************/
404
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700405static inline int is_laguna(const struct cirrusfb_info *cinfo)
406{
407 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
408}
409
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
412/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700416 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 return 0;
418}
419
420/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
423 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700424 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 return 0;
426}
427
428/**** END Interface used by the World *************************************/
429/****************************************************************************/
430/**** BEGIN Hardware specific Routines **************************************/
431
Krzysztof Helt486ff382008-10-15 22:03:42 -0700432/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700433static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700435 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700436 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
Krzysztof Helt486ff382008-10-15 22:03:42 -0700438 /* Read MCLK value */
439 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700440 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700443 * should divide it by to get VCLK
444 */
445
446 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700447 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 1;
449 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700450 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 }
453
Krzysztof Helt486ff382008-10-15 22:03:42 -0700454 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455}
456
Krzysztof Helt99a45842009-03-31 15:25:09 -0700457static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
458 struct fb_info *info)
459{
460 long freq;
461 long maxclock;
462 struct cirrusfb_info *cinfo = info->par;
463 unsigned maxclockidx = var->bits_per_pixel >> 3;
464
465 /* convert from ps to kHz */
466 freq = PICOS2KHZ(var->pixclock);
467
468 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
469
470 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
471 cinfo->multiplexing = 0;
472
473 /* If the frequency is greater than we can support, we might be able
474 * to use multiplexing for the video mode */
475 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700476 dev_err(info->device,
477 "Frequency greater than maxclock (%ld kHz)\n",
478 maxclock);
479 return -EINVAL;
480 }
481 /*
482 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
483 * pixel clock
484 */
485 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700486 switch (cinfo->btype) {
487 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700488 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700489 case BT_PICASSO4:
490 if (freq > 85500)
491 cinfo->multiplexing = 1;
492 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700493 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700494 if (freq > 135100)
495 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700496 break;
497
498 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700499 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700500 }
501 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700502
503 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700504 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700505 cinfo->doubleVCLK = 0;
506 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
507 var->bits_per_pixel == 16) {
508 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700509 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700510
Krzysztof Helt99a45842009-03-31 15:25:09 -0700511 return 0;
512}
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514static int cirrusfb_check_var(struct fb_var_screeninfo *var,
515 struct fb_info *info)
516{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700517 int yres;
518 /* memory size in pixels */
519 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700520 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700523 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 var->red.offset = 0;
525 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700526 var->green = var->red;
527 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 break;
529
530 case 8:
531 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700532 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700533 var->green = var->red;
534 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 break;
536
537 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700538 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 var->red.offset = 2;
540 var->green.offset = -3;
541 var->blue.offset = 8;
542 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700543 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 var->green.offset = 5;
545 var->blue.offset = 0;
546 }
547 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700548 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 var->blue.length = 5;
550 break;
551
Krzysztof Helt7cade312009-03-31 15:25:13 -0700552 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700553 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700554 var->red.offset = 0;
555 var->green.offset = 8;
556 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 } else {
558 var->red.offset = 16;
559 var->green.offset = 8;
560 var->blue.offset = 0;
561 }
562 var->red.length = 8;
563 var->green.length = 8;
564 var->blue.length = 8;
565 break;
566
567 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700568 dev_dbg(info->device,
569 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Krzysztof Helt0efb2a02009-04-13 14:39:55 -0700570 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 }
572
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700573 if (var->xres_virtual < var->xres)
574 var->xres_virtual = var->xres;
575 /* use highest possible virtual resolution */
576 if (var->yres_virtual == -1) {
577 var->yres_virtual = pixels / var->xres_virtual;
578
579 dev_info(info->device,
580 "virtual resolution set to maximum of %dx%d\n",
581 var->xres_virtual, var->yres_virtual);
582 }
583 if (var->yres_virtual < var->yres)
584 var->yres_virtual = var->yres;
585
586 if (var->xres_virtual * var->yres_virtual > pixels) {
587 dev_err(info->device, "mode %dx%dx%d rejected... "
588 "virtual resolution too high to fit into video memory!\n",
589 var->xres_virtual, var->yres_virtual,
590 var->bits_per_pixel);
591 return -EINVAL;
592 }
593
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700594 if (var->xoffset < 0)
595 var->xoffset = 0;
596 if (var->yoffset < 0)
597 var->yoffset = 0;
598
599 /* truncate xoffset and yoffset to maximum if too high */
600 if (var->xoffset > var->xres_virtual - var->xres)
601 var->xoffset = var->xres_virtual - var->xres - 1;
602 if (var->yoffset > var->yres_virtual - var->yres)
603 var->yoffset = var->yres_virtual - var->yres - 1;
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 var->red.msb_right =
606 var->green.msb_right =
607 var->blue.msb_right =
608 var->transp.offset =
609 var->transp.length =
610 var->transp.msb_right = 0;
611
612 yres = var->yres;
613 if (var->vmode & FB_VMODE_DOUBLE)
614 yres *= 2;
615 else if (var->vmode & FB_VMODE_INTERLACED)
616 yres = (yres + 1) / 2;
617
618 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700619 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700620 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return -EINVAL;
622 }
623
Krzysztof Helt99a45842009-03-31 15:25:09 -0700624 if (cirrusfb_check_pixclock(var, info))
625 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700627 if (!is_laguna(cinfo))
628 var->accel_flags = FB_ACCELF_TEXT;
629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return 0;
631}
632
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700633static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700635 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700636 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700637
Krzysztof Helt8503df62007-10-16 01:29:08 -0700638 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700639 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Krzysztof Helt486ff382008-10-15 22:03:42 -0700641 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700642 dev_dbg(info->device, "Set %s as pixclock source.\n",
643 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700644 old1f |= 0x40;
645 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
646 if (div == 2)
647 old1e |= 1;
648
649 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700651 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652}
653
654/*************************************************************************
655 cirrusfb_set_par_foo()
656
657 actually writes the values for a new video mode into the hardware,
658**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700659static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
661 struct cirrusfb_info *cinfo = info->par;
662 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 u8 __iomem *regbase = cinfo->regbase;
664 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700665 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700667 int hdispend, hsyncstart, hsyncend, htotal;
668 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700669 long freq;
670 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700671 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700673 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700675
676 switch (var->bits_per_pixel) {
677 case 1:
678 info->fix.line_length = var->xres_virtual / 8;
679 info->fix.visual = FB_VISUAL_MONO10;
680 break;
681
682 case 8:
683 info->fix.line_length = var->xres_virtual;
684 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
685 break;
686
687 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700688 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700689 info->fix.line_length = var->xres_virtual *
690 var->bits_per_pixel >> 3;
691 info->fix.visual = FB_VISUAL_TRUECOLOR;
692 break;
693 }
694 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700696 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 bi = &cirrusfb_board_info[cinfo->btype];
699
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700700 hsyncstart = var->xres + var->right_margin;
701 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700702 htotal = (hsyncend + var->left_margin) / 8;
703 hdispend = var->xres / 8;
704 hsyncstart = hsyncstart / 8;
705 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700706
Krzysztof Helt8636a922009-03-31 15:25:17 -0700707 vdispend = var->yres;
708 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700709 vsyncend = vsyncstart + var->vsync_len;
710 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700711
712 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700713 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700714 vsyncstart *= 2;
715 vsyncend *= 2;
716 vtotal *= 2;
717 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700718 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700719 vsyncstart = (vsyncstart + 1) / 2;
720 vsyncend = (vsyncend + 1) / 2;
721 vtotal = (vtotal + 1) / 2;
722 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700723 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700724 if (yres >= 1024) {
725 vtotal /= 2;
726 vsyncstart /= 2;
727 vsyncend /= 2;
728 vdispend /= 2;
729 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700730
731 vdispend -= 1;
732 vsyncstart -= 1;
733 vsyncend -= 1;
734 vtotal -= 2;
735
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700736 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700737 htotal /= 2;
738 hsyncstart /= 2;
739 hsyncend /= 2;
740 hdispend /= 2;
741 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700742
743 htotal -= 5;
744 hdispend -= 1;
745 hsyncstart += 1;
746 hsyncend += 1;
747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700749 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700752 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700753 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700755 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700756 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700758 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700759 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Krzysztof Helt8503df62007-10-16 01:29:08 -0700761 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700762 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700763 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700766 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 tmp = hsyncend % 32;
770 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700772 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700773 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700775 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700776 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
778 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700779 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700785 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700791 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700793 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700794 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700797 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 tmp |= 0x20;
799 if (var->vmode & FB_VMODE_DOUBLE)
800 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700801 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700802 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700804 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700805 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700807 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700808 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700810 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700811 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700813 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700814 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700816 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700817 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700819 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700820 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 tmp = 0;
823 if (var->vmode & FB_VMODE_INTERLACED)
824 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700825 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700827 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700829 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 tmp |= 128;
833
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700834 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700835 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700837 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700838 if (var->bits_per_pixel == 24)
839 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
840 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700841 if (cinfo->multiplexing)
842 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700843 if (cinfo->doubleVCLK)
844 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700845
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700846 bestclock(freq, &nom, &den, &div);
847
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700848 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
849 freq, nom, den, div);
850
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 /* set VCLK0 */
852 /* hardware RefClock: 14.31818 MHz */
853 /* formula: VClk = (OSC * N) / (D * (1+P)) */
854 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
855
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700856 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
857 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700858 /* if freq is close to mclk or mclk/2 select mclk
859 * as clock source
860 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700861 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700862 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700863 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700864 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700865 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700866 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700867 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
868 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
869 unsigned short tile_control;
870
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700871 if (cinfo->btype == BT_LAGUNAB) {
872 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
873 tile_control &= ~0x80;
874 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
875 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700876
877 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
878 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
879 control = fb_readw(cinfo->laguna_mmio + 0x402);
880 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
881 control &= ~0x6800;
882 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700883 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700884 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700885 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700886 tmp = den << 1;
887 if (div != 0)
888 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700889 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
890 if ((cinfo->btype == BT_SD64) ||
891 (cinfo->btype == BT_ALPINE) ||
892 (cinfo->btype == BT_GD5480))
893 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700895 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700896 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700897 vga_wseq(regbase, CL_SEQRE, tmp);
898 vga_wseq(regbase, CL_SEQR1E, nom);
899 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700900 vga_wseq(regbase, CL_SEQRE, nom);
901 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700902 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700903 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700905 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700907 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 else
909 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
910 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700911 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 /* don't know if it would hurt to also program this if no interlaced */
914 /* mode is used, but I feel better this way.. :-) */
915 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700916 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700918 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700920 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700921 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700922 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
924 tmp |= 0x40;
925 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
926 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929 /* text cursor on and start line */
930 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
931 /* text cursor end line */
932 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
934 /******************************************************
935 *
936 * 1 bpp
937 *
938 */
939
940 /* programming for different color depths */
941 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700942 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700943 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
945 /* SR07 */
946 switch (cinfo->btype) {
947 case BT_SD64:
948 case BT_PICCOLO:
949 case BT_PICASSO:
950 case BT_SPECTRUM:
951 case BT_PICASSO4:
952 case BT_ALPINE:
953 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700955 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 bi->sr07_1bpp_mux : bi->sr07_1bpp);
957 break;
958
959 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700960 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700961 vga_wseq(regbase, CL_SEQR7,
962 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 break;
964
965 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700966 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 break;
968 }
969
970 /* Extended Sequencer Mode */
971 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972
973 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700974 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700975 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
976 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 break;
978
979 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700980 /* ## vorher d0 avoid FIFO underruns..? */
981 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 break;
983
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700984 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 case BT_PICASSO4:
986 case BT_ALPINE:
987 case BT_GD5480:
988 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700989 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 /* do nothing */
991 break;
992
993 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700994 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 break;
996 }
997
Krzysztof Helt8503df62007-10-16 01:29:08 -0700998 /* pixel mask: pass-through for first plane */
999 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001000 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001001 /* hidden dac reg: 1280x1024 */
1002 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001004 /* hidden dac: nothing */
1005 WHDR(cinfo, 0);
1006 /* memory mode: odd/even, ext. memory */
1007 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1008 /* plane mask: only write to first plane */
1009 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 }
1011
1012 /******************************************************
1013 *
1014 * 8 bpp
1015 *
1016 */
1017
1018 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001019 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 switch (cinfo->btype) {
1021 case BT_SD64:
1022 case BT_PICCOLO:
1023 case BT_PICASSO:
1024 case BT_SPECTRUM:
1025 case BT_PICASSO4:
1026 case BT_ALPINE:
1027 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001028 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001029 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1031 break;
1032
1033 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001034 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001035 vga_wseq(regbase, CL_SEQR7,
1036 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001037 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 break;
1039
1040 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001041 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 break;
1043 }
1044
1045 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001049 /* Fast Page-Mode writes */
1050 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 break;
1052
1053 case BT_PICASSO4:
1054#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001055 /* ### INCOMPLETE!! */
1056 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001059 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 case BT_GD5480:
1061 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001062 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 /* do nothing */
1064 break;
1065
1066 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001067 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 break;
1069 }
1070
Krzysztof Helt8503df62007-10-16 01:29:08 -07001071 /* mode register: 256 color mode */
1072 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001073 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001074 /* hidden dac reg: 1280x1024 */
1075 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001077 /* hidden dac: nothing */
1078 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 }
1080
1081 /******************************************************
1082 *
1083 * 16 bpp
1084 *
1085 */
1086
1087 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001088 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001091 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001092 vga_wseq(regbase, CL_SEQR7, 0x87);
1093 /* Fast Page-Mode writes */
1094 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 break;
1096
1097 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001098 vga_wseq(regbase, CL_SEQR7, 0x27);
1099 /* Fast Page-Mode writes */
1100 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 break;
1102
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001103 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001106 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001107 vga_wseq(regbase, CL_SEQR7,
1108 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 break;
1110
1111 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001112 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 /* We already set SRF and SR1F */
1114 break;
1115
1116 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001117 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001118 vga_wseq(regbase, CL_SEQR7,
1119 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001120 control |= 0x2000;
1121 format |= 0x1400;
1122 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 break;
1124
1125 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001126 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 break;
1128 }
1129
Krzysztof Helt8503df62007-10-16 01:29:08 -07001130 /* mode register: 256 color mode */
1131 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001133 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134#elif defined(CONFIG_ZORRO)
1135 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001136 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 }
1139
1140 /******************************************************
1141 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001142 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 *
1144 */
1145
Krzysztof Helt7cade312009-03-31 15:25:13 -07001146 else if (var->bits_per_pixel == 24) {
1147 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001150 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001151 vga_wseq(regbase, CL_SEQR7, 0x85);
1152 /* Fast Page-Mode writes */
1153 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 break;
1155
1156 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001157 vga_wseq(regbase, CL_SEQR7, 0x25);
1158 /* Fast Page-Mode writes */
1159 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 break;
1161
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001162 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001165 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001166 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 break;
1168
1169 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001170 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 /* We already set SRF and SR1F */
1172 break;
1173
1174 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001175 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001176 vga_wseq(regbase, CL_SEQR7,
1177 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001178 control |= 0x4000;
1179 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001180 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 break;
1182
1183 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001184 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 break;
1186 }
1187
Krzysztof Helt8503df62007-10-16 01:29:08 -07001188 /* mode register: 256 color mode */
1189 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001190 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1191 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 }
1193
1194 /******************************************************
1195 *
1196 * unknown/unsupported bpp
1197 *
1198 */
1199
Krzysztof Helt8503df62007-10-16 01:29:08 -07001200 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001201 dev_err(info->device,
1202 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
Krzysztof Helt6683e012009-03-31 15:25:06 -07001205 pitch = info->fix.line_length >> 3;
1206 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001208 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 tmp |= 0x10; /* offset overflow bit */
1210
Krzysztof Helt8503df62007-10-16 01:29:08 -07001211 /* screen start addr #16-18, fastpagemode cycles */
1212 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001214 /* screen start address bit 19 */
1215 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001216 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001218 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001219 tmp = 0;
1220 if ((htotal + 5) & 256)
1221 tmp |= 128;
1222 if (hdispend & 256)
1223 tmp |= 64;
1224 if (hsyncstart & 256)
1225 tmp |= 48;
1226 if (vtotal & 1024)
1227 tmp |= 8;
1228 if (vdispend & 1024)
1229 tmp |= 4;
1230 if (vsyncstart & 1024)
1231 tmp |= 3;
1232
1233 vga_wcrt(regbase, CL_CRT1E, tmp);
1234 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1235 }
1236
Krzysztof Helt8503df62007-10-16 01:29:08 -07001237 /* pixel panning */
1238 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239
1240 /* [ EGS: SetOffset(); ] */
1241 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001242 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001244 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001245 /* no tiles */
1246 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1247 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1248 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1249 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 /* finally, turn on everything - turn off "FullBandwidth" bit */
1251 /* also, set "DotClock%2" bit where requested */
1252 tmp = 0x01;
1253
1254/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1255 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1256 tmp |= 0x08;
1257*/
1258
Krzysztof Helt8503df62007-10-16 01:29:08 -07001259 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001260 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001263 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264#endif
1265
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 return 0;
1267}
1268
1269/* for some reason incomprehensible to me, cirrusfb requires that you write
1270 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001271static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001273 cirrusfb_set_par_foo(info);
1274 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275}
1276
Krzysztof Helt8503df62007-10-16 01:29:08 -07001277static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1278 unsigned blue, unsigned transp,
1279 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280{
1281 struct cirrusfb_info *cinfo = info->par;
1282
1283 if (regno > 255)
1284 return -EINVAL;
1285
1286 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1287 u32 v;
1288 red >>= (16 - info->var.red.length);
1289 green >>= (16 - info->var.green.length);
1290 blue >>= (16 - info->var.blue.length);
1291
Krzysztof Helt8503df62007-10-16 01:29:08 -07001292 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 return 1;
1294 v = (red << info->var.red.offset) |
1295 (green << info->var.green.offset) |
1296 (blue << info->var.blue.offset);
1297
Krzysztof Helt060b6002007-10-16 01:29:13 -07001298 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 return 0;
1300 }
1301
Krzysztof Helt8503df62007-10-16 01:29:08 -07001302 if (info->var.bits_per_pixel == 8)
1303 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
1305 return 0;
1306
1307}
1308
1309/*************************************************************************
1310 cirrusfb_pan_display()
1311
1312 performs display panning - provided hardware permits this
1313**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001314static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1315 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001317 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001319 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 struct cirrusfb_info *cinfo = info->par;
1321
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 /* no range checks for xoffset and yoffset, */
1323 /* as fb_pan_display has already done this */
1324 if (var->vmode & FB_VMODE_YWRAP)
1325 return -EINVAL;
1326
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
Krzysztof Helt99a45842009-03-31 15:25:09 -07001329 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330
1331 if (info->var.bits_per_pixel == 1) {
1332 /* base is already correct */
1333 xpix = (unsigned char) (var->xoffset % 8);
1334 } else {
1335 base /= 4;
1336 xpix = (unsigned char) ((xoffset % 4) * 2);
1337 }
1338
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001339 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001340 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341
1342 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001343 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1344 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001346 /* 0xf2 is %11110010, exclude tmp bits */
1347 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 /* construct bits 16, 17 and 18 of screen start address */
1349 if (base & 0x10000)
1350 tmp |= 0x01;
1351 if (base & 0x20000)
1352 tmp |= 0x04;
1353 if (base & 0x40000)
1354 tmp |= 0x08;
1355
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001356 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
1358 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001359 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001360 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1361 if (is_laguna(cinfo))
1362 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1363 else
1364 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001365 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1366 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Krzysztof Helt8503df62007-10-16 01:29:08 -07001368 /* write pixel panning value to AR33; this does not quite work in 8bpp
1369 *
1370 * ### Piccolo..? Will this work?
1371 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001373 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
Krzysztof Helt8503df62007-10-16 01:29:08 -07001375 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376}
1377
Krzysztof Helt8503df62007-10-16 01:29:08 -07001378static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379{
1380 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001381 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1382 * then the caller blanks by setting the CLUT (Color Look Up Table)
1383 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1384 * failed due to e.g. a video mode which doesn't support it.
1385 * Implements VESA suspend and powerdown modes on hardware that
1386 * supports disabling hsync/vsync:
1387 * blank_mode == 2: suspend vsync
1388 * blank_mode == 3: suspend hsync
1389 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 */
1391 unsigned char val;
1392 struct cirrusfb_info *cinfo = info->par;
1393 int current_mode = cinfo->blank_mode;
1394
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001395 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
1397 if (info->state != FBINFO_STATE_RUNNING ||
1398 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001399 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 return 0;
1401 }
1402
1403 /* Undo current */
1404 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001405 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001406 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001407 val = 0;
1408 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001409 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001410 val = 0x20;
1411
1412 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1413 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
1415 switch (blank_mode) {
1416 case FB_BLANK_UNBLANK:
1417 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001418 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 break;
1420 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001421 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 break;
1423 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001424 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 break;
1426 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001427 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428 break;
1429 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001430 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 return 1;
1432 }
1433
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001434 vga_wgfx(cinfo->regbase, CL_GRE, val);
1435
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001437 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
1439 /* Let fbcon do a soft blank for us */
1440 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1441}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001442
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443/**** END Hardware specific Routines **************************************/
1444/****************************************************************************/
1445/**** BEGIN Internal Routines ***********************************************/
1446
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001447static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001449 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 const struct cirrusfb_board_info_rec *bi;
1451
Krzysztof Helt8503df62007-10-16 01:29:08 -07001452 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
1454 bi = &cirrusfb_board_info[cinfo->btype];
1455
1456 /* reset board globally */
1457 switch (cinfo->btype) {
1458 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001459 WSFR(cinfo, 0x01);
1460 udelay(500);
1461 WSFR(cinfo, 0x51);
1462 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 break;
1464 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001465 WSFR2(cinfo, 0xff);
1466 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 break;
1468 case BT_SD64:
1469 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001470 WSFR(cinfo, 0x1f);
1471 udelay(500);
1472 WSFR(cinfo, 0x4f);
1473 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 break;
1475 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001476 /* disable flickerfixer */
1477 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1478 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001479 /* mode */
1480 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001481 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001482 /* from Klaus' NetBSD driver: */
1483 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001484 case BT_ALPINE: /* fall through */
1485 /* put blitter into 542x compat */
1486 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 break;
1488
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001489 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001490 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 /* Nothing to do to reset the board. */
1492 break;
1493
1494 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001495 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 break;
1497 }
1498
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001499 /* make sure RAM size set by this point */
1500 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
1502 /* the P4 is not fully initialized here; I rely on it having been */
1503 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001507 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1508 WGen(cinfo, CL_POS102, 0x01);
1509 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510
1511 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001512 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
Krzysztof Helt8503df62007-10-16 01:29:08 -07001514 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001515 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
Krzysztof Helt8503df62007-10-16 01:29:08 -07001517 /* FullBandwidth (video off) and 8/9 dot clock */
1518 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
Krzysztof Helt8503df62007-10-16 01:29:08 -07001520 /* "magic cookie" - doesn't make any sense to me.. */
1521/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1522 /* unlock all extension registers */
1523 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 switch (cinfo->btype) {
1526 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001527 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 break;
1529 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001530 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001531 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 break;
1533 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001534#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001535 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001536#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 break;
1538 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1540 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 break;
1542 }
1543 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001544 /* plane mask: nothing */
1545 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1546 /* character map select: doesn't even matter in gx mode */
1547 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001548 /* memory mode: chain4, ext. memory */
1549 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
1551 /* controller-internal base address of video memory */
1552 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1556 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Krzysztof Helt8503df62007-10-16 01:29:08 -07001558 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1559 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1560 /* graphics cursor Y position (..."... ) */
1561 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1562 /* graphics cursor attributes */
1563 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1564 /* graphics cursor pattern address */
1565 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566
1567 /* writing these on a P4 might give problems.. */
1568 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001569 /* configuration readback and ext. color */
1570 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1571 /* signature generator */
1572 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 }
1574
Krzysztof Helt8503df62007-10-16 01:29:08 -07001575 /* Screen A preset row scan: none */
1576 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1577 /* Text cursor start: disable text cursor */
1578 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1579 /* Text cursor end: - */
1580 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001581 /* text cursor location high: 0 */
1582 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1583 /* text cursor location low: 0 */
1584 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585
Krzysztof Helt8503df62007-10-16 01:29:08 -07001586 /* Underline Row scanline: - */
1587 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001589 /* ext. display controls: ext.adr. wrap */
1590 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
Krzysztof Helt8503df62007-10-16 01:29:08 -07001592 /* Set/Reset registes: - */
1593 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1594 /* Set/Reset enable: - */
1595 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1596 /* Color Compare: - */
1597 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1598 /* Data Rotate: - */
1599 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1600 /* Read Map Select: - */
1601 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1602 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1603 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1604 /* Miscellaneous: memory map base address, graphics mode */
1605 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1606 /* Color Don't care: involve all planes */
1607 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1608 /* Bit Mask: no mask at all */
1609 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001610
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001611 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1612 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001613 /* (5434 can't have bit 3 set for bitblt) */
1614 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 /* Graphics controller mode extensions: finer granularity,
1617 * 8byte data latches
1618 */
1619 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
Krzysztof Helt8503df62007-10-16 01:29:08 -07001621 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1622 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1623 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1624 /* Background color byte 1: - */
1625 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1626 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
Krzysztof Helt8503df62007-10-16 01:29:08 -07001628 /* Attribute Controller palette registers: "identity mapping" */
1629 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1630 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1644 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
Krzysztof Helt8503df62007-10-16 01:29:08 -07001646 /* Attribute Controller mode: graphics mode */
1647 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1648 /* Overscan color reg.: reg. 0 */
1649 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1650 /* Color Plane enable: Enable all 4 planes */
1651 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001652 /* Color Select: - */
1653 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
Krzysztof Helt8503df62007-10-16 01:29:08 -07001655 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 /* BLT Start/status: Blitter reset */
1658 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1659 /* - " - : "end-of-reset" */
1660 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661
1662 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001663 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 return;
1665}
1666
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668{
1669#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1670 static int IsOn = 0; /* XXX not ok for multiple boards */
1671
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 if (cinfo->btype == BT_PICASSO4)
1673 return; /* nothing to switch */
1674 if (cinfo->btype == BT_ALPINE)
1675 return; /* nothing to switch */
1676 if (cinfo->btype == BT_GD5480)
1677 return; /* nothing to switch */
1678 if (cinfo->btype == BT_PICASSO) {
1679 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 return;
1682 }
1683 if (on) {
1684 switch (cinfo->btype) {
1685 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001686 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 break;
1688 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 break;
1691 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001692 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 break;
1694 default: /* do nothing */ break;
1695 }
1696 } else {
1697 switch (cinfo->btype) {
1698 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001699 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 break;
1701 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 break;
1704 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001707 default: /* do nothing */
1708 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 }
1710 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711#endif /* CONFIG_ZORRO */
1712}
1713
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714/******************************************/
1715/* Linux 2.6-style accelerated functions */
1716/******************************************/
1717
Krzysztof Helt8343c892009-03-31 15:25:11 -07001718static int cirrusfb_sync(struct fb_info *info)
1719{
1720 struct cirrusfb_info *cinfo = info->par;
1721
1722 if (!is_laguna(cinfo)) {
1723 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1724 cpu_relax();
1725 }
1726 return 0;
1727}
1728
Krzysztof Helt8503df62007-10-16 01:29:08 -07001729static void cirrusfb_fillrect(struct fb_info *info,
1730 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 struct fb_fillrect modded;
1733 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001734 struct cirrusfb_info *cinfo = info->par;
1735 int m = info->var.bits_per_pixel;
1736 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1737 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
1739 if (info->state != FBINFO_STATE_RUNNING)
1740 return;
1741 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1742 cfb_fillrect(info, region);
1743 return;
1744 }
1745
1746 vxres = info->var.xres_virtual;
1747 vyres = info->var.yres_virtual;
1748
1749 memcpy(&modded, region, sizeof(struct fb_fillrect));
1750
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 modded.dx >= vxres || modded.dy >= vyres)
1753 return;
1754
Krzysztof Helt8503df62007-10-16 01:29:08 -07001755 if (modded.dx + modded.width > vxres)
1756 modded.width = vxres - modded.dx;
1757 if (modded.dy + modded.height > vyres)
1758 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759
Krzysztof Helt060b6002007-10-16 01:29:13 -07001760 cirrusfb_RectFill(cinfo->regbase,
1761 info->var.bits_per_pixel,
1762 (region->dx * m) / 8, region->dy,
1763 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001764 color, color,
1765 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766}
1767
Krzysztof Helt8503df62007-10-16 01:29:08 -07001768static void cirrusfb_copyarea(struct fb_info *info,
1769 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 struct fb_copyarea modded;
1772 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001773 struct cirrusfb_info *cinfo = info->par;
1774 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775
1776 if (info->state != FBINFO_STATE_RUNNING)
1777 return;
1778 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1779 cfb_copyarea(info, area);
1780 return;
1781 }
1782
1783 vxres = info->var.xres_virtual;
1784 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001785 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786
Krzysztof Helt8503df62007-10-16 01:29:08 -07001787 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 modded.sx >= vxres || modded.sy >= vyres ||
1789 modded.dx >= vxres || modded.dy >= vyres)
1790 return;
1791
Krzysztof Helt8503df62007-10-16 01:29:08 -07001792 if (modded.sx + modded.width > vxres)
1793 modded.width = vxres - modded.sx;
1794 if (modded.dx + modded.width > vxres)
1795 modded.width = vxres - modded.dx;
1796 if (modded.sy + modded.height > vyres)
1797 modded.height = vyres - modded.sy;
1798 if (modded.dy + modded.height > vyres)
1799 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
Krzysztof Helt060b6002007-10-16 01:29:13 -07001801 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1802 (area->sx * m) / 8, area->sy,
1803 (area->dx * m) / 8, area->dy,
1804 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001805 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001806
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807}
1808
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809static void cirrusfb_imageblit(struct fb_info *info,
1810 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811{
1812 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001813 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
Krzysztof Helt9e848062009-03-31 15:25:11 -07001815 if (info->state != FBINFO_STATE_RUNNING)
1816 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001817 /* Alpine/SD64 does not work at 24bpp ??? */
1818 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1819 cfb_imageblit(info, image);
1820 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1821 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001822 cfb_imageblit(info, image);
1823 else {
1824 unsigned size = ((image->width + 7) >> 3) * image->height;
1825 int m = info->var.bits_per_pixel;
1826 u32 fg, bg;
1827
1828 if (info->var.bits_per_pixel == 8) {
1829 fg = image->fg_color;
1830 bg = image->bg_color;
1831 } else {
1832 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1833 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1834 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001835 if (info->var.bits_per_pixel == 24) {
1836 /* clear background first */
1837 cirrusfb_RectFill(cinfo->regbase,
1838 info->var.bits_per_pixel,
1839 (image->dx * m) / 8, image->dy,
1840 (image->width * m) / 8,
1841 image->height,
1842 bg, bg,
1843 info->fix.line_length, 0x40);
1844 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001845 cirrusfb_RectFill(cinfo->regbase,
1846 info->var.bits_per_pixel,
1847 (image->dx * m) / 8, image->dy,
1848 (image->width * m) / 8, image->height,
1849 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001850 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001851 memcpy(info->screen_base, image->data, size);
1852 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853}
1854
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855#ifdef CONFIG_PPC_PREP
1856#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1857#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001858static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 *display = PREP_VIDEO_BASE;
1861 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862}
1863
1864#endif /* CONFIG_PPC_PREP */
1865
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001867static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868
1869/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1870 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1871 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1872 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001873static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1874 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875{
1876 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001877 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001879 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001880 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1881
1882 mem = ((SR14 & 7) + 1) << 20;
1883 } else {
1884 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1885 switch ((SRF & 0x18)) {
1886 case 0x08:
1887 mem = 512 * 1024;
1888 break;
1889 case 0x10:
1890 mem = 1024 * 1024;
1891 break;
1892 /* 64-bit DRAM data bus width; assume 2MB.
1893 * Also indicates 2MB memory on the 5430.
1894 */
1895 case 0x18:
1896 mem = 2048 * 1024;
1897 break;
1898 default:
1899 dev_warn(info->device, "Unknown memory size!\n");
1900 mem = 1024 * 1024;
1901 }
1902 /* If DRAM bank switching is enabled, there must be
1903 * twice as much memory installed. (4MB on the 5434)
1904 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001905 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001906 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001908
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 return mem;
1911}
1912
Krzysztof Helt8503df62007-10-16 01:29:08 -07001913static void get_pci_addrs(const struct pci_dev *pdev,
1914 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001916 assert(pdev != NULL);
1917 assert(display != NULL);
1918 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 *display = 0;
1921 *registers = 0;
1922
1923 /* This is a best-guess for now */
1924
1925 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1926 *display = pci_resource_start(pdev, 1);
1927 *registers = pci_resource_start(pdev, 0);
1928 } else {
1929 *display = pci_resource_start(pdev, 0);
1930 *registers = pci_resource_start(pdev, 1);
1931 }
1932
Krzysztof Helt8503df62007-10-16 01:29:08 -07001933 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934}
1935
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001936static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001938 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001939 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001941 if (cinfo->laguna_mmio == NULL)
1942 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001943 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944#if 0 /* if system didn't claim this region, we would... */
1945 release_mem_region(0xA0000, 65535);
1946#endif
1947 if (release_io_ports)
1948 release_region(0x3C0, 32);
1949 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950}
1951#endif /* CONFIG_PCI */
1952
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001954static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Al Virod91f5bb2007-10-17 00:27:18 +01001956 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001957 struct zorro_dev *zdev = to_zorro_dev(info->device);
1958
1959 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960
1961 if (cinfo->btype == BT_PICASSO4) {
1962 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001963 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001964 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001966 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001967 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969}
1970#endif /* CONFIG_ZORRO */
1971
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001972/* function table of the above functions */
1973static struct fb_ops cirrusfb_ops = {
1974 .owner = THIS_MODULE,
1975 .fb_open = cirrusfb_open,
1976 .fb_release = cirrusfb_release,
1977 .fb_setcolreg = cirrusfb_setcolreg,
1978 .fb_check_var = cirrusfb_check_var,
1979 .fb_set_par = cirrusfb_set_par,
1980 .fb_pan_display = cirrusfb_pan_display,
1981 .fb_blank = cirrusfb_blank,
1982 .fb_fillrect = cirrusfb_fillrect,
1983 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001984 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001985 .fb_imageblit = cirrusfb_imageblit,
1986};
1987
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001988static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001990 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 struct fb_var_screeninfo *var = &info->var;
1992
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 info->pseudo_palette = cinfo->pseudo_palette;
1994 info->flags = FBINFO_DEFAULT
1995 | FBINFO_HWACCEL_XPAN
1996 | FBINFO_HWACCEL_YPAN
1997 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07001998 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002000 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002002 info->fix.accel = FB_ACCEL_NONE;
2003 } else
2004 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
2005
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002007
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 if (cinfo->btype == BT_GD5480) {
2009 if (var->bits_per_pixel == 16)
2010 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002011 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 info->screen_base += 2 * MB_;
2013 }
2014
2015 /* Fill fix common fields */
2016 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2017 sizeof(info->fix.id));
2018
2019 /* monochrome: only 1 memory plane */
2020 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002021 info->fix.smem_len = info->screen_size;
2022 if (var->bits_per_pixel == 1)
2023 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 info->fix.xpanstep = 1;
2026 info->fix.ypanstep = 1;
2027 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028
2029 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031
2032 fb_alloc_cmap(&info->cmap, 256, 0);
2033
2034 return 0;
2035}
2036
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002037static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002039 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041
2042 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002043 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002045 /* set all the vital stuff */
2046 cirrusfb_set_fbinfo(info);
2047
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002048 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002050 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2051 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002052 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002053 err = -EINVAL;
2054 goto err_dealloc_cmap;
2055 }
2056
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 info->var.activate = FB_ACTIVATE_NOW;
2058
Krzysztof Helt99a45842009-03-31 15:25:09 -07002059 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 if (err < 0) {
2061 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002062 dev_dbg(info->device,
2063 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002064 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 }
2066
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 err = register_framebuffer(info);
2068 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002069 dev_err(info->device,
2070 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 goto err_dealloc_cmap;
2072 }
2073
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 return 0;
2075
2076err_dealloc_cmap:
2077 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 return err;
2079}
2080
Krzysztof Helt8503df62007-10-16 01:29:08 -07002081static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082{
2083 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084
Krzysztof Helt8503df62007-10-16 01:29:08 -07002085 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002086 unregister_framebuffer(info);
2087 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002088 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002089 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002090 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091}
2092
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002094static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2095 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096{
2097 struct cirrusfb_info *cinfo;
2098 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 unsigned long board_addr, board_size;
2100 int ret;
2101
2102 ret = pci_enable_device(pdev);
2103 if (ret < 0) {
2104 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2105 goto err_out;
2106 }
2107
2108 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2109 if (!info) {
2110 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2111 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002112 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 }
2114
2115 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002116 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002118 dev_dbg(info->device,
2119 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002120 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002121 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2122 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123
Krzysztof Helt8503df62007-10-16 01:29:08 -07002124 if (isPReP) {
2125 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002127 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002129 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002130 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002132 dev_dbg(info->device,
2133 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002134 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002135 /* FIXME: this forces VGA. alternatives? */
2136 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002137 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 }
2139
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002140 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002141 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002143 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002144 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145
2146 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002147 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002148 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2149 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 goto err_release_fb;
2151 }
2152#if 0 /* if the system didn't claim this region, we would... */
2153 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002154 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2155 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 ret = -EBUSY;
2157 goto err_release_regions;
2158 }
2159#endif
2160 if (request_region(0x3C0, 32, "cirrusfb"))
2161 release_io_ports = 1;
2162
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002163 info->screen_base = ioremap(board_addr, board_size);
2164 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165 ret = -EIO;
2166 goto err_release_legacy;
2167 }
2168
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002169 info->fix.smem_start = board_addr;
2170 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 cinfo->unmap = cirrusfb_pci_unmap;
2172
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002173 dev_info(info->device,
2174 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2175 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 pci_set_drvdata(pdev, info);
2177
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002178 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002179 if (!ret)
2180 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002182 pci_set_drvdata(pdev, NULL);
2183 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184err_release_legacy:
2185 if (release_io_ports)
2186 release_region(0x3C0, 32);
2187#if 0
2188 release_mem_region(0xA0000, 65535);
2189err_release_regions:
2190#endif
2191 pci_release_regions(pdev);
2192err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002193 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002194 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196err_out:
2197 return ret;
2198}
2199
Krzysztof Helt8503df62007-10-16 01:29:08 -07002200static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201{
2202 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203
Krzysztof Helt8503df62007-10-16 01:29:08 -07002204 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205}
2206
2207static struct pci_driver cirrusfb_pci_driver = {
2208 .name = "cirrusfb",
2209 .id_table = cirrusfb_pci_table,
2210 .probe = cirrusfb_pci_register,
2211 .remove = __devexit_p(cirrusfb_pci_unregister),
2212#ifdef CONFIG_PM
2213#if 0
2214 .suspend = cirrusfb_pci_suspend,
2215 .resume = cirrusfb_pci_resume,
2216#endif
2217#endif
2218};
2219#endif /* CONFIG_PCI */
2220
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002222static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2223 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224{
2225 struct cirrusfb_info *cinfo;
2226 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002227 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 struct zorro_dev *z2 = NULL;
2229 unsigned long board_addr, board_size, size;
2230 int ret;
2231
2232 btype = ent->driver_data;
2233 if (cirrusfb_zorro_table2[btype].id2)
2234 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2235 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236
2237 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2238 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002239 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 ret = -ENOMEM;
2241 goto err_out;
2242 }
2243
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002244 dev_info(info->device, "%s board detected\n",
2245 cirrusfb_board_info[btype].name);
2246
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248 cinfo->btype = btype;
2249
Al Viro36ea96a2007-10-27 19:46:58 +01002250 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002251 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 board_addr = zorro_resource_start(z);
2254 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002255 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256
2257 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002258 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2259 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 ret = -EBUSY;
2261 goto err_release_fb;
2262 }
2263
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 ret = -EIO;
2265
2266 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002267 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268
2269 /* To be precise, for the P4 this is not the */
2270 /* begin of the board, but the begin of RAM. */
2271 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2272 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002273 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 if (!cinfo->regbase)
2275 goto err_release_region;
2276
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002277 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002278 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002280 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002282 info->fix.smem_start = board_addr + 16777216;
2283 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2284 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285 goto err_unmap_regbase;
2286 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002287 dev_info(info->device, " REG at $%lx\n",
2288 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002290 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002292 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002294 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2295 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 goto err_release_region;
2297
2298 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002299 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002300 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002302 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002303 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 }
2305 cinfo->unmap = cirrusfb_zorro_unmap;
2306
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002307 dev_info(info->device,
2308 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2309 board_size / MB_, board_addr);
2310
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311 zorro_set_drvdata(z, info);
2312
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002313 /* MCLK select etc. */
2314 if (cirrusfb_board_info[btype].init_sr1f)
2315 vga_wseq(cinfo->regbase, CL_SEQR1F,
2316 cirrusfb_board_info[btype].sr1f);
2317
Al Virod91f5bb2007-10-17 00:27:18 +01002318 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002319 if (!ret)
2320 return 0;
2321
2322 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2323 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324
2325err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002326 if (btype == BT_PICASSO4)
2327 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328err_release_region:
2329 release_region(board_addr, board_size);
2330err_release_fb:
2331 framebuffer_release(info);
2332err_out:
2333 return ret;
2334}
2335
2336void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2337{
2338 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339
Krzysztof Helt8503df62007-10-16 01:29:08 -07002340 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341}
2342
2343static struct zorro_driver cirrusfb_zorro_driver = {
2344 .name = "cirrusfb",
2345 .id_table = cirrusfb_zorro_table,
2346 .probe = cirrusfb_zorro_register,
2347 .remove = __devexit_p(cirrusfb_zorro_unregister),
2348};
2349#endif /* CONFIG_ZORRO */
2350
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002352static int __init cirrusfb_setup(char *options)
2353{
Vlada Pericee119402008-11-19 15:36:45 -08002354 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 if (!options || !*options)
2357 return 0;
2358
Krzysztof Helt8503df62007-10-16 01:29:08 -07002359 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002360 if (!*this_opt)
2361 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363 if (!strcmp(this_opt, "noaccel"))
2364 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002365 else if (!strncmp(this_opt, "mode:", 5))
2366 mode_option = this_opt + 5;
2367 else
2368 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369 }
2370 return 0;
2371}
2372#endif
2373
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374 /*
2375 * Modularization
2376 */
2377
2378MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2379MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2380MODULE_LICENSE("GPL");
2381
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002382static int __init cirrusfb_init(void)
2383{
2384 int error = 0;
2385
2386#ifndef MODULE
2387 char *option = NULL;
2388
2389 if (fb_get_options("cirrusfb", &option))
2390 return -ENODEV;
2391 cirrusfb_setup(option);
2392#endif
2393
2394#ifdef CONFIG_ZORRO
2395 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2396#endif
2397#ifdef CONFIG_PCI
2398 error |= pci_register_driver(&cirrusfb_pci_driver);
2399#endif
2400 return error;
2401}
2402
Krzysztof Helt8503df62007-10-16 01:29:08 -07002403static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404{
2405#ifdef CONFIG_PCI
2406 pci_unregister_driver(&cirrusfb_pci_driver);
2407#endif
2408#ifdef CONFIG_ZORRO
2409 zorro_unregister_driver(&cirrusfb_zorro_driver);
2410#endif
2411}
2412
2413module_init(cirrusfb_init);
2414
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002415module_param(mode_option, charp, 0);
2416MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002417module_param(noaccel, bool, 0);
2418MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002419
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420#ifdef MODULE
2421module_exit(cirrusfb_exit);
2422#endif
2423
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424/**********************************************************************/
2425/* about the following functions - I have used the same names for the */
2426/* functions as Markus Wild did in his Retina driver for NetBSD as */
2427/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002428/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429/**********************************************************************/
2430
2431/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002432static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433 int regnum, unsigned char val)
2434{
2435 unsigned long regofs = 0;
2436
2437 if (cinfo->btype == BT_PICASSO) {
2438 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002439/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2440 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2442 regofs = 0xfff;
2443 }
2444
Krzysztof Helt8503df62007-10-16 01:29:08 -07002445 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446}
2447
2448/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002449static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450{
2451 unsigned long regofs = 0;
2452
2453 if (cinfo->btype == BT_PICASSO) {
2454 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002455/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2456 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2458 regofs = 0xfff;
2459 }
2460
Krzysztof Helt8503df62007-10-16 01:29:08 -07002461 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462}
2463
2464/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002465static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002467 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 /* if we're just in "write value" mode, write back the */
2471 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002472 vga_w(cinfo->regbase, VGA_ATT_IW,
2473 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 }
2475 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2477 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478
2479 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481}
2482
2483/*** WHDR() - write into the Hidden DAC register ***/
2484/* as the HDR is the only extension register that requires special treatment
2485 * (the other extension registers are accessible just like the "ordinary"
2486 * registers of their functional group) here is a specialized routine for
2487 * accessing the HDR
2488 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002489static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490{
2491 unsigned char dummy;
2492
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002493 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002494 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 if (cinfo->btype == BT_PICASSO) {
2496 /* Klaus' hint for correct access to HDR on some boards */
2497 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002498 WGen(cinfo, VGA_PEL_MSK, 0x00);
2499 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002501 dummy = RGen(cinfo, VGA_PEL_IW);
2502 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 }
2504 /* now do the usual stuff to access the HDR */
2505
Krzysztof Helt8503df62007-10-16 01:29:08 -07002506 dummy = RGen(cinfo, VGA_PEL_MSK);
2507 udelay(200);
2508 dummy = RGen(cinfo, VGA_PEL_MSK);
2509 udelay(200);
2510 dummy = RGen(cinfo, VGA_PEL_MSK);
2511 udelay(200);
2512 dummy = RGen(cinfo, VGA_PEL_MSK);
2513 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514
Krzysztof Helt8503df62007-10-16 01:29:08 -07002515 WGen(cinfo, VGA_PEL_MSK, val);
2516 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517
2518 if (cinfo->btype == BT_PICASSO) {
2519 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520 dummy = RGen(cinfo, VGA_PEL_IW);
2521 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522
2523 /* and at the end, restore the mask value */
2524 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002525 WGen(cinfo, VGA_PEL_MSK, 0xff);
2526 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527 }
2528}
2529
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002531static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532{
2533#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002534 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002536 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537#endif
2538}
2539
2540/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542{
2543#ifdef CONFIG_ZORRO
2544 /* writing an arbitrary value to this one causes the monitor switcher */
2545 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549#endif
2550}
2551
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 unsigned char green, unsigned char blue)
2555{
2556 unsigned int data = VGA_PEL_D;
2557
2558 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002559 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560
2561 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002562 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002563 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564 /* but DAC data register IS, at least for Picasso II */
2565 if (cinfo->btype == BT_PICASSO)
2566 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002567 vga_w(cinfo->regbase, data, red);
2568 vga_w(cinfo->regbase, data, green);
2569 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002571 vga_w(cinfo->regbase, data, blue);
2572 vga_w(cinfo->regbase, data, green);
2573 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 }
2575}
2576
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577#if 0
2578/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 unsigned char *green, unsigned char *blue)
2581{
2582 unsigned int data = VGA_PEL_D;
2583
Krzysztof Helt8503df62007-10-16 01:29:08 -07002584 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585
2586 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2587 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2588 if (cinfo->btype == BT_PICASSO)
2589 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590 *red = vga_r(cinfo->regbase, data);
2591 *green = vga_r(cinfo->regbase, data);
2592 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002594 *blue = vga_r(cinfo->regbase, data);
2595 *green = vga_r(cinfo->regbase, data);
2596 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597 }
2598}
2599#endif
2600
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601/*******************************************************************
2602 cirrusfb_WaitBLT()
2603
2604 Wait for the BitBLT engine to complete a possible earlier job
2605*********************************************************************/
2606
2607/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002608static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002610 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002611 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612}
2613
2614/*******************************************************************
2615 cirrusfb_BitBLT()
2616
2617 perform accelerated "scrolling"
2618********************************************************************/
2619
Krzysztof Helt8343c892009-03-31 15:25:11 -07002620static void cirrusfb_set_blitter(u8 __iomem *regbase,
2621 u_short nwidth, u_short nheight,
2622 u_long nsrc, u_long ndest,
2623 u_short bltmode, u_short line_length)
2624
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002627 /* dest pitch low */
2628 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2629 /* dest pitch hi */
2630 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2631 /* source pitch low */
2632 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2633 /* source pitch hi */
2634 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635
2636 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002637 /* BLT width low */
2638 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2639 /* BLT width hi */
2640 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641
2642 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002643 /* BLT height low */
2644 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2645 /* BLT width hi */
2646 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647
2648 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002649 /* BLT dest low */
2650 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2651 /* BLT dest mid */
2652 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2653 /* BLT dest hi */
2654 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655
2656 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002657 /* BLT src low */
2658 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2659 /* BLT src mid */
2660 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2661 /* BLT src hi */
2662 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663
2664 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002665 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666
2667 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002668 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669
2670 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002671 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002672}
2673
2674/*******************************************************************
2675 cirrusfb_BitBLT()
2676
2677 perform accelerated "scrolling"
2678********************************************************************/
2679
2680static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2681 u_short curx, u_short cury,
2682 u_short destx, u_short desty,
2683 u_short width, u_short height,
2684 u_short line_length)
2685{
2686 u_short nwidth = width - 1;
2687 u_short nheight = height - 1;
2688 u_long nsrc, ndest;
2689 u_char bltmode;
2690
2691 bltmode = 0x00;
2692 /* if source adr < dest addr, do the Blt backwards */
2693 if (cury <= desty) {
2694 if (cury == desty) {
2695 /* if src and dest are on the same line, check x */
2696 if (curx < destx)
2697 bltmode |= 0x01;
2698 } else
2699 bltmode |= 0x01;
2700 }
2701 /* standard case: forward blitting */
2702 nsrc = (cury * line_length) + curx;
2703 ndest = (desty * line_length) + destx;
2704 if (bltmode) {
2705 /* this means start addresses are at the end,
2706 * counting backwards
2707 */
2708 nsrc += nheight * line_length + nwidth;
2709 ndest += nheight * line_length + nwidth;
2710 }
2711
2712 cirrusfb_WaitBLT(regbase);
2713
2714 cirrusfb_set_blitter(regbase, nwidth, nheight,
2715 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716}
2717
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718/*******************************************************************
2719 cirrusfb_RectFill()
2720
2721 perform accelerated rectangle fill
2722********************************************************************/
2723
Krzysztof Helt8503df62007-10-16 01:29:08 -07002724static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002726 u32 fg_color, u32 bg_color, u_short line_length,
2727 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002729 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 u_char op;
2731
Krzysztof Helt8503df62007-10-16 01:29:08 -07002732 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734 /* This is a ColorExpand Blt, using the */
2735 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002736 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2737 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738
Krzysztof Helt9e848062009-03-31 15:25:11 -07002739 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002740 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002741 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2742 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2743 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002744 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002745 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002746 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2747 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002748 op = 0xa0;
2749 }
2750 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002751 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2752 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2753 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002755 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002756 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757}
2758
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759/**************************************************************************
2760 * bestclock() - determine closest possible clock lower(?) than the
2761 * desired pixel clock
2762 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002763static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002765 int n, d;
2766 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767
Krzysztof Helt8503df62007-10-16 01:29:08 -07002768 assert(nom != NULL);
2769 assert(den != NULL);
2770 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771
2772 *nom = 0;
2773 *den = 0;
2774 *div = 0;
2775
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 if (freq < 8000)
2777 freq = 8000;
2778
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002779 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780
2781 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002782 int s = 0;
2783
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002784 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002786 int temp = d;
2787
2788 if (temp > 31) {
2789 s = 1;
2790 temp >>= 1;
2791 }
2792 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002793 h = h > freq ? h - freq : freq - h;
2794 if (h < diff) {
2795 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002797 *den = temp;
2798 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 }
2800 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002801 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002803 if (d > 31) {
2804 s = 1;
2805 d >>= 1;
2806 }
2807 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002808 h = h > freq ? h - freq : freq - h;
2809 if (h < diff) {
2810 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002812 *den = d;
2813 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 }
2815 }
2816 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817}
2818
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819/* -------------------------------------------------------------------------
2820 *
2821 * debugging functions
2822 *
2823 * -------------------------------------------------------------------------
2824 */
2825
2826#ifdef CIRRUSFB_DEBUG
2827
2828/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 * cirrusfb_dbg_print_regs
2830 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2831 * @reg_class: type of registers to read: %CRT, or %SEQ
2832 *
2833 * DESCRIPTION:
2834 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2835 * old-style I/O ports are queried for information, otherwise MMIO is
2836 * used at the given @base address to query the information.
2837 */
2838
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002839static void cirrusfb_dbg_print_regs(struct fb_info *info,
2840 caddr_t regbase,
2841 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842{
2843 va_list list;
2844 unsigned char val = 0;
2845 unsigned reg;
2846 char *name;
2847
Krzysztof Helt8503df62007-10-16 01:29:08 -07002848 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849
Krzysztof Helt8503df62007-10-16 01:29:08 -07002850 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002852 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853
2854 switch (reg_class) {
2855 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002856 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 break;
2858 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002859 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860 break;
2861 default:
2862 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002863 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 break;
2865 }
2866
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002867 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868
Krzysztof Helt8503df62007-10-16 01:29:08 -07002869 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870 }
2871
Krzysztof Helt8503df62007-10-16 01:29:08 -07002872 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873}
2874
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 * cirrusfb_dbg_reg_dump
2877 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2878 *
2879 * DESCRIPTION:
2880 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2881 * old-style I/O ports are queried for information, otherwise MMIO is
2882 * used at the given @base address to query the information.
2883 */
2884
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002885static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002887 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002889 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 "CR00", 0x00,
2891 "CR01", 0x01,
2892 "CR02", 0x02,
2893 "CR03", 0x03,
2894 "CR04", 0x04,
2895 "CR05", 0x05,
2896 "CR06", 0x06,
2897 "CR07", 0x07,
2898 "CR08", 0x08,
2899 "CR09", 0x09,
2900 "CR0A", 0x0A,
2901 "CR0B", 0x0B,
2902 "CR0C", 0x0C,
2903 "CR0D", 0x0D,
2904 "CR0E", 0x0E,
2905 "CR0F", 0x0F,
2906 "CR10", 0x10,
2907 "CR11", 0x11,
2908 "CR12", 0x12,
2909 "CR13", 0x13,
2910 "CR14", 0x14,
2911 "CR15", 0x15,
2912 "CR16", 0x16,
2913 "CR17", 0x17,
2914 "CR18", 0x18,
2915 "CR22", 0x22,
2916 "CR24", 0x24,
2917 "CR26", 0x26,
2918 "CR2D", 0x2D,
2919 "CR2E", 0x2E,
2920 "CR2F", 0x2F,
2921 "CR30", 0x30,
2922 "CR31", 0x31,
2923 "CR32", 0x32,
2924 "CR33", 0x33,
2925 "CR34", 0x34,
2926 "CR35", 0x35,
2927 "CR36", 0x36,
2928 "CR37", 0x37,
2929 "CR38", 0x38,
2930 "CR39", 0x39,
2931 "CR3A", 0x3A,
2932 "CR3B", 0x3B,
2933 "CR3C", 0x3C,
2934 "CR3D", 0x3D,
2935 "CR3E", 0x3E,
2936 "CR3F", 0x3F,
2937 NULL);
2938
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002939 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002941 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002943 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944 "SR00", 0x00,
2945 "SR01", 0x01,
2946 "SR02", 0x02,
2947 "SR03", 0x03,
2948 "SR04", 0x04,
2949 "SR08", 0x08,
2950 "SR09", 0x09,
2951 "SR0A", 0x0A,
2952 "SR0B", 0x0B,
2953 "SR0D", 0x0D,
2954 "SR10", 0x10,
2955 "SR11", 0x11,
2956 "SR12", 0x12,
2957 "SR13", 0x13,
2958 "SR14", 0x14,
2959 "SR15", 0x15,
2960 "SR16", 0x16,
2961 "SR17", 0x17,
2962 "SR18", 0x18,
2963 "SR19", 0x19,
2964 "SR1A", 0x1A,
2965 "SR1B", 0x1B,
2966 "SR1C", 0x1C,
2967 "SR1D", 0x1D,
2968 "SR1E", 0x1E,
2969 "SR1F", 0x1F,
2970 NULL);
2971
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002972 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973}
2974
2975#endif /* CIRRUSFB_DEBUG */
2976