blob: 620327436082f37504a82f6ddcca0995364bd1ad [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/errno.h>
40#include <linux/string.h>
41#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/slab.h>
43#include <linux/delay.h>
44#include <linux/fb.h>
45#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/pgtable.h>
47
48#ifdef CONFIG_ZORRO
49#include <linux/zorro.h>
50#endif
51#ifdef CONFIG_PCI
52#include <linux/pci.h>
53#endif
54#ifdef CONFIG_AMIGA
55#include <asm/amigahw.h>
56#endif
57#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110058#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070059#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#else
61#define isPReP 0
62#endif
63
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070064#include <video/vga.h>
65#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Linus Torvalds1da177e2005-04-16 15:20:36 -070067/*****************************************************************
68 *
69 * debugging and utility macros
70 *
71 */
72
Linus Torvalds1da177e2005-04-16 15:20:36 -070073/* disable runtime assertions? */
74/* #define CIRRUSFB_NDEBUG */
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076/* debugging assertions */
77#ifndef CIRRUSFB_NDEBUG
78#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070079 if (!(expr)) { \
80 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070081 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#else
84#define assert(expr)
85#endif
86
Krzysztof Helt8503df62007-10-16 01:29:08 -070087#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/*****************************************************************
90 *
91 * chipset information
92 *
93 */
94
95/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070096enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 BT_NONE = 0,
Krzysztof Helt7cade312009-03-31 15:25:13 -070098 BT_SD64, /* GD5434 */
99 BT_PICCOLO, /* GD5426 */
100 BT_PICASSO, /* GD5426 or GD5428 */
101 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700105 BT_LAGUNA, /* GD5462/64 */
106 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700107};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109/*
110 * per-board-type information, used for enumerating and abstracting
111 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700112 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 * use direct indexing on this array
114 * NOTE: '__initdata' cannot be used as some of this info
115 * is required at runtime. Maybe separate into an init-only and
116 * a run-time table?
117 */
118static const struct cirrusfb_board_info_rec {
119 char *name; /* ASCII name of chipset */
120 long maxclock[5]; /* maximum video clock */
121 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700122 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
123 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700124 /* construct bit 19 of screen start address */
125 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* initial SR07 value, then for each mode */
128 unsigned char sr07;
129 unsigned char sr07_1bpp;
130 unsigned char sr07_1bpp_mux;
131 unsigned char sr07_8bpp;
132 unsigned char sr07_8bpp_mux;
133
134 unsigned char sr1f; /* SR1F VGA initial register value */
135} cirrusfb_board_info[] = {
136 [BT_SD64] = {
137 .name = "CL SD64",
138 .maxclock = {
139 /* guess */
140 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700141 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700143 .init_sr07 = true,
144 .init_sr1f = true,
145 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .sr07 = 0xF0,
147 .sr07_1bpp = 0xF0,
148 .sr07_8bpp = 0xF1,
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700149 .sr1f = 0x1E
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 },
151 [BT_PICCOLO] = {
152 .name = "CL Piccolo",
153 .maxclock = {
154 /* guess */
155 90000, 90000, 90000, 90000, 90000
156 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700157 .init_sr07 = true,
158 .init_sr1f = true,
159 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 .sr07 = 0x80,
161 .sr07_1bpp = 0x80,
162 .sr07_8bpp = 0x81,
163 .sr1f = 0x22
164 },
165 [BT_PICASSO] = {
166 .name = "CL Picasso",
167 .maxclock = {
168 /* guess */
169 90000, 90000, 90000, 90000, 90000
170 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700171 .init_sr07 = true,
172 .init_sr1f = true,
173 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 .sr07 = 0x20,
175 .sr07_1bpp = 0x20,
176 .sr07_8bpp = 0x21,
177 .sr1f = 0x22
178 },
179 [BT_SPECTRUM] = {
180 .name = "CL Spectrum",
181 .maxclock = {
182 /* guess */
183 90000, 90000, 90000, 90000, 90000
184 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700185 .init_sr07 = true,
186 .init_sr1f = true,
187 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 .sr07 = 0x80,
189 .sr07_1bpp = 0x80,
190 .sr07_8bpp = 0x81,
191 .sr1f = 0x22
192 },
193 [BT_PICASSO4] = {
194 .name = "CL Picasso4",
195 .maxclock = {
196 135100, 135100, 85500, 85500, 0
197 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700198 .init_sr07 = true,
199 .init_sr1f = false,
200 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700201 .sr07 = 0xA0,
202 .sr07_1bpp = 0xA0,
203 .sr07_1bpp_mux = 0xA6,
204 .sr07_8bpp = 0xA1,
205 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 .sr1f = 0
207 },
208 [BT_ALPINE] = {
209 .name = "CL Alpine",
210 .maxclock = {
211 /* for the GD5430. GD5446 can do more... */
212 85500, 85500, 50000, 28500, 0
213 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700214 .init_sr07 = true,
215 .init_sr1f = true,
216 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700218 .sr07_1bpp = 0xA0,
219 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 .sr07_8bpp = 0xA1,
221 .sr07_8bpp_mux = 0xA7,
222 .sr1f = 0x1C
223 },
224 [BT_GD5480] = {
225 .name = "CL GD5480",
226 .maxclock = {
227 135100, 200000, 200000, 135100, 135100
228 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700229 .init_sr07 = true,
230 .init_sr1f = true,
231 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 .sr07 = 0x10,
233 .sr07_1bpp = 0x11,
234 .sr07_8bpp = 0x11,
235 .sr1f = 0x1C
236 },
237 [BT_LAGUNA] = {
238 .name = "CL Laguna",
239 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700240 /* taken from X11 code */
241 170000, 170000, 170000, 170000, 135100,
242 },
243 .init_sr07 = false,
244 .init_sr1f = false,
245 .scrn_start_bit19 = true,
246 },
247 [BT_LAGUNAB] = {
248 .name = "CL Laguna AGP",
249 .maxclock = {
250 /* taken from X11 code */
251 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700330enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700331 CRT,
332 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700333};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700334#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336/* info about board */
337struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700339 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700340 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 unsigned char SFR; /* Shadow of special function register */
342
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700343 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700345 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700347 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348};
349
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700350static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700351static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353/****************************************************************************/
354/**** BEGIN PROTOTYPES ******************************************************/
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700357static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
358 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700361static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700362static void switch_monitor(struct cirrusfb_info *cinfo, int on);
363static void WGen(const struct cirrusfb_info *cinfo,
364 int regnum, unsigned char val);
365static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
366static void AttrOn(const struct cirrusfb_info *cinfo);
367static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
368static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
369static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
370static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
371 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700373static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
374 unsigned char *red, unsigned char *green,
375 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700377static void cirrusfb_WaitBLT(u8 __iomem *regbase);
378static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
379 u_short curx, u_short cury,
380 u_short destx, u_short desty,
381 u_short width, u_short height,
382 u_short line_length);
383static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
384 u_short x, u_short y,
385 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700386 u32 fg_color, u32 bg_color,
387 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700389static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700392static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
393static void cirrusfb_dbg_print_regs(struct fb_info *info,
394 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700395 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396#endif /* CIRRUSFB_DEBUG */
397
398/*** END PROTOTYPES ********************************************************/
399/*****************************************************************************/
400/*** BEGIN Interface Used by the World ***************************************/
401
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700402static inline int is_laguna(const struct cirrusfb_info *cinfo)
403{
404 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
405}
406
Krzysztof Helt8503df62007-10-16 01:29:08 -0700407static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
409/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 return 0;
415}
416
417/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700418static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 return 0;
423}
424
425/**** END Interface used by the World *************************************/
426/****************************************************************************/
427/**** BEGIN Hardware specific Routines **************************************/
428
Krzysztof Helt486ff382008-10-15 22:03:42 -0700429/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700430static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700432 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700433 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Krzysztof Helt486ff382008-10-15 22:03:42 -0700435 /* Read MCLK value */
436 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700437 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700440 * should divide it by to get VCLK
441 */
442
443 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700444 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700445 return 1;
446 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700447 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
450
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452}
453
Krzysztof Helt99a45842009-03-31 15:25:09 -0700454static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
455 struct fb_info *info)
456{
457 long freq;
458 long maxclock;
459 struct cirrusfb_info *cinfo = info->par;
460 unsigned maxclockidx = var->bits_per_pixel >> 3;
461
462 /* convert from ps to kHz */
463 freq = PICOS2KHZ(var->pixclock);
464
465 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
466
467 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
468 cinfo->multiplexing = 0;
469
470 /* If the frequency is greater than we can support, we might be able
471 * to use multiplexing for the video mode */
472 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700473 dev_err(info->device,
474 "Frequency greater than maxclock (%ld kHz)\n",
475 maxclock);
476 return -EINVAL;
477 }
478 /*
479 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
480 * pixel clock
481 */
482 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700483 switch (cinfo->btype) {
484 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700485 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700486 case BT_PICASSO4:
487 if (freq > 85500)
488 cinfo->multiplexing = 1;
489 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700490 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700491 if (freq > 135100)
492 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700493 break;
494
495 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700496 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700497 }
498 }
499#if 0
500 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
501 * the VCLK is double the pixel clock. */
502 switch (var->bits_per_pixel) {
503 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700504 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700505 if (var->xres <= 800)
506 /* Xbh has this type of clock for 32-bit */
507 freq /= 2;
508 break;
509 }
510#endif
511 return 0;
512}
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514static int cirrusfb_check_var(struct fb_var_screeninfo *var,
515 struct fb_info *info)
516{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700517 int yres;
518 /* memory size in pixels */
519 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700520 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700523 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 var->red.offset = 0;
525 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700526 var->green = var->red;
527 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 break;
529
530 case 8:
531 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700532 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700533 var->green = var->red;
534 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 break;
536
537 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700538 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 var->red.offset = 2;
540 var->green.offset = -3;
541 var->blue.offset = 8;
542 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700543 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 var->green.offset = 5;
545 var->blue.offset = 0;
546 }
547 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700548 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 var->blue.length = 5;
550 break;
551
Krzysztof Helt7cade312009-03-31 15:25:13 -0700552 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700553 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700554 var->red.offset = 0;
555 var->green.offset = 8;
556 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 } else {
558 var->red.offset = 16;
559 var->green.offset = 8;
560 var->blue.offset = 0;
561 }
562 var->red.length = 8;
563 var->green.length = 8;
564 var->blue.length = 8;
565 break;
566
567 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700568 dev_dbg(info->device,
569 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700570 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 /* should never occur */
572 break;
573 }
574
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700575 if (var->xres_virtual < var->xres)
576 var->xres_virtual = var->xres;
577 /* use highest possible virtual resolution */
578 if (var->yres_virtual == -1) {
579 var->yres_virtual = pixels / var->xres_virtual;
580
581 dev_info(info->device,
582 "virtual resolution set to maximum of %dx%d\n",
583 var->xres_virtual, var->yres_virtual);
584 }
585 if (var->yres_virtual < var->yres)
586 var->yres_virtual = var->yres;
587
588 if (var->xres_virtual * var->yres_virtual > pixels) {
589 dev_err(info->device, "mode %dx%dx%d rejected... "
590 "virtual resolution too high to fit into video memory!\n",
591 var->xres_virtual, var->yres_virtual,
592 var->bits_per_pixel);
593 return -EINVAL;
594 }
595
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700596 if (var->xoffset < 0)
597 var->xoffset = 0;
598 if (var->yoffset < 0)
599 var->yoffset = 0;
600
601 /* truncate xoffset and yoffset to maximum if too high */
602 if (var->xoffset > var->xres_virtual - var->xres)
603 var->xoffset = var->xres_virtual - var->xres - 1;
604 if (var->yoffset > var->yres_virtual - var->yres)
605 var->yoffset = var->yres_virtual - var->yres - 1;
606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 var->red.msb_right =
608 var->green.msb_right =
609 var->blue.msb_right =
610 var->transp.offset =
611 var->transp.length =
612 var->transp.msb_right = 0;
613
614 yres = var->yres;
615 if (var->vmode & FB_VMODE_DOUBLE)
616 yres *= 2;
617 else if (var->vmode & FB_VMODE_INTERLACED)
618 yres = (yres + 1) / 2;
619
620 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700621 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700622 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 return -EINVAL;
624 }
625
Krzysztof Helt99a45842009-03-31 15:25:09 -0700626 if (cirrusfb_check_pixclock(var, info))
627 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700629 if (!is_laguna(cinfo))
630 var->accel_flags = FB_ACCELF_TEXT;
631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 return 0;
633}
634
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700635static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700637 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700638 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700639
Krzysztof Helt8503df62007-10-16 01:29:08 -0700640 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700641 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Krzysztof Helt486ff382008-10-15 22:03:42 -0700643 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700644 dev_dbg(info->device, "Set %s as pixclock source.\n",
645 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700646 old1f |= 0x40;
647 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
648 if (div == 2)
649 old1e |= 1;
650
651 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700653 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654}
655
656/*************************************************************************
657 cirrusfb_set_par_foo()
658
659 actually writes the values for a new video mode into the hardware,
660**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700661static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
663 struct cirrusfb_info *cinfo = info->par;
664 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 u8 __iomem *regbase = cinfo->regbase;
666 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700667 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700669 int hdispend, hsyncstart, hsyncend, htotal;
670 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700671 long freq;
672 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700673 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700675 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700677
678 switch (var->bits_per_pixel) {
679 case 1:
680 info->fix.line_length = var->xres_virtual / 8;
681 info->fix.visual = FB_VISUAL_MONO10;
682 break;
683
684 case 8:
685 info->fix.line_length = var->xres_virtual;
686 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
687 break;
688
689 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700690 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700691 info->fix.line_length = var->xres_virtual *
692 var->bits_per_pixel >> 3;
693 info->fix.visual = FB_VISUAL_TRUECOLOR;
694 break;
695 }
696 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700698 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 bi = &cirrusfb_board_info[cinfo->btype];
701
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700702 hsyncstart = var->xres + var->right_margin;
703 hsyncend = hsyncstart + var->hsync_len;
704 htotal = (hsyncend + var->left_margin) / 8 - 5;
705 hdispend = var->xres / 8 - 1;
706 hsyncstart = hsyncstart / 8 + 1;
707 hsyncend = hsyncend / 8 + 1;
708
709 yres = var->yres;
710 vsyncstart = yres + var->lower_margin;
711 vsyncend = vsyncstart + var->vsync_len;
712 vtotal = vsyncend + var->upper_margin;
713 vdispend = yres - 1;
714
715 if (var->vmode & FB_VMODE_DOUBLE) {
716 yres *= 2;
717 vsyncstart *= 2;
718 vsyncend *= 2;
719 vtotal *= 2;
720 } else if (var->vmode & FB_VMODE_INTERLACED) {
721 yres = (yres + 1) / 2;
722 vsyncstart = (vsyncstart + 1) / 2;
723 vsyncend = (vsyncend + 1) / 2;
724 vtotal = (vtotal + 1) / 2;
725 }
726
727 vtotal -= 2;
728 vsyncstart -= 1;
729 vsyncend -= 1;
730
731 if (yres >= 1024) {
732 vtotal /= 2;
733 vsyncstart /= 2;
734 vsyncend /= 2;
735 vdispend /= 2;
736 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700737 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700738 htotal /= 2;
739 hsyncstart /= 2;
740 hsyncend /= 2;
741 hdispend /= 2;
742 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700744 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
746 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700747 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700748 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700750 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700751 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700753 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Krzysztof Helt8503df62007-10-16 01:29:08 -0700756 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700757 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700758 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700759 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700761 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 tmp = hsyncend % 32;
765 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700767 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700768 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700770 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700771 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
773 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700774 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700776 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700782 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700789 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 tmp |= 0x20;
794 if (var->vmode & FB_VMODE_DOUBLE)
795 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700796 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700797 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700799 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700802 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700805 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700806 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700808 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700811 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700812 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700814 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700815 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 tmp = 0;
818 if (var->vmode & FB_VMODE_INTERLACED)
819 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700820 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700822 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700826 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 tmp |= 128;
828
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700829 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700830 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700832 freq = PICOS2KHZ(var->pixclock);
Krzysztof Helt7cade312009-03-31 15:25:13 -0700833 if (cinfo->btype == BT_ALPINE && var->bits_per_pixel == 24)
834 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700835 if (cinfo->multiplexing)
836 freq /= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700837
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700838 bestclock(freq, &nom, &den, &div);
839
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700840 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
841 freq, nom, den, div);
842
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 /* set VCLK0 */
844 /* hardware RefClock: 14.31818 MHz */
845 /* formula: VClk = (OSC * N) / (D * (1+P)) */
846 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
847
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700848 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
849 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700850 /* if freq is close to mclk or mclk/2 select mclk
851 * as clock source
852 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700853 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700854 if (divMCLK) {
855 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700856 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700857 }
858 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700859 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700860 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
861 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
862 unsigned short tile_control;
863
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700864 if (cinfo->btype == BT_LAGUNAB) {
865 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
866 tile_control &= ~0x80;
867 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
868 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700869
870 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
871 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
872 control = fb_readw(cinfo->laguna_mmio + 0x402);
873 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
874 control &= ~0x6800;
875 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700876 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700877 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700878 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700879 tmp = den << 1;
880 if (div != 0)
881 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700882 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
883 if ((cinfo->btype == BT_SD64) ||
884 (cinfo->btype == BT_ALPINE) ||
885 (cinfo->btype == BT_GD5480))
886 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700888 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
889 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700890 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700891 vga_wseq(regbase, CL_SEQRE, tmp);
892 vga_wseq(regbase, CL_SEQR1E, nom);
893 } else {
894 vga_wseq(regbase, CL_SEQRB, nom);
895 vga_wseq(regbase, CL_SEQR1B, tmp);
896 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700897 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700899 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700901 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 else
903 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
904 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700905 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 /* don't know if it would hurt to also program this if no interlaced */
908 /* mode is used, but I feel better this way.. :-) */
909 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700910 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700915 /* enable display memory & CRTC I/O address for color mode */
916 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
918 tmp |= 0x40;
919 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
920 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700921 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700922 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700923 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
Krzysztof Helt8503df62007-10-16 01:29:08 -0700925 /* text cursor on and start line */
926 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
927 /* text cursor end line */
928 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
930 /******************************************************
931 *
932 * 1 bpp
933 *
934 */
935
936 /* programming for different color depths */
937 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700938 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700939 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
941 /* SR07 */
942 switch (cinfo->btype) {
943 case BT_SD64:
944 case BT_PICCOLO:
945 case BT_PICASSO:
946 case BT_SPECTRUM:
947 case BT_PICASSO4:
948 case BT_ALPINE:
949 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700950 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700951 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 bi->sr07_1bpp_mux : bi->sr07_1bpp);
953 break;
954
955 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700956 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700957 vga_wseq(regbase, CL_SEQR7,
958 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 break;
960
961 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700962 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 break;
964 }
965
966 /* Extended Sequencer Mode */
967 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
969 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700970 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700971 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
972 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 break;
974
975 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700976 /* ## vorher d0 avoid FIFO underruns..? */
977 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 break;
979
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700980 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 case BT_PICASSO4:
982 case BT_ALPINE:
983 case BT_GD5480:
984 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700985 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 /* do nothing */
987 break;
988
989 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700990 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 break;
992 }
993
Krzysztof Helt8503df62007-10-16 01:29:08 -0700994 /* pixel mask: pass-through for first plane */
995 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700996 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 /* hidden dac reg: 1280x1024 */
998 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 /* hidden dac: nothing */
1001 WHDR(cinfo, 0);
1002 /* memory mode: odd/even, ext. memory */
1003 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1004 /* plane mask: only write to first plane */
1005 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 }
1007
1008 /******************************************************
1009 *
1010 * 8 bpp
1011 *
1012 */
1013
1014 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001015 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 switch (cinfo->btype) {
1017 case BT_SD64:
1018 case BT_PICCOLO:
1019 case BT_PICASSO:
1020 case BT_SPECTRUM:
1021 case BT_PICASSO4:
1022 case BT_ALPINE:
1023 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001024 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001025 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1027 break;
1028
1029 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001030 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001031 vga_wseq(regbase, CL_SEQR7,
1032 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001033 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 break;
1035
1036 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001037 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 break;
1039 }
1040
1041 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001045 /* Fast Page-Mode writes */
1046 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 break;
1048
1049 case BT_PICASSO4:
1050#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001051 /* ### INCOMPLETE!! */
1052 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 /* We already set SRF and SR1F */
1056 break;
1057
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 Helt3b921832008-10-15 22:03:41 -07001106 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 break;
1108
1109 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001110 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 /* We already set SRF and SR1F */
1112 break;
1113
1114 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001115 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001116 vga_wseq(regbase, CL_SEQR7,
1117 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001118 control |= 0x2000;
1119 format |= 0x1400;
1120 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 break;
1122
1123 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001124 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126 }
1127
Krzysztof Helt8503df62007-10-16 01:29:08 -07001128 /* mode register: 256 color mode */
1129 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001131 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132#elif defined(CONFIG_ZORRO)
1133 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001134 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 }
1137
1138 /******************************************************
1139 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001140 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 *
1142 */
1143
Krzysztof Helt7cade312009-03-31 15:25:13 -07001144 else if (var->bits_per_pixel == 24) {
1145 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001148 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001149 vga_wseq(regbase, CL_SEQR7, 0x85);
1150 /* Fast Page-Mode writes */
1151 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 break;
1153
1154 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 vga_wseq(regbase, CL_SEQR7, 0x25);
1156 /* Fast Page-Mode writes */
1157 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 break;
1159
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001160 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001163 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001164 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 break;
1166
1167 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001168 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 /* We already set SRF and SR1F */
1170 break;
1171
1172 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001173 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001174 vga_wseq(regbase, CL_SEQR7,
1175 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001176 control |= 0x4000;
1177 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001178 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 break;
1180
1181 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001182 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 break;
1184 }
1185
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 /* mode register: 256 color mode */
1187 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001188 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1189 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 }
1191
1192 /******************************************************
1193 *
1194 * unknown/unsupported bpp
1195 *
1196 */
1197
Krzysztof Helt8503df62007-10-16 01:29:08 -07001198 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001199 dev_err(info->device,
1200 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
Krzysztof Helt6683e012009-03-31 15:25:06 -07001203 pitch = info->fix.line_length >> 3;
1204 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001206 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 tmp |= 0x10; /* offset overflow bit */
1208
Krzysztof Helt8503df62007-10-16 01:29:08 -07001209 /* screen start addr #16-18, fastpagemode cycles */
1210 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001212 /* screen start address bit 19 */
1213 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001214 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001216 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001217 tmp = 0;
1218 if ((htotal + 5) & 256)
1219 tmp |= 128;
1220 if (hdispend & 256)
1221 tmp |= 64;
1222 if (hsyncstart & 256)
1223 tmp |= 48;
1224 if (vtotal & 1024)
1225 tmp |= 8;
1226 if (vdispend & 1024)
1227 tmp |= 4;
1228 if (vsyncstart & 1024)
1229 tmp |= 3;
1230
1231 vga_wcrt(regbase, CL_CRT1E, tmp);
1232 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1233 }
1234
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235 /* pixel panning */
1236 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
1238 /* [ EGS: SetOffset(); ] */
1239 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001240 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001242 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001243 /* no tiles */
1244 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1245 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1246 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1247 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 /* finally, turn on everything - turn off "FullBandwidth" bit */
1249 /* also, set "DotClock%2" bit where requested */
1250 tmp = 0x01;
1251
1252/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1253 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1254 tmp |= 0x08;
1255*/
1256
Krzysztof Helt8503df62007-10-16 01:29:08 -07001257 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001258 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001261 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262#endif
1263
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 return 0;
1265}
1266
1267/* for some reason incomprehensible to me, cirrusfb requires that you write
1268 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001269static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001271 cirrusfb_set_par_foo(info);
1272 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273}
1274
Krzysztof Helt8503df62007-10-16 01:29:08 -07001275static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1276 unsigned blue, unsigned transp,
1277 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278{
1279 struct cirrusfb_info *cinfo = info->par;
1280
1281 if (regno > 255)
1282 return -EINVAL;
1283
1284 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1285 u32 v;
1286 red >>= (16 - info->var.red.length);
1287 green >>= (16 - info->var.green.length);
1288 blue >>= (16 - info->var.blue.length);
1289
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 return 1;
1292 v = (red << info->var.red.offset) |
1293 (green << info->var.green.offset) |
1294 (blue << info->var.blue.offset);
1295
Krzysztof Helt060b6002007-10-16 01:29:13 -07001296 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 return 0;
1298 }
1299
Krzysztof Helt8503df62007-10-16 01:29:08 -07001300 if (info->var.bits_per_pixel == 8)
1301 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
1303 return 0;
1304
1305}
1306
1307/*************************************************************************
1308 cirrusfb_pan_display()
1309
1310 performs display panning - provided hardware permits this
1311**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001312static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1313 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001315 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001317 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 struct cirrusfb_info *cinfo = info->par;
1319
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 /* no range checks for xoffset and yoffset, */
1321 /* as fb_pan_display has already done this */
1322 if (var->vmode & FB_VMODE_YWRAP)
1323 return -EINVAL;
1324
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
Krzysztof Helt99a45842009-03-31 15:25:09 -07001327 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329 if (info->var.bits_per_pixel == 1) {
1330 /* base is already correct */
1331 xpix = (unsigned char) (var->xoffset % 8);
1332 } else {
1333 base /= 4;
1334 xpix = (unsigned char) ((xoffset % 4) * 2);
1335 }
1336
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001337 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001338 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001341 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1342 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001344 /* 0xf2 is %11110010, exclude tmp bits */
1345 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 /* construct bits 16, 17 and 18 of screen start address */
1347 if (base & 0x10000)
1348 tmp |= 0x01;
1349 if (base & 0x20000)
1350 tmp |= 0x04;
1351 if (base & 0x40000)
1352 tmp |= 0x08;
1353
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001354 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
1356 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001357 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001358 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1359 if (is_laguna(cinfo))
1360 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1361 else
1362 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001363 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1364 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 /* write pixel panning value to AR33; this does not quite work in 8bpp
1367 *
1368 * ### Piccolo..? Will this work?
1369 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001371 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Krzysztof Helt8503df62007-10-16 01:29:08 -07001373 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374}
1375
Krzysztof Helt8503df62007-10-16 01:29:08 -07001376static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377{
1378 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001379 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1380 * then the caller blanks by setting the CLUT (Color Look Up Table)
1381 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1382 * failed due to e.g. a video mode which doesn't support it.
1383 * Implements VESA suspend and powerdown modes on hardware that
1384 * supports disabling hsync/vsync:
1385 * blank_mode == 2: suspend vsync
1386 * blank_mode == 3: suspend hsync
1387 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 */
1389 unsigned char val;
1390 struct cirrusfb_info *cinfo = info->par;
1391 int current_mode = cinfo->blank_mode;
1392
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001393 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
1395 if (info->state != FBINFO_STATE_RUNNING ||
1396 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001397 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 return 0;
1399 }
1400
1401 /* Undo current */
1402 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001403 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001404 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001405 val = 0;
1406 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001407 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001408 val = 0x20;
1409
1410 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1411 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
1413 switch (blank_mode) {
1414 case FB_BLANK_UNBLANK:
1415 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001416 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 break;
1418 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001419 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 break;
1421 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001422 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 break;
1424 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001425 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 break;
1427 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001428 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 return 1;
1430 }
1431
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001432 vga_wgfx(cinfo->regbase, CL_GRE, val);
1433
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001435 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
1437 /* Let fbcon do a soft blank for us */
1438 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1439}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001440
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441/**** END Hardware specific Routines **************************************/
1442/****************************************************************************/
1443/**** BEGIN Internal Routines ***********************************************/
1444
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001445static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001447 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 const struct cirrusfb_board_info_rec *bi;
1449
Krzysztof Helt8503df62007-10-16 01:29:08 -07001450 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451
1452 bi = &cirrusfb_board_info[cinfo->btype];
1453
1454 /* reset board globally */
1455 switch (cinfo->btype) {
1456 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001457 WSFR(cinfo, 0x01);
1458 udelay(500);
1459 WSFR(cinfo, 0x51);
1460 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 break;
1462 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001463 WSFR2(cinfo, 0xff);
1464 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 break;
1466 case BT_SD64:
1467 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001468 WSFR(cinfo, 0x1f);
1469 udelay(500);
1470 WSFR(cinfo, 0x4f);
1471 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 break;
1473 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001474 /* disable flickerfixer */
1475 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1476 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 /* mode */
1478 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001479 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001480 /* from Klaus' NetBSD driver: */
1481 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001482 case BT_ALPINE: /* fall through */
1483 /* put blitter into 542x compat */
1484 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 break;
1486
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001487 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001488 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 /* Nothing to do to reset the board. */
1490 break;
1491
1492 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001493 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 break;
1495 }
1496
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001497 /* make sure RAM size set by this point */
1498 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
1500 /* the P4 is not fully initialized here; I rely on it having been */
1501 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001502 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503
1504 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001505 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1506 WGen(cinfo, CL_POS102, 0x01);
1507 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
1509 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001510 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
Krzysztof Helt8503df62007-10-16 01:29:08 -07001512 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001513 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
Krzysztof Helt8503df62007-10-16 01:29:08 -07001515 /* FullBandwidth (video off) and 8/9 dot clock */
1516 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
Krzysztof Helt8503df62007-10-16 01:29:08 -07001518 /* "magic cookie" - doesn't make any sense to me.. */
1519/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1520 /* unlock all extension registers */
1521 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 switch (cinfo->btype) {
1524 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001525 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 break;
1527 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001528 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001529 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 break;
1531 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001532 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 break;
1534 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001535 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1536 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 break;
1538 }
1539 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 /* plane mask: nothing */
1541 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1542 /* character map select: doesn't even matter in gx mode */
1543 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001544 /* memory mode: chain4, ext. memory */
1545 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
1547 /* controller-internal base address of video memory */
1548 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001549 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
Krzysztof Helt8503df62007-10-16 01:29:08 -07001551 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1552 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Krzysztof Helt8503df62007-10-16 01:29:08 -07001554 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1555 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1556 /* graphics cursor Y position (..."... ) */
1557 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1558 /* graphics cursor attributes */
1559 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1560 /* graphics cursor pattern address */
1561 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562
1563 /* writing these on a P4 might give problems.. */
1564 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001565 /* configuration readback and ext. color */
1566 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1567 /* signature generator */
1568 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 }
1570
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 /* Screen A preset row scan: none */
1572 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1573 /* Text cursor start: disable text cursor */
1574 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1575 /* Text cursor end: - */
1576 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001577 /* text cursor location high: 0 */
1578 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1579 /* text cursor location low: 0 */
1580 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
Krzysztof Helt8503df62007-10-16 01:29:08 -07001582 /* Underline Row scanline: - */
1583 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 /* ext. display controls: ext.adr. wrap */
1586 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587
Krzysztof Helt8503df62007-10-16 01:29:08 -07001588 /* Set/Reset registes: - */
1589 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1590 /* Set/Reset enable: - */
1591 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1592 /* Color Compare: - */
1593 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1594 /* Data Rotate: - */
1595 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1596 /* Read Map Select: - */
1597 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1598 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1599 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1600 /* Miscellaneous: memory map base address, graphics mode */
1601 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1602 /* Color Don't care: involve all planes */
1603 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1604 /* Bit Mask: no mask at all */
1605 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001606
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001607 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001608 /* (5434 can't have bit 3 set for bitblt) */
1609 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 /* Graphics controller mode extensions: finer granularity,
1612 * 8byte data latches
1613 */
1614 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1617 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1618 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1619 /* Background color byte 1: - */
1620 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1621 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 /* Attribute Controller palette registers: "identity mapping" */
1624 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1625 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1626 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1627 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1628 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1629 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1630 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 /* Attribute Controller mode: graphics mode */
1642 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1643 /* Overscan color reg.: reg. 0 */
1644 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1645 /* Color Plane enable: Enable all 4 planes */
1646 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001647 /* Color Select: - */
1648 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Krzysztof Helt8503df62007-10-16 01:29:08 -07001652 /* BLT Start/status: Blitter reset */
1653 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1654 /* - " - : "end-of-reset" */
1655 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
1657 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001658 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 return;
1660}
1661
Krzysztof Helt8503df62007-10-16 01:29:08 -07001662static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
1664#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1665 static int IsOn = 0; /* XXX not ok for multiple boards */
1666
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 if (cinfo->btype == BT_PICASSO4)
1668 return; /* nothing to switch */
1669 if (cinfo->btype == BT_ALPINE)
1670 return; /* nothing to switch */
1671 if (cinfo->btype == BT_GD5480)
1672 return; /* nothing to switch */
1673 if (cinfo->btype == BT_PICASSO) {
1674 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 return;
1677 }
1678 if (on) {
1679 switch (cinfo->btype) {
1680 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001681 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 break;
1683 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 break;
1686 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 break;
1689 default: /* do nothing */ break;
1690 }
1691 } else {
1692 switch (cinfo->btype) {
1693 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 break;
1696 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 break;
1699 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001700 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001702 default: /* do nothing */
1703 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 }
1705 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706#endif /* CONFIG_ZORRO */
1707}
1708
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709/******************************************/
1710/* Linux 2.6-style accelerated functions */
1711/******************************************/
1712
Krzysztof Helt8343c892009-03-31 15:25:11 -07001713static int cirrusfb_sync(struct fb_info *info)
1714{
1715 struct cirrusfb_info *cinfo = info->par;
1716
1717 if (!is_laguna(cinfo)) {
1718 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1719 cpu_relax();
1720 }
1721 return 0;
1722}
1723
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724static void cirrusfb_fillrect(struct fb_info *info,
1725 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 struct fb_fillrect modded;
1728 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001729 struct cirrusfb_info *cinfo = info->par;
1730 int m = info->var.bits_per_pixel;
1731 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1732 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733
1734 if (info->state != FBINFO_STATE_RUNNING)
1735 return;
1736 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1737 cfb_fillrect(info, region);
1738 return;
1739 }
1740
1741 vxres = info->var.xres_virtual;
1742 vyres = info->var.yres_virtual;
1743
1744 memcpy(&modded, region, sizeof(struct fb_fillrect));
1745
Krzysztof Helt8503df62007-10-16 01:29:08 -07001746 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 modded.dx >= vxres || modded.dy >= vyres)
1748 return;
1749
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750 if (modded.dx + modded.width > vxres)
1751 modded.width = vxres - modded.dx;
1752 if (modded.dy + modded.height > vyres)
1753 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754
Krzysztof Helt060b6002007-10-16 01:29:13 -07001755 cirrusfb_RectFill(cinfo->regbase,
1756 info->var.bits_per_pixel,
1757 (region->dx * m) / 8, region->dy,
1758 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001759 color, color,
1760 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761}
1762
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763static void cirrusfb_copyarea(struct fb_info *info,
1764 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 struct fb_copyarea modded;
1767 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001768 struct cirrusfb_info *cinfo = info->par;
1769 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
1771 if (info->state != FBINFO_STATE_RUNNING)
1772 return;
1773 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1774 cfb_copyarea(info, area);
1775 return;
1776 }
1777
1778 vxres = info->var.xres_virtual;
1779 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001780 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
Krzysztof Helt8503df62007-10-16 01:29:08 -07001782 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 modded.sx >= vxres || modded.sy >= vyres ||
1784 modded.dx >= vxres || modded.dy >= vyres)
1785 return;
1786
Krzysztof Helt8503df62007-10-16 01:29:08 -07001787 if (modded.sx + modded.width > vxres)
1788 modded.width = vxres - modded.sx;
1789 if (modded.dx + modded.width > vxres)
1790 modded.width = vxres - modded.dx;
1791 if (modded.sy + modded.height > vyres)
1792 modded.height = vyres - modded.sy;
1793 if (modded.dy + modded.height > vyres)
1794 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Krzysztof Helt060b6002007-10-16 01:29:13 -07001796 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1797 (area->sx * m) / 8, area->sy,
1798 (area->dx * m) / 8, area->dy,
1799 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001800 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001801
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802}
1803
Krzysztof Helt8503df62007-10-16 01:29:08 -07001804static void cirrusfb_imageblit(struct fb_info *info,
1805 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806{
1807 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001808 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
Krzysztof Helt9e848062009-03-31 15:25:11 -07001810 if (info->state != FBINFO_STATE_RUNNING)
1811 return;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001812 /* Alpine acceleration does not work at 24bpp ?!? */
1813 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1 ||
1814 (cinfo->btype == BT_ALPINE && op == 0xc))
Krzysztof Helt9e848062009-03-31 15:25:11 -07001815 cfb_imageblit(info, image);
1816 else {
1817 unsigned size = ((image->width + 7) >> 3) * image->height;
1818 int m = info->var.bits_per_pixel;
1819 u32 fg, bg;
1820
1821 if (info->var.bits_per_pixel == 8) {
1822 fg = image->fg_color;
1823 bg = image->bg_color;
1824 } else {
1825 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1826 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1827 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001828 if (info->var.bits_per_pixel == 24) {
1829 /* clear background first */
1830 cirrusfb_RectFill(cinfo->regbase,
1831 info->var.bits_per_pixel,
1832 (image->dx * m) / 8, image->dy,
1833 (image->width * m) / 8,
1834 image->height,
1835 bg, bg,
1836 info->fix.line_length, 0x40);
1837 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001838 cirrusfb_RectFill(cinfo->regbase,
1839 info->var.bits_per_pixel,
1840 (image->dx * m) / 8, image->dy,
1841 (image->width * m) / 8, image->height,
1842 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001843 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001844 memcpy(info->screen_base, image->data, size);
1845 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846}
1847
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848#ifdef CONFIG_PPC_PREP
1849#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1850#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001851static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 *display = PREP_VIDEO_BASE;
1854 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855}
1856
1857#endif /* CONFIG_PPC_PREP */
1858
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001860static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
1862/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1863 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1864 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1865 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001866static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1867 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868{
1869 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001870 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001872 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001873 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1874
1875 mem = ((SR14 & 7) + 1) << 20;
1876 } else {
1877 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1878 switch ((SRF & 0x18)) {
1879 case 0x08:
1880 mem = 512 * 1024;
1881 break;
1882 case 0x10:
1883 mem = 1024 * 1024;
1884 break;
1885 /* 64-bit DRAM data bus width; assume 2MB.
1886 * Also indicates 2MB memory on the 5430.
1887 */
1888 case 0x18:
1889 mem = 2048 * 1024;
1890 break;
1891 default:
1892 dev_warn(info->device, "Unknown memory size!\n");
1893 mem = 1024 * 1024;
1894 }
1895 /* If DRAM bank switching is enabled, there must be
1896 * twice as much memory installed. (4MB on the 5434)
1897 */
1898 if (SRF & 0x80)
1899 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 return mem;
1904}
1905
Krzysztof Helt8503df62007-10-16 01:29:08 -07001906static void get_pci_addrs(const struct pci_dev *pdev,
1907 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001909 assert(pdev != NULL);
1910 assert(display != NULL);
1911 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 *display = 0;
1914 *registers = 0;
1915
1916 /* This is a best-guess for now */
1917
1918 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1919 *display = pci_resource_start(pdev, 1);
1920 *registers = pci_resource_start(pdev, 0);
1921 } else {
1922 *display = pci_resource_start(pdev, 0);
1923 *registers = pci_resource_start(pdev, 1);
1924 }
1925
Krzysztof Helt8503df62007-10-16 01:29:08 -07001926 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927}
1928
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001929static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001931 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001932 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001934 if (cinfo->laguna_mmio == NULL)
1935 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001936 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937#if 0 /* if system didn't claim this region, we would... */
1938 release_mem_region(0xA0000, 65535);
1939#endif
1940 if (release_io_ports)
1941 release_region(0x3C0, 32);
1942 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943}
1944#endif /* CONFIG_PCI */
1945
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001947static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948{
Al Virod91f5bb2007-10-17 00:27:18 +01001949 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001950 struct zorro_dev *zdev = to_zorro_dev(info->device);
1951
1952 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953
1954 if (cinfo->btype == BT_PICASSO4) {
1955 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001956 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001957 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001959 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001960 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962}
1963#endif /* CONFIG_ZORRO */
1964
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001965/* function table of the above functions */
1966static struct fb_ops cirrusfb_ops = {
1967 .owner = THIS_MODULE,
1968 .fb_open = cirrusfb_open,
1969 .fb_release = cirrusfb_release,
1970 .fb_setcolreg = cirrusfb_setcolreg,
1971 .fb_check_var = cirrusfb_check_var,
1972 .fb_set_par = cirrusfb_set_par,
1973 .fb_pan_display = cirrusfb_pan_display,
1974 .fb_blank = cirrusfb_blank,
1975 .fb_fillrect = cirrusfb_fillrect,
1976 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001977 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001978 .fb_imageblit = cirrusfb_imageblit,
1979};
1980
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001981static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001983 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 struct fb_var_screeninfo *var = &info->var;
1985
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 info->pseudo_palette = cinfo->pseudo_palette;
1987 info->flags = FBINFO_DEFAULT
1988 | FBINFO_HWACCEL_XPAN
1989 | FBINFO_HWACCEL_YPAN
1990 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07001991 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001993 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001995 info->fix.accel = FB_ACCEL_NONE;
1996 } else
1997 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
1998
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002000
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 if (cinfo->btype == BT_GD5480) {
2002 if (var->bits_per_pixel == 16)
2003 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002004 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 info->screen_base += 2 * MB_;
2006 }
2007
2008 /* Fill fix common fields */
2009 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2010 sizeof(info->fix.id));
2011
2012 /* monochrome: only 1 memory plane */
2013 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002014 info->fix.smem_len = info->screen_size;
2015 if (var->bits_per_pixel == 1)
2016 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 info->fix.xpanstep = 1;
2019 info->fix.ypanstep = 1;
2020 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021
2022 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024
2025 fb_alloc_cmap(&info->cmap, 256, 0);
2026
2027 return 0;
2028}
2029
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002030static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002032 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034
2035 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002036 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002038 /* set all the vital stuff */
2039 cirrusfb_set_fbinfo(info);
2040
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002041 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002043 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2044 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002045 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002046 err = -EINVAL;
2047 goto err_dealloc_cmap;
2048 }
2049
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 info->var.activate = FB_ACTIVATE_NOW;
2051
Krzysztof Helt99a45842009-03-31 15:25:09 -07002052 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 if (err < 0) {
2054 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002055 dev_dbg(info->device,
2056 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002057 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 }
2059
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 err = register_framebuffer(info);
2061 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002062 dev_err(info->device,
2063 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 goto err_dealloc_cmap;
2065 }
2066
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 return 0;
2068
2069err_dealloc_cmap:
2070 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 return err;
2072}
2073
Krzysztof Helt8503df62007-10-16 01:29:08 -07002074static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075{
2076 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
Krzysztof Helt8503df62007-10-16 01:29:08 -07002078 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002079 unregister_framebuffer(info);
2080 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002081 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002082 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002083 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084}
2085
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002087static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2088 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089{
2090 struct cirrusfb_info *cinfo;
2091 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 unsigned long board_addr, board_size;
2093 int ret;
2094
2095 ret = pci_enable_device(pdev);
2096 if (ret < 0) {
2097 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2098 goto err_out;
2099 }
2100
2101 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2102 if (!info) {
2103 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2104 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002105 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 }
2107
2108 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002109 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002111 dev_dbg(info->device,
2112 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002113 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002114 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2115 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116
Krzysztof Helt8503df62007-10-16 01:29:08 -07002117 if (isPReP) {
2118 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002120 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002122 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002123 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002125 dev_dbg(info->device,
2126 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002127 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002128 /* FIXME: this forces VGA. alternatives? */
2129 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002130 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 }
2132
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002133 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002134 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002136 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002137 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138
2139 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002140 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002141 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2142 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 goto err_release_fb;
2144 }
2145#if 0 /* if the system didn't claim this region, we would... */
2146 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002147 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2148 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 ret = -EBUSY;
2150 goto err_release_regions;
2151 }
2152#endif
2153 if (request_region(0x3C0, 32, "cirrusfb"))
2154 release_io_ports = 1;
2155
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002156 info->screen_base = ioremap(board_addr, board_size);
2157 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 ret = -EIO;
2159 goto err_release_legacy;
2160 }
2161
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002162 info->fix.smem_start = board_addr;
2163 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 cinfo->unmap = cirrusfb_pci_unmap;
2165
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002166 dev_info(info->device,
2167 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2168 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 pci_set_drvdata(pdev, info);
2170
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002171 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002172 if (!ret)
2173 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002175 pci_set_drvdata(pdev, NULL);
2176 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177err_release_legacy:
2178 if (release_io_ports)
2179 release_region(0x3C0, 32);
2180#if 0
2181 release_mem_region(0xA0000, 65535);
2182err_release_regions:
2183#endif
2184 pci_release_regions(pdev);
2185err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002186 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002187 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189err_out:
2190 return ret;
2191}
2192
Krzysztof Helt8503df62007-10-16 01:29:08 -07002193static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194{
2195 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196
Krzysztof Helt8503df62007-10-16 01:29:08 -07002197 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198}
2199
2200static struct pci_driver cirrusfb_pci_driver = {
2201 .name = "cirrusfb",
2202 .id_table = cirrusfb_pci_table,
2203 .probe = cirrusfb_pci_register,
2204 .remove = __devexit_p(cirrusfb_pci_unregister),
2205#ifdef CONFIG_PM
2206#if 0
2207 .suspend = cirrusfb_pci_suspend,
2208 .resume = cirrusfb_pci_resume,
2209#endif
2210#endif
2211};
2212#endif /* CONFIG_PCI */
2213
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002215static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2216 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217{
2218 struct cirrusfb_info *cinfo;
2219 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002220 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 struct zorro_dev *z2 = NULL;
2222 unsigned long board_addr, board_size, size;
2223 int ret;
2224
2225 btype = ent->driver_data;
2226 if (cirrusfb_zorro_table2[btype].id2)
2227 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2228 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229
2230 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2231 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002232 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 ret = -ENOMEM;
2234 goto err_out;
2235 }
2236
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002237 dev_info(info->device, "%s board detected\n",
2238 cirrusfb_board_info[btype].name);
2239
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 cinfo->btype = btype;
2242
Al Viro36ea96a2007-10-27 19:46:58 +01002243 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002244 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 board_addr = zorro_resource_start(z);
2247 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002248 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
2250 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002251 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2252 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 ret = -EBUSY;
2254 goto err_release_fb;
2255 }
2256
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 ret = -EIO;
2258
2259 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002260 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261
2262 /* To be precise, for the P4 this is not the */
2263 /* begin of the board, but the begin of RAM. */
2264 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2265 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002266 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 if (!cinfo->regbase)
2268 goto err_release_region;
2269
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002270 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002271 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002273 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002275 info->fix.smem_start = board_addr + 16777216;
2276 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2277 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 goto err_unmap_regbase;
2279 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002280 dev_info(info->device, " REG at $%lx\n",
2281 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002283 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002285 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002287 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2288 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 goto err_release_region;
2290
2291 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002292 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002293 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002295 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002296 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 }
2298 cinfo->unmap = cirrusfb_zorro_unmap;
2299
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002300 dev_info(info->device,
2301 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2302 board_size / MB_, board_addr);
2303
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 zorro_set_drvdata(z, info);
2305
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002306 /* MCLK select etc. */
2307 if (cirrusfb_board_info[btype].init_sr1f)
2308 vga_wseq(cinfo->regbase, CL_SEQR1F,
2309 cirrusfb_board_info[btype].sr1f);
2310
Al Virod91f5bb2007-10-17 00:27:18 +01002311 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002312 if (!ret)
2313 return 0;
2314
2315 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2316 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
2318err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002319 if (btype == BT_PICASSO4)
2320 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321err_release_region:
2322 release_region(board_addr, board_size);
2323err_release_fb:
2324 framebuffer_release(info);
2325err_out:
2326 return ret;
2327}
2328
2329void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2330{
2331 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332
Krzysztof Helt8503df62007-10-16 01:29:08 -07002333 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334}
2335
2336static struct zorro_driver cirrusfb_zorro_driver = {
2337 .name = "cirrusfb",
2338 .id_table = cirrusfb_zorro_table,
2339 .probe = cirrusfb_zorro_register,
2340 .remove = __devexit_p(cirrusfb_zorro_unregister),
2341};
2342#endif /* CONFIG_ZORRO */
2343
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002345static int __init cirrusfb_setup(char *options)
2346{
Vlada Pericee119402008-11-19 15:36:45 -08002347 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 if (!options || !*options)
2350 return 0;
2351
Krzysztof Helt8503df62007-10-16 01:29:08 -07002352 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002353 if (!*this_opt)
2354 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 if (!strcmp(this_opt, "noaccel"))
2357 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002358 else if (!strncmp(this_opt, "mode:", 5))
2359 mode_option = this_opt + 5;
2360 else
2361 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362 }
2363 return 0;
2364}
2365#endif
2366
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 /*
2368 * Modularization
2369 */
2370
2371MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2372MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2373MODULE_LICENSE("GPL");
2374
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002375static int __init cirrusfb_init(void)
2376{
2377 int error = 0;
2378
2379#ifndef MODULE
2380 char *option = NULL;
2381
2382 if (fb_get_options("cirrusfb", &option))
2383 return -ENODEV;
2384 cirrusfb_setup(option);
2385#endif
2386
2387#ifdef CONFIG_ZORRO
2388 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2389#endif
2390#ifdef CONFIG_PCI
2391 error |= pci_register_driver(&cirrusfb_pci_driver);
2392#endif
2393 return error;
2394}
2395
Krzysztof Helt8503df62007-10-16 01:29:08 -07002396static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397{
2398#ifdef CONFIG_PCI
2399 pci_unregister_driver(&cirrusfb_pci_driver);
2400#endif
2401#ifdef CONFIG_ZORRO
2402 zorro_unregister_driver(&cirrusfb_zorro_driver);
2403#endif
2404}
2405
2406module_init(cirrusfb_init);
2407
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002408module_param(mode_option, charp, 0);
2409MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002410module_param(noaccel, bool, 0);
2411MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002412
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413#ifdef MODULE
2414module_exit(cirrusfb_exit);
2415#endif
2416
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417/**********************************************************************/
2418/* about the following functions - I have used the same names for the */
2419/* functions as Markus Wild did in his Retina driver for NetBSD as */
2420/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002421/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422/**********************************************************************/
2423
2424/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002425static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426 int regnum, unsigned char val)
2427{
2428 unsigned long regofs = 0;
2429
2430 if (cinfo->btype == BT_PICASSO) {
2431 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002432/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2433 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2435 regofs = 0xfff;
2436 }
2437
Krzysztof Helt8503df62007-10-16 01:29:08 -07002438 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439}
2440
2441/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002442static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443{
2444 unsigned long regofs = 0;
2445
2446 if (cinfo->btype == BT_PICASSO) {
2447 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002448/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2449 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2451 regofs = 0xfff;
2452 }
2453
Krzysztof Helt8503df62007-10-16 01:29:08 -07002454 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455}
2456
2457/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002458static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461
Krzysztof Helt8503df62007-10-16 01:29:08 -07002462 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 /* if we're just in "write value" mode, write back the */
2464 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002465 vga_w(cinfo->regbase, VGA_ATT_IW,
2466 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467 }
2468 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2470 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471
2472 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474}
2475
2476/*** WHDR() - write into the Hidden DAC register ***/
2477/* as the HDR is the only extension register that requires special treatment
2478 * (the other extension registers are accessible just like the "ordinary"
2479 * registers of their functional group) here is a specialized routine for
2480 * accessing the HDR
2481 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002482static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483{
2484 unsigned char dummy;
2485
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002486 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002487 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 if (cinfo->btype == BT_PICASSO) {
2489 /* Klaus' hint for correct access to HDR on some boards */
2490 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002491 WGen(cinfo, VGA_PEL_MSK, 0x00);
2492 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002494 dummy = RGen(cinfo, VGA_PEL_IW);
2495 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 }
2497 /* now do the usual stuff to access the HDR */
2498
Krzysztof Helt8503df62007-10-16 01:29:08 -07002499 dummy = RGen(cinfo, VGA_PEL_MSK);
2500 udelay(200);
2501 dummy = RGen(cinfo, VGA_PEL_MSK);
2502 udelay(200);
2503 dummy = RGen(cinfo, VGA_PEL_MSK);
2504 udelay(200);
2505 dummy = RGen(cinfo, VGA_PEL_MSK);
2506 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 WGen(cinfo, VGA_PEL_MSK, val);
2509 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510
2511 if (cinfo->btype == BT_PICASSO) {
2512 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 dummy = RGen(cinfo, VGA_PEL_IW);
2514 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515
2516 /* and at the end, restore the mask value */
2517 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518 WGen(cinfo, VGA_PEL_MSK, 0xff);
2519 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 }
2521}
2522
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002524static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525{
2526#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002529 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530#endif
2531}
2532
2533/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002534static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535{
2536#ifdef CONFIG_ZORRO
2537 /* writing an arbitrary value to this one causes the monitor switcher */
2538 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002539 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542#endif
2543}
2544
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 unsigned char green, unsigned char blue)
2548{
2549 unsigned int data = VGA_PEL_D;
2550
2551 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002552 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553
2554 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002555 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002556 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 /* but DAC data register IS, at least for Picasso II */
2558 if (cinfo->btype == BT_PICASSO)
2559 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560 vga_w(cinfo->regbase, data, red);
2561 vga_w(cinfo->regbase, data, green);
2562 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002564 vga_w(cinfo->regbase, data, blue);
2565 vga_w(cinfo->regbase, data, green);
2566 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 }
2568}
2569
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570#if 0
2571/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573 unsigned char *green, unsigned char *blue)
2574{
2575 unsigned int data = VGA_PEL_D;
2576
Krzysztof Helt8503df62007-10-16 01:29:08 -07002577 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578
2579 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2580 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2581 if (cinfo->btype == BT_PICASSO)
2582 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002583 *red = vga_r(cinfo->regbase, data);
2584 *green = vga_r(cinfo->regbase, data);
2585 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002587 *blue = vga_r(cinfo->regbase, data);
2588 *green = vga_r(cinfo->regbase, data);
2589 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 }
2591}
2592#endif
2593
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594/*******************************************************************
2595 cirrusfb_WaitBLT()
2596
2597 Wait for the BitBLT engine to complete a possible earlier job
2598*********************************************************************/
2599
2600/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002601static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002603 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002604 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605}
2606
2607/*******************************************************************
2608 cirrusfb_BitBLT()
2609
2610 perform accelerated "scrolling"
2611********************************************************************/
2612
Krzysztof Helt8343c892009-03-31 15:25:11 -07002613static void cirrusfb_set_blitter(u8 __iomem *regbase,
2614 u_short nwidth, u_short nheight,
2615 u_long nsrc, u_long ndest,
2616 u_short bltmode, u_short line_length)
2617
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002620 /* dest pitch low */
2621 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2622 /* dest pitch hi */
2623 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2624 /* source pitch low */
2625 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2626 /* source pitch hi */
2627 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628
2629 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002630 /* BLT width low */
2631 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2632 /* BLT width hi */
2633 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634
2635 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002636 /* BLT height low */
2637 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2638 /* BLT width hi */
2639 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640
2641 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002642 /* BLT dest low */
2643 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2644 /* BLT dest mid */
2645 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2646 /* BLT dest hi */
2647 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648
2649 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002650 /* BLT src low */
2651 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2652 /* BLT src mid */
2653 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2654 /* BLT src hi */
2655 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656
2657 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002658 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
2660 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002661 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662
2663 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002664 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002665}
2666
2667/*******************************************************************
2668 cirrusfb_BitBLT()
2669
2670 perform accelerated "scrolling"
2671********************************************************************/
2672
2673static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2674 u_short curx, u_short cury,
2675 u_short destx, u_short desty,
2676 u_short width, u_short height,
2677 u_short line_length)
2678{
2679 u_short nwidth = width - 1;
2680 u_short nheight = height - 1;
2681 u_long nsrc, ndest;
2682 u_char bltmode;
2683
2684 bltmode = 0x00;
2685 /* if source adr < dest addr, do the Blt backwards */
2686 if (cury <= desty) {
2687 if (cury == desty) {
2688 /* if src and dest are on the same line, check x */
2689 if (curx < destx)
2690 bltmode |= 0x01;
2691 } else
2692 bltmode |= 0x01;
2693 }
2694 /* standard case: forward blitting */
2695 nsrc = (cury * line_length) + curx;
2696 ndest = (desty * line_length) + destx;
2697 if (bltmode) {
2698 /* this means start addresses are at the end,
2699 * counting backwards
2700 */
2701 nsrc += nheight * line_length + nwidth;
2702 ndest += nheight * line_length + nwidth;
2703 }
2704
2705 cirrusfb_WaitBLT(regbase);
2706
2707 cirrusfb_set_blitter(regbase, nwidth, nheight,
2708 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709}
2710
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711/*******************************************************************
2712 cirrusfb_RectFill()
2713
2714 perform accelerated rectangle fill
2715********************************************************************/
2716
Krzysztof Helt8503df62007-10-16 01:29:08 -07002717static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002719 u32 fg_color, u32 bg_color, u_short line_length,
2720 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002722 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 u_char op;
2724
Krzysztof Helt8503df62007-10-16 01:29:08 -07002725 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727 /* This is a ColorExpand Blt, using the */
2728 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002729 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2730 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731
Krzysztof Helt9e848062009-03-31 15:25:11 -07002732 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002733 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002734 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2735 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2736 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002737 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002738 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002739 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2740 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002741 op = 0xa0;
2742 }
2743 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002744 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2745 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2746 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002748 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002749 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750}
2751
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752/**************************************************************************
2753 * bestclock() - determine closest possible clock lower(?) than the
2754 * desired pixel clock
2755 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002756static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002758 int n, d;
2759 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760
Krzysztof Helt8503df62007-10-16 01:29:08 -07002761 assert(nom != NULL);
2762 assert(den != NULL);
2763 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764
2765 *nom = 0;
2766 *den = 0;
2767 *div = 0;
2768
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 if (freq < 8000)
2770 freq = 8000;
2771
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002772 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773
2774 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002775 int s = 0;
2776
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002777 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002779 int temp = d;
2780
2781 if (temp > 31) {
2782 s = 1;
2783 temp >>= 1;
2784 }
2785 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002786 h = h > freq ? h - freq : freq - h;
2787 if (h < diff) {
2788 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002790 *den = temp;
2791 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 }
2793 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002794 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002796 if (d > 31) {
2797 s = 1;
2798 d >>= 1;
2799 }
2800 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002801 h = h > freq ? h - freq : freq - h;
2802 if (h < diff) {
2803 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002805 *den = d;
2806 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 }
2808 }
2809 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810}
2811
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812/* -------------------------------------------------------------------------
2813 *
2814 * debugging functions
2815 *
2816 * -------------------------------------------------------------------------
2817 */
2818
2819#ifdef CIRRUSFB_DEBUG
2820
2821/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 * cirrusfb_dbg_print_regs
2823 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2824 * @reg_class: type of registers to read: %CRT, or %SEQ
2825 *
2826 * DESCRIPTION:
2827 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2828 * old-style I/O ports are queried for information, otherwise MMIO is
2829 * used at the given @base address to query the information.
2830 */
2831
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002832static void cirrusfb_dbg_print_regs(struct fb_info *info,
2833 caddr_t regbase,
2834 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835{
2836 va_list list;
2837 unsigned char val = 0;
2838 unsigned reg;
2839 char *name;
2840
Krzysztof Helt8503df62007-10-16 01:29:08 -07002841 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842
Krzysztof Helt8503df62007-10-16 01:29:08 -07002843 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002845 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846
2847 switch (reg_class) {
2848 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002849 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 break;
2851 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002852 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853 break;
2854 default:
2855 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002856 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 break;
2858 }
2859
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002860 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861
Krzysztof Helt8503df62007-10-16 01:29:08 -07002862 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 }
2864
Krzysztof Helt8503df62007-10-16 01:29:08 -07002865 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866}
2867
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869 * cirrusfb_dbg_reg_dump
2870 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2871 *
2872 * DESCRIPTION:
2873 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2874 * old-style I/O ports are queried for information, otherwise MMIO is
2875 * used at the given @base address to query the information.
2876 */
2877
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002878static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002879{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002880 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002882 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883 "CR00", 0x00,
2884 "CR01", 0x01,
2885 "CR02", 0x02,
2886 "CR03", 0x03,
2887 "CR04", 0x04,
2888 "CR05", 0x05,
2889 "CR06", 0x06,
2890 "CR07", 0x07,
2891 "CR08", 0x08,
2892 "CR09", 0x09,
2893 "CR0A", 0x0A,
2894 "CR0B", 0x0B,
2895 "CR0C", 0x0C,
2896 "CR0D", 0x0D,
2897 "CR0E", 0x0E,
2898 "CR0F", 0x0F,
2899 "CR10", 0x10,
2900 "CR11", 0x11,
2901 "CR12", 0x12,
2902 "CR13", 0x13,
2903 "CR14", 0x14,
2904 "CR15", 0x15,
2905 "CR16", 0x16,
2906 "CR17", 0x17,
2907 "CR18", 0x18,
2908 "CR22", 0x22,
2909 "CR24", 0x24,
2910 "CR26", 0x26,
2911 "CR2D", 0x2D,
2912 "CR2E", 0x2E,
2913 "CR2F", 0x2F,
2914 "CR30", 0x30,
2915 "CR31", 0x31,
2916 "CR32", 0x32,
2917 "CR33", 0x33,
2918 "CR34", 0x34,
2919 "CR35", 0x35,
2920 "CR36", 0x36,
2921 "CR37", 0x37,
2922 "CR38", 0x38,
2923 "CR39", 0x39,
2924 "CR3A", 0x3A,
2925 "CR3B", 0x3B,
2926 "CR3C", 0x3C,
2927 "CR3D", 0x3D,
2928 "CR3E", 0x3E,
2929 "CR3F", 0x3F,
2930 NULL);
2931
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002932 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002934 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002936 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937 "SR00", 0x00,
2938 "SR01", 0x01,
2939 "SR02", 0x02,
2940 "SR03", 0x03,
2941 "SR04", 0x04,
2942 "SR08", 0x08,
2943 "SR09", 0x09,
2944 "SR0A", 0x0A,
2945 "SR0B", 0x0B,
2946 "SR0D", 0x0D,
2947 "SR10", 0x10,
2948 "SR11", 0x11,
2949 "SR12", 0x12,
2950 "SR13", 0x13,
2951 "SR14", 0x14,
2952 "SR15", 0x15,
2953 "SR16", 0x16,
2954 "SR17", 0x17,
2955 "SR18", 0x18,
2956 "SR19", 0x19,
2957 "SR1A", 0x1A,
2958 "SR1B", 0x1B,
2959 "SR1C", 0x1C,
2960 "SR1D", 0x1D,
2961 "SR1E", 0x1E,
2962 "SR1F", 0x1F,
2963 NULL);
2964
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002965 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966}
2967
2968#endif /* CIRRUSFB_DEBUG */
2969