blob: 8d8dfda2f86801648d1b4a3d0779b769fab30a88 [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};
302
303static const struct {
304 zorro_id id2;
305 unsigned long size;
306} cirrusfb_zorro_table2[] = {
307 [BT_SD64] = {
308 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
309 .size = 0x400000
310 },
311 [BT_PICCOLO] = {
312 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
313 .size = 0x200000
314 },
315 [BT_PICASSO] = {
316 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
317 .size = 0x200000
318 },
319 [BT_SPECTRUM] = {
320 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
321 .size = 0x200000
322 },
323 [BT_PICASSO4] = {
324 .id2 = 0,
325 .size = 0x400000
326 }
327};
328#endif /* CONFIG_ZORRO */
329
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700331enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700332 CRT,
333 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700334};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700335#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337/* info about board */
338struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700340 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700341 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 unsigned char SFR; /* Shadow of special function register */
343
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700344 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700345 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700347 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700349 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350};
351
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700352static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700353static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
355/****************************************************************************/
356/**** BEGIN PROTOTYPES ******************************************************/
357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700359static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
360 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700363static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700364static void switch_monitor(struct cirrusfb_info *cinfo, int on);
365static void WGen(const struct cirrusfb_info *cinfo,
366 int regnum, unsigned char val);
367static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
368static void AttrOn(const struct cirrusfb_info *cinfo);
369static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
370static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
371static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
372static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
373 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700375static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
376 unsigned char *red, unsigned char *green,
377 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700379static void cirrusfb_WaitBLT(u8 __iomem *regbase);
380static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
381 u_short curx, u_short cury,
382 u_short destx, u_short desty,
383 u_short width, u_short height,
384 u_short line_length);
385static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
386 u_short x, u_short y,
387 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700388 u32 fg_color, u32 bg_color,
389 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700391static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
393#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700394static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
395static void cirrusfb_dbg_print_regs(struct fb_info *info,
396 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700397 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398#endif /* CIRRUSFB_DEBUG */
399
400/*** END PROTOTYPES ********************************************************/
401/*****************************************************************************/
402/*** BEGIN Interface Used by the World ***************************************/
403
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700404static inline int is_laguna(const struct cirrusfb_info *cinfo)
405{
406 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
407}
408
Krzysztof Helt8503df62007-10-16 01:29:08 -0700409static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
411/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700412static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413{
414 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700415 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 return 0;
417}
418
419/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700420static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421{
422 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700423 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 return 0;
425}
426
427/**** END Interface used by the World *************************************/
428/****************************************************************************/
429/**** BEGIN Hardware specific Routines **************************************/
430
Krzysztof Helt486ff382008-10-15 22:03:42 -0700431/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700432static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700434 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700435 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Krzysztof Helt486ff382008-10-15 22:03:42 -0700437 /* Read MCLK value */
438 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700439 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
441 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700442 * should divide it by to get VCLK
443 */
444
445 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700446 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700447 return 1;
448 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700449 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700450 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 }
452
Krzysztof Helt486ff382008-10-15 22:03:42 -0700453 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454}
455
Krzysztof Helt99a45842009-03-31 15:25:09 -0700456static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
457 struct fb_info *info)
458{
459 long freq;
460 long maxclock;
461 struct cirrusfb_info *cinfo = info->par;
462 unsigned maxclockidx = var->bits_per_pixel >> 3;
463
464 /* convert from ps to kHz */
465 freq = PICOS2KHZ(var->pixclock);
466
467 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
468
469 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
470 cinfo->multiplexing = 0;
471
472 /* If the frequency is greater than we can support, we might be able
473 * to use multiplexing for the video mode */
474 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700475 dev_err(info->device,
476 "Frequency greater than maxclock (%ld kHz)\n",
477 maxclock);
478 return -EINVAL;
479 }
480 /*
481 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
482 * pixel clock
483 */
484 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700485 switch (cinfo->btype) {
486 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700487 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700488 case BT_PICASSO4:
489 if (freq > 85500)
490 cinfo->multiplexing = 1;
491 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700492 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700493 if (freq > 135100)
494 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700495 break;
496
497 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700498 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700499 }
500 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700501
502 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700503 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700504 cinfo->doubleVCLK = 0;
505 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
506 var->bits_per_pixel == 16) {
507 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700508 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700509
Krzysztof Helt99a45842009-03-31 15:25:09 -0700510 return 0;
511}
512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513static int cirrusfb_check_var(struct fb_var_screeninfo *var,
514 struct fb_info *info)
515{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700516 int yres;
517 /* memory size in pixels */
518 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700519 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
521 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700522 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 var->red.offset = 0;
524 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700525 var->green = var->red;
526 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 break;
528
529 case 8:
530 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700531 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700532 var->green = var->red;
533 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 break;
535
536 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700537 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 var->red.offset = 2;
539 var->green.offset = -3;
540 var->blue.offset = 8;
541 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700542 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 var->green.offset = 5;
544 var->blue.offset = 0;
545 }
546 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700547 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 var->blue.length = 5;
549 break;
550
Krzysztof Helt7cade312009-03-31 15:25:13 -0700551 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700552 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700553 var->red.offset = 0;
554 var->green.offset = 8;
555 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 } else {
557 var->red.offset = 16;
558 var->green.offset = 8;
559 var->blue.offset = 0;
560 }
561 var->red.length = 8;
562 var->green.length = 8;
563 var->blue.length = 8;
564 break;
565
566 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700567 dev_dbg(info->device,
568 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Krzysztof Helt0efb2a02009-04-13 14:39:55 -0700569 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 }
571
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700572 if (var->xres_virtual < var->xres)
573 var->xres_virtual = var->xres;
574 /* use highest possible virtual resolution */
575 if (var->yres_virtual == -1) {
576 var->yres_virtual = pixels / var->xres_virtual;
577
578 dev_info(info->device,
579 "virtual resolution set to maximum of %dx%d\n",
580 var->xres_virtual, var->yres_virtual);
581 }
582 if (var->yres_virtual < var->yres)
583 var->yres_virtual = var->yres;
584
585 if (var->xres_virtual * var->yres_virtual > pixels) {
586 dev_err(info->device, "mode %dx%dx%d rejected... "
587 "virtual resolution too high to fit into video memory!\n",
588 var->xres_virtual, var->yres_virtual,
589 var->bits_per_pixel);
590 return -EINVAL;
591 }
592
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700593 if (var->xoffset < 0)
594 var->xoffset = 0;
595 if (var->yoffset < 0)
596 var->yoffset = 0;
597
598 /* truncate xoffset and yoffset to maximum if too high */
599 if (var->xoffset > var->xres_virtual - var->xres)
600 var->xoffset = var->xres_virtual - var->xres - 1;
601 if (var->yoffset > var->yres_virtual - var->yres)
602 var->yoffset = var->yres_virtual - var->yres - 1;
603
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 var->red.msb_right =
605 var->green.msb_right =
606 var->blue.msb_right =
607 var->transp.offset =
608 var->transp.length =
609 var->transp.msb_right = 0;
610
611 yres = var->yres;
612 if (var->vmode & FB_VMODE_DOUBLE)
613 yres *= 2;
614 else if (var->vmode & FB_VMODE_INTERLACED)
615 yres = (yres + 1) / 2;
616
617 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700618 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700619 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 return -EINVAL;
621 }
622
Krzysztof Helt99a45842009-03-31 15:25:09 -0700623 if (cirrusfb_check_pixclock(var, info))
624 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700626 if (!is_laguna(cinfo))
627 var->accel_flags = FB_ACCELF_TEXT;
628
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 return 0;
630}
631
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700632static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700634 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700635 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700636
Krzysztof Helt8503df62007-10-16 01:29:08 -0700637 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700638 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Krzysztof Helt486ff382008-10-15 22:03:42 -0700640 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700641 dev_dbg(info->device, "Set %s as pixclock source.\n",
642 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700643 old1f |= 0x40;
644 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
645 if (div == 2)
646 old1e |= 1;
647
648 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700650 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651}
652
653/*************************************************************************
654 cirrusfb_set_par_foo()
655
656 actually writes the values for a new video mode into the hardware,
657**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700658static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
660 struct cirrusfb_info *cinfo = info->par;
661 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 u8 __iomem *regbase = cinfo->regbase;
663 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700664 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700666 int hdispend, hsyncstart, hsyncend, htotal;
667 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700668 long freq;
669 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700670 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700672 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700674
675 switch (var->bits_per_pixel) {
676 case 1:
677 info->fix.line_length = var->xres_virtual / 8;
678 info->fix.visual = FB_VISUAL_MONO10;
679 break;
680
681 case 8:
682 info->fix.line_length = var->xres_virtual;
683 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
684 break;
685
686 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700687 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700688 info->fix.line_length = var->xres_virtual *
689 var->bits_per_pixel >> 3;
690 info->fix.visual = FB_VISUAL_TRUECOLOR;
691 break;
692 }
693 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700695 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 bi = &cirrusfb_board_info[cinfo->btype];
698
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700699 hsyncstart = var->xres + var->right_margin;
700 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700701 htotal = (hsyncend + var->left_margin) / 8;
702 hdispend = var->xres / 8;
703 hsyncstart = hsyncstart / 8;
704 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700705
Krzysztof Helt8636a922009-03-31 15:25:17 -0700706 vdispend = var->yres;
707 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700708 vsyncend = vsyncstart + var->vsync_len;
709 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700710
711 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700712 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700713 vsyncstart *= 2;
714 vsyncend *= 2;
715 vtotal *= 2;
716 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700717 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700718 vsyncstart = (vsyncstart + 1) / 2;
719 vsyncend = (vsyncend + 1) / 2;
720 vtotal = (vtotal + 1) / 2;
721 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700722 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700723 if (yres >= 1024) {
724 vtotal /= 2;
725 vsyncstart /= 2;
726 vsyncend /= 2;
727 vdispend /= 2;
728 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700729
730 vdispend -= 1;
731 vsyncstart -= 1;
732 vsyncend -= 1;
733 vtotal -= 2;
734
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700735 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700736 htotal /= 2;
737 hsyncstart /= 2;
738 hsyncend /= 2;
739 hdispend /= 2;
740 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700741
742 htotal -= 5;
743 hdispend -= 1;
744 hsyncstart += 1;
745 hsyncend += 1;
746
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700748 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
750 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700751 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700752 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700754 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700755 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700757 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700758 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Krzysztof Helt8503df62007-10-16 01:29:08 -0700760 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700761 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700762 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700763 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700765 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700766 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700768 tmp = hsyncend % 32;
769 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700771 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700772 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700774 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700775 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
777 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700782 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700788 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700792 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700793 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700796 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 tmp |= 0x20;
798 if (var->vmode & FB_VMODE_DOUBLE)
799 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700800 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700801 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700803 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700806 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700809 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700810 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700812 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700813 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700816 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700818 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700819 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
821 tmp = 0;
822 if (var->vmode & FB_VMODE_INTERLACED)
823 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700826 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700828 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700830 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 tmp |= 128;
832
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700833 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700836 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700837 if (var->bits_per_pixel == 24)
838 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
839 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700840 if (cinfo->multiplexing)
841 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700842 if (cinfo->doubleVCLK)
843 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700844
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700845 bestclock(freq, &nom, &den, &div);
846
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700847 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
848 freq, nom, den, div);
849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 /* set VCLK0 */
851 /* hardware RefClock: 14.31818 MHz */
852 /* formula: VClk = (OSC * N) / (D * (1+P)) */
853 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
854
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700855 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
856 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700857 /* if freq is close to mclk or mclk/2 select mclk
858 * as clock source
859 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700860 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700861 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700862 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700863 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700864 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700865 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700866 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
867 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
868 unsigned short tile_control;
869
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700870 if (cinfo->btype == BT_LAGUNAB) {
871 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
872 tile_control &= ~0x80;
873 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
874 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700875
876 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
877 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
878 control = fb_readw(cinfo->laguna_mmio + 0x402);
879 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
880 control &= ~0x6800;
881 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700882 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700883 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700884 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700885 tmp = den << 1;
886 if (div != 0)
887 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700888 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
889 if ((cinfo->btype == BT_SD64) ||
890 (cinfo->btype == BT_ALPINE) ||
891 (cinfo->btype == BT_GD5480))
892 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700894 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700895 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700896 vga_wseq(regbase, CL_SEQRE, tmp);
897 vga_wseq(regbase, CL_SEQR1E, nom);
898 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700899 vga_wseq(regbase, CL_SEQRE, nom);
900 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700901 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700902 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700904 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700906 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 else
908 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
909 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700910 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 /* don't know if it would hurt to also program this if no interlaced */
913 /* mode is used, but I feel better this way.. :-) */
914 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700915 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700917 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700919 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700920 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700921 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
923 tmp |= 0x40;
924 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
925 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700926 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
Krzysztof Helt8503df62007-10-16 01:29:08 -0700928 /* text cursor on and start line */
929 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
930 /* text cursor end line */
931 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
933 /******************************************************
934 *
935 * 1 bpp
936 *
937 */
938
939 /* programming for different color depths */
940 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700941 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700942 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943
944 /* SR07 */
945 switch (cinfo->btype) {
946 case BT_SD64:
947 case BT_PICCOLO:
948 case BT_PICASSO:
949 case BT_SPECTRUM:
950 case BT_PICASSO4:
951 case BT_ALPINE:
952 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700953 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700954 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 bi->sr07_1bpp_mux : bi->sr07_1bpp);
956 break;
957
958 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700959 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700960 vga_wseq(regbase, CL_SEQR7,
961 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 break;
963
964 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700965 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 break;
967 }
968
969 /* Extended Sequencer Mode */
970 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971
972 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700973 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700974 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
975 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 break;
977
978 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 /* ## vorher d0 avoid FIFO underruns..? */
980 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 break;
982
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700983 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 case BT_PICASSO4:
985 case BT_ALPINE:
986 case BT_GD5480:
987 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700988 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 /* do nothing */
990 break;
991
992 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700993 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 break;
995 }
996
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 /* pixel mask: pass-through for first plane */
998 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700999 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 /* hidden dac reg: 1280x1024 */
1001 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001003 /* hidden dac: nothing */
1004 WHDR(cinfo, 0);
1005 /* memory mode: odd/even, ext. memory */
1006 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1007 /* plane mask: only write to first plane */
1008 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 }
1010
1011 /******************************************************
1012 *
1013 * 8 bpp
1014 *
1015 */
1016
1017 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001018 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 switch (cinfo->btype) {
1020 case BT_SD64:
1021 case BT_PICCOLO:
1022 case BT_PICASSO:
1023 case BT_SPECTRUM:
1024 case BT_PICASSO4:
1025 case BT_ALPINE:
1026 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001027 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001028 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1030 break;
1031
1032 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001033 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001034 vga_wseq(regbase, CL_SEQR7,
1035 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001036 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 break;
1038
1039 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001040 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 break;
1042 }
1043
1044 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001048 /* Fast Page-Mode writes */
1049 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 break;
1051
1052 case BT_PICASSO4:
1053#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001054 /* ### INCOMPLETE!! */
1055 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001058 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 case BT_GD5480:
1060 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001061 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 /* do nothing */
1063 break;
1064
1065 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001066 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 break;
1068 }
1069
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* mode register: 256 color mode */
1071 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001072 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 /* hidden dac reg: 1280x1024 */
1074 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001076 /* hidden dac: nothing */
1077 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 }
1079
1080 /******************************************************
1081 *
1082 * 16 bpp
1083 *
1084 */
1085
1086 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001087 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001090 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001091 vga_wseq(regbase, CL_SEQR7, 0x87);
1092 /* Fast Page-Mode writes */
1093 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 break;
1095
1096 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001097 vga_wseq(regbase, CL_SEQR7, 0x27);
1098 /* Fast Page-Mode writes */
1099 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 break;
1101
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001102 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001105 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001106 vga_wseq(regbase, CL_SEQR7,
1107 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 break;
1109
1110 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001111 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 /* We already set SRF and SR1F */
1113 break;
1114
1115 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001116 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001117 vga_wseq(regbase, CL_SEQR7,
1118 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001119 control |= 0x2000;
1120 format |= 0x1400;
1121 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 break;
1123
1124 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001125 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 break;
1127 }
1128
Krzysztof Helt8503df62007-10-16 01:29:08 -07001129 /* mode register: 256 color mode */
1130 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001132 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133#elif defined(CONFIG_ZORRO)
1134 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001135 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 }
1138
1139 /******************************************************
1140 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001141 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 *
1143 */
1144
Krzysztof Helt7cade312009-03-31 15:25:13 -07001145 else if (var->bits_per_pixel == 24) {
1146 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001149 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001150 vga_wseq(regbase, CL_SEQR7, 0x85);
1151 /* Fast Page-Mode writes */
1152 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 break;
1154
1155 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 vga_wseq(regbase, CL_SEQR7, 0x25);
1157 /* Fast Page-Mode writes */
1158 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
1160
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001161 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001164 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001165 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 break;
1167
1168 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001169 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 /* We already set SRF and SR1F */
1171 break;
1172
1173 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001174 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001175 vga_wseq(regbase, CL_SEQR7,
1176 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001177 control |= 0x4000;
1178 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001179 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 break;
1181
1182 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001183 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 break;
1185 }
1186
Krzysztof Helt8503df62007-10-16 01:29:08 -07001187 /* mode register: 256 color mode */
1188 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1190 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 }
1192
1193 /******************************************************
1194 *
1195 * unknown/unsupported bpp
1196 *
1197 */
1198
Krzysztof Helt8503df62007-10-16 01:29:08 -07001199 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001200 dev_err(info->device,
1201 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203
Krzysztof Helt6683e012009-03-31 15:25:06 -07001204 pitch = info->fix.line_length >> 3;
1205 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001207 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 tmp |= 0x10; /* offset overflow bit */
1209
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 /* screen start addr #16-18, fastpagemode cycles */
1211 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001213 /* screen start address bit 19 */
1214 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001215 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001217 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001218 tmp = 0;
1219 if ((htotal + 5) & 256)
1220 tmp |= 128;
1221 if (hdispend & 256)
1222 tmp |= 64;
1223 if (hsyncstart & 256)
1224 tmp |= 48;
1225 if (vtotal & 1024)
1226 tmp |= 8;
1227 if (vdispend & 1024)
1228 tmp |= 4;
1229 if (vsyncstart & 1024)
1230 tmp |= 3;
1231
1232 vga_wcrt(regbase, CL_CRT1E, tmp);
1233 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1234 }
1235
Krzysztof Helt8503df62007-10-16 01:29:08 -07001236 /* pixel panning */
1237 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
1239 /* [ EGS: SetOffset(); ] */
1240 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001241 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001243 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001244 /* no tiles */
1245 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1246 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1247 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1248 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 /* finally, turn on everything - turn off "FullBandwidth" bit */
1250 /* also, set "DotClock%2" bit where requested */
1251 tmp = 0x01;
1252
1253/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1254 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1255 tmp |= 0x08;
1256*/
1257
Krzysztof Helt8503df62007-10-16 01:29:08 -07001258 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001259 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001262 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263#endif
1264
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 return 0;
1266}
1267
1268/* for some reason incomprehensible to me, cirrusfb requires that you write
1269 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001270static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001272 cirrusfb_set_par_foo(info);
1273 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274}
1275
Krzysztof Helt8503df62007-10-16 01:29:08 -07001276static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1277 unsigned blue, unsigned transp,
1278 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279{
1280 struct cirrusfb_info *cinfo = info->par;
1281
1282 if (regno > 255)
1283 return -EINVAL;
1284
1285 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1286 u32 v;
1287 red >>= (16 - info->var.red.length);
1288 green >>= (16 - info->var.green.length);
1289 blue >>= (16 - info->var.blue.length);
1290
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 return 1;
1293 v = (red << info->var.red.offset) |
1294 (green << info->var.green.offset) |
1295 (blue << info->var.blue.offset);
1296
Krzysztof Helt060b6002007-10-16 01:29:13 -07001297 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 return 0;
1299 }
1300
Krzysztof Helt8503df62007-10-16 01:29:08 -07001301 if (info->var.bits_per_pixel == 8)
1302 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
1304 return 0;
1305
1306}
1307
1308/*************************************************************************
1309 cirrusfb_pan_display()
1310
1311 performs display panning - provided hardware permits this
1312**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1314 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001316 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001318 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 struct cirrusfb_info *cinfo = info->par;
1320
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 /* no range checks for xoffset and yoffset, */
1322 /* as fb_pan_display has already done this */
1323 if (var->vmode & FB_VMODE_YWRAP)
1324 return -EINVAL;
1325
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
Krzysztof Helt99a45842009-03-31 15:25:09 -07001328 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 if (info->var.bits_per_pixel == 1) {
1331 /* base is already correct */
1332 xpix = (unsigned char) (var->xoffset % 8);
1333 } else {
1334 base /= 4;
1335 xpix = (unsigned char) ((xoffset % 4) * 2);
1336 }
1337
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001338 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001339 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340
1341 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001342 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1343 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001345 /* 0xf2 is %11110010, exclude tmp bits */
1346 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 /* construct bits 16, 17 and 18 of screen start address */
1348 if (base & 0x10000)
1349 tmp |= 0x01;
1350 if (base & 0x20000)
1351 tmp |= 0x04;
1352 if (base & 0x40000)
1353 tmp |= 0x08;
1354
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001355 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
1357 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001358 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001359 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1360 if (is_laguna(cinfo))
1361 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1362 else
1363 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001364 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1365 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Krzysztof Helt8503df62007-10-16 01:29:08 -07001367 /* write pixel panning value to AR33; this does not quite work in 8bpp
1368 *
1369 * ### Piccolo..? Will this work?
1370 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001372 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
Krzysztof Helt8503df62007-10-16 01:29:08 -07001374 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375}
1376
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378{
1379 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001380 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1381 * then the caller blanks by setting the CLUT (Color Look Up Table)
1382 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1383 * failed due to e.g. a video mode which doesn't support it.
1384 * Implements VESA suspend and powerdown modes on hardware that
1385 * supports disabling hsync/vsync:
1386 * blank_mode == 2: suspend vsync
1387 * blank_mode == 3: suspend hsync
1388 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 */
1390 unsigned char val;
1391 struct cirrusfb_info *cinfo = info->par;
1392 int current_mode = cinfo->blank_mode;
1393
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001394 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
1396 if (info->state != FBINFO_STATE_RUNNING ||
1397 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001398 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 return 0;
1400 }
1401
1402 /* Undo current */
1403 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001404 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001406 val = 0;
1407 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001408 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001409 val = 0x20;
1410
1411 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1412 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
1414 switch (blank_mode) {
1415 case FB_BLANK_UNBLANK:
1416 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001417 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 break;
1419 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001420 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 break;
1422 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001423 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 break;
1425 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001426 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 break;
1428 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001429 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 return 1;
1431 }
1432
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001433 vga_wgfx(cinfo->regbase, CL_GRE, val);
1434
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001436 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437
1438 /* Let fbcon do a soft blank for us */
1439 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1440}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001441
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442/**** END Hardware specific Routines **************************************/
1443/****************************************************************************/
1444/**** BEGIN Internal Routines ***********************************************/
1445
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001446static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001448 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 const struct cirrusfb_board_info_rec *bi;
1450
Krzysztof Helt8503df62007-10-16 01:29:08 -07001451 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
1453 bi = &cirrusfb_board_info[cinfo->btype];
1454
1455 /* reset board globally */
1456 switch (cinfo->btype) {
1457 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001458 WSFR(cinfo, 0x01);
1459 udelay(500);
1460 WSFR(cinfo, 0x51);
1461 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 break;
1463 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001464 WSFR2(cinfo, 0xff);
1465 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 break;
1467 case BT_SD64:
1468 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001469 WSFR(cinfo, 0x1f);
1470 udelay(500);
1471 WSFR(cinfo, 0x4f);
1472 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 break;
1474 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001475 /* disable flickerfixer */
1476 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1477 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001478 /* mode */
1479 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001480 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 /* from Klaus' NetBSD driver: */
1482 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001483 case BT_ALPINE: /* fall through */
1484 /* put blitter into 542x compat */
1485 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 break;
1487
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001488 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001489 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 /* Nothing to do to reset the board. */
1491 break;
1492
1493 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001494 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 break;
1496 }
1497
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001498 /* make sure RAM size set by this point */
1499 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
1501 /* the P4 is not fully initialized here; I rely on it having been */
1502 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001503 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001506 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1507 WGen(cinfo, CL_POS102, 0x01);
1508 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509
1510 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001511 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001514 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
Krzysztof Helt8503df62007-10-16 01:29:08 -07001516 /* FullBandwidth (video off) and 8/9 dot clock */
1517 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
Krzysztof Helt8503df62007-10-16 01:29:08 -07001519 /* "magic cookie" - doesn't make any sense to me.. */
1520/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1521 /* unlock all extension registers */
1522 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 switch (cinfo->btype) {
1525 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001526 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 break;
1528 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001529 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001530 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 break;
1532 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001533#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001535#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 break;
1537 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001538 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1539 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 break;
1541 }
1542 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 /* plane mask: nothing */
1544 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1545 /* character map select: doesn't even matter in gx mode */
1546 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001547 /* memory mode: chain4, ext. memory */
1548 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
1550 /* controller-internal base address of video memory */
1551 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001552 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Krzysztof Helt8503df62007-10-16 01:29:08 -07001554 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1555 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1558 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1559 /* graphics cursor Y position (..."... ) */
1560 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1561 /* graphics cursor attributes */
1562 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1563 /* graphics cursor pattern address */
1564 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
1566 /* writing these on a P4 might give problems.. */
1567 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001568 /* configuration readback and ext. color */
1569 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1570 /* signature generator */
1571 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 }
1573
Krzysztof Helt8503df62007-10-16 01:29:08 -07001574 /* Screen A preset row scan: none */
1575 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1576 /* Text cursor start: disable text cursor */
1577 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1578 /* Text cursor end: - */
1579 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 /* text cursor location high: 0 */
1581 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1582 /* text cursor location low: 0 */
1583 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 /* Underline Row scanline: - */
1586 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001588 /* ext. display controls: ext.adr. wrap */
1589 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590
Krzysztof Helt8503df62007-10-16 01:29:08 -07001591 /* Set/Reset registes: - */
1592 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1593 /* Set/Reset enable: - */
1594 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1595 /* Color Compare: - */
1596 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1597 /* Data Rotate: - */
1598 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1599 /* Read Map Select: - */
1600 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1601 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1602 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1603 /* Miscellaneous: memory map base address, graphics mode */
1604 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1605 /* Color Don't care: involve all planes */
1606 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1607 /* Bit Mask: no mask at all */
1608 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001609
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001610 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1611 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001612 /* (5434 can't have bit 3 set for bitblt) */
1613 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001615 /* Graphics controller mode extensions: finer granularity,
1616 * 8byte data latches
1617 */
1618 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619
Krzysztof Helt8503df62007-10-16 01:29:08 -07001620 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1621 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1622 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1623 /* Background color byte 1: - */
1624 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1625 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
Krzysztof Helt8503df62007-10-16 01:29:08 -07001627 /* Attribute Controller palette registers: "identity mapping" */
1628 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1629 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1630 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
Krzysztof Helt8503df62007-10-16 01:29:08 -07001645 /* Attribute Controller mode: graphics mode */
1646 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1647 /* Overscan color reg.: reg. 0 */
1648 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1649 /* Color Plane enable: Enable all 4 planes */
1650 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 /* Color Select: - */
1652 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
Krzysztof Helt8503df62007-10-16 01:29:08 -07001654 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 /* BLT Start/status: Blitter reset */
1657 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1658 /* - " - : "end-of-reset" */
1659 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
1661 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001662 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 return;
1664}
1665
Krzysztof Helt8503df62007-10-16 01:29:08 -07001666static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667{
1668#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1669 static int IsOn = 0; /* XXX not ok for multiple boards */
1670
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 if (cinfo->btype == BT_PICASSO4)
1672 return; /* nothing to switch */
1673 if (cinfo->btype == BT_ALPINE)
1674 return; /* nothing to switch */
1675 if (cinfo->btype == BT_GD5480)
1676 return; /* nothing to switch */
1677 if (cinfo->btype == BT_PICASSO) {
1678 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001679 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 return;
1681 }
1682 if (on) {
1683 switch (cinfo->btype) {
1684 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001685 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 break;
1687 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001688 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 break;
1690 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 break;
1693 default: /* do nothing */ break;
1694 }
1695 } else {
1696 switch (cinfo->btype) {
1697 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001698 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 break;
1700 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 break;
1703 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001704 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001706 default: /* do nothing */
1707 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 }
1709 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710#endif /* CONFIG_ZORRO */
1711}
1712
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713/******************************************/
1714/* Linux 2.6-style accelerated functions */
1715/******************************************/
1716
Krzysztof Helt8343c892009-03-31 15:25:11 -07001717static int cirrusfb_sync(struct fb_info *info)
1718{
1719 struct cirrusfb_info *cinfo = info->par;
1720
1721 if (!is_laguna(cinfo)) {
1722 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1723 cpu_relax();
1724 }
1725 return 0;
1726}
1727
Krzysztof Helt8503df62007-10-16 01:29:08 -07001728static void cirrusfb_fillrect(struct fb_info *info,
1729 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 struct fb_fillrect modded;
1732 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001733 struct cirrusfb_info *cinfo = info->par;
1734 int m = info->var.bits_per_pixel;
1735 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1736 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737
1738 if (info->state != FBINFO_STATE_RUNNING)
1739 return;
1740 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1741 cfb_fillrect(info, region);
1742 return;
1743 }
1744
1745 vxres = info->var.xres_virtual;
1746 vyres = info->var.yres_virtual;
1747
1748 memcpy(&modded, region, sizeof(struct fb_fillrect));
1749
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 modded.dx >= vxres || modded.dy >= vyres)
1752 return;
1753
Krzysztof Helt8503df62007-10-16 01:29:08 -07001754 if (modded.dx + modded.width > vxres)
1755 modded.width = vxres - modded.dx;
1756 if (modded.dy + modded.height > vyres)
1757 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758
Krzysztof Helt060b6002007-10-16 01:29:13 -07001759 cirrusfb_RectFill(cinfo->regbase,
1760 info->var.bits_per_pixel,
1761 (region->dx * m) / 8, region->dy,
1762 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001763 color, color,
1764 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765}
1766
Krzysztof Helt8503df62007-10-16 01:29:08 -07001767static void cirrusfb_copyarea(struct fb_info *info,
1768 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 struct fb_copyarea modded;
1771 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001772 struct cirrusfb_info *cinfo = info->par;
1773 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774
1775 if (info->state != FBINFO_STATE_RUNNING)
1776 return;
1777 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1778 cfb_copyarea(info, area);
1779 return;
1780 }
1781
1782 vxres = info->var.xres_virtual;
1783 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001784 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785
Krzysztof Helt8503df62007-10-16 01:29:08 -07001786 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787 modded.sx >= vxres || modded.sy >= vyres ||
1788 modded.dx >= vxres || modded.dy >= vyres)
1789 return;
1790
Krzysztof Helt8503df62007-10-16 01:29:08 -07001791 if (modded.sx + modded.width > vxres)
1792 modded.width = vxres - modded.sx;
1793 if (modded.dx + modded.width > vxres)
1794 modded.width = vxres - modded.dx;
1795 if (modded.sy + modded.height > vyres)
1796 modded.height = vyres - modded.sy;
1797 if (modded.dy + modded.height > vyres)
1798 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
Krzysztof Helt060b6002007-10-16 01:29:13 -07001800 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1801 (area->sx * m) / 8, area->sy,
1802 (area->dx * m) / 8, area->dy,
1803 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001804 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001805
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806}
1807
Krzysztof Helt8503df62007-10-16 01:29:08 -07001808static void cirrusfb_imageblit(struct fb_info *info,
1809 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810{
1811 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001812 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813
Krzysztof Helt9e848062009-03-31 15:25:11 -07001814 if (info->state != FBINFO_STATE_RUNNING)
1815 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001816 /* Alpine/SD64 does not work at 24bpp ??? */
1817 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1818 cfb_imageblit(info, image);
1819 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1820 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001821 cfb_imageblit(info, image);
1822 else {
1823 unsigned size = ((image->width + 7) >> 3) * image->height;
1824 int m = info->var.bits_per_pixel;
1825 u32 fg, bg;
1826
1827 if (info->var.bits_per_pixel == 8) {
1828 fg = image->fg_color;
1829 bg = image->bg_color;
1830 } else {
1831 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1832 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1833 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001834 if (info->var.bits_per_pixel == 24) {
1835 /* clear background first */
1836 cirrusfb_RectFill(cinfo->regbase,
1837 info->var.bits_per_pixel,
1838 (image->dx * m) / 8, image->dy,
1839 (image->width * m) / 8,
1840 image->height,
1841 bg, bg,
1842 info->fix.line_length, 0x40);
1843 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001844 cirrusfb_RectFill(cinfo->regbase,
1845 info->var.bits_per_pixel,
1846 (image->dx * m) / 8, image->dy,
1847 (image->width * m) / 8, image->height,
1848 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001849 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001850 memcpy(info->screen_base, image->data, size);
1851 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852}
1853
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854#ifdef CONFIG_PPC_PREP
1855#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1856#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001857static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 *display = PREP_VIDEO_BASE;
1860 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861}
1862
1863#endif /* CONFIG_PPC_PREP */
1864
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001866static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867
1868/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1869 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1870 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1871 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001872static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1873 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874{
1875 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001876 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001878 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001879 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1880
1881 mem = ((SR14 & 7) + 1) << 20;
1882 } else {
1883 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1884 switch ((SRF & 0x18)) {
1885 case 0x08:
1886 mem = 512 * 1024;
1887 break;
1888 case 0x10:
1889 mem = 1024 * 1024;
1890 break;
1891 /* 64-bit DRAM data bus width; assume 2MB.
1892 * Also indicates 2MB memory on the 5430.
1893 */
1894 case 0x18:
1895 mem = 2048 * 1024;
1896 break;
1897 default:
1898 dev_warn(info->device, "Unknown memory size!\n");
1899 mem = 1024 * 1024;
1900 }
1901 /* If DRAM bank switching is enabled, there must be
1902 * twice as much memory installed. (4MB on the 5434)
1903 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001904 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001905 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001907
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 return mem;
1910}
1911
Krzysztof Helt8503df62007-10-16 01:29:08 -07001912static void get_pci_addrs(const struct pci_dev *pdev,
1913 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001915 assert(pdev != NULL);
1916 assert(display != NULL);
1917 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 *display = 0;
1920 *registers = 0;
1921
1922 /* This is a best-guess for now */
1923
1924 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1925 *display = pci_resource_start(pdev, 1);
1926 *registers = pci_resource_start(pdev, 0);
1927 } else {
1928 *display = pci_resource_start(pdev, 0);
1929 *registers = pci_resource_start(pdev, 1);
1930 }
1931
Krzysztof Helt8503df62007-10-16 01:29:08 -07001932 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933}
1934
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001935static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001937 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001938 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001940 if (cinfo->laguna_mmio == NULL)
1941 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001942 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943#if 0 /* if system didn't claim this region, we would... */
1944 release_mem_region(0xA0000, 65535);
1945#endif
1946 if (release_io_ports)
1947 release_region(0x3C0, 32);
1948 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949}
1950#endif /* CONFIG_PCI */
1951
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001953static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954{
Al Virod91f5bb2007-10-17 00:27:18 +01001955 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001956 struct zorro_dev *zdev = to_zorro_dev(info->device);
1957
1958 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
1960 if (cinfo->btype == BT_PICASSO4) {
1961 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001962 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001963 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001965 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001966 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968}
1969#endif /* CONFIG_ZORRO */
1970
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001971/* function table of the above functions */
1972static struct fb_ops cirrusfb_ops = {
1973 .owner = THIS_MODULE,
1974 .fb_open = cirrusfb_open,
1975 .fb_release = cirrusfb_release,
1976 .fb_setcolreg = cirrusfb_setcolreg,
1977 .fb_check_var = cirrusfb_check_var,
1978 .fb_set_par = cirrusfb_set_par,
1979 .fb_pan_display = cirrusfb_pan_display,
1980 .fb_blank = cirrusfb_blank,
1981 .fb_fillrect = cirrusfb_fillrect,
1982 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001983 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001984 .fb_imageblit = cirrusfb_imageblit,
1985};
1986
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001987static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001989 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 struct fb_var_screeninfo *var = &info->var;
1991
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 info->pseudo_palette = cinfo->pseudo_palette;
1993 info->flags = FBINFO_DEFAULT
1994 | FBINFO_HWACCEL_XPAN
1995 | FBINFO_HWACCEL_YPAN
1996 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07001997 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001999 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002001 info->fix.accel = FB_ACCEL_NONE;
2002 } else
2003 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
2004
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002006
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 if (cinfo->btype == BT_GD5480) {
2008 if (var->bits_per_pixel == 16)
2009 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002010 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 info->screen_base += 2 * MB_;
2012 }
2013
2014 /* Fill fix common fields */
2015 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2016 sizeof(info->fix.id));
2017
2018 /* monochrome: only 1 memory plane */
2019 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002020 info->fix.smem_len = info->screen_size;
2021 if (var->bits_per_pixel == 1)
2022 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 info->fix.xpanstep = 1;
2025 info->fix.ypanstep = 1;
2026 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027
2028 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030
2031 fb_alloc_cmap(&info->cmap, 256, 0);
2032
2033 return 0;
2034}
2035
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002036static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002038 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040
2041 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002042 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002044 /* set all the vital stuff */
2045 cirrusfb_set_fbinfo(info);
2046
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002047 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002049 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2050 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002051 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002052 err = -EINVAL;
2053 goto err_dealloc_cmap;
2054 }
2055
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 info->var.activate = FB_ACTIVATE_NOW;
2057
Krzysztof Helt99a45842009-03-31 15:25:09 -07002058 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 if (err < 0) {
2060 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002061 dev_dbg(info->device,
2062 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002063 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 }
2065
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 err = register_framebuffer(info);
2067 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002068 dev_err(info->device,
2069 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 goto err_dealloc_cmap;
2071 }
2072
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 return 0;
2074
2075err_dealloc_cmap:
2076 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 return err;
2078}
2079
Krzysztof Helt8503df62007-10-16 01:29:08 -07002080static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081{
2082 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083
Krzysztof Helt8503df62007-10-16 01:29:08 -07002084 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002085 unregister_framebuffer(info);
2086 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002087 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002088 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002089 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090}
2091
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002093static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2094 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095{
2096 struct cirrusfb_info *cinfo;
2097 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 unsigned long board_addr, board_size;
2099 int ret;
2100
2101 ret = pci_enable_device(pdev);
2102 if (ret < 0) {
2103 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2104 goto err_out;
2105 }
2106
2107 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2108 if (!info) {
2109 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2110 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002111 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 }
2113
2114 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002115 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002117 dev_dbg(info->device,
2118 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002119 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002120 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2121 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122
Krzysztof Helt8503df62007-10-16 01:29:08 -07002123 if (isPReP) {
2124 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002126 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002128 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002129 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002131 dev_dbg(info->device,
2132 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002133 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002134 /* FIXME: this forces VGA. alternatives? */
2135 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002136 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 }
2138
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002139 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002140 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002142 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002143 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144
2145 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002146 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002147 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2148 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 goto err_release_fb;
2150 }
2151#if 0 /* if the system didn't claim this region, we would... */
2152 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002153 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2154 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 ret = -EBUSY;
2156 goto err_release_regions;
2157 }
2158#endif
2159 if (request_region(0x3C0, 32, "cirrusfb"))
2160 release_io_ports = 1;
2161
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002162 info->screen_base = ioremap(board_addr, board_size);
2163 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 ret = -EIO;
2165 goto err_release_legacy;
2166 }
2167
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002168 info->fix.smem_start = board_addr;
2169 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 cinfo->unmap = cirrusfb_pci_unmap;
2171
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002172 dev_info(info->device,
2173 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2174 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 pci_set_drvdata(pdev, info);
2176
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002177 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002178 if (!ret)
2179 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002181 pci_set_drvdata(pdev, NULL);
2182 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183err_release_legacy:
2184 if (release_io_ports)
2185 release_region(0x3C0, 32);
2186#if 0
2187 release_mem_region(0xA0000, 65535);
2188err_release_regions:
2189#endif
2190 pci_release_regions(pdev);
2191err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002192 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002193 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195err_out:
2196 return ret;
2197}
2198
Krzysztof Helt8503df62007-10-16 01:29:08 -07002199static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200{
2201 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202
Krzysztof Helt8503df62007-10-16 01:29:08 -07002203 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204}
2205
2206static struct pci_driver cirrusfb_pci_driver = {
2207 .name = "cirrusfb",
2208 .id_table = cirrusfb_pci_table,
2209 .probe = cirrusfb_pci_register,
2210 .remove = __devexit_p(cirrusfb_pci_unregister),
2211#ifdef CONFIG_PM
2212#if 0
2213 .suspend = cirrusfb_pci_suspend,
2214 .resume = cirrusfb_pci_resume,
2215#endif
2216#endif
2217};
2218#endif /* CONFIG_PCI */
2219
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002221static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2222 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223{
2224 struct cirrusfb_info *cinfo;
2225 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002226 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 struct zorro_dev *z2 = NULL;
2228 unsigned long board_addr, board_size, size;
2229 int ret;
2230
2231 btype = ent->driver_data;
2232 if (cirrusfb_zorro_table2[btype].id2)
2233 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2234 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235
2236 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2237 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002238 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239 ret = -ENOMEM;
2240 goto err_out;
2241 }
2242
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002243 dev_info(info->device, "%s board detected\n",
2244 cirrusfb_board_info[btype].name);
2245
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 cinfo->btype = btype;
2248
Al Viro36ea96a2007-10-27 19:46:58 +01002249 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002250 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 board_addr = zorro_resource_start(z);
2253 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002254 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255
2256 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002257 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2258 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 ret = -EBUSY;
2260 goto err_release_fb;
2261 }
2262
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 ret = -EIO;
2264
2265 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002266 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267
2268 /* To be precise, for the P4 this is not the */
2269 /* begin of the board, but the begin of RAM. */
2270 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2271 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002272 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 if (!cinfo->regbase)
2274 goto err_release_region;
2275
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002276 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002277 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002279 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002281 info->fix.smem_start = board_addr + 16777216;
2282 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2283 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 goto err_unmap_regbase;
2285 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002286 dev_info(info->device, " REG at $%lx\n",
2287 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002289 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002291 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002293 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2294 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 goto err_release_region;
2296
2297 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002298 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002299 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002301 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002302 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 }
2304 cinfo->unmap = cirrusfb_zorro_unmap;
2305
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002306 dev_info(info->device,
2307 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2308 board_size / MB_, board_addr);
2309
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 zorro_set_drvdata(z, info);
2311
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002312 /* MCLK select etc. */
2313 if (cirrusfb_board_info[btype].init_sr1f)
2314 vga_wseq(cinfo->regbase, CL_SEQR1F,
2315 cirrusfb_board_info[btype].sr1f);
2316
Al Virod91f5bb2007-10-17 00:27:18 +01002317 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002318 if (!ret)
2319 return 0;
2320
2321 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2322 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323
2324err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002325 if (btype == BT_PICASSO4)
2326 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327err_release_region:
2328 release_region(board_addr, board_size);
2329err_release_fb:
2330 framebuffer_release(info);
2331err_out:
2332 return ret;
2333}
2334
2335void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2336{
2337 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338
Krzysztof Helt8503df62007-10-16 01:29:08 -07002339 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340}
2341
2342static struct zorro_driver cirrusfb_zorro_driver = {
2343 .name = "cirrusfb",
2344 .id_table = cirrusfb_zorro_table,
2345 .probe = cirrusfb_zorro_register,
2346 .remove = __devexit_p(cirrusfb_zorro_unregister),
2347};
2348#endif /* CONFIG_ZORRO */
2349
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002351static int __init cirrusfb_setup(char *options)
2352{
Vlada Pericee119402008-11-19 15:36:45 -08002353 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 if (!options || !*options)
2356 return 0;
2357
Krzysztof Helt8503df62007-10-16 01:29:08 -07002358 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002359 if (!*this_opt)
2360 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362 if (!strcmp(this_opt, "noaccel"))
2363 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002364 else if (!strncmp(this_opt, "mode:", 5))
2365 mode_option = this_opt + 5;
2366 else
2367 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368 }
2369 return 0;
2370}
2371#endif
2372
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373 /*
2374 * Modularization
2375 */
2376
2377MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2378MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2379MODULE_LICENSE("GPL");
2380
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002381static int __init cirrusfb_init(void)
2382{
2383 int error = 0;
2384
2385#ifndef MODULE
2386 char *option = NULL;
2387
2388 if (fb_get_options("cirrusfb", &option))
2389 return -ENODEV;
2390 cirrusfb_setup(option);
2391#endif
2392
2393#ifdef CONFIG_ZORRO
2394 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2395#endif
2396#ifdef CONFIG_PCI
2397 error |= pci_register_driver(&cirrusfb_pci_driver);
2398#endif
2399 return error;
2400}
2401
Krzysztof Helt8503df62007-10-16 01:29:08 -07002402static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403{
2404#ifdef CONFIG_PCI
2405 pci_unregister_driver(&cirrusfb_pci_driver);
2406#endif
2407#ifdef CONFIG_ZORRO
2408 zorro_unregister_driver(&cirrusfb_zorro_driver);
2409#endif
2410}
2411
2412module_init(cirrusfb_init);
2413
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002414module_param(mode_option, charp, 0);
2415MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002416module_param(noaccel, bool, 0);
2417MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002418
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419#ifdef MODULE
2420module_exit(cirrusfb_exit);
2421#endif
2422
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423/**********************************************************************/
2424/* about the following functions - I have used the same names for the */
2425/* functions as Markus Wild did in his Retina driver for NetBSD as */
2426/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002427/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428/**********************************************************************/
2429
2430/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002431static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432 int regnum, unsigned char val)
2433{
2434 unsigned long regofs = 0;
2435
2436 if (cinfo->btype == BT_PICASSO) {
2437 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002438/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2439 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2441 regofs = 0xfff;
2442 }
2443
Krzysztof Helt8503df62007-10-16 01:29:08 -07002444 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445}
2446
2447/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002448static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449{
2450 unsigned long regofs = 0;
2451
2452 if (cinfo->btype == BT_PICASSO) {
2453 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002454/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2455 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2457 regofs = 0xfff;
2458 }
2459
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461}
2462
2463/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002466 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467
Krzysztof Helt8503df62007-10-16 01:29:08 -07002468 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 /* if we're just in "write value" mode, write back the */
2470 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002471 vga_w(cinfo->regbase, VGA_ATT_IW,
2472 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473 }
2474 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002475/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2476 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477
2478 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002479 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480}
2481
2482/*** WHDR() - write into the Hidden DAC register ***/
2483/* as the HDR is the only extension register that requires special treatment
2484 * (the other extension registers are accessible just like the "ordinary"
2485 * registers of their functional group) here is a specialized routine for
2486 * accessing the HDR
2487 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002488static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489{
2490 unsigned char dummy;
2491
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002492 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002493 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 if (cinfo->btype == BT_PICASSO) {
2495 /* Klaus' hint for correct access to HDR on some boards */
2496 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002497 WGen(cinfo, VGA_PEL_MSK, 0x00);
2498 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002500 dummy = RGen(cinfo, VGA_PEL_IW);
2501 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502 }
2503 /* now do the usual stuff to access the HDR */
2504
Krzysztof Helt8503df62007-10-16 01:29:08 -07002505 dummy = RGen(cinfo, VGA_PEL_MSK);
2506 udelay(200);
2507 dummy = RGen(cinfo, VGA_PEL_MSK);
2508 udelay(200);
2509 dummy = RGen(cinfo, VGA_PEL_MSK);
2510 udelay(200);
2511 dummy = RGen(cinfo, VGA_PEL_MSK);
2512 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513
Krzysztof Helt8503df62007-10-16 01:29:08 -07002514 WGen(cinfo, VGA_PEL_MSK, val);
2515 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516
2517 if (cinfo->btype == BT_PICASSO) {
2518 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002519 dummy = RGen(cinfo, VGA_PEL_IW);
2520 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521
2522 /* and at the end, restore the mask value */
2523 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002524 WGen(cinfo, VGA_PEL_MSK, 0xff);
2525 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 }
2527}
2528
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002530static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531{
2532#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002533 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002535 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536#endif
2537}
2538
2539/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541{
2542#ifdef CONFIG_ZORRO
2543 /* writing an arbitrary value to this one causes the monitor switcher */
2544 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002545 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002547 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548#endif
2549}
2550
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002552static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 unsigned char green, unsigned char blue)
2554{
2555 unsigned int data = VGA_PEL_D;
2556
2557 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559
2560 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002561 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002562 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563 /* but DAC data register IS, at least for Picasso II */
2564 if (cinfo->btype == BT_PICASSO)
2565 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566 vga_w(cinfo->regbase, data, red);
2567 vga_w(cinfo->regbase, data, green);
2568 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002570 vga_w(cinfo->regbase, data, blue);
2571 vga_w(cinfo->regbase, data, green);
2572 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573 }
2574}
2575
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576#if 0
2577/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 unsigned char *green, unsigned char *blue)
2580{
2581 unsigned int data = VGA_PEL_D;
2582
Krzysztof Helt8503df62007-10-16 01:29:08 -07002583 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584
2585 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2586 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2587 if (cinfo->btype == BT_PICASSO)
2588 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002589 *red = vga_r(cinfo->regbase, data);
2590 *green = vga_r(cinfo->regbase, data);
2591 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002593 *blue = vga_r(cinfo->regbase, data);
2594 *green = vga_r(cinfo->regbase, data);
2595 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 }
2597}
2598#endif
2599
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600/*******************************************************************
2601 cirrusfb_WaitBLT()
2602
2603 Wait for the BitBLT engine to complete a possible earlier job
2604*********************************************************************/
2605
2606/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002607static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002610 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611}
2612
2613/*******************************************************************
2614 cirrusfb_BitBLT()
2615
2616 perform accelerated "scrolling"
2617********************************************************************/
2618
Krzysztof Helt8343c892009-03-31 15:25:11 -07002619static void cirrusfb_set_blitter(u8 __iomem *regbase,
2620 u_short nwidth, u_short nheight,
2621 u_long nsrc, u_long ndest,
2622 u_short bltmode, u_short line_length)
2623
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002626 /* dest pitch low */
2627 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2628 /* dest pitch hi */
2629 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2630 /* source pitch low */
2631 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2632 /* source pitch hi */
2633 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634
2635 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002636 /* BLT width low */
2637 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2638 /* BLT width hi */
2639 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640
2641 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002642 /* BLT height low */
2643 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2644 /* BLT width hi */
2645 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646
2647 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002648 /* BLT dest low */
2649 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2650 /* BLT dest mid */
2651 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2652 /* BLT dest hi */
2653 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654
2655 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656 /* BLT src low */
2657 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2658 /* BLT src mid */
2659 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2660 /* BLT src hi */
2661 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662
2663 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002664 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665
2666 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668
2669 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002670 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002671}
2672
2673/*******************************************************************
2674 cirrusfb_BitBLT()
2675
2676 perform accelerated "scrolling"
2677********************************************************************/
2678
2679static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2680 u_short curx, u_short cury,
2681 u_short destx, u_short desty,
2682 u_short width, u_short height,
2683 u_short line_length)
2684{
2685 u_short nwidth = width - 1;
2686 u_short nheight = height - 1;
2687 u_long nsrc, ndest;
2688 u_char bltmode;
2689
2690 bltmode = 0x00;
2691 /* if source adr < dest addr, do the Blt backwards */
2692 if (cury <= desty) {
2693 if (cury == desty) {
2694 /* if src and dest are on the same line, check x */
2695 if (curx < destx)
2696 bltmode |= 0x01;
2697 } else
2698 bltmode |= 0x01;
2699 }
2700 /* standard case: forward blitting */
2701 nsrc = (cury * line_length) + curx;
2702 ndest = (desty * line_length) + destx;
2703 if (bltmode) {
2704 /* this means start addresses are at the end,
2705 * counting backwards
2706 */
2707 nsrc += nheight * line_length + nwidth;
2708 ndest += nheight * line_length + nwidth;
2709 }
2710
2711 cirrusfb_WaitBLT(regbase);
2712
2713 cirrusfb_set_blitter(regbase, nwidth, nheight,
2714 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715}
2716
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717/*******************************************************************
2718 cirrusfb_RectFill()
2719
2720 perform accelerated rectangle fill
2721********************************************************************/
2722
Krzysztof Helt8503df62007-10-16 01:29:08 -07002723static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002725 u32 fg_color, u32 bg_color, u_short line_length,
2726 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002728 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729 u_char op;
2730
Krzysztof Helt8503df62007-10-16 01:29:08 -07002731 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733 /* This is a ColorExpand Blt, using the */
2734 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002735 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2736 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737
Krzysztof Helt9e848062009-03-31 15:25:11 -07002738 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002739 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002740 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2741 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2742 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002743 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002744 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002745 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2746 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002747 op = 0xa0;
2748 }
2749 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002750 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2751 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2752 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002754 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002755 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756}
2757
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758/**************************************************************************
2759 * bestclock() - determine closest possible clock lower(?) than the
2760 * desired pixel clock
2761 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002762static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002764 int n, d;
2765 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766
Krzysztof Helt8503df62007-10-16 01:29:08 -07002767 assert(nom != NULL);
2768 assert(den != NULL);
2769 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770
2771 *nom = 0;
2772 *den = 0;
2773 *div = 0;
2774
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 if (freq < 8000)
2776 freq = 8000;
2777
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002778 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779
2780 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002781 int s = 0;
2782
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002783 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002785 int temp = d;
2786
2787 if (temp > 31) {
2788 s = 1;
2789 temp >>= 1;
2790 }
2791 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002792 h = h > freq ? h - freq : freq - h;
2793 if (h < diff) {
2794 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002796 *den = temp;
2797 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 }
2799 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002800 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002802 if (d > 31) {
2803 s = 1;
2804 d >>= 1;
2805 }
2806 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002807 h = h > freq ? h - freq : freq - h;
2808 if (h < diff) {
2809 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002811 *den = d;
2812 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 }
2814 }
2815 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816}
2817
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818/* -------------------------------------------------------------------------
2819 *
2820 * debugging functions
2821 *
2822 * -------------------------------------------------------------------------
2823 */
2824
2825#ifdef CIRRUSFB_DEBUG
2826
2827/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 * cirrusfb_dbg_print_regs
2829 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2830 * @reg_class: type of registers to read: %CRT, or %SEQ
2831 *
2832 * DESCRIPTION:
2833 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2834 * old-style I/O ports are queried for information, otherwise MMIO is
2835 * used at the given @base address to query the information.
2836 */
2837
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002838static void cirrusfb_dbg_print_regs(struct fb_info *info,
2839 caddr_t regbase,
2840 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841{
2842 va_list list;
2843 unsigned char val = 0;
2844 unsigned reg;
2845 char *name;
2846
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848
Krzysztof Helt8503df62007-10-16 01:29:08 -07002849 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002851 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852
2853 switch (reg_class) {
2854 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002855 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856 break;
2857 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002858 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 break;
2860 default:
2861 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002862 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 break;
2864 }
2865
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002866 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867
Krzysztof Helt8503df62007-10-16 01:29:08 -07002868 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869 }
2870
Krzysztof Helt8503df62007-10-16 01:29:08 -07002871 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872}
2873
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 * cirrusfb_dbg_reg_dump
2876 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2877 *
2878 * DESCRIPTION:
2879 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2880 * old-style I/O ports are queried for information, otherwise MMIO is
2881 * used at the given @base address to query the information.
2882 */
2883
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002884static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002886 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002888 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 "CR00", 0x00,
2890 "CR01", 0x01,
2891 "CR02", 0x02,
2892 "CR03", 0x03,
2893 "CR04", 0x04,
2894 "CR05", 0x05,
2895 "CR06", 0x06,
2896 "CR07", 0x07,
2897 "CR08", 0x08,
2898 "CR09", 0x09,
2899 "CR0A", 0x0A,
2900 "CR0B", 0x0B,
2901 "CR0C", 0x0C,
2902 "CR0D", 0x0D,
2903 "CR0E", 0x0E,
2904 "CR0F", 0x0F,
2905 "CR10", 0x10,
2906 "CR11", 0x11,
2907 "CR12", 0x12,
2908 "CR13", 0x13,
2909 "CR14", 0x14,
2910 "CR15", 0x15,
2911 "CR16", 0x16,
2912 "CR17", 0x17,
2913 "CR18", 0x18,
2914 "CR22", 0x22,
2915 "CR24", 0x24,
2916 "CR26", 0x26,
2917 "CR2D", 0x2D,
2918 "CR2E", 0x2E,
2919 "CR2F", 0x2F,
2920 "CR30", 0x30,
2921 "CR31", 0x31,
2922 "CR32", 0x32,
2923 "CR33", 0x33,
2924 "CR34", 0x34,
2925 "CR35", 0x35,
2926 "CR36", 0x36,
2927 "CR37", 0x37,
2928 "CR38", 0x38,
2929 "CR39", 0x39,
2930 "CR3A", 0x3A,
2931 "CR3B", 0x3B,
2932 "CR3C", 0x3C,
2933 "CR3D", 0x3D,
2934 "CR3E", 0x3E,
2935 "CR3F", 0x3F,
2936 NULL);
2937
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002938 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002940 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002941
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002942 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 "SR00", 0x00,
2944 "SR01", 0x01,
2945 "SR02", 0x02,
2946 "SR03", 0x03,
2947 "SR04", 0x04,
2948 "SR08", 0x08,
2949 "SR09", 0x09,
2950 "SR0A", 0x0A,
2951 "SR0B", 0x0B,
2952 "SR0D", 0x0D,
2953 "SR10", 0x10,
2954 "SR11", 0x11,
2955 "SR12", 0x12,
2956 "SR13", 0x13,
2957 "SR14", 0x14,
2958 "SR15", 0x15,
2959 "SR16", 0x16,
2960 "SR17", 0x17,
2961 "SR18", 0x18,
2962 "SR19", 0x19,
2963 "SR1A", 0x1A,
2964 "SR1B", 0x1B,
2965 "SR1C", 0x1C,
2966 "SR1D", 0x1D,
2967 "SR1E", 0x1E,
2968 "SR1F", 0x1F,
2969 NULL);
2970
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002971 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972}
2973
2974#endif /* CIRRUSFB_DEBUG */
2975