blob: 6603273f4ce59a886374609c5288369866b85127 [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,
98 BT_SD64,
99 BT_PICCOLO,
100 BT_PICASSO,
101 BT_SPECTRUM,
102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700105 BT_LAGUNA, /* GD5462/64 */
106 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700107};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109/*
110 * per-board-type information, used for enumerating and abstracting
111 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700112 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 * use direct indexing on this array
114 * NOTE: '__initdata' cannot be used as some of this info
115 * is required at runtime. Maybe separate into an init-only and
116 * a run-time table?
117 */
118static const struct cirrusfb_board_info_rec {
119 char *name; /* ASCII name of chipset */
120 long maxclock[5]; /* maximum video clock */
121 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700122 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
123 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700124 /* construct bit 19 of screen start address */
125 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* initial SR07 value, then for each mode */
128 unsigned char sr07;
129 unsigned char sr07_1bpp;
130 unsigned char sr07_1bpp_mux;
131 unsigned char sr07_8bpp;
132 unsigned char sr07_8bpp_mux;
133
134 unsigned char sr1f; /* SR1F VGA initial register value */
135} cirrusfb_board_info[] = {
136 [BT_SD64] = {
137 .name = "CL SD64",
138 .maxclock = {
139 /* guess */
140 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700141 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700143 .init_sr07 = true,
144 .init_sr1f = true,
145 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .sr07 = 0xF0,
147 .sr07_1bpp = 0xF0,
148 .sr07_8bpp = 0xF1,
149 .sr1f = 0x20
150 },
151 [BT_PICCOLO] = {
152 .name = "CL Piccolo",
153 .maxclock = {
154 /* guess */
155 90000, 90000, 90000, 90000, 90000
156 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700157 .init_sr07 = true,
158 .init_sr1f = true,
159 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 .sr07 = 0x80,
161 .sr07_1bpp = 0x80,
162 .sr07_8bpp = 0x81,
163 .sr1f = 0x22
164 },
165 [BT_PICASSO] = {
166 .name = "CL Picasso",
167 .maxclock = {
168 /* guess */
169 90000, 90000, 90000, 90000, 90000
170 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700171 .init_sr07 = true,
172 .init_sr1f = true,
173 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 .sr07 = 0x20,
175 .sr07_1bpp = 0x20,
176 .sr07_8bpp = 0x21,
177 .sr1f = 0x22
178 },
179 [BT_SPECTRUM] = {
180 .name = "CL Spectrum",
181 .maxclock = {
182 /* guess */
183 90000, 90000, 90000, 90000, 90000
184 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700185 .init_sr07 = true,
186 .init_sr1f = true,
187 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 .sr07 = 0x80,
189 .sr07_1bpp = 0x80,
190 .sr07_8bpp = 0x81,
191 .sr1f = 0x22
192 },
193 [BT_PICASSO4] = {
194 .name = "CL Picasso4",
195 .maxclock = {
196 135100, 135100, 85500, 85500, 0
197 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700198 .init_sr07 = true,
199 .init_sr1f = false,
200 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700201 .sr07 = 0xA0,
202 .sr07_1bpp = 0xA0,
203 .sr07_1bpp_mux = 0xA6,
204 .sr07_8bpp = 0xA1,
205 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 .sr1f = 0
207 },
208 [BT_ALPINE] = {
209 .name = "CL Alpine",
210 .maxclock = {
211 /* for the GD5430. GD5446 can do more... */
212 85500, 85500, 50000, 28500, 0
213 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700214 .init_sr07 = true,
215 .init_sr1f = true,
216 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700218 .sr07_1bpp = 0xA0,
219 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 .sr07_8bpp = 0xA1,
221 .sr07_8bpp_mux = 0xA7,
222 .sr1f = 0x1C
223 },
224 [BT_GD5480] = {
225 .name = "CL GD5480",
226 .maxclock = {
227 135100, 200000, 200000, 135100, 135100
228 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700229 .init_sr07 = true,
230 .init_sr1f = true,
231 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 .sr07 = 0x10,
233 .sr07_1bpp = 0x11,
234 .sr07_8bpp = 0x11,
235 .sr1f = 0x1C
236 },
237 [BT_LAGUNA] = {
238 .name = "CL Laguna",
239 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700240 /* taken from X11 code */
241 170000, 170000, 170000, 170000, 135100,
242 },
243 .init_sr07 = false,
244 .init_sr1f = false,
245 .scrn_start_bit19 = true,
246 },
247 [BT_LAGUNAB] = {
248 .name = "CL Laguna AGP",
249 .maxclock = {
250 /* taken from X11 code */
251 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700330enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700331 CRT,
332 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700333};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700334#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336/* info about board */
337struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700339 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700340 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 unsigned char SFR; /* Shadow of special function register */
342
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700343 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700345 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700347 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348};
349
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700350static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700351static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353/****************************************************************************/
354/**** BEGIN PROTOTYPES ******************************************************/
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700357static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
358 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700361static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700362static void switch_monitor(struct cirrusfb_info *cinfo, int on);
363static void WGen(const struct cirrusfb_info *cinfo,
364 int regnum, unsigned char val);
365static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
366static void AttrOn(const struct cirrusfb_info *cinfo);
367static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
368static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
369static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
370static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
371 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700373static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
374 unsigned char *red, unsigned char *green,
375 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700377static void cirrusfb_WaitBLT(u8 __iomem *regbase);
378static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
379 u_short curx, u_short cury,
380 u_short destx, u_short desty,
381 u_short width, u_short height,
382 u_short line_length);
383static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
384 u_short x, u_short y,
385 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700386 u32 fg_color, u32 bg_color,
387 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700389static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700392static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
393static void cirrusfb_dbg_print_regs(struct fb_info *info,
394 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700395 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396#endif /* CIRRUSFB_DEBUG */
397
398/*** END PROTOTYPES ********************************************************/
399/*****************************************************************************/
400/*** BEGIN Interface Used by the World ***************************************/
401
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700402static inline int is_laguna(const struct cirrusfb_info *cinfo)
403{
404 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
405}
406
Krzysztof Helt8503df62007-10-16 01:29:08 -0700407static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
409/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 return 0;
415}
416
417/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700418static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 return 0;
423}
424
425/**** END Interface used by the World *************************************/
426/****************************************************************************/
427/**** BEGIN Hardware specific Routines **************************************/
428
Krzysztof Helt486ff382008-10-15 22:03:42 -0700429/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700430static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700432 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700433 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Krzysztof Helt486ff382008-10-15 22:03:42 -0700435 /* Read MCLK value */
436 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700437 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700440 * should divide it by to get VCLK
441 */
442
443 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700444 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700445 return 1;
446 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700447 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
450
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452}
453
Krzysztof Helt99a45842009-03-31 15:25:09 -0700454static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
455 struct fb_info *info)
456{
457 long freq;
458 long maxclock;
459 struct cirrusfb_info *cinfo = info->par;
460 unsigned maxclockidx = var->bits_per_pixel >> 3;
461
462 /* convert from ps to kHz */
463 freq = PICOS2KHZ(var->pixclock);
464
465 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
466
467 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
468 cinfo->multiplexing = 0;
469
470 /* If the frequency is greater than we can support, we might be able
471 * to use multiplexing for the video mode */
472 if (freq > maxclock) {
473 switch (cinfo->btype) {
474 case BT_ALPINE:
475 case BT_GD5480:
476 cinfo->multiplexing = 1;
477 break;
478
479 default:
480 dev_err(info->device,
481 "Frequency greater than maxclock (%ld kHz)\n",
482 maxclock);
483 return -EINVAL;
484 }
485 }
486#if 0
487 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
488 * the VCLK is double the pixel clock. */
489 switch (var->bits_per_pixel) {
490 case 16:
491 case 32:
492 if (var->xres <= 800)
493 /* Xbh has this type of clock for 32-bit */
494 freq /= 2;
495 break;
496 }
497#endif
498 return 0;
499}
500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501static int cirrusfb_check_var(struct fb_var_screeninfo *var,
502 struct fb_info *info)
503{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700504 int yres;
505 /* memory size in pixels */
506 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700509 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 var->red.offset = 0;
511 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700512 var->green = var->red;
513 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 break;
515
516 case 8:
517 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700518 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700519 var->green = var->red;
520 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 break;
522
523 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700524 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 var->red.offset = 2;
526 var->green.offset = -3;
527 var->blue.offset = 8;
528 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700529 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 var->green.offset = 5;
531 var->blue.offset = 0;
532 }
533 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700534 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 var->blue.length = 5;
536 break;
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700539 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 var->red.offset = 8;
541 var->green.offset = 16;
542 var->blue.offset = 24;
543 } else {
544 var->red.offset = 16;
545 var->green.offset = 8;
546 var->blue.offset = 0;
547 }
548 var->red.length = 8;
549 var->green.length = 8;
550 var->blue.length = 8;
551 break;
552
553 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700554 dev_dbg(info->device,
555 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700556 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 /* should never occur */
558 break;
559 }
560
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700561 if (var->xres_virtual < var->xres)
562 var->xres_virtual = var->xres;
563 /* use highest possible virtual resolution */
564 if (var->yres_virtual == -1) {
565 var->yres_virtual = pixels / var->xres_virtual;
566
567 dev_info(info->device,
568 "virtual resolution set to maximum of %dx%d\n",
569 var->xres_virtual, var->yres_virtual);
570 }
571 if (var->yres_virtual < var->yres)
572 var->yres_virtual = var->yres;
573
574 if (var->xres_virtual * var->yres_virtual > pixels) {
575 dev_err(info->device, "mode %dx%dx%d rejected... "
576 "virtual resolution too high to fit into video memory!\n",
577 var->xres_virtual, var->yres_virtual,
578 var->bits_per_pixel);
579 return -EINVAL;
580 }
581
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700582 if (var->xoffset < 0)
583 var->xoffset = 0;
584 if (var->yoffset < 0)
585 var->yoffset = 0;
586
587 /* truncate xoffset and yoffset to maximum if too high */
588 if (var->xoffset > var->xres_virtual - var->xres)
589 var->xoffset = var->xres_virtual - var->xres - 1;
590 if (var->yoffset > var->yres_virtual - var->yres)
591 var->yoffset = var->yres_virtual - var->yres - 1;
592
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 var->red.msb_right =
594 var->green.msb_right =
595 var->blue.msb_right =
596 var->transp.offset =
597 var->transp.length =
598 var->transp.msb_right = 0;
599
600 yres = var->yres;
601 if (var->vmode & FB_VMODE_DOUBLE)
602 yres *= 2;
603 else if (var->vmode & FB_VMODE_INTERLACED)
604 yres = (yres + 1) / 2;
605
606 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700607 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700608 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 return -EINVAL;
610 }
611
Krzysztof Helt99a45842009-03-31 15:25:09 -0700612 if (cirrusfb_check_pixclock(var, info))
613 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 return 0;
616}
617
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700618static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700620 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700621 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700622
Krzysztof Helt8503df62007-10-16 01:29:08 -0700623 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700624 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Krzysztof Helt486ff382008-10-15 22:03:42 -0700626 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700627 dev_dbg(info->device, "Set %s as pixclock source.\n",
628 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700629 old1f |= 0x40;
630 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
631 if (div == 2)
632 old1e |= 1;
633
634 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700636 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637}
638
639/*************************************************************************
640 cirrusfb_set_par_foo()
641
642 actually writes the values for a new video mode into the hardware,
643**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700644static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645{
646 struct cirrusfb_info *cinfo = info->par;
647 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 u8 __iomem *regbase = cinfo->regbase;
649 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700650 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700652 int hdispend, hsyncstart, hsyncend, htotal;
653 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700654 long freq;
655 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700656 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700658 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700660
661 switch (var->bits_per_pixel) {
662 case 1:
663 info->fix.line_length = var->xres_virtual / 8;
664 info->fix.visual = FB_VISUAL_MONO10;
665 break;
666
667 case 8:
668 info->fix.line_length = var->xres_virtual;
669 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
670 break;
671
672 case 16:
673 case 32:
674 info->fix.line_length = var->xres_virtual *
675 var->bits_per_pixel >> 3;
676 info->fix.visual = FB_VISUAL_TRUECOLOR;
677 break;
678 }
679 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700681 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 bi = &cirrusfb_board_info[cinfo->btype];
684
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700685 hsyncstart = var->xres + var->right_margin;
686 hsyncend = hsyncstart + var->hsync_len;
687 htotal = (hsyncend + var->left_margin) / 8 - 5;
688 hdispend = var->xres / 8 - 1;
689 hsyncstart = hsyncstart / 8 + 1;
690 hsyncend = hsyncend / 8 + 1;
691
692 yres = var->yres;
693 vsyncstart = yres + var->lower_margin;
694 vsyncend = vsyncstart + var->vsync_len;
695 vtotal = vsyncend + var->upper_margin;
696 vdispend = yres - 1;
697
698 if (var->vmode & FB_VMODE_DOUBLE) {
699 yres *= 2;
700 vsyncstart *= 2;
701 vsyncend *= 2;
702 vtotal *= 2;
703 } else if (var->vmode & FB_VMODE_INTERLACED) {
704 yres = (yres + 1) / 2;
705 vsyncstart = (vsyncstart + 1) / 2;
706 vsyncend = (vsyncend + 1) / 2;
707 vtotal = (vtotal + 1) / 2;
708 }
709
710 vtotal -= 2;
711 vsyncstart -= 1;
712 vsyncend -= 1;
713
714 if (yres >= 1024) {
715 vtotal /= 2;
716 vsyncstart /= 2;
717 vsyncend /= 2;
718 vdispend /= 2;
719 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700720 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700721 htotal /= 2;
722 hsyncstart /= 2;
723 hsyncend /= 2;
724 hdispend /= 2;
725 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700727 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700730 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700731 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700733 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700734 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700736 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700737 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Krzysztof Helt8503df62007-10-16 01:29:08 -0700739 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700740 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700741 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700742 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700744 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700745 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700747 tmp = hsyncend % 32;
748 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700750 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700751 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700753 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
756 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700757 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700759 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700761 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700763 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700765 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700771 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700772 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
774 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700775 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 tmp |= 0x20;
777 if (var->vmode & FB_VMODE_DOUBLE)
778 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700779 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700780 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700782 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700785 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700791 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700794 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700795 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700797 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700798 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 tmp = 0;
801 if (var->vmode & FB_VMODE_INTERLACED)
802 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700805 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 tmp |= 128;
811
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700812 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700813 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700815 freq = PICOS2KHZ(var->pixclock);
816 bestclock(freq, &nom, &den, &div);
817
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700818 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
819 freq, nom, den, div);
820
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 /* set VCLK0 */
822 /* hardware RefClock: 14.31818 MHz */
823 /* formula: VClk = (OSC * N) / (D * (1+P)) */
824 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
825
Krzysztof Helt527410f2009-03-31 15:25:13 -0700826 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700827 /* if freq is close to mclk or mclk/2 select mclk
828 * as clock source
829 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700830 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700831 if (divMCLK) {
832 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700833 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700834 }
835 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700836 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700837 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
838 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
839 unsigned short tile_control;
840
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700841 if (cinfo->btype == BT_LAGUNAB) {
842 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
843 tile_control &= ~0x80;
844 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
845 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700846
847 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
848 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
849 control = fb_readw(cinfo->laguna_mmio + 0x402);
850 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
851 control &= ~0x6800;
852 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700853 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700854 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700855 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700856 tmp = den << 1;
857 if (div != 0)
858 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700859 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
860 if ((cinfo->btype == BT_SD64) ||
861 (cinfo->btype == BT_ALPINE) ||
862 (cinfo->btype == BT_GD5480))
863 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700865 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
866 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700867 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700868 vga_wseq(regbase, CL_SEQRE, tmp);
869 vga_wseq(regbase, CL_SEQR1E, nom);
870 } else {
871 vga_wseq(regbase, CL_SEQRB, nom);
872 vga_wseq(regbase, CL_SEQR1B, tmp);
873 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700874 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700876 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700878 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 else
880 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
881 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700882 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 /* don't know if it would hurt to also program this if no interlaced */
885 /* mode is used, but I feel better this way.. :-) */
886 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700887 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700889 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700892 /* enable display memory & CRTC I/O address for color mode */
893 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
895 tmp |= 0x40;
896 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
897 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700898 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700899 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700900 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901
Krzysztof Helt8503df62007-10-16 01:29:08 -0700902 /* text cursor on and start line */
903 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
904 /* text cursor end line */
905 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
907 /******************************************************
908 *
909 * 1 bpp
910 *
911 */
912
913 /* programming for different color depths */
914 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700915 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
918 /* SR07 */
919 switch (cinfo->btype) {
920 case BT_SD64:
921 case BT_PICCOLO:
922 case BT_PICASSO:
923 case BT_SPECTRUM:
924 case BT_PICASSO4:
925 case BT_ALPINE:
926 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700928 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 bi->sr07_1bpp_mux : bi->sr07_1bpp);
930 break;
931
932 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700933 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700934 vga_wseq(regbase, CL_SEQR7,
935 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 break;
937
938 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700939 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 break;
941 }
942
943 /* Extended Sequencer Mode */
944 switch (cinfo->btype) {
945 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700946 /* setting the SEQRF on SD64 is not necessary
947 * (only during init)
948 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700949 /* MCLK select */
950 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 break;
952
953 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700954 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700955 /* ### ueberall 0x22? */
956 /* ##vorher 1c MCLK select */
957 vga_wseq(regbase, CL_SEQR1F, 0x22);
958 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
959 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 break;
961
962 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700963 /* ##vorher 22 MCLK select */
964 vga_wseq(regbase, CL_SEQR1F, 0x22);
965 /* ## vorher d0 avoid FIFO underruns..? */
966 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 break;
968
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 case BT_PICASSO4:
970 case BT_ALPINE:
971 case BT_GD5480:
972 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700973 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 /* do nothing */
975 break;
976
977 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700978 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 break;
980 }
981
Krzysztof Helt8503df62007-10-16 01:29:08 -0700982 /* pixel mask: pass-through for first plane */
983 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700984 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700985 /* hidden dac reg: 1280x1024 */
986 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700988 /* hidden dac: nothing */
989 WHDR(cinfo, 0);
990 /* memory mode: odd/even, ext. memory */
991 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
992 /* plane mask: only write to first plane */
993 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 }
995
996 /******************************************************
997 *
998 * 8 bpp
999 *
1000 */
1001
1002 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001003 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 switch (cinfo->btype) {
1005 case BT_SD64:
1006 case BT_PICCOLO:
1007 case BT_PICASSO:
1008 case BT_SPECTRUM:
1009 case BT_PICASSO4:
1010 case BT_ALPINE:
1011 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001012 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001013 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1015 break;
1016
1017 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001018 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001019 vga_wseq(regbase, CL_SEQR7,
1020 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001021 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 break;
1023
1024 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001025 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 break;
1027 }
1028
1029 switch (cinfo->btype) {
1030 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001031 /* MCLK select */
1032 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 break;
1034
1035 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001038 /* ### vorher 1c MCLK select */
1039 vga_wseq(regbase, CL_SEQR1F, 0x22);
1040 /* Fast Page-Mode writes */
1041 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 break;
1043
1044 case BT_PICASSO4:
1045#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046 /* ### INCOMPLETE!! */
1047 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 /* We already set SRF and SR1F */
1051 break;
1052
1053 case BT_GD5480:
1054 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001055 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 /* do nothing */
1057 break;
1058
1059 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001060 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 break;
1062 }
1063
Krzysztof Helt8503df62007-10-16 01:29:08 -07001064 /* mode register: 256 color mode */
1065 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001066 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 /* hidden dac reg: 1280x1024 */
1068 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* hidden dac: nothing */
1071 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 }
1073
1074 /******************************************************
1075 *
1076 * 16 bpp
1077 *
1078 */
1079
1080 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001081 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 switch (cinfo->btype) {
1083 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001084 /* Extended Sequencer Mode: 256c col. mode */
1085 vga_wseq(regbase, CL_SEQR7, 0xf7);
1086 /* MCLK select */
1087 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 break;
1089
1090 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001091 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001092 vga_wseq(regbase, CL_SEQR7, 0x87);
1093 /* Fast Page-Mode writes */
1094 vga_wseq(regbase, CL_SEQRF, 0xb0);
1095 /* MCLK select */
1096 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 break;
1098
1099 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001100 vga_wseq(regbase, CL_SEQR7, 0x27);
1101 /* Fast Page-Mode writes */
1102 vga_wseq(regbase, CL_SEQRF, 0xb0);
1103 /* MCLK select */
1104 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 break;
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001109 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 break;
1111
1112 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001113 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 /* We already set SRF and SR1F */
1115 break;
1116
1117 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001118 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001119 vga_wseq(regbase, CL_SEQR7,
1120 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001121 control |= 0x2000;
1122 format |= 0x1400;
1123 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 break;
1125
1126 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001127 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 break;
1129 }
1130
Krzysztof Helt8503df62007-10-16 01:29:08 -07001131 /* mode register: 256 color mode */
1132 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001134 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135#elif defined(CONFIG_ZORRO)
1136 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001137 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 }
1140
1141 /******************************************************
1142 *
1143 * 32 bpp
1144 *
1145 */
1146
1147 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001148 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 switch (cinfo->btype) {
1150 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001151 /* Extended Sequencer Mode: 256c col. mode */
1152 vga_wseq(regbase, CL_SEQR7, 0xf9);
1153 /* MCLK select */
1154 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 break;
1156
1157 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001158 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001159 vga_wseq(regbase, CL_SEQR7, 0x85);
1160 /* Fast Page-Mode writes */
1161 vga_wseq(regbase, CL_SEQRF, 0xb0);
1162 /* MCLK select */
1163 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 break;
1165
1166 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001167 vga_wseq(regbase, CL_SEQR7, 0x25);
1168 /* Fast Page-Mode writes */
1169 vga_wseq(regbase, CL_SEQRF, 0xb0);
1170 /* MCLK select */
1171 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 break;
1173
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001176 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 break;
1178
1179 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001180 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 /* We already set SRF and SR1F */
1182 break;
1183
1184 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001185 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 vga_wseq(regbase, CL_SEQR7,
1187 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001188 control |= 0x6000;
1189 format |= 0x3400;
1190 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 break;
1192
1193 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001194 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 break;
1196 }
1197
Krzysztof Helt8503df62007-10-16 01:29:08 -07001198 /* mode register: 256 color mode */
1199 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001200 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1201 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 }
1203
1204 /******************************************************
1205 *
1206 * unknown/unsupported bpp
1207 *
1208 */
1209
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001211 dev_err(info->device,
1212 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214
Krzysztof Helt6683e012009-03-31 15:25:06 -07001215 pitch = info->fix.line_length >> 3;
1216 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001218 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 tmp |= 0x10; /* offset overflow bit */
1220
Krzysztof Helt8503df62007-10-16 01:29:08 -07001221 /* screen start addr #16-18, fastpagemode cycles */
1222 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001224 /* screen start address bit 19 */
1225 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001226 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001228 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001229 tmp = 0;
1230 if ((htotal + 5) & 256)
1231 tmp |= 128;
1232 if (hdispend & 256)
1233 tmp |= 64;
1234 if (hsyncstart & 256)
1235 tmp |= 48;
1236 if (vtotal & 1024)
1237 tmp |= 8;
1238 if (vdispend & 1024)
1239 tmp |= 4;
1240 if (vsyncstart & 1024)
1241 tmp |= 3;
1242
1243 vga_wcrt(regbase, CL_CRT1E, tmp);
1244 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1245 }
1246
Krzysztof Helt8503df62007-10-16 01:29:08 -07001247 /* pixel panning */
1248 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249
1250 /* [ EGS: SetOffset(); ] */
1251 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001252 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001254 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001255 /* no tiles */
1256 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1257 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1258 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 /* finally, turn on everything - turn off "FullBandwidth" bit */
1261 /* also, set "DotClock%2" bit where requested */
1262 tmp = 0x01;
1263
1264/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1265 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1266 tmp |= 0x08;
1267*/
1268
Krzysztof Helt8503df62007-10-16 01:29:08 -07001269 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001270 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001273 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274#endif
1275
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 return 0;
1277}
1278
1279/* for some reason incomprehensible to me, cirrusfb requires that you write
1280 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001281static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001283 cirrusfb_set_par_foo(info);
1284 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285}
1286
Krzysztof Helt8503df62007-10-16 01:29:08 -07001287static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1288 unsigned blue, unsigned transp,
1289 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
1291 struct cirrusfb_info *cinfo = info->par;
1292
1293 if (regno > 255)
1294 return -EINVAL;
1295
1296 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1297 u32 v;
1298 red >>= (16 - info->var.red.length);
1299 green >>= (16 - info->var.green.length);
1300 blue >>= (16 - info->var.blue.length);
1301
Krzysztof Helt8503df62007-10-16 01:29:08 -07001302 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 return 1;
1304 v = (red << info->var.red.offset) |
1305 (green << info->var.green.offset) |
1306 (blue << info->var.blue.offset);
1307
Krzysztof Helt060b6002007-10-16 01:29:13 -07001308 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 return 0;
1310 }
1311
Krzysztof Helt8503df62007-10-16 01:29:08 -07001312 if (info->var.bits_per_pixel == 8)
1313 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
1315 return 0;
1316
1317}
1318
1319/*************************************************************************
1320 cirrusfb_pan_display()
1321
1322 performs display panning - provided hardware permits this
1323**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001324static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1325 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001327 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001329 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 struct cirrusfb_info *cinfo = info->par;
1331
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001332 dev_dbg(info->device,
1333 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
1335 /* no range checks for xoffset and yoffset, */
1336 /* as fb_pan_display has already done this */
1337 if (var->vmode & FB_VMODE_YWRAP)
1338 return -EINVAL;
1339
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341
Krzysztof Helt99a45842009-03-31 15:25:09 -07001342 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
1344 if (info->var.bits_per_pixel == 1) {
1345 /* base is already correct */
1346 xpix = (unsigned char) (var->xoffset % 8);
1347 } else {
1348 base /= 4;
1349 xpix = (unsigned char) ((xoffset % 4) * 2);
1350 }
1351
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001352 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001353 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
1355 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001356 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1357 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001359 /* 0xf2 is %11110010, exclude tmp bits */
1360 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 /* construct bits 16, 17 and 18 of screen start address */
1362 if (base & 0x10000)
1363 tmp |= 0x01;
1364 if (base & 0x20000)
1365 tmp |= 0x04;
1366 if (base & 0x40000)
1367 tmp |= 0x08;
1368
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001369 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
1371 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001372 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001373 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1374 if (is_laguna(cinfo))
1375 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1376 else
1377 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001378 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Krzysztof Helt8503df62007-10-16 01:29:08 -07001381 /* write pixel panning value to AR33; this does not quite work in 8bpp
1382 *
1383 * ### Piccolo..? Will this work?
1384 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001386 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001388 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001389 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
Krzysztof Helt8503df62007-10-16 01:29:08 -07001391 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392}
1393
Krzysztof Helt8503df62007-10-16 01:29:08 -07001394static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395{
1396 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1398 * then the caller blanks by setting the CLUT (Color Look Up Table)
1399 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1400 * failed due to e.g. a video mode which doesn't support it.
1401 * Implements VESA suspend and powerdown modes on hardware that
1402 * supports disabling hsync/vsync:
1403 * blank_mode == 2: suspend vsync
1404 * blank_mode == 3: suspend hsync
1405 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 */
1407 unsigned char val;
1408 struct cirrusfb_info *cinfo = info->par;
1409 int current_mode = cinfo->blank_mode;
1410
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001411 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
1413 if (info->state != FBINFO_STATE_RUNNING ||
1414 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001415 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 return 0;
1417 }
1418
1419 /* Undo current */
1420 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001421 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001422 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001423 val = 0;
1424 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001425 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001426 val = 0x20;
1427
1428 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1429 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430
1431 switch (blank_mode) {
1432 case FB_BLANK_UNBLANK:
1433 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001434 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 break;
1436 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001437 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 break;
1439 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001440 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 break;
1442 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001443 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 break;
1445 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001446 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 return 1;
1448 }
1449
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001450 vga_wgfx(cinfo->regbase, CL_GRE, val);
1451
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001453 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
1455 /* Let fbcon do a soft blank for us */
1456 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1457}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001458
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459/**** END Hardware specific Routines **************************************/
1460/****************************************************************************/
1461/**** BEGIN Internal Routines ***********************************************/
1462
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001463static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001465 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 const struct cirrusfb_board_info_rec *bi;
1467
Krzysztof Helt8503df62007-10-16 01:29:08 -07001468 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469
1470 bi = &cirrusfb_board_info[cinfo->btype];
1471
1472 /* reset board globally */
1473 switch (cinfo->btype) {
1474 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001475 WSFR(cinfo, 0x01);
1476 udelay(500);
1477 WSFR(cinfo, 0x51);
1478 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 break;
1480 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 WSFR2(cinfo, 0xff);
1482 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 break;
1484 case BT_SD64:
1485 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001486 WSFR(cinfo, 0x1f);
1487 udelay(500);
1488 WSFR(cinfo, 0x4f);
1489 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 break;
1491 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001492 /* disable flickerfixer */
1493 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1494 mdelay(100);
1495 /* from Klaus' NetBSD driver: */
1496 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1497 /* put blitter into 542x compat */
1498 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1499 /* mode */
1500 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 break;
1502
1503 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 /* from Klaus' NetBSD driver: */
1505 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 break;
1507
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001508 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001509 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 case BT_ALPINE:
1511 /* Nothing to do to reset the board. */
1512 break;
1513
1514 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001515 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 break;
1517 }
1518
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001519 /* make sure RAM size set by this point */
1520 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
1522 /* the P4 is not fully initialized here; I rely on it having been */
1523 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001524 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
1526 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001527 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1528 WGen(cinfo, CL_POS102, 0x01);
1529 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530
1531 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001532 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001535 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
Krzysztof Helt8503df62007-10-16 01:29:08 -07001537 /* FullBandwidth (video off) and 8/9 dot clock */
1538 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 /* "magic cookie" - doesn't make any sense to me.. */
1541/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1542 /* unlock all extension registers */
1543 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 switch (cinfo->btype) {
1546 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001547 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 break;
1549 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001550 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001551 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 break;
1553 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001554 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 break;
1556 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1558 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
1560 }
1561 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001562 /* plane mask: nothing */
1563 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1564 /* character map select: doesn't even matter in gx mode */
1565 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001566 /* memory mode: chain4, ext. memory */
1567 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
1569 /* controller-internal base address of video memory */
1570 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
Krzysztof Helt8503df62007-10-16 01:29:08 -07001573 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1574 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
Krzysztof Helt8503df62007-10-16 01:29:08 -07001576 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1577 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1578 /* graphics cursor Y position (..."... ) */
1579 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1580 /* graphics cursor attributes */
1581 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1582 /* graphics cursor pattern address */
1583 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584
1585 /* writing these on a P4 might give problems.. */
1586 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 /* configuration readback and ext. color */
1588 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1589 /* signature generator */
1590 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 }
1592
1593 /* MCLK select etc. */
1594 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001595 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596
Krzysztof Helt8503df62007-10-16 01:29:08 -07001597 /* Screen A preset row scan: none */
1598 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1599 /* Text cursor start: disable text cursor */
1600 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1601 /* Text cursor end: - */
1602 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001603 /* text cursor location high: 0 */
1604 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1605 /* text cursor location low: 0 */
1606 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
Krzysztof Helt8503df62007-10-16 01:29:08 -07001608 /* Underline Row scanline: - */
1609 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 /* ext. display controls: ext.adr. wrap */
1612 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
Krzysztof Helt8503df62007-10-16 01:29:08 -07001614 /* Set/Reset registes: - */
1615 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1616 /* Set/Reset enable: - */
1617 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1618 /* Color Compare: - */
1619 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1620 /* Data Rotate: - */
1621 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1622 /* Read Map Select: - */
1623 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1624 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1625 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1626 /* Miscellaneous: memory map base address, graphics mode */
1627 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1628 /* Color Don't care: involve all planes */
1629 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1630 /* Bit Mask: no mask at all */
1631 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001632
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001633 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001634 /* (5434 can't have bit 3 set for bitblt) */
1635 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001637 /* Graphics controller mode extensions: finer granularity,
1638 * 8byte data latches
1639 */
1640 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Krzysztof Helt8503df62007-10-16 01:29:08 -07001642 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1643 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1644 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1645 /* Background color byte 1: - */
1646 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1647 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
Krzysztof Helt8503df62007-10-16 01:29:08 -07001649 /* Attribute Controller palette registers: "identity mapping" */
1650 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1651 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1652 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1653 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1654 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1655 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1656 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1657 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1658 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1659 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1660 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1661 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1662 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1663 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1664 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1665 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667 /* Attribute Controller mode: graphics mode */
1668 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1669 /* Overscan color reg.: reg. 0 */
1670 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1671 /* Color Plane enable: Enable all 4 planes */
1672 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001673 /* Color Select: - */
1674 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675
Krzysztof Helt8503df62007-10-16 01:29:08 -07001676 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
Krzysztof Helt8503df62007-10-16 01:29:08 -07001678 /* BLT Start/status: Blitter reset */
1679 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1680 /* - " - : "end-of-reset" */
1681 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682
1683 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 return;
1686}
1687
Krzysztof Helt8503df62007-10-16 01:29:08 -07001688static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689{
1690#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1691 static int IsOn = 0; /* XXX not ok for multiple boards */
1692
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 if (cinfo->btype == BT_PICASSO4)
1694 return; /* nothing to switch */
1695 if (cinfo->btype == BT_ALPINE)
1696 return; /* nothing to switch */
1697 if (cinfo->btype == BT_GD5480)
1698 return; /* nothing to switch */
1699 if (cinfo->btype == BT_PICASSO) {
1700 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 return;
1703 }
1704 if (on) {
1705 switch (cinfo->btype) {
1706 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001707 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 break;
1709 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001710 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 break;
1712 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001713 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 break;
1715 default: /* do nothing */ break;
1716 }
1717 } else {
1718 switch (cinfo->btype) {
1719 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001720 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 break;
1722 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001723 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 break;
1725 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001726 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001728 default: /* do nothing */
1729 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 }
1731 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732#endif /* CONFIG_ZORRO */
1733}
1734
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735/******************************************/
1736/* Linux 2.6-style accelerated functions */
1737/******************************************/
1738
Krzysztof Helt8343c892009-03-31 15:25:11 -07001739static int cirrusfb_sync(struct fb_info *info)
1740{
1741 struct cirrusfb_info *cinfo = info->par;
1742
1743 if (!is_laguna(cinfo)) {
1744 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1745 cpu_relax();
1746 }
1747 return 0;
1748}
1749
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750static void cirrusfb_fillrect(struct fb_info *info,
1751 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 struct fb_fillrect modded;
1754 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001755 struct cirrusfb_info *cinfo = info->par;
1756 int m = info->var.bits_per_pixel;
1757 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1758 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759
1760 if (info->state != FBINFO_STATE_RUNNING)
1761 return;
1762 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1763 cfb_fillrect(info, region);
1764 return;
1765 }
1766
1767 vxres = info->var.xres_virtual;
1768 vyres = info->var.yres_virtual;
1769
1770 memcpy(&modded, region, sizeof(struct fb_fillrect));
1771
Krzysztof Helt8503df62007-10-16 01:29:08 -07001772 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 modded.dx >= vxres || modded.dy >= vyres)
1774 return;
1775
Krzysztof Helt8503df62007-10-16 01:29:08 -07001776 if (modded.dx + modded.width > vxres)
1777 modded.width = vxres - modded.dx;
1778 if (modded.dy + modded.height > vyres)
1779 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780
Krzysztof Helt060b6002007-10-16 01:29:13 -07001781 cirrusfb_RectFill(cinfo->regbase,
1782 info->var.bits_per_pixel,
1783 (region->dx * m) / 8, region->dy,
1784 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001785 color, color,
1786 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787}
1788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789static void cirrusfb_copyarea(struct fb_info *info,
1790 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 struct fb_copyarea modded;
1793 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001794 struct cirrusfb_info *cinfo = info->par;
1795 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796
1797 if (info->state != FBINFO_STATE_RUNNING)
1798 return;
1799 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1800 cfb_copyarea(info, area);
1801 return;
1802 }
1803
1804 vxres = info->var.xres_virtual;
1805 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001806 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Krzysztof Helt8503df62007-10-16 01:29:08 -07001808 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 modded.sx >= vxres || modded.sy >= vyres ||
1810 modded.dx >= vxres || modded.dy >= vyres)
1811 return;
1812
Krzysztof Helt8503df62007-10-16 01:29:08 -07001813 if (modded.sx + modded.width > vxres)
1814 modded.width = vxres - modded.sx;
1815 if (modded.dx + modded.width > vxres)
1816 modded.width = vxres - modded.dx;
1817 if (modded.sy + modded.height > vyres)
1818 modded.height = vyres - modded.sy;
1819 if (modded.dy + modded.height > vyres)
1820 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
Krzysztof Helt060b6002007-10-16 01:29:13 -07001822 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1823 (area->sx * m) / 8, area->sy,
1824 (area->dx * m) / 8, area->dy,
1825 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001826 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001827
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828}
1829
Krzysztof Helt8503df62007-10-16 01:29:08 -07001830static void cirrusfb_imageblit(struct fb_info *info,
1831 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832{
1833 struct cirrusfb_info *cinfo = info->par;
1834
Krzysztof Helt9e848062009-03-31 15:25:11 -07001835 if (info->state != FBINFO_STATE_RUNNING)
1836 return;
1837 if (info->flags & FBINFO_HWACCEL_DISABLED)
1838 cfb_imageblit(info, image);
1839 else {
1840 unsigned size = ((image->width + 7) >> 3) * image->height;
1841 int m = info->var.bits_per_pixel;
1842 u32 fg, bg;
1843
1844 if (info->var.bits_per_pixel == 8) {
1845 fg = image->fg_color;
1846 bg = image->bg_color;
1847 } else {
1848 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1849 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1850 }
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001851 cirrusfb_WaitBLT(cinfo->regbase);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001852 /* byte rounded scanlines */
1853 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1854 cirrusfb_RectFill(cinfo->regbase,
1855 info->var.bits_per_pixel,
1856 (image->dx * m) / 8, image->dy,
1857 (image->width * m) / 8, image->height,
1858 fg, bg,
1859 info->fix.line_length, 0x04);
1860 memcpy(info->screen_base, image->data, size);
1861 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862}
1863
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864#ifdef CONFIG_PPC_PREP
1865#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1866#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001867static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 *display = PREP_VIDEO_BASE;
1870 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871}
1872
1873#endif /* CONFIG_PPC_PREP */
1874
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001876static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877
1878/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1879 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1880 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1881 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001882static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1883 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884{
1885 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001886 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001888 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001889 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1890
1891 mem = ((SR14 & 7) + 1) << 20;
1892 } else {
1893 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1894 switch ((SRF & 0x18)) {
1895 case 0x08:
1896 mem = 512 * 1024;
1897 break;
1898 case 0x10:
1899 mem = 1024 * 1024;
1900 break;
1901 /* 64-bit DRAM data bus width; assume 2MB.
1902 * Also indicates 2MB memory on the 5430.
1903 */
1904 case 0x18:
1905 mem = 2048 * 1024;
1906 break;
1907 default:
1908 dev_warn(info->device, "Unknown memory size!\n");
1909 mem = 1024 * 1024;
1910 }
1911 /* If DRAM bank switching is enabled, there must be
1912 * twice as much memory installed. (4MB on the 5434)
1913 */
1914 if (SRF & 0x80)
1915 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001917
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 return mem;
1920}
1921
Krzysztof Helt8503df62007-10-16 01:29:08 -07001922static void get_pci_addrs(const struct pci_dev *pdev,
1923 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001925 assert(pdev != NULL);
1926 assert(display != NULL);
1927 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 *display = 0;
1930 *registers = 0;
1931
1932 /* This is a best-guess for now */
1933
1934 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1935 *display = pci_resource_start(pdev, 1);
1936 *registers = pci_resource_start(pdev, 0);
1937 } else {
1938 *display = pci_resource_start(pdev, 0);
1939 *registers = pci_resource_start(pdev, 1);
1940 }
1941
Krzysztof Helt8503df62007-10-16 01:29:08 -07001942 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943}
1944
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001945static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001947 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001948 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001950 if (cinfo->laguna_mmio == NULL)
1951 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001952 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953#if 0 /* if system didn't claim this region, we would... */
1954 release_mem_region(0xA0000, 65535);
1955#endif
1956 if (release_io_ports)
1957 release_region(0x3C0, 32);
1958 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959}
1960#endif /* CONFIG_PCI */
1961
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001963static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964{
Al Virod91f5bb2007-10-17 00:27:18 +01001965 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001966 struct zorro_dev *zdev = to_zorro_dev(info->device);
1967
1968 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969
1970 if (cinfo->btype == BT_PICASSO4) {
1971 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001972 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001973 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001975 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001976 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978}
1979#endif /* CONFIG_ZORRO */
1980
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001981/* function table of the above functions */
1982static struct fb_ops cirrusfb_ops = {
1983 .owner = THIS_MODULE,
1984 .fb_open = cirrusfb_open,
1985 .fb_release = cirrusfb_release,
1986 .fb_setcolreg = cirrusfb_setcolreg,
1987 .fb_check_var = cirrusfb_check_var,
1988 .fb_set_par = cirrusfb_set_par,
1989 .fb_pan_display = cirrusfb_pan_display,
1990 .fb_blank = cirrusfb_blank,
1991 .fb_fillrect = cirrusfb_fillrect,
1992 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001993 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001994 .fb_imageblit = cirrusfb_imageblit,
1995};
1996
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001997static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001999 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 struct fb_var_screeninfo *var = &info->var;
2001
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 info->pseudo_palette = cinfo->pseudo_palette;
2003 info->flags = FBINFO_DEFAULT
2004 | FBINFO_HWACCEL_XPAN
2005 | FBINFO_HWACCEL_YPAN
2006 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002007 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002009 if (noaccel || is_laguna(cinfo))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 info->flags |= FBINFO_HWACCEL_DISABLED;
2011 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002012
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 if (cinfo->btype == BT_GD5480) {
2014 if (var->bits_per_pixel == 16)
2015 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002016 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 info->screen_base += 2 * MB_;
2018 }
2019
2020 /* Fill fix common fields */
2021 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2022 sizeof(info->fix.id));
2023
2024 /* monochrome: only 1 memory plane */
2025 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002026 info->fix.smem_len = info->screen_size;
2027 if (var->bits_per_pixel == 1)
2028 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 info->fix.xpanstep = 1;
2031 info->fix.ypanstep = 1;
2032 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033
2034 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 info->fix.mmio_len = 0;
2036 info->fix.accel = FB_ACCEL_NONE;
2037
2038 fb_alloc_cmap(&info->cmap, 256, 0);
2039
2040 return 0;
2041}
2042
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002043static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002045 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047
2048 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002049 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002051 /* set all the vital stuff */
2052 cirrusfb_set_fbinfo(info);
2053
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002054 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002056 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2057 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002058 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002059 err = -EINVAL;
2060 goto err_dealloc_cmap;
2061 }
2062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 info->var.activate = FB_ACTIVATE_NOW;
2064
Krzysztof Helt99a45842009-03-31 15:25:09 -07002065 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 if (err < 0) {
2067 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002068 dev_dbg(info->device,
2069 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002070 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 }
2072
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 err = register_framebuffer(info);
2074 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002075 dev_err(info->device,
2076 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 goto err_dealloc_cmap;
2078 }
2079
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 return 0;
2081
2082err_dealloc_cmap:
2083 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 return err;
2085}
2086
Krzysztof Helt8503df62007-10-16 01:29:08 -07002087static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088{
2089 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090
Krzysztof Helt8503df62007-10-16 01:29:08 -07002091 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002092 unregister_framebuffer(info);
2093 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002094 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002095 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002096 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097}
2098
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002100static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2101 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102{
2103 struct cirrusfb_info *cinfo;
2104 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 unsigned long board_addr, board_size;
2106 int ret;
2107
2108 ret = pci_enable_device(pdev);
2109 if (ret < 0) {
2110 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2111 goto err_out;
2112 }
2113
2114 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2115 if (!info) {
2116 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2117 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002118 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 }
2120
2121 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002122 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002124 dev_dbg(info->device,
2125 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002126 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002127 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2128 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129
Krzysztof Helt8503df62007-10-16 01:29:08 -07002130 if (isPReP) {
2131 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002133 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002135 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002136 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002138 dev_dbg(info->device,
2139 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002140 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002141 /* FIXME: this forces VGA. alternatives? */
2142 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002143 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 }
2145
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002146 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002147 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002149 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002150 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151
2152 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002154 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2155 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 goto err_release_fb;
2157 }
2158#if 0 /* if the system didn't claim this region, we would... */
2159 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002160 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2161 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 ret = -EBUSY;
2163 goto err_release_regions;
2164 }
2165#endif
2166 if (request_region(0x3C0, 32, "cirrusfb"))
2167 release_io_ports = 1;
2168
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002169 info->screen_base = ioremap(board_addr, board_size);
2170 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 ret = -EIO;
2172 goto err_release_legacy;
2173 }
2174
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002175 info->fix.smem_start = board_addr;
2176 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 cinfo->unmap = cirrusfb_pci_unmap;
2178
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002179 dev_info(info->device,
2180 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2181 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 pci_set_drvdata(pdev, info);
2183
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002184 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002185 if (!ret)
2186 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002188 pci_set_drvdata(pdev, NULL);
2189 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190err_release_legacy:
2191 if (release_io_ports)
2192 release_region(0x3C0, 32);
2193#if 0
2194 release_mem_region(0xA0000, 65535);
2195err_release_regions:
2196#endif
2197 pci_release_regions(pdev);
2198err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002199 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002200 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202err_out:
2203 return ret;
2204}
2205
Krzysztof Helt8503df62007-10-16 01:29:08 -07002206static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207{
2208 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209
Krzysztof Helt8503df62007-10-16 01:29:08 -07002210 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211}
2212
2213static struct pci_driver cirrusfb_pci_driver = {
2214 .name = "cirrusfb",
2215 .id_table = cirrusfb_pci_table,
2216 .probe = cirrusfb_pci_register,
2217 .remove = __devexit_p(cirrusfb_pci_unregister),
2218#ifdef CONFIG_PM
2219#if 0
2220 .suspend = cirrusfb_pci_suspend,
2221 .resume = cirrusfb_pci_resume,
2222#endif
2223#endif
2224};
2225#endif /* CONFIG_PCI */
2226
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002228static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2229 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230{
2231 struct cirrusfb_info *cinfo;
2232 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002233 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 struct zorro_dev *z2 = NULL;
2235 unsigned long board_addr, board_size, size;
2236 int ret;
2237
2238 btype = ent->driver_data;
2239 if (cirrusfb_zorro_table2[btype].id2)
2240 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2241 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242
2243 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2244 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002245 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 ret = -ENOMEM;
2247 goto err_out;
2248 }
2249
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002250 dev_info(info->device, "%s board detected\n",
2251 cirrusfb_board_info[btype].name);
2252
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 cinfo->btype = btype;
2255
Al Viro36ea96a2007-10-27 19:46:58 +01002256 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002257 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 board_addr = zorro_resource_start(z);
2260 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002261 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262
2263 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002264 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2265 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 ret = -EBUSY;
2267 goto err_release_fb;
2268 }
2269
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 ret = -EIO;
2271
2272 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002273 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274
2275 /* To be precise, for the P4 this is not the */
2276 /* begin of the board, but the begin of RAM. */
2277 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2278 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002279 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 if (!cinfo->regbase)
2281 goto err_release_region;
2282
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002283 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002284 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002286 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002288 info->fix.smem_start = board_addr + 16777216;
2289 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2290 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 goto err_unmap_regbase;
2292 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002293 dev_info(info->device, " REG at $%lx\n",
2294 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002296 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002298 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002300 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2301 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 goto err_release_region;
2303
2304 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002305 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002306 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002308 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002309 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 }
2311 cinfo->unmap = cirrusfb_zorro_unmap;
2312
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002313 dev_info(info->device,
2314 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2315 board_size / MB_, board_addr);
2316
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 zorro_set_drvdata(z, info);
2318
Al Virod91f5bb2007-10-17 00:27:18 +01002319 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002320 if (!ret)
2321 return 0;
2322
2323 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2324 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325
2326err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002327 if (btype == BT_PICASSO4)
2328 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329err_release_region:
2330 release_region(board_addr, board_size);
2331err_release_fb:
2332 framebuffer_release(info);
2333err_out:
2334 return ret;
2335}
2336
2337void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2338{
2339 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340
Krzysztof Helt8503df62007-10-16 01:29:08 -07002341 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342}
2343
2344static struct zorro_driver cirrusfb_zorro_driver = {
2345 .name = "cirrusfb",
2346 .id_table = cirrusfb_zorro_table,
2347 .probe = cirrusfb_zorro_register,
2348 .remove = __devexit_p(cirrusfb_zorro_unregister),
2349};
2350#endif /* CONFIG_ZORRO */
2351
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002353static int __init cirrusfb_setup(char *options)
2354{
Vlada Pericee119402008-11-19 15:36:45 -08002355 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357 if (!options || !*options)
2358 return 0;
2359
Krzysztof Helt8503df62007-10-16 01:29:08 -07002360 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002361 if (!*this_opt)
2362 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364 if (!strcmp(this_opt, "noaccel"))
2365 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002366 else if (!strncmp(this_opt, "mode:", 5))
2367 mode_option = this_opt + 5;
2368 else
2369 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 }
2371 return 0;
2372}
2373#endif
2374
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375 /*
2376 * Modularization
2377 */
2378
2379MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2380MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2381MODULE_LICENSE("GPL");
2382
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002383static int __init cirrusfb_init(void)
2384{
2385 int error = 0;
2386
2387#ifndef MODULE
2388 char *option = NULL;
2389
2390 if (fb_get_options("cirrusfb", &option))
2391 return -ENODEV;
2392 cirrusfb_setup(option);
2393#endif
2394
2395#ifdef CONFIG_ZORRO
2396 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2397#endif
2398#ifdef CONFIG_PCI
2399 error |= pci_register_driver(&cirrusfb_pci_driver);
2400#endif
2401 return error;
2402}
2403
Krzysztof Helt8503df62007-10-16 01:29:08 -07002404static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405{
2406#ifdef CONFIG_PCI
2407 pci_unregister_driver(&cirrusfb_pci_driver);
2408#endif
2409#ifdef CONFIG_ZORRO
2410 zorro_unregister_driver(&cirrusfb_zorro_driver);
2411#endif
2412}
2413
2414module_init(cirrusfb_init);
2415
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002416module_param(mode_option, charp, 0);
2417MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002418module_param(noaccel, bool, 0);
2419MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002420
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421#ifdef MODULE
2422module_exit(cirrusfb_exit);
2423#endif
2424
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425/**********************************************************************/
2426/* about the following functions - I have used the same names for the */
2427/* functions as Markus Wild did in his Retina driver for NetBSD as */
2428/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002429/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430/**********************************************************************/
2431
2432/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002433static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 int regnum, unsigned char val)
2435{
2436 unsigned long regofs = 0;
2437
2438 if (cinfo->btype == BT_PICASSO) {
2439 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2441 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2443 regofs = 0xfff;
2444 }
2445
Krzysztof Helt8503df62007-10-16 01:29:08 -07002446 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447}
2448
2449/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002450static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451{
2452 unsigned long regofs = 0;
2453
2454 if (cinfo->btype == BT_PICASSO) {
2455 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002456/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2457 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2459 regofs = 0xfff;
2460 }
2461
Krzysztof Helt8503df62007-10-16 01:29:08 -07002462 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463}
2464
2465/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002466static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002468 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469
Krzysztof Helt8503df62007-10-16 01:29:08 -07002470 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471 /* if we're just in "write value" mode, write back the */
2472 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473 vga_w(cinfo->regbase, VGA_ATT_IW,
2474 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475 }
2476 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002477/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2478 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479
2480 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002481 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482}
2483
2484/*** WHDR() - write into the Hidden DAC register ***/
2485/* as the HDR is the only extension register that requires special treatment
2486 * (the other extension registers are accessible just like the "ordinary"
2487 * registers of their functional group) here is a specialized routine for
2488 * accessing the HDR
2489 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002490static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491{
2492 unsigned char dummy;
2493
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002494 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002495 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 if (cinfo->btype == BT_PICASSO) {
2497 /* Klaus' hint for correct access to HDR on some boards */
2498 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002499 WGen(cinfo, VGA_PEL_MSK, 0x00);
2500 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002502 dummy = RGen(cinfo, VGA_PEL_IW);
2503 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 }
2505 /* now do the usual stuff to access the HDR */
2506
Krzysztof Helt8503df62007-10-16 01:29:08 -07002507 dummy = RGen(cinfo, VGA_PEL_MSK);
2508 udelay(200);
2509 dummy = RGen(cinfo, VGA_PEL_MSK);
2510 udelay(200);
2511 dummy = RGen(cinfo, VGA_PEL_MSK);
2512 udelay(200);
2513 dummy = RGen(cinfo, VGA_PEL_MSK);
2514 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515
Krzysztof Helt8503df62007-10-16 01:29:08 -07002516 WGen(cinfo, VGA_PEL_MSK, val);
2517 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518
2519 if (cinfo->btype == BT_PICASSO) {
2520 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 dummy = RGen(cinfo, VGA_PEL_IW);
2522 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523
2524 /* and at the end, restore the mask value */
2525 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002526 WGen(cinfo, VGA_PEL_MSK, 0xff);
2527 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528 }
2529}
2530
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002532static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533{
2534#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002535 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538#endif
2539}
2540
2541/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002542static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543{
2544#ifdef CONFIG_ZORRO
2545 /* writing an arbitrary value to this one causes the monitor switcher */
2546 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002547 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002549 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550#endif
2551}
2552
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555 unsigned char green, unsigned char blue)
2556{
2557 unsigned int data = VGA_PEL_D;
2558
2559 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561
2562 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002563 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002564 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 /* but DAC data register IS, at least for Picasso II */
2566 if (cinfo->btype == BT_PICASSO)
2567 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568 vga_w(cinfo->regbase, data, red);
2569 vga_w(cinfo->regbase, data, green);
2570 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572 vga_w(cinfo->regbase, data, blue);
2573 vga_w(cinfo->regbase, data, green);
2574 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 }
2576}
2577
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578#if 0
2579/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002580static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 unsigned char *green, unsigned char *blue)
2582{
2583 unsigned int data = VGA_PEL_D;
2584
Krzysztof Helt8503df62007-10-16 01:29:08 -07002585 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586
2587 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2588 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2589 if (cinfo->btype == BT_PICASSO)
2590 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002591 *red = vga_r(cinfo->regbase, data);
2592 *green = vga_r(cinfo->regbase, data);
2593 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002595 *blue = vga_r(cinfo->regbase, data);
2596 *green = vga_r(cinfo->regbase, data);
2597 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598 }
2599}
2600#endif
2601
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602/*******************************************************************
2603 cirrusfb_WaitBLT()
2604
2605 Wait for the BitBLT engine to complete a possible earlier job
2606*********************************************************************/
2607
2608/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002611 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002612 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613}
2614
2615/*******************************************************************
2616 cirrusfb_BitBLT()
2617
2618 perform accelerated "scrolling"
2619********************************************************************/
2620
Krzysztof Helt8343c892009-03-31 15:25:11 -07002621static void cirrusfb_set_blitter(u8 __iomem *regbase,
2622 u_short nwidth, u_short nheight,
2623 u_long nsrc, u_long ndest,
2624 u_short bltmode, u_short line_length)
2625
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002628 /* dest pitch low */
2629 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2630 /* dest pitch hi */
2631 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2632 /* source pitch low */
2633 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2634 /* source pitch hi */
2635 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636
2637 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002638 /* BLT width low */
2639 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2640 /* BLT width hi */
2641 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642
2643 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002644 /* BLT height low */
2645 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2646 /* BLT width hi */
2647 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648
2649 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002650 /* BLT dest low */
2651 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2652 /* BLT dest mid */
2653 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2654 /* BLT dest hi */
2655 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656
2657 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002658 /* BLT src low */
2659 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2660 /* BLT src mid */
2661 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2662 /* BLT src hi */
2663 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664
2665 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002666 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667
2668 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002669 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670
2671 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002672 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002673}
2674
2675/*******************************************************************
2676 cirrusfb_BitBLT()
2677
2678 perform accelerated "scrolling"
2679********************************************************************/
2680
2681static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2682 u_short curx, u_short cury,
2683 u_short destx, u_short desty,
2684 u_short width, u_short height,
2685 u_short line_length)
2686{
2687 u_short nwidth = width - 1;
2688 u_short nheight = height - 1;
2689 u_long nsrc, ndest;
2690 u_char bltmode;
2691
2692 bltmode = 0x00;
2693 /* if source adr < dest addr, do the Blt backwards */
2694 if (cury <= desty) {
2695 if (cury == desty) {
2696 /* if src and dest are on the same line, check x */
2697 if (curx < destx)
2698 bltmode |= 0x01;
2699 } else
2700 bltmode |= 0x01;
2701 }
2702 /* standard case: forward blitting */
2703 nsrc = (cury * line_length) + curx;
2704 ndest = (desty * line_length) + destx;
2705 if (bltmode) {
2706 /* this means start addresses are at the end,
2707 * counting backwards
2708 */
2709 nsrc += nheight * line_length + nwidth;
2710 ndest += nheight * line_length + nwidth;
2711 }
2712
2713 cirrusfb_WaitBLT(regbase);
2714
2715 cirrusfb_set_blitter(regbase, nwidth, nheight,
2716 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717}
2718
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719/*******************************************************************
2720 cirrusfb_RectFill()
2721
2722 perform accelerated rectangle fill
2723********************************************************************/
2724
Krzysztof Helt8503df62007-10-16 01:29:08 -07002725static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002727 u32 fg_color, u32 bg_color, u_short line_length,
2728 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002730 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731 u_char op;
2732
Krzysztof Helt8503df62007-10-16 01:29:08 -07002733 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 /* This is a ColorExpand Blt, using the */
2736 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002737 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2738 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739
Krzysztof Helt9e848062009-03-31 15:25:11 -07002740 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002741 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002742 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2743 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2744 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002745 }
2746 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002747 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2748 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
2749 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2750 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2751 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002753 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002754 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755}
2756
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757/**************************************************************************
2758 * bestclock() - determine closest possible clock lower(?) than the
2759 * desired pixel clock
2760 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002761static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002763 int n, d;
2764 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765
Krzysztof Helt8503df62007-10-16 01:29:08 -07002766 assert(nom != NULL);
2767 assert(den != NULL);
2768 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769
2770 *nom = 0;
2771 *den = 0;
2772 *div = 0;
2773
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 if (freq < 8000)
2775 freq = 8000;
2776
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002777 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778
2779 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002780 int s = 0;
2781
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002782 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002784 int temp = d;
2785
2786 if (temp > 31) {
2787 s = 1;
2788 temp >>= 1;
2789 }
2790 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002791 h = h > freq ? h - freq : freq - h;
2792 if (h < diff) {
2793 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002795 *den = temp;
2796 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 }
2798 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002799 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002801 if (d > 31) {
2802 s = 1;
2803 d >>= 1;
2804 }
2805 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002806 h = h > freq ? h - freq : freq - h;
2807 if (h < diff) {
2808 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002810 *den = d;
2811 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812 }
2813 }
2814 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815}
2816
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817/* -------------------------------------------------------------------------
2818 *
2819 * debugging functions
2820 *
2821 * -------------------------------------------------------------------------
2822 */
2823
2824#ifdef CIRRUSFB_DEBUG
2825
2826/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 * cirrusfb_dbg_print_regs
2828 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2829 * @reg_class: type of registers to read: %CRT, or %SEQ
2830 *
2831 * DESCRIPTION:
2832 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2833 * old-style I/O ports are queried for information, otherwise MMIO is
2834 * used at the given @base address to query the information.
2835 */
2836
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002837static void cirrusfb_dbg_print_regs(struct fb_info *info,
2838 caddr_t regbase,
2839 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840{
2841 va_list list;
2842 unsigned char val = 0;
2843 unsigned reg;
2844 char *name;
2845
Krzysztof Helt8503df62007-10-16 01:29:08 -07002846 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847
Krzysztof Helt8503df62007-10-16 01:29:08 -07002848 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002850 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851
2852 switch (reg_class) {
2853 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002854 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 break;
2856 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002857 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 break;
2859 default:
2860 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002861 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862 break;
2863 }
2864
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002865 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866
Krzysztof Helt8503df62007-10-16 01:29:08 -07002867 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 }
2869
Krzysztof Helt8503df62007-10-16 01:29:08 -07002870 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871}
2872
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 * cirrusfb_dbg_reg_dump
2875 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2876 *
2877 * DESCRIPTION:
2878 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2879 * old-style I/O ports are queried for information, otherwise MMIO is
2880 * used at the given @base address to query the information.
2881 */
2882
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002883static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002885 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002887 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 "CR00", 0x00,
2889 "CR01", 0x01,
2890 "CR02", 0x02,
2891 "CR03", 0x03,
2892 "CR04", 0x04,
2893 "CR05", 0x05,
2894 "CR06", 0x06,
2895 "CR07", 0x07,
2896 "CR08", 0x08,
2897 "CR09", 0x09,
2898 "CR0A", 0x0A,
2899 "CR0B", 0x0B,
2900 "CR0C", 0x0C,
2901 "CR0D", 0x0D,
2902 "CR0E", 0x0E,
2903 "CR0F", 0x0F,
2904 "CR10", 0x10,
2905 "CR11", 0x11,
2906 "CR12", 0x12,
2907 "CR13", 0x13,
2908 "CR14", 0x14,
2909 "CR15", 0x15,
2910 "CR16", 0x16,
2911 "CR17", 0x17,
2912 "CR18", 0x18,
2913 "CR22", 0x22,
2914 "CR24", 0x24,
2915 "CR26", 0x26,
2916 "CR2D", 0x2D,
2917 "CR2E", 0x2E,
2918 "CR2F", 0x2F,
2919 "CR30", 0x30,
2920 "CR31", 0x31,
2921 "CR32", 0x32,
2922 "CR33", 0x33,
2923 "CR34", 0x34,
2924 "CR35", 0x35,
2925 "CR36", 0x36,
2926 "CR37", 0x37,
2927 "CR38", 0x38,
2928 "CR39", 0x39,
2929 "CR3A", 0x3A,
2930 "CR3B", 0x3B,
2931 "CR3C", 0x3C,
2932 "CR3D", 0x3D,
2933 "CR3E", 0x3E,
2934 "CR3F", 0x3F,
2935 NULL);
2936
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002937 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002939 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002941 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942 "SR00", 0x00,
2943 "SR01", 0x01,
2944 "SR02", 0x02,
2945 "SR03", 0x03,
2946 "SR04", 0x04,
2947 "SR08", 0x08,
2948 "SR09", 0x09,
2949 "SR0A", 0x0A,
2950 "SR0B", 0x0B,
2951 "SR0D", 0x0D,
2952 "SR10", 0x10,
2953 "SR11", 0x11,
2954 "SR12", 0x12,
2955 "SR13", 0x13,
2956 "SR14", 0x14,
2957 "SR15", 0x15,
2958 "SR16", 0x16,
2959 "SR17", 0x17,
2960 "SR18", 0x18,
2961 "SR19", 0x19,
2962 "SR1A", 0x1A,
2963 "SR1B", 0x1B,
2964 "SR1C", 0x1C,
2965 "SR1D", 0x1D,
2966 "SR1E", 0x1E,
2967 "SR1F", 0x1F,
2968 NULL);
2969
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002970 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971}
2972
2973#endif /* CIRRUSFB_DEBUG */
2974